mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-26 06:11:37 +00:00
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:
parent
efc7a7830e
commit
2f60326595
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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']
|
||||
|
||||
|
@ -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']
|
||||
|
@ -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
|
@ -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
|
@ -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
|
||||
|
@ -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
|
@ -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, ×tampHns, 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, ¤tLength);
|
||||
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, ×tamp);
|
||||
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, ×tampHns, 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
|
@ -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
|
@ -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
|
@ -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_
|
@ -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']
|
Loading…
Reference in New Issue
Block a user