//------------------------------------------------------------------------------ // // 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 ; }