最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Seeking is not working in recorded video with MediaRecorder API - Stack Overflow

programmeradmin2浏览0评论

I'm trying to build a screen recording with MediaRecorder API.

As Suggestive MediaRecorded Approach

var chunks = [];
var recorder = new MediaRecorder(stream);

recorder.streams = [stream];

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

recorder.onstop = function(){
      var blob = new Blob(chunks, {type: "video/webm"});
      chunks = [];

      var mimeType = 'video/webm';
      var fileExtension = 'webm';

      var file = new File([blob ? blob : ''], getFileName(fileExtension), {
          type: mimeType
      });
};

Using this approach recording is working fine, but recorded video seeking is not working.

I had done some searching on web regarding this problem, I came across that video header doesn't containing duration.

On printing file object on console it contains following properties,

lastModified : 1527592713006
lastModifiedDate : Tue May 29 2018 16:48:33 GMT+0530 (India Standard Time) 
name : "Recording-May,29 2018 4:48:33 PM.webm"
size : 1971220
type : "video/webm"
webkitRelativePath : ""

One can see file object doesn;t contained duration property.

Can anyone suggest any javascript library available which can repairs video header on client side only while preparing the video file?

I'm trying to build a screen recording with MediaRecorder API.

As Suggestive MediaRecorded Approach

var chunks = [];
var recorder = new MediaRecorder(stream);

recorder.streams = [stream];

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

recorder.onstop = function(){
      var blob = new Blob(chunks, {type: "video/webm"});
      chunks = [];

      var mimeType = 'video/webm';
      var fileExtension = 'webm';

      var file = new File([blob ? blob : ''], getFileName(fileExtension), {
          type: mimeType
      });
};

Using this approach recording is working fine, but recorded video seeking is not working.

I had done some searching on web regarding this problem, I came across that video header doesn't containing duration.

On printing file object on console it contains following properties,

lastModified : 1527592713006
lastModifiedDate : Tue May 29 2018 16:48:33 GMT+0530 (India Standard Time) 
name : "Recording-May,29 2018 4:48:33 PM.webm"
size : 1971220
type : "video/webm"
webkitRelativePath : ""

One can see file object doesn;t contained duration property.

Can anyone suggest any javascript library available which can repairs video header on client side only while preparing the video file?

Share Improve this question asked May 29, 2018 at 14:15 Akshay RathoreAkshay Rathore 8291 gold badge10 silver badges23 bronze badges 1
  • As per this bug report the length info will be missing in Chrome. Firefox seems to have solved the problem. Can you confirm you're only having the problem on Chrome? – octavn Commented Jul 13, 2018 at 12:15
Add a comment  | 

4 Answers 4

Reset to default 8

get a look at getSeekableBlob at https://recordrtc.org/

this is the code:

function getSeekableBlob(inputBlob, callback) {
    // EBML.js copyrights goes to: https://github.com/legokichi/ts-ebml
    if (typeof EBML === 'undefined') {
        throw new Error('Please link: https://cdn.webrtc-experiment.com/EBML.js');
    }
    var reader = new EBML.Reader();
    var decoder = new EBML.Decoder();
    var tools = EBML.tools;
    var fileReader = new FileReader();
    fileReader.onload = function(e) {
        var ebmlElms = decoder.decode(this.result);
        ebmlElms.forEach(function(element) {
            reader.read(element);
        });
        reader.stop();
        var refinedMetadataBuf = tools.makeMetadataSeekable(reader.metadatas, reader.duration, reader.cues);
        var body = this.result.slice(reader.metadataSize);
        var newBlob = new Blob([refinedMetadataBuf, body], {
            type: 'video/webm'
        });
        callback(newBlob);
    };
    fileReader.readAsArrayBuffer(inputBlob);
}

This is a well known bug in Chrome. Basically, the duration of the recorded media isn't added to the headers of the final file.

Sadly, this bug is currently marked as WontFix by the Chromium team. However, there are a couple of workarounds:

  • On the backend, using ffmpeg to fix the headers: ffmpeg -i old.webm output.webm

  • On the frontend, the workaround on this answer or using the package ts-ebml

A few years have passed and there is still no official solution. The best solution I've tried so far is this webm-duration-fix for those who need it. It supports fixing recording files larger than 2GB and has a low memory footprint when fixing.

import fixWebmDuration from 'webm-duration-fix';

const mimeType = 'video/webm\;codecs=vp9';
const blobSlice: BlobPart[] = [];

mediaRecorder = new MediaRecorder(stream, {
  mimeType
});

mediaRecorder.ondataavailable = (event: BlobEvent) => {
  blobSlice.push(event.data);
}

mediaRecorder.onstop = async () => {  
    // fix blob, support fix webm file larger than 2GB
    const fixBlob = await fixWebmDuration(new Blob([...blobSlice], { type: mimeType }));
    // to write locally, it is recommended to use fs.createWriteStream to reduce memory usage
    const fileWriteStream = fs.createWriteStream(inputPath);
    const blobReadstream = fixBlob.stream();
    const blobReader = blobReadstream.getReader();
  
    while (true) {
      let { done, value } = await blobReader.read();
      if (done) {
        console.log('write done.');
        fileWriteStream.close();
        break;
      }
      fileWriteStream.write(value);
      value = null;
    }
    blobSlice = [];
};

This is a cleaner modern version that uses Promise instead of FileReader of the current answer, that answer works great for me btw:

async function getSeekableBlobAsync(arrayBuffer) {
    const reader = new EBML.Reader();
    const decoder = new EBML.Decoder();
    const tools = EBML.tools;

    const ebmlElms = decoder.decode(arrayBuffer);
    ebmlElms.forEach((element) => {
        reader.read(element);
    });
    reader.stop();
    const refinedMetadataBuf = tools.makeMetadataSeekable(reader.metadatas, reader.duration, reader.cues);
    const body = arrayBuffer.slice(reader.metadataSize);
    const newBlob = new Blob([refinedMetadataBuf, body], {
        type: "audio/webm",
    });

    return newBlob;
}

// To use with a Blob:
async getSeekableBlobFromBlobAsync(blob) {
    const arr = await blob.arrayBuffer();
    return await getSeekableBlobAsync(arr);
}

发布评论

评论列表(0)

  1. 暂无评论