经典ABR算法介绍:BOLA (INFOCOM ‘16) dash.js代码实现

前言

BOLA(Buffer Occupancy based Lyapunov Algorithm)是DASH视频流中一种经典的基于播放缓冲区的(Buffer-based)ABR(自适应码率)算法,并且其改进版本是如今dash.js开源播放器的默认ABR算法。本文基于dash.js v2.6.0介绍BOLA的代码实现。关于BOLA的原理部分,可参见:BOLA (INFOCOM ’16) 核心算法逻辑

在dash.js中实现BOLA版本为BOLA-O,其文件路径为:src\streaming\rules\abr\BolaRule.js。其中,ABR算法逻辑对应getMaxIndex函数(详见:如何在dash.js中添加自定义ABR规则?)。
注:

  • 选择v2.6.0版本,一是遵从BOLA论文的建议,二是因为后续版本中实现的是BOLA-E和DYNAMIC算法
  • BOLA代码中引入了placeholder(虚拟buffer),其为后续改进版本BOLA-E的核心设计(详见:dash.js的ABR逻辑),本文不讨论这部分内容。

BOLA的三种状态

BOLA的代码实现中存在三种状态:

  • BOLA_STATE_ONE_BITRATE:特殊case,如仅有一个码率或者初始化失败
  • BOLA_STATE_STARTUP:默认初始状态,基于placeholder buffer进行码率决策
  • BOLA_STATE_STEADY:buffer足够时,基于BOLA进行决策

主要关注第三种状态即可,对应BOLA的核心逻辑。

getMaxIndex主逻辑

(注:此部分忽略placeholder相关逻辑)

getMaxIndex为dash.js的ABR算法决策函数,其在BOLA中的主流程如下:

  • 初始化
  • 获取BOLA状态:getBolaState
    • 第一次:getInitialBolaState
      • 计算BOLA参数:calculateBolaParameters
      • 置为BOLA_STATE_STARTUP
    • 否则:checkBolaStateStableBufferTime
  • 根据BOLA的状态进行码率决策
    • BOLA_STATE_STARTUP:
      • 启动阶段基于RB选择码率:getQualityForBitrate
      • 若buffer高于视频块时长,则切换为BOLA_STATE_STEADY状态
    • BOLA_STATE_STEADY:
      • 稳定阶段基于BOLA-BASIC选择码率:getQualityFromBufferLevel(BOLA的核心决策逻辑,与论文一致,不再赘述,可参见:BOLA (INFOCOM ’16) 核心算法逻辑
      • BOLA-O逻辑

参数计算

BOLA代码中有Vp和gp两个参数,分别对应其论文中 V V V γ \gamma γ两个参数与视频块时长 p p p的乘积。参数计算一般仅在会话启动前初始化阶段进行(除非目标buffer发生了变化)。

注意代码实现的参数计算与论文中给出的参考值并不一致。看代码中的注释:

// If using Math.log utilities, we can choose Vp and gp to always prefer bitrates[0] at minimumBufferS and bitrates[max] at bufferTarget.
// (Vp * (utility + gp) - bufferLevel) / bitrate has the maxima described when:
// Vp * (utilities[0] + gp - 1) === minimumBufferS and Vp * (utilities[max] + gp - 1) === bufferTarget
// giving:
const gp = (utilities[highestUtilityIndex] - 1) / (bufferTime / MINIMUM_BUFFER_S - 1);
const Vp = MINIMUM_BUFFER_S / gp;
// note that expressions for gp and Vp assume utilities[0] === 1, which is true because of normalization

可以看出,代码中参数设置的逻辑是:使得BOLA可以在缓冲区水平为minimumBufferS时选择最低码率,在bufferTarget时选择最高码率,这个设定类似于BBA的双阈值。

结合原论文,假设 M M M表示最高质量的码率级别,1表示最低质量的码率级别。 B m a x B_{max} Bmax代表选择最高码率的buffer阈值,对应视频块数量为 Q m a x Q_{max} Qmax B m i n B_{min} Bmin代表选择最低码率的buffer阈值,对应视频块数量为 Q m i n Q_{min} Qmin。令 v m v_m vm表示码率级别 m ∈ { 1 , 2 , … , M } m\in\{1,2,\dots,M\} m{ 1,2,,M}对应的效用(utility), S m S_m Sm为对应码率级别的视频块大小。

选择最低码率的参数设定需要满足:
( V ( v 1 + γ p ) − Q m i n ) / S 1 = 0 (V(v_1 + \gamma p) - Q_{min}) / S_1 = 0 (V(v1+γp)Qmin)/S1=0
两边同时乘以视频块时长 p p p,则有下式:
V p ( v 1 + γ p ) = B m i n Vp(v_1 + \gamma p) = B_{min} Vp(v1+γp)=Bmin

其中,效用 v m v_m vm基于自然对数计算,有 v 1 = 0 v_1=0 v1=0。BOLA代码实现中,在计算参数时使用的效用比基于自然对数的计算值大了1(归一化),若将此值定义为 u m u_m um,则有 u m = v m + 1 u_m=v_m+1 um=vm+1 u 1 = 1 u_1=1 u1=1,因此:
V p ( u 1 − 1 + γ p ) = B m i n (1) \tag{1} Vp(u_1 - 1 + \gamma p) = B_{min} Vp(u11+γp)=Bmin(1)

同理,在最大buffer处选择最高码率时需要满足下式:
V p ( u M − 1 + γ p ) = B m a x (2) \tag{2} Vp(u_M - 1 + \gamma p) = B_{max} Vp(uM1+γp)=Bmax(2)

考虑到 u 1 − 1 = 0 u_1-1=0 u11=0,基于式(1)有:
V p = B m i n γ p Vp = \frac{B_{min}}{\gamma p} Vp=γpBmin
将式(1)与式(2)相除,,可得:
γ p = u M − 1 B m a x / B m i n − 1 \gamma p=\frac{u_M - 1}{B_{max} / B_{min}-1} γp=Bmax/Bmin1uM1
这就是代码中参数设置的来源。

【但是效用归一化后引入了一个新问题:BOLA的实际决策逻辑和论文是一致的,这样可能会导致参数设置和决策逻辑不同,即无法在给定buffer处选到特定的码率。如果需要的保持一致的话,决策部分的效用应该也需要减一。】

相关参数:

  • B m i n B_{min} Bmin:对应MINIMUM_BUFFER_S,默认为10s
  • B m a x B_{max} Bmax:对应bufferTime,大概意思是基于MINIMUM_BUFFER_S为每档码率级别留出2s的阈值间隔
    • bufferTime = Math.max(stableBufferTime, MINIMUM_BUFFER_S + MINIMUM_BUFFER_PER_BITRATE_LEVEL_S * bitrates.length);
    • stableBufferTime:DEFAULT_MIN_BUFFER_TIME,12s(不开Fast Switch的话)
      • 这个值允许重新配置,但似乎没有被调用过
    • MINIMUM_BUFFER_PER_BITRATE_LEVEL_S:2s,应该是对应每档码率的buffer间隔

猜你喜欢

转载自blog.csdn.net/LvGreat/article/details/131212369