//------------------------------------------------------------------------------
//
// 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:
filter.cpp
Abstract:
This module contains the filter framework code - pin management,
connection management, etc... -- CBaseFilter method overrides.
Revision History:
02-Jul-1999 created
--*/
#include "precomp.h"
#include "mp2demux.h"
#include "mp2enum.h"
#include "tsstats.h"
#include "mp2seek.h"
#include "pin_out.h"
#include "bufsrc.h"
#include "plparse.h"
#include "program.h"
#include "clock.h"
#include "tsctrlr.h"
#include "filter.h"
#include "pin_in.h"
#include
// ---------------------------------------------------------------------------
// property page GUIDs
// keep these in sync with what's in the propage\mp2demux subdirectory
// ---------------------------------------------------------------------------
// output pin manipulation
// {960F051B-A25C-4ac4-8D30-050CD47A814F}
DEFINE_GUID (CLSID_MPEG2DemuxPropOutputPins,
0x960f051b, 0xa25c, 0x4ac4, 0x8d, 0x30, 0x5, 0xc, 0xd4, 0x7a, 0x81, 0x4f) ;
// PID mappings
// {ae83f13d-51b5-4a85-8c3a-ecc9f50c557a}
DEFINE_GUID (CLSID_MPEG2DemuxPropPIDMap,
0xae83f13d, 0x51b5, 0x4a85, 0x8c, 0x3a, 0xec, 0xc9, 0xf5, 0xc, 0x55, 0x7a) ;
// stream_id mappings
// {E04BBB8F-CB77-499e-B815-468B1C3ED88F}
DEFINE_GUID(CLSID_MPEG2DemuxPropStreamIdMap,
0xe04bbb8f, 0xcb77, 0x499e, 0xb8, 0x15, 0x46, 0x8b, 0x1c, 0x3e, 0xd8, 0x8f) ;
// ============================================================================
// PIN_PERSIST_INFO is used to persist filter state; when persisting, each output
// pin is obtained and the PIN_PERSIST_INFO data structure is filled in and written
// out, followed by the pin name and the pin format block
typedef
struct {
ULONG NameLength ; // in WCHARs; includes NULL term.
AM_MEDIA_TYPE MediaType ; // pbFormat ignored;
// cbFormat must be accurate
// pUnk ignored
LONG lBatchSize ; // size of the batches for downstream sends
BOOL fBatchExact ; // send only batches of media samples downstream
DWORD dwMediaSampleLen ; // requested bytes available in each media sample
DWORD dwMediaSamplePool ; // requested number of media samples in pin's allocator pool
} PIN_PERSIST_INFO ;
#define PERSIST_ENDOFPIN_MARKER 0xCA5EBABE
// constructor : initializes the class
CMPEG2Demultiplexer::CMPEG2Demultiplexer (
IN TCHAR * pClassName,
IN IUnknown * pIUnknown,
IN REFCLSID rclsid,
OUT HRESULT * pHr
) : CBaseFilter (pClassName,
pIUnknown,
new CRefCountedCritSec, // if this fails, we'll hit an ASSERT in the CBaseFilter constructor
rclsid
),
CPersistStream (pIUnknown,
pHr
),
m_pInputPin (NULL),
m_pMPEG2Controller (NULL),
m_pDShowDemuxInterfaces (NULL),
m_pStats (NULL),
m_hkeyRoot (NULL),
m_pMPEG2PushClock (NULL),
m_fImplementPushClock (FALSE),
m_pcrtSeekingLock (NULL),
m_pSeekingCore (NULL)
/*++
NOTE: aggregation - if this filter is being aggregated, it's controlling
unknown is set down in the base classes. The filter itself instantiates
several objects that implement a COM interface, and they delegate all
IUnknown calls to the filter, always. If the filter has been aggregated,
the calls will delegate further out to the controlling unknown. If the
filter has not been aggregated, it will handle the subobject's IUnknown
method calls.
--*/
{
HRESULT hr ;
DWORD dw ;
BOOL r ;
LARGE_INTEGER li ;
HKEY hkey ;
LONG l ;
DWORD dwDisposition ;
O_TRACE_ENTER_0 (TEXT("CMPEG2Demultiplexer::CMPEG2Demultiplexer ()")) ;
TRACE_CONSTRUCTOR (TEXT ("CMPEG2Demultiplexer")) ;
InitializeCriticalSection (& m_csReceive) ;
// instantiate our seeking lock
m_pcrtSeekingLock = new CRefCountedCritSec ;
if (!m_pcrtSeekingLock) {
(* pHr) = E_OUTOFMEMORY ;
goto cleanup ;
}
// the input pin is created the first time GetPin () is called with
// index 0; or is explicitely created in the IBDA_Topology interface
// if this is NULL, it means we failed the above call
if (m_pLock == NULL) {
* pHr = E_OUTOFMEMORY ;
goto cleanup ;
}
m_pSeekingCore = new CMpeg2DemuxMediaSeekingCore (
this,
m_pcrtSeekingLock
) ;
if (m_pSeekingCore == NULL) {
* pHr = E_OUTOFMEMORY ;
goto cleanup ;
}
// -------------------------------------------------------------------
// stats
// instantiate the stats module
m_pStats = new CMpeg2Stats () ;
if (m_pStats == NULL) {
* pHr = E_OUTOFMEMORY ;
goto cleanup ;
}
// push clock relies heavily on QPC-derived values; if we can't obtain
// them we fail our right here
r = QueryPerformanceFrequency (& li) ;
if (!r) {
* pHr = E_FAIL ;
goto cleanup ;
}
// instantiate our dshow-style interface
m_pDShowDemuxInterfaces = new CDShowMPEG2Demux (this) ;
if (m_pDShowDemuxInterfaces == NULL) {
* pHr = E_OUTOFMEMORY ;
goto cleanup ;
}
//
// registry checks
//
// we may set the stream type immediately; check now
l = RegCreateKeyEx (
HKEY_CURRENT_USER,
REG_MPEG2_DEMUX,
NULL,
NULL,
REG_OPTION_NON_VOLATILE,
(KEY_READ | KEY_WRITE),
NULL,
& hkey,
& dwDisposition
) ;
if (l == ERROR_SUCCESS) {
// should we preload ?
dw = REG_DEFAULT_INIT_STREAM_TYPE ; // default value
l = RegGetValIfExist (
hkey,
REG_STREAM_TYPE,
TRUE,
& dw
) ;
if (l == ERROR_SUCCESS &&
dw != MPEG2_STREAM_UNDEFINED) {
hr = SetStreamType (dw) ;
}
RegCloseKey (hkey) ;
}
else {
// if we failed the above call, other failures will occur later which
// will render the filter useless, so we fail the instantiation now
(* pHr) = HRESULT_FROM_WIN32 (l) ;
goto cleanup ;
}
// CBaseInputPin calls CBasePin, which does nothing with
// the pHr parameter; set it here
(* pHr) = Reset_ () ;
cleanup :
return ;
}
// destructor : frees up the resources that are used up in this class
CMPEG2Demultiplexer::~CMPEG2Demultiplexer (
)
{
DEMUX_PIN_RECORD ** ppPins ;
DWORD cPins ;
O_TRACE_ENTER_0 (TEXT("CMPEG2Demultiplexer::~CMPEG2Demultiplexer")) ;
TRACE_DESTRUCTOR (TEXT ("CMPEG2Demultiplexer")) ;
// free up the output pins
cPins = m_Pins.GetCount () ;
ppPins = m_Pins.GetVector () ;
for (DWORD i = 0; i < cPins; i++) {
ppPins [i] -> Release () ;
}
// free up the input pin, if it's using resources
RELEASE_AND_CLEAR (m_pInputPin) ;
// the controller
delete m_pMPEG2Controller ;
// the clock
delete m_pMPEG2PushClock ;
// destroy our custom interface objects
delete m_pDShowDemuxInterfaces ;
// our seeking core
delete m_pSeekingCore ;
// done with the lock
(reinterpret_cast (m_pLock)) -> Release () ;
// might be NULL if the call failed in the destructor, so don't assert on it
RELEASE_AND_CLEAR (m_pStats) ;
// our registry key
CLOSE_RESET_REG_KEY (m_hkeyRoot) ;
// seeking lock
RELEASE_AND_CLEAR (m_pcrtSeekingLock) ;
DeleteCriticalSection (& m_csReceive) ;
}
HRESULT
CMPEG2Demultiplexer::LoadMpeg2Transport_ (
)
{
CMPEG2TransportController * pTSController ;
HRESULT hr ;
LONG l ;
DWORD dwDisposition ;
DWORD dw ;
DWORD dwClockSubordinateSamplingWindowMillis ;
DWORD dwClockSubordinateHistoryMillis ;
DWORD dwClockSubordinateMinSubordinatable ;
DWORD dwClockSubordinateMaxSubordinatable ;
DWORD dwMaxGlitchesPerHour ;
pTSController = NULL ;
// first time call; load the transport controller
ASSERT (pTSController == NULL) ;
ASSERT (m_pMPEG2PushClock == NULL) ;
ASSERT (m_hkeyRoot == NULL) ;
ASSERT (m_pMPEG2Controller == NULL) ;
ASSERT (m_pStats) ;
// registry root is transport type
l = RegCreateKeyEx (
HKEY_CURRENT_USER,
REG_MPEG2_TRANSPORT_DEMUX,
NULL,
NULL,
REG_OPTION_NON_VOLATILE,
(KEY_READ | KEY_WRITE),
NULL,
& m_hkeyRoot,
& dwDisposition
) ;
if (l == ERROR_SUCCESS) {
ASSERT (m_hkeyRoot != NULL) ;
dw = REG_DEFAULT_IMPLEMENT_IREFERENCECLOCK ;
::RegGetValIfExist (m_hkeyRoot, REG_IMPLEMENT_IREFERENCECLOCK, TRUE, & dw) ;
m_fImplementPushClock = (dw != 0) ? TRUE : FALSE ;
dwClockSubordinateSamplingWindowMillis = REG_DEF_CLOCKSUBORDINATE_SAMPLING_WIN_MILLIS ;
::RegGetValIfExist (m_hkeyRoot, REG_CLOCKSUBORDINATE_SAMPLING_WIN_MILLIS_NAME, TRUE, & dwClockSubordinateSamplingWindowMillis) ;
dwClockSubordinateHistoryMillis = REG_DEF_CLOCKSUBORDINATE_HISTORY_MILLIS ;
::RegGetValIfExist (m_hkeyRoot, REG_CLOCKSUBORDINATE_HISTORY_MILLIS_NAME, TRUE, & dwClockSubordinateHistoryMillis) ;
dwClockSubordinateMinSubordinatable = REG_DEF_CLOCKSUBORDINATE_MIN_SUBORDINATABLE ;
::RegGetValIfExist (m_hkeyRoot, REG_CLOCKSUBORDINATE_MIN_SUBORDINATABLE_NAME, TRUE, & dwClockSubordinateMinSubordinatable) ;
dwClockSubordinateMaxSubordinatable = REG_DEF_CLOCKSUBORDINATE_MAX_SUBORDINATABLE ;
::RegGetValIfExist (m_hkeyRoot, REG_CLOCKSUBORDINATE_MAX_SUBORDINATABLE_NAME, TRUE, & dwClockSubordinateMaxSubordinatable) ;
dwMaxGlitchesPerHour = REG_DEF_MAX_GLITCHES_PER_HOUR ;
::RegGetValIfExist (m_hkeyRoot, REG_MAX_GLITCHES_PER_HOUR_NAME, TRUE, & dwMaxGlitchesPerHour) ;
m_pMPEG2PushClock = new CMPEG2PushClock (
m_fImplementPushClock,
m_hkeyRoot,
m_pStats,
this,
dwClockSubordinateSamplingWindowMillis,
dwClockSubordinateHistoryMillis,
dwClockSubordinateMinSubordinatable,
dwClockSubordinateMaxSubordinatable,
dwMaxGlitchesPerHour,
& hr
) ;
if (m_pMPEG2PushClock &&
SUCCEEDED (hr)) {
pTSController = new CMPEG2TransportController (
m_hkeyRoot,
this,
m_pStats,
m_pMPEG2PushClock,
& hr
) ;
if (pTSController &&
SUCCEEDED (hr)) {
// reset
hr = pTSController -> Reset () ;
if (SUCCEEDED (hr)) {
// set it
m_pMPEG2Controller = pTSController ;
// set the AV stream notification interface
ASSERT (m_pMPEG2Controller -> GetStreamContentManager ()) ;
m_pMPEG2PushClock -> SetIAVStreamNotify (m_pMPEG2Controller -> GetStreamContentManager ()) ;
// ----------------------------------------------------
// now setup the stats to the desired settings
//
// set the stats level; we don't fail if this call fails because
// it should not prevent the entire filter from running; it would nice
// to write something into the error log however
ASSERT (m_pStats) ;
m_pStats -> Initialize (
StatsEnabled (REG_MPEG2_TRANSPORT_DEMUX),
MPEG2_STREAM_TRANSPORT
) ;
}
else {
// failed to reset/initialize
DELETE_RESET (pTSController) ;
}
}
else {
DELETE_RESET (pTSController) ;
hr = FAILED (hr) ? hr : E_OUTOFMEMORY ;
}
}
}
else {
hr = HRESULT_FROM_WIN32 (l) ;
}
// if anything failed; reset to original state
if (FAILED (hr)) {
DELETE_RESET (pTSController) ;
DELETE_RESET (m_pMPEG2PushClock) ;
CLOSE_RESET_REG_KEY (m_hkeyRoot) ;
}
return hr ;
}
HRESULT
CMPEG2Demultiplexer::LoadMpeg2Program_ (
)
{
CMPEG2ProgramController * pPSController ;
HRESULT hr ;
LONG l ;
DWORD dwDisposition ;
DWORD dw ;
DWORD dwClockSubordinateSamplingWindowMillis ;
DWORD dwClockSubordinateHistoryMillis ;
DWORD dwClockSubordinateMinSubordinatable ;
DWORD dwClockSubordinateMaxSubordinatable ;
DWORD dwMaxGlitchesPerHour ;
pPSController = NULL ;
// first time call; load the transport controller
ASSERT (pPSController == NULL) ;
ASSERT (m_pMPEG2PushClock == NULL) ;
ASSERT (m_hkeyRoot == NULL) ;
ASSERT (m_pMPEG2Controller == NULL) ;
ASSERT (m_pStats) ;
// registry root is transport type
l = RegCreateKeyEx (
HKEY_CURRENT_USER,
REG_MPEG2_PROGRAM_DEMUX,
NULL,
NULL,
REG_OPTION_NON_VOLATILE,
(KEY_READ | KEY_WRITE),
NULL,
& m_hkeyRoot,
& dwDisposition
) ;
if (l == ERROR_SUCCESS) {
ASSERT (m_hkeyRoot != NULL) ;
dw = REG_DEFAULT_IMPLEMENT_IREFERENCECLOCK ;
::RegGetValIfExist (m_hkeyRoot, REG_IMPLEMENT_IREFERENCECLOCK, TRUE, & dw) ;
m_fImplementPushClock = (dw != 0) ? TRUE : FALSE ;
dwClockSubordinateSamplingWindowMillis = REG_DEF_CLOCKSUBORDINATE_SAMPLING_WIN_MILLIS ;
::RegGetValIfExist (m_hkeyRoot, REG_CLOCKSUBORDINATE_SAMPLING_WIN_MILLIS_NAME, TRUE, & dwClockSubordinateSamplingWindowMillis) ;
dwClockSubordinateHistoryMillis = REG_DEF_CLOCKSUBORDINATE_HISTORY_MILLIS ;
::RegGetValIfExist (m_hkeyRoot, REG_CLOCKSUBORDINATE_HISTORY_MILLIS_NAME, TRUE, & dwClockSubordinateHistoryMillis) ;
dwClockSubordinateMinSubordinatable = REG_DEF_CLOCKSUBORDINATE_MIN_SUBORDINATABLE ;
::RegGetValIfExist (m_hkeyRoot, REG_CLOCKSUBORDINATE_MIN_SUBORDINATABLE_NAME, TRUE, & dwClockSubordinateMinSubordinatable) ;
dwClockSubordinateMaxSubordinatable = REG_DEF_CLOCKSUBORDINATE_MAX_SUBORDINATABLE ;
::RegGetValIfExist (m_hkeyRoot, REG_CLOCKSUBORDINATE_MAX_SUBORDINATABLE_NAME, TRUE, & dwClockSubordinateMaxSubordinatable) ;
dwMaxGlitchesPerHour = REG_DEF_MAX_GLITCHES_PER_HOUR ;
::RegGetValIfExist (m_hkeyRoot, REG_MAX_GLITCHES_PER_HOUR_NAME, TRUE, & dwMaxGlitchesPerHour) ;
// we still need clock methods so we instantiate regardless
m_pMPEG2PushClock = new CMPEG2PushClock (
m_fImplementPushClock,
m_hkeyRoot,
m_pStats,
this,
dwClockSubordinateSamplingWindowMillis,
dwClockSubordinateHistoryMillis,
dwClockSubordinateMinSubordinatable,
dwClockSubordinateMaxSubordinatable,
dwMaxGlitchesPerHour,
& hr
) ;
if (m_pMPEG2PushClock &&
SUCCEEDED (hr)) {
pPSController = new CMPEG2ProgramController (
m_hkeyRoot,
this,
m_pStats,
m_pMPEG2PushClock,
& hr
) ;
if (pPSController &&
SUCCEEDED (hr)) {
// reset
hr = pPSController -> Reset () ;
if (SUCCEEDED (hr)) {
// set it
m_pMPEG2Controller = pPSController ;
// set the AV stream notification interface
ASSERT (m_pMPEG2Controller -> GetStreamContentManager ()) ;
m_pMPEG2PushClock -> SetIAVStreamNotify (m_pMPEG2Controller -> GetStreamContentManager ()) ;
// ----------------------------------------------------
// now setup the stats to the desired settings
//
// set the stats level; we don't fail if this call fails because
// it should not prevent the entire filter from running; it would nice
// to write something into the error log however
ASSERT (m_pStats) ;
m_pStats -> Initialize (
StatsEnabled (REG_MPEG2_PROGRAM_DEMUX),
MPEG2_STREAM_PROGRAM
) ;
}
else {
// failed to reset/initialize
DELETE_RESET (pPSController) ;
}
}
else {
DELETE_RESET (pPSController) ;
hr = FAILED (hr) ? hr : E_OUTOFMEMORY ;
}
}
}
else {
hr = HRESULT_FROM_WIN32 (l) ;
}
if (FAILED (hr)) {
// if anything failed; reset to original state
DELETE_RESET (pPSController) ;
DELETE_RESET (m_pMPEG2PushClock) ;
CLOSE_RESET_REG_KEY (m_hkeyRoot) ;
}
return hr ;
}
HRESULT
CMPEG2Demultiplexer::SetStreamType (
IN DWORD Mpeg2RequestedStreamType
)
{
HRESULT hr ;
O_TRACE_ENTER_1 (TEXT("CMPEG2Demultiplexer::SetStreamType (%08xh)"), Mpeg2RequestedStreamType) ;
LockReceive () ;
LockFilter () ;
// if we're already the type being asked to be
if (GetStreamType () == Mpeg2RequestedStreamType) {
// success
hr = S_OK ;
}
else {
// otherwise, we must load
switch (Mpeg2RequestedStreamType)
{
case MPEG2_STREAM_TRANSPORT :
// first time call
if (GetStreamType () == MPEG2_STREAM_UNDEFINED) {
hr = LoadMpeg2Transport_ () ;
}
else {
hr = E_NOTIMPL ;
}
break ;
case MPEG2_STREAM_PROGRAM :
// first time call
if (GetStreamType () == MPEG2_STREAM_UNDEFINED) {
hr = LoadMpeg2Program_ () ;
}
else {
hr = E_NOTIMPL ;
}
break ;
case MPEG2_STREAM_UNDEFINED :
// we are being asked to offload
hr = E_NOTIMPL ;
break ;
} ;
}
UnlockFilter () ;
UnlockReceive () ;
return hr ;
}
DWORD
CMPEG2Demultiplexer::GetStreamType (
)
{
O_TRACE_ENTER_0 (TEXT("CMPEG2Demultiplexer::GetStreamType ()")) ;
if (m_pMPEG2Controller != NULL) {
return m_pMPEG2Controller -> GetStreamType () ;
}
else {
return MPEG2_STREAM_UNDEFINED ;
}
}
HRESULT
CMPEG2Demultiplexer::ResetController_ (
)
{
O_TRACE_ENTER_0 (TEXT("CMPEG2Demultiplexer::ResetController_ ()")) ;
switch (GetStreamType ())
{
case MPEG2_STREAM_TRANSPORT :
case MPEG2_STREAM_PROGRAM :
ASSERT (m_pMPEG2Controller) ;
return m_pMPEG2Controller -> Reset () ;
case MPEG2_STREAM_UNDEFINED :
default :
return S_OK ;
}
}
HRESULT
CMPEG2Demultiplexer::Reset_ (
)
/*++
locks held:
filter
--*/
{
HRESULT hr ;
O_TRACE_ENTER_0 (TEXT("CMPEG2Demultiplexer::Reset_ ()")) ;
hr = ResetController_ () ;
NE_SPEW (hr, S_OK, TEXT ("failed to reset the controller")) ;
return hr ;
}
int
CMPEG2Demultiplexer::GetPinCount (
void
)
// returns the count of pins
{
int r ;
O_TRACE_ENTER_0 (TEXT("CMPEG2Demultiplexer::GetPinCount ()")) ;
// always include input pin
m_Pins.Lock () ;
r = m_Pins.GetCount () + 1 ;
m_Pins.Unlock () ;
return r ;
}
CBasePin *
CMPEG2Demultiplexer::GetPin (
IN int Index
)
/*++
Purpose:
Parameters:
Index 0-based index into the pins; no order is specified
Return Values:
success: CBasePin pointer
failure: NULL
Notes:
don't AddRef returned pointer
--*/
{
CBasePin * pRet ;
O_TRACE_ENTER_1 (TEXT("CMPEG2Demultiplexer::GetPin (%d)"), Index) ;
LockFilter () ;
if (Index == 0) {
// input pin
// if first time we're being called; create it
if (m_pInputPin == NULL) {
CreateInputPinLocked_ () ;
}
// don't AddRef !
pRet = m_pInputPin ;
}
else if (Index > 0) {
// output pin
// ppPins is 0-based vector of output pins; if Index is out of bounds,
// vector class returns NULL
pRet = m_Pins [Index - 1] -> pDemuxPin ;
// don't addref
}
else {
// bogus index: < 0
pRet = NULL ;
}
UnlockFilter () ;
return pRet ;
}
STDMETHODIMP
CMPEG2Demultiplexer::Pause (
)
/*++
purpose:
The purpose of this method is to transition the filter to a
paused state. The paused state says that media samples should
start flowing through the filter graph, right up to the rendering
filter, but not be rendered. The thinking is then that a ::Run
command has ready media samples to be displayed, and they are
instantly on. This means that there's no difference to us,
steady state, between running and paused. What this means is
that we do something only if we're stopped. If we're running
we're already there.
parameters:
none
return values:
S_OK
notes:
--*/
{
HRESULT hr ;
DEMUX_PIN_RECORD ** ppPinRecord ;
DWORD cPins ;
DWORD i ;
O_TRACE_ENTER_0 (TEXT("CMPEG2Demultiplexer::Pause ()")) ;
hr = S_OK ;
LockFilter () ;
if (m_State == State_Stopped) {
if (GetStreamType () != MPEG2_STREAM_UNDEFINED) {
// the only difference between what we do and what the
// base class does is that we start by calling ::Active
// on the OUTPUT pins, then switch the state, then
// activate the input pin; we do this because the input
// pin may be in pull mode, in which case it will spawn a
// a thread when we activate it, which could preempt us
// right here, before we have a chance to officially change
// the state; we'll lose a number of packets in ::Receive
// because the filter state will still be stopped
ppPinRecord = m_Pins.GetVector () ;
cPins = m_Pins.GetCount () ;
for (i = 0; i < cPins; i++) {
if (GET_OUTPUT_PIN (ppPinRecord [i]) -> IsConnected ()) {
GET_OUTPUT_PIN (ppPinRecord [i]) -> Active() ;
}
}
// we activate the controller - this resets internally
ASSERT (m_pMPEG2Controller) ;
hr = m_pMPEG2Controller -> Active () ;
if (SUCCEEDED (hr)) {
// change the state
m_State = State_Paused ;
// LAST: activate the input pin
if (m_pInputPin -> IsConnected ()) {
m_pInputPin -> Active () ;
}
}
else {
m_pMPEG2Controller -> Inactive () ;
for (i = 0; i < cPins; i++) {
if (GET_OUTPUT_PIN (ppPinRecord [i]) -> IsConnected ()) {
// inactivate
GET_OUTPUT_PIN (ppPinRecord [i]) -> Inactive() ;
}
}
}
}
else {
// cannot transition if we don't know what we are
// xxxx: should we just default load 1 or the other and
// continue ??
hr = E_FAIL ;
}
} else {
m_State = State_Paused;
}
if (SUCCEEDED (hr) &&
m_pMPEG2PushClock) {
m_pMPEG2PushClock -> FilterStateChanged (
m_State,
0
) ;
}
UnlockFilter () ;
return hr ;
}
STDMETHODIMP
CMPEG2Demultiplexer::Stop (
)
/*++
purpose:
The purpose of this method is to stop the filter. This means
that no more data will be accepted by the input pin after
we return.
parameters:
none
return values:
S_OK
notes:
The only difference between this and the base class implementation
is that we handle the input pin carefully. The base class rolls
through all the pins, from 0 to n, inactivating each one, and we
want to make sure that we inactivate the input pin first
(synchronously), then inactivate the output pins.
--*/
{
DEMUX_PIN_RECORD ** ppPinRecord ;
DWORD cPins ;
O_TRACE_ENTER_0 (TEXT("CMPEG2Demultiplexer::Stop ()")) ;
// stop the input pin first; this is a synchronous stop if
// the input pin is currently sporting a pull pin - the thread
// will be stopped when we return
ASSERT (m_pInputPin) ;
m_pInputPin -> Inactive () ;
// need to grab the receiver lock & the filter lock; release in
// reverse order
LockReceive () ;
LockFilter () ;
// now walk the output pins and inactivate them; NOTE: since we have
// the filter lock, we can access the vector without locking it
// explicitely
cPins = m_Pins.GetCount () ;
ppPinRecord = m_Pins.GetVector () ;
// abort everything in the controller
if (m_pMPEG2Controller) {
m_pMPEG2Controller -> AbortBuffers () ;
}
for (DWORD i = 0; i < cPins; i++) {
if (GET_OUTPUT_PIN (ppPinRecord [i]) -> IsConnected ()) {
// inactivate
GET_OUTPUT_PIN (ppPinRecord [i]) -> Inactive() ;
}
}
// inactivate the controller
if (m_pMPEG2Controller) {
m_pMPEG2Controller -> Inactive () ;
}
// last, set the state
m_State = State_Stopped ;
// release our locks
UnlockFilter () ;
UnlockReceive () ;
// notify clock that we're stopped
if (m_pMPEG2PushClock) {
m_pMPEG2PushClock -> FilterStateChanged (
m_State,
0
) ;
}
return S_OK ;
}
STDMETHODIMP
CMPEG2Demultiplexer::Run (
IN REFERENCE_TIME tStart
)
/*++
notifies the reference clock
--*/
{
HRESULT hr ;
O_TRACE_ENTER_1 (TEXT("CMPEG2Demultiplexer::Run (%I64d)"), tStart) ;
hr = CBaseFilter::Run (tStart) ;
if (SUCCEEDED (hr)) {
ASSERT (m_State == State_Running) ;
if (m_pMPEG2PushClock) {
m_pMPEG2PushClock -> FilterStateChanged (
m_State,
tStart
) ;
}
}
return hr ;
}
// called by the DirectShow class factory code
CUnknown *
WINAPI
CMPEG2Demultiplexer::CreateInstance (
IN IUnknown * pIUnknown,
IN HRESULT * pHr
)
{
CMPEG2Demultiplexer * pDemux ;
TRACE_ENTER_1 (TEXT("CMPEG2Demultiplexer::CreateInstance (); pIUnknown = 0x%08x"), pIUnknown) ;
pDemux = new CMPEG2Demultiplexer (
NAME ("CMPEG2Demultiplexer"),
pIUnknown,
CLSID_MPEG2Demultiplexer,
pHr
) ;
if (pDemux == NULL) {
* pHr = E_OUTOFMEMORY ;
}
if (FAILED (* pHr)) {
delete pDemux ;
pDemux = NULL ;
}
return pDemux ;
}
STDMETHODIMP
CMPEG2Demultiplexer::NonDelegatingQueryInterface (
IN REFIID riid,
OUT void ** ppv
)
{
O_TRACE_ENTER_0 (TEXT("CMPEG2Demultiplexer::NonDelegatingQueryInterface ()")) ;
// ------------------------------------------------------------------------
// IPersistStream; we do persist
if (riid == IID_IPersistStream &&
!IsInPullMode ()) {
return GetInterface (
(IPersistStream *) this,
ppv
) ;
}
// ------------------------------------------------------------------------
// IReferenceClock; if we wish to expose it; we don't expose it if we're
// in pull mode
else if (riid == IID_IReferenceClock &&
ImplementPushClock ()) {
return GetInterface (
(IReferenceClock *) m_pMPEG2PushClock,
ppv
) ;
}
// ------------------------------------------------------------------------
// IAMFilterMiscFlags; must implement this interface if we're to be picked
// as graph clock; only implement this if we're going to implement
// IReferenceClock, so the conditions above are identical
else if (riid == IID_IAMFilterMiscFlags &&
ImplementPushClock ()) {
return GetInterface (
(IAMFilterMiscFlags *) this,
ppv
) ;
}
// ------------------------------------------------------------------------
// ISpecifyPropertyPages; allows an app to enumerate CLSIDs for our
// property pages
else if (riid == IID_ISpecifyPropertyPages &&
m_pDShowDemuxInterfaces != NULL &&
!IsInPullMode ()) {
return GetInterface (
(ISpecifyPropertyPages *) m_pDShowDemuxInterfaces,
ppv
) ;
}
// ------------------------------------------------------------------------
// IMpeg2Demultiplexer; demux-specific interface to create/delete output
// pins
else if (riid == IID_IMpeg2Demultiplexer &&
m_pDShowDemuxInterfaces != NULL &&
!IsInPullMode ()) {
return GetInterface (
(IMpeg2Demultiplexer *) m_pDShowDemuxInterfaces,
ppv
) ;
}
// ------------------------------------------------------------------------
// IMpeg2DemultiplexerTesting; interface used purely for testing purposes
else if (riid == IID_IMpeg2DemultiplexerTesting &&
m_pDShowDemuxInterfaces != NULL) {
return GetInterface (
(IMpeg2DemultiplexerTesting *) m_pDShowDemuxInterfaces,
ppv
) ;
}
return CBaseFilter::NonDelegatingQueryInterface (riid, ppv) ;
}
HRESULT
CMPEG2Demultiplexer::PersistPinPIDMaps_ (
IN IStream * pIStream,
IN DEMUX_PIN_RECORD * pDemuxPinRecord
)
{
IEnumPIDMap * pIEnumPIDMap ;
IMPEG2PIDMap * pIPinPIDMap ;
PID_MAP PIDMap ;
DWORD dwGot ;
HRESULT hr ;
DWORD cPIDMaps ;
O_TRACE_ENTER_0 (TEXT("CMPEG2Demultiplexer::PersisPinPIDMaps_ ()")) ;
ASSERT (GetStreamType () == MPEG2_STREAM_TRANSPORT) ;
ASSERT (pIStream) ;
ASSERT (pDemuxPinRecord) ;
hr = pDemuxPinRecord -> pDemuxPin -> QueryInterface (
IID_IMPEG2PIDMap,
(void **) & pIPinPIDMap
) ;
if (SUCCEEDED (hr)) {
hr = pIPinPIDMap -> EnumPIDMap (
& pIEnumPIDMap
) ;
// regardless of the success/failure of the call above, we're done
// with this interface
RELEASE_AND_CLEAR (pIPinPIDMap) ;
// get the PID map count
hr = pIEnumPIDMap -> Next (0, & PIDMap, & cPIDMaps) ;
if (SUCCEEDED (hr)) {
// save off the PID map count
hr = pIStream -> Write ((BYTE *) & cPIDMaps, sizeof cPIDMaps, NULL) ;
// now loop through and persist each one
while (SUCCEEDED (hr) && cPIDMaps > 0) {
// get the PID map
hr = pIEnumPIDMap -> Next (
1,
& PIDMap,
& dwGot
) ;
ASSERT (SUCCEEDED (hr)) ;
// ------------------------------------------------------------
// write in the PID
hr = pIStream -> Write ((BYTE *) & PIDMap.ulPID, sizeof PIDMap.ulPID, NULL) ;
if (SUCCEEDED (hr)) {
// --------------------------------------------------------
// and the media sample content
hr = pIStream -> Write ((BYTE *) & PIDMap.MediaSampleContent, sizeof PIDMap.MediaSampleContent, NULL) ;
}
cPIDMaps-- ;
}
RELEASE_AND_CLEAR (pIEnumPIDMap) ;
}
}
return hr ;
}
HRESULT
CMPEG2Demultiplexer::RestorePinPIDMaps_ (
IN IStream * pIStream,
IN DEMUX_PIN_RECORD * pDemuxPinRecord
)
{
IMPEG2PIDMap * pIMPEG2PIDMap ;
PID_MAP PIDMap ;
#ifndef UNDER_CE
DWORD dwGot ;
#endif //UNDER_CE
HRESULT hr ;
DWORD cPIDMaps ;
DWORD i ;
O_TRACE_ENTER_0 (TEXT("CMPEG2Demultiplexer::RestorePinPIDMaps_ ()")) ;
hr = pDemuxPinRecord -> pDemuxPin -> QueryInterface (
IID_IMPEG2PIDMap,
(void **) & pIMPEG2PIDMap
) ;
if (SUCCEEDED (hr)) {
// --------------------------------------------------------------------
// read in the number of PID maps
hr = pIStream -> Read ((BYTE *) & cPIDMaps, sizeof cPIDMaps, NULL) ;
if (SUCCEEDED (hr)) {
// ----------------------------------------------------------------
// restore each PID map
for (i = 0; i < cPIDMaps && SUCCEEDED (hr); i++) {
// read in the PID
hr = pIStream -> Read ((BYTE *) & PIDMap.ulPID, sizeof PIDMap.ulPID, NULL) ;
if (SUCCEEDED (hr)) {
// and the media sample content
hr = pIStream -> Read ((BYTE *) & PIDMap.MediaSampleContent, sizeof PIDMap.MediaSampleContent, NULL) ;
if (SUCCEEDED (hr)) {
// map the PID
hr = pIMPEG2PIDMap -> MapPID (
1,
& PIDMap.ulPID,
PIDMap.MediaSampleContent
) ;
}
}
}
}
pIMPEG2PIDMap -> Release () ;
}
return hr ;
}
HRESULT
CMPEG2Demultiplexer::RestorePinStreamIdMaps_ (
IN IStream * pIStream,
IN DEMUX_PIN_RECORD * pDemuxPinRecord
)
{
IMPEG2StreamIdMap * pIMPEG2StreamIdMap ;
STREAM_ID_MAP StreamIdMap ;
#ifndef UNDER_CE
DWORD dwGot ;
#endif //UNDER_CE
HRESULT hr ;
DWORD cStreamIdMaps ;
DWORD i ;
O_TRACE_ENTER_0 (TEXT("CMPEG2Demultiplexer::RestorePinStreamIdMaps_ ()")) ;
hr = pDemuxPinRecord -> pDemuxPin -> QueryInterface (
IID_IMPEG2StreamIdMap,
(void **) & pIMPEG2StreamIdMap
) ;
if (SUCCEEDED (hr)) {
// --------------------------------------------------------------------
// read in the number of StreamId maps
hr = pIStream -> Read ((BYTE *) & cStreamIdMaps, sizeof cStreamIdMaps, NULL) ;
if (SUCCEEDED (hr)) {
// ----------------------------------------------------------------
// restore each StreamId map
for (i = 0; i < cStreamIdMaps && SUCCEEDED (hr); i++) {
hr = pIStream -> Read ((BYTE *) & StreamIdMap, sizeof StreamIdMap, NULL) ;
if (SUCCEEDED (hr)) {
// map the StreamId
hr = pIMPEG2StreamIdMap -> MapStreamId (
StreamIdMap.stream_id,
StreamIdMap.dwMediaSampleContent,
StreamIdMap.ulSubstreamFilterValue,
StreamIdMap.iDataOffset
) ;
}
}
}
pIMPEG2StreamIdMap -> Release () ;
}
return hr ;
}
HRESULT
CMPEG2Demultiplexer::PersistPinStreamIdMaps_ (
IN IStream * pIStream,
IN DEMUX_PIN_RECORD * pDemuxPinRecord
)
{
IEnumStreamIdMap * pIEnumStreamIdMap ;
IMPEG2StreamIdMap * pIPinStreamIdMap ;
STREAM_ID_MAP StreamIdMap ;
DWORD dwGot ;
HRESULT hr ;
DWORD cStreamIdMaps ;
O_TRACE_ENTER_0 (TEXT("CMPEG2Demultiplexer::PersistPinStreamIdMaps_ ()")) ;
ASSERT (GetStreamType () == MPEG2_STREAM_PROGRAM) ;
ASSERT (pIStream) ;
ASSERT (pDemuxPinRecord) ;
hr = pDemuxPinRecord -> pDemuxPin -> QueryInterface (
IID_IMPEG2StreamIdMap,
(void **) & pIPinStreamIdMap
) ;
if (SUCCEEDED (hr)) {
hr = pIPinStreamIdMap -> EnumStreamIdMap (
& pIEnumStreamIdMap
) ;
// regardless of the success/failure of the call above, we're done
// with this interface
RELEASE_AND_CLEAR (pIPinStreamIdMap) ;
// get the StreamId map count
hr = pIEnumStreamIdMap -> Next (0, & StreamIdMap, & cStreamIdMaps) ;
if (SUCCEEDED (hr)) {
// save off the StreamId map count
hr = pIStream -> Write ((BYTE *) & cStreamIdMaps, sizeof cStreamIdMaps, NULL) ;
// now loop through and persist each one
while (SUCCEEDED (hr) && cStreamIdMaps > 0) {
// get the StreamId map
hr = pIEnumStreamIdMap -> Next (
1,
& StreamIdMap,
& dwGot
) ;
ASSERT (SUCCEEDED (hr)) ;
hr = pIStream -> Write ((BYTE *) & StreamIdMap, sizeof StreamIdMap, NULL) ;
cStreamIdMaps-- ;
}
RELEASE_AND_CLEAR (pIEnumStreamIdMap) ;
}
}
return hr ;
}
HRESULT
CMPEG2Demultiplexer::PersistOutputPin_ (
IN IStream * pIStream,
IN DEMUX_PIN_RECORD * pDemuxPinRecord
)
{
HRESULT hr ;
PIN_PERSIST_INFO PinRecord ;
CMediaType * pmt ;
ULONG ul ;
O_TRACE_ENTER_0 (TEXT("CMPEG2Demultiplexer::PersistOutputPin_ ()")) ;
ASSERT (pIStream) ;
ASSERT (pDemuxPinRecord) ;
ZeroMemory (& PinRecord, sizeof PinRecord) ;
pmt = new CMediaType () ;
if (pmt) {
hr = pDemuxPinRecord -> pDemuxPin -> GetMediaType (0, pmt) ;
if (SUCCEEDED (hr)) {
// don't handle this
ASSERT (pmt -> pUnk == NULL) ;
// transfer the AM_MEDIA_TYPE members; everything but pbFormat; if there's
// a format block, we save it off elsewhere
PinRecord.MediaType.majortype = pmt -> majortype ;
PinRecord.MediaType.subtype = pmt -> subtype ;
PinRecord.MediaType.bFixedSizeSamples = pmt -> bFixedSizeSamples ;
PinRecord.MediaType.bTemporalCompression = pmt -> bTemporalCompression ;
PinRecord.MediaType.lSampleSize = pmt -> lSampleSize ;
PinRecord.MediaType.formattype = pmt -> formattype ;
PinRecord.MediaType.cbFormat = pmt -> cbFormat ;
ASSERT (pDemuxPinRecord -> pszPinID) ;
PinRecord.NameLength = wcslen (pDemuxPinRecord -> pszPinID) + 1 ; // include NULL terminator
// set the media sample stuff here
PinRecord.lBatchSize = (static_cast (pDemuxPinRecord -> pDemuxPin)) -> m_lBatchSize ;
PinRecord.fBatchExact = (static_cast (pDemuxPinRecord -> pDemuxPin)) -> m_fBatchExact ;
PinRecord.dwMediaSampleLen = (static_cast (pDemuxPinRecord -> pDemuxPin)) -> m_dwMediaSampleLen ;
PinRecord.dwMediaSamplePool = (static_cast (pDemuxPinRecord -> pDemuxPin)) -> m_dwMediaSamplePool ;
// save off the PIN_PERSIST_INFO
hr = pIStream -> Write ((BYTE *) & PinRecord, sizeof PinRecord, NULL) ;
if (SUCCEEDED (hr)) {
// save off the pin name
hr = pIStream -> Write ((BYTE *) pDemuxPinRecord -> pszPinID, PinRecord.NameLength * sizeof WCHAR, NULL) ;
if (SUCCEEDED (hr)) {
// save off the format block, if there is one
if (pmt -> cbFormat > 0) {
hr = pIStream -> Write (pmt -> pbFormat, PinRecord.MediaType.cbFormat, NULL) ;
}
// hideous HACK
// write out a magic trailer marker; when we recover, if we don't get
// this we abort
if (SUCCEEDED (hr)) {
ul = PERSIST_ENDOFPIN_MARKER ;
hr = pIStream -> Write ((BYTE *) & ul, sizeof ul, NULL) ;
}
}
}
}
}
else {
hr = E_OUTOFMEMORY ;
}
delete pmt ;
return hr ;
}
HRESULT
CMPEG2Demultiplexer::RestoreOutputPin_ (
IN IStream * pIStream,
OUT DEMUX_PIN_RECORD ** ppDemuxPinRecord
)
{
HRESULT hr ;
PIN_PERSIST_INFO PinPersist ;
#ifndef UNDER_CE
CMediaType * pmt ;
#endif //UNDER_CE
ULONG ul ;
WCHAR PinName [128] ; // PIN_INFO : max is 128
BYTE * pbFormat ;
O_TRACE_ENTER_0 (TEXT("CMPEG2Demultiplexer::RestoreOutputPin_ ()")) ;
ASSERT (pIStream) ;
ASSERT (ppDemuxPinRecord) ;
ZeroMemory (& PinPersist, sizeof PinPersist) ;
// read in the persist info
hr = pIStream -> Read ((BYTE *) & PinPersist, sizeof PinPersist, NULL) ;
if (SUCCEEDED (hr)) {
// make sure that superficially, things look ok
if (PinPersist.NameLength > 0 &&
PinPersist.NameLength <= 128) {
// read in the pin name
hr = pIStream -> Read ((BYTE *) & PinName [0], PinPersist.NameLength * sizeof WCHAR, NULL) ;
if (SUCCEEDED (hr)) {
// then the format block, if there is one
if (PinPersist.MediaType.cbFormat > 0) {
pbFormat = new BYTE [PinPersist.MediaType.cbFormat] ;
if (pbFormat) {
// read in the format block
hr = pIStream -> Read (pbFormat, PinPersist.MediaType.cbFormat, NULL) ;
if (SUCCEEDED (hr)) {
PinPersist.MediaType.pbFormat = pbFormat ;
}
}
else {
hr = E_OUTOFMEMORY ;
}
}
else {
PinPersist.MediaType.pbFormat = NULL ;
}
if (SUCCEEDED (hr)) {
// last is the magic marker
hr = pIStream -> Read ((BYTE *) & ul, sizeof ul, NULL) ;
if (SUCCEEDED (hr) && ul == PERSIST_ENDOFPIN_MARKER) {
// ok, we now have what appears to be a valid record;
// create the pin
hr = CreateOutputPin_ (
PinName,
& PinPersist.MediaType,
ppDemuxPinRecord
) ;
}
else {
hr = FAILED (hr) ? hr : E_FAIL ;
}
}
delete [] PinPersist.MediaType.pbFormat ;
}
}
else {
// we're onto a bogus record of sorts; fail out
hr = E_FAIL ;
}
}
return hr ;
}
HRESULT
CMPEG2Demultiplexer::WriteToStream (
IN IStream * pIStream
)
/*++
--*/
{
DWORD cPins ;
HRESULT hr ;
DWORD i ;
DWORD Mpeg2StreamType ;
DEMUX_PIN_RECORD * pDemuxPinRecord ;
BOOL fPullMode ;
O_TRACE_ENTER_0 (TEXT("CMPEG2Demultiplexer::WriteToStream ()")) ;
ASSERT (pIStream) ;
LockReceive () ;
LockFilter () ;
// ------------------------------------------------------------------------
// pull mode or push mode ? Only save information if we're in push mode
fPullMode = IsInPullMode () ;
hr = pIStream -> Write ((BYTE *) & fPullMode, sizeof fPullMode, NULL) ;
if (!fPullMode &&
SUCCEEDED (hr)) {
// --------------------------------------------------------------------
// persist the stream type
Mpeg2StreamType = GetStreamType () ;
hr = pIStream -> Write ((BYTE *) & Mpeg2StreamType, sizeof Mpeg2StreamType, NULL) ;
if (SUCCEEDED (hr)) {
// ----------------------------------------------------------------
// save off the number of output pins
cPins = m_Pins.GetCount () ;
hr = pIStream -> Write ((BYTE *) & cPins, sizeof cPins, NULL) ;
if (SUCCEEDED (hr)) {
// ------------------------------------------------------------
// now for each pin
for (i = 0; i < cPins && SUCCEEDED (hr); i++) {
pDemuxPinRecord = m_Pins [i] ;
ASSERT (pDemuxPinRecord) ;
ASSERT (pDemuxPinRecord -> PinDirection == PINDIR_OUTPUT) ;
// persist the output pin
hr = PersistOutputPin_ (pIStream, pDemuxPinRecord) ;
if (SUCCEEDED (hr)) {
// and its associated
switch (Mpeg2StreamType)
{
case MPEG2_STREAM_UNDEFINED :
// nothing to persist
break ;
case MPEG2_STREAM_TRANSPORT :
// PID maps
hr = PersistPinPIDMaps_ (pIStream, pDemuxPinRecord) ;
break ;
case MPEG2_STREAM_PROGRAM :
// stream_id maps
hr = PersistPinStreamIdMaps_ (pIStream, pDemuxPinRecord) ;
}
}
}
}
}
}
UnlockFilter () ;
UnlockReceive () ;
return hr ;
}
HRESULT
CMPEG2Demultiplexer::ReadFromStream (
IN IStream * pIStream
)
/*++
--*/
{
DWORD Mpeg2StreamType ;
HRESULT hr ;
DWORD cPins ;
DWORD i ;
DEMUX_PIN_RECORD * pDemuxPinRecord ;
#ifndef UNDER_CE
WCHAR * pszPinId ;
#endif //UNDER_CE
BOOL fPullMode ;
O_TRACE_ENTER_0 (TEXT("CMPEG2Demultiplexer::ReadFromStream ()")) ;
ASSERT (pIStream) ;
LockReceive () ;
LockFilter () ;
// ------------------------------------------------------------------------
// pull mode; nothing gets restored if we're not in pull mode
hr = pIStream -> Read ((BYTE *) & fPullMode, sizeof fPullMode, NULL) ;
if (SUCCEEDED (hr) &&
!fPullMode) {
// --------------------------------------------------------------------
// stream type
hr = pIStream -> Read ((BYTE *) & Mpeg2StreamType, sizeof Mpeg2StreamType, NULL) ;
if (SUCCEEDED (hr)) {
// set the stream type we read in above
hr = SetStreamType (Mpeg2StreamType) ;
if (SUCCEEDED (hr)) {
// ------------------------------------------------------------
// output pin count
hr = pIStream -> Read ((BYTE *) & cPins, sizeof cPins, NULL) ;
if (SUCCEEDED (hr)) {
// ------------------------------------------------------------
// for each pin
for (i = 0; i < cPins && SUCCEEDED (hr); i++) {
hr = RestoreOutputPin_ (
pIStream,
& pDemuxPinRecord
) ;
if (SUCCEEDED (hr)) {
ASSERT (pDemuxPinRecord) ;
switch (Mpeg2StreamType)
{
case MPEG2_STREAM_UNDEFINED :
// nothing to restore
break ;
case MPEG2_STREAM_TRANSPORT :
hr = RestorePinPIDMaps_ (
pIStream,
pDemuxPinRecord
) ;
break ;
case MPEG2_STREAM_PROGRAM:
hr = RestorePinStreamIdMaps_ (
pIStream,
pDemuxPinRecord
) ;
break ;
}
pDemuxPinRecord -> Release () ;
}
}
}
}
}
}
UnlockFilter () ;
UnlockReceive () ;
return hr ;
}
STDMETHODIMP
CMPEG2Demultiplexer::GetClassID (
OUT CLSID * pCLSID
)
{
if (!pCLSID) {
return E_POINTER ;
}
(* pCLSID) = CLSID_MPEG2Demultiplexer ;
return S_OK ;
}
HRESULT
CMPEG2Demultiplexer::ProcessMediaSampleLocked (
IN IMediaSample * pIMediaSample
)
/*++
Purpose:
The purpose of this method is to process a media sample the input pin has
received.
Called by the input pin.
Parameters:
pIMediaSample the media sample to process
Return Values:
Locks Held:
receiver
--*/
{
HRESULT hr ;
O_TRACE_ENTER_1 (TEXT ("CMPEG2Demultiplexer::ProcessMediaSample ()"), pIMediaSample) ;
ASSERT (pIMediaSample) ;
if (pIMediaSample -> IsDiscontinuity () == S_OK) {
TRACE_0 (LOG_TRACE, 1, TEXT ("Input Discontinuity")) ;
hr = InputStreamDiscontinuity () ;
if (FAILED (hr)) {
TRACE_ERROR_1 (
TEXT ("called InputStreamDiscontinuity (); hr = %08xh returned"),
hr
) ;
return hr ;
}
}
ASSERT (m_pMPEG2Controller) ;
hr = m_pMPEG2Controller -> ProcessMediaSampleLocked (pIMediaSample) ;
NE_SPEW (hr, S_OK, TEXT ("m_pMPEG2Controller -> ProcessMediaSample (pIMediaSample)")) ;
return hr ;
}
HRESULT
CMPEG2Demultiplexer::EndOfStreamLocked (
)
{
DEMUX_PIN_RECORD ** ppPinRecord ;
DWORD cPins ;
O_TRACE_ENTER_0 (TEXT ("CMPEG2Demultiplexer::EndOfStream ()")) ;
LockFilter () ;
ASSERT (m_pMPEG2Controller) ;
m_pMPEG2Controller -> AbortBuffers () ;
m_Pins.GetVector (& ppPinRecord, & cPins) ;
for (DWORD i = 0; i < cPins; i++) {
if (ppPinRecord [i] -> pDemuxPin -> IsConnected ()) {
GET_OUTPUT_PIN (ppPinRecord [i]) -> DeliverEndOfStream () ;
}
}
UnlockFilter () ;
return S_OK ;
}
HRESULT
CMPEG2Demultiplexer::MapStreamToPinLocked_ (
IN ULONG cStream,
IN ULONG * pulStream,
IN ULONG ulMask,
IN CMPEG2DemuxOutputPin * pDemuxOutputPin,
IN DWORD dwMediaSampleContent,
IN LPVOID pvParserArg
)
/*++
creates the PID map; caller must make sure the pin still exists in the
filter's vector after the call returns i.e. there is no locking of
the vector in this call
locks held:
filter
--*/
{
HRESULT hr ;
O_TRACE_ENTER_4 (TEXT ("CMPEG2Demultiplexer::MapStreamToPinLocked_ (%08xh, %08xh, %08xh, %08xh)"), cStream, pulStream, pDemuxOutputPin, dwMediaSampleContent) ;
ASSERT (cStream > 0) ;
ASSERT (pulStream) ;
ASSERT (pDemuxOutputPin) ;
ASSERT (m_pMPEG2Controller) ;
hr = S_OK ;
// for each Stream, create a mapping
for (DWORD i = 0; i < cStream; i++) {
hr = m_pMPEG2Controller -> MapStream (
ulMask & pulStream [i],
pDemuxOutputPin,
dwMediaSampleContent,
pvParserArg
) ;
if (FAILED (hr)) {
break ;
}
SetDirty (TRUE) ;
}
return hr ;
}
HRESULT
CMPEG2Demultiplexer::UnmapStreamFromPinLocked_ (
IN ULONG cStream,
IN ULONG * pulStream,
IN ULONG ulMask,
IN CMPEG2DemuxOutputPin * pDemuxOutputPin
)
/*++
Locks Held:
filter
--*/
{
HRESULT hr ;
O_TRACE_ENTER_3 (TEXT ("CMPEG2Demultiplexer::UnmapStreamFromPinLocked_ (%08xh, %08xh, %08xh)"), cStream, pulStream, pDemuxOutputPin) ;
ASSERT (cStream > 0) ;
ASSERT (pulStream) ;
ASSERT (pDemuxOutputPin) ;
ASSERT (m_pMPEG2Controller) ;
hr = S_OK ;
// for each Stream, delete the map
for (DWORD i = 0; i < cStream; i++) {
hr = m_pMPEG2Controller -> UnmapStream (
ulMask & pulStream [i],
pDemuxOutputPin
) ;
if (FAILED (hr)) {
break ;
}
SetDirty (TRUE) ;
}
return hr ;
}
HRESULT
CMPEG2Demultiplexer::CreatePin_ (
IN LPWSTR pszPinID,
IN PIN_DIRECTION PinDirection,
IN AM_MEDIA_TYPE * pMediaType,
IN LONG lOutputPinBatchSize,
IN BOOL fOutputPinBatchExact,
IN DWORD dwOutputPinMediaSampleLen,
IN DWORD dwOutputPinMediaSamplePool,
OUT DEMUX_PIN_RECORD ** ppPinRecord
)
/*++
--*/
{
HRESULT hr ;
DEMUX_PIN_RECORD * pPinRecord ;
CBasePin * pNewPin ;
DWORD cElements ;
DEMUX_PIN_RECORD ** ppPins ;
O_TRACE_ENTER_3 (TEXT ("CMPEG2Demultiplexer::CreatePin_ (%08xh, %08xh, %08xh)"), pszPinID, PinDirection, ppPinRecord) ;
ASSERT (pszPinID) ;
// for now just output pins
ASSERT (PinDirection == PINDIR_OUTPUT) ;
// lock the filter; all subsequent code paths need to go through
// the cleanup label, defined below
LockFilter () ;
// make sure the pin name will be unique
pPinRecord = NULL ;
hr = FindPinRecordLocked_ (
pszPinID,
& pPinRecord
) ;
if (SUCCEEDED (hr)) {
ASSERT (pPinRecord) ;
pPinRecord -> Release () ;
hr = VFW_E_DUPLICATE_NAME ;
goto cleanup ;
}
// reset from above call
hr = S_OK ;
// create the pin
pNewPin = NULL ;
if (PinDirection == PINDIR_OUTPUT) {
ASSERT (pMediaType) ;
ASSERT (m_pStats) ;
ASSERT (VALID_MEDIA_SAMPLE_SETTINGS(lOutputPinBatchSize, fOutputPinBatchExact, dwOutputPinMediaSampleLen, dwOutputPinMediaSamplePool)) ;
if (IsAVMediaType (pMediaType)) {
// A/V - needs a bigger buffer pool min
dwOutputPinMediaSamplePool = Max (dwOutputPinMediaSamplePool, MIN_AV_BUFFER_POOL_SIZE) ;
}
pNewPin = new CMPEG2DemuxOutputPin (
pMediaType,
pszPinID,
NAME ("CMPEG2DemuxOutputPin"),
this,
reinterpret_cast (m_pLock),
m_pSeekingCore,
m_pcrtSeekingLock,
m_pStats,
lOutputPinBatchSize,
fOutputPinBatchExact,
dwOutputPinMediaSampleLen,
dwOutputPinMediaSamplePool,
& hr
) ;
}
else if (PinDirection == PINDIR_INPUT) {
ASSERT (m_pStats) ;
pNewPin = new CMPEG2DemuxInputPin (
NAME ("CMPEG2DemuxInputPin"),
this,
reinterpret_cast (m_pLock),
m_pcrtSeekingLock,
m_pStats,
& hr
) ;
}
// check for failures in the pin creation
if (pNewPin == NULL ||
FAILED (hr)) {
RELEASE_AND_CLEAR (pNewPin) ;
hr = FAILED (hr) ? hr : E_OUTOFMEMORY ;
goto cleanup ;
}
// now create the record
pPinRecord = new DEMUX_PIN_RECORD (pNewPin, pszPinID, PinDirection, & hr) ;
RELEASE_AND_CLEAR (pNewPin) ; // DEMUX_PIN_RECORD should AddRef the pin; we don't use
// use it later in this method, so we're done with it
// check for the failure
if (FAILED (hr) ||
pPinRecord == NULL) {
RELEASE_AND_CLEAR (pPinRecord) ;
hr = FAILED (hr) ? hr : E_OUTOFMEMORY ;
goto cleanup ;
}
// append the new pin record to the pin record pointer vector
hr = m_Pins.Append (pPinRecord, & cElements) ;
if (FAILED (hr)) {
// if we fail we release the 1 refcount on the new pin record and
// return the error code; when the record destructs it will release
// the remaining refcount on the new pin
RELEASE_AND_CLEAR (pPinRecord) ;
goto cleanup ;
}
// get the vector so we can sort things
ppPins = m_Pins.GetVector () ;
ASSERT (cElements > 0) ;
ASSERT (ppPins) ;
// sort
qsort (
ppPins,
cElements,
sizeof (DEMUX_PIN_RECORD *),
DEMUX_PIN_RECORD::Compare
) ;
// refcounts: pPinRecord is instantiated with a count of 1, which we leave
// alone as the vector's refcount
// if the caller wants to get the record, set the return parameter and AddRef it
if (ppPinRecord) {
ASSERT (pPinRecord) ;
* ppPinRecord = pPinRecord ;
(* ppPinRecord) -> AddRef () ;
}
IncrementPinVersion () ;
cleanup :
UnlockFilter () ;
return hr ;
}
HRESULT
CMPEG2Demultiplexer::DeletePin_ (
IN LPWSTR pszPinID
)
/*++
--*/
{
HRESULT hr ;
DEMUX_PIN_RECORD * pPinRecord ;
DWORD dwIndex ;
O_TRACE_ENTER_1 (TEXT ("CMPEG2Demultiplexer::DeletePin_ (%08xh)"), pszPinID) ;
ASSERT (pszPinID) ;
// lock the filter; subsequent exit code paths must go through the
// :cleanup label
LockFilter () ;
pPinRecord = NULL ;
hr = FindPinRecordLocked_ (
pszPinID,
& pPinRecord,
& dwIndex
) ;
if (FAILED (hr)) {
goto cleanup ;
}
ASSERT (pPinRecord) ;
ASSERT (pPinRecord -> pDemuxPin) ;
// if it's connected, disconnect first
if (pPinRecord -> pDemuxPin -> IsConnected ()) {
pPinRecord -> pDemuxPin -> GetConnected () -> Disconnect () ;
pPinRecord -> pDemuxPin -> Disconnect () ;
}
// Release once for the FindPinRecord_ call
pPinRecord -> Release () ;
// remove from the vector
m_Pins.Remove (dwIndex) ;
// we still hold the vector's refcount
// if this is an output pin, need to unmap all Stream maps
if (m_pMPEG2Controller &&
pPinRecord -> PinDirection == PINDIR_OUTPUT) {
hr = m_pMPEG2Controller -> UnmapAllStreams (
reinterpret_cast (pPinRecord -> pDemuxPin)
) ;
}
// Release the vector's refcount
pPinRecord -> Release () ;
IncrementPinVersion () ;
cleanup :
UnlockFilter () ;
return hr ;
}
HRESULT
CMPEG2Demultiplexer::FindPinRecordLocked_ (
IN LPWSTR pszPinID,
OUT DEMUX_PIN_RECORD ** ppPinRecord,
OUT DWORD * pdwIndex // optional parameter
)
/*++
locks held:
filter lock
--*/
{
#ifndef UNDER_CE
HRESULT hr ;
#endif //UNDER_CE
DEMUX_PIN_RECORD ** ppPins ;
DWORD cElements ;
int start, end, index ;
int r ;
O_TRACE_ENTER_2 (TEXT ("CMPEG2Demultiplexer::FindPinRecordLocked_ (%08xh, %08xh)"), pszPinID, ppPinRecord) ;
ASSERT (pszPinID) ;
ASSERT (ppPinRecord) ;
* ppPinRecord = NULL ;
ppPins = m_Pins.GetVector () ;
cElements = m_Pins.GetCount () ;
// check for the obvious cases
if (cElements == 0 ||
ppPins == NULL) {
return E_FAIL ;
}
// binary search of the vector
start = 0 ;
end = cElements - 1 ;
while (start <= end) {
index = (start + end) / 2 ;
ASSERT (ppPins [index] -> pszPinID) ;
// compare
r = _wcsicmp (ppPins [index] -> pszPinID, pszPinID) ;
// found it
if (r == 0) {
* ppPinRecord = ppPins [index] ;
(* ppPinRecord) -> AddRef () ;
if (pdwIndex) {
* pdwIndex = index ;
}
return S_OK ;
}
// search bracket moves up
else if (r < 0) {
start = index + 1 ;
}
// search bracket moves down
else {
end = index - 1 ;
}
}
// did not find it
return E_FAIL ;
}
HRESULT
CMPEG2Demultiplexer::DeleteAllOutputPins_ (
)
{
HRESULT hr ;
DEMUX_PIN_RECORD * pPin ;
O_TRACE_ENTER_0 (TEXT("CMPEG2Demultiplexer::DeleteAllOutputPins_ ()")) ;
hr = S_OK ;
pPin = m_Pins [0] ;
while (pPin &&
SUCCEEDED (hr)) {
pPin -> AddRef () ;
hr = DeletePin_ (pPin -> pszPinID) ;
pPin -> Release () ;
pPin = m_Pins [0] ;
}
return hr ;
}
HRESULT
CMPEG2Demultiplexer::CreateInputPinLocked_ (
)
/*++
locks held:
filter lock
--*/
{
HRESULT hr ;
O_TRACE_ENTER_0 (TEXT("CMPEG2Demultiplexer::CreateInputPinLocked_ ()")) ;
// condition should have been filtered before calling into this method
ASSERT (m_pInputPin == NULL) ;
ASSERT (m_pStats) ;
// try to create it
hr = S_OK ;
m_pInputPin = new CMPEG2DemuxInputPin (
NAME ("CMPEG2DemuxInputPin"),
this,
reinterpret_cast (m_pLock),
m_pcrtSeekingLock,
m_pStats,
& hr
) ;
// and check for the possible errors
if (m_pInputPin == NULL) {
TRACE_ERROR_0 (TEXT ("CMPEG2Demultiplexer::CreateInputPinLocked_ () : out of memory error")) ;
hr = E_OUTOFMEMORY ;
}
else if (FAILED (hr)) {
TRACE_ERROR_0 (TEXT ("CMPEG2Demultiplexer::CreateInputPinLocked_ () : & hr returned an error; resetting")) ;
RELEASE_AND_CLEAR (m_pInputPin) ;
}
return hr ;
}
HRESULT
CMPEG2Demultiplexer::SetPinMediaTypeLocked_ (
IN CBasePin * pPin,
IN AM_MEDIA_TYPE * pNewMediaType
)
/*++
locks held:
filter lock
--*/
{
HRESULT hr ;
CMediaType mt ;
IPin * pIConnectedInputPin ;
IMediaSample2 * pIMediaSample2 ;
IMediaSample * pIMediaSample ;
AM_SAMPLE2_PROPERTIES Prop2 ;
CMPEG2DemuxOutputPin * pOutputPin ;
O_TRACE_ENTER_2 (TEXT("CMPEG2Demultiplexer::SetPinMediaTypeLocked_ (%08xh, %08xh)"), pPin, pNewMediaType) ;
ASSERT (pPin) ;
ASSERT (pNewMediaType) ;
hr = S_OK ;
pOutputPin = static_cast (pPin) ;
if (pOutputPin -> IsConnected ()) {
// we're connected; need to make sure that (1) the connected
// pin will accept the new media type, (2) if #1 is satisfied
// to make it happen
pIConnectedInputPin = pOutputPin -> GetConnected () ;
// XXXX: possible race here..
ASSERT (pIConnectedInputPin) ;
pIConnectedInputPin -> AddRef () ;
// check if the connected pin will outright fail the request
hr = pIConnectedInputPin -> QueryAccept (pNewMediaType) ;
if (SUCCEEDED (hr)) {
if (IsStopped ()) {
// filter is stopped; simple: reconnect
hr = ReconnectPin (
pOutputPin,
pNewMediaType
) ;
}
else {
// filter is running, so we need to send a media sample
// across with the new media type
// we get a media sample, and send it across, with 0-length, but
// the new media type
// get a media sample
ASSERT (pOutputPin -> m_pAllocator) ;
hr = pOutputPin -> m_pAllocator -> GetBuffer (
& pIMediaSample,
NULL,
NULL,
FALSE
) ;
if (SUCCEEDED (hr)) {
// get the IMediaSample2 interface
ASSERT (pIMediaSample) ;
hr = pIMediaSample -> QueryInterface (IID_IMediaSample2, (void **) & pIMediaSample2) ;
if (SUCCEEDED (hr)) {
ASSERT (pIMediaSample2) ;
ASSERT (pIMediaSample2 -> GetActualDataLength () == 0) ;
// get the properties
hr = pIMediaSample2 -> GetProperties (
sizeof Prop2,
(BYTE *) & Prop2
) ;
if (SUCCEEDED (hr)) {
// and flag the sample properties as a type change
Prop2.dwSampleFlags = AM_SAMPLE_TYPECHANGED ;
Prop2.pMediaType = pNewMediaType ;
// send it downstream
hr = pOutputPin -> SendSample (pIMediaSample) ;
}
pIMediaSample2 -> Release () ;
}
pIMediaSample -> Release () ;
}
}
}
pIConnectedInputPin -> Release () ;
}
// if we are connected, and the connected pin has been updated
// we now update our pin; if we are not connected, update our
// pin now (hr is still S_OK)
if (SUCCEEDED (hr)) {
mt = (* pNewMediaType) ;
hr = pOutputPin -> SetMediaType (& mt) ;
// XXXX: need to deal with the case where media type is
// changing to/from MEDIATYPE_MPEG2_PSI; any buffer
// source that is currently active, will need to know
// about this change, so it can start/stop tagging the
// the media samples with Streams
if (SUCCEEDED (hr)) {
(static_cast (pOutputPin)) -> m_MediaType = mt ;
}
}
return hr ;
}
HRESULT
CMPEG2Demultiplexer::CreateStreamMap_ (
IN DWORD cStream,
IN DWORD * pdwStream,
IN LPWSTR pszPinID,
IN DWORD dwMediaSampleContent,
IN DWORD dwStreamMask,
IN LPVOID pvParserArg
)
{
HRESULT hr ;
DEMUX_PIN_RECORD * pPinRecord ;
CMediaType mtPin ;
O_TRACE_ENTER_5 (
TEXT("CMPEG2Demultiplexer::CreateStreamMap_ (%08xh, %08xh, %08xh, %08xh, %08xh)"),
cStream,
pdwStream,
pszPinID,
dwMediaSampleContent,
dwStreamMask
) ;
ASSERT (cStream > 0) ;
ASSERT (pdwStream) ;
ASSERT (pszPinID) ;
ASSERT (wcslen (pszPinID) > 0) ;
pPinRecord = NULL ;
LockFilter () ;
// get the pin
pPinRecord = NULL ;
hr = FindPinRecordLocked_ (
pszPinID,
& pPinRecord
) ;
if (FAILED (hr)) {
goto cleanup ;
}
// make sure pin is an output pin
if (pPinRecord -> PinDirection != PINDIR_OUTPUT) {
// caller is confused
hr = E_FAIL ;
goto cleanup ;
}
ASSERT (pPinRecord) ;
ASSERT (pPinRecord -> pDemuxPin) ;
ASSERT (m_pMPEG2Controller) ;
hr = pPinRecord -> pDemuxPin -> GetMediaType (
0, // output pins have just 1 media type
& mtPin
) ;
if (hr != S_OK) {
// no media type .. ?
goto cleanup ;
}
// and map the stream(s)
hr = MapStreamToPinLocked_ (
cStream,
pdwStream,
dwStreamMask,
reinterpret_cast (pPinRecord -> pDemuxPin),
dwMediaSampleContent,
pvParserArg
) ;
cleanup :
// unlock the pin vector and release our ref to the pin record
UnlockFilter () ;
RELEASE_AND_CLEAR (pPinRecord) ;
return hr ;
}
HRESULT
CMPEG2Demultiplexer::DeleteStreamMap_ (
IN DWORD cStream,
IN DWORD * pdwStream,
IN LPWSTR pszPinID,
IN DWORD dwStreamMask
)
{
HRESULT hr ;
DEMUX_PIN_RECORD * pPinRecord ;
O_TRACE_ENTER_4 (
TEXT("CMPEG2Demultiplexer::DeleteStreamMap_ (%08xh, %08xh, %08xh, %08xh)"),
cStream,
pdwStream,
pszPinID,
dwStreamMask
) ;
pPinRecord = NULL ;
LockFilter () ;
pPinRecord = NULL ;
hr = FindPinRecordLocked_ (
pszPinID,
& pPinRecord
) ;
if (FAILED (hr)) {
goto cleanup ;
}
ASSERT (pPinRecord) ;
ASSERT (pPinRecord -> pDemuxPin) ;
// and unmap the PID(s)
hr = UnmapStreamFromPinLocked_ (
cStream,
pdwStream,
dwStreamMask,
reinterpret_cast (pPinRecord -> pDemuxPin)
) ;
cleanup :
RELEASE_AND_CLEAR (pPinRecord) ;
UnlockFilter () ;
return hr ;
}
HRESULT
CMPEG2Demultiplexer::BeginFlushLocked (
)
/*++
locks held:
receiver, filter
--*/
{
HRESULT hr ;
DEMUX_PIN_RECORD ** ppPinRecord ;
DWORD cPins ;
O_TRACE_ENTER_0 (TEXT("CMPEG2Demultiplexer::BeginFlushLocked ()")) ;
hr = S_OK ;
// then deliver it to all the filter's output pins
m_Pins.GetVector (& ppPinRecord, & cPins) ;
for (DWORD i = 0; i < cPins; i++) {
ASSERT (ppPinRecord) ;
hr = GET_OUTPUT_PIN (ppPinRecord [i]) -> DeliverBeginFlush () ;
if (hr == VFW_E_NOT_CONNECTED) {
hr = S_OK ;
}
// if we see another type of failure abort the procedure
else if (FAILED (hr)) {
break ;
}
}
return hr ;
}
HRESULT
CMPEG2Demultiplexer::EndFlushLocked (
)
/*++
locks held:
receiver, filter
--*/
{
HRESULT hr ;
DEMUX_PIN_RECORD ** ppPinRecord ;
DWORD cPins ;
O_TRACE_ENTER_0 (TEXT("CMPEG2Demultiplexer::EndFlushLocked ()")) ;
// initialize
hr = S_OK ;
m_Pins.GetVector (& ppPinRecord, & cPins) ;
for (DWORD i = 0; i < cPins; i++) {
ASSERT (ppPinRecord) ;
hr = GET_OUTPUT_PIN (ppPinRecord [i]) -> DeliverEndFlush () ;
if (hr == VFW_E_NOT_CONNECTED) {
hr = S_OK ;
}
// if we see another type of failure abort the procedure
else if (FAILED (hr)) {
break ;
}
}
if (SUCCEEDED (hr)) {
// if we've got an input stream discontinuity, we've got a controller;
// this call configures all the parsers to send a discontinuity downstream
ASSERT (m_pMPEG2Controller) ;
hr = m_pMPEG2Controller -> Initialize () ;
}
return hr ;
}
HRESULT
CMPEG2Demultiplexer::InputStreamDiscontinuity (
)
{
HRESULT hr ;
O_TRACE_ENTER_0 (TEXT("CMPEG2Demultiplexer::InputStreamDiscontinuity ()")) ;
LockReceive () ;
LockFilter () ;
hr = m_pMPEG2Controller -> OnInputStreamDiscontinuity () ;
UnlockFilter () ;
UnlockReceive () ;
return hr ;
}
STDMETHODIMP
CMPEG2Demultiplexer::SetSyncSource (
IN IReferenceClock * pIRefClock
)
{
HRESULT hr ;
O_TRACE_ENTER_0 (TEXT("CMPEG2Demultiplexer::SetSyncSource ()")) ;
hr = CBaseFilter::SetSyncSource (pIRefClock) ;
if (SUCCEEDED (hr) &&
m_pMPEG2PushClock) {
m_pMPEG2PushClock -> SetGraphClock (pIRefClock) ;
}
return hr ;
}
HRESULT
CMPEG2Demultiplexer::InitPullModeStream (
IN IAsyncReader * pIAsyncReader
)
{
HRESULT hr ;
O_TRACE_ENTER_0 (TEXT("CMPEG2Demultiplexer::InitPullModeStream ()")) ;
ASSERT (CanOperateInPullMode ()) ;
ASSERT (m_pMPEG2Controller) ;
hr = (m_pMPEG2Controller -> InitPullModeStream (pIAsyncReader)) ;
if (FAILED (hr)) {
// make sure everything gets reset if we fail
m_pMPEG2Controller -> Reset () ;
DeleteAllOutputPins_ () ;
}
return hr ;
}
BOOL
CMPEG2Demultiplexer::CanOperateInPullMode (
)
{
O_TRACE_ENTER_0 (TEXT("CMPEG2Demultiplexer::CanOperateInPullMode ()")) ;
if (m_pMPEG2Controller) {
return m_pMPEG2Controller -> CanOperateInPullMode () ;
}
else {
return FALSE ;
}
}
BOOL
CMPEG2Demultiplexer::IsInPullMode (
)
{
O_TRACE_ENTER_0 (TEXT("CMPEG2Demultiplexer::IsInPullMode ()")) ;
return (m_pMPEG2Controller ? m_pMPEG2Controller -> IsInPullMode () : FALSE) ;
}
HRESULT
CMPEG2Demultiplexer::BreakConnectInput (
)
{
HRESULT hr ;
O_TRACE_ENTER_0 (TEXT("CMPEG2Demultiplexer::BreakConnectInput ()")) ;
// reset controller ??
if (m_pMPEG2Controller &&
m_pMPEG2Controller -> IsInPullMode ()) {
// we're in pull mode; reset the controller
hr = m_pMPEG2Controller -> Reset () ;
DeleteAllOutputPins_ () ;
}
else {
hr = S_OK ;
}
return hr ;
}
void
CMPEG2Demultiplexer::SetTotalFileLength (IN LONGLONG ll)
{
O_TRACE_ENTER_1 (TEXT("CMPEG2Demultiplexer::SetTotalFileLength (%I64d)"), ll) ;
m_pSeekingCore -> SetTotalFileLength (ll) ;
}
LONGLONG
CMPEG2Demultiplexer::GetTotalFileLength ()
{
O_TRACE_ENTER_0 (TEXT("CMPEG2Demultiplexer::GetTotalFileLength ()")) ;
return m_pSeekingCore -> GetTotalFileLength () ;
}
void
CMPEG2Demultiplexer::SetTotalFileDuration (IN REFERENCE_TIME rt)
{
O_TRACE_ENTER_1 (TEXT("CMPEG2Demultiplexer::SetTotalFileDuration (%I64d)"), rt) ;
m_pSeekingCore -> SetTotalFileDuration (rt) ;
}
REFERENCE_TIME
CMPEG2Demultiplexer::GetTotalFileDuration ()
{
O_TRACE_ENTER_0 (TEXT("CMPEG2Demultiplexer::GetTotalFileDuration ()")) ;
return m_pSeekingCore -> GetTotalFileDuration () ;
}
BOOL
CMPEG2Demultiplexer::IsSeekable (
)
{
BOOL r ;
O_TRACE_ENTER_0 (TEXT("CMPEG2Demultiplexer::IsSeekable ()")) ;
if (m_pMPEG2Controller) {
r = m_pMPEG2Controller -> IsInPullMode () ;
}
else {
r = FALSE ;
}
return r ;
}
BOOL
CMPEG2Demultiplexer::IsSeekingPin (
IN CMPEG2DemuxOutputPin * pOutputPin
)
/*++
Description:
Method returns TRUE/FALSE if the specified pin is a seeking pin. Per
session (state pause or run) we have 1 seeking pin on the filter. By
default, the video pin is considered seeking. If there is no video pin
the first pin on the list of output pins is considered the seeking pin.
Parameters:
pOutputPin output pin
Return Values:
TRUE specified pin is the seeking pin
FALSE specified pin is not the seeking pin
Notes:
Filter lock must be held for this call.
--*/
{
#ifndef UNDER_CE
HRESULT hr ;
#endif //UNDER_CE
BOOL r ;
DWORD i ;
DEMUX_PIN_RECORD * pPinEach ;
O_TRACE_ENTER_0 (TEXT("CMPEG2Demultiplexer::IsSeekingPin ()")) ;
// should be seekable, or should not be getting called
ASSERT (IsSeekable ()) ;
// if we're not connected, who's calling ??
ASSERT (pOutputPin -> IsConnected ()) ;
if (pOutputPin -> IsVideo ()) {
// this is the video pin; simple
// note: if we are in pull mode, we created our own output pins
// based on our analysis of the input, and we stoppeda after
// creating 1 video pin at most, so we know we won't have
// multiple video pins
r = TRUE ;
}
else {
// wasn't the video pin; first we'll look for a video pin in our list;
// if none is found, the first pin the list is designated seeking
// pin
// filter lock should be held, so the vector should not be "in
// flux"
for (i = 0;;i++) {
pPinEach = m_Pins [i] ;
if (pPinEach &&
GET_OUTPUT_PIN (pPinEach) &&
GET_OUTPUT_PIN (pPinEach) -> IsVideo ()) {
// found an output pin that is video & not the specified pin;
// fail the call
r = FALSE ;
break ;
}
else if (!pPinEach) {
// we processed the entire list of pins and did not find a
// video pin; is the specified pin the first in the list ?
// if so, it's the designated seeking pin; we have at least 1
// pin, or who'd be calling ??
if (m_Pins [0]) {
r = (GET_OUTPUT_PIN (m_Pins [0]) == pOutputPin ? TRUE : FALSE) ;
}
else {
r = FALSE ;
}
break ;
}
// processed an output pin that wasn't video; try the next
}
}
return r ;
}
void
CMPEG2Demultiplexer::SetPlaybackRate (
IN double dPlaybackRate
)
{
O_TRACE_ENTER_0 (TEXT("CMPEG2Demultiplexer::SetPlaybackRate ()")) ;
ASSERT (m_pMPEG2PushClock) ;
m_pMPEG2PushClock -> SetPlaybackRate (dPlaybackRate) ;
}
void
CMPEG2Demultiplexer::SetStrideLengths (
IN int iPreStrideLength,
IN int iPostStrideLength
)
{
if (m_pMPEG2Controller) {
m_pMPEG2Controller -> SetStrideLengths (
iPreStrideLength,
iPostStrideLength
) ;
}
}
// ---------------------------------------------------------------------------
// CDShowMPEG2Demux
// ---------------------------------------------------------------------------
CDShowMPEG2Demux::CDShowMPEG2Demux (
IN CMPEG2Demultiplexer * punk // controlling unknown, always the filter
) : m_NewPinID (1),
m_pMPEG2Demux (punk)
{
O_TRACE_ENTER_1 (TEXT ("CDShowMPEG2Demux::CDShowMPEG2Demux (%08xh)"), punk) ;
TRACE_CONSTRUCTOR (TEXT ("CDShowMPEG2Demux")) ;
ASSERT (punk) ;
}
CDShowMPEG2Demux::~CDShowMPEG2Demux (
)
{
TRACE_DESTRUCTOR (TEXT ("CDShowMPEG2Demux")) ;
}
// IUnknown
STDMETHODIMP
CDShowMPEG2Demux::QueryInterface (
IN REFIID riid,
OUT void ** ppv
)
{
// delegate always
return m_pMPEG2Demux -> QueryInterface (riid, ppv) ;
}
STDMETHODIMP_(ULONG)
CDShowMPEG2Demux::AddRef (
)
{
// delegate always
return m_pMPEG2Demux -> AddRef () ;
}
STDMETHODIMP_(ULONG)
CDShowMPEG2Demux::Release (
)
{
// hmm.. if we return what the demuxfilter returns, could be the case where
// the demux filter destroys itself, and thus destroys this object (if we're
// in the destructor), and the code then comes back out through here ..
// could this happen .. ? The remedy is to not explicitely delete this object
// from the filter's destructor, but instead to examine the value of the
// the Release call to the filter and delete self if it's 0
// delegate always
return m_pMPEG2Demux -> Release () ;
}
STDMETHODIMP
CDShowMPEG2Demux::GetPages (
CAUUID * pPages
)
{
DWORD Mpeg2StreamType ;
if (!pPages) {
return E_POINTER ;
}
Mpeg2StreamType = m_pMPEG2Demux -> GetStreamType () ;
// property pages depend on the type of streaming we will do
switch (Mpeg2StreamType)
{
case MPEG2_STREAM_TRANSPORT :
case MPEG2_STREAM_PROGRAM :
pPages -> cElems = 2 ;
break ;
case MPEG2_STREAM_UNDEFINED :
default :
pPages -> cElems = 1 ;
break ;
}
pPages -> pElems = (GUID *) CoTaskMemAlloc (pPages -> cElems * sizeof GUID) ;
if (pPages -> pElems == NULL) {
return E_OUTOFMEMORY;
}
// always this one
(pPages -> pElems) [0] = CLSID_MPEG2DemuxPropOutputPins ;
if (Mpeg2StreamType == MPEG2_STREAM_TRANSPORT) {
(pPages -> pElems) [1] = CLSID_MPEG2DemuxPropPIDMap ;
}
else if (Mpeg2StreamType == MPEG2_STREAM_PROGRAM) {
(pPages -> pElems) [1] = CLSID_MPEG2DemuxPropStreamIdMap ;
}
return S_OK ;
}