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