chromium/media/gpu/v4l2/v4l2_image_processor_backend.h

// Copyright 2014 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_IMAGE_PROCESSOR_BACKEND_H_
#define MEDIA_GPU_V4L2_V4L2_IMAGE_PROCESSOR_BACKEND_H_

#include <linux/videodev2.h>
#include <stddef.h>
#include <stdint.h>

#include <memory>
#include <optional>
#include <vector>

#include "base/containers/queue.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/single_thread_task_runner.h"
#include "media/gpu/chromeos/image_processor_backend.h"
#include "media/gpu/media_gpu_export.h"
#include "media/gpu/v4l2/v4l2_device.h"
#include "ui/gfx/geometry/size.h"

namespace base {
class TimeTicks;
}

namespace media {

// Handles image processing accelerators that expose a V4L2 memory-to-memory
// interface. The threading model of this class is the same as for other V4L2
// hardware accelerators (see V4L2VideoDecodeAccelerator) for more details.
class MEDIA_GPU_EXPORT V4L2ImageProcessorBackend
    : public ImageProcessorBackend {
 public:
  // Factory method to create V4L2ImageProcessorBackend to convert from
  // input_config to output_config. The number of input buffers and output
  // buffers will be |num_buffers|. Provided |error_cb| will be posted to the
  // same thread Create() is called if an error occurs after initialization.
  // Returns nullptr if V4L2ImageProcessorBackend fails to create.
  // Note: output_mode will be removed once all its clients use import mode.
  // TODO(crbug.com/917798): remove |device| parameter once
  //     V4L2VideoDecodeAccelerator no longer creates and uses
  //     |image_processor_device_| before V4L2ImageProcessorBackend is created.
  static std::unique_ptr<ImageProcessorBackend> Create(
      scoped_refptr<V4L2Device> device,
      size_t num_buffers,
      const PortConfig& input_config,
      const PortConfig& output_config,
      OutputMode output_mode,
      ErrorCB error_cb);

  V4L2ImageProcessorBackend(const V4L2ImageProcessorBackend&) = delete;
  V4L2ImageProcessorBackend& operator=(const V4L2ImageProcessorBackend&) =
      delete;

  // ImageProcessor implementation.
  void ProcessFrame(scoped_refptr<FrameResource> input_frame,
                    scoped_refptr<FrameResource> output_frame,
                    FrameResourceReadyCB cb) override;
  void ProcessLegacyFrame(scoped_refptr<FrameResource> frame,
                          LegacyFrameResourceReadyCB cb) override;
  void Reset() override;

  // Returns true if image processing is supported on this platform.
  static bool IsSupported();

  // Returns a vector of supported input formats in fourcc.
  static std::vector<uint32_t> GetSupportedInputFormats();

  // Returns a vector of supported output formats in fourcc.
  static std::vector<uint32_t> GetSupportedOutputFormats();

  // Gets |output_size| and |num_planes|] required by the device for conversion
  // from |input_pixelformat| with |input_size| to |output_pixelformat| with
  // expected |output_size|. On success, returns true with adjusted
  // |output_size| and |num_planes|. On failure, returns false without touching
  // |output_size| and |num_planes|.
  // TODO(b/191450183): Remove |output_size| and assert
  // DCHECK_EQ(input_size, output_size) inside the body.
  static bool TryOutputFormat(uint32_t input_pixelformat,
                              uint32_t output_pixelformat,
                              const gfx::Size& input_size,
                              gfx::Size* output_size,
                              size_t* num_planes);

  std::string type() const override;

 private:
  friend struct std::default_delete<V4L2ImageProcessorBackend>;

  // Callback for initialization.
  using InitCB = base::OnceCallback<void(bool)>;

  // Job record. Jobs are processed in a FIFO order. |input_frame| will be
  // processed and the result written into |output_frame|. Once processing is
  // complete, |ready_cb| or |legacy_frame_ready_cb| will be called depending on
  // which Process() method has been used to create that JobRecord.
  struct JobRecord {
    JobRecord();
    ~JobRecord();
    scoped_refptr<FrameResource> input_frame;
    FrameResourceReadyCB ready_cb;
    LegacyFrameResourceReadyCB legacy_ready_cb;
    scoped_refptr<FrameResource> output_frame;
    size_t output_buffer_id;

    // This is filled only if chrome tracing in "media" category is enabled.
    std::optional<base::TimeTicks> start_time;
  };

  V4L2ImageProcessorBackend(scoped_refptr<V4L2Device> device,
                            const PortConfig& input_config,
                            const PortConfig& output_config,
                            v4l2_memory input_memory_type,
                            v4l2_memory output_memory_type,
                            OutputMode output_mode,
                            ErrorCB error_cb);
  ~V4L2ImageProcessorBackend() override;
  void Destroy() override;
  // Stop all processing on |poll_task_runner_|.
  void DestroyOnPollSequence();

  void EnqueueInput(const JobRecord* job_record, V4L2WritableBufferRef buffer);
  void EnqueueOutput(JobRecord* job_record, V4L2WritableBufferRef buffer);
  void Dequeue();
  bool EnqueueInputRecord(const JobRecord* job_record,
                          V4L2WritableBufferRef buffer);
  bool EnqueueOutputRecord(JobRecord* job_record, V4L2WritableBufferRef buffer);
  bool CreateInputBuffers(size_t num_buffers);
  bool CreateOutputBuffers(size_t num_buffers);
  // Reconfigure the |type| queue for |size|.
  bool ReconfigureV4L2Format(const gfx::Size& size, enum v4l2_buf_type type);

  // Callback of FrameResource destruction. Since FrameResource destruction
  // callback might be executed on any sequence, we use a thunk to post the
  // task to |device_task_runner_|.
  static void V4L2VFRecycleThunk(
      scoped_refptr<base::SequencedTaskRunner> task_runner,
      std::optional<base::WeakPtr<V4L2ImageProcessorBackend>> image_processor,
      V4L2ReadableBufferRef buf);
  void V4L2VFRecycleTask(V4L2ReadableBufferRef buf);

  void NotifyError();

  // ImageProcessor implementation.
  void ProcessJobs();
  void ServiceDevice();

  void TriggerPoll(bool poll_device);
  // Ran on |poll_task_runner_| to wait for device events.
  void DevicePollTask(bool poll_device);

  const v4l2_memory input_memory_type_;
  const v4l2_memory output_memory_type_;

  // V4L2 device in use. Accessed from both the main task runner and
  // |poll_task_runner_|.
  // TODO(mcasas): Unclear whether that's racy.
  scoped_refptr<V4L2Device> device_ GUARDED_BY_FIXME(sequence_checker_);

  base::queue<std::unique_ptr<JobRecord>> input_job_queue_
      GUARDED_BY_CONTEXT(sequence_checker_);
  base::queue<std::unique_ptr<JobRecord>> running_jobs_
      GUARDED_BY_CONTEXT(sequence_checker_);

  scoped_refptr<V4L2Queue> input_queue_ GUARDED_BY_CONTEXT(sequence_checker_);
  scoped_refptr<V4L2Queue> output_queue_ GUARDED_BY_CONTEXT(sequence_checker_);

  // Sequence and its checker used to poll the V4L2 for events only.
  scoped_refptr<base::SingleThreadTaskRunner> poll_task_runner_;
  SEQUENCE_CHECKER(poll_sequence_checker_);

  SEQUENCE_CHECKER(sequence_checker_);

  base::WeakPtr<V4L2ImageProcessorBackend> weak_this_;
  base::WeakPtr<V4L2ImageProcessorBackend> poll_weak_this_;
  base::WeakPtrFactory<V4L2ImageProcessorBackend> weak_this_factory_{this};
  base::WeakPtrFactory<V4L2ImageProcessorBackend> poll_weak_this_factory_{this};
};

}  // namespace media

namespace std {

template <>
struct default_delete<media::V4L2ImageProcessorBackend>
    : public default_delete<media::ImageProcessorBackend> {};

}  // namespace std

#endif  // MEDIA_GPU_V4L2_V4L2_IMAGE_PROCESSOR_BACKEND_H_