How to play WEBM files individually which are created by MediaRecorder

后端 未结 2 1044
眼角桃花
眼角桃花 2020-12-18 10:18

For recording audio and video, I am creating webm files under the ondataavailable of MediaRecorder API. I have to play each created webm file individually.

相关标签:
2条回答
  • 2020-12-18 10:50

    Okay looks like this is not as easy as you have to scan through the blob to find the magic value.

    let offset = -1;
    let value = 0;
    const magicNumber = parseInt("0x1F43B675".match(/[a-fA-F0-9]{2}/g).reverse().join(''), 16)
    
    while(value !== magicNumber) {
      offset = offset + 1;
    
      try {
        const arr = await firstChunk.slice(offset, offset + 4).arrayBuffer().then(buffer => new Int32Array(buffer));
        value = arr[0];
      }
      catch(error) {
        return;
      }
    }
    
    offset = offset + 4;
    

    The answer is 193 199

    const header = firstChunk.slice(0, offset);
    const blobType = firstChunk.type;
    const blob = new Blob([header, chunk], { type: blobType });
    

    And there you have it. Now question is how did I get this number? Why is it not multiple of 42?

    Brute force

    Well the logic is simple, record the video, gather chunks, slice the first chunk, compute new blob and try to play it with HTMLVideoElement. If it fails increase the offset.

    (async() => {
    
        const microphoneAudioStream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
    
        const mediaRecorder = new MediaRecorder(microphoneAudioStream);
        let chunks = [];
    
        mediaRecorder.addEventListener('dataavailable', (event) => {
            const blob = event.data;
            chunks = [...chunks, blob];
        });
    
        mediaRecorder.addEventListener("stop", async () => {
            const [firstChunk, ...restofChunks] = chunks;
            const [secondBlob] = restofChunks;
            const blobType = firstChunk.type;
    
            let index = 0;
            const video = document.createElement("video");
    
            while(index < 1000) {
                const header = firstChunk.slice(0, index);
                const blob = new Blob([header, secondBlob], { type: blobType });
                const url = window.URL.createObjectURL(blob);
    
                try {
                    video.setAttribute("src", url);
                    await video.play();
                    console.log(index);
                    break;
                }
                catch(error) {
    
                }
    
                window.URL.revokeObjectURL(url);
    
                index++;
            }
        })
    
        mediaRecorder.start(200);
    
        const stop = () => {
            mediaRecorder.stop();
        }
    
        setTimeout(stop, 400)
    
    })();
    

    I noticed that for smaller timeslice param in MediaRecorder.start and timeout param in setTimeout the header offset becomes 1. Sadly still not 42.

    0 讨论(0)
  • 2020-12-18 10:53

    // for the most regular webm files, the header information exists

    // between 0 to 189 Uint8 array elements

    Without seeing the actual file data it's hard to say, but this is possibly wrong. The "header information" needs to be everything up to the first Cluster element. That is, you want to keep all data from the start of the file up to before you see 0x1F43B675 and treat it as initialization data. This can/will vary from file to file. In my test file, this occurs a little after 1 KB in.

    and perpended this header information into second chunk, still the second chunk could not play, but this time the browser is showing poster (single frame) of video and duration of sum of two chunks, eg:10 seconds; duration of each chunk is 5 second.

    The chunks output from the MediaRecorder aren't relevant for segmentation, and can occur at various times. You would actually want to split on the Cluster element. That means you need to parse this WebM file, at least to the point of splitting out Clusters when their identifier 0x1F43B675 comes by.

    Is there any way that we can play the webm files/chunks individually ?

    You're on the right path, just prepend everything before the first Cluster to a later Cluster.

    Once you've got that working, the next problem you'll likely hit is that you won't be able to do this with just any cluster. The first Cluster must begin with a keyframe or the browser won't decode it. Chrome will skip over to the next cluster, to a point, but it isn't reliable. Unfortunately, there's no way to configure keyframe placement with MediaRecorder. If you're lucky enough to be able to process this video server-side, here's how to do it with FFmpeg: https://stackoverflow.com/a/45172617/362536

    0 讨论(0)
提交回复
热议问题