浏览代码

linux-pipewire: Dup syncobj fds

PipeWire format renegotiation runs in parallel with video rendering.

When the stream format is renegotiated, PipeWire removes the existing
buffers and closes the syncobj file descriptors. At the same time, the
video rendering thread may try to import the (already closed) syncobj
acquire fd, and hang on waiting for the fence to become available.

This is not a problem for the dmabuf fd because it's imported into a
texture right away, which doesn't disappear when PipeWire closes the fd.

This commit adds duping to the syncobj fds so that they too remain open
as long as the rendering thread may access them.
Ivan Molodetskikh 3 月之前
父节点
当前提交
a7de3f4bde
共有 1 个文件被更改,包括 12 次插入2 次删除
  1. 12 2
      plugins/linux-pipewire/pipewire.c

+ 12 - 2
plugins/linux-pipewire/pipewire.c

@@ -26,6 +26,7 @@
 
 #include <gio/gio.h>
 #include <gio/gunixfdlist.h>
+#include <glib/gstdio.h>
 
 #include <fcntl.h>
 #include <glad/glad.h>
@@ -765,14 +766,18 @@ static void process_video_sync(obs_pipewire_stream *obs_pw_stream)
 		}
 
 #if PW_CHECK_VERSION(1, 2, 0)
+		g_clear_fd(&obs_pw_stream->sync.acquire_syncobj_fd, NULL);
+		g_clear_fd(&obs_pw_stream->sync.release_syncobj_fd, NULL);
+
 		if (synctimeline && (buffer->n_datas == (planes + 2))) {
 			assert(buffer->datas[planes].type == SPA_DATA_SyncObj);
 			assert(buffer->datas[planes + 1].type == SPA_DATA_SyncObj);
 
-			obs_pw_stream->sync.acquire_syncobj_fd = buffer->datas[planes].fd;
+			obs_pw_stream->sync.acquire_syncobj_fd = fcntl(buffer->datas[planes].fd, F_DUPFD_CLOEXEC, 5);
 			obs_pw_stream->sync.acquire_point = synctimeline->acquire_point;
 
-			obs_pw_stream->sync.release_syncobj_fd = buffer->datas[planes + 1].fd;
+			obs_pw_stream->sync.release_syncobj_fd =
+				fcntl(buffer->datas[planes + 1].fd, F_DUPFD_CLOEXEC, 5);
 			obs_pw_stream->sync.release_point = synctimeline->release_point;
 
 			obs_pw_stream->sync.set = true;
@@ -1179,6 +1184,8 @@ obs_pipewire_stream *obs_pipewire_connect_stream(obs_pipewire *obs_pw, obs_sourc
 	obs_pw_stream->cursor.visible = connect_info->screencast.cursor_visible;
 	obs_pw_stream->framerate.set = connect_info->video.framerate != NULL;
 	obs_pw_stream->resolution.set = connect_info->video.resolution != NULL;
+	obs_pw_stream->sync.acquire_syncobj_fd = -1;
+	obs_pw_stream->sync.release_syncobj_fd = -1;
 
 	if (obs_pw_stream->framerate.set)
 		obs_pw_stream->framerate.fraction = *connect_info->video.framerate;
@@ -1409,6 +1416,9 @@ void obs_pipewire_stream_destroy(obs_pipewire_stream *obs_pw_stream)
 	g_clear_pointer(&obs_pw_stream->stream, pw_stream_destroy);
 	pw_thread_loop_unlock(obs_pw_stream->obs_pw->thread_loop);
 
+	g_clear_fd(&obs_pw_stream->sync.acquire_syncobj_fd, NULL);
+	g_clear_fd(&obs_pw_stream->sync.release_syncobj_fd, NULL);
+
 	clear_format_info(obs_pw_stream);
 	bfree(obs_pw_stream);
 }