6.1. 4K图片的问题

答:有些客户需要的图片较大如8K等,由于VPP只支持4K大小的图片,所以通过opencv读取图片后,会自动保持比例缩放到一个4K以内的尺寸。

6.2. Opencv读取图片后,cvMat转为bmimage, 之后,调用bmcv_image_vpp_convert做缩放或者颜色空间转换,得到的图片不一致

原因分析:opencv内部的转换矩阵和bmcv_image_vpp_convert使用的转换矩阵不一致,需要调用bmcv_image_vpp_csc_matrix_covert, 并且指定CSC_YPbPr2RGB_BT601来进行转换才能保持一致。

6.3. Opencv imread读取图片性能问题。

原因分析:如果碰到图片小于16x16大小的图片,或者progressive 格式的jpeg,芯片不能实现加速,结果走了CPU的路径,导致客户发现图片解码并没有加速。

6.4. VideoWriter.write性能问题,有些客户反应,存文件慢。

解析:就目前来看看采用YUV采集,然后编码10-20ms之间写入一帧数据属于正常现象。

6.5. Ffmpeg的阻塞问题

原因分析:如果没有及时释放avframe,就会导致阻塞,vpu内部是循环buffer。

6.6. 关于什么时候调用uploadMat/downloadMat接口的问题。

解析:当创建了一个cv::Mat, 然后调用cv::bmcv里面的接口进行了一些处理后,设备内存的内容改变了,这时候需要downloadMat 来同步一下设备内存和系统内存。当调用了cv::resize等opencv原生的接口后,系统内存的内容改变了,需要uploadMat,使设备内存和系统内存同步。

6.7. opencv下如何获取视频帧的timestamp?

答:opencv原生提供了获取timestamp的接口,可以在cap.read()每一帧后获取当前帧的timestamp.

代码如下:

Mat frame;
cap.read(frame);
int64_t timestamp = (int64_t)cap.getProperty(CAP_PROP_TIMESTAMP);    *// 获取timestamp,返回值为double类型*

6.8. SA3 opencv下videocapture经常5分钟左右断网的解决方案

答:最近因为路由板软件版本有问题,在udp方式下经常会发生RTSP数据连上后3-5分钟就”connection timeout”的问题,这个问题最终解决方案是更新最新的路由板软件。验证方法可以用TCP测试下,如果TCP没有问题可以确认是这类问题。

使用TCP的方式见下面:

export  OPENCV_FFMPEG_CAPTURE_OPTIONS="rtsp_transport;tcp”

执行应用 (如果用sudo执行,需要sudo -E把环境变量带过去) 注意:最新的middleware-soc将使用TCP作为默认协议,对原来客户需要使用UDP传输协议的,需要引导客户按照下面方式进行设置

使用UDP方式:

export OPENCV_FFMPEG_CAPTURE_OPTIONS="rtsp_transport;udp”

执行应用 (如果用sudo执行,需要sudo -E把环境变量带过去)

UDP适用的环境:当网络带宽比较窄,比如4G/3G等移动通信系统,此时用udp比较合适 TCP适用的环境:网络带宽足够,对视频花屏要求比较高,对延时要求较小的应用场景,适合TCP

6.9. 如何获取rtsp中原来的timestamp

答:opencv中默认获取的rtsp时间戳是从0开始的,如果想获取rtsp中的原始时间戳,可以用环境变量进行控制, 然后按照问题1进行获取即可

export OPENCV_FFMPEG_CAPTURE_OPTOINS="keep_rtsp_timestamp;1"

6.10. 如何判断视频花屏的原因

答:这里提的视频花屏是长时间的花屏,对于偶尔的花屏有可能是网络数据传输错误导致的,此类不属于应用代码可控的方位。如果视频出现长时间的花屏,很大概率是由于视频帧读取不及时,导致内部缓存满以后,socket recv buffer溢出导致的。

  1. 将加大rmem_max到2M,如果此时花屏消失,说明应用的数据处理抖动很大,应该要加大buffer queue进行平滑

    echo 2097152 > /proc/sys/net/core/rmem_max
    
  2. 用netstat -na, 一般是一下格式,找到rtsp的那个端口(udp在应用中会有打印,tcp的话可以看目标rtsp地址),这里的Recv-Q, Send-Q在正常情况应该都是0,或者不满的,如果Recv-Q经常有很大的数,就说明overflow了。一般Send- Q不会出问题,如果这个也很大的话,那么很可能network driver驱动挂死了。

    Proto Recv-Q Send-Q Local Address Foreign Address State
    tcp 0 0 0.0.0.0:111 0.0.0.0:\* LISTEN
    

6.11. 无法连接rtsp?

答:可以通过ffmpeg固有命令来进行连接测试:(url为rtsp连接地址)

ffmpeg -rtsp_transport tcp -i url -f rawvideo -y /dev/null
或者
ffmpeg -rtsp_transport udp -i url -f rawvideo -y /dev/null
若以上无法连接成功,请检查网络。

6.12. 确认解码器是否能正常工作:(url为文件名或者rtsp连接地址)

答:

ffmpeg -i url -f rawvideo -y /dev/null

6.13. 确认解码器和vpp的OpenCV接口是否正常工作:

答:

vidmulti number_of_instances url1 url2

6.14. 解码不正确或者无法解码的最终调试手段

答:如果经常各种调试后,在现场仍然无法解决问题,可以通过打开环境变量,把问题发生前后的数据dump下来,供后续进行进一步分析

在PCIE模式下

export BMVID_PCIE_DUMP_STREAM_NUM=1000

dump的数据在/data/下(需要创建该文件夹并且有写权限),dump的数据根据core index和instance index存储。

在SOC模式下

echo “0 0 1000 100” > /proc/vpuinfo

(dump第1个core的第1个instance的码流数据)

这个配置会在两个文件之间循环存储1000帧数据,当问题发生的时候,把这两个发生前后的那个10000帧文件拷贝回来就可以。两个文件的存储位置在/data/core_%dinst%d_stream%d.bin. (注意:PCIE需要提前准备/data/目录的写权限)

6.15. 判断rtsp是否正常工作

答:

方法一:通过vlc播放视频(推荐),分别设置tcp,udp方式

方法二:在高密度服务器上用vidmutil播放,vidmutil默认是 udp方式,通过设置环境变量使用tcp方式。

export OPENCV_FFMPEG_CAPTURE_OPTIONS="rtsp_transport;tcp|buffer_size;1024000|max_delay;50000"
sudo -E ./vidmulti 8

6.16. 高密度服务器播放rtsp流出现断连情况验证

答:可以使用vlc播放相同的视频,在相同的时间下,看vlc播放是否有断连的情况,注意设置vlc的缓冲区大小。

6.17. 验证当前rtsp服务输出的视频是否有花屏

答:使用vlc播放视频,持续一段时间,看视频是否有花屏

6.18. 查看rtsp服务是否实时推流

答:通过rtspserver日志,查看当前播放的文件是否正在发送。

6.19. 对于cvQueryFrame等老的opencv接口支持状况

答:

有些客户采用旧版opencv的C接口,接口列表如下

void cvReleaseCapture( CvCapture** pcapture )

IplImage* cvQueryFrame( CvCapture* capture )

int cvGrabFrame( CvCapture* capture )

IplImage\* cvRetrieveFrame( CvCapture* capture, int idx )

double cvGetCaptureProperty( CvCapture* capture, int id )

int cvSetCaptureProperty( CvCapture* capture, int id, double value )

int cvGetCaptureDomain( CvCapture* capture)

CvCapture * cvCreateCameraCapture (int index)

CvCapture * cvCreateFileCaptureWithPreference (const char * filename, int apiPreference)

CvCapture * cvCreateFileCapture (const char * filename)

对于这些接口,大部分都是支持的,只有返回值是iplImage的接口无法支持,这是因为我们硬件底层的ion内存类型是保存在MAT的uMatData类型中的,而iplIMage类型没有uMatData数据结构。

因此对于客户目前使用 cvQueryFrame接口的,建议客户基于cap.read接口封一个返回值为Mat数据的C函数接口,不要直接调用opencv老版的接口。

6.20. 对于VPP硬件不支持的YUV格式转换,采取什么样的软件方式最快?

答:

建议采用内部增强版版本的libyuv。

相比较原始版本,增加了许多NEON64优化过的格式转换API函数。其中包含许多JPEG YCbCr相关的函数。

位置:middleware-soc/bm_opencv/3rdparty/libyuv/

6.21. OpenCV中的BGR格式,在libyuv中对应的那个格式?OpenCV中的RGB格式呢?

答:

  • OpenCV中的BGR格式,在libyuv中对应的格式为RGB24

  • OpenCV中的RGB格式,在libyuv中对应的格式为RAW。

6.22. 若是采用libyuv处理JPEG方面的输出或者输入,需要注意什么事项?

答:

若是处理jpeg方面的输出或者输入,需要使用J400,J420,J422,J444等字样的函数,不然输出结果会有色差。

原因是JPEG的格式转换矩阵跟视频的不一样。

6.23. 高密度及小盒子支持gb28181 协议,部署步骤如下

答:

  1. 启动sip代理

    sip代理部署在外部x86或者主控板上,sip代理配置文件[GB28181.cfg]说明参考24

    启动命令,

    ./ctrlCenter 192.168.193.62 8081
    

    ctrlCenter为可执行程序。

    192.168.193.62 是本机ip。

    8081 是本机端口号,给web访问用。

    启动成功打印日志:

    ~/sip_proxy/ctrlCenter/build$ ./ctrlCenter 192.168.193.62 8089
    10:13:36.572 critsec !Mutex created
    10:13:36.572 critsec !Mutex: thread thr0x7f6cb6bcc740 is waiting (mutex owner=)
    10:13:36.572 critsec Mutex acquired by thread thr0x7f6cb6bcc740 (level=1)
    10:13:36.572 critsec Mutex released by thread thr0x7f6cb6bcc740 (level=0)
    10:13:36.572 critsec Mutex: thread thr0x7f6cb6bcc740 is waiting (mutex owner=)
    10:13:36.572 critsec Mutex acquired by thread thr0x7f6cb6bcc740 (level=1)
    10:13:36.572 critsec Mutex released by thread thr0x7f6cb6bcc740 (level=0)
    10:13:36.572 os_core_unix.c pjlib 2.5.5 for POSIX initialized
    ip addr 192.168.193.62
    http start at port 8089
    
  2. 相机或下及平台注册到sip代理

    具体注册方式参考下级平台或相机配置。

    设备注册成功打印日志:

    UserAgentRegistration 34020000001310101202
    -----------[UA 34020000001310101202 register] ---------
    new device id is 34020000001310101202
    
  3. 获取前端设备列表

    http://192.168.193.62:8081/sipproxy?type=getdevicelist

    返回结果

    {"devidelist": [{"id": "34010000001310000009"}{"id": "34010000001310000010"}{"id": "34020000001310101202"}]}
    

    34010000001310000009等为设备20位编码。

  4. 高密度配置端口映射,若为小盒子跳过此步

    配置端口映射,播放一路国标流需要映射两个UDP端口,并且端口映射中out_start_port与in_start_port要相同 。gb28181地址中的【媒体接收端的视频流端口】需要做端口映射,并且需要做一个对应rtcp的端口映射【媒体接收端的视频流端口+1】。

    具体配置方法参考[内部路由器配置使用说明]

  5. 设备上获取 国标视频流

    与打开rtsp流的方式相同

    • 方式一:可以在高密度核心板或小盒子上输入命令

    udp实时流:

    sudo /system/bin/vidmulti  1 gb28181://34020000002019000001:123456@35.26.240.99:5666?deviceid=35018284001310090010#localid=12478792871163624979#localip=172.10.18.201#localmediaport=20108
    

    udp回放流:

    sudo /system/bin/vidmulti  1 gb28181_playback://34020000002019000001:123456@35.26.240.99:5666?deviceid=35018284001310090010#devicetype=3#localid=12478792871163624979#localip=172.10.18.201#localmediaport=20108#begtime=20191018160000#endtime=20191026163713
    

    tcp实时流:

    sudo /system/bin/vidmulti 1 gb28181://34020000002019000001:123456@35.26.240.99:5666?deviceid=35018284001310090010#localid=12478792871163624979#localip=172.10.18.201
    

    tcp回放流:

    sudo /system/bin/vidmulti  1 gb28181_playback://34020000002019000001:123456@35.26.240.99:5666?deviceid=35018284001310090010#devicetype=3#localid=12478792871163624979#localip=172.10.18.201#begtime=20191018160000#endtime=20191026163713
    

    gb28181url地址参数说明参考 21

    • 方式二:与rtsp地址使用用法的,需要获取国标流时,只需把rtsp地址换成gb28181url。

6.24. 配置文件[GB28181.cfg]说明

答:

#local info

localID=34020000002000000001      sip代理的20位编码
localRealm=34020000
localPasswd=123456                         sip代理的密码
localIp=192.168.193.62                     sip代理的ip
localPort=5070                                  sip代理端口号

6.25. ffmpeg&opencv 支持 gb28181 协议,传入的url地址形式如下

答:

udp实时流地址

gb28181://34020000002019000001:123456@35.26.240.99:5666?deviceid=35018284001310090010#localid=12478792871163624979#localip=172.10.18.201#localmediaport=2010834020000002019000001:123456@35.26.240.99:5666:sip服务器国标编码:sip服务器的密码@sip服务器的ip地址:sip服务器的port
deviceid:前段设备20位编码
localid:本地20位编码,可选项
localip:本地ip,可选项. 不设置会获取 eth0 的ip,如果没有eth0需要手动设置
localmediaport:媒体接收端的视频流端口,需要做端口映射,映射两个端口(rtp:11801,rtcp:11802),两个端口映射的in和out要相同.同一个核心板端口不可重复。

udp回放流地址

gb28181_playback://34020000002019000001:123456@35.26.240.99:5666?deviceid=\35018284001310090010#devicetype=3#localid=12478792871163624979#localip=172.10.18.201#localmediaport=20108#begtime=20191018160000#endtime=2019102616371334020000002019000001:123456@35.26.240.99:5666:sip服务器国标编码:sip服务器的密码@sip服务器的ip地址:sip服务器的port
deviceid:前段设备20位编码
devicetype:录像存储累类型
localid:本地20位编码,可选项. 不设置会获取 eth0 的ip,如果没有eth0需要手动设置
localip:本地ip,可选项
localmediaport:媒体接收端的视频流端口,需要做端口映射,映射两个端口(rtp:11801,rtcp:11802),两个端口映射的in和out要相同.同一个核心板端口不可重复。
begtime:录像起始时间
endtime:录像结束时间

tcp实时流地址

gb28181://34020000002019000001:123456@35.26.240.99:5666?deviceid=35018284001310090010#localid=12478792871163624979#localip=172.10.18.20134020000002019000001:123456@35.26.240.99:5666:sip服务器国标编码:sip服务器的密码@sip服务器的ip地址:sip服务器的port
deviceid:前段设备20位编码
localid:本地20位编码,可选项
localip: 本地ip,是可选项.不设置会获取 eth0 的ip,如果没有eth0需要手动设置

tcp回放流地址

gb28181_playback://34020000002019000001:123456@35.26.240.99:5666?deviceid=35018284001310090010#devicetype=3#localid=12478792871163624979#localip=172.10.18.201#begtime=20191018160000#endtime=2019102616371334020000002019000001:123456@35.26.240.99:5666:sip服务器国标编码:sip服务器的密码@sip服务器的ip地址:sip服务器的port
deviceid:前段设备20位编码
devicetype :录像存储累类型
localid :本地20位编码,可选项
localip :本地ip,可选项. 不设置会获取 eth0 的ip,如果没有eth0需要手动设置
begtime :录像起始时间
endtime :录像结束时间

注意

  1. 流媒体传输默认是udp方式,如果使用tcp方式获取实时流或回放流,需要显示的指定。

    Ffmpeg指定tcp方式为接口调用 通过av_dict_set设置 gb28181_transport_rtp 为tcp。

    Opencv指定方式是设置环境变量

    export OPENCV_FFMPEG_CAPTURE_OPTIONS="gb28181_transport_rtp;tcp"
    
  2. 如果使用udp方式外部无法访问到内部ip/port,localmediaport需要做端口映射,端口映射需要两个 rtp和rtcp。

6.26. 现在opencv中默认是使用ION内存作为MAT的data空间,如何指定Mat对象基于system memory内存去创建使用?

答:

using namespace cv;

Mat m; m.allocator = m.getDefaultAllocator();     // get system allocator

然后就可以正常调用各种mat函数了,如m.create() m.copyto(),后面就会按照指定的allocator来分配内存了。

m.allocator = hal::getDefaultAllocator();  // get ion allocator

就又可以恢复使用ION内存分配器来分配内存。

6.27. FFMPEG JPEG example for encode and transcode.

答:

  • a jpeg encoder example for command line

    ffmpeg -c:v jpeg_bm -i src/5.jpg -c:v jpeg_bm  -is_dma_buffer 1 -y 5nx.jpg
    
  • mjpeg transcoder examples for command line

    ffmpeg -c:v jpeg_bm -num_extra_framebuffers 2 -i in_mjpeg.avi -c:v jpeg_bm -is_dma_buffer 1 -y test_avi.mov
    
    ffmpeg -c:v jpeg_bm -num_extra_framebuffers 2 -i in_mjpeg.mov -c:v jpeg_bm -is_dma_buffer 1 -y test_mov.mov
    

6.28. How to read bitstream from input buffer in FFMPEG?

答:

There is an example in FFMPEG source code. bm_ffmpeg/doc/examples/avio_reading.c (or http://www.ffmpeg.org/doxygen/trunk/doc_2examples_2avio_reading_8c-example.html)

This case makes libavformat demuxer access media content through a custom AVIOContext read callback instread of file, rtsp, etc protocols defined in FFMPEG.

This is an example in middleware-soc about using avio + jpeg_bm to decode still jpeg picture. (bm_ffmpeg/doc/examples/avio_decode_jpeg.c)

6.29. 从内存读取图片,用AVIOContext *avio =avio_alloc_context(),以及avformat_open_input()来初始化,发现初始化时间有290ms;但是如果从本地读取图片,只有3ms。为啥初始化时间要这么长?怎样减少初始化时间?

答:

ret = avformat_open_input(&fmt_ctx, NULL, NULL, NULL);

这里是最简单的调用。因此,avformat内部会会读取数据,并遍历所有的数据,来确认avio中的数据格式。

若是避免在这个函数中读取数据、避免做这种匹配工作。在已经知道需要使用的demuxer的前提下,譬如,已知jpeg的demuxer是mjpeg,可将代码改成下面的试试。

AVInputFormat\* input_format = av_find_input_format("mjpeg");

ret = avformat_open_input(&fmt_ctx, NULL, input_format, NULL);

6.30. how to know the jpeg demuxer name supported in FFMPEG?

答:

root@linaro-developer:~# ffmpeg -demuxers | grep jpeg
D jpeg_pipe piped jpeg sequence
D jpegls_pipe piped jpegls sequence
D mjpeg raw MJPEG video
D mjpeg_2000 raw MJPEG 2000 video
D mpjpeg MIME multipart JPEG
D smjpeg Loki SDL MJPEG

6.31. how to know all bm hardware decoder names supported in FFMPEG?

答:

root@linaro-developer:/home/sophgo/test# ffmpeg -decoders | grep _bm
V….. avs_bm bm AVS decoder wrapper (codec avs)
V….. cavs_bm bm CAVS decoder wrapper (codec cavs)
V….. flv1_bm bm FLV1 decoder wrapper (codec flv1)
V….. h263_bm bm H.263 decoder wrapper (codec h263)
V….. h264_bm bm H.264 decoder wrapper (codec h264)
V….. hevc_bm bm HEVC decoder wrapper (codec hevc)
V….. jpeg_bm BM JPEG DECODER (codec mjpeg)
V….. mpeg1_bm bm MPEG1 decoder wrapper (codec mpeg1video)
V….. mpeg2_bm bm MPEG2 decoder wrapper (codec mpeg2video)
V….. mpeg4_bm bm MPEG4 decoder wrapper (codec mpeg4)
V….. mpeg4v3_bm bm MPEG4V3 decoder wrapper (codec msmpeg4v3)
V….. vc1_bm bm VC1 decoder wrapper (codec vc1)
V….. vp3_bm bm VP3 decoder wrapper (codec vp3)
V….. vp8_bm bm VP8 decoder wrapper (codec vp8)
V….. wmv1_bm bm WMV1 decoder wrapper (codec wmv1)
V….. wmv2_bm bm WMV2 decoder wrapper (codec wmv2)
V….. wmv3_bm bm WMV3 decoder wrapper (codec wmv3)

6.32. How to show the decoder info, for example, jpeg_bm, in FFMPEG?

答:

root@linaro-developer:/home/sophgo/test# ffmpeg -h decoder=jpeg_bm
Decoder jpeg_bm [BM JPEG DECODER]:
General capabilities: avoidprobe
Threading capabilities: none
jpeg_bm_decoder AVOptions:
-bs_buffer_size <int> .D.V….. the bitstream buffer size (Kbytes) for bm jpeg decoder (from 0 to INT_MAX) (default 5120)
-chroma_interleave <flags> .D.V….. chroma interleave of output frame for bm jpeg decoder (default 0)
-num_extra_framebuffers <int> .D.V….. the number of extra frame buffer for jpeg decoder (0 for still jpeg, at least 2 for motion jpeg) (from 0 to INT_MAX) (default 0)

6.33. How to show the encoder info, for example, jpeg_bm, in FFMPEG?

答:

root@linaro-developer:/home/sophgo/test# ffmpeg -h encoder=jpeg_bm
Encoder jpeg_bm [BM JPEG ENCODER]:
General capabilities: none
Threading capabilities: none
Supported pixel formats: yuvj420p yuvj422p yuvj444p
jpeg_bm_encoder AVOptions:
-is_dma_buffer <flags> E..V….. flag to indicate if the input frame buffer is DMA buffer (default 0)

6.34. A jpeg encoder example for calling api function

答:

AVDictionary* dict = NULL;

av_dict_set_int(&dict, "is_dma_buffer", 1, 0);// this means the input frame for encoder is as that in note 2.

ret = avcodec_open2(pCodecContext, pCodec, &dict);

6.35. example to set decoder jpeg_bm when calling ffmpeg api for decoding still jpeg picture

答:

AVDictionary* dict = NULL;

/* The output of bm jpeg decoder is chroma-interleaved,for example, NV12 */

av_dict_set_int(&dict, "chroma_interleave", 1, 0);

/* The bitstream buffer is 3100KB(less than 1920x1080x3),
/* assuming the max dimension is 1920x1080 */
av_dict_set_int(&dict, "bs_buffer_size", 3100, 0);
/* Extra frame buffers: 0 for still jpeg, at least 2 for mjpeg */
av_dict_set_int(&dict, "num_extra_framebuffers", 0, 0);

ret = avcodec_open2(pCodecContext, pCodec, &dict);

6.36. example to set decoder jpeg_bm when calling ffmpeg api for decoding motion jpeg picture

答:

AVDictionary* dict = NULL;

/* The output of bm jpeg decoder is chroma-separated, for example, YUVJ420 */

av_dict_set_int(&dict, "chroma_interleave", 0, 0);

/* The bitstream buffer is 3100KB,
/* assuming the max dimension is 1920x1080 */
av_dict_set_int(&dict, "bs_buffer_size", 3100, 0);
/* Extra frame buffers: 0 for still jpeg, at least 2 for mjpeg */
av_dict_set_int(&dict, "num_extra_framebuffers", 2, 0);

ret = avcodec_open2(pCodecContext, pCodec, &dict);

6.37. BM1684解码性能对于H264/H265有差别吗?如果调整码率的话,最多可以解多少路呢?有没有对应的数据参考?

答:

264,265是解码路数相同的。

码率对解码帧率会有影响,这个变化就需要实测,我们说的BM1684解码能达到960fps是针对监控码流而言的,这类监控码流没有B帧,场景波动较小,码率基本在2~4Mbps。如果是电影或者其他码率很高的,比如10Mbps,20Mbps甚至更多,是会有明显影响的,具体多大这个需要实测。

分辨率对于解码帧率的影响,是可以按照比例来换算的。我们说的960fps是针对1920x1080 HD分辨率而言的。

6.38. 是否可以通过抽帧来提高BM1684的解码路数

答:

我们opencv中提供的抽帧,是在解码出来的结果中做的,并不是只解I/P帧的抽帧概念。这里的抽帧解码主要是保证出来帧数的均匀,使得后续的分析处理是等间隔的进行,这是为后续模型分析比较复杂的时候,不能达到每帧都检测而设计的解决方案,但并不能达到增加解码路数的效果。

这里顺便解释下,为什么不提供只解I/P帧的抽帧功能。如果只解I、P帧的话,抽帧的间隔就完全取决于码流的编码结构,这样是比较难控制住性能,比如监控码流中的没有B帧,那其实就相当于没有抽帧了。如果客户可以控制编码端,那更切合实际的做法是直接降低编码端的编码帧率,比如降到15fps,那样解码路数就可以直接 提升;反之,如果客户没有办法控制编码端,那么同样的,只解IP帧的抽帧方式就也无法达到增加解码路数的效果。

6.39. 是否支持avi, f4v, mov, 3gp, mp4, ts, asf, flv, mkv封装格式的H264/H265视频解析?

答:我们使用ffmpeg对这些封装格式进行支持,ffmpeg支持的我们也支持。经查,这些封装格式ffmpeg都是支持的。但是封装格式对于H265/264的支持,取决于该封装格式的标准定义,比如flv标准中对h265就没有支持,目前国内的都是自己扩展的。

6.40. 是否支持png, jpg, bmp, jpeg等图像格式

答:Jpg/jpeg格式除了有jpeg2000外,自身标准还有很多档次,我们采用软硬结合的方式对其进行支持。对jpeg baseline的除了极少部分外,都用硬件加速支持,其他的jpeg/jpg/bmp/png采用软件加速的方式进行支持。主要的接口有opencv/ffmpeg库。

6.41. Valgrind内存检查为什么有那么多警告,影响到应用的调试了

答:

我们的版本发布每次都会用valgrind检查一遍内存泄漏问题,如果有内存泄露问题我们会检查修正的。之所以没有去掉有些警告,是因为这些警告大部分都是内存没有初始化,如果对这些内存每个都加上初始化,会明显导致速度性能下降,而且我们确认后续操作是在硬件对其赋值后再进行的,对于此类警告,我们就不会去消除。

为了避免警告过多对上层应用调试造成影响,建议使用valgrind的suppression功能,可以通过过滤配置文件,来避免我们模块产生的valgrind警告,从而方便上层应用调试自身的程序。

6.42. 使用opencv的video write编码,提示物理内存(heap2)分配失败

答:

确认heap2 设置的大小,如果heap2 默认大小是几十MB,需要设置heap2 size为1G。目前出厂默认配置是1G。

Update_boot_info 可查询heap2 size

update_boot_info –heap2_size=0x40000000 –dev=0x0 设置heap2 size为1G。设置后重装驱动。

6.43. Bm_opencv的imread jpeg解码结果和原生opencv的imread jpeg结果不同,有误差

答:是的。原生opencv使用libjpeg-turbo,而bm_opencv采用了bm168x芯片中的jpeg硬件加速单元进行解码。

误差主要来自解码输出YUV转换到BGR的过程中。1)YUV需要上采样到YUV444才能进行BGR转换。这个upsample的做法没有标准强制统一,jpeg-turbo提供了默认Fancy upsample,也提供了快速复制上采样的算法,原生opencv采用默认的fancy upsample;而BM168x硬件加速单元采用快速复制的算法。2)YUV444到BGR的转换是浮点运算,浮点系数精度的不同会有+/-1的误差。其中1)是误差的主要来源。

这个误差并不是错误,而是双方采用了不同的upsample算法所导致的。

6.44. 如何查看vpu/jpu的内存、使用率等状态

答:

在pcie模式下,可以用下面的方法查看:

cat /proc/bmsophon/card0/bmsophon0/media

cat /proc/bmsophon/card0/bmsophon0/jpu

在soc模式下,可以用下面的方法查看:

cat /proc/vpuinfo

cat /proc/jpuinfo

6.45. 视频支持32路甚至更多的时候,报视频内存不够使用,如何优化内存使用空间

答:

在PCIE板卡下,视频内存有3G,一般来说支持32路甚至更多的路数都绰绰有余。但在soc模式下,视频内存的默认配置是2G,正常使用在16路是绰绰有余的,但在32路视频需要在应用层面上仔细设计,不能有任何的浪费。

如果解码使用的是FFMPEG框架,首先保证视频输出格式使用压缩格式,即output_format 101。Opencv框架的话,内部已经默认使用压缩格式了;

其次如果应用在获取到解码输出avFrame后,并不是直接压入队列,而是转换到RGB或者非压缩数据后再缓存的话,可以用av_dict_set extra_frame_buffer_num为1(默认为5)。Opencv内部在最新版本中会默认优化。

最后,如果以上优化过后,仍然不够的话,在soc模式下可以考虑更改dtb设置,给视频挪用分配更多的内存,当然相应的,其他模块就要相应的减少内存。这个要从系统角度去调配。

6.46. Opencv中mat是如何分配设备内存和系统内存的?

答:

因为受设计影响,这个问题细节比较多,主要从三方面能解释。

  1. 在soc模式下,设备内存和系统内存是同一份物理内存,通过操作系统的ION内存进行管理,系统内存是ION内存的mmap获得。在pcie模式下,设备内存和系统内存是两份物理内存,设备内存指BM168x卡上的内存,系统内存指服务器上操作系统内存。如果用户想只开辟系统内存,和开源opencv保持一致,可 以参见FAQ26的回答。

  2. 在sophon opencv中默认会同时开辟设备内存和系统内存,其中系统内存放在mat.u->data或mat.data中,设备内存放在mat.u->addr中。只有以下几种情况会不开辟设备内存,而仅提供系统内存:

    • 当data由外部开辟并提供给mat的时候。即用以下方式声明的时候:

      Mat mat(h, w, type, data); 或 mat.create(h, w, type, data);
      
    • 在soc模式下,当type不属于(CV_8UC3, CV_32FC1, CV_32FC3)其中之一的时候。这里特别注意CV_8UC1是不开辟的,这是为了保证我们的opencv能够通过开源opencv的opencv_test_core的一致性验证检查。

    • 当宽或者高小于16的时候。因为这类宽高,硬件不支持

  3. 在BM1682和BM1684的SOC模式下,mat分配的CV_8UC3类型的设备内存会自动做64对齐,即分配的内存大小一定是64对齐的(注意:仅对soc模式的CV_8UC3而言,且仅对BM1684/BM1682芯片)。在PCIE模式下,分配的内存是byte对齐的。

6.47. ffmpeg中做图像格式/大小变换导致视频播放时回退或者顺序不对的情况处理办法

答:ffmpeg在编码的时候底层维护了一个avframe的队列作为编码器的输入源,编码期间应保证队列中数据有效,如果在解码后需要缩放或者像素格式转换时候需要注意送进编码器的avframe的数据有效和释放问题。

在例子ff_bmcv_transcode中从解码输出src-avframe转换成src-bm_image然后做像素格式转换或者缩放为dst-bm_image最后转回dst-avframe 去编码的过程中src-avframe、src-bm_image的设备内存是同一块,dst- avframe、dst-bm_image的设备内存是同一块。在得到dst-bm_image后即可释放src_avframe和src-bm_image的内存(二者释放其中一个即可释放设备内存),作为编码器的输入dts-bm_image在转换成dst- avframe之后其设备内存依然不能被释放(常见的异常情况是函数结束dts-bm_image的引用计数为0导致其被释放),如果dst-bm_image被释放了此时用dst-avframe去编码结果肯定会有问题。

解决方法是dst-bm_image的指针是malloc一块内存,然后将其传给av_buffer_create,这样就保证在函数结束的时候dst-bm_image引用计数不会减1,释放的方法是将malloc的dst-bm_image指针通过av_buffer_create传给释放的回调函数,当dst- avframe引用计数为0的时候会调用回调函数将malloc的指针和dst-bm_image的设备内存一起释放。详见例子ff_bmcv_transcode/ff_avframe_convert.cpp。

6.48. 启动设备首次执行某个函数慢,重启进程再次运行正常

现象:设备上电后第一次执行程序,函数处理时间长,再次执行程序,运行正常。

解决:先做个验证,如果不重启可复现,就说明是文件cache导致的变慢。

  1. 上电后第一次执行慢,第二次执行正常,之后进入root用户

  2. 清除cache echo 3 > /proc/sys/vm/drop_caches

  3. 再次执行程序,运行慢,即可确定是cache导致的。

6.49. Opencv mat创建失败,提示“terminate called after throwing an instance of ‘cv::Exception’ what(): OpenCV(4.1.0) …… matrix.cpp:452: error: (-215:Assertion failed) u != 0 in function ‘creat’”

答:

这种错误主要是设备内存分配失败。失败的原因有两种:

  1. 句柄数超过系统限制,原因有可能是因为句柄泄漏,或者系统句柄数设置过小,可以用如下方法确认:

    查看系统定义的最大句柄数:

    ulimit -n
    

    查看当前进程所使用的句柄数:

    lsof -n|awk ‘{print $2}’|sort|uniq -c|sort -nr|more
    
  2. 设备内存不够用。可以用如下方法确认:

    • SOC模式下

      cat /sys/kernel/debug/ion/bm_vpp_heap_dump/summary
      
    • PCIE模式下, bm-smi工具可以查看设备内存空间

解决方案:在排除代码本身的内存泄漏或者句柄泄漏问题后,可以通过加大系统最大句柄数来解决句柄的限制问题:ulimit -HSn 65536

设备内存不够就需要通过优化程序来减少对设备内存的占用,或者通过修改dts文件中的内存布局来增加对应的设备内存。详细可以参考SM5用户手册中的说明。

6.50. opencv转bm_image的时候,报错“Memory allocated by user, no device memory assigned. Not support BMCV!”

答:这种错误通常发生在soc模式下,所转换的Mat只分配了系统内存,没有分配设备内存,而bm_image要求必须有设备内存,因此转换失败。而在pcie模式下,接口内部会自动分配设备内存,因此不会报这个错误。

会产生这类问题的Mat通常是由外部分配的data内存attach过去的,即调用Mat(h, w, data) 或者Mat.create(h,w, data)来创建的,而data!=NULL,由外部分配。

对于这种情况,因为bm_image必须要求设备内存,因此解决方案有

  1. 新生成个Mat,默认创建设备内存,然后用copyTo()拷贝一次,把数据移到设备内存上,再重新用这个Mat来转成bm_image

  2. 直接创建bm_image,然后用bm_image_copy_host_to_device,将Mat.data中的数据拷贝到bm_image的设备内存中。

6.51. Opencv用已有Mat的内存data,宽高去创建新的Mat后,新Mat保存的图像数据错行,显示不正常

答:保存的图像错行,通常是由于Mat中step信息丢失所造成。

一般用已有Mat去生成一个新Mat,并且要求内存复用时,可以直接赋值给新的Mat来简单实现,如 Mat1 = Mat2.

但在某些情况下,比如有些客户受限于架构,函数参数只能用C风格的指针传递,就只能用Mat中的data指针,rows,cols成员来重新恢复这个Mat。 这时候就需要注意step变量的设置,在默认情况下是AUTO_STEP配置,即每行数据没有填充数据。但是在很多种情况下,经过opencv处理后,会导致每行出现填充数据。如,

  1. soc模式下,我们的Mat考虑执行效率,在创建Mat内存时每行数据会做64字节对齐,以适配硬件加速的需求(仅在soc模式下)

  2. opencv的固有操作,如这个Mat是另一个Mat的子矩阵(即rect的选定区域),或者其他可能导致填充的操作。

因此,按照opencv定义,通用处理方式就是在生成新的Mat的时候必须指定step,如下所示:

cv::Mat image_mat = cv::imread(filename,IMREAD_COLOR,0);
cv::Mat image_mat_temp(image_mat.rows,image_mat.cols,CV_8UC3,image_mat.data,image_mat.step[0]);
cv::imwrite("sophgo1.jpg",image_mat_temp);

6.52. 在soc模式下客户用ffmpeg解码时拿到AVframe将data[0-3] copy到系统内存发现copy时间是在20ms左右而相同数据量在系统内存两块地址copy只需要1-3ms

答:上述问题的原因是系统在ffmpeg中默认是禁止cache的,因此用cpu copy性能很低,使能cache就能达到系统内存互相copy同样的速度。可以用以下接口使能cache.

av_dict_set_int(&opts, "enable_cache", 1, 0);

但是这样直接copy数据保存是非常占用内存、带宽和cpu算力的,我们推荐采用零拷贝的方式来实现原始解码数据的保存:

  1. 推荐使用 extra_frame_buffer_num 参数指定增大硬件帧缓存数量,可以根据自己的需要选择缓存帧的数量。 这个方式的弊端,一个是占用解码器内存,可能减少视频解码的路数;另一个是当不及时释放,当缓存帧全部用完时,会造成视频硬件解码堵塞。

    av_dict_set_int(&opts, "extra_frame_buffer_num", extra_frame_buffer_num, 0);
    
  2. 推荐使用 output_format参数设置解码器输出压缩格式数据,然后使用vpp处理输出非压缩yuv数据(当需要缩放,crop时,可以同步完成), 然后直接零拷贝引用非压缩yuv数据。这种方式不会影响到硬件解码性能,并且可以缓存的数据空间也大很多。

    av_dict_set_int(&opts, "output_format", 101, 0);
    

6.53. 在opencv VideoCapture 解码视频时提示: maybe grab ends normally, retry count = 513

上述问题是因为在VideoCapture存在超时检测,如果在一定时间未收到有效的packet则会输出以上log,此时如果视频源是网络码流可以用vlc拉流验证码流是否正常,如果是文件一般是文件播放到末尾需调用VidoeCapture.release后重新VideoCapture.open

6.54. [问题分析]客户反馈碰到如下错误提示信息”VPU_DecRegisterFrameBuffer failed Error code is 0x3”, 然后提示Allocate Frame Buffer内存失败。

这个提示信息表示:分配的解码器缓存帧个数,超过了最大允许的解码帧。导致这个问题的原因有可能是:

  1. 不支持的视频编码格式,比如场格式,此时可以用FAQ14的方法,把码流数据录下来,提交给我们分析。

  2. 设置了过大的extra_frame_buffer_num。理论上,extra_frame_buffer_num不能超过15,超过了以后就有可能不能满足标准所需的最大缓存帧数。因为大部分编码码流并没有用到最大值,所以extra_frame_buffer_num大于15的时候,对大部分码流仍然是可以工作的。

目前发现可能导致这个问题的原因有上述两种,后续有新的案例继续增补

6.55. SOC模式下,opencv在使用8UC1 Mat的时候报错,而当Mat格式为8UC3的时候,同样的程序完全工作正常。

这个问题碰到的客户比较多,这次专门设立一个FAQ以便搜索。其核心内容在FAQ46 “Opencv中mat是如何分配设备内存和系统内存的”有过介绍,可以继续参考FAQ46

在soc模式下,默认创建的8UC1 Mat是不分配设备内存的。因此当需要用到硬件加速的时候,比如推理,bmcv操作等,就会导致各种内存异常错误。

解决方案可以参看FAQ26 “如何指定Mat对象基于system memory内存去创建使用”, 指定8UC1 Mat在创建的时候,内部使用ion分配器去分配内存。如下所示。

cv::Mat gray_mat;
gray_mat.allocator = hal::getAllocator();
gray_mat.create(h, w, CV_8UC1);

6.56. 调用 bmcv_image_vpp_convert_padding 接口时,报缩放比例超过32倍的错:“vpp not support: scaling ratio greater than 32”。

bm1684的vpp中硬件限制图片的缩放不能超过32倍。即应满足 dst_crop_w <= src_crop_w * 32, src_crop_w <= dst_crop_w * 32, dst_crop_h <= src_crop_h * 32 , src_crop_h <= dst_crop_h * 32。

此问题原因可能是:

  1. 输入 crop_rect 中的crop_w, crop_h 与 输出 padding_attr 中的dst_crop_w ,dst_crop_h 缩放比例超过了32倍。

  2. crop_rect,padding_attr 值的数量应与 output_num的数量一致。

6.57. [问题分析]程序提示“VPU_DecGetOutputInfo decode fail framdIdx xxx error(0x00000000) reason(0x00400000), reasonExt(0x00000000)”是可能什么问题,这里reason的具体数值可能不同

这个提示通常是由码流错误造成的,提示的含义是第xxx帧解码错误,错误原因为….。这里具体原因对于上层应用来说,不用关心,只需知道这是由码流错误导致的即可。

进一步分析,导致码流错误的原因通常可以分为两类,我们要有针对的进行处理。因为一旦频繁出现这种提示,说明解码出来的数据是不正确的,这时候有可能是各种马赛克或者图像花,对于后续的处理会造成各种异常情况,所以我们必须尽量减少这种情况的发生。

  1. 网络情况导致的丢包。这时候可以用我们的测试程序vidmulti验证下,如果vidmulti没有解码错误,那么可以排除这种情况。如果确认网络丢包的话,要分辨下是否网络带宽本身就不够,如果本身带宽不够,那没有办法,只能降低视频码流的码率。如果带宽是够的,要检查下网线。当码流连接数超过20多路的时候,这时候有可能已经超出百兆了,这时网线也必须换到CAT6,与千兆网相匹配

  2. 解码性能达到上限造成丢包。这种情况发生在流媒体环境中,对于文件播放是不会发生的。这时也可以用我们的vidmulti跑一下,作为比较。如果vidmulti也发生错误,说明性能确实到了上限了,否则说明应用本身还有优化的空间。

6.58. [问题分析]程序提示“coreIdx 0 InstIdx 0: VPU interrupt wait timeout”,这是怎么回事?

这个提示表示视频解码或者编码中断超时。这个提示只是警告,会再次尝试,因此只要没有连续出现就可以忽略。这种情况一般是由解码数据错误导致或者负荷过重产生的。例如在板卡情况下,由于板卡数据交换过于频繁,造成解码或者编码数据传输堵塞,使得中断超时。

6.59. 采用TCP传输码流的时候如果码流服务器停止推流,ffmpeg阻塞在av_read_frame

这是因为超时时间过长导致的,可以用一下参数设置超时时间减短。

av_dict_set(&options, “stimeout”, “1000000”, 0);

6.60. [问题分析]当用ffmpeg jpeg_bm解码超大JPEG图片的时候,有时候会报“ERROR:DMA buffer size(5242880) is less than input data size(xxxxxxx)”,如何解决?

在用FFMPEG的jpeg_bm硬件解码器解码JPEG图片的时候,默认的输入buffer是5120K。在拿到JPEG文件前提前分配好输入缓存,在MJPG文件解码时可以避免频繁地创建和销毁内存。当出现默认输入buffer大小比输入jpeg文件小的时候,可以通过下面的命令来调大输入缓存。

av_dict_set_int(&opts, “bs_buffer_size”, 8192, 0); //注意: bs_buffer_size是以Kbyte为单位的

6.61. 调用 bmcv_image_vpp_basic 接口时,csc_type_t csc_type 和 csc_matrix_t* matrix该如何填?

bmcv中vpp在做csc 色彩转换时,默认提供了4组601系数和4组709系数, 如csc_type_t所示。

  1. csc_type可以填为CSC_MAX_ENUM, matrix填NULL,会默认配置 601 YCbCr与RGB互转系数。

  2. csc_type可以填csc_type_t中参数,如YCbCr2RGB_BT709,matrix填NULL,会按照所选类型配置对应系数。

  3. csc_type可以填CSC_USER_DEFINED_MATRIX,matrix填自定义系数。会按照自定义系数配置。

csc_matrix_t 中系数参考如下公式:

Y = csc_coe00 * R + csc_coe01 * G + csc_coe02 * B + csc_add0;

U = csc_coe10 * R + csc_coe11 * G + csc_coe12 * B + csc_add1;

V = csc_coe20 * R + csc_coe21 * G + csc_coe22 * B + csc_add2;

由于1684 vpp精度为10位,整数处理。

csc_coe 与 csc_add的计算方法为: csc_coe = round(浮点数 * 1024)后按整数取补码。

csc_coe取低13bit,即 csc_coe = csc_coe & 0x1fff,csc_add取低21bit,即 csc_add = csc_add & 0x1fffff。

举例如下:

floating-point coe matrix => fixed-point coe matrix

0.1826 0.6142 0.0620 16.0000 => 0x00bb 0x0275 0x003f 0x004000

6.62. [问题分析]不同线程对同一个bm_imag调用 bm_image_destroy 时,程序崩溃。

bm_image_destroy(bm_image image) 接口设计时,采用了结构体做形参,内部释放了image.image_private指向的内存,但是对指针image.image_private的修改无法传到函数外,导致第二次调用时出现了野指针问题。

为了使客户代码对于sdk的兼容性达到最好,目前不对接口做修改。 建议使用bm_image_destroy(image)后将 image.image_private = NULL,避免多线程时引发野指针问题。