Understanding the Media Source State Machine in OBS Studio

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, 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 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 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 (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 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 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:

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 at lines 52-63.

Implementing a Custom Media Source

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

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 at lines 85-89 and 124-135.

Reacting to Hotkeys in the VLC Plugin

The VLC source demonstrates state-aware hotkey handling:

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 at lines 60-68.

Key Files in the Media State Architecture

File Role
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 Implements obs_source_media_get_state and other helper functions
plugins/vlc-video/vlc-video-source.c Maps libVLC states to OBS states and demonstrates hotkey handling
plugins/obs-ffmpeg/obs-ffmpeg-source.c Shows explicit state transitions for FFmpeg-based media
plugins/image-source/obs-slideshow.c Uses set_media_state for image slideshows
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, 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 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, 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 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 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.

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 →