Firefox MediaRecorder interface provides only new video data every two seconds

I am trying to send a video stream received by the WebRTC getUserMedia () method to a server for additional processing. Delay is important because I want to detect changes in the video stream and immediately update the client. For this specific use only the solution is acceptable for Firefox, so I am studying the MediaRecorder interface .

I put together a simple test case included below. There are no errors, and the ondataavailable callback call is called every 500 ms, as expected. However, for three out of four of these calls, the size of the data provided is zero. This suggests that the data is grouped into pieces for about two seconds (possibly due to the limitations of the video encoding used).

Is it possible for MediaRecorder to provide data with less granularity? If not, what is the way to get the video data from the userMedia stream to the server with a low latency? A Chrome or Firefox-specific interface would be nice, but one that worked in both would be even better.

<html>
  <body>
    <h1>MediaRecorder Test</h1>
    <video id="video" width="640" style="border: 1px solid black"></video>
  </body>
</html>

<script>
 // The variable that holds the video stream
 var mediastream = null;

 // Start video capture (and provide a way to stop it)
 navigator.mozGetUserMedia ( { video: true, audio: false },
   function(stream_arg) {
     mediastream = stream_arg;
     var vendorURL = window.URL || window.webkitURL;
     video.src = vendorURL.createObjectURL(mediastream);
     video.play();
     recordStream();
   },
   function(err) { console.log("Error starting video stream: " + err); }
 );

 // Record the stream
 var recorder = null;
 function recordStream() {
   recorder = new MediaRecorder(mediastream);
   recorder.ondataavailable = function(ev) {
     console.log("Got: "+ev.data.size);
   };
   recorder.start(500);
 }
</script>
+4
source share
2 answers

The 500 ms interval you send to MediaRecorder is advisory. To use the codec, larger pieces of data / time may be required to operate. This probably gives you data as quickly as possible.

, MediaRecorder . WebRTC , . , WebRTC, , .

, - API MediaRecorder , .

0

: N ( ) , base64. , base64. 2 :

( ). , .

index.html

<!DOCTYPE html>
<html>
<head>
<script src="record-test.js"></script>
</head>
<body>

    <video id="video"></video>
    <canvas id="canvas" style="display:none;"></canvas>
    <input type="button" id="stopRecordBtn" value="Stop recording">

</body>
</html>

-test.js:

(function() {
    'use strict';

    //you can play with these settings
    var FRAME_INTERVAL_MS = 500;  //take snapshot each 500 ms
    var FRAME_WIDTH = 320;    //width and
    var FRAME_HEIGHT = 240;   //height of resulting frame

    navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
    window.URL = window.URL || window.webkitURL;

    var video, canvas, ctx;
    var mediaStream;
    var videoRecordItv;
    var base64Frames = [];

    var init = function() {
        video = document.getElementById('video');

        canvas = document.getElementById('canvas'); //use canvas to capture a frame and convert it to base64 data
        canvas.width = FRAME_WIDTH;
        canvas.height = FRAME_HEIGHT;
        ctx = canvas.getContext('2d');

        var stopBtn = document.getElementById('stopRecordBtn');
        stopBtn.addEventListener('click', stopRecording);

        navigator.getUserMedia({video: true}, onGotStream, function(e) {console.log(e);});
    }

    var onGotStream = function(stream) {
        mediaStream = stream;
        video.src = URL.createObjectURL(mediaStream);
        video.play();

        videoRecordItv = setInterval(function() {  //capture a frame each FRAME_INTERVAL_MS milliseconds
            var frame = getBase64FrameFromVideo();
            base64Frames.push(frame);
        }, FRAME_INTERVAL_MS);
    }

    var getBase64FrameFromVideo = function() {
        ctx.drawImage(video, 0, 0, FRAME_WIDTH, FRAME_HEIGHT);
        //a canvas snapshot looks like data:image/jpeg;base64,ACTUAL_DATA_HERE
        //we need to cut out first 22 characters:
        var base64PrefixLength = 'data:image/jpeg;base64,'.length;
        return canvas.toDataURL('image/jpeg').slice(base64PrefixLength);
    }

    var stopRecording = function() {
        mediaStream && mediaStream.stop && mediaStream.stop();
        mediaStream = null;
        clearInterval(videoRecordItv);  //stop capturing video

        uploadFramesToServer();
    }

    var uploadFramesToServer = function() {
        var sid = Math.random(); //generate unique id
        var curFrameIdx = 0;  //current frame index
        (function postFrame() {
            console.log('post frame #' + curFrameIdx);

            var base64Frame = base64Frames[curFrameIdx];
            var blobFrame = base64ToBlob(base64Frame, 'image/jpeg');
            var formData = new FormData;
            formData.append('frame', blobFrame, 'upload.jpg');
            formData.append('sid', sid);
            var xhr = new XMLHttpRequest();
            //post a single frame to /postFrame url with multipart/form-data enctype
            //on the server you get "sid" param and "frame" file as you would post a file with regular html form
            xhr.open('POST', '/postFrame', true);
            xhr.onload = function(e) {
                console.log(this.response);
                if (base64Frames[++curFrameIdx]) {
                    postFrame(); //post next frame
                } else {
                    //DONE!
                    console.log('finish post frames');
                }
            };
            xhr.send(formData);
        })();
    }

    var base64ToBlob = function(base64Data, contentType, sliceSize) {
        contentType = contentType || '';
        sliceSize = sliceSize || 512;

        var byteCharacters = atob(base64Data);
        var byteArrays = [];

        for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {
            var slice = byteCharacters.slice(offset, offset + sliceSize);

            var byteNumbers = new Array(slice.length);
            for (var i = 0; i < slice.length; i++) {
                byteNumbers[i] = slice.charCodeAt(i);
            }

            var byteArray = new Uint8Array(byteNumbers);

            byteArrays.push(byteArray);
        }

        return new Blob(byteArrays, {type: contentType});
    }

    document.addEventListener('DOMContentLoaded', init);
})();

, , FFmpeg.

Chrome, Firefox.

, . !

0

Source: https://habr.com/ru/post/1547904/


All Articles