blob: 9e0041ac585dcb6e23f5f7f7f4b896974037d7c9 [file] [log] [blame]
// 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.h"
#include "eventutil.h"
#include "mfmediastream.h"
#include "mfutil.h"
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