# Understanding the Media Source State Machine in OBS Studio

> Master the OBS Studio media source state machine. Understand OBS_MEDIA_STATE_PLAYING, BUFFERING, PAUSED, and ERROR for seamless media control in your streams.

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

---

**OBS Studio tracks playback status through the `obs_media_state` enum, which defines states like `OBS_MEDIA_STATE_PLAYING`, `BUFFERING`, `PAUSED`, and `ERROR` to standardize media control across VLC, FFmpeg, and custom sources.**

The media source state machine in OBS Studio provides a unified interface for controlling and monitoring playback across diverse media inputs. Defined in [`libobs/obs-source.h`](https://github.com/obsproject/obs-studio/blob/main/libobs/obs-source.h), the `obs_media_state` enumeration standardizes how sources report whether they are actively playing, buffering, paused, or encountering errors. This architecture allows the frontend UI and scripting interfaces to control VLC videos, FFmpeg streams, and image slideshows through a consistent API.

## The OBS_MEDIA_STATE Enumeration

The core of the media source state machine is the `enum obs_media_state` defined in [`libobs/obs-source.h`](https://github.com/obsproject/obs-studio/blob/main/libobs/obs-source.h) at lines 64-73. This enumeration provides nine distinct states that cover the entire lifecycle of media playback:

| State | Description |
|-------|-------------|
| **OBS_MEDIA_STATE_NONE** | No media loaded or the source does not support media control |
| **OBS_MEDIA_STATE_PLAYING** | Media is actively playing |
| **OBS_MEDIA_STATE_OPENING** | Media file is being opened/initialized |
| **OBS_MEDIA_STATE_BUFFERING** | Media is buffering data before playback |
| **OBS_MEDIA_STATE_PAUSED** | Playback is paused |
| **OBS_MEDIA_STATE_STOPPED** | Playback has been stopped (but not ended) |
| **OBS_MEDIA_STATE_ENDED** | Media reached its end |
| **OBS_MEDIA_STATE_ERROR** | An error occurred during playback |

These states enable the frontend to display appropriate controls and status indicators regardless of the underlying media engine.

## How the Media Source State Machine Works

The state machine operates through a combination of capability flags, callback functions, and standardized query mechanisms.

### Registering a Controllable Media Source

For a source to participate in the media source state machine, it must declare itself as controllable during registration. In [`libobs/obs-source.h`](https://github.com/obsproject/obs-studio/blob/main/libobs/obs-source.h) at lines 85-89, sources set the `OBS_SOURCE_CONTROLLABLE_MEDIA` flag in their `output_flags` within the `obs_source_info` structure.

Additionally, the source must implement the optional media callbacks defined at lines 124-135 of the same file:

- `media_play_pause`
- `media_restart`
- `media_stop`
- `media_next`
- `media_previous`
- `media_get_duration`
- `media_get_time`
- `media_set_time`
- `media_get_state`

### Querying the Current State

The generic helper `obs_source_media_get_state(obs_source_t *source)` in [`libobs/obs-source.c`](https://github.com/obsproject/obs-studio/blob/main/libobs/obs-source.c) (lines 52-63) provides the primary interface for state retrieval. This function verifies that the source has the `OBS_SOURCE_CONTROLLABLE_MEDIA` flag set, then forwards the request to the source's `media_get_state` callback if implemented.

### State Transitions in Practice

Different source types implement state transitions according to their underlying media engines.

**VLC Video Source**: The VLC plugin translates libVLC's internal states to OBS enums in `vlcs_get_state()` at [`plugins/vlc-video/vlc-video-source.c`](https://github.com/obsproject/obs-studio/blob/main/plugins/vlc-video/vlc-video-source.c) lines 66-85. For example, `libvlc_Playing` maps to `OBS_MEDIA_STATE_PLAYING`, `libvlc_Buffering` to `OBS_MEDIA_STATE_BUFFERING`, and `libvlc_Error` to `OBS_MEDIA_STATE_ERROR`.

**FFmpeg Media Source**: The FFmpeg source manually updates its internal state using helpers like `set_media_state(s, OBS_MEDIA_STATE_PLAYING)` as seen in [`plugins/obs-ffmpeg/obs-ffmpeg-source.c`](https://github.com/obsproject/obs-studio/blob/main/plugins/obs-ffmpeg/obs-ffmpeg-source.c) lines 284-332. This explicit state management allows the source to signal buffering during network streams or errors during decoding failures.

## Working with Media States in Code

### Querying the Current Media State

To check playback status in plugins or scripts:

```c
obs_media_state state = obs_source_media_get_state(source);

if (state == OBS_MEDIA_STATE_PLAYING) {
    blog(LOG_INFO, "Media is currently playing");
} else if (state == OBS_MEDIA_STATE_BUFFERING) {
    blog(LOG_INFO, "Media is buffering...");
} else if (state == OBS_MEDIA_STATE_ERROR) {
    blog(LOG_ERROR, "Media playback error detected");
}

```

*Source reference:* `obs_source_media_get_state` implementation in [`libobs/obs-source.c`](https://github.com/obsproject/obs-studio/blob/main/libobs/obs-source.c) at lines 52-63.

### Implementing a Custom Media Source

To create a source that participates in the media source state machine:

```c
static enum obs_media_state my_media_get_state(void *data)
{
    struct my_source *s = data;
    return s->current_state; // Return OBS_MEDIA_STATE_PLAYING, etc.
}

static void my_media_play_pause(void *data, bool pause)
{
    struct my_source *s = data;
    if (pause) {
        s->current_state = OBS_MEDIA_STATE_PAUSED;
        pause_playback(s);
    } else {
        s->current_state = OBS_MEDIA_STATE_PLAYING;
        resume_playback(s);
    }
}

static struct obs_source_info my_media_source = {
    .id = "custom_media_source",
    .type = OBS_SOURCE_TYPE_INPUT,
    .output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_AUDIO |
                    OBS_SOURCE_CONTROLLABLE_MEDIA,
    .media_get_state = my_media_get_state,
    .media_play_pause = my_media_play_pause,
    /* additional callbacks… */
};

obs_register_source(&my_media_source);

```

*Key fields:* The `OBS_SOURCE_CONTROLLABLE_MEDIA` flag and media callbacks are defined in [`libobs/obs-source.h`](https://github.com/obsproject/obs-studio/blob/main/libobs/obs-source.h) at lines 85-89 and 124-135.

### Reacting to Hotkeys in the VLC Plugin

The VLC source demonstrates state-aware hotkey handling:

```c
static void vlcs_play_pause_hotkey(void *data, obs_hotkey_id id,
                                   obs_hotkey_t *hotkey, bool pressed)
{
    struct vlc_source *c = data;
    enum obs_media_state state = obs_source_media_get_state(c->source);

    if (pressed && obs_source_showing(c->source)) {
        if (state == OBS_MEDIA_STATE_PLAYING)
            obs_source_media_play_pause(c->source, true);
        else if (state == OBS_MEDIA_STATE_PAUSED)
            obs_source_media_play_pause(c->source, false);
    }
}

```

*Source reference:* VLC hotkey implementation in [`plugins/vlc-video/vlc-video-source.c`](https://github.com/obsproject/obs-studio/blob/main/plugins/vlc-video/vlc-video-source.c) at lines 60-68.

## Key Files in the Media State Architecture

| File | Role |
|------|------|
| [`libobs/obs-source.h`](https://github.com/obsproject/obs-studio/blob/main/libobs/obs-source.h) | Defines `enum obs_media_state`, the `OBS_SOURCE_CONTROLLABLE_MEDIA` flag, and the media callbacks in `obs_source_info` |
| [`libobs/obs-source.c`](https://github.com/obsproject/obs-studio/blob/main/libobs/obs-source.c) | Implements `obs_source_media_get_state` and other helper functions |
| [`plugins/vlc-video/vlc-video-source.c`](https://github.com/obsproject/obs-studio/blob/main/plugins/vlc-video/vlc-video-source.c) | Maps libVLC states to OBS states and demonstrates hotkey handling |
| [`plugins/obs-ffmpeg/obs-ffmpeg-source.c`](https://github.com/obsproject/obs-studio/blob/main/plugins/obs-ffmpeg/obs-ffmpeg-source.c) | Shows explicit state transitions for FFmpeg-based media |
| [`plugins/image-source/obs-slideshow.c`](https://github.com/obsproject/obs-studio/blob/main/plugins/image-source/obs-slideshow.c) | Uses `set_media_state` for image slideshows |
| [`frontend/components/MediaControls.cpp`](https://github.com/obsproject/obs-studio/blob/main/frontend/components/MediaControls.cpp) | UI component that reads media state to update controls |
| `docs/sphinx/reference-sources.rst` | Official documentation of the state enumeration |

## Summary

- The **media source state machine** in OBS Studio centers on the `obs_media_state` enum defined in [`libobs/obs-source.h`](https://github.com/obsproject/obs-studio/blob/main/libobs/obs-source.h), providing nine distinct states from `OBS_MEDIA_STATE_PLAYING` to `OBS_MEDIA_STATE_ERROR`.
- Sources must set the `OBS_SOURCE_CONTROLLABLE_MEDIA` flag and implement media callbacks (such as `media_get_state` and `media_play_pause`) to participate in the state machine.
- The helper function `obs_source_media_get_state()` in [`libobs/obs-source.c`](https://github.com/obsproject/obs-studio/blob/main/libobs/obs-source.c) provides the standard interface for querying playback status across VLC, FFmpeg, and custom sources.
- State transitions are backend-specific: VLC maps libVLC states in `vlcs_get_state()`, while FFmpeg sources manually update state via `set_media_state()`.

## Frequently Asked Questions

### What is the difference between OBS_MEDIA_STATE_STOPPED and OBS_MEDIA_STATE_ENDED?

**OBS_MEDIA_STATE_STOPPED** indicates that playback was manually halted by the user or API call, while **OBS_MEDIA_STATE_ENDED** signifies that the media file reached its natural conclusion. A stopped source can typically be restarted from the beginning, whereas an ended source may require explicit restart commands depending on the implementation in `obs_source_info`.

### How do I check if a media source is currently buffering?

Query the state using `obs_source_media_get_state()` and compare the result against `OBS_MEDIA_STATE_BUFFERING`. According to the VLC plugin implementation in [`plugins/vlc-video/vlc-video-source.c`](https://github.com/obsproject/obs-studio/blob/main/plugins/vlc-video/vlc-video-source.c), this state maps directly from underlying library events such as `libvlc_Buffering`, allowing the UI to display loading indicators during network stream initialization.

### Can custom plugins implement the media source state machine?

Yes, any source can participate by setting the `OBS_SOURCE_CONTROLLABLE_MEDIA` flag in its `obs_source_info` structure and implementing the required media callbacks including `media_get_state`, `media_play_pause`, and `media_stop`. The FFmpeg source in [`plugins/obs-ffmpeg/obs-ffmpeg-source.c`](https://github.com/obsproject/obs-studio/blob/main/plugins/obs-ffmpeg/obs-ffmpeg-source.c) demonstrates how to manually manage state transitions using internal helpers like `set_media_state()`.

### What happens when a media source encounters an error?

When decoding fails or network streams drop, sources transition to `OBS_MEDIA_STATE_ERROR`. The VLC plugin maps `libvlc_Error` to this state in `vlcs_get_state()`, while the UI components in [`frontend/components/MediaControls.cpp`](https://github.com/obsproject/obs-studio/blob/main/frontend/components/MediaControls.cpp) check for this state to disable play buttons or show error indicators. Once in error state, the source typically requires a restart or media reload to resume operation.