# obs_output_delay Configuration: Implementing Time-Shifted Output in OBS Studio

> Configure obs_output_delay in OBS Studio for time-shifted streaming. Learn how to implement buffering and delay for your encoded output with ease.

- Repository: [OBS Project/obs-studio](https://github.com/obsproject/obs-studio)
- Tags: how-to-guide
- Published: 2026-03-03

---

**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`](https://github.com/obsproject/obs-studio/blob/main/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`](https://github.com/obsproject/obs-studio/blob/main/include/obs.h) and implemented in [`libobs/obs-output.c`](https://github.com/obsproject/obs-studio/blob/main/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:

```c
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`](https://github.com/obsproject/obs-studio/blob/main/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:

```c
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:

```c
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`](https://github.com/obsproject/obs-studio/blob/main/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:

```c
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:

```c
#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`](https://github.com/obsproject/obs-studio/blob/main/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`](https://github.com/obsproject/obs-studio/blob/main/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.