1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183
  184
  185
  186
  187
  188
  189
  190
  191
  192
  193
  194
  195
  196
  197
  198
  199
  200
  201
  202
  203
  204
  205

media / gpu / v4l2 / v4l2_stateful_video_decoder.h [blame]

// 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_