gstreamer拉流rtsp使用appsink获取帧数据(预览+截图)

系列文章


目录

系列文章

管道结构

appsink和probe的差异

probe

appsink

结语

附: linux qt 工程链接


管道结构

完整的QT工程代码在文章末尾

话不多说,先上管道图

该管道是使用tee的方式将拉来的rstp流分为两路,一路为显示预览,一路为appsink保存图片。

gstreamer的rtsp拉流部分和tee分为两路的整体管道创建和链接比较简单就不重点介绍了。本文主要讲appsink获取帧数的方式。

前面有说到使用数据探针(probe)的方式可以直接在videosink增加探针来的到帧数据。可能有人会问probe只需要一路就能实现为何还要用appsink。下面就讲appsink和probe有何不同。

appsink和probe的差异

probe

当管道只有一路显示输出时管道中的视频流数据会被显示的videosink强制格式。比如上面管道中使用的ximagesink的格式为BGRx。即使在管道中使用videoconvert强制caps输出为其他格式也是无效的。而gstreamer常在qt中使用,而qt中的Qimage类目前不能识别BGRx格式。所以往往使用probe时可能无法得到想要的图像格式。

appsink

  相比较而言,appsink属性中的有caps选项因而可以自由设置输出格式。

#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一路来编码保存视频就可以实现录像。就可以轻松实现完整的照相机(预览、截图、录像)功能。

附: linux qt 工程链接

gstreamer拉流rtsp使用appsink获取帧数据(预览+截图)-图像处理文档类资源-CSDN下载

Guess you like

Origin blog.csdn.net/qq_41563600/article/details/121257849