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