1

I've been playing around with the MediaRecorderAPI for a while now and I'm stuck on videos. I have built a frontend where you can add multiple video and audio streams, which will then be recorded by the API. After stopping the recording a zip is created, which can be downloaded. The frontend shows a visual reprasentation of the incoming audio (waves) and the video (live feed), which works fine so far. The download and recording process work similar for audio and video, as I built a parent class for it.

Now when I unzip my downloaded streams I can open audio (mp3) files and the metadata (duration) is available. However when I try to open the videos (webm), they do show the video but no metadata is available.

I need to sync multiple video and audio streams in the future so having to use ffmpeg locally to export every video again, just to get a duration, is annoying. I have no idea what's going wrong though.

This is my code:

class RecordHandler {

    mediaStream = null;
    chunks = [];
    exportMediaType = '';
    static zip;
    deviceName = "";

    constructor(exportMediaType) {
        this.exportMediaType = exportMediaType;
    }

    startRecording() {
        this.mediaRecorder = new MediaRecorder(this.mediaStream);
        this.mediaRecorder.ondataavailable = this.handleMediaDataAvailable.bind(this);
        this.mediaRecorder.onstop = this.handleMediaStop.bind(this);
        this.mediaRecorder.start(200);
    }

    stopRecording() {
        if (this.mediaRecorder.state == 'inactive') return;
        this.mediaRecorder.stop();
    }

    handleMediaDataAvailable(e) {
        this.chunks.push(e.data);
    }

    handleMediaStop(e) {
        const blob = new Blob(this.chunks, {
            type: this.exportMediaType
        });
        const type = this.exportMediaType.includes('video') ? '_video.webm' : '_audio.mp3';
        this.chunks = [];
        const name = this.deviceName.replace(/\s/g, '-').replace(/\(|\)|:/g, '');
        RecordHandler.zip.file(`${getDateString()}_${name}${type}`, blob);
    }
}
class VideoHandler extends RecordHandler {

    //other stuff

    constructor() {
        super('video/webm');
    }

    async createVideoInputStream() {
        this.mediaStream = await navigator.mediaDevices.getUserMedia({ video: { deviceId: this.inputDeviceId, frameRate: 30 } });
    }

    //other stuff   

}
class MicrophoneHandler extends RecordHandler {

    //other stuff

    constructor() {
        super('audio/mpeg');
        this.audioCtx = new (window.AudioContext || window.webkitAudioContext)();
        this.analyser = this.audioCtx.createAnalyser();
    }
 
    async init(audioDeviceId, outputDeviceId) {
        this.mediaStream = await navigator.mediaDevices.getUserMedia({ video: false, audio: { deviceId: audioDeviceId } });
    }

    //other stuff
}

Please note that the deviceName from the parent class is defined in another method (and therefore not visible in the code) but the name is always unique.

I did try this approach, however ontimeupdate was never called and here audio.duration logged Infinity as well, even if the download had a duration.

I'd appreciate every help.

Tabea
  • 95
  • 9

0 Answers0