背景

业务需要自己做解码,因为软解码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编解码一般步骤

  1. 创建所需elements
  2. gst_bin_add_many添加创建的elements
  3. 链接elements
  4. 设置pipeline为Playing状态
  5. 设置采样回调

三、完整代码示例

#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流及代码实现相关推荐

  1. ffmpeg 找不到bin_FFmpeg开发笔记(九):ffmpeg解码rtsp流并使用SDL同步播放

    若该文为原创文章,转载请注明原文出处 本文章博客地址:https://blog.csdn.net/qq21497936/article/details/109603499 各位读者,知识无穷而人力有穷 ...

  2. EasyPlayerPro:安卓视频播放器Android H.265硬解码方案(内含代码)

    背景介绍 H.265是ITU-TVCEG继H.264之后所制定的新的视频编码标准.H.265标准围绕着现有的视频编码标准H.264,保留原来的某些技术,同时对一些相关的技术加以改进.H.265使用先进 ...

  3. SOPHON sail.Decoder无法正常解码rtsp流(使用ffmpeg和opencv可以正常解码)

    问题描述: rtsp流使用ffmpeg和opencv可以正常解码,但是使用sail.Decoder无法正常解码 定位问题的原因是extra_frame_buffer_num给的太大. 分别分析几个矛盾 ...

  4. ffmpeg解码ps流部分代码以及PS播放器demo

    之前的设备研发算是告一段落了,最近一直在忙视频监控平台的架构以及实现,想把自己的设备接到自己的平台里,设备上的码流是ps流,要在平台里解码ps流->解码成h264->yuv->rgb ...

  5. 如何使用硬件解码器在python中解码RTSP流?(NVidia JetSon Nano)

    我有NVIDIA Jetson Nano和FullHD Ip相机.摄像机流RTSP / h264.我想从这台相机的python脚本中解码帧以进行分析. CPU 解码 因此,我尝试使用类似的方法: # ...

  6. ffmpeg 硬件解码rtsp流_树莓派使用硬件加速视频转码

    现在随着智能设备普及以及宽带的升级,越来越的的视频素材在不断的产生.无论是我们自己拍摄的视频,还是从网上收集来的电影.电视剧,并不是全部都值得我们保存最高清的版本.打个比方,比如你下载了一个 1080 ...

  7. 摄像头RTSP流硬解码

    1. 问题分析 项目中,之前用的是OpenCV对摄像头的RTSP流进行解码.随着时间的推移以及业务摄像头的增加,发现十路流CPU就已经100%啦,很明显解码所占CPU资源较多,导致整个系统处理效率不高 ...

  8. 采集rtsp流摄像头到浏览器实时播放方案

    本文旨在实现使用摄像头采集视频,并且可以在网页实时显示,主要参考的两篇博文为:  1.  视频实时显示的三种方案   2.  使用WebSockets进行HTML5视频直播   我们使用博文1介绍的第 ...

  9. 调用D3D11硬解码和渲染VideoProcessor版本

    在https://blog.csdn.net/robothn/article/details/78781321一文中使用shader来显示FFMPEG硬解码后的YUV420P,本文调用D3D11的vi ...

最新文章

  1. python利用wx.grid网格显示数据
  2. 仅50张图片训练数据的AI分类技术PK​,阿里拿下ECCV 2020竞赛冠军
  3. 计算机科学和PYTHON编程导论_15_概率与分布
  4. 算法学习:主席树(可持久化线段树)
  5. 【Linux】rpm常用命令及rpm参数介绍
  6. MYSQL查看 table 表状态常用的命令
  7. 在Ubuntu 8.04 LTS(hardy)下安装配置nginx和fastcgi方式的php
  8. 二改注册登录版素材代下载搜索引擎系统源码,自带火车头采集
  9. java线程轮询_基于springboot实现轮询线程自动执行任务
  10. 2021当一名优雅的代码打工人
  11. 第七章 (一)暴力求解法
  12. 小D课堂 - 新版本微服务springcloud+Docker教程_5-03 feign结合hystrix断路器开发实战上...
  13. Java Foundation serial ( 一 )
  14. 真实项目,用微信小程序开门编码实现(完结)
  15. 一文看懂DCDC拓扑原理
  16. 超简单的Spring入门案例制作,快来看看吧!
  17. java的pdf转永中_永中PDF转Word 免费转换不求人!
  18. 系统分析员、系统架构师、项目经理的区别
  19. Xcelsius 2008 sp3的新部件与新功能
  20. SCI论文写作高频词汇短语汇总

热门文章

  1. php获取json中的内容
  2. thinkphp sql查询范围大于等于和小于等于
  3. 9.23浩鲸科技java开发校招电话一面
  4. 腾讯研发动画组件,以后动画制作用PAG
  5. 用arduino和16路舵机控制板制作一个蛇形仿生机器人
  6. FPGA—VGA显示
  7. termux安装linux命令,Termux安装发行版Linux
  8. 工作手册:如何实施数据质量管理(上)
  9. 3ds Max随堂笔记 可编辑多边形(高级建模)
  10. 编写支持SSR的通用组件指南