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+
2129struct 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)
112130static 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+
130204static 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}
265393static 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