obs_output_delay Configuration: Implementing Time-Shifted Output in OBS Studio
OBS Studio's obs_output_delay API enables time-shifted streaming and recording by buffering encoded packets in a thread-safe deque for a configurable duration before transmission.
The obs_output_delay mechanism in the obsproject/obs-studio repository provides a robust buffering layer within the libobs core. By configuring obs_output_set_delay(), developers can implement delayed output pipelines that hold audio and video packets for a specified interval, enabling features like stream delay for content moderation or time-shifted recording.
How obs_output_delay Works in libobs
The delay implementation centers on a specialized data pipeline defined in libobs/obs-output-delay.c. When an output is configured with a non-zero delay, the system initializes a thread-safe queue structure to manage buffered packets.
Core Data Structures
The delay mechanism relies on several critical components:
struct delay_data– Stores control messages (DELAY_MSG_STARTorDELAY_MSG_STOP) alongside packet timestampsoutput->delay_data– Adequestructure that holds buffered items during the delay intervaloutput->delay_mutex– Protects concurrent access to the queue across threads- Atomic flags –
delay_activeindicates whether the pipeline is enabled, whiledelay_capturingtracks active packet ingestion
Packet Timing and Emission
Each encoded packet passing through the delay pipeline receives a nanosecond timestamp via os_gettime_ns(). The system compares this timestamp against the configured delay duration (output->active_delay_ns). When the elapsed time exceeds the delay interval, process_delay() emits the packet through the original output callback, effectively creating a time-shifted stream.
Configuring Output Delay with obs_output_set_delay
The public API exposed in include/obs.h and implemented in libobs/obs-output.c provides straightforward configuration methods for output delay.
Setting Delay Duration and Flags
Use obs_output_set_delay() to configure the buffer duration and behavior flags:
obs_output_t *output = obs_output_create("rtmp_output", "stream", settings, NULL);
uint32_t delay_seconds = 10;
uint32_t flags = OBS_OUTPUT_DELAY_PRESERVE; // Maintain delay across reconnections
obs_output_set_delay(output, delay_seconds, flags);
This invocation stores delay_sec and delay_flags in the output structure. When OBS_OUTPUT_DELAY_PRESERVE is set, the delay pipeline remains active during streaming reconnections, preventing disruption of the time-shifted buffer.
Starting a Delayed Output
When obs_output_start() is invoked, the system checks output->delay_sec in libobs/obs-output.c (lines 96-108). If a delay is configured, execution routes to obs_output_delay_start() rather than the immediate start path:
if (!obs_output_start(output)) {
blog(LOG_ERROR, "Failed to initiate delayed output");
}
The obs_output_delay_start() function pushes a DELAY_MSG_START control message into the delay queue, optionally invokes obs_output_begin_data_capture(), and signals the "starting" event to observers.
Monitoring Active Delay with obs_output_get_active_delay
During runtime, applications can query the actual buffered delay to display status indicators or synchronize external systems. The obs_output_get_active_delay() function converts the internal nanosecond timestamp (output->active_delay_ns) into seconds:
while (obs_output_active(output)) {
uint32_t current_delay = obs_output_get_active_delay(output);
blog(LOG_INFO, "Live buffer delay: %u seconds", current_delay);
os_sleep_ms(1000);
}
This polling mechanism reads the atomic delay state maintained by the delay pipeline in libobs/obs-output-delay.c, providing real-time visibility into the time-shift offset.
Stopping and Cleanup
Terminating a delayed output requires flushing the buffered packet queue to ensure all time-shifted data reaches the destination. When obs_output_stop() is called, the system invokes obs_output_delay_stop() if a delay is active:
obs_output_stop(output);
obs_output_release(output);
The obs_output_delay_stop() function pushes a DELAY_MSG_STOP control message into the delay queue. The output callback processes this message to flush remaining packets, signal the "stopping" event, and release delay-specific resources once the buffer empties.
Complete Implementation Example
The following example demonstrates creating an output, configuring a 15-second delay with preservation flags, starting the stream, monitoring active delay, and graceful shutdown:
#include <obs.h>
void run_delayed_output(obs_data_t *settings)
{
/* Create the output instance */
obs_output_t *output = obs_output_create("rtmp_output", "delayed_stream", settings, NULL);
if (!output) {
blog(LOG_ERROR, "Failed to create output");
return;
}
/* Configure 15-second delay with reconnection preservation */
uint32_t delay_sec = 15;
uint32_t flags = OBS_OUTPUT_DELAY_PRESERVE;
obs_output_set_delay(output, delay_sec, flags);
/* Initiate delayed start */
if (!obs_output_start(output)) {
blog(LOG_ERROR, "Failed to start delayed output");
obs_output_release(output);
return;
}
/* Monitor active delay during stream */
while (obs_output_active(output)) {
uint32_t active = obs_output_get_active_delay(output);
blog(LOG_INFO, "Buffer delay: %u seconds", active);
os_sleep_ms(2000);
}
/* Graceful shutdown */
obs_output_stop(output);
obs_output_release(output);
blog(LOG_INFO, "Delayed output terminated");
}
This implementation leverages the full delay pipeline in libobs/obs-output-delay.c, utilizing the thread-safe queue and nanosecond-precision timing to maintain a consistent 15-second time shift throughout the streaming session.
Summary
- obs_output_delay implements time-shifted streaming through a dedicated delay pipeline in
libobs/obs-output-delay.c - Configuration occurs via
obs_output_set_delay(), which storesdelay_secanddelay_flags(includingOBS_OUTPUT_DELAY_PRESERVE) - The delay mechanism uses a thread-safe
deque(delay_data) protected bydelay_mutex, with atomic flags trackingdelay_activeanddelay_capturingstates - Packet timestamps from
os_gettime_ns()determine emission timing; packets forward only after the configured nanosecond interval elapses - Runtime monitoring is available through
obs_output_get_active_delay(), which converts internal nanosecond counters to seconds - Clean shutdown requires
obs_output_delay_stop()to flush the queue and signal the stopping event
Frequently Asked Questions
What is the maximum delay supported by obs_output_delay?
The obs_output_delay system stores delay values as uint32_t seconds in the output structure, theoretically supporting up to 4,294,967,295 seconds. However, practical limitations depend on available system memory, as each delayed packet consumes queue space in the delay_data deque until the interval elapses and the packet emits.
How does OBS_OUTPUT_DELAY_PRESERVE affect reconnection behavior?
When the OBS_OUTPUT_DELAY_PRESERVE flag is passed to obs_output_set_delay(), the delay pipeline maintains its active state and buffered queue across temporary disconnections and reconnections. Without this flag, a reconnection might reset the delay buffer, causing a gap in the time-shifted stream; with the flag preserved, the delay duration remains consistent throughout the session.
Can obs_output_delay be modified while the output is active?
No, the delay configuration must be set before calling obs_output_start(). The obs_output_set_delay() function writes to output->delay_sec and output->delay_flags, but obs_output_start() reads these values only during initialization to determine whether to route to obs_output_delay_start() or the immediate start path. Changing the delay mid-stream would require stopping and restarting the output.
What happens to buffered packets when obs_output_stop is called?
When obs_output_stop() triggers obs_output_delay_stop(), the function pushes a DELAY_MSG_STOP control message into the delay queue. The output callback continues processing queued packets in chronological order based on their os_gettime_ns() timestamps until reaching the stop message, ensuring all time-shifted data reaches the destination before the output signals the "stopping" event and releases resources.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →