硬件平台:研华mic-710ailx 有基于Jeston的gpu硬件
阅读本博客需要有对gstreamer基础知识的了解,这里给出学习链接:
GStreamer系列 - 基本介绍 - John.Leng - 博客园
关于这个硬件设备下使用ffmpeg进行硬解码,我研究了很久都没有办法支持,而使用英伟达的硬解码API又过于底层抽象,难以理解还涉及到cuda编程。关于ffmpeg使用该类型的gpu进行rtsp H264硬解码的问题,有懂的老哥dd我~
1.可行性验证
1.1屏幕输出
使用gst-lauch-1.0验证rtsp硬解码管道连接和输出帧是否正常:
gst-launch-1.0 rtspsrc location=你的rtsp地址 ! queue ! rtph264depay ! h264parse ! queue ! omxh264dec ! autovideosink
可以看到这个pipeline由7个element元件构成,每个element都实现各自的功能:
- rtspsrc 读取rtsp流
- queue 缓存数据
- rtph264depay 从rtspsrc接收到rtp包
- h264parse 分割输出H264帧数据
- queue 缓存数据
- omxh264dec 硬解码H264帧数据
- autovideosink 获取帧数据并自动找到屏幕进行输出
注:如果没有显示器屏幕可能会报错 ,可以将autovideosink换成appsink观察命令是否正常执行不会异常退出
使用该命令会将获取到的rtsp流视频在屏幕上显示出来,验证其可使用omxh264dec进行硬解码H264帧数据
1.2后端获取
为了能在程序中获取帧数据以及尽可能将解封装和解码使用gpu来完成,可指定以下命令:
gst-launch-1.0 rtspsrc location=你的rtsp地址 ! rtph264depay ! h264parse ! omxh264dec ! nvvidconv ! video/x-raw,width=1280,height=720,format=BGRx,latency=200 ! videoconvert ! appsink
可以看到这个pipeline由7个element元件构成,每个element都实现各自的功能:
- rtspsrc 读取rtsp流
- rtph264depay 从rtspsrc接收到rtp包
- h264parse 分割输出H264帧数据
- omxh264dec 硬解码H264帧数据
- nvvidconv 硬解码drm_prime帧数据成BGRx
- videoconvert 转换video数据格式
- appsink 获取BGRx数据
其中:
“location=你的rtsp地址”紧跟着rtspsrc意为给rtspsrc元件成员location赋值,用于指定rtsp流url。
“video/x-raw,width=1280,height=720,format=BGRx,latency=200”意为通过Cap来指定输出的数据类型
若命令没有异常退出,可视作各元件可正常使用并能获取到rtsp数据
2.代码实现
gstreamer初始化,监听信号和设置响应回调函数略,可以参考我之前的gstreamer读取解析mipi csi摄像头的博客:
Gstreamer使用mipi csi摄像头,转码成BGR格式的cv::Mat_幡神的博客-CSDN博客
以下给出伪代码:
元件都放入管道后进行rtspsrc之后的连接:
/*
* 初始化,监听信号和设置回调略
*/
//在进行各个元件连接时注意:
//连接元素,注意顺序不能错,rtspsrc没法在这里和rtph264depay连接,因为还没有确定数据的格式
//所以将rtsp之后的元件连接起来,检查判断返回值是否成功
gst_element_link_many (rtph264depay,h264parse,omxh264dec,nvvidconv,videoconvert,appsink, NULL)
// 因为rtsp和rtph264depay还没有连接,所以必须设置pad-added信号监听,在管道开始工作后,确定了数据格式,再把它们连接起来
// 注意必须使用静态函数来实现回调:static void pad_added_handler(GstElement *src, GstPad *new_pad, GstreamerRTSPData *data);
// 回调里使用&gstData进行与外部通讯
g_signal_connect(rtsp, "pad-added", G_CALLBACK (pad_added_handler), &gstData);
接收到pad-added信号进行rtsp和rtph264depay连接的回调函数:
// 这个是pad-added信号的回调函数
void pad_added_handler(GstElement *src, GstPad *new_pad, GstreamerRTSPData *data){
//获取到gstData->rtph264depay的sink pad
GstPad *sink_pad = gst_element_get_static_pad (gstData->rtph264depay, "sink");
//使用gst_pad_is_linked(sink_pad)来检查是否已经连接好了,若是则忽略该信号,这里略
// 用于获取rtspsrc pad信息的变量
// new_pad在这里指就是rtspsrc的source pad
GstCaps *new_pad_caps;
GstStructure *new_pad_struct;
const gchar *new_pad_type;
new_pad_caps = gst_pad_get_current_caps (new_pad);
new_pad_struct = gst_caps_get_structure (new_pad_caps, 0);
new_pad_type = gst_structure_get_name (new_pad_struct);
//因为rtph264depay要求application/x-rtp格式的输入数据,所以找rtspsrc的pad里面有没有这种格式的数据,判断返回略
g_str_has_prefix (new_pad_type, "application/x-rtp");
//如果找到了application/x-rtp格式的输入数据,则将rtspsrc的source pad和rtph264depay的sink pad连起来
//GstPadLinkReturn ret;通过ret来检查结果,这里略
gst_pad_link (new_pad, sink_pad);
//释放资源略
}
接收到new-sample信号进行获取和转码图像数据的回调函数:
/*
* 从sink中获取sample、获取宽高 以及
* 从sample获取buffer和把buffer映射到map流程与处理mipi csi摄像头的流程一致
* 这里略
*/
GstFlowReturn RTSPGstreamerReader::get_sample(GstElement *sink,
GstreamerRTSPData *gstData) {
//前略,已从buffer映射数据到了map,可以通过map.data取到buffer的数据
/*
* 与mipi csi摄像头处理不同的是,此时map.data中的数据格式是BGRx的
* BGRx通道为4,与BGR对比只是多了一位x数据用来存储其他信息
* 舍弃掉x位那一位的数据将其保存至BGR中即可
* 舍弃掉x位后经测试没有发现图像质量有下降
*/
//创建一个三通道的cv::Mat用于保存结果
gstData->outData.bgrImg.create(gstData->outData.height, gstData->outData.width, CV_8UC3);
//将BGRx转成BGR
cvtColorBGRx2BGR(gstData->outData.bgrImg.data,gstData->outData.map.data,gstData->outData.width,gstData->outData.height);
//这样,解析完成的BGR格式的cv::Mat就保存在gstData->outData.bgrImg中
//资源释放、抽帧和返回这里略
}
BGRx转BGR:
//获取到的数据是BGRx,比需要的BGR多一个通道,需要进行转换
void RTSPGstreamerReader::cvtColorBGRx2BGR(guint8 *BGR, const guint8 *BGRx, int width, int height)
{
for (int h = 0; h < height; h++)
{
for (int w = 0, w1 = 0; w < width*3; w += 3, w1 += 4)
{
BGR[w] = BGRx[w1];
BGR[w + 1] = BGRx[w1+1];
BGR[w + 2] = BGRx[w1+2];
}
BGR += width*3;
BGRx += width*4;
}
return;
}
目录
硬件平台:研华mic-710ailx 有基于Jeston的gpu硬件
接收到pad-added信号进行rtsp和rtph264depay连接的回调函数:
接收到new-sample信号进行获取和转码图像数据的回调函数: