| // Copyright 2019 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef MEDIA_GPU_CHROMEOS_VIDEO_DECODER_PIPELINE_H_ |
| #define MEDIA_GPU_CHROMEOS_VIDEO_DECODER_PIPELINE_H_ |
| |
| #include <atomic> |
| #include <memory> |
| |
| #include "base/functional/callback_forward.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/sequence_checker.h" |
| #include "build/build_config.h" |
| #include "build/chromeos_buildflags.h" |
| #include "gpu/config/gpu_driver_bug_workarounds.h" |
| #include "media/base/cdm_context.h" |
| #include "media/base/limits.h" |
| #include "media/base/supported_video_decoder_config.h" |
| #include "media/base/video_decoder.h" |
| #include "media/base/video_decoder_config.h" |
| #include "media/gpu/chromeos/chromeos_status.h" |
| #include "media/gpu/chromeos/fourcc.h" |
| #include "media/gpu/chromeos/image_processor_with_pool.h" |
| #include "media/gpu/chromeos/mailbox_video_frame_converter.h" |
| #include "media/gpu/media_gpu_export.h" |
| #include "media/mojo/mojom/stable/stable_video_decoder.mojom.h" |
| #include "third_party/abseil-cpp/absl/types/optional.h" |
| #include "ui/gfx/geometry/size.h" |
| #include "ui/gfx/native_pixmap.h" |
| #include "ui/gfx/native_pixmap_handle.h" |
| |
| #if BUILDFLAG(IS_CHROMEOS) |
| #include "media/gpu/chromeos/decoder_buffer_transcryptor.h" |
| #endif // BUILDFLAG(IS_CHROMEOS) |
| namespace base { |
| class SequencedTaskRunner; |
| } |
| |
| namespace media { |
| |
| class DmabufVideoFramePool; |
| class MediaLog; |
| |
| // This interface extends VideoDecoder to provide a few more methods useful |
| // for VideoDecoderPipeline operation. This class should be operated and |
| // destroyed on |decoder_task_runner_|. |
| class MEDIA_GPU_EXPORT VideoDecoderMixin : public VideoDecoder { |
| public: |
| // Client interface of VideoDecoderMixin. |
| class MEDIA_GPU_EXPORT Client { |
| public: |
| Client() = default; |
| virtual ~Client() = default; |
| |
| // Returns the video frame pool without giving up ownership or nullptr if |
| // the decoder is responsible for allocating its own frames. Note that |
| // callers may not assume that the returned pointer is valid after a call to |
| // PickDecoderOutputFormat(). |
| virtual DmabufVideoFramePool* GetVideoFramePool() const = 0; |
| |
| // After this method is called from |decoder_|, the client needs to call |
| // VideoDecoderMixin::ApplyResolutionChange() when all pending frames are |
| // flushed. |
| virtual void PrepareChangeResolution() = 0; |
| |
| // Negotiates the output format and size of the decoder: if not scaling |
| // (i.e., the size of |decoder_visible_rect| is equal to |output_size|), it |
| // selects a renderable format out of |candidates| and initializes the main |
| // video frame pool with the selected format and the given arguments. If |
| // scaling, or if none of the |candidates| is considered renderable, or if |
| // (VA-API-only) the modifier of the frame pool's buffers is incompatible |
| // with the hardware decoder, this method attempts to initialize an image |
| // processor to reconcile the formats and/or perform scaling. |
| // |need_aux_frame_pool| indicates whether the caller needs a frame pool in |
| // the event that an image processor is needed: if true, a new pool is |
| // initialized and that pool can be obtained by calling GetVideoFramePool(). |
| // This pool will provide buffers consistent with the selected candidate out |
| // of |candidates|. If false, the caller must allocate its own buffers. |
| // if |allocator| is not absl::nullopt, the frame pool will be set |
| // to use the allocator provided for allocating video frames. |
| // |
| // The client also provides the |num_codec_reference_frames|, which is |
| // sometimes fixed (e.g. kVp9NumRefFrames) and sometimes variable (e.g. |
| // H.264, HEVC). |
| // |
| // Note: after a call to this method, callers should assume that a pointer |
| // returned by a prior call to GetVideoFramePool() is no longer valid. |
| virtual CroStatus::Or<ImageProcessor::PixelLayoutCandidate> |
| PickDecoderOutputFormat( |
| const std::vector<ImageProcessor::PixelLayoutCandidate>& candidates, |
| const gfx::Rect& decoder_visible_rect, |
| const gfx::Size& decoder_natural_size, |
| absl::optional<gfx::Size> output_size, |
| size_t num_codec_reference_frames, |
| bool use_protected, |
| bool need_aux_frame_pool, |
| absl::optional<DmabufVideoFramePool::CreateFrameCB> allocator) = 0; |
| }; |
| |
| VideoDecoderMixin( |
| std::unique_ptr<MediaLog> media_log, |
| scoped_refptr<base::SequencedTaskRunner> decoder_task_runner, |
| base::WeakPtr<VideoDecoderMixin::Client> client); |
| |
| VideoDecoderMixin(const VideoDecoderMixin&) = delete; |
| VideoDecoderMixin& operator=(const VideoDecoderMixin&) = delete; |
| |
| ~VideoDecoderMixin() override; |
| |
| // After DecoderInterface calls |prepare_change_resolution_cb| passed |
| // from the constructor, this method is called when the pipeline flushes |
| // pending frames. |
| virtual void ApplyResolutionChange() = 0; |
| |
| // For protected content implementations that require transcryption of the |
| // content before being sent into the HW decoders. (Currently only used by |
| // AMD). Default implementation returns false. |
| virtual bool NeedsTranscryption(); |
| |
| // Set the DMA coherency of the video decoder buffers. Only relevant for |
| // V4L2. |
| virtual void SetDmaIncoherentV4L2(bool incoherent) {} |
| |
| // The VideoDecoderPipeline can use this to query a decoder for an upper bound |
| // on the size of the frame pool that the decoder writes into. The default |
| // implementation indicates no limit. |
| virtual size_t GetMaxOutputFramePoolSize() const; |
| |
| protected: |
| const std::unique_ptr<MediaLog> media_log_; |
| |
| // Decoder task runner. All public methods of |
| // VideoDecoderMixin are executed at this task runner. |
| const scoped_refptr<base::SequencedTaskRunner> decoder_task_runner_; |
| |
| // The WeakPtr client instance, bound to |decoder_task_runner_|. |
| base::WeakPtr<VideoDecoderMixin::Client> client_; |
| }; |
| |
| class MEDIA_GPU_EXPORT VideoDecoderPipeline : public VideoDecoder, |
| public VideoDecoderMixin::Client { |
| public: |
| using CreateDecoderFunctionCB = |
| base::OnceCallback<std::unique_ptr<VideoDecoderMixin>( |
| std::unique_ptr<MediaLog> media_log, |
| scoped_refptr<base::SequencedTaskRunner>, |
| base::WeakPtr<VideoDecoderMixin::Client>)>; |
| |
| // Creates a VideoDecoderPipeline instance that allocates VideoFrames from |
| // |frame_pool| and converts the decoded VideoFrames using |frame_converter|. |
| // |renderable_fourccs| is the list of formats that VideoDecoderPipeline may |
| // use when outputting frames, in order of preference. |
| static std::unique_ptr<VideoDecoder> Create( |
| const gpu::GpuDriverBugWorkarounds& workarounds, |
| scoped_refptr<base::SequencedTaskRunner> client_task_runner, |
| std::unique_ptr<DmabufVideoFramePool> frame_pool, |
| std::unique_ptr<MailboxVideoFrameConverter> frame_converter, |
| std::vector<Fourcc> renderable_fourccs, |
| std::unique_ptr<MediaLog> media_log, |
| mojo::PendingRemote<stable::mojom::StableVideoDecoder> oop_video_decoder); |
| |
| static std::unique_ptr<VideoDecoder> CreateForTesting( |
| scoped_refptr<base::SequencedTaskRunner> client_task_runner, |
| std::unique_ptr<MediaLog> media_log, |
| bool ignore_resolution_changes_to_smaller_for_testing = false); |
| |
| static std::vector<Fourcc> DefaultPreferredRenderableFourccs(); |
| |
| // Ensures that the video decoder supported configurations are known. When |
| // they are, |cb| is called with a PendingRemote that corresponds to the same |
| // connection as |oop_video_decoder| (which may be |oop_video_decoder| |
| // itself). If |oop_video_decoder| is valid, the supported configurations are |
| // those of an out-of-process video decoder (and in this case, |
| // |oop_video_decoder| may be used internally to query those configurations). |
| // Otherwise, the supported configurations are those of an in-process, |
| // platform-specific decoder (e.g., VaapiVideoDecoder or V4L2VideoDecoder). |
| // |
| // |cb| is called with |oop_video_decoder| before NotifySupportKnown() returns |
| // if the supported configurations are already known. |
| // |
| // This method is thread- and sequence-safe. |cb| is always called on the same |
| // sequence as NotifySupportKnown(). |
| static void NotifySupportKnown( |
| mojo::PendingRemote<stable::mojom::StableVideoDecoder> oop_video_decoder, |
| base::OnceCallback< |
| void(mojo::PendingRemote<stable::mojom::StableVideoDecoder>)> cb); |
| |
| static absl::optional<SupportedVideoDecoderConfigs> GetSupportedConfigs( |
| VideoDecoderType decoder_type, |
| const gpu::GpuDriverBugWorkarounds& workarounds); |
| |
| ~VideoDecoderPipeline() override; |
| static void DestroyAsync(std::unique_ptr<VideoDecoderPipeline>); |
| |
| // VideoDecoder implementation |
| VideoDecoderType GetDecoderType() const override; |
| bool IsPlatformDecoder() const override; |
| int GetMaxDecodeRequests() const override; |
| bool FramesHoldExternalResources() const override; |
| bool NeedsBitstreamConversion() const override; |
| bool CanReadWithoutStalling() 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 Reset(base::OnceClosure reset_cb) override; |
| void Decode(scoped_refptr<DecoderBuffer> buffer, DecodeCB decode_cb) override; |
| |
| // VideoDecoderMixin::Client implementation. |
| DmabufVideoFramePool* GetVideoFramePool() const override; |
| void PrepareChangeResolution() override; |
| CroStatus::Or<ImageProcessor::PixelLayoutCandidate> PickDecoderOutputFormat( |
| const std::vector<ImageProcessor::PixelLayoutCandidate>& candidates, |
| const gfx::Rect& decoder_visible_rect, |
| const gfx::Size& decoder_natural_size, |
| absl::optional<gfx::Size> output_size, |
| size_t num_codec_reference_frames, |
| bool use_protected, |
| bool need_aux_frame_pool, |
| absl::optional<DmabufVideoFramePool::CreateFrameCB> allocator) override; |
| |
| private: |
| friend class VideoDecoderPipelineTest; |
| #if BUILDFLAG(USE_VAAPI) |
| FRIEND_TEST_ALL_PREFIXES(VideoDecoderPipelineTest, |
| PickDecoderOutputFormatLinearModifier); |
| #endif |
| |
| VideoDecoderPipeline( |
| const gpu::GpuDriverBugWorkarounds& workarounds, |
| scoped_refptr<base::SequencedTaskRunner> client_task_runner, |
| std::unique_ptr<DmabufVideoFramePool> frame_pool, |
| std::unique_ptr<MailboxVideoFrameConverter> frame_converter, |
| std::vector<Fourcc> renderable_fourccs, |
| std::unique_ptr<MediaLog> media_log, |
| CreateDecoderFunctionCB create_decoder_function_cb, |
| bool uses_oop_video_decoder); |
| |
| void InitializeTask(const VideoDecoderConfig& config, |
| bool low_delay, |
| CdmContext* cdm_context, |
| InitCB init_cb, |
| const OutputCB& output_cb, |
| const WaitingCB& waiting_cb); |
| void ResetTask(base::OnceClosure reset_cb); |
| void DecodeTask(scoped_refptr<DecoderBuffer> buffer, DecodeCB decode_cb); |
| |
| void OnInitializeDone(InitCB init_cb, |
| CdmContext* cdm_context, |
| DecoderStatus status); |
| |
| void OnDecodeDone(bool eos_buffer, DecodeCB decode_cb, DecoderStatus status); |
| void OnResetDone(base::OnceClosure reset_cb); |
| void OnError(const std::string& msg); |
| |
| // Called when |decoder_| finishes decoding a frame. |
| void OnFrameDecoded(scoped_refptr<VideoFrame> frame); |
| // Called when |image_processor_| finishes processing a frame. |
| void OnFrameProcessed(scoped_refptr<VideoFrame> frame); |
| // Called when |frame_converter_| finishes converting a frame. |
| void OnFrameConverted(scoped_refptr<VideoFrame> frame); |
| // Called when |decoder_| invokes the waiting callback. |
| void OnDecoderWaiting(WaitingReason reason); |
| |
| // Return true if the pipeline has pending frames that are returned from |
| // |decoder_| but haven't been passed to the client. |
| // i.e. |image_processor_| or |frame_converter_| has pending frames. |
| bool HasPendingFrames() const; |
| |
| // Call VideoDecoderMixin::ApplyResolutionChange() when we need to. |
| void CallApplyResolutionChangeIfNeeded(); |
| |
| // Calls the client flush callback if there is a flush in progress and there |
| // are no pending frames in the pipeline. If |override_status| is not nullopt, |
| // we use it as the DecoderStatus to call the client flush callback with. |
| // Otherwise, we use the original DecoderStatus passed by the underlying |
| // decoder at the moment it notified us that the flush was completed at that |
| // level. This is useful to handle cases like the following: suppose the |
| // underlying decoder tells us a flush was completed without problems but we |
| // can't call the client flush callback yet because there are pending frames |
| // in the pipeline (perhaps in the image processor); then later, we get a |
| // reset request; once the reset request is completed, we can call the client |
| // flush callback but we should pass a DecoderStatus of kAborted instead of |
| // kOk. In this scenario, the original DecoderStatus is kOk and |
| // *|override_status| is kAborted. |
| void CallFlushCbIfNeeded(absl::optional<DecoderStatus> override_status); |
| |
| #if BUILDFLAG(IS_CHROMEOS) |
| // Callback for when transcryption of a buffer completes. |
| void OnBufferTranscrypted(scoped_refptr<DecoderBuffer> transcrypted_buffer, |
| DecodeCB decode_callback); |
| #endif // BUILDFLAG(IS_CHROMEOS) |
| |
| // Used to determine the decoder's maximum output frame pool size. |
| size_t GetDecoderMaxOutputFramePoolSize() const; |
| |
| // Used to figure out the supported configurations in Initialize(). |
| const gpu::GpuDriverBugWorkarounds gpu_workarounds_; |
| |
| SupportedVideoDecoderConfigs supported_configs_for_testing_; |
| |
| // The client task runner and its sequence checker. All public methods should |
| // run on this task runner. |
| const scoped_refptr<base::SequencedTaskRunner> client_task_runner_; |
| SEQUENCE_CHECKER(client_sequence_checker_); |
| |
| // The decoder task runner and its sequence checker. Call |decoder_|'s, |
| // |frame_pool_|'s, and |frame_converter_|'s methods on this task runner. |
| const scoped_refptr<base::SequencedTaskRunner> decoder_task_runner_; |
| SEQUENCE_CHECKER(decoder_sequence_checker_); |
| |
| // The frame pool passed from the client. While internally other additional |
| // frame pools might be used for intermediate results, all frames passed to |
| // the client should be created using this pool. DmabufVideoFramePool is |
| // thread safe and used from |client_task_runner_| and |
| // |decoder_task_runner_|. |
| std::unique_ptr<DmabufVideoFramePool> main_frame_pool_; |
| |
| // When an image processor is needed, |auxiliary_frame_pool_| is the pool of |
| // output buffers for the |decoder_| (which will serve as the input buffers |
| // for the image processor) and |main_frame_pool_| will be the pool of output |
| // buffers for the image processor. |
| std::unique_ptr<DmabufVideoFramePool> auxiliary_frame_pool_; |
| |
| // The image processor is only created when the decoder cannot output frames |
| // with renderable format. |
| std::unique_ptr<ImageProcessorWithPool> image_processor_ |
| GUARDED_BY_CONTEXT(decoder_sequence_checker_); |
| |
| // The frame converter passed from the client, otherwise used and destroyed on |
| // |decoder_task_runner_|. |
| std::unique_ptr<MailboxVideoFrameConverter> frame_converter_ |
| GUARDED_BY_CONTEXT(decoder_sequence_checker_); |
| |
| // The set of output formats allowed to be used in order of preference. |
| // VideoDecoderPipeline may perform copies to convert from the decoder's |
| // output to one of these formats. |
| const std::vector<Fourcc> renderable_fourccs_ |
| GUARDED_BY_CONTEXT(decoder_sequence_checker_); |
| |
| const std::unique_ptr<MediaLog> media_log_; |
| |
| #if BUILDFLAG(IS_CHROMEOS) |
| // The transcryptor for transcrypting DecoderBuffers when needed by the HW |
| // decoder implementation. |
| std::unique_ptr<DecoderBufferTranscryptor> buffer_transcryptor_ |
| GUARDED_BY_CONTEXT(decoder_sequence_checker_); |
| #endif // BUILDFLAG(IS_CHROMEOS) |
| |
| // The current video decoder implementation. Valid after initialization is |
| // successfully done. |
| std::unique_ptr<VideoDecoderMixin> decoder_ |
| GUARDED_BY_CONTEXT(decoder_sequence_checker_); |
| |
| // Only used after initialization on |decoder_task_runner_|. |
| CreateDecoderFunctionCB create_decoder_function_cb_ |
| GUARDED_BY_CONTEXT(decoder_sequence_checker_); |
| |
| // Callbacks provided by the client. Used on |decoder_task_runner_|. |
| // The callback methods themselves are exercised on |client_task_runner_|. |
| OutputCB client_output_cb_ GUARDED_BY_CONTEXT(decoder_sequence_checker_); |
| // For the flush callback, we keep a couple of things: the callback itself |
| // provided by the client and the DecoderStatus obtained from the underlying |
| // decoder at the moment it notifies the VideoDecoderPipeline that a flush has |
| // been completed. |
| struct ClientFlushCBState { |
| ClientFlushCBState(DecodeCB flush_cb, DecoderStatus decoder_decode_status); |
| ~ClientFlushCBState(); |
| DecodeCB flush_cb; |
| const DecoderStatus decoder_decode_status; |
| }; |
| absl::optional<ClientFlushCBState> client_flush_cb_state_ |
| GUARDED_BY_CONTEXT(decoder_sequence_checker_); |
| WaitingCB waiting_cb_ GUARDED_BY_CONTEXT(decoder_sequence_checker_); |
| |
| using CreateImageProcessorCBForTesting = |
| base::RepeatingCallback<std::unique_ptr<ImageProcessor>( |
| const std::vector<ImageProcessor::PixelLayoutCandidate>& |
| input_candidates, |
| const gfx::Rect& input_visible_rect, |
| const gfx::Size& output_size, |
| size_t num_buffers)>; |
| CreateImageProcessorCBForTesting create_image_processor_cb_for_testing_ |
| GUARDED_BY_CONTEXT(decoder_sequence_checker_); |
| |
| // True if we need to notify |decoder_| that the pipeline is flushed via |
| // VideoDecoderMixin::ApplyResolutionChange(). |
| bool need_apply_new_resolution GUARDED_BY_CONTEXT(decoder_sequence_checker_) = |
| false; |
| |
| // True if the decoder needs bitstream conversion before decoding. |
| bool needs_bitstream_conversion_ |
| GUARDED_BY_CONTEXT(client_sequence_checker_) = false; |
| |
| // |oop_decoder_can_read_without_stalling_| is accessed from multiple |
| // sequences: it's set on the decoder sequence for every frame we get from the |
| // |decoder_|, and it's read on the client sequence. |
| std::atomic<bool> oop_decoder_can_read_without_stalling_; |
| |
| // Set to true when any unexpected error occurs. |
| bool has_error_ GUARDED_BY_CONTEXT(decoder_sequence_checker_) = false; |
| |
| // Set to true when we need to tell the frame pool to rebuild itself. This is |
| // needed for protected content on Intel platforms. |
| bool need_frame_pool_rebuild_ GUARDED_BY_CONTEXT(decoder_sequence_checker_) = |
| false; |
| |
| // Calculated upon Initialize(), to determine how many buffers to allocate for |
| // the Renderer pipeline. |
| size_t estimated_num_buffers_for_renderer_ GUARDED_BY_CONTEXT( |
| decoder_sequence_checker_) = limits::kMaxVideoFrames + 1; |
| |
| // Set to true when the underlying |decoder_| is an OOPVideoDecoder. |
| const bool uses_oop_video_decoder_; |
| |
| // Set to true to bypass checks for encrypted content support for testing. |
| bool allow_encrypted_content_for_testing_ = false; |
| |
| // See VP9Decoder for information on this. |
| bool ignore_resolution_changes_to_smaller_for_testing_ = false; |
| |
| base::WeakPtr<VideoDecoderPipeline> decoder_weak_this_; |
| // The weak pointer of this, bound to |decoder_task_runner_|. |
| base::WeakPtrFactory<VideoDecoderPipeline> decoder_weak_this_factory_{this}; |
| }; |
| |
| } // namespace media |
| |
| #endif // MEDIA_GPU_CHROMEOS_VIDEO_DECODER_PIPELINE_H_ |