//------------------------------------------------------------------------------ // // 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 --*/ #ifndef __mp2demux_plparse_h #define __mp2demux_plparse_h struct PCR_RECORD { LONGLONG llPCR ; // PCR value (first 42 bits; upper 2 // bytes are 0) int iLastPCRByteOffset ; // offset from end of input buffer (which // contains the multiplexed transport // packets) to the last byte of the // program_clock_reference_extension; // always <= 0; we use this value to // compute bitrate (per H.222.0 equation // 2-5) } ; struct ANALOGCOPYPROTECTION_RECORD { DWORD dwACPBits ; // look at lowest 2 bits } ; /*++ This data structure provides a breakout of all MPEG-2 Transport header fields. The incoming stream has all the information embedded. The code in the filter, parses that out and assigns all the values in this data structure. --*/ struct MPEG2_TRANSPORT_HEADER : public MPEG2_SYS_BUFFER { // transport packet specific header; see the MPEG2 systems spec // for descriptions DWORD sync_byte ; BOOL transport_error_indicator ; BOOL payload_unit_start_indicator ; BOOL transport_priority ; DWORD PID ; DWORD transport_scrambling_control ; DWORD adaptation_field_control ; DWORD continuity_counter ; struct ADAPTATION_FIELD { DWORD adaptation_field_length ; BOOL discontinuity_indicator ; BOOL random_access_indicator ; BOOL elementary_stream_priority_indicator ; BOOL PCR_flag ; BOOL OPCR_flag ; BOOL splicing_point_flag ; BOOL transport_private_data_flag ; BOOL adaptation_field_extension_flag ; // if PCR_flag == 1 LONGLONG program_clock_reference_base ; DWORD program_clock_reference_extension ; // if OPCR_flag == 1 LONGLONG original_program_clock_reference_base ; DWORD original_program_clock_reference_extension ; // if splicing_point_flag == 1 DWORD splice_countdown ; // if transport_private_data_flag == 1 DWORD transport_private_data_length ; BYTE * private_data_byte ; // [transport_private_data_length] // if adaptation_field_extension_flag == 1 DWORD adaptation_field_extension_length ; BOOL ltw_flag ; // XXXX: ignored for now BOOL piecewise_rate_flag ; // XXXX: ignored for now BOOL seamless_splice_flag ; // XXXX: ignored for now // if ltw_flag == 1 BOOL ltw_valid_flag ; // XXXX: ignored for now DWORD ltw_offset ; // XXXX: ignored for now // if piecewise_rate_flag == 1 DWORD piecewise_rate ; // XXXX: ignored for now // if seamless_splice_flag == 1 DWORD splice_type ; // XXXX: ignored for now DWORD DTS_next_AU_32_30 ; // XXXX: ignored for now BOOL marker_bit_1 ; // XXXX: ignored for now DWORD DTS_next_AU_29_15 ; // XXXX: ignored for now BOOL marker_bit_2 ; // XXXX: ignored for now DWORD DTS_next_AU_14_0 ; // XXXX: ignored for now BOOL marker_bit_3 ; // XXXX: ignored for now DWORD stuffing_byte_length ; BYTE * stuffing_byte ; // [stuffing_byte_length] } AdaptationField ; } ; /*++ The purpose of this struct is to provide transport-specific data, for those parsers that care e.g. PSI parsers. PES parsers won't care and will use the pointers defined in the base struct (MPEG2_SYS_BUFFER). All parsing methods assume ** BYTE ALIGNMENT **. Bit alignment is NOT supported. --*/ struct MPEG2_TRANSPORT_PACKET : public MPEG2_TRANSPORT_HEADER { #define TS_ADAPTATION_FIELD_EXISTS(tsp) ((tsp)->adaptation_field_control == 2 || (tsp)->adaptation_field_control == 3) #define TS_IS_PCR_PACKET(tsp) ((TS_ADAPTATION_FIELD_EXISTS(tsp) && (tsp)->AdaptationField.adaptation_field_length > 0) ? (tsp)->AdaptationField.PCR_flag : FALSE) #define TS_PAYLOAD_EXISTS(tsp) ((tsp)->adaptation_field_control == 1 || (tsp)->adaptation_field_control == 3) #define TS_PACKET_ERROR(tsp) ((tsp)->transport_error_indicator) #define TS_DISCONTINUITY(tsp) (EXPECT_CONTINUITY_COUNTER_INCREMENT((tsp)->PID,(tsp)->adaptation_field_control) && \ (tsp)->continuity_counter != (tsp)->dwExpectedContinuityCounter) #define TS_MAYBE_DUPLICATE(tsp) (((tsp)->adaptation_field_control == 1 || (tsp)->adaptation_field_control == 3) && \ ((tsp)->dwExpectedContinuityCounter == INCREMENTED_CONTINUITY_COUNTER_VALUE((tsp)->continuity_counter))) #define TS_PACKET_OK(tsp) (((tsp)->iSysBufferLength == TS_PACKET_SIZE) && \ ((tsp)->pbSysBufferPayload ? (tsp)->pbSysBufferPayload >= (tsp)->pbSysBuffer + 4 : TRUE) && \ ((tsp)->iSysBufferPayloadLength <= (tsp)->iSysBufferLength - 4)) // set by the mapper; this value is based on the last PID packet processed DWORD dwExpectedContinuityCounter ; _inline BOOL ParseAdaptationField ( IN BYTE * pbAdaptationField // points to first byte of adaptation field ) /*++ parse out entire adaptation field, including PCR, etc... --*/ { BYTE * pb ; int iAdaptationFieldBytesRemaining ; AdaptationField.adaptation_field_length = ADAPTATION_FIELD_LENGTH_VALUE (pbAdaptationField) ; if (AdaptationField.adaptation_field_length > 0 && AdaptationField.adaptation_field_length <= 183) { // adaptation field seems valid AdaptationField.discontinuity_indicator = DISCONTINUITY_INDICATOR_BIT (pbAdaptationField) ; AdaptationField.random_access_indicator = RANDOM_ACCESS_INDICATOR_BIT (pbAdaptationField) ; AdaptationField.elementary_stream_priority_indicator = ELEMENTARY_STREAM_PRIORITY_INDICATOR_BIT (pbAdaptationField) ; AdaptationField.PCR_flag = PCR_FLAG_BIT (pbAdaptationField) ; AdaptationField.OPCR_flag = OPCR_FLAG_BIT (pbAdaptationField) ; AdaptationField.splicing_point_flag = SPLICING_POINT_FLAG_BIT (pbAdaptationField) ; AdaptationField.transport_private_data_flag = TRANSPORT_PRIVATE_DATA_FLAG_BIT (pbAdaptationField) ; AdaptationField.adaptation_field_extension_flag = ADAPTATION_FIELD_EXTENSION_FLAG_BIT (pbAdaptationField) ; // use pb from now on since byte offset will vary depending on content; // see macro comments for pointer location expectation pb = pbAdaptationField + 2 ; // plus TS header + first 2 bytes of adaptation_field // bytes left : adaptation_field_length does not include itself, but does include the byte // with all the flags we just parsed iAdaptationFieldBytesRemaining = AdaptationField.adaptation_field_length - 1 ; // if this flag is set we have a PCR in the adaptation field if (AdaptationField.PCR_flag) { AdaptationField.program_clock_reference_base = PCR_BASE_VALUE (pb) ; AdaptationField.program_clock_reference_extension = PCR_EXT_VALUE (pb) ; iAdaptationFieldBytesRemaining -= ((33 + 6 + 9) / 8) ; pb += ((33 + 6 + 9) / 8) ; // advance pointer to beyond } // if this flag is set we have an OPCR in the adaptation field if (AdaptationField.OPCR_flag) { AdaptationField.original_program_clock_reference_base = OPCR_BASE_VALUE (pb) ; AdaptationField.original_program_clock_reference_extension = OPCR_EXT_VALUE (pb) ; iAdaptationFieldBytesRemaining -= ((33 + 6 + 9) / 8) ; pb += ((33 + 6 + 9) / 8) ; // advance pointer to beyond } // if this flag is set, we have a splice_countdown value if (AdaptationField.splicing_point_flag) { AdaptationField.splice_countdown = SPLICE_COUNTDOWN_VALUE (pb) ; iAdaptationFieldBytesRemaining -= 1 ; pb += 1 ; } // if this flag is set, we are carrying private data if (AdaptationField.transport_private_data_flag) { AdaptationField.transport_private_data_length = TRANSPORT_PRIVATE_DATA_LENGTH_VALUE (pb) ; iAdaptationFieldBytesRemaining -= 1 ; pb += 1 ; // advance to private_data_byte array // make sure we're within bounds before dereferencing pb if (iAdaptationFieldBytesRemaining >= 0) { AdaptationField.private_data_byte = pb ; // [transport_private_data_length] } else { // bogus adapation field return FALSE ; } iAdaptationFieldBytesRemaining -= AdaptationField.transport_private_data_length ; if (iAdaptationFieldBytesRemaining < 0) { // bogus adaptation field return FALSE ; } pb += AdaptationField.transport_private_data_length ; // beyond the array } if (AdaptationField.adaptation_field_extension_flag) { AdaptationField.adaptation_field_extension_length = ADAPTATION_FIELD_EXTENSION_LENGTH_VALUE (pb) ; // XXXX: change this so we actually parse the fields iAdaptationFieldBytesRemaining -= 1 ; pb += 1 ; // advance to field extension fields iAdaptationFieldBytesRemaining -= AdaptationField.adaptation_field_extension_length ; if (iAdaptationFieldBytesRemaining < 0) { // bogus adaptation field return FALSE ; } pb += AdaptationField.adaptation_field_extension_length ; // beyond the extension } // pb now points to the end of the adaptation field data; set the stuffing_bytes // and stuffing_byte_length values; cast away the error since this should never // exceed the length of a TS packet; // validate pb first if (iAdaptationFieldBytesRemaining >= 0) { AdaptationField.stuffing_byte_length = (DWORD) (AdaptationField.adaptation_field_length - (pb - (pbAdaptationField + 1))) ; AdaptationField.stuffing_byte = pb ; // [stuffing_byte_length] // only stuffing bytes should have been left over return (iAdaptationFieldBytesRemaining == (int) AdaptationField.stuffing_byte_length) ; } else { // off the end: error return FALSE ; } } else if (AdaptationField.adaptation_field_length == 0) { // we have just 1 stuffing byte; set these because they are used by the // TS parser to determine offsetting for payload AdaptationField.stuffing_byte_length = 1 ; AdaptationField.stuffing_byte = pbAdaptationField ; return TRUE ; } else { // bogus adaptation_field_length return FALSE ; } } __inline BOOL Parse ( IN BYTE * pbTSPacket // points to first byte of transport packet header ) /*++ parse out the TS packet, including adaptation field if it exists --*/ { static BYTE * pb ; sync_byte = SYNC_BYTE_VALUE (pbTSPacket) ; transport_error_indicator = TRANSPORT_ERROR_INDICATOR_BIT (pbTSPacket) ; payload_unit_start_indicator = PAYLOAD_UNIT_START_INDICATOR_BIT (pbTSPacket) ; transport_priority = TRANSPORT_PRIORITY_BIT (pbTSPacket) ; PID = PID_VALUE (pbTSPacket) ; transport_scrambling_control = TRANSPORT_SCRAMBLING_CONTROL_VALUE (pbTSPacket) ; adaptation_field_control = ADAPTATION_FIELD_CONTROL_VALUE (pbTSPacket) ; continuity_counter = CONTINUITY_COUNTER_VALUE (pbTSPacket) ; // only parse out the adaptation field if there is one; these // values reference table 2-5 in the MPEG2 systems specification if (TS_ADAPTATION_FIELD_EXISTS (this)) { if (ParseAdaptationField (pbTSPacket + 4) == TRUE) { // validate what was parsed out if (VALID_ADAPTATION_FIELD (adaptation_field_control, AdaptationField.adaptation_field_length) == FALSE) { return FALSE ; } } else { return FALSE ; } } else { AdaptationField.adaptation_field_length = 0 ; } // if we're not a valid packet, we should never get to here ASSERT (sync_byte == TS_PACKET_SYNC_BYTE) ; // now set the pointers up pbSysBuffer = pbTSPacket ; iSysBufferLength = TS_PACKET_SIZE ; // do we have a payload ?; ref table 2-5 in the MPEG2 systems // specification if (TS_PAYLOAD_EXISTS (this)) { // if we don't have to deal with an adaptation field, things are // easy if (TS_ADAPTATION_FIELD_EXISTS (this) == FALSE) { pbSysBufferPayload = pbTSPacket + 4 ; } else { // we have to advance beyond the adaptation field, which ends // where its stuffing bytes end, unless the length is 0, in which // case the length field in the adaptation field is the stuffing // byte (ref 2.4.3.5 in H.222.0) if (AdaptationField.adaptation_field_length > 0) { pbSysBufferPayload = & AdaptationField.stuffing_byte [AdaptationField.stuffing_byte_length] ; } else { ASSERT (adaptation_field_control == 0x3) ; // 4 byte TS packet header + 1 stuffing byte pbSysBufferPayload = pbTSPacket + 4 + 1 ; } } // set the length iSysBufferPayloadLength = (DWORD) ((pbTSPacket + TS_PACKET_SIZE) - pbSysBufferPayload) ; } else { pbSysBufferPayload = NULL ; iSysBufferPayloadLength = 0 ; } return TRUE ; } } ; // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- void CMpeg2Stats::DroppedPacket ( IN MPEG2_SYS_BUFFER * pSysBuffer, IN BOOL fReportStat ) { if (fReportStat && m_pStats) { m_pStats -> cGlobalDroppedPackets++ ; // global if (m_Mpeg2StreamType == MPEG2_STREAM_TRANSPORT) { PID_DroppedPacket_ ((static_cast (pSysBuffer)) -> PID) ; // per PID (maybe) } else { if (pSysBuffer -> iSysBufferLength >= 4) { StartCode_DroppedPacket_ (START_CODE_VALUE (pSysBuffer -> pbSysBuffer)) ; } } } } void CMpeg2Stats::MappedPacket ( IN MPEG2_SYS_BUFFER * pSysBuffer, IN BOOL fReportStat ) { if (fReportStat && m_pStats) { m_pStats -> cGlobalMappedPackets++ ; // global if (m_Mpeg2StreamType == MPEG2_STREAM_TRANSPORT) { PID_MappedPacket_ ((static_cast (pSysBuffer)) -> PID) ; // per PID (maybe) } else { if (pSysBuffer -> iSysBufferLength >= 4) { StartCode_MappedPacket_ (START_CODE_VALUE (pSysBuffer -> pbSysBuffer)) ; } } } } // ---------------------------------------------------------------------------- // CDemuxBaseParser // ---------------------------------------------------------------------------- class CDemuxBaseParser { DWORD m_dwTimeoutMillis ; CMpeg2Stats * m_pStats ; LONG m_lRef ; DWORD m_dwStartTicks ; BOOL m_fKeepStats ; protected : // timeout constants // NOTE: these are checked no more often than the value of the TimeoutCheckThreshold // registry key, so that will override minimums here enum { TIMEOUT_MINIMUM_MILLISECONDS = REG_DEFAULT_TIMEOUT_CHECK_THRESHOLD, TIMEOUT_INCREMENT_QUANTUM = 50, // milliseconds TIMEOUT_SHORT = TIMEOUT_MINIMUM_MILLISECONDS, TIMEOUT_MEDIUM = TIMEOUT_MINIMUM_MILLISECONDS + 1 * TIMEOUT_INCREMENT_QUANTUM, TIMEOUT_LONG = TIMEOUT_MINIMUM_MILLISECONDS + 2 * TIMEOUT_INCREMENT_QUANTUM, TIMEOUT_NONE = -1 // value means parser's payloads never times out } ; public : CDemuxBaseParser ( DWORD dwTimeoutMillis, CMpeg2Stats * pStats, BOOL fKeepStats ) : m_dwTimeoutMillis (dwTimeoutMillis), m_pStats (pStats), m_lRef (1), m_dwStartTicks (0), m_fKeepStats (fKeepStats) { TRACE_CONSTRUCTOR (TEXT ("CDemuxBaseParser")) ; m_pStats -> AddRef () ; } virtual ~CDemuxBaseParser ( ) { TRACE_DESTRUCTOR (TEXT ("CDemuxBaseParser")) ; m_pStats -> Release () ; } virtual HRESULT ProcessSysBuffer ( IN IMediaSample * pIMediaSample, IN MPEG2_SYS_BUFFER * bSysBuffer, // 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 ) = 0 ; // -------------------------------------------------------------------- // refcounting ULONG AddRef () { return InterlockedIncrement (& m_lRef) ; } ULONG Release () { if (InterlockedDecrement (& m_lRef) == 0) { delete this ; return 0 ; } return m_lRef ; } // -------------------------------------------------------------------- // timeout methods BOOL CanTimeout () { return m_dwTimeoutMillis != TIMEOUT_NONE ; } void ResetTimer (IN DWORD dwTicks) { m_dwStartTicks = dwTicks ; } virtual void Timeout (IN CTStickyVal * pReset) {} void CheckTimeout (IN DWORD dwTicksNow, IN CTStickyVal * pReset) { O_TRACE_ENTER_1 (TEXT ("CDemuxBaseParser::CheckTimeout (%08xh)"), dwTicksNow) ; ASSERT (CanTimeout ()) ; if (dwTicksNow - m_dwStartTicks >= m_dwTimeoutMillis) { Timeout (pReset) ; ResetTimer (dwTicksNow) ; } } // -------------------------------------------------------------------- virtual void Abort () = 0 ; virtual void Reset () {} virtual void Active (IN BOOL fPullMode) {} virtual void Inactive () {} virtual void OnDiscontinuity () {} } ; // ---------------------------------------------------------------------------- // CBufferSourceManager // ---------------------------------------------------------------------------- class CBufferSourceManager { // general members CBufferSource * m_pBufferSource ; CMpeg2Stats * m_pStats ; BOOL m_fKeepStats ; // timestamp-related members BOOL m_fTimestampNext ; REFERENCE_TIME m_rtStart ; REFERENCE_TIME m_rtStop ; // discontinuity flag BOOL m_fSetDiscontinuityNext ; // copy buffer management members DWORD_PTR m_dwpBufferContext ; BYTE * m_pbStartBuffer ; int m_iBufferLength ; BYTE * m_pbCurrent ; // copy buffer management int BufferRemaining_ () { return m_iBufferLength - BufferUsed () ; } BOOL HaveCopyBuffer_ () { return m_pbStartBuffer != NULL ; } void ResetForNext_ ( ) ; HRESULT CompleteCopyBuffer_ ( IN CTStickyVal * pReset ) ; // all or nothing; no partial copies BOOL Copy_ ( IN BYTE * pb, IN int iLen, OUT int * piRemaining ) ; HRESULT GetCopyBuffer_ ( IN OUT int * piBufferLength ) ; // E_UNEXPECTED if we already have a buffer protected : REFERENCE_TIME StartPTS () { return m_rtStart ; } REFERENCE_TIME StopPTS () { return m_rtStop ; } BOOL IsGathering () { return HaveCopyBuffer_ () && BufferUsed () > 0 ; } int BufferUsed () { return (int) (m_pbCurrent - m_pbStartBuffer) ; } void SetDiscontinuityNext (IN BOOL f) { m_fSetDiscontinuityNext = f ; } HRESULT SendEmptyPacket ( IN IMediaSample * pIMS, IN BOOL fDiscontinuity = FALSE ) ; void SetTimestampNext ( IN REFERENCE_TIME * prtStart, IN REFERENCE_TIME * prtStop ) ; HRESULT GetCopyBlockStartPointer ( OUT BYTE ** ppbStartBuffer ) ; HRESULT Complete ( IN CTStickyVal * pReset ) ; virtual void CompletionRetVal_ ( IN HRESULT hr ) {} BOOL CopyBlock ( IN BYTE * pbBuffer, IN int iBufferLength, OUT int * piBufferRemaining ) ; // all or nothing HRESULT GetNewCopyBuffer ( IN OUT int * piBufferLength ) ; void AbortBuffer ( ) ; BOOL Wrap ( IN IMediaSample * pIMS, IN BYTE * pb, IN int iLen, IN CTStickyVal * pReset ) ; virtual BOOL 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 ) { return TRUE ; } BOOL IsTimestamped_ () { return m_fTimestampNext ; } public : CBufferSourceManager ( CBufferSource * pBufferSource, CMpeg2Stats * pStats, BOOL fKeepStats ) : m_pBufferSource (pBufferSource), m_pStats (pStats), m_fKeepStats (fKeepStats), m_dwpBufferContext (0), m_pbStartBuffer (NULL), m_pbCurrent (NULL), m_iBufferLength (0), m_fTimestampNext (FALSE), m_fSetDiscontinuityNext (TRUE) // first is a discontinuity { TRACE_CONSTRUCTOR (TEXT ("CBufferSourceManager")) ; m_pStats -> AddRef () ; m_pBufferSource -> AddRef () ; } virtual ~CBufferSourceManager ( ) { TRACE_DESTRUCTOR (TEXT ("CBufferSourceManager")) ; AbortBuffer () ; m_pStats -> Release () ; m_pBufferSource -> Release () ; } } ; // ---------------------------------------------------------------------------- // CStreamParser // ---------------------------------------------------------------------------- class CStreamParser : public CDemuxBaseParser, public CBufferSourceManager // implicitely has a timeout value { int m_iRequestBufferLength ; BOOL m_fKeepStats ; BOOL CopyMax_ ( IN BYTE * pbBuffer, IN int iBufferLength, OUT int * piCollected, IN CTStickyVal * pReset ) ; protected : CMpeg2Stats * m_pStats ; __inline BOOL KeepStats_ () { return m_fKeepStats ; } // -------------------------------------------------------------------- // buffer management for streaming; we override these so implicit // buffer manipulation occurs for the caller int WrapStream ( IN IMediaSample * pIMS, IN BYTE * pbBuffer, IN int iBufferLength, IN CTStickyVal * pReset ) ; // returns the total bytes successfully copied int CopyStream ( IN BYTE * pbBuffer, IN int iBufferLength, IN CTStickyVal * pReset ) ; public : CStreamParser ( CBufferSource * pBufferSource, CMpeg2Stats * pStats, int iRequestBufferLength ) ; CStreamParser ( DWORD dwTimeout, CBufferSource * pBufferSource, CMpeg2Stats * pStats, int iRequestBufferLength ) ; virtual ~CStreamParser ( ) ; virtual void OnDiscontinuity ( ) ; // -------------------------------------------------------------------- // buffer timedout virtual void Timeout ( IN CTStickyVal * pReset ) ; } ; // ---------------------------------------------------------------------------- // CCopyFrameParser // ---------------------------------------------------------------------------- class CCopyFrameParser : public CDemuxBaseParser, public CBufferSourceManager // // does not timeout - a complete frame must be gathered before outgoing // makes sense // // frame size is known as well i.e. getting a new payload before having // gathered a complete frame (unless in same packet), is odd // { int m_iMinFrameBuffer ; BOOL m_fKeepStats ; protected : CMpeg2Stats * m_pStats ; __inline BOOL KeepStats_ () { return m_fKeepStats ; } // -------------------------------------------------------------------- HRESULT GetNewFrameBuffer ( IN IMediaSample * pIMediaSample = NULL ) ; BYTE * GetFrameBufferStartPointer ( ) ; BOOL CopyFrameBytes ( IN BYTE * pbBuffer, IN int iBufferLen ) ; public : CCopyFrameParser ( CBufferSource * pBufferSource, CMpeg2Stats * pStats, int iMinFrameBufferSize ) ; virtual ~CCopyFrameParser ( ) ; } ; // ---------------------------------------------------------------------------- // CMpeg2PESStreamParser // ---------------------------------------------------------------------------- struct AUDIO_FILTER_INFO { DWORD dwSubstreamValue ; // AC-3: 0x80 | index; PCM: 0xA0 | index; first byte only int iDataOffset ; // AC-3: 4; } ; class CMpeg2PESStreamParser : public CStreamParser { enum { REQUEST_BUFFER_LENGTH = 65536 // arbitrary } ; enum PES_PARSER_STATE { EMPTY, IN_HEADER, IN_DATA_OFFSET, IN_PAYLOAD } ; // substate of IN_HEADER enum PES_HEADER_STATE { IN_HEADER_NEW, // no header bytes have been processed IN_HEADER_COMMON, // [packet_start_code_prefix, PES_packet_length] IN_HEADER_TIER1_REQ, // (PES_packet_length, PES_header_data_length] IN_HEADER_TIER1_OPT // (PES_header_data_length, PES_packet_data_byte) } ; // for field descriptions and parsing logic see section 2.4.3.7 of the // MPEG2 Systems specification (H.222.0) DWORD packet_start_code_prefix ; DWORD stream_id ; DWORD PES_packet_length ; DWORD PES_scrambling_control ; BOOL PES_priority ; BOOL data_alignment_indicator ; BOOL copyright ; BOOL original_or_copy ; DWORD PTS_DTS_flags ; BOOL ESCR_flag ; BOOL ES_rate_flag ; BOOL DSM_trick_mode_flag ; BOOL additional_copy_info_flag ; BOOL PES_CRC_flag ; BOOL PES_extension_flag ; int PES_header_data_length ; LONGLONG PTS ; LONGLONG DTS ; DWORD ESCR ; DWORD ES ; DWORD trick_mode_control ; BOOL intra_slice_refresh ; BOOL frequency_truncation ; DWORD rep_cntrl ; DWORD field_id ; DWORD additional_copy_info ; DWORD previous_PES_packet_CRC ; BOOL PES_private_data_flag ; BOOL pack_header_field_flag ; BOOL program_packet_sequence_counter_flag ; BOOL P_STD_buffer_flag ; BOOL PES_extension_flag_2 ; BYTE PES_private_data [16] ; DWORD pack_field_length ; DWORD program_packet_sequence_counter ; BOOL MPEG1_MPEG2_identifier ; DWORD original_stuff_length ; BOOL P_STD_buffer_scale ; DWORD P_STD_buffer_size ; DWORD PES_extension_field_length ; DWORD stuffing_byte_length ; BYTE * stuffing_byte ; // [stuffing_byte_length] DWORD PES_packet_data_byte_length ; BYTE * PES_packet_data_byte ; // [PES_packet_data_byte_length] CMpeg2Stats * m_pStats ; CIPTSConvert * m_pIPTSConvert ; DWORD_PTR m_dwpPTSContext ; int m_iPayloadLength ; PES_PARSER_STATE m_PESParserState ; PES_HEADER_STATE m_PESHeaderState ; BOOL m_fPESHeaderParsed ; int m_iPESPacketBytesProcessed ; LONGLONG m_llTotalGathered ; DWORD m_dwSubstreamValue ; // AC-3: 0x80 | index; etc... int m_iDataOffset ; // AC-3: 4; this is the end_of_PES_header -> PES_payload offset; usually 0 int m_iDataOffsetSkipped ; BOOL m_fFirstTimestampProcessed ; // after a channel change; TRUE/FALSE if we've seen our first timestamp BOOL m_fCopyOutgoing ; // depends on the type of buffer source we are given REFERENCE_TIME m_rtAudioPTSOffset ; // millis added to audio PTSs; value in use REFERENCE_TIME m_rtRegAudioPTSOffset ; // millis added to audio PTSs; registry value REFERENCE_TIME m_rtVideoPTSOffset ; // millis added to video PTSs; value in use REFERENCE_TIME m_rtRegVideoPTSOffset ; // millis added to video PTSs; registry value BOOL m_fPullMode ; BOOL m_fWaitMinPTS ; REFERENCE_TIME m_rtLastStart ; // header byte cache; if the a PES header spans system (transport or // program) packet boundaries, we cache it here TSizedDataCache m_PESHeaderCache ; int DataOffsetRemaining_ () { return m_iDataOffset - m_iDataOffsetSkipped ; } BOOL PTSExists_ () { return (PTS_DTS_flags & 0x00000002) != 0 ; } int PESPayloadBytesRemaining_ ( ) ; void ResetForNextPESPacket_ ( ) ; BOOL CompletePESCollection_ ( IN IMediaSample * pIMS, // non-NULL -> we're wrapping IN CTStickyVal * pReset ) ; void AbortPESCollection_ ( ) ; BOOL ParseCommonPESHeader_ ( IN BYTE * pbBuffer ) ; // pbBuffer points to first byte of PES header BOOL ParseTier1ReqHeader_ ( IN BYTE * pbBuffer ) ; // pbBuffer points to first byte of PES header BOOL ParseTier1OptHeader_ ( IN BYTE * pbBuffer, IN CTStickyVal * pReset ) ; // pbBuffer points to first byte of PES header BOOL ProcessPESHeader_ ( IN OUT BYTE ** ppbBuffer, IN OUT int * piBufferLen, IN CTStickyVal * pReset ) ; BOOL ProcessBuffer_ ( IN IMediaSample * pIMediaSample, IN BOOL fNewPayload, IN MPEG2_SYS_BUFFER * pSysBuffer, IN CTStickyVal * pReset ) ; virtual BOOL 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 ) ; virtual void CompletionRetVal_ ( IN HRESULT hr ) ; public : CMpeg2PESStreamParser ( CBufferSource * pBufferSource, CIPTSConvert * pIPTSConvert, DWORD dwStreamIdentifier, CMpeg2Stats * pStats, AUDIO_FILTER_INFO * pAudioFilterInfo, // can be NULL HKEY hkey, HRESULT * phr ) ; virtual ~CMpeg2PESStreamParser ( ) ; virtual HRESULT 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 ) ; virtual void OnDiscontinuity () ; virtual void Abort () ; virtual void Active (IN BOOL fPullMode) ; virtual void Reset () { Active (m_fPullMode) ; } } ; // ---------------------------------------------------------------------------- // CMpeg2TSPassThroughParser // ---------------------------------------------------------------------------- class CMpeg2TSPassThroughParser : public CStreamParser { enum { REQUEST_BUFFER_LENGTH = ((65536 / TS_PACKET_SIZE) * TS_PACKET_SIZE) } ; CMpeg2Stats * m_pStats ; public : CMpeg2TSPassThroughParser ( CBufferSource * pBufferSource, CMpeg2Stats * pStats ) : CStreamParser ( TIMEOUT_MEDIUM, pBufferSource, pStats, REQUEST_BUFFER_LENGTH ), m_pStats (pStats) { TRACE_CONSTRUCTOR (TEXT ("CMpeg2TSPassThroughParser")) ; m_pStats -> AddRef () ; } ~CMpeg2TSPassThroughParser ( ) { TRACE_DESTRUCTOR (TEXT ("CMpeg2TSPassThroughParser")) ; m_pStats -> Release () ; } virtual HRESULT 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 ) { int iCopied ; O_TRACE_ENTER_0 (TEXT ("CMpeg2TSPassThroughParser::ProcessSysBuffer ()")) ; ASSERT (pSysBuffer -> iSysBufferLength == TS_PACKET_SIZE) ; iCopied = CopyStream ( pSysBuffer -> pbSysBuffer, pSysBuffer -> iSysBufferLength, pReset ) ; return (iCopied == pSysBuffer -> iSysBufferLength ? S_OK : E_FAIL) ; } // -------------------------------------------------------------------- virtual void Abort ( ) { O_TRACE_ENTER_0 (TEXT ("CMpeg2TSPassThroughParser::Abort ()")) ; AbortBuffer () ; } } ; // ---------------------------------------------------------------------------- // CMpeg2PSIParse // ---------------------------------------------------------------------------- class CMpeg2PSIParse : public CCopyFrameParser { enum { REQUEST_BUFFER_LENGTH = PSI_MAX_SECTION_LENGTH } ; enum PSI_PARSER_STATE { EMPTY, // waiting for next (fNewPayload = TRUE) IN_HEADER, // not yet collected a complete header IN_PAYLOAD // collecting the payload } ; int m_iHeaderSize ; int m_iSectionTotalLength ; int m_iTotalSectionBytesCollected ; PSI_PARSER_STATE m_PSIParserState ; void ResetForNextSection_ ( ) ; int PSIHeaderBytesRemaining_ ( ) ; int SectionBytesRemaining_ ( ) ; BOOL StateCollecting_ () { return (m_PSIParserState == IN_PAYLOAD) || (m_PSIParserState == IN_HEADER) ; } void AbortPSICollection_ ( ) ; HRESULT CompletePSICollection_ ( IN CTStickyVal * pReset ) ; BOOL PointerField_ ( IN BYTE * pbSection, IN int iBufferLen, OUT BYTE * ppointer_field ) ; BOOL ProcessBuffer_ ( IN OUT BYTE ** ppbBuffer, IN OUT int * piBufferLen, IN CTStickyVal * pReset ) ; public : CMpeg2PSIParse ( CBufferSource * pBufferSource, CMpeg2Stats * pStats, int iHeaderSize ) ; virtual ~CMpeg2PSIParse ( ) ; virtual HRESULT 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 ) ; // if this method returns FALSE, non of the fields can be trusted, // and the entire transport packet should be dropped; if the method // returns TRUE, the header is good; caller should examine // pfTableIsGood returned val to decide whether to collect or // discard the table virtual BOOL CheckHeader ( IN BYTE * pbSectionHeader, IN int iSectionHeaderLength, OUT int * piSectionLength, OUT BOOL * pfTableIdGood ) = 0 ; virtual BOOL CheckCompleteSection ( IN BYTE * pbSection, IN int iSectionLength ) { return TRUE ; } virtual BOOL IsNewVersion ( ) { return TRUE ; } virtual BOOL CheckStreamId ( IN BYTE * pbSectionHeader, IN int iSectionHeaderLength ) { return TRUE ; } virtual void PSICurSectionAborted ( ) = 0 ; virtual void PSICurSectionCompleted ( IN HRESULT hr ) = 0 ; virtual void Abort ( ) ; } ; // ---------------------------------------------------------------------------- // CMpeg2NonVersionedPSIParse // ---------------------------------------------------------------------------- class CMpeg2NonVersionedPSIParse : public CMpeg2PSIParse { enum { REQUEST_BUFFER_LENGTH = PSI_MAX_SECTION_LENGTH } ; DWORD table_id ; BOOL section_syntax_indicator ; DWORD section_length ; public : CMpeg2NonVersionedPSIParse ( CBufferSource * pBufferSource, CMpeg2Stats * pStats ) : CMpeg2PSIParse ( pBufferSource, pStats, PSI_NON_VERSIONED_HEADER_SIZE ) { TRACE_CONSTRUCTOR (TEXT ("CMpeg2NonVersionedPSIParse")) ; } virtual ~CMpeg2NonVersionedPSIParse ( ) { TRACE_DESTRUCTOR (TEXT ("CMpeg2NonVersionedPSIParse")) ; } virtual BOOL CheckHeader ( IN BYTE * pbSectionHeader, IN int iSectionHeaderLength, OUT int * piSectionLength, OUT BOOL * pfTableIdGood ) { O_TRACE_ENTER_0 (TEXT ("CMpeg2NonVersionedPSIParse::CheckHeader ()")) ; ASSERT (iSectionHeaderLength == PSI_NON_VERSIONED_HEADER_SIZE) ; table_id = PSI_TABLE_ID_VALUE (pbSectionHeader) ; section_syntax_indicator = PSI_SECTION_SYNTAX_INDICATOR_BIT (pbSectionHeader) ; section_length = PSI_SECTION_LENGTH_VALUE (pbSectionHeader) ; // don't filter on table_id (* pfTableIdGood) = TRUE ; (* piSectionLength) = COMPLETE_SECTION_LENGTH (section_length) ; return (section_length <= PSI_MAX_SECTION_LENGTH_NUMBER && table_id != FORBIDDEN_TABLE_ID) ; } virtual void PSICurSectionAborted ( ) {} virtual void PSICurSectionCompleted ( IN HRESULT hr ) {} } ; // ---------------------------------------------------------------------------- // CMpeg2VersionFilteredPSIParse // ---------------------------------------------------------------------------- class CMpeg2VersionFilteredPSIParse : public CMpeg2PSIParse { class CVersionMap : public TGenericMap < DWORD, // object: version_number DWORD, // hashkey: section_number FALSE, // duplicates NOT allowed 5, // allocate 5 at a time 19 // fix table at 19 (expect small numbers) > { public : virtual ULONG Value (IN DWORD section_number) { return section_number ; } } ; enum { REQUEST_BUFFER_LENGTH = PSI_MAX_SECTION_LENGTH } ; CVersionMap m_Versions ; DWORD version_number ; DWORD section_number ; BOOL IsNewVersion_ ( IN DWORD dwVersion, IN DWORD dwSection ) { HRESULT hr ; DWORD dwCurVersion ; O_TRACE_ENTER_0 (TEXT ("CMpeg2VersionFilteredPSIParse::IsNewVersion_ ()")) ; hr = m_Versions.Find ( dwSection, & dwCurVersion ) ; if (SUCCEEDED (hr)) { return dwCurVersion != dwVersion ; } else { return TRUE ; } } public : CMpeg2VersionFilteredPSIParse ( CBufferSource * pBufferSource, CMpeg2Stats * pStats ) : CMpeg2PSIParse ( pBufferSource, pStats, PSI_VERSIONED_HEADER_SIZE ) { TRACE_CONSTRUCTOR (TEXT ("CMpeg2VersionFilteredPSIParse")) ; } virtual ~CMpeg2VersionFilteredPSIParse ( ) { TRACE_DESTRUCTOR (TEXT ("CMpeg2VersionFilteredPSIParse")) ; m_Versions.Clear () ; } virtual BOOL IsNewVersion ( ) { return IsNewVersion_ (version_number, section_number) ; } virtual BOOL CheckCompleteSection ( IN BYTE * pbSection, IN int iSectionLength ) ; virtual BOOL CheckHeader ( IN BYTE * pbSectionHeader, IN int iSectionHeaderLength, OUT int * piSectionLength, OUT BOOL * pfTableIdGood ) { BOOL r ; DWORD table_id ; BOOL section_syntax_indicator ; DWORD section_length ; BOOL current_next_indicator ; DWORD last_section_number ; O_TRACE_ENTER_0 (TEXT ("CMpeg2VersionFilteredPSIParse::CheckHeader ()")) ; ASSERT (iSectionHeaderLength == PSI_VERSIONED_HEADER_SIZE) ; // get the table_id; we do a quick check on this before proceeding table_id = PSI_TABLE_ID_VALUE (pbSectionHeader) ; if (CheckTableId (table_id)) { // this is a PMT, we can look at the header in detail (* pfTableIdGood) = TRUE ; section_syntax_indicator = PSI_SECTION_SYNTAX_INDICATOR_BIT (pbSectionHeader) ; section_length = PSI_SECTION_LENGTH_VALUE (pbSectionHeader) ; version_number = PSI_VERSION_NUMBER_VALUE (pbSectionHeader) ; // member current_next_indicator = PSI_CURRENT_NEXT_INDICATOR_BIT (pbSectionHeader) ; section_number = PSI_SECTION_NUMBER_VALUE (pbSectionHeader) ; // member last_section_number = PSI_LAST_SECTION_NUMBER_VALUE (pbSectionHeader) ; // sanity check what we got if (section_length <= PSI_MAX_SECTION_LENGTH_NUMBER && section_syntax_indicator == TRUE && section_number <= last_section_number) { ASSERT (table_id != FORBIDDEN_TABLE_ID) ; // set outgoing (* piSectionLength) = COMPLETE_SECTION_LENGTH (section_length) ; // header looks good r = TRUE ; } else { r = FALSE ; } } else { // perform just a generic check; try to use the section_length // so we can get to tables that may follow in this // transport packet (* pfTableIdGood) = FALSE ; section_length = PSI_SECTION_LENGTH_VALUE (pbSectionHeader) ; (* piSectionLength) = COMPLETE_SECTION_LENGTH (section_length) ; // very basic sanity check r = (section_length <= PSI_MAX_SECTION_LENGTH_NUMBER && table_id != FORBIDDEN_TABLE_ID) ; } // TRUE/FALSE if the header can be used return r ; } virtual BOOL CheckTableId ( IN DWORD table_id ) = 0 ; virtual void PSICurSectionAborted ( ) {} virtual void PSICurSectionCompleted ( IN HRESULT hr ) { DWORD dwOldVersion ; O_TRACE_ENTER_0 (TEXT ("CMpeg2VersionFilteredPSIParse::PSICurSectionCompleted ()")) ; if (SUCCEEDED (hr)) { // remove the old (might not exist) m_Versions.Remove ( section_number, & dwOldVersion ) ; // store the new m_Versions.Store ( version_number, section_number ) ; } else { // call failed; we purge everything so we send new copies // of tables down m_Versions.Clear () ; } } } ; // ---------------------------------------------------------------------------- // CMpeg2PATSectionParser // ---------------------------------------------------------------------------- class CMpeg2PATSectionParser : public CMpeg2VersionFilteredPSIParse { DWORD m_dwTransportStreamIdFilter ; public : CMpeg2PATSectionParser ( CBufferSource * pBufferSource, CMpeg2Stats * pStats, DWORD dwTransportStreamIdFilter ) : CMpeg2VersionFilteredPSIParse ( pBufferSource, pStats ), m_dwTransportStreamIdFilter (dwTransportStreamIdFilter) { TRACE_CONSTRUCTOR (TEXT ("CMpeg2PATSectionParser")) ; } virtual ~CMpeg2PATSectionParser ( ) { TRACE_DESTRUCTOR (TEXT ("CMpeg2PATSectionParser")) ; } virtual BOOL CheckStreamId ( IN BYTE * pbSectionHeader, IN int iSectionHeaderLength ) { DWORD transport_stream_id ; BOOL r ; ASSERT (pbSectionHeader) ; ASSERT (iSectionHeaderLength >= 5) ; if (m_dwTransportStreamIdFilter == WILDCARD_STREAM) { // all are fine r = TRUE ; } else { transport_stream_id = PAT_TRANSPORT_STREAM_ID_VALUE (pbSectionHeader) ; r = (transport_stream_id == m_dwTransportStreamIdFilter) ; } return r ; } virtual BOOL CheckTableId ( IN DWORD table_id ) { O_TRACE_ENTER_0 (TEXT ("CMpeg2PATSectionParser::CheckTableOId ()")) ; return table_id == PAT_TABLE_ID ; } } ; // ---------------------------------------------------------------------------- // CMpeg2PMTSectionParser // ---------------------------------------------------------------------------- class CMpeg2PMTSectionParser : public CMpeg2VersionFilteredPSIParse { DWORD m_dwProgramNumberFilter ; public : CMpeg2PMTSectionParser ( CBufferSource * pBufferSource, CMpeg2Stats * pStats, DWORD dwProgramNumberFilter ) : CMpeg2VersionFilteredPSIParse ( pBufferSource, pStats ), m_dwProgramNumberFilter (dwProgramNumberFilter) { TRACE_CONSTRUCTOR (TEXT ("CMpeg2PMTSectionParser")) ; } virtual ~CMpeg2PMTSectionParser ( ) { TRACE_DESTRUCTOR (TEXT ("CMpeg2PMTSectionParser")) ; } virtual BOOL CheckStreamId ( IN BYTE * pbSectionHeader, IN int iSectionHeaderLength ) { DWORD program_number ; BOOL r ; ASSERT (pbSectionHeader) ; ASSERT (iSectionHeaderLength >= 5) ; if (m_dwProgramNumberFilter != WILDCARD_STREAM) { program_number = PMT_PROGRAM_NUMBER_VALUE (pbSectionHeader) ; r = (program_number == m_dwProgramNumberFilter) ; } else { // all are ok r = TRUE ; } return r ; } virtual BOOL CheckTableId ( IN DWORD table_id ) { O_TRACE_ENTER_0 (TEXT ("CMpeg2PMTSectionParser::CheckTableOId ()")) ; return table_id == PMT_TABLE_ID ; } } ; // ---------------------------------------------------------------------------- // CMpeg2PCRParser // ---------------------------------------------------------------------------- class CMpeg2PCRParser : public CCopyFrameParser { void ExtractPCR_ ( IN MPEG2_TRANSPORT_HEADER * pMPEG2TransportHeader, IN PCR_RECORD * pPCRRecord ) { ASSERT (pPCRRecord) ; ASSERT (pMPEG2TransportHeader) ; ASSERT (TS_IS_PCR_PACKET (pMPEG2TransportHeader)) ; // equation 2-1, H.222.0 pPCRRecord -> llPCR = pMPEG2TransportHeader -> AdaptationField.program_clock_reference_base * 300 + pMPEG2TransportHeader -> AdaptationField.program_clock_reference_extension ; } public : CMpeg2PCRParser ( CBufferSource * pBufferSource, CMpeg2Stats * pStats ) : CCopyFrameParser ( pBufferSource, pStats, sizeof PCR_RECORD ) {} virtual HRESULT 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 ) ; virtual void Abort ( ) { O_TRACE_ENTER_0 (TEXT ("CMpeg2PCRParser::Abort ()")) ; AbortBuffer () ; } } ; // ---------------------------------------------------------------------------- // CMpeg2SCRParser // ---------------------------------------------------------------------------- class CMpeg2SCRParser : public CCopyFrameParser { enum { STATE_WAIT_NEXT, STATE_COLLECTING } ; // pack header fields LONGLONG system_clock_reference_base ; LONGLONG system_clock_reference_extension ; TSizedDataCache m_Cache ; DWORD m_dwState ; void ExtractSCR_ ( IN BYTE * pbPackHeader, IN PCR_RECORD * pPCRRecord ) // pbBuffer { ASSERT (pPCRRecord) ; ASSERT (START_CODE_PREFIX_VALUE (pbPackHeader) == START_CODE_PREFIX) ; ASSERT (START_CODE_VALUE (pbPackHeader) == MPEG2_PACK_START_CODE) ; system_clock_reference_base = PACK_HEADER_SCR_BASE (pbPackHeader) ; system_clock_reference_extension = PACK_HEADER_SCR_EXT (pbPackHeader) ; // equation 2-1, H.222.0 pPCRRecord -> llPCR = system_clock_reference_base * 300 + system_clock_reference_extension ; } public : CMpeg2SCRParser ( CBufferSource * pBufferSource, CMpeg2Stats * pStats ) : CCopyFrameParser ( pBufferSource, pStats, sizeof PCR_RECORD ), m_dwState (STATE_WAIT_NEXT) {} virtual HRESULT 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 ) ; virtual void Abort ( ) { O_TRACE_ENTER_0 (TEXT ("CMpeg2SCRParser::Abort ()")) ; AbortBuffer () ; } } ; // ---------------------------------------------------------------------------- // CMpeg2GenericTSPayload // ---------------------------------------------------------------------------- class CMpeg2GenericTSPayload : public CStreamParser { enum { REQUEST_BUFFER_SIZE = 65536 // completely arbitrary } ; enum PARSER_STATE { WAIT_NEW, PROCESSING } ; CMpeg2Stats * m_pStats ; PARSER_STATE m_State ; public : CMpeg2GenericTSPayload ( CBufferSource * pBufferSource, CMpeg2Stats * pStats ) : CStreamParser ( pBufferSource, pStats, REQUEST_BUFFER_SIZE ), m_State (WAIT_NEW), m_pStats (pStats) { m_pStats -> AddRef () ; } virtual ~CMpeg2GenericTSPayload ( ) { m_pStats -> Release () ; } virtual HRESULT 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 ) { int iCopied ; HRESULT hr ; if (fDiscontinuity || fPacketError) { Abort () ; } switch (m_State) { case WAIT_NEW : if (fNewPayload == FALSE) { // not a new payload; we must continue to wait hr = S_OK ; break ; } // -------------------------------------------------------- // WAIT_NEW -> PROCESSING state transition // setup for processing state and fall through m_State = PROCESSING ; case PROCESSING : // if this is a new packet, complete what we have if (fNewPayload) { Complete (pReset) ; } if (pSysBuffer -> iSysBufferPayloadLength > 0) { ASSERT (pSysBuffer -> pbSysBufferPayload != NULL) ; // if we have something to copy, do so now iCopied = CopyStream ( pSysBuffer -> pbSysBufferPayload, pSysBuffer -> iSysBufferPayloadLength, pReset ) ; hr = (iCopied == pSysBuffer -> iSysBufferPayloadLength ? S_OK : E_FAIL) ; if (FAILED (hr)) { // abort if the copy failed Abort () ; } } else { // otherwise success and continue hr = S_OK ; } } ; return hr ; } virtual void Abort ( ) { O_TRACE_ENTER_0 (TEXT ("CMpeg2PCRParser::Abort ()")) ; m_State = WAIT_NEW ; AbortBuffer () ; } } ; // ---------------------------------------------------------------------------- // CMpeg2AnalogCopyProtectionParser // ---------------------------------------------------------------------------- class CMpeg2AnalogCopyProtectionParser : public CCopyFrameParser { TSizedDataCache m_Cache ; DWORD m_dwACPLast ; ANALOGCOPYPROTECTION_RECORD m_ACPRecord ; // caller *must* make sure that we have MIN_VALID_ACP bytes HRESULT ProcessPESPacket_ ( IN BYTE * pbPESPacket, IN CTStickyVal * pReset ) ; public : CMpeg2AnalogCopyProtectionParser ( CBufferSource * pBufferSource, CMpeg2Stats * pStats ) ; virtual HRESULT 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 ) ; virtual void Abort ( ) { O_TRACE_ENTER_0 (TEXT ("CMpeg2AnalogCopyProtectionParser::Abort ()")) ; AbortBuffer () ; m_Cache.Reset () ; } virtual void Active (IN BOOL fPullMode) ; } ; #endif // __mp2demux_plparse_h