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_pausemedia_restartmedia_stopmedia_nextmedia_previousmedia_get_durationmedia_get_timemedia_set_timemedia_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_stateenum defined inlibobs/obs-source.h, providing nine distinct states fromOBS_MEDIA_STATE_PLAYINGtoOBS_MEDIA_STATE_ERROR. - Sources must set the
OBS_SOURCE_CONTROLLABLE_MEDIAflag and implement media callbacks (such asmedia_get_stateandmedia_play_pause) to participate in the state machine. - The helper function
obs_source_media_get_state()inlibobs/obs-source.cprovides 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 viaset_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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →