mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:30:27 +00:00
import changes from `dev' branch of rmottola/Arctic-Fox:
- fix misspatch and apply Bug 1097803: Report succesful Direct2D 1.1 usage. r=jrmuizel (adc553e15) - Refactor graphics device initialization on Windows. (bug 1183910 part 1, r=mattwoodrow,bas) (af396b2aa) - Add a pref to force TDRs for graphics testing. (bug 1183910 part 2, r=mattwoodrow) (c35e7131c) - Bug 1157476 - Remove synchronous dispatch in AndroidMediaResourceServer::Start. r=cajbir (39033ed48) - Bug 1174055 - Remove WMFReader. r=jya (2890dc625) - Bug 1144638. Retry getting an active display link. r=mstange (c786816cf) - Bug 1154322 - Allow using skia for content rendering. r=jrmuizel (26413363e) - Bug 1161731: Remove newline characters from the ends of NS_WARNING messages in /gfx and /layout. r=mstange (820583f0f) - Bug 1147297. Fix assert vsync adjustment time on windows to be >=. r=jrmuizel (c65917a4a) - Bug 1147953. Fix vsync adjustment time to allow negative timestamps. r=jrmuizel (10a725baa) - Bug 1160157: Part 1. Uninitialized statics aren't really uninitialized, but it doesn't read well. r=jmuizelaar (e9e214c52) - minor tweaks (17a5ae1e4) - Remove D3D11Status flag in favor of FeatureStatus. (bug 1183910 part 3, r=mattwoodrow) (734eaec03)
This commit is contained in:
@@ -190,6 +190,7 @@
|
||||
#include "mozilla/widget/PuppetBidiKeyboard.h"
|
||||
#include "mozilla/RemoteSpellCheckEngineChild.h"
|
||||
#include "GMPServiceChild.h"
|
||||
#include "gfxPlatform.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::docshell;
|
||||
@@ -2806,6 +2807,15 @@ ContentChild::RecvEndDragSession(const bool& aDoneDrag,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ContentChild::RecvTestGraphicsDeviceReset(const uint32_t& aResetReason)
|
||||
{
|
||||
#if defined(XP_WIN)
|
||||
gfxPlatform::GetPlatform()->TestDeviceReset(DeviceResetReason(aResetReason));
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
|
||||
@@ -463,6 +463,8 @@ public:
|
||||
|
||||
virtual bool RecvGamepadUpdate(const GamepadChangeEvent& aGamepadEvent) override;
|
||||
|
||||
virtual bool RecvTestGraphicsDeviceReset(const uint32_t& aResetReason) override;
|
||||
|
||||
private:
|
||||
virtual void ActorDestroy(ActorDestroyReason why) override;
|
||||
|
||||
|
||||
@@ -646,6 +646,13 @@ child:
|
||||
*/
|
||||
GamepadUpdate(GamepadChangeEvent aGamepadEvent);
|
||||
|
||||
|
||||
/**
|
||||
* Tell the child that for testing purposes, a graphics device reset has
|
||||
* occurred.
|
||||
*/
|
||||
async TestGraphicsDeviceReset(uint32_t aReason);
|
||||
|
||||
/**
|
||||
* Notify the child that presentation receiver has been launched with the
|
||||
* correspondent iframe.
|
||||
|
||||
@@ -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"
|
||||
@@ -318,14 +314,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)
|
||||
@@ -426,6 +414,7 @@ CanPlayStatus
|
||||
DecoderTraits::CanHandleCodecsType(const char* aMIMEType,
|
||||
const nsAString& aRequestedCodecs)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
char const* const* codecList = nullptr;
|
||||
#ifdef MOZ_RAW
|
||||
if (IsRawType(nsDependentCString(aMIMEType))) {
|
||||
@@ -481,7 +470,7 @@ DecoderTraits::CanHandleCodecsType(const char* aMIMEType,
|
||||
#endif
|
||||
#ifdef MOZ_ANDROID_OMX
|
||||
if (MediaDecoder::IsAndroidMediaEnabled()) {
|
||||
GetAndroidMediaPluginHost()->FindDecoder(nsDependentCString(aMIMEType), &codecList);
|
||||
EnsureAndroidMediaPluginHost()->FindDecoder(nsDependentCString(aMIMEType), &codecList))
|
||||
}
|
||||
#endif
|
||||
if (!codecList) {
|
||||
@@ -557,16 +546,6 @@ DecoderTraits::CanHandleMediaType(const char* aMIMEType,
|
||||
return CANPLAY_MAYBE;
|
||||
}
|
||||
#endif
|
||||
#ifdef MOZ_WMF
|
||||
if (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), nullptr)) {
|
||||
return CANPLAY_MAYBE;
|
||||
@@ -591,6 +570,7 @@ static
|
||||
already_AddRefed<MediaDecoder>
|
||||
InstantiateDecoder(const nsACString& aType, MediaDecoderOwner* aOwner)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
nsRefPtr<MediaDecoder> decoder;
|
||||
|
||||
#ifdef MOZ_FMP4
|
||||
@@ -664,7 +644,7 @@ if (IsAACSupportedType(aType)) {
|
||||
#endif
|
||||
#ifdef MOZ_ANDROID_OMX
|
||||
if (MediaDecoder::IsAndroidMediaEnabled() &&
|
||||
GetAndroidMediaPluginHost()->FindDecoder(aType, nullptr)) {
|
||||
EnsureAndroidMediaPluginHost()->FindDecoder(aType, nullptr)) {
|
||||
decoder = new AndroidMediaDecoder(aType);
|
||||
return decoder.forget();
|
||||
}
|
||||
@@ -681,12 +661,6 @@ if (IsAACSupportedType(aType)) {
|
||||
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();
|
||||
@@ -703,6 +677,7 @@ if (IsAACSupportedType(aType)) {
|
||||
already_AddRefed<MediaDecoder>
|
||||
DecoderTraits::CreateDecoder(const nsACString& aType, MediaDecoderOwner* aOwner)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
nsRefPtr<MediaDecoder> decoder(InstantiateDecoder(aType, aOwner));
|
||||
NS_ENSURE_TRUE(decoder != nullptr, nullptr);
|
||||
NS_ENSURE_TRUE(decoder->Init(aOwner), nullptr);
|
||||
@@ -713,6 +688,7 @@ DecoderTraits::CreateDecoder(const nsACString& aType, MediaDecoderOwner* aOwner)
|
||||
/* static */
|
||||
MediaDecoderReader* DecoderTraits::CreateReader(const nsACString& aType, AbstractMediaDecoder* aDecoder)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MediaDecoderReader* decoderReader = nullptr;
|
||||
|
||||
if (!aDecoder) {
|
||||
@@ -757,7 +733,7 @@ if (IsAACSupportedType(aType)) {
|
||||
#endif
|
||||
#ifdef MOZ_ANDROID_OMX
|
||||
if (MediaDecoder::IsAndroidMediaEnabled() &&
|
||||
GetAndroidMediaPluginHost()->FindDecoder(aType, nullptr)) {
|
||||
EnsureAndroidMediaPluginHost()->FindDecoder(aType, nullptr)) {
|
||||
decoderReader = new AndroidMediaReader(aDecoder, aType);
|
||||
} else
|
||||
#endif
|
||||
@@ -765,17 +741,10 @@ if (IsAACSupportedType(aType)) {
|
||||
decoderReader = new WebMReader(aDecoder);
|
||||
} else
|
||||
#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);
|
||||
@@ -814,9 +783,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;
|
||||
@@ -1482,14 +1478,6 @@ MediaDecoder::IsAndroidMediaEnabled()
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_WMF
|
||||
bool
|
||||
MediaDecoder::IsWMFEnabled()
|
||||
{
|
||||
return WMFDecoder::IsEnabled();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_APPLEMEDIA
|
||||
bool
|
||||
MediaDecoder::IsAppleMP3Enabled()
|
||||
|
||||
@@ -207,6 +207,7 @@ static const char* GetOmxLibraryName()
|
||||
|
||||
AndroidMediaPluginHost::AndroidMediaPluginHost() {
|
||||
MOZ_COUNT_CTOR(AndroidMediaPluginHost);
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
mResourceServer = AndroidMediaResourceServer::Start();
|
||||
|
||||
@@ -305,14 +306,21 @@ void AndroidMediaPluginHost::DestroyDecoder(Decoder *aDecoder)
|
||||
}
|
||||
|
||||
AndroidMediaPluginHost *sAndroidMediaPluginHost = nullptr;
|
||||
AndroidMediaPluginHost *GetAndroidMediaPluginHost()
|
||||
AndroidMediaPluginHost *EnsureAndroidMediaPluginHost()
|
||||
{
|
||||
MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
|
||||
if (!sAndroidMediaPluginHost) {
|
||||
sAndroidMediaPluginHost = new AndroidMediaPluginHost();
|
||||
}
|
||||
return sAndroidMediaPluginHost;
|
||||
}
|
||||
|
||||
AndroidMediaPluginHost *GetAndroidMediaPluginHost()
|
||||
{
|
||||
MOZ_ASSERT(sAndroidMediaPluginHost);
|
||||
return sAndroidMediaPluginHost;
|
||||
}
|
||||
|
||||
void AndroidMediaPluginHost::Shutdown()
|
||||
{
|
||||
delete sAndroidMediaPluginHost;
|
||||
|
||||
@@ -29,6 +29,11 @@ public:
|
||||
void DestroyDecoder(MPAPI::Decoder *aDecoder);
|
||||
};
|
||||
|
||||
// Must be called on the main thread. Creates the plugin host if it doesn't
|
||||
// already exist.
|
||||
AndroidMediaPluginHost *EnsureAndroidMediaPluginHost();
|
||||
|
||||
// May be called on any thread after EnsureAndroidMediaPluginHost has been called.
|
||||
AndroidMediaPluginHost *GetAndroidMediaPluginHost();
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
@@ -398,6 +398,7 @@ AndroidMediaResourceServer::AndroidMediaResourceServer() :
|
||||
NS_IMETHODIMP
|
||||
AndroidMediaResourceServer::Run()
|
||||
{
|
||||
MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
nsresult rv;
|
||||
@@ -420,13 +421,9 @@ AndroidMediaResourceServer::Run()
|
||||
already_AddRefed<AndroidMediaResourceServer>
|
||||
AndroidMediaResourceServer::Start()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
nsRefPtr<AndroidMediaResourceServer> server = new AndroidMediaResourceServer();
|
||||
// We should fix this up - see bug 1157476.
|
||||
if (NS_IsMainThread()) {
|
||||
server->Run();
|
||||
} else {
|
||||
NS_DispatchToMainThread(server, NS_DISPATCH_SYNC);
|
||||
}
|
||||
server->Run();
|
||||
return server.forget();
|
||||
}
|
||||
|
||||
|
||||
@@ -51,9 +51,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),
|
||||
mAudioFrameSum(0),
|
||||
mAudioFrameOffset(0),
|
||||
mHasAudio(false),
|
||||
mHasVideo(false),
|
||||
mUseHwAccel(false),
|
||||
mMustRecaptureAudioPosition(true),
|
||||
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::GetPlatform()->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']
|
||||
+3
-1
@@ -669,7 +669,9 @@ Factory::SetDirect3D10Device(ID3D10Device1 *aDevice)
|
||||
// do not throw on failure; return error codes and disconnect the device
|
||||
// On Windows 8 error codes are the default, but on Windows 7 the
|
||||
// default is to throw (or perhaps only with some drivers?)
|
||||
aDevice->SetExceptionMode(0);
|
||||
if (aDevice) {
|
||||
aDevice->SetExceptionMode(0);
|
||||
}
|
||||
mD3D10Device = aDevice;
|
||||
}
|
||||
|
||||
|
||||
@@ -346,11 +346,11 @@ TextureClient::CreateForDrawing(ISurfaceAllocator* aAllocator,
|
||||
#ifdef XP_WIN
|
||||
LayersBackend parentBackend = aAllocator->GetCompositorBackendType();
|
||||
if (parentBackend == LayersBackend::LAYERS_D3D11 &&
|
||||
(aMoz2DBackend == gfx::BackendType::DIRECT2D ||
|
||||
aMoz2DBackend == gfx::BackendType::DIRECT2D1_1) &&
|
||||
gfxWindowsPlatform::GetPlatform()->GetD3D10Device() &&
|
||||
((aMoz2DBackend == gfx::BackendType::DIRECT2D && Factory::GetDirect3D10Device()) ||
|
||||
(aMoz2DBackend == gfx::BackendType::DIRECT2D1_1 && Factory::GetDirect3D11Device())) &&
|
||||
aSize.width <= maxTextureSize &&
|
||||
aSize.height <= maxTextureSize) {
|
||||
aSize.height <= maxTextureSize)
|
||||
{
|
||||
texture = new TextureClientD3D11(aAllocator, aFormat, aTextureFlags);
|
||||
}
|
||||
if (parentBackend == LayersBackend::LAYERS_D3D9 &&
|
||||
|
||||
@@ -206,7 +206,7 @@ DeviceManagerD3D9::Init()
|
||||
if (!mNv3DVUtils) {
|
||||
mNv3DVUtils = new Nv3DVUtils();
|
||||
if (!mNv3DVUtils) {
|
||||
NS_WARNING("Could not create a new instance of Nv3DVUtils.\n");
|
||||
NS_WARNING("Could not create a new instance of Nv3DVUtils.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -673,7 +673,7 @@ CompositorParent::CompositorParent(nsIWidget* aWidget,
|
||||
}
|
||||
|
||||
if (UseVsyncComposition()) {
|
||||
NS_WARNING("Enabling vsync compositor\n");
|
||||
NS_WARNING("Enabling vsync compositor");
|
||||
mCompositorScheduler = new CompositorVsyncScheduler(this, aWidget);
|
||||
} else {
|
||||
mCompositorScheduler = new CompositorSoftwareTimerScheduler(this);
|
||||
|
||||
@@ -32,5 +32,12 @@ FeatureStatusToString(FeatureStatus aStatus)
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
IsFeatureStatusFailure(FeatureStatus aStatus)
|
||||
{
|
||||
return aStatus != FeatureStatus::Unused &&
|
||||
aStatus != FeatureStatus::Available;
|
||||
}
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
|
||||
@@ -39,6 +39,7 @@ enum class FeatureStatus
|
||||
};
|
||||
|
||||
const char* FeatureStatusToString(FeatureStatus aStatus);
|
||||
bool IsFeatureStatusFailure(FeatureStatus aStatus);
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
|
||||
@@ -492,7 +492,7 @@ gfxAndroidPlatform::CreateHardwareVsyncSource()
|
||||
VsyncSource::Display& display = vsyncSource->GetGlobalDisplay();
|
||||
display.EnableVsync();
|
||||
if (!display.IsVsyncEnabled()) {
|
||||
NS_WARNING("Error enabling gonk vsync. Falling back to software vsync\n");
|
||||
NS_WARNING("Error enabling gonk vsync. Falling back to software vsync");
|
||||
return gfxPlatform::CreateHardwareVsyncSource();
|
||||
}
|
||||
display.DisableVsync();
|
||||
|
||||
@@ -2350,7 +2350,7 @@ gfxPlatform::ShouldUseLayersAcceleration()
|
||||
if (gfxPrefs::LayersAccelerationForceEnabled()) {
|
||||
return true;
|
||||
}
|
||||
if (gfxPlatform::GetPlatform()->AccelerateLayersByDefault()) {
|
||||
if (AccelerateLayersByDefault()) {
|
||||
return true;
|
||||
}
|
||||
if (acceleratedEnv && *acceleratedEnv != '0') {
|
||||
@@ -2426,7 +2426,7 @@ gfxPlatform::UsesOffMainThreadCompositing()
|
||||
already_AddRefed<mozilla::gfx::VsyncSource>
|
||||
gfxPlatform::CreateHardwareVsyncSource()
|
||||
{
|
||||
NS_WARNING("Hardware Vsync support not yet implemented. Falling back to software timers\n");
|
||||
NS_WARNING("Hardware Vsync support not yet implemented. Falling back to software timers");
|
||||
nsRefPtr<mozilla::gfx::VsyncSource> softwareVsync = new SoftwareVsyncSource();
|
||||
return softwareVsync.forget();
|
||||
}
|
||||
|
||||
@@ -673,6 +673,10 @@ public:
|
||||
return mCompositorBackend;
|
||||
}
|
||||
|
||||
// Trigger a test-driven graphics device reset.
|
||||
virtual void TestDeviceReset(DeviceResetReason aReason)
|
||||
{}
|
||||
|
||||
protected:
|
||||
gfxPlatform();
|
||||
virtual ~gfxPlatform();
|
||||
|
||||
@@ -78,7 +78,8 @@ gfxPlatformMac::gfxPlatformMac()
|
||||
uint32_t canvasMask = BackendTypeBit(BackendType::CAIRO) |
|
||||
BackendTypeBit(BackendType::SKIA) |
|
||||
BackendTypeBit(BackendType::COREGRAPHICS);
|
||||
uint32_t contentMask = BackendTypeBit(BackendType::COREGRAPHICS);
|
||||
uint32_t contentMask = BackendTypeBit(BackendType::COREGRAPHICS) |
|
||||
BackendTypeBit(BackendType::SKIA);
|
||||
InitBackendPrefs(canvasMask, BackendType::COREGRAPHICS,
|
||||
contentMask, BackendType::COREGRAPHICS);
|
||||
|
||||
@@ -460,13 +461,26 @@ public:
|
||||
OSXDisplay()
|
||||
: mDisplayLink(nullptr)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
|
||||
}
|
||||
|
||||
~OSXDisplay()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mTimer->Cancel();
|
||||
mTimer = nullptr;
|
||||
DisableVsync();
|
||||
}
|
||||
|
||||
static void RetryEnableVsync(nsITimer* aTimer, void* aOsxDisplay)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
OSXDisplay* osxDisplay = static_cast<OSXDisplay*>(aOsxDisplay);
|
||||
MOZ_ASSERT(osxDisplay);
|
||||
osxDisplay->EnableVsync();
|
||||
}
|
||||
|
||||
virtual void EnableVsync() override
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
@@ -479,18 +493,25 @@ public:
|
||||
// situations. According to the docs, it is compatible with all displays running on the computer
|
||||
// But if we have different monitors at different display rates, we may hit issues.
|
||||
if (CVDisplayLinkCreateWithActiveCGDisplays(&mDisplayLink) != kCVReturnSuccess) {
|
||||
NS_WARNING("Could not create a display link with all active displays. Falling back to main display\n");
|
||||
NS_WARNING("Could not create a display link with all active displays. Retrying");
|
||||
CVDisplayLinkRelease(mDisplayLink);
|
||||
mDisplayLink = nullptr;
|
||||
|
||||
// bug 1142708 - When coming back from sleep, there may be no active displays ready yet,
|
||||
// even if listening for the kIOMessageSystemHasPoweredOn event from OS X sleep notifications.
|
||||
// bug 1142708 - When coming back from sleep,
|
||||
// or when changing displays, active displays may not be ready yet,
|
||||
// even if listening for the kIOMessageSystemHasPoweredOn event
|
||||
// from OS X sleep notifications.
|
||||
// Active displays are those that are drawable.
|
||||
// In these cases, default back to the main display to try to get a vsync event.
|
||||
// The alternative would be to keep polling the CGActiveDisplayList for the displays to be ready.
|
||||
if (CVDisplayLinkCreateWithCGDisplay(CGMainDisplayID(), &mDisplayLink) != kCVReturnSuccess) {
|
||||
MOZ_CRASH("Could not create a CVDisplayLink with either active displays or the main display");
|
||||
}
|
||||
NS_WARNING("Using the CVDisplayLink from the main display\n");
|
||||
// bug 1144638 - When changing display configurations and getting
|
||||
// notifications from CGDisplayReconfigurationCallBack, the
|
||||
// callback gets called twice for each active display
|
||||
// so it's difficult to know when all displays are active.
|
||||
// Instead, try again soon. The delay is arbitrary. 100ms chosen
|
||||
// because on a late 2013 15" retina, it takes about that
|
||||
// long to come back up from sleep.
|
||||
uint32_t delay = 100;
|
||||
mTimer->InitWithFuncCallback(RetryEnableVsync, this, delay, nsITimer::TYPE_ONE_SHOT);
|
||||
return;
|
||||
}
|
||||
|
||||
if (CVDisplayLinkSetOutputCallback(mDisplayLink, &VsyncCallback, this) != kCVReturnSuccess) {
|
||||
@@ -538,6 +559,7 @@ public:
|
||||
private:
|
||||
// Manages the display link render thread
|
||||
CVDisplayLinkRef mDisplayLink;
|
||||
nsRefPtr<nsITimer> mTimer;
|
||||
}; // OSXDisplay
|
||||
|
||||
private:
|
||||
@@ -575,7 +597,7 @@ gfxPlatformMac::CreateHardwareVsyncSource()
|
||||
VsyncSource::Display& primaryDisplay = osxVsyncSource->GetGlobalDisplay();
|
||||
primaryDisplay.EnableVsync();
|
||||
if (!primaryDisplay.IsVsyncEnabled()) {
|
||||
NS_WARNING("OS X Vsync source not enabled. Falling back to software vsync.\n");
|
||||
NS_WARNING("OS X Vsync source not enabled. Falling back to software vsync.");
|
||||
return gfxPlatform::CreateHardwareVsyncSource();
|
||||
}
|
||||
|
||||
|
||||
@@ -241,6 +241,7 @@ private:
|
||||
DECL_GFX_PREF(Once, "gfx.max-alloc-size", MaxAllocSize, int32_t, (int32_t)500000000);
|
||||
DECL_GFX_PREF(Once, "gfx.max-texture-size", MaxTextureSize, int32_t, (int32_t)32767);
|
||||
DECL_GFX_PREF(Live, "gfx.perf-warnings.enabled", PerfWarnings, bool, false);
|
||||
DECL_GFX_PREF(Live, "gfx.testing.device-reset", DeviceResetForTesting, int32_t, 0);
|
||||
DECL_GFX_PREF(Once, "gfx.touch.resample", TouchResampling, bool, false);
|
||||
|
||||
// These times should be in milliseconds
|
||||
|
||||
@@ -1320,7 +1320,7 @@ gfxUtils::WriteAsPNG(SourceSurface* aSurface, const char* aFile)
|
||||
}
|
||||
}
|
||||
if (!file) {
|
||||
NS_WARNING("Failed to open file to create PNG!\n");
|
||||
NS_WARNING("Failed to open file to create PNG!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
+413
-290
File diff suppressed because it is too large
Load Diff
@@ -229,9 +229,8 @@ public:
|
||||
|
||||
#ifdef CAIRO_HAS_DWRITE_FONT
|
||||
IDWriteFactory *GetDWriteFactory() { return mDWriteFactory; }
|
||||
inline bool DWriteEnabled() { return mUseDirectWrite; }
|
||||
inline bool DWriteEnabled() { return !!mDWriteFactory; }
|
||||
inline DWRITE_MEASURING_MODE DWriteMeasuringMode() { return mMeasuringMode; }
|
||||
IDWriteTextAnalyzer *GetDWriteAnalyzer() { return mDWriteAnalyzer; }
|
||||
|
||||
IDWriteRenderingParams *GetRenderingParams(TextRenderingMode aRenderMode)
|
||||
{ return mRenderingParams[aRenderMode]; }
|
||||
@@ -263,17 +262,20 @@ public:
|
||||
}
|
||||
bool SupportsApzTouchInput() const override;
|
||||
|
||||
// Recreate devices as needed for a device reset. Returns true if a device
|
||||
// reset occurred.
|
||||
bool HandleDeviceReset();
|
||||
void UpdateBackendPrefs();
|
||||
|
||||
// Return the diagnostic status of DirectX initialization. If
|
||||
// initialization has not been attempted, this returns
|
||||
// FeatureStatus::Unused.
|
||||
mozilla::gfx::FeatureStatus GetD3D11Status() const {
|
||||
return mD3D11Status;
|
||||
}
|
||||
mozilla::gfx::FeatureStatus GetD2DStatus() const {
|
||||
return mD2DStatus;
|
||||
}
|
||||
mozilla::gfx::FeatureStatus GetD3D11Status() const;
|
||||
mozilla::gfx::FeatureStatus GetD2DStatus() const;
|
||||
mozilla::gfx::FeatureStatus GetD2D1Status() const;
|
||||
unsigned GetD3D11Version();
|
||||
mozilla::gfx::FeatureStatus GetD2D1Status();
|
||||
|
||||
void TestDeviceReset(DeviceResetReason aReason) override;
|
||||
|
||||
virtual already_AddRefed<mozilla::gfx::VsyncSource> CreateHardwareVsyncSource() override;
|
||||
static mozilla::Atomic<size_t> sD3D11MemoryUsed;
|
||||
@@ -286,6 +288,7 @@ protected:
|
||||
return true;
|
||||
}
|
||||
void GetAcceleratedCompositorBackends(nsTArray<mozilla::layers::LayersBackend>& aBackends);
|
||||
virtual void GetPlatformCMSOutputProfile(void* &mem, size_t &size);
|
||||
|
||||
protected:
|
||||
RenderMode mRenderMode;
|
||||
@@ -296,34 +299,25 @@ protected:
|
||||
private:
|
||||
void Init();
|
||||
|
||||
void InitD3D11Devices();
|
||||
void InitializeDevices();
|
||||
void InitializeD3D11();
|
||||
void InitializeD2D();
|
||||
bool InitializeD2D1();
|
||||
bool InitDWriteSupport();
|
||||
|
||||
// Used by InitD3D11Devices().
|
||||
enum class D3D11Status {
|
||||
Ok,
|
||||
TryWARP,
|
||||
ForceWARP,
|
||||
Blocked
|
||||
};
|
||||
D3D11Status CheckD3D11Support();
|
||||
bool AttemptD3D11DeviceCreation(const nsTArray<D3D_FEATURE_LEVEL>& aFeatureLevels);
|
||||
bool AttemptWARPDeviceCreation(const nsTArray<D3D_FEATURE_LEVEL>& aFeatureLevels);
|
||||
bool AttemptD3D11ImageBridgeDeviceCreation(const nsTArray<D3D_FEATURE_LEVEL>& aFeatureLevels);
|
||||
bool AttemptD3D11ContentDeviceCreation(const nsTArray<D3D_FEATURE_LEVEL>& aFeatureLevels);
|
||||
void DisableD2D();
|
||||
|
||||
// Used by UpdateRenderMode().
|
||||
mozilla::gfx::FeatureStatus InitD2DSupport();
|
||||
void InitDWriteSupport();
|
||||
mozilla::gfx::FeatureStatus CheckD3D11Support(bool* aCanUseHardware);
|
||||
void AttemptD3D11DeviceCreation();
|
||||
void AttemptWARPDeviceCreation();
|
||||
void AttemptD3D11ImageBridgeDeviceCreation();
|
||||
bool AttemptD3D11ContentDeviceCreation();
|
||||
|
||||
IDXGIAdapter1 *GetDXGIAdapter();
|
||||
bool IsDeviceReset(HRESULT hr, DeviceResetReason* aReason);
|
||||
|
||||
bool mUseDirectWrite;
|
||||
bool mUsingGDIFonts;
|
||||
|
||||
#ifdef CAIRO_HAS_DWRITE_FONT
|
||||
nsRefPtr<IDWriteFactory> mDWriteFactory;
|
||||
nsRefPtr<IDWriteTextAnalyzer> mDWriteAnalyzer;
|
||||
nsRefPtr<IDWriteRenderingParams> mRenderingParams[TEXT_RENDERING_COUNT];
|
||||
DWRITE_MEASURING_MODE mMeasuringMode;
|
||||
#endif
|
||||
@@ -333,17 +327,21 @@ private:
|
||||
mozilla::RefPtr<ID3D11Device> mD3D11Device;
|
||||
mozilla::RefPtr<ID3D11Device> mD3D11ContentDevice;
|
||||
mozilla::RefPtr<ID3D11Device> mD3D11ImageBridgeDevice;
|
||||
bool mD3D11DeviceInitialized;
|
||||
mozilla::RefPtr<mozilla::layers::ReadbackManagerD3D11> mD3D11ReadbackManager;
|
||||
bool mIsWARP;
|
||||
bool mHasDeviceReset;
|
||||
bool mHasFakeDeviceReset;
|
||||
bool mDoesD3D11TextureSharingWork;
|
||||
DeviceResetReason mDeviceResetReason;
|
||||
|
||||
// These should not be accessed directly. Use the Get[Feature]Status
|
||||
// accessors instead.
|
||||
mozilla::gfx::FeatureStatus mAcceleration;
|
||||
mozilla::gfx::FeatureStatus mD3D11Status;
|
||||
mozilla::gfx::FeatureStatus mD2DStatus;
|
||||
mozilla::gfx::FeatureStatus mD2D1Status;
|
||||
|
||||
virtual void GetPlatformCMSOutputProfile(void* &mem, size_t &size);
|
||||
nsTArray<D3D_FEATURE_LEVEL> mFeatureLevels;
|
||||
};
|
||||
|
||||
#endif /* GFX_WINDOWS_PLATFORM_H */
|
||||
|
||||
@@ -859,7 +859,7 @@ CreateVsyncRefreshTimer()
|
||||
return;
|
||||
}
|
||||
|
||||
NS_WARNING("Enabling vsync refresh driver\n");
|
||||
NS_WARNING("Enabling vsync refresh driver");
|
||||
|
||||
if (XRE_IsParentProcess()) {
|
||||
// Make sure all vsync systems are ready.
|
||||
|
||||
Reference in New Issue
Block a user