【GStreamer】基于NVIDIATEGRA系列板卡的硬件解码及视
频推流
以NVIDIATX1为例硬解码就是利⽤硬件芯⽚来解码的,TX1有单独的解码模块,NVDEC.
软解码是⽤软件程序来解码,⽐较占⽤CPU资源
查看cpugpu以及编解码模块的使⽤:
sudo./tegrastats
1Gstreamer概述
Gstreamer是⼀个libraries和plugins的集合,⽤于帮助实现各种类型的多媒体应⽤程序,⽐如播放器,转码⼯具,多媒体服务器等。
利⽤Gstreamer编写多媒体应⽤程序,就是利⽤elements构建⼀个pipeline。element是⼀个对多媒体流进⾏处理的object,⽐如如下的处
理:
*读取⽂件。
*不同格式的编解码。
*从硬件采集设备上采集数据。
*在硬件设备上播放多媒体。
*多个流的复⽤。
elements的输⼊叫做sinkpads,输出叫做sourcepads。应⽤程序通过pad把element连接起来构成pipeline,如下图所⽰,其中顺着流
的⽅向为downstream,相反⽅向是upstream。
应⽤程序会收到来⾃pipeline的消息和通知,⽐如EOS等。
总体设计
Gstreamer的设计⽬标如下:
快速处理⼤规模数据。
对多线程处理的完全⽀持。
能处理各种格式的流媒体。
不同数据流的同步。
处理多种设备的能⼒。
基于Gstreamer的应⽤程序能够具备的处理能⼒依赖于系统中安装的不同种类功能的elements的数量。
Gstreamer核⼼不具备处理具体的media的功能,但是element处理media时需要具备的特性很多是由Gstreamer的核⼼提供的。
elements
element是pipeline的最⼩组成部分。element提供了多个pads,或者为sink,或者为source。⼀个element入团志愿书表格 有四种可能的状态,分别是
NULL,READY,PAUSED,PLAYING。NULL和READY状态下,element不对数据做任何处理,PLAYING状态对数据进⾏处
理,PAUSE状态介于两者之间,对数据进⾏preroll。应⽤程序通过函数调⽤控制pipeline在不同状态之间进⾏转换。
element的状态变换不能跳过中间状态,⽐如不能从READY状态直接变换到PLAYING状态,必须经过中间的PAUSE状态。
element的状态转换成PAUSE会激活element的pad。⾸先是sourcepad被激活,然后是sinkpad。pad被激活后会调⽤activate函数,
有⼀些pad会启动⼀个Task。
PAUSE状态下,pipeline会进⾏数据的preroll,⽬的是为后续的PLAYING状态准备好数据,使得PLAYING启动的速度更快。⼀些
element需接收到⾜够的数据才能完成向PAUSE状态的转变,sinkpad只有在接收到第⼀个数据才能实现向PAUSE的状态转变。
通常情况下,element的状态转变需要协调⼀致。
可对element进⾏如下分类:
source,只提供数据源。
sink,⽐如播放设备。
transform
demuxer
muxer
Bin
bin是由多个element构成的特殊的element,⽤图来说明:
Pipeline
pipeline是具备如下特性的特殊的bin:
选择并管理⼀个全局的时钟。
基于选定的时钟管理running_time。running_time⽤于同步,指的是pipeline在PLAYING状态下花费的时间。
管理pipeline的延迟。
通过GstBus提供element与应⽤程序间的通讯⽅式。
管理elements的全局状态,⽐如EOS,Error等。
Dataflowandbuffers
Gstreamer⽀持两种类型的数据流,分别是push模式和pull模式。在push模式下,upstream的element通过调⽤downstream的sink
pads的函数实现数据的传送。在pull模式下,downstream的element通过调⽤upstream的sourcepads的函数实现对数据的请求。
push模式是常⽤的模式,pull模式⼀般⽤于dem儿媳 uxer或者低延迟的⾳频应⽤等。
在pads之间传送的数据封装在Buffer⾥,Buffer中有⼀个指向实际数据的指针以及⼀些metadata。metadata的内容包括:
Timestamp
Offt
Duration
mediatype
其它
在push模式下,element通过调⽤gst_pad_push()函数把buffer传送给对应的pad。在pull模式下,element通过调⽤
gst_pad_pull_range()函数把pull过来。
element在pushbuffer之前需要确认对应的element具备处理buffer中的数据类型的能⼒。在传说红之前⾸先查询对应的element能够处
理的格式的种类,并从中选择合适的格式,通过gst_buffer_t_caps()函数对buffer进⾏设置,然后才传送数据。
收到⼀个buffer后,element要⾸先对buffer进⾏检查以确认是否能够处理。
可以调⽤gst_buffer_new()函数创建⼀个新的buffer,也可以调⽤gst_pad_alloc_buffer()函数申请⼀个可⽤的buffer。采⽤第⼆种
⽅法接收数据的buffer可以设定接收其它类型的数据,这是通过对buffer的caps进⾏设定来实现的。
选择媒体类型并对buffer进⾏设定的处理过程叫做capsnegotianation。
Caps
Caps,也就是媒体类型,采⽤key/value对的列表来描述。key是⼀个字符串类型,value的类型可能是int/float/string类型的
single/list/range。
Dataflowandevents
除了数据流,还有events流。与数据流不同,events的传送⽅向既有downstream的,也有upstream的。
events⽤于传递EOS,flushing,eking等消息。
有的events必须和dataflow⼀起进⾏rialized。rialized的events⽐如TAG,⾮rialized的events⽐如FLUSH。
Pipelineconstruction
gst_pipeline_create()函数⽤于创建⼀个pipeline,gst_bin_add()函数⽤于向pipeline中添加element,gst_bin_remove()函数
⽤于从pipeline中移除element。gst_element_get_pad()函数⽤于检索pipeline中的element。gst_pad_link()函数⽤于把pads连接
在⼀起。
有的element会在数据流开始传送的时候创建新的pads,通过调⽤函数g_signal_connect()函数,能在新的pads被创建的时候接收到消
息。
由于处理的数据互相不兼容,有的elements是不能被连接到⼀起的。gst_pad_get_caps()函数查询element能够处理的数据类型。
Pipelineclock
Pipeline的⼀个重要功能是为pipeline中的所有elements选择⼀个全局时钟。
时钟的作⽤是提供⼀个每秒为GST_SECOND的单调递增的时钟,单位是纳秒。element利⽤这个时钟时间来播放数据。
在pipeline被设为PLAYING之前,pipeline查询每⼀个element是否能提供clock,并按照如下次序来选择clock:
应⽤程序选择了⼀个clock。
如果sourceelement提供了clock。
其它任何提供了clock的element。
选择⼀个默认的系统clock。
也有特殊的情况,⽐如存在⾳频sink提供了clock,那么就选择其提供的clock。
Pipelinestates
完成了pads的链接和signals的链接,就可以设定pipeline为PAUSED状态启动数据流的处理。当bin(这⾥指的是pipeline)进⾏状态转换
的时候要转换所有的children的状态,转换的次序是从sinkelement开始到sourceelement结束,这样做的⽬的是为了确保upstream
element提供数据的时候,downstreamelement已经准备好。
Pipelinestatus
Pipeline会通过bus向应⽤程序通报发⽣的events。bus是由pipeline提供的⼀个object,可以通过gst_pipeline_get_bus()函数取得。
bus分布到加⼊pipeline的每⼀个element。element利⽤bus来发布messages。有各种不同类型的messages,⽐如
ERRORS,WARNINGS,EOS,STAT好看的古装电影 E_CHANGED等。
pipeline以特殊的⽅式处理接收到的EOSmessage,只有当所有的sinkelement发送了EOSmessage的时候,pipeline才会把EOS发送
给应⽤程序。
也可以通过gst_element_query()函数获取pipelinestatus,⽐如获取当前的位置或者播放的时间。
PipelineEOS
当sourcefilter遇上了流结束,会沿着downstream的⽅向向下⼀个element发送⼀个EOS的event,这个event依次传送给每⼀个
element,接收到EOSevent的element不再接收数据。
启动了线程的element发送了EOSevent后就不再发送数据。
E云破月来打一字 OSevent最终会到达sinkelement。sinkelement会发送⼀个EOS消息,通告流结束。pipeline在接收到EOS消息以后,把消息发送给
应⽤程序。只有在PLAYING状态下会把EOS的消息传送给应⽤程序。
发送了EOS以后,pipeline保持PLAYING状态,等待应⽤程序把pipeline的状态置为PAUSE或者READY。应⽤程序也可以进⾏ek操
作。
2Gstreamer解码
2.1调⽤Gstreamer解码多路rtsp(部分代码)
/*
*Author:mxj
*/
#ifndef__GSTREAMER_CAMERA_H__
#define__GSTREAMER_CAMERA_H__
#include
#include
struct_GstAppSink;//声明结构体和类
classQWaitCondition;
classQMutex;
/***gstreamerCSIcamerausingnvcamerasrc(oroptionallyv4l2src)
*@ingrouputil
*/
classgstCamera
{
public:
//创建camera类
staticgstCamera*Create(intv4l2_device=-1);//uonboardcamerabydefault(>=0forV4L2)
staticgstCamera*Create(uint32_twidth,uint32_theight,intv4l2_device=-1);
//析构函数
~gstCamera();
//开始和停⽌流
boolOpen();
voidClo();
//采集YUV(NV12格式)
boolCapture(void**cpu,void**cuda,unsignedlongtimeout=ULONG_MAX);
//抓取YUV-NV12CUDAimage,转换成float4RGBA(像素范围在0-255)
//转换如果在CPU上进⾏,设置zeroCopy=true,默认只在CUDA上.
boolConvertRGBA(void*input,void**output,boolzeroCopy=fal);
//图像⼤⼩信息inline(内联函数,适合简单的函数)
inlineuint32_tGetWidth()const{returnmWidth;}
inlineuint32_tGetHeight()const{returnmHeight;}
inlineuint32_tGetPixelDepth()const{returnmDepth;}
inlineuint32_tGetSize()const{returnmSize;}
//默认图像⼤⼩,可以在create时改变
staticconstuint32_tDefaultWidth=1280;
staticconstuint32_tDefaultHeight=720;
private:
staticvoidonEOS(_GstAppSink*sink,void*ur_data);
staticGstFlowReturnonPreroll(_GstAppSink*sink,void*ur_data);//GstFlowReturn传递流
staticGstFlowReturnonBuffer(_GstAppSink*sink,void*ur_data);
gstCamera();
boolinit();
boolbuildLaunchStr();
voidcheckMsgBus();
voidcheckBuffer();
//GstBus
_GstBus*mBus;//GstBus异步同步消息
_GstAppSink*mAppSink;
_GstElement*mPipeline;
std::stringmLaunchStr="rtspsrclocation=rtsp://admin:buaa123456@192.168.1.106:554/h264/ch1/main/av_streamlatency=0!queue!rtph264depay!h
uint32_tmWidth;
uint32_tmHeight;
uint32_tmDepth;
uint32_tmSize;
staticconstuint32_tNUM_RINGBUFFERS=16;//环形队列来解决数据阻塞问题
void*mRingbufferCPU[NUM_RINGBUFFERS];
void*mRingbufferGPU[NUM_RINGBUFFERS];
QWaitCondition*mWaitEve可爱漫画头像女生 nt;
//()//锁住互斥量(mutex)。如果互斥量是解锁的,那么当前线程就⽴即占⽤并锁定它。否则,当前线程就会被阻塞,知道掌握这个互斥量的线程对它解
//()//解锁
//k()//尝试解锁,如果该互斥量已经锁住,它就会⽴即返回
QMutex*mWaitMutex;
QMutex*mRingMutex;
uint32_tmLatestRGBA;
uint32_tmLatestRingbuffer;
boolmLatestRetrieved;
void*mRGBA[NUM_RINGBUFFERS];
intmV4L2Device;//-1foronboard,>=0forV4L2device
inlineboolonboardCamera()const{return(mV4L2Device<0);}
};
#endif
boolgstCamera::Capture(void**cpu,void**cuda,unsignedlongtimeout)
{
/*wait()函数必须传⼊⼀个已上锁的mutex对象,在wait()执⾏过程中,
mutex⼀直保持上锁状态,直到调⽤操作系统的wait_block在阻塞的⼀瞬间把mutex解锁
(严格说来应该是原⼦操作,即系统能保国产彩妆 证在真正执⾏阻塞等待指令时才解锁)。
另⼀线程唤醒后,wait()函数将在第⼀时间重新给mutex上锁(这种操作也是原⼦的)
,直到显⽰调⽤()解锁。*/
mWaitMutex->lock();
constboolwait_result=mWaitEvent->wait(mWaitMutex,timeout);
mWaitMutex->unlock();
if(!wait_result)
returnfal;
mRingMutex->lock();
constuint32_tlatest=mLatestRingbuffer;
constboolretrieved=mLatestRetrieved;
mLatestRetrieved=true;
mRingMutex->unlock();
//skipifitwasalreadyretrieved
if(retrieved)
returnfal;
if(cpu!=NULL)
*cpu=mRingbufferCPU[latest];
if(c蒜苗的种植方法 uda!=NULL)
*cuda=mRingbufferGPU[latest];
returntrue;
}
}
#definerelea_return{gst_sample_unref(gstSample);return;}
//checkBuffer
voidgstCamera::checkBuffer()
{
boolwrite_flags=true;//默认写数据
if(!mAppSink)
return;
//blockwaitingforthebuffer函数被唤醒untilAsampleorEOS可⽤或者appsink被设置成ready/nullstate
GstSample*gstSample=gst_app_sink_pull_sample(mAppSink);
if(!gstSample)
{
printf(LOG_GSTREAMER"gstreamercamera--gst_app_sink_pull_sample()");
return;
}
//getbufferfromgstSample
GstBuffer*gstBuffer=gst_sample_get_buffer(gstSample);
if(!gstBuffer)
{
printf(LOG_GSTREAMER"gstreamercamera--gst_sample_get_buffer()");
return;
}
//retrieve
GstMapInfomap;
if(!gst_buffer_map(gstBuffer,&map,GST_MAP_READ))
{
printf(LOG_GSTREAMER"gstreamercamera--gst_buffer_map()");
return;
}
//gst_util_dump_mem(,);
void*gstData=;//GST_BUFFER_DATA(gstBuffer);
constuint32_tgstSize=;//GST_BUFFER_SIZE(gstBuffer);
if(!gstData)
{
printf(LOG_GSTREAMER"");
relea_return;
}
//取出caps
GstCaps*gstCaps=gst_sample_get_caps(gstSample);
if(!gstCaps)
{
printf(LOG_GSTREAMER"");
relea_return;
}
GstStructure*gstCapsStruct=gst_caps_get_structure(gstCaps,0);
if(!gstCapsStruct)
{
printf(LOG_GSTREAMER"");
relea_return;
}
//getwidth&heightofthebuffer
//getwidth&heightofthebuffer
intwidth=0;
intheight=0;
if(!gst_structure_get_int(gstCapsStruct,"width",&width)||
!gst_structure_get_int(gstCapsStruct,"height",&height))
{
printf(LOG_GSTREAMER"gstreamercamera--gst_");
re阴影的英文 lea_return;
}
if(width<1||height<1)
relea_return;
mWidth=width;
mHeight=height;
mDepth=(gstSize*8)/(width*height);
mSize=gstSize;
//printf(LOG_GSTREAMER"gstreamercamerarecieved%ix%iframe(%ubytes,%ubpp)n",width,height,gstSize,mDepth);
//makesureringbufferisallocated
if(!mRingbufferCPU[0])
{
for(uint32_tn=0;n
{
if(!cudaAllocMapped(&mRingbufferCPU[n],&mRingbufferGPU[n],gstSize))
printf(LOG_CUDA"gstreamercamera--failedtoallocateringbuffer%u(size=%u)n",n,gstSize);
}
printf(LOG_CUDA"gstreamercamera--allocated%uringbuffers,%ubyteachn",NUM_RINGBUFFERS,gstSize);
}
//copytonextringbuffer
constuint32_tnextRingbuffer=(mLatestRingbuffer+1)%NUM_RINGBUFFERS;
//printf(LOG_GSTREAMER"gstreamercamera--usingringbuffer#%ufornextframen深渊巨口 ",nextRingbuffer);
memcpy(mRingbufferCPU[nextRingbuffer],gstData,gstSize);
//FILE*fp=fopen("","w+");
//fwrite(gstData,gstSize,1,fp);
//fclo(fp);
//testh264write
//void*writedata=;
//while(write_flags==true)
//{
//FILE*fp=fopen("out.264","a+");
//fwrite(writedata,gstSize,1,fp);
//write_flags=fal;
//fclo(fp);
//}
gst_buffer_unmap(gstBuffer,&map);
//gst_buffer_unref(gstBuffer);
gst_sample_unref(gstSample);
//updateandsignalsleepingthreads
mRingMutex->lock();
mLatestRingbuffer=nextRingbuffer;
mLatestRetrieved=fal;
mRingMutex->unlock();
mWaitEvent->wakeAll();
}
可以在带显⽰的时候解码4路1080rtsp流.
不加显⽰可以做到6路1080p解码
2.2opencv中使⽤Gstreamer解码海康rtsp摄像头
A.安装gstreamer依赖
B.重新编译安装opencv
cmake-DCMAKE_BUILD_TYPE=RELEASE-DCMAKE_INSTALL_PREFIX=/usr/local-DCUDA_GENERATION=Kepler..
C.确认opencvcmake后的提⽰中Gstreamer的五个选项都为on
D.编译opencv之后的使⽤⽅法:
VideoCapture(“rtspsrclocation=”rtsp://admin:admin666@192.168.1.106/h264/h264/main/av_stream”latency=10!
rtph264depay!h264par!omxh264dec!videoconvert!appsinksync=fal”)
2.3调⽤tegra_multimedia解码
这是nvidia⾃带的编解码框架⽬前⽤官⽅的解码1080p的h264可以做到解码速度达到150fps
接⼊rtsp的相机流
解决⽅法:ffmpeg/live555解析之后做个数据拷贝
我在看nvidia官⽅的demo,了解清楚数据结构之后就可以对接解析好的h264视海底两万里第一章读书笔记 频流(后续更新)
2.4视频推流
需求:将处理后的opencv数据进⾏推流到⽹页
解决⽅案:
A>⽤tegra_multimedia编码数据为h264,live555推流为rtsp
B>直接⽤gstreamer推流(gstreamer⾃带rtsp的部分)
本文发布于:2023-03-23 08:25:25,感谢您对本站的认可!
本文链接:https://www.wtabcd.cn/fanwen/zuowen/1679531127353780.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文word下载地址:硬解码.doc
本文 PDF 下载地址:硬解码.pdf
留言与评论(共有 0 条评论) |