做为移动开发者,大多数时候会须要接触到音视频相关的开发,而其实严格意义上我也并非专职的音视频开发工程师,只是在 2016 由于业务须要接触到音视频相关的领域,而开源的 GSYVideoPlayer
刚好火起来以后,为了解决系列问题成了“半桶水”的音视频开发工程师。后端
在维护 GSYVideoPlayer
的这几年里,我发现不少开发者对于音视频领域相关的基本概念仍是不清楚,因此我也常常能够收获这样的 issue:缓存
“为什 么xxx 能够播而 GSY 不能播?”bash
“我两个视频都是 mp4 为何其中一个播放不了?”网络
“为何缓冲过的视频 seek 完还要从新请求数据?”ide
“为何播放有黑边?”布局
“····”ui
而这些其实都是音视频开发过程当中的常识性问题,因此本篇将经过基本概念、常见问题、应用场景来科普音视频开发的基础知识。阿里云
首先,以下图所示是一个 .MOV
的视频文件,能够看到更多信息栏里编码器有 AAC
、HEVC
,而这个就是视频的音频编码和视频编码,而 MOV
其实就是封装协议,这其实就是咱们接下来要介绍的基本概念。编码
通常状况下,视频流从加载都准备播放是须要通过解协议、解封装、解编码这样的过程,其中协议指的就是流媒体协议;封装是的是视频的封装格式;编码又分为视频编码和音频编码。spa
协议通常有 HTTP
、RTSP
、RTMP
等,咱们就最多见的就是 HTTP
网络协议,而 RTSP
和 RTMP
通常用于直播流或支持带有控制信令的常见,好比远程监控。
视频封装协议指的是咱们常见的 MP4
、AVI
、RMVB
、MKV
、TS
、FLV
等常见后缀格式,它们所表示的就是多媒体的封装协议,就是在传输过程当中把音频和视频打包都一块儿的封装,因此播放前是须要把这部份内容解开,提取出对应音频编码和视频编码。
因此若是之后有人问你,你是视频编码是什么,专业的你不能再回答 “个人视频编码是 MP4” 这样的回复哟。
音频编码指的是音频数据的编码方式,常见的如:MP3
、 PCM
、WAV
、AAC
、AC-3
等,由于音频的原始数据大小通常不适合直接传入,好比原始大小通常能够按照采样率 * 声道数 * 样本格式
去计算,假设前面那个 MOV
的音频采样率是 44100 、样本格式是 16 bit 、单声道、24 秒,那么它原始音频大小应该是
44100 * 16 * 1 * 24 / 8 ≈ 2MB
而实际将音频信息提取出来的大小,以下图大概只有 200 多K,这就是音频编码的做用。
因此通常都会音频传输会采用各类编码格式进行压缩和去冗余,其中好比 WAV
/PCM
编码的音频质量比较好,可是体积会比较大;MP3
有损压缩能在音频质量还能够的状况下压缩音频的体积;AAC
也是有损压缩,可是又有分有 LC-AAC
、HE-AAC
等。
视频编码指的就是画面图像的编码压缩方式,通常有 H263
、H264
、HEVC
(H265
)、MPEG-2
、MPEG-4
等,其中H264
是目前比较常见的编码方式。
一般状况下咱们理解的画面是 RGB 组合出来,而目前视频领域可能更多使用 YUV 格式,其中 Y 表示的是亮度(灰度),而 U 和 V表示的是色度(饱和度)。
YUV 是对 RGB 的特殊处理和叠加来获取颜色,好比 YUV420 能够理解对色度以 2:1 的抽样率进行存储,而后亮度透过色度来显示画面,更多 YUV 的这里就不展开讨论,而为何使用 YUV 其中有一点因素就是为了兼容之前的黑白电视。
为何不直接用原始 YUV ?这里假设上面的 MOV 视频直接使用 YUV420 的格式,那么一帧的大小就会是:
1080 * 1920 * 1 + 1080 * 1920 * 0.5 = 2.9MB
若是在这个基础上,算上帧率(30)和一个视频的时长(一小时),那一部视频原始大小就会是天文数字,这样的状况明显不符合网络传输,因此才有了视频编码用于压缩图像。
在视频压缩里,又有几个概念须要知道,好比:
因此 I 帧是很关键的存在,压缩 I 帧就能够很容易压制掉空间的大小,而压缩 P/B 帧能够压缩掉时间上的冗余信息 。因此在视频 seek 的时候,I 帧很关键,若是视频 seek 以后发生往前的跳动,那极可能就是你的视频压缩得太厉害了。
二、还有一个叫 IDR 帧的概念,由于 H264 采用的是多帧预测,致使 I 帧不能做为独立的观察条件,因此多出一个叫 IDR 帧的特殊 I 帧用于参考,IDR 帧最关键的概念就是:在解码器过程当中一旦收到 IDR 帧,就会当即清空参考帧缓冲区,并将IDR帧做为被参考帧。
三、在视频解码里还有一个叫 DTS(Decoding Time Stamp) 和 PTS(Presentation Time Stamp)的存在,DTS主要用于视频的解码,PTS主要用于在解码阶段对视频进行同步和输出。
由于视频包里面数据解码不是连续的,而是须要经过解码数据源获得的 DTS,才 决定以包应该在何时被解码,而获得的PTS 决定了解码后的图片何时被绘制。
首先说一个常常被问的问题:ffmpeg 全称是 Fast Forward Mpeg ,因此读法为 (ef,ef,'em,peg)
,通常状况下 ffmpeg 使用的是软解码,也便是纯 CPU 解码;而使用平台的 MediaCodec
播放的是硬解码,也就是支持 GPU 协助。
问题1:“为何同一个视频机器A能够播机器B不能够?”
这个问题很大可能就是使用了 MediaCodec
的硬解码播放,不一样手机和系统版本,对于硬解码的支持是不同的。
问题2:“为何都是 ffmpeg 播放,vlc 能够播放,ijkplayer 却不行?”
这是由于 ffmpeg 是支持根据配置打包的,由于不少时候你并不须要那么多,好比在 configure
文件中打开和关闭某些格式的支持来达到按需打包的目的,因此一样是 ffmpeg 不一样项目打包支持的程度可能都不一样。
支持wav
--enable-libwavpack
--enable-muxer=wav
--enable-demuxer=wav
--enable-decoder=wavpack
--enable-encoder=wavpack
--enable-decoder=wav
--enable-encoder=wav
--enable-encoder=pcm_s16le
--enable-decoder=pcm_s16le
--enable-encoder=pcm_u8
--enable-decoder=pcm_u8
--enable-muxer=pcm_u8
--enable-demuxer=pcm_u8
支持mp2
--enable-encoder=mp2
--enable-decoder=mp2
--enable-muxer=mp2
--enable-decoder=mp2float
--enable-encoder=mp2fixed
支持 h265
--enable-decoder=hevc
复制代码
问题3:“为何个人视频缓冲了,在 seek 以后还须要从新请求?”
这就须要解释缓存和缓冲的区别:
缓冲:就像在倒垃圾的时候,不可能一有垃圾立刻跑去垃圾堆倒,而是先把垃圾倒到垃圾桶,垃圾桶满了再一块儿倒到垃堆。由于缓冲是在内存中,不可在内存中把整个视频都缓冲进去,因此通常状况下你看到的缓冲都是一段一段的临时数据,一个缓冲块是处于不断地加载又不断清除的过程。
缓存: 缓存的解释就简单多了,就是把视频在播放的时候一边下载到本地,这样在缓存区域内的数据就不须要发生二次请求。
问题4:“为何个人视频在拖拽以后会出现跳动?”
其实前面已经解释过了,这和视频的关键帧有关系,同时也和 ffmpeg 选择的兼容策略有关系,好比使用 -accurate_seek
可让位于跳转点和 position 之间的额外部分将被解码而且丢弃,对应 ijk 中就是 enable-accurate-seek
的配置。
问题5:“为何个人视频会出现音视频不一样步?”
首选肯定你的播放器使用的音视频同步协议是什么,好比 ijkplayer 是使用音频做为同步时钟,若是在 ijkplayer 里 在出现音视频不一样步,那么极可能就是视频的码率或者帧率过高,能够尝试使用使用 framedrop
丢帧或者开启硬解码支持。
问题6:“为何个人视频会出现大小和方向不对?”
通常状况下视频信息里是带有旋转角度的,好比 Android 手机上录制的视频就可能带有旋转角度,因此在布局和绘制时须要把旋转角度如: 270,90 这样的角度考虑上。
另外视频在获取大小还会有 Width Height Ratio
的信息也就是宽高比,例如这个信息在 ijkplayer 上是以 videoSarNum / videoSarDen
获得的,只有把宽高比和视频的宽高一块儿计算,才能获取到真正的展现宽高。
问题7:“为何个人视频会出现黑边?”
这个问题其实就是常识性问题,面对不一样尺寸不一样分辨率的平台,视频显示是根据你给定的 Surface
大小去进行显示,因此你能够选择使用拉伸、裁剪、适应高度、适应宽度等不一样布局模式去配置你的绘制控件,这样就能够达到你须要的控制黑边的场景。
诸如此类的问题还有 “如何获取某个时间戳的图像”、“如何同时播放几个视频”、“如何实现播放滤镜”、“如何实现倍速播放” 等问题,这里就不一一展开,感兴趣的能够去 GSYVideoPlayer
的 issue 或者搜索相关的 ffmpeg 实现。
最后讲一下音视频开发的使用场景,为何要说这个呢?
由于不少时候开发者可能觉得“不就是接个播放器 SDK 放个 Url 的功夫吗?” 其实还真不是,作过音视频开发的应该都深有体会。
一、首先在作音视频开发时,要肯定好本身须要支持的封装协议、视频编码、音频编码格式,由于编码格式千万种,通常状况下你不可能全都支持,因此首先要在需求内肯定好须要支持的格式范围。
二、若是存在用户自主上传视频的场景,最好还要在服务端提供转格式与转码率等功能。由于在服务端判断视频格式并转码能够规范编码统一,这可以减小客户端端由于编解码失败没法播放的问题;另外提供同一视频不一样码率的连接,能够在不一样手机型号和系统上可以拥有更好的播放体验,减小前面说过的由于码率过高出现音视频不一样步或者卡顿的问题。
相似功能在阿里云和腾讯云都支持。
三、在网络播放中存在不少场景,好比播放过程当中网络环境出现变化,是从 4G 转化为 Wifi 仍是从 Wifi 转到了 4G 的场景 ,这里面涉及到两个点:第一是网络环境发生改变,那么本来的拉流通道其实已经断开,这时候须要从新启动一个新的链接来替换旧的播放内核,才能实现继续播放;第二就是 Wifi 到 4G 之间的环境发生改变时,须要给用户提示并肯定是否执行后续操做。
四、还有好比当视频画面须要从列表切换到详情页,须要从本来的容器切换到另一个可渲染容器时,须要在播放内核不暂停的状况下去设置不一样的 Surface
来达到切换的目的;片头广告的播放与视频内容的预加载须要两个不一样的请求处理等等的场景。
视频播放场景涉及先后端的数据交流,还有用户环境场景的变换和业务需求的迭代,若是你老板和那你说:
“就按照 bilibili 通常作一个视频播放就行了”
相信我,那你的坑才刚刚开始。