Skip to content

Instantly share code, notes, and snippets.

@chobie
Created October 16, 2023 02:55
Show Gist options
  • Save chobie/49d219a6c658de3667231016b8c1d5e8 to your computer and use it in GitHub Desktop.
Save chobie/49d219a6c658de3667231016b8c1d5e8 to your computer and use it in GitHub Desktop.
at first, install libaom. then build libdatachannel normaly. it will generate av1-sender binary
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d4d29eb1..b0dd6c3e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -536,6 +536,7 @@ if(NOT NO_EXAMPLES)
if(NOT NO_MEDIA)
add_subdirectory(examples/media-receiver)
add_subdirectory(examples/media-sender)
+ add_subdirectory(examples/av1-sender)
add_subdirectory(examples/media-sfu)
endif()
if(NOT NO_MEDIA AND NOT NO_WEBSOCKET)
diff --git a/cmake/Modules/FindLibAom.cmake b/cmake/Modules/FindLibAom.cmake
new file mode 100644
index 00000000..076e9277
--- /dev/null
+++ b/cmake/Modules/FindLibAom.cmake
@@ -0,0 +1,18 @@
+include(FindPackageHandleStandardArgs)
+
+find_path(
+ LIBAOM_INCLUDE_DIR
+ NAMES aom/aom.h
+ PATHS /usr/include /usr/local/include)
+
+find_package_handle_standard_args(
+ libaom
+ REQUIRED_VARS LIBAOM_INCLUDE_DIR)
+mark_as_advanced(LIBAOM_INCLUDE_DIR)
+
+if(libaom_FOUND)
+ if(NOT TARGET libaom::libaom)
+ add_library(libaom::libaom INTERFACE IMPORTED)
+ set_target_properties(libaom::libaom PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${aom_INCLUDE_DIR}")
+ endif()
+endif()
diff --git a/examples/av1-sender/CMakeLists.txt b/examples/av1-sender/CMakeLists.txt
new file mode 100644
index 00000000..0b197eea
--- /dev/null
+++ b/examples/av1-sender/CMakeLists.txt
@@ -0,0 +1,22 @@
+cmake_minimum_required(VERSION 3.7)
+
+add_executable(datachannel-av1-sender main.cpp)
+set_target_properties(datachannel-av1-sender PROPERTIES
+ CXX_STANDARD 17
+ OUTPUT_NAME av1-sender)
+
+set_target_properties(datachannel-av1-sender PROPERTIES
+ XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER com.github.paullouisageneau.libdatachannel.examples.av1-sender)
+
+find_package(Threads REQUIRED)
+find_package(libaom REQUIRED)
+
+target_link_libraries(datachannel-av1-sender LibDataChannel::LibDataChannel Threads::Threads nlohmann_json::nlohmann_json libaom::libaom ${LIBAOM_LIBRARY})
+
+if(MSVC)
+ add_custom_command(TARGET datachannel-av1-sender POST_BUILD
+ COMMAND ${CMAKE_COMMAND} -E copy_if_different
+ "$<TARGET_FILE_DIR:datachannel>/datachannel.dll"
+ $<TARGET_FILE_DIR:datachannel-media-sender>
+ )
+endif()
diff --git a/examples/av1-sender/main.cpp b/examples/av1-sender/main.cpp
new file mode 100644
index 00000000..fd3e8945
--- /dev/null
+++ b/examples/av1-sender/main.cpp
@@ -0,0 +1,499 @@
+#include "rtc/rtc.hpp"
+
+#include <cstddef>
+#include <iostream>
+#include <memory>
+#include <stdexcept>
+#include <utility>
+
+#include <nlohmann/json.hpp>
+
+#include <unistd.h>
+#include <aom/aomcx.h>
+#include <aom/aom_encoder.h>
+#include <aom/aom_image.h>
+
+
+#ifdef _WIN32
+#define _WINSOCK_DEPRECATED_NO_WARNINGS
+#include <winsock2.h>
+#else
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+typedef int SOCKET;
+#endif
+
+using nlohmann::json;
+
+typedef struct {
+ uint8_t *y;
+ uint8_t *u;
+ uint8_t *v;
+ size_t y_size;
+ size_t uv_size;
+ size_t width;
+ size_t height;
+ int fps;
+
+ aom_codec_iface_t *iface;
+ aom_codec_enc_cfg_t cfg;
+ aom_codec_ctx_t codec;
+ aom_image_t raw;
+} aomutil_ctx;
+
+// Encoder configuration parameters
+static int kQpMin = 10;
+static int kUsageProfile = AOM_USAGE_REALTIME;
+static int kMinQindex = 145; // Min qindex threshold for QP scaling.
+static int kMaxQindex = 205; // Max qindex threshold for QP scaling.
+static int kBitDepth = 8;
+static int kLagInFrames = 0; // No look ahead.
+static int kRtpTicksPerSecond = 90000;
+static double kMinimumFrameRate = 1.0;
+
+
+aomutil_ctx* aomutil_init(const int width, const int height, const int fps);
+void aomutil_rgb2yuv(aomutil_ctx* ctx, uint8_t* rgb);
+void aomutil_copyyuv(aomutil_ctx* ctx);
+typedef int (*aomutil_encode_cb)(const uint8_t* buffer, size_t size, int64_t pts);
+int aomutil_encode_frame(aomutil_ctx *ctx, int frame_index, int flags, aomutil_encode_cb writer_cb);
+int aomutil_free(aomutil_ctx* ctx);
+
+static int error_codec(aom_codec_ctx_t *ctx, const char *s) {
+ const char *detail = aom_codec_error_detail(ctx);
+ printf("%s: %s\n", s, aom_codec_error(ctx));
+ if (detail) {
+ printf(" %s\n", detail);
+ }
+ return EXIT_FAILURE;
+}
+
+aomutil_ctx* aomutil_init(const int width, const int height, int fps) {
+ aomutil_ctx* ptr = (aomutil_ctx*)malloc(sizeof(aomutil_ctx));
+
+ memset(ptr, 0, sizeof(aomutil_ctx));
+
+
+ ptr->width = width;
+ ptr->height = height;
+ ptr->fps = fps;
+
+ ptr->iface = aom_codec_av1_cx();
+ ptr->y_size = width * height;
+ ptr->uv_size = (width / 2) * (height / 2);
+
+ ptr->y = (uint8_t *)malloc(ptr->y_size);
+ ptr->u = (uint8_t *)malloc(ptr->uv_size);
+ ptr->v = (uint8_t *)malloc(ptr->uv_size);
+
+ if (aom_codec_enc_config_default(ptr->iface, &ptr->cfg, 1)) {
+ fprintf(stderr, "Failed to get default codec config.\n");
+ return NULL;
+ }
+
+
+ if (ptr->width <= 0 || ptr->height <= 0 ||
+ (ptr->width % 2) != 0 || (ptr->height % 2) != 0) {
+ fprintf(stderr, "Invalid frame size: %zux%zu", ptr->width, ptr->height);
+ return NULL;
+ }
+
+
+ // these settings comes from libwebrtc source code.
+ // https://webrtc.googlesource.com/src/+/refs/heads/main/modules/video_coding/codecs/av1/libaom_av1_encoder.cc
+ ptr->cfg.rc_end_usage = AOM_CBR;
+ ptr->cfg.rc_target_bitrate = 1000;
+ ptr->cfg.g_w = width;
+ ptr->cfg.g_h = height;
+ ptr->cfg.g_threads = 8;
+ //cfg.g_profile = 1; // High profile
+ ptr->cfg.g_timebase.num = 1;
+ ptr->cfg.g_timebase.den = fps;
+ ptr->cfg.kf_mode = AOM_KF_AUTO;
+
+ ptr->cfg.g_input_bit_depth = kBitDepth;
+ ptr->cfg.kf_min_dist = 0;
+ ptr->cfg.kf_max_dist = 60;
+ ptr->cfg.kf_mode = AOM_KF_DISABLED;
+ //ptr->cfg.rc_min_quantizer = kQpMin;
+ //ptr->cfg.rc_max_quantizer = encoder_settings_.qpMax;
+ ptr->cfg.rc_undershoot_pct = 50;
+ ptr->cfg.rc_overshoot_pct = 50;
+ ptr->cfg.rc_buf_initial_sz = 600;
+ ptr->cfg.rc_buf_optimal_sz = 600;
+ ptr->cfg.rc_buf_sz = 1000;
+ ptr->cfg.g_usage = kUsageProfile;
+ ptr->cfg.g_error_resilient = 0;
+ // Low-latency settings.
+ ptr->cfg.rc_end_usage = AOM_CBR; // Constant Bit Rate (CBR) mode
+ ptr->cfg.g_pass = AOM_RC_ONE_PASS; // One-pass rate control
+ ptr->cfg.g_lag_in_frames = kLagInFrames; // No look ahead when lag equals 0.
+
+ // cfg.rc_target_bitrate = bitrate;
+
+ if (aom_codec_enc_init(&ptr->codec, ptr->iface, &ptr->cfg, 0)) {
+ fprintf(stderr, "Failed to initialize codec.\n");
+ return NULL;
+ }
+
+ printf("Using %s\n", aom_codec_iface_name(ptr->iface));
+
+ if (aom_codec_control(&ptr->codec, AOME_SET_CPUUSED, 7)) {
+ fprintf(stderr, "Failed to set cpu-used");
+ return NULL;
+ }
+ if (aom_codec_control(&ptr->codec, AV1E_SET_ENABLE_CDEF, 1)) {
+ fprintf(stderr, "set enable cdef");
+ return NULL;
+ }
+ if (aom_codec_control(&ptr->codec, AV1E_SET_ENABLE_TPL_MODEL, 0)) {
+ fprintf(stderr, "set enable cdef");
+ return NULL;
+ }
+ if (aom_codec_control(&ptr->codec, AV1E_SET_DELTAQ_MODE, 0)) {
+ fprintf(stderr, "set enable cdef");
+ return NULL;
+ }
+ if (aom_codec_control(&ptr->codec, AV1E_SET_ENABLE_ORDER_HINT, 0)) {
+ fprintf(stderr, "set enable cdef");
+ return NULL;
+ }
+ if (aom_codec_control(&ptr->codec, AV1E_SET_AQ_MODE, 0)) {
+ fprintf(stderr, "set enable cdef");
+ return NULL;
+ }
+ if (aom_codec_control(&ptr->codec, AV1E_SET_AQ_MODE, 3)) {
+ fprintf(stderr, "set enable cdef");
+ return NULL;
+ }
+ if (aom_codec_control(&ptr->codec, AOME_SET_MAX_INTRA_BITRATE_PCT, 300)) {
+ fprintf(stderr, "set enable cdef");
+ return NULL;
+ }
+ if (aom_codec_control(&ptr->codec, AV1E_SET_COEFF_COST_UPD_FREQ, 3)) {
+ fprintf(stderr, "set enable cdef");
+ return NULL;
+ }
+ if (aom_codec_control(&ptr->codec, AV1E_SET_MODE_COST_UPD_FREQ, 3)) {
+ fprintf(stderr, "set enable cdef");
+ return NULL;
+ }
+ if (aom_codec_control(&ptr->codec, AV1E_SET_MV_COST_UPD_FREQ, 3)) {
+ fprintf(stderr, "set enable cdef");
+ return NULL;
+ }
+
+ if (aom_codec_control(&ptr->codec, AV1E_SET_TILE_ROWS, 1)) {
+ fprintf(stderr, "set enable cdef");
+ return NULL;
+ }
+ if (aom_codec_control(&ptr->codec, AV1E_SET_TILE_COLUMNS, 2)) {
+ fprintf(stderr, "set enable cdef");
+ return NULL;
+ }
+ if (aom_codec_control(&ptr->codec, AV1E_SET_ROW_MT, 1)) {
+ fprintf(stderr, "set enable cdef");
+ return NULL;
+ }
+ if (aom_codec_control(&ptr->codec, AV1E_SET_ENABLE_OBMC, 0)) {
+ fprintf(stderr, "set enable cdef");
+ return NULL;
+ }
+ if (aom_codec_control(&ptr->codec, AV1E_SET_NOISE_SENSITIVITY, 0)) {
+ fprintf(stderr, "set enable cdef");
+ return NULL;
+ }
+ if (aom_codec_control(&ptr->codec, AV1E_SET_ENABLE_WARPED_MOTION, 0)) {
+ fprintf(stderr, "set enable cdef");
+ return NULL;
+ }
+ if (aom_codec_control(&ptr->codec, AV1E_SET_ENABLE_GLOBAL_MOTION, 0)) {
+ fprintf(stderr, "set enable cdef");
+ return NULL;
+ }
+ if (aom_codec_control(&ptr->codec, AV1E_SET_ENABLE_REF_FRAME_MVS, 0)) {
+ fprintf(stderr, "set enable cdef");
+ return NULL;
+ }
+ aom_codec_control(&ptr->codec, AV1E_SET_ENABLE_CFL_INTRA, 0);
+ aom_codec_control(&ptr->codec, AV1E_SET_ENABLE_SMOOTH_INTRA, 0);
+ aom_codec_control(&ptr->codec, AV1E_SET_ENABLE_ANGLE_DELTA, 0);
+ aom_codec_control(&ptr->codec, AV1E_SET_ENABLE_FILTER_INTRA, 0);
+ aom_codec_control(&ptr->codec, AV1E_SET_INTRA_DEFAULT_TX_ONLY, 1);
+ aom_codec_control(&ptr->codec, AV1E_SET_DISABLE_TRELLIS_QUANT, 1);
+ aom_codec_control(&ptr->codec, AV1E_SET_ENABLE_DIST_WTD_COMP, 0);
+ aom_codec_control(&ptr->codec, AV1E_SET_ENABLE_DIFF_WTD_COMP, 0);
+ aom_codec_control(&ptr->codec, AV1E_SET_ENABLE_DUAL_FILTER, 0);
+ aom_codec_control(&ptr->codec, AV1E_SET_ENABLE_INTERINTRA_COMP, 0);
+ aom_codec_control(&ptr->codec, AV1E_SET_ENABLE_INTERINTRA_WEDGE, 0);
+ aom_codec_control(&ptr->codec, AV1E_SET_ENABLE_INTRA_EDGE_FILTER, 0);
+ aom_codec_control(&ptr->codec, AV1E_SET_ENABLE_INTRABC, 0);
+ aom_codec_control(&ptr->codec, AV1E_SET_ENABLE_MASKED_COMP, 0);
+ aom_codec_control(&ptr->codec, AV1E_SET_ENABLE_PAETH_INTRA, 0);
+ aom_codec_control(&ptr->codec, AV1E_SET_ENABLE_QM, 0);
+ aom_codec_control(&ptr->codec, AV1E_SET_ENABLE_RECT_PARTITIONS, 0);
+ aom_codec_control(&ptr->codec, AV1E_SET_ENABLE_RESTORATION, 0);
+ aom_codec_control(&ptr->codec, AV1E_SET_ENABLE_SMOOTH_INTERINTRA, 0);
+ aom_codec_control(&ptr->codec, AV1E_SET_ENABLE_TX64, 0);
+ aom_codec_control(&ptr->codec, AV1E_SET_MAX_REFERENCE_FRAMES, 3);
+
+ if (!aom_img_alloc(&ptr->raw, AOM_IMG_FMT_I420, width, height, 1)) {
+ fprintf(stderr, "Failed to allocate image.\n");
+ return NULL;
+ }
+
+ ptr->raw.stride[AOM_PLANE_Y] = width;
+ ptr->raw.stride[AOM_PLANE_U] = width / 2;
+ ptr->raw.stride[AOM_PLANE_V] = width / 2;
+
+ return ptr;
+}
+
+int aomutil_encode_frame(aomutil_ctx* context, int frame_index, int flags, aomutil_encode_cb writer_cb) {
+ int got_pkts = 0;
+ aom_codec_iter_t iter = NULL;
+ const aom_codec_cx_pkt_t *pkt = NULL;
+ aom_codec_err_t error;
+
+ aom_image_t* raw = &context->raw;
+ if (frame_index < 0) {
+ raw = NULL;
+ }
+ error = aom_codec_encode(&context->codec, raw, frame_index, 60000, flags);
+ if (error != AOM_CODEC_OK) {
+ return error_codec(&context->codec, "Failed to encode frame.\n");
+ }
+
+ while ((pkt = aom_codec_get_cx_data(&context->codec, &iter))) {
+ got_pkts = 1;
+
+ if (pkt->kind == AOM_CODEC_CX_FRAME_PKT) {
+ const int keyframe = (pkt->data.frame.flags & AOM_FRAME_IS_KEY) != 0;
+
+ // NOTE: This is the original code that writes the whole frame
+ // if (writer_cb != NULL && !writer_cb((const uint8_t*)pkt->data.frame.buf, pkt->data.frame.sz, pkt->data.frame.pts)) {
+ // return error_codec(&context->codec, "Failed to write compressed frame");
+ // }
+
+
+ // NOTE: obu unit split version.
+ {
+ uint8_t obuHeader = ((uint8_t*)(pkt->data.frame.buf))[0];
+ uint8_t obuType = (obuHeader & 0b01111000) >> 3;
+ fprintf(stderr, "OBU Type: %d keyframe: %d\n", obuType, keyframe);
+
+ const uint8_t *obu_data = (uint8_t*)pkt->data.frame.buf;
+ size_t obu_size = pkt->data.frame.sz;
+ const uint8_t *end = obu_data + obu_size;
+
+ while (obu_data < end) {
+ const uint8_t *obu_unit_start = obu_data;
+ uint8_t obu_header = *obu_data++;
+ int obu_type = (obu_header >> 3) & 0x0F;
+ int obu_has_size_field = obu_header & 0x02;
+
+ size_t obu_payload_size = 0;
+ if (obu_has_size_field) {
+ int byte_count = 0;
+ while (obu_data < end) {
+ uint8_t leb128_byte = *obu_data++;
+ obu_payload_size |= (leb128_byte & 0x7F) << (byte_count * 7);
+ if ((leb128_byte & 0x80) == 0) {
+ break;
+ }
+ byte_count++;
+ }
+ }
+
+ size_t obu_unit_size = (obu_data - obu_unit_start) + obu_payload_size;
+ if (writer_cb != NULL && !writer_cb(obu_unit_start, obu_unit_size, pkt->data.frame.pts)) {
+ return error_codec(&context->codec, "Failed to write compressed frame");
+ }
+
+ obu_data += obu_payload_size;
+ }
+ fprintf(stderr, "process obu units end\n");
+ }
+ }
+ }
+
+ return got_pkts;
+}
+
+static void rgb_to_yuv420(uint8_t *rgb, uint8_t *y, uint8_t *u, uint8_t *v, int width, int height) {
+ for (int j = 0; j < height; j++) {
+ for (int i = 0; i < width; i++) {
+ int r = rgb[3 * (j * width + i)];
+ int g = rgb[3 * (j * width + i) + 1];
+ int b = rgb[3 * (j * width + i) + 2];
+
+ int yIndex = j * width + i;
+ int uvIndex = (j / 2) * (width / 2) + (i / 2);
+
+ y[yIndex] = (uint8_t)((0.257 * r) + (0.504 * g) + (0.098 * b) + 16);
+
+ if (j % 2 == 0 && i % 2 == 0) { // Only sample UV for every 2x2 block
+ u[uvIndex] = (uint8_t)(-(0.148 * r) - (0.291 * g) + (0.439 * b) + 128);
+ v[uvIndex] = (uint8_t)((0.439 * r) - (0.368 * g) - (0.071 * b) + 128);
+ }
+ }
+ }
+}
+
+
+void aomutil_rgb2yuv(aomutil_ctx* ctx, uint8_t* rgb) {
+ rgb_to_yuv420(rgb, ctx->y, ctx->u, ctx->v, ctx->width, ctx->height);
+ aomutil_copyyuv(ctx);
+}
+
+void aomutil_copyyuv(aomutil_ctx* ctx) {
+ memcpy(ctx->raw.planes[AOM_PLANE_Y], ctx->y, ctx->y_size);
+ memcpy(ctx->raw.planes[AOM_PLANE_U], ctx->u, ctx->uv_size);
+ memcpy(ctx->raw.planes[AOM_PLANE_V], ctx->v, ctx->uv_size);
+}
+
+int aomutil_free(aomutil_ctx* ctx) {
+ if (ctx != NULL) {
+
+ free(ctx->y);
+ free(ctx->u);
+ free(ctx->v);
+ ctx->y = NULL;
+ ctx->u = NULL;
+ ctx->v = NULL;
+
+
+ aom_img_free(&ctx->raw);
+ aom_codec_destroy(&ctx->codec);
+
+ free(ctx);
+ ctx = NULL;
+ }
+
+ return 0;
+}
+
+std::shared_ptr<rtc::Track> track;
+std::shared_ptr<rtc::RtpPacketizationConfig> rtpConfig;
+
+static int cb(const uint8_t* buffer, size_t size, int64_t pts) {
+ std::cout << "Encoded frame: " << size << " bytes" << std::endl;
+
+ // NOTE: simply add 3000 clock (90000hz / 30fps). this is not correct. but it works.
+ rtpConfig->timestamp += 3000;
+ track->send((const rtc::byte*)buffer, size);
+ return 1;
+}
+
+static void random_fill(uint8_t* buffer, int width, int height) {
+ for (int j = 0; j < height; j++) {
+ for (int i = 0; i < width; i++) {
+ buffer[3 * (j * width + i)] = rand() % 256;
+ buffer[3 * (j * width + i) + 1] = rand() % 256;
+ buffer[3 * (j * width + i) + 2] = rand() % 256;
+ }
+ }
+}
+
+// this is very rough av1 sender example.
+//
+// at first, open media-sender/main.html in chrome.
+// then, run this program `./av1-sender`
+// copy the sdp message from terminal. and copy it to chrome.
+// also copy answer in chrome to terminal.
+// finally, chrome will show the randomized video image.
+int main() {
+ int width = 640;
+ int height = 480;
+ int fps = 30;
+ // you can tweek keyfram interval. 1 means always encode keyframe.
+ // 30 means encode keyframe interval is 1sec (30fps)
+ int keyframe_interval = 30;
+
+ try {
+ rtc::InitLogger(rtc::LogLevel::Debug);
+ auto pc = std::make_shared<rtc::PeerConnection>();
+
+ pc->onStateChange(
+ [](rtc::PeerConnection::State state) { std::cout << "State: " << state << std::endl; });
+
+ pc->onGatheringStateChange([pc](rtc::PeerConnection::GatheringState state) {
+ std::cout << "Gathering State: " << state << std::endl;
+ if (state == rtc::PeerConnection::GatheringState::Complete) {
+ auto description = pc->localDescription();
+ json message = {{"type", description->typeString()},
+ {"sdp", std::string(description.value())}};
+ std::cout << message << std::endl;
+ }
+ });
+
+ const rtc::SSRC ssrc = 42;
+ const int payload_type = 45;
+ rtc::Description::Video media("video", rtc::Description::Direction::SendOnly);
+ media.addAV1Codec(payload_type); // Must match the payload type of the external h264 RTP stream
+ media.addSSRC(ssrc, "video-send");
+ track = pc->addTrack(media);
+
+
+ auto init = rtcPacketizationHandlerInit{0};
+ init.obuPacketization = RTC_OBU_PACKETIZED_OBU;
+ init.clockRate = 90 * 1000;
+ init.payloadType = payload_type;
+ init.ssrc = ssrc;
+ init.cname = "video";
+ rtpConfig = std::make_shared<rtc::RtpPacketizationConfig>(init.ssrc, init.cname,
+ init.payloadType, init.clockRate);
+ rtpConfig->sequenceNumber = init.sequenceNumber;
+ rtpConfig->timestamp = init.timestamp;
+ auto maxFragmentSize = init.maxFragmentSize ? init.maxFragmentSize
+ : RTC_DEFAULT_MAXIMUM_FRAGMENT_SIZE;
+ auto packetization = init.obuPacketization == RTC_OBU_PACKETIZED_TEMPORAL_UNIT
+ ? rtc::AV1RtpPacketizer::Packetization::TemporalUnit
+ : rtc::AV1RtpPacketizer::Packetization::Obu;
+ auto packetizer =
+ std::make_shared<rtc::AV1RtpPacketizer>(packetization, rtpConfig, maxFragmentSize);
+ auto av1Handler = std::make_shared<rtc::AV1PacketizationHandler>(packetizer);
+ track->setMediaHandler(av1Handler);
+
+ pc->setLocalDescription();
+
+ std::cout << "RTP video stream expected on localhost:6000" << std::endl;
+ std::cout << "Please copy/paste the answer provided by the browser: " << std::endl;
+ std::string sdp;
+ std::getline(std::cin, sdp);
+
+ json j = json::parse(sdp);
+ rtc::Description answer(j["sdp"].get<std::string>(), j["type"].get<std::string>());
+ pc->setRemoteDescription(answer);
+
+
+ aomutil_ctx* ctx = aomutil_init(width, height, fps);
+ size_t size = width * height * 3 * sizeof(uint8_t);
+ uint8_t* buffer = (uint8_t*)malloc(size);
+ memset(buffer, 0, size);
+
+ int frame_index = 0;
+
+ // wait signaling...
+ usleep(3000000);
+
+ while (true) {
+ // fill random data to image buffer
+ random_fill(buffer, width, height);
+ // copy rgb data into aom yuv buffer
+ aomutil_rgb2yuv(ctx, buffer);
+
+ int flags = 0;
+ if (frame_index % keyframe_interval == 0) {
+ flags |= AOM_EFLAG_FORCE_KF;
+ }
+ // then, encode frame and send it.
+ aomutil_encode_frame(ctx, frame_index++, flags, cb);
+ usleep(32000);
+ }
+
+ } catch (const std::exception &e) {
+ std::cerr << "Error: " << e.what() << std::endl;
+ }
+}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment