Smarter Replay Buffer Options?

FAT9L

New Member
I've done some research and poked around in my settings/plugins, and can't seem to find a way to do what I'm trying to accomplish. For context, I have my replay buffer running all the time, complete with a hotkey and OBS automatically starting minimized at login. This makes it behave essentially the same as a program like Shadowplay. Very convenient, especially with the NVENC encoder. It also performs slightly better than constantly recording, and avoids wasting disk space. No problems or complaints with that.

The issue I'm running into is that I want the replay buffer to not capture redundant footage, if I grab multiple clips from the same "session" (don't know a better term). For example, here's a generic example of how it currently behaves:
  • 00:00:00 - The replay buffer starts, and is recording 5 minutes of footage at a time.
  • 00:06:00 - I press my "save recording" hotkey, which captures 5 minutes (00:01:00 - 00:06:00)
  • 00:07:00 - I hit the hotkey again, which captures 5:00 minutes (00:02:00 - 00:07:00)
  • 00:13:00 - I hit the hotkey again, which captures 5:00 minutes (00:08:00 - 00:13:00)
Here's what I would like to happen instead:
  • 00:00:00 - The replay buffer starts, and is recording 5 minutes of footage at a time.
  • 00:06:00 - I press my "save recording" hotkey, which captures 5:00 minutes (00:01:00 - 00:06:00)
  • 00:07:00 - I hit the hotkey again, which captures ≤ 5 minutes, depending on whether or not I have recently saved a recording. Ideally this would still have a bit of padding on the leading end, let's say 30 seconds (~00:05:30 - 00:07:00 [starting from 00:06:00, minus the 30 second padding])
  • 00:13:00 - I hit the hotkey again, which captures ≤ 5 minutes (00:08:00 - 00:13:00)
Hopefully this makes sense.

I cannot seem to find a way to make this happen. It cuts down on my workflow efficiency when editing later on, due to having redundant footage that I have to skip through an arbitrary amount to find something new. This also consumes more storage space (not a huge concern for me, but annoying), and seems like it would be more efficient than repeatedly encoding/saving part of the replay buffer unnecessarily.

Thoughts, ideas, workarounds, plugins? Anything would help. Thank you in advance.
 

FAT9L

New Member
For what it's worth, I know that Advanced Scene Switcher supports macros to enable/disable the replay buffer, however I don't think that constantly stopping and restarting it every time I save a recording is the best option. I'd guess that it might cause issues as well, or miss parts of recordings between when it's stopped and started.
 

Tomasz Góral

Active Member
OBS don’t remember time of last saving replays.
Replays buffer is circullay, new data arrive, old data is removed.

You need somethings who after save clear replays buffers.
Now i see solution in macro, maybe in future, someone add options to clear buffer after save.
 

KillianM

New Member
Bump - @FAT9L Are you still utilising Advanced Scene Switcher for this purpose? Looking to set this up myself as I also do not like the overlap in recordings.
 

FAT9L

New Member
Bump - @FAT9L Are you still utilising Advanced Scene Switcher for this purpose? Looking to set this up myself as I also do not like the overlap in recordings.
I have not yet found a decent workaround or a way to make it happen.

I've poked around in a few different tools, done extensive research, and tried every possible thing I can think of, short of creating my own plugin. Nothing seems to be right for my use case.
 

FAT9L

New Member
Bump - @FAT9L Are you still utilising Advanced Scene Switcher for this purpose? Looking to set this up myself as I also do not like the overlap in recordings.
@KillianM
As I think you've already seen, this was fixed with the latest release shown here:

There is still the slight problem that you lose a couple of seconds between recordings, however I'm willing to accept that tradeoff. Best of luck.
 

anthonybaldwin

New Member
I was looking into this myself and found this thread. While there is probably a simple way to do the same or smarter with a Lua script, I'm using my Elgato Stream Deck and the OBS plugin w/in a multi action key to reset the buffer. The key simply saves buffer, waits, stops buffer, waits, then starts the buffer again.
  • OBS Studio: Replay Buffer Save
  • Delay (2000ms)
  • OBS Studio: Replay Buffer (Stop)
  • Delay (2000ms)
  • OBS Studio: Replay Buffer (Start)
Note: 1000ms also does the trick, but not including a delay did not. I'm erring on the side of caution with 2000ms.

Screenshot 2022-12-09 130933.png
 
Last edited:

HAMODE3389

New Member
I was looking into this myself and found this thread. While there is probably a simple way to do the same or smarter with a Lua script, I'm using my Elgato Stream Deck and the OBS plugin w/in a multi action key to reset the buffer. The key simply saves buffer, waits, stops buffer, waits, then starts the buffer again.
  • OBS Studio: Replay Buffer Save
  • Delay (2000ms)
  • OBS Studio: Replay Buffer (Stop)
  • Delay (2000ms)
  • OBS Studio: Replay Buffer (Start)
Note: 1000ms also does the trick, but not including a delay did not. I'm erring on the side of caution with 2000ms.

View attachment 89573
Lua:
obs = obslua

function script_description()
    return "Stops and re-enables replay buffer when a replay is saved."
end

function script_load(settings)
    obs.obs_frontend_add_event_callback(on_event)
end

function on_event(event)
    if event == obs.OBS_FRONTEND_EVENT_REPLAY_BUFFER_SAVED then
        toggle_replay_buffer()
    end
end

function toggle_replay_buffer()
    local replay_buffer_active = obs.obs_frontend_replay_buffer_active()
   
    if replay_buffer_active then
        obs.obs_frontend_replay_buffer_stop()
        obs.timer_add(start_replay_buffer, 2000)  -- Re-enable after 2 second
    end
end

function start_replay_buffer()
    obs.timer_remove(start_replay_buffer)
    obs.obs_frontend_replay_buffer_start()
end
Credit: ChatGPT
 

mee

New Member
Until the much-requested smarter replay buffer without overlap/waste gets implemented, one can "hack it" with Advanced Scene Switcher macros. The disadvantage is that you can potentially lose about 1 second of footage *after* every replay save (aka the time OBS takes to clumsily stop&restart the buffer)

1. install the Advanced Scene Switcher plugin https://obsproject.com/forum/resources/advanced-scene-switcher.395/
2. Tools -> Advanced Scene Switcher -> Macro -> right click the Macros menu on the left -> Import -> paste the code -> OK
JSON:
{
	"macros": [{
		"name": "replay-anti-overlap",
		"pause": false,
		"parallel": false,
		"onChange": true,
		"skipExecOnStart": false,
		"stopActionsIfNotDone": false,
		"group": true,
		"groupData": {
			"collapsed": false,
			"size": 2
		}
	}, {
		"name": "part1",
		"pause": false,
		"parallel": false,
		"onChange": false,
		"skipExecOnStart": false,
		"stopActionsIfNotDone": false,
		"group": false,
		"dockSettings": {
			"register": false,
			"hasRunButton": true,
			"hasPauseButton": true,
			"hasStatusLabel": false,
			"highlightIfConditionsTrue": false,
			"runButtonText": "Run",
			"pauseButtonText": "Pause",
			"unpauseButtonText": "Unpause",
			"conditionsTrueStatusText": "Conditions are true.",
			"conditionsFalseStatusText": "Conditions are false."
		},
		"macroActionConditionSplitterPosition": [{
			"pos": 210
		}, {
			"pos": 210
		}],
		"macroElseActionSplitterPosition": [{
			"pos": 172
		}, {
			"pos": 0
		}],
		"registerHotkeys": false,
		"pauseHotkey": [],
		"unpauseHotkey": [],
		"togglePauseHotkey": [],
		"conditions": [{
			"segmentSettings": {
				"collapsed": false,
				"useCustomLabel": false,
				"customLabel": "My label"
			},
			"id": "replay_buffer",
			"logic": 0,
			"durationModifier": {
				"time_constraint": 0,
				"seconds": {
					"value": {
						"value": 1.0,
						"type": 0
					},
					"unit": 0,
					"version": 1
				}
			},
			"state": 2
		}],
		"actions": [{
			"segmentSettings": {
				"collapsed": false,
				"useCustomLabel": false,
				"customLabel": "My label"
			},
			"id": "replay_buffer",
			"enabled": true,
			"action": 0
		}],
		"elseActions": [],
		"inputVariables": []
	}, {
		"name": "part2",
		"pause": false,
		"parallel": false,
		"onChange": false,
		"skipExecOnStart": false,
		"stopActionsIfNotDone": false,
		"group": false,
		"dockSettings": {
			"register": false,
			"hasRunButton": true,
			"hasPauseButton": true,
			"hasStatusLabel": false,
			"highlightIfConditionsTrue": false,
			"runButtonText": "Run",
			"pauseButtonText": "Pause",
			"unpauseButtonText": "Unpause",
			"conditionsTrueStatusText": "Conditions are true.",
			"conditionsFalseStatusText": "Conditions are false."
		},
		"macroActionConditionSplitterPosition": [{
			"pos": 210
		}, {
			"pos": 210
		}],
		"macroElseActionSplitterPosition": [{
			"pos": 172
		}, {
			"pos": 0
		}],
		"registerHotkeys": false,
		"pauseHotkey": [],
		"unpauseHotkey": [],
		"togglePauseHotkey": [],
		"conditions": [{
			"segmentSettings": {
				"collapsed": false,
				"useCustomLabel": false,
				"customLabel": "My label"
			},
			"id": "macro",
			"logic": 0,
			"durationModifier": {
				"time_constraint": 4,
				"seconds": {
					"value": {
						"value": 1.0,
						"type": 0
					},
					"unit": 0,
					"version": 1
				}
			},
			"macros": [],
			"macro": "part1",
			"type": 1,
			"condition": 0,
			"count": {
				"value": 0,
				"type": 0
			},
			"multiStateCount": {
				"value": 0,
				"type": 0
			},
			"multiStateCondition": 2,
			"actionIndex": {
				"value": 1,
				"type": 0
			},
			"version": 1
		}],
		"actions": [{
			"segmentSettings": {
				"collapsed": false,
				"useCustomLabel": false,
				"customLabel": "My label"
			},
			"id": "replay_buffer",
			"enabled": true,
			"action": 1
		}],
		"elseActions": [],
		"inputVariables": []
	}],
	"variables": [],
	"actionQueues": [],
	"version": "1.26.4"
}
This macro is a bit more reliable than the more barebones ones that wait for a fixed number of milliseconds, because by being slightly faster it tries to waste as little footage as possible
 

KillianM

New Member
Until the much-requested smarter replay buffer without overlap/waste gets implemented, one can "hack it" with Advanced Scene Switcher macros. The disadvantage is that you can potentially lose about 1 second of footage *after* every replay save (aka the time OBS takes to clumsily stop&restart the buffer)

1. install the Advanced Scene Switcher plugin https://obsproject.com/forum/resources/advanced-scene-switcher.395/
2. Tools -> Advanced Scene Switcher -> Macro -> right click the Macros menu on the left -> Import -> paste the code -> OK
JSON:
{
    "macros": [{
        "name": "replay-anti-overlap",
        "pause": false,
        "parallel": false,
        "onChange": true,
        "skipExecOnStart": false,
        "stopActionsIfNotDone": false,
        "group": true,
        "groupData": {
            "collapsed": false,
            "size": 2
        }
    }, {
        "name": "part1",
        "pause": false,
        "parallel": false,
        "onChange": false,
        "skipExecOnStart": false,
        "stopActionsIfNotDone": false,
        "group": false,
        "dockSettings": {
            "register": false,
            "hasRunButton": true,
            "hasPauseButton": true,
            "hasStatusLabel": false,
            "highlightIfConditionsTrue": false,
            "runButtonText": "Run",
            "pauseButtonText": "Pause",
            "unpauseButtonText": "Unpause",
            "conditionsTrueStatusText": "Conditions are true.",
            "conditionsFalseStatusText": "Conditions are false."
        },
        "macroActionConditionSplitterPosition": [{
            "pos": 210
        }, {
            "pos": 210
        }],
        "macroElseActionSplitterPosition": [{
            "pos": 172
        }, {
            "pos": 0
        }],
        "registerHotkeys": false,
        "pauseHotkey": [],
        "unpauseHotkey": [],
        "togglePauseHotkey": [],
        "conditions": [{
            "segmentSettings": {
                "collapsed": false,
                "useCustomLabel": false,
                "customLabel": "My label"
            },
            "id": "replay_buffer",
            "logic": 0,
            "durationModifier": {
                "time_constraint": 0,
                "seconds": {
                    "value": {
                        "value": 1.0,
                        "type": 0
                    },
                    "unit": 0,
                    "version": 1
                }
            },
            "state": 2
        }],
        "actions": [{
            "segmentSettings": {
                "collapsed": false,
                "useCustomLabel": false,
                "customLabel": "My label"
            },
            "id": "replay_buffer",
            "enabled": true,
            "action": 0
        }],
        "elseActions": [],
        "inputVariables": []
    }, {
        "name": "part2",
        "pause": false,
        "parallel": false,
        "onChange": false,
        "skipExecOnStart": false,
        "stopActionsIfNotDone": false,
        "group": false,
        "dockSettings": {
            "register": false,
            "hasRunButton": true,
            "hasPauseButton": true,
            "hasStatusLabel": false,
            "highlightIfConditionsTrue": false,
            "runButtonText": "Run",
            "pauseButtonText": "Pause",
            "unpauseButtonText": "Unpause",
            "conditionsTrueStatusText": "Conditions are true.",
            "conditionsFalseStatusText": "Conditions are false."
        },
        "macroActionConditionSplitterPosition": [{
            "pos": 210
        }, {
            "pos": 210
        }],
        "macroElseActionSplitterPosition": [{
            "pos": 172
        }, {
            "pos": 0
        }],
        "registerHotkeys": false,
        "pauseHotkey": [],
        "unpauseHotkey": [],
        "togglePauseHotkey": [],
        "conditions": [{
            "segmentSettings": {
                "collapsed": false,
                "useCustomLabel": false,
                "customLabel": "My label"
            },
            "id": "macro",
            "logic": 0,
            "durationModifier": {
                "time_constraint": 4,
                "seconds": {
                    "value": {
                        "value": 1.0,
                        "type": 0
                    },
                    "unit": 0,
                    "version": 1
                }
            },
            "macros": [],
            "macro": "part1",
            "type": 1,
            "condition": 0,
            "count": {
                "value": 0,
                "type": 0
            },
            "multiStateCount": {
                "value": 0,
                "type": 0
            },
            "multiStateCondition": 2,
            "actionIndex": {
                "value": 1,
                "type": 0
            },
            "version": 1
        }],
        "actions": [{
            "segmentSettings": {
                "collapsed": false,
                "useCustomLabel": false,
                "customLabel": "My label"
            },
            "id": "replay_buffer",
            "enabled": true,
            "action": 1
        }],
        "elseActions": [],
        "inputVariables": []
    }],
    "variables": [],
    "actionQueues": [],
    "version": "1.26.4"
}
This macro is a bit more reliable than the more barebones ones that wait for a fixed number of milliseconds, because by being slightly faster it tries to waste as little footage as possible
I had to reset things up and stumbled back on this thread. This is working like a charm (so far) for me. Thanks very much!
 

Suslik V

Active Member
Similar functionality with Python script (".py") for OBS:
 

HAMODE3389

New Member
Lua:
obs = obslua

function script_description()
    return "Stops and re-enables replay buffer when a replay is saved."
end

function script_load(settings)
    obs.obs_frontend_add_event_callback(on_event)
end

function on_event(event)
    if event == obs.OBS_FRONTEND_EVENT_REPLAY_BUFFER_SAVED then
        toggle_replay_buffer()
    end
end

function toggle_replay_buffer()
    local replay_buffer_active = obs.obs_frontend_replay_buffer_active()
  
    if replay_buffer_active then
        obs.obs_frontend_replay_buffer_stop()
        obs.timer_add(start_replay_buffer, 2000)  -- Re-enable after 2 second
    end
end

function start_replay_buffer()
    obs.timer_remove(start_replay_buffer)
    obs.obs_frontend_replay_buffer_start()
end
Credit: ChatGPT

Even though it’s been a while since I first shared this script, I’ve since improved it and have been using the updated version. Here’s the improved script:
Lua:
obs = obslua

SETTING_ENABLED = "enabled"

enabled = true

function script_description()
    return "Replay Buffer Always On."
end

function script_properties()
    local props = obs.obs_properties_create()
    obs.obs_properties_add_bool(props, SETTING_ENABLED, "AlwaysOn")
    return props
end

function script_load(settings)
    enabled = obs.obs_data_get_bool(settings, SETTING_ENABLED)
    obs.obs_frontend_add_event_callback(on_event)
    obs.timer_add(ensure_replay_buffer_active, 1000)  -- Check every 1 second
end

function script_update(settings)
    enabled = obs.obs_data_get_bool(settings, SETTING_ENABLED)
end

function on_event(event)
    if event == obs.OBS_FRONTEND_EVENT_EXIT then
        obs.timer_remove(ensure_replay_buffer_active)
    elseif event == obs.OBS_FRONTEND_EVENT_REPLAY_BUFFER_SAVED then
        stop_replay_buffer_temporarily()
    end
end

function stop_replay_buffer_temporarily()
    if enabled then
        obs.obs_frontend_replay_buffer_stop()
    end
end

function ensure_replay_buffer_active()
    if not enabled then
        return
    end
    
    local replay_buffer_active = obs.obs_frontend_replay_buffer_active()
    
    if not replay_buffer_active then
        obs.obs_frontend_replay_buffer_start()
    end
end

function script_save(settings)
    obs.obs_data_set_bool(settings, SETTING_ENABLED, enabled)
end
Credit: ChatGPT
 
Top