From f83f679fbd8a23630f89cf5e047f67b701018dc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gast=C3=B3n=20Bogar=C3=ADn?= Date: Mon, 27 Jul 2026 14:59:39 -0300 Subject: [PATCH 1/5] integrate moq5's codec signaling library --- CMakeLists.txt | 3 +- src/codec-signaling.h | 81 +++++++++++++++++++++++++++++++++++++++++++ src/moq-output.cpp | 27 +++++++-------- src/utils.h | 49 -------------------------- 4 files changed, 95 insertions(+), 65 deletions(-) create mode 100644 src/codec-signaling.h delete mode 100644 src/utils.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 2fdc58f..ec56176 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,7 +19,7 @@ find_package(libobs REQUIRED) target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE OBS::libobs) find_package(libmoq REQUIRED COMPONENTS service) -target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE moq::service) +target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE moq::service moq::codec-signaling) if(ENABLE_FRONTEND_API) find_package(obs-frontend-api REQUIRED) @@ -43,6 +43,7 @@ target_sources(${CMAKE_PROJECT_NAME} PRIVATE src/obs-moq.cpp src/moq-output.cpp src/moq-output.h + src/codec-signaling.h src/moq-service.cpp src/moq-service.h ) diff --git a/src/codec-signaling.h b/src/codec-signaling.h new file mode 100644 index 0000000..3238efe --- /dev/null +++ b/src/codec-signaling.h @@ -0,0 +1,81 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include + +struct TrackCodec { + moq_codec_source_format_t source_format; + moq_codec_config_format_t config_format; + const char *sample_entry; + bool has_object_type_indication; + uint8_t object_type_indication; +}; + +inline constexpr TrackCodec kCodecH264{MOQ_CODEC_SOURCE_AVC_ANNEXB, MOQ_CODEC_CONFIG_AVCC, "avc1", false, 0x00}; +inline constexpr TrackCodec kCodecH265{MOQ_CODEC_SOURCE_HEVC_ANNEXB, MOQ_CODEC_CONFIG_HVCC, "hvc1", false, 0x00}; +inline constexpr TrackCodec kCodecAac{MOQ_CODEC_SOURCE_AAC_ASC, MOQ_CODEC_CONFIG_AAC_ASC, "mp4a", true, 0x40}; + + +inline const TrackCodec *ResolveTrackCodec(const char *codec) +{ + if (!codec) + return nullptr; + if (strcmp(codec, "h264") == 0) + return &kCodecH264; + if (strcmp(codec, "hevc") == 0) + return &kCodecH265; + if (strcmp(codec, "aac") == 0) + return &kCodecAac; + return nullptr; +} + +inline std::vector BuildInitData(const char *codec, const uint8_t *src, size_t len) +{ + const TrackCodec *tc = ResolveTrackCodec(codec); + if (!tc || !src || len == 0) + return {}; + + moq_codec_init_data_cfg_t cfg; + moq_codec_init_data_cfg_init(&cfg); + cfg.source_format = tc->source_format; + cfg.source = {src, len}; + + size_t need = 0; + moq_result_t r = moq_codec_init_data_build(&cfg, nullptr, 0, &need); + if ((r != MOQ_ERR_BUFFER && r != MOQ_OK) || need == 0) + return {}; + + std::vector init_data(need); + if (moq_codec_init_data_build(&cfg, init_data.data(), init_data.size(), &need) != MOQ_OK) + return {}; + + init_data.resize(need); + return init_data; +} + +inline std::string BuildCodecString(const char *codec, const std::vector &init_data) +{ + const TrackCodec *tc = ResolveTrackCodec(codec); + if (!tc) + return {}; + + moq_codec_string_cfg_t cfg; + moq_codec_string_cfg_init(&cfg); + cfg.config_format = tc->config_format; + cfg.sample_entry = moq_bytes_cstr(tc->sample_entry); + cfg.has_mp4_object_type_indication = tc->has_object_type_indication; + cfg.mp4_object_type_indication = tc->object_type_indication; + cfg.decoder_config = {init_data.data(), init_data.size()}; + + uint8_t buf[64]; + size_t need = 0; + if (moq_codec_string_format(&cfg, buf, sizeof(buf), &need) == MOQ_OK) + return std::string((const char *)buf, need); + + return {}; +} diff --git a/src/moq-output.cpp b/src/moq-output.cpp index bbf7dab..9471417 100644 --- a/src/moq-output.cpp +++ b/src/moq-output.cpp @@ -1,5 +1,5 @@ #include "moq-output.h" -#include "utils.h" +#include "codec-signaling.h" #include #include @@ -68,8 +68,8 @@ bool MOQOutput::LoadVideoEncoderSettings() size_t extra_size = 0; obs_encoder_get_extra_data(venc, &extra, &extra_size); - video_init_data = AnnexBToAvcC(extra, extra_size); - video_codec = AvcCodecString(video_init_data); + video_init_data = BuildInitData(codec, extra, extra_size); + video_codec = BuildCodecString(codec, video_init_data); return true; } @@ -93,14 +93,8 @@ bool MOQOutput::LoadAudioEncoderSettings() uint8_t *extra = nullptr; size_t extra_size = 0; obs_encoder_get_extra_data(aenc, &extra, &extra_size); - - if (codec && strcmp(codec, "opus") == 0) { - audio_init_data.clear(); - audio_codec = "opus"; - } else { - audio_init_data.assign(extra, extra + extra_size); - audio_codec = AacCodecString(audio_init_data); - } + audio_init_data = BuildInitData(codec, extra, extra_size); + audio_codec = BuildCodecString(codec, audio_init_data); return true; } @@ -173,13 +167,16 @@ moq_media_track_t *MOQOutput::CreateVideoTrack(moq_media_sender_t *new_sender) moq_media_track_t *MOQOutput::CreateVideoTrackFromPacket(moq_media_sender_t *cur_sender, struct encoder_packet *packet) { - std::vector init = AnnexBToAvcC(packet->data, packet->size); + obs_encoder_t *venc = obs_output_get_video_encoder(output); + const char *codec = venc ? obs_encoder_get_codec(venc) : nullptr; + + std::vector init = BuildInitData(codec, packet->data, packet->size); if (init.empty()) { return nullptr; } video_init_data = std::move(init); - video_codec = AvcCodecString(video_init_data); + video_codec = BuildCodecString(codec, video_init_data); moq_media_track_t *new_track = CreateVideoTrack(cur_sender); if (!new_track) { @@ -478,8 +475,8 @@ void register_moq_output() // todo: change to OBS_OUTPUT_AV when audio is supported info.flags = OBS_OUTPUT_AV | OBS_OUTPUT_ENCODED | OBS_OUTPUT_SERVICE; info.protocols = "MOQ"; - // todo: add support for hevc and av1 - info.encoded_video_codecs = "h264"; + // todo: add support for av1 + info.encoded_video_codecs = "h264;hevc"; // todo: add support for opus and ac3 info.encoded_audio_codecs = "aac,opus"; diff --git a/src/utils.h b/src/utils.h deleted file mode 100644 index af54ed9..0000000 --- a/src/utils.h +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -#include -#include -#include - -static std::string AvcCodecString(const std::vector &avcc) -{ - uint8_t profile = 0x42, constraints = 0xe0, level = 0x1f; - - if (avcc.size() >= 4 && avcc[0] == 1) { - profile = avcc[1]; - constraints = avcc[2]; - level = avcc[3]; - } - - char buf[16]; - snprintf(buf, sizeof(buf), "avc1.%02x%02x%02x", profile, constraints, level); - return buf; -} - -static std::vector AnnexBToAvcC(const uint8_t *extra, size_t size) -{ - uint8_t *avcc = nullptr; - size_t avcc_size = obs_parse_avc_header(&avcc, extra, size); - if (!avcc || avcc_size == 0) { - bfree(avcc); - return {}; - } - std::vector out(avcc, avcc + avcc_size); - bfree(avcc); - return out; -} - -static std::string AacCodecString(const std::vector &asc) -{ - uint8_t object_type = 2; - - if (asc.size() >= 2) { - object_type = (asc[0] >> 3) & 0x1f; - if (object_type == 31 && asc.size() >= 3) { - object_type = 32 + (((asc[1] & 0x07) << 3) | ((asc[2] >> 5) & 0x07)); - } - } - - char buf[16]; - snprintf(buf, sizeof(buf), "mp4a.40.%u", object_type); - return buf; -} From 37ddbe085c26dcd3286db8925c6188946fe9a6ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gast=C3=B3n=20Bogar=C3=ADn?= Date: Tue, 28 Jul 2026 11:10:32 -0300 Subject: [PATCH 2/5] declare missing h265 in service's supported video codecs --- src/moq-service.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/moq-service.cpp b/src/moq-service.cpp index f6f6ca5..f7fe85a 100644 --- a/src/moq-service.cpp +++ b/src/moq-service.cpp @@ -4,7 +4,7 @@ #include const char *audio_codecs[] = {"aac", "opus", nullptr}; -const char *video_codecs[] = {"h264", nullptr}; +const char *video_codecs[] = {"h264", "h265", nullptr}; MOQService::MOQService(obs_data_t *settings, obs_service_t *service) { From f80681b47d40a89edec021c9a21cb68658412aab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gast=C3=B3n=20Bogar=C3=ADn?= Date: Tue, 28 Jul 2026 11:27:42 -0300 Subject: [PATCH 3/5] add opus support --- src/codec-signaling.h | 3 +++ src/moq-output.cpp | 4 ++-- src/moq-service.cpp | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/codec-signaling.h b/src/codec-signaling.h index 3238efe..28fd91c 100644 --- a/src/codec-signaling.h +++ b/src/codec-signaling.h @@ -19,6 +19,7 @@ struct TrackCodec { inline constexpr TrackCodec kCodecH264{MOQ_CODEC_SOURCE_AVC_ANNEXB, MOQ_CODEC_CONFIG_AVCC, "avc1", false, 0x00}; inline constexpr TrackCodec kCodecH265{MOQ_CODEC_SOURCE_HEVC_ANNEXB, MOQ_CODEC_CONFIG_HVCC, "hvc1", false, 0x00}; inline constexpr TrackCodec kCodecAac{MOQ_CODEC_SOURCE_AAC_ASC, MOQ_CODEC_CONFIG_AAC_ASC, "mp4a", true, 0x40}; +inline constexpr TrackCodec kCodecOpus{MOQ_CODEC_SOURCE_OPUS_HEAD, MOQ_CODEC_CONFIG_OPUS, "opus", false, 0x00}; inline const TrackCodec *ResolveTrackCodec(const char *codec) @@ -31,6 +32,8 @@ inline const TrackCodec *ResolveTrackCodec(const char *codec) return &kCodecH265; if (strcmp(codec, "aac") == 0) return &kCodecAac; + if (strcmp(codec, "opus") == 0) + return &kCodecOpus; return nullptr; } diff --git a/src/moq-output.cpp b/src/moq-output.cpp index 9471417..a2d7303 100644 --- a/src/moq-output.cpp +++ b/src/moq-output.cpp @@ -477,8 +477,8 @@ void register_moq_output() info.protocols = "MOQ"; // todo: add support for av1 info.encoded_video_codecs = "h264;hevc"; - // todo: add support for opus and ac3 - info.encoded_audio_codecs = "aac,opus"; + // todo: add support for ac3 + info.encoded_audio_codecs = "aac;opus"; info.get_name = [](void *) -> const char * { return obs_module_text("Output.Name"); diff --git a/src/moq-service.cpp b/src/moq-service.cpp index f7fe85a..58a3072 100644 --- a/src/moq-service.cpp +++ b/src/moq-service.cpp @@ -4,7 +4,7 @@ #include const char *audio_codecs[] = {"aac", "opus", nullptr}; -const char *video_codecs[] = {"h264", "h265", nullptr}; +const char *video_codecs[] = {"h264", "hevc", nullptr}; MOQService::MOQService(obs_data_t *settings, obs_service_t *service) { From e7918da33be7f6d0e57158c0ce6d7f3d88bcdfa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gast=C3=B3n=20Bogar=C3=ADn?= Date: Tue, 28 Jul 2026 12:25:12 -0300 Subject: [PATCH 4/5] add av1 support --- src/codec-signaling.h | 3 +++ src/moq-output.cpp | 5 +---- src/moq-service.cpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/codec-signaling.h b/src/codec-signaling.h index 28fd91c..5d4ee43 100644 --- a/src/codec-signaling.h +++ b/src/codec-signaling.h @@ -18,6 +18,7 @@ struct TrackCodec { inline constexpr TrackCodec kCodecH264{MOQ_CODEC_SOURCE_AVC_ANNEXB, MOQ_CODEC_CONFIG_AVCC, "avc1", false, 0x00}; inline constexpr TrackCodec kCodecH265{MOQ_CODEC_SOURCE_HEVC_ANNEXB, MOQ_CODEC_CONFIG_HVCC, "hvc1", false, 0x00}; +inline constexpr TrackCodec kCodecAv1{MOQ_CODEC_SOURCE_AV1_OBU, MOQ_CODEC_CONFIG_AV1C, "av01", false, 0x00}; inline constexpr TrackCodec kCodecAac{MOQ_CODEC_SOURCE_AAC_ASC, MOQ_CODEC_CONFIG_AAC_ASC, "mp4a", true, 0x40}; inline constexpr TrackCodec kCodecOpus{MOQ_CODEC_SOURCE_OPUS_HEAD, MOQ_CODEC_CONFIG_OPUS, "opus", false, 0x00}; @@ -30,6 +31,8 @@ inline const TrackCodec *ResolveTrackCodec(const char *codec) return &kCodecH264; if (strcmp(codec, "hevc") == 0) return &kCodecH265; + if (strcmp(codec, "av1") == 0) + return &kCodecAv1; if (strcmp(codec, "aac") == 0) return &kCodecAac; if (strcmp(codec, "opus") == 0) diff --git a/src/moq-output.cpp b/src/moq-output.cpp index a2d7303..1fddb5e 100644 --- a/src/moq-output.cpp +++ b/src/moq-output.cpp @@ -472,12 +472,9 @@ void register_moq_output() { struct obs_output_info info = {}; info.id = "moq_output"; - // todo: change to OBS_OUTPUT_AV when audio is supported info.flags = OBS_OUTPUT_AV | OBS_OUTPUT_ENCODED | OBS_OUTPUT_SERVICE; info.protocols = "MOQ"; - // todo: add support for av1 - info.encoded_video_codecs = "h264;hevc"; - // todo: add support for ac3 + info.encoded_video_codecs = "h264;hevc;av1"; info.encoded_audio_codecs = "aac;opus"; info.get_name = [](void *) -> const char * { diff --git a/src/moq-service.cpp b/src/moq-service.cpp index 58a3072..dfb7790 100644 --- a/src/moq-service.cpp +++ b/src/moq-service.cpp @@ -4,7 +4,7 @@ #include const char *audio_codecs[] = {"aac", "opus", nullptr}; -const char *video_codecs[] = {"h264", "hevc", nullptr}; +const char *video_codecs[] = {"h264", "hevc", "av1", nullptr}; MOQService::MOQService(obs_data_t *settings, obs_service_t *service) { From 88f36a1b6878771a377a0c12dde9611faa7d7206 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gast=C3=B3n=20Bogar=C3=ADn?= Date: Mon, 10 Aug 2026 14:14:26 -0300 Subject: [PATCH 5/5] style: apply clang-format --- src/codec-signaling.h | 1 - src/moq-service.cpp | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/codec-signaling.h b/src/codec-signaling.h index 5d4ee43..ab4e452 100644 --- a/src/codec-signaling.h +++ b/src/codec-signaling.h @@ -22,7 +22,6 @@ inline constexpr TrackCodec kCodecAv1{MOQ_CODEC_SOURCE_AV1_OBU, MOQ_CODEC_CONFIG inline constexpr TrackCodec kCodecAac{MOQ_CODEC_SOURCE_AAC_ASC, MOQ_CODEC_CONFIG_AAC_ASC, "mp4a", true, 0x40}; inline constexpr TrackCodec kCodecOpus{MOQ_CODEC_SOURCE_OPUS_HEAD, MOQ_CODEC_CONFIG_OPUS, "opus", false, 0x00}; - inline const TrackCodec *ResolveTrackCodec(const char *codec) { if (!codec) diff --git a/src/moq-service.cpp b/src/moq-service.cpp index dfb7790..5efbb1b 100644 --- a/src/moq-service.cpp +++ b/src/moq-service.cpp @@ -16,8 +16,7 @@ void MOQService::Update(obs_data_t *settings) server = obs_data_get_string(settings, "server"); // todo: verify that this is the correct key for the namespace moq_namespace = obs_data_get_string(settings, "key"); - blog(LOG_DEBUG, "[obs-moq] service updated: server='%s' namespace='%s'", server.c_str(), - moq_namespace.c_str()); + blog(LOG_DEBUG, "[obs-moq] service updated: server='%s' namespace='%s'", server.c_str(), moq_namespace.c_str()); } obs_properties_t *MOQService::Properties()