如何在Chrome中从MediaRecorder录制的音频中添加预定义长度?

问题描述 投票:13回答:3

我正在使用内置的MediaRecorder替换RecordRTC,以便在Chrome中录制音频。然后在带有音频api的程序中播放录制的音频。我无法使audio.duration属性工作。它说

如果视频(音频)是流式传输且没有预定义长度,则返回“Inf”(无限)。

使用RecordRTC,我不得不使用ffmpeg_asm.js将音频从wav转换为ogg。我的猜测是在RecordRTC设置预定义音频长度的过程中的某个地方。有没有办法使用MediaRecorder设置预定义的长度?

javascript google-chrome html5-audio mediarecorder recordrtc
3个回答
25
投票

这是一个chrome bug

FF确实暴露了录制媒体的持续时间,如果您将录制媒体的currentTime设置为超过其实际的duration,则该属性可用于Chrome ...

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>

因此,这里的建议是为bug report加注星标,以便铬的团队需要一些时间来修复它,即使这种解决方法可以解决问题......


2
投票

感谢@Kaiido识别错误并提供工作修复。

我准备了一个名为get-blob-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')

0
投票

Chrome中的一个错误,在2016年检测到,但仍然在今天(2019年3月)开放,是此行为背后的根本原因。在某些情况下,audioElement.duration将返回Infinity

Chrome Bug信息herehere

以下代码提供了一种避免该错误的解决方法。

用法:创建你的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 ) 
});
© www.soinside.com 2019 - 2024. All rights reserved.