录像流技术方案

目前录像流支持

  1. wasm(simd)软解码 + canvas(webgl/webgpu)渲染
  2. wcs硬解码+ canvas(webgl)渲染、 video渲染
  3. 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方法
})

特殊回放流/点播文件

特点如下:

  1. 会按照特定倍率推流(可能是几倍率,或者直接把文件推过来)
  2. 推流结束了之后会停止推流,并且关闭连接。
  3. 推流的码率是不固定的。

配置参数如下:

const jessibucaPro = new JessibucaPro({
    playType: 'playbackTF',
    isSpecialPlaybackVod: true,  // 开启特殊回放流/点播文件模式
    playbackConfig: {
        // 配置参数
    }
})

优点

改模式下支持:

  1. 暂停缓存数据
  2. 本地实现倍率播放
  3. 播放结束监听( playbackEnd 事件)
  4. 逐帧播放
  5. 逐关键帧(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配置

只需要配置playbackConfigcontrolType 字段为 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

  1. 调用服务器端暂停接口
  2. 调用播放器的playbackPause()方法,画面暂停
  3. 调用服务器端恢复接口
  4. 调用播放器的playbackResume()方法,画面播放

暂停(关闭流)

可以调用 playbackPause(true) 或者 pause() 方法。

倍率

倍率播放

  1. 首先调用服务器端接口,开启倍率播放
  2. 调用播放器的forward(rate)方法
  3. 关闭倍率播放,调用服务器端接口
  4. 调用播放器的forward(1)或者normal()方法

倍率播放(配置了rateConfig 参数)

  1. 首先选择UI上面的倍率按钮,配合playbackPreRateChange事件,拿到倍率,
  2. 调用服务器端接口,开启倍率播放。
  3. 调用播放器的forward(rate)方法。

固定倍率播放

会存在一些流,服务器端的录像流的推过来的fps不是定码率的,是波动性比较大的那种,这个时候就需要客户端去按照固定倍率去播放。

则需要配置使用固定fps进行渲染,并配置 fps 值。默认值是 25

playbackConfig:{
    isUseFpsRender:true,
    fps: '', // fps值
}

可能会存在fps 设置的不对的情况,所以播放器也会去校正这个参数。

播放器也会根据流的时间戳来计算出准确的fps大小,并更新到fps参数上面去。

其他

seek操作(配合UI)

  1. 点击时间轴UI,监听playbackSeek获取时间
  2. 调用服务器端seek接口
  3. 服务器端更新流
  4. 调用setPlaybackStartTime方法,更新ui时间

seek操作(自己的UI)

如果是用的业务自己的UI

  1. 调用服务器端seek接口
  2. 服务器端更新流
  3. 调用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
}

业务层通过监听playbackTimeplaybackTimestamp 事件,可以监听到当前播放的时间。

当前录像流的播放时间

可以监听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渲染