Использование интерфейса записи медиапотока

Этот перевод не завершён. Пожалуйста, помогите перевести эту статью с английского

Интерфейс MediaStream Recording  позволяет записывать аудио и видео потоки. При использовании с методом navigator.mediaDevices.getUserMedia(), предлагает простой способ записи данных с устройств пользователя, и немедленное их использование в веб приложениях.И аудио и видео могут быть записаны вместе или по отдельности.Цель данной статьи - предоставить базовое руководство о том как использовать объект MediaRecorder, реализующий такой програмный интерфей.

Пример приложени: Веб диктофон

An image of the Web dictaphone sample app - a sine wave sound visualization, then record and stop buttons, then an audio jukebox of recorded tracks that can be played back.

Для демонстрации основной функциональности интерфейса  MediaRecorder API, мы создали веб диктофон, позволяющий записывать отрывки аудио и проигрывать их после записи. Он визуализирует устройства ввода звука, используя интерфейс  Web Audio API. В этой статье будем концентрироваться на функциональности записи и воспроизведения.

Посмотрите получившуюся работающую демонстрацию, или скачайте исходники на GitHub.

CSS плюшки

Разметка HTML довольно проста, поэтому не будем ее рассматривать подробно, но есть более интересные места в CSS, которые стоило бы отметить, и о которых поговорим ниже. Если вам не интересна тема CSS , и хотите сразу приступить к  JavaScript, то перейдите к части основных настроек приложения Basic app setup.

Сохраняйте интерфейс ограниченным областью просмотра, независимо от высоты устройства, с функцией calc()

Функция calc одна из полезных утилит возникших в  CSS, которая не выглядет чем-то выдающимся, но в скоре заставит вас думать о том, почему вы не использовали её раньше?; и почему CSS2 макет такой неуклюжий? Она позволяет выполнять вычисления для определения значений из различных CSS единиц измерений, смешивая их в процессе вычисления.

К примеру, в приложении веб диктафона мы создали три области интерфейса, расположенные вертикально. Первые две имеют фиксированную высоту (заголовок и элементы управления):

header {
  height: 70px;
}

.main-controls {
  padding-bottom: 0.7rem;
  height: 170px;
}

Третья область (содержащая записываемые образцы, которые можно воспроизвести) должна занимать оставшуюся от первых двух область, независимо от высоты устройства пользователя. Задача может быть решена, установкой высоты третьей области равной 100% родительской высоты, минус высоты и отступы первых двух.

.sound-clips {
  box-shadow: inset 0 3px 4px rgba(0,0,0,0.7);
  background-color: rgba(0,0,0,0.1);
  height: calc(100% - 240px - 0.7rem);
  overflow: scroll;
}

Примечание : Функция calc() имеет хорошую поддержку среди современных браузеров, даже в устаревшем Internet Explorer 9.

Хак чекбокса для отображение/скрытия

Он уже хорошо документирован, но думаем его можно упоминуть, заключающийся в том, что можно кликнуть на  элемент <label> , содержащий элемент чекбокса для переключения самого чекбока. В нашем приложении веб диктафона он управляет отображением блока информации о приложении, при нажатии на иконку знака вопроса в правом верхнем углу. Сначала мы стилизуем элемент <label> , тем, что мы хотим, убеждаясь в том, что он имеет достаточно высокий  z-index , всегда находящийся выше других элементов :

label {
    font-family: 'NotoColorEmoji';
    font-size: 3rem;
    position: absolute;
    top: 2px;
    right: 3px;
    z-index: 5;
    cursor: pointer;
}

Затем скрываем настоящий чекбокс, избегая неразберихи в интерфейсе :

input[type=checkbox] {
   position: absolute;
   top: -100px;
}

Затем стилизуем блок информации (обернутый в элемент <aside>) по вкусу, давая ему фиксированную позицию, так что бы он не показывался в потоке разметки и влиял на основной интерфейс, трансформируем его позицию функцией трансформации, определяя его место по умолчанию, и меняем значение функции трансформации для плавного его отображения/скрытия:

aside {
   position: fixed;
   top: 0;
   left: 0;
   text-shadow: 1px 1px 1px black;  
   width: 100%;
   height: 100%;
   transform: translateX(100%);
   transition: 0.6s all;
   background-color: #999;
    background-image: linear-gradient(to top right, rgba(0,0,0,0), rgba(0,0,0,0.5));
}

Наконец определяем правило при нажатии чекбокса . Когда он выбран (когда нажат элемент label) соседний элемент <aside> получит значение горизонтального перехода и переместится в представление:

input[type=checkbox]:checked ~ aside {
  transform: translateX(0);
}

Основные настройки приложения

Для получения мелиапотока, который нужно захватить используется метод getUserMedia(). Затем используется интерфейс  MediaRecorder, для записи потока и вывода каждого отрывка записи в атрибут элемента  <audio>  для воспроизведения.

Объявим некоторые переменные для кнопок начала записи и остановки, а так же элемент <article> , который будет содержать аудио плееры:

const record = document.querySelector('.record');
const stop = document.querySelector('.stop');
const soundClips = document.querySelector('.sound-clips');

Наконец, для этого раздела создадим базовую структуру getUserMedia :

if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
   console.log('getUserMedia supported.');
   navigator.mediaDevices.getUserMedia (
      // Установим ограничение на получение только аудио потока 
      {
         audio: true
      })
      // Функция успешного получения потока
      .then(function(stream) {
 
        
      })
      // Функция ошибок
      .catch(function(err) {
         console.log('The following getUserMedia error occured: ' + err);
      }
   );
} else {
   console.log('getUserMedia not supported on your browser!');
}

Все обернуто в условный тест, проверяющий поддержку getUserMedia до запуска чего нибудь. Затем вызываем  getUserMedia() и внутри определяем:

  • Ограничения : Приложению нужен только аудиопоток .
  • Функция успешного вызова: Запускается при успешном получении потока функцией  getUserMedia .
  • Функция обработки ошибок: Если функция  getUserMedia по какой либо причине завершиться с ошибкой.

Примечание :  Весь код ниже помещается внутрь функции успешного вызова getUserMedia.

Захват медиа потока

Как только функция getUserMedia успешно получила медиапоток, создаем новый объект типа  Media Recorder конструктором MediaRecorder() и передаем ему поток, полученный функцией. Это точка входа использования интерфейса  MediaRecorder —  теперь поток готов для захвата и упаковки в объект  Blob, в формате по умолчанию, установленного для браузера.

const mediaRecorder = new MediaRecorder(stream);

Существуют несколько методов объекта MediaRecorder , позволяющие контролировать запись медиапотока; в приложении веб диктофон используется два и прослушиваем некоторые события. Прежде всего используем метод MediaRecorder.start() , для запуска записи потока, после нажатия кнопки старта:

record.onclick = function() {
  mediaRecorder.start();
  console.log(mediaRecorder.state);
  console.log("recorder started");
  record.style.background = "red";
  record.style.color = "black";
}

Когда объект  MediaRecorder приступает к записи его свойство MediaRecorder.state получает значение "recording".

По мере записи, нам нужно получать аудио данные. Для этого регистрируем обработчик события  mediaRecorder.ondataavailable:

let chunks = [];

mediaRecorder.ondataavailable = function(e) {
  chunks.push(e.data);
}

Примечание : Браузер будет запускать события dataavailable по необходимости (когда внутренний буфер объекта будет переполняться), но если разработчику нужно вмешаться, в вызов метода start() можно включить параметр timeslice , определяющий диапазон захвата в миллисекундах — к примеру, start(10000) ,  или вызывать функцию запроса данных  MediaRecorder.requestData() , запуская событие по необходимости.

Наконец используем метод  MediaRecorder.stop() при нажатии кнопки остановки записи и завершения упаковки объекта  Blob для его использования в приложении.

stop.onclick = function() {
  mediaRecorder.stop();
  console.log(mediaRecorder.state);
  console.log("recorder stopped");
  record.style.background = "";
  record.style.color = "";
}

Обратите внимание, что запись потока может остановиться естественно, если медиапоток кончился(к примеру, если захватывается музыкальный трек и он кончился, или пользователь отключил использование микрофона, чей поток захватывается).

Получение и использования объекта blob

Когда запись останавливается, свойство state получает значение "inactive", и запускается событие stop. Мы устанавливаем обработчик этого события, используя свойство mediaRecorder.onstop, завершая запись всех полученных порций  объекта blob на момент остановки:

mediaRecorder.onstop = function(e) {
  console.log("recorder stopped");

  const clipName = prompt('Enter a name for your sound clip');

  const clipContainer = document.createElement('article');
  const clipLabel = document.createElement('p');
  const audio = document.createElement('audio');
  const deleteButton = document.createElement('button');
           
  clipContainer.classList.add('clip');
  audio.setAttribute('controls', '');
  deleteButton.innerHTML = "Delete";
  clipLabel.innerHTML = clipName;

  clipContainer.appendChild(audio);
  clipContainer.appendChild(clipLabel);
  clipContainer.appendChild(deleteButton);
  soundClips.appendChild(clipContainer);

  const blob = new Blob(chunks, { 'type' : 'audio/ogg; codecs=opus' });
  chunks = [];
  const audioURL = window.URL.createObjectURL(blob);
  audio.src = audioURL;

  deleteButton.onclick = function(e) {
    let evtTgt = e.target;
    evtTgt.parentNode.parentNode.removeChild(evtTgt.parentNode);
  }
}

Пройдем весь код выше и посмотрим, что он делает.

Сначала отображаем пользователю диалог с запросом имени будущей записи.

Затем создаем структуру HTML , вставляя её в контейнер, представляющийся элементом <article> .

<article class="clip">
  <audio controls></audio>
  <p>your clip name</p>
  <button>Delete</button>
</article>

После этого создаем объект Blob с комбинацией наших записанных частей аудио и создаем объект URL ссылающийся на него, используя метод window.URL.createObjectURL(blob). Затем устанавливаем значение атрибута src элемента <audio> в созданный объект URL, так, что бы при нажатии на кнопку воспроизведения объект Blob передал свои данные элементу.

Наконец, устанавливаем обработчик события onclick на кнопке удаления, для удаления всей структуры HTML проигрывания результата записи (элемент clip).

Спецификации

Specification Status Comment
MediaStream Recording Рабочий черновик Initial definition

Совместимость с браузерами

MediaRecorder

Update compatibility data on GitHub
КомпьютерыМобильные
ChromeEdgeFirefoxInternet ExplorerOperaSafariAndroid webviewChrome для AndroidFirefox для AndroidOpera для AndroidSafari on iOSSamsung Internet
MediaRecorderChrome Полная поддержка 47Edge Полная поддержка 79Firefox Полная поддержка 25
Замечания
Полная поддержка 25
Замечания
Замечания Prior to Firefox 58, using MediaStream.addTrack() on a stream obtained using getUserMedia(), then attempting to record the resulting stream would result in only recording the original stream without the added tracks (severe bug).
IE Нет поддержки НетOpera Полная поддержка 36Safari Нет поддержки НетWebView Android Полная поддержка 47Chrome Android Полная поддержка 47Firefox Android Полная поддержка 25
Замечания
Полная поддержка 25
Замечания
Замечания Prior to Firefox 58, using MediaStream.addTrack() on a stream obtained using getUserMedia(), then attempting to record the resulting stream would result in only recording the original stream without the added tracks (severe bug).
Opera Android Полная поддержка 36Safari iOS Нет поддержки НетSamsung Internet Android Полная поддержка 5.0
MediaRecorder() constructorChrome Полная поддержка 47Edge Полная поддержка 79Firefox Полная поддержка 25IE Нет поддержки НетOpera Полная поддержка 36Safari Нет поддержки НетWebView Android Полная поддержка 47Chrome Android Полная поддержка 47Firefox Android Полная поддержка 25Opera Android Полная поддержка 36Safari iOS Нет поддержки НетSamsung Internet Android Полная поддержка 5.0
audioBitsPerSecond
Экспериментальная
Chrome Полная поддержка 49Edge Полная поддержка 79Firefox Полная поддержка 71IE Нет поддержки НетOpera Полная поддержка 36Safari Нет поддержки НетWebView Android Полная поддержка 49Chrome Android Полная поддержка 49Firefox Android ? Opera Android Полная поддержка 36Safari iOS Нет поддержки НетSamsung Internet Android Полная поддержка 5.0
error eventChrome Полная поддержка 49Edge Полная поддержка 79Firefox Полная поддержка 25IE Нет поддержки НетOpera Полная поддержка 36Safari Нет поддержки НетWebView Android Полная поддержка 49Chrome Android Полная поддержка 49Firefox Android Полная поддержка 25Opera Android Полная поддержка 36Safari iOS Нет поддержки НетSamsung Internet Android Полная поддержка 5.0
ignoreMutedMedia
УстаревшаяНестандартная
Chrome Нет поддержки 49 — 57Edge Нет поддержки НетFirefox ? IE Нет поддержки НетOpera Нет поддержки 36 — 44Safari Нет поддержки НетWebView Android Нет поддержки 49 — 57Chrome Android Нет поддержки 49 — 57Firefox Android ? Opera Android Нет поддержки 36 — 44Safari iOS Нет поддержки НетSamsung Internet Android Нет поддержки 5.0 — 7.0
isTypeSupportedChrome Полная поддержка 47Edge Полная поддержка 79Firefox Полная поддержка 25IE Нет поддержки НетOpera Полная поддержка 36Safari Нет поддержки НетWebView Android Полная поддержка 47Chrome Android Полная поддержка 47Firefox Android Полная поддержка 25Opera Android Полная поддержка 36Safari iOS Нет поддержки НетSamsung Internet Android Полная поддержка 5.0
mimeTypeChrome Полная поддержка 49
Полная поддержка 49
Нет поддержки 47 — 49
Замечания
Замечания Prior to Chrome 49, only video is supported, not audio.
Edge Полная поддержка 79Firefox Полная поддержка 25
Замечания
Полная поддержка 25
Замечания
Замечания Starting with Firefox 71, the behavior of mimeType is more consistent. For example, it now returns the media type even after recording has stopped.
IE Нет поддержки НетOpera Полная поддержка 36Safari Нет поддержки НетWebView Android Полная поддержка 49
Полная поддержка 49
Нет поддержки 47 — 49
Замечания
Замечания Prior to Chrome 49, only video is supported, not audio.
Chrome Android Полная поддержка 49
Полная поддержка 49
Нет поддержки 47 — 49
Замечания
Замечания Prior to Chrome 49, only video is supported, not audio.
Firefox Android Полная поддержка 25Opera Android Полная поддержка 36Safari iOS Нет поддержки НетSamsung Internet Android Полная поддержка 5.0
ondataavailableChrome Полная поддержка 49Edge Полная поддержка 79Firefox Полная поддержка 25IE Нет поддержки НетOpera Полная поддержка 36Safari Нет поддержки НетWebView Android Полная поддержка 49Chrome Android Полная поддержка 49Firefox Android Полная поддержка 25Opera Android Полная поддержка 36Safari iOS Нет поддержки НетSamsung Internet Android Полная поддержка 5.0
onerrorChrome Полная поддержка 49Edge Полная поддержка 79Firefox Полная поддержка 25IE Нет поддержки НетOpera Полная поддержка 36Safari Нет поддержки НетWebView Android Полная поддержка 49Chrome Android Полная поддержка 49Firefox Android Полная поддержка 25Opera Android Полная поддержка 36Safari iOS Нет поддержки НетSamsung Internet Android Полная поддержка 5.0
onpauseChrome Полная поддержка 49Edge Полная поддержка 79Firefox Полная поддержка 65IE Нет поддержки НетOpera Полная поддержка 36Safari Нет поддержки НетWebView Android Полная поддержка 49Chrome Android Полная поддержка 49Firefox Android Полная поддержка 65Opera Android Полная поддержка 36Safari iOS Нет поддержки НетSamsung Internet Android Полная поддержка 5.0
onresumeChrome Полная поддержка 49Edge Полная поддержка 79Firefox Полная поддержка 65IE Нет поддержки НетOpera Полная поддержка 36Safari Нет поддержки НетWebView Android Полная поддержка 49Chrome Android Полная поддержка 49Firefox Android Полная поддержка 65Opera Android Полная поддержка 36Safari iOS Нет поддержки НетSamsung Internet Android Полная поддержка 5.0
onstartChrome Полная поддержка 49Edge Полная поддержка 79Firefox Полная поддержка 25IE Нет поддержки НетOpera Полная поддержка 36Safari Нет поддержки НетWebView Android Полная поддержка 49Chrome Android Полная поддержка 49Firefox Android Полная поддержка 25Opera Android Полная поддержка 36Safari iOS Нет поддержки НетSamsung Internet Android Полная поддержка 5.0
onstopChrome Полная поддержка 49Edge Полная поддержка 79Firefox Полная поддержка 25IE Нет поддержки НетOpera Полная поддержка 36Safari Нет поддержки НетWebView Android Полная поддержка 49Chrome Android Полная поддержка 49Firefox Android Полная поддержка 25Opera Android Полная поддержка 36Safari iOS Нет поддержки НетSamsung Internet Android Полная поддержка 5.0
onwarning
Устаревшая
Chrome Полная поддержка 49Edge Полная поддержка 79Firefox Нет поддержки 25 — 71IE Нет поддержки НетOpera Полная поддержка 36Safari Нет поддержки НетWebView Android Полная поддержка 49Chrome Android Полная поддержка 49Firefox Android Полная поддержка 25Opera Android Полная поддержка 36Safari iOS Нет поддержки НетSamsung Internet Android Полная поддержка 5.0
pauseChrome Полная поддержка 49Edge Полная поддержка 79Firefox Полная поддержка 25IE Нет поддержки НетOpera Полная поддержка 36Safari Нет поддержки НетWebView Android Полная поддержка 49Chrome Android Полная поддержка 49Firefox Android Полная поддержка 25Opera Android Полная поддержка 36Safari iOS Нет поддержки НетSamsung Internet Android Полная поддержка 5.0
requestDataChrome Полная поддержка 49Edge Полная поддержка 79Firefox Полная поддержка 25IE Нет поддержки НетOpera Полная поддержка 36Safari Нет поддержки НетWebView Android Полная поддержка 49Chrome Android Полная поддержка 49Firefox Android Полная поддержка 25Opera Android Полная поддержка 36Safari iOS Нет поддержки НетSamsung Internet Android Полная поддержка 5.0
resumeChrome Полная поддержка 49Edge Полная поддержка 79Firefox Полная поддержка 25IE Нет поддержки НетOpera Полная поддержка 36Safari Нет поддержки НетWebView Android Полная поддержка 49Chrome Android Полная поддержка 49Firefox Android Полная поддержка 25Opera Android Полная поддержка 36Safari iOS Нет поддержки НетSamsung Internet Android Полная поддержка 5.0
startChrome Полная поддержка 47Edge Полная поддержка 79Firefox Полная поддержка 25IE Нет поддержки НетOpera Полная поддержка 36Safari Нет поддержки НетWebView Android Полная поддержка 47Chrome Android Полная поддержка 47Firefox Android Полная поддержка 25Opera Android Полная поддержка 36Safari iOS Нет поддержки НетSamsung Internet Android Полная поддержка 5.0
stateChrome Полная поддержка 49
Полная поддержка 49
Нет поддержки 47 — 49
Замечания
Замечания Prior to Chrome 49, only video is supported, not audio.
Edge Полная поддержка 79Firefox Полная поддержка 25IE Нет поддержки НетOpera Полная поддержка 36Safari Нет поддержки НетWebView Android Полная поддержка 49
Полная поддержка 49
Нет поддержки 47 — 49
Замечания
Замечания Prior to Chrome 49, only video is supported, not audio.
Chrome Android Полная поддержка 49
Полная поддержка 49
Нет поддержки 47 — 49
Замечания
Замечания Prior to Chrome 49, only video is supported, not audio.
Firefox Android Полная поддержка 25Opera Android Полная поддержка 36Safari iOS Нет поддержки НетSamsung Internet Android Полная поддержка 5.0
stopChrome Полная поддержка 49Edge Полная поддержка 79Firefox Полная поддержка 25IE Нет поддержки НетOpera Полная поддержка 36Safari Нет поддержки НетWebView Android Полная поддержка 49Chrome Android Полная поддержка 49Firefox Android Полная поддержка 25Opera Android Полная поддержка 36Safari iOS Нет поддержки НетSamsung Internet Android Полная поддержка 5.0
streamChrome Полная поддержка 49
Полная поддержка 49
Нет поддержки 47 — 49
Замечания
Замечания Prior to Chrome 49, only video is supported, not audio.
Edge Полная поддержка 79Firefox Полная поддержка 25IE Нет поддержки НетOpera Полная поддержка 36Safari Нет поддержки НетWebView Android Полная поддержка 49Chrome Android Полная поддержка 49
Полная поддержка 49
Нет поддержки 47 — 49
Замечания
Замечания Prior to Chrome 49, only video is supported, not audio.
Firefox Android Полная поддержка 25Opera Android Полная поддержка 36Safari iOS Нет поддержки НетSamsung Internet Android Полная поддержка 5.0
videoBitsPerSecond
Экспериментальная
Chrome Полная поддержка 49Edge Полная поддержка 79Firefox Полная поддержка 71IE Нет поддержки НетOpera Полная поддержка 36Safari Нет поддержки НетWebView Android Полная поддержка 49Chrome Android Полная поддержка 49Firefox Android ? Opera Android Полная поддержка 36Safari iOS Нет поддержки НетSamsung Internet Android Полная поддержка 5.0
warning event
Устаревшая
Chrome Полная поддержка 49Edge Полная поддержка 79Firefox Нет поддержки 25 — 71IE Нет поддержки НетOpera Полная поддержка 36Safari Нет поддержки НетWebView Android Полная поддержка 49Chrome Android Полная поддержка 49Firefox Android Полная поддержка 25Opera Android Полная поддержка 36Safari iOS Нет поддержки НетSamsung Internet Android Полная поддержка 5.0

Легенда

Полная поддержка  
Полная поддержка
Нет поддержки  
Нет поддержки
Совместимость неизвестна  
Совместимость неизвестна
Экспериментальная. Ожидаемое поведение может измениться в будущем.
Экспериментальная. Ожидаемое поведение может измениться в будущем.
Нестандартная. Ожидается плохая кросс-браузерная поддержка.
Нестандартная. Ожидается плохая кросс-браузерная поддержка.
Устаревшая. Не следует использовать в новых веб-сайтах
Устаревшая. Не следует использовать в новых веб-сайтах
Смотрите замечания реализации.
Смотрите замечания реализации.

Смтотри так же