//------------------------------------------------------------------------------ // // 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: pin_out.cpp Abstract: This module contains the output pin code Revision History: 02-Jul-1999 created XXXX: for now we use the pin lock in everything --*/ #include "precomp.h" #include // for ksmedia.h #include // for bdamedia.h #include // for KSDATAFORMAT_TYPE_MPEG2_SECTIONS #include "mp2demux.h" #include "tsstats.h" #include "mp2seek.h" #include "pin_out.h" #include "filter.h" // disable so we can use 'this' in the initializer list #pragma warning (disable:4355) CMPEG2DemuxOutputPin::CMPEG2DemuxOutputPin ( IN AM_MEDIA_TYPE * pmt, IN LPWSTR pszPinName, IN TCHAR * pObjectName, IN CMPEG2Demultiplexer * pOwningFilter, IN CRefCountedCritSec * pcrtFilterLock, IN CMpeg2DemuxMediaSeekingCore * pSeekingCore, IN CRefCountedCritSec * pcrtSeekingLock, IN CMpeg2Stats * pStats, IN LONG lBatchSize, IN BOOL fBatchExact, IN DWORD dwMediaSampleLen, IN DWORD dwMediaSamplePool, OUT HRESULT * pHr ) : CBaseOutputPin (pObjectName, pOwningFilter, pcrtFilterLock, pHr, pszPinName ), m_MediaType (* pmt), m_pOutputQueue (NULL), m_cRefcount (1), m_pStats (pStats), m_lQueueSize (DEFAULT_OUTPUT_PIN_BUFFER_POOL), m_lBatchSize (lBatchSize), m_fBatchExact (fBatchExact), m_dwMediaSampleLen (dwMediaSampleLen), m_dwMediaSamplePool (dwMediaSamplePool), m_pMpeg2DemuxFilter (pOwningFilter), m_Seek (this, m_pMpeg2DemuxFilter, pSeekingCore, pcrtSeekingLock ), m_pcrtSeekingLock (pcrtFilterLock), m_pSeekingCore (pSeekingCore), m_lFlushingRef (0), m_dwThreadPriority(248 + THREAD_PRIORITY_NORMAL) { O_TRACE_ENTER_0 (TEXT("CMPEG2DemuxOutputPin::CMPEG2DemuxOutputPin ()")) ; TRACE_CONSTRUCTOR (TEXT ("CMPEG2DemuxOutputPin")) ; ASSERT (pcrtFilterLock) ; pcrtFilterLock -> AddRef () ; ASSERT (m_pStats) ; m_pStats -> AddRef () ; ASSERT (m_pcrtSeekingLock) ; m_pcrtSeekingLock -> AddRef () ; HKEY hKey; if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CLASSES_ROOT, TEXT("CLSID\\{AFB6C280-2C41-11D3-8A60-0000F81E0E4A}\\Pins\\MPEG-2 Stream\\"), 0, 0, &hKey)) { DWORD dwType, dwSize, dwValue; // Use default value if the value isn't defined, or is invalid dwSize = sizeof (DWORD); if (ERROR_SUCCESS == RegQueryValueEx (hKey, TEXT("ThreadPriority"), NULL, &dwType, LPBYTE(&dwValue), &dwSize) && REG_DWORD == dwType && dwValue < 256) { m_dwThreadPriority = dwValue; } RegCloseKey (hKey); } // don't addref the demux or we'd get circular refs #ifdef DEBUG // need to do this because my pin starts with a refcount of 1, whereas all the // base class assumes starting with 0, and explicitely AddRef'ing to pass out // the first reference m_cRef = 1 ; #endif TRACE_5 (LOG_DEMUX_PINS, 4, TEXT ("Output pin created (%s)\n\tMedia sample details\n\t\tlen = %u bytes\n\t\tpool = %u\n\t\tbatch = %u\n\t\tbatch exact = %s"), m_pName, m_dwMediaSampleLen, m_dwMediaSamplePool, m_lBatchSize, m_fBatchExact ? TEXT ("TRUE") : TEXT ("FALSE")) ; } CMPEG2DemuxOutputPin::~CMPEG2DemuxOutputPin ( void ) { O_TRACE_ENTER_0 (TEXT("CMPEG2DemuxOutputPin::~CMPEG2DemuxOutputPin ()")) ; TRACE_DESTRUCTOR (TEXT ("CMPEG2DemuxOutputPin")) ; delete m_pOutputQueue ; // done with the filter lock ASSERT (m_pLock) ; (reinterpret_cast (m_pLock)) -> Release () ; // done with the seeking lock m_pcrtSeekingLock -> Release () ; ASSERT (m_pStats) ; m_pStats -> Release () ; } STDMETHODIMP_(ULONG) CMPEG2DemuxOutputPin::NonDelegatingAddRef ( ) { O_TRACE_ENTER_0 (TEXT("CMPEG2DemuxOutputPin::NonDelegatingAddRef ()")) ; #ifdef DEBUG // Update the debug only variable maintained by the base class m_cRef++; ASSERT(m_cRef > 0); #endif return InterlockedIncrement (& m_cRefcount) ; } STDMETHODIMP_(ULONG) CMPEG2DemuxOutputPin::NonDelegatingRelease() { O_TRACE_ENTER_0 (TEXT("CMPEG2DemuxOutputPin::NonDelegatingRelease ()")) ; #ifdef DEBUG // Update the debug only variable in CBasePin m_cRef--; ASSERT(m_cRef >= 0); #endif if (InterlockedDecrement (& m_cRefcount) == 0) { delete this ; return 0 ; } ASSERT (m_cRefcount > 0) ; return m_cRefcount ; } STDMETHODIMP CMPEG2DemuxOutputPin::NonDelegatingQueryInterface ( IN REFIID riid, IN LPVOID * ppv ) { HRESULT hr ; // ------------------------------------------------------------------------ // IMPEG2PIDMap; this interface is used when the filter is processing a // transport stream if (riid == IID_IMPEG2PIDMap && !(static_cast (m_pFilter) -> IsInPullMode ())) { // transport interface; set the stream type to see if we return the interface or not hr = static_cast (m_pFilter) -> SetStreamType (MPEG2_STREAM_TRANSPORT) ; if (SUCCEEDED (hr)) { return GetInterface ( (IMPEG2PIDMap *) this, ppv ) ; } else { // failed to load, for whatever reason; thus we "don't implement" // the interface return E_NOINTERFACE ; } } // ------------------------------------------------------------------------ // IMPEG2StreamIdMap; this interface is used when the filter is processing // a program stream else if (riid == IID_IMPEG2StreamIdMap && !(static_cast (m_pFilter) -> IsInPullMode ())) { // transport interface; set the stream type to see if we return the interface or not hr = static_cast (m_pFilter) -> SetStreamType (MPEG2_STREAM_PROGRAM) ; if (SUCCEEDED (hr)) { return GetInterface ( (IMPEG2StreamIdMap *) this, ppv ) ; } else { return E_NOINTERFACE ; } } // ------------------------------------------------------------------------ // IAMPushSource; this is tied to the filter's IReferenceClock, but we // implement it always, regardless of presence/absence of ref clock in // in filter because the graph code seems to cache this interface else if (riid == IID_IAMPushSource && static_cast (m_pFilter) -> ImplementPushClock ()) { return GetInterface ( (IAMPushSource *) this, ppv ) ; } // ------------------------------------------------------------------------ // IMediaSeeking; only implement this if we're seekable else if (riid == IID_IMediaSeeking && static_cast (m_pFilter) -> IsSeekable ()) { return GetInterface ( (IMediaSeeking *) & m_Seek, ppv ) ; } // ------------------------------------------------------------------------ // default else { return CBaseOutputPin::NonDelegatingQueryInterface (riid, ppv) ; } } HRESULT CMPEG2DemuxOutputPin::CheckMediaType ( IN const CMediaType * pmtCheck ) { CAutoLock ca (m_pLock) ; O_TRACE_ENTER_0 (TEXT("CMPEG2DemuxOutputPin::CheckMediaType ()")) ; return (m_MediaType == * pmtCheck) ? S_OK : S_FALSE ; } HRESULT CMPEG2DemuxOutputPin::GetMediaType ( IN int iPosition, OUT CMediaType * pmtGet ) { CAutoLock ca (m_pLock) ; ASSERT (pmtGet) ; O_TRACE_ENTER_2 (TEXT("CMPEG2DemuxOutputPin::GetMediaType (%u, 0x%08x)"), iPosition, pmtGet) ; // we only support 1 media type in the pin - the one we are created // with if (iPosition == 0) { * pmtGet = m_MediaType ; return S_OK ; } else { return VFW_S_NO_MORE_ITEMS ; } } HRESULT CMPEG2DemuxOutputPin::DecideAllocator ( IN IMemInputPin * pPin, IN IMemAllocator ** ppAlloc ) { HRESULT hr ; ALLOCATOR_PROPERTIES AllocatorProperties ; O_TRACE_ENTER_2 (TEXT("CMPEG2DemuxOutputPin::DecideAllocator (0x%08x, 0x%08x)"), pPin, ppAlloc) ; if (m_pMpeg2DemuxFilter -> GetStreamType () == MPEG2_STREAM_TRANSPORT) { // don't insist if we are transport; if we are still undecided, we // will be the allocator hr = CBaseOutputPin::DecideAllocator (pPin, ppAlloc) ; } else { hr = InitAllocator ( ppAlloc ) ; if (SUCCEEDED (hr)) { ZeroMemory (& AllocatorProperties, sizeof AllocatorProperties) ; hr = pPin -> GetAllocatorRequirements (& AllocatorProperties) ; if (SUCCEEDED (hr) || hr == E_NOTIMPL) { if (AllocatorProperties.cbAlign == 0) { AllocatorProperties.cbAlign = 1 ; } hr = DecideBufferSize ((* ppAlloc), & AllocatorProperties) ; if (SUCCEEDED (hr)) { hr = pPin -> NotifyAllocator ((* ppAlloc), FALSE) ; } } } } return hr ; } HRESULT CMPEG2DemuxOutputPin::DecideBufferSize ( IN IMemAllocator * pAlloc, IN ALLOCATOR_PROPERTIES * ppropInputRequest ) { CAutoLock ca (m_pLock) ; ALLOCATOR_PROPERTIES propActual = {0} ; O_TRACE_ENTER_2 (TEXT("CMPEG2DemuxOutputPin::DecideBufferSize (0x%08x, 0x%08x)"), pAlloc, ppropInputRequest) ; ASSERT (pAlloc) ; ASSERT (ppropInputRequest) ; ppropInputRequest -> cbBuffer = Max (ppropInputRequest -> cbBuffer, m_dwMediaSampleLen) ; ppropInputRequest -> cBuffers = (ppropInputRequest -> cBuffers > 0 ? ppropInputRequest -> cBuffers : m_dwMediaSamplePool) ; ppropInputRequest -> cbAlign = 1 ; ppropInputRequest -> cbPrefix = ppropInputRequest -> cbPrefix ; // output queue length max m_lQueueSize = ppropInputRequest -> cBuffers ; return pAlloc -> SetProperties (ppropInputRequest, & propActual) ; } HRESULT CMPEG2DemuxOutputPin::Active ( ) { HRESULT hr ; CAutoLock ca (m_pLock) ; O_TRACE_ENTER_0 (TEXT("CMPEG2DemuxOutputPin::Active ()")) ; //m_bDiscontinuity = TRUE ; m_lFlushingRef = 0 ; if (!IsConnected ()) { return S_OK ; } // call this first hr = CBaseOutputPin::Active () ; if (SUCCEEDED (hr)) { // we're set to go; instantiate our output queue ASSERT (m_pOutputQueue == NULL) ; ASSERT (IsConnected ()) ; // HACKHACKHACKHACKHACKHACKHACK // this fixes the problem where calling receive multiple on KSP // seems to lose about half the media samples if (m_mt.majortype == KSDATAFORMAT_TYPE_MPEG2_SECTIONS && m_mt.subtype == MEDIASUBTYPE_None && m_mt.formattype == FORMAT_None) { // don't batch m_pOutputQueue = new COutputQueue ( GetConnected (), // input pin & hr, // return value FALSE, // auto detect TRUE, // send directly 1, // batch size FALSE, // exact batch 1, // queue size m_dwThreadPriority) ; } else { // batch m_pOutputQueue = new COutputQueue ( GetConnected (), // input pin & hr, // return value TRUE, // auto detect TRUE, // send directly m_lBatchSize, // batch size m_fBatchExact, // exact batch m_lQueueSize, // queue size m_dwThreadPriority) ; } if (m_pOutputQueue == NULL) { hr = E_OUTOFMEMORY ; } } return hr ; } HRESULT CMPEG2DemuxOutputPin::Inactive ( ) { HRESULT hr ; CAutoLock ca (m_pLock) ; O_TRACE_ENTER_0 (TEXT("CMPEG2DemuxOutputPin::Inactive ()")) ; hr = CBaseOutputPin::Inactive () ; if (SUCCEEDED (hr)) { DELETE_RESET (m_pOutputQueue) ; } return hr ; } HRESULT CMPEG2DemuxOutputPin::SendSample ( IN IMediaSample * pIMediaSample ) /*++ the media sample that enters into this method has 1 refcount that belongs to the buffer source; this method MUST make sure that this refcount is kept across the method of delivery --*/ { HRESULT hr ; REFERENCE_TIME rtSegmentStart ; REFERENCE_TIME rtSegmentStop ; double dSegmentRate ; // // should not be holding the mapper lock across the call // m_pLock -> Lock () ; if (m_pOutputQueue) { // check if we've just been seeked and need to pass a new segment // notification downstream if (pIMediaSample -> IsDiscontinuity () == S_OK && static_cast (m_pFilter) -> IsSeekable ()) { // discontinuity follows a seeking call; send a segment // downstream #ifdef DEBUG REFERENCE_TIME rtPTSStart ; REFERENCE_TIME rtPTSStop ; hr = pIMediaSample -> GetTime (& rtPTSStart, & rtPTSStop) ; if (FAILED (hr)) { TRACE_0 (LOG_TRACE, 3, TEXT ("no timestamp on first media sample following seek")) ; } else { TRACE_1 (LOG_TRACE, 2, TEXT ("new segment start = %I64d"), rtPTSStart) ; } #endif ASSERT (m_pSeekingCore) ; hr = m_pSeekingCore -> RecvThreadGetSegmentValues ( & rtSegmentStart, & rtSegmentStop, & dSegmentRate ) ; if (SUCCEEDED (hr)) { // pass the new segment downstream m_pOutputQueue -> NewSegment ( rtSegmentStart, rtSegmentStop, dSegmentRate ) ; TRACE_4 ( LOG_TRACE, 3, TEXT ("New Segment returned : start = %-10I64d, stop = %-10I64d, rate = %-2.1f, rtPTSStart = %-10I64d"), rtSegmentStart, rtSegmentStop, dSegmentRate, rtPTSStart ) ; } // don't fail the SendSample() call because we failed to send a // newsegment downstream hr = S_OK ; } #ifdef DEBUG REFERENCE_TIME rtPTSStart2 ; REFERENCE_TIME rtPTSStop2 ; hr = pIMediaSample -> GetTime (& rtPTSStart2, & rtPTSStop2) ; if (hr == S_OK) { TRACE_3 (LOG_TIMING, 5, TEXT ("sending: [%08xh] start = %I64d (%I64d ms)"), this, rtPTSStart2, rtPTSStart2 / 10000) ; } #endif // COutputQueue Release's the media sample's refcount after this // call completes, so we make sure that we keep 1 count across the // call pIMediaSample -> AddRef () ; hr = m_pOutputQueue -> Receive (pIMediaSample) ; NE_SPEW (hr, S_OK, TEXT ("m_pOutputQueue -> Receive (pIMediaSample)")) ; // dshow has an annoying habit of failing a call with a non-FAILED // HRESULT i.e. high bit is not set; we want to accurately send back // the success/failure of the above call to the caller of this method, // so we set it here hr = (hr == S_OK ? S_OK : (FAILED (hr) ? hr : E_FAIL)) ; } else { // we're not connected; drop it and return a failure hr = VFW_E_NOT_CONNECTED ; } if (SUCCEEDED (hr)) { ASSERT (m_pStats) ; m_pStats -> Global_MediaSampleOut () ; } m_pLock -> Unlock () ; return hr ; } HRESULT CMPEG2DemuxOutputPin::DeliverBeginFlush ( ) { HRESULT hr ; O_TRACE_ENTER_0 (TEXT("CMPEG2DemuxOutputPin::DeliverBeginFlush ()")) ; m_pLock -> Lock () ; if (m_pOutputQueue) { if (m_lFlushingRef == 0) { ASSERT (IsConnected ()) ; m_pOutputQueue -> BeginFlush () ; TRACE_0 (LOG_TRACE, 1, TEXT ("BeginFlush")) ; } m_lFlushingRef++ ; hr = S_OK ; } else if (!IsConnected ()) { hr = VFW_E_NOT_CONNECTED ; } else { hr = S_OK ; } m_pLock -> Unlock () ; return hr ; } HRESULT CMPEG2DemuxOutputPin::DeliverEndFlush ( ) { HRESULT hr ; O_TRACE_ENTER_0 (TEXT("CMPEG2DemuxOutputPin::DeliverEndFlush ()")) ; m_pLock -> Lock () ; if (m_pOutputQueue) { ASSERT (m_lFlushingRef > 0) ; m_lFlushingRef-- ; if (m_lFlushingRef == 0) { ASSERT (IsConnected ()) ; m_pOutputQueue -> EndFlush () ; TRACE_0 (LOG_TRACE, 1, TEXT ("EndFlush")) ; } hr = S_OK ; } else if (!IsConnected ()) { hr = VFW_E_NOT_CONNECTED ; } else { hr = S_OK ; } m_pLock -> Unlock () ; return hr ; } HRESULT CMPEG2DemuxOutputPin::DeliverEndOfStream ( ) { #ifndef UNDER_CE HRESULT hr ; #endif //UNDER_CE O_TRACE_ENTER_0 (TEXT("CMPEG2DemuxOutputPin::DeliverEndOfStream ()")) ; if (IsConnected ()) { if (m_pOutputQueue) { m_pOutputQueue -> EOS () ; } return S_OK ; } return VFW_E_NOT_CONNECTED ; } HRESULT CMPEG2DemuxOutputPin::SetMediaType ( IN const CMediaType * pmt ) { HRESULT hr ; hr = CBaseOutputPin::SetMediaType (pmt) ; if (SUCCEEDED (hr)) { ASSERT ((* pmt) == m_mt) ; if (IsAVMediaType (& m_mt)) { // A/V - needs a bigger buffer pool min m_dwMediaSamplePool = Max (m_dwMediaSamplePool, MIN_AV_BUFFER_POOL_SIZE) ; } } return hr ; }