Gstreamer 硬解码Rtsp流及代码实现
背景
业务需要自己做解码,因为软解码CPU占有率太高,需要硬件加速,也就是硬解码。可以使用ffmpeg或者Gstreamer进行解码,我们选择用Gstreamer做解码。
系统环境:Ubuntu 20.04
代码功能:实现rtsp流的H264硬解码,获取解码后的数据;
一、Gstreamer介绍
Gstreamer是一个用于开发流媒体应用的开源框架,采用了基于插件(plugin)和管道(pipeline)的体系结构,框架中的所有的功能模块都被实现成可以插拔的元素(Element),并且能够很方便地安装到任意一个管道上。由于所有插件都通过管道机制进行统一的数据交换,因此很容易利用已有的各种插件“组装”出一个功能完善的流媒体应用程序。
二、Gstreamer基础概念
2.1 Element
从 GStreamer 自身的观点来看,GstElement 可以描述为一个具有特定属性的黑盒子,它通过连接点(link point)与外界进行交互,向框架中的其余部分表征自己的特性或者功能。
一个Element实现了一个功能(如读取文件,解码,输出等),一个程序需要创建多个element,并按顺序将其串连起来,构成一个完整的pipeline
按照各自功能上的差异,GstElement 可以细分成如下几类:
Source Element 数据源元件,只有输出端。它仅能用来产生供管道消费的数据,而不能对数据做任何处理。一个典型的数据源元件的例子是音频捕获单元,它负责从声卡读取原始的音频数据,然后作为数据源提供给其它模块使用。
Filter Element 过滤器元件,既有输入端又有输出端。它从输入端获得相应的数据,并在经过特殊处理之后传递给输出端。 一个典型的过滤器元件的例子是音频编码单元,它首先从外界获得音频数据,然后根据特定的压缩算法对其进行编码,最后再将编码后的结果提供给其它模块使用)
Sink Element 接收器元件,只有输入端。它仅具有消费数据的能力,是整条媒体管道的终端。一个典型的接收器元件的例子是音频回放单元,它负责将接收到的数据写到声卡上,通常这也是音频处理过程中的最后一个环节。
若干elemetns组成一个pipeline
三、Gstreamer编解码一般步骤
- 创建所需elements
- gst_bin_add_many添加创建的elements
- 链接elements
- 设置pipeline为Playing状态
- 设置采样回调
三、完整代码示例
#include <iostream>
#include <string>
#include <gst/gst.h>typedef struct _custom_data
{/* pipeline{source->rtpdepay->rtp_queue->h264parse->hard_decoder->cudaconvert->cudadowload->video_queue->sink} */GstElement *pipeline,*source, *rtpdepay, *rtp_queue, *h264parse, *h264_queue,*hard_decoder, *img_filter, *cudaconvert, *cudadowload, *video_queue, *sink;std::string Rtsp;
} CustomData;/* Create the elements */
GstFlowReturn InitGst(CustomData *data)
{GstFlowReturn retVal;/* Create the elements */data->pipeline = gst_pipeline_new("rtsp-decode-pipeline");/** param1: element 类型 param2: 名称* gst_element_factory_make(param1, param2)*/data->source = gst_element_factory_make("rtspsrc", "source");// rtph264depay: Extracts H264 video from RTP packets (RFC 3984)data->rtpdepay = gst_element_factory_make("rtph264depay", "rtpdepay");data->rtp_queue = gst_element_factory_make("queue", "rtp_queue");// h264parse: Parses H.264 streamsdata->h264parse = gst_element_factory_make("h264parse", "h264parse");data->h264_queue = gst_element_factory_make("queue", "h264_queue");// nvh264dec: NVDEC video decoderdata->hard_decoder = gst_element_factory_make("nvh264dec", "hard_decoder"); // 硬解// cudaconvert: Convert video frames between supported video formats.data->cudaconvert = gst_element_factory_make("cudaconvert", "cudaconvert");// cudadownload: Downloads data from NVIDA GPU via CUDA APIsdata->cudadowload = gst_element_factory_make("cudadownload", "cudadownload");if (!data->hard_decoder || !data->cudaconvert || !data->cudadowload){data->hard_decoder = gst_element_factory_make("avdec_h264", "hard_decoder"); // 软解data->cudaconvert = gst_element_factory_make("videoconvert", "cudaconvert");data->cudadowload = gst_element_factory_make("queue", "cudadownload");/* https://www.nvidia.cn/Download/index.aspx?lang=cn 驱动下载地址*/g_print("Use SoftDecoder, Ensure NVIDIA driver is the lastest version!!!\n");}else{g_print("Use HardDecode!\n");}data->video_queue = gst_element_factory_make("queue", "video_queue");data->sink = gst_element_factory_make("appsink", "sink"); // autovideosink, fakesink, appsink, filesinkif (!data->pipeline || !data->source || !data->rtp_queue || !data->rtpdepay || !data->h264_queue ||!data->h264parse || !data->hard_decoder || !data->cudaconvert || !data->cudadowload || !data->video_queue || !data->sink){g_print("Not all elements could be created!!!\n");return GST_FLOW_ERROR;}g_print("All elements created success\n");/* 设置rtsp的输入地址 */g_object_set(G_OBJECT(data->source), "location", data->Rtsp.c_str(), "latency", 2000, NULL);/* 设置输出格式 */g_object_set(G_OBJECT(data->sink),"sync", FALSE,"emit-signals", TRUE,"caps", gst_caps_new_simple("video/x-raw",// "width", G_TYPE_INT, 1920,// "height", G_TYPE_INT, 1080,// "framerate", GST_TYPE_FRACTION, 10, 1,"format", G_TYPE_STRING, "NV12", NULL),NULL);/* 创建pipeline,注意此时各个组件还没有连接,只是add到管道,也就是说,add要在link之前 */gst_bin_add_many(GST_BIN(data->pipeline), data->source, data->rtpdepay, data->rtp_queue, data->h264parse, data->h264_queue, data->hard_decoder, data->cudaconvert, data->cudadowload, data->video_queue, data->sink, NULL);// 此时rtsp src和 rtph264depay还没有连接,所以必须设置pad-added信号监听,// 在管道开始工作后,确定了数据格式,再把它们连接起来g_signal_connect(data->source, "pad-added", G_CALLBACK(RtspSrcPadAdded_callback), data);// 连接元素,注意顺序不能错,因为还没有确定数据的格式,所以此时rtsp src和rtph264depay还没建立连接,// 先将rtsp src之后的元件连接起来if (!gst_element_link_many(data->rtpdepay, data->rtp_queue, data->h264parse, data->hard_decoder, data->cudaconvert, data->cudadowload, data->video_queue, NULL)){g_printerr("@@@ OpenRtsp: Failed to link rtpdepay -> video_queue\n");return GST_FLOW_ERROR;}if (!gst_element_link_many(data->video_queue, data->sink, NULL)){g_printerr("@@@ OpenRtsp: Failed to link video_queue -> sink\n");return GST_FLOW_ERROR;}g_print("All elements Link success\n");/* 设置 pipeline 状态为 Playing */GstStateChangeReturn ret = gst_element_set_state(data->pipeline, GST_STATE_PLAYING);if (ret == GST_STATE_CHANGE_FAILURE){g_printerr("@@@ OpenRtsp: Unable to set the pipeline to the playing state.\n");return GST_FLOW_ERROR;}// 设置采样回调g_signal_connect(data->sink, "new-sample", G_CALLBACK(ReadvideoFrame_callback), data);return GST_FLOW_OK;
}/* source new src pad create */
static void RtspSrcPadAdded_callback(GstElement *src, GstPad *new_pad, gpointer user_data)
{CustomData *data = (CustomData *)user_data;GstPad *sink_pad = gst_element_get_static_pad(data->rtpdepay, "sink");GstCaps *p_caps;gchar *description;GstPadLinkReturn ret;GstCaps *new_pad_caps = NULL;GstStructure *new_pad_struct = NULL;const gchar *new_pad_type = NULL;g_print("Received new pad '%s' from '%s':\n", GST_PAD_NAME(new_pad), GST_ELEMENT_NAME(src));//使用gst_pad_is_linked(sink_pad)来检查是否已经连接好了,若是则忽略该信号if (gst_pad_is_linked(sink_pad)){g_print("We are already linked. Ignoring.\n");goto exit;}// here, you would setup a new pad link for the newly created pad// so, now find that rtph264depay is needed and link them?p_caps = gst_pad_get_pad_template_caps(new_pad);description = gst_caps_to_string(p_caps);g_print("new pad caps: %s\n", description);g_free(description);if (NULL != p_caps)gst_caps_unref(p_caps);/* Attempt the link *//* Check the new pad's 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里面有没有这种格式的数据if (!g_str_has_prefix(new_pad_type, "application/x-rtp")){g_print("It has type '%s' which is not application/x-rtp. Ignoring.\n", new_pad_type);goto exit;}//如果找到了application/x-rtp格式的输入数据,则将rtspsrc的source pad和rtph264depay的sink pad连起来ret = gst_pad_link(new_pad, sink_pad); // linkif (GST_PAD_LINK_FAILED(ret)){g_print("Type is '%s' but link failed.\n", new_pad_type);}else{g_print("Link succeeded (type '%s').\n", new_pad_type);}/* 解引用,释放资源*/if (NULL != new_pad_caps)gst_caps_unref(p_caps);exit:/* 解引用,释放资源*/if (sink_pad != NULL)gst_object_unref(sink_pad);
}/* 每次从视频流中获取一帧数据 */
void ReadVideoFrame_callback(GstElement *sink, gpointer user_data)
{CustomData *data = (CustomData *)user_data;GstSample *sample = nullptr;gsize data_size = 0;gsize stream_size = 0;// 使用pull-sample拉取视频帧,并映射到map变量,通过map拷贝出frame数据g_signal_emit_by_name(sink, "pull-sample", &sample);if (sample){GstBuffer *buffer = gst_sample_get_buffer(sample);if (buffer){GstMapInfo map;if (gst_buffer_map(buffer, &map, GST_MAP_READ)){char buf[4096] = {0};memcpy(buf, map.data, data_size); // 获取解码后的数据到buf// release buffer mappinggst_buffer_unmap(buffer, &map);}else{printf("fgst_buffer_map error failed!!!\n");}}else{printf("gst_sample_get_buffer failed!!!\n");}gst_sample_unref(sample); // release sample reference}else{printf("sample is null...\n");}return;
}int main()
{GstFlowReturn ret;CustomData *context = (CustomData*)malloc(sizeof(CustomData));if (!context){printf("context malloc failed!\n");return -1;}context->Rtsp = "rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mp4";if ((ret = InitGst(context)) == GST_FLOW_OK){printf("InitGst success..\n");}while(1);return 0;
}
Gstreamer 硬解码Rtsp流及代码实现相关推荐
- ffmpeg 找不到bin_FFmpeg开发笔记(九):ffmpeg解码rtsp流并使用SDL同步播放
若该文为原创文章,转载请注明原文出处 本文章博客地址:https://blog.csdn.net/qq21497936/article/details/109603499 各位读者,知识无穷而人力有穷 ...
- EasyPlayerPro:安卓视频播放器Android H.265硬解码方案(内含代码)
背景介绍 H.265是ITU-TVCEG继H.264之后所制定的新的视频编码标准.H.265标准围绕着现有的视频编码标准H.264,保留原来的某些技术,同时对一些相关的技术加以改进.H.265使用先进 ...
- SOPHON sail.Decoder无法正常解码rtsp流(使用ffmpeg和opencv可以正常解码)
问题描述: rtsp流使用ffmpeg和opencv可以正常解码,但是使用sail.Decoder无法正常解码 定位问题的原因是extra_frame_buffer_num给的太大. 分别分析几个矛盾 ...
- ffmpeg解码ps流部分代码以及PS播放器demo
之前的设备研发算是告一段落了,最近一直在忙视频监控平台的架构以及实现,想把自己的设备接到自己的平台里,设备上的码流是ps流,要在平台里解码ps流->解码成h264->yuv->rgb ...
- 如何使用硬件解码器在python中解码RTSP流?(NVidia JetSon Nano)
我有NVIDIA Jetson Nano和FullHD Ip相机.摄像机流RTSP / h264.我想从这台相机的python脚本中解码帧以进行分析. CPU 解码 因此,我尝试使用类似的方法: # ...
- ffmpeg 硬件解码rtsp流_树莓派使用硬件加速视频转码
现在随着智能设备普及以及宽带的升级,越来越的的视频素材在不断的产生.无论是我们自己拍摄的视频,还是从网上收集来的电影.电视剧,并不是全部都值得我们保存最高清的版本.打个比方,比如你下载了一个 1080 ...
- 摄像头RTSP流硬解码
1. 问题分析 项目中,之前用的是OpenCV对摄像头的RTSP流进行解码.随着时间的推移以及业务摄像头的增加,发现十路流CPU就已经100%啦,很明显解码所占CPU资源较多,导致整个系统处理效率不高 ...
- 采集rtsp流摄像头到浏览器实时播放方案
本文旨在实现使用摄像头采集视频,并且可以在网页实时显示,主要参考的两篇博文为: 1. 视频实时显示的三种方案 2. 使用WebSockets进行HTML5视频直播 我们使用博文1介绍的第 ...
- 调用D3D11硬解码和渲染VideoProcessor版本
在https://blog.csdn.net/robothn/article/details/78781321一文中使用shader来显示FFMPEG硬解码后的YUV420P,本文调用D3D11的vi ...
最新文章
- python利用wx.grid网格显示数据
- 仅50张图片训练数据的AI分类技术PK​,阿里拿下ECCV 2020竞赛冠军
- 计算机科学和PYTHON编程导论_15_概率与分布
- 算法学习:主席树(可持久化线段树)
- 【Linux】rpm常用命令及rpm参数介绍
- MYSQL查看 table 表状态常用的命令
- 在Ubuntu 8.04 LTS(hardy)下安装配置nginx和fastcgi方式的php
- 二改注册登录版素材代下载搜索引擎系统源码,自带火车头采集
- java线程轮询_基于springboot实现轮询线程自动执行任务
- 2021当一名优雅的代码打工人
- 第七章 (一)暴力求解法
- 小D课堂 - 新版本微服务springcloud+Docker教程_5-03 feign结合hystrix断路器开发实战上...
- Java Foundation serial ( 一 )
- 真实项目,用微信小程序开门编码实现(完结)
- 一文看懂DCDC拓扑原理
- 超简单的Spring入门案例制作,快来看看吧!
- java的pdf转永中_永中PDF转Word 免费转换不求人!
- 系统分析员、系统架构师、项目经理的区别
- Xcelsius 2008 sp3的新部件与新功能
- SCI论文写作高频词汇短语汇总