//------------------------------------------------------------------------------ // // 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: program.h Abstract: This module contains all the class declarations that are used to manage and track the content of one transport stream. Revision History: 27-Aug-1999 created 07-Jan-2000 added PSI parsing and change tracking to PAT/PMT depth 17-Apr-2000 added the PTS context methods to the IPTSConver interface Notes: Locking 2 assumptions are made: 1. parser processing is already serialized 2. if the content manager is reset, or reconfigured NOT via the parsers i.e. PSI updates, then the streams have been stopped by the caller Because of these assumptions, there is no explicit locking in the content manager or the program stream objects --*/ #ifndef __mp2demux_program_h #define __mp2demux_program_h #define SECTION_POOL_MAX 5 // these values cannot be the same as those defined in table 2-29 (MPEG-2 Systems // specification); the values in the table consist of the set [0x00, 0xff], so // any other value will never be found in the PSI and can be meaningful to this // program only #define STREAM_TYPE_PAT 0x80000000 #define STREAM_TYPE_PMT 0x40000000 #define STREAM_TYPE_PCR 0x20000000 // singular stream object represents 1 PID stream struct SINGULAR_STREAM { LIST_ENTRY ListEntry ; DWORD dwPID ; DWORD dwStreamType ; DWORD dwProgramNumber ; } ; // section buffer struct struct SECTION_BUFFER { DWORD dwSection ; // MPEG-2 PSI section_number DWORD dwVersion ; // MPEG-2 PSI version_number BYTE pbSection [PSI_MAX_SECTION_LENGTH_ROUNDED] ; } ; class CISectionEventCallback /*++ The purpose of this abstract class is to define a callback interface that provides a means for a CMPEG2PSISectionBufferSource to post events to that indicate events. --*/ { public : virtual HRESULT NewSectionBuffer ( IN SECTION_BUFFER * pNewSectionBuffer, IN SECTION_BUFFER * pOldSectionBuffer ) = 0 ; } ; class CIPCRValueNotify /*++ The purpose of this abstract class is to define a notification interface that the CPCRValue can callback into when a new PCR value is received. The clock will implement this interface --*/ { public : virtual HRESULT NewPCRValue ( IN LONGLONG llPCR, // PCR value IN BOOL fDiscontinuity, // TRUE if this PCR follows a discontinuity IN int iLastPCRByteOffset, // input buffer offset IN CTStickyVal * pReset // OUT: true if the demux needs a reset ) = 0 ; } ; class CIPTSConvert /*++ The purpose of this abstract class is to define the interface via which the PES parsers convert PTS time to dshow REFERENCE_TIME. --*/ { public : // get a new context to use virtual HRESULT GetNewPTSContext ( IN DWORD dwStreamIdentifier, OUT DWORD_PTR * pdwContext ) = 0 ; // recycle a context virtual void RecyclePTSContext ( IN DWORD_PTR dwContext ) = 0 ; // TRUE/FALSE if a virtual BOOL FirstPCRProcessed ( IN DWORD_PTR dwContext ) = 0 ; virtual void MediaSampleTimesFromPTS ( IN DWORD_PTR dwContext, // PTS context IN DWORD stream_id, // PES stream_id IN LONGLONG llPES_PTS, // PES value IN CTStickyVal * pReset, OUT REFERENCE_TIME * pStartTime, // return start time OUT REFERENCE_TIME * pStopTime, // return stop time OUT BOOL * pfPaddingAdjust ) = 0 ; } ; class CIAVStreamNotify /*++ This interface is used for AV stream mapping notification. The content manager uses to ensure that the correct PCR stream is used. --*/ { public : virtual HRESULT AVStreamMapped ( IN DWORD dwStreamIdentifier ) = 0 ; virtual void AVStreamUnmapped ( IN DWORD dwStreamIdentifier ) = 0 ; } ; class CPCRRecordBufferSource : public CBufferSource { CMpeg2Stats * m_pStats ; // stats object CIPCRValueNotify * m_pIPCRValueNotify ; // notification interface CRITICAL_SECTION m_crt ; PCR_RECORD m_PCR_RECORD ; void Lock_ () { EnterCriticalSection (& m_crt) ; } void Unlock_ () { LeaveCriticalSection (& m_crt) ; } public : CPCRRecordBufferSource ( IN CMpeg2Stats * pStats ) ; ~CPCRRecordBufferSource ( ) ; // registers a notification interface; if NULL, clears an existing // notification interface void RegisterIPCRValueNotify ( IN CIPCRValueNotify * pIPCRValueNotify ) ; // ------------------------------------------------------------------- // CBufferSource methods // gets a new buffer virtual HRESULT GetCopyBuffer ( OUT DWORD_PTR * pdwContext, OUT BYTE ** ppbBuffer, IN OUT int * piBufferLength ) ; // processes a completed buffer; this is a handoff virtual HRESULT CompleteCopyBuffer ( IN DWORD_PTR dwContext, IN BYTE * pbBuffer, IN int iBufferLength, IN BOOL fDiscontinuity, IN CTStickyVal * pReset // OUT: true if the demux needs a reset ) ; // recycles the buffer; only called when something has failed // and a buffer that was obtained via GetCopyBuffer is no longer // needed virtual HRESULT ReleaseCopyBuffer ( IN DWORD_PTR dwContext, IN BYTE * pbBuffer, IN int iBufferLength ) ; } ; class CPCRValue : public CPCRRecordBufferSource /*++ The purpose of this class is to collect PCR values and funnel those to the clock This object lives exactly as long as the content manager --*/ { DWORD m_dwPCRPID ; public : CPCRValue ( IN DWORD dwPCRPID, IN CMpeg2Stats * pStats ) : CPCRRecordBufferSource ( pStats ), m_dwPCRPID (dwPCRPID) {} DWORD GetPCRPID () { return m_dwPCRPID ; } } ; template < class T, // objects to store in this map int iAllocationUnitCount // allocation quantum > class StreamPIDMap : public TGenericMap < T, // storage type ULONG, // hash key type (PID) TRUE, // duplicates ARE allowed iAllocationUnitCount, // allocation quantum 19 // table size (fixed) > { public : virtual ULONG Value (IN ULONG HashKey) { return HashKey ; } } ; template < class T, // objects to store in this map int iAllocationUnitCount // allocation quantum > class StreamMap : public TGenericMap < T, // storage type ULONG, // hash key type (program_number or PID) FALSE, // duplicates NOT allowed iAllocationUnitCount, // allocation quantum 19 // table size (fixed) > { public : virtual ULONG Value (IN ULONG HashKey) { return HashKey ; } } ; class CMPEG2PSISectionBufferSource : public CBufferSource /*++ The purpose of this class is to provide a simple means of collecting and organizing MPEG-2 PSI sections. This class is connected to a parser gets sections that are filtered by the parser based on version_number. --*/ { CISectionEventCallback * m_pIEventCallback ; LIST_ENTRY m_PSISectionListHead ; // list of current sections TListEntryContainerPool m_PSISectionBufferPool ; // section buffer pool void InsertNewSection_ ( IN LIST_ENTRY_CONTAINER * pNew, OUT LIST_ENTRY_CONTAINER ** ppOld ) ; public : CMPEG2PSISectionBufferSource ( IN CMpeg2Stats * pStats, IN CISectionEventCallback * pICallback ) ; ~CMPEG2PSISectionBufferSource ( ) ; void Clear ( ) ; // ------------------------------------------------------------------- // CBufferSource methods // we must implement these because we are a buffer source; // since we are not refcounted, we return a fixed value virtual ULONG AddRef () { return 1 ; } virtual ULONG Release () { return 1 ; } // gets a new buffer virtual HRESULT GetCopyBuffer ( OUT DWORD_PTR * pdwContext, OUT BYTE ** ppbBuffer, IN OUT int * piBufferLength ) ; // processes a completed buffer; this is a handoff virtual HRESULT CompleteCopyBuffer ( IN DWORD_PTR dwContext, IN BYTE * pbBuffer, IN int iBufferLength, IN BOOL fDiscontinuity, IN CTStickyVal * pReset // OUT: true if the demux needs a reset ) ; // recycles the buffer; only called when something has failed // and a buffer that was obtained via GetCopyBuffer is no longer // needed virtual HRESULT ReleaseCopyBuffer ( IN DWORD_PTR dwContext, IN BYTE * pbBuffer, IN int iBufferLength ) ; HRESULT ReleaseSectionBuffer ( IN LIST_ENTRY_CONTAINER * pSectionBuffer ) ; } ; class CMpeg2StreamContentManager : public CIAVStreamNotify /*++ --*/ { protected : CMpeg2Stats * m_pStats ; CMPEG2Controller * m_pMPEG2Controller ; CMPEG2PushClock * m_pCMPEG2PushClock ; virtual void ClearConfig_ ( ) = 0 ; virtual HRESULT Initialize_ ( ) = 0 ; public : CMpeg2StreamContentManager ( IN CMPEG2Controller * pController, IN CMpeg2Stats * pStats, IN CMPEG2PushClock * pCMPEG2PushClock ) ; virtual ~CMpeg2StreamContentManager ( ) ; virtual HRESULT Reset ( ) { ClearConfig_ () ; return Initialize_ () ; } } ; class CPSContentManager : public CMpeg2StreamContentManager /*++ --*/ { CPCRRecordBufferSource * m_pSCR ; DWORD_PTR m_dwpSCRMapContext ; protected : virtual void ClearConfig_ ( ) ; virtual HRESULT Initialize_ ( ) ; public : CPSContentManager ( IN CMPEG2Controller * pController, IN CMpeg2Stats * pStats, IN CMPEG2PushClock * pCMPEG2PushClock, IN HKEY hkey, OUT HRESULT * pHr ) ; ~CPSContentManager ( ) ; // -------------------------------------------------------------------- // IAMStreamNofity virtual HRESULT AVStreamMapped ( IN DWORD dwStreamId ) ; virtual void AVStreamUnmapped ( IN DWORD dwStreamId ) ; } ; class CTSContentManager : public CMpeg2StreamContentManager, public CIPCRValueNotify, // PCR value notifications public CISectionEventCallback // used when new PAT sections are received /*++ The purpose of this class is to manage the content of one transport stream. It does this by receiving PAT sections, creating & deleting program objects based on the content of the PAT sections. It also receives notifications of PID updates from the program objects. These notifications update a table that is kept at this level, for mapped PID content in the stream (PIDs described in the PAT and PMT). --*/ { struct MAPPED_AV_STREAM { LIST_ENTRY ListEntry ; DWORD dwPID ; DWORD dwProgramNumber ; } ; enum { PID_DEF_ALLOC = 10, // allocation quantum for number PIDs described PROGRAM_ALLOC = 5, // allocation quantum to store stream maps AV_PID_MAP_ALLOC = 3 // AV streams mapped table } ; CMPEG2PSISectionBufferSource * m_pPATSectionBuffers ; // list of sections DWORD_PTR m_dwpPAT_PIDMapContext ; LIST_ENTRY m_SingularStreamFreePool ; BOOL m_fTrackPSI ; // registry settable SINGULAR_STREAM * m_pPATPSIStream ; // PAT PSI stream BOOL m_fAllPCRStreamsQuenched ; // TRUE if no PCR stream is being sent through DWORD m_dwPCRInUse ; ObjectPool m_AVStreamRefPool ; CRITICAL_SECTION m_crtAVStreamLock ; LIST_ENTRY m_MappedAVStreamQueue ; StreamMap m_Programs ; // valid programs; key: program_number StreamMap m_ScratchPrograms ; // empty except during PSI updates StreamPIDMap m_PIDsInPSI ; // maps PID -> DWORD_PTR for those PIDs that are described in PSI TSimpleVector m_PCRValues ; // 0th is always the one that is currently supplying // values to the clock void PCRStreamConfigLock_ () { EnterCriticalSection (& m_crtAVStreamLock) ; } void PCRStreamConfigUnlock_ () { LeaveCriticalSection (& m_crtAVStreamLock) ; } void MappedAVStreamPush_ ( IN MAPPED_AV_STREAM * ) ; void MappedAVStreamPop_ ( IN MAPPED_AV_STREAM * ) ; MAPPED_AV_STREAM * GetAVStreamHead_ ( ) ; MAPPED_AV_STREAM * MappedAVStreamFind_ ( IN DWORD dwPID ) ; BOOL MaybeSetNewPCRStream_ ( ) ; HRESULT NewSection_ ( IN SECTION_BUFFER * pNewSectionBuffer ) ; HRESULT ReplaceSection_ ( IN SECTION_BUFFER * pNewSectionBuffer, IN SECTION_BUFFER * pOldSectionBuffer ) ; HRESULT GetNewInitializedTSProgram_ ( IN DWORD dwProgramNumber, IN DWORD dwPID, OUT CTSProgram ** ppTSProgram ) ; protected : virtual void ClearConfig_ ( ) ; virtual HRESULT Initialize_ ( ) ; public : CTSContentManager ( IN CMPEG2Controller * pController, IN CMpeg2Stats * pStats, IN CMPEG2PushClock * pCMPEG2PushClock, IN HKEY hkey, OUT HRESULT * pHr ) ; ~CTSContentManager ( ) ; // ==================================================================== // -------------------------------------------------------------------- // CIPCRValueNotify virtual HRESULT NewPCRValue ( IN LONGLONG llPCR, // PCR value IN BOOL fDiscontinuity, // TRUE if this PCR follows a discontinuity IN int iLastPCRByteOffset, // input buffer offset IN CTStickyVal * pReset // OUT: true if the demux needs a reset ) ; // -------------------------------------------------------------------- // IAMStreamNofity // ** must serialize via receiver lock !! virtual HRESULT AVStreamMapped ( IN DWORD dwPID ) ; virtual void AVStreamUnmapped ( IN DWORD dwPID ) ; // ==================================================================== HRESULT MapPID ( IN DWORD dwPID, IN CBufferSource * pBufferSource, IN DWORD dwTypeParser, // MPEG2_PSI_PAT, MPEG2_PSI_CAT, MPEG2_PSI_PMT, ..; see mp2const.h IN LPVOID pvParserArg, // depends on the type of parser; can be NULL OUT DWORD_PTR * pdwpContext ) ; HRESULT UnmapPID ( IN DWORD dwPID, IN DWORD_PTR pdwpContext ) ; HRESULT RegisterKnownStream ( IN SINGULAR_STREAM * pStream ) ; HRESULT UnregisterKnownStream ( IN SINGULAR_STREAM * pStream ) ; HRESULT RegisterPCRValueSource ( IN CPCRValue * ) ; void UnregisterPCRValueSource ( IN CPCRValue * ) ; HRESULT GetNew ( OUT CTSProgram ** ) ; void Recycle ( IN CTSProgram * ) ; HRESULT GetNew ( OUT SINGULAR_STREAM ** ) ; void Recycle ( IN SINGULAR_STREAM * ) ; // ------------------------------------------------------------------- // CISectionEventCallback methods virtual HRESULT NewSectionBuffer ( IN SECTION_BUFFER * pNewSectionBuffer, IN SECTION_BUFFER * pOldSectionBuffer ) { if (pOldSectionBuffer) { return ReplaceSection_ ( pNewSectionBuffer, pOldSectionBuffer ) ; } else { return NewSection_ ( pNewSectionBuffer ) ; } } } ; class CTSProgram : public CISectionEventCallback /*++ The purpose of this class is to track changes in 1 program that is carried in a transport stream, and to manage a list of proxies that are being used by parsers. It gathers PMT sections in order to track changes in PSI. --*/ { enum { PID_UNDEFINED = -1, SUBSTREAM_ALLOC = 5 // allocation quantum in our Maps } ; CMpeg2Stats * m_pStats ; // stats object CMPEG2PSISectionBufferSource * m_pPMTSectionBuffers ; DWORD m_dwProgramNumber ; CTSContentManager * m_pContentManager ; DWORD_PTR m_dwpPMTPIDMapContext ; DWORD_PTR m_dwpPCRPIDMapContext ; CPCRValue * m_pCPCRValue ; DWORD m_dwPMTPID ; SINGULAR_STREAM m_PCRStream ; StreamMap m_Streams ; // valid streams; key: PID StreamMap m_ScratchStreams ; // empty except during a PSI update HRESULT CheckPCRPID_ ( IN SECTION_BUFFER * pNewSectionBuffer ) ; void ClearPCRReception_ ( ) ; HRESULT NewSection_ ( IN SECTION_BUFFER * pNewSectionBuffer ) ; HRESULT ReplaceSection_ ( IN SECTION_BUFFER * pNewSectionBuffer, IN SECTION_BUFFER * pOldSectionBuffer ) ; public : CTSProgram ( IN CMpeg2Stats * pStats, // stats IN CTSContentManager * pTSContentManager // content manager ) ; ~CTSProgram ( ) ; HRESULT Initialize ( IN DWORD dwProgramNumber, // program_number IN DWORD dwPMTPID // program_map_PID in PAT ) ; HRESULT Shutdown ( ) ; DWORD GetProgramNumber () { return m_dwProgramNumber ; } DWORD GetPMTPID () { ASSERT (m_dwPMTPID != PID_UNDEFINED) ; return m_dwPMTPID ; } CPCRValue * GetCPCRValue () { return m_pCPCRValue ; } CBufferSource * GetBufferSource ( ) { return m_pPMTSectionBuffers ; } // ------------------------------------------------------------------- // CISectionEventCallback methods virtual HRESULT NewSectionBuffer ( IN SECTION_BUFFER * pNewSectionBuffer, IN SECTION_BUFFER * pOldSectionBuffer ) { if (pOldSectionBuffer != NULL) { return ReplaceSection_ ( pNewSectionBuffer, pOldSectionBuffer ) ; } else { return NewSection_ ( pNewSectionBuffer ) ; } } } ; #endif // __mp2demux_program_h