AWS WebRTC: 判断viewer端拉流是否稳定的算法

在使用sdk-c viewer端进行拉流的过程中,viewer端拉取的是视频帧和音频帧,不会在播放器中播放,所以要根据收到的流来判断拉流过程是否稳定流畅。

我这边采用的算法是:依据相邻帧之间的时间间隔是否落在期望值的 ±20% 范围内。

音频帧、视频帧的日志打印如下:

bash 复制代码
 07:19:26.263 VERBOSE sampleAudioFrameHandler(): Audio Frame received. TrackId: 140092278368896, Size: 160, Flags 3210729368
2025-06-12 07:19:26.283 VERBOSE sampleAudioFrameHandler(): Audio Frame received. TrackId: 140092278368896, Size: 160, Flags 3210729368
2025-06-12 07:19:26.298 VERBOSE sampleVideoFrameHandler(): Video Frame received. TrackId: 140092278368896, Size: 4458, Flags 3210729368
2025-06-12 07:19:26.303 VERBOSE sampleAudioFrameHandler(): Audio Frame received. TrackId: 140092278368896, Size: 160, Flags 3210729368
2025-06-12 07:19:26.323 VERBOSE sampleAudioFrameHandler(): Audio Frame received. TrackId: 140092278368896, Size: 160, Flags 3210729368
2025-06-12 07:19:26.338 VERBOSE sampleVideoFrameHandler(): Video Frame received. TrackId: 140092278368896, Size: 263, Flags 3210729368
2025-06-12 07:19:26.343 VERBOSE sampleAudioFrameHandler(): Audio Frame received. TrackId: 140092278368896, Size: 160, Flags 3210729368
2025-06-12 07:19:26.363 VERBOSE sampleAudioFrameHandler(): Audio Frame received. TrackId: 140092278368896, Size: 160, Flags 3210729368

假设期望帧间隔是 20ms,那接收的帧间隔必须在lower和upper之间:

  • lower = 16ms (-20%)
  • upper = 24ms (+20%)

之后遍历相邻的时间戳,计算差值即时间间隔:

  • 如果任何一个间隔超出 [16, 24]ms 范围,就返回 1(表示不稳定)
  • 如果所有间隔都在范围内,最后返回 0(表示稳定)

这里有一点需要注意,如果时间间隔大于最大值很好理解,肯定是卡顿了,那么小于最小值为什么也被认定为不稳定呢,时间差越短不是越流畅吗?这里解释一下:

比如视频帧率是 25fps,对应每帧间隔约 40ms。如果某几帧突然变成 20ms,就不是"按帧率稳定输出"的表现。帧率不稳,会导致:

  • 音视频不同步
  • 抖动(jitter)
  • 丢帧或重复帧等问题

流媒体播放是"节奏感"不是"越快越好"

一个稳定的视频流(比如 25fps)应该每 40ms 一帧。如果突然来一帧只间隔了 10ms,会导致播放器不知道该怎么同步音视频,甚至可能触发跳帧。过快或过慢,都是对稳定性的破坏。

时间间隔短,可能是网络包聚合延迟之后一次性发送多帧的情况。

举个例子

假设有个节拍器本来每 1 秒"哒"一下,现在突然 0.1 秒"哒哒哒"3 次,那么它可能出故障了!

代码实现

bash 复制代码
# 判断帧时间是否稳定(±20%)
is_stable() {
    local expected_ms=$1
    shift
    local -a times=("$@")
    local lower=$((expected_ms * 8 / 10))
    local upper=$((expected_ms * 12 / 10))

    for ((i = 1; i < ${#times[@]}; i++)); do
        local diff=$((times[i] - times[i-1]))
        if (( diff < lower || diff > upper )); then
            return 1
        fi
    done
    return 0
}

第一个参数 $1:是期望的帧间隔(单位是毫秒),比如:

  • 音频帧:20 毫秒
  • 视频帧:40 毫秒

后续参数:是一系列时间戳(以毫秒为单位),代表每帧的接收时间。这些时间戳是在分析viewer端日志的过程中,获取到的n帧视频帧时间戳和n帧音频帧时间戳。

后续参数跟这行代码相关:

bash 复制代码
local -a times=("$@")

这行代码是将函数传入的参数中除了第一个参数外的所有参数,作为一个数组存入 times 变量中。因为前面 shift 过一次了,所以 $@ 就是"剩余的所有时间戳列表"。

例如:

bash 复制代码
is_stable 20 1000 1020 980 1010

进入函数后:

$1 = 20(期望间隔)

shift 移除 $1 后

$@ 就变成了:1000 1020 980 1010

times=("$@") 等价于:times=(1000 1020 980 1010)

如果去掉 -a,也不会出错,但那样会变成普通字符串变量,只存第一个参数值:

bash 复制代码
local times=("$@")  # 结果是 times=1000

-a 是告诉 Bash:"我要定义一个数组变量"。

总结就是

部分 含义
local 声明这是一个函数内的局部变量,不影响外部环境
-a 表示这个变量是一个数组类型
times 数组变量的名字
=("$@") 把函数剩余的所有参数(即 $@)作为数组元素赋值给 times

前一篇:https://blog.csdn.net/zhang_jiamin/article/details/149053832?spm=1011.2415.3001.5331

相关推荐
--运维实习生--28 分钟前
shell脚本第二阶段-----选择结构
linux·运维·shell编程
Clownseven44 分钟前
Linux服务器健康检查Shell脚本:一键生成自动化巡检报告
linux·服务器·自动化
时间裂缝里的猫-O-1 小时前
@Linux问题 :bash fork Cannot allocate memory 错误分析与解决方案
linux·chrome·bash
躺不平的小刘1 小时前
从YOLOv5到RKNN:零冲突转换YOLOv5模型至RK3588 NPU全指南
linux·python·嵌入式硬件·yolo·conda·pyqt·pip
愚昧之山绝望之谷开悟之坡2 小时前
| `cat /etc/os-release` | 发行版详细信息(如 Ubuntu、CentOS) |
linux·ubuntu·centos
mysla2 小时前
嵌入式学习day34-网络-tcp/udp
服务器·网络·学习
明天见~~3 小时前
Linux下的网络编程
linux·运维·网络
NEXU53 小时前
Linux:网络层IP协议
linux·网络·tcp/ip
Aczone283 小时前
Linux 软件编程(九)网络编程:IP、端口与 UDP 套接字
linux·网络·网络协议·tcp/ip·http·c#
倔强的石头_3 小时前
【Linux指南】Makefile入门:从概念到基础语法
linux