OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "media/gpu/dxva_video_decode_accelerator_win.h" | 5 #include "media/gpu/dxva_video_decode_accelerator_win.h" |
6 | 6 |
7 #include <memory> | 7 #include <memory> |
8 | 8 |
9 #if !defined(OS_WIN) | 9 #if !defined(OS_WIN) |
10 #error This file should only be built on Windows. | 10 #error This file should only be built on Windows. |
(...skipping 20 matching lines...) Expand all Loading... |
31 #include "base/macros.h" | 31 #include "base/macros.h" |
32 #include "base/memory/shared_memory.h" | 32 #include "base/memory/shared_memory.h" |
33 #include "base/path_service.h" | 33 #include "base/path_service.h" |
34 #include "base/single_thread_task_runner.h" | 34 #include "base/single_thread_task_runner.h" |
35 #include "base/stl_util.h" | 35 #include "base/stl_util.h" |
36 #include "base/threading/thread_task_runner_handle.h" | 36 #include "base/threading/thread_task_runner_handle.h" |
37 #include "base/trace_event/trace_event.h" | 37 #include "base/trace_event/trace_event.h" |
38 #include "base/win/windows_version.h" | 38 #include "base/win/windows_version.h" |
39 #include "build/build_config.h" | 39 #include "build/build_config.h" |
40 #include "gpu/command_buffer/service/gpu_preferences.h" | 40 #include "gpu/command_buffer/service/gpu_preferences.h" |
| 41 #include "media/base/win/mf_helpers.h" |
41 #include "media/base/win/mf_initializer.h" | 42 #include "media/base/win/mf_initializer.h" |
42 #include "media/gpu/dxva_picture_buffer_win.h" | 43 #include "media/gpu/dxva_picture_buffer_win.h" |
43 #include "media/video/video_decode_accelerator.h" | 44 #include "media/video/video_decode_accelerator.h" |
44 #include "third_party/angle/include/EGL/egl.h" | 45 #include "third_party/angle/include/EGL/egl.h" |
45 #include "third_party/angle/include/EGL/eglext.h" | 46 #include "third_party/angle/include/EGL/eglext.h" |
46 #include "ui/gl/gl_bindings.h" | 47 #include "ui/gl/gl_bindings.h" |
47 #include "ui/gl/gl_context.h" | 48 #include "ui/gl/gl_context.h" |
48 #include "ui/gl/gl_fence.h" | 49 #include "ui/gl/gl_fence.h" |
49 #include "ui/gl/gl_surface_egl.h" | 50 #include "ui/gl/gl_surface_egl.h" |
50 | 51 |
(...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
177 0x9990, 0x9991, 0x9992, 0x9993, 0x9994, 0x9995, 0x9996, 0x9997, 0x9998, | 178 0x9990, 0x9991, 0x9992, 0x9993, 0x9994, 0x9995, 0x9996, 0x9997, 0x9998, |
178 0x9999, 0x999a, 0x999b, 0x999c, 0x999d, 0x99a0, 0x99a2, 0x99a4, | 179 0x9999, 0x999a, 0x999b, 0x999c, 0x999d, 0x99a0, 0x99a2, 0x99a4, |
179 }; | 180 }; |
180 | 181 |
181 // Legacy Intel GPUs (Second generation) which have trouble with resolutions | 182 // Legacy Intel GPUs (Second generation) which have trouble with resolutions |
182 // higher than 1920 x 1088 | 183 // higher than 1920 x 1088 |
183 static const DWORD g_IntelLegacyGPUList[] = { | 184 static const DWORD g_IntelLegacyGPUList[] = { |
184 0x102, 0x106, 0x116, 0x126, | 185 0x102, 0x106, 0x116, 0x126, |
185 }; | 186 }; |
186 | 187 |
187 // Provides scoped access to the underlying buffer in an IMFMediaBuffer | |
188 // instance. | |
189 class MediaBufferScopedPointer { | |
190 public: | |
191 MediaBufferScopedPointer(IMFMediaBuffer* media_buffer) | |
192 : media_buffer_(media_buffer), | |
193 buffer_(nullptr), | |
194 max_length_(0), | |
195 current_length_(0) { | |
196 HRESULT hr = media_buffer_->Lock(&buffer_, &max_length_, ¤t_length_); | |
197 CHECK(SUCCEEDED(hr)); | |
198 } | |
199 | |
200 ~MediaBufferScopedPointer() { | |
201 HRESULT hr = media_buffer_->Unlock(); | |
202 CHECK(SUCCEEDED(hr)); | |
203 } | |
204 | |
205 uint8_t* get() { return buffer_; } | |
206 | |
207 DWORD current_length() const { return current_length_; } | |
208 | |
209 private: | |
210 base::win::ScopedComPtr<IMFMediaBuffer> media_buffer_; | |
211 uint8_t* buffer_; | |
212 DWORD max_length_; | |
213 DWORD current_length_; | |
214 | |
215 DISALLOW_COPY_AND_ASSIGN(MediaBufferScopedPointer); | |
216 }; | |
217 | |
218 } // namespace | 188 } // namespace |
219 | 189 |
220 namespace media { | 190 namespace media { |
221 | 191 |
222 static const VideoCodecProfile kSupportedProfiles[] = { | 192 static const VideoCodecProfile kSupportedProfiles[] = { |
223 H264PROFILE_BASELINE, H264PROFILE_MAIN, H264PROFILE_HIGH, | 193 H264PROFILE_BASELINE, H264PROFILE_MAIN, H264PROFILE_HIGH, |
224 VP8PROFILE_ANY, VP9PROFILE_PROFILE0, VP9PROFILE_PROFILE1, | 194 VP8PROFILE_ANY, VP9PROFILE_PROFILE0, VP9PROFILE_PROFILE1, |
225 VP9PROFILE_PROFILE2, VP9PROFILE_PROFILE3}; | 195 VP9PROFILE_PROFILE2, VP9PROFILE_PROFILE3}; |
226 | 196 |
227 CreateDXGIDeviceManager | 197 CreateDXGIDeviceManager |
228 DXVAVideoDecodeAccelerator::create_dxgi_device_manager_ = NULL; | 198 DXVAVideoDecodeAccelerator::create_dxgi_device_manager_ = NULL; |
229 | 199 |
230 #define RETURN_ON_FAILURE(result, log, ret) \ | |
231 do { \ | |
232 if (!(result)) { \ | |
233 DLOG(ERROR) << log; \ | |
234 return ret; \ | |
235 } \ | |
236 } while (0) | |
237 | |
238 #define RETURN_ON_HR_FAILURE(result, log, ret) \ | |
239 RETURN_ON_FAILURE(SUCCEEDED(result), \ | |
240 log << ", HRESULT: 0x" << std::hex << result, ret); | |
241 | |
242 #define RETURN_AND_NOTIFY_ON_FAILURE(result, log, error_code, ret) \ | |
243 do { \ | |
244 if (!(result)) { \ | |
245 DVLOG(1) << log; \ | |
246 StopOnError(error_code); \ | |
247 return ret; \ | |
248 } \ | |
249 } while (0) | |
250 | |
251 #define RETURN_AND_NOTIFY_ON_HR_FAILURE(result, log, error_code, ret) \ | |
252 RETURN_AND_NOTIFY_ON_FAILURE(SUCCEEDED(result), \ | |
253 log << ", HRESULT: 0x" << std::hex << result, \ | |
254 error_code, ret); | |
255 | |
256 enum { | 200 enum { |
257 // Maximum number of iterations we allow before aborting the attempt to flush | 201 // Maximum number of iterations we allow before aborting the attempt to flush |
258 // the batched queries to the driver and allow torn/corrupt frames to be | 202 // the batched queries to the driver and allow torn/corrupt frames to be |
259 // rendered. | 203 // rendered. |
260 kFlushDecoderSurfaceTimeoutMs = 1, | 204 kFlushDecoderSurfaceTimeoutMs = 1, |
261 // Maximum iterations where we try to flush the d3d device. | 205 // Maximum iterations where we try to flush the d3d device. |
262 kMaxIterationsForD3DFlush = 4, | 206 kMaxIterationsForD3DFlush = 4, |
263 // Maximum iterations where we try to flush the ANGLE device before reusing | 207 // Maximum iterations where we try to flush the ANGLE device before reusing |
264 // the texture. | 208 // the texture. |
265 kMaxIterationsForANGLEReuseFlush = 16, | 209 kMaxIterationsForANGLEReuseFlush = 16, |
266 // We only request 5 picture buffers from the client which are used to hold | 210 // We only request 5 picture buffers from the client which are used to hold |
267 // the decoded samples. These buffers are then reused when the client tells | 211 // the decoded samples. These buffers are then reused when the client tells |
268 // us that it is done with the buffer. | 212 // us that it is done with the buffer. |
269 kNumPictureBuffers = 5, | 213 kNumPictureBuffers = 5, |
270 // The keyed mutex should always be released before the other thread | 214 // The keyed mutex should always be released before the other thread |
271 // attempts to acquire it, so AcquireSync should always return immediately. | 215 // attempts to acquire it, so AcquireSync should always return immediately. |
272 kAcquireSyncWaitMs = 0, | 216 kAcquireSyncWaitMs = 0, |
273 }; | 217 }; |
274 | 218 |
275 static IMFSample* CreateEmptySample() { | |
276 base::win::ScopedComPtr<IMFSample> sample; | |
277 HRESULT hr = MFCreateSample(sample.Receive()); | |
278 RETURN_ON_HR_FAILURE(hr, "MFCreateSample failed", NULL); | |
279 return sample.Detach(); | |
280 } | |
281 | |
282 // Creates a Media Foundation sample with one buffer of length |buffer_length| | |
283 // on a |align|-byte boundary. Alignment must be a perfect power of 2 or 0. | |
284 static IMFSample* CreateEmptySampleWithBuffer(uint32_t buffer_length, | |
285 int align) { | |
286 CHECK_GT(buffer_length, 0U); | |
287 | |
288 base::win::ScopedComPtr<IMFSample> sample; | |
289 sample.Attach(CreateEmptySample()); | |
290 | |
291 base::win::ScopedComPtr<IMFMediaBuffer> buffer; | |
292 HRESULT hr = E_FAIL; | |
293 if (align == 0) { | |
294 // Note that MFCreateMemoryBuffer is same as MFCreateAlignedMemoryBuffer | |
295 // with the align argument being 0. | |
296 hr = MFCreateMemoryBuffer(buffer_length, buffer.Receive()); | |
297 } else { | |
298 hr = | |
299 MFCreateAlignedMemoryBuffer(buffer_length, align - 1, buffer.Receive()); | |
300 } | |
301 RETURN_ON_HR_FAILURE(hr, "Failed to create memory buffer for sample", NULL); | |
302 | |
303 hr = sample->AddBuffer(buffer.get()); | |
304 RETURN_ON_HR_FAILURE(hr, "Failed to add buffer to sample", NULL); | |
305 | |
306 buffer->SetCurrentLength(0); | |
307 return sample.Detach(); | |
308 } | |
309 | |
310 // Creates a Media Foundation sample with one buffer containing a copy of the | 219 // Creates a Media Foundation sample with one buffer containing a copy of the |
311 // given Annex B stream data. | 220 // given Annex B stream data. |
312 // If duration and sample time are not known, provide 0. | 221 // If duration and sample time are not known, provide 0. |
313 // |min_size| specifies the minimum size of the buffer (might be required by | 222 // |min_size| specifies the minimum size of the buffer (might be required by |
314 // the decoder for input). If no alignment is required, provide 0. | 223 // the decoder for input). If no alignment is required, provide 0. |
315 static IMFSample* CreateInputSample(const uint8_t* stream, | 224 static IMFSample* CreateInputSample(const uint8_t* stream, |
316 uint32_t size, | 225 uint32_t size, |
317 uint32_t min_size, | 226 uint32_t min_size, |
318 int alignment) { | 227 int alignment) { |
319 CHECK(stream); | 228 CHECK(stream); |
320 CHECK_GT(size, 0U); | 229 CHECK_GT(size, 0U); |
321 base::win::ScopedComPtr<IMFSample> sample; | 230 base::win::ScopedComPtr<IMFSample> sample; |
322 sample.Attach( | 231 sample.Attach( |
323 CreateEmptySampleWithBuffer(std::max(min_size, size), alignment)); | 232 mf::CreateEmptySampleWithBuffer(std::max(min_size, size), alignment)); |
324 RETURN_ON_FAILURE(sample.get(), "Failed to create empty sample", NULL); | 233 RETURN_ON_FAILURE(sample.get(), "Failed to create empty sample", NULL); |
325 | 234 |
326 base::win::ScopedComPtr<IMFMediaBuffer> buffer; | 235 base::win::ScopedComPtr<IMFMediaBuffer> buffer; |
327 HRESULT hr = sample->GetBufferByIndex(0, buffer.Receive()); | 236 HRESULT hr = sample->GetBufferByIndex(0, buffer.Receive()); |
328 RETURN_ON_HR_FAILURE(hr, "Failed to get buffer from sample", NULL); | 237 RETURN_ON_HR_FAILURE(hr, "Failed to get buffer from sample", NULL); |
329 | 238 |
330 DWORD max_length = 0; | 239 DWORD max_length = 0; |
331 DWORD current_length = 0; | 240 DWORD current_length = 0; |
332 uint8_t* destination = NULL; | 241 uint8_t* destination = NULL; |
333 hr = buffer->Lock(&destination, &max_length, ¤t_length); | 242 hr = buffer->Lock(&destination, &max_length, ¤t_length); |
(...skipping 2325 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2659 | 2568 |
2660 HRESULT DXVAVideoDecodeAccelerator::CheckConfigChanged(IMFSample* sample, | 2569 HRESULT DXVAVideoDecodeAccelerator::CheckConfigChanged(IMFSample* sample, |
2661 bool* config_changed) { | 2570 bool* config_changed) { |
2662 if (codec_ != kCodecH264) | 2571 if (codec_ != kCodecH264) |
2663 return S_FALSE; | 2572 return S_FALSE; |
2664 | 2573 |
2665 base::win::ScopedComPtr<IMFMediaBuffer> buffer; | 2574 base::win::ScopedComPtr<IMFMediaBuffer> buffer; |
2666 HRESULT hr = sample->GetBufferByIndex(0, buffer.Receive()); | 2575 HRESULT hr = sample->GetBufferByIndex(0, buffer.Receive()); |
2667 RETURN_ON_HR_FAILURE(hr, "Failed to get buffer from input sample", hr); | 2576 RETURN_ON_HR_FAILURE(hr, "Failed to get buffer from input sample", hr); |
2668 | 2577 |
2669 MediaBufferScopedPointer scoped_media_buffer(buffer.get()); | 2578 mf::MediaBufferScopedPointer scoped_media_buffer(buffer.get()); |
2670 | 2579 |
2671 if (!config_change_detector_->DetectConfig( | 2580 if (!config_change_detector_->DetectConfig( |
2672 scoped_media_buffer.get(), scoped_media_buffer.current_length())) { | 2581 scoped_media_buffer.get(), scoped_media_buffer.current_length())) { |
2673 RETURN_ON_HR_FAILURE(E_FAIL, "Failed to detect H.264 stream config", | 2582 RETURN_ON_HR_FAILURE(E_FAIL, "Failed to detect H.264 stream config", |
2674 E_FAIL); | 2583 E_FAIL); |
2675 } | 2584 } |
2676 *config_changed = config_change_detector_->config_changed(); | 2585 *config_changed = config_change_detector_->config_changed(); |
2677 return S_OK; | 2586 return S_OK; |
2678 } | 2587 } |
2679 | 2588 |
2680 void DXVAVideoDecodeAccelerator::ConfigChanged(const Config& config) { | 2589 void DXVAVideoDecodeAccelerator::ConfigChanged(const Config& config) { |
2681 DCHECK(main_thread_task_runner_->BelongsToCurrentThread()); | 2590 DCHECK(main_thread_task_runner_->BelongsToCurrentThread()); |
2682 | 2591 |
2683 SetState(kConfigChange); | 2592 SetState(kConfigChange); |
2684 Invalidate(); | 2593 Invalidate(); |
2685 Initialize(config_, client_); | 2594 Initialize(config_, client_); |
2686 decoder_thread_task_runner_->PostTask( | 2595 decoder_thread_task_runner_->PostTask( |
2687 FROM_HERE, | 2596 FROM_HERE, |
2688 base::Bind(&DXVAVideoDecodeAccelerator::DecodePendingInputBuffers, | 2597 base::Bind(&DXVAVideoDecodeAccelerator::DecodePendingInputBuffers, |
2689 base::Unretained(this))); | 2598 base::Unretained(this))); |
2690 } | 2599 } |
2691 | 2600 |
2692 } // namespace media | 2601 } // namespace media |
OLD | NEW |