OLD | NEW |
| (Empty) |
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 | |
3 // found in the LICENSE file. | |
4 | |
5 #ifndef CONTENT_COMMON_GPU_MEDIA_DXVA_VIDEO_DECODE_ACCELERATOR_H_ | |
6 #define CONTENT_COMMON_GPU_MEDIA_DXVA_VIDEO_DECODE_ACCELERATOR_H_ | |
7 | |
8 #include <d3d11.h> | |
9 #include <d3d9.h> | |
10 #include <initguid.h> | |
11 #include <stdint.h> | |
12 | |
13 // Work around bug in this header by disabling the relevant warning for it. | |
14 // https://connect.microsoft.com/VisualStudio/feedback/details/911260/dxva2api-h
-in-win8-sdk-triggers-c4201-with-w4 | |
15 #pragma warning(push) | |
16 #pragma warning(disable:4201) | |
17 #include <dxva2api.h> | |
18 #pragma warning(pop) | |
19 #include <mfidl.h> | |
20 | |
21 #include <list> | |
22 #include <map> | |
23 #include <memory> | |
24 #include <vector> | |
25 | |
26 #include "base/compiler_specific.h" | |
27 #include "base/macros.h" | |
28 #include "base/memory/linked_ptr.h" | |
29 #include "base/memory/weak_ptr.h" | |
30 #include "base/synchronization/lock.h" | |
31 #include "base/threading/non_thread_safe.h" | |
32 #include "base/threading/thread.h" | |
33 #include "base/win/scoped_comptr.h" | |
34 #include "content/common/content_export.h" | |
35 #include "content/common/gpu/media/gpu_video_decode_accelerator_helpers.h" | |
36 #include "media/filters/h264_parser.h" | |
37 #include "media/video/video_decode_accelerator.h" | |
38 | |
39 interface IMFSample; | |
40 interface IDirect3DSurface9; | |
41 | |
42 namespace gfx { | |
43 class GLContext; | |
44 } | |
45 | |
46 typedef HRESULT (WINAPI* CreateDXGIDeviceManager)( | |
47 UINT* reset_token, | |
48 IMFDXGIDeviceManager** device_manager); | |
49 | |
50 namespace content { | |
51 | |
52 // Provides functionality to detect H.264 stream configuration changes. | |
53 // TODO(ananta) | |
54 // Move this to a common place so that all VDA's can use this. | |
55 class H264ConfigChangeDetector { | |
56 public: | |
57 H264ConfigChangeDetector(); | |
58 ~H264ConfigChangeDetector(); | |
59 | |
60 // Detects stream configuration changes. | |
61 // Returns false on failure. | |
62 bool DetectConfig(const uint8_t* stream, unsigned int size); | |
63 | |
64 bool config_changed() const { | |
65 return config_changed_; | |
66 } | |
67 | |
68 private: | |
69 // These fields are used to track the SPS/PPS in the H.264 bitstream and | |
70 // are eventually compared against the SPS/PPS in the bitstream to detect | |
71 // a change. | |
72 int last_sps_id_; | |
73 std::vector<uint8_t> last_sps_; | |
74 int last_pps_id_; | |
75 std::vector<uint8_t> last_pps_; | |
76 // Set to true if we detect a stream configuration change. | |
77 bool config_changed_; | |
78 // We want to indicate configuration changes only after we see IDR slices. | |
79 // This flag tracks that we potentially have a configuration change which | |
80 // we want to honor after we see an IDR slice. | |
81 bool pending_config_changed_; | |
82 | |
83 std::unique_ptr<media::H264Parser> parser_; | |
84 | |
85 DISALLOW_COPY_AND_ASSIGN(H264ConfigChangeDetector); | |
86 }; | |
87 | |
88 | |
89 // Class to provide a DXVA 2.0 based accelerator using the Microsoft Media | |
90 // foundation APIs via the VideoDecodeAccelerator interface. | |
91 // This class lives on a single thread and DCHECKs that it is never accessed | |
92 // from any other. | |
93 class CONTENT_EXPORT DXVAVideoDecodeAccelerator | |
94 : public media::VideoDecodeAccelerator { | |
95 public: | |
96 enum State { | |
97 kUninitialized, // un-initialized. | |
98 kNormal, // normal playing state. | |
99 kResetting, // upon received Reset(), before ResetDone() | |
100 kStopped, // upon output EOS received. | |
101 kFlushing, // upon flush request received. | |
102 kConfigChange, // stream configuration change detected. | |
103 }; | |
104 | |
105 // Does not take ownership of |client| which must outlive |*this|. | |
106 DXVAVideoDecodeAccelerator( | |
107 const GetGLContextCallback& get_gl_context_cb, | |
108 const MakeGLContextCurrentCallback& make_context_current_cb, | |
109 bool enable_accelerated_vpx_decode); | |
110 ~DXVAVideoDecodeAccelerator() override; | |
111 | |
112 // media::VideoDecodeAccelerator implementation. | |
113 bool Initialize(const Config& config, Client* client) override; | |
114 void Decode(const media::BitstreamBuffer& bitstream_buffer) override; | |
115 void AssignPictureBuffers( | |
116 const std::vector<media::PictureBuffer>& buffers) override; | |
117 void ReusePictureBuffer(int32_t picture_buffer_id) override; | |
118 void Flush() override; | |
119 void Reset() override; | |
120 void Destroy() override; | |
121 bool TryToSetupDecodeOnSeparateThread( | |
122 const base::WeakPtr<Client>& decode_client, | |
123 const scoped_refptr<base::SingleThreadTaskRunner>& decode_task_runner) | |
124 override; | |
125 GLenum GetSurfaceInternalFormat() const override; | |
126 | |
127 static media::VideoDecodeAccelerator::SupportedProfiles | |
128 GetSupportedProfiles(); | |
129 | |
130 // Preload dlls required for decoding. | |
131 static void PreSandboxInitialization(); | |
132 | |
133 private: | |
134 typedef void* EGLConfig; | |
135 typedef void* EGLSurface; | |
136 | |
137 // Returns the minimum resolution for the |profile| passed in. | |
138 static std::pair<int, int> GetMinResolution( | |
139 const media::VideoCodecProfile profile); | |
140 | |
141 // Returns the maximum resolution for the |profile| passed in. | |
142 static std::pair<int, int> GetMaxResolution( | |
143 const media::VideoCodecProfile profile); | |
144 | |
145 // Returns the maximum resolution for H264 video. | |
146 static std::pair<int, int> GetMaxH264Resolution(); | |
147 | |
148 // Certain AMD GPU drivers like R600, R700, Evergreen and Cayman and | |
149 // some second generation Intel GPU drivers crash if we create a video | |
150 // device with a resolution higher then 1920 x 1088. This function | |
151 // checks if the GPU is in this list and if yes returns true. | |
152 static bool IsLegacyGPU(ID3D11Device* device); | |
153 | |
154 // Creates and initializes an instance of the D3D device and the | |
155 // corresponding device manager. The device manager instance is eventually | |
156 // passed to the IMFTransform interface implemented by the decoder. | |
157 bool CreateD3DDevManager(); | |
158 | |
159 // Creates and initializes an instance of the DX11 device and the | |
160 // corresponding device manager. The device manager instance is eventually | |
161 // passed to the IMFTransform interface implemented by the decoder. | |
162 bool CreateDX11DevManager(); | |
163 | |
164 // Creates, initializes and sets the media codec types for the decoder. | |
165 bool InitDecoder(media::VideoCodecProfile profile); | |
166 | |
167 // Validates whether the decoder supports hardware video acceleration. | |
168 bool CheckDecoderDxvaSupport(); | |
169 | |
170 // Returns information about the input and output streams. This includes | |
171 // alignment information, decoder support flags, minimum sample size, etc. | |
172 bool GetStreamsInfoAndBufferReqs(); | |
173 | |
174 // Registers the input and output media types on the decoder. This includes | |
175 // the expected input and output formats. | |
176 bool SetDecoderMediaTypes(); | |
177 | |
178 // Registers the input media type for the decoder. | |
179 bool SetDecoderInputMediaType(); | |
180 | |
181 // Registers the output media type for the decoder. | |
182 bool SetDecoderOutputMediaType(const GUID& subtype); | |
183 | |
184 // Passes a command message to the decoder. This includes commands like | |
185 // start of stream, end of stream, flush, drain the decoder, etc. | |
186 bool SendMFTMessage(MFT_MESSAGE_TYPE msg, int32_t param); | |
187 | |
188 // The bulk of the decoding happens here. This function handles errors, | |
189 // format changes and processes decoded output. | |
190 void DoDecode(); | |
191 | |
192 // Invoked when we have a valid decoded output sample. Retrieves the D3D | |
193 // surface and maintains a copy of it which is passed eventually to the | |
194 // client when we have a picture buffer to copy the surface contents to. | |
195 bool ProcessOutputSample(IMFSample* sample); | |
196 | |
197 // Processes pending output samples by copying them to available picture | |
198 // slots. | |
199 void ProcessPendingSamples(); | |
200 | |
201 // Helper function to notify the accelerator client about the error. | |
202 void StopOnError(media::VideoDecodeAccelerator::Error error); | |
203 | |
204 // Transitions the decoder to the uninitialized state. The decoder will stop | |
205 // accepting requests in this state. | |
206 void Invalidate(); | |
207 | |
208 // Notifies the client that the input buffer identifed by input_buffer_id has | |
209 // been processed. | |
210 void NotifyInputBufferRead(int input_buffer_id); | |
211 | |
212 // Notifies the client that the decoder was flushed. | |
213 void NotifyFlushDone(); | |
214 | |
215 // Notifies the client that the decoder was reset. | |
216 void NotifyResetDone(); | |
217 | |
218 // Requests picture buffers from the client. | |
219 void RequestPictureBuffers(int width, int height); | |
220 | |
221 // Notifies the client about the availability of a picture. | |
222 void NotifyPictureReady(int picture_buffer_id, | |
223 int input_buffer_id); | |
224 | |
225 // Sends pending input buffer processed acks to the client if we don't have | |
226 // output samples waiting to be processed. | |
227 void NotifyInputBuffersDropped(); | |
228 | |
229 // Decodes pending input buffers. | |
230 void DecodePendingInputBuffers(); | |
231 | |
232 // Helper for handling the Flush operation. | |
233 void FlushInternal(); | |
234 | |
235 // Helper for handling the Decode operation. | |
236 void DecodeInternal(const base::win::ScopedComPtr<IMFSample>& input_sample); | |
237 | |
238 // Handles mid stream resolution changes. | |
239 void HandleResolutionChanged(int width, int height); | |
240 | |
241 struct DXVAPictureBuffer; | |
242 typedef std::map<int32_t, linked_ptr<DXVAPictureBuffer>> OutputBuffers; | |
243 | |
244 // Tells the client to dismiss the stale picture buffers passed in. | |
245 void DismissStaleBuffers(bool force); | |
246 | |
247 // Called after the client indicates we can recycle a stale picture buffer. | |
248 void DeferredDismissStaleBuffer(int32_t picture_buffer_id); | |
249 | |
250 // Sets the state of the decoder. Called from the main thread and the decoder | |
251 // thread. The state is changed on the main thread. | |
252 void SetState(State state); | |
253 | |
254 // Gets the state of the decoder. Can be called from the main thread and | |
255 // the decoder thread. Thread safe. | |
256 State GetState(); | |
257 | |
258 // Starts the thread used for decoding. | |
259 void StartDecoderThread(); | |
260 | |
261 // Returns if we have output samples waiting to be processed. We only | |
262 // allow one output sample to be present in the output queue at any given | |
263 // time. | |
264 bool OutputSamplesPresent(); | |
265 | |
266 // Copies the source surface |src_surface| to the destination |dest_surface|. | |
267 // The copying is done on the decoder thread. | |
268 void CopySurface(IDirect3DSurface9* src_surface, | |
269 IDirect3DSurface9* dest_surface, | |
270 int picture_buffer_id, | |
271 int input_buffer_id); | |
272 | |
273 // This is a notification that the source surface |src_surface| was copied to | |
274 // the destination |dest_surface|. Received on the main thread. | |
275 void CopySurfaceComplete(IDirect3DSurface9* src_surface, | |
276 IDirect3DSurface9* dest_surface, | |
277 int picture_buffer_id, | |
278 int input_buffer_id); | |
279 | |
280 // Copies the source texture |src_texture| to the destination |dest_texture|. | |
281 // The copying is done on the decoder thread. The |video_frame| parameter | |
282 // is the sample containing the frame to be copied. | |
283 void CopyTexture(ID3D11Texture2D* src_texture, | |
284 ID3D11Texture2D* dest_texture, | |
285 base::win::ScopedComPtr<IDXGIKeyedMutex> dest_keyed_mutex, | |
286 uint64_t keyed_mutex_value, | |
287 IMFSample* video_frame, | |
288 int picture_buffer_id, | |
289 int input_buffer_id); | |
290 | |
291 // Flushes the decoder device to ensure that the decoded surface is copied | |
292 // to the target surface. |iterations| helps to maintain an upper limit on | |
293 // the number of times we try to complete the flush operation. | |
294 void FlushDecoder(int iterations, | |
295 IDirect3DSurface9* src_surface, | |
296 IDirect3DSurface9* dest_surface, | |
297 int picture_buffer_id, | |
298 int input_buffer_id); | |
299 | |
300 // Polls to wait for GPU commands to be finished on the picture buffer | |
301 // before reusing it. | |
302 void WaitForOutputBuffer(int32_t picture_buffer_id, int count); | |
303 | |
304 // Initializes the DX11 Video format converter media types. | |
305 // Returns true on success. | |
306 bool InitializeDX11VideoFormatConverterMediaType(int width, int height); | |
307 | |
308 // Returns the output video frame dimensions (width, height). | |
309 // |sample| :- This is the output sample containing the video frame. | |
310 // |width| :- The width is returned here. | |
311 // |height| :- The height is returned here. | |
312 // Returns true on success. | |
313 bool GetVideoFrameDimensions(IMFSample* sample, int* width, int* height); | |
314 | |
315 // Sets the output type on the |transform| to the GUID identified by the | |
316 // the |output_type| parameter. The GUID can be MFVideoFormat_RGB32, | |
317 // MFVideoFormat_ARGB32, MFVideoFormat_NV12, etc. | |
318 // Additionally if the |width| and |height| parameters are non zero, then | |
319 // this function also sets the MF_MT_FRAME_SIZE attribute on the type. | |
320 // Returns true on success. | |
321 bool SetTransformOutputType(IMFTransform* transform, | |
322 const GUID& output_type, | |
323 int width, | |
324 int height); | |
325 | |
326 // Checks if the resolution, bitrate etc of the stream changed. We do this | |
327 // by keeping track of the SPS/PPS frames and if they change we assume | |
328 // that the configuration changed. | |
329 // Returns S_OK or S_FALSE on succcess. | |
330 // The |config_changed| parameter is set to true if we detect a change in the | |
331 // stream. | |
332 HRESULT CheckConfigChanged(IMFSample* sample, bool* config_changed); | |
333 | |
334 // Called when we detect a stream configuration change. We reinitialize the | |
335 // decoder here. | |
336 void ConfigChanged(const Config& config); | |
337 | |
338 // To expose client callbacks from VideoDecodeAccelerator. | |
339 media::VideoDecodeAccelerator::Client* client_; | |
340 | |
341 base::win::ScopedComPtr<IMFTransform> decoder_; | |
342 base::win::ScopedComPtr<IMFTransform> video_format_converter_mft_; | |
343 | |
344 base::win::ScopedComPtr<IDirect3D9Ex> d3d9_; | |
345 base::win::ScopedComPtr<IDirect3DDevice9Ex> d3d9_device_ex_; | |
346 base::win::ScopedComPtr<IDirect3DDeviceManager9> device_manager_; | |
347 base::win::ScopedComPtr<IDirect3DQuery9> query_; | |
348 | |
349 base::win::ScopedComPtr<ID3D11Device > d3d11_device_; | |
350 base::win::ScopedComPtr<IMFDXGIDeviceManager> d3d11_device_manager_; | |
351 base::win::ScopedComPtr<ID3D10Multithread> multi_threaded_; | |
352 base::win::ScopedComPtr<ID3D11DeviceContext> d3d11_device_context_; | |
353 base::win::ScopedComPtr<ID3D11Query> d3d11_query_; | |
354 | |
355 // Ideally the reset token would be a stack variable which is used while | |
356 // creating the device manager. However it seems that the device manager | |
357 // holds onto the token and attempts to access it if the underlying device | |
358 // changes. | |
359 // TODO(ananta): This needs to be verified. | |
360 uint32_t dev_manager_reset_token_; | |
361 | |
362 // Reset token for the DX11 device manager. | |
363 uint32_t dx11_dev_manager_reset_token_; | |
364 | |
365 uint32_t dx11_dev_manager_reset_token_format_conversion_; | |
366 | |
367 // The EGL config to use for decoded frames. | |
368 EGLConfig egl_config_; | |
369 | |
370 // Current state of the decoder. | |
371 volatile State state_; | |
372 | |
373 MFT_INPUT_STREAM_INFO input_stream_info_; | |
374 MFT_OUTPUT_STREAM_INFO output_stream_info_; | |
375 | |
376 // Contains information about a decoded sample. | |
377 struct PendingSampleInfo { | |
378 PendingSampleInfo(int32_t buffer_id, IMFSample* sample); | |
379 PendingSampleInfo(const PendingSampleInfo& other); | |
380 ~PendingSampleInfo(); | |
381 | |
382 int32_t input_buffer_id; | |
383 | |
384 // The target picture buffer id where the frame would be copied to. | |
385 // Defaults to -1. | |
386 int picture_buffer_id; | |
387 | |
388 base::win::ScopedComPtr<IMFSample> output_sample; | |
389 }; | |
390 | |
391 typedef std::list<PendingSampleInfo> PendingOutputSamples; | |
392 | |
393 // List of decoded output samples. Protected by |decoder_lock_|. | |
394 PendingOutputSamples pending_output_samples_; | |
395 | |
396 // This map maintains the picture buffers passed the client for decoding. | |
397 // The key is the picture buffer id. | |
398 OutputBuffers output_picture_buffers_; | |
399 | |
400 // After a resolution change there may be a few output buffers which have yet | |
401 // to be displayed so they cannot be dismissed immediately. We move them from | |
402 // |output_picture_buffers_| to this map so they may be dismissed once they | |
403 // become available. | |
404 OutputBuffers stale_output_picture_buffers_; | |
405 | |
406 // Set to true if we requested picture slots from the client. | |
407 bool pictures_requested_; | |
408 | |
409 // Counter which holds the number of input packets before a successful | |
410 // decode. | |
411 int inputs_before_decode_; | |
412 | |
413 // Set to true when the drain message is sent to the decoder during a flush | |
414 // operation. Used to ensure the message is only sent once after | |
415 // |pending_input_buffers_| is drained. Protected by |decoder_lock_|. | |
416 bool sent_drain_message_; | |
417 | |
418 // List of input samples waiting to be processed. | |
419 typedef std::list<base::win::ScopedComPtr<IMFSample>> PendingInputs; | |
420 PendingInputs pending_input_buffers_; | |
421 | |
422 // Callback to get current GLContext. | |
423 GetGLContextCallback get_gl_context_cb_; | |
424 // Callback to set the correct gl context. | |
425 MakeGLContextCurrentCallback make_context_current_cb_; | |
426 | |
427 // Which codec we are decoding with hardware acceleration. | |
428 media::VideoCodec codec_; | |
429 // Thread on which the decoder operations like passing input frames, | |
430 // getting output frames are performed. One instance of this thread | |
431 // is created per decoder instance. | |
432 base::Thread decoder_thread_; | |
433 | |
434 // Task runner to be used for posting tasks to the decoder thread. | |
435 scoped_refptr<base::SingleThreadTaskRunner> decoder_thread_task_runner_; | |
436 | |
437 // Task runner to be used for posting tasks to the main thread. | |
438 scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_; | |
439 | |
440 // Used to synchronize access between the decoder thread and the main thread. | |
441 base::Lock decoder_lock_; | |
442 | |
443 // Disallow rebinding WeakReference ownership to a different thread by | |
444 // keeping a persistent reference. This avoids problems with the | |
445 // thread safety of reaching into this class from multiple threads to | |
446 // attain a WeakPtr. | |
447 base::WeakPtr<DXVAVideoDecodeAccelerator> weak_ptr_; | |
448 | |
449 // Set to true if we are in the context of a Flush operation. Used to prevent | |
450 // multiple flush done notifications being sent out. | |
451 bool pending_flush_; | |
452 | |
453 // Defaults to false. Indicates if we should use D3D or DX11 interfaces for | |
454 // H/W decoding. | |
455 bool use_dx11_; | |
456 | |
457 // True if we should use DXGI keyed mutexes to synchronize between the two | |
458 // contexts. | |
459 bool use_keyed_mutex_; | |
460 | |
461 // Set to true if the DX11 video format converter input media types need to | |
462 // be initialized. Defaults to true. | |
463 bool dx11_video_format_converter_media_type_needs_init_; | |
464 | |
465 // Set to true if we are sharing ANGLE's device. | |
466 bool using_angle_device_; | |
467 | |
468 // Enables experimental hardware acceleration for VP8/VP9 video decoding. | |
469 const bool enable_accelerated_vpx_decode_; | |
470 | |
471 // The media foundation H.264 decoder has problems handling changes like | |
472 // resolution change, bitrate change etc. If we reinitialize the decoder | |
473 // when these changes occur then, the decoder works fine. The | |
474 // H264ConfigChangeDetector class provides functionality to check if the | |
475 // stream configuration changed. | |
476 std::unique_ptr<H264ConfigChangeDetector> config_change_detector_; | |
477 | |
478 // Contains the initialization parameters for the video. | |
479 Config config_; | |
480 | |
481 // WeakPtrFactory for posting tasks back to |this|. | |
482 base::WeakPtrFactory<DXVAVideoDecodeAccelerator> weak_this_factory_; | |
483 | |
484 // Function pointer for the MFCreateDXGIDeviceManager API. | |
485 static CreateDXGIDeviceManager create_dxgi_device_manager_; | |
486 | |
487 DISALLOW_COPY_AND_ASSIGN(DXVAVideoDecodeAccelerator); | |
488 }; | |
489 | |
490 } // namespace content | |
491 | |
492 #endif // CONTENT_COMMON_GPU_MEDIA_DXVA_VIDEO_DECODE_ACCELERATOR_H_ | |
OLD | NEW |