Как добавить предопределенную длину в аудио, записанную в MediaRecorder в Chrome?

Я занимаюсь заменой RecordRTC встроенным MediaRecorder для записи аудио в Chrome. Затем записанный звук воспроизводится в программе со звуком api. У меня возникла проблема с работой свойства audio.duration. В нем говорится:

Если видео (аудио) передается потоком и не имеет предопределенной длины, возвращается "Inf" (бесконечность).

С RecordRTC мне пришлось использовать ffmpeg_asm.js для преобразования звука из wav в ogg. Моя догадка где-то в процессе RecordRTC устанавливает предопределенную длину аудио. Есть ли способ установить предопределенную длину с помощью MediaRecorder?

Ответ 1

Это хром-ошибка.

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 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>

Ответ 2

Благодаря @Kaiido для определения ошибки и предложения исправления.

Я подготовил пакет npm под названием get-blob-duration, который вы можете установить, чтобы получить приятную функцию, предназначенную для обезвреживания, для выполнения грязной работы.

Использование выглядит следующим образом:

// 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')