Gstreamer中获取帧数据的方式
gstreamer中如何使用probe(探针)获取帧数据
gstreamer拉流rtsp使用appsink获取帧数据(预览+截图)
gstreamer中如何使用fakesink获取帧数据(预览+截图)
gstreamer中tee如何实现动态连接(预览+截图+录像)
目录
系列文章
管道结构
appsink和probe的差异
probe
appsink
结语
附: linux qt 工程链接
完整的QT工程代码在文章末尾
话不多说,先上管道图
该管道是使用tee的方式将拉来的rstp流分为两路,一路为显示预览,一路为appsink保存图片。
gstreamer的rtsp拉流部分和tee分为两路的整体管道创建和链接比较简单就不重点介绍了。本文主要讲appsink获取帧数的方式。
前面有说到使用数据探针(probe)的方式可以直接在videosink增加探针来的到帧数据。可能有人会问probe只需要一路就能实现为何还要用appsink。下面就讲appsink和probe有何不同。
当管道只有一路显示输出时管道中的视频流数据会被显示的videosink强制格式,这个前面文章也有讲到。比如上面管道中使用的ximagesink的格式为BGRx。即使在管道中使用videoconvert强制caps输出为其他格式也是无效的。而gstreamer常在qt中使用,而qt中的Qimage类目前不能识别BGRx格式,就需要自己去实现图像格式的转换。所以使用probe时得到的图像格式是先关的element决定的,想要得到想要的格式需要自己实现格式转换。
相比较而言,appsink属性中的有caps选项因而可以自由设置输出格式。往往只需要在appsink前加一个videoconvert就能在管道内得到gstreamer支持的任意格式。
#define CAPS "video/x-raw,format=RGB,pixel-aspect-ratio=1/1" //设置appsink输出的视频格式
/* Configure appsink */
GstElement *appsink = gst_element_factory_make ("appsink", "sink");
GstCaps *video_caps;
gchar *video_caps_text;
video_caps_text = g_strdup_printf (CAPS);
video_caps = gst_caps_from_string (video_caps_text);
if(!video_caps){
g_printerr("gst_caps_from_string fail\n");
return -1;
}
g_object_set (appsink, "caps", video_caps, NULL);
appsink的Element Signals主要是 "new-preroll"和"new-sample" 两种方式。两种信号的详细区别可见官网介绍gstreamer官网appsink详细介绍。这里用new-sample举例,要使用信号触发事件首先需要设置emit-signals属性为TRUE,然后再链接信号和事件。
g_object_set (data.appsink, "emit-signals", TRUE, NULL);
g_signal_connect (data.appsink, "new-sample", G_CALLBACK (new_sample), gpointer user_data);
为了得到帧数据可以在回填函数里操作
static GstFlowReturn new_sample (GstElement *sink, gpointer user_data){
GstSample *sample;
GstBuffer *buffer;
GstCaps *caps;
GstStructure *s;
gint width, height; //图片的尺寸
/* Retrieve the buffer */
g_signal_emit_by_name (sink, "pull-sample", &sample);
if (sample){
//g_print ("*");
caps = gst_sample_get_caps (sample);
if (!caps) {
g_print ("gst_sample_get_caps fail\n");
gst_sample_unref (sample);
return GST_FLOW_ERROR;
}
s = gst_caps_get_structure (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");
gst_sample_unref (sample);
return GST_FLOW_ERROR;
}
//获取视频的一帧buffer,注意,这个buffer是无法直接用的,它不是char类型
buffer = gst_sample_get_buffer (sample);
if(!buffer){
g_print ("gst_sample_get_buffer fail\n");
gst_sample_unref (sample);
return GST_FLOW_ERROR;
}
GstMapInfo map;
//把buffer映射到map,这样我们就可以通过map.data取到buffer的数据
if (gst_buffer_map (buffer, &map, GST_MAP_READ)){
g_print("jpg size = %ld \n", map.size);
QImage img(map.data, width, height, width * 3, QImage::Format_RGB888);
QString strFile = QString("Capture-%0.jpg").arg(QDateTime::currentDateTime().toString("yyyyMMddhhmmssz"));
img.save(strFile, "jpg");
g_print("save %s succese\n",strFile.toStdString().c_str());
gst_buffer_unmap (buffer, &map); //解除映射
}
gst_sample_unref (sample); //释放资源
GST_DEBUG_BIN_TO_DOT_FILE(GST_BIN(data->pipeline), GST_DEBUG_GRAPH_SHOW_ALL, "capture"); //打印此时刻的管道状态图
return GST_FLOW_OK;
}
return GST_FLOW_OK ;
}
通过gstreamer相关的数据结构获得rgb图像后丢给QT中的Qimage来处理保存为想要的格式,就不用自己去找相关的图像保存接口。不要把大量时间花在重复造轮子上。
既然已经实现预览和截图的功能,那么在此基础上tee一路来编码保存视频就可以实现录像。就可以轻松实现完整的照相机(预览、截图、录像)功能。
gstreamer拉流rtsp使用appsink获取帧数据(预览+截图)-图像处理文档类资源-CSDN下载