Walker.Xuhtml
短视频无疑是2017年最火的事情之一,套用业内术语就是很大的风口之一。android
短视频也确实颇有魅力,可能迎合了人们时间碎片化下的精神娱乐需求,或者如今追求“短平快”的大环境,我也有点短视频中毒,没事常常光顾某几个短视频APP,以致于冷落了某头条和某易新闻基本不多点开了,这些时间加起来holy bible估计都能读好几遍了。固然这是一篇技术文章,其余心理学,社会学问题,产品问题就不在这里讨论了,咱也没那个水平。缓存
言归正传,咱们也推出了短视频相关的产品。
在短视频的体验中,起播速度无疑是最影响体验的指标之一,由于短视频很短,十几秒到几分钟不等,若是一个十几秒的视频,加载时间都要3秒,确定是一个很坏的体验;因此在产品定义之初,起播速度就设定了控制在1秒左右,大部分在1秒内,也就是业内说的“秒播”,这须要对播放流程进行优化。
艾玛,终于绕到主题上了。服务器
如上图所示,移动设备的播放器经过某个视频url的域名,经过DNS服务请求到IP地址,经过这个IP地址与视频服务器创建TCP链接,而后在链接之上创建http协议,最终请求到数据,给到播放器进行解析音视频解码显示,用户看到画面听到声音,一个起播流程结束了。网络
图中蓝色的部分是能够优化的,但因为实际状况是,flyme只在客户端接入了内容,内容都是放在CP的服务器上的,虽然有优化空间可是flyme这边优化不到,但在后边咱们也会介绍有哪些优化空间能够操做。这个是行业现状,不过能够多接入几家内容选择岂不是更好。
图中灰色部分是不能优化的,在流程上没有优化空间,并且这部分容易受到网络状况的影响,因此咱们后续提到的优化,是基于大多数normal的网络状况的,虽然部分逻辑对极端网络状况也适用,可是这不是咱们讨论的重点。
图中绿色的部分是能够优化而且在项目现实中能够实施的。框架
DNS请求包会先发到本地DNS服务器,若是查不到,会递归到根域名服务器,这个过程是比较耗时的,固然若是你请求过了,或者期间有其余人请求过相同的域名,那域名服务器就会有缓存,再次请求的时候就很快了;可是通常缓存的周期很短,须要有人不停地请求才能保持更新,因此具备很大的不肯定性。socket
有两方面能够作
1.注意请求使用的IP协议版本,作播放的确定都绕不过ffmpeg,在ffmpeg里为了兼容性,DNS请求的IP协议版本设置为AF_UNSPEC,这样在请求的时候会先请求IPv6的地址,若是没有再请求IPv4的地址,是很保险,可是在实际的项目中,没有IPv6的地址,形成一直递归到根域名服务器也查不到IPv6地址,极大的浪费了时间,可使用AF_INET指定请求IPv4地址,节省一半以上的时间,首次请求或缓存过时后请求,耗时大概在大几十毫秒到100毫秒左右,能够经过监测getaddrinfo函数的耗时的到。tcp
hints.ai_family = AF_INET;
getaddrinfo(hostname, portstr, &hints, &ai);
2.预置或预解析域名IP地址,100毫秒仍是很大的一笔时间对于秒内播放来讲,这个方案就是提早把域名解析出来,用的时候直接使用IP地址,可是这种方案有局限性,可能适合特定的音视频直播,对于短视频播放地址比较多样来讲操做起来有必定难度,并且还存在CP切流和更换接入CP的状况,因此这个100毫秒目前只能先放在这里了。函数
TCP connection在客户端的具体操做中基本都是经过socket实现的,在socket中有一个缓冲区的概念,发送端先把数据写到缓冲区,接收端数据也是先通过缓冲区,再从缓冲区读出,移动设备做为接收端,接收端缓冲区设置的过小,影响效率,接收端缓冲区设置的太大,会短期内吃掉带宽,若是带宽不够会引发网络传输问题,还会形成流量的浪费。这些都会影响首屏数据的及时送达。测试
根据实际状况调整接收端缓冲区大小,经过计算和测试数据获得一个比较合理的值。能够在ffmpeg的network和tcp里进行调整,这是比较低层的修改了,为了通用性能够扩展http/tcp的options并经过ffmpeg提供的AVDictionary机制在avformat API这一层进行透传相关设置参数。
av_dict_set(&avdictionary, “param”, “value of param”);
setsockopt(fd, SOL_SOCKET, SO_RCVBUF,&len,sizeof(len));
在播放端,一开始并不知道要播放的视频的相关信息,好比封装格式,分辨率,音视频编码等信息,须要先读一段数据进来,再对这段数据进行探测,得出相应的信息,而存放这段探测数据须要一个buffer。这个buffer设置的过小可能致使分析不出信息致使从新探测,设置的太大就会增长收流的时间从而影响了首屏的播放,过小太大都会引入延迟。
根据实际状况调整这个buffer,经过计算和测试数据获得一个比较合理的值。能够经过ffmpeg的AVFormatContext结构体的probesize和max_analy_duration把对buffer的限制透传下去。
能够经过观察avformat_find_stream_info这个函数来评价探测耗时。
AVFormatContext->probesize = n;
AVFormatContext->max_analyze_duration = m*AV_TIME_BASE;
一样是探测的流程,一开始播放端并不知道这段数据是什么格式,须要根据本身支持的格式经过探测得出一个分数,而后依据这个分数给出相应的格式,相似于android的sniff,因此若是ffmpeg设置的支持的格式越多这个探测list就越长,相应的探测时间也就越长。
而对于短视频来讲,CP的内容格式基本是肯定的,基本都是MP4+H264/H265(不常见)+AAC。因此不少格式的探测是没必要要的。
对于没有用的格式在ffmpeg build config里移除,只保留须要的格式,好比mp4,最大限度的减少probe list。
能够经过观察avformat_find_stream_info这个函数来评价探测耗时。
disable avi
disable asf
disalbe mkv
and so on…
对于非直播类的播放器,业内的通常作法是都会在player内设计一个缓冲buffer,这是为了播放流畅性和音视频同步的须要,尤为是在网络不稳定或较差的状况下,这个缓冲buffer显得尤其重要。
通常这个缓冲buffer有按照帧数设置的,也有设置为1-2秒的,也有设置为3-5秒的,由于通常的播放好比在线电影,电视剧考虑的是整个播放过程几十分钟,甚至几个小时的体验,在开始缓冲个几秒是能够接受的,可是在短视频的场景下这是不可接受的。
策略性的优化,保证视频第一时间输出,把缓冲机制移到首屏播放以后,固然这里也要照顾到音频,保证音视频的同步,有些取舍要作。
这里实际上是很重要的一个环节,Android的nuplayer框架设计上受限于这些因素起播速度就远达不到要求,又搞了个exoplayer,可是不作二次开发exoplayer仍是不能知足需求。
这个须要根据本身的播放框架来作设计,咱们使用的是自研的Normandie播放框架,该框架已经上线将近两年,支持了多个音视频的业务,这里就不详细展开了。
分辨率这个不难理解,若是视频文件的分辨率很高,那它一帧的数据会很大,相应的传输时间就会变长。因此在内容产生的时候选择合适的分辨率录制或转码为合适的分辨率也是为播放端的负载在考虑,移动端720P左右足够了对于我的秀之类的短视频,内容聚合类的短视频分辨率能够更低。
QP指的是图像质量,同一个分辨率的图像能够有不少级的QP,它是跟编码密切相关的,QP越高图像质量越高码率也越高,相应的传输时间也就越长。一样,不是QP越高越好,对于不是不一样场景快速切换的720P视频,3M和5M码率的区别不大。选择合适的QP在画面质量和传输上找到一个平衡。
I帧位置,指的是视频I帧在视频文件开头的位置,播放器为了防止花屏之类的问题出现,通常在开始播放或seek时都会找到第一个I帧进行解码,通常视频文件一秒有25-30帧,很明显I帧放在第一帧和放在最后一帧对秒播是有影响的。
根据实际状况,在产品服务链条中选择合适的分辨率/QP。
把I帧放在文件开头第一帧的位置。
若是在起播过程当中发生了http re-connection耗时确定会增长,并且http connection的耗时基本是不可优化的,因此要避免http re-connection的发生。
可是mp4做为主流的短视频封装格式,它的MOOV box在文件中的位置直接影响了会否发生http re-connection,直接点说就是MOOV box放在文件的后面也就是MDAT box后,会产生http-reconnection,引入延时。
1.在上传mp4文件的时候把MOOV box放在前面
2.在mp4文件上传到服务器以后,从新封装,把MOOV box放到前面
若是是传统音视频出身的工程师应该很清楚MOOV box,这里作下简单的介绍。
mp4是由不少box组成的,其中MOOV box里存放的是音视频编码之类的对播放很重要的信息,先要拿到这些信息才能播放。因此
MOOV box放在文件的前面经过一次http请求就能够直接读取到,解析,继续读取音视频数据播放。
MOOV box放在文件的后边,可是播放器不知道它放在后面,须要先发起一次http请求读开头部分数据,发现没有MOOV box,它会seek到文件后边位置,读取MOOV box,读完以后再seek到文件前边位置,从开始位置读取音视频帧数据播放,每发生一次seek就从新发起一次http connetion请求。
因此MOOV box放在后边比放在前边多了2次http connection,用时是放在前边方案的3倍。下边是两个短视频一个moov box放在了前边,一个放在了后边,放在了后边的经过NmdPlayer的log能够都看到会发生re-connection,请求文件最后一段数据来获取moov box。
CDN节点部署,路由策略
缓存仍是拉流
都会对延时产生影响
server进行相关的优化。
协议耗时,好比TCP的握手机制等,在稳定的网络下耗时基本是固定的,在较差网络下耗时会较长
CDN骨干网络的部署能够改善这种状况。
协议耗时,在稳定的网络下耗时基本是固定的,在较差网络下耗时会较长
CDN骨干网络的部署能够改善这种状况。
对于作技术的同窗来讲,并无多么高深的东西,须要作的只是抓住问题的核心,把一个大问题拆解成一个个小问题,而后完成一个个小目标,最后水到渠成。其实不少事情都是这样的,你就把它当作一个技术问题,把它拆解成一个个小问题,完成一个个小目标,好比先赚它1个亿。
咱们也完成了一个小目标,经过优化大多数的短视频起播速度都落在1秒内,达到了业内的“秒播”水平。
追求体验的极致,一直是咱们追求的目标,而每一个功能每一个细节体验的极致累加起来就是最终产品的极致。
1.经过上边的分析,咱们认为还有至少100~200ms的优化空间。
2.另外咱们下一步计划用大数据来监控没有落在1秒内的状况,根据数据分析报告推进对这种状况的优化。
这是咱们下一个小目标。
很早以前录的一个视频,在线播放分辨率960x720的短视频,这算是比较高的分辨率了,以快手,抖音等为表明的我的秀类差很少是这个分辨率,抖音可能低一些;以西瓜等为表明的内容聚合类的,分辨率要更低些大多640x360或480P。
http://v.youku.com/v_show/id_...
http://v.youku.com/v_show/id_...