-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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