//------------------------------------------------------------------------------
//
// 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
--*/
#include "precomp.h"
#include "mp2demux.h"
#include "mp2enum.h"
#include "tsstats.h"
#include "mp2seek.h"
#include "pin_out.h"
#include "filter.h"
#include "bufsrc.h"
#include "plparse.h"
#include "program.h"
#include "tsctrlr.h"
#include "tsmapper.h"
#include
//
// This table was generated with the polynomial 0x04c11db7. It is used to
// check the CRC on MPEG-2 PSI, as defined in H.222.0, Systems specification
//
static
ULONG
g_MPEG2_PSI_CRC_32_Lookup [] = {
0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f,
0x2f8ad6d6, 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9,
0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3,
0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5,
0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027,
0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c,
0x2e003dc5, 0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca,
0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08,
0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e,
0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044,
0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59,
0x608edb80, 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f,
0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5,
0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623,
0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601,
0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad,
0x81b02d74, 0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c,
0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e,
0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088,
0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12,
0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06,
0xa6322bdf, 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 } ;
static
BOOL
ConfirmMpeg2PSICRC_32 (
IN BYTE * pb,
IN ULONG ulLen
)
{
ULONG ulCRCAccum ;
ULONG i, j ;
ulCRCAccum = 0xffffffff ;
for (j = 0; j < ulLen; j++, pb++) {
i = (( ulCRCAccum >> 24) ^ (* pb)) & 0xff ;
ulCRCAccum = ((ulCRCAccum << 8 ) ^ g_MPEG2_PSI_CRC_32_Lookup [i]) ;
}
return ulCRCAccum == 0x00000000 ;
}
static
__inline
BOOL
IsMpeg2VideoSequenceHeaderCode (
IN BYTE * pb
)
{
// mpeg2-video sequence_header_code = 00 00 01 B3
// H.262.0, 6.3.3, field definition
return (IsStartCodePrefix (pb) &&
StartCode (pb) == MPEG2_SEQUENCE_HEADER_START_CODE) ;
}
static
BOOL
IsPESAudio (
IN DWORD stream_id
)
{
BOOL r ;
r = (stream_id >= STREAM_ID_AUDIO_STREAM_MIN &&
stream_id <= STREAM_ID_AUDIO_STREAM_MAX) ;
return r ;
}
// ---------------------------------------------------------------------------
// CBufferSourceManager
// ---------------------------------------------------------------------------
void
CBufferSourceManager::ResetForNext_ ()
{
O_TRACE_ENTER_0 (TEXT ("CBufferSourceManager::ResetForNext_ ()")) ;
m_pbStartBuffer = m_pbCurrent = NULL ;
m_fTimestampNext = FALSE ;
m_iBufferLength = 0 ;
}
HRESULT
CBufferSourceManager::CompleteCopyBuffer_ (
IN CTStickyVal * pReset
)
{
HRESULT hr ;
BOOL r ;
int iBufferLength ;
O_TRACE_ENTER_0 (TEXT ("CBufferSourceManager::CompleteCopyBuffer_ ()")) ;
ASSERT (HaveCopyBuffer_ ()) ;
iBufferLength = BufferUsed () ;
r = CheckCompletePacket (
NULL,
& m_pbStartBuffer,
& iBufferLength,
& m_fSetDiscontinuityNext,
& m_fTimestampNext,
(m_fTimestampNext ? & m_rtStart : NULL),
(m_fTimestampNext ? & m_rtStop : NULL)
) ;
if (r) {
// good buffer; send it
if (!m_fTimestampNext) {
hr = m_pBufferSource -> CompleteCopyBuffer (
m_dwpBufferContext,
m_pbStartBuffer,
iBufferLength,
m_fSetDiscontinuityNext,
pReset
) ;
}
else {
hr = m_pBufferSource -> CompleteCopyBuffer (
m_dwpBufferContext,
m_pbStartBuffer,
iBufferLength,
m_fSetDiscontinuityNext,
& m_rtStart,
& m_rtStop,
pReset
) ;
}
CompletionRetVal_ (hr) ;
}
else {
// don't fail the call because of this
hr = S_OK ;
}
m_pBufferSource -> ReleaseCopyBuffer (
m_dwpBufferContext,
m_pbStartBuffer,
m_iBufferLength
) ;
m_dwpBufferContext = 0 ;
ResetForNext_ () ;
return hr ;
}
// all or nothing; no partial copies
BOOL
CBufferSourceManager::Copy_ (
IN BYTE * pb,
IN int iLen,
OUT int * piRemaining
)
{
O_TRACE_ENTER_2 (TEXT ("CBufferSourceManager::Copy_ (%08xh, %08xh)"), pb, iLen) ;
ASSERT (HaveCopyBuffer_ ()) ;
(* piRemaining) = BufferRemaining_ () ;
if (iLen <= (* piRemaining)) {
// copy
CopyMemory (
m_pbCurrent,
pb,
iLen
) ;
// advance
(* piRemaining) -= iLen ;
m_pbCurrent += iLen ;
ASSERT ((* piRemaining) == BufferRemaining_ ()) ;
ASSERT (BufferUsed () <= m_iBufferLength) ;
return TRUE ;
}
else {
return FALSE ;
}
}
BOOL
CBufferSourceManager::Wrap (
IN IMediaSample * pIMS,
IN BYTE * pb,
IN int iLen,
IN CTStickyVal * pReset
)
{
HRESULT hr ;
BOOL r ;
O_TRACE_ENTER_0 (TEXT ("CBufferSourceManager::Wrap_ ()")) ;
r = CheckCompletePacket (
pIMS,
& pb,
& iLen,
& m_fSetDiscontinuityNext,
& m_fTimestampNext,
(m_fTimestampNext ? & m_rtStart : NULL),
(m_fTimestampNext ? & m_rtStop : NULL)
) ;
if (r) {
if (!m_fTimestampNext) {
hr = m_pBufferSource -> WrapAndComplete (
pIMS,
pb,
iLen,
m_fSetDiscontinuityNext,
pReset
) ;
}
else {
hr = m_pBufferSource -> WrapAndComplete (
pIMS,
pb,
iLen,
m_fSetDiscontinuityNext,
& m_rtStart,
& m_rtStop,
pReset
) ;
}
// only reset the discontinuity if we successfully completed
if (SUCCEEDED (hr)) {
m_fSetDiscontinuityNext = FALSE ;
}
}
else {
// drop this one
hr = S_OK ;
}
ResetForNext_ () ;
return (SUCCEEDED (hr) ? TRUE : FALSE) ;
}
HRESULT
CBufferSourceManager::GetCopyBuffer_ (
IN OUT int * piBufferLength
)
// E_UNEXPECTED if we already have a buffer
{
HRESULT hr ;
O_TRACE_ENTER_0 (TEXT ("CBufferSourceManager::GetCopyBuffer_ ()")) ;
if (!HaveCopyBuffer_ ()) {
hr = m_pBufferSource -> GetCopyBuffer (
& m_dwpBufferContext,
& m_pbStartBuffer,
piBufferLength
) ;
if (SUCCEEDED (hr)) {
m_pbCurrent = m_pbStartBuffer ;
m_iBufferLength = (* piBufferLength) ;
ASSERT (BufferRemaining_ () == (* piBufferLength)) ;
ASSERT (BufferUsed () == 0) ;
ASSERT (HaveCopyBuffer_ ()) ;
}
else {
TRACE_ERROR_0 (TEXT ("m_pBufferSource -> GetCopyBuffer () call failed")) ;
}
}
else {
hr = E_UNEXPECTED ;
}
return hr ;
}
HRESULT
CBufferSourceManager::SendEmptyPacket (
IN IMediaSample * pIMS,
IN BOOL fDiscontinuity
)
{
BYTE * pb ;
DWORD_PTR dwpContext ;
int iLen ;
HRESULT hr ;
CScratchMediaSample * pScratchMS ;
CTStickyVal Reset (TRUE) ;
O_TRACE_ENTER_1 (TEXT ("CBufferSourceManager::SendEmptyPacket (%08x)"), fDiscontinuity) ;
iLen = 0 ;
if (pIMS == NULL) {
hr = m_pBufferSource -> GetCopyBuffer (
& dwpContext,
& pb,
& iLen
) ;
if (SUCCEEDED (hr)) {
hr = m_pBufferSource -> CompleteCopyBuffer (
dwpContext,
pb,
0,
fDiscontinuity,
& Reset
) ;
}
}
else {
pScratchMS = new CScratchMediaSample ;
if (pScratchMS) {
hr = m_pBufferSource -> WrapAndComplete (
pScratchMS,
NULL,
0,
fDiscontinuity,
& Reset
) ;
pScratchMS -> Release () ;
}
else {
hr = E_OUTOFMEMORY ;
}
}
return hr ;
}
void
CBufferSourceManager::SetTimestampNext (
IN REFERENCE_TIME * prtStart,
IN REFERENCE_TIME * prtStop
)
{
O_TRACE_ENTER_0 (TEXT ("CBufferSourceManager::SetTimestampNext ()")) ;
m_fTimestampNext = TRUE ;
m_rtStart = (* prtStart) ;
m_rtStop = (* prtStop) ;
}
HRESULT
CBufferSourceManager::GetCopyBlockStartPointer (
OUT BYTE ** ppbStartBuffer
)
{
O_TRACE_ENTER_0 (TEXT ("CBufferSourceManager::GetCopyBlockStartPointer ()")) ;
if (HaveCopyBuffer_ ()) {
(* ppbStartBuffer) = m_pbStartBuffer ;
return S_OK ;
}
else {
return E_FAIL ;
}
}
HRESULT
CBufferSourceManager::Complete (
IN CTStickyVal * pReset
)
{
HRESULT hr ;
O_TRACE_ENTER_0 (TEXT ("CBufferSourceManager::Complete ()")) ;
TRACE_4 (LOG_TRACE, 4, TEXT ("[%08xh] CBufferSourceManager::Complete () : BufferUsed () = %d; BufferRemaining_ () = %d; HaveCopyBuffer_ () = %08xh"), this, BufferUsed (), BufferRemaining_ (), HaveCopyBuffer_ ()) ;
if (HaveCopyBuffer_ ()) {
hr = CompleteCopyBuffer_ (pReset) ;
}
else {
hr = E_UNEXPECTED ;
}
// only reset the discontinuity if we successfully completed
if (SUCCEEDED (hr)) {
m_fSetDiscontinuityNext = FALSE ;
}
// BUGBUG: part of comment that failing to complete a buffer
// should not still do away with it !!
ResetForNext_ () ;
return hr ;
}
BOOL
CBufferSourceManager::CopyBlock (
IN BYTE * pbBuffer,
IN int iBufferLength,
OUT int * piBufferRemaining
)
// all or nothing
{
O_TRACE_ENTER_0 (TEXT ("CBufferSourceManager::CopyBlock ()")) ;
if (HaveCopyBuffer_ ()) {
return Copy_ (pbBuffer, iBufferLength, piBufferRemaining) ;
}
else {
(* piBufferRemaining) = 0 ;
return FALSE ;
}
}
HRESULT
CBufferSourceManager::GetNewCopyBuffer (
IN OUT int * piBufferLength
)
{
O_TRACE_ENTER_0 (TEXT ("CBufferSourceManager::GetNewCopyBuffer ()")) ;
return GetCopyBuffer_ (piBufferLength) ;
}
void
CBufferSourceManager::AbortBuffer (
)
{
O_TRACE_ENTER_0 (TEXT ("CBufferSourceManager::AbortBuffer ()")) ;
TRACE_4 (LOG_TRACE, 4, TEXT ("[%08xh] CBufferSourceManager::AbortBuffer () : BufferUsed () = %d; BufferRemaining_ () = %d; HaveCopyBuffer_ () = %08xh"), this, BufferUsed (), BufferRemaining_ (), HaveCopyBuffer_ ()) ;
TRACE_3 (LOG_TRACE, 4, TEXT ("[%08xh] CBufferSourceManager::AbortBuffer () : start = %08xh; current = %08xh"), this, m_pbStartBuffer, m_pbCurrent) ;
if (HaveCopyBuffer_ ()) {
m_pStats -> Global_MediaSampleAbort (m_fKeepStats) ;
m_pStats -> Global_BytesAborted (BufferUsed (), m_fKeepStats) ;
m_pBufferSource -> ReleaseCopyBuffer (
m_dwpBufferContext,
m_pbStartBuffer,
m_iBufferLength
) ;
m_dwpBufferContext = 0 ;
}
ResetForNext_ () ;
SetDiscontinuityNext (TRUE) ;
}
// ---------------------------------------------------------------------------
// CStreamParser
// ---------------------------------------------------------------------------
BOOL
CStreamParser::CopyMax_ (
IN BYTE * pbBuffer,
IN int iBufferLength,
OUT int * piCollected,
IN CTStickyVal * pReset
)
{
BOOL r ;
int iBufferRemaining ;
HRESULT hr ;
int iCopyPart ;
int iRequest ;
O_TRACE_ENTER_0 (TEXT ("CStreamParser::CopyMax_ ()")) ;
// try for the whole buffer
r = CopyBlock (
pbBuffer,
iBufferLength,
& iBufferRemaining
) ;
if (r) {
// got the whole buffer
(* piCollected) = iBufferLength ;
}
else {
if (iBufferRemaining > 0) {
// copy as much as possible
iCopyPart = iBufferRemaining ;
}
else {
// we need to get a new buffer; we complete ours on the way
// out if they are full, so we know we don't have full one
iRequest = m_iRequestBufferLength ;
hr = GetNewCopyBuffer (
& iRequest
) ;
if (FAILED (hr)) {
// failed to get a buffer, abort
(* piCollected) = 0 ;
return FALSE ;
}
// we have a new buffer; might be able to copy entire thing
iCopyPart = MIN (iRequest, iBufferLength) ;
// reset the timeout
ResetTimer (GetTickCount ()) ;
}
ASSERT (iCopyPart > 0) ;
// try for what's left
r = CopyBlock (
pbBuffer,
iCopyPart,
& iBufferRemaining
) ;
if (r) {
(* piCollected) = iCopyPart ;
}
else {
(* piCollected) = 0 ;
return FALSE ;
}
}
// if the buffer is full, complete it
if (iBufferRemaining == 0) {
hr = Complete (pReset) ;
// BUGBUG: return code
}
return TRUE ;
}
void
CStreamParser::OnDiscontinuity (
)
{
Abort () ;
SetDiscontinuityNext (TRUE) ;
}
int
CStreamParser::WrapStream (
IN IMediaSample * pIMS,
IN BYTE * pbBuffer,
IN int iBufferLength,
IN CTStickyVal * pReset
)
// returns the total bytes successfully copied
{
BOOL r ;
r = Wrap (pIMS, pbBuffer, iBufferLength, pReset) ;
return (r ? iBufferLength : 0) ;
}
int
CStreamParser::CopyStream (
IN BYTE * pbBuffer,
IN int iBufferLength,
IN CTStickyVal * pReset
)
{
int iCollectedEach ;
int iCollectedTotal ;
BOOL r ;
O_TRACE_ENTER_2 (TEXT ("CStreamParser::CopyStream (%08xh, %08xh)"), pbBuffer, iBufferLength) ;
iCollectedTotal = 0 ;
r = TRUE ;
while (r &&
iBufferLength > 0) {
r = CopyMax_ (
pbBuffer,
iBufferLength,
& iCollectedEach,
pReset
) ;
iBufferLength -= iCollectedEach ;
pbBuffer += iCollectedEach ;
iCollectedTotal += iCollectedEach ;
}
return iCollectedTotal ;
}
CStreamParser::CStreamParser (
CBufferSource * pBufferSource,
CMpeg2Stats * pStats,
int iRequestBufferLength
) : CDemuxBaseParser (
TIMEOUT_NONE,
pStats,
pBufferSource -> ReportStats ()
),
CBufferSourceManager (
pBufferSource,
pStats,
pBufferSource -> ReportStats ()
),
m_pStats (pStats),
m_fKeepStats (pBufferSource -> ReportStats ()),
m_iRequestBufferLength (iRequestBufferLength)
{
TRACE_CONSTRUCTOR (TEXT ("CStreamParser")) ;
m_pStats -> AddRef () ;
}
CStreamParser::CStreamParser (
DWORD dwTimeout,
CBufferSource * pBufferSource,
CMpeg2Stats * pStats,
int iRequestBufferLength
) : CDemuxBaseParser (
dwTimeout,
pStats,
pBufferSource -> ReportStats ()
),
CBufferSourceManager (
pBufferSource,
pStats,
pBufferSource -> ReportStats ()
),
m_pStats (pStats),
m_fKeepStats (pBufferSource -> ReportStats ()),
m_iRequestBufferLength (iRequestBufferLength)
{
TRACE_CONSTRUCTOR (TEXT ("CStreamParser")) ;
m_pStats -> AddRef () ;
}
CStreamParser::~CStreamParser (
)
{
TRACE_DESTRUCTOR (TEXT ("CStreamParser")) ;
AbortBuffer () ;
m_pStats -> Release () ;
}
void
CStreamParser::Timeout (
IN CTStickyVal * pReset
)
{
O_TRACE_ENTER_0 (TEXT ("CStreamParser::Timeout ()")) ;
if (IsGathering ()) {
Complete (pReset) ;
}
}
// ---------------------------------------------------------------------------
// CCopyFrameParser
// ---------------------------------------------------------------------------
HRESULT
CCopyFrameParser::GetNewFrameBuffer (
IN IMediaSample * pIMediaSample
)
{
HRESULT hr ;
int iRequest ;
O_TRACE_ENTER_0 (TEXT ("CCopyFrameParser::GetNewFrameBuffer ()")) ;
iRequest = m_iMinFrameBuffer ;
hr = GetNewCopyBuffer (& iRequest) ;
if (SUCCEEDED (hr)) {
// make sure we have the minimum required
if (iRequest < m_iMinFrameBuffer) {
// we did not get what we needed; abort the buffer and
// return a failure
Abort () ;
hr = E_FAIL ;
}
}
return hr ;
}
BYTE *
CCopyFrameParser::GetFrameBufferStartPointer (
)
{
HRESULT hr ;
BYTE * pb ;
O_TRACE_ENTER_0 (TEXT ("CCopyFrameParser::GetFrameBufferStartPointer ()")) ;
hr = GetCopyBlockStartPointer (& pb) ;
ASSERT (SUCCEEDED (hr)) ;
return pb ;
}
BOOL
CCopyFrameParser::CopyFrameBytes (
IN BYTE * pbBuffer,
IN int iBufferLen
)
{
BOOL r ;
int iRemaining ;
O_TRACE_ENTER_2 (TEXT ("CCopyFrameParser::Collect (%08xh, %08xh)"), pbBuffer, iBufferLen) ;
r = CopyBlock (
pbBuffer,
iBufferLen,
& iRemaining
) ;
return r ;
}
CCopyFrameParser::CCopyFrameParser (
CBufferSource * pBufferSource,
CMpeg2Stats * pStats,
int iMinFrameBufferSize
) : CDemuxBaseParser (
TIMEOUT_NONE,
pStats,
pBufferSource -> ReportStats ()
),
CBufferSourceManager (
pBufferSource,
pStats,
pBufferSource -> ReportStats ()
),
m_pStats (pStats),
m_iMinFrameBuffer (iMinFrameBufferSize),
m_fKeepStats (pBufferSource -> ReportStats ())
{
TRACE_CONSTRUCTOR (TEXT ("CCopyFrameParser")) ;
m_pStats -> AddRef () ;
}
CCopyFrameParser::~CCopyFrameParser (
)
{
TRACE_DESTRUCTOR (TEXT ("CCopyFrameParser")) ;
AbortBuffer () ;
m_pStats -> Release () ;
}
// ---------------------------------------------------------------------------
// CMpeg2PESStreamParser
// ---------------------------------------------------------------------------
int
CMpeg2PESStreamParser::PESPayloadBytesRemaining_ (
)
{
O_TRACE_ENTER_0 (TEXT ("CMpeg2PESStreamParser::PESPayloadBytesRemaining ()")) ;
if (PES_packet_length == 0) {
// length is undefined
return INT_MAX ;
}
else {
return PES_PACKET_LENGTH_LAST_BYTE_OFFSET + PES_packet_length - m_iPESPacketBytesProcessed ;
}
}
void
CMpeg2PESStreamParser::ResetForNextPESPacket_ (
)
{
O_TRACE_ENTER_0 (TEXT ("CMpeg2PESStreamParser::ResetForNextPESPacket_ ()")) ;
// state
m_PESParserState = EMPTY ;
m_PESHeaderState = IN_HEADER_NEW ;
// counters
m_iPESPacketBytesProcessed = 0 ;
}
void
CMpeg2PESStreamParser::CompletionRetVal_ (
IN HRESULT hr
)
{
if (IsTimestamped_ () && SUCCEEDED (hr)) {
m_rtLastStart = StartPTS () ;
m_fFirstTimestampProcessed = TRUE ;
}
}
BOOL
CMpeg2PESStreamParser::CompletePESCollection_ (
IN IMediaSample * pIMS,
IN CTStickyVal * pReset
)
{
HRESULT hr ;
O_TRACE_ENTER_0 (TEXT ("CMpeg2PESStreamParser::CompletePESCollection ()")) ;
if (pIMS == NULL) {
// only complete if we're copying; if we're not, wrap and send
// operation is atomic
hr = Complete (pReset) ;
}
else {
hr = S_OK ;
}
ResetForNextPESPacket_ () ;
return SUCCEEDED (hr) ;
}
void
CMpeg2PESStreamParser::AbortPESCollection_ (
)
{
O_TRACE_ENTER_0 (TEXT ("CMpeg2PESStreamParser::AbortPESCollection ()")) ;
AbortBuffer () ;
ResetForNextPESPacket_ () ;
}
BOOL
CMpeg2PESStreamParser::ParseCommonPESHeader_ (
IN BYTE * pbBuffer
)
// pbBuffer points to first byte of PES header
{
O_TRACE_ENTER_0 (TEXT ("CMpeg2PESStreamParser::ParseCommonPESHeader ()")) ;
packet_start_code_prefix = PES_PACKET_START_CODE_PREFIX_VALUE (pbBuffer) ;
stream_id = PES_STREAM_ID_VALUE (pbBuffer) ;
PES_packet_length = PES_PACKET_LENGTH_VALUE (pbBuffer) ;
return packet_start_code_prefix == PES_START_CODE_PREFIX ;
}
BOOL
CMpeg2PESStreamParser::ParseTier1ReqHeader_ (
IN BYTE * pbBuffer
)
// pbBuffer points to first byte of PES header
{
O_TRACE_ENTER_0 (TEXT ("CMpeg2PESStreamParser::ParseTier1ReqHeader ()")) ;
ASSERT (IS_TIER1_STREAM (stream_id)) ;
// advance to tier1 headers
pbBuffer += PES_COMMON_HEADER_FIELD_LEN ;
// common header fields have already been parsed; commented out those
// that we don't care about
PES_scrambling_control = PES_SCRAMBLING_CONTROL_VALUE (pbBuffer) ;
PES_priority = PES_PRIORITY_BIT (pbBuffer) ;
data_alignment_indicator = PES_DATA_ALIGNMENT_INDICATOR_BIT (pbBuffer) ;
copyright = PES_COPYRIGHT_BIT (pbBuffer) ;
original_or_copy = PES_ORIGINAL_OR_COPY_BIT (pbBuffer) ;
PTS_DTS_flags = PES_PTS_DTS_FLAGS_VALUE (pbBuffer) ;
ESCR_flag = PES_ESCR_FLAG_BIT (pbBuffer) ;
ES_rate_flag = PES_ES_RATE_FLAG_BIT (pbBuffer) ;
DSM_trick_mode_flag = PES_DSM_TRICK_MODE_FLAG_BIT (pbBuffer) ;
additional_copy_info_flag = PES_ADDITIONAL_COPY_INFO_FLAG_BIT (pbBuffer) ;
PES_CRC_flag = PES_CRC_FLAG_BIT (pbBuffer) ;
PES_extension_flag = PES_EXTENSION_FLAG_BIT (pbBuffer) ;
PES_header_data_length = PES_PES_HEADER_DATA_LENGTH_VALUE (pbBuffer) ;
return TRUE ;
}
BOOL
CMpeg2PESStreamParser::ProcessBuffer_ (
IN IMediaSample * pIMediaSample,
IN BOOL fNewPayload,
IN MPEG2_SYS_BUFFER * pSysBuffer,
IN CTStickyVal * pReset
)
{
BOOL r ;
#ifndef UNDER_CE
HRESULT hr ;
#endif //UNDER_CE
int iCollected ;
BYTE * pbBuffer ;
int iBufferLength ;
int iDataOffsetRemaining ;
#ifndef UNDER_CE
CScratchMediaSample * pScratchMS ;
#endif //UNDER_CE
O_TRACE_ENTER_0 (TEXT ("CMpeg2PESStreamParser::ProcessBuffer_ ()")) ;
// init locals
r = TRUE ;
pbBuffer = pSysBuffer -> pbSysBufferPayload ;
iBufferLength = pSysBuffer -> iSysBufferPayloadLength ;
switch (m_PESParserState)
{
case EMPTY :
// only start to process if this is a new payload
if (!fNewPayload) {
TRACE_ERROR_0 (TEXT ("DROPPED (pes): waiting for payload_unit_start")) ;
m_pStats -> DroppedPacket (pSysBuffer, KeepStats_ ()) ;
break ;
}
// ------------------------------------------------------------
// EMPTY -> IN_HEADER state transition
m_PESParserState = IN_HEADER ;
PES_header_data_length = 0 ;
m_fPESHeaderParsed = FALSE ;
m_iPESPacketBytesProcessed = 0 ;
// fall through
case IN_HEADER :
r = ProcessPESHeader_ (
& pbBuffer,
& iBufferLength,
pReset
) ;
if (!r ||
!m_fPESHeaderParsed) {
NE_SPEW (r, TRUE, TEXT ("CMpeg2PESStreamParser::ProcessBuffer_ () : ProcessPESHeader_ () failed")) ;
break ;
}
// we are done processing the header; make sure this is not
// just a padding packet; if so, we reset and wait for the
// next PES packet
if (stream_id == STREAM_ID_PADDING_STREAM) {
ResetForNextPESPacket_ () ;
break ;
}
// still wait until first PCR is received
if (!m_fFirstTimestampProcessed && // haven't started yet
!m_pIPTSConvert -> FirstPCRProcessed (m_dwpPTSContext)) { // first PCR not yet seen
ResetForNextPESPacket_ () ;
// assume this gets NULLed by compiler when free bits are compiled
if (IS_PES_VIDEO (stream_id)) {
TRACE_ERROR_0 (TEXT ("DROPPED (video pes): waiting for first valid PCR")) ;
}
else {
TRACE_ERROR_0 (TEXT ("DROPPED (audio pes): waiting for first valid PCR")) ;
}
m_pStats -> DroppedPacket (pSysBuffer, KeepStats_ ()) ;
break ;
}
// ------------------------------------------------------------
// IN_HEADER -> IN_DATA_OFFSET state transition
m_PESParserState = IN_DATA_OFFSET ;
m_iDataOffsetSkipped = 0 ;
case IN_DATA_OFFSET :
if (m_dwSubstreamValue != SUBSTREAM_FILTER_VAL_NONE && // valid value to compare
m_iDataOffsetSkipped == 0 && // we're on the 0th byte
iBufferLength > 0) { // we have 0th byte to look at
if (pbBuffer [0] != (BYTE) (m_dwSubstreamValue & 0x000000ff)) {
// not the substream we want; wait for the next
ResetForNextPESPacket_ () ;
break ;
}
}
iDataOffsetRemaining = DataOffsetRemaining_ () ;
if (iBufferLength >= iDataOffsetRemaining) {
// outright skip over it
pbBuffer += iDataOffsetRemaining ;
iBufferLength -= iDataOffsetRemaining ;
}
else {
// skip over what we can; wait for next
m_iDataOffsetSkipped += iBufferLength ;
ASSERT (DataOffsetRemaining_ () > 0) ;
break ;
}
// ------------------------------------------------------------
// IN_DATA_OFFSET -> IN_PAYLOAD state transition
m_PESParserState = IN_PAYLOAD ;
case IN_PAYLOAD :
if (pIMediaSample == NULL) {
iCollected = CopyStream (
pbBuffer,
iBufferLength,
pReset
) ;
}
else {
// BUGBUG : don't ignore spanning header
iCollected = WrapStream (
pIMediaSample,
pbBuffer,
iBufferLength,
pReset
) ;
}
m_iPESPacketBytesProcessed += iCollected ;
if (iCollected == iBufferLength) {
r = TRUE ;
if (PESPayloadBytesRemaining_ () == 0 &&
IsGathering ()) {
r = CompletePESCollection_ (pIMediaSample, pReset) ;
}
}
else {
NE_SPEW (r, TRUE, TEXT ("CMpeg2PESStreamParser::ProcessBuffer_ () : WrapStream () failed")) ;
r = FALSE ;
}
break ;
} ;
return r ;
}
BOOL
CMpeg2PESStreamParser::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
)
{
if (!m_fFirstTimestampProcessed) {
// we're still waiting for our first timestamp
if ((* pfTimestampNext) &&
(* prtStart) != UNDEFINED) {
// found it; flag a discontinuity and try to send it out;
// m_fFirstTimestampProcessed is toggled upon successfull
// downstream transmission
(* pfDiscontinuity) = TRUE ;
if (IS_PES_VIDEO (stream_id)) {
m_pStats -> VideoPTSBase (prtStart, & m_rtVideoPTSOffset) ;
}
else {
m_pStats -> AudioPTSBase (prtStart, & m_rtAudioPTSOffset) ;
}
}
else {
// not timestamped; continue to wait
TRACE_0 (LOG_TRACE, 1, TEXT ("dropped data (CheckCompletePacketFailed)")) ;
ResetForNextPESPacket_ () ;
return FALSE ;
}
}
if (* pfTimestampNext) {
if (IS_PES_VIDEO (stream_id)) {
m_pStats -> VideoPTSOut () ;
// need to be 0-based, or as close to as possible, if in pull mode
(* prtStart) += m_rtVideoPTSOffset ;
(* prtStop) += m_rtVideoPTSOffset ;
}
else {
m_pStats -> AudioPTSOut () ;
// need to be 0-based, or as close to as possible, if in pull mode
(* prtStart) += m_rtAudioPTSOffset ;
(* prtStop) += m_rtAudioPTSOffset ;
}
}
return TRUE ;
}
void
CMpeg2PESStreamParser::OnDiscontinuity (
)
{
CTStickyVal Reset (TRUE) ;
if (IsGathering ()) {
Complete (& Reset) ;
}
//
// if completion failed, we'll abort what we're collecting; we ignore the
// completion return code deliberately
//
m_fWaitMinPTS = TRUE ;
m_fFirstTimestampProcessed = FALSE ;
CStreamParser::OnDiscontinuity () ;
}
void
CMpeg2PESStreamParser::Abort (
)
{
O_TRACE_ENTER_0 (TEXT ("CMpeg2PESStreamParser::Abort ()")) ;
AbortPESCollection_ () ;
}
void
CMpeg2PESStreamParser::Active (
IN BOOL fPullMode
)
{
O_TRACE_ENTER_0 (TEXT ("CMpeg2PESStreamParser::Active ()")) ;
Abort () ;
stream_id = UNDEFINED ;
m_fFirstTimestampProcessed = FALSE ;
m_rtLastStart = 0 ;
m_fWaitMinPTS = FALSE ;
m_fPullMode = fPullMode ;
if (m_fPullMode) {
// don't bias if we're in pull mode
m_rtAudioPTSOffset = 0 ;
m_rtVideoPTSOffset = 0 ;
}
else {
m_rtAudioPTSOffset = m_rtRegAudioPTSOffset ;
m_rtVideoPTSOffset = m_rtRegVideoPTSOffset ;
}
}
CMpeg2PESStreamParser::CMpeg2PESStreamParser (
CBufferSource * pBufferSource,
CIPTSConvert * pIPTSConvert,
DWORD dwStreamIdentifier,
CMpeg2Stats * pStats,
AUDIO_FILTER_INFO * pAudioFilterInfo, // can be NULL
HKEY hkey,
HRESULT * phr
) : CStreamParser (
TIMEOUT_MEDIUM,
pBufferSource,
pStats,
REQUEST_BUFFER_LENGTH
),
m_pIPTSConvert (pIPTSConvert),
m_pStats (pStats),
m_dwpPTSContext (0),
m_iDataOffset (0),
m_dwSubstreamValue (SUBSTREAM_FILTER_VAL_NONE),
m_fFirstTimestampProcessed (FALSE),
m_fCopyOutgoing (pBufferSource -> IsCopyBufferSource ()),
m_fPullMode (FALSE),
m_rtLastStart (0),
m_fWaitMinPTS (FALSE)
{
DWORD dw ;
LONG l ;
TRACE_CONSTRUCTOR (TEXT ("CMpeg2PESStreamParser")) ;
ASSERT (m_pIPTSConvert) ;
ASSERT (m_pStats) ;
m_pStats -> AddRef () ;
if (pAudioFilterInfo) {
// arbitrary value that we validate against
if (0 <= pAudioFilterInfo -> iDataOffset &&
pAudioFilterInfo -> iDataOffset <= 10) {
// if some high bits were set make sure they are valid
if (((pAudioFilterInfo -> dwSubstreamValue) & 0xffffff00) != 0x00000000) {
// only accept the 1 type for now
if (pAudioFilterInfo -> dwSubstreamValue != SUBSTREAM_FILTER_VAL_NONE) {
(* phr) = E_INVALIDARG ;
return ;
}
}
m_dwSubstreamValue = pAudioFilterInfo -> dwSubstreamValue ;
m_iDataOffset = pAudioFilterInfo -> iDataOffset ;
}
else {
(* phr) = E_INVALIDARG ;
return ;
}
}
// read the audio offset milliseconds; bound the value
dw = REG_DEFAULT_REG_AUDIO_PTS_OFFSET_MILLIS ;
l = RegGetValIfExist (
hkey,
REG_AUDIO_PTS_OFFSET_MILLIS,
TRUE,
(DWORD *) & dw
) ;
if (l == ERROR_SUCCESS) {
m_rtRegAudioPTSOffset = MillisToDShow (Min (dw, MAX_REG_AUDIO_PTS_OFFSET_MILLIS)) ;
}
else {
m_rtRegAudioPTSOffset = MillisToDShow (REG_DEFAULT_REG_AUDIO_PTS_OFFSET_MILLIS) ;
}
// read the video offset milliseconds; bound the value
dw = REG_DEFAULT_REG_VIDEO_PTS_OFFSET_MILLIS ;
l = RegGetValIfExist (
hkey,
REG_VIDEO_PTS_OFFSET_MILLIS,
TRUE,
(DWORD *) & dw
) ;
if (l == ERROR_SUCCESS) {
m_rtRegVideoPTSOffset = MillisToDShow (Min (dw, MAX_REG_VIDEO_PTS_OFFSET_MILLIS)) ;
}
else {
m_rtRegVideoPTSOffset = MillisToDShow (REG_DEFAULT_REG_VIDEO_PTS_OFFSET_MILLIS) ;
}
// wait for next PES packet
ResetForNextPESPacket_ () ;
(* phr) = m_pIPTSConvert -> GetNewPTSContext (
dwStreamIdentifier,
& m_dwpPTSContext
) ;
m_llTotalGathered = 0 ;
}
CMpeg2PESStreamParser::~CMpeg2PESStreamParser (
)
{
TRACE_DESTRUCTOR (TEXT ("CMpeg2PESStreamParser")) ;
m_pStats -> Release () ;
if (m_dwpPTSContext != 0) {
m_pIPTSConvert -> RecyclePTSContext (
m_dwpPTSContext
) ;
}
}
BOOL
CMpeg2PESStreamParser::ParseTier1OptHeader_ (
IN BYTE * pbBuffer,
IN CTStickyVal * pReset
)
// pbBuffer points to first byte of PES header
{
LONGLONG PTS ;
REFERENCE_TIME rtStart ;
REFERENCE_TIME rtStop ;
#ifndef UNDER_CE
BOOL fTimeDiscontinuity ;
BOOL fValidTimes ;
#endif //UNDER_CE
BOOL fPaddingAdjust ;
// Define the packet drop threshold to be 66ms.
static const REFERENCE_TIME rtPacketDropThreshold = MillisToDShow(66);
O_TRACE_ENTER_0 (TEXT ("CMpeg2PESStreamParser::ParseTier1OptHeader_ ()")) ;
ASSERT (IS_TIER1_STREAM (stream_id)) ;
ASSERT (PES_header_data_length > 0) ;
// we only care about the PTS field
if (PTSExists_ ()) {
// advance to it
pbBuffer += PES_TIER1_REQ_HEADER_FIELD_LEN ;
// and get the value
PTS = PES_PTS_VALUE (pbBuffer) ;
TRACE_3 (LOG_TIMING, 2, TEXT ("stream_id = %02x; PES PTS = %I64d (%I64d ms)"), stream_id, PTS, PTS / 90) ;
if (IS_PES_VIDEO (stream_id)) {
m_pStats -> VideoPESPTS (& PTS) ;
}
else {
m_pStats -> AudioPESPTS (& PTS) ;
}
// convert to dshow timestamps, and flag the next buffer
m_pIPTSConvert -> MediaSampleTimesFromPTS (
m_dwpPTSContext,
stream_id,
PTS,
pReset,
& rtStart,
& rtStop,
& fPaddingAdjust
) ;
if (m_fWaitMinPTS) {
// we must wait for a PTS that is >= the last-sent PTS; if we have
// a valid PTS, we compare to the last sent PTS; if it's > then
// we send this one out, else we drop the PES payload and wait
// for the next
if (rtStart != UNDEFINED) {
//
// we have a valid PTS; compare it
//
if (rtStart <= (m_rtLastStart - rtPacketDropThreshold)) {
// too early to resume sending; drop this and wait for
// next opportunity
ResetForNextPESPacket_ () ;
TRACE_4 (LOG_TRACE, 1, TEXT ("DROPPED: Start %I64d (%I64d ms) < %I64d (%I64d ms)"), rtStart, rtStart / 10000, m_rtLastStart, m_rtLastStart / 10000) ;
goto cleanup ;
}
else {
// we're done waiting; move on; flag first as discontinuity
// always
TRACE_4 (LOG_TRACE, 1, TEXT ("RESUMING: Start %I64d (%I64d ms) >= %I64d (%I64d ms)"), rtStart, rtStart / 10000, m_rtLastStart, m_rtLastStart / 10000) ;
m_fWaitMinPTS = FALSE ;
SetDiscontinuityNext (TRUE) ;
}
}
else {
//
// timestamp is not valid which probably means we're not yet
// receiving our PCR/SCRs; drop this packet
//
ResetForNextPESPacket_ () ;
TRACE_2 (LOG_TRACE, 1, TEXT ("DROPPED: Start UNDEFINED < %I64d (%I64d ms)"), m_rtLastStart, m_rtLastStart / 10000) ;
goto cleanup ;
}
}
if (fPaddingAdjust &&
IsPESAudio (stream_id)) {
// audio renderer will subordinate faster if adjustment is flagged as
// discontinuity
SetDiscontinuityNext (TRUE) ;
}
// want this timestamp on the next buffer sent out
SetTimestampNext (
& rtStart,
& rtStop
) ;
TRACE_3 (LOG_TRACE, 3, TEXT ("Start %016I64x,%I64u, %I64u ms"), rtStart, rtStart, rtStart / 10000) ;
}
cleanup :
return TRUE ;
}
BOOL
CMpeg2PESStreamParser::ProcessPESHeader_ (
IN OUT BYTE ** ppbBuffer,
IN OUT int * piBufferLen,
IN CTStickyVal * pReset
)
{
BOOL r ;
int iCopied ;
O_TRACE_ENTER_2 (
TEXT ("CMpeg2PESStreamParser::ProcessPESHeader (%08xh, %08xh)"),
(* ppbBuffer),
(* piBufferLen)
) ;
r = TRUE ;
switch (m_PESHeaderState)
{
case IN_HEADER_NEW :
// initialize
m_PESHeaderCache.Reset () ;
PES_header_data_length = 0 ;
m_PESHeaderState = IN_HEADER_COMMON ;
// and fall through
case IN_HEADER_COMMON :
iCopied = Min ((* piBufferLen), PES_COMMON_HEADER_FIELD_LEN - m_PESHeaderCache.CurCacheSize ()) ;
r = m_PESHeaderCache.Append (
(* ppbBuffer),
iCopied
) ;
if (!r) {
break ;
}
(* ppbBuffer) += iCopied ;
(* piBufferLen) -= iCopied ;
m_iPESPacketBytesProcessed += iCopied ;
if (m_PESHeaderCache.CurCacheSize () == PES_COMMON_HEADER_FIELD_LEN) {
r = ParseCommonPESHeader_ (
m_PESHeaderCache.Get ()
) ;
if (!r) {
// bad header
break ;
}
}
else {
// we still haven't cached enough
break ;
}
// common PES header has been parsed successfully, with no
// errors found;
// we may/may not continue; this depends if this is a tier_1
// PES header
if (!IS_TIER1_STREAM (stream_id)) {
// nope: we are done
m_fPESHeaderParsed = TRUE ;
break ;
}
// tier_1 header; required portion
m_PESHeaderState = IN_HEADER_TIER1_REQ ;
// fall through
case IN_HEADER_TIER1_REQ :
iCopied = Min ((* piBufferLen), PES_TIER1_REQ_HEADER_FIELD_LEN - m_PESHeaderCache.CurCacheSize ()) ;
r = m_PESHeaderCache.Append (
(* ppbBuffer),
iCopied
) ;
if (!r) {
break ;
}
(* ppbBuffer) += iCopied ;
(* piBufferLen) -= iCopied ;
m_iPESPacketBytesProcessed += iCopied ;
if (m_PESHeaderCache.CurCacheSize () == PES_TIER1_REQ_HEADER_FIELD_LEN) {
r = ParseTier1ReqHeader_ (
m_PESHeaderCache.Get ()
) ;
if (!r) {
// bad header
break ;
}
}
else {
// more to collect
break ;
}
// we may be done
if (PES_header_data_length == 0) {
// nothing follows; we are done parsing the header
m_fPESHeaderParsed = TRUE ;
break ;
}
// PES_header_data_len is not 0, so we have optional fields
// still left to parse
// tier1 header; optional portion
m_PESHeaderState = IN_HEADER_TIER1_OPT ;
// fall through
case IN_HEADER_TIER1_OPT :
iCopied = Min ((* piBufferLen), PES_TIER1_REQ_HEADER_FIELD_LEN + PES_header_data_length - m_PESHeaderCache.CurCacheSize ()) ;
r = m_PESHeaderCache.Append (
(* ppbBuffer),
iCopied
) ;
if (!r) {
break ;
}
(* ppbBuffer) += iCopied ;
(* piBufferLen) -= iCopied ;
m_iPESPacketBytesProcessed += iCopied ;
if (m_PESHeaderCache.CurCacheSize () == PES_TIER1_REQ_HEADER_FIELD_LEN + PES_header_data_length) {
// we have enough to parse; do so now
r = ParseTier1OptHeader_ (
m_PESHeaderCache.Get (),
pReset
) ;
// we're done parsing regardless
m_fPESHeaderParsed = r ;
}
break ;
} ;
return r ;
}
HRESULT
CMpeg2PESStreamParser::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
)
{
BOOL r ;
O_TRACE_ENTER_1 (
TEXT ("CMpeg2PESStreamParser::ProcessSysBuffer (%08xh)"),
pIMediaSample
) ;
m_pStats -> MappedPacket (pSysBuffer, KeepStats_ ()) ;
m_pStats -> Global_MappedPacketPES (KeepStats_ ()) ;
// we need a payload to work with
if (pSysBuffer -> iSysBufferPayloadLength == 0) {
return S_OK ;
}
if (fDiscontinuity ||
fPacketError ) {
// complete what we may have gathered so far
AbortPESCollection_ () ;
}
if (fNewPayload) {
CompletePESCollection_ ((m_fCopyOutgoing ? NULL : pIMediaSample), pReset) ;
}
r = ProcessBuffer_ (
(m_fCopyOutgoing ? NULL : pIMediaSample),
fNewPayload,
pSysBuffer,
pReset
) ;
if (r) {
return S_OK ;
}
else {
// an error occured; abort what we may have; update stats
m_pStats -> Global_MediaSampleAbort (KeepStats_ ()) ;
m_pStats -> Global_BytesAborted (BufferUsed (), KeepStats_ ()) ;
m_pStats -> DroppedPacket (pSysBuffer, KeepStats_ ()) ;
TRACE_ERROR_0 (TEXT ("DROPPED (pes): ProcessBuffer_ () failed")) ;
AbortPESCollection_ () ;
return E_FAIL ;
}
}
// ---------------------------------------------------------------------------
// CMpeg2PSIParse
// ---------------------------------------------------------------------------
void
CMpeg2PSIParse::ResetForNextSection_ (
)
{
O_TRACE_ENTER_0 (TEXT ("CMpeg2PSIParse::ResetForNextSection_ ()")) ;
// reset state and properties to start over
m_PSIParserState = EMPTY ;
m_iTotalSectionBytesCollected = 0 ;
m_iSectionTotalLength = 0 ;
}
int
CMpeg2PSIParse::PSIHeaderBytesRemaining_ (
)
{
O_TRACE_ENTER_0 (TEXT ("CMpeg2PSIParse::PSIHeaderBytesRemaining_ ()")) ;
// make sure we don't underflow if we read in a header that looked good
// but wasn't
return Max (m_iHeaderSize - m_iTotalSectionBytesCollected, 0) ;
}
int
CMpeg2PSIParse::SectionBytesRemaining_ (
)
{
O_TRACE_ENTER_0 (TEXT ("CMpeg2PSIParse::SectionBytesRemaining_ ()")) ;
// make sure we don't underflow if we read in a header that looked good
// but wasn't
return Max (m_iSectionTotalLength - m_iTotalSectionBytesCollected, 0) ;
}
void
CMpeg2PSIParse::AbortPSICollection_ (
)
{
O_TRACE_ENTER_0 (TEXT ("CMpeg2PSIParse::AbortPSICollection_ ()")) ;
// abort the buffer collection
AbortBuffer () ;
ResetForNextSection_ () ;
PSICurSectionAborted () ;
SetDiscontinuityNext (TRUE) ;
}
HRESULT
CMpeg2PSIParse::CompletePSICollection_ (
IN CTStickyVal * pReset
)
{
HRESULT hr ;
O_TRACE_ENTER_0 (TEXT ("CMpeg2PSIParse::CompletePSICollection_ ()")) ;
ASSERT (SectionBytesRemaining_ () == 0) ;
ASSERT (m_iTotalSectionBytesCollected > 0) ;
ASSERT (m_PSIParserState == IN_PAYLOAD) ;
hr = Complete (pReset) ;
PSICurSectionCompleted (hr) ;
ResetForNextSection_ () ;
return hr ;
}
BOOL
CMpeg2PSIParse::PointerField_ (
IN BYTE * pbSection,
IN int iBufferLen,
OUT BYTE * ppointer_field
)
{
O_TRACE_ENTER_0 (TEXT ("CMpeg2PSIParse::PointerField_ ()")) ;
(* ppointer_field) = BYTE_VALUE (pbSection,0) ;
return VALID_POINTER_FIELD ((* ppointer_field), (DWORD) (iBufferLen)) ;
}
BOOL
CMpeg2PSIParse::ProcessBuffer_ (
IN OUT BYTE ** ppbBuffer,
IN OUT int * piBufferLen,
IN CTStickyVal * pReset
)
{
BOOL r ;
HRESULT hr ;
int iCollect ;
int iSkip ;
BOOL fTableIdGood ;
O_TRACE_ENTER_0 (TEXT ("CMpeg2PSIParse::ProcessBuffer_ ()")) ;
switch (m_PSIParserState)
{
case EMPTY :
// update state information
m_PSIParserState = IN_HEADER ;
// fall through
case IN_HEADER :
ASSERT (PSIHeaderBytesRemaining_ () > 0) ;
// if we've collected nothing and can validate the header
// without collecting it (and then having to abort everything
// if the header check fails; such as a version check)
if (m_iTotalSectionBytesCollected == 0 &&
(* piBufferLen) >= m_iHeaderSize) {
// check it out first
r = CheckHeader (
(* ppbBuffer),
m_iHeaderSize,
& m_iSectionTotalLength,
& fTableIdGood
) ;
if (r) {
// the header is good; if it's not, we discard the entire
// transport packet, since we cannot trust the length
// field
// check if the table_id is good
// check the version
// check the stream ID; default returns TRUE
// if any are not, we discard this specific PSI section and
// try again with the next
if (!fTableIdGood ||
!IsNewVersion () ||
!CheckStreamId ((* ppbBuffer), m_iHeaderSize)) {
// skip ahead; we know the length is valid or we'd have
// bailed above
iSkip = Min ((* piBufferLen), SectionBytesRemaining_ ()) ;
(* ppbBuffer) += iSkip ;
(* piBufferLen) -= iSkip ;
// not a new version, or wrong substream, or wrong table_id;
// drop this one on the floor
AbortPSICollection_ () ;
break ;
}
// get a buffer to copy to
hr = GetNewFrameBuffer () ;
if (SUCCEEDED (hr)) {
r = CopyFrameBytes (
(* ppbBuffer),
m_iHeaderSize
) ;
if (r) {
// we have the whole header
m_iTotalSectionBytesCollected = m_iHeaderSize ;
(* ppbBuffer) += m_iHeaderSize ;
(* piBufferLen) -= m_iHeaderSize ;
}
}
else {
// failed to get a new frame buffer
r = FALSE ;
TRACE_ERROR_0 (TEXT ("CMpeg2PSIParse::ProcessBuffer_ () : needed a copy buffer for 1st part of header and failed to obtain 1")) ;
}
}
}
else {
// we either have a partial header, or we have a
// partially collected header from the last pass
iCollect = Min ((* piBufferLen), PSIHeaderBytesRemaining_ ()) ;
// if we're going to start collecting and don't have any buffer to
// collect into, do so now
if (iCollect > 0 &&
m_iTotalSectionBytesCollected == 0) {
hr = GetNewFrameBuffer () ;
}
else {
hr = S_OK ;
}
if (SUCCEEDED (hr)) {
r = CopyFrameBytes (
(* ppbBuffer),
iCollect
) ;
if (r) {
m_iTotalSectionBytesCollected += iCollect ;
(* ppbBuffer) += iCollect ;
(* piBufferLen) -= iCollect ;
// if we have everything, check it
if (PSIHeaderBytesRemaining_ () == 0) {
r = CheckHeader (
GetFrameBufferStartPointer (),
m_iHeaderSize,
& m_iSectionTotalLength,
& fTableIdGood
) ;
if (r) {
// header is good (length at the very least)
// check if the table_id is good
// check the version
// check the stream ID; default returns TRUE
// if any are not, we discard this specific PSI section and
// try again with the next
if (!fTableIdGood ||
!IsNewVersion () ||
!CheckStreamId (GetFrameBufferStartPointer (), m_iHeaderSize)) {
// skip ahead; we know the length is valid or we'd have
// bailed above
iSkip = Min ((* piBufferLen), SectionBytesRemaining_ ()) ;
(* ppbBuffer) += iSkip ;
(* piBufferLen) -= iSkip ;
// not a new version, or wrong substream, or wrong table_id; drop
// this one on the floor
AbortPSICollection_ () ;
break ;
}
}
}
}
}
else {
// failed to obtain a buffer when we needed one
r = FALSE ;
TRACE_ERROR_0 (TEXT ("CMpeg2PSIParse::ProcessBuffer_ () : needed a copy buffer for 1st part of header and failed to obtain 1")) ;
}
}
// if anything failed above; abort
if (!r) {
TRACE_ERROR_0 (TEXT ("CMpeg2PSIParse::ProcessBuffer_ () : aborting from the PSI header")) ;
AbortPSICollection_ () ;
break ;
}
// if we haven't collected the entire header yet, break
if (PSIHeaderBytesRemaining_ () > 0) {
ASSERT ((* piBufferLen) == 0) ;
break ;
}
//
// otherwise we have an entire header
//
// --------------------------------------------------------
// --------------------------------------------------------
// IN_HEADER -> IN_PAYLOAD state transition
m_PSIParserState = IN_PAYLOAD ;
ASSERT (m_iTotalSectionBytesCollected == m_iHeaderSize) ;
// fall through
case IN_PAYLOAD :
iCollect = Min ((* piBufferLen), SectionBytesRemaining_ ()) ;
r = CopyFrameBytes (
(* ppbBuffer),
iCollect
) ;
if (r) {
m_iTotalSectionBytesCollected += iCollect ;
(* ppbBuffer) += iCollect ;
(* piBufferLen) -= iCollect ;
if (SectionBytesRemaining_ () == 0) {
r = CheckCompleteSection (GetFrameBufferStartPointer (), BufferUsed ()) ;
if (r) {
hr = CompletePSICollection_ (pReset) ;
r = SUCCEEDED (hr) ;
}
}
}
else {
TRACE_ERROR_0 (TEXT ("CMpeg2PSIParse::ProcessBuffer_ () : aborting; an error occured while processing the PSI payload")) ;
AbortPSICollection_ () ;
}
break ;
} ;
return r ;
}
CMpeg2PSIParse::CMpeg2PSIParse (
CBufferSource * pBufferSource,
CMpeg2Stats * pStats,
int iHeaderSize
) : CCopyFrameParser (
pBufferSource,
pStats,
REQUEST_BUFFER_LENGTH
),
m_iHeaderSize (iHeaderSize)
{
TRACE_CONSTRUCTOR (TEXT ("CMpeg2PSIParse")) ;
ResetForNextSection_ () ;
}
CMpeg2PSIParse::~CMpeg2PSIParse (
)
{
TRACE_DESTRUCTOR (TEXT ("CMpeg2PSIParse")) ;
}
HRESULT
CMpeg2PSIParse::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
)
{
BOOL r ;
BYTE * pbBuffer ;
int iBufferLen ;
#ifndef UNDER_CE
int iEachSection ;
#endif //UNDER_CE
BYTE pointer_field ;
O_TRACE_ENTER_0 (TEXT ("CMpeg2PSIParse::ProcessSysBuffer_ ()")) ;
m_pStats -> MappedPacket (pSysBuffer, KeepStats_ ()) ;
m_pStats -> MappedPacketPSI (KeepStats_ ()) ;
// we need a payload to work with; we would never expect this
// but don't leave it in as an ASSERT
if (pSysBuffer -> iSysBufferPayloadLength == 0) {
return S_OK ;
}
// either of these conditions cause us to abort the collection
if (fDiscontinuity ||
fPacketError) {
AbortPSICollection_ () ;
}
if (fNewPayload) {
// a new payload implies a pointer_field
// get its value
r = PointerField_ (
pSysBuffer -> pbSysBufferPayload,
pSysBuffer -> iSysBufferPayloadLength,
& pointer_field
) ;
if (!r) {
AbortPSICollection_ () ;
TRACE_ERROR_0 (TEXT ("DROPPED (psi): pointer_field not found")) ;
m_pStats -> DroppedPacket (pSysBuffer, KeepStats_ ()) ;
return E_FAIL ;
}
// if we're collecting
if (StateCollecting_ ()) {
// and the pointer_field encloses something
if (pointer_field > 0) {
// set the buffer pointer and length so we process just
// what's enclosed by the pointer_field
pbBuffer = pSysBuffer -> pbSysBufferPayload + 1 ;
iBufferLen = pointer_field ;
// per h.222.0 the pointer_field offsets to the
// FIRST new section, so we know we at most have
// have 1 to complete;
ProcessBuffer_ (
& pbBuffer,
& iBufferLen,
pReset
) ;
if (StateCollecting_ ()) {
AbortPSICollection_ () ;
}
// everything that was enclosed in the pointer_field
// bracket has now been processed
}
else {
// something strange is going on; abort what we've collected
// to now but don't fail all the way out; what's left may
// be good
AbortPSICollection_ () ;
}
}
// set the buffer pointer and length, consider the pointer_field
pbBuffer = pSysBuffer -> pbSysBufferPayload + pointer_field + 1 ;
iBufferLen = pSysBuffer -> iSysBufferPayloadLength - pointer_field - 1 ;
}
else if (StateCollecting_ ()) {
// there's no pointer_field; we'll start with the first byte
// of the payload
pbBuffer = pSysBuffer -> pbSysBufferPayload ;
iBufferLen = pSysBuffer -> iSysBufferPayloadLength ;
}
else {
// we're not collecting and it's not a new payload; drop the packet
m_pStats -> DroppedPacket (pSysBuffer, KeepStats_ ()) ;
TRACE_ERROR_0 (TEXT ("DROPPED (psi): waiting for payload_unit_start")) ;
return S_OK ;
}
// now process everything we can
do {
r = ProcessBuffer_ (
& pbBuffer,
& iBufferLen,
pReset
) ;
} while (r && // no errors
iBufferLen > 0 && // we have something to look at
(* pbBuffer) != FORBIDDEN_TABLE_ID) ; // table_id != 0xff
if (r) {
return S_OK ;
}
else {
m_pStats -> DroppedPacket (pSysBuffer, KeepStats_ ()) ;
TRACE_ERROR_0 (TEXT ("DROPPED (psi): ProcessBuffer_ () error")) ;
return E_FAIL ;
}
}
void
CMpeg2PSIParse::Abort (
)
{
O_TRACE_ENTER_0 (TEXT ("CMpeg2PESStreamParser::Abort ()")) ;
AbortPSICollection_ () ;
}
// ----------------------------------------------------------------------------
// CMpeg2VersionFilteredPSIParse
// ----------------------------------------------------------------------------
BOOL
CMpeg2VersionFilteredPSIParse::CheckCompleteSection (
IN BYTE * pbSection,
IN int iSectionLength
)
{
O_TRACE_ENTER_2 (
TEXT ("CMpeg2VersionFilteredPSIParse::CheckCompleteSection (%08xh, %08xh)"),
pbSection,
iSectionLength
) ;
return ConfirmMpeg2PSICRC_32 (
pbSection,
iSectionLength
) ;
}
// ---------------------------------------------------------------------------
// CMpeg2PCRParser
// ---------------------------------------------------------------------------
HRESULT
CMpeg2PCRParser::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
)
{
HRESULT hr ;
#ifndef UNDER_CE
int iCollected ;
#endif // UNDER_CE
MPEG2_TRANSPORT_HEADER * pMPEG2TransportHeader ;
PCR_RECORD PCRRecord ;
BOOL r ;
O_TRACE_ENTER_0 (
TEXT ("CMpeg2PCRParser::ProcessSysBuffer ()")
) ;
ASSERT (pSysBuffer) ;
pMPEG2TransportHeader = static_cast (pSysBuffer) ;
if (TS_IS_PCR_PACKET (pMPEG2TransportHeader) &&
!fPacketError) {
// collect the PCR
ExtractPCR_ (
pMPEG2TransportHeader,
& PCRRecord
) ;
// compute last PCR field offset as follows
// iPacketLastByteOffset
// - 188 // previous packet last byte
// + 4 // transport header fixed fields
// + 2 // adaptation field fixed fields
// + 6 // to byte after program_clock_reference_extension
// - 1 // back to last byte of program_clock_reference_extension
PCRRecord.iLastPCRByteOffset = iPacketLastByteOffset - 188 + 4 + 2 + 6 - 1 ;
// we now get a new buffer, copy into it, and complete the buffer
hr = GetNewFrameBuffer () ;
if (SUCCEEDED (hr)) {
r = CopyFrameBytes (
reinterpret_cast (& PCRRecord),
sizeof PCR_RECORD
) ;
if (r) {
Complete (pReset) ;
}
else {
AbortBuffer () ;
hr = E_FAIL ;
}
}
}
else {
hr = S_OK ;
}
return hr ;
}
// ---------------------------------------------------------------------------
// CMpeg2SCRParser
// ---------------------------------------------------------------------------
HRESULT
CMpeg2SCRParser::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
)
{
HRESULT hr ;
#ifndef UNDER_CE
int iCollected ;
#endif // UNDER_CE
PCR_RECORD PCRRecord ;
BOOL r ;
O_TRACE_ENTER_0 (
TEXT ("CMpeg2SCRParser::ProcessSysBuffer ()")
) ;
if (fDiscontinuity ||
fNewPayload) {
m_dwState = STATE_WAIT_NEXT ;
}
r = TRUE ;
switch (m_dwState) {
case STATE_WAIT_NEXT :
if (!fNewPayload) {
break ;
}
// transition
m_Cache.Reset () ;
m_dwState = STATE_COLLECTING ;
// fall through
case STATE_COLLECTING :
if (m_Cache.CurCacheSize () == 0) {
if (pSysBuffer -> iSysBufferLength >= PACK_HEADER_SCR_LEN) {
ExtractSCR_ (
pSysBuffer -> pbSysBuffer,
& PCRRecord
) ;
}
else {
r = m_Cache.Append (
pSysBuffer -> pbSysBuffer,
pSysBuffer -> iSysBufferLength
) ;
break ;
}
}
else {
r = m_Cache.Append (
pSysBuffer -> pbSysBuffer,
Min (pSysBuffer -> iSysBufferLength, PACK_HEADER_SCR_LEN - m_Cache.CurCacheSize ())
) ;
if (r &&
m_Cache.CurCacheSize () == PACK_HEADER_SCR_LEN) {
ExtractSCR_ (
m_Cache.Get (),
& PCRRecord
) ;
}
else {
break ;
}
}
// we're here because the header was successfully parsed
hr = GetNewFrameBuffer () ;
if (SUCCEEDED (hr)) {
r = CopyFrameBytes (
reinterpret_cast (& PCRRecord),
sizeof PCR_RECORD
) ;
if (r) {
Complete (pReset) ;
}
else {
AbortBuffer () ;
hr = E_FAIL ;
}
}
r = (SUCCEEDED (hr) ? TRUE : FALSE) ;
m_dwState = STATE_WAIT_NEXT ;
} ;
if (!r) {
m_dwState = STATE_WAIT_NEXT ;
}
return (r ? S_OK : E_FAIL) ;
}
// ----------------------------------------------------------------------------
// CMpeg2AnalogCopyProtectionParser
// ----------------------------------------------------------------------------
CMpeg2AnalogCopyProtectionParser::CMpeg2AnalogCopyProtectionParser (
CBufferSource * pBufferSource,
CMpeg2Stats * pStats
) : CCopyFrameParser (pBufferSource,
pStats,
sizeof ANALOGCOPYPROTECTION_RECORD
),
m_dwACPLast (UNDEFINED)
{
m_Cache.Reset () ;
}
HRESULT
CMpeg2AnalogCopyProtectionParser::ProcessPESPacket_ (
IN BYTE * pbPESPacket,
IN CTStickyVal * pReset
)
{
HRESULT hr ;
DWORD dwCurACP ;
BOOL r ;
ASSERT (PES_PACKET_LENGTH_IS_MIN_VALID_ANALOGCOPYPROTECTION (pbPESPacket)) ;
ASSERT (PES_STREAM_ID_VALUE (pbPESPacket) == STREAM_ID_ANALOGCOPYPROTECTION) ;
hr = S_OK ;
// now confirm the substream; if not the right one, we just drop the packet
if (SUBSTREAMID_IS_ANALOGCOPYPROTECTION (pbPESPacket)) {
dwCurACP = PES_ACP_BITS (pbPESPacket) ;
if (dwCurACP != m_dwACPLast) {
// new bits; process them
hr = GetNewFrameBuffer () ;
if (SUCCEEDED (hr)) {
m_ACPRecord.dwACPBits = dwCurACP ;
r = CopyFrameBytes (
reinterpret_cast (& m_ACPRecord),
sizeof m_ACPRecord
) ;
if (r) {
Complete (pReset) ;
// don't process again unless they change
m_dwACPLast = dwCurACP ;
}
else {
AbortBuffer () ;
hr = E_FAIL ;
}
}
}
}
return hr ;
}
HRESULT
CMpeg2AnalogCopyProtectionParser::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
)
{
HRESULT hr ;
int iCopy ;
BOOL r ;
#ifndef UNDER_CE
DWORD PES_packet_length ;
#endif // UNDER_CE
hr = S_OK ;
// reset our cache if we're a new payload, or if there's been a
// discontinuity
if (fNewPayload ||
fDiscontinuity) {
m_Cache.Reset () ;
}
// don't process unless it makes sense to
if ((fNewPayload && m_Cache.IsEmpty ()) || // starting a new PES packet
(!fNewPayload && m_Cache.CurCacheSize () > 0) // continuing an unfinished packet
) {
// copy as much as possible into our cache
iCopy = ::Min (
pSysBuffer -> iSysBufferPayloadLength,
MIN_VALID_ANALOGCOPYPROTECTION_PES_PACKET_LENGTH - m_Cache.CurCacheSize ()
) ;
r = m_Cache.Append (pSysBuffer -> pbSysBufferPayload, iCopy) ;
ASSERT (r) ;
//
// always parse out of the cache (guarantees contiguous data)
//
// we might have enough to check the entire packet outright
if (m_Cache.CurCacheSize () >= MIN_VALID_ANALOGCOPYPROTECTION_PES_PACKET_LENGTH) {
// we've got enough so the cache holds everything we (might) need
hr = ProcessPESPacket_ (
m_Cache.Get (),
pReset
) ;
// done regardless of success or failure
m_Cache.Reset () ;
}
// or we might have enough to check the PES_packet_length field at least
else if (m_Cache.CurCacheSize () >= PES_COMMON_HEADER_FIELD_LEN) {
if (!PES_PACKET_LENGTH_IS_MIN_VALID_ANALOGCOPYPROTECTION (m_Cache.Get ())) {
// PES_packet_length value indicates that this packet will never be
// long enough to be a analog copy protection packet; punt; this isn't
// a failure, it's just not what we need
m_Cache.Reset () ;
}
}
}
else {
m_Cache.Reset () ;
}
return hr ;
}
void
CMpeg2AnalogCopyProtectionParser::Active (
IN BOOL fPullMode
)
{
m_dwACPLast = UNDEFINED ;
CCopyFrameParser::Active (fPullMode) ;
}