问题
I want to record user's microphone 5 seconds long segments and upload each to the server. I tried using MediaRecorder and I called start() and stop() methods at 5 seconds time interval, but when I concatenate these recordings there is a "drop" sound between. So I tried to record 5 seconds segments using timeslice parameter of start() method:
navigator.mediaDevices.getUserMedia({ audio: { channelCount: 2, volume: 1.0, echoCancellation: false, noiseSuppression: false } }).then(function(stream) {
const Recorder = new MediaRecorder(stream, { audioBitsPerSecond: 128000, mimeType: "audio/ogg; codecs=opus" });
Recorder.start(5000);
Recorder.addEventListener("dataavailable", function(event) {
const audioBlob = new Blob([event.data], { type: 'audio/ogg' });
upload(audioBlob);
});
});
But only the first segment is playable. What can I do, or how can I make all blobs playable? I MUST record then upload each segment. I CAN'T make an array of blobs (because the user could record 24hours of data or even more and the data needs to be uploaded on the server while the user is recording - with a 5 seconds delay).
Thank you!
回答1:
You have to understand how media files are built.
It is not only some raw data that can be converted to either audio or video directly.
It will depend on the format chosen, but the basic case is that you have what is called metadata which are like a dictionary describing how the file is structured.
These metadata are necessary for the software that will then read the file to know how it should parse the actual data that is contained in the file.
The MediaRecorder API is in a strange position here, since it must be able to at the same time write these metadata, and also add non-determined data (it is a live recorder).
So what happens is that browsers will put the main metadata at the beginning of the file, in a way that they'll be able to simply push new data to the file, and still be a valid file (even though some info like duration will be missing).
Now, what you get in datavailableEvent.data
is only a part of a whole file, that is being generated.
The first one will generally contain the metadata, and some other data, depending on when the event has been told to fire, but the next parts won't necessarily contain any metadata.
So you can't just grab these parts as standalone files, because the only file that is generated is the one that is made of all these parts, joined together in a single Blob.
So, to your problem, you have different possible approaches:
You could send to your server the latest slices you got from your recorder in an interval, and merge these server-side.
const recorder = new MediaRecorder(stream); const chunks = []; recorder.ondataavailable = e => chunks.push(e.data); recorder.start(); // you don't need the timeslice argument setInterval(()=>{ // here we both empty the 'chunks' array, and send its content to the server sendToServer(new Blob(chunks.splice(0,chunks.length))) }, 5000);
And on your server-side, you would append the newly sent data to the being recorded file.
An other way would be to generate a lot of small standalone files, and to do this, you could simply generate a new MediaRecorder in an interval:
function record_and_send(stream) { const recorder = new MediaRecorder(stream); const chunks = []; recorder.ondataavailable = e => chunks.push(e.data); recorder.onstop = e => sendToServer(new Blob(chunks)); setTimeout(()=> recorder.stop(), 5000); // we'll have a 5s media file recorder.start(); } // generate a new file every 5s setInterval(record_and_send, 5000);
Doing so, each file will be standalone, with a duration of approximately 5 seconds, and you will be able to play these files one by one.
Now if you wish to only store a single file on server, still using this method, you can very well merge these files together on server-side too, using e.g a tool like ffmpeg.
来源:https://stackoverflow.com/questions/51325136/record-5-seconds-segments-of-audio-using-mediarecorder-and-then-upload-to-the-se