Chromeでオーディオを録音するためにRecordRTCを組み込みのMediaRecorderに置き換える作業を行っています。録音されたオーディオは、オーディオAPIを使用してプログラムで再生されます。 audio.durationプロパティを機能させるのに問題があります。それは言う
ビデオ(オーディオ)がストリーミングされ、事前定義された長さがない場合、「Inf」(Infinity)が返されます。
RecordRTCでは、ffmpeg_asm.jsを使用してオーディオをwavからoggに変換する必要がありました。私の推測では、RecordRTCが事前定義されたオーディオの長さを設定するプロセスのどこかにあります。 MediaRecorderを使用して事前定義された長さを設定する方法はありますか?
これは chromeバグ です。
FFは、記録されたメディアの期間を公開します。記録されたメディアのcurrentTime
を実際のduration
よりも大きく設定すると、プロパティはクロムで使用できます。
var recorder,
chunks = [],
ctx = new AudioContext(),
aud = document.getElementById('aud');
function exportAudio() {
var blob = new Blob(chunks, {
type: 'audio/ogg'
});
aud.src = URL.createObjectURL(new Blob(chunks));
aud.onloadedmetadata = function() {
// it should already be available here
log.textContent = ' duration: ' + aud.duration;
// handle chrome's bug
if (aud.duration === Infinity) {
// set it to bigger than the actual duration
aud.currentTime = 1e101;
aud.ontimeupdate = function() {
this.ontimeupdate = () => {
return;
}
log.textContent += ' after workaround: ' + aud.duration;
aud.currentTime = 0;
}
}
}
}
function getData() {
var request = new XMLHttpRequest();
request.open('GET', 'https://upload.wikimedia.org/wikipedia/commons/4/4b/011229beowulf_grendel.ogg', true);
request.responseType = 'arraybuffer';
request.onload = decodeAudio;
request.send();
}
function decodeAudio(evt) {
var audioData = this.response;
ctx.decodeAudioData(audioData, startRecording);
}
function startRecording(buffer) {
var source = ctx.createBufferSource();
source.buffer = buffer;
var dest = ctx.createMediaStreamDestination();
source.connect(dest);
recorder = new MediaRecorder(dest.stream);
recorder.ondataavailable = saveChunks;
recorder.onstop = exportAudio;
source.start(0);
recorder.start();
log.innerHTML = 'recording...'
// record only 5 seconds
setTimeout(function() {
recorder.stop();
}, 5000);
}
function saveChunks(evt) {
if (evt.data.size > 0) {
chunks.Push(evt.data);
}
}
getData();
<audio id="aud" controls></audio><span id="log"></span>
したがって、ここでのアドバイスは、 バグレポート にスターを付けることです。これにより、この回避策でうまくいく場合でも、クロムのチームは修正に時間がかかります...
バグを特定し、実用的な修正を提供してくれた@Kaiidoに感謝します。
get-blob-duration というnpmパッケージを用意しました。これをインストールすると、NicePromiseでラップされた関数を取得してダーティな作業を実行できます。
使用法は次のとおりです。
// Returns Promise<Number>
getBlobDuration(blob).then(function(duration) {
console.log(duration + ' seconds');
});
またはECMAScript6:
// yada yada async
const duration = await getBlobDuration(blob)
console.log(duration + ' seconds')
ありがとう@ colxi実際のソリューションについて、いくつかの検証手順を追加しました(ソリューションは正常に機能していましたが、問題があったため)長いオーディオファイルを使用)。
長いオーディオファイルで動作するようになるまでに4時間ほどかかりましたが、検証が修正されたことがわかりました
function fixInfinity(media) {
return new Promise((resolve, reject) => {
//Wait for media to load metadata
media.onloadedmetadata = () => {
//Changes the current time to update ontimeupdate
media.currentTime = Number.MAX_SAFE_INTEGER;
//Check if its infinite NaN or undefined
if (ifNull(media)) {
media.ontimeupdate = () => {
//If it is not null resolve the promise and send the duration
if (!ifNull(media)) {
//If it is not null resolve the promise and send the duration
resolve(media.duration);
}
//Check if its infinite NaN or undefined //The second ontime update is a fallback if the first one fails
media.ontimeupdate = () => {
if (!ifNull(media)) {
resolve(media.duration);
}
};
};
} else {
//If media duration was never infinity return it
resolve(media.duration);
}
};
});
}
//Check if null
function ifNull(media) {
if (media.duration === Infinity || media.duration === NaN || media.duration === undefined) {
return true;
} else {
return false;
}
}
//USAGE EXAMPLE
//Get audio player on html
const AudioPlayer = document.getElementById('audio');
const getInfinity = async () => {
//Await for promise
await fixInfinity(AudioPlayer).then(val => {
//Reset audio current time
AudioPlayer.currentTime = 0;
//Log duration
console.log(val)
})
}
2016年に検出されたが、現在も開いているChromeのバグ(2019年3月)が、この動作の根本的な原因です。特定のシナリオではaudioElement.duration
はInfinity
を返します。
次のコードは、バグを回避するための回避策を提供します。
Usage:audioElement
を作成し、この関数を1回呼び出して、audioElement
の参照を提供します。返されたpromise
が解決されると、audioElement.duration
プロパティには正しい値が含まれている必要があります。 (videoElements
の同じ問題も修正されます)
/**
* calculateMediaDuration()
* Force media element duration calculation.
* Returns a promise, that resolves when duration is calculated
**/
function calculateMediaDuration(media){
return new Promise( (resolve,reject)=>{
media.onloadedmetadata = function(){
// set the mediaElement.currentTime to a high value beyond its real duration
media.currentTime = Number.MAX_SAFE_INTEGER;
// listen to time position change
media.ontimeupdate = function(){
media.ontimeupdate = function(){};
// setting player currentTime back to 0 can be buggy too, set it first to .1 sec
media.currentTime = 0.1;
media.currentTime = 0;
// media.duration should now have its correct value, return it...
resolve(media.duration);
}
}
});
}
// USAGE EXAMPLE :
calculateMediaDuration( yourAudioElement ).then( ()=>{
console.log( yourAudioElement.duration )
});