diff --git a/.gitignore b/.gitignore index b4a0a99..6628fc8 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ !/build-aux !/cmake !/data +!/hang-lib !/src !.clang-format !.gersemirc diff --git a/CMakeLists.txt b/CMakeLists.txt index e4cad9c..c7fbeaf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,6 +16,14 @@ add_library(${CMAKE_PROJECT_NAME} MODULE) find_package(libobs REQUIRED) target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE OBS::libobs) +add_library(hang SHARED IMPORTED) +set(HANG_LIB_DIR "${CMAKE_CURRENT_SOURCE_DIR}/hang-lib") +set_target_properties(hang PROPERTIES + IMPORTED_LOCATION "${HANG_LIB_DIR}/libhang.dylib" + INTERFACE_INCLUDE_DIRECTORIES "${HANG_LIB_DIR}" +) +target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE hang) + if(ENABLE_FRONTEND_API) find_package(obs-frontend-api REQUIRED) target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE OBS::obs-frontend-api) diff --git a/CMakePresets.json b/CMakePresets.json index 21fd22a..54378a8 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -11,14 +11,17 @@ "hidden": true, "cacheVariables": { "ENABLE_FRONTEND_API": false, - "ENABLE_QT": false + "ENABLE_QT": false, + "CMAKE_EXPORT_COMPILE_COMMANDS": true } }, { "name": "macos", "displayName": "macOS Universal", "description": "Build for macOS 12.0+ (Universal binary)", - "inherits": ["template"], + "inherits": [ + "template" + ], "binaryDir": "${sourceDir}/build_macos", "condition": { "type": "equals", @@ -26,17 +29,22 @@ "rhs": "Darwin" }, "generator": "Xcode", - "warnings": {"dev": true, "deprecated": true}, + "warnings": { + "dev": true, + "deprecated": true + }, "cacheVariables": { "CMAKE_OSX_DEPLOYMENT_TARGET": "12.0", - "CMAKE_OSX_ARCHITECTURES": "arm64;x86_64", + "CMAKE_OSX_ARCHITECTURES": "arm64", "CODESIGN_IDENTITY": "$penv{CODESIGN_IDENT}", "CODESIGN_TEAM": "$penv{CODESIGN_TEAM}" } }, { "name": "macos-ci", - "inherits": ["macos"], + "inherits": [ + "macos" + ], "displayName": "macOS Universal CI build", "description": "Build for macOS 12.0+ (Universal binary) for CI", "generator": "Xcode", @@ -49,7 +57,9 @@ "name": "windows-x64", "displayName": "Windows x64", "description": "Build for Windows x64", - "inherits": ["template"], + "inherits": [ + "template" + ], "binaryDir": "${sourceDir}/build_x64", "condition": { "type": "equals", @@ -58,11 +68,16 @@ }, "generator": "Visual Studio 17 2022", "architecture": "x64,version=10.0.22621", - "warnings": {"dev": true, "deprecated": true} + "warnings": { + "dev": true, + "deprecated": true + } }, { "name": "windows-ci-x64", - "inherits": ["windows-x64"], + "inherits": [ + "windows-x64" + ], "displayName": "Windows x64 CI build", "description": "Build for Windows x64 on CI", "cacheVariables": { @@ -73,7 +88,9 @@ "name": "ubuntu-x86_64", "displayName": "Ubuntu x86_64", "description": "Build for Ubuntu x86_64", - "inherits": ["template"], + "inherits": [ + "template" + ], "binaryDir": "${sourceDir}/build_x86_64", "condition": { "type": "equals", @@ -81,7 +98,10 @@ "rhs": "Linux" }, "generator": "Ninja", - "warnings": {"dev": true, "deprecated": true}, + "warnings": { + "dev": true, + "deprecated": true + }, "cacheVariables": { "CMAKE_BUILD_TYPE": "RelWithDebInfo", "CMAKE_INSTALL_LIBDIR": "lib/CMAKE_SYSTEM_PROCESSOR-linux-gnu" @@ -89,7 +109,9 @@ }, { "name": "ubuntu-ci-x86_64", - "inherits": ["ubuntu-x86_64"], + "inherits": [ + "ubuntu-x86_64" + ], "displayName": "Ubuntu x86_64 CI build", "description": "Build for Ubuntu x86_64 on CI", "cacheVariables": { @@ -143,4 +165,4 @@ "configuration": "RelWithDebInfo" } ] -} +} \ No newline at end of file diff --git a/hang-lib/hang.h b/hang-lib/hang.h new file mode 100644 index 0000000..8e1b78f --- /dev/null +++ b/hang-lib/hang.h @@ -0,0 +1,36 @@ +#include +#include +#include +#include + +/** + * # Safety + * + * The caller must ensure that: + * - `c_server_url` and `c_path` are valid null-terminated C strings + * - The pointers remain valid for the duration of this function call + */ +void hang_start_from_c(const char *c_server_url, const char *c_path, const char *_c_profile); + +void hang_stop_from_c(void); + +/** + * # Safety + * + * The caller must ensure that: + * - `data` points to a valid buffer of at least `size` bytes + * - The buffer remains valid for the duration of this function call + */ +void hang_write_video_packet_from_c(const uint8_t *data, + uintptr_t size, + int32_t keyframe, + uint64_t dts); + +/** + * # Safety + * + * The caller must ensure that: + * - `data` points to a valid buffer of at least `size` bytes + * - The buffer remains valid for the duration of this function call + */ +void hang_write_audio_packet_from_c(const uint8_t *data, uintptr_t size, uint64_t dts); diff --git a/hang-lib/hang.pc b/hang-lib/hang.pc new file mode 100644 index 0000000..b880123 --- /dev/null +++ b/hang-lib/hang.pc @@ -0,0 +1,10 @@ +prefix=/usr/local +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${prefix}/include + +Name: hang +Description: Media over QUIC C Interface +Version: 0.7.0 +Libs: -L${libdir} -lhang +Cflags: -I${includedir} diff --git a/hang-lib/libhang.a b/hang-lib/libhang.a new file mode 100644 index 0000000..b1b7205 Binary files /dev/null and b/hang-lib/libhang.a differ diff --git a/hang-lib/libhang.d b/hang-lib/libhang.d new file mode 100644 index 0000000..e5fd1e0 --- /dev/null +++ b/hang-lib/libhang.d @@ -0,0 +1 @@ +/Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/target/release/libhang.a: /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/hang/src/annexb/error.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/hang/src/annexb/import.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/hang/src/annexb/mod.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/hang/src/catalog/audio/aac.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/hang/src/catalog/audio/codec.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/hang/src/catalog/audio/mod.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/hang/src/catalog/chat.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/hang/src/catalog/location.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/hang/src/catalog/mod.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/hang/src/catalog/preview.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/hang/src/catalog/root.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/hang/src/catalog/track.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/hang/src/catalog/user.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/hang/src/catalog/video/av1.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/hang/src/catalog/video/codec.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/hang/src/catalog/video/h264.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/hang/src/catalog/video/h265.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/hang/src/catalog/video/mod.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/hang/src/catalog/video/vp9.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/hang/src/cmaf/error.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/hang/src/cmaf/import.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/hang/src/cmaf/mod.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/hang/src/error.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/hang/src/feedback/capabilities.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/hang/src/feedback/mod.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/hang/src/feedback/root.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/hang/src/lib.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/hang/src/model/frame.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/hang/src/model/group.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/hang/src/model/location.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/hang/src/model/mod.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/hang/src/model/track.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/hang-c/build.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/hang-c/src/lib.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq/src/coding/decode.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq/src/coding/encode.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq/src/coding/mod.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq/src/coding/reader.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq/src/coding/size.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq/src/coding/stream.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq/src/coding/varint.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq/src/coding/version.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq/src/coding/writer.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq/src/error.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq/src/ietf/control.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq/src/ietf/fetch.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq/src/ietf/goaway.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq/src/ietf/group.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq/src/ietf/location.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq/src/ietf/message.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq/src/ietf/mod.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq/src/ietf/namespace.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq/src/ietf/parameters.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq/src/ietf/publish.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq/src/ietf/publish_namespace.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq/src/ietf/publisher.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq/src/ietf/request.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq/src/ietf/session.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq/src/ietf/setup.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq/src/ietf/subscribe.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq/src/ietf/subscribe_namespace.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq/src/ietf/subscriber.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq/src/ietf/track.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq/src/ietf/version.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq/src/lib.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq/src/lite/announce.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq/src/lite/group.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq/src/lite/info.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq/src/lite/message.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq/src/lite/mod.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq/src/lite/parameters.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq/src/lite/priority.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq/src/lite/publisher.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq/src/lite/session.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq/src/lite/setup.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq/src/lite/stream.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq/src/lite/subscribe.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq/src/lite/subscriber.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq/src/lite/version.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq/src/model/broadcast.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq/src/model/frame.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq/src/model/group.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq/src/model/mod.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq/src/model/origin.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq/src/model/produce.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq/src/model/track.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq/src/path.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq/src/session.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq/src/setup.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq-native/src/client.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq-native/src/crypto.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq-native/src/lib.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq-native/src/log.rs /Users/emilsanturio/Documents/SummerCamp\ 2026/moq/rs/moq-native/src/server.rs diff --git a/hang-lib/libhang.dylib b/hang-lib/libhang.dylib new file mode 100755 index 0000000..2c6b665 Binary files /dev/null and b/hang-lib/libhang.dylib differ diff --git a/src/logger.h b/src/logger.h new file mode 100644 index 0000000..29e224d --- /dev/null +++ b/src/logger.h @@ -0,0 +1,8 @@ +#include + +// Logging macros +#define LOG(level, format, ...) blog(level, "[obs-moq] " format, ##__VA_ARGS__) +#define LOG_DEBUG(format, ...) LOG(LOG_DEBUG, format, ##__VA_ARGS__) +#define LOG_INFO(format, ...) LOG(LOG_INFO, format, ##__VA_ARGS__) +#define LOG_WARNING(format, ...) LOG(LOG_WARNING, format, ##__VA_ARGS__) +#define LOG_ERROR(format, ...) LOG(LOG_ERROR, format, ##__VA_ARGS__) diff --git a/src/moq-output.cpp b/src/moq-output.cpp index c32b172..85fba40 100644 --- a/src/moq-output.cpp +++ b/src/moq-output.cpp @@ -1,40 +1,102 @@ +#include + #include "moq-output.h" -#include +extern "C" { +#include "hang.h" +} MoQOutput::MoQOutput(obs_data_t *, obs_output_t *output) : output(output), server_url(), - token(), - stream_name(), + path(), total_bytes_sent(0), connect_time_ms(0) { + LOG_INFO("MoQOutput instance created"); } MoQOutput::~MoQOutput() { + LOG_INFO("MoQOutput instance being destroyed"); Stop(); } bool MoQOutput::Start() { - if (!obs_output_can_begin_data_capture(output, 0)) + LOG_INFO("Starting MoQ output..."); + + ConfigureVideoTrack(); + ConfigureAudioTrack(); + + obs_service_t *service = obs_output_get_service(output); + if (!service) { + LOG_ERROR("Failed to get service from output"); + obs_output_signal_stop(output, OBS_OUTPUT_ERROR); return false; + } - if (!obs_output_initialize_encoders(output, 0)) + if (!obs_output_can_begin_data_capture(output, 0)) { + LOG_ERROR("Cannot begin data capture"); return false; - + } + + if (!obs_output_initialize_encoders(output, 0)) { + LOG_ERROR("Failed to initialize encoders"); + return false; + } + + server_url = obs_service_get_connect_info(service, OBS_SERVICE_CONNECT_INFO_SERVER_URL); + if (server_url.empty()) { + LOG_ERROR("Server URL is empty"); + obs_output_signal_stop(output, OBS_OUTPUT_BAD_PATH); + return false; + } + + LOG_INFO("Server URL: %s", server_url.c_str()); + + path = obs_service_get_connect_info(service, OBS_SERVICE_CONNECT_INFO_STREAM_KEY); + LOG_INFO("Stream path: %s", path.c_str()); + + const obs_encoder_t *encoder = obs_output_get_video_encoder2(output, 0); + + if (!encoder) { + LOG_ERROR("Failed to get video encoder"); + return false; + } + + // std::cout << "joy: " << obs_encoder_get_width(encoder) << std::endl; + + obs_data_t *encoder_settings = obs_encoder_get_settings(encoder); + const char *profile_str = obs_data_get_string(encoder_settings, "profile"); + + LOG_INFO("Video encoder - Width: %d, Height: %d, Profile: %s", obs_encoder_get_width(encoder), + obs_encoder_get_height(encoder), profile_str ? profile_str : "none"); + + LOG_DEBUG("Encoder settings: %s", obs_data_get_json_pretty(encoder_settings)); + + LOG_INFO("Initializing hang library with server: %s, path: %s", server_url.c_str(), path.c_str()); + hang_start_from_c(server_url.c_str(), path.c_str(), profile_str); + + obs_data_release(encoder_settings); + obs_output_begin_data_capture(output, 0); + LOG_INFO("MoQ output started successfully"); return true; } void MoQOutput::Stop(bool signal) { + LOG_INFO("Stopping MoQ output (signal: %s)", signal ? "true" : "false"); + if (signal) { obs_output_signal_stop(output, OBS_OUTPUT_SUCCESS); - } + + LOG_INFO("Stopping hang library"); + hang_stop_from_c(); + LOG_INFO("MoQ output stopped successfully. Total bytes sent: %zu", total_bytes_sent); + } return; } @@ -42,22 +104,87 @@ void MoQOutput::Stop(bool signal) void MoQOutput::Data(struct encoder_packet *packet) { if (!packet) { + LOG_ERROR("Received null packet, stopping output"); Stop(false); obs_output_signal_stop(output, OBS_OUTPUT_ENCODE_ERROR); return; } + if (packet->dts_usec < 0) { + LOG_ERROR("Received packet with negative dts, skipping"); + return; + } + if (packet->type == OBS_ENCODER_AUDIO) { - // TODO: Handle Audio packet + LOG_DEBUG("Received audio packet - size: %zu, dts: %lld", packet->size, packet->dts_usec); + hang_write_audio_packet_from_c(packet->data, packet->size, packet->dts_usec); return; } else if (packet->type == OBS_ENCODER_VIDEO) { - // TODO: Handle Video packet + LOG_DEBUG("Received video packet - size: %zu, keyframe: %s, dts: %lld", packet->size, + packet->keyframe ? "yes" : "no", packet->dts_usec); + hang_write_video_packet_from_c(packet->data, packet->size, packet->keyframe, packet->dts_usec); + total_bytes_sent += packet->size; return; } } +void MoQOutput::ConfigureVideoTrack() +{ + obs_encoder_t *encoder = obs_output_get_video_encoder2(output, 0); + if (!encoder) { + LOG_ERROR("Failed to get video encoder"); + return; + } + + OBSDataAutoRelease settings = obs_encoder_get_settings(encoder); + if (!settings) { + LOG_ERROR("Failed to get video encoder settings"); + return; + } + + LOG_DEBUG("Video encoder settings: %s", obs_data_get_json_pretty_with_defaults(settings)); + + const char *video_codec = obs_encoder_get_codec(encoder); + const char *profile = obs_data_get_string(settings, "profile"); // Could be "" + auto video_bitrate = (int)obs_data_get_int(settings, "bitrate"); + auto video_width = obs_encoder_get_width(encoder); + auto video_height = obs_encoder_get_height(encoder); + + LOG_INFO("Video codec: %s, profile: %s, bitrate: %d, width: %d, height: %d", video_codec, profile, video_bitrate, + video_height); + return; +} + +void MoQOutput::ConfigureAudioTrack() +{ + obs_encoder_t *encoder = obs_output_get_audio_encoder(output, 0); + if (!encoder) { + LOG_ERROR("Failed to get audio encoder"); + return; + } + + OBSDataAutoRelease settings = obs_encoder_get_settings(encoder); + if (!settings) { + LOG_ERROR("Failed to get audio encoder settings"); + return; + } + + LOG_DEBUG("Audio encoder settings: %s", obs_data_get_json_pretty_with_defaults(settings)); + + const char *audio_codec = obs_encoder_get_codec(encoder); + auto audio_bitrate = (int)obs_data_get_int(settings, "bitrate"); + auto audio_sample_rate = obs_encoder_get_sample_rate(encoder); + uint32_t audio_channels = 2; + + LOG_INFO("Audio codec: %s, bitrate: %d, sample rate: %d, channels: %d", audio_codec, audio_bitrate, + audio_sample_rate, audio_channels); + return; +} + void register_moq_output() { + LOG_INFO("Registering MoQ output types"); + const uint32_t base_flags = OBS_OUTPUT_ENCODED | OBS_OUTPUT_SERVICE; const char *audio_codecs = "aac"; @@ -95,15 +222,18 @@ void register_moq_output() info.protocols = "MoQ"; obs_register_output(&info); + LOG_INFO("Registered output type: moq_output (AV)"); info.id = "moq_output_video"; info.flags = OBS_OUTPUT_VIDEO | base_flags; info.encoded_audio_codecs = nullptr; obs_register_output(&info); + LOG_INFO("Registered output type: moq_output_video (video-only)"); info.id = "moq_output_audio"; info.flags = OBS_OUTPUT_AUDIO | base_flags; info.encoded_video_codecs = nullptr; info.encoded_audio_codecs = audio_codecs; obs_register_output(&info); + LOG_INFO("Registered output type: moq_output_audio (audio-only)"); } diff --git a/src/moq-output.h b/src/moq-output.h index 327182d..202d956 100644 --- a/src/moq-output.h +++ b/src/moq-output.h @@ -2,6 +2,7 @@ #include #include +#include "logger.h" class MoQOutput { public: @@ -18,12 +19,13 @@ class MoQOutput { private: // TODO: Add needed functions + void ConfigureVideoTrack(); + void ConfigureAudioTrack(); obs_output_t *output; std::string server_url; - std::string token; - std::string stream_name; + std::string path; size_t total_bytes_sent; int connect_time_ms; diff --git a/src/moq-service.cpp b/src/moq-service.cpp index 42c3534..e5e8cad 100644 --- a/src/moq-service.cpp +++ b/src/moq-service.cpp @@ -1,10 +1,10 @@ #include "moq-service.h" // TODO: Define supported codecs. -const char *audio_codecs[] = {"aac", nullptr}; +const char *audio_codecs[] = {"aac", "opus", nullptr}; const char *video_codecs[] = {"h264", "hevc", "av1", nullptr}; -MoQService::MoQService(obs_data_t *settings, obs_service_t *) : server(), token(), stream_name() +MoQService::MoQService(obs_data_t *settings, obs_service_t *) : server(), path() { Update(settings); } @@ -12,8 +12,7 @@ MoQService::MoQService(obs_data_t *settings, obs_service_t *) : server(), token( void MoQService::Update(obs_data_t *settings) { server = obs_data_get_string(settings, "server"); - token = obs_data_get_string(settings, "token"); - stream_name = obs_data_get_string(settings, "stream_name"); + path = obs_data_get_string(settings, "key"); } obs_properties_t *MoQService::Properties() @@ -23,8 +22,7 @@ obs_properties_t *MoQService::Properties() // Adds properties to be modified by the UI. // obs_property_t *obs_properties_add_text(obs_properties_t *props, const char *name, const char *desc, enum obs_text_type type) obs_properties_add_text(ppts, "server", "URL", OBS_TEXT_DEFAULT); - obs_properties_add_text(ppts, "token", "Access Token", OBS_TEXT_PASSWORD); - obs_properties_add_text(ppts, "stream_name", "Stream Name", OBS_TEXT_DEFAULT); + obs_properties_add_text(ppts, "key", "Path", OBS_TEXT_DEFAULT); return ppts; } @@ -54,10 +52,8 @@ const char *MoQService::GetConnectInfo(enum obs_service_connect_info type) switch (type) { case OBS_SERVICE_CONNECT_INFO_SERVER_URL: return server.c_str(); - case OBS_SERVICE_CONNECT_INFO_BEARER_TOKEN: - return token.c_str(); - case OBS_SERVICE_CONNECT_INFO_STREAM_ID: - return stream_name.c_str(); + case OBS_SERVICE_CONNECT_INFO_STREAM_KEY: + return path.c_str(); default: return nullptr; } diff --git a/src/moq-service.h b/src/moq-service.h index 7738d5f..57c5cac 100644 --- a/src/moq-service.h +++ b/src/moq-service.h @@ -5,8 +5,7 @@ struct MoQService { // TODO: Define needed params to connect to a relay std::string server; - std::string token; - std::string stream_name; + std::string path; MoQService(obs_data_t *settings, obs_service_t *service);