MediaSource,视频缓冲
MediaSource, video buffering
我想做一个低延迟的视频流服务和开源(以改进我的网络开发)。
我去年 2 月开始了这个项目,但我没有太多时间继续它。项目 该项目名为 twitch-with-nodejs。反正。
我不清楚 MediaSources 是如何工作的(我认为)。
我的代码在一个客户端中使用 MediaRecorded 并停止它以每 2 秒发送一次视频流量。然后,服务器将它放在内存中 30 秒,然后准备 return 它作为一个流。
另一个客户端想要观看流,因此他每 2 秒向服务器请求一次视频流量。然后,它适用于第一次调用,但不适用于以下调用。
这是我的代码示例:
第一个客户(主播):
this.media = await navigator.mediaDevices[what]({
audio: this.config.audio,
video: this.config.video ? {
width: this.config.width,
height: this.config.height,
frameRate: this.config.fps,
facingMode: (this.config.front ? "user" : "environment")
} : false
}).catch(e => {
console.log(e.name + ": " + e.message);
this.can = false;
});
let chunks = [];
const video = document.querySelector('video');
video.srcObject = this.media;
video.onloadedmetadata = () => {
video.play()
}
let mediaRecorder = new MediaRecorder(this.media, {"mimeType": "video/webm;"});
mediaRecorder.ondataavailable = function(ev) {
chunks.push(ev.data);
}
setInterval(() => {
mediaRecorder.stop();
mediaRecorder.start();
}, 2000);
mediaRecorder.onstop = () => {
let blob = new Blob(chunks, {'type': 'video/webm;'});
chunks = [];
const data = new FormData();
data.append('file', blob);
fetch("/postStream", {
"method": "post",
"body": data
});
}
服务器端:
app.post("/postStream", async (req, res) => {
const fileProperty = req?.files?.file;
if(!fileProperty?.data || fileProperty.mimetype !== 'video/webm') return res.json(false);
this.chunks.push({ data: fileProperty?.data, id: i++, date: Date.now() });
return res.json(true);
});
app.get("/playVideo", (req, res) => {
if(this?.chunks?.length < 1) return res.json(false);
const buffer = new Buffer.from(this.chunks?.reverse()?.[0]?.data, 'base64')
const stream = this.bufferToStream(buffer);
res.setHeader("content-type", "video/webm");
stream.pipe(res);
});
还有我的第二个客户,他想看视频 :
const mediaSource = new MediaSource();
videoPlaying.src = URL.createObjectURL(mediaSource);
mediaSource.addEventListener('sourceopen', sourceOpen);
var sourceBuffer;
const mime = "video/webm; codecs=\"vp8, vorbis\"";
function fetchSegment(){
return fetch("/playVideo").then(res => res.arrayBuffer());
}
async function sourceOpen(){
let data = await fetchSegment();
sourceBuffer = mediaSource.addSourceBuffer(mime);
sourceBuffer.appendBuffer(data);
sourceBuffer.addEventListener('updateend', onUpdateEnd);
videoPlaying.play();
}
function onUpdateEnd(){
//mediaSource.endOfStream();
//clearInterval(intervalSegment);
}
var intervalSegment = setInterval(async () => {
/* Here I catch the following error :
An attempt was made to use an object that is not, or is no longer, usable
*/
console.log(mediaSource.readyState, mediaSource.duration); // => Open, Infinit
let data = await fetchSegment();
sourceBuffer.appendBuffer(data)
console.log("Clearing memory...")
sourceBuffer.remove(0, 2);
}, 2000)
那么,为什么我会收到错误消息“试图使用一个不可用或不再可用的对象”?如何正确读取流?
好的,对于任何想知道正确遮阳篷的人来说,因为了解遮阳篷很酷。
我试图改变我的代码的工作方式。所以,观看视频的第二个客户有这个代码(现在):
function fetchSegment(){
return fetch("/playVideo").then(res => res.arrayBuffer());
}
var intervalSegment = setInterval(async () => {
if(sourceBuffer.updating) return;
let data = await fetchSegment();
if (queue.length > 0) {
queue.push(data);
} else {
sourceBuffer.appendBuffer(data);
videoPlaying.play();
}
}, 2000)
async function sourceOpen(){
let data = await fetchSegment();
queue.push(data);
sourceBuffer = mediaSource.addSourceBuffer(mime);
sourceBuffer.mode = 'sequence';
sourceBuffer.addEventListener('updateend', onUpdateEnd);
videoPlaying.play();
sourceBuffer.appendBuffer(queue.shift());
}
function onUpdateEnd(){
if (queue.length && !sourceBuffer.updating) {
sourceBuffer.appendBuffer(queue.shift())
}else{
setTimeout(() => {
onUpdateEnd();
}, 500)
}
}
所以现在,sourceBuffer 的模式是sequence
而不是segments
,如果用户更新chunck,则视频不会被推送。而且,如果队列是空的,我不会推入它,而是直接将段添加到 sourceBuffer。
我想做一个低延迟的视频流服务和开源(以改进我的网络开发)。 我去年 2 月开始了这个项目,但我没有太多时间继续它。项目 该项目名为 twitch-with-nodejs。反正。 我不清楚 MediaSources 是如何工作的(我认为)。
我的代码在一个客户端中使用 MediaRecorded 并停止它以每 2 秒发送一次视频流量。然后,服务器将它放在内存中 30 秒,然后准备 return 它作为一个流。
另一个客户端想要观看流,因此他每 2 秒向服务器请求一次视频流量。然后,它适用于第一次调用,但不适用于以下调用。
这是我的代码示例: 第一个客户(主播):
this.media = await navigator.mediaDevices[what]({
audio: this.config.audio,
video: this.config.video ? {
width: this.config.width,
height: this.config.height,
frameRate: this.config.fps,
facingMode: (this.config.front ? "user" : "environment")
} : false
}).catch(e => {
console.log(e.name + ": " + e.message);
this.can = false;
});
let chunks = [];
const video = document.querySelector('video');
video.srcObject = this.media;
video.onloadedmetadata = () => {
video.play()
}
let mediaRecorder = new MediaRecorder(this.media, {"mimeType": "video/webm;"});
mediaRecorder.ondataavailable = function(ev) {
chunks.push(ev.data);
}
setInterval(() => {
mediaRecorder.stop();
mediaRecorder.start();
}, 2000);
mediaRecorder.onstop = () => {
let blob = new Blob(chunks, {'type': 'video/webm;'});
chunks = [];
const data = new FormData();
data.append('file', blob);
fetch("/postStream", {
"method": "post",
"body": data
});
}
服务器端:
app.post("/postStream", async (req, res) => {
const fileProperty = req?.files?.file;
if(!fileProperty?.data || fileProperty.mimetype !== 'video/webm') return res.json(false);
this.chunks.push({ data: fileProperty?.data, id: i++, date: Date.now() });
return res.json(true);
});
app.get("/playVideo", (req, res) => {
if(this?.chunks?.length < 1) return res.json(false);
const buffer = new Buffer.from(this.chunks?.reverse()?.[0]?.data, 'base64')
const stream = this.bufferToStream(buffer);
res.setHeader("content-type", "video/webm");
stream.pipe(res);
});
还有我的第二个客户,他想看视频 :
const mediaSource = new MediaSource();
videoPlaying.src = URL.createObjectURL(mediaSource);
mediaSource.addEventListener('sourceopen', sourceOpen);
var sourceBuffer;
const mime = "video/webm; codecs=\"vp8, vorbis\"";
function fetchSegment(){
return fetch("/playVideo").then(res => res.arrayBuffer());
}
async function sourceOpen(){
let data = await fetchSegment();
sourceBuffer = mediaSource.addSourceBuffer(mime);
sourceBuffer.appendBuffer(data);
sourceBuffer.addEventListener('updateend', onUpdateEnd);
videoPlaying.play();
}
function onUpdateEnd(){
//mediaSource.endOfStream();
//clearInterval(intervalSegment);
}
var intervalSegment = setInterval(async () => {
/* Here I catch the following error :
An attempt was made to use an object that is not, or is no longer, usable
*/
console.log(mediaSource.readyState, mediaSource.duration); // => Open, Infinit
let data = await fetchSegment();
sourceBuffer.appendBuffer(data)
console.log("Clearing memory...")
sourceBuffer.remove(0, 2);
}, 2000)
那么,为什么我会收到错误消息“试图使用一个不可用或不再可用的对象”?如何正确读取流?
好的,对于任何想知道正确遮阳篷的人来说,因为了解遮阳篷很酷。
我试图改变我的代码的工作方式。所以,观看视频的第二个客户有这个代码(现在):
function fetchSegment(){
return fetch("/playVideo").then(res => res.arrayBuffer());
}
var intervalSegment = setInterval(async () => {
if(sourceBuffer.updating) return;
let data = await fetchSegment();
if (queue.length > 0) {
queue.push(data);
} else {
sourceBuffer.appendBuffer(data);
videoPlaying.play();
}
}, 2000)
async function sourceOpen(){
let data = await fetchSegment();
queue.push(data);
sourceBuffer = mediaSource.addSourceBuffer(mime);
sourceBuffer.mode = 'sequence';
sourceBuffer.addEventListener('updateend', onUpdateEnd);
videoPlaying.play();
sourceBuffer.appendBuffer(queue.shift());
}
function onUpdateEnd(){
if (queue.length && !sourceBuffer.updating) {
sourceBuffer.appendBuffer(queue.shift())
}else{
setTimeout(() => {
onUpdateEnd();
}, 500)
}
}
所以现在,sourceBuffer 的模式是sequence
而不是segments
,如果用户更新chunck,则视频不会被推送。而且,如果队列是空的,我不会推入它,而是直接将段添加到 sourceBuffer。