1 // An AV1 encoder using the SVT-AV1 encoder library. (libaom does not seem
2 // to be suitable for real-time streaming as of 2022, as it is not using
3 // threads particularly efficiently.) AV1 is a newer format than H.264,
4 // obviously both for better and for worse: Higher coding efficiency
5 // (with sufficient amount of cores, SVT-AV1 is even better than x264
6 // at higher frame rates), but generally smaller ecosystem, no speed
7 // control, less sophisticated rate control, etc..
9 // We don't support storing AV1 to disk currently, only to HTTP.
10 // We also don't support hardware AV1 encoding, as hardware supporting it
11 // is very rare currently.
13 // Since SVT-AV1 does not support VFR, you need to declare the frame rate
14 // up-front, using the --av1-framerate flag. If the given frame rate is
15 // too high (ie., you are producing frames too slowly), rate control will get
16 // confused and use too little bitrate. If it is too low, Nageru will need to
17 // drop frames before input to the encoder.
19 #ifndef _AV1_ENCODER_H
20 #define _AV1_ENCODER_H 1
25 #include <condition_variable>
31 #include <unordered_map>
35 #include <libavformat/avformat.h>
38 #include <movit/image_format.h>
41 #include "shared/metrics.h"
42 #include "print_latency.h"
43 #include "video_codec_interface.h"
46 struct EbBufferHeaderType;
47 struct EbComponentType;
49 class AV1Encoder : public VideoCodecInterface {
51 AV1Encoder(const AVOutputFormat *oformat); // Does not take ownership.
53 // Called after the last frame. Will block; once this returns,
54 // the last data is flushed.
55 ~AV1Encoder() override;
57 // Must be called before first frame. Does not take ownership.
58 void add_mux(Mux *mux) override { muxes.push_back(mux); }
60 // <data> is taken to be raw NV12 data of WIDTHxHEIGHT resolution.
62 void add_frame(int64_t pts, int64_t duration, movit::YCbCrLumaCoefficients ycbcr_coefficients, const uint8_t *data, const ReceivedTimestamps &received_ts) override;
64 std::string get_global_headers() const override {
65 while (!av1_init_done) {
68 return global_headers;
73 int64_t pts, duration;
74 movit::YCbCrLumaCoefficients ycbcr_coefficients;
76 ReceivedTimestamps received_ts;
78 void encoder_thread_func();
80 void encode_frame(QueuedFrame qf);
81 void process_packet(EbBufferHeaderType *buf); // Takes ownership.
83 // One big memory chunk of all 50 (or whatever) frames, allocated in
84 // the constructor. All data functions just use pointers into this
86 std::unique_ptr<uint8_t[]> frame_pool;
88 std::vector<Mux *> muxes;
89 const bool wants_global_headers;
91 std::string global_headers;
93 std::thread encoder_thread;
94 std::atomic<bool> av1_init_done{false};
95 std::atomic<bool> should_quit{false};
96 EbComponentType *encoder;
98 int64_t last_pts = -1;
100 // Protects everything below it.
103 // Frames that are not being encoded or waiting to be encoded,
104 // so that add_frame() can use new ones.
105 // TODO: Do we actually need this queue, or is SVT-AV1's queue
106 // non-blocking so that we can drop it?
107 std::queue<uint8_t *> free_frames;
109 // Frames that are waiting to be encoded (ie., add_frame() has been
110 // called, but they are not picked up for encoding yet).
111 std::queue<QueuedFrame> queued_frames;
113 // Whenever the state of <queued_frames> changes.
114 std::condition_variable queued_frames_nonempty;
116 // Key is the pts of the frame.
117 std::unordered_map<int64_t, ReceivedTimestamps> frames_being_encoded;
120 #endif // !defined(_AV1_ENCODER_H)