Skip to content
Open
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
24 changes: 19 additions & 5 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,25 @@ add_library(obs-moq MODULE)
if(${BUILD_PLUGIN})
find_package(libobs REQUIRED)
# FFmpeg dependency
include(FindPkgConfig)
pkg_check_modules(FFMPEG REQUIRED libavcodec libavutil libswscale libswresample)
target_include_directories(obs-moq PRIVATE ${FFMPEG_INCLUDE_DIRS})
target_link_directories(obs-moq PRIVATE ${FFMPEG_LIBRARY_DIRS})
target_link_libraries(obs-moq PRIVATE ${FFMPEG_LIBRARIES})
if(WIN32)
find_path(FFMPEG_INCLUDE_DIR
NAMES libavcodec/avcodec.h
PATH_SUFFIXES include
REQUIRED)
find_library(AVCODEC_LIBRARY NAMES avcodec PATH_SUFFIXES lib REQUIRED)
find_library(AVUTIL_LIBRARY NAMES avutil PATH_SUFFIXES lib REQUIRED)
find_library(SWSCALE_LIBRARY NAMES swscale PATH_SUFFIXES lib REQUIRED)
find_library(SWRESAMPLE_LIBRARY NAMES swresample PATH_SUFFIXES lib REQUIRED)

target_include_directories(obs-moq PRIVATE ${FFMPEG_INCLUDE_DIR})
target_link_libraries(obs-moq PRIVATE ${AVCODEC_LIBRARY} ${AVUTIL_LIBRARY} ${SWSCALE_LIBRARY} ${SWRESAMPLE_LIBRARY})
else()
include(FindPkgConfig)
pkg_check_modules(FFMPEG REQUIRED libavcodec libavutil libswscale libswresample)
target_include_directories(obs-moq PRIVATE ${FFMPEG_INCLUDE_DIRS})
target_link_directories(obs-moq PRIVATE ${FFMPEG_LIBRARY_DIRS})
target_link_libraries(obs-moq PRIVATE ${FFMPEG_LIBRARIES})
endif()
else()
find_package(FFmpeg REQUIRED avcodec avutil swscale swresample)
target_link_libraries(obs-moq PRIVATE FFmpeg::avcodec FFmpeg::avutil FFmpeg::swscale FFmpeg::swresample)
Expand Down
36 changes: 25 additions & 11 deletions src/moq-output.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,29 @@ MoQOutput::MoQOutput(obs_data_t *, obs_output_t *output)
connect_time_ms(0),
origin(moq_origin_create()),
session(0),
broadcast(moq_publish_create())
broadcast(moq_publish_create()),
broadcast_published(false)
{
}

bool MoQOutput::PublishBroadcast()
{
if (broadcast_published) {
return true;
}

LOG_INFO("Publishing broadcast: %s", path.c_str());

auto result = moq_origin_publish(origin, path.data(), path.size(), broadcast);
if (result < 0) {
LOG_ERROR("Failed to publish broadcast to session: %d", result);
Comment on lines +29 to +33
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add the required [obs-moq] prefix to new log messages.

Line 29 and Line 33 use project logging macros but omit the required prefix, which makes filtering/attribution inconsistent.

Suggested fix
-	LOG_INFO("Publishing broadcast: %s", path.c_str());
+	LOG_INFO("[obs-moq] Publishing broadcast: %s", path.c_str());
@@
-		LOG_ERROR("Failed to publish broadcast to session: %d", result);
+		LOG_ERROR("[obs-moq] Failed to publish broadcast to session: %d", result);

As per coding guidelines, **/*.{cpp,h,hpp} must use LOG_DEBUG(), LOG_INFO(), LOG_WARNING(), LOG_ERROR() with [obs-moq] prefix.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
LOG_INFO("Publishing broadcast: %s", path.c_str());
auto result = moq_origin_publish(origin, path.data(), path.size(), broadcast);
if (result < 0) {
LOG_ERROR("Failed to publish broadcast to session: %d", result);
LOG_INFO("[obs-moq] Publishing broadcast: %s", path.c_str());
auto result = moq_origin_publish(origin, path.data(), path.size(), broadcast);
if (result < 0) {
LOG_ERROR("[obs-moq] Failed to publish broadcast to session: %d", result);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/moq-output.cpp` around lines 29 - 33, Update the two logging calls to
include the required "[obs-moq]" prefix: change the LOG_INFO call that logs the
publishing path and the LOG_ERROR call inside the moq_origin_publish error
branch so their messages begin with "[obs-moq]". Locate the LOG_INFO("Publishing
broadcast: %s", path.c_str()) and LOG_ERROR("Failed to publish broadcast to
session: %d", result) statements (near the moq_origin_publish(origin,
path.data(), path.size(), broadcast) call) and prepend the prefix in their
format strings so they read e.g. LOG_INFO("[obs-moq] ...") and
LOG_ERROR("[obs-moq] ...").

return false;
}

broadcast_published = true;
return true;
}
Comment on lines +23 to +39
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Reset publish state per session, not per object lifetime.

At Line 25, PublishBroadcast() permanently short-circuits once broadcast_published is true, but this flag is only initialized in the constructor (Line 19). On a later stop/start cycle with the same MoQOutput instance, the new session can skip moq_origin_publish(...).

Suggested fix
 bool MoQOutput::Start()
 {
@@
 	// Path (broadcast name) is optional; an empty string publishes to the unnamed broadcast.
 	const char *path_value = obs_service_get_connect_info(service, OBS_SERVICE_CONNECT_INFO_STREAM_KEY);
 	path = path_value ? path_value : "";
+	broadcast_published = false;
@@
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/moq-output.cpp` around lines 23 - 39, The PublishBroadcast method
currently short-circuits using the instance-level flag broadcast_published,
causing publishes to be skipped across session restarts; fix by making the
published state session-scoped — either reset broadcast_published when a session
stops/starts (e.g., in your session Start/Stop handlers) or track the last
published session (add a last_published_session id member and compare it to the
current origin/session before skipping). Ensure PublishBroadcast (and
moq_origin_publish call) uses the current origin/session identity (origin, path)
and sets the session-scoped flag after a successful publish instead of relying
on a single lifetime-initialized broadcast_published.


MoQOutput::~MoQOutput()
{
moq_publish_close(broadcast);
Expand Down Expand Up @@ -80,6 +99,11 @@ bool MoQOutput::Start()
auto self = static_cast<MoQOutput *>(user_data);

if (error_code == 0) {
if (!self->PublishBroadcast()) {
obs_output_signal_stop(self->output, OBS_OUTPUT_ERROR);
return;
}

auto elapsed = std::chrono::steady_clock::now() - self->connect_start;
self->connect_time_ms = static_cast<int>(std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count());
LOG_INFO("MoQ session established (%d ms): %s", self->connect_time_ms,
Expand All @@ -97,16 +121,6 @@ bool MoQOutput::Start()
return false;
}

LOG_INFO("Publishing broadcast: %s", path.c_str());

// Publish the broadcast to the origin we created.
// TODO: There is currently no unpublish function.
auto result = moq_origin_publish(origin, path.data(), path.size(), broadcast);
if (result < 0) {
LOG_ERROR("Failed to publish broadcast to session: %d", result);
return false;
}

obs_output_begin_data_capture(output, 0);

return true;
Expand Down
2 changes: 2 additions & 0 deletions src/moq-output.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class MoQOutput
}

private:
bool PublishBroadcast();
void VideoInit(obs_encoder_t *encoder);
void VideoData(struct encoder_packet *packet);
void AudioInit(obs_encoder_t *encoder);
Expand All @@ -44,6 +45,7 @@ class MoQOutput
int origin;
int session;
int broadcast;
bool broadcast_published;
std::map<obs_encoder_t *, int> video_tracks;
std::map<obs_encoder_t *, int> audio_tracks;
};
Expand Down