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