chromium/media/capture/video/chromeos/request_manager.h

// Copyright 2018 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_CAPTURE_VIDEO_CHROMEOS_REQUEST_MANAGER_H_
#define MEDIA_CAPTURE_VIDEO_CHROMEOS_REQUEST_MANAGER_H_

#include <cstring>
#include <map>
#include <memory>
#include <optional>
#include <queue>
#include <set>
#include <vector>

#include "base/containers/flat_map.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/task/single_thread_task_runner.h"
#include "base/time/time.h"
#include "media/capture/mojom/image_capture.mojom.h"
#include "media/capture/video/chromeos/camera_app_device_impl.h"
#include "media/capture/video/chromeos/camera_device_context.h"
#include "media/capture/video/chromeos/camera_device_delegate.h"
#include "media/capture/video/chromeos/capture_metadata_dispatcher.h"
#include "media/capture/video/chromeos/mojom/camera3.mojom.h"
#include "media/capture/video/chromeos/mojom/camera_app.mojom.h"
#include "media/capture/video/chromeos/request_builder.h"
#include "media/capture/video/chromeos/stream_buffer_manager.h"
#include "media/capture/video_capture_types.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver.h"

namespace media {

class CameraBufferFactory;

// The JPEG transport header as defined by Android camera HAL v3 API.  The JPEG
// transport header is at the end of the blob buffer filled by the HAL.
constexpr uint16_t kCamera3JpegBlobId = 0x00FF;
struct Camera3JpegBlob {
  uint16_t jpeg_blob_id;
  uint32_t jpeg_size;
};

// Minimum configured streams should at least contain kPreviewOutput.
constexpr int32_t kMinConfiguredStreams = 1;

// Maximum configured streams could contain two optional YUV streams.
constexpr int32_t kMaxConfiguredStreams = 4;

// The interface to register/retire buffer from the buffer pool maintained in
// the camera HAL side.
class CAPTURE_EXPORT VideoCaptureBufferObserver {
 public:
  VideoCaptureBufferObserver(base::WeakPtr<RequestManager> request_manager);

  ~VideoCaptureBufferObserver();

  // Registers buffer to the camera HAL buffer pool.
  void OnNewBuffer(ClientType client_type,
                   cros::mojom::CameraBufferHandlePtr buffer);

  // Retires a buffer from the camera HAL buffer pool.
  void OnBufferRetired(ClientType client_type, uint64_t buffer_id);

 private:
  const base::WeakPtr<RequestManager> request_manager_;
};

// RequestManager is responsible for managing the flow for sending capture
// requests and receiving capture results. Having RequestBuilder to build
// request and StreamBufferManager to handles stream buffers, it focuses on
// controlling the capture flow between Chrome and camera HAL process.
class CAPTURE_EXPORT RequestManager final
    : public cros::mojom::Camera3CallbackOps,
      public CaptureMetadataDispatcher {
 public:
  using BlobifyCallback = base::RepeatingCallback<mojom::BlobPtr(
      const uint8_t* buffer,
      const uint32_t bytesused,
      const VideoCaptureFormat& capture_format,
      int screen_rotation)>;

  // CaptureResult is used to hold the pending capture results for each frame.
  struct CaptureResult {
    CaptureResult();
    ~CaptureResult();
    // The shutter timestamp in nanoseconds.
    uint64_t shutter_timestamp;
    // |reference_time| and |timestamp| are derived from the shutter time of
    // this frame.  They are be passed to |client_->OnIncomingCapturedData|
    // along with the |buffers| when the captured frame is submitted.
    base::TimeTicks reference_time;
    base::TimeDelta timestamp;
    // The result metadata.  Contains various information about the captured
    // frame.
    cros::mojom::CameraMetadataPtr metadata;
    // The buffer handles that hold the captured data of this frame.
    std::map<StreamType, cros::mojom::Camera3StreamBufferPtr> buffers;
    // The set of the partial metadata received.  For each capture result, the
    // total number of partial metadata should equal to
    // |partial_result_count_|.
    std::set<uint32_t> partial_metadata_received;
    // Incremented for every stream buffer requested for the given frame.
    // StreamBufferManager destructs the CaptureResult when
    // |unsubmitted_buffer_count| drops to zero.
    size_t unsubmitted_buffer_count;
    // The callback used to return the captured still capture JPEG buffer.  Set
    // if and only if the capture request was sent with a still capture buffer.
    VideoCaptureDevice::TakePhotoCallback still_capture_callback;
    // This map stores callbacks that can be used to return buffers for portrait
    // mode requests.
    TakePhotoCallbackMap portrait_callbacks_map;
  };

  RequestManager() = delete;

  RequestManager(const std::string& device_id,
                 mojo::PendingReceiver<cros::mojom::Camera3CallbackOps>
                     callback_ops_receiver,
                 std::unique_ptr<StreamCaptureInterface> capture_interface,
                 CameraDeviceContext* device_context,
                 VideoCaptureBufferType buffer_type,
                 std::unique_ptr<CameraBufferFactory> camera_buffer_factory,
                 BlobifyCallback blobify_callback,
                 scoped_refptr<base::SingleThreadTaskRunner> ipc_task_runner,
                 uint32_t device_api_version,
                 bool use_buffer_management_apis);

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

  ~RequestManager() override;

  // Sets up the stream context and allocate buffers according to the
  // configuration specified in |streams|.
  void SetUpStreamsAndBuffers(
      base::flat_map<ClientType, VideoCaptureParams> capture_params,
      const cros::mojom::CameraMetadataPtr& static_metadata,
      std::vector<cros::mojom::Camera3StreamPtr> streams);

  cros::mojom::Camera3StreamPtr GetStreamConfiguration(StreamType stream_type);

  bool HasStreamsConfiguredForTakePhoto();

  // StartPreview is the entry point to starting the video capture. The way
  // the video capture loop works is:
  //
  //  (1) Preparing capture request by mixing preview request, one-shot request
  //      and effect request if they exists. And build the capture request by
  //      RequestBuilder.
  //  (2) Once the capture request is built, it sends the capture request and
  //      it will go back to (1) to generate next capture request.
  //  (3) The camera HAL returns the shutter time of a capture request through
  //      Notify(), and the filled buffer through ProcessCaptureResult().
  //  (4) Once all the result metadata are collected, it would pass
  //      TrySubmitPendingBuffers() and SubmitCaptureResult() will be triggered
  //      to deliver the filled buffer to Chrome. After the buffer is consumed
  //      by Chrome it is enqueued back to the free buffer queue. Goto (1) to
  //      start another capture loop.
  void StartPreview(cros::mojom::CameraMetadataPtr preview_settings);

  // Stops the capture loop.  After StopPreview is called |callback_ops_| is
  // unbound, so no new capture request or result will be processed. It will
  // also try to trigger Flush() and pass the |callback| to it.
  void StopPreview(base::OnceCallback<void(int32_t)> callback);

  void TakePhoto(cros::mojom::CameraMetadataPtr settings,
                 VideoCaptureDevice::TakePhotoCallback callback);

  void TakePortraitPhoto(cros::mojom::CameraMetadataPtr settings,
                         TakePhotoCallbackMap callbacks_map);

  base::WeakPtr<RequestManager> GetWeakPtr();

  // CaptureMetadataDispatcher implementations.
  void AddResultMetadataObserver(ResultMetadataObserver* observer) override;
  void RemoveResultMetadataObserver(ResultMetadataObserver* observer) override;

  // Registers buffer to the camera HAL buffer pool.
  void OnNewBuffer(ClientType client_type,
                   cros::mojom::CameraBufferHandlePtr buffer);

  // Retires a buffer from the camera HAL buffer pool.
  void OnBufferRetired(ClientType client_type, uint64_t buffer_id);

  // Queues a capture setting that will be send along with the earliest next
  // capture request.
  void SetCaptureMetadata(cros::mojom::CameraMetadataTag tag,
                          cros::mojom::EntryType type,
                          size_t count,
                          std::vector<uint8_t> value) override;

  void SetRepeatingCaptureMetadata(cros::mojom::CameraMetadataTag tag,
                                   cros::mojom::EntryType type,
                                   size_t count,
                                   std::vector<uint8_t> value) override;

  void UnsetRepeatingCaptureMetadata(
      cros::mojom::CameraMetadataTag tag) override;

 private:
  friend class RequestManagerTest;

  // Puts Portrait Mode vendor tag into the metadata.
  void SetPortraitModeVendorKey(cros::mojom::CameraMetadataPtr* settings);

  // Puts JPEG orientation information into the metadata.
  void SetJpegOrientation(cros::mojom::CameraMetadataPtr* settings,
                          int32_t orientation);

  // Puts JPEG thumbnail size information into the metadata.
  void SetJpegThumbnailSize(cros::mojom::CameraMetadataPtr* settings) const;

  // Puts availability of Zero Shutter Lag into the metadata.
  void SetZeroShutterLag(cros::mojom::CameraMetadataPtr* settings,
                         bool enabled);

  // Prepares a capture request by mixing repeating request with one-shot/effect
  // request if it exists.
  void PrepareCaptureRequest();

  bool TryPreparePortraitModeRequest(std::set<StreamType>* stream_types,
                                     cros::mojom::CameraMetadataPtr* settings,
                                     TakePhotoCallbackMap* callbacks_map);

  bool TryPreparePreviewRequest(std::set<StreamType>* stream_types,
                                cros::mojom::CameraMetadataPtr* settings);

  bool TryPrepareOneShotRequest(
      std::set<StreamType>* stream_types,
      cros::mojom::CameraMetadataPtr* settings,
      VideoCaptureDevice::TakePhotoCallback* callback);

  bool TryPrepareRecordingRequest(std::set<StreamType>* stream_types);

  // Callback for ProcessCaptureRequest().
  void OnProcessedCaptureRequest(int32_t result);

  // ProcessCaptureResult receives the result metadata as well as the filled
  // buffer from camera HAL.  The result metadata may be divided and delivered
  // in several stages.  Before all the result metadata is received the
  // partial results are kept in |pending_results_|.
  void ProcessCaptureResult(
      cros::mojom::Camera3CaptureResultPtr result) override;

  // Checks if the pending buffers are ready to submit. Trigger
  // SubmitCaptureResult() if the buffers are ready to submit.
  void TrySubmitPendingBuffers(uint32_t frame_number);

  // Notify receives the shutter time of capture requests and various errors
  // from camera HAL.  The shutter time is used as the timestamp in the video
  // frame delivered to Chrome.
  void Notify(cros::mojom::Camera3NotifyMsgPtr message) override;

  void HandleNotifyError(uint32_t frame_number,
                         StreamType stream_type,
                         cros::mojom::Camera3ErrorMsgCode error_code);

  // RequestStreamBuffers receives output buffer requests and a callback to
  // receive results.
  void RequestStreamBuffers(
      std::vector<cros::mojom::Camera3BufferRequestPtr> buffer_reqs,
      RequestStreamBuffersCallback callback) override;

  // ReturnStreamBuffers receives returned output buffers.
  void ReturnStreamBuffers(
      std::vector<cros::mojom::Camera3StreamBufferPtr> buffers) override;

  // Submits the captured buffer of frame |frame_number_| for the given
  // |stream_type| to Chrome if all the required metadata and the captured
  // buffer are received.  After the buffer is submitted the function then
  // enqueues the buffer to free buffer queue for the next capture request.
  void SubmitCaptureResult(uint32_t frame_number,
                           StreamType stream_type,
                           cros::mojom::Camera3StreamBufferPtr stream_buffer);
  void SubmitCapturedPreviewRecordingBuffer(uint32_t frame_number,
                                            uint64_t buffer_ipc_id,
                                            StreamType stream_type);
  void SubmitCapturedJpegBuffer(uint32_t frame_number,
                                uint64_t buffer_ipc_id,
                                StreamType stream_type);

  // If there are some metadata set by SetCaptureMetadata() or
  // SetRepeatingCaptureMetadata(), update them onto |capture_settings|.
  void UpdateCaptureSettings(cros::mojom::CameraMetadataPtr* capture_settings);

  // The unique device id which is retrieved from VideoCaptureDeviceDescriptor.
  std::string device_id_;

  mojo::Receiver<cros::mojom::Camera3CallbackOps> callback_ops_;

  std::unique_ptr<StreamCaptureInterface> capture_interface_;

  raw_ptr<CameraDeviceContext> device_context_;

  bool zero_shutter_lag_supported_;

  bool video_capture_use_gmb_;

  // StreamBufferManager should be declared before RequestBuilder since
  // RequestBuilder holds an instance of StreamBufferManager and should be
  // destroyed first.
  std::unique_ptr<StreamBufferManager> stream_buffer_manager_;

  std::unique_ptr<RequestBuilder> request_builder_;

  BlobifyCallback blobify_callback_;

  // Where all the Mojo IPC calls takes place.
  const scoped_refptr<base::SingleThreadTaskRunner> ipc_task_runner_;

  // A flag indicating whether the capture loops is running.
  bool capturing_;

  // The number of partial stages.  |partial_result_count_| is learned by
  // querying |static_metadata_|.  In case the result count is absent in
  // |static_metadata_|, it defaults to one which means all the result
  // metadata and captured buffer of a frame are returned together in one
  // shot.
  uint32_t partial_result_count_;

  // The pipeline depth reported in the ANDROID_REQUEST_PIPELINE_MAX_DEPTH
  // metadata.
  size_t pipeline_depth_;

  // The number of preview buffers queued to the camera service.  The request
  // manager needs to try its best to queue |pipeline_depth_| preview buffers to
  // avoid camera frame drops.
  size_t preview_buffers_queued_;

  // The shutter time of the first frame.  We derive the |timestamp| of a
  // frame using the difference between the frame's shutter time and
  // |first_frame_shutter_time_|.
  base::TimeTicks first_frame_shutter_time_;

  // The repeating request settings.  The settings come from the default preview
  // request settings reported by the HAL.  |repeating_request_settings_| is the
  // default settings for each capture request.
  cros::mojom::CameraMetadataPtr repeating_request_settings_;

  // A queue of oneshot request settings.  These are the request settings for
  // each still capture requests.  |oneshot_request_settings_| overrides
  // |repeating_request_settings_| if present.
  std::queue<cros::mojom::CameraMetadataPtr> oneshot_request_settings_;

  // StreamBufferManager does not own the ResultMetadataObservers.  The
  // observers are responsible for removing itself before self-destruction.
  std::unordered_set<raw_ptr<ResultMetadataObserver, CtnExperimental>>
      result_metadata_observers_;

  // The list of settings to set/override once in the capture request.
  std::vector<cros::mojom::CameraMetadataEntryPtr> capture_settings_override_;

  // The settings to set/override repeatedly in the capture request.  In
  // conflict with |capture_settings_override_|, this one has lower priority.
  std::map<cros::mojom::CameraMetadataTag, cros::mojom::CameraMetadataEntryPtr>
      capture_settings_repeating_override_;

  // Stores the pending capture results of the current in-flight frames.
  std::map<uint32_t, CaptureResult> pending_results_;

  std::queue<cros::mojom::CameraMetadataPtr> take_photo_settings_queue_;

  // Callback for TakePhoto(). When preparing capture request, the callback will
  // be popped and moved to CaptureResult.
  std::queue<VideoCaptureDevice::TakePhotoCallback> take_photo_callback_queue_;

  // Map for Portrait Mode request callbacks.
  TakePhotoCallbackMap take_portrait_photo_callback_map_;

  // Map for retrieving the last received frame number. It is used to check for
  // duplicate or out of order of frames.
  std::map<StreamType, uint32_t> last_received_frame_number_map_;

  // The JPEG thumbnail size chosen for current stream configuration.
  gfx::Size jpeg_thumbnail_size_;

  base::WeakPtr<CameraAppDeviceImpl> camera_app_device_;

  // The API version of the camera device.
  uint32_t device_api_version_;

  // Set true if the buffer management APIs are enabled.
  bool use_buffer_management_apis_;

  base::WeakPtrFactory<RequestManager> weak_ptr_factory_{this};
};

}  // namespace media

#endif  // MEDIA_CAPTURE_VIDEO_CHROMEOS_REQUEST_MANAGER_H_