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

基于摄像头标识符拆分GStreamer多摄像头流水线的实现方法

嘿,这个需求我之前帮人折腾过类似的,给你几个实用的方案,都是基于GStreamer的元数据机制来实现拆分的,正好匹配你说的给图像加ID标签的思路:

方案1:用GStreamer内置的Caps Metadata加ID

这是最快捷的方式,因为你已经在用appsrc推流,直接在每帧的caps里加个自定义字段就行,不用写太多额外代码。

步骤1:推流时给每帧加Camera ID

结合你的Basler Pylon API代码,在把图像推到appsrc之前,给buffer的caps添加camera-id字段:

// 从Pylon获取到一帧有效图像后
Pylon::CGrabResultPtr ptrGrabResult = camera.RetrieveResult(5000, Pylon::TimeoutHandling_ThrowException);
if (ptrGrabResult->GrabSucceeded()) {
    // 创建GStreamer Buffer并拷贝图像数据
    GstBuffer *buffer = gst_buffer_new_allocate(NULL, ptrGrabResult->GetImageSize(), NULL);
    GstMapInfo map;
    gst_buffer_map(buffer, &map, GST_MAP_WRITE);
    memcpy(map.data, ptrGrabResult->GetBuffer(), ptrGrabResult->GetImageSize());
    gst_buffer_unmap(buffer, &map);

    // 定义当前帧的caps,记得匹配你的实际图像格式(比如分辨率、格式)
    GstCaps *caps = gst_caps_from_string("video/x-raw,format=BGRx,width=1920,height=1080,framerate=30/1");
    // 给caps添加camera-id标签,比如当前摄像头是ID 0
    gst_caps_set_simple(caps, "camera-id", G_TYPE_INT, camera_id, NULL);

    // 将caps绑定到buffer上
    gst_buffer_set_caps(buffer, caps);
    gst_caps_unref(caps);

    // 推送到appsrc
    g_signal_emit_by_name(appsrc, "push-buffer", buffer, &ret);
    gst_buffer_unref(buffer);
}

步骤2:用Caps Filter拆分流

推流后,用tee分流,每个分支加capsfilter来过滤对应ID的帧就行。比如命令行示例:

gst-launch-1.0 appsrc name=src ! videoconvert ! tee name=t \
t. ! queue ! capsfilter caps="video/x-raw,format=BGRx,camera-id=0" ! autovideosink \
t. ! queue ! capsfilter caps="video/x-raw,format=BGRx,camera-id=1" ! autovideosink

如果是C++代码构建流水线,直接给每个分支的capsfilter设置包含对应camera-id的caps即可。

方案2:自定义元数据(更灵活)

如果你后续还想给帧加更多信息(比如摄像头参数、精准时间戳),自定义元数据会更合适。

步骤1:注册自定义元数据类型

先定义一个元数据结构,注册到GStreamer:

// 自定义元数据结构
typedef struct {
    gint camera_id;
    gint64 capture_timestamp;
} CameraMeta;

// 元数据的拷贝和释放函数
CameraMeta* camera_meta_copy(CameraMeta *meta) {
    CameraMeta *copy = g_new(CameraMeta, 1);
    *copy = *meta;
    return copy;
}

void camera_meta_free(CameraMeta *meta) {
    g_free(meta);
}

// 注册元数据类型
GType camera_meta_get_type(void) {
    static GType type = 0;
    if (!type) {
        type = g_boxed_type_register_static("CameraMeta",
            (GBoxedCopyFunc)camera_meta_copy,
            (GBoxedFreeFunc)camera_meta_free);
    }
    return type;
}

步骤2:推流时添加自定义元数据

在appsrc推流前,把元数据加到buffer上:

CameraMeta *meta = g_new(CameraMeta, 1);
meta->camera_id = camera_id;
meta->capture_timestamp = ptrGrabResult->GetTimeStamp();
// 将元数据添加到buffer
gst_buffer_add_meta(buffer, 
    gst_meta_register_custom(camera_meta_get_type(), "CameraMeta",
    (GstMetaFreeFunc)camera_meta_free, NULL), 
    meta);

步骤3:用Pad Probe拆分流

这种方式需要给每个分支的pad加一个探针,过滤掉不符合ID的帧:

// 探针回调函数,过滤指定ID的帧
GstPadProbeReturn camera_filter_probe(GstPad *pad, GstPadProbeInfo *info, gpointer user_data) {
    gint target_id = GPOINTER_TO_INT(user_data);
    if (info->type & GST_PAD_PROBE_TYPE_BUFFER) {
        GstBuffer *buffer = GST_BUFFER(info->data);
        // 获取自定义元数据
        CameraMeta *meta = (CameraMeta*)gst_buffer_get_meta(buffer, camera_meta_get_type());
        if (meta && meta->camera_id != target_id) {
            // 丢弃不符合ID的帧
            return GST_PAD_PROBE_RETURN_DROP;
        }
    }
    return GST_PAD_PROBE_OK;
}

// 给某个分支的sink pad添加探针(比如ID 0的分支)
GstPad *pad = gst_element_get_static_pad(queue_0, "sink");
gst_pad_add_probe(pad, GST_PAD_PROBE_TYPE_BUFFER, camera_filter_probe, GINT_TO_POINTER(0), NULL);
gst_object_unref(pad);
几个关键注意事项
  • 用Caps Metadata的时候,一定要确保caps里的其他参数(比如format、分辨率)和实际帧完全匹配,不然capsfilter会过滤掉所有帧。
  • 自定义元数据要保证所有需要访问它的元件都能拿到元数据类型的定义,不然会获取不到。
  • 分流时每个分支都要加queue,避免不同摄像头帧率不一致导致流水线阻塞。

这样你就能在神经网络元件开发完成前,先把交织流拆分成独立的分支,后续NN处理完也可以复用这些元数据来对应输出到正确的分支~

内容的提问来源于stack exchange,提问作者cbalos

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 10:03:51