Through debugging technology, I have clarified the principle of the fast video playback at station b

The video on station b is played very quickly, and it basically plays wherever you click.

And if you saw a certain position last time, it will continue to play from that position next time.

So here comes the question: If it takes a long time to download a large video, how can you click on a location to quickly play the video at that location?

I wrote an article on range requests earlier, that is, not downloading the entire content of the resource, but only downloading the part of the range corresponding to the range.

Is the fast playback of the video also based on the range?

Let's review the range request first:

Bring range when requesting:

The server will return a 206 status code, and the Content-Range header represents which part of the entire resource is currently downloaded:

Content-Length here is the length of the current content, and Content-Range is the total length of the resource and the range of the current resource.

For more information about Range, please read this article: Concurrent Download of File Partitions Based on HTTP Range!

Is the video at station b fast played using Range?

Let's try it out on Zhihu's video first:

Just open a video page, such as this one:

Then open devtools, refresh the page, and drag the progress bar, you can see that there is indeed a 206 status code:

We can enter status-code:206 in the search box to filter it out:

Here's a trick called filters:

Can be filtered according to method, domain, mime-type, etc.

  • has-response-header: filter responses containing a header request

  • method: Filter requests based on request methods such as GET and POST

  • domain: Filter by domain name

  • status-code: Filter requests whose response code is xxx, such as 404, 500, etc.

  • larger-than: Filter requests that exceed the size, such as 100k, 1M

  • mime-type: Filter requests of a certain mime type, such as png, mp4, json, html, etc.

  • resource-type:根据请求分类来过滤,比如 document 文档请求,stylesheet 样式请求、fetch 请求,xhr 请求,preflight 预检请求

  • cookie-name:过滤带有某个名字的 cookie 的请求

当然,这些不需要记,输入一个 - 就会提示所有的过滤器:

但是这个减号之后要去掉,它是非的意思:

和右边的 invert 选项功能一样。

然后点开状态码为 206 的请求看一下:

确实,这是标准的 range 请求。

我点击进度条到后面的位置,可以看到发出了新的 range 请求:

那这些 range 请求有什么关系呢?

我们需要分析下 Content-Range,但是一个个点开看不直观。

这时候可以自定义显示的列:

右键单击列名,可以勾选展示的 header,不过这里面没有我们想要的 header,需要自定义:

点击 Manage Header Columns

添加自定义的 header,输入 Content-Range:

这时候就可以直观的看出这些 range 请求的范围之间的关系:

点击 Content-Range 这一列,升序排列。

我们刷新下页面,从头来试一下:

随着视频的播放,你会看到一个个 range 请求发出:

这些 range 请求是能连起来的,也就是说边播边下载后面的部分。

视频进度条这里的灰条也在更新:

当你直接点击后面的进度条:

观察下 range,是不是新下载的片段和前面不连续了?

也就是说会根据进度来计算出 range,再去请求。

那这个 range 是完全随意的么?

并不是。

我们当前点击的是 15:22 的位置:

我刷新下页面,点击 15:31 的位置:

如果是任意的 range,下载的部分应该和之前的不同吧。

但是你观察下两次的 range,都是 2097152-3145727

也就是说,视频分成多少段是提前就确定的,你点击进度条的时候,会计算出在哪个 range,然后下载对应 range 的视频片段来播放。

那有了这些视频片段,怎么播放呢?

浏览器有一个 SourceBuffer 的 api,我们在 MDN 看一下:

大概是这样用的:

也就是说,可以一部分一部分的下载视频片段,然后 append 上去。

拖动进度条的时候,可以把之前的部分删掉,再 append 新的:

我们验证下,搜索下代码里是否有 SourceBuffer:

按住 command + f 可以搜索请求内容:

可以看到搜索出 3 个结果。

在其中搜索下 SourceBuffer:

可以看到很多用到 SourceBuffer 的方法,基本可以确认就是基于 SourceBuffer 实现的。

也就是说,知乎视频是通过 range 来请求部分视频片段,通过 SourceBuffer 来动态播放这个片段,来实现的快速播放的目的。具体的分段是提前确定好的,会根据进度条来计算出下载哪个 range 的视频。

那服务端是不是也要分段存储这些视频呢?

确实,有这样一种叫做 m3u8 的视频格式,它的存储就是一个个片段 ts 文件来存储的,这样就可以一部分一部分下载。

不过知乎没用这种格式,还是 mp4 存储的,这种就需要根据 range 来读取部分文件内容来返回了:

再来看看 b 站,它也是用的 range 请求的方式来下载视频片段:

大概 600k 一个片段:

下载 600k 在现在的网速下需要多久?这样播放能不快么?

相比之下,知乎大概是 1M 一个片段:

网速不快的时候,体验肯定是不如 b 站的。

而且 b 站用的是一种叫做 m4s 的视频格式:

它和 m3u8 类似,也是分段存储的,这样提前分成不同的小文件,然后 range 请求不同的片段文件,速度自然会很快。

然后再 command + f 搜索下代码,同样是用的 SourceBuffer:

这样,我们就知道了为什么 b 站视频播放的那么快了:

m4s stores videos in segments, dynamically downloads a certain video segment through range request, and then dynamically plays this segment through SourceBuffer.

Summarize

We analyzed the reason why the video playback speed of station b and Zhihu is very fast.

The conclusion is to dynamically request a segment of the video through range, and then dynamically play this segment through SourceBuffer.

This range is determined in advance, and the video of which range to download will be calculated according to the progress bar.

When playing, it will download the following range while playing, and when adjusting the progress, it will also start downloading from the corresponding range.

The way the server stores these video clips, the m4s used by station b, of course, can also use m3u8, or like Zhihu, dynamically read part of the content of the mp4 file and return it.

In addition to the conclusion, the debugging process is also very important:

We use the status-code filter to filter requests except for the 206 status code.

The Content-Range is displayed directly in the list through a custom column:

Searched the content of the response by command + f:

This article is a comprehensive application of these debugging techniques.

When you watch Bilibili and Zhihu videos in the future, will you remember that it is segmented download and playback based on range?

For more debugging techniques, please see my debugging booklet "Cheats for Front-End Debugging and Customs Clearance"

Guess you like

Origin juejin.im/post/7255110638154072120