| // Copyright 2017 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef MEDIA_GPU_WINDOWS_D3D11_VIDEO_DECODER_H_ |
| #define MEDIA_GPU_WINDOWS_D3D11_VIDEO_DECODER_H_ |
| |
| #include <d3d11.h> |
| #include <vector> |
| |
| #include "base/memory/ptr_util.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/sequence_checker.h" |
| #include "base/sequenced_task_runner.h" |
| #include "base/single_thread_task_runner.h" |
| #include "base/threading/sequence_bound.h" |
| #include "gpu/config/gpu_driver_bug_workarounds.h" |
| #include "gpu/config/gpu_preferences.h" |
| #include "media/base/callback_registry.h" |
| #include "media/base/supported_video_decoder_config.h" |
| #include "media/base/video_decoder.h" |
| #include "media/gpu/command_buffer_helper.h" |
| #include "media/gpu/media_gpu_export.h" |
| #include "media/gpu/windows/d3d11_com_defs.h" |
| #include "media/gpu/windows/d3d11_decoder_configurator.h" |
| #include "media/gpu/windows/d3d11_h264_accelerator.h" |
| #include "media/gpu/windows/d3d11_texture_selector.h" |
| #include "media/gpu/windows/d3d11_video_decoder_client.h" |
| #include "media/gpu/windows/d3d11_video_decoder_impl.h" |
| #include "media/gpu/windows/d3d11_vp9_accelerator.h" |
| |
| namespace gpu { |
| class CommandBufferStub; |
| } // namespace gpu |
| |
| namespace media { |
| |
| class D3D11PictureBuffer; |
| class D3D11VideoDecoderTest; |
| class MediaLog; |
| |
| // Video decoder that uses D3D11 directly. It is intended that this class will |
| // run the decoder on whatever thread it lives on. However, at the moment, it |
| // only works if it's on the gpu main thread. |
| class MEDIA_GPU_EXPORT D3D11VideoDecoder : public VideoDecoder, |
| public D3D11VideoDecoderClient { |
| public: |
| // Callback to get a D3D11 device. |
| using GetD3D11DeviceCB = base::RepeatingCallback<ComD3D11Device()>; |
| |
| // List of configs that we'll check against when initializing. This is only |
| // needed since GpuMojoMediaClient merges our supported configs with the VDA |
| // supported configs. |
| using SupportedConfigs = std::vector<SupportedVideoDecoderConfig>; |
| |
| // |helper| must be called from |gpu_task_runner|. |
| static std::unique_ptr<VideoDecoder> Create( |
| scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner, |
| std::unique_ptr<MediaLog> media_log, |
| const gpu::GpuPreferences& gpu_preferences, |
| const gpu::GpuDriverBugWorkarounds& gpu_workarounds, |
| base::RepeatingCallback<gpu::CommandBufferStub*()> get_stub_cb, |
| GetD3D11DeviceCB get_d3d11_device_cb, |
| SupportedConfigs supported_configs, |
| bool is_hdr_supported); |
| |
| // VideoDecoder implementation: |
| VideoDecoderType GetDecoderType() const override; |
| void Initialize(const VideoDecoderConfig& config, |
| bool low_delay, |
| CdmContext* cdm_context, |
| InitCB init_cb, |
| const OutputCB& output_cb, |
| const WaitingCB& waiting_cb) override; |
| void Decode(scoped_refptr<DecoderBuffer> buffer, DecodeCB decode_cb) override; |
| void Reset(base::OnceClosure closure) override; |
| bool NeedsBitstreamConversion() const override; |
| bool CanReadWithoutStalling() const override; |
| int GetMaxDecodeRequests() const override; |
| |
| // D3D11VideoDecoderClient implementation. |
| D3D11PictureBuffer* GetPicture() override; |
| void UpdateTimestamp(D3D11PictureBuffer* picture_buffer) override; |
| bool OutputResult(const CodecPicture* picture, |
| D3D11PictureBuffer* picture_buffer) override; |
| void SetDecoderCB(const SetAcceleratorDecoderCB&) override; |
| |
| static bool GetD3D11FeatureLevel( |
| ComD3D11Device dev, |
| const gpu::GpuDriverBugWorkarounds& gpu_workarounds, |
| D3D_FEATURE_LEVEL* feature_level); |
| |
| // Return the set of video decoder configs that we support. |
| static std::vector<SupportedVideoDecoderConfig> |
| GetSupportedVideoDecoderConfigs( |
| const gpu::GpuPreferences& gpu_preferences, |
| const gpu::GpuDriverBugWorkarounds& gpu_workarounds, |
| GetD3D11DeviceCB get_d3d11_device_cb); |
| |
| protected: |
| // Owners should call Destroy(). This is automatic via |
| // std::default_delete<media::VideoDecoder> when held by a |
| // std::unique_ptr<media::VideoDecoder>. |
| ~D3D11VideoDecoder() override; |
| |
| private: |
| friend class D3D11VideoDecoderTest; |
| |
| D3D11VideoDecoder( |
| scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner, |
| std::unique_ptr<MediaLog> media_log, |
| const gpu::GpuPreferences& gpu_preferences, |
| const gpu::GpuDriverBugWorkarounds& gpu_workarounds, |
| base::SequenceBound<D3D11VideoDecoderImpl> impl, |
| base::RepeatingCallback<scoped_refptr<CommandBufferHelper>()> |
| get_helper_cb, |
| GetD3D11DeviceCB get_d3d11_device_cb, |
| SupportedConfigs supported_configs, |
| bool is_hdr_supported); |
| |
| // Receive |buffer|, that is now unused by the client. |
| void ReceivePictureBufferFromClient(scoped_refptr<D3D11PictureBuffer> buffer); |
| |
| // Called when the gpu side of initialization is complete. |
| void OnGpuInitComplete( |
| bool success, |
| D3D11VideoDecoderImpl::ReleaseMailboxCB release_mailbox_cb); |
| |
| // Run the decoder loop. |
| void DoDecode(); |
| |
| // instantiate |accelerated_video_decoder_| based on the video profile |
| HRESULT InitializeAcceleratedDecoder(const VideoDecoderConfig& config, |
| ComD3D11VideoDecoder video_decoder); |
| |
| // Query the video device for a specific decoder ID. |
| bool DeviceHasDecoderID(GUID decoder_guid); |
| |
| // Create new PictureBuffers. Currently, this completes synchronously, but |
| // really should have an async interface since it must do some work on the |
| // gpu main thread. |
| void CreatePictureBuffers(); |
| |
| // Create a D3D11VideoDecoder, if possible, based on the current config. |
| StatusOr<ComD3D11VideoDecoder> CreateD3D11Decoder(); |
| |
| enum class NotSupportedReason { |
| kVideoIsSupported = 0, |
| |
| // D3D11 version 11.1 required. |
| kInsufficientD3D11FeatureLevel = 1, |
| |
| // The video profile is not supported . |
| kProfileNotSupported = 2, |
| |
| // GPU options: require zero copy. |
| kZeroCopyNv12Required = 3, |
| |
| // GPU options: require zero copy. |
| kZeroCopyVideoRequired = 4, |
| |
| // The video codec must be H264. |
| kCodecNotSupported = 5, |
| |
| // The media was encrypted. |
| kEncryptedMedia = 6, |
| |
| // Call to get the D3D11 device failed. |
| kCouldNotGetD3D11Device = 7, |
| |
| // GPU workarounds has turned this off. |
| kOffByWorkaround = 8, |
| |
| // For UMA. Must be the last entry. It should be initialized to the |
| // numerically largest value above; if you add more entries, then please |
| // update this to the last one. |
| kMaxValue = kOffByWorkaround |
| }; |
| |
| enum class D3D11LifetimeProgression { |
| kInitializeStarted = 0, |
| kInitializeSucceeded = 1, |
| kPlaybackSucceeded = 2, |
| |
| // For UMA. Must be the last entry. It should be initialized to the |
| // numerically largest value above; if you add more entries, then please |
| // update this to the last one. |
| kMaxValue = kPlaybackSucceeded |
| }; |
| |
| // Log UMA progression state. |
| void AddLifetimeProgressionStage(D3D11LifetimeProgression stage); |
| |
| std::unique_ptr<MediaLog> media_log_; |
| |
| enum class State { |
| // Initializing resources required to create a codec. |
| kInitializing, |
| |
| // Initialization has completed and we're running. This is the only state |
| // in which |codec_| might be non-null. If |codec_| is null, a codec |
| // creation is pending. |
| kRunning, |
| |
| // A fatal error occurred. A terminal state. |
| kError, |
| }; |
| |
| // Enter the kError state. This will fail any pending |init_cb_| and / or |
| // pending decode as well. Do not add new uses of the char* overload; send a |
| // Status instead. |
| void NotifyError(const char* reason); |
| void NotifyError(const Status& reason); |
| |
| // The implementation, which lives on the GPU main thread. |
| base::SequenceBound<D3D11VideoDecoderImpl> impl_; |
| |
| // GPU main thread task runner. |
| scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner_; |
| |
| // Task runner on which |this| lives. |
| scoped_refptr<base::SequencedTaskRunner> decoder_task_runner_; |
| |
| // Set in initialize, and used to determine reinitializations. |
| bool already_initialized_; |
| |
| gpu::GpuPreferences gpu_preferences_; |
| gpu::GpuDriverBugWorkarounds gpu_workarounds_; |
| |
| // During init, these will be set. |
| VideoDecoderConfig config_; |
| InitCB init_cb_; |
| OutputCB output_cb_; |
| |
| // Callback to be used as a release CB for VideoFrames. Be sure to |
| // BindToCurrentLoop the closure that it takes. |
| D3D11VideoDecoderImpl::ReleaseMailboxCB release_mailbox_cb_; |
| |
| // Right now, this is used both for the video decoder and for display. In |
| // the future, this should only be for the video decoder. We should use |
| // the ANGLE device for display (plus texture sharing, if needed). |
| GetD3D11DeviceCB get_d3d11_device_cb_; |
| |
| // These may be accessed from |decoder_task_runner_|, since the angle device |
| // is in multi-threaded mode. Just be sure not to set any global state. |
| ComD3D11Device device_; |
| ComD3D11DeviceContext device_context_; |
| ComD3D11VideoDevice video_device_; |
| |
| // D3D11 version on this device. |
| D3D_FEATURE_LEVEL usable_feature_level_; |
| |
| std::unique_ptr<AcceleratedVideoDecoder> accelerated_video_decoder_; |
| |
| std::unique_ptr<D3D11DecoderConfigurator> decoder_configurator_; |
| |
| std::unique_ptr<TextureSelector> texture_selector_; |
| |
| std::list<std::pair<scoped_refptr<DecoderBuffer>, DecodeCB>> |
| input_buffer_queue_; |
| scoped_refptr<DecoderBuffer> current_buffer_; |
| DecodeCB current_decode_cb_; |
| base::TimeDelta current_timestamp_; |
| |
| // Must be called on the gpu main thread. So, don't call it from here, |
| // since we don't know what thread we're on. |
| base::RepeatingCallback<gpu::CommandBufferStub*()> get_stub_cb_; |
| |
| // It would be nice to unique_ptr these, but we give a ref to the VideoFrame |
| // so that the texture is retained until the mailbox is opened. |
| std::vector<scoped_refptr<D3D11PictureBuffer>> picture_buffers_; |
| |
| State state_ = State::kInitializing; |
| |
| // Profile of the video being decoded. |
| VideoCodecProfile profile_ = VIDEO_CODEC_PROFILE_UNKNOWN; |
| |
| // Callback to get a command buffer helper. Must be called from the gpu |
| // main thread only. |
| base::RepeatingCallback<scoped_refptr<CommandBufferHelper>()> get_helper_cb_; |
| |
| // Entire class should be single-sequence. |
| SEQUENCE_CHECKER(sequence_checker_); |
| |
| SupportedConfigs supported_configs_; |
| |
| // Should we assume that we're outputting to an HDR display? |
| bool is_hdr_supported_ = false; |
| |
| // Should we use multiple single textures for the decoder output (true) or one |
| // texture with multiple array slices (false)? |
| bool use_single_video_decoder_texture_ = false; |
| |
| // Word-salad callback to set / update D3D11 Video callback to the |
| // accelerator. Needed for config changes. |
| SetAcceleratorDecoderCB set_accelerator_decoder_cb_; |
| |
| // The currently configured bit depth for the decoder. When this changes we |
| // need to recreate the decoder. |
| uint8_t bit_depth_ = 8u; |
| |
| base::WeakPtrFactory<D3D11VideoDecoder> weak_factory_{this}; |
| |
| DISALLOW_COPY_AND_ASSIGN(D3D11VideoDecoder); |
| }; |
| |
| } // namespace media |
| |
| #endif // MEDIA_GPU_WINDOWS_D3D11_VIDEO_DECODER_H_ |