michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim:set ts=2 sw=2 sts=2 et cindent: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "DXVA2Manager.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "ImageContainer.h" michael@0: #include "D3D9SurfaceImage.h" michael@0: #include "mozilla/Preferences.h" michael@0: michael@0: namespace mozilla { michael@0: michael@0: using layers::Image; michael@0: using layers::ImageContainer; michael@0: using layers::D3D9SurfaceImage; michael@0: michael@0: class D3D9DXVA2Manager : public DXVA2Manager michael@0: { michael@0: public: michael@0: D3D9DXVA2Manager(); michael@0: virtual ~D3D9DXVA2Manager(); michael@0: michael@0: HRESULT Init(); michael@0: michael@0: IUnknown* GetDXVADeviceManager() MOZ_OVERRIDE; michael@0: michael@0: // Copies a region (aRegion) of the video frame stored in aVideoSample michael@0: // into an image which is returned by aOutImage. michael@0: HRESULT CopyToImage(IMFSample* aVideoSample, michael@0: const nsIntRect& aRegion, michael@0: ImageContainer* aContainer, michael@0: Image** aOutImage) MOZ_OVERRIDE; michael@0: michael@0: private: michael@0: nsRefPtr mD3D9; michael@0: nsRefPtr mDevice; michael@0: nsRefPtr mDeviceManager; michael@0: UINT32 mResetToken; michael@0: }; michael@0: michael@0: D3D9DXVA2Manager::D3D9DXVA2Manager() michael@0: : mResetToken(0) michael@0: { michael@0: MOZ_COUNT_CTOR(D3D9DXVA2Manager); michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: } michael@0: michael@0: D3D9DXVA2Manager::~D3D9DXVA2Manager() michael@0: { michael@0: MOZ_COUNT_DTOR(D3D9DXVA2Manager); michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: } michael@0: michael@0: IUnknown* michael@0: D3D9DXVA2Manager::GetDXVADeviceManager() michael@0: { michael@0: MutexAutoLock lock(mLock); michael@0: return mDeviceManager; michael@0: } michael@0: michael@0: HRESULT michael@0: D3D9DXVA2Manager::Init() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: // Create D3D9Ex. michael@0: HMODULE d3d9lib = LoadLibraryW(L"d3d9.dll"); michael@0: NS_ENSURE_TRUE(d3d9lib, E_FAIL); michael@0: decltype(Direct3DCreate9Ex)* d3d9Create = michael@0: (decltype(Direct3DCreate9Ex)*) GetProcAddress(d3d9lib, "Direct3DCreate9Ex"); michael@0: nsRefPtr d3d9Ex; michael@0: HRESULT hr = d3d9Create(D3D_SDK_VERSION, getter_AddRefs(d3d9Ex)); michael@0: if (!d3d9Ex) { michael@0: NS_WARNING("Direct3DCreate9 failed"); michael@0: return E_FAIL; michael@0: } michael@0: michael@0: // Ensure we can do the YCbCr->RGB conversion in StretchRect. michael@0: // Fail if we can't. michael@0: hr = d3d9Ex->CheckDeviceFormatConversion(D3DADAPTER_DEFAULT, michael@0: D3DDEVTYPE_HAL, michael@0: (D3DFORMAT)MAKEFOURCC('N','V','1','2'), michael@0: D3DFMT_X8R8G8B8); michael@0: NS_ENSURE_TRUE(SUCCEEDED(hr), hr); michael@0: michael@0: // Create D3D9DeviceEx. michael@0: D3DPRESENT_PARAMETERS params = {0}; michael@0: params.BackBufferWidth = 1; michael@0: params.BackBufferHeight = 1; michael@0: params.BackBufferFormat = D3DFMT_UNKNOWN; michael@0: params.BackBufferCount = 1; michael@0: params.SwapEffect = D3DSWAPEFFECT_DISCARD; michael@0: params.hDeviceWindow = ::GetShellWindow(); michael@0: params.Windowed = TRUE; michael@0: params.Flags = D3DPRESENTFLAG_VIDEO; michael@0: michael@0: nsRefPtr device; michael@0: hr = d3d9Ex->CreateDeviceEx(D3DADAPTER_DEFAULT, michael@0: D3DDEVTYPE_HAL, michael@0: ::GetShellWindow(), michael@0: D3DCREATE_FPU_PRESERVE | michael@0: D3DCREATE_MULTITHREADED | michael@0: D3DCREATE_MIXED_VERTEXPROCESSING, michael@0: ¶ms, michael@0: nullptr, michael@0: getter_AddRefs(device)); michael@0: NS_ENSURE_TRUE(SUCCEEDED(hr), hr); michael@0: michael@0: // Ensure we can create queries to synchronize operations between devices. michael@0: // Without this, when we make a copy of the frame in order to share it with michael@0: // another device, we can't be sure that the copy has finished before the michael@0: // other device starts using it. michael@0: nsRefPtr query; michael@0: michael@0: hr = device->CreateQuery(D3DQUERYTYPE_EVENT, getter_AddRefs(query)); michael@0: NS_ENSURE_TRUE(SUCCEEDED(hr), hr); michael@0: michael@0: // Create and initialize IDirect3DDeviceManager9. michael@0: UINT resetToken = 0; michael@0: nsRefPtr deviceManager; michael@0: michael@0: hr = wmf::DXVA2CreateDirect3DDeviceManager9(&resetToken, michael@0: getter_AddRefs(deviceManager)); michael@0: NS_ENSURE_TRUE(SUCCEEDED(hr), hr); michael@0: hr = deviceManager->ResetDevice(device, resetToken); michael@0: NS_ENSURE_TRUE(SUCCEEDED(hr), hr); michael@0: michael@0: mResetToken = resetToken; michael@0: mD3D9 = d3d9Ex; michael@0: mDevice = device; michael@0: mDeviceManager = deviceManager; michael@0: michael@0: return S_OK; michael@0: } michael@0: michael@0: HRESULT michael@0: D3D9DXVA2Manager::CopyToImage(IMFSample* aSample, michael@0: const nsIntRect& aRegion, michael@0: ImageContainer* aImageContainer, michael@0: Image** aOutImage) michael@0: { michael@0: nsRefPtr buffer; michael@0: HRESULT hr = aSample->GetBufferByIndex(0, getter_AddRefs(buffer)); michael@0: NS_ENSURE_TRUE(SUCCEEDED(hr), hr); michael@0: michael@0: nsRefPtr surface; michael@0: hr = wmf::MFGetService(buffer, michael@0: MR_BUFFER_SERVICE, michael@0: IID_IDirect3DSurface9, michael@0: getter_AddRefs(surface)); michael@0: NS_ENSURE_TRUE(SUCCEEDED(hr), hr); michael@0: michael@0: nsRefPtr image = aImageContainer->CreateImage(ImageFormat::D3D9_RGB32_TEXTURE); michael@0: NS_ENSURE_TRUE(image, E_FAIL); michael@0: NS_ASSERTION(image->GetFormat() == ImageFormat::D3D9_RGB32_TEXTURE, michael@0: "Wrong format?"); michael@0: michael@0: D3D9SurfaceImage* videoImage = static_cast(image.get()); michael@0: hr = videoImage->SetData(D3D9SurfaceImage::Data(surface, aRegion)); michael@0: michael@0: image.forget(aOutImage); michael@0: michael@0: return S_OK; michael@0: } michael@0: michael@0: // Count of the number of DXVAManager's we've created. This is also the michael@0: // number of videos we're decoding with DXVA. Use on main thread only. michael@0: static uint32_t sDXVAVideosCount = 0; michael@0: michael@0: /* static */ michael@0: DXVA2Manager* michael@0: DXVA2Manager::Create() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: HRESULT hr; michael@0: michael@0: // DXVA processing takes up a lot of GPU resources, so limit the number of michael@0: // videos we use DXVA with at any one time. michael@0: const uint32_t dxvaLimit = michael@0: Preferences::GetInt("media.windows-media-foundation.max-dxva-videos", 8); michael@0: if (sDXVAVideosCount == dxvaLimit) { michael@0: return nullptr; michael@0: } michael@0: michael@0: nsAutoPtr d3d9Manager(new D3D9DXVA2Manager()); michael@0: hr = d3d9Manager->Init(); michael@0: if (SUCCEEDED(hr)) { michael@0: return d3d9Manager.forget(); michael@0: } michael@0: michael@0: // No hardware accelerated video decoding. :( michael@0: return nullptr; michael@0: } michael@0: michael@0: DXVA2Manager::DXVA2Manager() michael@0: : mLock("DXVA2Manager") michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: ++sDXVAVideosCount; michael@0: } michael@0: michael@0: DXVA2Manager::~DXVA2Manager() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: --sDXVAVideosCount; michael@0: } michael@0: michael@0: } // namespace mozilla