“AI 技术+人才”如何成为企业增长新引擎?戳此了解>>> 了解详情
写点什么

极致首帧播放方案:零首帧解决方案

字节跳动视频云技术团队-琨君

  • 2023-03-04
    北京
  • 本文字数:3142 字

    阅读完需:约 10 分钟

极致首帧播放方案:零首帧解决方案

背景介绍


首帧时间,是指用户从点击开始播放到视频首帧画面展现出来的时间。「零首帧」并不是真的 0 毫秒启播,而是用户几乎感知不到有首帧时间的存在,在我们的播放质量埋点中对应小于 100ms 以内的首帧时间。在我们的播放器中,在各环节提供了极致的首帧优化方法,在条件允许符合时,可以将首帧时间压缩到 100ms 以下,用户感知到的就是完全平滑播放,没有首屏的顿感。当然在现实业务中,有些场景是无法使用所有的优化条件的,比如在随机播放的场景不能进行预加载、某些场景不适合使用播放器复用技术等。结合实际业务场景,尽量多的使用我们提供的优化能力,就可以使大部分用户体验为零首帧情况。


首帧的构成


首帧的概念,首帧对于视频播放体验的影响

首帧时间,是视频类应用的一个重要核心指标,也是影响用户观看体验的核心因素之一。举个例子,如果一个视频需要花好几秒时间才加载出首帧,大部分用户都等不到首帧加载出来就放弃播放了。因此,优化视频播放的首帧时间是极其重要的。



上面这幅图是一个视频点击播放的整个流程,可以看出首帧时间主要包含这么几个部分: 获取视频播放链接,网络建连,下载视频头部数据,音视频解码和渲染。本文将从视频播放的整个过程出发,介绍首帧优化的一些通用方案。同时在本文最后,会以长视频场景播放和带历史进度的起播为例,介绍面向场景的首帧优化


通用的首帧优化方法

获取播放地址


播放地址随 feed 下发

视频播放的第一步就是获取视频资源的播放链接,通常而言,视频资源会有唯一标识 video id , 在点播的服务端会有一个根据 video id 信息获取播放链接的服务,如果 app server 端能够调用 vod 服务生成播放地址,然后将播放地址随 feed 流一起下发,则省去了客户端的一次网络请求耗时。


网络建连


DNS 预解析,预连接、连接复用,https TLS 1.3 false start, session 复用 0RTT

在拿到播放地址之后,播放器会去与 cdn 建连,首先会进行 DNS 解析。而为了防止 DNS 劫持,大部分客户端会采取 HTTPDNS 的方式做 DNS 解析,这又会涉及到一个网络请求的耗时。我们可以采取 DNS 预解析的策略,比如 app 启动时,服务端就可以提前下发 app 可能用到的域名,客户端则可以对这几个域名提前做 DNS 解析,并做缓存。在 HTTP 1.1 中支持连接复用,因此我们可以预先创建几个与 CDN 的 socket 连接,然后在播放时直接复用连接即可。另外,为了应对内容劫持,在部分地区播放开启了 https,而 https 相比于 http 多了 TLS 握手的过程,这个握手过程会给视频首帧多引入 2 个 RTT 。通过 TLS False Start 加上 session 复用,可以做到 0RTT 握手。


音视频首包


减少 probe、moov 位置

在播放器与 CDN 完成建连后,播放器就开始下载视频文件, 首先播放器会尝试探测视频文件的格式、编码等信息。如果视频源经过服务端统一转码,那么就可以省去这个探测的过程。同时,值得一提的是,常见的 mp4 视频文件,有一个 moov box,这里面会存储音视频流 track 信息比如解码信息、以及音视频帧与文件对应的关系(用于 seek ),因此通常播放器都会先下载 moov 的数据。而 moov 的位置则会对起播造成一定的影响。举个例子,如果 moov 在文件尾部,当下载了视频前面部分数据,发现 moov 没找到,就去尾部查找 moov,这样就又多了两次网络请求。对于这个问题,我们可以通过转码将 moov 挪到视频文件的头部,从而缩短首帧耗时。


音视频解码


解码器异步初始化、解码器复用

通常情况下,在播放器读取到视频数据,拿到视频的解码信息后就可以开始创建解码器解码了。不过解码器创建这个过程,尤其是在 Android 平台上 MediaCodec 的创建是一个比较耗时的操作。这里我主要介绍两个优化: 解码器异步初始化和复用。如果 app server 提前把视频的解码信息传递给播放器,那么播放器就可以在建连的同时去异步初始化解码器,这样就可以减小硬解创建耗时的影响。而解码器复用则可以完全消除这个耗时,顺着这个思路,我们可以做播放器线程的复用甚至整个播放器的复用,这些方法都可以大幅优化首帧耗时。


起播水位


理论上,要做到极致首帧,可以当视频首帧解码完成就直接播放。但是实践发现,这样做会导致视频播放的卡顿增多,尤其是视频起播后 1~3s 的卡顿增加。经过大量的实验,我们发现,如果对视频起播做一定限制,比如让起播之前缓存一定的数据,这样可以大幅减少卡顿,同时对首帧的影响不大,并且可以显著提升用户的观看时长和观看 vv。


预加载


预加载是一种常见的首帧优化措施,我们可以提前下载视频数据的一部分以达到快速起播的目的。但是什么时候去预加载、预加载多少、并行预加载数量等都是实际需要考虑的问题。首先是预加载时机的问题,对于 15s 的短视频而言,完全可以等到当前视频加载完成之后再启动预加载,这样预加载就不会和当前视频的播放抢占带宽。但是如果一个视频时长超过 1min ,我们就得重新考虑预加载时机的问题了,具体来说,我们需要考虑当前播放视频的可用缓存、当前网络的下载速度、当前视频的码率以及即将预加载视频的码率、并行预加载数量,通过这些数据我们能够构建一个模型去预测接下来视频播放的卡顿状况,如果大概率是不会发生卡顿,则可以开启预加载,反之则不启用或者暂停预加载。另外一个问题,预加载多少,直观认识,至少得保证首帧能加载出来。一个粗略的估算方法是 moov 大小加上视频的平均码率 * 预加载时长,这样就可以通过服务端下发 moov 头大小及视频的平均码率,然后在 app 端上通过实验去调整预加载时长参数,进而调整预加载大小。


预渲染


原理介绍

预加载只能够将网络请求的耗时消除掉,但播放器还是需要经历解复用、解码、渲染的步骤,在中低端机器有 200ms 以上的耗时。如果能够将视频的首帧提前渲染好而不播放,将会缩减掉这部分的耗时。而预渲染则是提前将视频的首帧渲染出来的技术。具体来说,预渲染会提前解码出视频首帧,并且将首帧渲染出来,但是这个过程中音频不会播放出来。举个例子,在小视频场景上,当滑动视频卡片时,就已经开始启动预渲染,在卡片滑动过程中,视频的首帧很可能就已经通过预渲染加载出来,这样当卡片滑到中央时,则直接启动播放,这时候用户基本上感受不到视频的加载。


结合使用场景的优化


前面提到各种首帧优化的手段都是比较通用的策略,而面向场景的优化也是极为重要的,在本文接下来的部分会介绍两种场景下的起播优化: 1.长视频场景的播放优化 2.带历史进度的起播优化。


长视频场景优化


mp4 格式介绍,moov 大小与时长关系

短视频通常采用 mp4 这种视频格式,前面也提到过 moov 的下载是 mp4 视频起播的重要条件,而 moov 的大小则与视频时长正相关,粗略统计 moov 的大小约为 40KB/min 。这样 1h 的长视频 moov 头就有 2.4MB ,如果平均网速 1MB/s ,则需要 2.4 秒的加载时间,这对于弱网用户而言是极差的体验。而 fmp4 这种视频格式则能很好解决这个问题,fmp4 将一个完整的视频拆分成若干个小的片段,而每个片段的索引则存在于 sidx box 中,这样起播所需要的数据量就大幅下降,从而缩短了首帧耗时。另外,长视频往往有前贴广告,我们也可以在前贴广告播放器期间,结合预渲染提前加载正片首帧。


带历史进度的起播


关键帧起播

在中长视频中有一个功能是记住历史进度播放,通常的实现方式是 seek 到历史进度前面最近的一个关键帧,然后把视频帧塞给解码器,在解码器中做丢帧处理,直到 pts 到了指定的历史进度。假设这个视频的码率是 4Mbps ,视频的 GOP 大小为 5s ,那么这种场景的起播最坏情况需要额外下载 4 * 5=20Mb 的数据。如果我们限制只在关键帧位置起播,则可以避免这些额外数据的下载,从而显著缩短首帧的耗时。


总结


本文主要按照首帧的各个阶段分别介绍了对应的优化方案,也简单介绍了预加载和预渲染这两个优化首帧的利器,在文章最后针对长视频以及历史进度起播这两种场景,介绍了对应的优化手段。

公众号推荐:

2024 年 1 月,InfoQ 研究中心重磅发布《大语言模型综合能力测评报告 2024》,揭示了 10 个大模型在语义理解、文学创作、知识问答等领域的卓越表现。ChatGPT-4、文心一言等领先模型在编程、逻辑推理等方面展现出惊人的进步,预示着大模型将在 2024 年迎来更广泛的应用和创新。关注公众号「AI 前线」,回复「大模型报告」免费获取电子版研究报告。

AI 前线公众号
2023-03-04 19:007389
用户头像
鲁冬雪 InfoQ 主编

发布了 330 篇内容, 共 186.8 次阅读, 收获喜欢 267 次。

关注

评论

发布
暂无评论
发现更多内容

这 30 个工具和服务可以更好地监控和管理 Linux 服务器,很全面!

wljslmz

三周年连更

OpenGL入门二:绘制三角形

轻口味

opengl 图形图像 三周年连更

五款提高程序员生产效率的ChatGPT神器,你一定不要错过!

cwang

Code Review Git Commit ChatGPT GPT-4 Prompt

文心一言 VS chatgpt (13)-- 算法导论3.1 8题 3.2 1题

福大大架构师每日一题

福大大 ChatGPT 文心一言

算法题每日一练:无重复字符的最长子串

知心宝贝

数据结构 算法 前端 后端 三周年连更

设计一个即时群聊天系统软件(采用华为云ECS服务器作为云服务端 )

DS小龙哥

三周年连更

推荐一款基于Vue3的移动H5模板,加速你的移动应用开发

cwang

前端 vant Vue 3 Web H5

Mac 电脑解决无法使用uiautomator viewer的问题

IT蜗壳-Tango

三周年连更

Matlab实现周期卷积

袁袁袁袁满

三周年征文

Nautilus Chain 测试网第二阶段,推出忠诚度计划及广泛空投

EOSdreamer111

云服务管理技术

阿泽🧸

三周年连更 云服务管理

Django笔记二十之手动编写migration文件

Hunter熊

Python django migration

Windows下 IDE工具常见编译错误FAQ下

鸿蒙之旅

OpenHarmony 三周年连更

Qz学算法-数据结构篇(顺序存储二叉树、线索化+遍历)

浅辄

数据结构 三周年连更

一文了解Spring Framework 5 新 Web 框架:Spring WebFlux

Java架构历程

三周年连更

为什么老有人想让我们“程序员”失业? | 社区征文

se7en

三周年征文

vue2.x中keep-alive源码解析以及LRU缓存策略使用

不叫猫先生

缓存 Vue LRU keep-alive实现原理 三周年连更

Nautilus Chain 测试网第二阶段,推出忠诚度计划及广泛空投

西柚子

再来一篇,Go+Vue前后端分离设计实践

海风极客

三周年连更

云环境下的新型IT运维体系

穿过生命散发芬芳

运维体系 三周年连更

【Python实战】Python中parsel两种获取数据方式

BROKEN

三周年连更

通过ChatGPT快速学习英语

石云升

AI ChatGPT 三周年连更

挑战 30 天学完 Python:Day16 时间datetime

MegaQi

挑战30天学完Python 三周年连更

2023-04-30:用go语言重写ffmpeg的resampling_audio.c示例,它实现了音频重采样的功能。

福大大架构师每日一题

Go 音视频 ffmpeg 流媒体 福大大

Prometheus监控神器-自动发现篇

乌龟哥哥

三周年连更

什么是 RUM JavaScript

Jerry Wang

JavaScript 前端开发 三周年连更

Nautilus Chain 测试网第二阶段,推出忠诚度计划及广泛空投

股市老人

KubeShark: Kubernetes的Wireshark

俞凡

Kubernetes 云原生

【程序猿未来之路】作为互联网技术人的“我们”该如何破局 | 社区征文

洛神灬殇

4月日更 AIGC 三周年征文 三周年连更 生存指南

为什么 Go for-range 的 value 值地址每次都一样?

AlwaysBeta

Go 面试

IT组织架构之反思

agnostic

IT组织

极致首帧播放方案:零首帧解决方案_语言 & 开发_InfoQ精选文章