| // Copyright 2023 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_V4L2_V4L2_STATEFUL_VIDEO_DECODER_H_ |
| #define MEDIA_GPU_V4L2_V4L2_STATEFUL_VIDEO_DECODER_H_ |
| |
| #include <linux/videodev2.h> |
| |
| #include "base/atomic_ref_count.h" |
| #include "base/containers/flat_map.h" |
| #include "base/containers/queue.h" |
| #include "base/memory/scoped_refptr.h" |
| #include "base/sequence_checker.h" |
| #include "base/task/cancelable_task_tracker.h" |
| #include "media/base/supported_video_decoder_config.h" |
| #include "media/base/video_types.h" |
| #include "media/gpu/chromeos/video_decoder_pipeline.h" |
| #include "media/gpu/media_gpu_export.h" |
| |
| namespace base { |
| class Location; |
| class SingleThreadTaskRunner; |
| } // namespace base |
| |
| namespace media { |
| |
| class H264FrameReassembler; |
| class H264Parser; |
| class V4L2FrameRateControl; |
| class V4L2Queue; |
| |
| // V4L2StatefulVideoDecoder is an implementation of VideoDecoderMixin |
| // (an augmented media::VideoDecoder) for stateful V4L2 decoding drivers |
| // (e.g. those in ChromeOS Qualcomm devices, and Mediatek 8173). This API has |
| // changed along the kernel versions, but a given copy can be found in [1] |
| // (the most up-to-date is in [2]). |
| // |
| // This class operates on a single thread, where it is constructed and |
| // destroyed. |
| // |
| // [1] |
| // https://www.kernel.org/doc/html/v5.15/userspace-api/media/v4l/dev-decoder.html |
| // [2] |
| // https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/dev-decoder.html |
| class MEDIA_GPU_EXPORT V4L2StatefulVideoDecoder : public VideoDecoderMixin { |
| public: |
| static std::unique_ptr<VideoDecoderMixin> Create( |
| std::unique_ptr<MediaLog> media_log, |
| scoped_refptr<base::SequencedTaskRunner> task_runner, |
| base::WeakPtr<VideoDecoderMixin::Client> client); |
| |
| // VideoDecoderMixin implementation, VideoDecoder part. |
| void Initialize(const VideoDecoderConfig& config, |
| bool low_delay, |
| CdmContext* cdm_context, |
| InitCB init_cb, |
| const PipelineOutputCB& output_cb, |
| const WaitingCB& waiting_cb) override; |
| void Decode(scoped_refptr<DecoderBuffer> buffer, DecodeCB decode_cb) override; |
| void Reset(base::OnceClosure reset_cb) override; |
| bool NeedsBitstreamConversion() const override; |
| bool CanReadWithoutStalling() const override; |
| int GetMaxDecodeRequests() const override; |
| VideoDecoderType GetDecoderType() const override; |
| bool IsPlatformDecoder() const override; |
| // VideoDecoderMixin implementation, specific part. |
| void ApplyResolutionChange() override; |
| size_t GetMaxOutputFramePoolSize() const override; |
| void SetDmaIncoherentV4L2(bool incoherent) override; |
| |
| static int GetMaxNumDecoderInstancesForTesting() { |
| return GetMaxNumDecoderInstances(); |
| } |
| |
| private: |
| V4L2StatefulVideoDecoder(std::unique_ptr<MediaLog> media_log, |
| scoped_refptr<base::SequencedTaskRunner> task_runner, |
| base::WeakPtr<VideoDecoderMixin::Client> client); |
| ~V4L2StatefulVideoDecoder() override; |
| |
| // Tries to create, configure and fill |CAPTURE_queue_|. This method, which |
| // should be called after PollOnceForResolutionChangeEvent() has returned |
| // true, queries the native |CAPTURE_queue_| configuration and supported |
| // capabilities and negotiates the preferred configuration with our |client_| |
| // (via PickDecoderOutputFormat()). It then tries to configure, stream on, |
| // and fill in said |CAPTURE_queue_|. Returns false if any step goes wrong, |
| // in particular any ioctl() call. |
| bool InitializeCAPTUREQueue(); |
| |
| // Before |CAPTURE_queue_| is to be configured, we need to ask our |client_| |
| // (usually VideoDecoderPipeline) to PickDecoderOutputFormat(), for which we |
| // provide some candidates. This method enumerates such candidates, or returns |
| // an empty vector. |
| std::vector<ImageProcessor::PixelLayoutCandidate> |
| EnumeratePixelLayoutCandidates(const gfx::Size& coded_size); |
| |
| // Estimates the number of buffers needed for the |CAPTURE_queue_| and for |
| // codec reference requirements.This function cannot fail (at least returns a |
| // default, conservative value). |
| size_t GetNumberOfReferenceFrames(); |
| |
| // Convenience method to PostTask a wait for a |CAPTURE_queue_| event with |
| // callbacks pointing to TryAndDequeueCAPTUREQueueBuffers() (for data |
| // available) and InitializeCAPTUREQueue() (for re/configuration events). |
| void RearmCAPTUREQueueMonitoring(); |
| // Dequeues all the available |CAPTURE_queue_| buffers and sends their |
| // associated VideoFrames to |output_cb_|. If all goes well, it will |
| // RearmCAPTUREQueueMonitoring(). |
| // TODO(mcasas): Currently we also TryAndEnqueueCAPTUREQueueBuffers(), is this |
| // a good spot for that? |
| void TryAndDequeueCAPTUREQueueBuffers(); |
| |
| // Tries to "enqueue" all available |CAPTURE_queue_| buffers in the driver's |
| // CAPTURE queue (V4L2Queues don't do that by default upon allocation). |
| void TryAndEnqueueCAPTUREQueueBuffers(); |
| |
| // Dequeues all the available |OUTPUT_queue_| buffers. This will effectively |
| // make those available for sending further encoded chunks to the driver. |
| // Returns false if any ioctl fails, true otherwise. |
| bool DrainOUTPUTQueue(); |
| |
| // Tries to "enqueue" all encoded chunks in |decoder_buffer_and_callbacks_| |
| // in |OUTPUT_queue_|, Run()nning their respective DecodeCBs. Returns false if |
| // any enqueueing operation's ioctl fails, true otherwise. |
| bool TryAndEnqueueOUTPUTQueueBuffers(); |
| |
| // Prints a VLOG with the state of |OUTPUT_queue| and |CAPTURE_queue_| for |
| // debugging, preceded with |from_here|s function name. Also TRACEs the |
| // queues' state. |
| void PrintAndTraceQueueStates(const base::Location& from_here); |
| |
| // Returns true if this class has successfully Initialize()d. |
| bool IsInitialized() const; |
| |
| // Pages with multiple decoder instances might run out of memory (e.g. |
| // b/170870476) or crash (e.g. crbug.com/1109312). this class method provides |
| // that number to prevent that erroneous behaviour during Initialize(). |
| static int GetMaxNumDecoderInstances(); |
| // Tracks the number of decoder instances globally in the process. |
| static base::AtomicRefCount num_decoder_instances_; |
| |
| base::ScopedFD device_fd_ GUARDED_BY_CONTEXT(sequence_checker_); |
| // This |wake_event_| is used to interrupt a blocking poll() call, such as the |
| // one started by e.g. RearmCAPTUREQueueMonitoring(). |
| base::ScopedFD wake_event_ GUARDED_BY_CONTEXT(sequence_checker_); |
| |
| // VideoDecoderConfigs supported by the driver. Cached on first Initialize(). |
| SupportedVideoDecoderConfigs supported_configs_ |
| GUARDED_BY_CONTEXT(sequence_checker_); |
| |
| // Bitstream information and other stuff collected during Initialize(). |
| VideoDecoderConfig config_ GUARDED_BY_CONTEXT(sequence_checker_); |
| PipelineOutputCB output_cb_ GUARDED_BY_CONTEXT(sequence_checker_); |
| DecodeCB flush_cb_ GUARDED_BY_CONTEXT(sequence_checker_); |
| // Set to true when the driver identifies itself as a Mediatek 8173. |
| bool is_mtk8173_ GUARDED_BY_CONTEXT(sequence_checker_) = false; |
| |
| // Used only on V4L2_MEMORY_MMAP queues (e.g. Hana MT8173) to grab the visible |
| // rectangle upon |CAPTURE_queue_| configuration in InitializeCAPTUREQueue(). |
| gfx::Rect visible_rect_; |
| |
| // Map of enqueuing timecodes to system timestamp, for histogramming purposes. |
| base::flat_map<int64_t, base::TimeTicks> encoding_timestamps_; |
| |
| // Holds pairs of encoded chunk (DecoderBuffer) and associated DecodeCB for |
| // decoding via TryAndEnqueueOUTPUTQueueBuffers(). |
| base::queue<std::pair<scoped_refptr<DecoderBuffer>, DecodeCB>> |
| decoder_buffer_and_callbacks_; |
| |
| // OUTPUT in V4L2 terminology is the queue holding encoded chunks of |
| // bitstream. CAPTURE is the queue holding decoded pictures. See e.g. [1]. |
| // https://www.kernel.org/doc/html/v5.15/userspace-api/media/v4l/dev-decoder.html#glossary |
| scoped_refptr<V4L2Queue> OUTPUT_queue_ GUARDED_BY_CONTEXT(sequence_checker_); |
| scoped_refptr<V4L2Queue> CAPTURE_queue_ GUARDED_BY_CONTEXT(sequence_checker_); |
| |
| // Some drivers, e.g. QC SC7180, require the client to inform the driver of |
| // the framerate (to tweak internal resources). |
| std::unique_ptr<V4L2FrameRateControl> framerate_control_; |
| |
| // A sequenced TaskRunner to wait for events coming from |CAPTURE_queue_| or |
| // |wake_event_|. |
| scoped_refptr<base::SingleThreadTaskRunner> event_task_runner_; |
| // Used to (try to) cancel the Tasks sent by RearmCAPTUREQueueMonitoring(), |
| // and not serviced yet, when no longer needed. |
| base::CancelableTaskTracker cancelable_task_tracker_ |
| GUARDED_BY_CONTEXT(sequence_checker_); |
| |
| // Optional helper class to reassemble full H.264 frames out of NALUs. |
| std::unique_ptr<H264FrameReassembler> h264_frame_reassembler_ |
| GUARDED_BY_CONTEXT(sequence_checker_); |
| |
| // Pegged to the construction and main work thread. Notably, |task_runner| is |
| // not used. |
| SEQUENCE_CHECKER(sequence_checker_); |
| |
| // Weak factories associated with the main thread (|sequence_checker|). |
| base::WeakPtrFactory<V4L2StatefulVideoDecoder> weak_ptr_factory_for_events_; |
| base::WeakPtrFactory<V4L2StatefulVideoDecoder> |
| weak_ptr_factory_for_CAPTURE_availability_; |
| }; |
| |
| } // namespace media |
| |
| #endif // MEDIA_GPU_V4L2_V4L2_STATEFUL_VIDEO_DECODER_H_ |