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

如何在C++中获取GStreamer rtspsrc当前帧的URI或元素名称?

在GStreamer中为多RTSP源帧标记并在自研插件中获取源信息

完全可以实现这个需求,下面是两种实用的C++方案,适配不同的管线结构:


方案一:给Buffer添加自定义元数据(推荐,适配复杂管线)

这种方法是在每个rtspsrc的输出帧上打上包含源信息的"标签",不管中间经过多少元素(比如tee、queue、解码器),自研插件都能从Buffer里读出源信息,灵活性最高。

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

首先在代码中定义存储源信息的Meta结构,并注册到GStreamer的元数据系统:

#include <gst/gst.h>

// 自定义元数据结构,存储rtspsrc的名称和location
typedef struct _GstRtspSrcMeta {
  GstMeta meta;
  gchar* src_name;
  gchar* location;
} GstRtspSrcMeta;

// 注册元数据的API宏
GType gst_rtsp_src_meta_api_get_type(void);
#define GST_RTSP_SRC_META_API_TYPE (gst_rtsp_src_meta_api_get_type())
#define gst_buffer_get_rtsp_src_meta(b) ((GstRtspSrcMeta*)gst_buffer_get_meta((b), GST_RTSP_SRC_META_API_TYPE))

// 注册元数据的实现
static const GstMetaInfo* gst_rtsp_src_meta_get_info(void);
#define GST_RTSP_SRC_META_INFO (gst_rtsp_src_meta_get_info())

GType gst_rtsp_src_meta_api_get_type(void) {
  static GType type;
  static const gchar* tags[] = { "rtspsrc", "source-info", NULL };
  if (!type) {
    type = gst_meta_api_type_register("GstRtspSrcMetaAPI", tags);
  }
  return type;
}

static gboolean gst_rtsp_src_meta_init(GstMeta* meta, gpointer params, GstBuffer* buffer) {
  GstRtspSrcMeta* src_meta = (GstRtspSrcMeta*)meta;
  src_meta->src_name = NULL;
  src_meta->location = NULL;
  return TRUE;
}

static void gst_rtsp_src_meta_free(GstMeta* meta, GstBuffer* buffer) {
  GstRtspSrcMeta* src_meta = (GstRtspSrcMeta*)meta;
  g_free(src_meta->src_name);
  g_free(src_meta->location);
}

static const GstMetaInfo* gst_rtsp_src_meta_get_info(void) {
  static const GstMetaInfo* meta_info;
  if (!meta_info) {
    meta_info = gst_meta_info_register(
      GST_RTSP_SRC_META_API_TYPE,
      "GstRtspSrcMeta",
      sizeof(GstRtspSrcMeta),
      gst_rtsp_src_meta_init,
      gst_rtsp_src_meta_free,
      NULL // 复制函数,按需实现
    );
  }
  return meta_info;
}

// 给Buffer添加RTSP源元数据的工具函数
GstRtspSrcMeta* gst_buffer_add_rtsp_src_meta(GstBuffer* buffer, const gchar* src_name, const gchar* location) {
  GstRtspSrcMeta* meta = (GstRtspSrcMeta*)gst_buffer_add_meta(buffer, GST_RTSP_SRC_META_INFO, NULL);
  if (meta) {
    meta->src_name = g_strdup(src_name);
    meta->location = g_strdup(location);
  }
  return meta;
}

步骤2:为每个rtspsrc的src pad添加Probe,注入元数据

在构建管线时,给每个rtspsrc的输出pad挂一个probe,当帧通过时,把当前rtspsrc的name和location写入Buffer的自定义元数据:

// 给指定rtspsrc添加probe
void attach_rtsp_src_probe(GstElement* rtsp_src) {
  GstPad* src_pad = gst_element_get_static_pad(rtsp_src, "src");
  if (!src_pad) return;

  // 获取rtspsrc的name和location属性
  const gchar* src_name = gst_element_get_name(rtsp_src);
  gchar* location = NULL;
  g_object_get(G_OBJECT(rtsp_src), "location", &location, NULL);

  // probe回调函数
  GstPadProbeReturn pad_probe_cb(GstPad* pad, GstPadProbeInfo* info, gpointer user_data) {
    if (GST_PAD_PROBE_TYPE_BUFFER & info->type) {
      GstBuffer* buffer = GST_BUFFER(info->data);
      // 注入元数据
      gst_buffer_add_rtsp_src_meta(buffer, (const gchar*)user_data, location);
    }
    return GST_PAD_PROBE_OK;
  }

  // 挂载probe,传递src_name作为用户数据
  gst_pad_add_probe(src_pad, GST_PAD_PROBE_TYPE_BUFFER, pad_probe_cb, (gpointer)src_name, NULL);

  g_object_unref(src_pad);
  g_free(location);
}

// 调用示例:假设已创建source_2、source_3、source_4三个rtspsrc元素
attach_rtsp_src_probe(source_2);
attach_rtsp_src_probe(source_3);
attach_rtsp_src_probe(source_4);

步骤3:在自研插件中提取元数据,实现分支逻辑

在自研插件的chain或process函数中,从Buffer里取出自定义元数据,拿到源信息后做分支:

// 自研插件的chain函数示例
static GstFlowReturn gst_my_plugin_chain(GstPad* pad, GstObject* parent, GstBuffer* buffer) {
  // 提取自定义元数据
  GstRtspSrcMeta* src_meta = gst_buffer_get_rtsp_src_meta(buffer);
  if (src_meta) {
    g_print("当前帧来自RTSP源:%s,Location:%s\n", src_meta->src_name, src_meta->location);

    // 分支逻辑实现
    if (g_str_equal(src_meta->src_name, "source_2")) {
      // 处理source_2的帧
    } else if (g_str_equal(src_meta->src_name, "source_3")) {
      // 处理source_3的帧
    }
  }

  // 继续传递Buffer
  return gst_pad_push(pad, buffer);
}

方案二:通过Pad追溯上游rtspsrc(适合简单管线)

如果你的管线结构简单,每个rtspsrc的链路独立(无tee等分支元素),可以直接从插件的sink pad向上追溯,找到对应的rtspsrc元素,读取它的属性。

在自研插件中实现追溯逻辑

static GstFlowReturn gst_my_plugin_chain(GstPad* pad, GstObject* parent, GstBuffer* buffer) {
  // 获取插件sink pad的上游pad
  GstPad* upstream_pad = gst_pad_get_peer(pad);
  if (!upstream_pad) goto done;

  // 向上追溯到元素
  GstElement* upstream_elem = gst_pad_get_parent_element(upstream_pad);
  if (!upstream_elem) goto cleanup_pad;

  // 递归查找rtspsrc类型的元素
  GstElement* rtsp_src = NULL;
  while (upstream_elem) {
    if (g_type_is_a(G_OBJECT_TYPE(upstream_elem), GST_TYPE_RTSP_SRC)) {
      rtsp_src = upstream_elem;
      break;
    }
    // 获取当前元素的sink pad,继续向上追溯
    GstPad* elem_sink_pad = gst_element_get_static_pad(upstream_elem, "sink");
    if (!elem_sink_pad) break;
    GstPad* next_upstream = gst_pad_get_peer(elem_sink_pad);
    g_object_unref(elem_sink_pad);
    if (!next_upstream) break;
    GstElement* next_elem = gst_pad_get_parent_element(next_upstream);
    g_object_unref(next_upstream);
    g_object_unref(upstream_elem);
    upstream_elem = next_elem;
  }

  if (rtsp_src) {
    const gchar* src_name = gst_element_get_name(rtsp_src);
    gchar* location = NULL;
    g_object_get(G_OBJECT(rtsp_src), "location", &location, NULL);
    g_print("当前帧来自RTSP源:%s,Location:%s\n", src_name, location);

    // 分支逻辑实现
    if (g_str_equal(src_name, "source_4")) {
      // 处理source_4的帧
    }

    g_free(location);
    g_object_unref(rtsp_src);
  }

cleanup_pad:
  g_object_unref(upstream_pad);
done:
  return gst_pad_push(pad, buffer);
}

注意事项

  • 方案一的自定义元数据默认会随Buffer传递,除非有元素明确移除,适合复杂管线。
  • 方案二的追溯逻辑依赖管线结构,如果中间有tee等分支元素,可能无法定位到正确的rtspsrc,此时优先选方案一。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 03:00:01