GStreamer 1.24.3中实现裁剪前后图像同步配对并通过单个AppSink输出的问题排查
GStreamer 1.24.3中实现裁剪前后图像同步配对并通过单个AppSink输出的问题排查
看起来你遇到的核心问题是自定义GstMeta在管道传递过程中丢失,导致没法把原始FHD图像附在缩放后的Child图像里,一起传递到单个AppSink。我来帮你一步步排查问题,同时给出可行的修复方案和替代思路。
一、自定义Meta的Transform函数逻辑错误
你当前的fhd_image_transform函数存在两个关键问题,这是Meta丢失的主要原因:
- 当目标Buffer不可写时,直接返回FALSE,导致Meta无法被传递到新的Buffer
- 当目标Buffer已有Meta时,直接赋值指针而没有处理引用计数,会导致内存泄漏或者野指针
正确的Transform函数应该这样实现:
gboolean fhd_image_transform(GstBuffer* dst, GstMeta* src_meta, GstBuffer* src, GQuark type, gpointer data) { UNUSED(data); FhdImageMeta* src_fhd = (FhdImageMeta*)src_meta; FhdImageMeta* dst_fhd = NULL; // 先检查目标Buffer是否已经有我们的Meta dst_fhd = (FhdImageMeta*)gst_buffer_get_meta(dst, fhd_image_meta_api_get_type()); if (dst_fhd) { // 如果已有Meta,先释放旧的Buffer引用,再引用新的 if (dst_fhd->fhd_image) { gst_buffer_unref(dst_fhd->fhd_image); } dst_fhd->fhd_image = gst_buffer_ref(src_fhd->fhd_image); return TRUE; } // 如果目标Buffer不可写,尝试创建可写拷贝 if (!gst_buffer_is_writable(dst)) { GstBuffer* writable_dst = gst_buffer_make_writable(dst); if (!writable_dst) { GST_WARNING("Failed to make destination buffer writable"); return FALSE; } // 替换为可写的Buffer GST_PAD_PROBE_INFO_DATA(data) = writable_dst; dst = writable_dst; } // 添加新的Meta并拷贝FHD Buffer引用 dst_fhd = (FhdImageMeta*)gst_buffer_add_meta(dst, fhd_image_meta_get_info(), NULL); if (dst_fhd) { dst_fhd->fhd_image = gst_buffer_ref(src_fhd->fhd_image); return TRUE; } GST_WARNING("Failed to add FHD image meta to destination buffer"); return FALSE; }
二、Probe函数的细节修正
你的attach_metadata probe函数中,在修改Buffer后,只有当Buffer是新创建的才替换数据,但其实应该直接替换——因为gst_buffer_make_writable返回的如果是原Buffer(已经可写),替换也不会有问题:
GstPadProbeReturn attach_metadata(GstPad* pad, GstPadProbeInfo* info, gpointer user_data) { UNUSED(pad); UNUSED(user_data); GstBuffer* buffer = GST_PAD_PROBE_INFO_BUFFER(info); GstBuffer* writable_buf = gst_buffer_make_writable(buffer); if (!writable_buf) { GST_WARNING("Failed to make buffer writable in probe"); return GST_PAD_PROBE_OK; } const GstMetaInfo* metainfo = fhd_image_meta_get_info(); FhdImageMeta* meta = (FhdImageMeta*)gst_buffer_add_meta(writable_buf, metainfo, NULL); if (meta) { // 引用当前的Buffer作为FHD图像(probe中的是原始FHD Buffer) meta->fhd_image = gst_buffer_ref(writable_buf); } else { GST_WARNING("Failed to add FHD meta to buffer"); } // 不管是不是新Buffer,都替换probe中的数据 GST_PAD_PROBE_INFO_DATA(info) = writable_buf; return GST_PAD_PROBE_OK; }
三、使用GstParentBufferMeta的正确姿势
你提到尝试用GstParentBufferMeta但失败了,这是因为没有正确使用它的API。GstParentBufferMeta会自动处理引用计数和Meta传递,正确用法如下:
在probe中添加Meta:
GstPadProbeReturn attach_parent_meta(GstPad* pad, GstPadProbeInfo* info, gpointer user_data) { UNUSED(pad); UNUSED(user_data); GstBuffer* buffer = GST_PAD_PROBE_INFO_BUFFER(info); GstBuffer* writable_buf = gst_buffer_make_writable(buffer); if (!writable_buf) { return GST_PAD_PROBE_OK; } // 添加Parent Buffer Meta,当前Buffer就是原始FHD图像 gst_buffer_add_parent_buffer_meta(writable_buf, writable_buf, NULL); GST_PAD_PROBE_INFO_DATA(info) = writable_buf; return GST_PAD_PROBE_OK; }
在AppSink回调中获取原始FHD图像:
// AppSink回调中的逻辑 GstBuffer* child_buffer = gst_buffer_ref(gst_sample_get_buffer(sample)); GstParentBufferMeta* parent_meta = gst_buffer_get_parent_buffer_meta(child_buffer); if (parent_meta) { GstBuffer* fhd_buffer = parent_meta->parent; // 这里可以直接使用fhd_buffer,无需手动unref(Meta会自动管理) // 处理图像逻辑... } gst_buffer_unref(child_buffer);
四、替代方案:强制tee管道同步
如果Meta传递的方式还是有问题,你可以回到最初的tee管道,通过配置元素参数强制两路同步:
- 给tee元素设置
async=false和sync=true,强制两路同步输出 - 给两个AppSink都设置
sync=true,确保它们按时钟同步处理 - 给两路的queue元素设置
max-size-buffers=1,避免Buffer堆积导致不同步
这样配置后,两路图像会以相同速率输出,基本可以保证同时到达,不需要复杂的配对逻辑。
五、额外注意事项
- 给videorate元素设置固定帧率,比如
caps="video/x-raw,framerate=30/1",确保管道所有元素按统一帧率处理 - 在AppSink中设置
drop=false和max-buffers=2,避免丢失Buffer - 自定义Meta的注册逻辑要保证线程安全(你当前用
g_once_init的实现是正确的)
内容来源于stack exchange
相关产品推荐
相关产品推荐

