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