@@ -137,6 +137,14 @@ struct _obs_pipewire_stream {
137137 struct spa_fraction fraction ;
138138 bool set ;
139139 } framerate ;
140+
141+ struct {
142+ int acquire_syncobj_fd ;
143+ int release_syncobj_fd ;
144+ uint64_t acquire_point ;
145+ uint64_t release_point ;
146+ bool set ;
147+ } sync ;
140148};
141149
142150/* auxiliary methods */
@@ -545,6 +553,20 @@ static void renegotiate_format(void *data, uint64_t expirations)
545553
546554/* ------------------------------------------------- */
547555
556+ static void return_unused_pw_buffer (struct pw_stream * stream , struct pw_buffer * b )
557+ {
558+ #if PW_CHECK_VERSION (1 , 2 , 0 )
559+ struct spa_buffer * buffer = b -> buffer ;
560+ struct spa_data * last_data = & buffer -> datas [buffer -> n_datas - 1 ];
561+ struct spa_meta_sync_timeline * synctimeline =
562+ spa_buffer_find_meta_data (buffer , SPA_META_SyncTimeline , sizeof (struct spa_meta_sync_timeline ));
563+
564+ if (synctimeline && (last_data -> type == SPA_DATA_SyncObj ))
565+ gs_sync_signal_syncobj_timeline_point (last_data -> fd , synctimeline -> release_point );
566+ #endif
567+ pw_stream_queue_buffer (stream , b );
568+ }
569+
548570static inline struct pw_buffer * find_latest_buffer (struct pw_stream * stream )
549571{
550572 struct pw_buffer * b ;
@@ -556,13 +578,27 @@ static inline struct pw_buffer *find_latest_buffer(struct pw_stream *stream)
556578 if (!aux )
557579 break ;
558580 if (b )
559- pw_stream_queue_buffer (stream , b );
581+ return_unused_pw_buffer (stream , b );
560582 b = aux ;
561583 }
562584
563585 return b ;
564586}
565587
588+ static uint32_t get_spa_buffer_plane_count (const struct spa_buffer * buffer )
589+ {
590+ uint32_t plane_count = 0 ;
591+
592+ while (plane_count < buffer -> n_datas ) {
593+ if (buffer -> datas [plane_count ].type == SPA_DATA_DmaBuf )
594+ plane_count ++ ;
595+ else
596+ break ;
597+ }
598+
599+ return plane_count ;
600+ }
601+
566602static enum video_colorspace video_colorspace_from_spa_color_matrix (enum spa_video_color_matrix matrix )
567603{
568604 switch (matrix ) {
@@ -681,7 +717,7 @@ static void process_video_sync(obs_pipewire_stream *obs_pw_stream)
681717 header = spa_buffer_find_meta_data (buffer , SPA_META_Header , sizeof (* header ));
682718 if (header && (header -> flags & SPA_META_HEADER_FLAG_CORRUPTED ) > 0 ) {
683719 blog (LOG_ERROR , "[pipewire] buffer is corrupt" );
684- pw_stream_queue_buffer (obs_pw_stream -> stream , b );
720+ return_unused_pw_buffer (obs_pw_stream -> stream , b );
685721 return ;
686722 }
687723
@@ -695,13 +731,17 @@ static void process_video_sync(obs_pipewire_stream *obs_pw_stream)
695731 goto read_metadata ;
696732
697733 if (buffer -> datas [0 ].type == SPA_DATA_DmaBuf ) {
698- uint32_t planes = buffer -> n_datas ;
734+ uint32_t planes = get_spa_buffer_plane_count ( buffer ) ;
699735 uint32_t * offsets = alloca (sizeof (uint32_t ) * planes );
700736 uint32_t * strides = alloca (sizeof (uint32_t ) * planes );
701737 uint64_t * modifiers = alloca (sizeof (uint64_t ) * planes );
702738 int * fds = alloca (sizeof (int ) * planes );
703739 bool use_modifiers ;
704740 bool corrupt = false;
741+ #if PW_CHECK_VERSION (1 , 2 , 0 )
742+ struct spa_meta_sync_timeline * synctimeline =
743+ spa_buffer_find_meta_data (buffer , SPA_META_SyncTimeline , sizeof (struct spa_meta_sync_timeline ));
744+ #endif
705745
706746#ifdef DEBUG_PIPEWIRE
707747 blog (LOG_DEBUG , "[pipewire] DMA-BUF info: fd:%ld, stride:%d, offset:%u, size:%dx%d" ,
@@ -724,6 +764,25 @@ static void process_video_sync(obs_pipewire_stream *obs_pw_stream)
724764 corrupt |= (buffer -> datas [plane ].chunk -> flags & SPA_CHUNK_FLAG_CORRUPTED ) > 0 ;
725765 }
726766
767+ #if PW_CHECK_VERSION (1 , 2 , 0 )
768+ if (synctimeline && (buffer -> n_datas == (planes + 2 ))) {
769+ assert (buffer -> datas [planes ].type == SPA_DATA_SyncObj );
770+ assert (buffer -> datas [planes + 1 ].type == SPA_DATA_SyncObj );
771+
772+ obs_pw_stream -> sync .acquire_syncobj_fd = buffer -> datas [planes ].fd ;
773+ obs_pw_stream -> sync .acquire_point = synctimeline -> acquire_point ;
774+
775+ obs_pw_stream -> sync .release_syncobj_fd = buffer -> datas [planes + 1 ].fd ;
776+ obs_pw_stream -> sync .release_point = synctimeline -> release_point ;
777+
778+ obs_pw_stream -> sync .set = true;
779+ } else {
780+ obs_pw_stream -> sync .set = false;
781+ }
782+ #else
783+ obs_pw_stream -> sync .set = false;
784+ #endif
785+
727786 if (corrupt ) {
728787 blog (LOG_DEBUG , "[pipewire] buffer contains corrupted data" );
729788 goto read_metadata ;
@@ -861,13 +920,16 @@ static void on_param_changed_cb(void *user_data, uint32_t id, const struct spa_p
861920 obs_pipewire_stream * obs_pw_stream = user_data ;
862921 obs_pipewire * obs_pw = obs_pw_stream -> obs_pw ;
863922 struct spa_pod_builder pod_builder ;
864- const struct spa_pod * params [5 ];
923+ const struct spa_pod * params [7 ];
865924 const char * format_name ;
866925 uint32_t n_params = 0 ;
867926 uint32_t buffer_types ;
868927 uint32_t output_flags ;
869928 uint8_t params_buffer [1024 ];
870929 int result ;
930+ #if PW_CHECK_VERSION (1 , 2 , 0 )
931+ bool supports_explicit_sync = false;
932+ #endif
871933
872934 if (!param || id != SPA_PARAM_Format )
873935 return ;
@@ -887,8 +949,14 @@ static void on_param_changed_cb(void *user_data, uint32_t id, const struct spa_p
887949 buffer_types = 1 << SPA_DATA_MemPtr ;
888950 bool has_modifier = spa_pod_find_prop (param , NULL , SPA_FORMAT_VIDEO_modifier ) != NULL ;
889951 if ((has_modifier || check_pw_version (& obs_pw -> server_version , 0 , 3 , 24 )) &&
890- (output_flags & OBS_SOURCE_ASYNC_VIDEO ) != OBS_SOURCE_ASYNC_VIDEO )
952+ (output_flags & OBS_SOURCE_ASYNC_VIDEO ) != OBS_SOURCE_ASYNC_VIDEO ) {
891953 buffer_types |= 1 << SPA_DATA_DmaBuf ;
954+ #if PW_CHECK_VERSION (1 , 2 , 0 )
955+ obs_enter_graphics ();
956+ supports_explicit_sync = gs_query_sync_capabilities ();
957+ obs_leave_graphics ();
958+ #endif
959+ }
892960
893961 blog (LOG_INFO , "[pipewire] Negotiated format:" );
894962
@@ -921,9 +989,33 @@ static void on_param_changed_cb(void *user_data, uint32_t id, const struct spa_p
921989 CURSOR_META_SIZE (1024 , 1024 )));
922990
923991 /* Buffer options */
992+ #if PW_CHECK_VERSION (1 , 2 , 0 )
993+ if (supports_explicit_sync ) {
994+ struct spa_pod_frame dmabuf_explicit_sync_frame ;
995+
996+ spa_pod_builder_push_object (& pod_builder , & dmabuf_explicit_sync_frame , SPA_TYPE_OBJECT_ParamBuffers ,
997+ SPA_PARAM_Buffers );
998+ spa_pod_builder_add (& pod_builder , SPA_PARAM_BUFFERS_dataType , SPA_POD_Int (1 << SPA_DATA_DmaBuf ), 0 );
999+ spa_pod_builder_prop (& pod_builder , SPA_PARAM_BUFFERS_metaType , SPA_POD_PROP_FLAG_MANDATORY );
1000+ spa_pod_builder_int (& pod_builder , 1 << SPA_META_SyncTimeline );
1001+
1002+ params [n_params ++ ] = spa_pod_builder_pop (& pod_builder , & dmabuf_explicit_sync_frame );
1003+ }
1004+ #endif
1005+
9241006 params [n_params ++ ] = spa_pod_builder_add_object (& pod_builder , SPA_TYPE_OBJECT_ParamBuffers , SPA_PARAM_Buffers ,
9251007 SPA_PARAM_BUFFERS_dataType , SPA_POD_Int (buffer_types ));
9261008
1009+ /* Sync timeline */
1010+ #if PW_CHECK_VERSION (1 , 2 , 0 )
1011+ if (supports_explicit_sync ) {
1012+ params [n_params ++ ] = spa_pod_builder_add_object (& pod_builder , SPA_TYPE_OBJECT_ParamMeta , SPA_PARAM_Meta ,
1013+ SPA_PARAM_META_type , SPA_POD_Id (SPA_META_SyncTimeline ),
1014+ SPA_PARAM_META_size ,
1015+ SPA_POD_Int (sizeof (struct spa_meta_sync_timeline )));
1016+ }
1017+ #endif
1018+
9271019 /* Meta header */
9281020 params [n_params ++ ] = spa_pod_builder_add_object (& pod_builder , SPA_TYPE_OBJECT_ParamMeta , SPA_PARAM_Meta ,
9291021 SPA_PARAM_META_type , SPA_POD_Id (SPA_META_Header ),
@@ -1211,6 +1303,13 @@ void obs_pipewire_stream_video_render(obs_pipewire_stream *obs_pw_stream, gs_eff
12111303 if (!obs_pw_stream -> texture )
12121304 return ;
12131305
1306+ if (obs_pw_stream -> sync .set ) {
1307+ gs_sync_t * acquire_sync = gs_sync_create_from_syncobj_timeline_point (
1308+ obs_pw_stream -> sync .acquire_syncobj_fd , obs_pw_stream -> sync .acquire_point );
1309+ gs_sync_wait (acquire_sync );
1310+ gs_sync_destroy (acquire_sync );
1311+ }
1312+
12141313 image = gs_effect_get_param_by_name (effect , "image" );
12151314 gs_effect_set_texture (image , obs_pw_stream -> texture );
12161315
@@ -1253,6 +1352,13 @@ void obs_pipewire_stream_video_render(obs_pipewire_stream *obs_pw_stream, gs_eff
12531352 }
12541353
12551354 gs_blend_state_pop ();
1355+
1356+ if (obs_pw_stream -> sync .set ) {
1357+ gs_sync_t * release_sync = gs_sync_create ();
1358+ gs_sync_export_syncobj_timeline_point (release_sync , obs_pw_stream -> sync .release_syncobj_fd ,
1359+ obs_pw_stream -> sync .release_point );
1360+ gs_sync_destroy (release_sync );
1361+ }
12561362}
12571363
12581364void obs_pipewire_stream_set_cursor_visible (obs_pipewire_stream * obs_pw_stream , bool cursor_visible )
0 commit comments