视频边下边播

背景

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

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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,233评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,013评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,030评论 0 241
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,827评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,221评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,542评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,814评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,513评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,225评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,497评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,998评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,342评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,986评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,812评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,560评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,461评论 2 266

推荐阅读更多精彩内容