系列文章
Gstreamer中获取帧数据的方式
gstreamer中如何使用probe(探针)获取帧数据
gstreamer拉流rtsp使用appsink获取帧数据(预览+截图)
gstreamer中如何使用fakesink获取帧数据(预览+截图)
gstreamer中tee如何实现动态连接(预览+截图+录像)
功能
gstreamer应用程序中提供一种能够监视和控制焊盘上的方式。分为以下几种类型:
- be notified when the pad is/becomes idle and make sure the pad stays idle. This is essential to be able to implement dynamic relinking of elements without breaking the dataflow.
- be notified when data, events or queries are pushed or sent on a pad. It should also be possible to inspect and modify the data.
- be able to drop, pass and block on data based on the result of the callback.
- be able to drop, pass data on blocking pads based on methods performed by the application thread.
概述
gst_pad_add_probe()用于将探针添加到pad。
gulong gst_pad_add_probe (GstPad *pad,
GstPadProbeType mask,
GstPadProbeCallback callback,
gpointer user_data,
GDestroyNotify destroy_data);
mask表示探针类型
typedef enum
{
GST_PAD_PROBE_TYPE_INVALID = 0,
/* flags to control blocking */
GST_PAD_PROBE_TYPE_IDLE = (1 << 0),
GST_PAD_PROBE_TYPE_BLOCK = (1 << 1),
/* flags to select datatypes */
GST_PAD_PROBE_TYPE_BUFFER = (1 << 4),
GST_PAD_PROBE_TYPE_BUFFER_LIST = (1 << 5),
GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM = (1 << 6),
GST_PAD_PROBE_TYPE_EVENT_UPSTREAM = (1 << 7),
GST_PAD_PROBE_TYPE_EVENT_FLUSH = (1 << 8),
GST_PAD_PROBE_TYPE_QUERY_DOWNSTREAM = (1 << 9),
GST_PAD_PROBE_TYPE_QUERY_UPSTREAM = (1 << 10),
/* flags to select scheduling mode */
GST_PAD_PROBE_TYPE_PUSH = (1 << 12),
GST_PAD_PROBE_TYPE_PULL = (1 << 13),
} GstPadProbeType;
当添加带有IDLE或BLOCK标志的探针时,该探针将成为阻塞探针。否则,探针将是DATA探针。
数据类型和调度选择器标志用于选择回调中应允许的数据类型和调度模式。
阻止标志必须与触发的探针完全匹配。
回调函数定义为:
GstPadProbeReturn (*GstPadProbeCallback) (GstPad *pad,
GstPadProbeInfo *info,
gpointer user_data);
数据探针
定义
本文主要介绍如何使用探针获取帧数据,下面开始介绍数据探针。
在整个playing状态的管道中,pad上有数据通过时,数据探针会通知你。通过 GST_PAD_PROBE_TYPE_BUFFER和/或GST_PAD_PROBE_TYPE_BUFFER_LIST以 gst_pad_add_probe ()创建这种探头。下面给出在qt应用中的例子。因为qt里的Qimage可以直接操作rgb数据,如果使用gstreamer管道过程中源数据不是rgb格式,可以使用gstreamer中的videoconvert实现转码功能。命令行形式如下
`
gst-launch-1.0 videotestsrc ! videoconvert ! video/x-raw,format=RGB ! fakevideosink
gst-launch-1.0 videotestsrc ! videoconvert ! video/x-raw,format=BGRx ! ximagesink
将probe位置设置在sink的pad 上。
代码如下`
GstElement *sink = gst_element_factory_make ("ximagesink", "videosink");
//date probe
GstPad *pad = gst_element_get_static_pad (sink, "sink");
gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_BUFFER,
(GstPadProbeCallback) cb_have_data, NULL, NULL);
gst_object_unref (pad);
RGB类型图像格式转换
注意:linux下的ximagesink显示会强制成BGRx的格式,xvimagesink则是UYVY格式。在这两个地方插入probe需要注意格式转换。这些都不是常用的BGR或者RGB形式。那么只有自己动手,丰衣足食了。
弄清楚RGB各种格式在内存中的排序方式,自己写一个转换就行。比如BGRx转BGR或者RGB只需要将其中的x分量去掉或者改变R和G的位置就行。代码如下 :
void cvtColorGBRx2GBR(guint8 *dst, guint8 *src, int width, int height)
{
for (int h = 0; h < height; h++)
{
for (int w = 0, w1 = 0; w < width*3; w += 3, w1 += 4)
{
dst[w] = src[w1];
dst[w + 1] = src[w1+1];
dst[w + 2] = src[w1+2];
}
dst += width*3;
src += width*4;
}
return;
}
void cvtColorGBRx2RGB(guint8 *dst, guint8 *src, int width, int height)
{
for (int h = 0; h < height; h++)
{
for (int w = 0, w1 = 0; w < width*3; w += 3, w1 += 4)
{
dst[w] = src[w1+2];
dst[w + 1] = src[w1+1];
dst[w + 2] = src[w1];
}
dst += width*3;
src += width*4;
}
return;
}
回调函数
static GstPadProbeReturn
cb_have_data (GstPad *pad,
GstPadProbeInfo *info,
gpointer user_data){
GstBuffer *buffer = nullptr ;
GstMapInfo map_info;
GstStructure *s;
gint width, height; //图片的尺寸
GstCaps *sink_caps =gst_pad_get_current_caps(pad);
s = gst_caps_get_structure (sink_caps, 0);
gboolean res;
res = gst_structure_get_int (s, "width", &width); //获取图片的宽
res |= gst_structure_get_int (s, "height", &height); //获取图片的高
if (!res) {
g_print ("gst_structure_get_int fail\n");
return GST_PAD_PROBE_DROP;
}
// 使用mapinfo获取图像数据
buffer = GST_PAD_PROBE_INFO_BUFFER (info); //info和buffer中的数据不能直接操作
if (!gst_buffer_map(buffer, &map_info, GST_MAP_READ)) {
//映射出数据
g_print("gst_buffer_map() error!");
return GST_PAD_PROBE_DROP;
}
g_print("jpg size = %ld \n", map_info.size);
QString strFile = QString("Capture-%0.jpg").arg(QDateTime::currentDateTime().toString("yyyyMMddhhmmssz"));
unsigned char rgb [width*height*3]={
0} ;
cvtColorGBRx2GBR(rgb, map_info.data, width, height);
// QImage保存图片
#if 1
QImage img(rgb, width, height, width * 3, QImage::Format_RGB888); //直接操作映射出的数据map_info
img.save(strFile, "jpeg");
#endif
//也可以使用GDK的方式保存图片
#if 0
GError *error = NULL;
GdkPixbuf * pixbuf = gdk_pixbuf_new_from_data (rgb, GDK_COLORSPACE_RGB, FALSE, 8, width, height,GST_ROUND_UP_4 (width * 3), NULL, NULL); //将数据保存到GdkPixbuf
/* save the pixbuf */
gdk_pixbuf_save (pixbuf, strFile.toStdString().c_str(), "jpeg", &error, NULL); //把GdkPixbuf保存成jpeg格式
g_object_unref(pixbuf); //释放掉GdkPixbuf
#endif
g_print("save %s succese\n",strFile.toStdString().c_str());
gst_buffer_unmap (buffer, &map_info);
return GST_PAD_PROBE_OK;
}
根据官方应用手册描述probe适合检测数据或者在当前probe插入的位置修改数据时使用。上面演示只是从里面拿取数据。
也可以将map出来的数据做修改后还回管道中。修改后还回数据只需要解除GstBuffe映射后将GstBuffer的内容给GstPadProbeInfo就行。代码如下:
GST_PAD_PROBE_INFO_DATA (info) = buffer;
总结
probe只适合内部获取数据并处理的场景使用。如需要导出大量帧数据在外部应用使用时怎么办?gstreamer则是给出了appsink用来处理。后面会介绍appsink使用方法。