0%

同步视频进度条

同步视频进度条

​ 晚上在逛b站的时候,在看《无间道》,此时要吃饭了,我就把电脑上的视频暂停了,在手机上看。发现手机上的视频观看进度同步了我在电脑上的观看进度,就突然很好奇这个功能怎么实现的,在多端环境下如何同步视频进度

抓包

刚进入网页观看视频时,通过控制台抓包查看请求数据,发现请求api.bilibili.com/x/player/v2会得到last_play_time字段,值为最近一次的观看进度,那这个值是从哪里得到的呢?

image-20220226233236588

发现会定时发送api.bilibili.com/x/click-interface/web/heartbeat的请求,其中Body参数中的played_time就包含了当前的播放进度时刻。并且在我每一次拖动进度条和点击暂停时会马上发送这个请求。此刻就解惑了,在抓包之前我猜想会是这么实现,但是又觉得定时请求后端记录进度时刻会不会服务器压力太大,毕竟B站的qps还是很大的。但heartbeat的定时并没有精确到秒,压力也没有这么大。

image-20220226224208945

分析请求

heartbeat的定时请求是多少秒的请求一次的呢,我看了半天,F12控制台并没有显示请求发送时间的功能(其实可以查看两次heartbeat请求的played_time字段间隔就可以,不用这么麻烦,刚开始没想到),没办法,只能上Fiddler专业的抓包工具了,其实Fiddler也是没有显示请求发送时间的,但是只需要加几行代码就可以自定义显示列了。

加入Fiddler后按Ctrl+R编辑CustomRules.js文件

添加/* XXXX */下的代码就可以了(根据上下文选择插入的位置)

/* 显示请求耗时 */ —— 显示请求到响应的耗时

/* 显示客户端请求时间 */ —— 显示请求发送的时间

/* 添加ip列 */ ——显示请求服务器的IP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88

import System;
import System.Windows.Forms;
import Fiddler;

class Handlers
{

.....
// You can create a custom menu like so:
/*
QuickLinkMenu("&Links")
QuickLinkItem("IE GeoLoc TestDrive", "http://ie.microsoft.com/testdrive/HTML5/Geolocation/Default.html")
QuickLinkItem("FiddlerCore", "http://fiddler2.com/fiddlercore")
public static function DoLinksMenu(sText: String, sAction: String)
{
Utilities.LaunchHyperlink(sAction);
}
*/

/* 显示请求耗时 */
function BeginRequestTime(oS: Session)
{
if (oS.Timers != null)
{
return oS.Timers.ClientBeginRequest.ToString();
}
return String.Empty;
}

public static BindUIColumn("TimeTaken")
function CalcTimingCol(oS: Session){
var sResult = String.Empty;
if ((oS.Timers.ServerDoneResponse > oS.Timers.ClientDoneRequest))
{
sResult = (oS.Timers.ServerDoneResponse - oS.Timers.ClientDoneRequest).ToString();
}
return sResult;
}

/* 显示客户端请求时间 */
function StartRequestTime(oS: Session)
{
if (oS.Timers != null)
{
return oS.Timers.ClientBeginRequest.ToString();
}
return String.Empty;
}

public static BindUIColumn("Start")
function ShowStartRequestTime(oS: Session){
var sResult = String.Empty;
if ((oS.Timers.ClientDoneRequest != null))
{
sResult = oS.Timers.ClientDoneRequest.ToString();
}
return sResult;
}

public static RulesOption("Hide 304s")
BindPref("fiddlerscript.rules.Hide304s")
var m_Hide304s: boolean = false;

.........

/*
static function OnDetach() {
MessageBox.Show("Fiddler is no longer the system proxy");
}
*/

// The Main() function runs everytime your FiddlerScript compiles
static function Main() {
var today: Date = new Date();
/* 添加ip列 */
FiddlerObject.UI.lvSessions.AddBoundColumn("ServerIP", 120, "X-HostIP");
FiddlerObject.StatusText = " CustomRules.js was loaded at: " + today;

// Uncomment to add a "Server" column containing the response "Server" header, if present
// UI.lvSessions.AddBoundColumn("Server", 50, "@response.server");

// Uncomment to add a global hotkey (Win+G) that invokes the ExecAction method below...
// UI.RegisterCustomHotkey(HotkeyModifiers.Windows, Keys.G, "screenshot");
}


...

添加完成后重启Fiddler,就可以看到这三列了(没有的在列最后面,按住往前拖就可以了)。可以看到黄颜色的请求就是heartbeat,发送的时间间隔是15秒左右,也就是每15秒就会记录当前进度条时间。

image-20220226230901263

测试猜想

本来我想看一下在请求heartbeat后的15秒之内刷新一下看看进度条同步会不会丢失这15秒之内,结果发现一刷新也会马上发送heartbeat请求记录当前进度条时刻。然后尝试直接关闭浏览器,发现也会马上请求记录,然后通过直接杀chrome后台来模拟应用突然崩溃,发现进度条只恢复到最近一次heartbeat,也就是说,应用突然崩溃不会马上请求记录了,会丢失0~15秒

文章作者:xpp011

发布时间:2022年02月26日 - 21:02

原始链接:http://xpp011.cn/2022/02/26/f9fda241.html

许可协议: 转载请保留原文链接及作者。