如何在Chrome中为从MediaRecorder录制的音频添加预定义的长度
How can I add predefined length to audio recorded from MediaRecorder in Chrome?
我正在用内置的MediaRecorder替换RecordRTC,以便在Chrome中录制音频。录制的音频随后在带有音频api的程序中播放。我在使audio.duration属性工作时遇到问题。上面写着
如果视频(音频)是流式传输的,并且没有预定义的长度,则返回"Inf"(无限)。
使用RecordRTC,我不得不使用ffmpeg_asm.js将音频从wav转换为ogg。我的猜测是RecordRTC正在设置预定义的音频长度。有没有办法使用MediaRecorder设置预定义的长度?
这是一个chrome错误。
FF确实公开了录制媒体的持续时间,如果您确实将录制媒体的currentTime
设置为大于其实际duration
,则该属性在chrome中可用。。。
var recorder,
chunks = [],
ctx = new AudioContext(),
aud = document.getElementById('aud');
function exportAudio() {
var blob = new Blob(chunks);
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);
}
}
// we need user-activation
document.getElementById('button').onclick = function(evt){
getData();
this.remove();
}
<button id="button">start</button>
<audio id="aud" controls></audio><span id="log"></span>
因此,这里的建议是在错误报告中添加星号,这样chromium的团队就需要一些时间来修复它,即使这种变通方法可以奏效。。。
感谢@Kaiido识别错误并提供有效修复。
我准备了一个名为getblob-duration的npm包,您可以安装它来获得一个很好的Promise包装函数来完成脏工作。
用法如下:
// Returns Promise<Number>
getBlobDuration(blob).then(function(duration) {
console.log(duration + ' seconds');
});
或ECMAScript 6:
// yada yada async
const duration = await getBlobDuration(blob)
console.log(duration + ' seconds')
Chrome中的一个错误是这种行为背后的根本原因,该错误于2016年检测到,但今天(2019年3月)仍然存在。在某些情况下,audioElement.duration
将返回Infinity
。
Chrome Bug此处和此处的信息
以下代码提供了避免该错误的解决方法
用法:创建audioElement
,并一次性调用此函数,提供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 )
});
感谢@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)
})
}
我封装了webm持续时间修复包来解决webm长度问题,它可以在nodejs和web浏览器中使用,以支持2GB以上的视频文件,而不会占用太多内存。
用法如下:
import fixWebmDuration from 'webm-duration-fix';
const mimeType = 'video/webm';codecs=vp9';
const blobSlice: BlobPart[] = [];
mediaRecorder = new MediaRecorder(stream, {
mimeType
});
mediaRecorder.ondataavailable = (event: BlobEvent) => {
blobSlice.push(event.data);
}
mediaRecorder.onstop = async () => {
// fix blob, support fix webm file larger than 2GB
const fixBlob = await fixWebmDuration(new Blob([...blobSlice], { type: mimeType }));
// to write locally, it is recommended to use fs.createWriteStream to reduce memory usage
const fileWriteStream = fs.createWriteStream(inputPath);
const blobReadstream = fixBlob.stream();
const blobReader = blobReadstream.getReader();
while (true) {
let { done, value } = await blobReader.read();
if (done) {
console.log('write done.');
fileWriteStream.close();
break;
}
fileWriteStream.write(value);
value = null;
}
blobSlice = [];
};
//如果你想完全修改视频文件,可以使用这个包"webmFixDuration";,其他方法仅应用于视频标签的显示级别。使用此方法,修改完整的视频文件
webmFixDuration github示例
mediaRecorder.onstop = async () => {
const duration = Date.now() - startTime;
const buggyBlob = new Blob(mediaParts, { type: 'video/webm' });
const fixedBlob = await webmFixDuration(buggyBlob, duration);
displayResult(fixedBlob);
};
- 将音频添加到我的网页
- 如何使用Bootstrap和Heroku为Rails应用程序添加隐藏的播放音频
- 使用Javascript为HTML5音频添加循环
- 正在添加音频以单击事件
- 如何通过音频切换将背景音乐添加到网页中
- 正在尝试在HTML5中添加音频元素
- WebRTC:在SDP中没有捆绑线路的音频通话中添加视频
- 使用javascript和jquery将“下一个”和“上一个”按钮添加到来自JSON的HTML5音频播放器
- 如何在花式盒子灯箱中为每张幻灯片添加音频
- 如何将淡入/淡出方法添加到 JS 音频对象
- 在chrome中动态添加的音频html无法正常播放,在Firefox中正常工作
- 向音频元素添加事件监听器;不起作用
- 网络音频Api:我如何添加一个工作卷积器
- 如何在WebRTC视频聊天中添加音频/视频静音/取消静音按钮
- Javascript添加事件侦听器以更改音频音量
- Rails4向Audio_tag添加多个音频文件
- 音频标签,点击链接时添加src
- 可以将addEventListener添加到播放的音频标签中,但不能设置onplay功能
- 添加混响效果到我的音频文件
- 如何在Chrome中为从MediaRecorder录制的音频添加预定义的长度