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