如何在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
相关产品推荐
相关产品推荐

