问题
I am working on recording speeches and converting them to downloadable WAV files. I am using Angular6 and MediaRecorder. Firstly I obtain the blob and from the blob, I get the .wav file. The problem is that the WAV file (which can be played and sounds good) loss much of its properties during the process and is not a valid WAV. It keeps being a WebM file. For further processing, I need really valid and high-quality WAV files. In the end, I get files of ~20KB, instead of bigger files of ~300KB.
My code looks like this:
//convert Blob to File. Pass the blob and the file title as arguments
var blobToFile = (theBlob: Blob, fileName:string): File => {
var b: any = theBlob;
//Add properties to the blob
b.lastModifiedDate = new Date();
b.name = fileName;
return <File>theBlob;
}
var browser = <any>navigator;
var headers = new Headers();
var audioCtx = new AudioContext();
var chunks =[];
var constraints = { audio: true, video: false };
var promisifiedOldGUM = function(constraints, successCallback, errorCallback) {
var getUserMedia = (browser.getUserMedia || browser.webkitGetUserMedia || browser.mozGetUserMedia || browser.msGetUserMedia);
// Some browsers just don't implement it - return a rejected promise with an error to keep a consistent interface
if(!getUserMedia) {
return Promise.reject(new Error('getUserMedia is not implemented in this browser'));
}
// Otherwise, wrap the call to the old navigator.getUserMedia with a Promise
return new Promise(function(successCallback, errorCallback) {
getUserMedia.call(browser, constraints, successCallback, errorCallback);
});
}
if (browser.mediaDevices.getUserMedia) {
browser.mediaDevices.getUserMedia(constraints).then((stream) => {
this.mediaRecorder = new MediaRecorder(stream);
this.mediaRecorder.onstop = function(){
var last_bit= chunks[chunks.length-1];
var blob = new Blob([last_bit], { 'type' : 'audio/wav' });
var audioURL = window.URL.createObjectURL(blob);
//convert Blob to file
var file = blobToFile(blob, "my-recording.wav");
var link = document.createElement("a");
link.href = audioURL;
link.download = 'audio_recording_' + new Date().getTime() + '.wav';
link.innerHTML = "download file";
document.body.appendChild(link);
};
And the MediaRecorder setup in typings.d.ts
file looks like this:
declare class MediaRecorder extends EventTarget {
// readonly mimeType: string;
readonly MimeType: 'audio/x-wav'; // 'audio/vnd.wav';
readonly state: 'inactive' | 'recording' | 'paused';
readonly stream: MediaStream;
ignoreMutedMedia: boolean;
videoBitsPerSecond: number;
audioBitsPerSecond: 16000//number;
ondataavailable: (event : MediaRecorderDataAvailableEvent) => void;
onerror: (event: MediaRecorderErrorEvent) => void;
onpause: () => void;
onresume: () => void;
onstart: () => void;
onstop: () => void;
constructor(stream: MediaStream);
start();
stop();
resume();
pause();
isTypeSupported(type: string): boolean;
requestData();
addEventListener<K extends keyof MediaRecorderEventMap>(type: K, listener: (this: MediaStream, ev: MediaRecorderEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
removeEventListener<K extends keyof MediaRecorderEventMap>(type: K, listener: (this: MediaStream, ev: MediaRecorderEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
}
I don't know how can change the blobToFile()
function in order to preserve the recording quality during conversion. If you can help me with this, I would appreciate very much.
回答1:
As I can see in the metadatas of the file you've linked in the comment. You use a codec (here Opus) that compresses the file.
I see two solutions:
- Reduce the compressing ratio of the codec if you can.
- Use
WAV
container with an uncompressed configuration (for example PCM)
as you described me the problem in the chat I think it's more of the second solution. I don't use mediarecorder myself but I've found this method to check if a mime type works with it.
MediaRecorder.isTypeSupported("audio/wav;codecs=MS_PCM")
then you I suggest you change the mime type when creating the Blob
to
new Blob(chunks, { 'type' : 'audio/wav; codecs=MS_PCM' });
or
new Blob(chunks, { 'type' : 'audio/wav; codecs=0' });
回答2:
You can just convert the results of your mediaRecorder
instance to wav
by creating a new Blob
. Easy but not that obvious, and only available with Chrome (I tested this with v77.0.3865.90).
Even if MediaRecorder.isTypeSupported('audio/wav')
returns false
on your browser's console you can do the following:
navigator.mediaDevices.getUserMedia({ audio: true })
.then(stream => {
const mediaRecorder = new MediaRecorder(stream);
this.mediaRecorder.ondataavailable = (e) => {
const blobDataInWebaFormat = e.data; // .weba = webaudio; subset of webm
const blobDataInWavFormat: Blob = new Blob([blobDataInWebaFormat], { type : 'audio/wav; codecs=0' });
const dataUrl = URL.createObjectURL(blobDataInWavFormat);
console.log(dataUrl); // There you can listen to your recording in a wav format
});
};
});
Hope it's useful for someone, even one year later!
来源:https://stackoverflow.com/questions/52021331/convert-blob-to-wav-file-without-loosing-data-or-compressing