Bug 1174055 - Remove WMFReader. r=jya

--HG--
rename : dom/media/wmf/DXVA2Manager.cpp => dom/media/platforms/wmf/DXVA2Manager.cpp
rename : dom/media/wmf/DXVA2Manager.h => dom/media/platforms/wmf/DXVA2Manager.h
rename : dom/media/wmf/WMF.h => dom/media/platforms/wmf/WMF.h
rename : dom/media/wmf/WMFUtils.cpp => dom/media/platforms/wmf/WMFUtils.cpp
rename : dom/media/wmf/WMFUtils.h => dom/media/platforms/wmf/WMFUtils.h
This commit is contained in:
Chris Pearce 2015-06-16 14:28:01 +12:00
parent efc7a7830e
commit 2f60326595
18 changed files with 13 additions and 2406 deletions

View File

@ -56,10 +56,6 @@
#include "RtspOmxDecoder.h"
#include "RtspOmxReader.h"
#endif
#ifdef MOZ_WMF
#include "WMFDecoder.h"
#include "WMFReader.h"
#endif
#ifdef MOZ_DIRECTSHOW
#include "DirectShowDecoder.h"
#include "DirectShowReader.h"
@ -334,14 +330,6 @@ IsAndroidMediaType(const nsACString& aType)
}
#endif
#ifdef MOZ_WMF
static bool
IsWMFSupportedType(const nsACString& aType)
{
return WMFDecoder::CanPlayType(aType, NS_LITERAL_STRING(""));
}
#endif
#ifdef MOZ_DIRECTSHOW
static bool
IsDirectShowSupportedType(const nsACString& aType)
@ -481,23 +469,10 @@ DecoderTraits::CanHandleMediaType(const char* aMIMEType,
}
#endif
#ifdef MOZ_DIRECTSHOW
// Note: DirectShow should come before WMF, so that we prefer DirectShow's
// MP3 support over WMF's.
if (DirectShowDecoder::GetSupportedCodecs(nsDependentCString(aMIMEType), &codecList)) {
result = CANPLAY_MAYBE;
}
#endif
#ifdef MOZ_WMF
if (!Preferences::GetBool("media.fragmented-mp4.exposed", false) &&
IsWMFSupportedType(nsDependentCString(aMIMEType))) {
if (!aHaveRequestedCodecs) {
return CANPLAY_MAYBE;
}
return WMFDecoder::CanPlayType(nsDependentCString(aMIMEType),
aRequestedCodecs)
? CANPLAY_YES : CANPLAY_NO;
}
#endif
#ifdef MOZ_APPLEMEDIA
if (IsAppleMediaSupportedType(nsDependentCString(aMIMEType), &codecList)) {
result = CANPLAY_MAYBE;
@ -637,12 +612,6 @@ InstantiateDecoder(const nsACString& aType, MediaDecoderOwner* aOwner)
return decoder.forget();
}
#endif
#ifdef MOZ_WMF
if (IsWMFSupportedType(aType)) {
decoder = new WMFDecoder();
return decoder.forget();
}
#endif
#ifdef MOZ_APPLEMEDIA
if (IsAppleMediaSupportedType(aType)) {
decoder = new AppleDecoder();
@ -727,17 +696,10 @@ MediaDecoderReader* DecoderTraits::CreateReader(const nsACString& aType, Abstrac
} else
#endif
#ifdef MOZ_DIRECTSHOW
// Note: DirectShowReader is preferred for MP3, but if it's disabled we
// fallback to the WMFReader.
if (IsDirectShowSupportedType(aType)) {
decoderReader = new DirectShowReader(aDecoder);
} else
#endif
#ifdef MOZ_WMF
if (IsWMFSupportedType(aType)) {
decoderReader = new WMFReader(aDecoder);
} else
#endif
#ifdef MOZ_APPLEMEDIA
if (IsAppleMediaSupportedType(aType)) {
decoderReader = new AppleMP3Reader(aDecoder);
@ -781,9 +743,6 @@ bool DecoderTraits::IsSupportedInVideoDocument(const nsACString& aType)
IsMP4SupportedType(aType) ||
#endif
IsMP3SupportedType(aType) ||
#ifdef MOZ_WMF
IsWMFSupportedType(aType) ||
#endif
#ifdef MOZ_DIRECTSHOW
IsDirectShowSupportedType(aType) ||
#endif

View File

@ -28,10 +28,6 @@
#include "mozilla/dom/VideoTrack.h"
#include "mozilla/dom/VideoTrackList.h"
#ifdef MOZ_WMF
#include "WMFDecoder.h"
#endif
using namespace mozilla::dom;
using namespace mozilla::layers;
using namespace mozilla::media;
@ -1509,14 +1505,6 @@ MediaDecoder::IsAndroidMediaEnabled()
}
#endif
#ifdef MOZ_WMF
bool
MediaDecoder::IsWMFEnabled()
{
return WMFDecoder::IsEnabled();
}
#endif
#ifdef MOZ_APPLEMEDIA
bool
MediaDecoder::IsAppleMP3Enabled()

View File

@ -54,9 +54,6 @@ if CONFIG['MOZ_DIRECTSHOW']:
if CONFIG['MOZ_ANDROID_OMX']:
DIRS += ['android']
if CONFIG['MOZ_WMF']:
DIRS += ['wmf']
if CONFIG['MOZ_FMP4']:
DIRS += ['fmp4']

View File

@ -5,13 +5,17 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
EXPORTS += [
'DXVA2Manager.h',
'MFTDecoder.h',
'WMF.h',
'WMFAudioMFTManager.h',
'WMFDecoderModule.h',
'WMFMediaDataDecoder.h',
'WMFUtils.h',
'WMFVideoMFTManager.h',
]
UNIFIED_SOURCES += [
'DXVA2Manager.cpp',
'MFTDecoder.cpp',
'WMFAudioMFTManager.cpp',
'WMFDecoderModule.cpp',
@ -19,8 +23,17 @@ UNIFIED_SOURCES += [
'WMFVideoMFTManager.cpp',
]
SOURCES += [
'WMFUtils.cpp',
]
include('/ipc/chromium/chromium-config.mozbuild')
FINAL_LIBRARY = 'xul'
if CONFIG['OS_ARCH'] == 'WINNT':
DEFINES['NOMINMAX'] = True
FAIL_ON_WARNINGS = True
CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']

View File

@ -1,680 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "WMF.h"
#include <unknwn.h>
#include <ole2.h>
#include "WMFByteStream.h"
#include "WMFSourceReaderCallback.h"
#include "WMFUtils.h"
#include "MediaResource.h"
#include "nsISeekableStream.h"
#include "mozilla/RefPtr.h"
#include "nsIThreadPool.h"
#include "nsXPCOMCIDInternal.h"
#include "nsComponentManagerUtils.h"
#include "mozilla/DebugOnly.h"
#include "SharedThreadPool.h"
#include <algorithm>
#include <cassert>
namespace mozilla {
PRLogModuleInfo* gWMFByteStreamLog = nullptr;
#define WMF_BS_LOG(...) MOZ_LOG(gWMFByteStreamLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
WMFByteStream::WMFByteStream(MediaResource* aResource,
WMFSourceReaderCallback* aSourceReaderCallback)
: mSourceReaderCallback(aSourceReaderCallback),
mResource(aResource),
mReentrantMonitor("WMFByteStream.Data"),
mOffset(0),
mIsShutdown(false)
{
NS_ASSERTION(NS_IsMainThread(), "Must be on main thread.");
NS_ASSERTION(mSourceReaderCallback, "Must have a source reader callback.");
if (!gWMFByteStreamLog) {
gWMFByteStreamLog = PR_NewLogModule("WMFByteStream");
}
WMF_BS_LOG("[%p] WMFByteStream CTOR", this);
MOZ_COUNT_CTOR(WMFByteStream);
}
WMFByteStream::~WMFByteStream()
{
MOZ_COUNT_DTOR(WMFByteStream);
WMF_BS_LOG("[%p] WMFByteStream DTOR", this);
}
nsresult
WMFByteStream::Init()
{
NS_ASSERTION(NS_IsMainThread(), "Must be on main thread.");
mThreadPool = SharedThreadPool::Get(NS_LITERAL_CSTRING("WMFByteStream IO"), 4);
NS_ENSURE_TRUE(mThreadPool, NS_ERROR_FAILURE);
NS_ConvertUTF8toUTF16 contentTypeUTF16(mResource->GetContentType());
if (!contentTypeUTF16.IsEmpty()) {
HRESULT hr = wmf::MFCreateAttributes(byRef(mAttributes), 1);
NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
hr = mAttributes->SetString(MF_BYTESTREAM_CONTENT_TYPE,
contentTypeUTF16.get());
NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
WMF_BS_LOG("[%p] WMFByteStream has Content-Type=%s", this, mResource->GetContentType().get());
}
return NS_OK;
}
nsresult
WMFByteStream::Shutdown()
{
{
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
mIsShutdown = true;
}
mSourceReaderCallback->Cancel();
return NS_OK;
}
// IUnknown Methods
STDMETHODIMP
WMFByteStream::QueryInterface(REFIID aIId, void **aInterface)
{
WMF_BS_LOG("[%p] WMFByteStream::QueryInterface %s", this, GetGUIDName(aIId).get());
if (aIId == IID_IMFByteStream) {
return DoGetInterface(static_cast<IMFByteStream*>(this), aInterface);
}
if (aIId == IID_IUnknown) {
return DoGetInterface(static_cast<IMFByteStream*>(this), aInterface);
}
if (aIId == IID_IMFAttributes) {
return DoGetInterface(static_cast<IMFAttributes*>(this), aInterface);
}
*aInterface = nullptr;
return E_NOINTERFACE;
}
NS_IMPL_ADDREF(WMFByteStream)
NS_IMPL_RELEASE(WMFByteStream)
// Stores data regarding an async read opreation.
class ReadRequest final : public IUnknown {
~ReadRequest() {}
public:
ReadRequest(int64_t aOffset, BYTE* aBuffer, ULONG aLength)
: mOffset(aOffset),
mBuffer(aBuffer),
mBufferLength(aLength),
mBytesRead(0)
{}
// IUnknown Methods
STDMETHODIMP QueryInterface(REFIID aRIID, LPVOID *aOutObject);
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();
int64_t mOffset;
BYTE* mBuffer;
ULONG mBufferLength;
ULONG mBytesRead;
// IUnknown ref counting.
ThreadSafeAutoRefCnt mRefCnt;
NS_DECL_OWNINGTHREAD
};
NS_IMPL_ADDREF(ReadRequest)
NS_IMPL_RELEASE(ReadRequest)
// IUnknown Methods
STDMETHODIMP
ReadRequest::QueryInterface(REFIID aIId, void **aInterface)
{
WMF_BS_LOG("ReadRequest::QueryInterface %s", GetGUIDName(aIId).get());
if (aIId == IID_IUnknown) {
return DoGetInterface(static_cast<IUnknown*>(this), aInterface);
}
*aInterface = nullptr;
return E_NOINTERFACE;
}
class ProcessReadRequestEvent final : public nsRunnable {
public:
ProcessReadRequestEvent(WMFByteStream* aStream,
IMFAsyncResult* aResult,
ReadRequest* aRequestState)
: mStream(aStream),
mResult(aResult),
mRequestState(aRequestState) {}
NS_IMETHOD Run() {
mStream->ProcessReadRequest(mResult, mRequestState);
return NS_OK;
}
private:
RefPtr<WMFByteStream> mStream;
RefPtr<IMFAsyncResult> mResult;
RefPtr<ReadRequest> mRequestState;
};
// IMFByteStream Methods
STDMETHODIMP
WMFByteStream::BeginRead(BYTE *aBuffer,
ULONG aLength,
IMFAsyncCallback *aCallback,
IUnknown *aCallerState)
{
NS_ENSURE_TRUE(aBuffer, E_POINTER);
NS_ENSURE_TRUE(aCallback, E_POINTER);
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
WMF_BS_LOG("[%p] WMFByteStream::BeginRead() mOffset=%lld tell=%lld length=%lu mIsShutdown=%d",
this, mOffset, mResource->Tell(), aLength, mIsShutdown);
if (mIsShutdown || mOffset < 0) {
return E_INVALIDARG;
}
// Create an object to store our state.
RefPtr<ReadRequest> requestState = new ReadRequest(mOffset, aBuffer, aLength);
// Create an IMFAsyncResult, this is passed back to the caller as a token to
// retrieve the number of bytes read.
RefPtr<IMFAsyncResult> callersResult;
HRESULT hr = wmf::MFCreateAsyncResult(requestState,
aCallback,
aCallerState,
byRef(callersResult));
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
// Dispatch an event to perform the read in the thread pool.
nsCOMPtr<nsIRunnable> r = new ProcessReadRequestEvent(this,
callersResult,
requestState);
nsresult rv = mThreadPool->Dispatch(r, NS_DISPATCH_NORMAL);
if (mResource->GetLength() > -1) {
mOffset = std::min<int64_t>(mOffset + aLength, mResource->GetLength());
} else {
mOffset += aLength;
}
return NS_SUCCEEDED(rv) ? S_OK : E_FAIL;
}
nsresult
WMFByteStream::Read(ReadRequest* aRequestState)
{
// Read in a loop to ensure we fill the buffer, when possible.
ULONG totalBytesRead = 0;
nsresult rv = NS_OK;
while (totalBytesRead < aRequestState->mBufferLength) {
BYTE* buffer = aRequestState->mBuffer + totalBytesRead;
ULONG bytesRead = 0;
ULONG length = aRequestState->mBufferLength - totalBytesRead;
rv = mResource->ReadAt(aRequestState->mOffset + totalBytesRead,
reinterpret_cast<char*>(buffer),
length,
reinterpret_cast<uint32_t*>(&bytesRead));
NS_ENSURE_SUCCESS(rv, rv);
totalBytesRead += bytesRead;
if (bytesRead == 0) {
break;
}
}
aRequestState->mBytesRead = totalBytesRead;
return NS_OK;
}
// Note: This is called on one of the thread pool's threads.
void
WMFByteStream::ProcessReadRequest(IMFAsyncResult* aResult,
ReadRequest* aRequestState)
{
if (mResource->GetLength() > -1 &&
aRequestState->mOffset > mResource->GetLength()) {
aResult->SetStatus(S_OK);
wmf::MFInvokeCallback(aResult);
WMF_BS_LOG("[%p] WMFByteStream::ProcessReadRequest() read offset greater than length, soft-failing read", this);
return;
}
nsresult rv = Read(aRequestState);
if (NS_FAILED(rv)) {
Shutdown();
aResult->SetStatus(E_ABORT);
} else {
aResult->SetStatus(S_OK);
}
WMF_BS_LOG("[%p] WMFByteStream::ProcessReadRequest() read %d at %lld finished rv=%x",
this, aRequestState->mBytesRead, aRequestState->mOffset, rv);
// Let caller know read is complete.
DebugOnly<HRESULT> hr = wmf::MFInvokeCallback(aResult);
NS_ASSERTION(SUCCEEDED(hr), "Failed to invoke callback!");
}
STDMETHODIMP
WMFByteStream::BeginWrite(const BYTE *, ULONG ,
IMFAsyncCallback *,
IUnknown *)
{
WMF_BS_LOG("[%p] WMFByteStream::BeginWrite()", this);
return E_NOTIMPL;
}
STDMETHODIMP
WMFByteStream::Close()
{
WMF_BS_LOG("[%p] WMFByteStream::Close()", this);
return S_OK;
}
STDMETHODIMP
WMFByteStream::EndRead(IMFAsyncResult* aResult, ULONG *aBytesRead)
{
NS_ENSURE_TRUE(aResult, E_POINTER);
NS_ENSURE_TRUE(aBytesRead, E_POINTER);
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
// Extract our state object.
RefPtr<IUnknown> unknown;
HRESULT hr = aResult->GetObject(byRef(unknown));
if (FAILED(hr) || !unknown) {
return E_INVALIDARG;
}
ReadRequest* requestState =
static_cast<ReadRequest*>(unknown.get());
// Report result.
*aBytesRead = requestState->mBytesRead;
WMF_BS_LOG("[%p] WMFByteStream::EndRead() offset=%lld *aBytesRead=%u mOffset=%lld status=0x%x hr=0x%x eof=%d",
this, requestState->mOffset, *aBytesRead, mOffset, aResult->GetStatus(), hr, IsEOS());
return aResult->GetStatus();
}
STDMETHODIMP
WMFByteStream::EndWrite(IMFAsyncResult *, ULONG *)
{
WMF_BS_LOG("[%p] WMFByteStream::EndWrite()", this);
return E_NOTIMPL;
}
STDMETHODIMP
WMFByteStream::Flush()
{
WMF_BS_LOG("[%p] WMFByteStream::Flush()", this);
return S_OK;
}
STDMETHODIMP
WMFByteStream::GetCapabilities(DWORD *aCapabilities)
{
WMF_BS_LOG("[%p] WMFByteStream::GetCapabilities()", this);
NS_ENSURE_TRUE(aCapabilities, E_POINTER);
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
bool seekable = mResource->IsTransportSeekable();
bool cached = mResource->IsDataCachedToEndOfResource(0);
*aCapabilities = MFBYTESTREAM_IS_READABLE |
MFBYTESTREAM_IS_SEEKABLE |
(!cached ? MFBYTESTREAM_IS_PARTIALLY_DOWNLOADED : 0) |
(!seekable ? MFBYTESTREAM_HAS_SLOW_SEEK : 0);
return S_OK;
}
STDMETHODIMP
WMFByteStream::GetCurrentPosition(QWORD *aPosition)
{
NS_ENSURE_TRUE(aPosition, E_POINTER);
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
// Note: Returning the length of stream as position when read
// cursor is < 0 seems to be the behaviour expected by WMF, but
// also note it doesn't seem to expect that the position is an
// unsigned value since if you seek to > length and read WMF
// expects the read to succeed after reading 0 bytes, but if you
// seek to < 0 and read, the read is expected to fails... So
// go figure...
*aPosition = mOffset < 0 ? mResource->GetLength() : mOffset;
WMF_BS_LOG("[%p] WMFByteStream::GetCurrentPosition() %lld", this, mOffset);
return S_OK;
}
STDMETHODIMP
WMFByteStream::GetLength(QWORD *aLength)
{
NS_ENSURE_TRUE(aLength, E_POINTER);
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
*aLength = mResource->GetLength();
WMF_BS_LOG("[%p] WMFByteStream::GetLength() %lld", this, *aLength);
return S_OK;
}
bool
WMFByteStream::IsEOS()
{
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
return mResource->GetLength() > -1 &&
(mOffset < 0 ||
mOffset >= mResource->GetLength());
}
STDMETHODIMP
WMFByteStream::IsEndOfStream(BOOL *aEndOfStream)
{
NS_ENSURE_TRUE(aEndOfStream, E_POINTER);
*aEndOfStream = IsEOS();
WMF_BS_LOG("[%p] WMFByteStream::IsEndOfStream() %d", this, *aEndOfStream);
return S_OK;
}
STDMETHODIMP
WMFByteStream::Read(BYTE* aBuffer, ULONG aBufferLength, ULONG* aOutBytesRead)
{
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
nsRefPtr<ReadRequest> request = new ReadRequest(mOffset, aBuffer, aBufferLength);
if (NS_FAILED(Read(request))) {
WMF_BS_LOG("[%p] WMFByteStream::Read() offset=%lld failed!", this, mOffset);
return E_FAIL;
}
if (aOutBytesRead) {
*aOutBytesRead = request->mBytesRead;
}
WMF_BS_LOG("[%p] WMFByteStream::Read() offset=%lld length=%u bytesRead=%u",
this, mOffset, aBufferLength, request->mBytesRead);
mOffset += request->mBytesRead;
return S_OK;
}
STDMETHODIMP
WMFByteStream::Seek(MFBYTESTREAM_SEEK_ORIGIN aSeekOrigin,
LONGLONG aSeekOffset,
DWORD aSeekFlags,
QWORD *aCurrentPosition)
{
WMF_BS_LOG("[%p] WMFByteStream::Seek(%d, %lld)", this, aSeekOrigin, aSeekOffset);
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
int64_t offset = mOffset;
if (aSeekOrigin == msoBegin) {
offset = aSeekOffset;
} else {
offset += aSeekOffset;
}
int64_t length = mResource->GetLength();
if (length > -1) {
mOffset = std::min<int64_t>(offset, length);
} else {
mOffset = offset;
}
if (aCurrentPosition) {
*aCurrentPosition = mOffset;
}
return S_OK;
}
STDMETHODIMP
WMFByteStream::SetCurrentPosition(QWORD aPosition)
{
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
WMF_BS_LOG("[%p] WMFByteStream::SetCurrentPosition(%lld)",
this, aPosition);
int64_t length = mResource->GetLength();
if (length > -1) {
mOffset = std::min<int64_t>(aPosition, length);
} else {
mOffset = aPosition;
}
return S_OK;
}
STDMETHODIMP
WMFByteStream::SetLength(QWORD)
{
WMF_BS_LOG("[%p] WMFByteStream::SetLength()", this);
return E_NOTIMPL;
}
STDMETHODIMP
WMFByteStream::Write(const BYTE *, ULONG, ULONG *)
{
WMF_BS_LOG("[%p] WMFByteStream::Write()", this);
return E_NOTIMPL;
}
// IMFAttributes methods
STDMETHODIMP
WMFByteStream::GetItem(REFGUID guidKey, PROPVARIANT* pValue)
{
MOZ_ASSERT(mAttributes);
return mAttributes->GetItem(guidKey, pValue);
}
STDMETHODIMP
WMFByteStream::GetItemType(REFGUID guidKey, MF_ATTRIBUTE_TYPE* pType)
{
assert(mAttributes);
return mAttributes->GetItemType(guidKey, pType);
}
STDMETHODIMP
WMFByteStream::CompareItem(REFGUID guidKey, REFPROPVARIANT Value, BOOL* pbResult)
{
assert(mAttributes);
return mAttributes->CompareItem(guidKey, Value, pbResult);
}
STDMETHODIMP
WMFByteStream::Compare(IMFAttributes* pTheirs,
MF_ATTRIBUTES_MATCH_TYPE MatchType,
BOOL* pbResult)
{
assert(mAttributes);
return mAttributes->Compare(pTheirs, MatchType, pbResult);
}
STDMETHODIMP
WMFByteStream::GetUINT32(REFGUID guidKey, UINT32* punValue)
{
assert(mAttributes);
return mAttributes->GetUINT32(guidKey, punValue);
}
STDMETHODIMP
WMFByteStream::GetUINT64(REFGUID guidKey, UINT64* punValue)
{
assert(mAttributes);
return mAttributes->GetUINT64(guidKey, punValue);
}
STDMETHODIMP
WMFByteStream::GetDouble(REFGUID guidKey, double* pfValue)
{
assert(mAttributes);
return mAttributes->GetDouble(guidKey, pfValue);
}
STDMETHODIMP
WMFByteStream::GetGUID(REFGUID guidKey, GUID* pguidValue)
{
assert(mAttributes);
return mAttributes->GetGUID(guidKey, pguidValue);
}
STDMETHODIMP
WMFByteStream::GetStringLength(REFGUID guidKey, UINT32* pcchLength)
{
assert(mAttributes);
return mAttributes->GetStringLength(guidKey, pcchLength);
}
STDMETHODIMP
WMFByteStream::GetString(REFGUID guidKey, LPWSTR pwszValue, UINT32 cchBufSize, UINT32* pcchLength)
{
assert(mAttributes);
return mAttributes->GetString(guidKey, pwszValue, cchBufSize, pcchLength);
}
STDMETHODIMP
WMFByteStream::GetAllocatedString(REFGUID guidKey, LPWSTR* ppwszValue, UINT32* pcchLength)
{
assert(mAttributes);
return mAttributes->GetAllocatedString(guidKey, ppwszValue, pcchLength);
}
STDMETHODIMP
WMFByteStream::GetBlobSize(REFGUID guidKey, UINT32* pcbBlobSize)
{
assert(mAttributes);
return mAttributes->GetBlobSize(guidKey, pcbBlobSize);
}
STDMETHODIMP
WMFByteStream::GetBlob(REFGUID guidKey, UINT8* pBuf, UINT32 cbBufSize, UINT32* pcbBlobSize)
{
assert(mAttributes);
return mAttributes->GetBlob(guidKey, pBuf, cbBufSize, pcbBlobSize);
}
STDMETHODIMP
WMFByteStream::GetAllocatedBlob(REFGUID guidKey, UINT8** ppBuf, UINT32* pcbSize)
{
assert(mAttributes);
return mAttributes->GetAllocatedBlob(guidKey, ppBuf, pcbSize);
}
STDMETHODIMP
WMFByteStream::GetUnknown(REFGUID guidKey, REFIID riid, LPVOID* ppv)
{
assert(mAttributes);
return mAttributes->GetUnknown(guidKey, riid, ppv);
}
STDMETHODIMP
WMFByteStream::SetItem(REFGUID guidKey, REFPROPVARIANT Value)
{
assert(mAttributes);
return mAttributes->SetItem(guidKey, Value);
}
STDMETHODIMP
WMFByteStream::DeleteItem(REFGUID guidKey)
{
assert(mAttributes);
return mAttributes->DeleteItem(guidKey);
}
STDMETHODIMP
WMFByteStream::DeleteAllItems()
{
assert(mAttributes);
return mAttributes->DeleteAllItems();
}
STDMETHODIMP
WMFByteStream::SetUINT32(REFGUID guidKey, UINT32 unValue)
{
assert(mAttributes);
return mAttributes->SetUINT32(guidKey, unValue);
}
STDMETHODIMP
WMFByteStream::SetUINT64(REFGUID guidKey,UINT64 unValue)
{
assert(mAttributes);
return mAttributes->SetUINT64(guidKey, unValue);
}
STDMETHODIMP
WMFByteStream::SetDouble(REFGUID guidKey, double fValue)
{
assert(mAttributes);
return mAttributes->SetDouble(guidKey, fValue);
}
STDMETHODIMP
WMFByteStream::SetGUID(REFGUID guidKey, REFGUID guidValue)
{
assert(mAttributes);
return mAttributes->SetGUID(guidKey, guidValue);
}
STDMETHODIMP
WMFByteStream::SetString(REFGUID guidKey, LPCWSTR wszValue)
{
assert(mAttributes);
return mAttributes->SetString(guidKey, wszValue);
}
STDMETHODIMP
WMFByteStream::SetBlob(REFGUID guidKey, const UINT8* pBuf, UINT32 cbBufSize)
{
assert(mAttributes);
return mAttributes->SetBlob(guidKey, pBuf, cbBufSize);
}
STDMETHODIMP
WMFByteStream::SetUnknown(REFGUID guidKey, IUnknown* pUnknown)
{
assert(mAttributes);
return mAttributes->SetUnknown(guidKey, pUnknown);
}
STDMETHODIMP
WMFByteStream::LockStore()
{
assert(mAttributes);
return mAttributes->LockStore();
}
STDMETHODIMP
WMFByteStream::UnlockStore()
{
assert(mAttributes);
return mAttributes->UnlockStore();
}
STDMETHODIMP
WMFByteStream::GetCount(UINT32* pcItems)
{
assert(mAttributes);
return mAttributes->GetCount(pcItems);
}
STDMETHODIMP
WMFByteStream::GetItemByIndex(UINT32 unIndex, GUID* pguidKey, PROPVARIANT* pValue)
{
assert(mAttributes);
return mAttributes->GetItemByIndex(unIndex, pguidKey, pValue);
}
STDMETHODIMP
WMFByteStream::CopyAllItems(IMFAttributes* pDest)
{
assert(mAttributes);
return mAttributes->CopyAllItems(pDest);
}
} // namespace mozilla

View File

@ -1,162 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#if !defined(WMFByteStream_h_)
#define WMFByteStream_h_
#include "WMF.h"
#include "nsISupportsImpl.h"
#include "nsCOMPtr.h"
#include "mozilla/ReentrantMonitor.h"
#include "mozilla/Attributes.h"
#include "nsAutoPtr.h"
#include "mozilla/RefPtr.h"
namespace mozilla {
class MediaResource;
class ReadRequest;
class WMFSourceReaderCallback;
class SharedThreadPool;
// Wraps a MediaResource around an IMFByteStream interface, so that it can
// be used by the IMFSourceReader. Each WMFByteStream creates a WMF Work Queue
// on which blocking I/O is performed. The SourceReader requests reads
// asynchronously using {Begin,End}Read(), and more rarely synchronously
// using Read().
//
// Note: This implementation attempts to be bug-compatible with Windows Media
// Foundation's implementation of IMFByteStream. The behaviour of WMF's
// IMFByteStream was determined by creating it and testing the edge cases.
// For details see the test code at:
// https://github.com/cpearce/IMFByteStreamBehaviour/
class WMFByteStream final : public IMFByteStream
, public IMFAttributes
{
~WMFByteStream();
public:
WMFByteStream(MediaResource* aResource, WMFSourceReaderCallback* aCallback);
nsresult Init();
nsresult Shutdown();
// IUnknown Methods.
STDMETHODIMP QueryInterface(REFIID aIId, LPVOID *aInterface);
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();
// IMFByteStream Methods.
STDMETHODIMP BeginRead(BYTE *aBuffer,
ULONG aLength,
IMFAsyncCallback *aCallback,
IUnknown *aCallerState);
STDMETHODIMP BeginWrite(const BYTE *, ULONG ,
IMFAsyncCallback *,
IUnknown *);
STDMETHODIMP Close();
STDMETHODIMP EndRead(IMFAsyncResult* aResult, ULONG *aBytesRead);
STDMETHODIMP EndWrite(IMFAsyncResult *, ULONG *);
STDMETHODIMP Flush();
STDMETHODIMP GetCapabilities(DWORD *aCapabilities);
STDMETHODIMP GetCurrentPosition(QWORD *aPosition);
STDMETHODIMP GetLength(QWORD *pqwLength);
STDMETHODIMP IsEndOfStream(BOOL *aIsEndOfStream);
STDMETHODIMP Read(BYTE *, ULONG, ULONG *);
STDMETHODIMP Seek(MFBYTESTREAM_SEEK_ORIGIN aSeekOrigin,
LONGLONG aSeekOffset,
DWORD aSeekFlags,
QWORD *aCurrentPosition);
STDMETHODIMP SetCurrentPosition(QWORD aPosition);
STDMETHODIMP SetLength(QWORD);
STDMETHODIMP Write(const BYTE *, ULONG, ULONG *);
// IMFAttributes methods
STDMETHODIMP GetItem(REFGUID guidKey, PROPVARIANT* pValue);
STDMETHODIMP GetItemType(REFGUID guidKey, MF_ATTRIBUTE_TYPE* pType);
STDMETHODIMP CompareItem(REFGUID guidKey, REFPROPVARIANT Value, BOOL* pbResult);
STDMETHODIMP Compare(IMFAttributes* pTheirs, MF_ATTRIBUTES_MATCH_TYPE MatchType, BOOL* pbResult);
STDMETHODIMP GetUINT32(REFGUID guidKey, UINT32* punValue);
STDMETHODIMP GetUINT64(REFGUID guidKey, UINT64* punValue);
STDMETHODIMP GetDouble(REFGUID guidKey, double* pfValue);
STDMETHODIMP GetGUID(REFGUID guidKey, GUID* pguidValue);
STDMETHODIMP GetStringLength(REFGUID guidKey, UINT32* pcchLength);
STDMETHODIMP GetString(REFGUID guidKey, LPWSTR pwszValue, UINT32 cchBufSize, UINT32* pcchLength);
STDMETHODIMP GetAllocatedString(REFGUID guidKey, LPWSTR* ppwszValue, UINT32* pcchLength);
STDMETHODIMP GetBlobSize(REFGUID guidKey, UINT32* pcbBlobSize);
STDMETHODIMP GetBlob(REFGUID guidKey, UINT8* pBuf, UINT32 cbBufSize, UINT32* pcbBlobSize);
STDMETHODIMP GetAllocatedBlob(REFGUID guidKey, UINT8** ppBuf, UINT32* pcbSize);
STDMETHODIMP GetUnknown(REFGUID guidKey, REFIID riid, LPVOID* ppv);
STDMETHODIMP SetItem(REFGUID guidKey, REFPROPVARIANT Value);
STDMETHODIMP DeleteItem(REFGUID guidKey);
STDMETHODIMP DeleteAllItems();
STDMETHODIMP SetUINT32(REFGUID guidKey, UINT32 unValue);
STDMETHODIMP SetUINT64(REFGUID guidKey,UINT64 unValue);
STDMETHODIMP SetDouble(REFGUID guidKey, double fValue);
STDMETHODIMP SetGUID(REFGUID guidKey, REFGUID guidValue);
STDMETHODIMP SetString(REFGUID guidKey, LPCWSTR wszValue);
STDMETHODIMP SetBlob(REFGUID guidKey, const UINT8* pBuf, UINT32 cbBufSize);
STDMETHODIMP SetUnknown(REFGUID guidKey, IUnknown* pUnknown);
STDMETHODIMP LockStore();
STDMETHODIMP UnlockStore();
STDMETHODIMP GetCount(UINT32* pcItems);
STDMETHODIMP GetItemByIndex(UINT32 unIndex, GUID* pguidKey, PROPVARIANT* pValue);
STDMETHODIMP CopyAllItems(IMFAttributes* pDest);
// We perform an async read operation in this callback implementation.
// Processes an async read request, storing the result in aResult, and
// notifying the caller when the read operation is complete.
void ProcessReadRequest(IMFAsyncResult* aResult,
ReadRequest* aRequestState);
private:
// Locks the MediaResource and performs the read. The other read methods
// call this function.
nsresult Read(ReadRequest* aRequestState);
// Returns true if the current position of the stream is at end of stream.
bool IsEOS();
// Reference to the thread pool in which we perform the reads asynchronously.
// Note this is pool is shared amongst all active WMFByteStreams.
RefPtr<SharedThreadPool> mThreadPool;
// Reference to the source reader's callback. We use this reference to
// notify threads waiting on a ReadSample() callback to stop waiting
// if the stream is closed, which happens when the media element is
// shutdown.
RefPtr<WMFSourceReaderCallback> mSourceReaderCallback;
// Resource we're wrapping.
nsRefPtr<MediaResource> mResource;
// Protects mOffset, which is accessed by the SourceReaders thread(s), and
// on the work queue thread.
ReentrantMonitor mReentrantMonitor;
// Current offset of the logical read cursor. We maintain this separately
// from the media resource's offset since a partially complete read (in Invoke())
// would leave the resource's offset at a value unexpected by the caller,
// since the read hadn't yet completed.
int64_t mOffset;
// We implement IMFAttributes by forwarding all calls to an instance of the
// standard IMFAttributes class, which we store a reference to here.
RefPtr<IMFAttributes> mAttributes;
// True if the resource has been shutdown, either because the WMFReader is
// shutting down, or because the underlying MediaResource has closed.
bool mIsShutdown;
// IUnknown ref counting.
ThreadSafeAutoRefCnt mRefCnt;
NS_DECL_OWNINGTHREAD
};
} // namespace mozilla
#endif

View File

@ -1,142 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "WMF.h"
#include "WMFDecoder.h"
#include "WMFReader.h"
#include "WMFUtils.h"
#include "MediaDecoderStateMachine.h"
#include "mozilla/Preferences.h"
#include "mozilla/WindowsVersion.h"
#include "nsCharSeparatedTokenizer.h"
#ifdef MOZ_DIRECTSHOW
#include "DirectShowDecoder.h"
#endif
namespace mozilla {
MediaDecoderStateMachine* WMFDecoder::CreateStateMachine()
{
return new MediaDecoderStateMachine(this, new WMFReader(this));
}
/* static */
bool
WMFDecoder::IsMP3Supported()
{
MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
if (!MediaDecoder::IsWMFEnabled()) {
return false;
}
// MP3 works fine in WMF on Windows Vista and Windows 8.
if (!IsWin7OrLater()) {
return true;
}
// MP3 support is disabled if we're on Windows 7 and no service packs are
// installed, since it's crashy. Win7 with service packs is not so crashy,
// so we enable it there. Note we prefer DirectShow for MP3 playback, but
// we still support MP3 in MP4 via WMF, or MP3 in WMF if DirectShow is
// disabled.
return IsWin7SP1OrLater();
}
static bool
IsSupportedH264Codec(const nsAString& aCodec)
{
// According to the WMF documentation:
// http://msdn.microsoft.com/en-us/library/windows/desktop/dd797815%28v=vs.85%29.aspx
// "The Media Foundation H.264 video decoder is a Media Foundation Transform
// that supports decoding of Baseline, Main, and High profiles, up to level
// 5.1.". We also report that we can play Extended profile, as there are
// bitstreams that are Extended compliant that are also Baseline compliant.
int16_t profile = 0, level = 0;
if (!ExtractH264CodecDetails(aCodec, profile, level)) {
return false;
}
return level >= H264_LEVEL_1 &&
level <= H264_LEVEL_5_1 &&
(profile == H264_PROFILE_BASE ||
profile == H264_PROFILE_MAIN ||
profile == H264_PROFILE_EXTENDED ||
profile == H264_PROFILE_HIGH);
}
bool
WMFDecoder::CanPlayType(const nsACString& aType,
const nsAString& aCodecs)
{
if (!MediaDecoder::IsWMFEnabled() ||
NS_FAILED(LoadDLLs())) {
return false;
}
// Assume that if LoadDLLs() didn't fail, we can playback the types that
// we know should be supported by Windows Media Foundation.
if ((aType.EqualsASCII("audio/mpeg") || aType.EqualsASCII("audio/mp3")) &&
IsMP3Supported()) {
// Note: We block MP3 playback on Window 7 SP0 since it seems to crash
// in some circumstances.
return !aCodecs.Length() || aCodecs.EqualsASCII("mp3");
}
// AAC-LC or MP3 in M4A.
if (aType.EqualsASCII("audio/mp4") || aType.EqualsASCII("audio/x-m4a")) {
return !aCodecs.Length() ||
aCodecs.EqualsASCII("mp4a.40.2") ||
aCodecs.EqualsASCII("mp3");
}
if (!aType.EqualsASCII("video/mp4")) {
return false;
}
// H.264 + AAC in MP4. Verify that all the codecs specifed are ones that
// we expect that we can play.
nsCharSeparatedTokenizer tokenizer(aCodecs, ',');
bool expectMoreTokens = false;
while (tokenizer.hasMoreTokens()) {
const nsSubstring& token = tokenizer.nextToken();
expectMoreTokens = tokenizer.separatorAfterCurrentToken();
if (token.EqualsASCII("mp4a.40.2") || // AAC-LC
token.EqualsASCII("mp3") ||
IsSupportedH264Codec(token)) {
continue;
}
return false;
}
if (expectMoreTokens) {
// Last codec name was empty
return false;
}
return true;
}
nsresult
WMFDecoder::LoadDLLs()
{
return NS_OK; // Removed in next patch in series.
}
void
WMFDecoder::UnloadDLLs()
{
// Removed in next patch in series.
}
/* static */
bool
WMFDecoder::IsEnabled()
{
// We only use WMF on Windows Vista and up
return IsVistaOrLater() &&
Preferences::GetBool("media.windows-media-foundation.enabled");
}
} // namespace mozilla

View File

@ -1,51 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#if !defined(WMFDecoder_h_)
#define WMFDecoder_h_
#include "MediaDecoder.h"
namespace mozilla {
// Decoder that uses Windows Media Foundation to playback H.264/AAC in MP4
// and M4A files, and MP3 files if the DirectShow backend is disabled.
// Playback is strictly limited to only those codecs.
class WMFDecoder : public MediaDecoder
{
public:
virtual MediaDecoder* Clone() {
if (!IsWMFEnabled()) {
return nullptr;
}
return new WMFDecoder();
}
virtual MediaDecoderStateMachine* CreateStateMachine();
// Loads the DLLs required by Windows Media Foundation. If this returns
// failure, you can assume that WMF is not available on the user's system.
static nsresult LoadDLLs();
static void UnloadDLLs();
// Returns true if the WMF backend is preffed on, and we're running on a
// version of Windows which is likely to support WMF.
static bool IsEnabled();
// Returns true if MP3 decoding is enabled on this system. We block
// MP3 playback on Windows 7 SP0, since it's crashy on that platform.
static bool IsMP3Supported();
// Returns the HTMLMediaElement.canPlayType() result for the mime type
// and codecs parameter. aCodecs can be empty.
static bool CanPlayType(const nsACString& aType,
const nsAString& aCodecs);
};
} // namespace mozilla
#endif

View File

@ -1,929 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "WMFReader.h"
#include "WMFDecoder.h"
#include "WMFUtils.h"
#include "WMFByteStream.h"
#include "WMFSourceReaderCallback.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/dom/HTMLMediaElement.h"
#include "mozilla/Preferences.h"
#include "DXVA2Manager.h"
#include "ImageContainer.h"
#include "Layers.h"
#include "mozilla/layers/LayersTypes.h"
#include "gfxWindowsPlatform.h"
#ifndef MOZ_SAMPLE_TYPE_FLOAT32
#error We expect 32bit float audio samples on desktop for the Windows Media Foundation media backend.
#endif
#include "MediaDecoder.h"
#include "VideoUtils.h"
#include "gfx2DGlue.h"
using namespace mozilla::gfx;
using namespace mozilla::media;
using mozilla::layers::Image;
using mozilla::layers::LayerManager;
using mozilla::layers::LayersBackend;
namespace mozilla {
extern PRLogModuleInfo* gMediaDecoderLog;
#define DECODER_LOG(...) MOZ_LOG(gMediaDecoderLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
// Uncomment to enable verbose per-sample logging.
//#define LOG_SAMPLE_DECODE 1
WMFReader::WMFReader(AbstractMediaDecoder* aDecoder)
: MediaDecoderReader(aDecoder),
mSourceReader(nullptr),
mAudioChannels(0),
mAudioBytesPerSample(0),
mAudioRate(0),
mVideoWidth(0),
mVideoHeight(0),
mVideoStride(0),
mAudioFrameOffset(0),
mAudioFrameSum(0),
mMustRecaptureAudioPosition(true),
mHasAudio(false),
mHasVideo(false),
mUseHwAccel(false),
mIsMP3Enabled(WMFDecoder::IsMP3Supported()),
mCOMInitialized(false)
{
NS_ASSERTION(NS_IsMainThread(), "Must be on main thread.");
MOZ_COUNT_CTOR(WMFReader);
}
WMFReader::~WMFReader()
{
NS_ASSERTION(NS_IsMainThread(), "Must be on main thread.");
// Note: We must shutdown the byte stream before calling MFShutdown, else we
// get assertion failures when unlocking the byte stream's work queue.
if (mByteStream) {
DebugOnly<nsresult> rv = mByteStream->Shutdown();
NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to shutdown WMFByteStream");
}
DebugOnly<HRESULT> hr = wmf::MFShutdown();
NS_ASSERTION(SUCCEEDED(hr), "MFShutdown failed");
MOZ_COUNT_DTOR(WMFReader);
}
bool
WMFReader::InitializeDXVA()
{
if (gfxWindowsPlatform::GetPlatform()->IsWARP() ||
!gfxPlatform::CanUseHardwareVideoDecoding()) {
return false;
}
MOZ_ASSERT(mDecoder->GetImageContainer());
// Extract the layer manager backend type so that we can determine
// whether it's worthwhile using DXVA. If we're not running with a D3D
// layer manager then the readback of decoded video frames from GPU to
// CPU memory grinds painting to a halt, and makes playback performance
// *worse*.
MediaDecoderOwner* owner = mDecoder->GetOwner();
NS_ENSURE_TRUE(owner, false);
dom::HTMLMediaElement* element = owner->GetMediaElement();
NS_ENSURE_TRUE(element, false);
nsRefPtr<LayerManager> layerManager =
nsContentUtils::LayerManagerForDocument(element->OwnerDoc());
NS_ENSURE_TRUE(layerManager, false);
LayersBackend backend = layerManager->GetCompositorBackendType();
if (backend != LayersBackend::LAYERS_D3D9 &&
backend != LayersBackend::LAYERS_D3D11) {
return false;
}
mDXVA2Manager = DXVA2Manager::CreateD3D9DXVA();
return mDXVA2Manager != nullptr;
}
nsresult
WMFReader::Init(MediaDecoderReader* aCloneDonor)
{
NS_ASSERTION(NS_IsMainThread(), "Must be on main thread.");
nsresult rv = WMFDecoder::LoadDLLs();
NS_ENSURE_SUCCESS(rv, rv);
if (FAILED(wmf::MFStartup())) {
NS_WARNING("Failed to initialize Windows Media Foundation");
return NS_ERROR_FAILURE;
}
mSourceReaderCallback = new WMFSourceReaderCallback();
// Must be created on main thread.
mByteStream = new WMFByteStream(mDecoder->GetResource(), mSourceReaderCallback);
rv = mByteStream->Init();
NS_ENSURE_SUCCESS(rv, rv);
if (mDecoder->GetImageContainer() != nullptr &&
IsVideoContentType(mDecoder->GetResource()->GetContentType())) {
mUseHwAccel = InitializeDXVA();
} else {
mUseHwAccel = false;
}
return NS_OK;
}
bool
WMFReader::HasAudio()
{
MOZ_ASSERT(OnTaskQueue());
return mHasAudio;
}
bool
WMFReader::HasVideo()
{
MOZ_ASSERT(OnTaskQueue());
return mHasVideo;
}
static HRESULT
ConfigureSourceReaderStream(IMFSourceReader *aReader,
const DWORD aStreamIndex,
const GUID& aOutputSubType,
const GUID* aAllowedInSubTypes,
const uint32_t aNumAllowedInSubTypes)
{
NS_ENSURE_TRUE(aReader, E_POINTER);
NS_ENSURE_TRUE(aAllowedInSubTypes, E_POINTER);
RefPtr<IMFMediaType> nativeType;
RefPtr<IMFMediaType> type;
HRESULT hr;
// Find the native format of the stream.
hr = aReader->GetNativeMediaType(aStreamIndex, 0, byRef(nativeType));
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
// Get the native output subtype of the stream. This denotes the uncompressed
// type.
GUID subType;
hr = nativeType->GetGUID(MF_MT_SUBTYPE, &subType);
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
// Ensure the input type of the media is in the allowed formats list.
bool isSubTypeAllowed = false;
for (uint32_t i = 0; i < aNumAllowedInSubTypes; i++) {
if (aAllowedInSubTypes[i] == subType) {
isSubTypeAllowed = true;
break;
}
}
if (!isSubTypeAllowed) {
nsCString name = GetGUIDName(subType);
DECODER_LOG("ConfigureSourceReaderStream subType=%s is not allowed to be decoded", name.get());
return E_FAIL;
}
// Find the major type.
GUID majorType;
hr = nativeType->GetGUID(MF_MT_MAJOR_TYPE, &majorType);
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
// Define the output type.
hr = wmf::MFCreateMediaType(byRef(type));
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
hr = type->SetGUID(MF_MT_MAJOR_TYPE, majorType);
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
hr = type->SetGUID(MF_MT_SUBTYPE, aOutputSubType);
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
// Set the uncompressed format. This can fail if the decoder can't produce
// that type.
return aReader->SetCurrentMediaType(aStreamIndex, nullptr, type);
}
// Returns the duration of the resource, in microseconds.
HRESULT
GetSourceReaderDuration(IMFSourceReader *aReader,
int64_t& aOutDuration)
{
AutoPropVar var;
HRESULT hr = aReader->GetPresentationAttribute(MF_SOURCE_READER_MEDIASOURCE,
MF_PD_DURATION,
&var);
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
// WMF stores duration in hundred nanosecond units.
int64_t duration_hns = 0;
hr = wmf::PropVariantToInt64(var, &duration_hns);
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
aOutDuration = HNsToUsecs(duration_hns);
return S_OK;
}
HRESULT
GetSourceReaderCanSeek(IMFSourceReader* aReader, bool& aOutCanSeek)
{
NS_ENSURE_TRUE(aReader, E_FAIL);
HRESULT hr;
AutoPropVar var;
hr = aReader->GetPresentationAttribute(MF_SOURCE_READER_MEDIASOURCE,
MF_SOURCE_READER_MEDIASOURCE_CHARACTERISTICS,
&var);
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
ULONG flags = 0;
hr = wmf::PropVariantToUInt32(var, &flags);
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
aOutCanSeek = ((flags & MFMEDIASOURCE_CAN_SEEK) == MFMEDIASOURCE_CAN_SEEK);
return S_OK;
}
HRESULT
WMFReader::ConfigureVideoFrameGeometry(IMFMediaType* aMediaType)
{
NS_ENSURE_TRUE(aMediaType != nullptr, E_POINTER);
HRESULT hr;
// Verify that the video subtype is what we expect it to be.
// When using hardware acceleration/DXVA2 the video format should
// be NV12, which is DXVA2's preferred format. For software decoding
// we use YV12, as that's easier for us to stick into our rendering
// pipeline than NV12. NV12 has interleaved UV samples, whereas YV12
// is a planar format.
GUID videoFormat;
hr = aMediaType->GetGUID(MF_MT_SUBTYPE, &videoFormat);
NS_ENSURE_TRUE(videoFormat == MFVideoFormat_NV12 || !mUseHwAccel, E_FAIL);
NS_ENSURE_TRUE(videoFormat == MFVideoFormat_YV12 || mUseHwAccel, E_FAIL);
nsIntRect pictureRegion;
hr = GetPictureRegion(aMediaType, pictureRegion);
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
UINT32 width = 0, height = 0;
hr = MFGetAttributeSize(aMediaType, MF_MT_FRAME_SIZE, &width, &height);
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
uint32_t aspectNum = 0, aspectDenom = 0;
hr = MFGetAttributeRatio(aMediaType,
MF_MT_PIXEL_ASPECT_RATIO,
&aspectNum,
&aspectDenom);
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
// Calculate and validate the picture region and frame dimensions after
// scaling by the pixel aspect ratio.
nsIntSize frameSize = nsIntSize(width, height);
nsIntSize displaySize = nsIntSize(pictureRegion.width, pictureRegion.height);
ScaleDisplayByAspectRatio(displaySize, float(aspectNum) / float(aspectDenom));
if (!IsValidVideoRegion(frameSize, pictureRegion, displaySize)) {
// Video track's frame sizes will overflow. Ignore the video track.
return E_FAIL;
}
// Success! Save state.
mInfo.mVideo.mDisplay = displaySize;
GetDefaultStride(aMediaType, &mVideoStride);
mVideoWidth = width;
mVideoHeight = height;
mPictureRegion = pictureRegion;
DECODER_LOG("WMFReader frame geometry frame=(%u,%u) stride=%u picture=(%d, %d, %d, %d) display=(%d,%d) PAR=%d:%d",
width, height,
mVideoStride,
mPictureRegion.x, mPictureRegion.y, mPictureRegion.width, mPictureRegion.height,
displaySize.width, displaySize.height,
aspectNum, aspectDenom);
return S_OK;
}
HRESULT
WMFReader::ConfigureVideoDecoder()
{
NS_ASSERTION(mSourceReader, "Must have a SourceReader before configuring decoders!");
// Determine if we have video.
if (!mSourceReader ||
!SourceReaderHasStream(mSourceReader, MF_SOURCE_READER_FIRST_VIDEO_STREAM)) {
// No stream, no error.
return S_OK;
}
if (!mDecoder->GetImageContainer()) {
// We can't display the video, so don't bother to decode; disable the stream.
return mSourceReader->SetStreamSelection(MF_SOURCE_READER_FIRST_VIDEO_STREAM, FALSE);
}
static const GUID MP4VideoTypes[] = {
MFVideoFormat_H264
};
HRESULT hr = ConfigureSourceReaderStream(mSourceReader,
MF_SOURCE_READER_FIRST_VIDEO_STREAM,
mUseHwAccel ? MFVideoFormat_NV12 : MFVideoFormat_YV12,
MP4VideoTypes,
ArrayLength(MP4VideoTypes));
if (FAILED(hr)) {
DECODER_LOG("Failed to configured video output");
return hr;
}
RefPtr<IMFMediaType> mediaType;
hr = mSourceReader->GetCurrentMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM,
byRef(mediaType));
if (FAILED(hr)) {
NS_WARNING("Failed to get configured video media type");
return hr;
}
if (FAILED(ConfigureVideoFrameGeometry(mediaType))) {
NS_WARNING("Failed configured video frame dimensions");
return hr;
}
DECODER_LOG("Successfully configured video stream");
mHasVideo = true;
return S_OK;
}
void
WMFReader::GetSupportedAudioCodecs(const GUID** aCodecs, uint32_t* aNumCodecs)
{
MOZ_ASSERT(aCodecs);
MOZ_ASSERT(aNumCodecs);
if (mIsMP3Enabled) {
GUID aacOrMp3 = MFMPEG4Format_Base;
aacOrMp3.Data1 = 0x6D703461;// FOURCC('m','p','4','a');
static const GUID codecs[] = {
MFAudioFormat_AAC,
MFAudioFormat_MP3,
aacOrMp3
};
*aCodecs = codecs;
*aNumCodecs = ArrayLength(codecs);
} else {
static const GUID codecs[] = {
MFAudioFormat_AAC
};
*aCodecs = codecs;
*aNumCodecs = ArrayLength(codecs);
}
}
HRESULT
WMFReader::ConfigureAudioDecoder()
{
NS_ASSERTION(mSourceReader, "Must have a SourceReader before configuring decoders!");
if (!mSourceReader ||
!SourceReaderHasStream(mSourceReader, MF_SOURCE_READER_FIRST_AUDIO_STREAM)) {
// No stream, no error.
return S_OK;
}
const GUID* codecs;
uint32_t numCodecs = 0;
GetSupportedAudioCodecs(&codecs, &numCodecs);
HRESULT hr = ConfigureSourceReaderStream(mSourceReader,
MF_SOURCE_READER_FIRST_AUDIO_STREAM,
MFAudioFormat_Float,
codecs,
numCodecs);
if (FAILED(hr)) {
NS_WARNING("Failed to configure WMF Audio decoder for PCM output");
return hr;
}
RefPtr<IMFMediaType> mediaType;
hr = mSourceReader->GetCurrentMediaType(MF_SOURCE_READER_FIRST_AUDIO_STREAM,
byRef(mediaType));
if (FAILED(hr)) {
NS_WARNING("Failed to get configured audio media type");
return hr;
}
mAudioRate = MFGetAttributeUINT32(mediaType, MF_MT_AUDIO_SAMPLES_PER_SECOND, 0);
mAudioChannels = MFGetAttributeUINT32(mediaType, MF_MT_AUDIO_NUM_CHANNELS, 0);
mAudioBytesPerSample = MFGetAttributeUINT32(mediaType, MF_MT_AUDIO_BITS_PER_SAMPLE, 16) / 8;
mInfo.mAudio.mChannels = mAudioChannels;
mInfo.mAudio.mRate = mAudioRate;
mHasAudio = true;
DECODER_LOG("Successfully configured audio stream. rate=%u channels=%u bitsPerSample=%u",
mAudioRate, mAudioChannels, mAudioBytesPerSample);
return S_OK;
}
HRESULT
WMFReader::CreateSourceReader()
{
HRESULT hr;
RefPtr<IMFAttributes> attr;
hr = wmf::MFCreateAttributes(byRef(attr), 1);
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
hr = attr->SetUnknown(MF_SOURCE_READER_ASYNC_CALLBACK, mSourceReaderCallback);
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
if (mUseHwAccel) {
hr = attr->SetUnknown(MF_SOURCE_READER_D3D_MANAGER,
mDXVA2Manager->GetDXVADeviceManager());
if (FAILED(hr)) {
DECODER_LOG("Failed to set DXVA2 D3D Device manager on source reader attributes");
mUseHwAccel = false;
}
}
hr = wmf::MFCreateSourceReaderFromByteStream(mByteStream, attr, byRef(mSourceReader));
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
hr = ConfigureVideoDecoder();
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
hr = ConfigureAudioDecoder();
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
if (mUseHwAccel && mInfo.HasVideo()) {
RefPtr<IMFTransform> videoDecoder;
hr = mSourceReader->GetServiceForStream(MF_SOURCE_READER_FIRST_VIDEO_STREAM,
GUID_NULL,
IID_IMFTransform,
(void**)(IMFTransform**)(byRef(videoDecoder)));
if (SUCCEEDED(hr)) {
ULONG_PTR manager = ULONG_PTR(mDXVA2Manager->GetDXVADeviceManager());
hr = videoDecoder->ProcessMessage(MFT_MESSAGE_SET_D3D_MANAGER,
manager);
if (hr == MF_E_TRANSFORM_TYPE_NOT_SET) {
// Ignore MF_E_TRANSFORM_TYPE_NOT_SET. Vista returns this here
// on some, perhaps all, video cards. This may be because activating
// DXVA changes the available output types. It seems to be safe to
// ignore this error.
hr = S_OK;
}
}
if (FAILED(hr)) {
DECODER_LOG("Failed to set DXVA2 D3D Device manager on decoder hr=0x%x", hr);
mUseHwAccel = false;
}
}
return hr;
}
nsresult
WMFReader::ReadMetadata(MediaInfo* aInfo,
MetadataTags** aTags)
{
MOZ_ASSERT(OnTaskQueue());
DECODER_LOG("WMFReader::ReadMetadata()");
HRESULT hr;
const bool triedToInitDXVA = mUseHwAccel;
if (FAILED(CreateSourceReader())) {
mSourceReader = nullptr;
if (triedToInitDXVA && !mUseHwAccel) {
// We tried to initialize DXVA and failed. Try again to create the
// IMFSourceReader but this time we won't use DXVA. Note that we
// must recreate the IMFSourceReader from scratch, as on some systems
// (AMD Radeon 3000) we cannot successfully reconfigure an existing
// reader to not use DXVA after we've failed to configure DXVA.
// See bug 987127.
if (FAILED(CreateSourceReader())) {
mSourceReader = nullptr;
}
}
}
if (!mSourceReader) {
NS_WARNING("Failed to create IMFSourceReader");
return NS_ERROR_FAILURE;
}
if (mInfo.HasVideo()) {
DECODER_LOG("Using DXVA: %s", (mUseHwAccel ? "Yes" : "No"));
}
// Abort if both video and audio failed to initialize.
NS_ENSURE_TRUE(mInfo.HasValidMedia(), NS_ERROR_FAILURE);
// Get the duration, and report it to the decoder if we have it.
int64_t duration = 0;
hr = GetSourceReaderDuration(mSourceReader, duration);
if (SUCCEEDED(hr)) {
mInfo.mMetadataDuration.emplace(TimeUnit::FromMicroseconds(duration));
}
*aInfo = mInfo;
*aTags = nullptr;
// aTags can be retrieved using techniques like used here:
// http://blogs.msdn.com/b/mf/archive/2010/01/12/mfmediapropdump.aspx
return NS_OK;
}
bool
WMFReader::IsMediaSeekable()
{
// Get the duration
int64_t duration = 0;
HRESULT hr = GetSourceReaderDuration(mSourceReader, duration);
// We can seek if we get a duration *and* the reader reports that it's
// seekable.
bool canSeek = false;
if (FAILED(hr) || FAILED(GetSourceReaderCanSeek(mSourceReader, canSeek)) ||
!canSeek) {
return false;
}
return true;
}
bool
WMFReader::DecodeAudioData()
{
MOZ_ASSERT(OnTaskQueue());
HRESULT hr;
hr = mSourceReader->ReadSample(MF_SOURCE_READER_FIRST_AUDIO_STREAM,
0, // control flags
0, // read stream index
nullptr,
nullptr,
nullptr);
if (FAILED(hr)) {
DECODER_LOG("WMFReader::DecodeAudioData() ReadSample failed with hr=0x%x", hr);
// End the stream.
return false;
}
DWORD flags = 0;
LONGLONG timestampHns = 0;
RefPtr<IMFSample> sample;
hr = mSourceReaderCallback->Wait(&flags, &timestampHns, byRef(sample));
if (FAILED(hr) ||
(flags & MF_SOURCE_READERF_ERROR) ||
(flags & MF_SOURCE_READERF_ENDOFSTREAM) ||
(flags & MF_SOURCE_READERF_CURRENTMEDIATYPECHANGED)) {
DECODER_LOG("WMFReader::DecodeAudioData() ReadSample failed with hr=0x%x flags=0x%x",
hr, flags);
// End the stream.
return false;
}
if (!sample) {
// Not enough data? Try again...
return true;
}
RefPtr<IMFMediaBuffer> buffer;
hr = sample->ConvertToContiguousBuffer(byRef(buffer));
NS_ENSURE_TRUE(SUCCEEDED(hr), false);
BYTE* data = nullptr; // Note: *data will be owned by the IMFMediaBuffer, we don't need to free it.
DWORD maxLength = 0, currentLength = 0;
hr = buffer->Lock(&data, &maxLength, &currentLength);
NS_ENSURE_TRUE(SUCCEEDED(hr), false);
uint32_t numFrames = currentLength / mAudioBytesPerSample / mAudioChannels;
NS_ASSERTION(sizeof(AudioDataValue) == mAudioBytesPerSample, "Size calculation is wrong");
nsAutoArrayPtr<AudioDataValue> pcmSamples(new AudioDataValue[numFrames * mAudioChannels]);
memcpy(pcmSamples.get(), data, currentLength);
buffer->Unlock();
// We calculate the timestamp and the duration based on the number of audio
// frames we've already played. We don't trust the timestamp stored on the
// IMFSample, as sometimes it's wrong, possibly due to buggy encoders?
// If this sample block comes after a discontinuity (i.e. a gap or seek)
// reset the frame counters, and capture the timestamp. Future timestamps
// will be offset from this block's timestamp.
UINT32 discontinuity = false;
sample->GetUINT32(MFSampleExtension_Discontinuity, &discontinuity);
if (mMustRecaptureAudioPosition || discontinuity) {
mAudioFrameSum = 0;
hr = HNsToFrames(timestampHns, mAudioRate, &mAudioFrameOffset);
NS_ENSURE_TRUE(SUCCEEDED(hr), false);
mMustRecaptureAudioPosition = false;
}
int64_t timestamp;
hr = FramesToUsecs(mAudioFrameOffset + mAudioFrameSum, mAudioRate, &timestamp);
NS_ENSURE_TRUE(SUCCEEDED(hr), false);
mAudioFrameSum += numFrames;
int64_t duration;
hr = FramesToUsecs(numFrames, mAudioRate, &duration);
NS_ENSURE_TRUE(SUCCEEDED(hr), false);
mAudioQueue.Push(new AudioData(mDecoder->GetResource()->Tell(),
timestamp,
duration,
numFrames,
pcmSamples.forget(),
mAudioChannels,
mAudioRate));
#ifdef LOG_SAMPLE_DECODE
DECODER_LOG("Decoded audio sample! timestamp=%lld duration=%lld currentLength=%u",
timestamp, duration, currentLength);
#endif
return true;
}
HRESULT
WMFReader::CreateBasicVideoFrame(IMFSample* aSample,
int64_t aTimestampUsecs,
int64_t aDurationUsecs,
int64_t aOffsetBytes,
VideoData** aOutVideoData)
{
NS_ENSURE_TRUE(aSample, E_POINTER);
NS_ENSURE_TRUE(aOutVideoData, E_POINTER);
*aOutVideoData = nullptr;
HRESULT hr;
RefPtr<IMFMediaBuffer> buffer;
// Must convert to contiguous buffer to use IMD2DBuffer interface.
hr = aSample->ConvertToContiguousBuffer(byRef(buffer));
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
// Try and use the IMF2DBuffer interface if available, otherwise fallback
// to the IMFMediaBuffer interface. Apparently IMF2DBuffer is more efficient,
// but only some systems (Windows 8?) support it.
BYTE* data = nullptr;
LONG stride = 0;
RefPtr<IMF2DBuffer> twoDBuffer;
hr = buffer->QueryInterface(static_cast<IMF2DBuffer**>(byRef(twoDBuffer)));
if (SUCCEEDED(hr)) {
hr = twoDBuffer->Lock2D(&data, &stride);
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
} else {
hr = buffer->Lock(&data, nullptr, nullptr);
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
stride = mVideoStride;
}
// YV12, planar format: [YYYY....][VVVV....][UUUU....]
// i.e., Y, then V, then U.
VideoData::YCbCrBuffer b;
// Y (Y') plane
b.mPlanes[0].mData = data;
b.mPlanes[0].mStride = stride;
b.mPlanes[0].mHeight = mVideoHeight;
b.mPlanes[0].mWidth = mVideoWidth;
b.mPlanes[0].mOffset = 0;
b.mPlanes[0].mSkip = 0;
// The V and U planes are stored 16-row-aligned, so we need to add padding
// to the row heights to ensure the Y'CbCr planes are referenced properly.
uint32_t padding = 0;
if (mVideoHeight % 16 != 0) {
padding = 16 - (mVideoHeight % 16);
}
uint32_t y_size = stride * (mVideoHeight + padding);
uint32_t v_size = stride * (mVideoHeight + padding) / 4;
uint32_t halfStride = (stride + 1) / 2;
uint32_t halfHeight = (mVideoHeight + 1) / 2;
uint32_t halfWidth = (mVideoWidth + 1) / 2;
// U plane (Cb)
b.mPlanes[1].mData = data + y_size + v_size;
b.mPlanes[1].mStride = halfStride;
b.mPlanes[1].mHeight = halfHeight;
b.mPlanes[1].mWidth = halfWidth;
b.mPlanes[1].mOffset = 0;
b.mPlanes[1].mSkip = 0;
// V plane (Cr)
b.mPlanes[2].mData = data + y_size;
b.mPlanes[2].mStride = halfStride;
b.mPlanes[2].mHeight = halfHeight;
b.mPlanes[2].mWidth = halfWidth;
b.mPlanes[2].mOffset = 0;
b.mPlanes[2].mSkip = 0;
nsRefPtr<VideoData> v = VideoData::Create(mInfo.mVideo,
mDecoder->GetImageContainer(),
aOffsetBytes,
aTimestampUsecs,
aDurationUsecs,
b,
false,
-1,
mPictureRegion);
if (twoDBuffer) {
twoDBuffer->Unlock2D();
} else {
buffer->Unlock();
}
v.forget(aOutVideoData);
return S_OK;
}
HRESULT
WMFReader::CreateD3DVideoFrame(IMFSample* aSample,
int64_t aTimestampUsecs,
int64_t aDurationUsecs,
int64_t aOffsetBytes,
VideoData** aOutVideoData)
{
NS_ENSURE_TRUE(aSample, E_POINTER);
NS_ENSURE_TRUE(aOutVideoData, E_POINTER);
NS_ENSURE_TRUE(mDXVA2Manager, E_ABORT);
NS_ENSURE_TRUE(mUseHwAccel, E_ABORT);
*aOutVideoData = nullptr;
HRESULT hr;
nsRefPtr<Image> image;
hr = mDXVA2Manager->CopyToImage(aSample,
mPictureRegion,
mDecoder->GetImageContainer(),
getter_AddRefs(image));
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
NS_ENSURE_TRUE(image, E_FAIL);
nsRefPtr<VideoData> v = VideoData::CreateFromImage(mInfo.mVideo,
mDecoder->GetImageContainer(),
aOffsetBytes,
aTimestampUsecs,
aDurationUsecs,
image.forget(),
false,
-1,
mPictureRegion);
NS_ENSURE_TRUE(v, E_FAIL);
v.forget(aOutVideoData);
return S_OK;
}
bool
WMFReader::DecodeVideoFrame(bool &aKeyframeSkip,
int64_t aTimeThreshold)
{
MOZ_ASSERT(OnTaskQueue());
// Record number of frames decoded and parsed. Automatically update the
// stats counters using the AutoNotifyDecoded stack-based class.
AbstractMediaDecoder::AutoNotifyDecoded a(mDecoder);
HRESULT hr;
hr = mSourceReader->ReadSample(MF_SOURCE_READER_FIRST_VIDEO_STREAM,
0, // control flags
0, // read stream index
nullptr,
nullptr,
nullptr);
if (FAILED(hr)) {
DECODER_LOG("WMFReader::DecodeVideoData() ReadSample failed with hr=0x%x", hr);
return false;
}
DWORD flags = 0;
LONGLONG timestampHns = 0;
RefPtr<IMFSample> sample;
hr = mSourceReaderCallback->Wait(&flags, &timestampHns, byRef(sample));
if (flags & MF_SOURCE_READERF_ERROR) {
NS_WARNING("WMFReader: Catastrophic failure reading video sample");
// Future ReadSample() calls will fail, so give up and report end of stream.
return false;
}
if (FAILED(hr)) {
// Unknown failure, ask caller to try again?
return true;
}
if (!sample) {
if ((flags & MF_SOURCE_READERF_ENDOFSTREAM)) {
DECODER_LOG("WMFReader; Null sample after video decode, at end of stream");
return false;
}
DECODER_LOG("WMFReader; Null sample after video decode. Maybe insufficient data...");
return true;
}
if ((flags & MF_SOURCE_READERF_CURRENTMEDIATYPECHANGED)) {
DECODER_LOG("WMFReader: Video media type changed!");
RefPtr<IMFMediaType> mediaType;
hr = mSourceReader->GetCurrentMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM,
byRef(mediaType));
if (FAILED(hr) ||
FAILED(ConfigureVideoFrameGeometry(mediaType))) {
NS_WARNING("Failed to reconfigure video media type");
return false;
}
}
int64_t timestamp = HNsToUsecs(timestampHns);
if (timestamp < aTimeThreshold) {
return true;
}
int64_t offset = mDecoder->GetResource()->Tell();
int64_t duration = GetSampleDuration(sample);
VideoData* v = nullptr;
if (mUseHwAccel) {
hr = CreateD3DVideoFrame(sample, timestamp, duration, offset, &v);
} else {
hr = CreateBasicVideoFrame(sample, timestamp, duration, offset, &v);
}
NS_ENSURE_TRUE(SUCCEEDED(hr) && v, false);
a.mParsed++;
a.mDecoded++;
mVideoQueue.Push(v);
#ifdef LOG_SAMPLE_DECODE
DECODER_LOG("Decoded video sample timestamp=%lld duration=%lld stride=%d height=%u flags=%u",
timestamp, duration, mVideoStride, mVideoHeight, flags);
#endif
if ((flags & MF_SOURCE_READERF_ENDOFSTREAM)) {
// End of stream.
DECODER_LOG("End of video stream");
return false;
}
return true;
}
nsRefPtr<MediaDecoderReader::SeekPromise>
WMFReader::Seek(int64_t aTargetUs, int64_t aEndTime)
{
nsresult res = SeekInternal(aTargetUs);
if (NS_FAILED(res)) {
return SeekPromise::CreateAndReject(res, __func__);
} else {
return SeekPromise::CreateAndResolve(aTargetUs, __func__);
}
}
nsresult
WMFReader::SeekInternal(int64_t aTargetUs)
{
DECODER_LOG("WMFReader::Seek() %lld", aTargetUs);
MOZ_ASSERT(OnTaskQueue());
#ifdef DEBUG
bool canSeek = false;
GetSourceReaderCanSeek(mSourceReader, canSeek);
NS_ASSERTION(canSeek, "WMFReader::Seek() should only be called if we can seek!");
#endif
nsresult rv = ResetDecode();
NS_ENSURE_SUCCESS(rv, rv);
// Mark that we must recapture the audio frame count from the next sample.
// WMF doesn't set a discontinuity marker when we seek to time 0, so we
// must remember to recapture the audio frame offset and reset the frame
// sum on the next audio packet we decode.
mMustRecaptureAudioPosition = true;
AutoPropVar var;
HRESULT hr = InitPropVariantFromInt64(UsecsToHNs(aTargetUs), &var);
NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
hr = mSourceReader->SetCurrentPosition(GUID_NULL, var);
NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
return NS_OK;
}
} // namespace mozilla

View File

@ -1,114 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#if !defined(WMFReader_h_)
#define WMFReader_h_
#include "WMF.h"
#include "MediaDecoderReader.h"
#include "nsAutoPtr.h"
#include "mozilla/RefPtr.h"
#include "nsRect.h"
namespace mozilla {
class WMFByteStream;
class WMFSourceReaderCallback;
class DXVA2Manager;
// Decoder backend for reading H.264/AAC in MP4/M4A, and MP3 files using
// Windows Media Foundation.
class WMFReader : public MediaDecoderReader
{
public:
WMFReader(AbstractMediaDecoder* aDecoder);
virtual ~WMFReader();
nsresult Init(MediaDecoderReader* aCloneDonor) override;
bool DecodeAudioData() override;
bool DecodeVideoFrame(bool &aKeyframeSkip,
int64_t aTimeThreshold) override;
bool HasAudio() override;
bool HasVideo() override;
nsresult ReadMetadata(MediaInfo* aInfo,
MetadataTags** aTags) override;
nsRefPtr<SeekPromise>
Seek(int64_t aTime, int64_t aEndTime) override;
bool IsMediaSeekable() override;
private:
HRESULT CreateSourceReader();
HRESULT ConfigureAudioDecoder();
HRESULT ConfigureVideoDecoder();
HRESULT ConfigureVideoFrameGeometry(IMFMediaType* aMediaType);
void GetSupportedAudioCodecs(const GUID** aCodecs, uint32_t* aNumCodecs);
HRESULT CreateBasicVideoFrame(IMFSample* aSample,
int64_t aTimestampUsecs,
int64_t aDurationUsecs,
int64_t aOffsetBytes,
VideoData** aOutVideoData);
HRESULT CreateD3DVideoFrame(IMFSample* aSample,
int64_t aTimestampUsecs,
int64_t aDurationUsecs,
int64_t aOffsetBytes,
VideoData** aOutVideoData);
// Attempt to initialize DXVA. Returns true on success.
bool InitializeDXVA();
nsresult SeekInternal(int64_t aTime);
RefPtr<IMFSourceReader> mSourceReader;
RefPtr<WMFByteStream> mByteStream;
RefPtr<WMFSourceReaderCallback> mSourceReaderCallback;
nsAutoPtr<DXVA2Manager> mDXVA2Manager;
// Region inside the video frame that makes up the picture. Pixels outside
// of this region should not be rendered.
nsIntRect mPictureRegion;
uint32_t mAudioChannels;
uint32_t mAudioBytesPerSample;
uint32_t mAudioRate;
uint32_t mVideoWidth;
uint32_t mVideoHeight;
uint32_t mVideoStride;
// The offset, in audio frames, at which playback started since the
// last discontinuity.
int64_t mAudioFrameOffset;
// The number of audio frames that we've played since the last
// discontinuity.
int64_t mAudioFrameSum;
// True if we need to re-initialize mAudioFrameOffset and mAudioFrameSum
// from the next audio packet we decode. This happens after a seek, since
// WMF doesn't mark a stream as having a discontinuity after a seek(0).
bool mMustRecaptureAudioPosition;
bool mHasAudio;
bool mHasVideo;
bool mUseHwAccel;
// We can't call WMFDecoder::IsMP3Supported() on non-main threads, since it
// checks a pref, so we cache its value in mIsMP3Enabled and use that on
// the decode thread.
const bool mIsMP3Enabled;
bool mCOMInitialized;
};
} // namespace mozilla
#endif

View File

@ -1,151 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "WMFSourceReaderCallback.h"
#include "WMFUtils.h"
namespace mozilla {
static PRLogModuleInfo* gWMFSourceReaderCallbackLog = nullptr;
#define WMF_CB_LOG(...) MOZ_LOG(gWMFSourceReaderCallbackLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
// IUnknown Methods
STDMETHODIMP
WMFSourceReaderCallback::QueryInterface(REFIID aIId, void **aInterface)
{
WMF_CB_LOG("WMFSourceReaderCallback::QueryInterface %s", GetGUIDName(aIId).get());
if (aIId == IID_IMFSourceReaderCallback) {
return DoGetInterface(static_cast<WMFSourceReaderCallback*>(this), aInterface);
}
if (aIId == IID_IUnknown) {
return DoGetInterface(static_cast<WMFSourceReaderCallback*>(this), aInterface);
}
*aInterface = nullptr;
return E_NOINTERFACE;
}
NS_IMPL_ADDREF(WMFSourceReaderCallback)
NS_IMPL_RELEASE(WMFSourceReaderCallback)
WMFSourceReaderCallback::WMFSourceReaderCallback()
: mMonitor("WMFSourceReaderCallback")
, mResultStatus(S_OK)
, mStreamFlags(0)
, mTimestamp(0)
, mSample(nullptr)
, mReadFinished(false)
{
if (!gWMFSourceReaderCallbackLog) {
gWMFSourceReaderCallbackLog = PR_NewLogModule("WMFSourceReaderCallback");
}
}
HRESULT
WMFSourceReaderCallback::NotifyReadComplete(HRESULT aReadStatus,
DWORD aStreamIndex,
DWORD aStreamFlags,
LONGLONG aTimestamp,
IMFSample *aSample)
{
// Note: aSample can be nullptr on success if more data is required!
ReentrantMonitorAutoEnter mon(mMonitor);
if (mSample) {
// The WMFReader should have called Wait() to retrieve the last
// sample returned by the last ReadSample() call, but if we're
// aborting the read before Wait() is called the sample ref
// can be non-null.
mSample->Release();
mSample = nullptr;
}
if (SUCCEEDED(aReadStatus)) {
if (aSample) {
mTimestamp = aTimestamp;
mSample = aSample;
mSample->AddRef();
}
}
mResultStatus = aReadStatus;
mStreamFlags = aStreamFlags;
// Set the sentinal value and notify the monitor, so that threads waiting
// in Wait() are awoken.
mReadFinished = true;
mon.NotifyAll();
return S_OK;
}
STDMETHODIMP
WMFSourceReaderCallback::OnReadSample(HRESULT aReadStatus,
DWORD aStreamIndex,
DWORD aStreamFlags,
LONGLONG aTimestamp,
IMFSample *aSample)
{
WMF_CB_LOG("WMFSourceReaderCallback::OnReadSample() hr=0x%x flags=0x%x time=%lld sample=%p",
aReadStatus, aStreamFlags, aTimestamp, aSample);
return NotifyReadComplete(aReadStatus,
aStreamIndex,
aStreamFlags,
aTimestamp,
aSample);
}
HRESULT
WMFSourceReaderCallback::Cancel()
{
WMF_CB_LOG("WMFSourceReaderCallback::Cancel()");
return NotifyReadComplete(E_ABORT,
0,
0,
0,
nullptr);
}
STDMETHODIMP
WMFSourceReaderCallback::OnEvent(DWORD, IMFMediaEvent *)
{
return S_OK;
}
STDMETHODIMP
WMFSourceReaderCallback::OnFlush(DWORD)
{
return S_OK;
}
HRESULT
WMFSourceReaderCallback::Wait(DWORD* aStreamFlags,
LONGLONG* aTimeStamp,
IMFSample** aSample)
{
ReentrantMonitorAutoEnter mon(mMonitor);
WMF_CB_LOG("WMFSourceReaderCallback::Wait() starting wait");
while (!mReadFinished) {
mon.Wait();
}
mReadFinished = false;
WMF_CB_LOG("WMFSourceReaderCallback::Wait() done waiting");
*aStreamFlags = mStreamFlags;
*aTimeStamp = mTimestamp;
*aSample = mSample;
HRESULT hr = mResultStatus;
mSample = nullptr;
mTimestamp = 0;
mStreamFlags = 0;
mResultStatus = S_OK;
return hr;
}
} // namespace mozilla

View File

@ -1,85 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#if !defined(WMFSourceReaderCallback_h_)
#define WMFSourceReaderCallback_h_
#include "WMF.h"
#include "mozilla/ReentrantMonitor.h"
#include "mozilla/RefPtr.h"
#include "nsISupportsImpl.h"
namespace mozilla {
// A listener which we pass into the IMFSourceReader upon creation which is
// notified when an asynchronous call to IMFSourceReader::ReadSample()
// completes. This allows us to abort ReadSample() operations when the
// WMFByteStream's underlying MediaResource is closed. This ensures that
// the decode threads don't get stuck in a synchronous ReadSample() call
// when the MediaResource is unexpectedly shutdown.
class WMFSourceReaderCallback final : public IMFSourceReaderCallback
{
~WMFSourceReaderCallback() {}
public:
WMFSourceReaderCallback();
// IUnknown Methods.
STDMETHODIMP QueryInterface(REFIID aIId, LPVOID *aInterface);
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();
// IMFSourceReaderCallback methods
STDMETHODIMP OnReadSample(HRESULT hrStatus,
DWORD dwStreamIndex,
DWORD dwStreamFlags,
LONGLONG llTimestamp,
IMFSample *pSample);
STDMETHODIMP OnEvent(DWORD, IMFMediaEvent *);
STDMETHODIMP OnFlush(DWORD);
// Causes the calling thread to block waiting for the
// IMFSourceReader::ReadSample() result callback to occur, or for the
// WMFByteStream to be closed.
HRESULT Wait(DWORD* aStreamFlags,
LONGLONG* aTimeStamp,
IMFSample** aSample);
// Cancels Wait() calls.
HRESULT Cancel();
private:
// Sets state to record the result of a read, and awake threads
// waiting in Wait().
HRESULT NotifyReadComplete(HRESULT aReadStatus,
DWORD aStreamIndex,
DWORD aStreamFlags,
LONGLONG aTimestamp,
IMFSample *aSample);
// Synchronizes all member data in this class, and Wait() blocks on
// and NotifyReadComplete() notifies this monitor.
ReentrantMonitor mMonitor;
// Read result data.
HRESULT mResultStatus;
DWORD mStreamFlags;
LONGLONG mTimestamp;
IMFSample* mSample;
// Sentinal. Set to true when a read result is returned. Wait() won't exit
// until this is set to true.
bool mReadFinished;
// IUnknown ref counting.
ThreadSafeAutoRefCnt mRefCnt;
NS_DECL_OWNINGTHREAD
};
} // namespace mozilla
#endif // WMFSourceReaderCallback_h_

View File

@ -1,36 +0,0 @@
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
EXPORTS += [
'DXVA2Manager.h',
'WMF.h',
'WMFDecoder.h',
'WMFReader.h',
'WMFUtils.h',
]
UNIFIED_SOURCES += [
'DXVA2Manager.cpp',
'WMFByteStream.cpp',
'WMFDecoder.cpp',
'WMFReader.cpp',
'WMFSourceReaderCallback.cpp',
]
SOURCES += [
'WMFUtils.cpp',
]
include('/ipc/chromium/chromium-config.mozbuild')
FAIL_ON_WARNINGS = True
FINAL_LIBRARY = 'xul'
if CONFIG['OS_ARCH'] == 'WINNT':
DEFINES['NOMINMAX'] = True
CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']