Видео / аудио потоки WebRTC не синхронизированы (MediaStream -> MediaRecorder -> MediaSource -> Video Element)

Я беру MediaStream и объединяю две отдельные дорожки (видео и аудио), используя холст и API WebAudio. Сам MediaStream, похоже, не выходит из строя, но после чтения его в MediaRecorder и буферизации его в элемент видео, звук всегда будет воспроизводиться намного раньше, чем видео. Вот код, который, кажется, имеет проблему:

let stream = new MediaStream();

// Get the mixed sources drawn to the canvas
this.canvas.captureStream().getVideoTracks().forEach(track => {
  stream.addTrack(track);
});

// Add mixed audio tracks to the stream
// https://stackru.com/questions/42138545/webrtc-mix-local-and-remote-audio-steams-and-record
this.audioMixer.dest.stream.getAudioTracks().forEach(track => {
  stream.addTrack(track);
});

// stream = stream;
let mediaRecorder = new MediaRecorder(stream, { mimeType: 'video/webm;codecs=opus,vp8' });

let mediaSource = new MediaSource();
let video = document.createElement('video');
video.src = URL.createObjectURL(mediaSource);
document.body.appendChild(video);
video.controls = true;
video.autoplay = true;

// Source open
mediaSource.onsourceopen = () => {
  let sourceBuffer = mediaSource.addSourceBuffer(mediaRecorder.mimeType);

  mediaRecorder.ondataavailable = (event) => {

    if (event.data.size > 0) {
      const reader = new FileReader();
      reader.readAsArrayBuffer(event.data);
      reader.onloadend = () => {
        sourceBuffer.appendBuffer(reader.result);
        console.log(mediaSource.sourceBuffers);
        console.log(event.data);
      }
    }
  }
  mediaRecorder.start(1000);
}

AudioMixer.js

export default class AudioMixer {

  constructor() {
    // Initialize an audio context
    this.audioContext = new AudioContext();

    // Destination outputs one track of mixed audio
    this.dest = this.audioContext.createMediaStreamDestination();

    // Array of current streams in mixer
    this.sources = [];
  }

  // Add an audio stream to the mixer
  addStream(id, stream) {
    // Get the audio tracks from the stream and add them to the mixer
    let sources = stream.getAudioTracks().map(track => this.audioContext.createMediaStreamSource(new MediaStream([track])));
    sources.forEach(source => {

      // Add it to the current sources being mixed
      this.sources.push(source);
      source.connect(this.dest);

      // Connect to analyser to update volume slider
      let analyser = this.audioContext.createAnalyser();
      source.connect(analyser);
      ...
    });
  }

  // Remove all current sources from the mixer
  flushAll() {
    this.sources.forEach(source => {
      source.disconnect(this.dest);
    });

    this.sources = [];
  }

  // Clean up the audio context for the mixer
  cleanup() {
    this.audioContext.close();
  }
}

Я предполагаю, что это связано с тем, как данные помещаются в буфер MediaSource, но я не уверен. Что я делаю, что десинхронизирует поток?

3 ответа

Поздний ответ на старый пост, но может кому-то помочь...

У меня была точно такая же проблема: у меня есть видеопоток, который надо дополнить аудиопотоком. В аудиопотоке время от времени воспроизводятся короткие звуки (AudioBuffer). Все это записывается через MediaRecorder. В Chrome все работает нормально. Но в Chrome для Android все звуки воспроизводились быстро. Параметр when для play() игнорировался на Android. (audiocontext.currentTime со временем продолжал увеличиваться... - не в этом дело).

Мое решение похоже на комментарий Джейкоба 2 сен 2018 в 7:41: Я создал и подключил синусоидальный генератор с неслышимой частотой 48000 Гц, который постоянно воспроизводился в аудиопотоке во время записи. Видимо это приводит к своевременному прогрессу.

Конечная точка RTP, которая испускает несколько связанных потоков RTP, требующих синхронизации на другой конечной точке (ах), ДОЛЖНА использовать один и тот же CNAME RTCP для всех потоков, которые должны быть синхронизированы. Для этого требуется краткосрочное постоянное CNAME RTCP, которое является общим для нескольких потоков RTP и, возможно, для нескольких связанных сеансов RTP. Типичный пример такого использования происходит при синхронизации аудио- и видеопотоков в мультимедийном сеансе, когда один участник должен использовать одну и ту же запись RTCP CNAME для своего аудио-сеанса RTP и для своего видео-сеанса RTP. Другим примером может быть синхронизация уровней многоуровневого аудиокодека, где один и тот же CNAME RTCP должен использоваться для каждого уровня.

https://datatracker.ietf.org/doc/html/rfc6222#page-2

В Chrome есть ошибка, которая воспроизводит буферизованный аудиопоток с частотой 44100 кГц, даже если он закодирован с частотой 48000 (что приводит к пропускам и рассинхронизации видео). Все остальные браузеры вроде нормально работают. Вы можете изменить кодек на тот, который поддерживает кодировку 44,1 кГц, или воспроизвести файл с веб-ссылки в качестве источника (таким образом, Chrome может правильно воспроизвести его)

Другие вопросы по тегам