javascriptweb-mediarecorder

Chrome MediaRecorder is randomly generating corrupted video files



I am using this code to generate small stand alone videos from a MediaRecorder:

var mediaRecorder;
var chunks;

// call this function to start the process
function startRecording(stream) {
  chunks = [];

  mediaRecorder = new MediaRecorder(stream, { mimeType: "video/webm; codecs=vp8" });

  mediaRecorder.ondataavailable = function (e) {
    chunks.push(e.data);
  };

  mediaRecorder.onstop = function () {
    actualChunks = chunks.splice(0, chunks.length);
    const blob = new Blob(actualChunks, { type: "video/webm; codecs=vp8" });
    uploadVideoPart(blob); // Upload to server
  };

  recordVideoChunk(stream);
};

function recordVideoChunk(stream) {
  mediaRecorder.start();

  setTimeout(function() {
    mediaRecorder.stop();
    recordVideoChunk(stream);
  }, 10000); // 10 seconds videos
}

The problem is that some of the files generated are considered corrupted by ffmpeg:

ffmpeg -v error -i 03_video_part_1638535340218.webm -f null -

Error while decoding stream #0:0: Invalid data found when processing input

More info about the file:

> ffprobe 03_video_part_1638535340218.webm
ffprobe version 4.4.1 Copyright (c) 2007-2021 the FFmpeg developers
  built with Apple clang version 12.0.0 (clang-1200.0.32.29)
  configuration: --prefix=/usr/local/Cellar/ffmpeg/4.4.1_3 --enable-shared --enable-pthreads --enable-version3 --cc=clang --host-cflags= --host-ldflags= --enable-ffplay --enable-gnutls --enable-gpl --enable-libaom --enable-libbluray --enable-libdav1d --enable-libmp3lame --enable-libopus --enable-librav1e --enable-librist --enable-librubberband --enable-libsnappy --enable-libsrt --enable-libtesseract --enable-libtheora --enable-libvidstab --enable-libvmaf --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libxvid --enable-lzma --enable-libfontconfig --enable-libfreetype --enable-frei0r --enable-libass --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libspeex --enable-libsoxr --enable-libzmq --enable-libzimg --disable-libjack --disable-indev=jack --enable-avresample --enable-videotoolbox
  libavutil      56. 70.100 / 56. 70.100
  libavcodec     58.134.100 / 58.134.100
  libavformat    58. 76.100 / 58. 76.100
  libavdevice    58. 13.100 / 58. 13.100
  libavfilter     7.110.100 /  7.110.100
  libavresample   4.  0.  0 /  4.  0.  0
  libswscale      5.  9.100 /  5.  9.100
  libswresample   3.  9.100 /  3.  9.100
  libpostproc    55.  9.100 / 55.  9.100
Input #0, matroska,webm, from '03_video_part_1638535340218.webm':
  Metadata:
    encoder         : Chrome
  Duration: N/A, start: 0.000000, bitrate: N/A
  Stream #0:0(eng): Video: vp8, yuv420p(progressive), 1280x1236, SAR 1:1 DAR 320:309, 60 tbr, 1k tbn, 1k tbc (default)
    Metadata:
      alpha_mode      : 1
  Stream #0:1(eng): Audio: opus, 48000 Hz, stereo, fltp (default)

The error doesn't happen always and not in all stand alone videos.

I was not able to reproduce the error using Firefox

I have tracked the error to the browser itself, creating links in the same page that I download and check through ffmpeg. So possible causes in the upload mechanism are eliminated. The file is corrupted from origin.

Looks like the error never happens in the first file.

Things I have tried

Using other codecs:


Solution

  • I moved the creation of the MediaRecorder instance to the recurrent function and now it is working properly:

    var mediaRecorder;
    var chunks;
    
    // call this function to start the process
    function startRecording(stream) {
      chunks = [];
    
      recordVideoChunk(stream);
    };
    
    function recordVideoChunk(stream) {
      mediaRecorder = new MediaRecorder(stream, { mimeType: "video/webm; codecs=vp8" });
    
      mediaRecorder.ondataavailable = function (e) {
        chunks.push(e.data);
      };
    
      mediaRecorder.onstop = function () {
        actualChunks = chunks.splice(0, chunks.length);
        const blob = new Blob(actualChunks, { type: "video/webm; codecs=vp8" });
        uploadVideoPart(blob); // Upload to server
      };
    
      mediaRecorder.start();
    
      setTimeout(function() {
        mediaRecorder.stop();
        recordVideoChunk(stream);
      }, 10000); // 10 seconds videos
    }
    

    I think there was some kind of race condition that made the event listeners (ondataavailable and onstop) to not be called in the proper order, or to have multiple calls... to be honest I don't know what is the real problem and if there is a better version of the code I can create that also works.