You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

GStreamer 1.24.3中实现裁剪前后图像同步配对并通过单个AppSink输出的问题排查

GStreamer 1.24.3中实现裁剪前后图像同步配对并通过单个AppSink输出的问题排查

看起来你遇到的核心问题是自定义GstMeta在管道传递过程中丢失,导致没法把原始FHD图像附在缩放后的Child图像里,一起传递到单个AppSink。我来帮你一步步排查问题,同时给出可行的修复方案和替代思路。

一、自定义Meta的Transform函数逻辑错误

你当前的fhd_image_transform函数存在两个关键问题,这是Meta丢失的主要原因:

  1. 当目标Buffer不可写时,直接返回FALSE,导致Meta无法被传递到新的Buffer
  2. 当目标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堆积导致不同步

这样配置后,两路图像会以相同速率输出,基本可以保证同时到达,不需要复杂的配对逻辑。

五、额外注意事项

  1. 给videorate元素设置固定帧率,比如caps="video/x-raw,framerate=30/1",确保管道所有元素按统一帧率处理
  2. 在AppSink中设置drop=false和max-buffers=2,避免丢失Buffer
  3. 自定义Meta的注册逻辑要保证线程安全(你当前用g_once_init的实现是正确的)

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.07 07:08:04