【问题标题】:MediaSource, video bufferingMediaSource,视频缓冲
【发布时间】:2021-09-29 14:52:50
【问题描述】:

我想做一个低延迟的视频流服务并开源(以改善我的网络开发)。 我去年二月开始了这个项目,但我没有太多时间继续它。项目项目名为twitch-with-nodejs。反正。 我不清楚 MediaSources 是如何工作的(我认为)。

我的代码在一个客户端中使用 MediaRecorded 并停止它以每 2 秒发送一次视频通量。然后,服务器将其放入内存中 30 秒,然后准备好将其作为流返回。

另一个客户端想要观看视频流,因此他每 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)

那么,为什么我会捕捉到错误“尝试使用不可用或不再可用的对象”?如何正确读取流?

【问题讨论】:

    标签: node.js arrays arraybuffer media-source


    【解决方案1】:

    好的,所以对于任何想知道正确的遮阳篷的人来说,了解遮阳篷很酷。

    我试图改变我的代码的工作方式。所以,观看视频的第二个客户(现在)有这个代码:

    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,如果用户更新块,视频不会被推送。而且,如果队列是空的,我不会推入,而是直接将段添加到 sourceBuffer。

    【讨论】:

      猜你喜欢
      • 2023-03-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-09
      • 1970-01-01
      相关资源
      最近更新 更多