【问题标题】:MediaRecorder - How to play chunk/blob of video without black frames?MediaRecorder - 如何播放没有黑框的视频块/块?
【发布时间】:2018-04-03 23:52:58
【问题描述】:

借助 MediaRecorder,我正在录制我的网络摄像头的短视频大约 1 秒。录制视频后,我将其推入桌子。最后,当我将一些视频推送到表格中时,我会尝试一个接一个地阅读每个视频(就像只有一个大视频)。但是我在每个视频之间都有某种黑框。就好像视频之间的过渡并不完美,阅读时断断续续。有人能告诉我它发生的原因吗?

这是我用来录制小视频的部分代码:

                navigator.mediaDevices.getUserMedia().then(function(media_stream) {
                    var recorder = new MediaRecorder(media_stream);

                    recorder.start();

                    recorder.onstart = function(event) {
                        setTimeout(function(){ recorder.stop(); }, 1000);
                    };

                    recorder.ondataavailable = event => {
                        tableau_rondelle.push(event.data);
                    };

                    recorder.onstop = function(event) {
                        recorder.start();
                    };
                })
                .catch(function(error) {});

这是我用来阅读大视频的部分代码:

                            document.getElementById("video_output").onended = function(event) {
                                document.getElementById("video_output").src = URL.createObjectURL(tableau_rondelle.shift());
                            };

                            document.getElementById("video_output").src = URL.createObjectURL(tableau_rondelle.shift());

谢谢。

【问题讨论】:

  • 解码器正在视频之间重新初始化。自定义格式可能需要不会重置解码器的自定义播放器。
  • 在 Szatmary:感谢您的帮助!好的,那么您是在告诉我更改录音的格式吗?目前我正在使用 mimeType: "video/webm" 录制视频。我必须更改此信息吗?
  • 你需要告诉玩家这是一个碎片化的格式。像 HLS 或 DASH。
  • 我是全新的 html 5 自适应流媒体。假设我想在服务器上设置一个自适应流媒体源,以便在 HTML5 媒体元素中使用。应使用 MPEG-DASH。但这就是我想要的吗?我想从客户端 A 录制短视频,然后通过 websocket 将视频发送到客户端 B。然后,从客户端 B 的目标是一个接一个地播放该文件,每个视频之间没有黑帧(即没有重置解码器)。我不想将文件保存在服务器上,然后将其流式传输到客户端。您认为 MPEG-DASH 仍然是解决方案吗?
  • 堆栈溢出不是本次讨论的正确位置。你应该做一些研究并自己决定什么是你项目的最佳解决方案,讨论应该在另一个论坛上进行。堆栈溢出最好作为一个问题,每页一个答案。祝你好运。

标签: video blob mediarecorder chunks mediastream


【解决方案1】:

尝试使用 recorder.pause 和 recorder.resume 来解决这个问题。

【讨论】:

    【解决方案2】:

    黑框与您使用 MediaRecorder 制作视频这一事实无关。
    只是onend事件和加载视频的整个过程不同步造成的:

    // We will first load all the video's data in order to avoid HTTP request timings
    console.log('generating all blobURIs');
    const base_url = "https://dl.dropboxusercontent.com/s/";
    const urls = [
      'ihleenn2498tg0a/BigChunksBunny0.mp4',
      'hyeredbcn60feei/BigChunksBunny1.mp4',
      'sjd141zuyc6giaa/BigChunksBunny2.mp4',
      'i9upd28ege6di7s/BigChunksBunny3.mp4',
      'nf43s8jnzk0rmkt/BigChunksBunny4.mp4',
      'mewgtcucsq3lrgq/BigChunksBunny5.mp4',
      '4qan8epsratlkxo/BigChunksBunny6.mp4',
      '28a6i3646ruh5dt/BigChunksBunny7.mp4',
      'prwo7uyqbjbfrc5/BigChunksBunny8.mp4',
      'n2ak9x3zuww8yf4/BigChunksBunny9.mp4',
      '12ic7m1cgmjrygc/BigChunksBunny10.mp4'
    ].map(url => fetch(base_url + url)
      .then(response => response.blob())
      .then(blob => URL.createObjectURL(blob))
    );
    Promise.all(urls)
      .then(urls => {
        console.log('generated all blobURIs');
        var current = -1;
    
        var timings = {
          timeupdate: null,
          end: null,
          play: null
        };
        vid.onended = playNext;
        vid.ontimeupdate = ontimeupdate;
        vid.onplaying = logTimings;
        playNext();
    
        function playNext() {
          timings.end = performance.now(); // save the current time
    
          if (++current >= urls.length) return;
          vid.src = urls[current];
          vid.play();
        }
    
        function ontimeupdate() {
          // timeupdate fires just before onend
          if (this.currentTime >= vid.duration) {
            timings.timeupdate = performance.now();
          }
        }
    
        function logTimings() {
          if (!timings.timeupdate) return;
          timings.play = performance.now();
          console.log('took ' + (timings.end - timings.timeupdate) + 'ms to fire onend event');
          console.log('took ' + (timings.play - timings.end) + 'ms to start the video');
          console.log('black frame lasted ' + (timings.play - timings.timeupdate) + 'ms');
        }
      });
    <video id="vid" autoplay></video>

    那么如何规避呢? 好吧,这并不容易,因为我们无法确定开始播放视频需要多长时间。

    可以肯定的是,将所有视频预加载到他们自己的视频播放器中会对您有所帮助。
    另一件可能有帮助的事情是在上一个视频的最后一帧显示之前触发下一个视频的播放,因此在 timeupdate 事件中。

    console.log('preloading all videos');
    const base_url = "https://dl.dropboxusercontent.com/s/";
    const vids = [
      'ihleenn2498tg0a/BigChunksBunny0.mp4',
      'hyeredbcn60feei/BigChunksBunny1.mp4',
      'sjd141zuyc6giaa/BigChunksBunny2.mp4',
      'i9upd28ege6di7s/BigChunksBunny3.mp4',
      'nf43s8jnzk0rmkt/BigChunksBunny4.mp4',
      'mewgtcucsq3lrgq/BigChunksBunny5.mp4',
      '4qan8epsratlkxo/BigChunksBunny6.mp4',
      '28a6i3646ruh5dt/BigChunksBunny7.mp4',
      'prwo7uyqbjbfrc5/BigChunksBunny8.mp4',
      'n2ak9x3zuww8yf4/BigChunksBunny9.mp4',
      '12ic7m1cgmjrygc/BigChunksBunny10.mp4'
    ].map(url => fetch(base_url + url)
      .then(response => response.blob())
      .then(blob => URL.createObjectURL(blob))
      // convert all these urls directly to video players, preloaded
      .then(blobURI => {
        const vid = document.createElement('video');
        vid.src = blobURI;
        return vid.play()
          .then(() => {
            vid.pause();
            vid.currentTime = 0;
            return vid;
          });
      })
    );
    
    Promise.all(vids)
      .then(vids => {
        console.log('preloaded all videos');
        let current = -1;
        let vid = vids[0];
        vids.forEach(vid => {
          vid.onended = onend;
          vid.ontimeupdate = ontimeupdate;
        });
        document.body.appendChild(vid);
        playNext();
    
        function playNext() {
          if (++current >= vids.length) return;
          let next = vids[current];
          vid.replaceWith(next);
          vid = next;
          vid.play();
        }
    
        function onend() {
          if (!chck.checked) {
            playNext();
          }
        }
    
        function ontimeupdate() {
          // timeupdate fires just before onend
          if (chck.checked) {
            if (this._ended) return;
            let buffer_time = 400 / 1000; // this is completely arbitrary...
            if (this.currentTime >= this.duration - buffer_time) {
              this._ended = true;
              playNext();
            }
          }
        }
      });
    <label>update in timeupdate<input type="checkbox" id="chck"></label><br>

    但由于您自己的问题是使用 MediaRecorder API 录制的视频,并且您想要播放整个序列,因此只需并行使用多个 MediaRecorder:每个片段一个,一个用于完整视频。

    请注意,您可以随意pauseresume 录制。

    navigator.mediaDevices.getUserMedia({video: true}).then(stream => {
      window.stream = stream;
      vid.srcObject = stream;
      vid.play();
      recordSegments(stream);
      recordFull(stream);
      });
    const segments = [];
    function recordSegments(stream){
      let int = setInterval(()=>{
        if(segments.length >= 10){
          clearInterval(int);
          stream.getTracks().forEach(t=>t.stop());
          return;
          }
        const chunks = [];
        const rec = new MediaRecorder(stream);
        rec.ondataavailable = e => chunks.push(e.data);
        rec.onstop = e => segments.push(new Blob(chunks));
        rec.start();
        setTimeout(()=>rec.stop(), 1000);
      }, 1000);
    }
    
    function recordFull(stream){
      const chunks = [];
      const rec = new MediaRecorder(stream);
      rec.ondataavailable = e => chunks.push(e.data);
      rec.onstop = e => exportAll(new Blob(chunks));
      rec.start();
      setTimeout(()=>rec.stop(), 10000);
      }
    
    function exportAll(full){
      vid.remove();
      segments.unshift(full);
      segments.forEach(blob=>{
        const vid = document.createElement('video');
        vid.src = URL.createObjectURL(blob);
        vid.controls = true;
        document.body.appendChild(vid);
      });
    }
    <video id="vid"></video>

    作为fiddle,因为 StackSnippets 可能会阻止 gUM 请求。

    【讨论】:

    • 你说只是onend事件和加载视频的全过程不同步造成的。可以肯定的是,将所有视频预加载到他们自己的视频播放器中会对您有所帮助。所以我尝试使用2个视频播放器叠加。在播放器 1 上播放第一个视频时,播放器 2 被隐藏,第二个视频被暂停。当 player1 结束时, player2 变为可见并正在播放。然后 Player1 被隐藏和暂停等。它允许我不间断地显示每个视频或黑框。幻觉是完美的。感谢您帮助我更好地了解我的问题并解决它。
    • @Bernard,是的,如果你没有声音,多玩家之夜做得很好。
    猜你喜欢
    • 2018-03-19
    • 2016-10-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-08
    • 1970-01-01
    相关资源
    最近更新 更多