577 lines
14 KiB
C++
577 lines
14 KiB
C++
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// MPEG1Stream.cpp
|
|
// Implements the stream object (IMFMediaStream) for the MPEG-1 source.
|
|
//
|
|
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
|
|
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
|
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
|
// PARTICULAR PURPOSE.
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "MPEG1Source.h"
|
|
|
|
#pragma warning( push )
|
|
#pragma warning( disable : 4355 ) // 'this' used in base member initializer list
|
|
|
|
|
|
/* MPEG1Stream::SourceLock class methods */
|
|
|
|
//-------------------------------------------------------------------
|
|
// MPEG1Stream::SourceLock constructor - locks the source
|
|
//-------------------------------------------------------------------
|
|
|
|
MPEG1Stream::SourceLock::SourceLock(MPEG1Source *pSource)
|
|
: m_pSource(NULL)
|
|
{
|
|
if (pSource)
|
|
{
|
|
m_pSource = pSource;
|
|
m_pSource->AddRef();
|
|
m_pSource->Lock();
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// MPEG1Stream::SourceLock destructor - unlocks the source
|
|
//-------------------------------------------------------------------
|
|
|
|
MPEG1Stream::SourceLock::~SourceLock()
|
|
{
|
|
if (m_pSource)
|
|
{
|
|
m_pSource->Unlock();
|
|
m_pSource->Release();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* Public class methods */
|
|
|
|
//-------------------------------------------------------------------
|
|
// IUnknown methods
|
|
//-------------------------------------------------------------------
|
|
|
|
ULONG MPEG1Stream::AddRef() { return RefCountedObject::AddRef(); }
|
|
|
|
ULONG MPEG1Stream::Release() { return RefCountedObject::Release(); }
|
|
|
|
HRESULT MPEG1Stream::QueryInterface(REFIID riid, void** ppv)
|
|
{
|
|
static const QITAB qit[] =
|
|
{
|
|
QITABENT(MPEG1Stream, IMFMediaEventGenerator),
|
|
QITABENT(MPEG1Stream, IMFMediaStream),
|
|
{ 0 }
|
|
};
|
|
return QISearch(this, qit, riid, ppv);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// IMFMediaEventGenerator methods
|
|
//
|
|
// For remarks, see MPEG1Source.cpp
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT MPEG1Stream::BeginGetEvent(IMFAsyncCallback* pCallback,IUnknown* punkState)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
SourceLock lock(m_pSource);
|
|
|
|
CHECK_HR(hr = CheckShutdown());
|
|
CHECK_HR(hr = m_pEventQueue->BeginGetEvent(pCallback, punkState));
|
|
|
|
done:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT MPEG1Stream::EndGetEvent(IMFAsyncResult* pResult, IMFMediaEvent** ppEvent)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
SourceLock lock(m_pSource);
|
|
|
|
CHECK_HR(hr = CheckShutdown());
|
|
CHECK_HR(hr = m_pEventQueue->EndGetEvent(pResult, ppEvent));
|
|
|
|
done:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT MPEG1Stream::GetEvent(DWORD dwFlags, IMFMediaEvent** ppEvent)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
IMFMediaEventQueue *pQueue = NULL;
|
|
|
|
{ // scope for lock
|
|
|
|
SourceLock lock(m_pSource);
|
|
|
|
// Check shutdown
|
|
CHECK_HR(hr = CheckShutdown());
|
|
|
|
// Cache a local pointer to the queue.
|
|
pQueue = m_pEventQueue;
|
|
pQueue->AddRef();
|
|
|
|
} // release lock
|
|
|
|
// Use the local pointer to call GetEvent.
|
|
CHECK_HR(hr = pQueue->GetEvent(dwFlags, ppEvent));
|
|
|
|
done:
|
|
SAFE_RELEASE(pQueue);
|
|
return hr;
|
|
}
|
|
|
|
HRESULT MPEG1Stream::QueueEvent(MediaEventType met, REFGUID guidExtendedType, HRESULT hrStatus, const PROPVARIANT* pvValue)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
SourceLock lock(m_pSource);
|
|
|
|
CHECK_HR(hr = CheckShutdown());
|
|
|
|
CHECK_HR(hr = m_pEventQueue->QueueEventParamVar(met, guidExtendedType, hrStatus, pvValue));
|
|
|
|
done:
|
|
return hr;
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// IMFMediaStream methods
|
|
//-------------------------------------------------------------------
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// GetMediaSource:
|
|
// Returns a pointer to the media source.
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT MPEG1Stream::GetMediaSource(IMFMediaSource** ppMediaSource)
|
|
{
|
|
SourceLock lock(m_pSource);
|
|
|
|
if (ppMediaSource == NULL)
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
if (m_pSource == NULL)
|
|
{
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
CHECK_HR(hr = CheckShutdown());
|
|
|
|
// QI the source for IMFMediaSource.
|
|
// (Does not hold the source's critical section.)
|
|
CHECK_HR(hr = m_pSource->QueryInterface(IID_PPV_ARGS(ppMediaSource)));
|
|
|
|
done:
|
|
return hr;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// GetStreamDescriptor:
|
|
// Returns a pointer to the stream descriptor for this stream.
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT MPEG1Stream::GetStreamDescriptor(IMFStreamDescriptor** ppStreamDescriptor)
|
|
{
|
|
SourceLock lock(m_pSource);
|
|
|
|
if (ppStreamDescriptor == NULL)
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
if (m_pStreamDescriptor == NULL)
|
|
{
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
HRESULT hr = CheckShutdown();
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
*ppStreamDescriptor = m_pStreamDescriptor;
|
|
(*ppStreamDescriptor)->AddRef();
|
|
|
|
};
|
|
return hr;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// RequestSample:
|
|
// Requests data from the stream.
|
|
//
|
|
// pToken: Token used to track the request. Can be NULL.
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT MPEG1Stream::RequestSample(IUnknown* pToken)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
IMFMediaSource *pSource = NULL;
|
|
|
|
SourceLock lock(m_pSource);
|
|
|
|
CHECK_HR(hr = CheckShutdown());
|
|
|
|
if (m_state == STATE_STOPPED)
|
|
{
|
|
CHECK_HR(hr = MF_E_INVALIDREQUEST);
|
|
}
|
|
|
|
if (!m_bActive)
|
|
{
|
|
// If the stream is not active, it should not get sample requests.
|
|
CHECK_HR(hr = MF_E_INVALIDREQUEST);
|
|
}
|
|
|
|
// Fail if we reached the end of the stream AND the sample queue is empty,
|
|
if (m_bEOS && m_Samples.IsEmpty())
|
|
{
|
|
CHECK_HR(hr = MF_E_END_OF_STREAM);
|
|
}
|
|
|
|
CHECK_HR(hr = m_Requests.InsertBack(pToken));
|
|
|
|
// Dispatch the request.
|
|
CHECK_HR(hr = DispatchSamples());
|
|
|
|
done:
|
|
|
|
// If there was an error, queue MEError from the source (except after shutdown).
|
|
if (FAILED(hr) && (m_state != STATE_SHUTDOWN))
|
|
{
|
|
hr = m_pSource->QueueEvent(MEError, GUID_NULL, hr, NULL);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// Public non-interface methods
|
|
//-------------------------------------------------------------------
|
|
|
|
|
|
MPEG1Stream::MPEG1Stream(MPEG1Source *pSource, IMFStreamDescriptor *pSD, HRESULT& hr) :
|
|
m_pEventQueue(NULL),
|
|
m_state(STATE_STOPPED),
|
|
m_bActive(FALSE),
|
|
m_bEOS(FALSE)
|
|
{
|
|
assert(pSource != NULL);
|
|
assert(pSD != NULL);
|
|
|
|
m_pSource = pSource;
|
|
m_pSource->AddRef();
|
|
|
|
m_pStreamDescriptor = pSD;
|
|
m_pStreamDescriptor->AddRef();
|
|
|
|
// Create the media event queue.
|
|
hr = MFCreateEventQueue(&m_pEventQueue);
|
|
}
|
|
|
|
MPEG1Stream::~MPEG1Stream()
|
|
{
|
|
assert(m_state == STATE_SHUTDOWN);
|
|
SAFE_RELEASE(m_pSource);
|
|
}
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// Activate
|
|
// Activates or deactivates the stream. Called by the media source.
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT MPEG1Stream::Activate(BOOL bActive)
|
|
{
|
|
SourceLock lock(m_pSource);
|
|
|
|
if (bActive == m_bActive)
|
|
{
|
|
return S_OK; // No op
|
|
}
|
|
|
|
m_bActive = bActive;
|
|
|
|
if (!bActive)
|
|
{
|
|
m_Samples.Clear();
|
|
m_Requests.Clear();
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// Start
|
|
// Starts the stream. Called by the media source.
|
|
//
|
|
// varStart: Starting position.
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT MPEG1Stream::Start(const PROPVARIANT& varStart)
|
|
{
|
|
SourceLock lock(m_pSource);
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
CHECK_HR(hr = CheckShutdown());
|
|
|
|
// Queue the stream-started event.
|
|
CHECK_HR(hr = QueueEvent(
|
|
MEStreamStarted,
|
|
GUID_NULL,
|
|
S_OK,
|
|
&varStart
|
|
));
|
|
|
|
m_state = STATE_STARTED;
|
|
|
|
// If we are restarting from paused, there may be
|
|
// queue sample requests. Dispatch them now.
|
|
CHECK_HR(hr = DispatchSamples());
|
|
|
|
done:
|
|
return hr;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// Pause
|
|
// Pauses the stream. Called by the media source.
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT MPEG1Stream::Pause()
|
|
{
|
|
SourceLock lock(m_pSource);
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
CHECK_HR(hr = CheckShutdown());
|
|
|
|
m_state = STATE_PAUSED;
|
|
|
|
CHECK_HR(hr = QueueEvent(MEStreamPaused, GUID_NULL, S_OK, NULL));
|
|
|
|
done:
|
|
return hr;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// Stop
|
|
// Stops the stream. Called by the media source.
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT MPEG1Stream::Stop()
|
|
{
|
|
SourceLock lock(m_pSource);
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
CHECK_HR(hr = CheckShutdown());
|
|
|
|
m_Requests.Clear();
|
|
m_Samples.Clear();
|
|
|
|
m_state = STATE_STOPPED;
|
|
|
|
CHECK_HR(hr = QueueEvent(MEStreamStopped, GUID_NULL, S_OK, NULL));
|
|
|
|
done:
|
|
return hr;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// EndOfStream
|
|
// Notifies the stream that the source reached the end of the MPEG-1
|
|
// stream. For more information, see MPEG1Source::EndOfMPEGStream().
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT MPEG1Stream::EndOfStream()
|
|
{
|
|
SourceLock lock(m_pSource);
|
|
|
|
m_bEOS = TRUE;
|
|
|
|
return DispatchSamples();
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// Shutdown
|
|
// Shuts down the stream and releases all resources.
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT MPEG1Stream::Shutdown()
|
|
{
|
|
SourceLock lock(m_pSource);
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
CHECK_HR(hr = CheckShutdown());
|
|
|
|
m_state = STATE_SHUTDOWN;
|
|
|
|
// Shut down the event queue.
|
|
if (m_pEventQueue)
|
|
{
|
|
m_pEventQueue->Shutdown();
|
|
}
|
|
|
|
// Release objects.
|
|
m_Samples.Clear();
|
|
m_Requests.Clear();
|
|
|
|
SAFE_RELEASE(m_pStreamDescriptor);
|
|
SAFE_RELEASE(m_pEventQueue);
|
|
|
|
// NOTE:
|
|
// Do NOT release the source pointer here, because the stream uses
|
|
// it to hold the critical section. In particular, the stream must
|
|
// hold the critical section when checking the shutdown status,
|
|
// which obviously can occur after the stream is shut down.
|
|
|
|
// It is OK to hold a ref count on the source after shutdown,
|
|
// because the source releases its ref count(s) on the streams,
|
|
// which breaks the circular ref count.
|
|
|
|
done:
|
|
return hr;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// NeedsData
|
|
// Returns TRUE if the stream needs more data.
|
|
//-------------------------------------------------------------------
|
|
|
|
BOOL MPEG1Stream::NeedsData()
|
|
{
|
|
SourceLock lock(m_pSource);
|
|
|
|
// Note: The stream tries to keep a minimum number of samples
|
|
// queued ahead.
|
|
|
|
return (m_bActive && !m_bEOS && (m_Samples.GetCount() < SAMPLE_QUEUE));
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// DeliverPayload
|
|
// Delivers a sample to the stream.
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT MPEG1Stream::DeliverPayload(IMFSample *pSample)
|
|
{
|
|
SourceLock lock(m_pSource);
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
// Queue the sample.
|
|
CHECK_HR(hr = m_Samples.InsertBack(pSample));
|
|
|
|
// Deliver the sample if there is an outstanding request.
|
|
CHECK_HR(hr = DispatchSamples());
|
|
|
|
done:
|
|
return hr;
|
|
}
|
|
|
|
/* Private methods */
|
|
|
|
//-------------------------------------------------------------------
|
|
// DispatchSamples
|
|
// Dispatches as many pending sample requests as possible.
|
|
//-------------------------------------------------------------------
|
|
|
|
HRESULT MPEG1Stream::DispatchSamples()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
BOOL bNeedData = FALSE;
|
|
BOOL bEOS = FALSE;
|
|
|
|
IMFSample *pSample = NULL;
|
|
IUnknown *pToken = NULL;
|
|
|
|
SourceLock lock(m_pSource);
|
|
|
|
// It's possible that an I/O request completed after the source
|
|
// paused, stopped, or shut down. We should not deliver any samples
|
|
// unless the source is running.
|
|
if (m_state != STATE_STARTED)
|
|
{
|
|
hr = S_OK;
|
|
goto done;
|
|
}
|
|
|
|
// Deliver as many samples as we can.
|
|
while (!m_Samples.IsEmpty() && !m_Requests.IsEmpty())
|
|
{
|
|
// Pull the next sample from the queue.
|
|
CHECK_HR(hr = m_Samples.RemoveFront(&pSample));
|
|
|
|
// Pull the next request token from the queue. Tokens can be NULL.
|
|
CHECK_HR(hr = m_Requests.RemoveFront(&pToken));
|
|
|
|
if (pToken)
|
|
{
|
|
CHECK_HR(hr = pSample->SetUnknown(MFSampleExtension_Token, pToken));
|
|
}
|
|
|
|
CHECK_HR(hr = m_pEventQueue->QueueEventParamUnk(MEMediaSample, GUID_NULL, S_OK, pSample));
|
|
|
|
SAFE_RELEASE(pSample);
|
|
SAFE_RELEASE(pToken);
|
|
}
|
|
|
|
if (m_Samples.IsEmpty() && m_bEOS)
|
|
{
|
|
// The sample queue is empty AND we have reached the end of the source stream.
|
|
// Notify the pipeline by sending the end-of-stream event.
|
|
CHECK_HR(hr = m_pEventQueue->QueueEventParamVar(MEEndOfStream, GUID_NULL, S_OK, NULL));
|
|
|
|
// Also notify the source, so that it can send the end-of-presentation event.
|
|
CHECK_HR(hr = m_pSource->QueueAsyncOperation(SourceOp::OP_END_OF_STREAM));
|
|
}
|
|
else if (NeedsData())
|
|
{
|
|
// The sample queue is empty and the request queue is not empty (and we did not
|
|
// reach the end of the stream). Ask the source for more data.
|
|
CHECK_HR(hr = m_pSource->QueueAsyncOperation(SourceOp::OP_REQUEST_DATA));
|
|
}
|
|
|
|
done:
|
|
|
|
// If there was an error, queue MEError from the source (except after shutdown).
|
|
if (FAILED(hr) && (m_state != STATE_SHUTDOWN))
|
|
{
|
|
m_pSource->QueueEvent(MEError, GUID_NULL, hr, NULL);
|
|
}
|
|
|
|
SAFE_RELEASE(pSample);
|
|
SAFE_RELEASE(pToken);
|
|
return S_OK;
|
|
}
|
|
|
|
#pragma warning( pop ) |