javascript - 使用 mp4 文件的 Uint8Array() 块调用 appendBuffer() 后,SourceBuffer 对象上触发错误事件
问题描述
我正在尝试创建一个视频播放器,我想在其中使用服务器的 Range 标头下载 MP4 文件的片段/块(我在客户端 React.js 应用程序和我的应用程序的实际资源服务器之间有一个代理服务器特定的安全原因)。
我正在正确接收缓冲区中的 mp4 文件块,我尝试在控制台上打印数据。但是一旦第一次使用文件的第一个块调用 appendBuffer() 方法,SourceBuffer 就会引发错误事件。
这是代码:
loadSegment = async () => {
if (!this.state.videoEnded) {
fetch(`http://localhost:5008/video?id=${this.state.sessionID}&byteCursor=${this.state.byte}&tok=${this.state.token}`, {
method: "GET"
}).then((res) => {
res.arrayBuffer().then(buf => {
console.log('buf', buf)
let status = res.status
console.log('status', status)
let newTime = this.state.time + (buf.byteLength / (this.state.contentLength / this.state.videoDuration))
let newByte = this.state.byte + buf.byteLength
this.setState({
time: newTime,
byte: newByte
}, () => {
videoSourceBuffer.appendBuffer(new Uint8Array(buf));
console.log('buffer appended')
if (status == 200) {
console.log('end of stream')
this.setState({
videoEnded: true
})
} else if (status == 206) {
let video = document.getElementById("video")
if (video.paused) {
// start playing after first chunk is appended
video.play();
console.log('video was paused; played')
}
console.log("this.state.token before incr", this.state.token)
this.setState({
token: parseInt(this.state.token) + 1,
time: newTime
}, () => {
console.log('token incremented; loadSegment called again')
console.log("this.state.token after incr", this.state.token)
})
}
})
}).catch(e => {
console.log(e)
})
}).catch(e => {
console.log(e)
})
}
}
如代码所示,我将 API 响应转换为 ArrayBuffer,然后将其转换为 Uint8Array,该 Uint8Array 将传递给 SourceBuffer 对象上的 appendBuffer() 方法。
这些是我的 SourceBuffer 的 updateend 和 MediaSource 的 sourceopen 事件处理程序:
mediaSourceOpenEventListener = () => {
console.log("media source open")
if (MediaSource.isTypeSupported('video/mp4; codecs="avc1.42E01E, mp4a.40.2"')) {
console.log("mp4 codec supported");
} else {
console.log("mp4 codec not supported");
}
videoSourceBuffer =
ms.addSourceBuffer('video/mp4; codecs="avc1.64001E"');
videoSourceBuffer.mode = 'sequence';
console.log("created source buffer")
videoSourceBuffer.addEventListener('updateend', this.videoSourceBufferUpdateEndListener);
videoSourceBuffer.addEventListener('error', (e) => {
console.log("Video Source Error: ", e)
});
ms.addEventListener('sourceended', (e) => {
console.log("MS sourceended event: ", e)
});
ms.addEventListener('sourceclose', (e) => {
console.log("MS sourceclose event: ", e)
});
this.loadSegment()
}
videoSourceBufferUpdateEndListener = () => {
if (this.state.videoEnded) {
videoSourceBuffer.onupdateend = null
ms.onsourceopen = null
videoSourceBuffer.abort()
}
// videoSourceBuffer.timestampOffset = this.state.time
console.log('Finished updating buffer');
this.loadSegment()
}
最后,在我的 render() JSX 中,我像这样安装了视频标签
<video loop="loop" id="video" height="320px" width="640px" controls="1" onError={(e) => {console.log(e)}}/>
这些是我得到的日志
Services.js:78 Create ms object
Services.js:80 Create ms url object
Services.js:82 got video tag
Services.js:84 src assigned to video tag
Services.js:100 media source open
Services.js:102 mp4 codec supported
Services.js:109 created source buffer
Services.js:140 buf ArrayBuffer(256001) {}
Services.js:142 status 206
Services.js:150 buffer appended
Services.js:161 video was paused; played
Services.js:163 this.state.token before incr 213843
Services.js:169 token incremented; loadSegment called again
Services.js:170 this.state.token after incr 213844
Services.js:112 Video Source Error: Event {isTrusted: true, type: "error", target: SourceBuffer, currentTarget: SourceBuffer, eventPhase: 2, …}
Services.js:130 Finished updating buffer
Services.js:115 MS sourceended event: Event {isTrusted: true, type: "sourceended", target: MediaSource, currentTarget: MediaSource, eventPhase: 2, …}
Services.js:373 SyntheticEvent {dispatchConfig: {…}, _targetInst: FiberNode, _dispatchInstances: FiberNode, nativeEvent: Event, _dispatchListeners: ƒ, …}
index.js:1 video tag error Event {isTrusted: true, type: "error", target: video#video, currentTarget: video#video, eventPhase: 2, …}
SourceBuffer 正在触发错误事件,然后立即触发 updateend 事件。我无法理解这段代码的实际问题是什么。我可能做错了什么?
解决方案
推荐阅读
- azure - 如何使用 jenkins 和 TerraForm 将 React 应用程序(github 上的代码)部署到 Azure WebApp
- python - Matplotlib.pyplot.eventplot:在事件图中使用颜色
- python - TensorFlow Federated Compression:如何实现在 TFF 的 build_federated_averaging_process 中使用的有状态编码器?
- javascript - 脚本标签的动态变化不反映页面的内容
- javascript - 使用范围列表过滤数字列表
- r - 更改串扰的 filter_slider() 函数背后的代码
- graphics - 请解释一下取消映射
- docker - docker-compose COPY 在运行 endrypoint 之前
- python - 为什么我的代码在运行时不循环?
- c# - 两个使用 MVVM 的命令按钮,但只有前 1 个触发