📁
GStreamer中文教程
  • Intuduction
  • Basic Theory
    • Building an Application
    • Autoplugging
    • Threads
  • Tutorial
    • Basic tutorial1: Hello world!
    • Basic tutorial 2: GStreamer concepts
    • Basic tutorial 3: Dynamic pipelines
    • Basic tutorial 6: Media formats and Pad Capabilities
    • Basic tutorial 7: Multithreading and Pad Availability
    • Basic tutorial 8: Short-cutting the pipeline
    • Playback tutorial 1: Playbin usage
    • Playback tutorial 2: Subtitle management
    • Playback tutorial 3: Short-cutting the pipeline
    • Playback tutorial 7: Custom playbin sinks
    • Playback tutorial 4: Progressive streaming
    • Playback tutorial 8: Hardware-accelerated video decoding
  • Application Development
    • Build Pipeline
    • App
      • Appsink
      • Appsrc
    • uridecodebin
    • GstPadProbe
  • Qualcomm GStreamer Plugins
    • qtioverlay
  • DeepStream
    • nvdsosd
    • DeepStream学习拾遗
  • Useful Tricks
    • GStreamer源码剖析之——rtspsrc(1)
    • GStreamer源码剖析——uridecodebin(1)
  • Post Script
Powered by GitBook
On this page
  • GstPadProbe
  • Pipeline
  • Issue
  • Summary

Was this helpful?

  1. Application Development

GstPadProbe

Add probe callback for GstPad.

PreviousuridecodebinNextQualcomm GStreamer Plugins

Last updated 3 years ago

Was this helpful?

在章节讲到了应用程序和GStreamer pipeline进行数据方式的一种方式,并且在示例中,使用appsink完成了从pipeline中取图像数据绘制,并把绘制后的图像经由appsrc重新送回pipeline中,这是目前基于GStreamer框架开发的应用程序最简单的一种架构。但是需要注意的是这里的appsink和appsrc实际上是两条pipeline,使用起来非常麻烦。在这篇教程中我将展示如何使用类似于中一样的example pipeline来实现相同的目标。

GstPadProbe

GstElement实际是通过GstPad完成连接,这是一种非常轻量的原始链接点。数据在GstPad之间进行传递,

gst_pad_add_probe()

gulong
gst_pad_add_probe (GstPad * pad,
GstPadProbeType mask,
GstPadProbeCallback callback,
gpointer user_data,
GDestroyNotify destroy_data)

在pad状态发生改变的时候发出通知,为匹配掩码的每个状态调用提供的回调函数。

pad:添加probe的GstPad

mask:probe的掩码,详细参考

callback:回调函数指针

user_data:用户传递给回调的数据

destroy_data:

返回值是一个无符号整型id,用于标识probe,gst_pad_remove_probe()释放probe用.

GstPadProbeCallback

GstPadProbeReturn
(*GstPadProbeCallback) (GstPad * pad,
GstPadProbeInfo * info,
gpointer user_data)

pad的对应状态下调用的probe回调函数,可以修改info指向的数据。

GstPadProbeInfo

struct _GstPadProbeInfo
{
GstPadProbeType type;
gulong id;
gpointer data;
guint64 offset;
guint size;
​
/*< private >*/
union {
gpointer _gst_reserved[GST_PADDING];
struct {
GstFlowReturn flow_ret;
} abi;
} ABI;
};

data根据不同的probe type具有不同的类型,可以直接操作data指针,也可以通过GstPadProbeInfo提供的借口获取其下的数据。常用的有gst_pad_probe_info_get_buffer(),用于获取经过pad的GstBuffer。

Pipeline

Overview

开头说过,appsink和appsrc各为一条pipeline,为了程序的正常运行,需要用户自行维护两条pipeline的数据同步,这是一个令人头疼的问题,并且为了画图总共发生了两次内存拷贝,这在应用中将占用一部分CPU性能。在本教程中,我们通过在queue0的src pad中注册一个GST_PAD_PROBE_TYPE_BUFFER类型的probe回调,取出经过queue0的GstBuffer并将要绘制的内容直接加到该buffer的metadata中,使用qtioverlay完成了相关内容的绘制。

qtioverlay

Issue

tee的request-pad

GstPad *
gst_element_request_pad_simple (GstElement * element,
const gchar * name)

但是需要注意的是gst_element_request_pad_simple()是在GStreamer-1.20之后才引入的新特性,旧的版本应该使用gst_element_get_request_pad()来申请。

GstBuffer isn't writable

  • cb_queue0_probe()

(GstPadProbe:9069): GStreamer-CRITICAL **: 14:05:03.871: gst_buffer_add_meta: assertion 'gst_buffer_is_writable (buffer)' failed

1. 等待GstBuffer同步

If there are multiple references to a single buffer, writing while another thread may be reading results in data corruption.

假如传递的buffer存在多个引用,在一个线程读buffer的同时在另一个线程中执行写buffer操作会引起竞争。

这句话的核心在于pipeline的多个分支线程中维护的其实是同一个buffer的不同引用,这是建立在tee插件只做了浅拷贝而不是深拷贝的基础上的,官方对于tee的说明其实比较含糊,只提到Split data to multiple pads.用的是split而不是copy也不是reference,所以我也并不确定tee的底层机制。

假如基于tee只是增加引用计数的思路来考虑,这就意味着display branch和appsink branch使用的是同一个GstBuffer的不同引用,也就是当cb_queue0_probe()请求访问probe buffer的时候,qtivtransform有可能正在对这个buffer进行读写操作,这时候为了线程安全自然应该上锁,所以probe buffer不可写。

因此我的解决思路是给qtivtransform的src-pad也加一个probe,当GstBuffer到达src-pad时说明qtivtransform的操作已经完成,这时进行一个unlock通知cb_queue0_probe取buffer并进行相关操作即可。

// sync
if (info->type & GST_PAD_PROBE_TYPE_BUFFER && !vp->isExited) {
g_mutex_lock (&vp->m_syncMuxtex);
while (g_atomic_int_get (&vp->m_syncCount) <= 0)
g_cond_wait (&vp->m_syncCondition, &vp->m_syncMuxtex);
if (!g_atomic_int_dec_and_test (&vp->m_syncCount)) {
//LOG_INFO_MSG ("m_syncCount:%d/%d", vp->m_syncCount,
// vp->pipeline_id_);
}
g_mutex_unlock (&vp->m_syncMuxtex);
}
​
// osd the result
if (vp->m_getResultFunc) {
const std::shared_ptr<cv::Rect> result =
vp->m_getResultFunc (vp->m_getResultArgs);
if (result && vp->m_procDataFunc) {
vp->m_procDataFunc (buffer, result);
}
}

2. gst_buffer_make_writable()

查看GstBffer文档可以知道,我们还可以通过gst_buffer_make_writable()来拷贝一份buffer,使得buffer可写,而且假如原buffer已经可写,那么这个调用只是简单的返回,拷贝并不会发生,因此不会造成过多的性能损耗。

buffer操作完之后再使用gst_pad_push()将buffer传递给与srd pad连接的下一个插件的sink pad中即可。

buffer = gst_buffer_make_writable (buffer);
​
// osd the result
if (vp->m_getResultFunc) {
const std::shared_ptr<cv::Rect> result =
vp->m_getResultFunc (vp->m_getResultArgs);
if (result && vp->m_procDataFunc) {
vp->m_procDataFunc (buffer, result);
}
}
​
gst_pad_push (pad, buffer);

Summary

至此我相信读者已经具备了开发自己的pipeline的能力,作为一个嵌入式平台的开发者,性能永远是第一目标,因此在实际使用中pipeline的架构需要反复斟酌优化。事实上通过GStreamer-APP和GstPadProbe两个例子,应该已经具备了初步的优化意识,关于架构优化,欢迎读者按顺序阅读下面三个repo的README,它记录了我基于GStreamer框架下的一个yolov3物体识别算法视频应用的从诞生到完善的完整流程:

希望能给各位一些启发。

在教程的实例中,我们通过appsink将GstBuffer传递到用户空间然后使用OpenCV绘制了一个红色的矩形框和appsink字符串,并且通过appsrc的回调中绘制了一个绿色的矩形框和appsrc字符串,最后将绘制后的cv::Mat转为GstBuffer送回pipeline中并用waylandsink显示在屏幕上。

qtioverlay是高通平台上的一个Overlay插件,内部依赖metadata调用C2D库完成了在NV12图像上bounding box和一个简单的bbox text的绘制,为了支持动态修改overlay color,我为其添加了一个meta-color的property,有关修改和使用的详情请阅读。

假如只需要在NV12图像上画矩形框,库实现了相同的功能。

在中使用了gst_element_request_pad_simple()向tee请求生成的src-pad,并且使用gst_element_release_request_pad()释放请求的GstPad。

在这个关于tee的issue中提到:

GStreamer-APP
Basic tutorial 7: Multithreading and Pad Availability
GstPadProbeType
GStreamer-APP
Qualcomm-gst-plugin: qtioverlay
draw-yuv-rectangle
Basic tutorial 7: Multithreading and Pad Availability
Buffers not writable after tee
Ericsson-Yolov3-SNPE
Gst-AIDemo-Optimize
yolov3-thread-pool