Skip to content

Commit 1844bb3

Browse files
committed
add restart and pause hotkey
add visibility and end actions
1 parent cc0f8e5 commit 1844bb3

File tree

1 file changed

+175
-33
lines changed

1 file changed

+175
-33
lines changed

replay-source.c

Lines changed: 175 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,34 @@
1818
#define warn(format, ...) \
1919
blog(LOG_WARNING, format, ##__VA_ARGS__)
2020

21+
#define VISIBILITY_ACTION_RESTART 0
22+
#define VISIBILITY_ACTION_PAUSE 1
23+
#define VISIBILITY_ACTION_CONTINUE 2
24+
25+
#define END_ACTION_HIDE 0
26+
#define END_ACTION_PAUSE 1
27+
#define END_ACTION_LOOP 2
28+
2129
struct replay_source {
2230
obs_source_t *source;
2331
obs_source_t *source_filter;
2432
char *source_name;
2533
long duration;
2634
int speed_percent;
35+
int visibility_action;
36+
int end_action;
2737
obs_hotkey_id replay_hotkey;
38+
obs_hotkey_id restart_hotkey;
39+
obs_hotkey_id pause_hotkey;
2840
uint64_t first_frame_timestamp;
2941
uint64_t start_timestamp;
3042
uint64_t last_frame_timestamp;
3143
uint64_t previous_frame_timestamp;
32-
bool loop;
44+
45+
bool play;
46+
bool restart;
47+
bool active;
48+
bool end;
3349

3450
/* contains struct obs_source_frame* */
3551
struct circlebuf video_frames;
@@ -85,7 +101,9 @@ static void replay_source_update(void *data, obs_data_t *settings)
85101
}
86102

87103
context->duration = obs_data_get_int(settings, "duration");
88-
context->loop = obs_data_get_bool(settings, "loop");
104+
context->visibility_action = obs_data_get_int(settings, "visibility_action");
105+
context->end_action = obs_data_get_int(settings, "end_action");
106+
89107
context->speed_percent = obs_data_get_int(settings, "speed_percent");
90108
if (context->speed_percent < 1 || context->speed_percent > 200)
91109
context->speed_percent = 100;
@@ -112,7 +130,6 @@ static void replay_source_update(void *data, obs_data_t *settings)
112130
static void replay_source_defaults(obs_data_t *settings)
113131
{
114132
obs_data_set_default_int(settings,SETTING_DURATION,5);
115-
obs_data_set_default_bool(settings,SETTING_LOOP,true);
116133
obs_data_set_default_int(settings, "speed_percent", 100);
117134
}
118135

@@ -127,6 +144,63 @@ static void replay_source_hide(void *data)
127144

128145
}
129146

147+
static void replay_source_active(void *data)
148+
{
149+
struct replay_source *context = data;
150+
if(context->visibility_action == VISIBILITY_ACTION_PAUSE)
151+
{
152+
context->play = true;
153+
}
154+
else if(context->visibility_action == VISIBILITY_ACTION_RESTART)
155+
{
156+
context->play = true;
157+
context->restart = true;
158+
}
159+
context->active = true;
160+
}
161+
162+
static void replay_source_deactive(void *data)
163+
{
164+
struct replay_source *context = data;
165+
if(context->visibility_action == VISIBILITY_ACTION_PAUSE)
166+
{
167+
context->play = false;
168+
}
169+
else if(context->visibility_action == VISIBILITY_ACTION_RESTART)
170+
{
171+
context->play = false;
172+
context->restart = true;
173+
}
174+
context->active = false;
175+
}
176+
177+
static void replay_restart_hotkey(void *data, obs_hotkey_id id,
178+
obs_hotkey_t *hotkey, bool pressed)
179+
{
180+
UNUSED_PARAMETER(id);
181+
UNUSED_PARAMETER(hotkey);
182+
183+
struct replay_source *c = data;
184+
185+
if(pressed){
186+
c->restart = true;
187+
c->play = true;
188+
}
189+
}
190+
191+
static void replay_pause_hotkey(void *data, obs_hotkey_id id,
192+
obs_hotkey_t *hotkey, bool pressed)
193+
{
194+
UNUSED_PARAMETER(id);
195+
UNUSED_PARAMETER(hotkey);
196+
197+
struct replay_source *c = data;
198+
199+
if(pressed){
200+
c->play = !c->play;
201+
}
202+
}
203+
130204
static void replay_hotkey(void *data, obs_hotkey_id id,
131205
obs_hotkey_t *hotkey, bool pressed)
132206
{
@@ -170,6 +244,10 @@ static void replay_hotkey(void *data, obs_hotkey_id id,
170244
circlebuf_push_back(&c->video_frames, &frame, sizeof(struct obs_source_frame*));
171245
}
172246
pthread_mutex_unlock(&parent->async_mutex);
247+
if(c->visibility_action == VISIBILITY_ACTION_CONTINUE || c->active)
248+
{
249+
c->play = true;
250+
}
173251
}
174252
}
175253
obs_source_release(s);
@@ -187,6 +265,16 @@ static void *replay_source_create(obs_data_t *settings, obs_source_t *source)
187265
"ReplaySource.Replay",
188266
obs_module_text("Replay"),
189267
replay_hotkey, context);
268+
269+
context->restart_hotkey = obs_hotkey_register_source(source,
270+
"ReplaySource.Restart",
271+
obs_module_text("Restart"),
272+
replay_restart_hotkey, context);
273+
274+
context->pause_hotkey = obs_hotkey_register_source(source,
275+
"ReplaySource.Pause",
276+
obs_module_text("Pause"),
277+
replay_pause_hotkey, context);
190278

191279
return context;
192280
}
@@ -219,48 +307,88 @@ static void replay_source_tick(void *data, float seconds)
219307
{
220308
struct replay_source *context = data;
221309

222-
if(context->video_frames.size){
223-
struct obs_source_frame *frame;
224-
struct obs_source_frame *peek_frame;
225-
circlebuf_peek_front(&context->video_frames, &peek_frame, sizeof(struct obs_source_frame*));
226-
const uint64_t timestamp = obs_get_video_frame_time();
227-
if(context->first_frame_timestamp == peek_frame->timestamp)
310+
if(!context->video_frames.size){
311+
context->play = false;
312+
}
313+
if(!context->play)
314+
{
315+
if(context->end && context->end_action == END_ACTION_HIDE)
228316
{
229-
context->start_timestamp = timestamp;
317+
obs_source_output_video(context->source, NULL);
230318
}
231-
uint64_t video_duration = timestamp - context->start_timestamp;
232-
uint64_t source_duration = (peek_frame->timestamp - context->first_frame_timestamp) * 100 / context->speed_percent ;
233-
if(video_duration < source_duration)
234-
return;
319+
return;
320+
}
321+
context->end = false;
322+
struct obs_source_frame *frame;
323+
struct obs_source_frame *peek_frame;
324+
circlebuf_peek_front(&context->video_frames, &peek_frame, sizeof(struct obs_source_frame*));
325+
const uint64_t timestamp = obs_get_video_frame_time();
326+
if(context->first_frame_timestamp == peek_frame->timestamp)
327+
{
328+
context->start_timestamp = timestamp;
329+
context->restart = false;
330+
}
331+
else if(context->restart)
332+
{
333+
while(peek_frame->timestamp != context->first_frame_timestamp)
334+
{
335+
circlebuf_pop_front(&context->video_frames, &frame, sizeof(struct obs_source_frame*));
235336

236-
while(context->video_frames.size && video_duration >= source_duration){
337+
struct obs_source_frame *new_frame = obs_source_frame_create(frame->format, frame->width, frame->height);
338+
new_frame->refs = 1;
339+
obs_source_frame_copy(new_frame, frame);
340+
circlebuf_push_back(&context->video_frames, &new_frame, sizeof(struct obs_source_frame*));
237341

238-
circlebuf_pop_front(&context->video_frames, &frame, sizeof(struct obs_source_frame*));
239-
if(context->loop){
240-
struct obs_source_frame *new_frame = obs_source_frame_create(frame->format, frame->width, frame->height);
241-
new_frame->refs = 1;
242-
obs_source_frame_copy(new_frame, frame);
243-
circlebuf_push_back(&context->video_frames, &new_frame, sizeof(struct obs_source_frame*));
244-
}
245342
circlebuf_peek_front(&context->video_frames, &peek_frame, sizeof(struct obs_source_frame*));
246-
source_duration = (peek_frame->timestamp - context->first_frame_timestamp) * 100 / context->speed_percent;
247-
if(context->first_frame_timestamp == peek_frame->timestamp)
343+
}
344+
context->restart = false;
345+
context->start_timestamp = timestamp;
346+
}
347+
if(context->last_frame_timestamp == peek_frame->timestamp)
348+
{
349+
if(context->end_action != END_ACTION_LOOP)
350+
{
351+
context->play = false;
352+
context->end = true;
353+
}
354+
}
355+
uint64_t video_duration = timestamp - context->start_timestamp;
356+
uint64_t source_duration = (peek_frame->timestamp - context->first_frame_timestamp) * 100 / context->speed_percent ;
357+
if(video_duration < source_duration)
358+
return;
359+
360+
while(context->play && context->video_frames.size && video_duration >= source_duration){
361+
362+
if(context->last_frame_timestamp == peek_frame->timestamp)
363+
{
364+
if(context->end_action != END_ACTION_LOOP)
248365
{
249-
context->start_timestamp = timestamp;
250-
video_duration = timestamp - context->start_timestamp;
366+
context->play = false;
367+
context->end = true;
251368
}
252369
}
253-
if(context->speed_percent != 100)
370+
circlebuf_pop_front(&context->video_frames, &frame, sizeof(struct obs_source_frame*));
371+
372+
struct obs_source_frame *new_frame = obs_source_frame_create(frame->format, frame->width, frame->height);
373+
new_frame->refs = 1;
374+
obs_source_frame_copy(new_frame, frame);
375+
circlebuf_push_back(&context->video_frames, &new_frame, sizeof(struct obs_source_frame*));
376+
377+
circlebuf_peek_front(&context->video_frames, &peek_frame, sizeof(struct obs_source_frame*));
378+
source_duration = (peek_frame->timestamp - context->first_frame_timestamp) * 100 / context->speed_percent;
379+
if(context->first_frame_timestamp == peek_frame->timestamp)
254380
{
255-
frame->timestamp = frame->timestamp * 100 / context->speed_percent;
381+
context->start_timestamp = timestamp;
382+
video_duration = timestamp - context->start_timestamp;
256383
}
257-
context->previous_frame_timestamp = frame->timestamp;
258-
obs_source_output_video(context->source, frame);
259384
}
260-
else
385+
if(context->speed_percent != 100)
261386
{
262-
obs_source_output_video(context->source, NULL);
387+
frame->timestamp = frame->timestamp * 100 / context->speed_percent;
263388
}
389+
context->previous_frame_timestamp = frame->timestamp;
390+
obs_source_output_video(context->source, frame);
391+
264392
}
265393
static bool EnumSources(void *data, obs_source_t *source)
266394
{
@@ -280,7 +408,19 @@ static obs_properties_t *replay_source_properties(void *data)
280408
obs_enum_sources(EnumSources, prop);
281409

282410
obs_properties_add_int(props,SETTING_DURATION,TEXT_DURATION,1,200,1);
283-
obs_properties_add_bool(props,SETTING_LOOP,TEXT_LOOP);
411+
412+
prop = obs_properties_add_list(props, "visibilty_action", "Visibility Action",
413+
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
414+
obs_property_list_add_int(prop, "Restart", VISIBILITY_ACTION_RESTART);
415+
obs_property_list_add_int(prop, "Pause", VISIBILITY_ACTION_PAUSE);
416+
obs_property_list_add_int(prop, "Continue", VISIBILITY_ACTION_CONTINUE);
417+
418+
prop = obs_properties_add_list(props, "end_action", "End Action",
419+
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
420+
obs_property_list_add_int(prop, "Hide", END_ACTION_HIDE);
421+
obs_property_list_add_int(prop, "Pause", END_ACTION_PAUSE);
422+
obs_property_list_add_int(prop, "Loop", END_ACTION_LOOP);
423+
284424
obs_properties_add_int_slider(props, "speed_percent",
285425
obs_module_text("SpeedPercentage"), 1, 200, 1);
286426

@@ -300,6 +440,8 @@ struct obs_source_info replay_source_info = {
300440
.get_defaults = replay_source_defaults,
301441
.show = replay_source_show,
302442
.hide = replay_source_hide,
443+
.activate = replay_source_active,
444+
.deactivate = replay_source_deactive,
303445
.video_tick = replay_source_tick,
304446
.get_properties = replay_source_properties
305447
};

0 commit comments

Comments
 (0)