//------------------------------------------------------------------------------ // // 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: tsmapper.h Abstract: This module contains the class declarations for the transport stream mapper/parser. Revision History: 29-Jul-1999 created --*/ #ifndef __mp2demux_tsmapper_h #define __mp2demux_tsmapper_h class CParserRef { LONG m_cRef ; CDemuxBaseParser * m_pParser ; LIST_ENTRY m_ListEntry ; public : CParserRef ( CDemuxBaseParser * pParser ) : m_pParser (pParser), m_cRef (1) { ASSERT (pParser) ; m_pParser -> AddRef () ; InitializeListHead (& m_ListEntry) ; } ~CParserRef ( ) { // remove self from list we're on Unhook () ; // release our ref to any parser RELEASE_AND_CLEAR (m_pParser) ; } LIST_ENTRY * ListEntry () { return & m_ListEntry ; } void InsertHead (IN LIST_ENTRY * pListEntry) { InsertHeadList (pListEntry, & m_ListEntry) ; } void InsertTail (IN LIST_ENTRY * pListEntry) { InsertTailList (pListEntry, & m_ListEntry) ; } void Unhook () { RemoveEntryList (& m_ListEntry) ; InitializeListHead (& m_ListEntry) ; } static CParserRef * RecoverParserRef (IN LIST_ENTRY * pListEntry) { return CONTAINING_RECORD (pListEntry, CParserRef, m_ListEntry) ; } CDemuxBaseParser * GetParser () { return m_pParser ; } void CheckTimeout (IN DWORD dwTicks, IN CTStickyVal * pReset) { m_pParser -> CheckTimeout (dwTicks, pReset) ; } BOOL CanTimeout () { return m_pParser -> CanTimeout () ; } ULONG AddRef () { return InterlockedIncrement (& m_cRef) ; } ULONG Release () { if (InterlockedDecrement (& m_cRef) == 0) { delete this ; return 0 ; } return m_cRef ; } } ; class CStreamMapper { friend class CStreamMapContext ; TLookupTableBase * m_pStreamFilter ; LIST_ENTRY m_TimeoutParserListHead ; // list header for parsers that can timeout BOOL m_fEnableTimeouts ; DWORD m_dwTimeoutCheckThreshold ; // minimum amount of milliseconds that must pass // before all the stream maps are checked for timeout // condition DWORD m_dwLastTimeoutCheckTickCount ; // milliseconds the last time all stream maps were // checked CIInputStreamEvent * m_pIInputStreamEvent ; // callback interface // returns existing, or creates new, CStreamMapContext CStreamMapContext * GetStreamMapContext_ ( IN DWORD dwStreamIdentifier ) ; // removes the CStreamMapContext, releases references to resources HRESULT RemoveStreamMapContext_ ( IN DWORD dwStreamIdentifier ) ; protected : CMpeg2Stats * m_pStats ; // stats module CMPEG2PushClock * m_pCMPEG2PushClock ; // push clock CTStickyVal m_Reset ; // checks for timeouts __inline void CheckForTimeouts_ ( IN CTStickyVal * pReset ) ; CStreamMapContext * GetIndexed ( IN DWORD dwIndex ) { return m_pStreamFilter -> GetIndexed (dwIndex) ; } virtual CStreamMapContext * NewStreamMapContext_ ( IN DWORD dwStreamIdentifier ) = 0 ; void ParserInstructedReset_ ( ) ; public : CStreamMapper ( IN CMpeg2Stats * pStats, IN DWORD dwTimeoutCheckThreshold, IN CMPEG2PushClock * pCMPEG2PushClock, IN TLookupTableBase * pStreamFilter, IN CIInputStreamEvent * pIInputStreamEvent ) ; CStreamMapper ( IN CMpeg2Stats * pStats, IN CMPEG2PushClock * pCMPEG2PushClock, IN TLookupTableBase * pStreamFilter, IN CIInputStreamEvent * pIInputStreamEvent ) ; virtual ~CStreamMapper () ; // given a buffer of transport stream packets, processes them virtual HRESULT Process ( IN IMediaSample * pIOwningMediaSample, IN BYTE * pbBuffer, IN int iBufferLength ) = 0 ; // maps a stream to the passed parser HRESULT MapStream ( IN DWORD dwStreamIdentifier, IN CDemuxBaseParser * pPayloadParse ) ; HRESULT UnmapStream ( IN DWORD dwStreamIdentifier, IN CDemuxBaseParser * pPayloadParse ) ; virtual HRESULT Initialize ( ) = 0 ; } ; class CTransportStreamMapper : public CStreamMapper { enum TS_MAPPER_STATE { IN_PRE_STRIDE, IN_TS_PACKET, IN_POST_STRIDE } ; TS_MAPPER_STATE m_TSMapperState ; int m_iPreStrideLength ; // pre-packet bytes; skip int m_iPreStrideBytesRemaining ; // per-packet state int m_iPostStrideLength ; // post-packet bytes; skip int m_iPostStrideBytesRemaining ; // per-packet state // PID lookup table TLookupTable m_PIDFilter ; // The spanning packet is used when a TS packet spans more than 1 // input buffer. A source filter will almost never hand over a // media sample containing only complete TS packets. Most of the // time the last bytes of the media sample contain the first few // bytes of the first packet in the next media sample. The spanning // packet is used to copy what's left of what looks to be the // the beginning of a packet, and complete it with the next // media sample, process it, then move on to the remainder of // the media sample. TSizedDataCache m_SpanningPacket ; __inline void InitForNextTSPacket_ ( ) { m_iPreStrideBytesRemaining = m_iPreStrideLength ; m_iPostStrideBytesRemaining = m_iPostStrideLength ; m_TSMapperState = IN_PRE_STRIDE ; } // processes each packet __inline void ProcessTSPacket_ ( IN IMediaSample * pIOwningMediaSample, IN BYTE * pbTSPacket, IN int iPacketLastByteOffset, IN CTStickyVal * pReset ) ; // finds the next sync_byte value BOOL SeekSyncByte_ ( IN OUT BYTE ** ppbBuffer, IN OUT int * piBufferLength ) ; protected : virtual CStreamMapContext * NewStreamMapContext_ ( IN DWORD dwStreamIdentifier ) ; public : CTransportStreamMapper ( IN CMpeg2Stats * pStats, IN DWORD dwTimeoutCheckThreshold, IN CMPEG2PushClock * pCMPEG2PushClock, IN CIInputStreamEvent * pIInputStreamEvent ) : CStreamMapper (pStats, dwTimeoutCheckThreshold, pCMPEG2PushClock, & m_PIDFilter, pIInputStreamEvent ), m_iPreStrideLength (0), m_iPostStrideLength (0) { Initialize () ; } // given a buffer of transport stream packets, processes them virtual HRESULT Process ( IN IMediaSample * pIOwningMediaSample, IN BYTE * pbBuffer, IN int iBufferLength ) ; virtual HRESULT Initialize ( ) ; void SetStrideLengths ( IN int iPreStrideLength, IN int iPostStrideLength ) { m_iPreStrideLength = iPreStrideLength ; m_iPostStrideLength = iPostStrideLength ; } } ; class CProgramStreamMapper : public CStreamMapper { enum PS_MAPPER_STATE { WAIT_START, // wait for { 00 00 01 } IN_START_CODE, // gathering remainder of prefix and parsing start code: { xx } IN_HEADER, // parsing header { 00 00 01 xx ... }; 14 bytes pack; 6 bytes otherwise; includes start_code IN_PAYLOAD // in payload; 0 bytes long if pack header } ; PS_MAPPER_STATE m_PSMapperState ; BOOL m_fWaitNextPack ; int m_iHeaderLength ; int m_iPayloadLength ; int m_iPayloadProcessed ; BYTE m_dwStartCode ; int m_iPacketLength ; // includes the header int m_iPacketBytesProcessed ; // includes the header // really don't need double-indirected lookup since the size is smaller // than transport.. but this simplifies the base-class implementation TLookupTable m_StreamIdFilter ; TSizedDataCache m_HeaderCache ; // ------------------------------------------------------------------------ // pack header core fields int pack_stuffing_length ; LONGLONG system_clock_reference_base ; LONGLONG system_clock_reference_extension ; DWORD program_mux_rate ; // PES: packet_length // system header: header_length // PSM: program_stream_map_length // etc.. int packet_length_value ; int PacketBytesRemaining_ () { return m_iPacketLength - m_iPacketBytesProcessed ; } void WaitForNextPack_ ( ) ; void WaitForNextStartPrefix_ ( ) ; void AbortCurrent_ ( ) ; int HeaderLength_ ( IN BYTE bStartCode ) { int iLength ; switch (bStartCode) { case MPEG2_PACK_START_CODE : iLength = PACK_HEADER_CORE_LEN ; break ; default : iLength = PACK_PAYLOAD_HEADER_LEN ; break ; } ; return iLength ; } BOOL ParseHeader_ ( IN BYTE bStartCode, IN BYTE * pbBuffer ) ; BOOL ProcessStream_ ( IN IMediaSample * pIOwningMediaSample, IN DWORD dwStartCode, IN MPEG2_SYS_BUFFER * pSysBuffer, IN CTStickyVal * pReset ) ; protected : // called by parent class to obtain a stream map context, that is // program stream specific in this case virtual CStreamMapContext * NewStreamMapContext_ ( IN DWORD dwStreamIdentifier ) ; public : CProgramStreamMapper ( IN CMpeg2Stats * pStats, IN CMPEG2PushClock * pCMPEG2PushClock, IN CIInputStreamEvent * pIInputStreamEvent ) ; virtual ~CProgramStreamMapper ( ) ; // given a buffer of transport stream packets, processes them virtual HRESULT Process ( IN IMediaSample * pIOwningMediaSample, IN BYTE * pbBuffer, IN int iBufferLength ) ; virtual HRESULT Initialize ( ) ; } ; // CStreamMapContext is the primary data structure that the mapper uses // for a stream's mapping; it contains all per-stream mappings, statistics, // etc... that is specific to one stream; if this data structure exists // there is a mapping for the stream class CStreamMapContext { LIST_ENTRY m_PayloadParsers ; DWORD m_dwStreamIdentifier ; CStreamMapper * m_pStreamMapper ; LONG m_cRef ; BOOL m_fDiscontinuity ; public : CStreamMapContext ( IN CStreamMapper * pStreamMapper, IN DWORD dwStreamIdentifier ) ; // removes its mapping from the mapper's stream filter virtual ~CStreamMapContext ( ) ; ULONG AddRef ( ) { return InterlockedIncrement (& m_cRef) ; } ULONG Release ( ) { if (InterlockedDecrement (& m_cRef) == 0) { delete this ; return 0 ; } return m_cRef ; } __inline void Process ( IN IMediaSample * pIOwningMediaSample, // owning media sample 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 ) ; HRESULT Add ( IN CDemuxBaseParser * pPayloadParser ) ; void Remove ( IN CDemuxBaseParser * pPayloadParser ) ; void SetDiscontinuity (IN BOOL f) { m_fDiscontinuity = f ; } DWORD GetStreamIdentifier () { return m_dwStreamIdentifier ; } } ; class CTransportStreamMapContext : public CStreamMapContext { BYTE m_bExpectedContinuityCounter ; // expected value, based on last processed public : CTransportStreamMapContext ( IN CStreamMapper * pStreamMapper, IN DWORD dwStreamIdentifier ) : CStreamMapContext (pStreamMapper, dwStreamIdentifier), m_bExpectedContinuityCounter (0xff) {} BYTE GetExpectedContinuityCounter () { return m_bExpectedContinuityCounter ; } void SetExpectedContinuityCounter (BYTE b) { m_bExpectedContinuityCounter = b ; } } ; class CProgramStreamMapContext : public CStreamMapContext { public : CProgramStreamMapContext ( IN CStreamMapper * pStreamMapper, IN DWORD dwStreamIdentifier ) : CStreamMapContext (pStreamMapper, dwStreamIdentifier) {} } ; #endif // __mp2demux_tsmapper_h