xref: /aosp_15_r20/external/openscreen/cast/standalone_sender/streaming_av1_encoder.h (revision 3f982cf4871df8771c9d4abe6e9a6f8d829b2736)
1 // Copyright 2021 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef CAST_STANDALONE_SENDER_STREAMING_AV1_ENCODER_H_
6 #define CAST_STANDALONE_SENDER_STREAMING_AV1_ENCODER_H_
7 
8 #include <aom/aom_encoder.h>
9 #include <aom/aom_image.h>
10 
11 #include <algorithm>
12 #include <condition_variable>  // NOLINT
13 #include <functional>
14 #include <memory>
15 #include <mutex>
16 #include <queue>
17 #include <thread>
18 #include <vector>
19 
20 #include "absl/base/thread_annotations.h"
21 #include "cast/standalone_sender/streaming_video_encoder.h"
22 #include "cast/streaming/constants.h"
23 #include "cast/streaming/frame_id.h"
24 #include "cast/streaming/rtp_time.h"
25 #include "platform/api/task_runner.h"
26 #include "platform/api/time.h"
27 
28 namespace openscreen {
29 
30 class TaskRunner;
31 
32 namespace cast {
33 
34 class Sender;
35 
36 // Uses libaom to encode AV1 video and streams it to a Sender. Includes
37 // extensive logic for fine-tuning the encoder parameters in real-time, to
38 // provide the best quality results given external, uncontrollable factors:
39 // CPU/network availability, and the complexity of the video frame content.
40 //
41 // Internally, a separate encode thread is created and used to prevent blocking
42 // the main thread while frames are being encoded. All public API methods are
43 // assumed to be called on the same sequence/thread as the main TaskRunner
44 // (injected via the constructor).
45 //
46 // Usage:
47 //
48 // 1. EncodeAndSend() is used to queue-up video frames for encoding and sending,
49 // which will be done on a best-effort basis.
50 //
51 // 2. The client is expected to call SetTargetBitrate() frequently based on its
52 // own bandwidth estimates and congestion control logic. In addition, a client
53 // may provide a callback for each frame's encode statistics, which can be used
54 // to further optimize the user experience. For example, the stats can be used
55 // as a signal to reduce the data volume (i.e., resolution and/or frame rate)
56 // coming from the video capture source.
57 class StreamingAv1Encoder : public StreamingVideoEncoder {
58  public:
59   StreamingAv1Encoder(const Parameters& params,
60                       TaskRunner* task_runner,
61                       Sender* sender);
62 
63   ~StreamingAv1Encoder();
64 
65   int GetTargetBitrate() const override;
66   void SetTargetBitrate(int new_bitrate) override;
67   void EncodeAndSend(const VideoFrame& frame,
68                      Clock::time_point reference_time,
69                      std::function<void(Stats)> stats_callback) override;
70 
71  private:
72   // Syntactic convenience to wrap the aom_image_t alloc/free API in a smart
73   // pointer.
74   struct Av1ImageDeleter {
operatorAv1ImageDeleter75     void operator()(aom_image_t* ptr) const { aom_img_free(ptr); }
76   };
77   using Av1ImageUniquePtr = std::unique_ptr<aom_image_t, Av1ImageDeleter>;
78 
79   // Represents the state of one frame encode. This is created in
80   // EncodeAndSend(), and passed to the encode thread via the |encode_queue_|.
81   struct WorkUnit {
82     Av1ImageUniquePtr image;
83     Clock::duration duration;
84     Clock::time_point reference_time;
85     RtpTimeTicks rtp_timestamp;
86     std::function<void(Stats)> stats_callback;
87   };
88 
89   // Same as WorkUnit, but with additional fields to carry the encode results.
90   struct WorkUnitWithResults : public WorkUnit {
91     std::vector<uint8_t> payload;
92     bool is_key_frame = false;
93     Stats stats;
94   };
95 
is_encoder_initialized()96   bool is_encoder_initialized() const { return config_.g_threads != 0; }
97 
98   // Destroys the AV1 encoder context if it has been initialized.
99   void DestroyEncoder();
100 
101   // The procedure for the |encode_thread_| that loops, processing work units
102   // from the |encode_queue_| by calling Encode() until it's time to end the
103   // thread.
104   void ProcessWorkUnitsUntilTimeToQuit();
105 
106   // If the |encoder_| is live, attempt reconfiguration to allow it to encode
107   // frames at a new frame size or target bitrate. If reconfiguration is not
108   // possible, destroy the existing instance and re-create a new |encoder_|
109   // instance.
110   void PrepareEncoder(int width, int height, int target_bitrate);
111 
112   // Wraps the complex libaom aom_codec_encode() call using inputs from
113   // |work_unit| and populating results there.
114   void EncodeFrame(bool force_key_frame, WorkUnitWithResults& work_unit);
115 
116   // Computes and populates |work_unit.stats| after the last call to
117   // EncodeFrame().
118   void ComputeFrameEncodeStats(Clock::duration encode_wall_time,
119                                int target_bitrate,
120                                WorkUnitWithResults& work_unit);
121 
122   // Assembles and enqueues an EncodedFrame with the Sender on the main thread.
123   void SendEncodedFrame(WorkUnitWithResults results);
124 
125   // Allocates a aom_image_t and copies the content from |frame| to it.
126   static Av1ImageUniquePtr CloneAsAv1Image(const VideoFrame& frame);
127 
128   // The reference time of the first frame passed to EncodeAndSend().
129   Clock::time_point start_time_ = Clock::time_point::min();
130 
131   // The RTP timestamp of the last frame that was pushed into the
132   // |encode_queue_| by EncodeAndSend(). This is used to check whether
133   // timestamps are monotonically increasing.
134   RtpTimeTicks last_enqueued_rtp_timestamp_;
135 
136   // Guards a few members shared by both the main and encode threads.
137   std::mutex mutex_;
138 
139   // Used by the encode thread to sleep until more work is available.
140   std::condition_variable cv_ ABSL_GUARDED_BY(mutex_);
141 
142   // These encode parameters not passed in the WorkUnit struct because it is
143   // desirable for them to be applied as soon as possible, with the very next
144   // WorkUnit popped from the |encode_queue_| on the encode thread, and not to
145   // wait until some later WorkUnit is processed.
146   bool needs_key_frame_ ABSL_GUARDED_BY(mutex_) = true;
147   int target_bitrate_ ABSL_GUARDED_BY(mutex_) = 2 << 20;  // Default: 2 Mbps.
148 
149   // The queue of frame encodes. The size of this queue is implicitly bounded by
150   // EncodeAndSend(), where it checks for the total in-flight media duration and
151   // maybe drops a frame.
152   std::queue<WorkUnit> encode_queue_ ABSL_GUARDED_BY(mutex_);
153 
154   // Current AV1 encoder configuration. Most of the fields are unchanging, and
155   // are populated in the ctor; but thereafter, only the encode thread accesses
156   // this struct.
157   //
158   // The speed setting is controlled via a separate libaom API (see members
159   // below).
160   aom_codec_enc_cfg_t config_{};
161 
162   // libaom AV1 encoder instance. Only the encode thread accesses this.
163   aom_codec_ctx_t encoder_;
164 };
165 
166 }  // namespace cast
167 }  // namespace openscreen
168 
169 #endif  // CAST_STANDALONE_SENDER_STREAMING_AV1_ENCODER_H_
170