| // Copyright (c) 2010 The WebM project authors. All Rights Reserved. | |
| // | |
| // Use of this source code is governed by a BSD-style license | |
| // that can be found in the LICENSE file in the root of the source | |
| // tree. An additional intellectual property rights grant can be found | |
| // in the file PATENTS. All contributing project authors may | |
| // be found in the AUTHORS file in the root of the source tree. | |
| #include <windows.h> | |
| #include <windowsx.h> | |
| #include <comdef.h> | |
| #include <mfapi.h> | |
| #include <mferror.h> | |
| #include <mfidl.h> | |
| #include <shlwapi.h> | |
| #include <cassert> | |
| #include <string> | |
| #include "debugutil.hpp" | |
| #include "eventutil.hpp" | |
| #include "comreg.hpp" | |
| #include "comdllwrapper.hpp" | |
| #include "mfmediastream.hpp" | |
| #include "mfsrcwrap.hpp" | |
| #include "mfutil.hpp" | |
| #include "webmtypes.hpp" | |
| namespace WebmMfUtil | |
| { | |
| MfByteStreamHandlerWrapper::MfByteStreamHandlerWrapper(): | |
| audio_stream_count_(0), | |
| event_type_recvd_(0), | |
| expected_event_type_(0), | |
| media_event_error_(0), | |
| ptr_audio_stream_(NULL), | |
| ptr_com_dll_(NULL), | |
| ptr_video_stream_(NULL), | |
| ref_count_(0), | |
| selected_stream_count_(0), | |
| state_(MFSTATE_STOPPED), | |
| stream_count_(0), | |
| video_stream_count_(0) | |
| { | |
| } | |
| MfByteStreamHandlerWrapper::~MfByteStreamHandlerWrapper() | |
| { | |
| if (ptr_audio_stream_) | |
| { | |
| ptr_audio_stream_->Release(); | |
| ptr_audio_stream_ = NULL; | |
| } | |
| if (ptr_video_stream_) | |
| { | |
| ptr_video_stream_->Release(); | |
| ptr_video_stream_ = NULL; | |
| } | |
| if (ptr_media_src_) | |
| { | |
| ptr_media_src_->Shutdown(); | |
| } | |
| if (ptr_byte_stream_) | |
| { | |
| ptr_byte_stream_->Close(); | |
| } | |
| } | |
| HRESULT MfByteStreamHandlerWrapper::Create_(std::wstring dll_path, | |
| GUID mfobj_clsid) | |
| { | |
| HRESULT hr = ComDllWrapper::Create(dll_path, mfobj_clsid, &ptr_com_dll_); | |
| if (FAILED(hr) || !ptr_com_dll_) | |
| { | |
| DBGLOG("ComDllWrapper::Create failed path=" << dll_path.c_str() | |
| << HRLOG(hr)); | |
| return hr; | |
| } | |
| hr = ptr_com_dll_->CreateInstance(IID_IMFByteStreamHandler, | |
| reinterpret_cast<void**>(&ptr_handler_)); | |
| if (FAILED(hr) || !ptr_handler_) | |
| { | |
| DBGLOG("GetInterfacePtr failed" << HRLOG(hr)); | |
| return hr; | |
| } | |
| hr = open_event_.Create(); | |
| if (FAILED(hr)) | |
| { | |
| DBGLOG("open event creation failed" << HRLOG(hr)); | |
| return hr; | |
| } | |
| hr = media_source_event_.Create(); | |
| if (FAILED(hr)) | |
| { | |
| DBGLOG("media source event creation failed" << HRLOG(hr)); | |
| return hr; | |
| } | |
| return hr; | |
| } | |
| HRESULT MfByteStreamHandlerWrapper::Create( | |
| std::wstring dll_path, | |
| GUID mfobj_clsid, | |
| MfByteStreamHandlerWrapper** ptr_bsh_instance) | |
| { | |
| *ptr_bsh_instance = new (std::nothrow) MfByteStreamHandlerWrapper(); | |
| if (!*ptr_bsh_instance) | |
| { | |
| DBGLOG("ctor failed"); | |
| return E_OUTOFMEMORY; | |
| } | |
| MfByteStreamHandlerWrapper* ptr_bsh_wrapper = *ptr_bsh_instance; | |
| HRESULT hr = ptr_bsh_wrapper->Create_(dll_path, mfobj_clsid); | |
| if (SUCCEEDED(hr)) | |
| { | |
| ptr_bsh_wrapper->AddRef(); | |
| } | |
| else | |
| { | |
| DBGLOG("ERROR, Create_ failed" << HRLOG(hr)); | |
| } | |
| return hr; | |
| } | |
| HRESULT MfByteStreamHandlerWrapper::GetAudioSample(IMFSample** ptr_sample) | |
| { | |
| if (0 == audio_stream_count_) | |
| { | |
| DBGLOG("no audio streams"); | |
| return E_INVALIDARG; | |
| } | |
| if (NULL == ptr_audio_stream_) | |
| { | |
| DBGLOG("ERROR, audio stream not created, E_UNEXPECTED"); | |
| return E_UNEXPECTED; | |
| } | |
| return ptr_audio_stream_->GetSample(ptr_sample); | |
| } | |
| HRESULT MfByteStreamHandlerWrapper::GetAudioMediaType( | |
| IMFMediaType **ptr_type) const | |
| { | |
| if (0 == audio_stream_count_) | |
| { | |
| DBGLOG("no audio streams"); | |
| return E_INVALIDARG; | |
| } | |
| if (NULL == ptr_audio_stream_) | |
| { | |
| DBGLOG("ERROR, audio stream not created, E_UNEXPECTED"); | |
| return E_UNEXPECTED; | |
| } | |
| return ptr_audio_stream_->GetMediaType(ptr_type); | |
| } | |
| UINT MfByteStreamHandlerWrapper::GetAudioStreamCount() const | |
| { | |
| return audio_stream_count_; | |
| } | |
| HRESULT MfByteStreamHandlerWrapper::GetVideoSample(IMFSample** ptr_sample) | |
| { | |
| if (0 == video_stream_count_) | |
| { | |
| DBGLOG("no video streams"); | |
| return E_INVALIDARG; | |
| } | |
| if (NULL == ptr_video_stream_) | |
| { | |
| DBGLOG("ERROR, video stream not created, E_UNEXPECTED"); | |
| return E_UNEXPECTED; | |
| } | |
| return ptr_video_stream_->GetSample(ptr_sample); | |
| } | |
| HRESULT MfByteStreamHandlerWrapper::GetVideoMediaType( | |
| IMFMediaType **ptr_type) const | |
| { | |
| if (0 == video_stream_count_) | |
| { | |
| DBGLOG("no video streams"); | |
| return E_INVALIDARG; | |
| } | |
| if (NULL == ptr_video_stream_) | |
| { | |
| DBGLOG("ERROR, video stream not created, E_UNEXPECTED"); | |
| return E_UNEXPECTED; | |
| } | |
| return ptr_video_stream_->GetMediaType(ptr_type); | |
| } | |
| UINT MfByteStreamHandlerWrapper::GetVideoStreamCount() const | |
| { | |
| return video_stream_count_; | |
| } | |
| HRESULT MfByteStreamHandlerWrapper::OpenURL(std::wstring url) | |
| { | |
| if (1 > url.length()) | |
| { | |
| DBGLOG("ERROR, empty url string"); | |
| return E_INVALIDARG; | |
| } | |
| if (!ptr_handler_) | |
| { | |
| DBGLOG("ERROR, byte stream handler NULL"); | |
| return E_OUTOFMEMORY; | |
| } | |
| HRESULT hr = MFCreateFile(MF_ACCESSMODE_READ, | |
| MF_OPENMODE_FAIL_IF_NOT_EXIST, | |
| MF_FILEFLAGS_NONE, url.c_str(), | |
| &ptr_byte_stream_); | |
| if (FAILED(hr)) | |
| { | |
| DBGLOG("ERROR, MFCreateFile failed" << HRLOG(hr)); | |
| return hr; | |
| } | |
| hr = ptr_handler_->BeginCreateObject(ptr_byte_stream_, url.c_str(), 0, | |
| NULL, NULL, this, NULL); | |
| if (FAILED(hr)) | |
| { | |
| DBGLOG("ERROR, byte stream handler BeginCreateObject failed, hr=" | |
| << hr); | |
| return hr; | |
| } | |
| hr = open_event_.Wait(); | |
| if (FAILED(hr)) | |
| { | |
| DBGLOG("ERROR, open event wait failed" << HRLOG(hr)); | |
| return hr; | |
| } | |
| if (!ptr_media_src_) | |
| { | |
| DBGLOG("ERROR, NULL media source"); | |
| hr = E_OUTOFMEMORY; | |
| } | |
| return hr; | |
| } | |
| HRESULT MfByteStreamHandlerWrapper::QueryInterface(REFIID riid, void** ppv) | |
| { | |
| static const QITAB qit[] = | |
| { | |
| QITABENT(MfByteStreamHandlerWrapper, IMFAsyncCallback), | |
| { 0 } | |
| }; | |
| return QISearch(this, qit, riid, ppv); | |
| } | |
| ULONG MfByteStreamHandlerWrapper::AddRef() | |
| { | |
| return InterlockedIncrement(&ref_count_); | |
| } | |
| ULONG MfByteStreamHandlerWrapper::Release() | |
| { | |
| UINT ref_count = InterlockedDecrement(&ref_count_); | |
| if (ref_count == 0) | |
| { | |
| delete this; | |
| } | |
| return ref_count; | |
| } | |
| // IMFAsyncCallback method | |
| STDMETHODIMP MfByteStreamHandlerWrapper::GetParameters(DWORD*, DWORD*) | |
| { | |
| // Implementation of this method is optional. | |
| return E_NOTIMPL; | |
| } | |
| // IMFAsyncCallback method | |
| STDMETHODIMP MfByteStreamHandlerWrapper::Invoke(IMFAsyncResult* pAsyncResult) | |
| { | |
| if (!ptr_media_src_ && !ptr_event_queue_) | |
| { | |
| assert(ptr_media_src_ == NULL); | |
| IUnknownPtr ptr_unk_media_src_; | |
| MF_OBJECT_TYPE obj_type; | |
| HRESULT hr = ptr_handler_->EndCreateObject(pAsyncResult, &obj_type, | |
| &ptr_unk_media_src_); | |
| if (FAILED(hr) || !ptr_unk_media_src_) | |
| { | |
| DBGLOG("ERROR, EndCreateObject failed" << HRLOG(hr) | |
| << " return E_FAIL."); | |
| return E_FAIL; | |
| } | |
| else | |
| { | |
| ptr_media_src_ = ptr_unk_media_src_; | |
| hr = ptr_media_src_->QueryInterface(IID_IMFMediaEventGenerator, | |
| reinterpret_cast<void**>(&ptr_event_queue_)); | |
| if (FAILED(hr) || !ptr_event_queue_) | |
| { | |
| DBGLOG("ERROR, failed to obtain media source event generator" | |
| << HRLOG(hr) << " return E_FAIL."); | |
| return E_FAIL; | |
| } | |
| } | |
| return open_event_.Set(); | |
| } | |
| else | |
| { | |
| IMFMediaEventPtr ptr_event; | |
| HRESULT hr = ptr_event_queue_->EndGetEvent(pAsyncResult, &ptr_event); | |
| if (FAILED(hr)) | |
| { | |
| DBGLOG("ERROR, EndGetEvent failed" << HRLOG(hr) | |
| << " return E_FAIL."); | |
| return E_FAIL; | |
| } | |
| return HandleMediaSourceEvent_(ptr_event); | |
| } | |
| } | |
| HRESULT MfByteStreamHandlerWrapper::HandleMediaSourceEvent_( | |
| IMFMediaEventPtr& ptr_event) | |
| { | |
| MediaEventType event_type = MEError; | |
| HRESULT hr = ptr_event->GetType(&event_type); | |
| if (FAILED(hr)) | |
| { | |
| media_event_error_ = hr; | |
| DBGLOG("ERROR, cannot get event type" << HRLOG(hr)); | |
| } | |
| else | |
| { | |
| if (0 != expected_event_type_ && event_type != expected_event_type_) | |
| { | |
| DBGLOG("ERROR, unexpected event type, expected " | |
| << expected_event_type_ << " got " << event_type); | |
| media_event_error_ = E_UNEXPECTED; | |
| } | |
| else | |
| { | |
| switch (event_type) | |
| { | |
| case MENewStream: | |
| DBGLOG("MENewStream"); | |
| media_event_error_ = OnNewStream_(ptr_event); | |
| if (FAILED(media_event_error_)) | |
| { | |
| DBGLOG("MENewStream handling failed"); | |
| } | |
| break; | |
| case MESourcePaused: | |
| DBGLOG("MESourcePaused"); | |
| media_event_error_ = OnSourcePaused_(ptr_event); | |
| if (FAILED(media_event_error_)) | |
| { | |
| DBGLOG("MESourcePaused handling failed"); | |
| } | |
| break; | |
| case MESourceSeeked: | |
| DBGLOG("MESourceSeeked"); | |
| media_event_error_ = OnSourceSeeked_(ptr_event); | |
| if (FAILED(media_event_error_)) | |
| { | |
| DBGLOG("MESourceSeeked handling failed"); | |
| } | |
| break; | |
| case MESourceStarted: | |
| DBGLOG("MESourceStarted"); | |
| media_event_error_ = OnSourceStarted_(ptr_event); | |
| if (FAILED(media_event_error_)) | |
| { | |
| DBGLOG("MESourceStarted handling failed"); | |
| } | |
| break; | |
| case MESourceStopped: | |
| DBGLOG("MESourceStopped"); | |
| media_event_error_ = OnSourceStopped_(ptr_event); | |
| if (FAILED(media_event_error_)) | |
| { | |
| DBGLOG("MESourceStopped handling failed"); | |
| } | |
| break; | |
| case MEUpdatedStream: | |
| DBGLOG("MEUpdatedStream"); | |
| media_event_error_ = OnUpdatedStream_(ptr_event); | |
| if (FAILED(media_event_error_)) | |
| { | |
| DBGLOG("MEUpdatedStream handling failed"); | |
| } | |
| break; | |
| default: | |
| DBGLOG("unhandled event_type=" << event_type); | |
| media_event_error_ = E_UNEXPECTED; | |
| break; | |
| } | |
| } | |
| } | |
| expected_event_type_ = 0; | |
| event_type_recvd_ = event_type; | |
| return media_source_event_.Set(); | |
| } | |
| HRESULT MfByteStreamHandlerWrapper::OnNewStream_(IMFMediaEventPtr &ptr_event) | |
| { | |
| IUnknownPtr ptr_iunk; | |
| HRESULT hr = get_event_iunk_ptr(ptr_event, &ptr_iunk); | |
| if (FAILED(hr)) | |
| { | |
| DBGLOG("ERROR, could not obtain IUnknown from event" << HRLOG(hr)); | |
| return hr; | |
| } | |
| IMFMediaStreamPtr ptr_media_stream = ptr_iunk; | |
| if (!ptr_media_stream) | |
| { | |
| DBGLOG("ERROR, stream pointer null"); | |
| return E_POINTER; | |
| } | |
| _COM_SMARTPTR_TYPEDEF(IMFStreamDescriptor, IID_IMFStreamDescriptor); | |
| IMFStreamDescriptorPtr ptr_stream_desc; | |
| hr = ptr_media_stream->GetStreamDescriptor(&ptr_stream_desc); | |
| if (FAILED(hr)) | |
| { | |
| DBGLOG("ERROR, could not get stream descriptor" << HRLOG(hr)); | |
| return hr; | |
| } | |
| if (!ptr_stream_desc) | |
| { | |
| DBGLOG("ERROR, stream descriptor null"); | |
| return E_POINTER; | |
| } | |
| _COM_SMARTPTR_TYPEDEF(IMFMediaTypeHandler, IID_IMFMediaTypeHandler); | |
| IMFMediaTypeHandlerPtr ptr_handler; | |
| hr = ptr_stream_desc->GetMediaTypeHandler(&ptr_handler); | |
| if (FAILED(hr)) | |
| { | |
| DBGLOG("ERROR, could not get media type handler" << HRLOG(hr)); | |
| return hr; | |
| } | |
| if (!ptr_handler) | |
| { | |
| DBGLOG("ERROR, media type handler null"); | |
| return E_POINTER; | |
| } | |
| GUID major_type = GUID_NULL; | |
| hr = ptr_handler->GetMajorType(&major_type); | |
| if (FAILED(hr)) | |
| { | |
| DBGLOG("ERROR, could not get major type" << HRLOG(hr)); | |
| return hr; | |
| } | |
| if (MFMediaType_Audio != major_type && MFMediaType_Video != major_type) | |
| { | |
| DBGLOG("ERROR, unexpected major type (not audio or video)"); | |
| return E_FAIL; | |
| } | |
| // TODO(tomfinegan): should I hang onto the IMFMediaStream's, or should | |
| // I only interact with them in event handlers? | |
| if (MFMediaType_Audio == major_type) | |
| { | |
| hr = MfMediaStream::Create(ptr_media_stream, &ptr_audio_stream_); | |
| if (FAILED(hr) || !ptr_audio_stream_) | |
| { | |
| DBGLOG("audio MfMediaStream creation failed" << HRLOG(hr)); | |
| } | |
| } | |
| else | |
| { | |
| hr = MfMediaStream::Create(ptr_media_stream, &ptr_video_stream_); | |
| if (FAILED(hr) || !ptr_video_stream_) | |
| { | |
| DBGLOG("video MfMediaStream creation failed" << HRLOG(hr)); | |
| } | |
| } | |
| return hr; | |
| } | |
| HRESULT MfByteStreamHandlerWrapper::OnUpdatedStream_(IMFMediaEventPtr &) | |
| { | |
| // no-op for now | |
| return S_OK; | |
| } | |
| HRESULT MfByteStreamHandlerWrapper::OnSourcePaused_( | |
| IMFMediaEventPtr&) | |
| { | |
| // no-op for now | |
| return S_OK; | |
| } | |
| HRESULT MfByteStreamHandlerWrapper::OnSourceSeeked_( | |
| IMFMediaEventPtr&) | |
| { | |
| // no-op for now | |
| return S_OK; | |
| } | |
| HRESULT MfByteStreamHandlerWrapper::OnSourceStarted_( | |
| IMFMediaEventPtr&) | |
| { | |
| // no-op for now | |
| return S_OK; | |
| } | |
| HRESULT MfByteStreamHandlerWrapper::OnSourceStopped_( | |
| IMFMediaEventPtr&) | |
| { | |
| // no-op for now | |
| return S_OK; | |
| } | |
| HRESULT MfByteStreamHandlerWrapper::WaitForEvent_( | |
| MediaEventType expected_event_type) | |
| { | |
| expected_event_type_ = expected_event_type; | |
| HRESULT hr = ptr_media_src_->BeginGetEvent(this, NULL); | |
| if (FAILED(hr)) | |
| { | |
| DBGLOG("ERROR, BeginGetEvent failed" << HRLOG(hr)); | |
| return hr; | |
| } | |
| hr = media_source_event_.Wait(); | |
| if (FAILED(hr)) | |
| { | |
| DBGLOG("ERROR, media source event wait failed" << HRLOG(hr)); | |
| return hr; | |
| } | |
| if (FAILED(media_event_error_)) | |
| { | |
| // when event handling fails the last error is stored in | |
| // |event_type_recvd_|, just return it to the caller | |
| DBGLOG("ERROR, media source event handling failed" | |
| << HRLOG(media_event_error_)); | |
| return media_event_error_; | |
| } | |
| return hr; | |
| } | |
| HRESULT MfByteStreamHandlerWrapper::WaitForPausedEvents_() | |
| { | |
| // http://msdn.microsoft.com/en-us/library/ms694275(v=VS.85).aspx | |
| // When paused the source sends an MESourcePaused event, and every active | |
| // stream sends an MEStreamPaused event. | |
| // wait for MESourcePaused | |
| HRESULT hr = WaitForEvent_(MESourcePaused); | |
| if (FAILED(hr)) | |
| { | |
| DBGLOG("ERROR, source pause wait failed" << HRLOG(hr)); | |
| return hr; | |
| } | |
| // now wait for the MEStreamPaused events | |
| // TODO(tomfinegan): might have to flush RequestSample event responses from | |
| // the stream event queues on Pause because: | |
| // http://msdn.microsoft.com/en-us/library/ms694275(v=VS.85).aspx | |
| // Note that the source's event queue is not serialized with the stream | |
| // event queues, so the client might receive some samples after the | |
| // MESourcePaused event, due to multi-threading issues. But the client | |
| // will not receive any samples from a stream after the MEStreamPaused | |
| // event. | |
| if (ptr_audio_stream_) | |
| { | |
| hr = ptr_audio_stream_->WaitForStreamEvent(MEStreamPaused); | |
| if (FAILED(hr)) | |
| { | |
| DBGLOG("ERROR, audio stream pause wait failed" << HRLOG(hr)); | |
| return hr; | |
| } | |
| } | |
| if (ptr_video_stream_) | |
| { | |
| hr = ptr_video_stream_->WaitForStreamEvent(MEStreamPaused); | |
| if (FAILED(hr)) | |
| { | |
| DBGLOG("ERROR, video stream pause wait failed" << HRLOG(hr)); | |
| return hr; | |
| } | |
| } | |
| return hr; | |
| } | |
| HRESULT MfByteStreamHandlerWrapper::WaitForNewStreamEvents_() | |
| { | |
| const UINT num_new_events = audio_stream_count_ + video_stream_count_; | |
| if (0 == num_new_events) | |
| { | |
| DBGLOG("ERROR, 0 events to wait on" << HRLOG(E_INVALIDARG)); | |
| return E_INVALIDARG; | |
| } | |
| // Previous call to Start on |ptr_media_src_| was our first: | |
| // http://msdn.microsoft.com/en-us/library/ms694101(v=VS.85).aspx | |
| // For each new stream, the source sends an MENewStream event. This | |
| // event is sent for the first Start call in which the stream appears. | |
| // The event data is a pointer to the stream's IMFMediaStream | |
| // interface. | |
| HRESULT hr = E_FAIL; | |
| UINT num_events = 0; | |
| while (num_events < num_new_events) | |
| { | |
| hr = WaitForEvent_(MENewStream); | |
| if (FAILED(hr)) | |
| { | |
| DBGLOG("ERROR, WaitForEvent_ MENewStream failed" | |
| << HRLOG(hr)); | |
| return hr; | |
| } | |
| ++num_events; | |
| } | |
| return hr; | |
| } | |
| HRESULT MfByteStreamHandlerWrapper::WaitForUpdatedStreamEvents_() | |
| { | |
| const UINT num_update_events = audio_stream_count_ + video_stream_count_; | |
| if (0 == num_update_events) | |
| { | |
| DBGLOG("ERROR, 0 events to wait on" << HRLOG(E_INVALIDARG)); | |
| return E_INVALIDARG; | |
| } | |
| // Most recent call to Start was a restart | |
| // http://msdn.microsoft.com/en-us/library/ms694101(v=VS.85).aspx | |
| // For each updated stream, the source sends an MEUpdatedStream | |
| // event. A stream is updated if the stream already existed when | |
| // Start was called (for example, if the application seeks during | |
| // playback). The event data is a pointer to the stream's | |
| // IMFMediaStream interface. | |
| HRESULT hr = E_FAIL; | |
| UINT num_events = 0; | |
| while (num_events < num_update_events) | |
| { | |
| hr = WaitForEvent_(MEUpdatedStream); | |
| if (FAILED(hr)) | |
| { | |
| DBGLOG("ERROR, stream update wait failed" << HRLOG(hr)); | |
| return hr; | |
| } | |
| ++num_events; | |
| } | |
| return hr; | |
| } | |
| HRESULT MfByteStreamHandlerWrapper::WaitForStartedEvents_() | |
| { | |
| // http://msdn.microsoft.com/en-us/library/ms694101(v=VS.85).aspx | |
| // If the source sends an MESourceStarted event, each media stream sends | |
| // an MEStreamStarted event. | |
| // wait for MESourceStarted | |
| HRESULT hr = WaitForEvent_(MESourceStarted); | |
| if (FAILED(hr)) | |
| { | |
| DBGLOG("ERROR, source start wait failed" << HRLOG(hr)); | |
| return hr; | |
| } | |
| // now wait for the MEStreamStarted events | |
| if (ptr_audio_stream_) | |
| { | |
| hr = ptr_audio_stream_->WaitForStreamEvent(MEStreamStarted); | |
| if (FAILED(hr)) | |
| { | |
| DBGLOG("ERROR, audio stream start wait failed" << HRLOG(hr)); | |
| return hr; | |
| } | |
| } | |
| if (ptr_video_stream_) | |
| { | |
| hr = ptr_video_stream_->WaitForStreamEvent(MEStreamStarted); | |
| if (FAILED(hr)) | |
| { | |
| DBGLOG("ERROR, video stream start wait failed" << HRLOG(hr)); | |
| return hr; | |
| } | |
| } | |
| return hr; | |
| } | |
| HRESULT MfByteStreamHandlerWrapper::WaitForSeekedEvents_() | |
| { | |
| // http://msdn.microsoft.com/en-us/library/ms694101(v=VS.85).aspx | |
| // If the source sends an MESourceSeeked event, each stream sends an | |
| // MEStreamSeeked event. | |
| // wait for MESourceSeeked | |
| HRESULT hr = WaitForEvent_(MESourceSeeked); | |
| if (FAILED(hr)) | |
| { | |
| DBGLOG("ERROR, source start wait failed" << HRLOG(hr)); | |
| return hr; | |
| } | |
| // now wait for the MEStreamSeeked events | |
| if (ptr_audio_stream_) | |
| { | |
| hr = ptr_audio_stream_->WaitForStreamEvent(MEStreamSeeked); | |
| if (FAILED(hr)) | |
| { | |
| DBGLOG("ERROR, audio stream seek wait failed" << HRLOG(hr)); | |
| return hr; | |
| } | |
| } | |
| if (ptr_video_stream_) | |
| { | |
| hr = ptr_video_stream_->WaitForStreamEvent(MEStreamSeeked); | |
| if (FAILED(hr)) | |
| { | |
| DBGLOG("ERROR, video stream seek wait failed" << HRLOG(hr)); | |
| return hr; | |
| } | |
| } | |
| return hr; | |
| } | |
| HRESULT MfByteStreamHandlerWrapper::WaitForStoppedEvents_() | |
| { | |
| // http://msdn.microsoft.com/en-us/library/ms694101(v=VS.85).aspx | |
| // If the source sends an MESourceStopped event, each stream sends an | |
| // MEStreamStopped event. | |
| // wait for MESourceStopped | |
| HRESULT hr = WaitForEvent_(MESourceStopped); | |
| if (FAILED(hr)) | |
| { | |
| DBGLOG("ERROR, source start wait failed" << HRLOG(hr)); | |
| return hr; | |
| } | |
| // now wait for the MEStreamStopped events | |
| if (ptr_audio_stream_) | |
| { | |
| hr = ptr_audio_stream_->WaitForStreamEvent(MEStreamStopped); | |
| if (FAILED(hr)) | |
| { | |
| DBGLOG("ERROR, audio stream stop wait failed" << HRLOG(hr)); | |
| return hr; | |
| } | |
| } | |
| if (ptr_video_stream_) | |
| { | |
| hr = ptr_video_stream_->WaitForStreamEvent(MEStreamStopped); | |
| if (FAILED(hr)) | |
| { | |
| DBGLOG("ERROR, video stream stop wait failed" << HRLOG(hr)); | |
| return hr; | |
| } | |
| } | |
| return hr; | |
| } | |
| // TODO(tomfinegan): I should rename this to GetDefaultStreams_, then add a | |
| // GetAvailableStreams method that exposes all of the | |
| // available streams after calling GetDefaultStreams_. | |
| HRESULT MfByteStreamHandlerWrapper::LoadMediaStreams() | |
| { | |
| HRESULT hr = ptr_media_src_->CreatePresentationDescriptor(&ptr_pres_desc_); | |
| if (FAILED(hr) || !ptr_pres_desc_) | |
| { | |
| DBGLOG("ERROR, CreatePresentationDescriptor failed" << HRLOG(hr)); | |
| return hr; | |
| } | |
| hr = ptr_pres_desc_->GetStreamDescriptorCount(&stream_count_); | |
| if (FAILED(hr) || !ptr_pres_desc_) | |
| { | |
| DBGLOG("ERROR, GetStreamDescriptorCount failed" << HRLOG(hr)); | |
| return hr; | |
| } | |
| // get stream descriptors and store audio/video descs in our vectors | |
| for (DWORD i = 0; i < stream_count_; ++i) | |
| { | |
| BOOL selected; | |
| _COM_SMARTPTR_TYPEDEF(IMFStreamDescriptor, IID_IMFStreamDescriptor); | |
| IMFStreamDescriptorPtr ptr_desc; | |
| hr = ptr_pres_desc_->GetStreamDescriptorByIndex(i, &selected, | |
| &ptr_desc); | |
| if (FAILED(hr) || !ptr_desc) | |
| { | |
| DBGLOG("ERROR, GetStreamDescriptorByIndex failed, count=" | |
| << stream_count_ << " index=" << i << HRLOG(hr)); | |
| return hr; | |
| } | |
| // TODO(tomfinegan): decide what to do w/unselected streams | |
| if (selected) | |
| { | |
| GUID major_type; | |
| hr = get_major_type(ptr_desc, &major_type); | |
| if (FAILED(hr)) | |
| { | |
| DBGLOG("ERROR, get_major_type failed, count=" << stream_count_ | |
| << " index=" << i << HRLOG(hr)); | |
| return hr; | |
| } | |
| if (MFMediaType_Audio == major_type) | |
| { | |
| ++audio_stream_count_; | |
| } | |
| else if (MFMediaType_Video == major_type) | |
| { | |
| ++video_stream_count_; | |
| } | |
| ++selected_stream_count_; | |
| } | |
| } | |
| return hr; | |
| } | |
| HRESULT MfByteStreamHandlerWrapper::Start(bool seeking, LONGLONG start_time) | |
| { | |
| if (!ptr_media_src_) | |
| { | |
| DBGLOG("ERROR, no media source"); | |
| return E_INVALIDARG; | |
| } | |
| if (!ptr_pres_desc_) | |
| { | |
| DBGLOG("ERROR, no presentation descriptor"); | |
| return E_INVALIDARG; | |
| } | |
| if (!audio_stream_count_ && !video_stream_count_) | |
| { | |
| DBGLOG("ERROR, no streams"); | |
| return E_INVALIDARG; | |
| } | |
| if (!ptr_event_queue_) | |
| { | |
| DBGLOG("ERROR, no event queue"); | |
| return E_INVALIDARG; | |
| } | |
| PROPVARIANT time_var; | |
| PropVariantInit(&time_var); | |
| if (seeking) | |
| { | |
| // seeking enabled, store the time in |time_var| | |
| time_var.vt = VT_I8; | |
| time_var.hVal.QuadPart = start_time; | |
| } | |
| else | |
| { | |
| // not seeking, leave |time_var| empty | |
| time_var.vt = VT_EMPTY; | |
| } | |
| // GUID_NULL == TIME_FORMAT_NONE, 100 ns units (aka TIME_FORMAT_MEDIA_TIME | |
| // in DirectShow) | |
| const GUID time_format = GUID_NULL; | |
| HRESULT hr = ptr_media_src_->Start(ptr_pres_desc_, &time_format, | |
| &time_var); | |
| // Note: if IMFMediaSource::Start fails asynchronously, our handler will | |
| // receive an MESourceStarted event w/data set to error code. | |
| // However, a comment in webmmfsource lists this behavior as TODO. | |
| if (FAILED(hr)) | |
| { | |
| DBGLOG("ERROR, IMFMediaSource::Start failed" << HRLOG(hr)); | |
| return hr; | |
| } | |
| state_ = MFSTATE_STARTED; | |
| DBGLOG("state_=MFSTATE_STARTED"); | |
| if (!ptr_audio_stream_ && !ptr_video_stream_) | |
| { | |
| // Our |IMFMediaStreamPtr|'s pointers are stored when MENewStream | |
| // is received in |WaitForNewStreamEvents_|. | |
| hr = WaitForNewStreamEvents_(); | |
| if (FAILED(hr)) | |
| { | |
| state_ = MFSTATE_ERROR; | |
| DBGLOG("state_=MFSTATE_ERROR, new stream event wait failed" | |
| << HRLOG(hr)); | |
| return hr; | |
| } | |
| // always set seeking to false; we're getting MESourceStarted and | |
| // MEStreamStarted in response to the first Start when a time is | |
| // specified. | |
| seeking = false; | |
| } | |
| else | |
| { | |
| // If the streams already exist when calling IMFMediaSource::Start, | |
| // each stream will send a MEUpdatedStream event. | |
| hr = WaitForUpdatedStreamEvents_(); | |
| if (FAILED(hr)) | |
| { | |
| state_ = MFSTATE_ERROR; | |
| DBGLOG("state_=MFSTATE_ERROR, updated stream event wait failed" | |
| << HRLOG(hr)); | |
| return hr; | |
| } | |
| } | |
| if (!seeking) | |
| { | |
| // if we did not seek, webmmfsource will send: | |
| // MESourceStarted | |
| // MEStreamStarted * num streams | |
| hr = WaitForStartedEvents_(); | |
| if (FAILED(hr)) | |
| { | |
| state_ = MFSTATE_ERROR; | |
| DBGLOG("state_=MFSTATE_ERROR, start event wait failed" | |
| << HRLOG(hr)); | |
| return hr; | |
| } | |
| } | |
| else | |
| { | |
| // when we seek, webmmfsource will send: | |
| // MESourceSeeked | |
| // MEStreamSeeked * num streams | |
| hr = WaitForSeekedEvents_(); | |
| if (FAILED(hr)) | |
| { | |
| state_ = MFSTATE_ERROR; | |
| DBGLOG("state_=MFSTATE_ERROR, seek event wait failed" | |
| << HRLOG(hr)); | |
| return hr; | |
| } | |
| } | |
| return hr; | |
| } | |
| HRESULT MfByteStreamHandlerWrapper::Pause() | |
| { | |
| const bool no_streams = (!ptr_audio_stream_ && !ptr_video_stream_); | |
| const bool wrong_state = MFSTATE_STARTED != state_; | |
| if (no_streams || wrong_state) | |
| { | |
| DBGLOG("ERROR, MF_E_INVALID_STATE_TRANSITION"); | |
| return MF_E_INVALID_STATE_TRANSITION; | |
| } | |
| HRESULT hr = E_FAIL; | |
| hr = ptr_media_src_->Pause(); | |
| if (FAILED(hr)) | |
| { | |
| DBGLOG("ERROR, Paused failed" << HRLOG(hr)); | |
| return hr; | |
| } | |
| hr = WaitForPausedEvents_(); | |
| if (FAILED(hr)) | |
| { | |
| DBGLOG("ERROR, Paused failed" << HRLOG(hr)); | |
| return hr; | |
| } | |
| state_ = MFSTATE_PAUSED; | |
| return hr; | |
| } | |
| HRESULT MfByteStreamHandlerWrapper::Stop() | |
| { | |
| const bool no_streams = (!ptr_audio_stream_ && !ptr_video_stream_); | |
| const bool wrong_state = | |
| (MFSTATE_STARTED != state_ && MFSTATE_PAUSED != state_); | |
| if (no_streams || wrong_state) | |
| { | |
| DBGLOG("ERROR, MF_E_INVALID_STATE_TRANSITION"); | |
| return MF_E_INVALID_STATE_TRANSITION; | |
| } | |
| HRESULT hr = E_FAIL; | |
| hr = ptr_media_src_->Stop(); | |
| if (FAILED(hr)) | |
| { | |
| DBGLOG("ERROR, Paused failed" << HRLOG(hr)); | |
| return hr; | |
| } | |
| hr = WaitForStoppedEvents_(); | |
| if (FAILED(hr)) | |
| { | |
| DBGLOG("ERROR, Stop failed" << HRLOG(hr)); | |
| return hr; | |
| } | |
| state_ = MFSTATE_STOPPED; | |
| return hr; | |
| } | |
| } // WebmMfUtil namespace |