Seek Functionality on a Source Filter

Seek Functionality on a Source Filter

CPushPin inherits from both CSourceStream and CSourceSeeking. CSourceSeeking allows the source filter s output pin to process and respond to seek commands. In the case of the PushSource filter, a seek command causes the frame count to rewind or fast-forward to a new value. Because this filter is frame- seekable (meaning we can position it to any arbitrary frame within the legal range), it needs to implement a number of other methods. The following code shows the basic methods that implement seek functionality:

void CPushPin::UpdateFromSeek() { if (ThreadExists()) // Filter is active? { DeliverBeginFlush(); // Shut down the thread and stop pushing data. Stop(); DeliverEndFlush(); // Restart the thread and start pushing data again. Pause(); // We'll set the discontinuity flag on the next sample. } } HRESULT CPushPin::OnThreadStartPlay() { m_bDiscontinuity = TRUE; // Set the discontinuity flag on the next sample // Send a NewSegment call downstream. return DeliverNewSegment(m_rtStart, m_rtStop, m_dRateSeeking); } HRESULT CPushPin::ChangeStart( ) { // Reset stream time to zero and the source time to m_rtStart. { CAutoLock lock(CSourceSeeking::m_pLock); m_rtStreamTime = 0; m_rtSourceTime = m_rtStart; m_iFrameNumber = (int)(m_rtStart / m_rtFrameLength); } UpdateFromSeek(); return S_OK; } HRESULT CPushPin::ChangeStop( ) { { CAutoLock lock(CSourceSeeking::m_pLock); if (m_rtSourceTime < m_rtStop) { return S_OK; } } // We're already past the new stop time. Flush the graph. UpdateFromSeek(); return S_OK; } //-------------------------------------------------------------------------HRESULT CPushPin::SetRate(double dRate) { if (dRate <= 0.0) { return E_INVALIDARG; } { CAutoLock lock(CSourceSeeking::m_pLock); m_dRateSeeking = dRate; } UpdateFromSeek(); return S_OK; } // Because we override SetRate, // the ChangeRate method won't ever be called. (It is only // ever called by SetRate.) // But it's pure virtual, so it needs a dummy implementation. HRESULT CPushPin::ChangeRate() { return S_OK; }

CPushPin::UpdateFromSeek forces the flush of the filter graph that needs to take place every time a seek is performed on the source filter. The buffer flushing process has four steps. Flushing begins (on the downstream pin) when DeliverBeginFlush is invoked. Next the streaming thread is stopped, suspending buffer processing throughout the filter graph. Then DeliverEndFlush is invoked; this sets up the downstream filters to receive new media samples. The streaming thread is then restarted. When restarted, CPushPin::OnThreadStartPlay is called, and any processing associated with starting the filter must be performed here. The new start, stop, and rate values for the stream are delivered downstream. If the filter does anything complicated to the threads, it s not hard to create a deadlock situation in which the entire filter graph seizes up, waiting for a lock that will never be released. Filter developers are strongly encouraged to read Threads and Critical Sections under Writing DirectShow Filters in the DirectX SDK documentation.

When the start position of the stream changes, CPushPin::ChangeStart is invoked. This method resets the stream time to zero (the current time, relatively, as the stream time counts upward while the filter runs) and changes the source time to the requested start time. This translates into a new frame number, which is calculated. CPushPin::UpdateFromSeek is called at the end of the routine so that any buffers in the filter graph can be flushed to make way for the new stream data.

When the filter receives a seek command that resets the stop position of the stream, CPushPin::ChangeStop is invoked. This method checks to see whether the new stop time is past the current time. If it is, the graph is flushed with a call to CPushPin::UpdateFromSeek.

The playback rate of a stream can be adjusted through a call to the IMedia Seeking interface. The playback rate of the PushSource filter, for example, is 30 frames a second (fps), but it could be adjusted to 15 or 60 fps or any other value. When the playback rate is changed, CPushPin::SetRate is invoked. It modifies the internal variable containing the playback rate and then calls CPushPin::UpdateFromSeek to flush the downstream filters. It s not necessary to override CSourceSeeking::SetRate, but we ve overridden it here in CPushPin because this version includes a parameter check on the rate value to ensure its validity.

All the routines discussed thus far handle seeking from a base of the DirectShow reference time, in units of 100 nanoseconds. DirectShow filters can also support other time formats. This can be handy when dealing with video, which is parceled out in units of frames, or fields. A source filter doesn t need to implement multiple time formats for seeking, but the PushSource filter does. These CPushPin methods, overridden from CSourceSeeking, allow the PushSource filter to work in a time base of frames, as shown in the following code:

STDMETHODIMP CPushPin::SetTimeFormat(const GUID *pFormat) { CAutoLock cAutoLock(m_pFilter->pStateLock()); CheckPointer(pFormat, E_POINTER); if (m_pFilter->IsActive()) { // Cannot switch formats while running. return VFW_E_WRONG_STATE; } if (S_OK != IsFormatSupported(pFormat)) { // We don't support this time format. return E_INVALIDARG; } m_TimeFormat = *pFormat; return S_OK; } STDMETHODIMP CPushPin::GetTimeFormat(GUID *pFormat) { CAutoLock cAutoLock(m_pFilter->pStateLock()); CheckPointer(pFormat, E_POINTER); *pFormat = m_TimeFormat; return S_OK; } STDMETHODIMP CPushPin::IsUsingTimeFormat(const GUID *pFormat) { CAutoLock cAutoLock(m_pFilter->pStateLock()); CheckPointer(pFormat, E_POINTER); return (*pFormat == m_TimeFormat ? S_OK : S_FALSE); } STDMETHODIMP CPushPin::IsFormatSupported( const GUID * pFormat) { CheckPointer(pFormat, E_POINTER); if (*pFormat == TIME_FORMAT_MEDIA_TIME) { return S_OK; } else if (*pFormat == TIME_FORMAT_FRAME) { return S_OK; } else { return S_FALSE; } } STDMETHODIMP CPushPin::QueryPreferredFormat(GUID *pFormat) { CheckPointer(pFormat, E_POINTER); *pFormat = TIME_FORMAT_FRAME; // Doesn't really matter which we prefer return S_OK; } STDMETHODIMP CPushPin::ConvertTimeFormat( LONGLONG *pTarget, // Receives the converted time value. const GUID *pTargetFormat, // Specifies the target format // for the conversion. LONGLONG Source, // Time value to convert. const GUID *pSourceFormat) // Time format for the Source time. { CheckPointer(pTarget, E_POINTER); // Either of the format GUIDs can be NULL, // which means "use the current time format"  GUID TargetFormat, SourceFormat; TargetFormat = (pTargetFormat == NULL ? m_TimeFormat : *pTargetFormat ); SourceFormat = (pSourceFormat == NULL ? m_TimeFormat : *pSourceFormat ); if (TargetFormat == TIME_FORMAT_MEDIA_TIME) { if (SourceFormat == TIME_FORMAT_FRAME) { *pTarget = FrameToTime(Source); return S_OK; } if (SourceFormat == TIME_FORMAT_MEDIA_TIME) { // no-op *pTarget = Source; return S_OK; } return E_INVALIDARG; // Invalid source format. } if (TargetFormat == TIME_FORMAT_FRAME) { if (SourceFormat == TIME_FORMAT_MEDIA_TIME) { *pTarget = TimeToFrame(Source); return S_OK; } if (SourceFormat == TIME_FORMAT_FRAME) { // no-op *pTarget = Source; return S_OK; } return E_INVALIDARG; // Invalid source format. } return E_INVALIDARG; // Invalid target format. } STDMETHODIMP CPushPin::SetPositions( LONGLONG *pCurrent, // New current position (can be NULL!) DWORD CurrentFlags, LONGLONG *pStop, // New stop position (can be NULL!) DWORD StopFlags) { HRESULT hr; if (m_TimeFormat == TIME_FORMAT_FRAME) { REFERENCE_TIME rtCurrent = 0, rtStop = 0; if (pCurrent) { rtCurrent = FrameToTime(*pCurrent); } if (pStop) { rtStop = FrameToTime(*pStop); } hr = CSourceSeeking::SetPositions(&rtCurrent, CurrentFlags, &rtStop, StopFlags); if (SUCCEEDED(hr)) { // The AM_SEEKING_ReturnTime flag // means the caller wants the input times // converted to the current time format. if (pCurrent && (CurrentFlags & AM_SEEKING_ReturnTime)) { *pCurrent = rtCurrent; } if (pStop && (StopFlags & AM_SEEKING_ReturnTime)) { *pStop = rtStop; } } } else { // Simple pass thru' hr = CSourceSeeking::SetPositions(pCurrent, CurrentFlags, pStop, StopFlags); } return hr; } STDMETHODIMP CPushPin::GetDuration(LONGLONG *pDuration) { HRESULT hr = CSourceSeeking::GetDuration(pDuration); if (SUCCEEDED(hr)) { if (m_TimeFormat == TIME_FORMAT_FRAME) { *pDuration = TimeToFrame(*pDuration); } } return S_OK; } STDMETHODIMP CPushPin::GetStopPosition(LONGLONG *pStop) { HRESULT hr = CSourceSeeking::GetStopPosition(pStop); if (SUCCEEDED(hr)) { if (m_TimeFormat == TIME_FORMAT_FRAME) { *pStop = TimeToFrame(*pStop); } } return S_OK; }

A request to change the time format can be set with a call to CPushPin::SetTimeFormat; the passed parameter is a GUID, which describes the time format. (The list of time format GUIDs can be found in the DirectX SDK documentation.) If CPushPin::IsSupportedFormat reports that the time format is supported, the format is changed. The two supported formats for CPushPin are TIME_FORMAT_MEDIA_TIME, which is the reference time of 100-nanosecond units, and TIME_FORMAT_FRAME, in which each unit constitutes one video frame.

Conversions between time formats are performed in CPushPin::ConvertTimeFormat. Because PushSource supports two time formats, the routine must perform conversions between TIME_FORMAT_MEDIA_TIME and TIME_FORMAT_FRAME. CPushPin::GetDuration, CPushPin::GetStopPosition, and CPushPin::SetPositions are overridden because their return values must reflect the current time format. (Imagine what would happen if the Filter Graph Manager, expecting frames, was informed there were 300,000,000 of them!)

There s just a bit more C++ code needed to handle the COM interfaces, which we ve seen previously in the YUVGray and SampleGrabber filters. When compiled and registered (which should happen automatically), the Generic Push Source filter will appear in the list of DirectShow filters in GraphEdit.



Programming Microsoft DirectShow for Digital Video and Television
Programming Microsoft DirectShow for Digital Video and Television (Pro-Developer)
ISBN: 0735618216
EAN: 2147483647
Year: 2002
Pages: 108
Authors: Mark D. Pesce

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net