diff -r 000000000000 -r 6474c204b198 content/media/wmf/DXVA2Manager.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/content/media/wmf/DXVA2Manager.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,210 @@ +/* -*- 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 "DXVA2Manager.h" +#include "nsThreadUtils.h" +#include "ImageContainer.h" +#include "D3D9SurfaceImage.h" +#include "mozilla/Preferences.h" + +namespace mozilla { + +using layers::Image; +using layers::ImageContainer; +using layers::D3D9SurfaceImage; + +class D3D9DXVA2Manager : public DXVA2Manager +{ +public: + D3D9DXVA2Manager(); + virtual ~D3D9DXVA2Manager(); + + HRESULT Init(); + + IUnknown* GetDXVADeviceManager() MOZ_OVERRIDE; + + // Copies a region (aRegion) of the video frame stored in aVideoSample + // into an image which is returned by aOutImage. + HRESULT CopyToImage(IMFSample* aVideoSample, + const nsIntRect& aRegion, + ImageContainer* aContainer, + Image** aOutImage) MOZ_OVERRIDE; + +private: + nsRefPtr mD3D9; + nsRefPtr mDevice; + nsRefPtr mDeviceManager; + UINT32 mResetToken; +}; + +D3D9DXVA2Manager::D3D9DXVA2Manager() + : mResetToken(0) +{ + MOZ_COUNT_CTOR(D3D9DXVA2Manager); + MOZ_ASSERT(NS_IsMainThread()); +} + +D3D9DXVA2Manager::~D3D9DXVA2Manager() +{ + MOZ_COUNT_DTOR(D3D9DXVA2Manager); + MOZ_ASSERT(NS_IsMainThread()); +} + +IUnknown* +D3D9DXVA2Manager::GetDXVADeviceManager() +{ + MutexAutoLock lock(mLock); + return mDeviceManager; +} + +HRESULT +D3D9DXVA2Manager::Init() +{ + MOZ_ASSERT(NS_IsMainThread()); + + // Create D3D9Ex. + HMODULE d3d9lib = LoadLibraryW(L"d3d9.dll"); + NS_ENSURE_TRUE(d3d9lib, E_FAIL); + decltype(Direct3DCreate9Ex)* d3d9Create = + (decltype(Direct3DCreate9Ex)*) GetProcAddress(d3d9lib, "Direct3DCreate9Ex"); + nsRefPtr d3d9Ex; + HRESULT hr = d3d9Create(D3D_SDK_VERSION, getter_AddRefs(d3d9Ex)); + if (!d3d9Ex) { + NS_WARNING("Direct3DCreate9 failed"); + return E_FAIL; + } + + // Ensure we can do the YCbCr->RGB conversion in StretchRect. + // Fail if we can't. + hr = d3d9Ex->CheckDeviceFormatConversion(D3DADAPTER_DEFAULT, + D3DDEVTYPE_HAL, + (D3DFORMAT)MAKEFOURCC('N','V','1','2'), + D3DFMT_X8R8G8B8); + NS_ENSURE_TRUE(SUCCEEDED(hr), hr); + + // Create D3D9DeviceEx. + D3DPRESENT_PARAMETERS params = {0}; + params.BackBufferWidth = 1; + params.BackBufferHeight = 1; + params.BackBufferFormat = D3DFMT_UNKNOWN; + params.BackBufferCount = 1; + params.SwapEffect = D3DSWAPEFFECT_DISCARD; + params.hDeviceWindow = ::GetShellWindow(); + params.Windowed = TRUE; + params.Flags = D3DPRESENTFLAG_VIDEO; + + nsRefPtr device; + hr = d3d9Ex->CreateDeviceEx(D3DADAPTER_DEFAULT, + D3DDEVTYPE_HAL, + ::GetShellWindow(), + D3DCREATE_FPU_PRESERVE | + D3DCREATE_MULTITHREADED | + D3DCREATE_MIXED_VERTEXPROCESSING, + ¶ms, + nullptr, + getter_AddRefs(device)); + NS_ENSURE_TRUE(SUCCEEDED(hr), hr); + + // Ensure we can create queries to synchronize operations between devices. + // Without this, when we make a copy of the frame in order to share it with + // another device, we can't be sure that the copy has finished before the + // other device starts using it. + nsRefPtr query; + + hr = device->CreateQuery(D3DQUERYTYPE_EVENT, getter_AddRefs(query)); + NS_ENSURE_TRUE(SUCCEEDED(hr), hr); + + // Create and initialize IDirect3DDeviceManager9. + UINT resetToken = 0; + nsRefPtr deviceManager; + + hr = wmf::DXVA2CreateDirect3DDeviceManager9(&resetToken, + getter_AddRefs(deviceManager)); + NS_ENSURE_TRUE(SUCCEEDED(hr), hr); + hr = deviceManager->ResetDevice(device, resetToken); + NS_ENSURE_TRUE(SUCCEEDED(hr), hr); + + mResetToken = resetToken; + mD3D9 = d3d9Ex; + mDevice = device; + mDeviceManager = deviceManager; + + return S_OK; +} + +HRESULT +D3D9DXVA2Manager::CopyToImage(IMFSample* aSample, + const nsIntRect& aRegion, + ImageContainer* aImageContainer, + Image** aOutImage) +{ + nsRefPtr buffer; + HRESULT hr = aSample->GetBufferByIndex(0, getter_AddRefs(buffer)); + NS_ENSURE_TRUE(SUCCEEDED(hr), hr); + + nsRefPtr surface; + hr = wmf::MFGetService(buffer, + MR_BUFFER_SERVICE, + IID_IDirect3DSurface9, + getter_AddRefs(surface)); + NS_ENSURE_TRUE(SUCCEEDED(hr), hr); + + nsRefPtr image = aImageContainer->CreateImage(ImageFormat::D3D9_RGB32_TEXTURE); + NS_ENSURE_TRUE(image, E_FAIL); + NS_ASSERTION(image->GetFormat() == ImageFormat::D3D9_RGB32_TEXTURE, + "Wrong format?"); + + D3D9SurfaceImage* videoImage = static_cast(image.get()); + hr = videoImage->SetData(D3D9SurfaceImage::Data(surface, aRegion)); + + image.forget(aOutImage); + + return S_OK; +} + +// Count of the number of DXVAManager's we've created. This is also the +// number of videos we're decoding with DXVA. Use on main thread only. +static uint32_t sDXVAVideosCount = 0; + +/* static */ +DXVA2Manager* +DXVA2Manager::Create() +{ + MOZ_ASSERT(NS_IsMainThread()); + HRESULT hr; + + // DXVA processing takes up a lot of GPU resources, so limit the number of + // videos we use DXVA with at any one time. + const uint32_t dxvaLimit = + Preferences::GetInt("media.windows-media-foundation.max-dxva-videos", 8); + if (sDXVAVideosCount == dxvaLimit) { + return nullptr; + } + + nsAutoPtr d3d9Manager(new D3D9DXVA2Manager()); + hr = d3d9Manager->Init(); + if (SUCCEEDED(hr)) { + return d3d9Manager.forget(); + } + + // No hardware accelerated video decoding. :( + return nullptr; +} + +DXVA2Manager::DXVA2Manager() + : mLock("DXVA2Manager") +{ + MOZ_ASSERT(NS_IsMainThread()); + ++sDXVAVideosCount; +} + +DXVA2Manager::~DXVA2Manager() +{ + MOZ_ASSERT(NS_IsMainThread()); + --sDXVAVideosCount; +} + +} // namespace mozilla