|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */ |
|
3 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 #include "DXVA2Manager.h" |
|
8 #include "nsThreadUtils.h" |
|
9 #include "ImageContainer.h" |
|
10 #include "D3D9SurfaceImage.h" |
|
11 #include "mozilla/Preferences.h" |
|
12 |
|
13 namespace mozilla { |
|
14 |
|
15 using layers::Image; |
|
16 using layers::ImageContainer; |
|
17 using layers::D3D9SurfaceImage; |
|
18 |
|
19 class D3D9DXVA2Manager : public DXVA2Manager |
|
20 { |
|
21 public: |
|
22 D3D9DXVA2Manager(); |
|
23 virtual ~D3D9DXVA2Manager(); |
|
24 |
|
25 HRESULT Init(); |
|
26 |
|
27 IUnknown* GetDXVADeviceManager() MOZ_OVERRIDE; |
|
28 |
|
29 // Copies a region (aRegion) of the video frame stored in aVideoSample |
|
30 // into an image which is returned by aOutImage. |
|
31 HRESULT CopyToImage(IMFSample* aVideoSample, |
|
32 const nsIntRect& aRegion, |
|
33 ImageContainer* aContainer, |
|
34 Image** aOutImage) MOZ_OVERRIDE; |
|
35 |
|
36 private: |
|
37 nsRefPtr<IDirect3D9Ex> mD3D9; |
|
38 nsRefPtr<IDirect3DDevice9Ex> mDevice; |
|
39 nsRefPtr<IDirect3DDeviceManager9> mDeviceManager; |
|
40 UINT32 mResetToken; |
|
41 }; |
|
42 |
|
43 D3D9DXVA2Manager::D3D9DXVA2Manager() |
|
44 : mResetToken(0) |
|
45 { |
|
46 MOZ_COUNT_CTOR(D3D9DXVA2Manager); |
|
47 MOZ_ASSERT(NS_IsMainThread()); |
|
48 } |
|
49 |
|
50 D3D9DXVA2Manager::~D3D9DXVA2Manager() |
|
51 { |
|
52 MOZ_COUNT_DTOR(D3D9DXVA2Manager); |
|
53 MOZ_ASSERT(NS_IsMainThread()); |
|
54 } |
|
55 |
|
56 IUnknown* |
|
57 D3D9DXVA2Manager::GetDXVADeviceManager() |
|
58 { |
|
59 MutexAutoLock lock(mLock); |
|
60 return mDeviceManager; |
|
61 } |
|
62 |
|
63 HRESULT |
|
64 D3D9DXVA2Manager::Init() |
|
65 { |
|
66 MOZ_ASSERT(NS_IsMainThread()); |
|
67 |
|
68 // Create D3D9Ex. |
|
69 HMODULE d3d9lib = LoadLibraryW(L"d3d9.dll"); |
|
70 NS_ENSURE_TRUE(d3d9lib, E_FAIL); |
|
71 decltype(Direct3DCreate9Ex)* d3d9Create = |
|
72 (decltype(Direct3DCreate9Ex)*) GetProcAddress(d3d9lib, "Direct3DCreate9Ex"); |
|
73 nsRefPtr<IDirect3D9Ex> d3d9Ex; |
|
74 HRESULT hr = d3d9Create(D3D_SDK_VERSION, getter_AddRefs(d3d9Ex)); |
|
75 if (!d3d9Ex) { |
|
76 NS_WARNING("Direct3DCreate9 failed"); |
|
77 return E_FAIL; |
|
78 } |
|
79 |
|
80 // Ensure we can do the YCbCr->RGB conversion in StretchRect. |
|
81 // Fail if we can't. |
|
82 hr = d3d9Ex->CheckDeviceFormatConversion(D3DADAPTER_DEFAULT, |
|
83 D3DDEVTYPE_HAL, |
|
84 (D3DFORMAT)MAKEFOURCC('N','V','1','2'), |
|
85 D3DFMT_X8R8G8B8); |
|
86 NS_ENSURE_TRUE(SUCCEEDED(hr), hr); |
|
87 |
|
88 // Create D3D9DeviceEx. |
|
89 D3DPRESENT_PARAMETERS params = {0}; |
|
90 params.BackBufferWidth = 1; |
|
91 params.BackBufferHeight = 1; |
|
92 params.BackBufferFormat = D3DFMT_UNKNOWN; |
|
93 params.BackBufferCount = 1; |
|
94 params.SwapEffect = D3DSWAPEFFECT_DISCARD; |
|
95 params.hDeviceWindow = ::GetShellWindow(); |
|
96 params.Windowed = TRUE; |
|
97 params.Flags = D3DPRESENTFLAG_VIDEO; |
|
98 |
|
99 nsRefPtr<IDirect3DDevice9Ex> device; |
|
100 hr = d3d9Ex->CreateDeviceEx(D3DADAPTER_DEFAULT, |
|
101 D3DDEVTYPE_HAL, |
|
102 ::GetShellWindow(), |
|
103 D3DCREATE_FPU_PRESERVE | |
|
104 D3DCREATE_MULTITHREADED | |
|
105 D3DCREATE_MIXED_VERTEXPROCESSING, |
|
106 ¶ms, |
|
107 nullptr, |
|
108 getter_AddRefs(device)); |
|
109 NS_ENSURE_TRUE(SUCCEEDED(hr), hr); |
|
110 |
|
111 // Ensure we can create queries to synchronize operations between devices. |
|
112 // Without this, when we make a copy of the frame in order to share it with |
|
113 // another device, we can't be sure that the copy has finished before the |
|
114 // other device starts using it. |
|
115 nsRefPtr<IDirect3DQuery9> query; |
|
116 |
|
117 hr = device->CreateQuery(D3DQUERYTYPE_EVENT, getter_AddRefs(query)); |
|
118 NS_ENSURE_TRUE(SUCCEEDED(hr), hr); |
|
119 |
|
120 // Create and initialize IDirect3DDeviceManager9. |
|
121 UINT resetToken = 0; |
|
122 nsRefPtr<IDirect3DDeviceManager9> deviceManager; |
|
123 |
|
124 hr = wmf::DXVA2CreateDirect3DDeviceManager9(&resetToken, |
|
125 getter_AddRefs(deviceManager)); |
|
126 NS_ENSURE_TRUE(SUCCEEDED(hr), hr); |
|
127 hr = deviceManager->ResetDevice(device, resetToken); |
|
128 NS_ENSURE_TRUE(SUCCEEDED(hr), hr); |
|
129 |
|
130 mResetToken = resetToken; |
|
131 mD3D9 = d3d9Ex; |
|
132 mDevice = device; |
|
133 mDeviceManager = deviceManager; |
|
134 |
|
135 return S_OK; |
|
136 } |
|
137 |
|
138 HRESULT |
|
139 D3D9DXVA2Manager::CopyToImage(IMFSample* aSample, |
|
140 const nsIntRect& aRegion, |
|
141 ImageContainer* aImageContainer, |
|
142 Image** aOutImage) |
|
143 { |
|
144 nsRefPtr<IMFMediaBuffer> buffer; |
|
145 HRESULT hr = aSample->GetBufferByIndex(0, getter_AddRefs(buffer)); |
|
146 NS_ENSURE_TRUE(SUCCEEDED(hr), hr); |
|
147 |
|
148 nsRefPtr<IDirect3DSurface9> surface; |
|
149 hr = wmf::MFGetService(buffer, |
|
150 MR_BUFFER_SERVICE, |
|
151 IID_IDirect3DSurface9, |
|
152 getter_AddRefs(surface)); |
|
153 NS_ENSURE_TRUE(SUCCEEDED(hr), hr); |
|
154 |
|
155 nsRefPtr<Image> image = aImageContainer->CreateImage(ImageFormat::D3D9_RGB32_TEXTURE); |
|
156 NS_ENSURE_TRUE(image, E_FAIL); |
|
157 NS_ASSERTION(image->GetFormat() == ImageFormat::D3D9_RGB32_TEXTURE, |
|
158 "Wrong format?"); |
|
159 |
|
160 D3D9SurfaceImage* videoImage = static_cast<D3D9SurfaceImage*>(image.get()); |
|
161 hr = videoImage->SetData(D3D9SurfaceImage::Data(surface, aRegion)); |
|
162 |
|
163 image.forget(aOutImage); |
|
164 |
|
165 return S_OK; |
|
166 } |
|
167 |
|
168 // Count of the number of DXVAManager's we've created. This is also the |
|
169 // number of videos we're decoding with DXVA. Use on main thread only. |
|
170 static uint32_t sDXVAVideosCount = 0; |
|
171 |
|
172 /* static */ |
|
173 DXVA2Manager* |
|
174 DXVA2Manager::Create() |
|
175 { |
|
176 MOZ_ASSERT(NS_IsMainThread()); |
|
177 HRESULT hr; |
|
178 |
|
179 // DXVA processing takes up a lot of GPU resources, so limit the number of |
|
180 // videos we use DXVA with at any one time. |
|
181 const uint32_t dxvaLimit = |
|
182 Preferences::GetInt("media.windows-media-foundation.max-dxva-videos", 8); |
|
183 if (sDXVAVideosCount == dxvaLimit) { |
|
184 return nullptr; |
|
185 } |
|
186 |
|
187 nsAutoPtr<D3D9DXVA2Manager> d3d9Manager(new D3D9DXVA2Manager()); |
|
188 hr = d3d9Manager->Init(); |
|
189 if (SUCCEEDED(hr)) { |
|
190 return d3d9Manager.forget(); |
|
191 } |
|
192 |
|
193 // No hardware accelerated video decoding. :( |
|
194 return nullptr; |
|
195 } |
|
196 |
|
197 DXVA2Manager::DXVA2Manager() |
|
198 : mLock("DXVA2Manager") |
|
199 { |
|
200 MOZ_ASSERT(NS_IsMainThread()); |
|
201 ++sDXVAVideosCount; |
|
202 } |
|
203 |
|
204 DXVA2Manager::~DXVA2Manager() |
|
205 { |
|
206 MOZ_ASSERT(NS_IsMainThread()); |
|
207 --sDXVAVideosCount; |
|
208 } |
|
209 |
|
210 } // namespace mozilla |