Coverage Report

Created: 2026-02-14 06:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gstreamer/subprojects/gst-plugins-base/gst-libs/gst/video/gstvideodmabufpool.c
Line
Count
Source
1
/* GStreamer video dmabuf pool
2
 *
3
 * Copyright (C) 2025 Collabora Ltd.
4
 * Author: Robert Mader <robert.mader@collabora.com>
5
 *
6
 * This library is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU Library General Public
8
 * License as published by the Free Software Foundation; either
9
 * version 2 of the License, or (at your option) any later version.
10
 *
11
 * This library is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
 * Library General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Library General Public
17
 * License along with this library; if not, write to the
18
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19
 * Boston, MA 02110-1301, USA.
20
 */
21
22
/**
23
 * SECTION:gstvideodmabufpool
24
 * @title: GstVideoDmabufPool
25
 * @short_description: Pool for virtual memory backed dmabufs
26
 * @see_also: #GstUdmabufAllocator
27
 *
28
 * Using #GstUdmabufAllocator, setting defaults and implementing implicit sync.
29
 *
30
 * Since: 1.28
31
 */
32
33
#ifdef HAVE_CONFIG_H
34
#include "config.h"
35
#endif
36
37
#include "gstvideodmabufpool.h"
38
39
#include <gst/allocators/gstudmabufallocator.h>
40
41
#ifdef HAVE_LINUX_DMA_BUF_H
42
#include <linux/dma-buf.h>
43
#include <sys/ioctl.h>
44
#include <unistd.h>
45
#endif
46
47
/* This alignment is needed on many AMD GPUs and is known to work well across
48
 * many vendors/GPUs, see e.g. GstGLDMABufBufferPool. */
49
0
#define UDMABUF_ALIGNMENT_MASK (256 - 1)
50
51
struct _GstVideoDmabufPool
52
{
53
  GstVideoBufferPool parent;
54
  GMainContext *context;
55
  GMainLoop *loop;
56
  GThread *thread;
57
};
58
59
GST_DEBUG_CATEGORY_STATIC (gst_video_dmabuf_pool_debug);
60
61
0
G_DEFINE_TYPE_WITH_CODE (GstVideoDmabufPool, gst_video_dmabuf_pool,
62
0
    GST_TYPE_VIDEO_BUFFER_POOL,
63
0
    GST_DEBUG_CATEGORY_INIT (gst_video_dmabuf_pool_debug, "video-dmabuf-pool",
64
0
        0, "video dmabuf pool");
65
0
    );
66
0
67
0
#ifdef HAVE_LINUX_DMA_BUF_H
68
0
#ifdef DMA_BUF_IOCTL_EXPORT_SYNC_FILE
69
0
typedef struct _DmaBufSource
70
0
{
71
0
  GSource base;
72
0
73
0
  GstVideoDmabufPool *pool;
74
0
  GstBuffer *buffer;
75
0
76
0
  gint mem_fds[GST_VIDEO_MAX_PLANES];
77
0
  gpointer fd_tags[GST_VIDEO_MAX_PLANES];
78
0
} DmaBufSource;
79
0
80
0
static gboolean
81
0
dma_buf_fd_readable (gint fd)
82
0
{
83
0
  GPollFD poll_fd;
84
0
85
0
  poll_fd.fd = fd;
86
0
  poll_fd.events = G_IO_IN;
87
0
  poll_fd.revents = 0;
88
0
89
0
  if (!g_poll (&poll_fd, 1, 0))
90
0
    return FALSE;
91
0
92
0
  return (poll_fd.revents & (G_IO_IN | G_IO_NVAL)) != 0;
93
0
}
94
0
95
0
static int
96
0
get_sync_file (gint fd)
97
0
{
98
0
  struct dma_buf_export_sync_file sync_file_in_out = {
99
0
    .flags = DMA_BUF_SYNC_WRITE,
100
0
    .fd = -1
101
0
  };
102
0
  gint ret;
103
0
104
0
  do {
105
0
    ret = ioctl (fd, DMA_BUF_IOCTL_EXPORT_SYNC_FILE, &sync_file_in_out);
106
0
  } while (ret == -1 && errno == EINTR);
107
0
108
0
  if (ret == 0)
109
0
    return sync_file_in_out.fd;
110
0
111
0
  return -1;
112
0
}
113
0
114
0
static gboolean
115
0
dma_buf_source_dispatch (GSource * base,
116
0
    GSourceFunc callback, gpointer user_data)
117
0
{
118
0
  DmaBufSource *source = (DmaBufSource *) base;
119
0
  GstVideoDmabufPool *self = GST_VIDEO_DMABUF_POOL (source->pool);
120
0
  gboolean ready;
121
0
122
0
  GST_DEBUG_OBJECT (self, "Dispatch source for buffer %p", source->buffer);
123
0
124
0
  ready = TRUE;
125
0
126
0
  for (gint i = 0; i < GST_VIDEO_MAX_PLANES; i++) {
127
0
    if (!source->fd_tags[i])
128
0
      continue;
129
0
130
0
    if (!dma_buf_fd_readable (source->mem_fds[i])) {
131
0
      GST_DEBUG_OBJECT (self, "Buffer %p not ready, sync file: %d",
132
0
          source->buffer, source->mem_fds[i]);
133
0
      ready = FALSE;
134
0
      continue;
135
0
    }
136
0
137
0
    close (source->mem_fds[i]);
138
0
    g_source_remove_unix_fd (base, source->fd_tags[i]);
139
0
    source->fd_tags[i] = NULL;
140
0
  }
141
0
142
0
  if (!ready)
143
0
    return G_SOURCE_CONTINUE;
144
0
145
0
  GST_DEBUG_OBJECT (self, "Releasing buffer %p from source, pool %p",
146
0
      source->buffer, self);
147
0
  GST_BUFFER_POOL_CLASS (gst_video_dmabuf_pool_parent_class)->release_buffer
148
0
      (GST_BUFFER_POOL_CAST (self), source->buffer);
149
0
  g_source_unref (base);
150
0
151
0
  return G_SOURCE_REMOVE;
152
0
}
153
0
154
0
static void
155
0
dma_buf_source_finalize (GSource * base)
156
0
{
157
0
  DmaBufSource *source = (DmaBufSource *) base;
158
0
  GstVideoDmabufPool *self = GST_VIDEO_DMABUF_POOL (source->pool);
159
0
  gboolean need_buffer_release = FALSE;
160
0
161
0
  for (gint i = 0; i < GST_VIDEO_MAX_PLANES; i++) {
162
0
    if (!source->fd_tags[i])
163
0
      continue;
164
0
165
0
    close (source->mem_fds[i]);
166
0
    g_source_remove_unix_fd (base, source->fd_tags[i]);
167
0
    source->fd_tags[i] = NULL;
168
0
    need_buffer_release = TRUE;
169
0
  }
170
0
171
0
  if (need_buffer_release) {
172
0
    GST_DEBUG_OBJECT (self, "Releasing buffer %p from source, pool %p",
173
0
        source->buffer, self);
174
0
    GST_BUFFER_POOL_CLASS (gst_video_dmabuf_pool_parent_class)->release_buffer
175
0
        (GST_BUFFER_POOL_CAST (self), source->buffer);
176
0
  }
177
0
}
178
0
179
0
static GSourceFuncs dma_buf_source_funcs = {
180
0
  .dispatch = dma_buf_source_dispatch,
181
0
  .finalize = dma_buf_source_finalize,
182
0
};
183
0
184
0
static gpointer
185
0
dmabuf_source_thread (gpointer data)
186
0
{
187
0
  GstVideoDmabufPool *self = data;
188
0
  GMainContext *context = NULL;
189
0
  GMainLoop *loop = NULL;
190
0
191
0
  GST_OBJECT_LOCK (self);
192
0
  if (self->context)
193
0
    context = g_main_context_ref (self->context);
194
0
  if (self->loop)
195
0
    loop = g_main_loop_ref (self->loop);
196
0
197
0
  if (context == NULL || loop == NULL) {
198
0
    g_clear_pointer (&loop, g_main_loop_unref);
199
0
    g_clear_pointer (&context, g_main_context_unref);
200
0
    GST_OBJECT_UNLOCK (self);
201
0
    return NULL;
202
0
  }
203
0
  GST_OBJECT_UNLOCK (self);
204
0
205
0
  g_main_context_push_thread_default (context);
206
0
207
0
  GST_DEBUG_OBJECT (self, "Running main loop");
208
0
  g_main_loop_run (loop);
209
0
  g_main_loop_unref (loop);
210
0
  g_main_context_unref (context);
211
0
212
0
  gst_object_unref (self);
213
0
214
0
  return NULL;
215
0
}
216
0
217
0
static gboolean
218
0
gst_video_dmabuf_pool_start (GstBufferPool * pool)
219
0
{
220
0
  GstVideoDmabufPool *self = GST_VIDEO_DMABUF_POOL (pool);
221
0
222
0
  GST_OBJECT_LOCK (self);
223
0
  GST_DEBUG_OBJECT (self, "Starting main loop");
224
0
  g_assert (self->context == NULL);
225
0
226
0
  self->context = g_main_context_new ();
227
0
  self->loop = g_main_loop_new (self->context, FALSE);
228
0
229
0
  self->thread =
230
0
      g_thread_new ("video-dmabuf-pool-source-loop", dmabuf_source_thread,
231
0
      g_object_ref (self));
232
0
233
0
  GST_OBJECT_UNLOCK (self);
234
0
  return
235
0
      GST_BUFFER_POOL_CLASS (gst_video_dmabuf_pool_parent_class)->start (pool);
236
0
}
237
0
238
0
static gboolean
239
0
gst_video_dmabuf_pool_stop (GstBufferPool * pool)
240
0
{
241
0
  GstVideoDmabufPool *self = GST_VIDEO_DMABUF_POOL (pool);
242
0
  GMainContext *context;
243
0
  GMainLoop *loop;
244
0
  GSource *idle_stop_source;
245
0
246
0
  GST_OBJECT_LOCK (self);
247
0
  GST_DEBUG_OBJECT (self, "Stopping main loop");
248
0
  context = self->context;
249
0
  loop = self->loop;
250
0
  self->context = NULL;
251
0
  self->loop = NULL;
252
0
  GST_OBJECT_UNLOCK (self);
253
0
254
0
  if (!context || !loop) {
255
0
    g_clear_pointer (&loop, g_main_loop_unref);
256
0
    g_clear_pointer (&context, g_main_context_unref);
257
0
    goto out;
258
0
  }
259
0
260
0
  idle_stop_source = g_idle_source_new ();
261
0
  g_source_set_callback (idle_stop_source, (GSourceFunc) g_main_loop_quit, loop,
262
0
      NULL);
263
0
  g_source_attach (idle_stop_source, context);
264
0
  g_source_unref (idle_stop_source);
265
0
266
0
  g_thread_join (self->thread);
267
0
  self->thread = NULL;
268
0
269
0
  g_main_loop_unref (loop);
270
0
  g_main_context_unref (context);
271
0
272
0
out:
273
0
  return
274
0
      GST_BUFFER_POOL_CLASS (gst_video_dmabuf_pool_parent_class)->stop (pool);
275
0
}
276
0
277
0
static void
278
0
gst_video_dmabuf_pool_release_buffer (GstBufferPool * pool, GstBuffer * buffer)
279
0
{
280
0
  GstVideoDmabufPool *self = GST_VIDEO_DMABUF_POOL (pool);
281
0
  DmaBufSource *source = NULL;
282
0
  guint n_mem;
283
0
284
0
  GST_DEBUG_OBJECT (self, "Buffer: %p", buffer);
285
0
286
0
  n_mem = gst_buffer_n_memory (buffer);
287
0
  for (gint i = 0; i < n_mem; i++) {
288
0
    GstMemory *mem;
289
0
    gint mem_fd, sync_file;
290
0
291
0
    mem = gst_buffer_peek_memory (buffer, i);
292
0
    if (!gst_is_dmabuf_memory (mem))
293
0
      continue;
294
0
295
0
    mem_fd = gst_dmabuf_memory_get_fd (mem);
296
0
    sync_file = get_sync_file (mem_fd);
297
0
    if (sync_file == -1) {
298
0
      GST_ERROR_OBJECT (self, "Exporting sync file failed");
299
0
      continue;
300
0
    }
301
0
302
0
    if (dma_buf_fd_readable (sync_file)) {
303
0
      GST_DEBUG_OBJECT (self, "Sync file readable");
304
0
      close (sync_file);
305
0
      continue;
306
0
    }
307
0
308
0
    if (!source) {
309
0
      GST_DEBUG_OBJECT (self, "Creating source for buffer %p, pool %p", buffer,
310
0
          self);
311
0
      source =
312
0
          (DmaBufSource *) g_source_new (&dma_buf_source_funcs,
313
0
          sizeof (*source));
314
0
      source->pool = self;
315
0
      source->buffer = buffer;
316
0
    }
317
0
318
0
    GST_DEBUG_OBJECT (self, "Adding sync file to source");
319
0
    source->mem_fds[i] = sync_file;
320
0
    source->fd_tags[i] =
321
0
        g_source_add_unix_fd (&source->base, sync_file, G_IO_IN);
322
0
  }
323
0
324
0
  if (source) {
325
0
    g_assert (self->context);
326
0
    g_source_attach ((GSource *) source, self->context);
327
0
    return;
328
0
  }
329
0
330
0
  GST_BUFFER_POOL_CLASS (gst_video_dmabuf_pool_parent_class)->release_buffer
331
0
      (pool, buffer);
332
0
}
333
0
#endif /* DMA_BUF_IOCTL_EXPORT_SYNC_FILE */
334
0
335
0
static gboolean
336
0
gst_video_dmabuf_pool_set_config (GstBufferPool * pool, GstStructure * config)
337
0
{
338
0
  GstVideoDmabufPool *self = GST_VIDEO_DMABUF_POOL (pool);
339
0
  GstAllocator *allocator;
340
0
  GstAllocationParams params;
341
0
  GstVideoAlignment video_align = { 0 };
342
0
  gboolean config_updated = FALSE;
343
0
  gboolean alignment_updated = FALSE;
344
0
  gboolean res;
345
346
0
  gst_buffer_pool_config_get_allocator (config, &allocator, &params);
347
0
  if (!GST_IS_DMABUF_ALLOCATOR (allocator) ||
348
0
      GST_OBJECT_FLAG_IS_SET (allocator, GST_ALLOCATOR_FLAG_CUSTOM_ALLOC)) {
349
0
    GST_DEBUG_OBJECT (self,
350
0
        "Allocator not a dmabuf allocator or having the CUSTOM_ALLOC flag set, trying to update to udmabuf");
351
352
0
    allocator = gst_udmabuf_allocator_get ();
353
0
    if (allocator) {
354
0
      params.align |= UDMABUF_ALIGNMENT_MASK;
355
0
      gst_buffer_pool_config_set_allocator (config, allocator, &params);
356
0
      gst_object_unref (allocator);
357
0
      config_updated = TRUE;
358
0
    } else {
359
0
      GST_ERROR_OBJECT (self, "udmabuf allocator not available");
360
0
      return FALSE;
361
0
    }
362
0
  } else if (params.align < UDMABUF_ALIGNMENT_MASK) {
363
0
    GST_DEBUG_OBJECT (self, "updating allocator params");
364
0
    params.align |= UDMABUF_ALIGNMENT_MASK;
365
0
    gst_buffer_pool_config_set_allocator (config, allocator, &params);
366
0
    config_updated = TRUE;
367
0
  }
368
369
0
  if (!gst_buffer_pool_config_has_option (config,
370
0
          GST_BUFFER_POOL_OPTION_VIDEO_META)) {
371
0
    GST_DEBUG_OBJECT (self, "missing video meta option");
372
0
    return FALSE;
373
0
  }
374
0
  if (!gst_buffer_pool_config_has_option (config,
375
0
          GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT)) {
376
0
    GST_DEBUG_OBJECT (self, "missing video alignment option");
377
0
    return FALSE;
378
0
  }
379
380
0
  gst_buffer_pool_config_get_video_alignment (config, &video_align);
381
0
  for (int i = 0; i != GST_VIDEO_MAX_PLANES; ++i) {
382
0
    if (video_align.stride_align[i] < UDMABUF_ALIGNMENT_MASK) {
383
0
      video_align.stride_align[i] |= UDMABUF_ALIGNMENT_MASK;
384
0
      alignment_updated = TRUE;
385
0
    }
386
0
  }
387
0
  if (alignment_updated) {
388
0
    GST_DEBUG_OBJECT (self, "updating video alignment");
389
0
    gst_buffer_pool_config_set_video_alignment (config, &video_align);
390
0
    config_updated = TRUE;
391
0
  }
392
393
0
  res =
394
0
      GST_BUFFER_POOL_CLASS (gst_video_dmabuf_pool_parent_class)->set_config
395
0
      (pool, config);
396
397
0
  if (config_updated)
398
0
    return FALSE;
399
400
0
  return res;
401
0
}
402
#endif /* HAVE_LINUX_DMA_BUF_H */
403
404
static void
405
gst_video_dmabuf_pool_init (GstVideoDmabufPool * self)
406
0
{
407
0
}
408
409
static void
410
gst_video_dmabuf_pool_class_init (GstVideoDmabufPoolClass * klass)
411
0
{
412
0
#ifdef HAVE_LINUX_DMA_BUF_H
413
0
  GstBufferPoolClass *pool_class = GST_BUFFER_POOL_CLASS (klass);
414
415
#ifdef DMA_BUF_IOCTL_EXPORT_SYNC_FILE
416
  pool_class->start = gst_video_dmabuf_pool_start;
417
  pool_class->stop = gst_video_dmabuf_pool_stop;
418
  pool_class->release_buffer = gst_video_dmabuf_pool_release_buffer;
419
#endif /* DMA_BUF_IOCTL_EXPORT_SYNC_FILE */
420
0
  pool_class->set_config = gst_video_dmabuf_pool_set_config;
421
0
#endif
422
0
}
423
424
/**
425
 * gst_video_dmabuf_pool_new:
426
 *
427
 * Create a new #GstVideoDmabufPool instance.
428
 *
429
 * Returns: (transfer full) (nullable): a #GstVideoDmabufPool or %NULL
430
 *     if dmabufs are not supported.
431
 *
432
 * Since: 1.28
433
 */
434
GstBufferPool *
435
gst_video_dmabuf_pool_new (void)
436
0
{
437
0
#ifdef HAVE_LINUX_DMA_BUF_H
438
0
  return g_object_new (GST_TYPE_VIDEO_DMABUF_POOL, NULL);
439
#else
440
  return NULL;
441
#endif
442
0
}