//------------------------------------------------------------------------------ // // 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 //====================================================================== /*++ Module Name: plparse.h Abstract: This module contains the declarations for the following components: 1. payload parsers 2. buffer sources 3. MPEG2_SYS_BUFFER Revision History: 19-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 "tsctrlr.h" #include "tsmapper.h" #include // // This table was generated with the polynomial 0x04c11db7. It is used to // check the CRC on MPEG-2 PSI, as defined in H.222.0, Systems specification // static ULONG g_MPEG2_PSI_CRC_32_Lookup [] = { 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 } ; static BOOL ConfirmMpeg2PSICRC_32 ( IN BYTE * pb, IN ULONG ulLen ) { ULONG ulCRCAccum ; ULONG i, j ; ulCRCAccum = 0xffffffff ; for (j = 0; j < ulLen; j++, pb++) { i = (( ulCRCAccum >> 24) ^ (* pb)) & 0xff ; ulCRCAccum = ((ulCRCAccum << 8 ) ^ g_MPEG2_PSI_CRC_32_Lookup [i]) ; } return ulCRCAccum == 0x00000000 ; } static __inline BOOL IsMpeg2VideoSequenceHeaderCode ( IN BYTE * pb ) { // mpeg2-video sequence_header_code = 00 00 01 B3 // H.262.0, 6.3.3, field definition return (IsStartCodePrefix (pb) && StartCode (pb) == MPEG2_SEQUENCE_HEADER_START_CODE) ; } static BOOL IsPESAudio ( IN DWORD stream_id ) { BOOL r ; r = (stream_id >= STREAM_ID_AUDIO_STREAM_MIN && stream_id <= STREAM_ID_AUDIO_STREAM_MAX) ; return r ; } // --------------------------------------------------------------------------- // CBufferSourceManager // --------------------------------------------------------------------------- void CBufferSourceManager::ResetForNext_ () { O_TRACE_ENTER_0 (TEXT ("CBufferSourceManager::ResetForNext_ ()")) ; m_pbStartBuffer = m_pbCurrent = NULL ; m_fTimestampNext = FALSE ; m_iBufferLength = 0 ; } HRESULT CBufferSourceManager::CompleteCopyBuffer_ ( IN CTStickyVal * pReset ) { HRESULT hr ; BOOL r ; int iBufferLength ; O_TRACE_ENTER_0 (TEXT ("CBufferSourceManager::CompleteCopyBuffer_ ()")) ; ASSERT (HaveCopyBuffer_ ()) ; iBufferLength = BufferUsed () ; r = CheckCompletePacket ( NULL, & m_pbStartBuffer, & iBufferLength, & m_fSetDiscontinuityNext, & m_fTimestampNext, (m_fTimestampNext ? & m_rtStart : NULL), (m_fTimestampNext ? & m_rtStop : NULL) ) ; if (r) { // good buffer; send it if (!m_fTimestampNext) { hr = m_pBufferSource -> CompleteCopyBuffer ( m_dwpBufferContext, m_pbStartBuffer, iBufferLength, m_fSetDiscontinuityNext, pReset ) ; } else { hr = m_pBufferSource -> CompleteCopyBuffer ( m_dwpBufferContext, m_pbStartBuffer, iBufferLength, m_fSetDiscontinuityNext, & m_rtStart, & m_rtStop, pReset ) ; } CompletionRetVal_ (hr) ; } else { // don't fail the call because of this hr = S_OK ; } m_pBufferSource -> ReleaseCopyBuffer ( m_dwpBufferContext, m_pbStartBuffer, m_iBufferLength ) ; m_dwpBufferContext = 0 ; ResetForNext_ () ; return hr ; } // all or nothing; no partial copies BOOL CBufferSourceManager::Copy_ ( IN BYTE * pb, IN int iLen, OUT int * piRemaining ) { O_TRACE_ENTER_2 (TEXT ("CBufferSourceManager::Copy_ (%08xh, %08xh)"), pb, iLen) ; ASSERT (HaveCopyBuffer_ ()) ; (* piRemaining) = BufferRemaining_ () ; if (iLen <= (* piRemaining)) { // copy CopyMemory ( m_pbCurrent, pb, iLen ) ; // advance (* piRemaining) -= iLen ; m_pbCurrent += iLen ; ASSERT ((* piRemaining) == BufferRemaining_ ()) ; ASSERT (BufferUsed () <= m_iBufferLength) ; return TRUE ; } else { return FALSE ; } } BOOL CBufferSourceManager::Wrap ( IN IMediaSample * pIMS, IN BYTE * pb, IN int iLen, IN CTStickyVal * pReset ) { HRESULT hr ; BOOL r ; O_TRACE_ENTER_0 (TEXT ("CBufferSourceManager::Wrap_ ()")) ; r = CheckCompletePacket ( pIMS, & pb, & iLen, & m_fSetDiscontinuityNext, & m_fTimestampNext, (m_fTimestampNext ? & m_rtStart : NULL), (m_fTimestampNext ? & m_rtStop : NULL) ) ; if (r) { if (!m_fTimestampNext) { hr = m_pBufferSource -> WrapAndComplete ( pIMS, pb, iLen, m_fSetDiscontinuityNext, pReset ) ; } else { hr = m_pBufferSource -> WrapAndComplete ( pIMS, pb, iLen, m_fSetDiscontinuityNext, & m_rtStart, & m_rtStop, pReset ) ; } // only reset the discontinuity if we successfully completed if (SUCCEEDED (hr)) { m_fSetDiscontinuityNext = FALSE ; } } else { // drop this one hr = S_OK ; } ResetForNext_ () ; return (SUCCEEDED (hr) ? TRUE : FALSE) ; } HRESULT CBufferSourceManager::GetCopyBuffer_ ( IN OUT int * piBufferLength ) // E_UNEXPECTED if we already have a buffer { HRESULT hr ; O_TRACE_ENTER_0 (TEXT ("CBufferSourceManager::GetCopyBuffer_ ()")) ; if (!HaveCopyBuffer_ ()) { hr = m_pBufferSource -> GetCopyBuffer ( & m_dwpBufferContext, & m_pbStartBuffer, piBufferLength ) ; if (SUCCEEDED (hr)) { m_pbCurrent = m_pbStartBuffer ; m_iBufferLength = (* piBufferLength) ; ASSERT (BufferRemaining_ () == (* piBufferLength)) ; ASSERT (BufferUsed () == 0) ; ASSERT (HaveCopyBuffer_ ()) ; } else { TRACE_ERROR_0 (TEXT ("m_pBufferSource -> GetCopyBuffer () call failed")) ; } } else { hr = E_UNEXPECTED ; } return hr ; } HRESULT CBufferSourceManager::SendEmptyPacket ( IN IMediaSample * pIMS, IN BOOL fDiscontinuity ) { BYTE * pb ; DWORD_PTR dwpContext ; int iLen ; HRESULT hr ; CScratchMediaSample * pScratchMS ; CTStickyVal Reset (TRUE) ; O_TRACE_ENTER_1 (TEXT ("CBufferSourceManager::SendEmptyPacket (%08x)"), fDiscontinuity) ; iLen = 0 ; if (pIMS == NULL) { hr = m_pBufferSource -> GetCopyBuffer ( & dwpContext, & pb, & iLen ) ; if (SUCCEEDED (hr)) { hr = m_pBufferSource -> CompleteCopyBuffer ( dwpContext, pb, 0, fDiscontinuity, & Reset ) ; } } else { pScratchMS = new CScratchMediaSample ; if (pScratchMS) { hr = m_pBufferSource -> WrapAndComplete ( pScratchMS, NULL, 0, fDiscontinuity, & Reset ) ; pScratchMS -> Release () ; } else { hr = E_OUTOFMEMORY ; } } return hr ; } void CBufferSourceManager::SetTimestampNext ( IN REFERENCE_TIME * prtStart, IN REFERENCE_TIME * prtStop ) { O_TRACE_ENTER_0 (TEXT ("CBufferSourceManager::SetTimestampNext ()")) ; m_fTimestampNext = TRUE ; m_rtStart = (* prtStart) ; m_rtStop = (* prtStop) ; } HRESULT CBufferSourceManager::GetCopyBlockStartPointer ( OUT BYTE ** ppbStartBuffer ) { O_TRACE_ENTER_0 (TEXT ("CBufferSourceManager::GetCopyBlockStartPointer ()")) ; if (HaveCopyBuffer_ ()) { (* ppbStartBuffer) = m_pbStartBuffer ; return S_OK ; } else { return E_FAIL ; } } HRESULT CBufferSourceManager::Complete ( IN CTStickyVal * pReset ) { HRESULT hr ; O_TRACE_ENTER_0 (TEXT ("CBufferSourceManager::Complete ()")) ; TRACE_4 (LOG_TRACE, 4, TEXT ("[%08xh] CBufferSourceManager::Complete () : BufferUsed () = %d; BufferRemaining_ () = %d; HaveCopyBuffer_ () = %08xh"), this, BufferUsed (), BufferRemaining_ (), HaveCopyBuffer_ ()) ; if (HaveCopyBuffer_ ()) { hr = CompleteCopyBuffer_ (pReset) ; } else { hr = E_UNEXPECTED ; } // only reset the discontinuity if we successfully completed if (SUCCEEDED (hr)) { m_fSetDiscontinuityNext = FALSE ; } // BUGBUG: part of comment that failing to complete a buffer // should not still do away with it !! ResetForNext_ () ; return hr ; } BOOL CBufferSourceManager::CopyBlock ( IN BYTE * pbBuffer, IN int iBufferLength, OUT int * piBufferRemaining ) // all or nothing { O_TRACE_ENTER_0 (TEXT ("CBufferSourceManager::CopyBlock ()")) ; if (HaveCopyBuffer_ ()) { return Copy_ (pbBuffer, iBufferLength, piBufferRemaining) ; } else { (* piBufferRemaining) = 0 ; return FALSE ; } } HRESULT CBufferSourceManager::GetNewCopyBuffer ( IN OUT int * piBufferLength ) { O_TRACE_ENTER_0 (TEXT ("CBufferSourceManager::GetNewCopyBuffer ()")) ; return GetCopyBuffer_ (piBufferLength) ; } void CBufferSourceManager::AbortBuffer ( ) { O_TRACE_ENTER_0 (TEXT ("CBufferSourceManager::AbortBuffer ()")) ; TRACE_4 (LOG_TRACE, 4, TEXT ("[%08xh] CBufferSourceManager::AbortBuffer () : BufferUsed () = %d; BufferRemaining_ () = %d; HaveCopyBuffer_ () = %08xh"), this, BufferUsed (), BufferRemaining_ (), HaveCopyBuffer_ ()) ; TRACE_3 (LOG_TRACE, 4, TEXT ("[%08xh] CBufferSourceManager::AbortBuffer () : start = %08xh; current = %08xh"), this, m_pbStartBuffer, m_pbCurrent) ; if (HaveCopyBuffer_ ()) { m_pStats -> Global_MediaSampleAbort (m_fKeepStats) ; m_pStats -> Global_BytesAborted (BufferUsed (), m_fKeepStats) ; m_pBufferSource -> ReleaseCopyBuffer ( m_dwpBufferContext, m_pbStartBuffer, m_iBufferLength ) ; m_dwpBufferContext = 0 ; } ResetForNext_ () ; SetDiscontinuityNext (TRUE) ; } // --------------------------------------------------------------------------- // CStreamParser // --------------------------------------------------------------------------- BOOL CStreamParser::CopyMax_ ( IN BYTE * pbBuffer, IN int iBufferLength, OUT int * piCollected, IN CTStickyVal * pReset ) { BOOL r ; int iBufferRemaining ; HRESULT hr ; int iCopyPart ; int iRequest ; O_TRACE_ENTER_0 (TEXT ("CStreamParser::CopyMax_ ()")) ; // try for the whole buffer r = CopyBlock ( pbBuffer, iBufferLength, & iBufferRemaining ) ; if (r) { // got the whole buffer (* piCollected) = iBufferLength ; } else { if (iBufferRemaining > 0) { // copy as much as possible iCopyPart = iBufferRemaining ; } else { // we need to get a new buffer; we complete ours on the way // out if they are full, so we know we don't have full one iRequest = m_iRequestBufferLength ; hr = GetNewCopyBuffer ( & iRequest ) ; if (FAILED (hr)) { // failed to get a buffer, abort (* piCollected) = 0 ; return FALSE ; } // we have a new buffer; might be able to copy entire thing iCopyPart = MIN (iRequest, iBufferLength) ; // reset the timeout ResetTimer (GetTickCount ()) ; } ASSERT (iCopyPart > 0) ; // try for what's left r = CopyBlock ( pbBuffer, iCopyPart, & iBufferRemaining ) ; if (r) { (* piCollected) = iCopyPart ; } else { (* piCollected) = 0 ; return FALSE ; } } // if the buffer is full, complete it if (iBufferRemaining == 0) { hr = Complete (pReset) ; // BUGBUG: return code } return TRUE ; } void CStreamParser::OnDiscontinuity ( ) { Abort () ; SetDiscontinuityNext (TRUE) ; } int CStreamParser::WrapStream ( IN IMediaSample * pIMS, IN BYTE * pbBuffer, IN int iBufferLength, IN CTStickyVal * pReset ) // returns the total bytes successfully copied { BOOL r ; r = Wrap (pIMS, pbBuffer, iBufferLength, pReset) ; return (r ? iBufferLength : 0) ; } int CStreamParser::CopyStream ( IN BYTE * pbBuffer, IN int iBufferLength, IN CTStickyVal * pReset ) { int iCollectedEach ; int iCollectedTotal ; BOOL r ; O_TRACE_ENTER_2 (TEXT ("CStreamParser::CopyStream (%08xh, %08xh)"), pbBuffer, iBufferLength) ; iCollectedTotal = 0 ; r = TRUE ; while (r && iBufferLength > 0) { r = CopyMax_ ( pbBuffer, iBufferLength, & iCollectedEach, pReset ) ; iBufferLength -= iCollectedEach ; pbBuffer += iCollectedEach ; iCollectedTotal += iCollectedEach ; } return iCollectedTotal ; } CStreamParser::CStreamParser ( CBufferSource * pBufferSource, CMpeg2Stats * pStats, int iRequestBufferLength ) : CDemuxBaseParser ( TIMEOUT_NONE, pStats, pBufferSource -> ReportStats () ), CBufferSourceManager ( pBufferSource, pStats, pBufferSource -> ReportStats () ), m_pStats (pStats), m_fKeepStats (pBufferSource -> ReportStats ()), m_iRequestBufferLength (iRequestBufferLength) { TRACE_CONSTRUCTOR (TEXT ("CStreamParser")) ; m_pStats -> AddRef () ; } CStreamParser::CStreamParser ( DWORD dwTimeout, CBufferSource * pBufferSource, CMpeg2Stats * pStats, int iRequestBufferLength ) : CDemuxBaseParser ( dwTimeout, pStats, pBufferSource -> ReportStats () ), CBufferSourceManager ( pBufferSource, pStats, pBufferSource -> ReportStats () ), m_pStats (pStats), m_fKeepStats (pBufferSource -> ReportStats ()), m_iRequestBufferLength (iRequestBufferLength) { TRACE_CONSTRUCTOR (TEXT ("CStreamParser")) ; m_pStats -> AddRef () ; } CStreamParser::~CStreamParser ( ) { TRACE_DESTRUCTOR (TEXT ("CStreamParser")) ; AbortBuffer () ; m_pStats -> Release () ; } void CStreamParser::Timeout ( IN CTStickyVal * pReset ) { O_TRACE_ENTER_0 (TEXT ("CStreamParser::Timeout ()")) ; if (IsGathering ()) { Complete (pReset) ; } } // --------------------------------------------------------------------------- // CCopyFrameParser // --------------------------------------------------------------------------- HRESULT CCopyFrameParser::GetNewFrameBuffer ( IN IMediaSample * pIMediaSample ) { HRESULT hr ; int iRequest ; O_TRACE_ENTER_0 (TEXT ("CCopyFrameParser::GetNewFrameBuffer ()")) ; iRequest = m_iMinFrameBuffer ; hr = GetNewCopyBuffer (& iRequest) ; if (SUCCEEDED (hr)) { // make sure we have the minimum required if (iRequest < m_iMinFrameBuffer) { // we did not get what we needed; abort the buffer and // return a failure Abort () ; hr = E_FAIL ; } } return hr ; } BYTE * CCopyFrameParser::GetFrameBufferStartPointer ( ) { HRESULT hr ; BYTE * pb ; O_TRACE_ENTER_0 (TEXT ("CCopyFrameParser::GetFrameBufferStartPointer ()")) ; hr = GetCopyBlockStartPointer (& pb) ; ASSERT (SUCCEEDED (hr)) ; return pb ; } BOOL CCopyFrameParser::CopyFrameBytes ( IN BYTE * pbBuffer, IN int iBufferLen ) { BOOL r ; int iRemaining ; O_TRACE_ENTER_2 (TEXT ("CCopyFrameParser::Collect (%08xh, %08xh)"), pbBuffer, iBufferLen) ; r = CopyBlock ( pbBuffer, iBufferLen, & iRemaining ) ; return r ; } CCopyFrameParser::CCopyFrameParser ( CBufferSource * pBufferSource, CMpeg2Stats * pStats, int iMinFrameBufferSize ) : CDemuxBaseParser ( TIMEOUT_NONE, pStats, pBufferSource -> ReportStats () ), CBufferSourceManager ( pBufferSource, pStats, pBufferSource -> ReportStats () ), m_pStats (pStats), m_iMinFrameBuffer (iMinFrameBufferSize), m_fKeepStats (pBufferSource -> ReportStats ()) { TRACE_CONSTRUCTOR (TEXT ("CCopyFrameParser")) ; m_pStats -> AddRef () ; } CCopyFrameParser::~CCopyFrameParser ( ) { TRACE_DESTRUCTOR (TEXT ("CCopyFrameParser")) ; AbortBuffer () ; m_pStats -> Release () ; } // --------------------------------------------------------------------------- // CMpeg2PESStreamParser // --------------------------------------------------------------------------- int CMpeg2PESStreamParser::PESPayloadBytesRemaining_ ( ) { O_TRACE_ENTER_0 (TEXT ("CMpeg2PESStreamParser::PESPayloadBytesRemaining ()")) ; if (PES_packet_length == 0) { // length is undefined return INT_MAX ; } else { return PES_PACKET_LENGTH_LAST_BYTE_OFFSET + PES_packet_length - m_iPESPacketBytesProcessed ; } } void CMpeg2PESStreamParser::ResetForNextPESPacket_ ( ) { O_TRACE_ENTER_0 (TEXT ("CMpeg2PESStreamParser::ResetForNextPESPacket_ ()")) ; // state m_PESParserState = EMPTY ; m_PESHeaderState = IN_HEADER_NEW ; // counters m_iPESPacketBytesProcessed = 0 ; } void CMpeg2PESStreamParser::CompletionRetVal_ ( IN HRESULT hr ) { if (IsTimestamped_ () && SUCCEEDED (hr)) { m_rtLastStart = StartPTS () ; m_fFirstTimestampProcessed = TRUE ; } } BOOL CMpeg2PESStreamParser::CompletePESCollection_ ( IN IMediaSample * pIMS, IN CTStickyVal * pReset ) { HRESULT hr ; O_TRACE_ENTER_0 (TEXT ("CMpeg2PESStreamParser::CompletePESCollection ()")) ; if (pIMS == NULL) { // only complete if we're copying; if we're not, wrap and send // operation is atomic hr = Complete (pReset) ; } else { hr = S_OK ; } ResetForNextPESPacket_ () ; return SUCCEEDED (hr) ; } void CMpeg2PESStreamParser::AbortPESCollection_ ( ) { O_TRACE_ENTER_0 (TEXT ("CMpeg2PESStreamParser::AbortPESCollection ()")) ; AbortBuffer () ; ResetForNextPESPacket_ () ; } BOOL CMpeg2PESStreamParser::ParseCommonPESHeader_ ( IN BYTE * pbBuffer ) // pbBuffer points to first byte of PES header { O_TRACE_ENTER_0 (TEXT ("CMpeg2PESStreamParser::ParseCommonPESHeader ()")) ; packet_start_code_prefix = PES_PACKET_START_CODE_PREFIX_VALUE (pbBuffer) ; stream_id = PES_STREAM_ID_VALUE (pbBuffer) ; PES_packet_length = PES_PACKET_LENGTH_VALUE (pbBuffer) ; return packet_start_code_prefix == PES_START_CODE_PREFIX ; } BOOL CMpeg2PESStreamParser::ParseTier1ReqHeader_ ( IN BYTE * pbBuffer ) // pbBuffer points to first byte of PES header { O_TRACE_ENTER_0 (TEXT ("CMpeg2PESStreamParser::ParseTier1ReqHeader ()")) ; ASSERT (IS_TIER1_STREAM (stream_id)) ; // advance to tier1 headers pbBuffer += PES_COMMON_HEADER_FIELD_LEN ; // common header fields have already been parsed; commented out those // that we don't care about PES_scrambling_control = PES_SCRAMBLING_CONTROL_VALUE (pbBuffer) ; PES_priority = PES_PRIORITY_BIT (pbBuffer) ; data_alignment_indicator = PES_DATA_ALIGNMENT_INDICATOR_BIT (pbBuffer) ; copyright = PES_COPYRIGHT_BIT (pbBuffer) ; original_or_copy = PES_ORIGINAL_OR_COPY_BIT (pbBuffer) ; PTS_DTS_flags = PES_PTS_DTS_FLAGS_VALUE (pbBuffer) ; ESCR_flag = PES_ESCR_FLAG_BIT (pbBuffer) ; ES_rate_flag = PES_ES_RATE_FLAG_BIT (pbBuffer) ; DSM_trick_mode_flag = PES_DSM_TRICK_MODE_FLAG_BIT (pbBuffer) ; additional_copy_info_flag = PES_ADDITIONAL_COPY_INFO_FLAG_BIT (pbBuffer) ; PES_CRC_flag = PES_CRC_FLAG_BIT (pbBuffer) ; PES_extension_flag = PES_EXTENSION_FLAG_BIT (pbBuffer) ; PES_header_data_length = PES_PES_HEADER_DATA_LENGTH_VALUE (pbBuffer) ; return TRUE ; } BOOL CMpeg2PESStreamParser::ProcessBuffer_ ( IN IMediaSample * pIMediaSample, IN BOOL fNewPayload, IN MPEG2_SYS_BUFFER * pSysBuffer, IN CTStickyVal * pReset ) { BOOL r ; #ifndef UNDER_CE HRESULT hr ; #endif //UNDER_CE int iCollected ; BYTE * pbBuffer ; int iBufferLength ; int iDataOffsetRemaining ; #ifndef UNDER_CE CScratchMediaSample * pScratchMS ; #endif //UNDER_CE O_TRACE_ENTER_0 (TEXT ("CMpeg2PESStreamParser::ProcessBuffer_ ()")) ; // init locals r = TRUE ; pbBuffer = pSysBuffer -> pbSysBufferPayload ; iBufferLength = pSysBuffer -> iSysBufferPayloadLength ; switch (m_PESParserState) { case EMPTY : // only start to process if this is a new payload if (!fNewPayload) { TRACE_ERROR_0 (TEXT ("DROPPED (pes): waiting for payload_unit_start")) ; m_pStats -> DroppedPacket (pSysBuffer, KeepStats_ ()) ; break ; } // ------------------------------------------------------------ // EMPTY -> IN_HEADER state transition m_PESParserState = IN_HEADER ; PES_header_data_length = 0 ; m_fPESHeaderParsed = FALSE ; m_iPESPacketBytesProcessed = 0 ; // fall through case IN_HEADER : r = ProcessPESHeader_ ( & pbBuffer, & iBufferLength, pReset ) ; if (!r || !m_fPESHeaderParsed) { NE_SPEW (r, TRUE, TEXT ("CMpeg2PESStreamParser::ProcessBuffer_ () : ProcessPESHeader_ () failed")) ; break ; } // we are done processing the header; make sure this is not // just a padding packet; if so, we reset and wait for the // next PES packet if (stream_id == STREAM_ID_PADDING_STREAM) { ResetForNextPESPacket_ () ; break ; } // still wait until first PCR is received if (!m_fFirstTimestampProcessed && // haven't started yet !m_pIPTSConvert -> FirstPCRProcessed (m_dwpPTSContext)) { // first PCR not yet seen ResetForNextPESPacket_ () ; // assume this gets NULLed by compiler when free bits are compiled if (IS_PES_VIDEO (stream_id)) { TRACE_ERROR_0 (TEXT ("DROPPED (video pes): waiting for first valid PCR")) ; } else { TRACE_ERROR_0 (TEXT ("DROPPED (audio pes): waiting for first valid PCR")) ; } m_pStats -> DroppedPacket (pSysBuffer, KeepStats_ ()) ; break ; } // ------------------------------------------------------------ // IN_HEADER -> IN_DATA_OFFSET state transition m_PESParserState = IN_DATA_OFFSET ; m_iDataOffsetSkipped = 0 ; case IN_DATA_OFFSET : if (m_dwSubstreamValue != SUBSTREAM_FILTER_VAL_NONE && // valid value to compare m_iDataOffsetSkipped == 0 && // we're on the 0th byte iBufferLength > 0) { // we have 0th byte to look at if (pbBuffer [0] != (BYTE) (m_dwSubstreamValue & 0x000000ff)) { // not the substream we want; wait for the next ResetForNextPESPacket_ () ; break ; } } iDataOffsetRemaining = DataOffsetRemaining_ () ; if (iBufferLength >= iDataOffsetRemaining) { // outright skip over it pbBuffer += iDataOffsetRemaining ; iBufferLength -= iDataOffsetRemaining ; } else { // skip over what we can; wait for next m_iDataOffsetSkipped += iBufferLength ; ASSERT (DataOffsetRemaining_ () > 0) ; break ; } // ------------------------------------------------------------ // IN_DATA_OFFSET -> IN_PAYLOAD state transition m_PESParserState = IN_PAYLOAD ; case IN_PAYLOAD : if (pIMediaSample == NULL) { iCollected = CopyStream ( pbBuffer, iBufferLength, pReset ) ; } else { // BUGBUG : don't ignore spanning header iCollected = WrapStream ( pIMediaSample, pbBuffer, iBufferLength, pReset ) ; } m_iPESPacketBytesProcessed += iCollected ; if (iCollected == iBufferLength) { r = TRUE ; if (PESPayloadBytesRemaining_ () == 0 && IsGathering ()) { r = CompletePESCollection_ (pIMediaSample, pReset) ; } } else { NE_SPEW (r, TRUE, TEXT ("CMpeg2PESStreamParser::ProcessBuffer_ () : WrapStream () failed")) ; r = FALSE ; } break ; } ; return r ; } BOOL CMpeg2PESStreamParser::CheckCompletePacket ( IN IMediaSample * pIMS, IN OUT BYTE ** ppbBuffer, IN OUT int * piBufferLength, IN OUT BOOL * pfDiscontinuity, IN OUT BOOL * pfTimestampNext, IN OUT REFERENCE_TIME * prtStart, IN OUT REFERENCE_TIME * prtStop ) { if (!m_fFirstTimestampProcessed) { // we're still waiting for our first timestamp if ((* pfTimestampNext) && (* prtStart) != UNDEFINED) { // found it; flag a discontinuity and try to send it out; // m_fFirstTimestampProcessed is toggled upon successfull // downstream transmission (* pfDiscontinuity) = TRUE ; if (IS_PES_VIDEO (stream_id)) { m_pStats -> VideoPTSBase (prtStart, & m_rtVideoPTSOffset) ; } else { m_pStats -> AudioPTSBase (prtStart, & m_rtAudioPTSOffset) ; } } else { // not timestamped; continue to wait TRACE_0 (LOG_TRACE, 1, TEXT ("dropped data (CheckCompletePacketFailed)")) ; ResetForNextPESPacket_ () ; return FALSE ; } } if (* pfTimestampNext) { if (IS_PES_VIDEO (stream_id)) { m_pStats -> VideoPTSOut () ; // need to be 0-based, or as close to as possible, if in pull mode (* prtStart) += m_rtVideoPTSOffset ; (* prtStop) += m_rtVideoPTSOffset ; } else { m_pStats -> AudioPTSOut () ; // need to be 0-based, or as close to as possible, if in pull mode (* prtStart) += m_rtAudioPTSOffset ; (* prtStop) += m_rtAudioPTSOffset ; } } return TRUE ; } void CMpeg2PESStreamParser::OnDiscontinuity ( ) { CTStickyVal Reset (TRUE) ; if (IsGathering ()) { Complete (& Reset) ; } // // if completion failed, we'll abort what we're collecting; we ignore the // completion return code deliberately // m_fWaitMinPTS = TRUE ; m_fFirstTimestampProcessed = FALSE ; CStreamParser::OnDiscontinuity () ; } void CMpeg2PESStreamParser::Abort ( ) { O_TRACE_ENTER_0 (TEXT ("CMpeg2PESStreamParser::Abort ()")) ; AbortPESCollection_ () ; } void CMpeg2PESStreamParser::Active ( IN BOOL fPullMode ) { O_TRACE_ENTER_0 (TEXT ("CMpeg2PESStreamParser::Active ()")) ; Abort () ; stream_id = UNDEFINED ; m_fFirstTimestampProcessed = FALSE ; m_rtLastStart = 0 ; m_fWaitMinPTS = FALSE ; m_fPullMode = fPullMode ; if (m_fPullMode) { // don't bias if we're in pull mode m_rtAudioPTSOffset = 0 ; m_rtVideoPTSOffset = 0 ; } else { m_rtAudioPTSOffset = m_rtRegAudioPTSOffset ; m_rtVideoPTSOffset = m_rtRegVideoPTSOffset ; } } CMpeg2PESStreamParser::CMpeg2PESStreamParser ( CBufferSource * pBufferSource, CIPTSConvert * pIPTSConvert, DWORD dwStreamIdentifier, CMpeg2Stats * pStats, AUDIO_FILTER_INFO * pAudioFilterInfo, // can be NULL HKEY hkey, HRESULT * phr ) : CStreamParser ( TIMEOUT_MEDIUM, pBufferSource, pStats, REQUEST_BUFFER_LENGTH ), m_pIPTSConvert (pIPTSConvert), m_pStats (pStats), m_dwpPTSContext (0), m_iDataOffset (0), m_dwSubstreamValue (SUBSTREAM_FILTER_VAL_NONE), m_fFirstTimestampProcessed (FALSE), m_fCopyOutgoing (pBufferSource -> IsCopyBufferSource ()), m_fPullMode (FALSE), m_rtLastStart (0), m_fWaitMinPTS (FALSE) { DWORD dw ; LONG l ; TRACE_CONSTRUCTOR (TEXT ("CMpeg2PESStreamParser")) ; ASSERT (m_pIPTSConvert) ; ASSERT (m_pStats) ; m_pStats -> AddRef () ; if (pAudioFilterInfo) { // arbitrary value that we validate against if (0 <= pAudioFilterInfo -> iDataOffset && pAudioFilterInfo -> iDataOffset <= 10) { // if some high bits were set make sure they are valid if (((pAudioFilterInfo -> dwSubstreamValue) & 0xffffff00) != 0x00000000) { // only accept the 1 type for now if (pAudioFilterInfo -> dwSubstreamValue != SUBSTREAM_FILTER_VAL_NONE) { (* phr) = E_INVALIDARG ; return ; } } m_dwSubstreamValue = pAudioFilterInfo -> dwSubstreamValue ; m_iDataOffset = pAudioFilterInfo -> iDataOffset ; } else { (* phr) = E_INVALIDARG ; return ; } } // read the audio offset milliseconds; bound the value dw = REG_DEFAULT_REG_AUDIO_PTS_OFFSET_MILLIS ; l = RegGetValIfExist ( hkey, REG_AUDIO_PTS_OFFSET_MILLIS, TRUE, (DWORD *) & dw ) ; if (l == ERROR_SUCCESS) { m_rtRegAudioPTSOffset = MillisToDShow (Min (dw, MAX_REG_AUDIO_PTS_OFFSET_MILLIS)) ; } else { m_rtRegAudioPTSOffset = MillisToDShow (REG_DEFAULT_REG_AUDIO_PTS_OFFSET_MILLIS) ; } // read the video offset milliseconds; bound the value dw = REG_DEFAULT_REG_VIDEO_PTS_OFFSET_MILLIS ; l = RegGetValIfExist ( hkey, REG_VIDEO_PTS_OFFSET_MILLIS, TRUE, (DWORD *) & dw ) ; if (l == ERROR_SUCCESS) { m_rtRegVideoPTSOffset = MillisToDShow (Min (dw, MAX_REG_VIDEO_PTS_OFFSET_MILLIS)) ; } else { m_rtRegVideoPTSOffset = MillisToDShow (REG_DEFAULT_REG_VIDEO_PTS_OFFSET_MILLIS) ; } // wait for next PES packet ResetForNextPESPacket_ () ; (* phr) = m_pIPTSConvert -> GetNewPTSContext ( dwStreamIdentifier, & m_dwpPTSContext ) ; m_llTotalGathered = 0 ; } CMpeg2PESStreamParser::~CMpeg2PESStreamParser ( ) { TRACE_DESTRUCTOR (TEXT ("CMpeg2PESStreamParser")) ; m_pStats -> Release () ; if (m_dwpPTSContext != 0) { m_pIPTSConvert -> RecyclePTSContext ( m_dwpPTSContext ) ; } } BOOL CMpeg2PESStreamParser::ParseTier1OptHeader_ ( IN BYTE * pbBuffer, IN CTStickyVal * pReset ) // pbBuffer points to first byte of PES header { LONGLONG PTS ; REFERENCE_TIME rtStart ; REFERENCE_TIME rtStop ; #ifndef UNDER_CE BOOL fTimeDiscontinuity ; BOOL fValidTimes ; #endif //UNDER_CE BOOL fPaddingAdjust ; // Define the packet drop threshold to be 66ms. static const REFERENCE_TIME rtPacketDropThreshold = MillisToDShow(66); O_TRACE_ENTER_0 (TEXT ("CMpeg2PESStreamParser::ParseTier1OptHeader_ ()")) ; ASSERT (IS_TIER1_STREAM (stream_id)) ; ASSERT (PES_header_data_length > 0) ; // we only care about the PTS field if (PTSExists_ ()) { // advance to it pbBuffer += PES_TIER1_REQ_HEADER_FIELD_LEN ; // and get the value PTS = PES_PTS_VALUE (pbBuffer) ; TRACE_3 (LOG_TIMING, 2, TEXT ("stream_id = %02x; PES PTS = %I64d (%I64d ms)"), stream_id, PTS, PTS / 90) ; if (IS_PES_VIDEO (stream_id)) { m_pStats -> VideoPESPTS (& PTS) ; } else { m_pStats -> AudioPESPTS (& PTS) ; } // convert to dshow timestamps, and flag the next buffer m_pIPTSConvert -> MediaSampleTimesFromPTS ( m_dwpPTSContext, stream_id, PTS, pReset, & rtStart, & rtStop, & fPaddingAdjust ) ; if (m_fWaitMinPTS) { // we must wait for a PTS that is >= the last-sent PTS; if we have // a valid PTS, we compare to the last sent PTS; if it's > then // we send this one out, else we drop the PES payload and wait // for the next if (rtStart != UNDEFINED) { // // we have a valid PTS; compare it // if (rtStart <= (m_rtLastStart - rtPacketDropThreshold)) { // too early to resume sending; drop this and wait for // next opportunity ResetForNextPESPacket_ () ; TRACE_4 (LOG_TRACE, 1, TEXT ("DROPPED: Start %I64d (%I64d ms) < %I64d (%I64d ms)"), rtStart, rtStart / 10000, m_rtLastStart, m_rtLastStart / 10000) ; goto cleanup ; } else { // we're done waiting; move on; flag first as discontinuity // always TRACE_4 (LOG_TRACE, 1, TEXT ("RESUMING: Start %I64d (%I64d ms) >= %I64d (%I64d ms)"), rtStart, rtStart / 10000, m_rtLastStart, m_rtLastStart / 10000) ; m_fWaitMinPTS = FALSE ; SetDiscontinuityNext (TRUE) ; } } else { // // timestamp is not valid which probably means we're not yet // receiving our PCR/SCRs; drop this packet // ResetForNextPESPacket_ () ; TRACE_2 (LOG_TRACE, 1, TEXT ("DROPPED: Start UNDEFINED < %I64d (%I64d ms)"), m_rtLastStart, m_rtLastStart / 10000) ; goto cleanup ; } } if (fPaddingAdjust && IsPESAudio (stream_id)) { // audio renderer will subordinate faster if adjustment is flagged as // discontinuity SetDiscontinuityNext (TRUE) ; } // want this timestamp on the next buffer sent out SetTimestampNext ( & rtStart, & rtStop ) ; TRACE_3 (LOG_TRACE, 3, TEXT ("Start %016I64x,%I64u, %I64u ms"), rtStart, rtStart, rtStart / 10000) ; } cleanup : return TRUE ; } BOOL CMpeg2PESStreamParser::ProcessPESHeader_ ( IN OUT BYTE ** ppbBuffer, IN OUT int * piBufferLen, IN CTStickyVal * pReset ) { BOOL r ; int iCopied ; O_TRACE_ENTER_2 ( TEXT ("CMpeg2PESStreamParser::ProcessPESHeader (%08xh, %08xh)"), (* ppbBuffer), (* piBufferLen) ) ; r = TRUE ; switch (m_PESHeaderState) { case IN_HEADER_NEW : // initialize m_PESHeaderCache.Reset () ; PES_header_data_length = 0 ; m_PESHeaderState = IN_HEADER_COMMON ; // and fall through case IN_HEADER_COMMON : iCopied = Min ((* piBufferLen), PES_COMMON_HEADER_FIELD_LEN - m_PESHeaderCache.CurCacheSize ()) ; r = m_PESHeaderCache.Append ( (* ppbBuffer), iCopied ) ; if (!r) { break ; } (* ppbBuffer) += iCopied ; (* piBufferLen) -= iCopied ; m_iPESPacketBytesProcessed += iCopied ; if (m_PESHeaderCache.CurCacheSize () == PES_COMMON_HEADER_FIELD_LEN) { r = ParseCommonPESHeader_ ( m_PESHeaderCache.Get () ) ; if (!r) { // bad header break ; } } else { // we still haven't cached enough break ; } // common PES header has been parsed successfully, with no // errors found; // we may/may not continue; this depends if this is a tier_1 // PES header if (!IS_TIER1_STREAM (stream_id)) { // nope: we are done m_fPESHeaderParsed = TRUE ; break ; } // tier_1 header; required portion m_PESHeaderState = IN_HEADER_TIER1_REQ ; // fall through case IN_HEADER_TIER1_REQ : iCopied = Min ((* piBufferLen), PES_TIER1_REQ_HEADER_FIELD_LEN - m_PESHeaderCache.CurCacheSize ()) ; r = m_PESHeaderCache.Append ( (* ppbBuffer), iCopied ) ; if (!r) { break ; } (* ppbBuffer) += iCopied ; (* piBufferLen) -= iCopied ; m_iPESPacketBytesProcessed += iCopied ; if (m_PESHeaderCache.CurCacheSize () == PES_TIER1_REQ_HEADER_FIELD_LEN) { r = ParseTier1ReqHeader_ ( m_PESHeaderCache.Get () ) ; if (!r) { // bad header break ; } } else { // more to collect break ; } // we may be done if (PES_header_data_length == 0) { // nothing follows; we are done parsing the header m_fPESHeaderParsed = TRUE ; break ; } // PES_header_data_len is not 0, so we have optional fields // still left to parse // tier1 header; optional portion m_PESHeaderState = IN_HEADER_TIER1_OPT ; // fall through case IN_HEADER_TIER1_OPT : iCopied = Min ((* piBufferLen), PES_TIER1_REQ_HEADER_FIELD_LEN + PES_header_data_length - m_PESHeaderCache.CurCacheSize ()) ; r = m_PESHeaderCache.Append ( (* ppbBuffer), iCopied ) ; if (!r) { break ; } (* ppbBuffer) += iCopied ; (* piBufferLen) -= iCopied ; m_iPESPacketBytesProcessed += iCopied ; if (m_PESHeaderCache.CurCacheSize () == PES_TIER1_REQ_HEADER_FIELD_LEN + PES_header_data_length) { // we have enough to parse; do so now r = ParseTier1OptHeader_ ( m_PESHeaderCache.Get (), pReset ) ; // we're done parsing regardless m_fPESHeaderParsed = r ; } break ; } ; return r ; } HRESULT CMpeg2PESStreamParser::ProcessSysBuffer ( IN IMediaSample * pIMediaSample, IN MPEG2_SYS_BUFFER * pSysBuffer, // buffer IN BOOL fPacketError, // packet has an error IN BOOL fDiscontinuity, // discontinuity IN BOOL fMaybeDuplicate, // packet looks like it might be a duplicate IN BOOL fNewPayload, // packet is the first of a new payload IN int iPacketLastByteOffset, // offset of first byte, relative to first byte of input buffer IN CTStickyVal * pReset // some parsers can detect when the demux should be reset i.e. // the same as when an input stream discontinuity occurs ) { BOOL r ; O_TRACE_ENTER_1 ( TEXT ("CMpeg2PESStreamParser::ProcessSysBuffer (%08xh)"), pIMediaSample ) ; m_pStats -> MappedPacket (pSysBuffer, KeepStats_ ()) ; m_pStats -> Global_MappedPacketPES (KeepStats_ ()) ; // we need a payload to work with if (pSysBuffer -> iSysBufferPayloadLength == 0) { return S_OK ; } if (fDiscontinuity || fPacketError ) { // complete what we may have gathered so far AbortPESCollection_ () ; } if (fNewPayload) { CompletePESCollection_ ((m_fCopyOutgoing ? NULL : pIMediaSample), pReset) ; } r = ProcessBuffer_ ( (m_fCopyOutgoing ? NULL : pIMediaSample), fNewPayload, pSysBuffer, pReset ) ; if (r) { return S_OK ; } else { // an error occured; abort what we may have; update stats m_pStats -> Global_MediaSampleAbort (KeepStats_ ()) ; m_pStats -> Global_BytesAborted (BufferUsed (), KeepStats_ ()) ; m_pStats -> DroppedPacket (pSysBuffer, KeepStats_ ()) ; TRACE_ERROR_0 (TEXT ("DROPPED (pes): ProcessBuffer_ () failed")) ; AbortPESCollection_ () ; return E_FAIL ; } } // --------------------------------------------------------------------------- // CMpeg2PSIParse // --------------------------------------------------------------------------- void CMpeg2PSIParse::ResetForNextSection_ ( ) { O_TRACE_ENTER_0 (TEXT ("CMpeg2PSIParse::ResetForNextSection_ ()")) ; // reset state and properties to start over m_PSIParserState = EMPTY ; m_iTotalSectionBytesCollected = 0 ; m_iSectionTotalLength = 0 ; } int CMpeg2PSIParse::PSIHeaderBytesRemaining_ ( ) { O_TRACE_ENTER_0 (TEXT ("CMpeg2PSIParse::PSIHeaderBytesRemaining_ ()")) ; // make sure we don't underflow if we read in a header that looked good // but wasn't return Max (m_iHeaderSize - m_iTotalSectionBytesCollected, 0) ; } int CMpeg2PSIParse::SectionBytesRemaining_ ( ) { O_TRACE_ENTER_0 (TEXT ("CMpeg2PSIParse::SectionBytesRemaining_ ()")) ; // make sure we don't underflow if we read in a header that looked good // but wasn't return Max (m_iSectionTotalLength - m_iTotalSectionBytesCollected, 0) ; } void CMpeg2PSIParse::AbortPSICollection_ ( ) { O_TRACE_ENTER_0 (TEXT ("CMpeg2PSIParse::AbortPSICollection_ ()")) ; // abort the buffer collection AbortBuffer () ; ResetForNextSection_ () ; PSICurSectionAborted () ; SetDiscontinuityNext (TRUE) ; } HRESULT CMpeg2PSIParse::CompletePSICollection_ ( IN CTStickyVal * pReset ) { HRESULT hr ; O_TRACE_ENTER_0 (TEXT ("CMpeg2PSIParse::CompletePSICollection_ ()")) ; ASSERT (SectionBytesRemaining_ () == 0) ; ASSERT (m_iTotalSectionBytesCollected > 0) ; ASSERT (m_PSIParserState == IN_PAYLOAD) ; hr = Complete (pReset) ; PSICurSectionCompleted (hr) ; ResetForNextSection_ () ; return hr ; } BOOL CMpeg2PSIParse::PointerField_ ( IN BYTE * pbSection, IN int iBufferLen, OUT BYTE * ppointer_field ) { O_TRACE_ENTER_0 (TEXT ("CMpeg2PSIParse::PointerField_ ()")) ; (* ppointer_field) = BYTE_VALUE (pbSection,0) ; return VALID_POINTER_FIELD ((* ppointer_field), (DWORD) (iBufferLen)) ; } BOOL CMpeg2PSIParse::ProcessBuffer_ ( IN OUT BYTE ** ppbBuffer, IN OUT int * piBufferLen, IN CTStickyVal * pReset ) { BOOL r ; HRESULT hr ; int iCollect ; int iSkip ; BOOL fTableIdGood ; O_TRACE_ENTER_0 (TEXT ("CMpeg2PSIParse::ProcessBuffer_ ()")) ; switch (m_PSIParserState) { case EMPTY : // update state information m_PSIParserState = IN_HEADER ; // fall through case IN_HEADER : ASSERT (PSIHeaderBytesRemaining_ () > 0) ; // if we've collected nothing and can validate the header // without collecting it (and then having to abort everything // if the header check fails; such as a version check) if (m_iTotalSectionBytesCollected == 0 && (* piBufferLen) >= m_iHeaderSize) { // check it out first r = CheckHeader ( (* ppbBuffer), m_iHeaderSize, & m_iSectionTotalLength, & fTableIdGood ) ; if (r) { // the header is good; if it's not, we discard the entire // transport packet, since we cannot trust the length // field // check if the table_id is good // check the version // check the stream ID; default returns TRUE // if any are not, we discard this specific PSI section and // try again with the next if (!fTableIdGood || !IsNewVersion () || !CheckStreamId ((* ppbBuffer), m_iHeaderSize)) { // skip ahead; we know the length is valid or we'd have // bailed above iSkip = Min ((* piBufferLen), SectionBytesRemaining_ ()) ; (* ppbBuffer) += iSkip ; (* piBufferLen) -= iSkip ; // not a new version, or wrong substream, or wrong table_id; // drop this one on the floor AbortPSICollection_ () ; break ; } // get a buffer to copy to hr = GetNewFrameBuffer () ; if (SUCCEEDED (hr)) { r = CopyFrameBytes ( (* ppbBuffer), m_iHeaderSize ) ; if (r) { // we have the whole header m_iTotalSectionBytesCollected = m_iHeaderSize ; (* ppbBuffer) += m_iHeaderSize ; (* piBufferLen) -= m_iHeaderSize ; } } else { // failed to get a new frame buffer r = FALSE ; TRACE_ERROR_0 (TEXT ("CMpeg2PSIParse::ProcessBuffer_ () : needed a copy buffer for 1st part of header and failed to obtain 1")) ; } } } else { // we either have a partial header, or we have a // partially collected header from the last pass iCollect = Min ((* piBufferLen), PSIHeaderBytesRemaining_ ()) ; // if we're going to start collecting and don't have any buffer to // collect into, do so now if (iCollect > 0 && m_iTotalSectionBytesCollected == 0) { hr = GetNewFrameBuffer () ; } else { hr = S_OK ; } if (SUCCEEDED (hr)) { r = CopyFrameBytes ( (* ppbBuffer), iCollect ) ; if (r) { m_iTotalSectionBytesCollected += iCollect ; (* ppbBuffer) += iCollect ; (* piBufferLen) -= iCollect ; // if we have everything, check it if (PSIHeaderBytesRemaining_ () == 0) { r = CheckHeader ( GetFrameBufferStartPointer (), m_iHeaderSize, & m_iSectionTotalLength, & fTableIdGood ) ; if (r) { // header is good (length at the very least) // check if the table_id is good // check the version // check the stream ID; default returns TRUE // if any are not, we discard this specific PSI section and // try again with the next if (!fTableIdGood || !IsNewVersion () || !CheckStreamId (GetFrameBufferStartPointer (), m_iHeaderSize)) { // skip ahead; we know the length is valid or we'd have // bailed above iSkip = Min ((* piBufferLen), SectionBytesRemaining_ ()) ; (* ppbBuffer) += iSkip ; (* piBufferLen) -= iSkip ; // not a new version, or wrong substream, or wrong table_id; drop // this one on the floor AbortPSICollection_ () ; break ; } } } } } else { // failed to obtain a buffer when we needed one r = FALSE ; TRACE_ERROR_0 (TEXT ("CMpeg2PSIParse::ProcessBuffer_ () : needed a copy buffer for 1st part of header and failed to obtain 1")) ; } } // if anything failed above; abort if (!r) { TRACE_ERROR_0 (TEXT ("CMpeg2PSIParse::ProcessBuffer_ () : aborting from the PSI header")) ; AbortPSICollection_ () ; break ; } // if we haven't collected the entire header yet, break if (PSIHeaderBytesRemaining_ () > 0) { ASSERT ((* piBufferLen) == 0) ; break ; } // // otherwise we have an entire header // // -------------------------------------------------------- // -------------------------------------------------------- // IN_HEADER -> IN_PAYLOAD state transition m_PSIParserState = IN_PAYLOAD ; ASSERT (m_iTotalSectionBytesCollected == m_iHeaderSize) ; // fall through case IN_PAYLOAD : iCollect = Min ((* piBufferLen), SectionBytesRemaining_ ()) ; r = CopyFrameBytes ( (* ppbBuffer), iCollect ) ; if (r) { m_iTotalSectionBytesCollected += iCollect ; (* ppbBuffer) += iCollect ; (* piBufferLen) -= iCollect ; if (SectionBytesRemaining_ () == 0) { r = CheckCompleteSection (GetFrameBufferStartPointer (), BufferUsed ()) ; if (r) { hr = CompletePSICollection_ (pReset) ; r = SUCCEEDED (hr) ; } } } else { TRACE_ERROR_0 (TEXT ("CMpeg2PSIParse::ProcessBuffer_ () : aborting; an error occured while processing the PSI payload")) ; AbortPSICollection_ () ; } break ; } ; return r ; } CMpeg2PSIParse::CMpeg2PSIParse ( CBufferSource * pBufferSource, CMpeg2Stats * pStats, int iHeaderSize ) : CCopyFrameParser ( pBufferSource, pStats, REQUEST_BUFFER_LENGTH ), m_iHeaderSize (iHeaderSize) { TRACE_CONSTRUCTOR (TEXT ("CMpeg2PSIParse")) ; ResetForNextSection_ () ; } CMpeg2PSIParse::~CMpeg2PSIParse ( ) { TRACE_DESTRUCTOR (TEXT ("CMpeg2PSIParse")) ; } HRESULT CMpeg2PSIParse::ProcessSysBuffer ( IN IMediaSample * pIMediaSample, IN MPEG2_SYS_BUFFER * pSysBuffer, // buffer IN BOOL fPacketError, // packet has an error IN BOOL fDiscontinuity, // discontinuity IN BOOL fMaybeDuplicate, // packet looks like it might be a duplicate IN BOOL fNewPayload, // packet is the first of a new payload IN int iPacketLastByteOffset, // offset of first byte, relative to first byte of input buffer IN CTStickyVal * pReset // some parsers can detect when the demux should be reset i.e. // the same as when an input stream discontinuity occurs ) { BOOL r ; BYTE * pbBuffer ; int iBufferLen ; #ifndef UNDER_CE int iEachSection ; #endif //UNDER_CE BYTE pointer_field ; O_TRACE_ENTER_0 (TEXT ("CMpeg2PSIParse::ProcessSysBuffer_ ()")) ; m_pStats -> MappedPacket (pSysBuffer, KeepStats_ ()) ; m_pStats -> MappedPacketPSI (KeepStats_ ()) ; // we need a payload to work with; we would never expect this // but don't leave it in as an ASSERT if (pSysBuffer -> iSysBufferPayloadLength == 0) { return S_OK ; } // either of these conditions cause us to abort the collection if (fDiscontinuity || fPacketError) { AbortPSICollection_ () ; } if (fNewPayload) { // a new payload implies a pointer_field // get its value r = PointerField_ ( pSysBuffer -> pbSysBufferPayload, pSysBuffer -> iSysBufferPayloadLength, & pointer_field ) ; if (!r) { AbortPSICollection_ () ; TRACE_ERROR_0 (TEXT ("DROPPED (psi): pointer_field not found")) ; m_pStats -> DroppedPacket (pSysBuffer, KeepStats_ ()) ; return E_FAIL ; } // if we're collecting if (StateCollecting_ ()) { // and the pointer_field encloses something if (pointer_field > 0) { // set the buffer pointer and length so we process just // what's enclosed by the pointer_field pbBuffer = pSysBuffer -> pbSysBufferPayload + 1 ; iBufferLen = pointer_field ; // per h.222.0 the pointer_field offsets to the // FIRST new section, so we know we at most have // have 1 to complete; ProcessBuffer_ ( & pbBuffer, & iBufferLen, pReset ) ; if (StateCollecting_ ()) { AbortPSICollection_ () ; } // everything that was enclosed in the pointer_field // bracket has now been processed } else { // something strange is going on; abort what we've collected // to now but don't fail all the way out; what's left may // be good AbortPSICollection_ () ; } } // set the buffer pointer and length, consider the pointer_field pbBuffer = pSysBuffer -> pbSysBufferPayload + pointer_field + 1 ; iBufferLen = pSysBuffer -> iSysBufferPayloadLength - pointer_field - 1 ; } else if (StateCollecting_ ()) { // there's no pointer_field; we'll start with the first byte // of the payload pbBuffer = pSysBuffer -> pbSysBufferPayload ; iBufferLen = pSysBuffer -> iSysBufferPayloadLength ; } else { // we're not collecting and it's not a new payload; drop the packet m_pStats -> DroppedPacket (pSysBuffer, KeepStats_ ()) ; TRACE_ERROR_0 (TEXT ("DROPPED (psi): waiting for payload_unit_start")) ; return S_OK ; } // now process everything we can do { r = ProcessBuffer_ ( & pbBuffer, & iBufferLen, pReset ) ; } while (r && // no errors iBufferLen > 0 && // we have something to look at (* pbBuffer) != FORBIDDEN_TABLE_ID) ; // table_id != 0xff if (r) { return S_OK ; } else { m_pStats -> DroppedPacket (pSysBuffer, KeepStats_ ()) ; TRACE_ERROR_0 (TEXT ("DROPPED (psi): ProcessBuffer_ () error")) ; return E_FAIL ; } } void CMpeg2PSIParse::Abort ( ) { O_TRACE_ENTER_0 (TEXT ("CMpeg2PESStreamParser::Abort ()")) ; AbortPSICollection_ () ; } // ---------------------------------------------------------------------------- // CMpeg2VersionFilteredPSIParse // ---------------------------------------------------------------------------- BOOL CMpeg2VersionFilteredPSIParse::CheckCompleteSection ( IN BYTE * pbSection, IN int iSectionLength ) { O_TRACE_ENTER_2 ( TEXT ("CMpeg2VersionFilteredPSIParse::CheckCompleteSection (%08xh, %08xh)"), pbSection, iSectionLength ) ; return ConfirmMpeg2PSICRC_32 ( pbSection, iSectionLength ) ; } // --------------------------------------------------------------------------- // CMpeg2PCRParser // --------------------------------------------------------------------------- HRESULT CMpeg2PCRParser::ProcessSysBuffer ( IN IMediaSample * pIMediaSample, IN MPEG2_SYS_BUFFER * pSysBuffer, // buffer IN BOOL fPacketError, // packet has an error IN BOOL fDiscontinuity, // discontinuity IN BOOL fMaybeDuplicate, // packet looks like it might be a duplicate IN BOOL fNewPayload, // packet is the first of a new payload IN int iPacketLastByteOffset, // offset of first byte, relative to first byte of input buffer IN CTStickyVal * pReset // some parsers can detect when the demux should be reset i.e. // the same as when an input stream discontinuity occurs ) { HRESULT hr ; #ifndef UNDER_CE int iCollected ; #endif // UNDER_CE MPEG2_TRANSPORT_HEADER * pMPEG2TransportHeader ; PCR_RECORD PCRRecord ; BOOL r ; O_TRACE_ENTER_0 ( TEXT ("CMpeg2PCRParser::ProcessSysBuffer ()") ) ; ASSERT (pSysBuffer) ; pMPEG2TransportHeader = static_cast (pSysBuffer) ; if (TS_IS_PCR_PACKET (pMPEG2TransportHeader) && !fPacketError) { // collect the PCR ExtractPCR_ ( pMPEG2TransportHeader, & PCRRecord ) ; // compute last PCR field offset as follows // iPacketLastByteOffset // - 188 // previous packet last byte // + 4 // transport header fixed fields // + 2 // adaptation field fixed fields // + 6 // to byte after program_clock_reference_extension // - 1 // back to last byte of program_clock_reference_extension PCRRecord.iLastPCRByteOffset = iPacketLastByteOffset - 188 + 4 + 2 + 6 - 1 ; // we now get a new buffer, copy into it, and complete the buffer hr = GetNewFrameBuffer () ; if (SUCCEEDED (hr)) { r = CopyFrameBytes ( reinterpret_cast (& PCRRecord), sizeof PCR_RECORD ) ; if (r) { Complete (pReset) ; } else { AbortBuffer () ; hr = E_FAIL ; } } } else { hr = S_OK ; } return hr ; } // --------------------------------------------------------------------------- // CMpeg2SCRParser // --------------------------------------------------------------------------- HRESULT CMpeg2SCRParser::ProcessSysBuffer ( IN IMediaSample * pIMediaSample, IN MPEG2_SYS_BUFFER * pSysBuffer, // buffer IN BOOL fPacketError, // packet has an error IN BOOL fDiscontinuity, // discontinuity IN BOOL fMaybeDuplicate, // packet looks like it might be a duplicate IN BOOL fNewPayload, // packet is the first of a new payload IN int iPacketLastByteOffset, // offset of first byte, relative to first byte of input buffer IN CTStickyVal * pReset // some parsers can detect when the demux should be reset i.e. // the same as when an input stream discontinuity occurs ) { HRESULT hr ; #ifndef UNDER_CE int iCollected ; #endif // UNDER_CE PCR_RECORD PCRRecord ; BOOL r ; O_TRACE_ENTER_0 ( TEXT ("CMpeg2SCRParser::ProcessSysBuffer ()") ) ; if (fDiscontinuity || fNewPayload) { m_dwState = STATE_WAIT_NEXT ; } r = TRUE ; switch (m_dwState) { case STATE_WAIT_NEXT : if (!fNewPayload) { break ; } // transition m_Cache.Reset () ; m_dwState = STATE_COLLECTING ; // fall through case STATE_COLLECTING : if (m_Cache.CurCacheSize () == 0) { if (pSysBuffer -> iSysBufferLength >= PACK_HEADER_SCR_LEN) { ExtractSCR_ ( pSysBuffer -> pbSysBuffer, & PCRRecord ) ; } else { r = m_Cache.Append ( pSysBuffer -> pbSysBuffer, pSysBuffer -> iSysBufferLength ) ; break ; } } else { r = m_Cache.Append ( pSysBuffer -> pbSysBuffer, Min (pSysBuffer -> iSysBufferLength, PACK_HEADER_SCR_LEN - m_Cache.CurCacheSize ()) ) ; if (r && m_Cache.CurCacheSize () == PACK_HEADER_SCR_LEN) { ExtractSCR_ ( m_Cache.Get (), & PCRRecord ) ; } else { break ; } } // we're here because the header was successfully parsed hr = GetNewFrameBuffer () ; if (SUCCEEDED (hr)) { r = CopyFrameBytes ( reinterpret_cast (& PCRRecord), sizeof PCR_RECORD ) ; if (r) { Complete (pReset) ; } else { AbortBuffer () ; hr = E_FAIL ; } } r = (SUCCEEDED (hr) ? TRUE : FALSE) ; m_dwState = STATE_WAIT_NEXT ; } ; if (!r) { m_dwState = STATE_WAIT_NEXT ; } return (r ? S_OK : E_FAIL) ; } // ---------------------------------------------------------------------------- // CMpeg2AnalogCopyProtectionParser // ---------------------------------------------------------------------------- CMpeg2AnalogCopyProtectionParser::CMpeg2AnalogCopyProtectionParser ( CBufferSource * pBufferSource, CMpeg2Stats * pStats ) : CCopyFrameParser (pBufferSource, pStats, sizeof ANALOGCOPYPROTECTION_RECORD ), m_dwACPLast (UNDEFINED) { m_Cache.Reset () ; } HRESULT CMpeg2AnalogCopyProtectionParser::ProcessPESPacket_ ( IN BYTE * pbPESPacket, IN CTStickyVal * pReset ) { HRESULT hr ; DWORD dwCurACP ; BOOL r ; ASSERT (PES_PACKET_LENGTH_IS_MIN_VALID_ANALOGCOPYPROTECTION (pbPESPacket)) ; ASSERT (PES_STREAM_ID_VALUE (pbPESPacket) == STREAM_ID_ANALOGCOPYPROTECTION) ; hr = S_OK ; // now confirm the substream; if not the right one, we just drop the packet if (SUBSTREAMID_IS_ANALOGCOPYPROTECTION (pbPESPacket)) { dwCurACP = PES_ACP_BITS (pbPESPacket) ; if (dwCurACP != m_dwACPLast) { // new bits; process them hr = GetNewFrameBuffer () ; if (SUCCEEDED (hr)) { m_ACPRecord.dwACPBits = dwCurACP ; r = CopyFrameBytes ( reinterpret_cast (& m_ACPRecord), sizeof m_ACPRecord ) ; if (r) { Complete (pReset) ; // don't process again unless they change m_dwACPLast = dwCurACP ; } else { AbortBuffer () ; hr = E_FAIL ; } } } } return hr ; } HRESULT CMpeg2AnalogCopyProtectionParser::ProcessSysBuffer ( IN IMediaSample * pIMediaSample, IN MPEG2_SYS_BUFFER * pSysBuffer, // buffer IN BOOL fPacketError, // packet has an error IN BOOL fDiscontinuity, // discontinuity IN BOOL fMaybeDuplicate, // packet looks like it might be a duplicate IN BOOL fNewPayload, // packet is the first of a new payload IN int iPacketLastByteOffset, // offset of first byte, relative to first byte of input buffer IN CTStickyVal * pReset // some parsers can detect when the demux should be reset i.e. // the same as when an input stream discontinuity occurs ) { HRESULT hr ; int iCopy ; BOOL r ; #ifndef UNDER_CE DWORD PES_packet_length ; #endif // UNDER_CE hr = S_OK ; // reset our cache if we're a new payload, or if there's been a // discontinuity if (fNewPayload || fDiscontinuity) { m_Cache.Reset () ; } // don't process unless it makes sense to if ((fNewPayload && m_Cache.IsEmpty ()) || // starting a new PES packet (!fNewPayload && m_Cache.CurCacheSize () > 0) // continuing an unfinished packet ) { // copy as much as possible into our cache iCopy = ::Min ( pSysBuffer -> iSysBufferPayloadLength, MIN_VALID_ANALOGCOPYPROTECTION_PES_PACKET_LENGTH - m_Cache.CurCacheSize () ) ; r = m_Cache.Append (pSysBuffer -> pbSysBufferPayload, iCopy) ; ASSERT (r) ; // // always parse out of the cache (guarantees contiguous data) // // we might have enough to check the entire packet outright if (m_Cache.CurCacheSize () >= MIN_VALID_ANALOGCOPYPROTECTION_PES_PACKET_LENGTH) { // we've got enough so the cache holds everything we (might) need hr = ProcessPESPacket_ ( m_Cache.Get (), pReset ) ; // done regardless of success or failure m_Cache.Reset () ; } // or we might have enough to check the PES_packet_length field at least else if (m_Cache.CurCacheSize () >= PES_COMMON_HEADER_FIELD_LEN) { if (!PES_PACKET_LENGTH_IS_MIN_VALID_ANALOGCOPYPROTECTION (m_Cache.Get ())) { // PES_packet_length value indicates that this packet will never be // long enough to be a analog copy protection packet; punt; this isn't // a failure, it's just not what we need m_Cache.Reset () ; } } } else { m_Cache.Reset () ; } return hr ; } void CMpeg2AnalogCopyProtectionParser::Active ( IN BOOL fPullMode ) { m_dwACPLast = UNDEFINED ; CCopyFrameParser::Active (fPullMode) ; }