Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/version.h.in ${CMAKE_CURRENT_SOURCE_D
target_sources(${PROJECT_NAME} PRIVATE
replay.c
replay-source.c
replay-types.inc
replay-types-properties.inc
replay-source-entrypoints.inc
replay-filter.c
replay-filter-audio.c
replay-filter-async.c
Expand Down
36 changes: 36 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,42 @@ Add a replay source.
Set the source, duration and speed in the properties.
Set the replay hotkey.

## Named replay types

A Replay Source can hold separate named clip queues, such as **Replay A** and
**Replay B**, and play them through the same source.

1. Open the Replay Source properties. In **Replay Types**, enter a name and
click **Add Type**. Repeat for each type.
2. Open OBS **Settings > Hotkeys**, find the Replay Source, and assign these
three actions for each type:
- **Store Replay: Replay A** appends the current buffered period to A.
- **Play Replays: Replay A** plays A's clips in capture order, from the start.
- **Clear Replays: Replay A** clears A and cancels its pending captures.
3. Use the corresponding Store, Play and Clear actions for Replay B.

Store does not start playback, change scenes, drain the rolling capture buffer,
or interrupt another replay. Play selects one type at a time and uses a snapshot
of its queue; clips stored during playback appear the next time you press Play.
Playing an empty type leaves playback unchanged. Clear stops playback only
when that type is playing.

**Maximum Clips Per Type** defaults to 10 (1–10). When full, the oldest stored
clip in that type is removed; an ongoing playback snapshot can finish it.
The source's duration, load delay, speed and audio/video settings apply to all
types. For type playback, end actions apply to the whole queue. **Load Replay
Switch Scene** runs when you press Play, and **Next Scene** runs after the queue
finishes.

Select a type to rename, reorder or remove it. Hotkey bindings follow its stable
identity through rename, reorder and saved scene collections. Existing
Next/Previous, trim, Remove and Clear controls act on the playing queue. The
original Load Replay or audio trigger returns playback to the original queue.

Clips live in RAM and disappear when the source is removed or OBS closes.
Names and hotkeys are saved; exporting a clip to disk still uses **Save Replay**.
Old scene collections keep their existing behavior and start with no types.

# Replay source
This source replays audio and video after it is retrieved from a replay filter by using the hotkey or the button in the properties.
# (async) replay filter
Expand Down
15 changes: 15 additions & 0 deletions data/locale/en-US.ini
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,21 @@ PrevFrame="Step Backwards One Frame"
NextFrame="Step Forwards One Frame"
PrevNFrames="Step Backwards 'N' Frames"
NextNFrames="Step Forwards 'N' Frames"
ReplayTypes="Replay Types"
ReplayTypesHelp="Add named queues, then assign Store, Play and Clear for each type in Settings > Hotkeys. Clips stay in memory until cleared or OBS closes."
ReplayType="Replay Type"
SelectReplayType="Select a type"
ReplayTypeName="Type Name"
AddReplayType="Add Type"
RenameReplayType="Rename Type"
RemoveReplayType="Remove Type"
MoveReplayTypeUp="Move Type Up"
MoveReplayTypeDown="Move Type Down"
ReplayTypeNameError="Enter a unique, non-empty type name."
MaxClipsPerType="Maximum Clips Per Type"
StoreReplayType="Store Replay"
PlayReplayType="Play Replays"
ClearReplayType="Clear Replays"

# win-dshow text
VideoCaptureDevice="Video Capture Device"
Expand Down
62 changes: 62 additions & 0 deletions replay-source-entrypoints.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/* Serialize OBS entry points with hotkey work performed on video_tick.
* The recursive mutex also permits OBS signal handlers to query media state. */
#define LOCKED_VOID(name) \
static void name##_locked(void *data) \
{ \
struct replay_source *c = data; \
pthread_mutex_lock(&c->state_mutex); \
name(data); \
pthread_mutex_unlock(&c->state_mutex); \
}

#define LOCKED_ARG(name, type, arg) \
static void name##_locked(void *data, type arg) \
{ \
struct replay_source *c = data; \
pthread_mutex_lock(&c->state_mutex); \
name(data, arg); \
pthread_mutex_unlock(&c->state_mutex); \
}

#define LOCKED_RESULT(name, type) \
static type name##_locked(void *data) \
{ \
struct replay_source *c = data; \
if (!c) return (type)0; \
pthread_mutex_lock(&c->state_mutex); \
type result = name(data); \
pthread_mutex_unlock(&c->state_mutex); \
return result; \
}

LOCKED_VOID(replay_source_show)
LOCKED_VOID(replay_source_hide)
LOCKED_VOID(replay_source_active)
LOCKED_VOID(replay_source_deactive)
LOCKED_VOID(replay_next)
LOCKED_VOID(replay_previous)
LOCKED_VOID(replay_restart)
LOCKED_VOID(replay_stop)
LOCKED_ARG(replay_source_update, obs_data_t *, settings)
LOCKED_ARG(replay_types_save, obs_data_t *, settings)
LOCKED_ARG(replay_source_tick, float, seconds)
LOCKED_ARG(replay_play_pause, bool, pause)
LOCKED_ARG(replay_set_time, int64_t, seconds)
LOCKED_RESULT(replay_get_duration, int64_t)
LOCKED_RESULT(replay_get_time, int64_t)
LOCKED_RESULT(replay_get_state, enum obs_media_state)

static obs_properties_t *replay_source_properties_locked(void *data)
{
struct replay_source *c = data;
if (!c)
return replay_source_properties(NULL);
pthread_mutex_lock(&c->state_mutex);
obs_properties_t *props = replay_source_properties(data);
pthread_mutex_unlock(&c->state_mutex);
return props;
}

#undef LOCKED_VOID
#undef LOCKED_ARG
#undef LOCKED_RESULT
Loading