JCHub

  • Home
  • Category
    • A/V
    • WebRTC
    • Beauty of Programming
    • Linux
    • Windows
    • Moments of Life
    • Campus Life
  • Reference
    • API Reference
    • Utilities
    • AV Test
    • Doc
  • Message Board
  • About
ffmpeg
A/V

ffmpeg视频编码YUV与AVFrame对应关系

最近群里有人问:NV12格式,怎么对应AVFrame中的data[0],data[1],data[2]。其实ffmpeg视频编码,YUV与AVFrame对应关系很简单。 在视频编码时,我们需要把YUV数据拷贝到AVFrame.data中,视频编码有硬件加速以及非硬件加速两种,所以对应关系也有两种。 硬件加速编码 指通过显卡进行硬件加速编码,例如指定vaapi进行编码。使用硬件加速编码时,YUV输入格式一般都是NV12,我做过的Intel以及Nvidia编码都是这样。之所以使用NV12格式,在Intel开发文档有这样说明: [crayon-69c68ea700c1d050333081/] 因为ffmpeg底层也是调用相关显卡SDK做硬件加速编码,所以我们把YUV数据拷贝给AVFrame时,也得按NV12格式。对于NV12格式,Y数据在最前,接着是UV交错排布,类似这样:YYYYYYYY UVUV,所以对于AVFrame,我们得把Y数据拷贝data[0],UV数据拷贝给data[1]。 [crayon-69c68ea700c2b796136070/] 非硬件加速编码 指通过CPU进行编码,例如指定libx264,libx265进行编码。对应关系就比较简单了,YUV三个分量数据依次对应data[0],data[1],data[2]。 [crayon-69c68ea700c30806951983/]

2019年3月24日 2comments 3710hotness 2likes Jeff Read all
A/V

Ubuntu vaapi-ffmpeg开发环境搭建

本篇文章有些内容比较过时,最新请参考:https://blog.jianchihu.net/intel-gpu-hw-video-codec-develop.html。包含CentOS以及Ubuntu的开发环境搭建 最近要在Linux上做编解码开发,为了成本考虑,没用NVIDIA的方案,用了Intel编解码方案。大家都知道Intel在Windows上有个Intel Media SDK的方案,比较常用,支持的CPU型号也多,在Linux上也有类似方案,叫做Intel Media Server Studio。但是Intel Media Server Studio支持的型号比较少,如下是官方文档说明的支持型号: [crayon-69c68ea7028a4869666637/] 不过还有一个叫做VA API(Video Acceleration API) 的方案,VA API是一个统一的编解码规范,类似Windows上的Dxva方案,主要由各大显卡厂商在驱动中实现。目前主要Intel与AMD实现这个VA API方案,不过AMD上支持的编解码特性比较少,也只是部分支持。对于Intel来说,基本上带集显的都支持VA API。所以为了成本,通用性考虑,我选用了这个VA API做Linux上的编解码开发。 如果基于原生VA API开发的话,比较复杂点,好在ffmpeg支持VA API,所以我们只需要编译支持VA API的ffmpeg即可。本篇文章主要讲下VA API开发环境的搭建,主要针对H264编解码。开发系统为ubuntu18.04.2 LTS server版本。需要注意的是,目前没看到好的显卡直通(passthrough)以及虚拟化方案,我们还是老实在实体机Linux开发,不能在虚拟机里,要不就用不了显卡硬件加速了。 基本库安装 [crayon-69c68ea7028af786664287/] VA API相关库驱动安装 Libva Libva是VA API的实现。 [crayon-69c68ea7028b3729055443/] intel-vaapi-driver 主要在我们的程序与Intel 集显之间起桥梁作用。传输打包的缓存以及命令到i965驱动(开源的intel集显驱动,已集成在Linux内核中),用于硬件加速的视频编解码,着色器处理等。 [crayon-69c68ea7028b7509814385/] libva-utils 提供一系列 VA API相关的测试。比如vainfo命令,可以用来检测我们的硬件支持哪些VA API编解码特性。 [crayon-69c68ea7028ba377138204/] 这一步安装完后,我们开始检测安装的成果,首先查看我们的显卡设备: [crayon-69c68ea7028bd981589021/] 可以看到我的电脑有两张显卡,一张独显,一张集显。下面开始通过vainfo命令验证显卡支持情况: [crayon-69c68ea7028c0070519147/] 可以看到我的AMD独显VA API支持很少,Intel集显基本都支持了。如果通过vainfo命令我们如上所示得到显卡支持情况,说明我们VA API相关驱动以及库安装成功了。下面介绍下支持VA API的ffmpeg的编译。 ffmpeg编译 参照官方编译,创建相关目录用于存放源码以及编译后的程序。 [crayon-69c68ea7028c4241518585/] 安装nasm与yasm。ffmpeg一些汇编程序用到。 [crayon-69c68ea7028c7422505888/] 编译安装libx264,用于软编H264。 [crayon-69c68ea7028ca533427122/] 下载最新的ffmepg源码,编译。 [crayon-69c68ea7028cd275746923/] 最后编译生成的静态库文件都在主目录ffmpeg_build里,用于我们的开发。ffmepg可执行程序在主目录bin下。运行ffmpeg程序,执行如下命令: [crayon-69c68ea7028d0217916627/] 可以看到我们编译的ffmppeg已经支持VA API了。 VA API编解码开发 ffmepg官方example有VA API使用教程,具体可以看官方示例代码。不过需要注意的是VA API编码时,输入的YUV格式必须是NV12,其他格式YUV得转为NV12格式。官方example:vaapi_encode.c有个AVFrame(sw_frame) ,用于存放我们输入的YUV数据,该AVFrame的data[0]用于存放Y数据,data[1]存放UV数据,由于输入格式是NV12,所以data[1]中UV数据的内存布局为:UVUVUVUV···UVUV。

2019年3月8日 0comments 3640hotness 22likes Jeff Read all
A/V

安防视频播放秒开优化

在视频播放中,播放器立即出图像(秒开)非常重要。能够极大提高用户的体验度。网上有很多关于直播行业秒开的优化经验,但是没有安防GB28181的,GB28181标准视频播放跟直播还是有些不一样的,下面说下一些GB28181标准视频秒开的优化经验。 直播行业视频封装传输遵循RTMP/HLS/DASH这些标准。安防摄像头视频播放大多遵循GBT28181。目前常见的视频编码格式有H264/H265/MPEG-4/Svac,通常都封装为PS流(Program Stream,封装标准参考iso13818-1 2.5小节)格式,然后作为负载封装成RTP包传输。在直播行业,视频流的采集主要是用户的手机或者PC设备,而安防行业视频流的采集来自于各大厂商的摄像头。 视频秒开需要从播放端,发送端以及网络传输考虑。视频秒开关键点是要立马获取到关键帧,后续视频帧的解码都得参考关键帧,有了关键帧,解码器才能立刻解码,然后出图像。 发送端 发送端一般分为两种:直连的设备以及流媒体服务器。直连设备是指播放端直接连接摄像机播放。 摄像机设置 由于视频来自摄像机,无论发送端是哪种,我们都要修改摄像机的关键帧间隔(GOP),一般设置2倍的帧率。这样确保在较短时间内,能够获取到关键帧播放,设置太短,会增大数据量,设置太长会造成等待关键帧时间太久,一旦出现丢包等异常,影响较大。如下图是某款海康摄像机配置,我们配置视频帧率为25帧,关键帧(I帧)间隔为2s,即50帧。 服务器设置 如果不是摄像机直连,通过我们自己的流媒体服务器的话就需要对我们的服务器做处理。这时我们需要做关键帧缓存,确保推给播放端的流第一帧就是关键帧。在互联网直播中通常用到了CDN,很多CDN厂商都会缓存一组GOP数据。安防行业一般都是私有网络,没有CDN,那服务端具体要怎么做呢? 服务端为每一路摄像机信号维护一个关键帧缓冲区,缓存最新的一组GOP数据(关键帧以及相关的一组P帧),每当该摄像机有新关键帧数据来时,都要更新这个缓冲区 当有播放端请求某摄像机信号时,先判断当前摄像机实时流是否是关键帧第一个包 是的话直接推实时流,同时更新缓冲区 不是的话就得从关键帧缓冲区拿数据推给播放端,直到实时流来了关键帧,此时切换到从实时流推流,然后更新缓冲区 播放端 解复用(Demux) Demux是指解析视频的封装格式,得到包含的音视频原始码流,Demux时间越短,就越快得到视频流,从而加快秒开速度。我们这里的Demux过程主要是解析RTP负载数据,对于每个RTP包,去除头部12字节头部数据后就是负载数据。由于国标视频基本都是封装为PS流格式,所以需要解复用PS流,从PS流里得到原始视频数据。对于PS流的Demux有两个方法,一个是自己写,PS流结构不是很复杂,1000行以内代码可以搞定,如果嫌麻烦,还有一个方法是使用ffmpeg,对于ffmpeg如何demux PS流,可以参考ffmpeg的avio_reading例子,通过探测流的方式demux PS流。 如果是自己写的Demux程序,我们在Demux PS流需要搜索各种头部,由于存在丢包等异常情况,所以搜索头部太久时需要做处理,丢弃无用的数据,避免耗时太久。由于PS中包长度都是用两字节表示,长度为2^16,所以我们可以设置一个值,比2^16大点,当搜索的字节数大于这个值还没搜索一个PS流的包头(0x000001开头),此时就要丢弃之前数据,处理新数据,因为之前数据很大可能丢包或其他问题。 如果是使用ffmpeg做PS流的Demux,有几点需要注意。由于ffmpeg Demux未知流时,需要探测一定大小数据,甚至会尝试解码未知流,这个过程如果不做优化会耗时很久。 ffmpeg通过AVIO方式探测流格式主要通过avformat_find_stream_info函数实现,我们可以通过设置AVFormatContext的probesize与max_analyze_duration限制ffmpeg探测大小与时长,提高Demux速度,我一般按如下设置: [crayon-69c68ea70c598754130605/] 限制流分析大小64K,时长2s。但有点需要注意的是如果送入播放端首帧视频不是关键帧avformat_find_stream_info很大可能性失败,因为ffmpeg在max_analyze_duration内如果获取不到关键帧数据基本会探测失败。所以使用ffmpeg探测PS流时需要确保首帧视频就是关键帧(视频重要参数都存储在关键帧内),这样在max_analyze_duration内才能获取到PS流信息。至于如何判断PS流的关键帧数据,这个很简单,可以根据PS流头部判断,PS流封装的关键帧都含有system_header(0x000001BB开头)与program_stream_map(0x000001BC开头)。 解码 Demux得到原始视频码流后就可以开始解码了,能硬解码就硬解码,硬解码速度会优于软解,尤其在解码路数多时。 渲染 解码得到YUV或RGB数据后,我们需要渲染到屏幕显示,这是最后一步了。渲染也存在是否硬件加速的区别。比如windows平台,优先D3D硬件加速渲染,充分发挥显卡的能力,加快渲染速度。如果前面解码是用硬解码,此时也必须硬件加速渲染。否则又要搬运显存中的硬解数据到内存,由于解码后的数据一般较大,所以这个过程很耗时,同时影响性能,提高CPU占用率。 网络传输 传输分为TCP与UDP。由于TCP的特性,所以TCP获取到首帧视频耗时长点,同时延时也大。所以优先UDP传输。但是UDP传输又存在丢包,乱序等问题,造成视频花屏,所以使用UDP传输时,需要做好抗丢包,拥塞控制等处理,这个我们以后会讨论。

2019年2月23日 0comments 3189hotness 3likes Jeff Read all
Audio & Video Sharing

ffmpeg从mp4提取音频命令

从mp4中提取出aac文件(听歌必备): [crayon-69c68ea70cf3f124530173/]

2018年3月19日 0comments 3333hotness 4likes Jeff Read all
A/V

mp4文件elst研究

elst全称Edit List Box,mp4文件中不一定都含有这个box。该box作用是使某个track的时间戳产生偏移。 结构 在ISO_IEC_14496-12中,elst结构定义如下: segment_duration:表示该edit段的时长,以Movie Header Box(mvhd)中的timescale为单位。 media_time:表示该edit段的起始时间,以track中Media Header Box(mdhd)中的timescale为单位。如果值为-1,表示是空edit,一个track中最后一个edit不能为空。 media_rate:edit段的速率为0的话,edit段相当于一个"dwell",即画面停止。画面会在media_time点上停止segment_duration时间。否则这个值始终为1。 例子 现在我们手里有个mp4文件,我们要让封装的视频延迟10秒才开始显示,封装的音频不变,这个可以通过修改视频的时间戳实现,将视频的所有时间戳都加上10秒,但是一个个改太麻烦了,此时elst就派上用场了,我们要通过它让视频时间戳偏移10秒。 下面我们先动手操作番,了解elst如何起作用。 1)找一个没有elst box的mp4文件:test.mp4,假设我放在D:\\bin目录下。至于mp4有没有包含elst可以用文章末尾链接提供的mp4分析工具Mp4Reader分析下。用mediainfo查看该视频的信息: 该mp4音视频时长都为3分32秒。 2)得到带elst的mp4。到https://gpac.wp.mines-telecom.fr/mp4box/ 下载windows下的mp4box,按提示一步步安装。 打开windows cmd命令行,cd到test.mp4目录,然后敲入Mp4Box的命令: [crayon-69c68ea70d800313888912/] 得到: 由此可知test.mp4中,视频的track id 为1,音频track id为2。 3)接着我们敲如下命令: [crayon-69c68ea70d80b080984978/] 由于我们只对视频操作,视频track id是1,所以是#1:delay=10000。得到: 此时在bin目录下会生成一个delay_10s.mp4,该mp4中视频track延迟了10秒,音频track不变。mediainfo查看delay_10s.mp4信息: 可以看到视频的时长多了10秒 4)打开mp4reader查看视频track的elst信息: 也就是: [crayon-69c68ea70d810809814351/] 可以看到有两个elst entry,第一个为空,Segment-duration为6000,由于timescale为600(该timescale在mvhd中获 得),6000除以timescale刚好为10秒。由此可知我们要延迟播放某个track,可以在elst中插入一个空的entry,Segment-duration设置为需要延迟播放的时间,Media-Time设置为-1,然后在插入一个entry,Segment-duration设置为正常播放时间,Media-Time也就是起始时间设置为0。 5)播放器验证。我们使用vlc播放器打开delay_10s.mp4: 前10秒视频没有播放,而声音正常播放,到第10秒时视频才开始播放,等声音播放结束后,视频还会播放10秒,可以看出视频确实是推迟了10秒播放,此时音视频已经不同步了。 不是所有的播放器都支持elst的,我测试了下,vlc与potplayer支持,windows自带播放器就不支持。 ffmpeg相关代码分析 下面结合ffmpeg中相关代码以及上面的视频延迟10秒的例子分析,看ffmpeg中对elst数据如何处理。ffmpeg中elst entry数据存放在MOVElst 结构体中: [crayon-69c68ea70d814023662354/] duration对应mp4标准中的segment_duration time对应mp4标准中的media_time 在ffmpeg源码libavformat\mov.c中的mov_build_index函数中有如下代码: [crayon-69c68ea70d819885312099/] 第8行代码中可以知道,如果e->time == -1,也就是第一个elst为空,此时得到empty_duration,按前面的例 子该值为10*timescale,下一个for循环得到start_time =e->time,值为0。 在第26行代码中,可知sc->time_offset =0 -10*timescale = -10timescale,然后所有dts都要减去该sc->time_offse,最后结果是都加上10timescale,与原来时间戳比相当于延迟了10s。所以当mp4存在elst时,dts要按如下计算: 1.参考上述ffmpeg代码得到time_offset 2.解码时间戳dts = sample_delta * n – time_offset,其中sample_delta在stts中获得,如果存在B帧,还要从ctts中获得sample_offset,此时: 显示时间戳pts= dts+ sample_offset 否则pts = dts。 接下来我们使用ffmpeg验证下,打印出所有viedeo packet的dts与pts: 可以看到所有时间戳都偏移了230000,也就是10timescale,在video track的mdhd中可知timescale为23000,刚好是10timescale = 23000*10: 由此可知elst的作用就是使某个track时间戳偏移,达到延迟播放的效果。在我们解析mp4文件时,如果存在elst,一定要解析,然后配合stts与ctts,这样才可以得到正确的时间戳。 相关下载 Mp4Reader:https://pan.baidu.com/s/1cBC_yRR-BUfMGpUnC2LN8g

2016年6月16日 1comments 5316hotness 11likes Jeff Read all
A/V

avcodec_decode_video2解码得不到图像

如下所示代码: [crayon-69c68ea70e92e284460541/] 在使用avcodec_decode_video2函数解码时经常会遇到frameFinished == 0,也就是无法得到一帧解码后的图像。有些人可能会怀疑是哪里出错了,其实这是正常的现象,ffmpeg内部解码时做了处理。处理如下: 1)该帧为B帧,由于B帧是前后参考帧,需要结合前面的I帧或者P帧,以及后面的P帧来生成完整图像,所以该帧如果是B帧,就无法立即解码,所以返回的frameFinished为0,需要解码完后一帧后才可以解码该帧; 2)缓存处理,解码器解码时会缓存几帧提高程序的效率,防止程序在解码这一步等待太久。当解码到最后av_read_frame没有返回新的packet时,由于解码器存在缓存,所以最后我们需要清空解码器,通过传入空的packet给avcodec_decode_video2,直到没有新的解码后的frame返回这一方法来清空解码器。

2016年4月8日 0comments 3010hotness 10likes Jeff Read all
Copyright Statement

Unauthorized reproduction or plagiarism in any form is strictly prohibited. For reprint requests, please contact via email.

Recent Comments
snail Published at 5 hours ago(03 03202633105 27 27pm26) 多谢,大佬。醍醐灌顶!
Bramsnawl Published at 12 hours ago(03 03202633110 27 27am26) Proper blood collection playing cards are measure ...
NasibDepdrotte Published at 24 hours ago(03 03202633110 26 26pm26) Inf ect isC linNo rth A m viiiix, Sm ets o urgo is...
Pereplanirovka kvartir_cvsr Published at 2 days ago(03 03202633105 25 25pm26) перепланировка услуги [url=https://pereplanirovka-...
Mirzoemele Published at 3 months ago(01 01202613104 06 06pm26) Double blind randomised controlled trial of two to...
Ad

COPYRIGHT © 2026 jianchihu.net. ALL RIGHTS RESERVED.

Theme Kratos Made By Seaton Jiang