录像流技术方案
目前录像流支持
- wasm(simd)软解码 + canvas(webgl/webgpu)渲染
- wcs硬解码+ canvas(webgl)渲染、 video渲染
- mse硬解码 + video渲染
录像流集成建议
涉及到录像流的 api 有:
- playback(url,options)
- playbackPause(isPause)
- pause()
- playbackResume()
- forward()
- normal()
- updatePlaybackForwardMaxRateDecodeIFrame(rate)
- setPlaybackStartTime(timestamp)
- getPlaybackCurrentRate()
- playbackClearCacheBuffer()
- isPlaybackPause()
- updatePlaybackLocalOneFrameTimestamp()
- setPlaybackShowPrecision(showPrecision)
- playbackCurrentTimeScroll()
涉及到录像流的 事件 有:
- playbackSeek
- playbackStats
- playbackTimestamp
- playbackPauseOrResume
- playbackPreRateChange
- playbackRateChange
- playbackRateChange
具体集成的demo 可以看 playback-demo.html文件
调用的是playback方法。
jessibuca.playback(url, {
// config
})
也可以直接在 new JessibucaPro()的时候,传入playbackConfig参数。 这样可以不会二次初始化播放器,提升加载速度。
jessibuca = new JessibucaPro({
playType: 'playbackTF',
playbackConfig: {
// config
}
})
jessibuca.playback(url)
业务类型
动态(根据流)(默认)
播放器的倍率播放完全是根据服务器端推流的速率,如果服务器端推的是一倍率且动态码率的流, 播放器就按照一倍率动态码率渲染。例如 fps会15到30之间跳动 。 (画面会看着卡顿)
定频(本地设置)
播放器端会提前缓存数据,然后根据流数据计算出fps,不会随着流的速率变化而变化。 支持设置播放器的播放倍率(提高渲染倍率)。在推流码率足够的情况下,播放器会按照固定倍率播放
需要配置参数如下:
// 推荐写法
const jessibucaPro = new JessibucaPro({
playType: 'playbackTF',
playbackConfig: {
isUseFpsRender:true, // 开启固定fps渲染
isCacheBeforeDecodeForFpsRender:true, // 开启解码前缓存数据
isPlaybackPauseClearCache:false, // playbackPause是否清除缓存数据
// 可选参数
uiUsePlaybackPause:true, // ui上面的暂停按钮,控制playbackPause方法
}
})
// 或者
// 调用playback 方法, 不推荐,会重置播放器。
jessibucaPro.playback(url, {
isUseFpsRender: true, // 开启固定fps渲染
isCacheBeforeDecodeForFpsRender: true, // 开启解码前缓存数据
isPlaybackPauseClearCache: false, // playbackPause是否清除缓存数据
// 可选参数
uiUsePlaybackPause: true, // ui上面的暂停按钮,控制playbackPause方法
})
特殊回放流/点播文件
特点如下:
- 会按照特定倍率推流(可能是几倍率,或者直接把文件推过来)
- 推流结束了之后会停止推流,并且关闭连接。
- 推流的码率是不固定的。
配置参数如下:
const jessibucaPro = new JessibucaPro({
playType: 'playbackTF',
isSpecialPlaybackVod: true, // 开启特殊回放流/点播文件模式
playbackConfig: {
// 配置参数
}
})
优点
改模式下支持:
- 暂停缓存数据
- 本地实现倍率播放
- 播放结束监听(
playbackEnd事件) - 逐帧播放
- 逐关键帧(I帧)
缺点
1.不支持seek操作
demo
看playback-simple-demo-forward.html示例。
配置硬解码(wcs、mse)
目前播放器支持回放流使用wcs、mse硬解码
jessibucaPro.playback(url, {
// 其他参数
useMSE: true // 使用mse解码
})
jessibucaPro.playback(url, {
// 其他参数
useWCS: true // 使用wcs解码
})
24H时间进度条
24小时UI配置
录像流支持配置24小时组件(支持放大和缩小)和倍率按钮
对于配置参数
jessibucaPro.playbackConfig('url', {
playList: [
{
start: 16538416340000,
end: 16538434200000
},
{
start: 1653881963,// 会自动补齐最后四个0
end: 1653885397 // 会自动补齐最后四个0
}
]
})
未匹配到的时间,在ui上面是不可触发点击事件的。
24小时UI点击事件(seek)
当点击了ui上面的时间,会触发playbackSeek事件,可以监听这个事件,来进行业务逻辑处理。
jessibucaPro.on('playbackSeek', (data) => {
// data 为对象
/**
* {
"hour": 1,
"min": 2,
"second": 0
}
*/
// 拿到时间,去调用服务器端的seek接口
})
业务代码可以根据回调的数据,来进行通知服务器端进行seek 操作。
当服务器端更新完了流,可以调用setPlaybackStartTime方法,来更新ui的时间。
总体流程: 点击ui获取时间->调用服务器端seek接口->服务器端更新流->调用setPlaybackStartTime方法更新ui时间
setPlaybackStartTime的参数是时间戳,单位是秒。指的是当天的时间戳。播放器拿到这个时候,会转换成 时 分 秒, 然后会更新UI 上面的当前时间。
例如 需要更新的时间是3:10:10
// 3:10:10
const time = new Date().setHours(3, 10, 10, 0)
jessibucaPro.setPlaybackStartTime(time)
ui 也支持更新当前的进度时间
特殊场景
会存在一种场景,就是seek之后,流媒体服务器端还有一小戳的seek之前的数据推给前端,因为前端24H UI 上面的时间戳是根据流里面的时间戳做 当对值得到的。
所以如果立马触发setPlaybackStartTime方法的话,会就有可能因为还是seek之前的时间戳,等seek之后的时间戳到来的时候,UI上面的时间会直接跳的不对。
解决方案:延迟些时间去触发setPlaybackStartTime方法。大概500ms左右。
setTimeout(() => {
jessibucaPro.setPlaybackStartTime(timestamp)
}, 500)
24小时UI配置(更新)开始时间
在录播流播放模式下,底部的开始时间是从playList[0].start开始的。
如果想要配置指定的开始时间,可以在playback(config) 配置下config.startTime 参数即可。
所以,如果需要配置开始时间,可以调用setPlaybackStartTime方法。
// 3:10:10
const time = new Date().setHours(3, 10, 10, 0)
jessibucaPro.setPlaybackStartTime(time)
这样配置了之后,UI上面的开始时间就是从3:10:10开始一秒一秒的走。
同样的在触发
seek事件的时候,需要在ui上面更新开始时间,也是调用这个方法。
固定时长进度条
固定时长UI配置
只需要配置playbackConfig的controlType 字段为 simple即可
jessibucaPro.playbackConfig('url', {
controlType: 'simple'
})
时长
jessibucaPro.playbackConfig('url', {
duration: 60 * 60 * 1000 // 1小时
})
固定时长UI点击事件(seek)
当点击了ui上面的时间,会触发playbackSeek事件,可以监听这个事件,来进行业务逻辑处理。
jessibucaPro.on('playbackSeek', (data) => {
// data 为对象
/**
* {
ts:100 // 秒单位
}
*/
// 拿到时间,去调用服务器端的seek接口
})
固定时长UI配置(更新)开始时间
在录播流播放模式下,底部的开始时间是从0开始的。
所以,如果需要配置开始时间,可以调用setPlaybackStartTime方法。
// 比如要到1:30 位置
const time = 1 * 60 + 30
jessibucaPro.setPlaybackStartTime(time)
这样配置了之后,UI上面的开始时间就是从1:30开始一秒一秒的走。
同样的在触发
seek事件的时候,需要在ui上面更新开始时间,也是调用这个方法。
暂停
暂停(不关闭流)
会存在业务上面的录像流暂停,但是不关闭流的需求,这个时候可以调用播放器的
playbackPause()方法,来暂停播放。
暂停的时候会把缓存的数据清空。所以如果不清除缓存可以设置参数
isPlaybackPauseClearCache:false。
- 调用服务器端暂停接口
- 调用播放器的
playbackPause()方法,画面暂停 - 调用服务器端恢复接口
- 调用播放器的
playbackResume()方法,画面播放
暂停(关闭流)
可以调用 playbackPause(true) 或者 pause() 方法。
倍率
倍率播放
- 首先调用服务器端接口,开启倍率播放
- 调用播放器的
forward(rate)方法 - 关闭倍率播放,调用服务器端接口
- 调用播放器的
forward(1)或者normal()方法
倍率播放(配置了rateConfig 参数)
- 首先选择UI上面的倍率按钮,配合
playbackPreRateChange事件,拿到倍率, - 调用服务器端接口,开启倍率播放。
- 调用播放器的
forward(rate)方法。
固定倍率播放
会存在一些流,服务器端的录像流的推过来的fps不是定码率的,是波动性比较大的那种,这个时候就需要客户端去按照固定倍率去播放。
则需要配置使用固定fps进行渲染,并配置 fps 值。默认值是 25
playbackConfig:{
isUseFpsRender:true,
fps: '', // fps值
}
可能会存在fps 设置的不对的情况,所以播放器也会去校正这个参数。
播放器也会根据流的时间戳来计算出准确的
fps大小,并更新到fps参数上面去。
其他
seek操作(配合UI)
- 点击时间轴UI,监听
playbackSeek获取时间 - 调用服务器端seek接口
- 服务器端更新流
- 调用
setPlaybackStartTime方法,更新ui时间
seek操作(自己的UI)
如果是用的业务自己的UI
- 调用服务器端seek接口
- 服务器端更新流
- 调用
playbackClearCacheBuffer()方法,清除缓存数据
如果不清除缓存数据,如果服务器端seek之后的数据不是首帧i帧,会导致播放器端渲染绿屏或者播放异常报错。调用了这个方法,播放器内部会清除缓存数据,并重新等待首帧i帧。
解码前缓存数据
会存在有些流,一倍率的时候,但是服务器端推送的流是2倍的,这个时候就需要播放器兼容这种特殊的流,只解码所需的1倍率的数据。
可以通过在调用playback方法播放的时候,config配置参数里面配置isCacheBeforeDecodeForFpsRender 参数
jessibucaPro.playback(url, {
isCacheBeforeDecodeForFpsRender: true
})
监听当前缓存情况(需要通知服务器端暂停推流)
目前播放器的缓存数据有,待解码,解码后待渲染 两块缓存数据,单位帧。
待解码缓存数据,是指,从服务器端获取到的数据,还没有解码的数据。
解码后待渲染缓存数据,是指,解码后的数据,还没有渲染的数据。
可以通过监听stats事件,来获取当前的缓存数据。
playbackVideoBuffer 指的是视频待渲染帧。
demuxBuffer 指的是视频待渲染帧。
可以通过stats 事件里面的fps 来计算出每一帧的时间,然后计算出缓存的时间。
假设fps 为25,那么每一帧的时间就是 1000 / 25 = 40ms。
如果playbackVideoBuffer 为100,那么缓存的时间就是 100 * 40 = 4000ms = 4s。如果demuxBuffer 为100,那么缓存的时间就是 100
- 40 = 4000ms = 4s。 那么一起的缓存时长就是 4 + 4 = 8s
stats 事件里面已经添加了
playbackCacheDataDuration值。
使用播放器本地时间戳回调时间(从0 开始那种)
存在一些流,流里面的时间戳不是从 0 开始的,这个时候,如果录像流想要从0 开始回调时间。
这个时候就需要配置 playbackConfig 参数
playbackConfig:{
isUseLocalCalculateTime:true, // 是否使用本地时间来计算playback时间
localOneFrameTimestamp:40 // 一帧的间隔时间戳 40ms 100 / 25 = 40ms
}
业务层通过监听playbackTime,playbackTimestamp 事件,可以监听到当前播放的时间。
当前录像流的播放时间
可以监听playbackTimestamp事件,来获取当前播放的时间。
jessibucaPro.on('playbackTimestamp', (data) => {
// data 为对象
/**
* {
"hour": 1,
"min": 2,
"second": 0,
"ts": "" // 时间戳
}
*/
})
配置I帧解码
过滤掉其他帧,只解码I帧,可以提高播放性能。
当倍率达到播放器性能瓶颈的时候,就需要配置I帧解码,来提高播放性能。
可以通过调用updatePlaybackForwardMaxRateDecodeIFrame方法,来配置I帧解码。
播放器默认I帧解码的倍率是4倍。
// 当倍率达到3倍的时候,就触发i帧解码。
jessibucaPro.updatePlaybackForwardMaxRateDecodeIFrame(3)
支持在播放前后调用,
播放结束
因为播放器播放的是流,所以播放器并不知道这个流的结束时间是多少。
播放器唯一能做的就是监听超时。
需要业务层自己结合后端接口来实现。
希望playbackPause() -> playbackResume() 方法不要清除缓存
暂停->恢复的时候,支持从暂停的时间点开始继续播放。
目前只支持wasm软解码。
需要配置
isPlaybackPauseClearCache:false; // playbackPause是否清除缓存数据
isCacheBeforeDecodeForFpsRender:true; // 是否在解码前缓存数据
isUseFpsRender:true // 是否使用固定的fps渲染