【客户端技术】深入了解视频播放器工作原理与实现

2020-09-03 10:10:10 浏览数 (1)

| 导语 想在APP中玩转视频播放吗?本文主要探讨播放器的工作原理及优化方向,并基于腾讯视频的开源TVKPlayer的设计,详解视频播放器的内部架构。

在下面的文章中,你将了解到播放器的视频解码、状态管理、功能扩展及二次封装的相关内容,带你深入了解视频播放器的工作原理。

1. 播放器之不得不做——视频解码

基本上我们都模糊的知道,视频播放需要解码器,那么解码视频究竟做了什么事情?为什么有些视频不能解析或者需要下载额外的解码器?

1.1 视频封装的那些事

1.1.1 视频的编码

视频是由一幅幅帧图像和一组音频构成的,因此必须要对视频中的图像以及音频进行编码压缩以减少视频所占的空间。 尤其是处理视频图像。

视频中图像编码的主要依赖:

  • 帧内的图像压缩编码
  • 帧之间的数据压缩编码

帧内的图像压缩很好理解就是对于图片中相同像素的压缩。

帧之间的数据压缩就主要依赖连续帧之间具有极高的相似性的原理。如一段视频有十几秒都是不动的或者有50%的画面都是不变的,那么这块存储就可以节省了。

视频压缩的核心原理就是将多张图像进行编码后生产成一段一段的 GOP ( Group of Pictures ) , 解码器在播放时则是读取一段一段的GOP进行解码后读取画面再渲染显示。一组GOP中的帧分为三类(有些视频只有两类,没有B帧),分别是:

1). I帧; 2). P帧; 3). B帧。

I帧:是自带全部信息的独立帧,是最完整的画面(占用的空间最大),无需参考其它图像便可独立进行解码。一个GOP视频序列中的第一个帧,始终都是I帧。

P帧:“帧间预测编码帧”,需要参考前面的I帧和/或P帧的不同部分,才能进行编码。P帧对前面的P和I参考帧有依赖性,也就是视频中运动的部分。P帧压缩率比较高,占用的空间较小。

B帧:“双向预测编码帧”,以前帧后帧作为参考帧。不仅参考前面,还参考后面的帧,所以,它的压缩率最高,可以达到200:1。不过,因为依赖后面的帧,所以不适合实时传输(例如视频会议)。

视频编码目前主流的标准有国际电联的H.261、H.263、H.264、H.265,运动静止图像专家组的M-JPEG和国际标准化组织运动图像专家组的MPEG系列标准。

1.1.2 音频的编码

音频编码的主要作用是将音频采样数据(PCM等)压缩成为音频码流,从而降低音频的数据量,偏于存储和传输。 常见的音频编码有WAV,MP3,AAC,Ogg,APE等。

1.1.3 视频的文件格式和封装格式

音视频编码完毕后,还需要有一个容器把编码后的文件封装起来,这个容器,就是把编码器生成的多媒体内容(视频,音频,字幕,章节信息等)混合封装在一起的标准,就是视频的封装格式。

而通常我们能看到各种后缀名的视频格式文件.mp4 .avi .mkv .flv,当我们看到这些后缀名时就知道他们是视频文件了,这些后缀名就是视频的文件格式

所以这些后缀名首先是为了让系统能识别文件类型并关联到相关的应用程序,比如双击.doc文件就会自动由office来打开。

其次,不同视频的后缀名,也往往代表了视频不同的封装格式。 下表列出了不同后缀名所对应的不同格式。 (这里主要是为了研究播放器的工作原理,不对封装格式进行对比。)

视频封装格式

视频文件格式

AVI

.avi

WMV

.wmv

MPEG

.mpg .mpeg .vob .mp4 等

Matroska

.mkv

Real Video

.rm .rmvb

QuickTime File Format

.mov

Flash Video

.flv

总结来说,一个视频文件产生经过了: 1.图像和音频编码 2.将音频视频的编码按一定格式封装于容器中

因此解码的过程其实就是解视频的封装格式和编码格式,将视频还原成一帧帧图像和音频的过程。

1.2 视频的传输协议

视频播放目前主要有本地播放,点播播放,和直播播放。本地播放和点播播放就是播放已处理好的有进度信息的视频,只不过本地播放视频存储在本地,而点播播放视频存储在远程服务器。直播播放则有边制作边播放的特点。

点播播放通常使用http协议来传输视频文件。 直播播放则主要有:

协议

传输层

特点

hls

http

有一个用于控制播放的.m3u8,而视频内容使用.ts格式封装

http-flv

http

流媒体数据封装成FLV格式,然后通过HTTP协议传输给客户端

rtmp

tcp

使用FLV,AMF封装格格式,通过TCP协议传输

1.3 客户端处理视频的工具

1.3.1 Android端

google已经在android端上实现了一套编解码的工具MediaExtractor用于解封装,MediaCodec用于解码。 MediaCodec中包含硬解码和软解码两种方式。 但是由于各个平台实现编解码的方式碎片化有差异,并且本身具有一些缺陷微信团队对 MediaCodec 编码器的研究

1.3.2 IOS端

VideoToolBox是由ios提供的解码工具。

1.4 用FFmpeg实现视频处理

1.4.1 FFmpeg工具简介

ffmpeg是一个基于C语言的开源的音视频处理软件,目前 PC端中Youtube , iTunes ,腾讯视频,b站,爱奇艺都是使用ffmpeg来进行视频处理的。

ffmpeg几乎囊括了所有的视音频编码标准以及传输协议。

ffmpeg包括的编解码格式 ffmpeg支持的协议解析

1.4.2 FFmpeg的其他功能

  • 提取视频图片
  • 视频翻转,旋转,缩放
  • 视频剪裁和填充
  • 视频格式转换
  • 设置视频码率以及调整视频分辨率

1.4.3 FFmpeg的弱点

由于FFmpeg是软解码(利用CPU计算解码),在客户端中如果使用FFmpeg来解码,手机将面临性能损耗高,耗电量大等问题!

1.5 一个综合的视频处理方案

目前FFmpeg已经支持使用MediaCodec和VideoToolBox来进行解码。 结合FFmpeg与客户端硬解码的优势,只用MediaCodec或VideoToolBox进行硬件解码,由FFmpeg处理解协议,解封装,以及编码兼容问题,此外FFmpeg强大的视频编辑功能也能够为视频播放的扩展功能提供支撑。

1.6 视频播放器的工作流程

综合来说播放器的基础工作步骤如下:

1.解协议(读取文件)

2.解封装

3.视音频分离

4.视音频分别解码

5.视音频同步

6.输出数据解码后的视音频数据

7.渲染图像和播放音频

2. 播放器之不可或缺——状态管理

2.1 播放器的状态管理

解码完成后,我们还需要确定播放器的状态机,用于定义播放器当前的状态,用户操作与播放器的交互操作就可以通过这组状态来定义: 对于播放器内核本身的状态有:

播放器管理类还需要添加:

首次播放:

必须在播放器为IDLE的状态下可进行初始化:

暂停播放:

必须在播放器为STARTED(开始播放)的状态下进行暂停:

继续播放:

必须在播放器为PAUSED(暂停播放)的状态下进行续播:

播放错误:

播放器为PREPARING,PREPARED,STARTED的状态中都可能出现播放错误:

通过这些状态,播放器每个交互都可以由一组固定的状态来表示,能够帮助播放器更好的管理各种复杂的情况,避免出现播放器状态混乱的情况。

2.2 播放器对外的状态回调

3. 播放器之锦上添花——功能扩展

前两章介绍了播放器必备的编解码和状态管理,这些仅仅是基础的功能。而一款优秀的视频播放器不仅仅需要处理视频,还要考虑播放流畅度用户交互以及播放器的扩展功能等问题。

接下来就以腾讯视频播放器为例简单介绍下在这些方面是它是如何处理的。

3.1 整体架构设计

首先腾讯视频(以android端为例)分为两层

PlayerCore:

  • 基于ffmpeg的解码组件
  • ffmpeg中其他API的封装
  • 调用android的多媒体接口
  • 对外提供的API

Android Application:

基于PlayerCore提供android上的视频功能。

  • 视频播放器管理类: 包括对视频信息的请求,扩展功能插件的管理,数据上报,和播放器状态管理等;
  • 视频下载管理组件;
  • 播放器view: 提供可嵌入布局的播放器view视图;
  • 其他公共模块: 包括鉴权,播放器配置等内容。

3.2 播放器缓存和代理

腾讯视频通过本地代理来预缓存视频,提升视频观看的流畅度。

3.3 其他功能设计

播放器扩展功能

  • 清晰度切换
  • 清晰度无缝切换
  • 精准seek
  • 倍速播放

其他插件

其他插件形式存在的功能:

  • 广告
  • 水印,动态水印
  • 播放流质量上报
  • 网络状态监听,用于确认视频清晰度
  • 二次缓冲逻辑

4. 播放器之渐入佳境——二次封装

我们自己在进行业务开发时使用腾讯视频播放器的SDK进行二次封装定制,就能使得播放器满足我们自己的业务需求。

4.1 基础视频功能

4.1.1 相应状态UI显示

腾讯视频播放器给我们提供了播放器的内核以及播放器状态的回调我们要做的就是在这些回调中给相应的播放器提供对应的状态。

根据需求我们设定了播放器有以下这些状态可能会展示给用户,并在布局文件中写好相应状态下的UI,在相应的状态下调用所映射的UI显示即可。

4.1.2 网络状态监听

由于网络视频传送时通常需要花费很多带宽,如果用户正在使用4G打开视频,一般的设计都是需要提示用户当前网络,避免用户在不知情的情况下使用大量流量。因此需要设置网络监听,通过不同的网络显示不同视图;

4.1.3 触摸手势

在我们使用播放器的时候往往会有很多手势,也相当于我们操作播放器的“快捷键”。这里我们给自己播放器设置的手势有: 1.点击视频窗口,若当前视频标题等信息隐藏,则显示视频信息;若当前视频标题等信息显示,则隐藏视频信息。 2.若当前视频标题等信息显示,用户不操作,几秒后自动隐藏。 3.双击视频窗口,若当前视频正在播放,则视频暂停;若当前视频暂停,则播放。 4.左侧上下滑动可调节音量。 5.右侧上下滑动可调节视频亮度。

使用监听onTouch事件处理触摸手势:

腾讯视频还添加右侧双击快进等功能,总之触摸手势就像快捷键,我们可以挖掘很多快捷键帮助用户更好的使用播放器。

4.1.4 大小屏切换

目前几乎所有的播放器都支持切换成大屏。大小屏切换也是播放器必备的功能。 小屏切换到大屏主要的功能点为:

1.记录小屏时的宽高,用于恢复;

2.屏幕设置为全屏(去除状态栏),旋转为横屏幕;

3.创建全屏容器,并将全屏容器放至整个页面的父容器下;

4.将原有播放器视图从小屏位置移除

5.将移除的播放器视图添加到全屏容器中

腾讯视频给我们提供了播放器view,可供我们进行视频视图的移动,也就是热插拔操作。

在热插拔的过程中可能出现黑屏或者视频播放器重新播放的情况,这是由于移除播放器视图时,会默认为视频播放器已销毁,因此在播放器视图移出父容器之前需要调用disableViewCallback方法阻断销毁的过程,在加入另一个父容器后调用enableViewCallback方法,从而实现热插拔。

4.1.5 重力感应和屏幕锁定

重力感应和屏幕锁定的实现非常简单,监听设备偏移角度变化即可,根据不同的旋转角度分为竖屏,横屏,反向横屏三种状态:

屏幕锁定也非常简单,关闭旋转监听即可。

完成这些功能后,一个基础的播放器就封装好了。

4.2 点播

4.2.1 进度监听

点播需要显示视频时间,当前播放进度,还可以跳转拉取进度等。 腾讯视频已为我们提供了播放器的seekTo功能,我们只需要获取用户当前操作的进度*乘以总时长,设置跳转到的时间点即可:

4.3 直播

4.3.1 秒开设置

直播播放器没有进度等内容,但是直播播放需要一定的实时性。

之前我们介绍视频编码的时候I帧是最全的视频帧,因此打开视频播放器时如果直接获取I帧就可以直接解码展示,达到秒开的效果。

因此我们可以通过规定服务器推流必须以I帧开头的一组视频流,并设置丢弃无用的帧缓存来设置达到秒开的效果。

4.4 弹幕添加

弹幕就相当于在视频视图的上层添加一层文字图层,并且将播放器的,初始化,播放,暂停,释放等状态映射到弹幕的相关状态中。

弹幕都有一个时间属性,用于表示什么时候显示在视频中。

点播弹幕的时间属性是以点播视频的时间片为基准的,也就是以视频播放进度为时间轴。 直播弹幕的时间属性则一般以发送时间为时间轴。

无处不在的辛普森悖论

从0到1实现一款轻量级大数据分析系统

腾讯的组织能力是什么

0 人点赞