一种边播边下的播放策略

本文为smm987独家授权发布本公众号,smm987的blog:http://www.jianshu.com/u/4f00ab501188。

背景

目前视频相关的需求越来越多,众所周知,视频文件一般都比较大,在移动端播放会耗费很大的流量,如何让用户以最少的流量播放网络视频,且以最快的速度满足视频的播放及用户拖动响应,这篇文章将分享一下已经实现的一些策略及方案。

mp4文件基本知识:

对于播放器而言,只要视频文件的头信息(时长,帧率,码率,视频数据偏移量等)解析到了,然后根据视频播放的当前时间对应的内容数据就可以播放视频,mp4的基本格式可参考http://www.jianshu.com/p/3ab4bd0d4219。基于以上,只要解析到视频的头信息,然后缓存视频数据内容就可以实现缓存播放及seek播放。

两种方案

视频的缓存播放目前有两种方案,

1、通过解析mp4的格式,将mp4的数据直接下载并写入文件,然后让播放器直接播放的是本地的视频文件;

2、使用本地代理服务器进行文件缓存,并将视频url地址转换成本地代理服务器地址来实现视频的缓存播放。

第一种方案

图1

如图1所示,第一种方案是先下载视频到本地文件,然后把本地视频文件地址传给播放器,播放器实际播放的是本地文件。当播放器的播放进度大于当前的可播放的下载缓存进度,则暂停播放,等缓存到足够播放时间之后,再让播放器开始播放。这种方案的下载方式是与播放器完全没有关系的,只是顺序的将服务器下发的视频数据写入本地文件,然后让播放器来读取数据。

但是在调研的过程中发现,对于mp4文件其实有两种格式的数据,一种是头信息(即moov)在视频头部,一种是在视频尾部,之前已经提到过,视频播放器只有解析到了头部才可以播放视频,所以应先获得mp4的moov才能播放。因此对于moov信息在后面的mp4文件,必须在视频缓存的时候把它写到文件前面才可以正常播放。对调换的过程以及mp4格式感兴趣的同学同样可以参考http://www.jianshu.com/p/3ab4bd0d4219 这篇文章。

这种方式虽然能够满足缓存播放这个需求,但是会产生很多问题,例如视频下载到本地,下载多少才可以把本地文件作为视频源传给播放器即视频开启播放速度;播放的速度大于下载速度的话,该怎么办?如果播放器seek到文件没有缓存的位置,应该怎么处理?对于视频关闭之后,第二次进入如何知道已经下载了多少?等等问题。

目前的解决方案是,当缓存到500kb才把缓存的地址传给播放器,视频文件小于500kb则下载完之后再播放,起播慢(需要改进)。当下载进度比播放进度多5秒的数据量才让播放器播放,不然的话就暂停。如果seek到没有缓存的地方就切换到网络上停止当前的下载,浪费一些流量。每次下载都会保存一份配置文件,来保存是否下载完成,没下载完成则第二次根据当前缓存文件大小,重新开始顺序下载。这个时候有些同学会想,这些数据怎么来的,---只是我们测试出来的经验值(亟待改进)。

总的来说第一种方案有如下缺点:

1、用户播放视频的时候可能等待的时间较长(起播慢)

2、流量浪费(seek之后会播网络流,停止下载)

3、需要太多控制视频播放的逻辑来进行辅助,与播放器代码耦合严重。

4、seek之后切源会耗时,每次seek比较慢

因此经过一段时间的研究,新的缓存方案应运而生。

第二种方案

图 2

核心技术要点:

1、 通过代理服务器,从socket截取播放器请求数据;

2、 根据截取的range信息,从网络服务器请求视频数据;

3、 视频数据写入本地文件,seek后可以从seek位置继续写入并播放;

4、 边下边播,加快播放速度;

5、 与播放器逻辑完全解耦,对于播放器只是一个地址

如图2所示,新的方案是在播放器与视频源服务器之间加一层代理服务器,截取视频播放器发送的请求,根据截取的请求,向网络服务器请求数据,然后写到本地。本地代理服务器从文件中读取数据并发送给播放器进行播放。过程如图3所示:

图 3

具体流程如下:

1、启动本地代理服务器。

2、视频源地址传给本地代理服务器。

3、将视频源地址转换成本地代理服务器的地址作为播放器的视频源地址。

4、播放器向本地代理服务器发送请求。

5、本地代理服务器截取这个请求,再根据解析出来请求的信息向真正的服务器发起请求。

6、本地代理服务器开始接受数据,写入文件并将文件数据再返回到播放器。

7、播放器接收到这些数据之后播放。

8、seek之后重新进行以上步骤。

代理服务器视频文件下载方案

考虑到播放视频的时候,用户会拖动进度条进行seek,而此时需要从用户拖动的位置进行下载,这样会让视频文件产生许多的空洞,如图4所示:

图 4

为了节省流量,只会下载文件中没有数据的部分,也就是图 4蓝色的部分。因此需要存储下载的片段信息。目前采用的数据结构如下所示:

fragment = [start,end];

array = [fragment 0,fragment 1,fragment 2,fragment 3];

其中fragment指的是下载的片段,start指的是片段开始的位置,end为片段的结束位置。

array指的是存储fragment的数组,数组中的fragment是依靠start从小到大来来插入到数组中的,保证了数组的有序性。

下载的片段是记录在一个数组中:array = [fragment0 ,fragment 1,fragment 2,fragment 3];

下载共分为两个阶段:seek阶段和补洞阶段。

seek阶段:即为在播放的时候,根据用户seek的位置来进行下载。

根据seek到的位置分为两种情况:

情况一:如果seek到的位置是在已有的片段中(例如图中的seek1的位置,该处有数据),就从该片段(fragment1)的末尾请求数据(end1),直到下个片段的开始位置处(fragment2的start),也就是向服务器请求的range为:

rang1 = (end1 ) —— start2;

这个片段下载完成后,假如把下载的片段记为fragment1.1,则会把fragment1、fragment1.1、fragment2合为一个片段为fragment1-2,则array = [fragment 0,fragement1-2,frament3];这次下载后的状态图5所示:

图 5

接下来一直下载直到array = [fragment 0,fragement1-3];之后会判断fragement1-3有没有到文件末尾,如果到了就下载结束,如果没到就从从fragement3的(end3)开始下载直到文件末尾。

情况二:如果seek到的位置没有在已有的片段中,(例如说是在图4中的seek2的位置),就从seek到的位置开始下载数据直到下一个片段的start(fragment2的start2),假如这个片段记为fragment1.1,则会把fragment1.1和fragment2合并即数组为:array= [fragment 0,fragment1,fagment1.1-2,fragment3];合并后的情况如图6所示:接下来的操作就是继续下载,直到下载到文件末尾;

图 6

如果片段太小保存起来就会让播放器下次播放的时候多发送一次请求,这样是很耗费资源。例如:如图6所示,如果fragment1的大小只有1kb,想要补充fragment0与fragment1.1-2之间的数据,就需要发送两次请求,这样频繁的发送请求,比较浪费资源。因此当fragment太小,就不存在配置数组中。这样会少发一次请求,也不会浪费很大的流量。

当下载片段太小(例如说下载的长度<20KB),就不保存在片段数组中(为了控制片段的粒度)。这样会产生一个问题,当视频文件中间有一个空洞小于20KB,这个片段永远补不上。这个时候就需要用到第二阶段。

第二阶段补洞阶段,就是第二次播放的时候,如果文件中有空洞,这个时候不论片段再小,也会存到片段中。

最后当配置数组中存的数据只剩下最后的{0,length},length为视频总长度的时候,表示文件已全部下载完成。

性能比较

下表的数据都是在第一次播放60秒,7.8M的视频得出来的性能数据。从表1中可以看出方案二性能比方案一的性能高出很多。

表 1

原文发布于微信公众号 - 何俊林(DriodDeveloper)

原文发表时间:2017-09-07

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏云计算D1net

横向扩展的NAS:混合云存储的关键

目前,世界上大多数的数据中心仍然使用垂直缩放的存储解决方案,这是一个困扰人们的问题。这种传统的存储方法在设计时并没有考虑到现在达到泽字节的庞大数据。企业以往任何...

5258
来自专栏web前端教室

《vue+vant+node+mongoDB+koa2》电商项目实战连载(1)

每节课程规划是大概12-15分钟左右,是以功能点来划分课程的节奏。预计总课时数大概40节左右吧,看实际情况吧。

1652
来自专栏架构师小秘圈

系统架构设计的原则和模式

1 分层架构 分层架构是最常见的架构,也被称为n层架构。多年以来,许多企业和公司都在他们的项目中使用这种架构,它已经几乎成为事实标准,因此被大多数架构师、开发者...

3867
来自专栏Java学习网

如何编写出优秀软件

如何编写出优秀软件   软件必须是自由的   优秀软件是自由软件。我将避免使用“开源”,因为它根本没有包含社会层面,它和技术本身同等重要。你可以从这里了解更多...

2355
来自专栏JAVA高级架构

日处理20亿数据,实时用户行为服务系统架构实践

携程实时用户行为服务作为基础服务,目前普遍应用在多个场景中,比如猜你喜欢(携程的推荐系统)、动态广告、用户画像、浏览历史等等。 以猜你喜欢为例,猜你喜欢为应...

1572
来自专栏架构师之路

究竟啥才是互联网架构“高并发”

一、什么是高并发 高并发(High Concurrency)是互联网分布式系统架构设计中必须考虑的因素之一,它通常是指,通过设计保证系统能够同时并行处理很多请求...

3706
来自专栏Java后端技术栈

图解分布式架构的演进过程!

透明性:是指每一个数据库分布节点对用户的应用来说都是透明的,看不出是本地还是远程。

1342
来自专栏程序员宝库

号称“开发者神器”的github,到底该怎么用?

GitHub是一个拥有数十亿行代码的网站,每天有数百万开发者聚集在一起,与开源软件进行协作和报告问题。简而言之,它是一个基于Git构建的软件开发人员的平台。

1024
来自专栏腾讯云技术沙龙

杨原:腾讯云Kafka自动化运营实践

下面我们有请腾讯云基础架构部高级工程师杨原给我们带来主题分享——腾讯云Kafka自动化运营实践。

1.2K13
来自专栏微信公众号:Java团长

图解分布式架构的演进

透明性:是指每一个数据库分布节点对用户的应用来说都是透明的,看不出是本地还是远程。

1471

扫码关注云+社区

领取腾讯云代金券