<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>jessibuca</title>
<script src="./jessibuca.js"></script>
<style>
html,
body {
height: 100%;
margin: 0px !important;
}
.page {
overflow: hidden;
padding: 0px !important;
margin: 0px !important;
}
#container {
width: 100%;
height: 100vh;
background: rgba(13, 14, 27, 0.7);
overflow: hidden;
padding: 0px !important;
margin: 0px !important;
}
</style>
</head>
<body class="page" style="margin: 0px !important">
<div class="root">
<div id="container"></div>
</div>
<script>
var $container = document.getElementById('container') // 播放器容器 若为 string ,则底层调用的是 document.getElementById('id')
var showOperateBtns = false // 是否显示按钮
var forceNoOffscreen = true //
var jessibuca = null
function isIFrame(frame) {
const { codec, data } = frame
const bytes = new Uint8Array(data)
let offset = 0
// 跳过 FLV 视频帧头(CodecID 1字节 + AVCPacketType 1字节 + CompositionTime 3字节)
offset += 1 + 1 + 3
// 查找 NAL 单元起始码(00 00 00 01 或 00 00 01)
while (offset + 3 < bytes.length) {
if (bytes[offset] === 0 && bytes[offset + 1] === 0) {
if (bytes[offset + 2] === 0 && bytes[offset + 3] === 1) {
offset += 4 // 00 00 00 01
} else if (bytes[offset + 2] === 1) {
offset += 3 // 00 00 01
}
const nalType = bytes[offset] & 0x1f // 提取 NAL 类型
// 根据编码类型判断 I 帧
if (codec === 'avc1') {
// H.264
return [1, 5].includes(nalType)
} else if (codec === 'hev1' || codec === 'hvc1') {
// H.265
return [19, 20].includes(nalType)
}
}
offset++
}
return false
}
function create() {
jessibuca = new Jessibuca({
container: $container, // 播放器容器 若为 string ,则底层调用的是 document.getElementById('id')
videoBuffer: 0.2, // 设置最大缓冲时长,单位秒,播放器会自动消除延迟
isResize: false, // 1. 当为`true`的时候:视频画面做等比缩放后,高或宽对齐canvas区域,画面不被拉伸,但有黑边。 等同于 `setScaleMode(1)` 2. 当为`false`的时候:视频画面完全填充canvas区域,画面会被拉伸。等同于 `setScaleMode(0)`
loadingText: '视频加载中', // 加载过程中文案
useMSE: false, // 是否开启MediaSource硬解码 视频编码只支持H.264视频(Safari on iOS不支持)不支持 forceNoOffscreen 为 false (开启离屏渲染)
useWCS: false, // 是否开启Webcodecs硬解码 视频编码只支持H.264视频 (需在chrome 94版本以上,需要https或者localhost环境) 支持 forceNoOffscreen 为 false (开启离屏渲染)
debug: false, // 是否开启控制台调试打
showBandwidth: false, // 是否显示显示网速
loadingTimeout: 60, // flv地址请求超时时间
operateBtns: {
// 配置操作按钮
fullscreen: showOperateBtns, // 是否显示全屏按钮
screenshot: showOperateBtns, // 是否显示截图按钮
play: showOperateBtns, // 是否显示播放暂停按钮
audio: false, // 是否显示声音按钮
recorder: false // 是否显示录制按
},
forceNoOffscreen: forceNoOffscreen, // 是否不使用离屏模式(提升渲染能力)
isNotMute: false, // 是否开启声音,默认是关闭声音播放的
hotKey: false, // 是否开启键盘快捷键 目前支持的键盘快捷键有:esc -> 退出全屏;arrowUp -> 声音增加;arrowDown -> 声音减少;
keepScreenOn: false, // 开启屏幕常亮,在手机浏览器上, canvas标签渲染视频并不会像video标签那样保持屏幕常亮
supportDblclickFullscreen: true, // 是否支持屏幕的双击事件,触发全屏,取消全屏事件
hasAudio: false,
autoWasm: true, // 在使用MSE或者Webcodecs 播放H265的时候,是否自动降级到wasm模式。
controlAutoHide: false, // 底部控制台是否自动隐藏 只有鼠标聚焦到播放器内部才会显示,移除之后,会消失。
wasmDecodeErrorReplay: true, // wasm解码报错之后,不再抛出异常,而是直接重新播放视频地址。
wcsUseVideoRender: true // webcodecs硬解码是否通过video标签渲染, 提升渲染性能。
})
jessibuca.on('audioInfo', function (audioInfo) {
console.log('audioInfo', audioInfo)
})
jessibuca.on('videoFrame', frame => {
if (isIFrame(frame)) {
console.log('首帧是 I 帧')
} else {
console.log('首帧不是 I 帧')
}
// jessibuca.destroy() // 停止解析后续数据
})
jessibuca.on('videoInfo', function (videoInfo) {
console.log('videoInfo', videoInfo)
console.log('width:', videoInfo.width, 'height:', videoInfo.height)
localStorage.setItem('videoInfoWidth', videoInfo.width)
localStorage.setItem('videoInfoHeight', videoInfo.height)
})
}
create()
// 取视频流地址
let playUrl = ''
// 1.存在地址栏
if (window.location.href.includes('playUrl')) {
playUrl = window.location.href.split('playUrl=')[1]
}
// 2.存在session缓存
else {
playUrl = window.sessionStorage.getItem('flv-url')
}
// 播放视频
jessibuca.play(playUrl)
// 销毁视频
const destroy = () => {
if (jessibuca) {
jessibuca.destroy()
}
create()
}
// 定时两分钟重新刷新视频消除延时
// window.setInterval(() => {
// destroy()
// setTimeout(() => {
// jessibuca.play(playUrl);
// })
// },1000 * 120)
</script>
</body>
</html>