| // 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 "debugutil.hpp" | |
| #include "eventutil.hpp" | |
| #include "mfmediastream.hpp" | |
| #include "mfutil.hpp" | |
| namespace WebmMfUtil | |
| { | |
| MfMediaStream::MfMediaStream(): | |
| expected_event_(0), | |
| ref_count_(0), | |
| stream_event_error_(S_OK), | |
| stream_event_recvd_(0) | |
| { | |
| } | |
| MfMediaStream::~MfMediaStream() | |
| { | |
| } | |
| HRESULT MfMediaStream::QueryInterface(REFIID riid, void** ppv) | |
| { | |
| static const QITAB qit[] = | |
| { | |
| QITABENT(MfMediaStream, IMFAsyncCallback), | |
| { 0 } | |
| }; | |
| return QISearch(this, qit, riid, ppv); | |
| } | |
| ULONG MfMediaStream::AddRef() | |
| { | |
| return InterlockedIncrement(&ref_count_); | |
| } | |
| ULONG MfMediaStream::Release() | |
| { | |
| UINT ref_count = InterlockedDecrement(&ref_count_); | |
| if (ref_count == 0) | |
| { | |
| delete this; | |
| } | |
| return ref_count; | |
| } | |
| HRESULT MfMediaStream::Create_(IMFMediaStreamPtr& ptr_stream) | |
| { | |
| ptr_stream_ = ptr_stream; | |
| HRESULT hr = ptr_stream_->QueryInterface(IID_IMFMediaEventGenerator, | |
| reinterpret_cast<void**>(&ptr_event_queue_)); | |
| if (FAILED(hr)) | |
| { | |
| DBGLOG("ERROR, failed to obtain stream event generator" << HRLOG(hr) | |
| << " returning E_FAIL."); | |
| return E_FAIL; | |
| } | |
| IMFStreamDescriptorPtr ptr_desc; | |
| hr = ptr_stream->GetStreamDescriptor(&ptr_desc); | |
| if (FAILED(hr)) | |
| { | |
| DBGLOG("ERROR, GetStreamDescriptor failed" << HRLOG(hr)); | |
| return hr; | |
| } | |
| hr = get_media_type(ptr_desc, &ptr_media_type_); | |
| if (FAILED(hr)) | |
| { | |
| DBGLOG("ERROR, get_media_type failed" << HRLOG(hr)); | |
| return hr; | |
| } | |
| hr = stream_event_.Create(); | |
| if (FAILED(hr)) | |
| { | |
| DBGLOG("ERROR, stream event creation failed" << HRLOG(hr)); | |
| return hr; | |
| } | |
| return hr; | |
| } | |
| // IMFAsyncCallback method | |
| STDMETHODIMP MfMediaStream::GetParameters(DWORD*, DWORD*) | |
| { | |
| // Implementation of this method is optional. | |
| return E_NOTIMPL; | |
| } | |
| // IMFAsyncCallback method | |
| STDMETHODIMP MfMediaStream::Invoke(IMFAsyncResult* pAsyncResult) | |
| { | |
| stream_event_error_ = E_FAIL; | |
| MediaEventType event_type = MEError; | |
| IMFMediaEventPtr ptr_event; | |
| HRESULT hr = ptr_event_queue_->EndGetEvent(pAsyncResult, &ptr_event); | |
| if (FAILED(hr)) | |
| { | |
| DBGLOG("ERROR, EndGetEvent failed" << HRLOG(hr)); | |
| } | |
| else | |
| { | |
| hr = ptr_event->GetType(&event_type); | |
| if (FAILED(hr)) | |
| { | |
| DBGLOG("ERROR, cannot get event type" << HRLOG(hr)); | |
| } | |
| if (0 != expected_event_ && event_type != expected_event_) | |
| { | |
| DBGLOG("ERROR, unexpected event type, expected " | |
| << expected_event_ << " got " << event_type); | |
| stream_event_error_ = E_UNEXPECTED; | |
| } | |
| else | |
| { | |
| switch (event_type) | |
| { | |
| case MEMediaSample: | |
| DBGLOG("MEMediaSample"); | |
| stream_event_error_ = OnMediaSample_(ptr_event); | |
| if (FAILED(stream_event_error_)) | |
| { | |
| DBGLOG("MEMediaSample handling failed"); | |
| } | |
| break; | |
| case MEStreamPaused: | |
| DBGLOG("MEStreamPaused"); | |
| stream_event_error_ = OnStreamPaused_(ptr_event); | |
| if (FAILED(stream_event_error_)) | |
| { | |
| DBGLOG("MEStreamSeeked handling failed"); | |
| } | |
| break; | |
| case MEStreamSeeked: | |
| DBGLOG("MEStreamSeeked"); | |
| stream_event_error_ = OnStreamSeeked_(ptr_event); | |
| if (FAILED(stream_event_error_)) | |
| { | |
| DBGLOG("MEStreamSeeked handling failed"); | |
| } | |
| break; | |
| case MEStreamStarted: | |
| DBGLOG("MEStreamStarted"); | |
| stream_event_error_ = OnStreamStarted_(ptr_event); | |
| if (FAILED(stream_event_error_)) | |
| { | |
| DBGLOG("MEStreamStarted handling failed"); | |
| } | |
| break; | |
| case MEStreamStopped: | |
| DBGLOG("MEStreamStopped"); | |
| stream_event_error_ = OnStreamStopped_(ptr_event); | |
| if (FAILED(stream_event_error_)) | |
| { | |
| DBGLOG("MEStreamStopped handling failed"); | |
| } | |
| break; | |
| default: | |
| stream_event_error_ = E_UNEXPECTED; | |
| DBGLOG("unhandled event_type=" << event_type); | |
| break; | |
| } | |
| } | |
| } | |
| expected_event_ = 0; | |
| stream_event_recvd_ = event_type; | |
| return stream_event_.Set(); | |
| } | |
| HRESULT MfMediaStream::OnMediaSample_(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; | |
| } | |
| ptr_sample_ = ptr_iunk; | |
| if (!ptr_sample_) | |
| { | |
| DBGLOG("ERROR, sample pointer null"); | |
| return E_POINTER; | |
| } | |
| return S_OK; | |
| } | |
| HRESULT MfMediaStream::OnStreamPaused_(IMFMediaEventPtr&) | |
| { | |
| // no-op for now | |
| return S_OK; | |
| } | |
| HRESULT MfMediaStream::OnStreamSeeked_(IMFMediaEventPtr&) | |
| { | |
| // no-op for now | |
| return S_OK; | |
| } | |
| HRESULT MfMediaStream::OnStreamStarted_(IMFMediaEventPtr&) | |
| { | |
| // no-op for now | |
| return S_OK; | |
| } | |
| HRESULT MfMediaStream::OnStreamStopped_(IMFMediaEventPtr&) | |
| { | |
| // no-op for now | |
| return S_OK; | |
| } | |
| HRESULT MfMediaStream::Create(IMFMediaStreamPtr& ptr_mfstream, | |
| MfMediaStream** ptr_instance) | |
| { | |
| if (!ptr_mfstream) | |
| { | |
| DBGLOG("null IMFMediaStream, returning E_INVALIDARG"); | |
| return E_INVALIDARG; | |
| } | |
| MfMediaStream* ptr_stream = new (std::nothrow) MfMediaStream(); | |
| if (!ptr_stream) | |
| { | |
| DBGLOG("null MfMediaStream, returning E_OUTOFMEMORY"); | |
| return E_OUTOFMEMORY; | |
| } | |
| HRESULT hr = ptr_stream->Create_(ptr_mfstream); | |
| if (FAILED(hr)) | |
| { | |
| DBGLOG("null MfMediaStream::Create_ failed" << HRLOG(hr)); | |
| return hr; | |
| } | |
| ptr_stream->AddRef(); | |
| *ptr_instance = ptr_stream; | |
| return hr; | |
| } | |
| HRESULT MfMediaStream::GetMediaType(IMFMediaType **ptr_type) | |
| { | |
| if (!ptr_media_type_) | |
| { | |
| DBGLOG("ERROR, media type not set, E_UNEXPECTED"); | |
| return E_UNEXPECTED; | |
| } | |
| return copy_media_type(ptr_media_type_, ptr_type); | |
| } | |
| HRESULT MfMediaStream::WaitForStreamEvent(MediaEventType event_type) | |
| { | |
| expected_event_ = event_type; | |
| HRESULT hr = ptr_stream_->BeginGetEvent(this, NULL); | |
| if (FAILED(hr)) | |
| { | |
| DBGLOG("ERROR, BeginGetEvent failed" << HRLOG(hr)); | |
| return hr; | |
| } | |
| hr = stream_event_.Wait(); | |
| if (FAILED(hr)) | |
| { | |
| DBGLOG("ERROR, stream event wait failed" << HRLOG(hr)); | |
| return hr; | |
| } | |
| if (FAILED(stream_event_error_)) | |
| { | |
| // when event handling fails the last error is stored in | |
| // |media_event_type_recvd_|, just return it to the caller | |
| DBGLOG("ERROR, stream event handling failed" | |
| << HRLOG(stream_event_error_)); | |
| return stream_event_error_; | |
| } | |
| return hr; | |
| } | |
| HRESULT MfMediaStream::GetSample(IMFSample **ptr_sample) | |
| { | |
| if (!ptr_stream_) | |
| { | |
| DBGLOG("ERROR, stream not set, E_UNEXPECTED"); | |
| return E_UNEXPECTED; | |
| } | |
| // TODO(tomfinegan): need to handle pause; RequestSample will succeed while | |
| // paused, but the stream won't deliver the sample. | |
| // This means we'll block in |WaitForStreamEvent|...? | |
| // Curse you for not being specific MSDN. I guess I have | |
| // to return a wrong state error when paused to avoid the | |
| // situation altogether. | |
| HRESULT hr = ptr_stream_->RequestSample(NULL); | |
| // MF_E_MEDIA_SOURCE_WRONGSTATE is not treated as an error by the pipeline | |
| if (FAILED(hr)) | |
| { | |
| DBGLOG("ERROR, RequestSample failed" << HRLOG(hr)); | |
| return hr; | |
| } | |
| hr = WaitForStreamEvent(MEMediaSample); | |
| if (FAILED(hr)) | |
| { | |
| DBGLOG("WaitForStreamEvent MEMediaSample failed" << HRLOG(hr)); | |
| return hr; | |
| } | |
| if (FAILED(stream_event_error_)) | |
| { | |
| DBGLOG("MEMediaSample event handling failed" | |
| << HRLOG(stream_event_error_)); | |
| return stream_event_error_; | |
| } | |
| if (!ptr_sample_) | |
| { | |
| // TODO(tomfinegan): | |
| DBGLOG("ERROR, null sample"); | |
| return E_OUTOFMEMORY; | |
| } | |
| *ptr_sample = ptr_sample_.Detach(); | |
| return hr; | |
| } | |
| } // WebmMfUtil namespace |