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
8 changes: 7 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@ 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 moq::cmaf-packager
)

if(ENABLE_FRONTEND_API)
find_package(obs-frontend-api REQUIRED)
Expand All @@ -43,6 +47,8 @@ target_sources(${CMAKE_PROJECT_NAME} PRIVATE
src/obs-moq.cpp
src/moq-output.cpp
src/moq-output.h
src/codec-signaling.h
src/moq-cmaf.h
src/moq-service.cpp
src/moq-service.h
)
Expand Down
87 changes: 87 additions & 0 deletions src/codec-signaling.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
#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 {};
}
67 changes: 67 additions & 0 deletions src/moq-cmaf.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#pragma once

#include <cstdint>
#include <cstring>

#include <obs-module.h>

#include "moq-output.h"

static moq_cmaf_codec_kind_t codec_kind_from_name(const char *codec)
{
if (!codec)
return MOQ_CMAF_CODEC_UNKNOWN;
if (strcmp(codec, "h264") == 0)
return MOQ_CMAF_CODEC_AVC;
if (strcmp(codec, "hevc") == 0)
return MOQ_CMAF_CODEC_HEVC;
if (strcmp(codec, "av1") == 0)
return MOQ_CMAF_CODEC_AV1;
if (strcmp(codec, "aac") == 0)
return MOQ_CMAF_CODEC_AAC;
if (strcmp(codec, "opus") == 0)
return MOQ_CMAF_CODEC_OPUS;
return MOQ_CMAF_CODEC_UNKNOWN;
}

static CMAFPackagerPtr create_packager(const moq_cmaf_packager_cfg_t *cfg, const char *codec)
{
moq_cmaf_packager_t *packager = nullptr;
const moq_result_t rc = moq_cmaf_packager_create(nullptr, cfg, &packager);
if (rc != MOQ_OK) {
blog(LOG_WARNING, "[obs-moq] CMAF: packager init failed for '%s': %s", codec, moq_strerror(rc));
return nullptr;
}

return CMAFPackagerPtr(packager);
}

static int64_t rescale_timestamp(int64_t v, int64_t num, int64_t den)
{
if (den <= 0 || num <= 0 || num == den)
return v;
return (v / den) * num + ((v % den) * num) / den;
}

static bool package_packet(moq_cmaf_packager_t *packager, const struct encoder_packet *packet, moq_bytes_t *out)
{
if (!packager || !packet || !packet->data || packet->size == 0)
return false;

const int64_t num = packet->timebase_num ? packet->timebase_num : 1;
const int64_t den = packet->timebase_den ? packet->timebase_den : 1;
const int64_t scale = static_cast<int64_t>(moq_cmaf_packager_timescale(packager)) * num;

moq_cmaf_packager_sample_t sample = {};
sample.data = {packet->data, packet->size};
sample.pts = rescale_timestamp(packet->pts, scale, den);
sample.dts = rescale_timestamp(packet->dts, scale, den);
sample.keyframe = packet->keyframe;

const moq_result_t rc = moq_cmaf_packager_write(packager, &sample, 1, out);
if (rc != MOQ_OK) {
return false;
}

return true;
}
Loading
Loading