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.