//------------------------------------------------------------------------- // // 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: mp2seek.cpp Abstract: This module contains the IMediaSeeking-related class declarations. Revision History: 06-Nov-2000 created Notes: Shamelessly stole/plagiarized/copied from the mpeg-1 splitter's IMediaSeeking implementation. --*/ #include "precomp.h" #include "mp2demux.h" #include "tsstats.h" #include "filter.h" #include "mp2seek.h" #include "pin_out.h" #include "pin_in.h" // throughought: "current" position means "start" position // I've tried to name variables accordingly to remove any confusion CMpeg2DemuxMediaSeekingCore::CMpeg2DemuxMediaSeekingCore ( IN CMPEG2Demultiplexer * pMpeg2Demultiplexer, IN CRefCountedCritSec * pcrtSeekingLock ) : m_pMpeg2Demultiplexer (pMpeg2Demultiplexer), m_pcrtSeekingLock (pcrtSeekingLock), m_guidTimeFormat (TIME_FORMAT_MEDIA_TIME), // not preferred, but we ASSERT in graph code otherwise ! m_llTotalFileLength (UNDEFINED), m_rtTotalFileDuration (UNDEFINED), m_dPlaybackRate (1.0) { ASSERT (m_pMpeg2Demultiplexer) ; ASSERT (m_pcrtSeekingLock) ; m_pcrtSeekingLock -> AddRef () ; } CMpeg2DemuxMediaSeekingCore::~CMpeg2DemuxMediaSeekingCore ( ) { m_pcrtSeekingLock -> Release () ; } LONGLONG CMpeg2DemuxMediaSeekingCore::GetLastRead_ ( ) { REFERENCE_TIME rtLastProcessedStart ; LONGLONG llLastProcessed ; O_TRACE_ENTER_0 (TEXT("CMpeg2DemuxMediaSeekingCore::GetLastRead_ ()")) ; rtLastProcessedStart = m_pMpeg2Demultiplexer -> GetInputPin () -> GetPullPin () -> GetLastProcessedStart () ; if (rtLastProcessedStart != UNDEFINED) { // convert this to something that's meaningful to us llLastProcessed = CStreamPull::CPullPinTimeToBytes (rtLastProcessedStart) ; } else { llLastProcessed = UNDEFINED ; } return llLastProcessed ; } BOOL CMpeg2DemuxMediaSeekingCore::IsSeekable () { O_TRACE_ENTER_0 (TEXT("CMpeg2DemuxMediaSeekingCore::IsSeekable ()")) ; return m_pMpeg2Demultiplexer -> IsSeekable () ; } BOOL CMpeg2DemuxMediaSeekingCore::IsSeekingPin (IN CMPEG2DemuxOutputPin * pPin) { O_TRACE_ENTER_0 (TEXT("CMpeg2DemuxMediaSeekingCore::IsSeekingPin ()")) ; return m_pMpeg2Demultiplexer -> IsSeekingPin (pPin) ; } BOOL CMpeg2DemuxMediaSeekingCore::IsSeekingFormatSupported ( IN const GUID * pFormat ) /*++ Description: Called by the seeking pin in response to an IMediaSeeking format query. Parameters: pFormat The following are defined in MSDN: TIME_FORMAT_NONE TIME_FORMAT_FRAME TIME_FORMAT_SAMPLE TIME_FORMAT_FIELD TIME_FORMAT_BYTE TIME_FORMAT_MEDIA_TIME This list is not restrictive however. MSDN states that 3rd parties are encouraged to define their own GUIDs for seeking granularities. Return Values: TRUE format is supported FALSE format is not supported --*/ { BOOL r ; TRACE_ENTER_0 (TEXT ("CMpeg2DemuxMediaSeekingCore::IsSeekingFormatSupported ()")) ; if ((* pFormat) == TIME_FORMAT_BYTE && m_llTotalFileLength != UNDEFINED) { // byte-based seeking is ok r = TRUE ; } else if ((* pFormat) == TIME_FORMAT_MEDIA_TIME && m_rtTotalFileDuration != UNDEFINED) { // time-based seeking is ok r = TRUE ; } else { // everything else is not ok r = FALSE ; } return r ; } HRESULT CMpeg2DemuxMediaSeekingCore::QueryPreferredSeekingFormat ( OUT GUID * pFormat ) { HRESULT hr ; O_TRACE_ENTER_0 (TEXT("CMpeg2DemuxMediaSeekingCore::QueryPreferredSeekingFormat ()")) ; // caller should have screened for bad parameters ASSERT (pFormat) ; if (m_llTotalFileLength != UNDEFINED) { (* pFormat) = TIME_FORMAT_BYTE ; hr = S_OK ; } else { // if we don't have the file length, we cannot compute duration; // don't even check hr = E_FAIL ; } return hr ; } HRESULT CMpeg2DemuxMediaSeekingCore::SetSeekingTimeFormat ( IN const GUID * pguidTimeFormat ) { HRESULT hr ; O_TRACE_ENTER_0 (TEXT("CMpeg2DemuxMediaSeekingCore::SetSeekingTimeFormat ()")) ; m_pMpeg2Demultiplexer -> LockFilter () ; ASSERT (pguidTimeFormat) ; if (m_pMpeg2Demultiplexer -> IsStopped ()) { if (IsSeekingFormatSupported (pguidTimeFormat)) { // looks ok; set it m_guidTimeFormat = (* pguidTimeFormat) ; hr = S_OK ; } else { // error: not supported hr = E_INVALIDARG ; } } else { // error: gotta be stopped hr = VFW_E_WRONG_STATE ; } m_pMpeg2Demultiplexer -> UnlockFilter () ; return hr ; } HRESULT CMpeg2DemuxMediaSeekingCore::GetSeekingTimeFormat ( OUT GUID * pguidTimeFormat ) { O_TRACE_ENTER_0 (TEXT("CMpeg2DemuxMediaSeekingCore::GetSeekingTimeFormat ()")) ; ASSERT (pguidTimeFormat) ; (* pguidTimeFormat) = m_guidTimeFormat ; return S_OK ; } // BUGBUG: total file duration or start -> stop duration ????? HRESULT CMpeg2DemuxMediaSeekingCore::GetFileDuration ( OUT LONGLONG * pllDuration ) { HRESULT hr ; O_TRACE_ENTER_0 (TEXT("CMpeg2DemuxMediaSeekingCore::GetFileDuration ()")) ; // parameters should be validated upon entry ASSERT (pllDuration) ; ASSERT (IsSeekable ()) ; if (m_guidTimeFormat == TIME_FORMAT_MEDIA_TIME) { ASSERT (m_rtTotalFileDuration != UNDEFINED) ; (* pllDuration) = m_rtTotalFileDuration ; hr = S_OK ; } else if (m_guidTimeFormat == TIME_FORMAT_BYTE) { (* pllDuration) = m_llTotalFileLength ; hr = S_OK ; } else { // strange .. hr = E_FAIL ; } return hr ; } HRESULT CMpeg2DemuxMediaSeekingCore::GetFileStopPosition ( OUT LONGLONG * pllStop ) { O_TRACE_ENTER_0 (TEXT("CMpeg2DemuxMediaSeekingCore::GetFileStopPosition ()")) ; return GetFileDuration (pllStop) ; } HRESULT CMpeg2DemuxMediaSeekingCore::GetFileStartPosition ( OUT LONGLONG * pllStart ) { HRESULT hr ; O_TRACE_ENTER_0 (TEXT("CMpeg2DemuxMediaSeekingCore::GetFileStartPosition ()")) ; // caller should be screening for bad params ASSERT (pllStart) ; if (m_pMpeg2Demultiplexer -> GetInputPin ()) { // should not be calling unless we're seekable i.e. have a pullpin ASSERT (IsSeekable ()) ; ASSERT (m_pMpeg2Demultiplexer -> GetInputPin () -> GetPullPin ()) ; // retrieve pullpin time of last send start (* pllStart) = m_pMpeg2Demultiplexer -> GetInputPin () -> GetPullPin () -> GetStart () ; // this is in cpullpin "units" i.e. read "from mars"; hold nose, and // convert to meaningful value (* pllStart) = CStreamPull::CPullPinTimeToBytes (* pllStart) ; hr = ByteOffsetToCurrentFormat (pllStart) ; } else { // don't even have an input pin, let alone a stream start/stop/Start hr = E_UNEXPECTED ; } return hr ; } HRESULT CMpeg2DemuxMediaSeekingCore::SeekTo ( IN LONGLONG * pllStart ) { HRESULT hr ; LONGLONG llStop ; O_TRACE_ENTER_1 (TEXT("CMpeg2DemuxMediaSeekingCore::SeekTo (%I64d)"), (* pllStart)) ; // begin the strange journey of converting the parameter value to // a "value" cpullpin will understand (extra credit if this is done // without drawing pictures) if (m_pMpeg2Demultiplexer -> GetInputPin ()) { // should not be calling unless we're seekable i.e. have a pullpin ASSERT (IsSeekable ()) ; ASSERT (m_pMpeg2Demultiplexer -> GetInputPin () -> GetPullPin ()) ; // figure out where we'll tell pull pin to stop hr = GetFileDuration (& llStop) ; if (SUCCEEDED (hr)) { hr = SeekTo (pllStart, & llStop) ; } } else { hr = E_UNEXPECTED ; } return hr ; } HRESULT CMpeg2DemuxMediaSeekingCore::SeekTo ( IN LONGLONG * pllStart, IN LONGLONG * pllStop, IN double dPlaybackRate ) { HRESULT hr ; O_TRACE_ENTER_3 ( TEXT("CMpeg2DemuxMediaSeekingCore::SeekTo (%I64d, %i64d, %2.1f)"), (* pllStart), (* pllStop), dPlaybackRate ) ; // begin the strange journey of converting the parameter value to // a "value" cpullpin will understand (extra credit if this is done // without drawing pictures) if (m_pMpeg2Demultiplexer -> GetInputPin ()) { // should not be calling unless we're seekable i.e. have a pullpin ASSERT (IsSeekable ()) ; ASSERT (m_pMpeg2Demultiplexer -> GetInputPin ()) ; // convert pllStop to pull pin offset // first get the byte offset of where we wish to stop hr = CurrentFormatToByteOffset (pllStop) ; if (SUCCEEDED (hr)) { // now translate the byte offset to a pullpin value (* pllStop) = CStreamPull::BytesToCPullPinTime (* pllStop) ; // now convert start // first get the byte offset of where we wish to start hr = CurrentFormatToByteOffset (pllStart) ; if (SUCCEEDED (hr)) { // now translate the byte offset to a pullpin value (* pllStart) = CStreamPull::BytesToCPullPinTime (* pllStart) ; if (SUCCEEDED (hr)) { // call should have validated these .. ASSERT ((* pllStart) <= (* pllStop)) ; // and finally (huffing and puffing), instruct the pin // to go there hr = m_pMpeg2Demultiplexer -> GetInputPin () -> Seek ( (* pllStart), (* pllStop), dPlaybackRate ) ; } } } } else { hr = E_UNEXPECTED ; } return hr ; } HRESULT CMpeg2DemuxMediaSeekingCore::SetFileStopPosition ( IN LONGLONG * pllStop ) { LONGLONG llLastProcessed ; LONGLONG llStart ; HRESULT hr ; O_TRACE_ENTER_1 (TEXT("CMpeg2DemuxMediaSeekingCore::SetFileStopPosition (%I64d)"), (* pllStop)) ; llLastProcessed = GetLastRead_ () ; if (llLastProcessed != UNDEFINED) { if (llLastProcessed >= (* pllStop)) { return E_INVALIDARG ; } } else { // we're stopped, or haven't started yet; compare against the start // time hr = GetFileStartPosition (& llStart) ; if (FAILED (hr) || llStart >= (* pllStop)) { return (FAILED (hr) ? hr : E_INVALIDARG) ; } } // we have a valid stop time; convert and set it hr = CurrentFormatToByteOffset (pllStop) ; if (SUCCEEDED (hr)) { (* pllStop) = CStreamPull::CPullPinTimeToBytes (* pllStop) ; m_pMpeg2Demultiplexer -> GetInputPin () -> GetPullPin () -> SetStop (* pllStop) ; } return hr ; } HRESULT CMpeg2DemuxMediaSeekingCore::SetPlaybackRate ( IN double dPlaybackRate ) { O_TRACE_ENTER_1 (TEXT("CMpeg2DemuxMediaSeekingCore::SetPlaybackRate (%2.1f)"), dPlaybackRate) ; ASSERT (dPlaybackRate > 0.0) ; // per robinsp, this call is preceded by a seek, the filter is stopped, // so we can set the value directly; a correct fix for this really ought // to be at the parser level where the new rate should be logged along // with a discontinuity, then the clock reset so timestamps will be // 0-based, and the parsers just divide the rate into the timestamps; for // now however, we'll maintain the status quo and just do as is done in // in the mpeg-1 splitter: assign a new value m_pMpeg2Demultiplexer -> SetPlaybackRate (dPlaybackRate) ; m_dPlaybackRate = dPlaybackRate ; return S_OK ; } HRESULT CMpeg2DemuxMediaSeekingCore::RecvThreadGetSegmentValues ( OUT REFERENCE_TIME * prtSegmentStart, OUT REFERENCE_TIME * prtSegmentStop, OUT double * pdSegmentRate ) { HRESULT hr ; ASSERT (prtSegmentStart) ; ASSERT (prtSegmentStop) ; ASSERT (pdSegmentRate) ; // don't grab the media seeking lock; downstream threads call into object // to perform seeking operations; if the bracket (start -> stop) changes, // the locking order is such that the seeking lock is grabbed, and the // receiver thread is signaled SYNCHRONOUSLY to pause, the playback // bracket is reset, the receiver thread resumed, and the seeking // lock released; if we (receiver thread) grab the seeking lock, or // rather, block on the seeking lock, with a seeking thread waiting on // us to pause, we get a deadlock; so we don't grab the lock; return // values are valid because they won't be reset unless receiver thread // is paused. // start - in the current units hr = GetFileStartPosition (prtSegmentStart) ; if (FAILED (hr)) { goto cleanup ; } // stop - in the current units hr = GetFileStopPosition (prtSegmentStop) ; if (FAILED (hr)) { goto cleanup ; } // may have to convert to time if (m_guidTimeFormat != TIME_FORMAT_MEDIA_TIME) { // should be a byte offset timeline ASSERT (m_guidTimeFormat == TIME_FORMAT_BYTE) ; ByteOffsetToTime (prtSegmentStart) ; ByteOffsetToTime (prtSegmentStop) ; } // rate (* pdSegmentRate) = GetPlaybackRate () ; cleanup : return hr ; } // ---------------------------------------------------------------------------- // ---------------------------------------------------------------------------- // ---------------------------------------------------------------------------- CMpeg2DemuxMediaSeekingCOM::CMpeg2DemuxMediaSeekingCOM ( IN CMPEG2DemuxOutputPin * pOutputPin, IN CMPEG2Demultiplexer * pMpeg2DemuxFilter, IN CMpeg2DemuxMediaSeekingCore * pSeekingCore, IN CCritSec * pLock ) : m_pOutputPin (pOutputPin), m_pMpeg2DemuxFilter (pMpeg2DemuxFilter), m_guidSeekingFormat (GUID_NULL), m_pLock (pLock), m_pSeekingCore (pSeekingCore) { TRACE_CONSTRUCTOR (TEXT ("CMpeg2DemuxMediaSeekingCOM")) ; ASSERT (m_pOutputPin) ; ASSERT (m_pSeekingCore) ; ASSERT (m_pSeekingCore) ; ASSERT (m_pLock) ; } CMpeg2DemuxMediaSeekingCOM::~CMpeg2DemuxMediaSeekingCOM ( ) { TRACE_DESTRUCTOR (TEXT ("CMpeg2DemuxMediaSeekingCOM")) ; } // ---------------------------------------------------------------------------- // IUnknown interface methods - delegate always STDMETHODIMP CMpeg2DemuxMediaSeekingCOM::QueryInterface ( REFIID riid, void ** ppv ) { return m_pOutputPin -> QueryInterface (riid, ppv) ; } STDMETHODIMP_ (ULONG) CMpeg2DemuxMediaSeekingCOM::AddRef ( ) { return m_pOutputPin -> AddRef () ; } STDMETHODIMP_ (ULONG) CMpeg2DemuxMediaSeekingCOM::Release ( ) { return m_pOutputPin -> Release () ; } // ---------------------------------------------------------------------------- // IMediaSeeking interface methods // Returns the capability flags; S_OK if successful STDMETHODIMP CMpeg2DemuxMediaSeekingCOM::GetCapabilities ( OUT DWORD * pCapabilities ) { O_TRACE_ENTER_0 (TEXT("CMpeg2DemuxMediaSeekingCOM::GetCapabilities ()")) ; if (!pCapabilities) { return E_POINTER ; } // remote chance: QIed for IMediaSeeking when graph was in pull mode, then // broke the input connection, went push mode, ran the graph -- just the // type of scenario testers would find if (!m_pSeekingCore -> IsSeekable ()) { return E_UNEXPECTED ; } return m_pSeekingCore -> GetSeekingCapabilities ( pCapabilities ) ; } // And's the capabilities flag with the capabilities requested. // Returns S_OK if all are present, S_FALSE if some are present, // E_FAIL if none. // * pCababilities is always updated with the result of the // 'and'ing and can be checked in the case of an S_FALSE return // code. STDMETHODIMP CMpeg2DemuxMediaSeekingCOM::CheckCapabilities ( IN OUT DWORD * pCapabilities ) { HRESULT hr ; DWORD dwCapabilities ; O_TRACE_ENTER_0 (TEXT("CMpeg2DemuxMediaSeekingCOM::CheckCapabilities ()")) ; if (!pCapabilities) { return E_POINTER ; } // remote chance: QIed for IMediaSeeking when graph was in pull mode, then // broke the input connection, went push mode, ran the graph -- just the // type of scenario testers would find if (!m_pSeekingCore -> IsSeekable ()) { return E_UNEXPECTED ; } hr = GetCapabilities (& dwCapabilities) ; if (SUCCEEDED(hr)) { dwCapabilities &= (* pCapabilities) ; hr = dwCapabilities ? ( dwCapabilities == (* pCapabilities) ? S_OK : S_FALSE ) : E_FAIL ; (* pCapabilities) = dwCapabilities ; } else { (* pCapabilities) = 0 ; } return hr; } // returns S_OK if mode is supported, S_FALSE otherwise STDMETHODIMP CMpeg2DemuxMediaSeekingCOM::IsFormatSupported ( IN const GUID * pFormat ) { HRESULT hr ; O_TRACE_ENTER_0 (TEXT("CMpeg2DemuxMediaSeekingCOM::IsFormatSupported ()")) ; if (!pFormat) { return E_POINTER ; } // remote chance: QIed for IMediaSeeking when graph was in pull mode, then // broke the input connection, went push mode, ran the graph -- just the // type of scenario testers would find if (!m_pSeekingCore -> IsSeekable ()) { return E_UNEXPECTED ; } // // Designated seeking pin will be video, but we have 1 pin that is the // designated seeking pin; all others don't do much // // However, we need to support TIME_FORMAT_MEDIA_TIME or the graph // code gets confused and starts using IMediaPosition if (m_pSeekingCore -> IsSeekingPin(m_pOutputPin)) { hr = (m_pSeekingCore -> IsSeekingFormatSupported (pFormat) ? S_OK : S_FALSE) ; } else { hr = (pFormat == NULL || (* pFormat) == TIME_FORMAT_MEDIA_TIME ? S_OK : S_FALSE) ; } return hr ; } // S_OK if successful // E_NOTIMPL, E_POINTER if unsuccessful STDMETHODIMP CMpeg2DemuxMediaSeekingCOM::QueryPreferredFormat ( OUT GUID * pFormat ) { HRESULT hr ; O_TRACE_ENTER_0 (TEXT("CMpeg2DemuxMediaSeekingCOM::QueryPreferredFormat ()")) ; if (!pFormat) { return E_POINTER ; } // remote chance: QIed for IMediaSeeking when graph was in pull mode, then // broke the input connection, went push mode, ran the graph -- just the // type of scenario testers would find if (!m_pSeekingCore -> IsSeekable ()) { return E_UNEXPECTED ; } // comment copied .. :-) /* Don't care - they're all just as bad as one another */ if (m_pSeekingCore -> IsSeekingPin (m_pOutputPin)) { hr = m_pSeekingCore -> QueryPreferredSeekingFormat (pFormat) ; } else { // not the seeking pin, then we don't support anything (* pFormat) = TIME_FORMAT_NONE ; hr = S_OK ; } return hr ; } // S_OK if successful STDMETHODIMP CMpeg2DemuxMediaSeekingCOM::GetTimeFormat ( OUT GUID * pFormat ) { HRESULT hr ; O_TRACE_ENTER_0 (TEXT("CMpeg2DemuxMediaSeekingCOM::GetTimeFormat ()")) ; if (!pFormat) { return E_POINTER ; } // remote chance: QIed for IMediaSeeking when graph was in pull mode, then // broke the input connection, went push mode, ran the graph -- just the // type of scenario testers would find if (!m_pSeekingCore -> IsSeekable ()) { return E_UNEXPECTED ; } if (m_pSeekingCore -> IsSeekingPin (m_pOutputPin)) { hr = m_pSeekingCore -> GetSeekingTimeFormat (pFormat) ; } else { (* pFormat) = TIME_FORMAT_NONE ; hr = S_OK ; } return hr ; } // Returns S_OK if *pFormat is the current time format, otherwise // S_FALSE // This may be used instead of the above and will save the copying // of the GUID STDMETHODIMP CMpeg2DemuxMediaSeekingCOM::IsUsingTimeFormat ( IN const GUID * pFormat ) { HRESULT hr ; GUID guidFormat ; O_TRACE_ENTER_0 (TEXT("CMpeg2DemuxMediaSeekingCOM::IsUsingTimeFormat ()")) ; if (!pFormat) { return E_POINTER ; } // remote chance: QIed for IMediaSeeking when graph was in pull mode, then // broke the input connection, went push mode, ran the graph -- just the // type of scenario testers would find if (!m_pSeekingCore -> IsSeekable ()) { return E_UNEXPECTED ; } hr = m_pSeekingCore -> GetSeekingTimeFormat (& guidFormat) ; if (SUCCEEDED (hr)) { hr = (guidFormat == (* pFormat) ? S_OK : S_FALSE) ; } return hr ; } // (may return VFE_E_WRONG_STATE if graph is stopped) STDMETHODIMP CMpeg2DemuxMediaSeekingCOM::SetTimeFormat ( IN const GUID * pFormat ) { HRESULT hr ; O_TRACE_ENTER_0 (TEXT("CMpeg2DemuxMediaSeekingCOM::SetTimeFormat ()")) ; if (!pFormat) { return E_POINTER ; } // remote chance: QIed for IMediaSeeking when graph was in pull mode, then // broke the input connection, went push mode, ran the graph -- just the // type of scenario testers would find if (!m_pSeekingCore -> IsSeekable ()) { return E_UNEXPECTED ; } // first make sure we're the seeking pin if (m_pSeekingCore -> IsSeekingPin (m_pOutputPin)) { hr = m_pSeekingCore -> SetSeekingTimeFormat (pFormat) ; } else { // ya sure.. no one cares what this is hr = ((* pFormat) == TIME_FORMAT_NONE ? S_OK : E_FAIL) ; } return hr ; } // return current properties STDMETHODIMP CMpeg2DemuxMediaSeekingCOM::GetDuration ( OUT LONGLONG * pDuration ) { HRESULT hr ; O_TRACE_ENTER_0 (TEXT("CMpeg2DemuxMediaSeekingCOM::GetDuration ()")) ; if (!pDuration) { return E_POINTER ; } // remote chance: QIed for IMediaSeeking when graph was in pull mode, then // broke the input connection, went push mode, ran the graph -- just the // type of scenario testers would find if (!m_pSeekingCore -> IsSeekable ()) { return E_UNEXPECTED ; } Lock_ () ; hr = m_pSeekingCore -> GetFileDuration (pDuration) ; Unlock_ () ; return hr ; } STDMETHODIMP CMpeg2DemuxMediaSeekingCOM::GetStopPosition ( OUT LONGLONG * pStop ) { HRESULT hr ; O_TRACE_ENTER_0 (TEXT("CMpeg2DemuxMediaSeekingCOM::GetStopPosition ()")) ; if (!pStop) { return E_POINTER ; } // remote chance: QIed for IMediaSeeking when graph was in pull mode, then // broke the input connection, went push mode, ran the graph -- just the // type of scenario testers would find if (!m_pSeekingCore -> IsSeekable ()) { return E_UNEXPECTED ; } Lock_ () ; hr = m_pSeekingCore -> GetFileStopPosition (pStop) ; Unlock_ () ; return hr ; } STDMETHODIMP CMpeg2DemuxMediaSeekingCOM::GetCurrentPosition ( OUT LONGLONG * pStart ) { HRESULT hr ; O_TRACE_ENTER_0 (TEXT("CMpeg2DemuxMediaSeekingCOM::GetCurrentPosition ()")) ; if (!pStart) { return E_POINTER ; } // remote chance: QIed for IMediaSeeking when graph was in pull mode, then // broke the input connection, went push mode, ran the graph -- just the // type of scenario testers would find if (!m_pSeekingCore -> IsSeekable ()) { return E_UNEXPECTED ; } Lock_ () ; hr = m_pSeekingCore -> GetFileStartPosition (pStart) ; Unlock_ () ; return hr ; } // Convert time from one format to another. // We must be able to convert between all of the formats that we // say we support. (However, we can use intermediate formats // (e.g. MEDIA_TIME).) // If a pointer to a format is null, it implies the currently selected format. STDMETHODIMP CMpeg2DemuxMediaSeekingCOM::ConvertTimeFormat( OUT LONGLONG * pTarget, IN const GUID * pTargetFormat, IN LONGLONG Source, IN const GUID * pSourceFormat ) { O_TRACE_ENTER_0 (TEXT("CMpeg2DemuxMediaSeekingCOM::ConvertTimeFormat ()")) ; return E_NOTIMPL ; } // Set Start and end positions in one operation // Either pointer may be null, implying no change STDMETHODIMP CMpeg2DemuxMediaSeekingCOM::SetPositions ( IN OUT LONGLONG * pStart, IN DWORD dwStartFlags, IN OUT LONGLONG * pStop, IN DWORD dwStopFlags ) { HRESULT hr ; LONGLONG llStart ; LONGLONG llStop ; DWORD dwPosStartBits ; DWORD dwPosStopBits ; LONGLONG llDuration ; BOOL r ; O_TRACE_ENTER_0 (TEXT("CMpeg2DemuxMediaSeekingCOM::SetPositions ()")) ; // remote chance: QIed for IMediaSeeking when graph was in pull mode, then // broke the input connection, went push mode, ran the graph -- just the // type of scenario testers would find if (!m_pSeekingCore -> IsSeekable ()) { return E_UNEXPECTED ; } // ------------------------------------------------------------------------ // obtain some preliminary values we'll use during the course of this // call // obtain the duration; we're going to use in several places hr = GetDuration (& llDuration) ; if (FAILED (hr)) { return hr ; } // the stop position hr = GetStopPosition (& llStop) ; if (FAILED (hr)) { return hr ; } // the Start position as well hr = GetCurrentPosition (& llStart) ; if (FAILED (hr)) { return hr ; } // ------------------------------------------------------------------------ // process "Start" dwPosStartBits = dwStartFlags & AM_SEEKING_PositioningBitsMask ; // validate the pointer if (dwPosStartBits != AM_SEEKING_NoPositioning && !pStart) { return E_POINTER ; } switch (dwPosStartBits) { case AM_SEEKING_NoPositioning : hr = S_OK ; break ; case AM_SEEKING_AbsolutePositioning : // make sure it doesn't exceed the duration llStart = Min ((* pStart), llDuration) ; TRACE_1 (LOG_TRACE, 2, TEXT ("seek to: request start = %-10I64d"), llStart) ; hr = S_OK ; break ; case AM_SEEKING_RelativePositioning : // pStart is relative to Start position llStart += (* pStart) ; hr = S_OK ; break ; case AM_SEEKING_IncrementalPositioning : // flag only applies to stop position; fall through default : hr = E_INVALIDARG ; break ; } ; if (FAILED (hr)) { return hr ; } // ------------------------------------------------------------------------ // process "stop" dwPosStopBits = dwStopFlags & AM_SEEKING_PositioningBitsMask ; // validate the pointer if (dwPosStopBits != AM_SEEKING_NoPositioning && !pStop) { return E_POINTER ; } switch (dwPosStopBits) { case AM_SEEKING_NoPositioning : if (dwPosStartBits != AM_SEEKING_NoPositioning) { hr = S_OK ; } else { // huh ? not sure what the purpose of this call is.. nothing // is valid hr = E_INVALIDARG ; } break ; case AM_SEEKING_AbsolutePositioning : // make sure it doesn't exceed the duration llStop = Min ((* pStop), llDuration) ; hr = S_OK ; break ; case AM_SEEKING_RelativePositioning : // pStop is relative to Startly used stop position llStop += (* pStop) ; llStop = Min (llStop, llDuration) ; hr = S_OK ; break ; case AM_SEEKING_IncrementalPositioning : // stop is relative to pStart if (dwPosStartBits != AM_SEEKING_NoPositioning && pStart) { llStop = Min ((* pStart) + (* pStop), llDuration) ; hr = S_OK ; } else { hr = E_INVALIDARG ; } break ; default : hr = E_INVALIDARG ; break ; } ; if (FAILED (hr)) { return hr ; } // ------------------------------------------------------------------------ // ok, llStart and llStop now are offsets that bracket our desired // playback Lock_ () ; // make sure llStart and llStop make sense r = (dwPosStartBits != AM_SEEKING_NoPositioning) && (llStart < 0 || (dwPosStopBits != AM_SEEKING_NoPositioning) && llStart > llStop) ; if (!r) { if (dwPosStartBits != AM_SEEKING_NoPositioning) { // we have a Start value; may or may not have a stop value if (dwPosStopBits == AM_SEEKING_NoPositioning) { // not stop value; seek just to Start hr = m_pSeekingCore -> SeekTo (& llStart) ; } else { // stop value is present; seek to a Start and specify a stop hr = m_pSeekingCore -> SeekTo (& llStart, & llStop) ; } } else { // set only the end point hr = m_pSeekingCore -> SetFileStopPosition (& llStop) ; } } else { // specified parameters that don't make sense hr = E_INVALIDARG ; } // ------------------------------------------------------------------------ // set outgoing, if desired // if (SUCCEEDED (hr) && (dwStartFlags & AM_SEEKING_ReturnTime)) { ASSERT (pStart) ; hr = GetCurrentPosition (pStart) ; TRACE_1 (LOG_TRACE, 2, TEXT ("seek to: ret start = %-10I64d"), (* pStart)) ; } if (SUCCEEDED (hr) && (dwStopFlags & AM_SEEKING_ReturnTime)) { ASSERT (pStop) ; hr = GetStopPosition (pStop) ; } Unlock_ () ; return hr ; } // Get StartPosition & StopTime // Either pointer may be null, implying not interested STDMETHODIMP CMpeg2DemuxMediaSeekingCOM::GetPositions ( OUT LONGLONG * pStart, OUT LONGLONG * pStop ) { HRESULT hr ; O_TRACE_ENTER_0 (TEXT("CMpeg2DemuxMediaSeekingCOM::GetPositions ()")) ; // remote chance: QIed for IMediaSeeking when graph was in pull mode, then // broke the input connection, went push mode, ran the graph -- just the // type of scenario testers would find if (!m_pSeekingCore -> IsSeekable ()) { return E_UNEXPECTED ; } hr = S_OK ; Lock_ () ; if (pStart) { hr = GetCurrentPosition (pStart) ; } if (pStop && SUCCEEDED (hr)) { hr = GetStopPosition (pStop) ; } Unlock_ () ; return hr ; } // Get earliest / latest times to which we can currently seek // "efficiently". This method is intended to help with graphs // where the source filter has a very high latency. Seeking // within the returned limits should just result in a re-pushing // of already cached data. Seeking beyond these limits may result // in extended delays while the data is fetched (e.g. across a // slow network). // (NULL pointer is OK, means caller isn't interested.) STDMETHODIMP CMpeg2DemuxMediaSeekingCOM::GetAvailable ( OUT LONGLONG * pEarliest, OUT LONGLONG * pLatest ) { HRESULT hr ; O_TRACE_ENTER_0 (TEXT("CMpeg2DemuxMediaSeekingCOM::GetAvailable ()")) ; // remote chance: QIed for IMediaSeeking when graph was in pull mode, then // broke the input connection, went push mode, ran the graph -- just the // type of scenario testers would find if (!m_pSeekingCore -> IsSeekable ()) { return E_UNEXPECTED ; } hr = S_OK ; Lock_ () ; if (pEarliest) { (* pEarliest) = 0 ; } if (pLatest) { hr = GetDuration (pLatest) ; } Unlock_ () ; return hr ; } // Rate stuff STDMETHODIMP CMpeg2DemuxMediaSeekingCOM::SetRate ( IN double dRate ) { HRESULT hr ; O_TRACE_ENTER_0 (TEXT("CMpeg2DemuxMediaSeekingCOM::SetRate ()")) ; // remote chance: QIed for IMediaSeeking when graph was in pull mode, then // broke the input connection, went push mode, ran the graph -- just the // type of scenario testers would find if (!m_pSeekingCore -> IsSeekable ()) { return E_UNEXPECTED ; } // validate the rate request if (dRate <= 0.0) { return E_INVALIDARG ; } Lock_ () ; hr = m_pSeekingCore -> SetPlaybackRate (dRate) ; Unlock_ () ; return hr ; } STDMETHODIMP CMpeg2DemuxMediaSeekingCOM::GetRate ( OUT double * pdRate ) { HRESULT hr ; O_TRACE_ENTER_0 (TEXT("CMpeg2DemuxMediaSeekingCOM::GetRate ()")) ; if (!pdRate) { return E_POINTER ; } // remote chance: QIed for IMediaSeeking when graph was in pull mode, then // broke the input connection, went push mode, ran the graph -- just the // type of scenario testers would find if (!m_pSeekingCore -> IsSeekable ()) { return E_UNEXPECTED ; } Lock_ () ; (* pdRate) = m_pSeekingCore -> GetPlaybackRate () ; hr = S_OK ; Unlock_ () ; return hr ; } // Preroll STDMETHODIMP CMpeg2DemuxMediaSeekingCOM::GetPreroll ( OUT LONGLONG * pllPreroll ) { O_TRACE_ENTER_0 (TEXT("CMpeg2DemuxMediaSeekingCOM::GetPreroll ()")) ; if (!pllPreroll) { return E_POINTER ; } // remote chance: QIed for IMediaSeeking when graph was in pull mode, then // broke the input connection, went push mode, ran the graph -- just the // type of scenario testers would find if (!m_pSeekingCore -> IsSeekable ()) { return E_UNEXPECTED ; } return E_NOTIMPL ; }