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