如何在GStreamer中通过零拷贝将DMABuf帧编码为视频流?
问题描述
通过Intel i915驱动的ioctl VFIO_DEVICE_QUERY_GFX_PLANE拿到了带帧缓冲区的dmabuf文件描述符,想在GStreamer里用零拷贝方式把它编码成H.264、H.265这类视频流,目前是用appsrc把单帧推送到管道里。考虑到用Intel硬件,选VAAPI是合理的,但VAAPI的sink pad只支持video/x-raw和video/x-raw(memory:VASurface)格式,而我手里的是video/x-raw(memory:DMABuf)格式。
想问两个问题:
- 有没有零拷贝的方法把
video/x-raw(memory:DMABuf)转成video/x-raw(memory:VASurface),或者直接把DMABuf导入成video/x-raw(memory:VASurface)? - 有没有比VAAPI更合适的框架?
当前推送帧的代码:
GstMemory* mem = gst_dmabuf_allocator_alloc(vedpy->gdata.allocator, dmabuf->fd, dmabuf->width * dmabuf->height * (dmabuf->stride/1024)); vedpy->gdata.buffer = gst_buffer_new(); gst_buffer_append_memory(vedpy->gdata.buffer, mem ); gsize offset[GST_VIDEO_MAX_PLANES] = {0, 0, 0, 0}; gint stride[GST_VIDEO_MAX_PLANES] = {dmabuf->stride, 0, 0, 0}; gst_buffer_add_video_meta_full( vedpy->gdata.buffer, GST_VIDEO_FRAME_FLAG_NONE, GST_VIDEO_FORMAT_ENCODED, dmabuf->width, dmabuf->height, 1, offset, stride); GstFlowReturn ret; vfio_encode_dpy *vedpy = container_of(dcl, vfio_encode_dpy, dcl); g_signal_emit_by_name (vedpy->gdata.source, "push-buffer", vedpy->gdata.buffer, &ret);
当前管道代码:
char launch_stream[] = "appsrc name=source ! " " video/x-raw(memory:DMABuf),width=1024,height=768,framerate=0/1,format={BGRx,BGRx:0x0100000000000001} ! " " vaapipostproc !" " vaapih265enc ! ...
显然这个管道跑不起来,因为vaapipostproc没法和前一个元素链接。
解决方案
一、零拷贝转换DMABuf到VASurface
GStreamer的VAAPI插件里有vaapidmabufupload元素,专门用来做DMABuf到VAAPI VASurface的零拷贝上传——它直接复用DMABuf的内存,不需要额外数据拷贝,完美解决格式不兼容的问题。
修改后的管道代码如下:
char launch_stream[] = "appsrc name=source ! " " video/x-raw(memory:DMABuf),width=1024,height=768,framerate=0/1,format=BGRx ! " " vaapidmabufupload ! " " vaapih265enc ! " ...
注意:要确保你的GStreamer VAAPI插件版本在1.18以上,这个元素是较新版本才加入的。
另外,你推送帧的代码里有个错误:GST_VIDEO_FORMAT_ENCODED是给编码后帧用的,你推送的是原始帧,应该改成对应原始格式,比如GST_VIDEO_FORMAT_BGRx,修正后这部分代码:
gst_buffer_add_video_meta_full( vedpy->gdata.buffer, GST_VIDEO_FRAME_FLAG_NONE, GST_VIDEO_FORMAT_BGRx, // 替换为实际的原始格式 dmabuf->width, dmabuf->height, 1, offset, stride);
二、替代VAAPI的框架
如果VAAPI不符合你的需求,还有两个靠谱的选择:
- oneVPL(原Intel Media SDK):Intel官方的媒体处理SDK,对自家硬件优化更彻底,支持DMABuf直接导入,配套的GStreamer
msdk系列插件(比如msdkh265enc)也支持零拷贝。管道可以改成:char launch_stream[] = "appsrc name=source ! " " video/x-raw(memory:DMABuf),width=1024,height=768,framerate=0/1,format=BGRx ! " " msdkupload ! " " msdkh265enc ! " ... - FFmpeg + libva:直接用FFmpeg的底层API处理,通过
av_hwframe_ctx_create_derived把DMABuf导入到VAAPI上下文后再编码,这种方式更灵活,适合定制化需求较多的场景。
额外注意点
- 确认DMABuf的格式是VAAPI/oneVPL支持的,BGRx属于兼容格式,如果是特殊格式可能需要提前处理,但
vaapidmabufupload和msdkupload都能处理大部分常见格式的零拷贝转换。 - 确保GStreamer进程有权限访问DMABuf的文件描述符,避免权限不足导致的失败。
- 推送帧时记得设置正确的时间戳,不然编码后的视频可能出现播放卡顿、时序混乱等问题。
内容的提问来源于stack exchange,提问作者Xaterus
相关产品推荐
相关产品推荐

