//------------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// The use and distribution terms for this software are covered by the
// Microsoft Limited Public License (Ms-LPL)
// which can be found in the file MS-LPL.txt at the root of this distribution.
// By using this software in any fashion, you are agreeing to be bound by
// the terms of this license.
//
// The software is licensed “as-is.”
//
// You must not remove this notice, or any other, from this software.
//
//
//
// Demux code for Windows CE
//
//-------------------------------------------------------------------------
//======================================================================
// Demux code for Windows CE
//======================================================================
/*++
Copyright (c) 1999 Microsoft Corporation
Module Name:
tsctrlr.cpp
Abstract:
This module contains the implementation code for the controller.
Revision History:
13-Jul-1999 created
--*/
#include "precomp.h"
#include "mp2demux.h"
#include "mp2enum.h"
#include "tsstats.h"
#include "mp2seek.h"
#include "pin_out.h"
#include "filter.h"
#include "bufsrc.h"
#include "plparse.h"
#include "program.h"
#include "clock.h"
#include "tsmapper.h"
#include "tsctrlr.h"
#include
#define DVD_AUDIOIDMASK 0Xf8
#define DVD_AC3ID 0x80
#define DVD_PCMID 0xa0
static
WORD
g_BitRates [3][16] = {
{ 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0 },
{ 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0 },
{ 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0 }
} ;
static
const
LONG
g_PictureTimes [16] = {
0,
(LONG) ((double) 10000000 / 23.976),
(LONG) ((double) 10000000 / 24),
(LONG) ((double) 10000000 / 25),
(LONG) ((double) 10000000 / 29.97),
(LONG) ((double) 10000000 / 30),
(LONG) ((double) 10000000 / 50),
(LONG) ((double) 10000000 / 59.94),
(LONG) ((double) 10000000 / 60)
} ;
static
const
LONG
g_AspectRatios [16] = {
0,
393700,
(LONG) (393700.0 * 0.6735),
(LONG) (393700.0 * 0.7031),
(LONG) (393700.0 * 0.7615),
(LONG) (393700.0 * 0.8055),
(LONG) (393700.0 * 0.8437),
(LONG) (393700.0 * 0.8935),
(LONG) (393700.0 * 0.9375),
(LONG) (393700.0 * 0.9815),
(LONG) (393700.0 * 1.0255),
(LONG) (393700.0 * 1.0695),
(LONG) (393700.0 * 1.1250),
(LONG) (393700.0 * 1.1575),
(LONG) (393700.0 * 1.2015),
0
} ;
static
const
float
g_fPictureRates [] =
{
(float) 0,
(float) 23.976,
(float) 24,
(float) 25,
(float) 29.97,
(float) 30,
(float) 50,
(float) 59.94,
(float) 60.0
} ;
typedef struct {
DWORD dwProfile ; // Profile - MPEG2 only
DWORD dwLevel ; // Level - MPEG2 only
LONG lWidth ; // Native Width in pixels
LONG lHeight ; // Native Height in pixels
LONG lvbv ; // vbv
REFERENCE_TIME tPictureTime ; // Time per picture in 100ns units
float fPictureRate ; // In frames per second
LONG lTimePerFrame ; // Time per picture in MPEG units
LONG dwBitRate ; // Bits per second
LONG lXPelsPerMeter ; // Pel aspect ratio
LONG lYPelsPerMeter ; // Pel aspect ratio
DWORD dwStartTimeCode ; // First GOP time code (or -1)
LONG lActualHeaderLen ; // Length of valid bytes in raw seq hdr
BYTE RawHeader [256] ; // The real sequence header
} SEQHDR_INFO;
typedef struct tag_MPEG2_VIDEO_DATA {
DWORD dwARWidth ; // Aspect ratio info
DWORD dwARHeight ;
DWORD dwLevel ;
DWORD dwProfile ;
} MPEG2_VIDEO_DATA ;
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// BEGIN static methods brought over from the old splitter
//------------------------------------------------------------------------------
inline WORD ByteSwapWord(BYTE* pData)
{
return static_cast((pData[0] << 8) | pData[1]);
}
static WORD LittleEndWord(BYTE Buffer[]) { return *reinterpret_cast(&Buffer[0]); }
static WORD BigEndWord(BYTE Buffer[]) { return ByteSwapWord(&Buffer[0]); }
static
BOOL
ParseAC3Header (
BYTE * pbData,
WAVEFORMATEX *pwf
)
{
ZeroMemory((PVOID)pwf, sizeof(*pwf));
BOOL bBigEndian = FALSE;
// First check it IS AC3
if (pbData[0] == 0x77 && pbData[1] == 0x0B) {
} else
if (pbData[0] == 0x0B && pbData[1] == 0x77) {
bBigEndian = TRUE;
} else {
return FALSE;
}
/* Get the sampling rate */
BYTE bData = bBigEndian ? pbData[4] : pbData[5];
int SampleRateCode = bData >> 6;
int BitRateCode = bData & 0x3F;
switch (SampleRateCode) {
case 1:
pwf->nSamplesPerSec = 44100;
break;
case 2:
pwf->nSamplesPerSec = 32000;
break;
default: // Isn't one illegal?
pwf->nSamplesPerSec = 48000;
break;
}
if (BitRateCode >= 38) {
return FALSE;
}
/* Get the bit rate */
const static int BitRates[] =
{
32, 32, 40, 40, 48, 48, 56, 56, 64, 64,
80, 80, 96, 96, 112, 112, 128, 128, 160, 160,
192, 192, 224, 224, 256, 256, 320, 320, 384, 384,
448, 448, 512, 512, 576, 576, 640, 640
};
pwf->nAvgBytesPerSec = BitRates[BitRateCode] * (1000 / 8);
/* Get the frame size
Since every frame contains 1536 samples we can compute it as
Bypes per frame = Samples per frame (1536) * Bytes per second
--------------------------------------------
Samples per second
*/
/* Round down then the first lsb of frmsizecod can be added on
to give the frame size
*/
pwf->nBlockAlign = (WORD)((1536 * pwf->nAvgBytesPerSec) / pwf->nSamplesPerSec);
/* Round to words */
pwf->nBlockAlign &= ~1;
pwf->nChannels = 6; // Hack!
pwf->wFormatTag = WAVE_FORMAT_UNKNOWN;
TRACE_0 (LOG_TRACE, 4, TEXT ("Wave Format Dolby AC3")) ;
TRACE_3 (LOG_TRACE, 4, TEXT ("%d bytes per sec, %d samples per sec, align %d"),
pwf -> nAvgBytesPerSec,
pwf -> nSamplesPerSec,
pwf -> nBlockAlign) ;
return TRUE;
}
static
DWORD crcCheck(const UNALIGNED WORD *pw, DWORD cbSize)
{
WAVEFORMATEX wfx;
if (!ParseAC3Header((BYTE *)pw, &wfx)) {
return 1;
}
// Run off the end
if (wfx.nBlockAlign > cbSize) {
return 0xFFFFFFFF;
}
BOOL bSwap = *pw != 0x0B77;
int iLen = wfx.nBlockAlign / 2;
if (wfx.nSamplesPerSec == 44100) {
const BYTE *pbData = (BYTE *)pw;
BYTE bData = bSwap ? pbData[4] : pbData[5];
iLen += (bData & 1);
}
WORD wTotal = 0;
for (int i = 1; i < iLen; i++) {
WORD w = *(++pw);
if (bSwap) {
w = ((w & 0xFF) << 8) + (w >> 8);
}
for (int j = 0; j < 16; j++) {
WORD wb0 = (WORD)((w & 0x8000) != 0);
// Need to cancel msb?
BOOL bAdd = (wTotal & 0x8000) != 0;
// Compute new remainder
wTotal = (wTotal << 1) + wb0;
if (bAdd) {
wTotal ^= 0x8005;
}
// Next bit
w <<= 1;
}
}
if (wTotal != 0) {
TRACE_0 (LOG_TRACE, 5, TEXT ("CRC failed")) ;
}
return (DWORD)wTotal;
}
static
int
FindFirstFrame (
IN BYTE Buffer [],
IN DWORD cbSize,
IN DWORD * pFirstFrameOffset,
IN WORD (* GetWord) (BYTE [])
) {
static WORD SyncWord = 0x0B77 ;
unsigned cursor = 0;
int SyncWordCount = 0;
while ( cursor + 6 < cbSize // we will dereference 6 bytes after the syncword when initializing the type block.
// && SyncWordCount < 1 // scan for first syncword
) {
WORD Curr = GetWord(& Buffer [cursor]);
DWORD dwCheck = 0 ;
if ( Curr == SyncWord
&& 0 == (dwCheck = crcCheck(reinterpret_cast(&Buffer[cursor]), cbSize - cursor))
) { // got one.
*pFirstFrameOffset = cursor;
++SyncWordCount;
WAVEFORMATEX wfx;
ParseAC3Header((BYTE *)&Buffer[cursor], &wfx);
ASSERT(wfx.nBlockAlign > 0);
cursor += wfx.nBlockAlign;
}
else {
// Only accept continguous frames
if (SyncWordCount > 0) {
return dwCheck == 0xFFFFFFFF ? SyncWordCount : 0;
}
++cursor;
}
}
return SyncWordCount;
}
static
int
FindFirstFrame( BYTE Buffer[]
, DWORD cbSize
, DWORD * pFirstFrameOffset
) {
int found = FindFirstFrame(Buffer, cbSize, pFirstFrameOffset, BigEndWord);
if (found) {
return found;
}
else {
return FindFirstFrame(Buffer, cbSize, pFirstFrameOffset, LittleEndWord);
}
}
static
__inline
LONG
BufferBytesRemaining (
IN LONG lBufferLength,
IN BYTE * pbBufferStart,
IN BYTE * pbBufferCurrent
)
{
LONG lBufferRemaining ;
lBufferRemaining = (LONG) (pbBufferCurrent - pbBufferStart) ;
return (lBufferRemaining >= 0 ? lBufferRemaining : 0) ;
}
static
int
SequenceHeaderSize (
IN BYTE * pb
)
{
// No quantization matrices
if ((pb [11] & 0x03) == 0x00) {
return 12 ;
}
// Just non-intra quantization matrix ?
if ((pb [11] & 0x03) == 0x01) {
return 12 + 64 ;
}
// Intra found - is there a non-intra ?
if (pb [11 + 64] & 0x01) {
return 12 + 64 + 64 ;
} else {
return 12 + 64 ;
}
}
static
HRESULT
GetMpeg2VideoMediaType (
IN OUT CMediaType * cmt,
IN OUT SEQHDR_INFO * pInfo,
IN OUT MPEG2_VIDEO_DATA * pMPEG2
)
{
BYTE * pFBuff ;
VIDEOINFOHEADER2 * videoInfo ;
MPEG2VIDEOINFO * mpgvideoInfo ;
cmt -> majortype = MEDIATYPE_Video ;
cmt -> subtype = MEDIASUBTYPE_MPEG2_VIDEO ;
pFBuff = cmt -> AllocFormatBuffer( sizeof MPEG2VIDEOINFO + sizeof DWORD * (pInfo->lActualHeaderLen / sizeof DWORD + 1)) ;
videoInfo = reinterpret_cast (pFBuff) ;
if (videoInfo == NULL) {
return E_OUTOFMEMORY ;
}
ZeroMemory(
videoInfo,
sizeof MPEG2VIDEOINFO
) ;
videoInfo -> dwBitRate = pInfo -> dwBitRate ;
videoInfo -> rcSource.right = pInfo -> lWidth ;
videoInfo -> bmiHeader.biWidth = pInfo -> lWidth ;
videoInfo -> rcSource.bottom = pInfo -> lHeight ;
videoInfo -> bmiHeader.biHeight = pInfo -> lHeight ;
videoInfo -> bmiHeader.biXPelsPerMeter = pInfo -> lXPelsPerMeter ;
videoInfo -> bmiHeader.biYPelsPerMeter = pInfo -> lYPelsPerMeter ;
videoInfo -> bmiHeader.biSize = sizeof BITMAPINFOHEADER ;
videoInfo -> AvgTimePerFrame = pInfo -> tPictureTime ;
mpgvideoInfo = reinterpret_cast (videoInfo) ;
mpgvideoInfo -> cbSequenceHeader = pInfo -> lActualHeaderLen ;
CopyMemory (
(PVOID) mpgvideoInfo -> dwSequenceHeader,
(PVOID) pInfo -> RawHeader,
pInfo -> lActualHeaderLen
) ;
mpgvideoInfo -> dwStartTimeCode = pInfo -> dwStartTimeCode ;
// ? This is all we support, right?
if (pMPEG2) {
mpgvideoInfo -> dwLevel = pMPEG2 -> dwLevel ;
mpgvideoInfo -> dwProfile = pMPEG2 -> dwProfile ;
// Set the aspect ration
videoInfo -> dwPictAspectRatioX = pMPEG2 -> dwARWidth ;
videoInfo -> dwPictAspectRatioY = pMPEG2 -> dwARHeight ;
}
cmt -> SetFormatType (& FORMAT_MPEG2Video) ;
return S_OK;
}
static
BOOL
CheckAudioHeader (
IN BYTE * pbData
)
{
if ((pbData [2] & 0x0C) == 0x0C) {
TRACE_ERROR_0 (TEXT("Invalid audio sampling frequency")) ;
return FALSE ;
}
if ((pbData [1] & 0x08) != 0x08) {
TRACE_ERROR_0 (TEXT("Invalid audio ID bit = 0")) ;
return FALSE ;
}
if (((pbData [1] >> 1) & 3) == 0x00) {
TRACE_ERROR_0 (TEXT("Invalid audio Layer")) ;
return FALSE ;
}
if (((pbData [2] >> 2) & 3) == 3) {
TRACE_ERROR_0 (TEXT("Invalid sample rate")) ;
return FALSE ;
}
if ((pbData [2] >> 4) == 0x0F) {
TRACE_ERROR_0 (TEXT("Invalid bit rate")) ;
return FALSE ;
}
return TRUE ;
}
static
LONG
SampleRate (
IN BYTE * pbData
)
{
switch ((pbData [2] >> 2) & 3) {
case 0 :
return 44100 ;
case 1 :
return 48000 ;
case 2 :
return 32000 ;
default :
return 44100 ;
}
}
static
BOOL
ParseAudioHeader (
IN BYTE * pbPESPayload,
IN MPEG1WAVEFORMAT * pFormat
)
{
int Layer ;
if (!CheckAudioHeader (pbPESPayload)) {
return FALSE ;
}
pFormat -> wfx.wFormatTag = WAVE_FORMAT_MPEG ;
switch (pbPESPayload [3] >> 6) {
case 0x00 :
pFormat->fwHeadMode = ACM_MPEG_STEREO ;
break ;
case 0x01 :
pFormat->fwHeadMode = ACM_MPEG_JOINTSTEREO ;
break ;
case 0x02 :
pFormat->fwHeadMode = ACM_MPEG_DUALCHANNEL ;
break ;
case 0x03 :
pFormat->fwHeadMode = ACM_MPEG_SINGLECHANNEL ;
break ;
}
pFormat -> wfx.nChannels = (WORD) (pFormat -> fwHeadMode == ACM_MPEG_SINGLECHANNEL ? 1 : 2) ;
pFormat -> fwHeadModeExt = (WORD) (1 << (pbPESPayload [3] >> 4)) ;
pFormat -> wHeadEmphasis = (WORD) ((pbPESPayload [3] & 0x03) + 1) ;
pFormat -> fwHeadFlags = (WORD) (((pbPESPayload [2] & 1) ? ACM_MPEG_PRIVATEBIT : 0) +
((pbPESPayload [3] & 8) ? ACM_MPEG_COPYRIGHT : 0) +
((pbPESPayload [3] & 4) ? ACM_MPEG_ORIGINALHOME : 0) +
((pbPESPayload [1] & 1) ? ACM_MPEG_PROTECTIONBIT : 0) +
((pbPESPayload [1] & 0x08) ? ACM_MPEG_ID_MPEG1 : 0)) ;
switch ((pbPESPayload [1] >> 1) & 3) {
case 3 :
pFormat -> fwHeadLayer = ACM_MPEG_LAYER1 ;
Layer = 1 ;
break ;
case 2 :
pFormat -> fwHeadLayer = ACM_MPEG_LAYER2 ;
Layer = 2 ;
break ;
case 1 :
pFormat -> fwHeadLayer = ACM_MPEG_LAYER3 ;
Layer = 3 ;
break ;
case 0 :
return FALSE ;
}
// Get samples per second from sampling frequency
pFormat -> wfx.nSamplesPerSec = SampleRate (pbPESPayload) ;
pFormat -> dwHeadBitrate = (DWORD) g_BitRates [Layer - 1] [pbPESPayload [2] >> 4] * 1000 ;
pFormat -> wfx.nAvgBytesPerSec = pFormat -> dwHeadBitrate / 8 ;
// Layer 3 can sometimes switch bitrates
if (pFormat -> wfx.nSamplesPerSec != 44100 &&
!(Layer == 3 && (pbPESPayload[2] >> 4) == 0)) {
if (Layer == 1) {
pFormat -> wfx.nBlockAlign = (WORD) (4 * ((pFormat -> dwHeadBitrate * 12) / pFormat -> wfx.nSamplesPerSec)) ;
}
else {
pFormat -> wfx.nBlockAlign = (WORD) ((144 * pFormat -> dwHeadBitrate) / pFormat -> wfx.nSamplesPerSec) ;
}
}
else {
pFormat -> wfx.nBlockAlign = 1 ;
}
pFormat -> wfx.wBitsPerSample = 0 ;
pFormat -> wfx.cbSize = sizeof MPEG1WAVEFORMAT - sizeof WAVEFORMATEX ;
pFormat -> dwPTSLow = 0 ;
pFormat -> dwPTSHigh = 0 ;
return TRUE ;
}
static
BOOL
ParseSequenceHeader(
IN BYTE * pbPESPayload,
IN LONG lData,
IN SEQHDR_INFO * pInfo
)
{
DWORD dwWidthAndHeight ;
BYTE PelAspectRatioAndPictureRate ;
LONG lNotionalPelsPerMeter ;
ASSERT(IsStartCodePrefix (pbPESPayload));
ASSERT (lData >= 11) ;
// Check random marker bit
if ((pbPESPayload[10] & 0x20) == 0x00) {
TRACE_ERROR_0 (TEXT ("Sequence header invalid marker bit")) ;
return FALSE ;
}
dwWidthAndHeight = ((DWORD) pbPESPayload [4] << 16) +
((DWORD) pbPESPayload [5] << 8) +
((DWORD) pbPESPayload [6]) ;
pInfo->lWidth = dwWidthAndHeight >> 12;
pInfo->lHeight = dwWidthAndHeight & 0xFFF;
// the '8' bit is the scramble flag used by sigma designs - ignore
PelAspectRatioAndPictureRate = pbPESPayload [7] ;
if ((PelAspectRatioAndPictureRate & 0x0F) > 8) {
PelAspectRatioAndPictureRate &= 0xF7 ;
}
if ((PelAspectRatioAndPictureRate & 0xF0) == 0x00 ||
(PelAspectRatioAndPictureRate & 0x0F) == 0x00) {
TRACE_ERROR_0 (TEXT ("Sequence header invalid ratio/rate")) ;
return FALSE ;
}
pInfo -> tPictureTime = (LONGLONG) g_PictureTimes [PelAspectRatioAndPictureRate & 0x0F] ;
pInfo -> fPictureRate = g_fPictureRates [PelAspectRatioAndPictureRate & 0x0F] ;
pInfo -> lTimePerFrame = MulDiv((LONG) pInfo -> tPictureTime, 9, 1000) ;
// Pull out the bit rate and aspect ratio for the type
pInfo -> dwBitRate = ((((DWORD) pbPESPayload [8] << 16) +
((DWORD) pbPESPayload [9] << 8) +
(DWORD) pbPESPayload [10]) >> 6) ;
if (pInfo->dwBitRate == 0x3FFFF) {
TRACE_ERROR_0 (TEXT("Variable video bit rate")) ;
pInfo -> dwBitRate = 0 ;
} else {
pInfo -> dwBitRate *= 400 ;
TRACE_ERROR_1 (TEXT("Video bit rate is %d bits per second"), pInfo -> dwBitRate) ;
}
lNotionalPelsPerMeter = 2000 ;
pInfo -> lXPelsPerMeter = lNotionalPelsPerMeter ;
pInfo -> lYPelsPerMeter = MulDiv(
lNotionalPelsPerMeter,
g_AspectRatios [PelAspectRatioAndPictureRate >> 4],
10000
) ;
// Pull out the vbv
pInfo->lvbv = ((((LONG)pbPESPayload [10] & 0x1F) << 5) |
((LONG)pbPESPayload [11] >> 3)) * 2048 ;
TRACE_1 (LOG_TRACE, 2, TEXT ("vbv size is %d bytes"), pInfo -> lvbv) ;
// Check constrained parameter stuff
if (pbPESPayload [11] & 0x04) {
TRACE_0 (LOG_TRACE, 2, TEXT ("Constrained parameter video stream")) ;
if (pInfo -> lvbv > 40960) {
TRACE_ERROR_1 (TEXT("Invalid vbv (%d) for Constrained stream"), pInfo -> lvbv) ;
// Have to let this through too! bisp.mpg has this
// But constrain it since it might be random
pInfo -> lvbv = 40960 ;
}
}
// tp_orig has a vbv of 2048 (!)
if (pInfo -> lvbv < 20000) {
TRACE_1 (LOG_TRACE, 2, TEXT("Small vbv (%d) - setting to 40960"), pInfo -> lvbv) ;
pInfo -> lvbv = 40960 ;
}
pInfo -> lActualHeaderLen = SequenceHeaderSize (pbPESPayload);
CopyMemory (
(PVOID) pInfo -> RawHeader,
(PVOID) pbPESPayload,
pInfo -> lActualHeaderLen
) ;
return TRUE;
}
// ENDD static methods brought over from the old splitter
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// ----------------------------------------------------------------------------
CMPEG2Controller::CMPEG2Controller (
IN HKEY hkeyRoot, // closed by caller after constructor completes
IN CMPEG2Demultiplexer * pCMPEG2Demultiplexer,
IN CMpeg2Stats * pStats,
IN CMPEG2PushClock * pCMPEG2PushClock,
IN DWORD StreamType,
OUT HRESULT * phr
) : m_pStreamMapper (NULL),
m_pMpeg2StreamContentManager (NULL),
m_pMapperLock (NULL),
m_fInPullMode (FALSE),
m_pStats (pStats),
m_hkeyRoot (hkeyRoot),
m_StreamType (StreamType),
m_pCMPEG2PushClock (pCMPEG2PushClock),
m_pCMPEG2Demultiplexer (pCMPEG2Demultiplexer),
m_dwMaxWrapperPoolSize (MAX_WRAPPER_POOL_SIZE)
{
O_TRACE_ENTER_0 (TEXT("CMPEG2Controller::CMPEG2Controller ()")) ;
TRACE_CONSTRUCTOR (TEXT ("CMPEG2Controller")) ;
ASSERT (m_pStats) ;
ASSERT (m_pCMPEG2PushClock) ;
ASSERT (m_pCMPEG2Demultiplexer) ;
InitializeListHead (& m_AVStreamMapsList) ;
// our ref to the stats
m_pStats -> AddRef () ;
// report discontinuities ?
m_fReportDiscontinuities = REG_DEFAULT_REPORT_DISCONTINUITIES ;
RegGetValIfExist (
hkeyRoot,
REG_REPORT_DISCONTINUITIES,
TRUE,
(DWORD *) & m_fReportDiscontinuities
) ;
// sync point behavior
m_fSetSyncPoints = REG_DEFAULT_SET_SYNC_POINTS ;
RegGetValIfExist (
hkeyRoot,
REG_SET_SYNC_POINTS,
TRUE,
(DWORD *) & m_fSetSyncPoints
) ;
// pull mode behavior
m_pMapperLock = new CRefCountedCritSec ;
if (m_pMapperLock == NULL) {
(* phr) = E_OUTOFMEMORY ;
return ;
}
// success !
(* phr) = S_OK ;
}
CMPEG2Controller::~CMPEG2Controller (
)
{
O_TRACE_ENTER_0 (TEXT("CMPEG2Controller::~CMPEG2Controller ()")) ;
TRACE_DESTRUCTOR (TEXT ("CMPEG2Controller")) ;
// call this from here, before the mapper goes out of scope
ClearConfig () ;
ASSERT (IsListEmpty (& m_AVStreamMapsList)) ;
delete m_pMpeg2StreamContentManager ;
// delete the mapper last
delete m_pStreamMapper ;
RELEASE_AND_CLEAR (m_pMapperLock) ;
RELEASE_AND_CLEAR (m_pStats) ;
// don't close m_hkeyRoot since it's not duplicated !
}
HRESULT
CMPEG2Controller::UnmapAllStreamsLocked_ (
IN CMPEG2DemuxOutputPin * pDemuxOutputPin
)
{
HRESULT hr ;
DWORD cMaps ;
#ifndef UNDER_CE
CStreamToPinMap ** ppStreamMap ;
#endif //UNDER_CE
CStreamToPinMap * pStreamMap ;
DWORD i ;
DWORD dwFirstIndex ;
O_TRACE_ENTER_1 (TEXT ("CMPEG2Controller::UnmapAllStreamsLocked_ (%08xh)"), pDemuxOutputPin) ;
// determine the scope, based on the pDemuxOutputPin ==/!= NULL
if (pDemuxOutputPin != NULL) {
// a pin has been specified; we unmap the block that is associated
// with just that pin
// discover the first mapping in the vector to the specified pin
hr = FindPinStreamMapRecordLocked_ (
pDemuxOutputPin,
& cMaps,
& dwFirstIndex
) ;
if (FAILED (hr)) {
// nothing was found
cMaps = 0 ;
}
}
else {
// no pin has been specified; remove all stream maps for all pins
cMaps = m_PinStreamMaps.GetCount () ;
dwFirstIndex = 0 ;
}
hr = S_OK ;
// if there are maps to be deleted, do so now
if (cMaps > 0) {
for (i = 0; i < cMaps; i++) {
// pull the first out
pStreamMap = m_PinStreamMaps [dwFirstIndex] ;
ASSERT (pStreamMap) ;
hr = m_pStreamMapper -> UnmapStream (
pStreamMap -> GetStream (),
pStreamMap -> GetNonRefdParser ()
) ;
// We remove the mapping in the controller iff the mapper successfully
// removed it. The reasonnning here is if the mapper did not remove it
// it's probably because it did not exist, so there's no perf hit in having
// a bogus Stream map sitting here in the controller. On the other hand, if
// the Stream is mapped, and it is a perf hit, we want to have a chance at
// removing it again. If we remove it from the controller, there's no way
// to get the right information to the mapper to remove the real thing.
if (SUCCEEDED (hr)) {
// if the map is AV must remove it from the list of AV streams
if (pStreamMap -> GetSampleContent () == MPEG2_MEDIA_ELEMENTARY_STREAM) {
AVStreamPop_ (pStreamMap) ;
// don't reset the other streams; this operation does not
// affect them
}
// remove the context from the vector
m_PinStreamMaps.Remove (dwFirstIndex) ;
// free the resources
delete pStreamMap ;
}
else {
// abort
break ;
}
}
}
return hr ;
}
HRESULT
CMPEG2Controller::Active (
)
{
HRESULT hr ;
hr = Initialize () ;
return hr ;
}
HRESULT
CMPEG2Controller::Inactive (
)
{
CStreamToPinMap * pStreamMap ;
DWORD i ;
O_TRACE_ENTER_0 (TEXT("CMPEG2Controller::Inactive ()")) ;
// activate all the streams
for (i = 0;;i++) {
pStreamMap = m_PinStreamMaps [i] ;
if (pStreamMap) {
pStreamMap -> Inactive () ;
}
else {
break ;
}
}
return S_OK ;
}
HRESULT
CMPEG2Controller::ClearConfig (
)
{
HRESULT hr ;
O_TRACE_ENTER_0 (TEXT("CMPEG2Controller::ClearConfig ()")) ;
// XXXX: revisit this condition
ASSERT (m_pStreamMapper) ;
LockRecv_ () ;
LockMapper_ () ;
// unmap everything in the controller and the mapper
hr = UnmapAllStreamsLocked_ () ;
SetPullMode (FALSE) ;
UnlockMapper_ () ;
UnlockRecv_ () ;
return hr ;
}
HRESULT
CMPEG2Controller::Initialize (
)
{
CStreamToPinMap * pStreamMap ;
DWORD i ;
HRESULT hr ;
O_TRACE_ENTER_0 (TEXT("CMPEG2Controller::Initialize ()")) ;
ASSERT (m_pCMPEG2PushClock) ;
ASSERT (m_pStreamMapper) ;
ASSERT (m_pMpeg2StreamContentManager) ;
LockRecv_ () ;
LockMapper_ () ;
// activate all the streams
for (i = 0;;i++) {
pStreamMap = m_PinStreamMaps [i] ;
if (pStreamMap) {
pStreamMap -> Active (m_fInPullMode) ;
}
else {
break ;
}
}
hr = m_pMpeg2StreamContentManager -> Reset () ;
if (SUCCEEDED (hr)) {
hr = m_pStreamMapper -> Initialize () ;
}
UnlockMapper_ () ;
UnlockRecv_ () ;
return hr ;
}
void
CMPEG2Controller::GetQPCBufferAndLocalTime_ (
IN IMediaSample * pIMediaSample,
OUT LONGLONG * pqpcBuffer,
OUT LONGLONG * pqpcLocal
)
{
HRESULT hr ;
LONGLONG llStart ;
LONGLONG llEnd ;
LARGE_INTEGER li ;
ASSERT (pIMediaSample) ;
// first get the local time
QueryPerformanceCounter (
& li
) ;
* pqpcLocal = li.QuadPart ;
hr = pIMediaSample -> GetMediaTime (
& llStart,
& llEnd
) ;
if (FAILED (hr) ||
llEnd == 0) {
// there's no qpc time on this media sample, so use the local time
* pqpcBuffer = * pqpcLocal ;
}
else {
* pqpcBuffer = llStart ;
}
}
HRESULT
CMPEG2Controller::ProcessMediaSampleLocked (
IN IMediaSample * pIMediaSample
)
/*++
locks held:
receiver
--*/
{
HRESULT hr ;
BYTE * pbData ;
int iDataLength ;
LONGLONG qpcBufferTime ;
LONGLONG qpcLocalTime ;
O_TRACE_ENTER_1(TEXT("CMPEG2Controller::ProcessMediaSampleLocked (%08xh)"), pIMediaSample) ;
hr = pIMediaSample -> GetPointer (& pbData) ;
if (FAILED (hr)) {
TRACE_ERROR_0 (TEXT ("CMPEG2Controller::ProcessMediaSample () : pIMediaSample -> GetPointer () failed")) ;
return hr ;
}
iDataLength = pIMediaSample -> GetActualDataLength () ;
if (iDataLength > 0) {
// get the buffer and local qpc times
GetQPCBufferAndLocalTime_ (
pIMediaSample,
& qpcBufferTime,
& qpcLocalTime
) ;
m_pMapperLock -> Lock () ;
PERFLOG_STREAMTRACE(
1,
PERFINFO_STREAMTRACE_MPEG2DEMUX_SAMPLE_RECEIVED,
qpcBufferTime, qpcLocalTime, 0, 0, 0 );
// log those with the clock
m_pCMPEG2PushClock -> LogInputBufferQPCTime (
qpcBufferTime,
qpcLocalTime,
iDataLength
) ;
// now process the buffer
hr = m_pStreamMapper -> Process (
pIMediaSample,
pbData,
iDataLength
) ;
NE_SPEW (hr, S_OK, TEXT ("CMPEG2Controller::ProcessMediaSample () : m_pStreamMapper -> ProcessTS () returned an error")) ;
m_pMapperLock -> Unlock () ;
}
return hr ;
}
HRESULT
CMPEG2Controller::FindPinAndContentRecordLocked_ (
IN DWORD MediaSampleContent,
IN CMPEG2DemuxOutputPin * pDemuxOutputPin,
OUT CDemuxBaseParser ** ppParser
)
{
HRESULT hr ;
DWORD cMaps ;
DWORD dwIndex ;
DWORD i ;
CStreamToPinMap * pStreamMap ;
ASSERT (pDemuxOutputPin) ;
// figure out the block for this pin's maps
hr = FindPinStreamMapRecordLocked_ (
pDemuxOutputPin,
& cMaps,
& dwIndex
) ;
if (SUCCEEDED (hr)) {
// initialize the return value for the search
hr = E_FAIL ;
m_PinStreamMaps.Lock () ;
for (i = dwIndex; i < cMaps; i++) {
pStreamMap = m_PinStreamMaps [i] ;
ASSERT (pStreamMap) ;
if (pStreamMap -> GetSampleContent () == MediaSampleContent) {
// success !
hr = S_OK ;
// set
(* ppParser) = pStreamMap -> GetNonRefdParser () ;
// and ref
(* ppParser) -> AddRef () ;
break ;
}
}
m_PinStreamMaps.Unlock () ;
}
return hr ;
}
void
CMPEG2Controller::AVStreamsResetAll_ (
)
{
LIST_ENTRY * pListEntry ;
CStreamToPinMap * pStreamMap ;
for (pListEntry = m_AVStreamMapsList.Flink;
pListEntry != & m_AVStreamMapsList;
pListEntry = pListEntry -> Flink) {
pStreamMap = CONTAINING_RECORD (pListEntry, CStreamToPinMap, m_ListEntry) ;
pStreamMap -> GetNonRefdParser () -> Reset () ;
pStreamMap -> GetNonRefdPin () -> DeliverBeginFlush () ;
pStreamMap -> GetNonRefdPin () -> DeliverEndFlush () ;
ASSERT (pStreamMap -> GetSampleContent () == MPEG2_MEDIA_ELEMENTARY_STREAM) ;
}
}
HRESULT
CMPEG2Controller::GetPayloadParser_ (
IN DWORD dwStreamIdentifier,
IN DWORD MediaSampleContent,
IN CMPEG2DemuxOutputPin * pDemuxOutputPin,
IN CBufferSource * pBufferSource,
IN LPVOID pvParserArg, // depends on the type of parser; can be NULL
OUT CDemuxBaseParser ** ppParser
)
{
HRESULT hr ;
O_TRACE_ENTER_4(TEXT("CMPEG2Controller::GetPayloadParser_ (%08xh, %08xh, %08xh, %08xh)"), dwStreamIdentifier, MediaSampleContent, pDemuxOutputPin, pBufferSource) ;
ASSERT (pBufferSource) ;
ASSERT (ppParser) ;
hr = S_OK ;
* ppParser = NULL ;
switch (MediaSampleContent) {
case MPEG2_MEDIA_TRANSPORT_PACKET :
// first try to find one that exists
hr = FindPinAndContentRecordLocked_ (
MPEG2_MEDIA_TRANSPORT_PACKET,
pDemuxOutputPin,
ppParser
) ;
if (FAILED (hr)) {
hr = S_OK ;
// one does not currently exist to the pin; instantiate new
(* ppParser) = new CMpeg2TSPassThroughParser (
pBufferSource,
m_pStats
) ;
}
break ;
case MPEG2_MEDIA_ELEMENTARY_STREAM :
(* ppParser) = new CMpeg2PESStreamParser (
pBufferSource,
m_pCMPEG2PushClock,
dwStreamIdentifier,
m_pStats,
reinterpret_cast (pvParserArg),
m_hkeyRoot,
& hr
) ;
if (FAILED (hr)) {
delete (* ppParser) ;
(* ppParser) = NULL ;
}
break ;
case MPEG2_MEDIA_TRANSPORT_PSI :
(* ppParser) = new CMpeg2NonVersionedPSIParse (
pBufferSource,
m_pStats
) ;
break ;
case MPEG2_MEDIA_TRANSPORT_PSI_PAT :
ASSERT (pvParserArg != NULL) ;
(* ppParser) = new CMpeg2PATSectionParser (
pBufferSource,
m_pStats,
* (DWORD *) pvParserArg
) ;
break ;
case MPEG2_MEDIA_TRANSPORT_PSI_PMT :
ASSERT (pvParserArg != NULL) ;
(* ppParser) = new CMpeg2PMTSectionParser (
pBufferSource,
m_pStats,
* (DWORD *) pvParserArg
) ;
break ;
case MPEG2_MEDIA_TRANSPORT_PCR :
(* ppParser) = new CMpeg2PCRParser (
pBufferSource,
m_pStats
) ;
break ;
case MPEG2_MEDIA_TRANSPORT_PAYLOAD :
(* ppParser) = new CMpeg2GenericTSPayload (
pBufferSource,
m_pStats
) ;
break ;
case MPEG2_MEDIA_PROGRAM_SCR :
(* ppParser) = new CMpeg2SCRParser (
pBufferSource,
m_pStats
) ;
break ;
case MPEG2_MEDIA_PROGRAM_ANALOGCOPYPROTECTION :
(* ppParser) = new CMpeg2AnalogCopyProtectionParser (
pBufferSource,
m_pStats
) ;
break ;
case MPEG2_MEDIA_PROGRAM_DIRECTORY_PES_PACKET :
case MPEG2_MEDIA_PROGRAM_PACK_HEADER :
case MPEG2_MEDIA_TRANSPORT_PSI_CAT :
case MPEG2_MEDIA_TRANSPORT_PSI_PRIVATE_PURE :
case MPEG2_MEDIA_TRANSPORT_PSI_PRIVATE_NOT_PURE :
case MPEG2_MEDIA_PROGRAM_STREAM_MAP :
case MPEG2_MEDIA_PROGRAM_SYSTEM_HEADER :
case MPEG2_MEDIA_PES_STREAM :
return E_NOTIMPL ;
} ;
if (* ppParser == NULL || FAILED (hr)) {
hr = (FAILED (hr) ? hr : E_OUTOFMEMORY) ;
}
return hr ;
}
HRESULT
CMPEG2Controller::MapStreamInternal (
IN DWORD dwStream,
IN CBufferSource * pBufferSource,
IN DWORD dwTypeParser, // MPEG2_PSI_PAT, MPEG2_PSI_CAT, MPEG2_PSI_PMT, ..; see mp2const.h
IN LPVOID pvParserArg, // depends on the type of parser; can be NULL
OUT DWORD_PTR * pdwpContext
)
{
HRESULT hr ;
CDemuxBaseParser * pPayloadParser ;
O_TRACE_ENTER_4 (TEXT("CMPEG2Controller::MapStreamInternal (%08xh, %08xh, %08xh, %08xh)"), dwStream, pBufferSource, dwTypeParser, pdwpContext) ;
// program manager can only request Stream maps of these types
ASSERT (pBufferSource) ;
ASSERT (pdwpContext) ;
hr = GetPayloadParser_ (
dwStream,
dwTypeParser,
NULL,
pBufferSource,
pvParserArg,
& pPayloadParser
) ;
if (SUCCEEDED (hr)) {
// pass the requested Stream mapping down to the mapper
hr = m_pStreamMapper -> MapStream (
dwStream,
pPayloadParser
) ;
if (SUCCEEDED (hr)) {
// only reference left should be mapper's
pPayloadParser -> Release () ;
// pass the parser back out as the context
* pdwpContext = reinterpret_cast (pPayloadParser) ;
}
}
return hr ;
}
// unmap a Stream from a pin
HRESULT
CMPEG2Controller::UnmapStreamInternal (
IN DWORD dwStream,
IN DWORD_PTR pdwpContext
)
{
ASSERT (pdwpContext) ;
return m_pStreamMapper -> UnmapStream (
dwStream,
reinterpret_cast (pdwpContext)
) ;
}
HRESULT
CMPEG2Controller::MapStream (
IN DWORD dwStreamIdentifier,
IN CMPEG2DemuxOutputPin * pDemuxOutputPin,
IN DWORD MediaSampleContent,
IN LPVOID pvParserArg
)
{
HRESULT hr ;
CBufferSource * pBufferSource ;
CDemuxBaseParser * pPayloadParser ;
CStreamToPinMap * pStreamMap ;
CStreamToPinMap ** ppStreamMap ;
DWORD cStreamMap ;
#ifndef UNDER_CE
DWORD i ;
#endif //UNDER_CE
DWORD dwIndex ;
CMediaType mt ;
O_TRACE_ENTER_3(TEXT("CMPEG2Controller::MapStream (dwStreamIdentifier = %08xh, pDemuxOutputPin = %08xh, content = %08xh)"), dwStreamIdentifier, pDemuxOutputPin, MediaSampleContent) ;
if (!IsValidStream (dwStreamIdentifier)) {
return E_INVALIDARG ;
}
ASSERT (pDemuxOutputPin) ;
m_pMapperLock -> Lock () ;
// first check that the pin is not already mapped
hr = FindPinStreamMapRecordLocked_ (
pDemuxOutputPin,
dwStreamIdentifier,
& dwIndex
) ;
if (SUCCEEDED (hr)) {
// it is mapped; we unmap it iff it's a different type of map
// than wnat we are being asked to do
pStreamMap = m_PinStreamMaps [dwIndex] ;
ASSERT (pStreamMap) ;
if (pStreamMap -> GetSampleContent () == MediaSampleContent) {
// we're already there; unlock and return
hr = S_OK ;
goto cleanup ;
}
// it's the same stream identifier but of a different type; unmap the
// the old and continue on
hr = UnmapStream (
dwStreamIdentifier,
pDemuxOutputPin
) ;
if (FAILED (hr)) {
goto cleanup ;
}
}
// reset from above failure (to find the map)
hr = S_OK ;
// initialize the parser to NULL
pPayloadParser = NULL ;
// instantiate the buffer source
pDemuxOutputPin -> GetMediaType (0, & mt) ;
if (IsCopyBufferSource_ (MediaSampleContent, & mt)) {
pBufferSource = new CMediaSampleCopyBuffer (
(CMPEG2DemuxOutputPin *) pDemuxOutputPin,
m_fReportDiscontinuities,
m_fSetSyncPoints,
m_pMapperLock,
m_hkeyRoot,
dwStreamIdentifier,
m_pStats
) ;
}
else {
pBufferSource = new CMediaSampleWrapperPool (
(CMPEG2DemuxOutputPin *) pDemuxOutputPin,
m_fReportDiscontinuities,
m_fSetSyncPoints,
m_pMapperLock,
m_dwMaxWrapperPoolSize,
m_hkeyRoot,
m_pStats,
& hr
) ;
}
if (pBufferSource == NULL ||
FAILED (hr)) {
delete pBufferSource ;
hr = (FAILED (hr) ? hr : E_OUTOFMEMORY) ;
TRACE_ERROR_0 (TEXT ("CMPEG2Controller::MapStreamLocked () : failed to obtain pBufferSource")) ;
goto cleanup ;
}
ASSERT (pBufferSource) ;
// get an appropriate parser
hr = GetPayloadParser_ (
dwStreamIdentifier,
MediaSampleContent,
pDemuxOutputPin,
pBufferSource,
pvParserArg,
& pPayloadParser
) ;
// release our own refcount on this object; if the above call succeeded, parser will
// be the remaining reference; if it failed, this will release the last reference to it
pBufferSource -> Release () ;
if (FAILED (hr)) {
TRACE_ERROR_0 (TEXT ("CMPEG2Controller::MapStreamLocked () : GetPayloadParser_ () call failed")) ;
goto cleanup ;
}
ASSERT (pPayloadParser) ;
// pass the shebang down to the mapper
hr = m_pStreamMapper -> MapStream (
dwStreamIdentifier,
pPayloadParser
) ;
if (FAILED (hr)) {
pPayloadParser -> Release () ;
TRACE_ERROR_0 (TEXT ("CMPEG2Controller::MapStreamLocked () : m_pStreamMapper -> MapStream ()")) ;
goto cleanup ;
}
// we still have a refcount of 1
// now set things up for out Stream map list
pStreamMap = new CStreamToPinMap (
pPayloadParser,
MediaSampleContent,
dwStreamIdentifier,
pDemuxOutputPin,
pvParserArg,
& hr
) ;
if (pStreamMap == NULL ||
FAILED (hr)) {
delete pStreamMap ;
// we still hold the lock, so we're ok
m_pStreamMapper -> UnmapStream (
dwStreamIdentifier,
pPayloadParser
) ;
// should be the final release
pPayloadParser -> Release () ;
hr = (FAILED (hr) ? hr : E_OUTOFMEMORY) ;
goto cleanup ;
}
// we're done handing the parser object around to be referenced; release our
// our own reference here
pPayloadParser -> Release () ;
hr = m_PinStreamMaps.Append (pStreamMap) ;
NE_SPEW (hr, S_OK, TEXT ("CMPEG2Controller::MapStreamLocked () : m_PinStreamMaps.Append (pStreamMap)")) ;
if (FAILED (hr)) {
// we still hold the receive lock, so we're ok
m_pStreamMapper -> UnmapStream (
dwStreamIdentifier,
pPayloadParser
) ;
// should be the final release
pPayloadParser -> Release () ;
goto cleanup ;
}
// sort the Stream map vector
m_PinStreamMaps.GetVector (& ppStreamMap, & cStreamMap) ;
ASSERT (ppStreamMap) ;
qsort (
ppStreamMap,
cStreamMap,
sizeof (CStreamToPinMap *),
CStreamToPinMap::Compare
) ;
// if this is an a/v stream, we need to reset PTSs, etc...
if (MediaSampleContent == MPEG2_MEDIA_ELEMENTARY_STREAM) {
AvStreamPush_ (pStreamMap) ;
AVStreamsResetAll_ () ;
}
cleanup :
m_pMapperLock -> Unlock () ;
return hr ;
}
// unmap a Stream from a pin
HRESULT
CMPEG2Controller::UnmapStream (
IN DWORD dwStreamIdentifier,
IN CMPEG2DemuxOutputPin * pDemuxOutputPin
)
/*++
Locks Held:
filter
--*/
{
HRESULT hr ;
#ifndef UNDER_CE
DWORD cMaps ;
#endif //UNDER_CE
CStreamToPinMap * pStreamMap ;
DWORD dwIndex ;
ASSERT (pDemuxOutputPin) ;
O_TRACE_ENTER_2 (TEXT("CMPEG2Controller::UnmapStreamLocked (dwStreamIdentifier = %08xh, pDemuxOutputPin = %08xh)"), dwStreamIdentifier, pDemuxOutputPin) ;
m_pMapperLock -> Lock () ;
hr = FindPinStreamMapRecordLocked_ (
pDemuxOutputPin,
dwStreamIdentifier,
& dwIndex
) ;
if (SUCCEEDED (hr)) {
ASSERT (dwIndex < m_PinStreamMaps.GetCount ()) ;
m_PinStreamMaps.Get (dwIndex, & pStreamMap) ;
ASSERT (pStreamMap) ;
ASSERT (pStreamMap -> GetNonRefdPin () == pDemuxOutputPin) ;
ASSERT (pStreamMap -> GetNonRefdParser ()) ;
hr = m_pStreamMapper -> UnmapStream (
dwStreamIdentifier,
pStreamMap -> GetNonRefdParser ()
) ;
// We remove the mapping in the controller iff the mapper successfully
// removed it. The reasonnning here is if the mapper did not remove it
// it's probably because it did not exist, so there's no perf hit in having
// a bogus Stream map sitting here in the controller. On the other hand, if
// the Stream is mapped, and it is a perf hit, we want to have a chance at
// removing it again. If we remove it from the controller, there's no way
// to get the right information to the mapper to remove the real thing.
if (SUCCEEDED (hr)) {
// if the map is AV must remove it from the list of AV streams
if (pStreamMap -> GetSampleContent () == MPEG2_MEDIA_ELEMENTARY_STREAM) {
AVStreamPop_ (pStreamMap) ;
// don't reset the other streams; this operation does not
// affect them
}
// remove the context from the vector; the vector remains sorted
m_PinStreamMaps.Remove (dwIndex) ;
// free the resources
delete pStreamMap ;
}
}
m_pMapperLock -> Unlock () ;
return hr ;
}
HRESULT
CMPEG2Controller::EnumStreamMap (
IN CMPEG2DemuxOutputPin * pDemuxOutputPin,
IN OUT CEnumStreamMapBase * pCEnumStreamMapBase
)
/*++
pIPinFilter == NULL --> return all pin-Stream maps
Locks Held:
filter
--*/
{
HRESULT hr ;
CStreamToPinMap ** ppStreamMap ;
DWORD cStreamMap ;
O_TRACE_ENTER_2 (TEXT ("CMPEG2Controller::EnumStreamMapLocked (%08xh, %08xh)"), pDemuxOutputPin, pCEnumStreamMapBase) ;
ASSERT (pCEnumStreamMapBase) ;
m_pCMPEG2Demultiplexer -> LockFilter () ;
m_PinStreamMaps.GetVector (& ppStreamMap, & cStreamMap) ;
hr = S_OK ;
for (DWORD i = 0; i < cStreamMap; i++) {
ASSERT (ppStreamMap [i] -> GetNonRefdPin ()) ;
// only add if
// 1. no pin filter is supplied
// 2. if a pin filter is supplied, then if this is to that pin
if (pDemuxOutputPin == NULL ||
pDemuxOutputPin == ppStreamMap [i] -> GetNonRefdPin ()) {
hr = pCEnumStreamMapBase -> AddStreamMap (
ppStreamMap [i] -> GetStream (),
ppStreamMap [i] -> GetNonRefdPin (),
ppStreamMap [i] -> GetSampleContent (),
ppStreamMap [i] -> GetParserArg ()
) ;
}
if (FAILED (hr)) {
break ;
}
}
m_pCMPEG2Demultiplexer -> UnlockFilter () ;
return hr ;
}
HRESULT
CMPEG2Controller::OnInputStreamDiscontinuity (
)
{
CStreamToPinMap ** ppStreamMap ;
DWORD cStreamMap ;
HRESULT hr ;
O_TRACE_ENTER_0 (TEXT ("CMPEG2Controller::FlushBuffers ()")) ;
// make sure no one is partying down in the mapper
LockRecv_ () ;
LockMapper_ () ;
m_PinStreamMaps.GetVector (& ppStreamMap, & cStreamMap) ;
for (DWORD i = 0; i < cStreamMap; i++) {
ASSERT (ppStreamMap [i]) ;
ASSERT (ppStreamMap [i] -> GetNonRefdParser ()) ;
ppStreamMap [i] -> GetNonRefdParser () -> OnDiscontinuity () ;
}
hr = m_pMpeg2StreamContentManager -> Reset () ;
if (SUCCEEDED (hr)) {
hr = m_pStreamMapper -> Initialize () ;
}
UnlockMapper_ () ;
UnlockRecv_ () ;
return hr ;
}
HRESULT
CMPEG2Controller::AbortBuffers (
)
{
CStreamToPinMap ** ppStreamMap ;
DWORD cStreamMap ;
O_TRACE_ENTER_0 (TEXT ("CMPEG2Controller::FlushBuffers ()")) ;
// make sure no one is partying down in the mapper
LockRecv_ () ;
LockMapper_ () ;
m_PinStreamMaps.GetVector (& ppStreamMap, & cStreamMap) ;
for (DWORD i = 0; i < cStreamMap; i++) {
ASSERT (ppStreamMap [i]) ;
ASSERT (ppStreamMap [i] -> GetNonRefdParser ()) ;
ppStreamMap [i] -> GetNonRefdParser () -> Abort () ;
}
UnlockMapper_ () ;
UnlockRecv_ () ;
return S_OK ;
}
HRESULT
CMPEG2Controller::FindPinStreamMapRecordLocked_ (
IN CMPEG2DemuxOutputPin * pDemuxOutputPin,
IN DWORD dwStream, // Stream
OUT DWORD * pdwIndex // optional; index
)
{
#ifndef UNDER_CE
HRESULT hr ;
#endif //UNDER_CE
CStreamToPinMap ** ppPinStreamMaps ;
DWORD cElements ;
int start, end, index ;
int r ;
O_TRACE_ENTER_3 (TEXT ("CMPEG2Controller::FindPinStreamMapRecordLocked_ (%08xh, dwStream = %08xh, %08xh)"), pDemuxOutputPin, dwStream, pdwIndex) ;
ASSERT (pDemuxOutputPin) ;
m_PinStreamMaps.GetVector (& ppPinStreamMaps, & cElements) ;
if (cElements > 0) {
ASSERT (ppPinStreamMaps) ;
// binary search of the vector
start = 0 ;
end = cElements - 1 ;
while (start <= end) {
index = (start + end) / 2 ;
ASSERT (ppPinStreamMaps [index] -> GetNonRefdPin ()) ;
// compare
r = CStreamToPinMap::CompareMapRecordValues (
ppPinStreamMaps [index] -> GetNonRefdPin (),
ppPinStreamMaps [index] -> GetStream (),
pDemuxOutputPin,
dwStream
) ;
// found it
if (r == 0) {
if (pdwIndex) {
* pdwIndex = index ;
}
return S_OK ;
}
// search bracket moves up
else if (r < 0) {
start = index + 1 ;
}
// search bracket moves down
else {
end = index - 1 ;
}
}
}
// did not find it
return E_FAIL ;
}
HRESULT
CMPEG2Controller::FindPinStreamMapRecordLocked_ (
IN CMPEG2DemuxOutputPin * pDemuxOutputPin,
OUT DWORD * pcPinStreamMaps, // number of Stream maps to the specified pin
OUT DWORD * pdwIndex // optional; index
)
{
#ifndef UNDER_CE
HRESULT hr ;
#endif //UNDER_CE
CStreamToPinMap ** ppPinStreamMaps ;
DWORD cElements ;
int start, end, index ;
int r ;
#ifndef UNDER_CE
DWORD i ;
#endif //UNDER_CE
O_TRACE_ENTER_3 (TEXT ("CMPEG2Controller::FindPinStreamMapRecordLocked_ (%08xh, %08xh, %08xh)"), pDemuxOutputPin, pcPinStreamMaps, pdwIndex) ;
ASSERT (pDemuxOutputPin) ;
ASSERT (pcPinStreamMaps) ;
m_PinStreamMaps.GetVector (& ppPinStreamMaps, & cElements) ;
if (cElements > 0) {
ASSERT (ppPinStreamMaps) ;
// binary search of the vector
start = 0 ;
end = cElements - 1 ;
while (start <= end) {
index = (start + end) / 2 ;
ASSERT (ppPinStreamMaps [index] -> GetNonRefdPin ()) ;
// compare
r = CStreamToPinMap::CompareMapRecordValues (
ppPinStreamMaps [index] -> GetNonRefdPin (),
pDemuxOutputPin
) ;
// found it
if (r == 0) {
// enumerate the number of Stream mappings to the particular pin,
// if it's requested i.e. if the out parameter is specified
if (pcPinStreamMaps) {
ASSERT (CStreamToPinMap::CompareMapRecordValues (ppPinStreamMaps [index] -> GetNonRefdPin (), pDemuxOutputPin) == 0) ;
// we now must discover the first and then count the number
// Stream maps for this particular pin
// backup
for (; index > 0 && CStreamToPinMap::CompareMapRecordValues (ppPinStreamMaps [index] -> GetNonRefdPin (), pDemuxOutputPin) == 0; index--) ;
// out of the failure condition
if (index > 0) {
ASSERT (CStreamToPinMap::CompareMapRecordValues (ppPinStreamMaps [index] -> GetNonRefdPin (), pDemuxOutputPin) != 0) ;
index++ ;
}
else {
if (CStreamToPinMap::CompareMapRecordValues (ppPinStreamMaps [index] -> GetNonRefdPin (), pDemuxOutputPin) != 0) {
index++ ;
}
}
// should now have the index to the first
ASSERT (CStreamToPinMap::CompareMapRecordValues (ppPinStreamMaps [index] -> GetNonRefdPin (), pDemuxOutputPin) == 0) ;
for (* pcPinStreamMaps = 0;;) {
// if we found another record
if (CStreamToPinMap::CompareMapRecordValues (
ppPinStreamMaps [index + (* pcPinStreamMaps)] -> GetNonRefdPin (),
pDemuxOutputPin) == 0) {
// increment our counter
(* pcPinStreamMaps)++ ;
}
else {
// otherwise break out
break ;
}
// if we're to the end of the vector, break out
if (index + (* pcPinStreamMaps) == cElements) {
break ;
}
}
}
if (pdwIndex) {
* pdwIndex = index ;
}
return S_OK ;
}
// search bracket moves up
else if (r < 0) {
start = index + 1 ;
}
// search bracket moves down
else {
end = index - 1 ;
}
}
}
// did not find it
return E_FAIL ;
}
HRESULT
CMPEG2Controller::UnmapAllStreams (
IN CMPEG2DemuxOutputPin * pDemuxOutputPin
)
{
HRESULT hr ;
ASSERT (pDemuxOutputPin) ;
m_pMapperLock -> Lock () ;
hr = UnmapAllStreamsLocked_ (pDemuxOutputPin) ;
m_pMapperLock -> Unlock () ;
return hr ;
}
HRESULT
CMPEG2Controller::InitPullModeStream (
IN IAsyncReader * pIAsyncReader
)
{
CMpeg2StreamAnalyzer * pAnalyzer ;
BOOL r ;
HRESULT hr ;
LONGLONG llLength ;
REFERENCE_TIME rtDuration ;
#ifndef UNDER_CE
LONGLONG llFirstSCR ;
LONGLONG llEarliestPTS ;
LONGLONG llBaselineSCR ;
#endif //UNDER_CE
pAnalyzer = GetAnalyzer_ (
m_pCMPEG2Demultiplexer,
pIAsyncReader
) ;
if (pAnalyzer) {
r = pAnalyzer -> ValidStream () ;
if (r) {
hr = pAnalyzer -> AnalyzeStream () ;
if (SUCCEEDED (hr)) {
hr = pAnalyzer -> GetStreamDuration (
& llLength,
& rtDuration
) ;
if (SUCCEEDED (hr)) {
m_pCMPEG2Demultiplexer -> SetTotalFileLength (llLength) ;
m_pCMPEG2Demultiplexer -> SetTotalFileDuration (rtDuration) ;
m_fInPullMode = TRUE ;
}
}
}
else {
hr = E_FAIL ;
}
RecycleAnalyzer_ (pAnalyzer) ;
}
else {
hr = E_OUTOFMEMORY ;
}
return hr ;
}
void CMPEG2Controller::LockRecv_ () { m_pCMPEG2Demultiplexer -> LockReceive () ; }
void CMPEG2Controller::UnlockRecv_ () { m_pCMPEG2Demultiplexer -> UnlockReceive () ; }
//------------------------------------------------------------------------------
// CMPEG2TransportController
//------------------------------------------------------------------------------
CMPEG2TransportController::CMPEG2TransportController (
IN HKEY hkey,
IN CMPEG2Demultiplexer * pDemultiplexer,
IN CMpeg2Stats * pStats,
IN CMPEG2PushClock * pClock,
OUT HRESULT * phr
) : CMPEG2Controller (
hkey,
pDemultiplexer,
pStats,
pClock,
MPEG2_STREAM_TRANSPORT,
phr
)
{
LONG l ;
#ifndef UNDER_CE
DWORD dwDisposition ;
#endif //UNDER_CE
DWORD dwTimeoutCheckThreshold ;
CTransportStreamMapper * pTransportStreamMapper ;
CTSContentManager * pMPEG2TSContentManager ;
if (FAILED (* phr)) {
return ;
}
// it's ok to just make a copy vs. duplicating the handle because we live exactly as
// long as the filter, which creates the handle; the filter should fail if it cannot
// open the registry key
ASSERT (hkey) ;
dwTimeoutCheckThreshold = REG_DEFAULT_TIMEOUT_CHECK_THRESHOLD ;
l = RegGetValIfExist (
hkey,
REG_TIMEOUT_CHECK_THRESHOLD,
TRUE,
& dwTimeoutCheckThreshold
) ;
if (l != ERROR_SUCCESS) {
TRACE_ERROR_1 (TEXT ("RegGetValIfExist (REG_TIMEOUT_CHECK_THRESHOLD) returned an error; l = %08xh"), l) ;
// revert to the default
dwTimeoutCheckThreshold = REG_DEFAULT_TIMEOUT_CHECK_THRESHOLD ;
}
// ------------------------------------------------------------------------
// instantiate the mapper
//
pTransportStreamMapper = new CTransportStreamMapper (
pStats,
dwTimeoutCheckThreshold,
pClock,
pDemultiplexer
) ;
if (pTransportStreamMapper) {
// success; configure the parent with the right mapper
SetStreamMapper_ (pTransportStreamMapper) ;
}
else {
(* phr) = E_OUTOFMEMORY ;
goto cleanup ;
}
// ------------------------------------------------------------------------
// instantiate the transport content manager
//
pMPEG2TSContentManager = new CTSContentManager (
this,
pStats,
pClock,
hkey,
phr
) ;
if (pMPEG2TSContentManager &&
SUCCEEDED (* phr)) {
// success; configure parent with the transport content manager
SetContentManager_ (pMPEG2TSContentManager) ;
}
else {
//
// don't delete the mapper; it's already been set in the parent;
// the parent is responsible for its destruction
//
(* phr) = FAILED (* phr) ? (* phr) : E_OUTOFMEMORY ;
delete pMPEG2TSContentManager ;
goto cleanup ;
}
// ------------------------------------------------------------------------
cleanup :
return ;
}
CMPEG2TransportController::~CMPEG2TransportController (
)
{
//
// parent class must delete the mapper & content manager; we just
// instantiate the right one for the parent class to use
//
}
BOOL
CMPEG2TransportController::IsValidStream (
IN DWORD dwStream
)
{
if (dwStream <= MAX_PID_VALUE) {
return TRUE ;
}
return FALSE ;
}
BOOL
CMPEG2TransportController::IsCopyBufferSource_ (
IN DWORD dwTypeParser,
IN CMediaType * pmtPin
)
{
// copy always for transport
return TRUE ;
}
void
CMPEG2TransportController::SetStrideLengths (
IN int iPreStrideLength,
IN int iPostStrideLength
)
{
reinterpret_cast (GetStreamMapper_ ()) -> SetStrideLengths (
iPreStrideLength,
iPostStrideLength
) ;
}
//------------------------------------------------------------------------------
// CMPEG2ProgramController
//------------------------------------------------------------------------------
CMPEG2ProgramController::CMPEG2ProgramController (
IN HKEY hkey,
IN CMPEG2Demultiplexer * pDemultiplexer,
IN CMpeg2Stats * pStats,
IN CMPEG2PushClock * pClock,
OUT HRESULT * phr
) : CMPEG2Controller (hkey,
pDemultiplexer,
pStats,
pClock,
MPEG2_STREAM_PROGRAM,
phr
),
m_pAnalogCopyProtection (NULL),
m_dwpACPStreamIdMap (0)
{
#ifndef UNDER_CE
LONG l ;
DWORD dw ;
#endif //UNDER_CE
CProgramStreamMapper * pProgramStreamMapper ;
CPSContentManager * pMPEG2PSContentManager ;
if (FAILED (* phr)) {
return ;
}
// it's ok to just make a copy vs. duplicating the handle because we live exactly as
// long as the filter, which creates the handle; the filter should fail if it cannot
// open the registry key
ASSERT (hkey) ;
// ------------------------------------------------------------------------
// instantiate the mapper
//
pProgramStreamMapper = new CProgramStreamMapper (
pStats,
pClock,
pDemultiplexer
) ;
if (pProgramStreamMapper) {
// success; configure the parent with the right mapper
SetStreamMapper_ (pProgramStreamMapper) ;
}
else {
(* phr) = E_OUTOFMEMORY ;
goto cleanup ;
}
// ------------------------------------------------------------------------
// instantiate the transport content manager
//
pMPEG2PSContentManager = new CPSContentManager (
this,
pStats,
pClock,
hkey,
phr
) ;
if (pMPEG2PSContentManager &&
SUCCEEDED (* phr)) {
// success; configure parent with the transport content manager
SetContentManager_ (pMPEG2PSContentManager) ;
}
else {
//
// don't delete the mapper; it's already been set in the parent;
// the parent is responsible for its destruction
//
(* phr) = FAILED (* phr) ? (* phr) : E_OUTOFMEMORY ;
delete pMPEG2PSContentManager ;
goto cleanup ;
}
// ------------------------------------------------------------------------
cleanup :
return ;
}
CMPEG2ProgramController::~CMPEG2ProgramController (
)
{
//
// parent class must delete the mapper & content manager; we just
// instantiate the right one for the parent class to use
//
ASSERT (!m_pAnalogCopyProtection) ;
}
BOOL
CMPEG2ProgramController::IsValidStream (
IN DWORD dwStream
)
{
if (dwStream >= STREAM_ID_MIN &&
dwStream <= STREAM_ID_MAX) {
return TRUE ;
}
else if (dwStream == MPEG2_DEFAULT_VIDEO_STREAM) {
return TRUE ;
}
else if (dwStream == MPEG2_DEFAULT_AUDIO_STREAM) {
return TRUE ;
}
return FALSE ;
}
BOOL
CMPEG2ProgramController::IsCopyBufferSource_ (
IN DWORD dwTypeParser,
IN CMediaType * pmtPin
)
{
#if 0
// #if'd out because this tends to be problematic wrt
// input buffering requirements; if we don't copy, we hang
// onto the input buffer all the way to decode, which means
// a far bigger pool is needed; by always copying, we introduce
// an inefficiency, but we know our buffering schema works
if (dwTypeParser == MPEG2_MEDIA_ELEMENTARY_STREAM &&
IsVideoMediaType (pmtPin)) {
// video elementary streams carried in program are wrapped
return FALSE ;
}
#endif
return TRUE ;
}
CMpeg2StreamAnalyzer *
CMPEG2ProgramController::GetAnalyzer_ (
IN CMPEG2Demultiplexer * pMpeg2Demultiplexer,
IN IAsyncReader * pIAsyncReader
)
{
return new CMpeg2ProgramStreamAnalyzer (
pMpeg2Demultiplexer,
pIAsyncReader
) ;
}
void
CMPEG2ProgramController::RecycleAnalyzer_ (
IN CMpeg2StreamAnalyzer * pAnalyzer
)
{
delete pAnalyzer ;
}
void
CMPEG2ProgramController::SetPullMode (
IN BOOL f
)
{
SetPullModeState (f) ;
}
HRESULT
CMPEG2ProgramController::CreateAnalogCopyProtectionMap_ (
)
{
HRESULT hr ;
ASSERT (m_pAnalogCopyProtection) ;
ASSERT (m_dwpACPStreamIdMap == 0) ;
hr = MapStreamInternal (
STREAM_ID_ANALOGCOPYPROTECTION,
m_pAnalogCopyProtection,
MPEG2_MEDIA_PROGRAM_ANALOGCOPYPROTECTION,
NULL,
& m_dwpACPStreamIdMap
) ;
return hr ;
}
HRESULT
CMPEG2ProgramController::DeleteAnalogCopyProtectionMap_ (
)
{
HRESULT hr ;
ASSERT (m_pAnalogCopyProtection) ;
if (m_dwpACPStreamIdMap) {
hr = UnmapStreamInternal (
STREAM_ID_ANALOGCOPYPROTECTION,
m_dwpACPStreamIdMap
) ;
if (SUCCEEDED (hr)) {
m_dwpACPStreamIdMap = 0 ;
}
}
else {
// already does not exist
hr = S_OK ;
}
return hr ;
}
HRESULT
CMPEG2ProgramController::Active (
)
{
HRESULT hr ;
LockRecv_ () ;
LockMapper_ () ;
m_pAnalogCopyProtection = new CMpeg2AnalogCopyProtection (
Mpeg2Demultiplexer_ (),
Stats_ (),
FALSE
) ;
if (m_pAnalogCopyProtection) {
hr = m_pAnalogCopyProtection -> MakeACPFilterList () ;
if (SUCCEEDED (hr)) {
hr = CreateAnalogCopyProtectionMap_ () ;
if (SUCCEEDED (hr)) {
hr = CMPEG2Controller::Active () ;
}
}
}
else {
hr = E_OUTOFMEMORY ;
}
UnlockMapper_ () ;
UnlockRecv_ () ;
return hr ;
}
// transitions to inactive state
HRESULT
CMPEG2ProgramController::Inactive (
)
{
HRESULT hr ;
LockRecv_ () ;
LockMapper_ () ;
if (m_pAnalogCopyProtection) {
DeleteAnalogCopyProtectionMap_ () ;
m_pAnalogCopyProtection -> FreeACPFilterList () ;
m_pAnalogCopyProtection -> ClearAnalogCopyProtectionLevel () ;
RELEASE_AND_CLEAR (m_pAnalogCopyProtection) ;
hr = CMPEG2Controller::Inactive () ;
}
else {
// already inactive
hr = S_OK ;
}
UnlockMapper_ () ;
UnlockRecv_ () ;
return hr ;
}
// ----------------------------------------------------------------------------
// CMPEG2ProgramController::CMpeg2AnalogCopyProtection
// ----------------------------------------------------------------------------
CMPEG2ProgramController::CMpeg2AnalogCopyProtection::CMpeg2AnalogCopyProtection (
IN IBaseFilter * pIBaseFilter,
IN CMpeg2Stats * pStats,
IN BOOL fReportStats
) : CAnalogCopyProtection (pIBaseFilter),
CBufferSource (TRUE,
pStats,
fReportStats
)
{
}
CMPEG2ProgramController::CMpeg2AnalogCopyProtection::~CMpeg2AnalogCopyProtection (
)
{
}
HRESULT
CMPEG2ProgramController::CMpeg2AnalogCopyProtection::GetCopyBuffer (
OUT DWORD_PTR * pdwContext,
OUT BYTE ** ppbBuffer,
IN OUT int * piBufferLength
)
{
ASSERT (pdwContext) ;
ASSERT (ppbBuffer) ;
ASSERT (piBufferLength) ;
::ZeroMemory (& m_ACPRecord, sizeof m_ACPRecord) ;
// set the outgoing fields
(* pdwContext) = reinterpret_cast (this) ;
(* ppbBuffer) = reinterpret_cast (& m_ACPRecord) ;
(* piBufferLength) = sizeof m_ACPRecord ;
// add ref ourselves - we are the copy buffer
AddRef () ;
return S_OK ;
}
HRESULT
CMPEG2ProgramController::CMpeg2AnalogCopyProtection::CompleteCopyBuffer (
IN DWORD_PTR dwContext,
IN BYTE * pbBuffer,
IN int iBufferLength,
IN BOOL fDiscontinuity,
IN CTStickyVal * pReset // OUT: true if the demux needs a reset
)
/*++
assumption: buffer is completed, THEN a new one is obtained vs.
buffer is obtained (& possibly written into), then
previous one is completed
--*/
{
HRESULT hr ;
ASSERT (pbBuffer) ;
ASSERT (dwContext == reinterpret_cast (this)) ;
ASSERT (pbBuffer == reinterpret_cast (& m_ACPRecord)) ;
hr = DistributeAnalogCopyProtection (m_ACPRecord.dwACPBits) ;
return hr ;
}
HRESULT
CMPEG2ProgramController::CMpeg2AnalogCopyProtection::ReleaseCopyBuffer (
IN DWORD_PTR dwContext,
IN BYTE * pbBuffer,
IN int iBufferLength
)
{
if (dwContext) {
// we're the copy buffer..
Release () ;
}
return S_OK ;
}
// ----------------------------------------------------------------------------
// CMpeg2StreamAnalyzer
// ----------------------------------------------------------------------------
HRESULT
CMpeg2StreamAnalyzer::CreateAVStream_ (
IN LPWSTR pszPinName,
IN AM_MEDIA_TYPE * pMediaType,
IN BYTE bStreamId,
IN BYTE bFilterValue,
IN int iDataOffset,
IN DWORD dwMediaSampleContent
)
{
HRESULT hr ;
ULONG ulStreamId ;
AUDIO_FILTER_INFO AudioFilterInfo ;
ASSERT (m_pMpeg2Demultiplexer) ;
hr = m_pMpeg2Demultiplexer -> CreateOutputPin_ (
pszPinName,
pMediaType
) ;
if (SUCCEEDED (hr)) {
ulStreamId = (0x000000ff & bStreamId) ;
AudioFilterInfo.dwSubstreamValue = (iDataOffset > 0 ? bFilterValue : SUBSTREAM_FILTER_VAL_NONE) ;
AudioFilterInfo.iDataOffset = iDataOffset ;
hr = m_pMpeg2Demultiplexer -> CreateStreamMap_ (
1,
& ulStreamId,
pszPinName,
dwMediaSampleContent,
0xffffffff,
(LPVOID) & AudioFilterInfo
) ;
if (FAILED (hr)) {
m_pMpeg2Demultiplexer -> DeletePin_ (pszPinName) ;
}
}
return hr ;
}
// ----------------------------------------------------------------------------
// CMpeg2ProgramStreamAnalyzer
// ----------------------------------------------------------------------------
HRESULT
CMpeg2ProgramStreamAnalyzer::InitVideoStream_ (
IN UCHAR uStreamId,
IN BYTE * pbPESPayload,
IN int iLen
)
/*++
validate
--*/
{
CMediaType cmt ;
SEQHDR_INFO SeqHdrInfo ;
MPEG2_VIDEO_DATA Mpeg2VideoData ;
BYTE * pbData ;
BOOL r ;
HRESULT hr ;
O_TRACE_ENTER_3 (TEXT ("CMpeg2ProgramStreamAnalyzer::InitVideoStream (%02xh, %08xh, %08xh)"), uStreamId, pbPESPayload, iLen) ;
pbData = pbPESPayload ;
// now seek down to the sequence_header
r = SeekToPrefix (
& pbData,
& iLen
) ;
if (!r ||
iLen < 4 ||
StartCode (pbData) != MPEG2_SEQUENCE_HEADER_START_CODE) {
// we don't know if the file is bad; just know that we did not find a
// sequence_header, so it's a general failure, not a definitive bad
// stream determination
return E_FAIL ;
}
// Now handle the sequence header the extension follows right
// on from the end and must be there, otherwise we're MPEG1
r = ParseSequenceHeader (
pbPESPayload,
iLen,
& SeqHdrInfo
) ;
if (!r) {
return VFW_E_UNKNOWN_FILE_TYPE ;
}
// Parse the extension (sort of)
pbData += SeqHdrInfo.lActualHeaderLen ;
iLen -= SeqHdrInfo.lActualHeaderLen ;
r = SeekToPrefix (
& pbData,
& iLen
) ;
if (!r ||
iLen == 0 ||
StartCode (pbData) != MPEG2_EXTENSION_START_CODE) {
// could be mpeg-1 but we don't support it
return VFW_E_UNKNOWN_FILE_TYPE ;
}
// Add extension area information to the sequence header information
CopyMemory (
(PVOID) (SeqHdrInfo.RawHeader + SeqHdrInfo.lActualHeaderLen),
(PVOID) pbData,
MPEG2_SEQUENCE_HEADER_LENGTH
) ;
SeqHdrInfo.lActualHeaderLen += MPEG2_SEQUENCE_HEADER_LENGTH ;
if (iLen < 14) {
return VFW_E_UNKNOWN_FILE_TYPE ;
}
// Get the extension info - note that the various extension start
// codes can be identified by the 4-bit ID following them
// So we can just keep eating extension start codes until we get
// bored an looking for the id - it's up to the authors to
// deliver them according to the spec order
// Check the sequence extension id
if ((pbData[4] >> 4) != 0x01) {
return VFW_E_UNKNOWN_FILE_TYPE ;
}
// The escape bit seems not to be used
SeqHdrInfo.dwProfile = pbData [4] & 0x07 ;
SeqHdrInfo.dwLevel = pbData [5] >> 4 ;
SeqHdrInfo.lWidth += ((pbData [5] & 1) << 13) + ((pbData [6] & 0x80) << 5) ;
SeqHdrInfo.lHeight += (pbData [6] & 0x60) << 7 ;
SeqHdrInfo.dwBitRate += 400 * (((pbData [6] & 0x1F) << (18 + 7)) +
((pbData [7] & 0xFE) << (18 - 1))) ;
// Why are the constants in dvdmedia.h not the same?
Mpeg2VideoData.dwLevel =
SeqHdrInfo.dwLevel == 10 ? AM_MPEG2Level_Low :
SeqHdrInfo.dwLevel == 8 ? AM_MPEG2Level_Main :
SeqHdrInfo.dwLevel == 6 ? AM_MPEG2Level_High1440 :
SeqHdrInfo.dwLevel == 4 ? AM_MPEG2Level_High :
0xFFFFFFFF ;
Mpeg2VideoData.dwProfile =
SeqHdrInfo.dwProfile == 5 ? AM_MPEG2Profile_Simple :
SeqHdrInfo.dwProfile == 4 ? AM_MPEG2Profile_Main :
SeqHdrInfo.dwProfile == 3 ? AM_MPEG2Profile_SNRScalable :
SeqHdrInfo.dwProfile == 2 ? AM_MPEG2Profile_SpatiallyScalable :
SeqHdrInfo.dwProfile == 1 ? AM_MPEG2Profile_High :
0xFFFFFFFF ;
switch (pbPESPayload [7] >> 4) {
case 2 :
Mpeg2VideoData.dwARWidth = 4 ;
Mpeg2VideoData.dwARHeight = 3 ;
break ;
case 3 :
Mpeg2VideoData.dwARWidth = 16 ;
Mpeg2VideoData.dwARHeight = 9 ;
break ;
case 4 :
Mpeg2VideoData.dwARWidth = 221 ;
Mpeg2VideoData.dwARHeight = 100 ;
break ;
case 1 :
default :
Mpeg2VideoData.dwARWidth = (DWORD)SeqHdrInfo.lWidth ;
Mpeg2VideoData.dwARHeight = (DWORD)SeqHdrInfo.lHeight ;
break ;
}
// Find out the total length and save it somewhere (!)
iLen -= START_CODE_LENGTH ;
pbData += START_CODE_LENGTH ;
r = SeekToPrefix (
& pbData,
& iLen
) ;
while (r &&
iLen > 0) {
if (StartCode (pbData) != MPEG2_EXTENSION_START_CODE) {
// We're there although we may have gone past - too bad
break ;
}
// If there's a display size give it to them
if (iLen >= 12 && (pbData[4] >> 4) == 2) {
SeqHdrInfo.lWidth = (pbData[8] << 6) + (pbData[9] >> 2);
SeqHdrInfo.lHeight = ((pbData[9] & 1) << 13) + (pbData[10] << 5)
+ (pbData[11] >> 3);
}
pbData += START_CODE_LENGTH ;
iLen -= START_CODE_LENGTH ;
r = SeekToPrefix (
& pbData,
& iLen
) ;
}
// Return Mpeg2 media type
hr = GetMpeg2VideoMediaType (
& cmt,
& SeqHdrInfo,
& Mpeg2VideoData
) ;
if (FAILED(hr)) {
return hr;
}
return CreateAVStream_ (
VIDEO_PIN_NAME,
& cmt,
uStreamId
) ;
}
HRESULT
CMpeg2ProgramStreamAnalyzer::InitMpeg1AudioStream_ (
IN UCHAR uStreamId,
IN BYTE * pbPESPayload,
IN int iLen
)
{
MPEG1WAVEFORMAT wf;
if (ParseAudioHeader (pbPESPayload, & wf)) {
CMediaType cmt (& MEDIATYPE_Audio) ;
cmt.subtype = MEDIASUBTYPE_MPEG2_AUDIO ;
cmt.SetFormat ((PBYTE) & wf, sizeof wf) ;
cmt.SetFormatType (& FORMAT_WaveFormatEx) ;
return CreateAVStream_ (
MPEG1_AUDIO_PIN_NAME,
& cmt,
uStreamId
) ;
}
else {
return VFW_E_UNKNOWN_FILE_TYPE ;
}
}
HRESULT
CMpeg2ProgramStreamAnalyzer::InitAC3AudioStream_ (
IN UCHAR uStreamId,
IN BYTE * pbPESPayload,
IN int iLen
)
{
CMediaType cmt ;
BOOL bDVD ;
BYTE * pbData ;
DWORD FirstFrameOffset ;
WAVEFORMATEX wf ;
BOOL r ;
int i ;
bDVD = FALSE ;
pbData = pbPESPayload ;
if ((pbPESPayload [0] & 0xF8) == 0x80) {
bDVD = TRUE ;
pbData += 4 ;
iLen -= 4;
}
// now look in this buffer for a valid frame.
FirstFrameOffset = 0 ;
i = FindFirstFrame (
& pbData [0],
iLen,
& FirstFrameOffset
) ;
if (i > 0) {
pbData = & pbData [FirstFrameOffset] ;
// Parse the rest of the info
r = ParseAC3Header (
pbData,
& wf
) ;
if (!r) {
return VFW_E_UNKNOWN_FILE_TYPE ;
}
// Create our media type
cmt.SetType (& MEDIATYPE_Audio) ;
cmt.SetSubtype (& MEDIASUBTYPE_DOLBY_AC3) ;
cmt.SetFormatType (& FORMAT_WaveFormatEx) ;
cmt.SetFormat ((PBYTE) & wf, sizeof wf) ;
return CreateAVStream_ (
AC3_AUDIO_PIN_NAME,
& cmt,
uStreamId,
(bDVD ? pbPESPayload [0] : 0),
(bDVD ? 4 : 0)
) ;
}
else {
return VFW_E_UNKNOWN_FILE_TYPE ;
}
}
HRESULT
CMpeg2ProgramStreamAnalyzer::InitLPCMAudioStream_ (
IN UCHAR uStreamId,
IN BYTE * pbPESPayload,
IN int iLen
)
{
// How do we verify it really IS LPCM and not native AC3?
WAVEFORMATEX wf ;
CMediaType cmt ;
ZeroMemory (& wf, sizeof wf) ;
wf.wFormatTag = WAVE_FORMAT_UNKNOWN ;
wf.wBitsPerSample = 16 + ((pbPESPayload [5] >> 6) * 4) ;
wf.nChannels = 1 + (pbPESPayload [5] & 7) ;
wf.nSamplesPerSec = 48000 * (1 + ((pbPESPayload [5] >> 4) & 3)) ;
wf.nAvgBytesPerSec = (wf.nChannels * wf.wBitsPerSample * wf.nSamplesPerSec) / 8 ;
wf.nBlockAlign = 0 ; // awkward for mono 20-bit.
cmt.SetType (& MEDIATYPE_Audio) ;
cmt.SetSubtype (& MEDIASUBTYPE_DVD_LPCM_AUDIO) ;
cmt.SetFormatType (& FORMAT_WaveFormatEx) ;
cmt.SetFormat ((PBYTE) & wf, sizeof wf) ;
return CreateAVStream_ (
LPCM_AUDIO_PIN_NAME,
& cmt,
uStreamId,
pbPESPayload [0],
7
) ;
}
BOOL
CMpeg2ProgramStreamAnalyzer::ValidStream (
)
{
return (GetFirstPackHeader_ () != NULL ? TRUE : FALSE) ;
}
BYTE *
CMpeg2ProgramStreamAnalyzer::GetFirstPackHeader_ (
)
{
HRESULT hr ;
LONGLONG llPacketOffset ;
BYTE * pbPacket ;
LONG lBufferLength ;
BYTE * pbPackHeader ;
// initialize
pbPackHeader = NULL ;
if (m_llFirstPackOffset == UNDEFINED) {
// starting at offset 0
llPacketOffset = 0 ;
hr = m_Mpeg2Sniffer.SeekToNextPacket (
& llPacketOffset,
& pbPacket,
& lBufferLength
) ;
// while we have a packet, and we're within bounds, look for a
// pack header from which we'll extract our SCR
while (SUCCEEDED (hr) &&
llPacketOffset <= MAX_LENGTH_ANALYZED) {
ASSERT (IsStartCodePrefix (pbPacket)) ;
if (StartCode (pbPacket) == MPEG2_PACK_START_CODE) {
// found a pack header
pbPackHeader = pbPacket ;
// save the offset off
m_llFirstPackOffset = llPacketOffset ;
break ;
}
hr = m_Mpeg2Sniffer.StepToNextPacketStrict (
& llPacketOffset,
& pbPacket,
& lBufferLength
) ;
}
}
else {
// we've been through this before so we already know the right offset;
// set it and get a pointer to it
llPacketOffset = m_llFirstPackOffset ;
hr = m_Mpeg2Sniffer.SeekToNextPacket (
& llPacketOffset,
& pbPacket,
& lBufferLength
) ;
if (SUCCEEDED (hr)) {
pbPackHeader = pbPacket ;
}
}
return pbPackHeader ;
}
HRESULT
CMpeg2ProgramStreamAnalyzer::AnalyzePESPacket_ (
IN BYTE * pbPacket
)
{
HRESULT hr ;
BYTE bStartCode ;
#ifndef UNDER_CE
BYTE * pbPESPayload ;
#endif //UNDER_CE
LONG lPESHeaderLength ;
LONG lPESPacketLength ;
ASSERT (IsStartCodePrefix (pbPacket)) ;
hr = S_OK ;
bStartCode = StartCode (pbPacket) ;
lPESHeaderLength = CMpeg2ProgramStreamSniffer::PESHeaderLength (pbPacket) ;
lPESPacketLength = CMpeg2ProgramStreamSniffer::PESPacketLength (pbPacket) ;
// this should already have been filtered by our ps Analyzer class
ASSERT (lPESPacketLength <= MAX_PS_PES_PACKET_LENGTH) ;
// ------------------------------------------------------------------------
// pack
if (bStartCode == MPEG2_PACK_START_CODE &&
m_llFirstSCR == UNDEFINED) {
// don't have our first SCR yet..
m_llFirstSCR = CMpeg2ProgramStreamSniffer::PackSCR (pbPacket) ;
// every pack should have an SCR; if this one didn't, or some other
// error occured, fail the file
hr = (m_llFirstSCR != UNDEFINED ? S_OK : VFW_E_UNKNOWN_FILE_TYPE) ;
}
// ------------------------------------------------------------------------
// video
else if (!m_fVideoConfigured &&
CMpeg2ProgramStreamSniffer::IsVideoStartCode (bStartCode)) {
// init the video stream
hr = InitVideoStream_ (
bStartCode,
& pbPacket [lPESHeaderLength],
lPESPacketLength - lPESHeaderLength
) ;
// video is configured if above call succeeded
if (SUCCEEDED (hr)) {
m_bVideoStreamId = bStartCode ;
m_fVideoConfigured = TRUE ;
}
}
// ------------------------------------------------------------------------
// mpeg-1 audio
else if (!m_fAudioConfigured &&
CMpeg2ProgramStreamSniffer::IsAudioStartCode (bStartCode)) {
// try to create a pin and map
hr = InitMpeg1AudioStream_ (
bStartCode,
& pbPacket [lPESHeaderLength],
lPESPacketLength - lPESHeaderLength
) ;
// audio is configured if above call succeeded
if (SUCCEEDED (hr)) {
m_bAudioStreamId = bStartCode ;
m_fAudioConfigured = TRUE ;
}
}
// ------------------------------------------------------------------------
// private - only look at this if we're still groveling for audio
else if (!m_fAudioConfigured &&
CMpeg2ProgramStreamSniffer::IsPrivateStream1 (bStartCode)) {
if ((pbPacket [lPESHeaderLength] & DVD_AUDIOIDMASK) == DVD_AC3ID) {
// AC-3
hr = InitAC3AudioStream_ (
bStartCode,
& pbPacket [lPESHeaderLength],
lPESPacketLength - lPESHeaderLength
) ;
}
else if ((pbPacket [lPESHeaderLength] & DVD_AUDIOIDMASK) == DVD_PCMID) {
// PCM
hr = InitLPCMAudioStream_ (
bStartCode,
& pbPacket [lPESHeaderLength],
lPESPacketLength - lPESHeaderLength
) ;
}
else {
// try AC-3 anyways
hr = InitAC3AudioStream_ (
bStartCode,
& pbPacket [lPESHeaderLength],
lPESPacketLength - lPESHeaderLength
) ;
if (hr == VFW_E_UNKNOWN_FILE_TYPE) {
// don't want to fail the entire connection because we tried
// this is an AC-3 stream and it wasn't
hr = E_FAIL ;
}
}
if (SUCCEEDED (hr)) {
// we've found the audio stream we were looking for
m_bAudioStreamId = bStartCode ;
m_fAudioConfigured = TRUE ;
}
}
return hr ;
}
HRESULT
CMpeg2ProgramStreamAnalyzer::AnalyzeStream (
)
{
HRESULT hr ;
LONGLONG llPacketOffset ;
BYTE * pbPacket ;
LONG lBufferLength ;
// if we know where the first pack is, we start there, otherwise we start
// at 0
llPacketOffset = (m_llFirstPackOffset != UNDEFINED ? m_llFirstPackOffset : 0) ;
hr = m_Mpeg2Sniffer.SeekToNextPacket (
& llPacketOffset,
& pbPacket,
& lBufferLength
) ;
// while we have a packet, and we're within bounds, look for the a
// pack header
while (SUCCEEDED (hr) &&
llPacketOffset <= MAX_LENGTH_ANALYZED) {
// should have a prefix, and have a complete packet, at least
ASSERT (IsStartCodePrefix (pbPacket)) ;
ASSERT (lBufferLength >= PES_COMMON_HEADER_FIELD_LEN) ;
ASSERT (lBufferLength >= CMpeg2ProgramStreamSniffer::PESPacketLength (pbPacket)) ;
// process the packet
hr = AnalyzePESPacket_ (pbPacket) ;
if (AnalysisComplete_ () ||
hr == VFW_E_UNKNOWN_FILE_TYPE) {
// we're done if we've successfully completed, or we found this
// stream to be specifically bogus
break ;
}
// pack header not found; step to the next packet
hr = m_Mpeg2Sniffer.StepToNextPacketStrict (
& llPacketOffset,
& pbPacket,
& lBufferLength
) ;
}
// if we failed i.e. went beyond, we'll still succeed this call if we
// found at least audio or video; we were looking for both, and a stream
// may have had just 1 or the other, but not both
if (FAILED (hr) &&
// hr != VFW_E_UNKNOWN_FILE_TYPE &&
(m_fAudioConfigured || m_fVideoConfigured)) {
hr = S_OK;
}
return hr ;
}
HRESULT
CMpeg2ProgramStreamAnalyzer::GetStreamDuration (
OUT LONGLONG * pllStreamBytes,
OUT REFERENCE_TIME * prtStreamDuration
)
/*++
Description:
This method computes the duration of the file.
The duration is computed as a byte length and a REFERENCE_TIME
length. Both are start to finish.
Since mpeg-2 is a non-linear compression standard, we cannot look at
the length of the file (bytes) and multiply and scale by 1 value to
compute length of playback time in REFERENCE_TIME.
Instead, we compute it by comparing the first SCR, extracted from the
the first pack header, to the last SCR, extracted from the last SCR.
This figure yields the first pack header to last pack header time, but
does not include the last pack playback time. This last figure is
computed by extracting the instantaneous mux_rate from the last pack
header, and then using that against the number of bytes remaining after
the last pack header, to compute a reasonnably "close" playback time.
The SCR to SCR time, plus the computed last pack playback time, yield
the closest estimate to the actual file playback time.
Parameters:
pllStreamBytes
prtStreamDuration
Return Values:
S_OK success
HRESULT failed code failure
--*/
{
HRESULT hr ;
LONGLONG llPacketOffset ;
BYTE * pbPacket ;
LONG lBufferLength ;
LONGLONG llProgramStreamLen ;
LONGLONG llLastSCR ; // last SCR; extracted
LONGLONG llSCRDuration ; // first to last SCR duration
DWORD dwMuxRate ;
LONGLONG llStartSeek ;
LONGLONG llStopSeek ;
LONGLONG llLastPackFileBytesRemaining ;
// shutup PREFIX
llLastPackFileBytesRemaining = 0 ;
dwMuxRate = 1 ;
// ------------------------------------------------------------------------
// make sure we have the first SCR
if (m_llFirstSCR == UNDEFINED) {
pbPacket = GetFirstPackHeader_ () ;
if (pbPacket) {
m_llFirstSCR = CMpeg2ProgramStreamSniffer::PackSCR (pbPacket) ;
}
if (m_llFirstSCR == UNDEFINED) {
// without a first SCR we cannot compute duration; fail the call
return E_FAIL ;
}
}
// ------------------------------------------------------------------------
// get the byte length of the stream
llProgramStreamLen = m_Mpeg2Sniffer.ProgramStreamLength () ;
// ------------------------------------------------------------------------
// now setup to seek for the last pack header
llStartSeek = Max (llProgramStreamLen - STREAM_DURATION_SEEK_STEP, 0) ;
llStopSeek = llProgramStreamLen ;
llLastSCR = UNDEFINED ;
while (llLastSCR == UNDEFINED &&
llStopSeek > 0) {
// set the starting point (llPacketOffset is modified throughout)
llPacketOffset = llStartSeek ;
// position on prefix boundary
hr = m_Mpeg2Sniffer.SeekToNextPacket (
& llPacketOffset,
& pbPacket,
& lBufferLength
) ;
// only fail out here if this is a bad stream, or we started at 0 and
// still have not found a pack
if (hr == VFW_E_UNKNOWN_FILE_TYPE ||
(FAILED (hr) && llStartSeek == 0)) {
break ;
}
// we're now positioned on a prefix boundary; seek forward, packet-packet
// until we find a pack, or go past our stop seek point
while (SUCCEEDED (hr) &&
llPacketOffset < llStopSeek) {
ASSERT (IsStartCodePrefix (pbPacket)) ;
if (StartCode (pbPacket) == MPEG2_PACK_START_CODE) {
// we found *a* pack header, which may not be the last, so we
// save off associated information (SCR, mux_rate) and keep
// on going; we want the last one
llLastSCR = CMpeg2ProgramStreamSniffer::PackSCR (pbPacket) ;
if (llLastSCR == UNDEFINED) {
hr = VFW_E_UNKNOWN_FILE_TYPE ;
break ;
}
// get the mux rate
dwMuxRate = CMpeg2ProgramStreamSniffer::PackMuxRate (pbPacket) ;
if (dwMuxRate == UNDEFINED) {
hr = VFW_E_UNKNOWN_FILE_TYPE ;
break ;
}
// compute how many bytes remain after the last pack header
ASSERT (llPacketOffset < llProgramStreamLen) ;
llLastPackFileBytesRemaining = llProgramStreamLen - llPacketOffset ;
}
hr = m_Mpeg2Sniffer.StepToNextPacketNonStrict (
& llPacketOffset,
& pbPacket,
& lBufferLength
) ;
}
// if we failed out of the loop because the sniffer found a bad file,
// stop looking
if (hr == VFW_E_UNKNOWN_FILE_TYPE) {
break ;
}
// otherwise, step backwards
llStopSeek = llStartSeek ;
llStartSeek = Max (llStartSeek - STREAM_DURATION_SEEK_STEP, 0) ;
}
if (llLastSCR != UNDEFINED) {
// first compute the FirstSCR to LastSCR time
if (m_llFirstSCR <= llLastSCR) {
// SCRs did not wrap, or the stream had just 1 pack
llSCRDuration = llLastSCR - m_llFirstSCR ;
}
else {
// SCRs wrapped; deal with it
llSCRDuration = llLastSCR + (MAX_SCR_VALUE - m_llFirstSCR) ;
}
// convert the SCR duration to DShow units (100 ns)
(* prtStreamDuration) = SCRToDShow (llSCRDuration) ;
// now, based on the file length remaining after the last pack header,
// and the mux rate we got from the last pack header, we can compute
// (estimate since the mux_rate is an instantaneous mux_rate) how
// long it will take to playback what's in the portion of the last
// pack we have
(* prtStreamDuration) += llMulDiv (llLastPackFileBytesRemaining, DSHOW_TICKS_PER_SECOND, dwMuxRate * 50, 0) ;
// set the actual file length
(* pllStreamBytes) = llProgramStreamLen ;
hr = S_OK ;
}
else {
hr = (FAILED (hr) ? hr : E_FAIL) ;
}
return hr ;
}