Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
)
Expand Down
86 changes: 86 additions & 0 deletions src/codec-signaling.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#pragma once

#include <cstddef>
#include <cstdint>
#include <cstring>
#include <string>
#include <vector>

#include <moq/codec_signaling.h>

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 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};

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, "av1") == 0)
return &kCodecAv1;
if (strcmp(codec, "aac") == 0)
return &kCodecAac;
if (strcmp(codec, "opus") == 0)
return &kCodecOpus;
return nullptr;
}

inline std::vector<uint8_t> 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<uint8_t> 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<uint8_t> &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 {};
}
30 changes: 12 additions & 18 deletions src/moq-output.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#include "moq-output.h"
#include "utils.h"
#include "codec-signaling.h"

#include <util/platform.h>
#include <obs.hpp>
Expand Down Expand Up @@ -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;
}

Expand All @@ -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;
}
Expand Down Expand Up @@ -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<uint8_t> 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<uint8_t> 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) {
Expand Down Expand Up @@ -475,13 +472,10 @@ 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 hevc and av1
info.encoded_video_codecs = "h264";
// todo: add support for opus and ac3
info.encoded_audio_codecs = "aac,opus";
info.encoded_video_codecs = "h264;hevc;av1";
info.encoded_audio_codecs = "aac;opus";

info.get_name = [](void *) -> const char * {
return obs_module_text("Output.Name");
Expand Down
5 changes: 2 additions & 3 deletions src/moq-service.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#include <obs.hpp>

const char *audio_codecs[] = {"aac", "opus", nullptr};
const char *video_codecs[] = {"h264", nullptr};
const char *video_codecs[] = {"h264", "hevc", "av1", nullptr};

MOQService::MOQService(obs_data_t *settings, obs_service_t *service)
{
Expand All @@ -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()
Expand Down
49 changes: 0 additions & 49 deletions src/utils.h

This file was deleted.

Loading