Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1706460
  • 博文数量: 98
  • 博客积分: 667
  • 博客等级: 上士
  • 技术积分: 1631
  • 用 户 组: 普通用户
  • 注册时间: 2009-04-27 15:59
个人简介

一沙一世界 一树一菩提

文章分类

全部博文(98)

文章存档

2021年(8)

2020年(16)

2019年(8)

2017年(1)

2016年(11)

2015年(17)

2014年(9)

2013年(4)

2012年(19)

2011年(1)

2009年(4)

分类: 嵌入式

2020-12-07 15:30:47

在HI3559AV100上的视频开发,整体结构基本都是下面的结构,我这是做成一个库,供应用层简单调用。应用层只要调用初始化,然后start/stop/setpara/getstatus即可。
整个系统需要cmake编译很多第三方库,我是把所有开源库编译成一个pic的so库。性能上可能差点,但是使用方便。性能够用。性价比还可以。rhein需要提示一下,根据你使用编码能力的大小,默认海思的驱动可能需要修改一些参数,打开一些控制开关。如果不打开,可能系统启动不正常,或者整个板子老是感觉哪里异常,你可以咨询你的芯片提供商或者方案提供商或者有关的技术支持,这块处理不好,卡你的10天半个月轻轻松啊。

    1    我做的第一版库业务大概是下面这样:

多输入,多输出。整体上电各模块根据配置信息启动。同时推流/本地保存。其中HDMI和USB经过vpss分别bind到不同的venc通道。venc设置不同的码率/帧率/分辨率。这里的帧率控制修改的时候设置venc通道属性的srcChnFrame在HDMI和USB输入时是不同的,就是说srcChnFrame分别是hdmi或者usb的输入帧率。一般推流H264,根据网络情况实时修改码率,一般远程推流不大于4M码流,本地存储H265格式,一般1080P30。IP-camera不经过3559硬件,直接通过ffmpeg拆封装,再次封装后,直接通过rtmp推流出去。只要av_read_frame-->av_rescale_q-->av_write_frame。不改变帧率/码率/分辨率。使用ffmpeg编程时候要注意资源回收与释放。

    2    第二版库业务框图大概如下:

多输入,多输出。我怎么看都觉得这就是一个nvr的方案。输入支持HDMIx1,USBx2,rtsp(ipcamera)x6,最多9路输出,usb输入解码使用jpeg,rtsp是264解码。hdmi直接给VO模块,多输入分别bind不同的VO通道,在一个显示层可以显示多画面,这样可以实现画中画功能。把VO输出bind到WD模块,然后到vpss的输入,vpss的输出给venc,这样就把多输入视频源编码,进行不同的分发了。下图是多通道显示:


这是我推流到服务器,然后从服务器拉流看到的效果。我这是4输入,蓝屏是hdmi,没有接输入,然后2路rtsp一路usb,在DHD0上使用VHD0,所有的输入都bind的VO的不同通道,每通道在显示界面显示的效果就是上图的样子。如果你想修改哪个通道的显示画面大小,直接VO_GetChnAttr,然后再VO_SetChnAttr即可,这个功能可以动态修改,硬件不必停下。使用的VHD0的缩放功能。你修改每通道的显示区域和范围大小,直接会在拉流播放界面看到响应。应用层调用接口,第一次看到效果,有点小成就感啊。支持帧率/码率/分辨率在线实时修改,下图是监控服务器看到的码率和帧率改变效果:

海思提供的sample的osd都是叠加到某一venc通道,我的框图绿色的线所示,这部分还没有实现,我的本意是利用区域管理加到vpss输入端,因为输出都是一样的,没必要每一路venc输出都叠加相同的内容,目前还没有实现,不知道按照我的想法能否实现。但是如果你支持osd定制那就按照绿色线的样子加就可以了。

NNIE和IVE是3559的很大一个卖点,输入视频源的目标识别与标识,可以使用这2个模块加速。目前也没有实现,不多说。

最后说下遇到的问题:
1    添加音频可能会造成视频的快播或变慢,从音频的pts入手,慢慢解决,internet多查查,问题不大。

2    我遇到的一个问题是,整个系统第一次启动,功能正常,由于输入源不能停,输出一路存储,一路推流,你可以停掉推功能,存储还是会正常进行,所以输入不能停,然后再次启动推流,推不出去。卡了2-3天也没毛线进展,实在没有办法,wireshark上阵,抓包推流过程,顺便详细了解下rtmp协议过程。

这是推流客户端与服务器的交互过程:
    A 客户端发送握手信号C0+C1
    B 服务器回复握手信号S0+S1+S2
    C 客户端回复握手信号C2
    D 客户端connect
    ...客户端告诉服务器端参数, 服务器端回复参数
    F publish
    G 服务器onstatus
    H 客户端发送 @setdataFrame()|Video Data
    ...客户端发送video
    I 客户端高速服务器端结束FCUnpublish|deleteStream
    J 服务器端回复客户端 onStatus(NetStream.Unpublish.Success)
上面是一个全过程。我遇到的情况是一旦执行上面最后的I和J阶段以后,再次启动就再也不能正常推流了。抓包分析发现,在第一次结束以后的客户端给服务器端发送setDataFrame|video Data数据包不正常。正常的是如下:
这是第一个正常的I帧的头部,下面看看异常的相同类型的数据包:

发现没,相同的数据包少了不少东西,分析发现,异常的数据包缺失了sps和pps相关信息。具体的rtmp数据包分析可以参考这里:https://blog.csdn.net/gesanghua601/article/details/81981898分析的不错。
然后根据得到的结果,找代码中关于第一个I帧帧头的部分,在avformat_write_head中发送的视频头部函数中。填充头部第一次正常,后面就不正常了。经分析得知:第一次编码器启动,用编码器输出的第一帧直接填充头部,刚启动得到的第一帧是I帧,得到的sps和pps信息没有啥问题,后面再次启动也是取当时得到的第一帧来填充,这时候由于编码器没有停下,是一直工作的,所以拿当时得到的第一帧来填充,关键这是时候拿到的这一帧未必是I帧,GOP是30,帧率是30的话,1s才有一次I帧,大部分都是P,小部分概率是B帧,一般网络传输可能没有B帧,只有P帧。只有很小概率随机能拿到I帧。知道了原因,就容易了,后面再次启动的时候,循环+超时查找I帧,直到找到I帧取出sps和pps内容,填充头部,然后调用avformat_write_head,OK,问题解决。就我自己遇到的问题,如果你用ffmpeg库writeframe遇到类似broken pipe,EOF,reset by peer,很大程度是因为你的数据发送给服务器,不满足格式,而当你设置不同的超时限制时,就会得到不同的错误,一般无外乎上面三种。

    3 如果遇到Failed to update header with correct duration,Failed to update header with correct filesize这样的提示,这不是错误,是警告。如下操作:

点击(此处)折叠或打开

  1. AVDictionary* opts = NULL;
  2. av_dict_set(&opts, "flvflags", "no_duration_filesize", 0);
  3. ret = avformat_write_header(pAVOutFmtCtx, &opts)
    4 如果你感觉当网络有问题的时候 ,超时时间太长,设置如下:

点击(此处)折叠或打开

  1. AVDictionary *opts = NULL;
  2. av_dict_set(&opts, "rw_timeout", "800000", 0);
  3. ret = avio_open2(&pAVOutFmtCtx->pb, filename, AVIO_FLAG_WRITE, NULL, &opts)

    5 我的硬件初始化顺序:
MPPinit-->VDECstart->VIstart->VPSSstart-->VOstart-->VENCstart-->WBCstart-->allbind。bind顺序:viBvo-->vdecBvo-->wbcBvpss-->vpssBvenc。另外注意,硬件exit的时候顺序和init的顺序相反。
    6 最后,使用ffmpeg注意资源与设备的回收与释放。



阅读(4824) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~