From b1bbc4784876113ea7819404ff2bb859189fa174 Mon Sep 17 00:00:00 2001 From: Scott <51915366+scottsdevelopment@users.noreply.github.com> Date: Mon, 20 Jan 2025 19:12:51 -0500 Subject: [PATCH 1/3] Update obs-output-delay.c Add dynamic stream delay to OBS --- libobs/obs-output-delay.c | 91 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 86 insertions(+), 5 deletions(-) diff --git a/libobs/obs-output-delay.c b/libobs/obs-output-delay.c index de72bd7156c97c..cac242f693e0e1 100644 --- a/libobs/obs-output-delay.c +++ b/libobs/obs-output-delay.c @@ -14,9 +14,9 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ - #include #include "obs-internal.h" +#include // For memset static inline bool delay_active(const struct obs_output *output) { @@ -43,6 +43,57 @@ static inline bool log_flag_encoded(const struct obs_output *output, const char return ret; } +static void generate_blank_video_frame(struct encoder_packet *packet, struct obs_output *output) +{ + video_t *video = obs_output_video(output); + if (!video) { + blog(LOG_ERROR, "Failed to retrieve video output for blank frame generation"); + return; + } + + uint32_t width = video_output_get_width(video); + uint32_t height = video_output_get_height(video); + size_t frame_size = width * height * 3 / 2; + uint8_t *frame_data = bmalloc(frame_size); + memset(frame_data, 0, frame_size); + + packet->data = frame_data; + packet->size = frame_size; + packet->type = OBS_ENCODER_VIDEO; + packet->pts = os_gettime_ns(); + packet->dts = packet->pts; + packet->keyframe = true; + packet->priority = 1; + packet->encoder = obs_output_get_video_encoder(output); // Use the associated video encoder + + blog(LOG_DEBUG, "Generated blank video frame (%ux%u)", width, height); +} + +static void generate_silent_audio_packet(struct encoder_packet *packet, struct obs_output *output) +{ + audio_t *audio = obs_output_audio(output); + if (!audio) { + blog(LOG_ERROR, "Failed to retrieve audio output for silent packet generation"); + return; + } + + uint32_t sample_rate = audio_output_get_sample_rate(audio); + size_t channels = audio_output_get_channels(audio); + size_t frame_size = sample_rate * channels * sizeof(float) / 100; + uint8_t *frame_data = bmalloc(frame_size); + memset(frame_data, 0, frame_size); + + packet->data = frame_data; + packet->size = frame_size; + packet->type = OBS_ENCODER_AUDIO; + packet->pts = os_gettime_ns(); + packet->dts = packet->pts; + packet->encoder = obs_output_get_audio_encoder(output, 1); + + + blog(LOG_DEBUG, "Generated silent audio frame (%uHz, %u channels)", sample_rate, channels); +} + static inline void push_packet(struct obs_output *output, struct encoder_packet *packet, struct encoder_packet_time *packet_time, uint64_t t) { @@ -60,6 +111,23 @@ static inline void push_packet(struct obs_output *output, struct encoder_packet pthread_mutex_unlock(&output->delay_mutex); } + +static inline void push_blank_packet(struct obs_output *output) +{ + struct encoder_packet blank_packet = {0}; + + if (obs_output_video(output)) { + generate_blank_video_frame(&blank_packet, output); + push_packet(output, &blank_packet, NULL, os_gettime_ns()); + } + + /* if (obs_output_audio(output)) { + generate_silent_audio_packet(&blank_packet, output); + push_packet(output, &blank_packet, NULL, os_gettime_ns()); + }*/ +} + + static inline void process_delay_data(struct obs_output *output, struct delay_data *dd) { switch (dd->msg) { @@ -99,29 +167,37 @@ static inline bool pop_packet(struct obs_output *output, uint64_t t) struct delay_data dd; bool popped = false; bool preserve; + bool blanks = false; /* ------------------------------------------------ */ preserve = (output->delay_cur_flags & OBS_OUTPUT_DELAY_PRESERVE) != 0; - pthread_mutex_lock(&output->delay_mutex); if (output->delay_data.size) { deque_peek_front(&output->delay_data, &dd, sizeof(dd)); elapsed_time = (t - dd.ts); - if (preserve && output->reconnecting) { output->active_delay_ns = elapsed_time; - } else if (elapsed_time > output->active_delay_ns) { deque_pop_front(&output->delay_data, NULL, sizeof(dd)); popped = true; + } else { + blanks = true; + // push_blank_packet(output); } } pthread_mutex_unlock(&output->delay_mutex); - /* ------------------------------------------------ */ + // Fix blank packets, error with discarding unsued audio packets + // possible issue is how the packets are being interleaved, + // didnt have time to study the encoder pipeline and hoping an expert + // can answer this question relatively easy. + // + // + //if (blanks) + // push_blank_packet(output); if (popped) process_delay_data(output, &dd); @@ -195,8 +271,12 @@ void obs_output_set_delay(obs_output_t *output, uint32_t delay_sec, uint32_t fla output->delay_sec = delay_sec; output->delay_flags = flags; + output->active_delay_ns = (uint64_t)delay_sec * 1000000000ULL; + + blog(LOG_INFO, "Delay set for output '%s' to %u seconds", output->context.name, delay_sec); } + uint32_t obs_output_get_delay(const obs_output_t *output) { return obs_output_valid(output, "obs_output_set_delay") ? output->delay_sec : 0; @@ -207,3 +287,4 @@ uint32_t obs_output_get_active_delay(const obs_output_t *output) return obs_output_valid(output, "obs_output_set_delay") ? (uint32_t)(output->active_delay_ns / 1000000000ULL) : 0; } + From d99fdcf5348e500c7d37c32199d13908fea86bfc Mon Sep 17 00:00:00 2001 From: Scott <51915366+scottsdevelopment@users.noreply.github.com> Date: Mon, 20 Jan 2025 19:14:43 -0500 Subject: [PATCH 2/3] Update MultitrackVideoOutput.cpp Remove warnings as a warning to you --- frontend/utility/MultitrackVideoOutput.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/utility/MultitrackVideoOutput.cpp b/frontend/utility/MultitrackVideoOutput.cpp index 1ee0fe170c20e0..62078651916d12 100644 --- a/frontend/utility/MultitrackVideoOutput.cpp +++ b/frontend/utility/MultitrackVideoOutput.cpp @@ -507,7 +507,6 @@ bool MultitrackVideoOutput::HandleIncompatibleSettings(QWidget *parent, config_t num += 1; }; - check_setting(useDelay, "Basic.Settings.Advanced.StreamDelay", "Basic.Settings.Advanced.StreamDelay"); #ifdef _WIN32 check_setting(enableNewSocketLoop, "Basic.Settings.Advanced.Network.EnableNewSocketLoop", "Basic.Settings.Advanced.Network"); From 492e125e6d65841e1eb127efa7aa2e4044d07d79 Mon Sep 17 00:00:00 2001 From: Scott <51915366+scottsdevelopment@users.noreply.github.com> Date: Mon, 20 Jan 2025 19:17:51 -0500 Subject: [PATCH 3/3] Create stream-delay.lua interface for the new feature. --- .../data/scripts/stream-delay.lua | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 frontend/plugins/frontend-tools/data/scripts/stream-delay.lua diff --git a/frontend/plugins/frontend-tools/data/scripts/stream-delay.lua b/frontend/plugins/frontend-tools/data/scripts/stream-delay.lua new file mode 100644 index 00000000000000..94cc774f6c1b84 --- /dev/null +++ b/frontend/plugins/frontend-tools/data/scripts/stream-delay.lua @@ -0,0 +1,49 @@ +obs = obslua + +function get_output() + local output = obs.obs_get_output_by_name("rtmp multitrack video") + return output +end + +-- Function to get the current delay and add 1 second +function increment_stream_delay() + -- Get the output (stream or recording) + local output = get_output() + local current_delay = obs.obs_output_get_delay(output) + + -- Add 1 second to the current delay + local new_delay = current_delay + 1 + + obs.obs_output_set_delay(output, new_delay, 0) + + -- Log the new delay + obs.script_log(obs.LOG_INFO, "Increased stream delay from " .. current_delay .. " to " .. new_delay .. " seconds.") +end + +function decrement_stream_delay() + -- Get the output (stream or recording) + local output = get_output() + local current_delay = obs.obs_output_get_delay(output) + + -- Add 1 second to the current delay + local new_delay = current_delay - 1 + + obs.obs_output_set_delay(output, new_delay, 0) + + -- Log the new delay + obs.script_log(obs.LOG_INFO, "Decreased stream delay from " .. current_delay .. " to " .. new_delay .. " seconds.") +end + +-- Register the function as a hotkey +hotkey_id = obs.obs_hotkey_register_frontend("increment_stream_delay", "Increase Stream Delay by 1s", increment_stream_delay) +hotkey_id = obs.obs_hotkey_register_frontend("decrement_stream_delay", "Descrease Stream Delay by 1s", decrement_stream_delay) +-- Script description (appears in the script UI in OBS) +function script_description() + return "This script increases the stream delay by 1 second every time the hotkey is pressed." +end + +-- Called when the script is unloaded +function script_unload() + -- obs.obs_hotkey_unregister(hotkey_id) +end +