android13/external/openscreen/cast/standalone_sender/streaming_av1_encoder.h

170 lines
6.4 KiB
C++

// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CAST_STANDALONE_SENDER_STREAMING_AV1_ENCODER_H_
#define CAST_STANDALONE_SENDER_STREAMING_AV1_ENCODER_H_
#include <aom/aom_encoder.h>
#include <aom/aom_image.h>
#include <algorithm>
#include <condition_variable> // NOLINT
#include <functional>
#include <memory>
#include <mutex>
#include <queue>
#include <thread>
#include <vector>
#include "absl/base/thread_annotations.h"
#include "cast/standalone_sender/streaming_video_encoder.h"
#include "cast/streaming/constants.h"
#include "cast/streaming/frame_id.h"
#include "cast/streaming/rtp_time.h"
#include "platform/api/task_runner.h"
#include "platform/api/time.h"
namespace openscreen {
class TaskRunner;
namespace cast {
class Sender;
// Uses libaom to encode AV1 video and streams it to a Sender. Includes
// extensive logic for fine-tuning the encoder parameters in real-time, to
// provide the best quality results given external, uncontrollable factors:
// CPU/network availability, and the complexity of the video frame content.
//
// Internally, a separate encode thread is created and used to prevent blocking
// the main thread while frames are being encoded. All public API methods are
// assumed to be called on the same sequence/thread as the main TaskRunner
// (injected via the constructor).
//
// Usage:
//
// 1. EncodeAndSend() is used to queue-up video frames for encoding and sending,
// which will be done on a best-effort basis.
//
// 2. The client is expected to call SetTargetBitrate() frequently based on its
// own bandwidth estimates and congestion control logic. In addition, a client
// may provide a callback for each frame's encode statistics, which can be used
// to further optimize the user experience. For example, the stats can be used
// as a signal to reduce the data volume (i.e., resolution and/or frame rate)
// coming from the video capture source.
class StreamingAv1Encoder : public StreamingVideoEncoder {
public:
StreamingAv1Encoder(const Parameters& params,
TaskRunner* task_runner,
Sender* sender);
~StreamingAv1Encoder();
int GetTargetBitrate() const override;
void SetTargetBitrate(int new_bitrate) override;
void EncodeAndSend(const VideoFrame& frame,
Clock::time_point reference_time,
std::function<void(Stats)> stats_callback) override;
private:
// Syntactic convenience to wrap the aom_image_t alloc/free API in a smart
// pointer.
struct Av1ImageDeleter {
void operator()(aom_image_t* ptr) const { aom_img_free(ptr); }
};
using Av1ImageUniquePtr = std::unique_ptr<aom_image_t, Av1ImageDeleter>;
// Represents the state of one frame encode. This is created in
// EncodeAndSend(), and passed to the encode thread via the |encode_queue_|.
struct WorkUnit {
Av1ImageUniquePtr image;
Clock::duration duration;
Clock::time_point reference_time;
RtpTimeTicks rtp_timestamp;
std::function<void(Stats)> stats_callback;
};
// Same as WorkUnit, but with additional fields to carry the encode results.
struct WorkUnitWithResults : public WorkUnit {
std::vector<uint8_t> payload;
bool is_key_frame = false;
Stats stats;
};
bool is_encoder_initialized() const { return config_.g_threads != 0; }
// Destroys the AV1 encoder context if it has been initialized.
void DestroyEncoder();
// The procedure for the |encode_thread_| that loops, processing work units
// from the |encode_queue_| by calling Encode() until it's time to end the
// thread.
void ProcessWorkUnitsUntilTimeToQuit();
// If the |encoder_| is live, attempt reconfiguration to allow it to encode
// frames at a new frame size or target bitrate. If reconfiguration is not
// possible, destroy the existing instance and re-create a new |encoder_|
// instance.
void PrepareEncoder(int width, int height, int target_bitrate);
// Wraps the complex libaom aom_codec_encode() call using inputs from
// |work_unit| and populating results there.
void EncodeFrame(bool force_key_frame, WorkUnitWithResults& work_unit);
// Computes and populates |work_unit.stats| after the last call to
// EncodeFrame().
void ComputeFrameEncodeStats(Clock::duration encode_wall_time,
int target_bitrate,
WorkUnitWithResults& work_unit);
// Assembles and enqueues an EncodedFrame with the Sender on the main thread.
void SendEncodedFrame(WorkUnitWithResults results);
// Allocates a aom_image_t and copies the content from |frame| to it.
static Av1ImageUniquePtr CloneAsAv1Image(const VideoFrame& frame);
// The reference time of the first frame passed to EncodeAndSend().
Clock::time_point start_time_ = Clock::time_point::min();
// The RTP timestamp of the last frame that was pushed into the
// |encode_queue_| by EncodeAndSend(). This is used to check whether
// timestamps are monotonically increasing.
RtpTimeTicks last_enqueued_rtp_timestamp_;
// Guards a few members shared by both the main and encode threads.
std::mutex mutex_;
// Used by the encode thread to sleep until more work is available.
std::condition_variable cv_ ABSL_GUARDED_BY(mutex_);
// These encode parameters not passed in the WorkUnit struct because it is
// desirable for them to be applied as soon as possible, with the very next
// WorkUnit popped from the |encode_queue_| on the encode thread, and not to
// wait until some later WorkUnit is processed.
bool needs_key_frame_ ABSL_GUARDED_BY(mutex_) = true;
int target_bitrate_ ABSL_GUARDED_BY(mutex_) = 2 << 20; // Default: 2 Mbps.
// The queue of frame encodes. The size of this queue is implicitly bounded by
// EncodeAndSend(), where it checks for the total in-flight media duration and
// maybe drops a frame.
std::queue<WorkUnit> encode_queue_ ABSL_GUARDED_BY(mutex_);
// Current AV1 encoder configuration. Most of the fields are unchanging, and
// are populated in the ctor; but thereafter, only the encode thread accesses
// this struct.
//
// The speed setting is controlled via a separate libaom API (see members
// below).
aom_codec_enc_cfg_t config_{};
// libaom AV1 encoder instance. Only the encode thread accesses this.
aom_codec_ctx_t encoder_;
};
} // namespace cast
} // namespace openscreen
#endif // CAST_STANDALONE_SENDER_STREAMING_AV1_ENCODER_H_