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_START or DELAY_MSG_STOP) alongside packet timestamps
  • output->delay_data – A deque structure that holds buffered items during the delay interval
  • output->delay_mutex – Protects concurrent access to the queue across threads
  • Atomic flags – delay_active indicates whether the pipeline is enabled, while delay_capturing tracks 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 stores delay_sec and delay_flags (including OBS_OUTPUT_DELAY_PRESERVE)
  • The delay mechanism uses a thread-safe deque (delay_data) protected by delay_mutex, with atomic flags tracking delay_active and delay_capturing states
  • 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →