Как использовать MediaRecorder в качестве MediaSource - PullRequest
8 голосов
/ 22 января 2020

Как упражнение в изучении WebRT C Я пытаюсь показать локальную веб-камеру и показывать ее с отложенным воспроизведением. Чтобы достичь этого, я пытаюсь передать записанные большие двоичные объекты в BufferSource и использовать соответствующий MediaSource в качестве источника для видеоэлемента.

// the ondataavailable callback for the MediaRecorder
async function handleDataAvailable(event) {
  // console.log("handleDataAvailable", event);
  if (event.data && event.data.size > 0) {
    recordedBlobs.push(event.data);
  }

  if (recordedBlobs.length > 5) {
    if (recordedBlobs.length === 5)
      console.log("buffered enough for delayed playback");
    if (!updatingBuffer) {
      updatingBuffer = true;
      const bufferedBlob = recordedBlobs.shift();
      const bufferedAsArrayBuffer = await bufferedBlob.arrayBuffer();
      if (!sourceBuffer.updating) {
        console.log("appending to buffer");
        sourceBuffer.appendBuffer(bufferedAsArrayBuffer);
      } else {
        console.warn("Buffer still updating... ");
        recordedBlobs.unshift(bufferedBlob);
      }
    }
  }
}
// connecting the media source to the video element
recordedVideo.src = null;
recordedVideo.srcObject = null;
recordedVideo.src = window.URL.createObjectURL(mediaSource);
recordedVideo.controls = true;
try {
  await recordedVideo.play();
} catch (e) {
  console.error(`Play failed: ${e}`);
}

Весь код: https://jsfiddle.net/43rm7258/1/

Когда я запускаю это в Chromium 78, я получаю NotSupportedError: Failed to load because no supported source was found. от play элемента видеоэлемента.

Я понятия не имею, что я делаю неправильно или как поступить на данный момент.

Это примерно что-то похожее, но мне не помогает: MediaSource случайным образом останавливает видео

Этот пример был моей отправной точкой: https://webrtc.github.io/samples/src/content/getusermedia/record/

1 Ответ

9 голосов
/ 27 января 2020

В итоге

Работать с Firefox и Chrome очень просто: вам просто нужно добавить аудиокод c в список кодеков! video/webm;codecs=opus,vp8

Заставить его работать в Safari значительно сложнее. MediaRecorder - это «экспериментальная» функция, которую необходимо включить вручную в настройках разработчика. После включения в Safari отсутствует метод isTypeSupported, поэтому вам нужно с этим справиться. Наконец, независимо от того, что вы запрашиваете у MediaRecorder, Safari будет всегда вручать вам файл MP4 - который не может быть передан потоковым способом, как WEBM. Это означает, что вам необходимо выполнить трансмуксинг в JavaScript для преобразования формата видео контейнера на лету

Android должен работать, если Chrome работает

iOS не поддерживает Media Source Расширения, поэтому SourceBuffer не определено в iOS, и все решение не будет работать

Исходное сообщение

Глядя на опубликованную вами JSFiddle, одно быстрое исправление, прежде чем мы начнем:

  • Вы ссылаетесь на переменную errorMsgElement, которая никогда не определяется. Вы должны добавить <div> на страницу с соответствующим идентификатором, а затем создать строку const errorMsgElement = document.querySelector(...), чтобы захватить ее

Теперь при работе с расширениями Media Source и MediaRecorder следует отметить, что поддержка будет сильно отличаться для каждого браузера. Несмотря на то, что это «стандартизированная» часть HTML5 spe c, она не очень согласована на разных платформах. По моему опыту, заставить MediaRecorder работать в Firefox не требуется слишком много усилий, заставить его работать в Chrome немного сложнее, заставить его работать в Safari чертовски почти невозможно, и заставить его работать на iOS буквально не то, что вы можете сделать.

Я прошел и отладил это для каждого браузера и записал свои шаги, чтобы вы могли понять некоторые инструменты, доступные вам при отладке проблем со СМИ

Firefox

Когда я проверил ваш JSFiddle в Firefox, я увидел следующую ошибку в консоли:

NotSupportedError: аудиодорожка не может быть записано: video / webm; codecs = vp8 указывает на неподдерживаемый код c

Я напоминаю, что VP8 / VP9 были большими толчками со стороны Google и поэтому могут не работать в Firefox, поэтому я попытался сделать одну небольшую настройку вашего кода. Я удалил параметр , options) из вашего звонка на new MediaRecorder(). Это говорит браузеру использовать любой код c, который он хочет, так что вы, вероятно, получите различный вывод в каждом браузере (но он должен по крайней мере работать в каждом браузере)

Это сработало в Firefox, поэтому я проверил Chrome.

Chrome

На этот раз я получил новую ошибку:

(индекс): 409 Uncaught (в обещании) DOMException: Не удалось выполнить 'appendBuffer' для 'SourceBuffer': этот SourceBuffer был удален из родительского источника мультимедиа. на MediaRecorder.handleDataAvailable (https://fiddle.jshell.net/43rm7258/1/show/: 409: 22 * ​​1058 *)

Поэтому я направился к chrome: // media-internals / в моем браузере и увидел это:

Код аудиопотока c opus не соответствует кодекам SourceBuffer.

В своем коде вы указываете видеокод c (VP9 или VP8) но не аудиокод c, поэтому MediaRecorder позволяет браузеру выбирать любой аудиокод c. Похоже, что в MediaRecorder Chrome по умолчанию выбирается "opus" в качестве аудиокода c, но SourceBuffer Chrome по умолчанию выбирает что-то другое. Это было тривиально исправлено. Я обновил ваши две строки, которые задают options.mimeType следующим образом:

  • options = { mimeType: "video/webm;codecs=opus, vp9" };
  • options = { mimeType: "video/webm;codecs=opus, vp8" };

Поскольку вы используете тот же options объект для объявления MediaRecorder и SourceBuffer, добавление аудиокода c в список означает, что SourceBuffer теперь объявлен с допустимым аудиокодом c, а видео воспроизводится

. Для хорошей проверки я протестировал новый код (с аудиокодом c) на Firefox. Это сработало! Таким образом, мы получаем 2 на 2, просто добавив аудиокод c в список options (и оставив его в параметрах объявления MediaRecorder)

Похоже, что VP8 и opus работают в Firefox, но не являются значениями по умолчанию (хотя в отличие от Chrome, значения по умолчанию для MediaRecorder и SourceBuffer одинаковы, поэтому удаление параметра options полностью сработало)

Safari

На этот раз мы получили сообщение об ошибке, с которым мы не можем работать:

Необработанное обещание Отклонение: ReferenceError: Невозможно найти переменную: MediaRecorder

Первое Я сделал Google Safari MediaRecorder, который обнаружил эту статью . Я думал, что попробую, поэтому посмотрел. Конечно же:

Safari Properties

Я нажал на это, чтобы включить MediaRecorder, и в консоли было встречено следующее:

Необработанное отклонение обещания: TypeError: MediaRecorder.isTypeSupported не является функцией. (В 'MediaRecorder.isTypeSupported (options.mimeType)' MediaRecorder.isTypeSupported не определено)

Так что в Safari нет метода isTypeSupported. Не волнуйтесь, мы просто скажем: «Если этот метод не существует, предположим, что это Safari, и установите соответствующий тип»

  if (MediaRecorder.isTypeSupported) {
    options = { mimeType: "video/webm;codecs=vp9" };
    if (!MediaRecorder.isTypeSupported(options.mimeType)) {
      console.error(`${options.mimeType} is not Supported`);
      errorMsgElement.innerHTML = `${options.mimeType} is not Supported`;
      options = { mimeType: "video/webm;codecs=vp8" };
      if (!MediaRecorder.isTypeSupported(options.mimeType)) {
        console.error(`${options.mimeType} is not Supported`);
        errorMsgElement.innerHTML = `${options.mimeType} is not Supported`;
        options = { mimeType: "video/webm" };
        if (!MediaRecorder.isTypeSupported(options.mimeType)) {
          console.error(`${options.mimeType} is not Supported`);
          errorMsgElement.innerHTML = `${options.mimeType} is not Supported`;
          options = { mimeType: "" };
        }
      }
    }
  } else {
    options = { mimeType: "" };
  }

Теперь мне просто нужно было найти mimeType, который поддерживается Safari. Некоторый легкий поиск в Google предполагает, что H.264 поддерживается, поэтому я попытался:

options = { mimeType: "video/webm;codecs=h264" };

Это успешно дало мне MediaRecorder started, но не получилось в строке addSourceBuffer с новой ошибкой:

NotSupportedError: Операция не поддерживается.

Я буду продолжать пытаться диагностировать, как это работает в Safari, но пока у меня есть как минимум адреса Firefox и Chrome

Обновление 1

Я продолжал работать над Safari. К сожалению, в Safari не хватает инструментов Chrome и Firefox для углубления во внутренние медиа-ресурсы, поэтому приходится много догадок.

Ранее я обнаружил, что мы получаем ошибку "Операция не поддерживается "при попытке позвонить addSourceBuffer. Поэтому я создал разовую страницу, чтобы попытаться вызвать только этот метод при других обстоятельствах:

  • Может быть, добавить исходный буфер до вызова play для видео
  • Может быть добавить исходный буфер до того, как источник мультимедиа был присоединен к элементу видео
  • Возможно, добавьте исходный буфер с разными кодеками
  • et c

Я обнаружил, что проблема все еще заключалась в коде c, и сообщение об ошибке «недопустимая операция» немного вводило в заблуждение. Это были параметры , которые не были разрешены. Простая поставка «h264» работала для MediaRecorder, но SourceBuffer требовал, чтобы я передавал параметры code c .

Одна из первых вещей, которые я попробовал, - это Пример страницы MDN и копирование используемых там кодеков: 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"'. Это дало ту же ошибку «операция не разрешена». Раскрытие значения этих параметров кода c (например, что, черт возьми, делает 42E01E даже означает ?). В то время как у меня был sh, у меня был лучший ответ, во время поиска в Google я наткнулся на это сообщение StackOverflow , в котором упоминалось использование 'video/mp4; codecs="avc1.64000d,mp4a.40.2"' в Safari. Я попробовал, и ошибки консоли исчезли!

Хотя ошибки консоли исчезли, я все еще не вижу видео. Так что еще есть над чем работать.

Обновление 2

Дальнейшие исследования в отладчике в Safari (установка нескольких точек останова и проверка переменных на каждом этапе процесса) показали, что handleDataAvailable никогда не было вызывается в сафари. Похоже, что в Firefox и Chrome mediaRecorder.start(100) будет правильно следовать за spe c и вызывать ondatavailable каждые 100 миллисекунд, но Safari игнорирует параметр и буферизует все в один массивный BLOB-объект. Вызов mediaRecorder.stop() вручную вызвал вызов ondataavailable со всем, что было записано до этого момента

Я пытался использовать setInterval для вызова mediaRecorder.requestData() каждые 100 миллисекунд, но requestData не было определено в Safari (так же, как isTypeSupported не было определено). Это поставило меня в тупик.

Затем я попытался очистить весь объект MediaRecorder и создать новый каждые 100 миллисекунд, но это вызвало ошибку в строке await bufferedBlob.arrayBuffer(). Я до сих пор выясняю, почему это не удалось

Обновление 3

Одна вещь, которую я вспоминаю о формате MP4, это то, что атом "moov" необходим для воспроизведения вернуть любой контент. Вот почему вы не можете скачать среднюю половину файла MP4 и воспроизвести его. Вам необходимо скачать ВЕСЬ файл. Поэтому мне стало интересно, не является ли тот факт, что я выбрал MP4, причиной того, что я не получал регулярные обновления.

Я попытался изменить video/mp4 на несколько разных значений и получил разные результаты:

  • video/webm - операция не поддерживается
  • video/x-m4v - вел себя как MP4, я получал данные только тогда, когда .stop() вызывался
  • video/3gpp - вел себя как MP4
  • video/flv - Операция не поддерживается
  • video/mpeg - Вела себя как MP4

Все, что велось как MP4, заставило меня проверить данные, которые были на самом деле передается в handleDataAvailable. Вот когда я заметил это:

enter image description here

Неважно что Я выбрал для видео формата, Safari всегда давал мне MP4!

Внезапно я вспомнил, почему Safari был таким кошмаром, и почему я мысленно классифицировал его как «чертовски почти невозможное». Чтобы соединить несколько MP4, потребуется JavaScript transmuxer

Именно тогда я вспомнил, , это именно то, что я делал до , Я работал с MediaRecorder и SourceBuffer чуть более года go, чтобы попытаться создать JavaScript RTMP-плеер. Когда проигрыватель был готов, я хотел добавить поддержку DVR (возвращаясь к частям видео, которые уже были переданы в потоковом режиме), что я сделал с помощью MediaRecorder и сохраняя кольцевой буфер в памяти 1-секундных видеоблоков. В Safari я пропустил эти видеоблобы через преобразователь, который я кодировал, чтобы преобразовать их из MP4 в ISO-BMFF, чтобы я мог объединить их вместе.

Я могу sh Я мог бы поделиться с вами кодом, но все принадлежит моему старому работодателю - поэтому на данный момент решение для меня потеряно. Я знаю, что кто-то столкнулся с проблемой компиляции FFMPEG для JavaScript с использованием emscripten, так что вы можете воспользоваться этим.

...