@@ -46,6 +46,16 @@ struct video_input {
4646 struct video_frame frame [MAX_CONVERT_BUFFERS ];
4747 int cur_frame ;
4848
49+ // allow outputting at fractions of main composition FPS,
50+ // e.g. 60 FPS with frame_rate_divisor = 1 turns into 30 FPS
51+ //
52+ // a separate counter is used in favor of using remainder calculations
53+ // to allow "inputs" started at the same time to start on the same frame
54+ // whereas with remainder calculation the frame alignment would depend on
55+ // the total frame count at the time the encoder was started
56+ uint32_t frame_rate_divisor ;
57+ uint32_t frame_rate_divisor_counter ;
58+
4959 void (* callback )(void * param , struct video_data * frame );
5060 void * param ;
5161};
@@ -77,6 +87,8 @@ struct video_output {
7787 size_t last_added ;
7888 struct cached_frame_info cache [MAX_CACHE_SIZE ];
7989
90+ struct video_output * parent ;
91+
8092 volatile bool raw_active ;
8193 volatile long gpu_refs ;
8294};
@@ -136,6 +148,17 @@ static inline bool video_output_cur_frame(struct video_output *video)
136148 struct video_input * input = video -> inputs .array + i ;
137149 struct video_data frame = frame_info -> frame ;
138150
151+ // an explicit counter is used instead of remainder calculation
152+ // to allow multiple encoders started at the same time to start on
153+ // the same frame
154+ uint32_t skip = input -> frame_rate_divisor_counter ++ ;
155+ if (input -> frame_rate_divisor_counter ==
156+ input -> frame_rate_divisor )
157+ input -> frame_rate_divisor_counter = 0 ;
158+
159+ if (skip )
160+ continue ;
161+
139162 if (scale_video_output (input , & frame ))
140163 input -> callback (input -> param , & frame );
141164 }
@@ -371,13 +394,37 @@ static inline void reset_frames(video_t *video)
371394 os_atomic_set_long (& video -> total_frames , 0 );
372395}
373396
397+ static const video_t * get_const_root (const video_t * video )
398+ {
399+ while (video -> parent )
400+ video = video -> parent ;
401+ return video ;
402+ }
403+
404+ static video_t * get_root (video_t * video )
405+ {
406+ while (video -> parent )
407+ video = video -> parent ;
408+ return video ;
409+ }
410+
374411bool video_output_connect (
375412 video_t * video , const struct video_scale_info * conversion ,
376413 void (* callback )(void * param , struct video_data * frame ), void * param )
414+ {
415+ return video_output_connect2 (video , conversion , 1 , callback , param );
416+ }
417+
418+ bool video_output_connect2 (
419+ video_t * video , const struct video_scale_info * conversion ,
420+ uint32_t frame_rate_divisor ,
421+ void (* callback )(void * param , struct video_data * frame ), void * param )
377422{
378423 bool success = false;
379424
380- if (!video || !callback )
425+ video = get_root (video );
426+
427+ if (!video || !callback || frame_rate_divisor == 0 )
381428 return false;
382429
383430 pthread_mutex_lock (& video -> input_mutex );
@@ -389,6 +436,8 @@ bool video_output_connect(
389436 input .callback = callback ;
390437 input .param = param ;
391438
439+ input .frame_rate_divisor = frame_rate_divisor ;
440+
392441 if (conversion ) {
393442 input .conversion = * conversion ;
394443 } else {
@@ -446,6 +495,8 @@ void video_output_disconnect(video_t *video,
446495 if (!video || !callback )
447496 return ;
448497
498+ video = get_root (video );
499+
449500 pthread_mutex_lock (& video -> input_mutex );
450501
451502 size_t idx = video_get_input_idx (video , callback , param );
@@ -468,7 +519,7 @@ bool video_output_active(const video_t *video)
468519{
469520 if (!video )
470521 return false;
471- return os_atomic_load_bool (& video -> raw_active );
522+ return os_atomic_load_bool (& get_const_root ( video ) -> raw_active );
472523}
473524
474525const struct video_output_info * video_output_get_info (const video_t * video )
@@ -485,6 +536,8 @@ bool video_output_lock_frame(video_t *video, struct video_frame *frame,
485536 if (!video )
486537 return false;
487538
539+ video = get_root (video );
540+
488541 pthread_mutex_lock (& video -> data_mutex );
489542
490543 if (video -> available_frames == 0 ) {
@@ -518,6 +571,8 @@ void video_output_unlock_frame(video_t *video)
518571 if (!video )
519572 return ;
520573
574+ video = get_root (video );
575+
521576 pthread_mutex_lock (& video -> data_mutex );
522577
523578 video -> available_frames -- ;
@@ -538,6 +593,8 @@ void video_output_stop(video_t *video)
538593 if (!video )
539594 return ;
540595
596+ video = get_root (video );
597+
541598 if (!video -> stop ) {
542599 video -> stop = true;
543600 os_sem_post (video -> update_semaphore );
@@ -550,40 +607,44 @@ bool video_output_stopped(video_t *video)
550607 if (!video )
551608 return true;
552609
553- return video -> stop ;
610+ return get_root ( video ) -> stop ;
554611}
555612
556613enum video_format video_output_get_format (const video_t * video )
557614{
558- return video ? video -> info .format : VIDEO_FORMAT_NONE ;
615+ return video ? get_const_root ( video ) -> info .format : VIDEO_FORMAT_NONE ;
559616}
560617
561618uint32_t video_output_get_width (const video_t * video )
562619{
563- return video ? video -> info .width : 0 ;
620+ return video ? get_const_root ( video ) -> info .width : 0 ;
564621}
565622
566623uint32_t video_output_get_height (const video_t * video )
567624{
568- return video ? video -> info .height : 0 ;
625+ return video ? get_const_root ( video ) -> info .height : 0 ;
569626}
570627
571628double video_output_get_frame_rate (const video_t * video )
572629{
573630 if (!video )
574631 return 0.0 ;
575632
633+ video = get_const_root (video );
634+
576635 return (double )video -> info .fps_num / (double )video -> info .fps_den ;
577636}
578637
579638uint32_t video_output_get_skipped_frames (const video_t * video )
580639{
581- return (uint32_t )os_atomic_load_long (& video -> skipped_frames );
640+ return (uint32_t )os_atomic_load_long (
641+ & get_const_root (video )-> skipped_frames );
582642}
583643
584644uint32_t video_output_get_total_frames (const video_t * video )
585645{
586- return (uint32_t )os_atomic_load_long (& video -> total_frames );
646+ return (uint32_t )os_atomic_load_long (
647+ & get_const_root (video )-> total_frames );
587648}
588649
589650/* Note: These four functions below are a very slight bit of a hack. If the
@@ -594,6 +655,8 @@ uint32_t video_output_get_total_frames(const video_t *video)
594655
595656void video_output_inc_texture_encoders (video_t * video )
596657{
658+ video = get_root (video );
659+
597660 if (os_atomic_inc_long (& video -> gpu_refs ) == 1 &&
598661 !os_atomic_load_bool (& video -> raw_active )) {
599662 reset_frames (video );
@@ -602,6 +665,8 @@ void video_output_inc_texture_encoders(video_t *video)
602665
603666void video_output_dec_texture_encoders (video_t * video )
604667{
668+ video = get_root (video );
669+
605670 if (os_atomic_dec_long (& video -> gpu_refs ) == 0 &&
606671 !os_atomic_load_bool (& video -> raw_active )) {
607672 log_skipped (video );
@@ -610,10 +675,32 @@ void video_output_dec_texture_encoders(video_t *video)
610675
611676void video_output_inc_texture_frames (video_t * video )
612677{
613- os_atomic_inc_long (& video -> total_frames );
678+ os_atomic_inc_long (& get_root ( video ) -> total_frames );
614679}
615680
616681void video_output_inc_texture_skipped_frames (video_t * video )
617682{
618- os_atomic_inc_long (& video -> skipped_frames );
683+ os_atomic_inc_long (& get_root (video )-> skipped_frames );
684+ }
685+
686+ video_t * video_output_create_with_frame_rate_divisor (video_t * video ,
687+ uint32_t divisor )
688+ {
689+ // `divisor == 1` would result in the same frame rate,
690+ // resulting in an unnecessary additional video output
691+ if (!video || divisor == 0 || divisor == 1 )
692+ return NULL ;
693+
694+ video_t * new_video = bzalloc (sizeof (video_t ));
695+ memcpy (new_video , video , sizeof (* new_video ));
696+ new_video -> parent = video ;
697+ new_video -> info .fps_den *= divisor ;
698+
699+ return new_video ;
700+ }
701+
702+ void video_output_free_frame_rate_divisor (video_t * video )
703+ {
704+ if (video && video -> parent )
705+ bfree (video );
619706}
0 commit comments