Включить media recorder в настройках браузера
В статье Использование интерфейса MediaStream Recording API демонстрируется использование объекта типа MediaRecorder для захвата потока, представляющего объект типа MediaStream , сгенерированного аппаратными средствами устройства и возвращаемого методом navigator.mediaDevices.getUserMedia() , но можно также использовать HTML медиа элемент (а именно или ) в качестве источника потока MediaStream для его записи. В этой статье рассматривается пример выполняющий это.
Рассмотрим ключевые моменты кода HTML. Это только небольшой отрывок, относящийся к информационной части приложения.
Основной интерфейс представляется в двух колонках. В левой находиться кнопка старта и элемент , который отображает предварительный просмотр видео. Это видео, воспроизводится камерой устройства. Заметьте, что используется атрибут autoplay , что бы поток начал воспроизводиться немедленно, снимаясь прямо с камеры. Атрибут muted гарантирует отключение звука с микрофона, для предотвращения цикличного эхо эффекта.
Справа мы видим кнопку остановки и элемент , который будет использоваться для воспроизведения записанного видео. Обратите внимание, что на панели воспроизведения не установлен режим автозапуска (поэтому воспроизведение не начинается сразу после поступления мультимедиа), а также установлен атрибут controls , что говорит о необходимости показывать пользовательские элементы управления для воспроизведения, паузы и т. д.
Под элементом воспроизведения находится кнопка для загрузки записанного видео.
JavaScript
Теперь давайте посмотрим на код JavaScript
Установка глобальных переменных
Мы начнём с установления некоторых глобальных переменных, которые нам понадобятся.
Большинство из них являются ссылками на элементы, с которыми нам нужно работать. Последняя, recordingTimeMS , установлена на 5000 миллисекунд (5 секунд);.
Используемые функции
Далее мы создадим несколько служебных функций, которые будут использованы позже.
Функция log () используется для вывода текстовых строк в , чтобы мы могли делиться информацией с пользователем.
The wait() function returns a new Promise which resolves once the specified number of milliseconds have elapsed. It works by using an arrow function which calls window.setTimeout() (en-US), specifying the promise's resolution handler as the timeout handler function. That lets us use promise syntax when using timeouts, which can be very handy when chaining promises, as we'll see later.
Starting media recording
The startRecording() function handles starting the recording process:
startRecording() takes two input parameters: a MediaStream to record from and the length in milliseconds of the recording to make. We always record no more than the specified number of milliseconds of media, although if the media stops before that time is reached, MediaRecorder automatically stops recording as well.
Stopping the input stream
The stop() function simply stops the input media:
Getting an input stream and setting up the recorder
Now let's look at the most intricate piece of code in this example: our event handler for clicks on the start button:
When a click (en-US) event occurs, here's what happens:
Lines 2-4 navigator.mediaDevices.getUserMedia() (en-US) is called to request a new MediaStream that has both video and audio tracks. This is the stream we'll record. Lines 5-9 When the Promise returned by getUserMedia() is resolved, the preview element's srcObject (en-US) property is set to be the input stream, which causes the video being captured by the user's camera to be displayed in the preview box. Since the element is muted, the audio won't play. The "Download" button's link is then set to refer to the stream as well. Then, in line 8, we arrange for preview.captureStream() to call preview.mozCaptureStream() so that our code will work on Firefox, on which the MediaRecorder.captureStream() method is prefixed. Then a new Promise which resolves when the preview video starts to play is created and returned. Line 10 When the preview video begins to play, we know there's media to record, so we respond by calling the startRecording() function we created earlier, passing in the preview video stream (as the source media to be recorded) and recordingTimeMS as the number of milliseconds of media to record. As mentioned before, startRecording() returns a Promise whose resolution handler is called (receiving as input an array of Blob objects containing the chunks of recorded media data) once recording has completed. Lines 11-15 The recording process's resolution handler receives as input an array of media data Blob s locally known as recordedChunks . The first thing we do is merge the chunks into a single Blob whose MIME type is "video/webm" by taking advantage of the fact that the Blob() constructor concatenates arrays of objects into one object. Then URL.createObjectURL() is used to create an URL that references the blob; this is then made the value of the recorded video playback element's src attribute (so that you can play the video from the blob) as well as the target of the download button's link.
Then the download button's download attribute is set. While the download attribute can be a Boolean, you can also set it to a string to use as the name for the downloaded file. So by setting the download link's download attribute to "RecordedVideo.webm", we tell the browser that clicking the button should download a file named "RecordedVideo.webm" whose contents are the recorded video.
Lines 17-18 The size and type of the recorded media are output to the log area below the two videos and the download button. Line 20 The catch() for all the Promise s outputs the error to the logging area by calling our log() function.
Handling the stop button
The last bit of code adds a handler for the click (en-US) event on the stop button using addEventListener() :
This simply calls the stop() function we covered earlier.
Result
When put all together with the rest of the HTML and the CSS not shown above, it looks and works like this:
You can take a look at all the code, including the parts hidden above because they aren't critical to the explanation of how the APIs are being used.
The MediaRecorder это интерфейс MediaStream Recording API представляющий функциональность для простой записи медиа. Создаётся с использованием MediaRecorder() конструктора.
Конструктор
MediaRecorder() Создаёт новый объект MediaRecorder , получающий MediaStream для записи. Доступны такие параметры, как установка типа MIME контейнера ( "video/webm","video/mp4" и другие) и скорости передачи аудио-и видеодорожек или одной общей скорости.
Свойства
MediaRecorder.mimeType (en-US) Только для чтения Возвращает тип MIME, который был выбран в качестве контейнера записи для объекта MediaRecorder при его создании. MediaRecorder.state Только для чтения Возвращает текущее состояние объекта MediaRecorder ( inactive , recording , или paused .) MediaRecorder.stream (en-US) Только для чтения Возвращает поток который был передан конструктору при создании объекта MediaRecorder MediaRecorder.ignoreMutedMedia Показывает записывает ли MediaRecorder дорожку MediaStreamTrack если она отключена. Если атрибут равен false, MediaRecorder будет записывать тишину для аудио и чёрные кадры для видео. По умолчанию равно false MediaRecorder.videoBitsPerSecond (en-US) Только для чтения Возвращает скорость кодирования видео. Она может отличаться от скорости, определённой в конструкторе (если была предоставлена). MediaRecorder.audioBitsPerSecond; Только для чтения Возвращает скорость кодирования аудио. Она может отличаться от скорости, определённой в конструкторе (если была предоставлена).
Методы
MediaRecorder.isTypeSupported() (en-US) Возвращает Boolean (en-US) значение показывающее поддерживается ли MIME тип текущим user agent. MediaRecorder.pause() (en-US) Приостанавливает запись медиа. MediaRecorder.requestData() Запрашивает Blob содержащий сохранённые данные полученные с начала записи (или с последнего вызова requestData() ). После вызова этого метода, запись продолжается, но в новый Blob. MediaRecorder.resume() (en-US) Возобновляет запись медиа после паузы. MediaRecorder.start() Начинает запись медиа. В этот метод можно передать аргумент timeslice со значением в миллисекундах. Если он определён, то медиа будет записываться в отдельные блоки заданной продолжительности, вместо записи в один большой блок. MediaRecorder.stop() (en-US) Останавливает запись, после чего запускается событие dataavailable , содержащее последний Blob сохранённых данных.
Обработчики событий
MediaRecorder.ondataavailable Вызывает обработчик dataavailable события, которое запускается раз в timeslice миллисекунд (или, если timeslice не был задан - по окончанию записи). Событие типа BlobEvent (en-US), сдержит записанное медиа в data (en-US). Вы можете использовать обработчик для сбора и других действий в зависимости от полученных данных. MediaRecorder.onerror (en-US) event handler вызывается для обработки события recordingerror , включающего отчёт об ошибках во время записи. Это фатальные ошибки, приводящие к остановке записи. Полученное событие основано на интерфейсе MediaRecorderErrorEvent (en-US), свойство которого error (en-US) содержит DOMException (en-US) описывающие произошедшие ошибки. MediaRecorder.onpause (en-US) event handler вызывается для обработки события pause (en-US) , случившегося во время приостановки медиа. MediaRecorder.onresume (en-US) event handler вызывается для обработки события resume , случившегося во время возобновления записи. MediaRecorder.onstart (en-US) event handler вызывается для обработки события start , случившегося во время начала записи. MediaRecorder.onstop (en-US) event handler вызывается для обработки события stop , случившегося во время завершения записи, а так же при окончании MediaStream — или после вызова MediaRecorder.stop() (en-US)
Пример
Код выше был взят из демо Web Dictaphone. Некоторые строчки были пропущены для краткости. Полный код смотрите здесь
Интерфейс MediaStream Recording позволяет записывать аудио и видео потоки. При использовании с методом navigator.mediaDevices.getUserMedia() , предлагает простой способ записи данных с устройств пользователя, и немедленное их использование в веб приложениях.И аудио и видео могут быть записаны вместе или по отдельности.Цель данной статьи - предоставить базовое руководство о том как использовать объект MediaRecorder , реализующий такой программный интерфейс.
Пример приложения: Веб диктофон
Для демонстрации основной функциональности интерфейса MediaRecorder API , мы создали веб диктофон, позволяющий записывать отрывки аудио и проигрывать их после записи. Он визуализирует устройства ввода звука, используя интерфейс Web Audio API . В этой статье будем концентрироваться на функциональности записи и воспроизведения.
CSS плюшки
Разметка HTML довольно проста, поэтому не будем её рассматривать подробно, но есть более интересные места в CSS, которые стоило бы отметить, и о которых поговорим ниже. Если вам не интересна тема CSS , и хотите сразу приступить к JavaScript, то перейдите к части основных настроек приложения Basic app setup.
Сохраняйте интерфейс ограниченным областью просмотра, независимо от высоты устройства, с функцией calc()
Функция calc одна из полезных утилит возникших в CSS, которая не выглядеть чем-то выдающимся, но вскоре заставит вас думать о том, почему вы не использовали её раньше?; и почему CSS2 макет такой неуклюжий? Она позволяет выполнять вычисления для определения значений из различных CSS единиц измерений, смешивая их в процессе вычисления.
К примеру, в приложении веб диктофона мы создали три области интерфейса, расположенные вертикально. Первые две имеют фиксированную высоту (заголовок и элементы управления):
Третья область (содержащая записываемые образцы, которые можно воспроизвести) должна занимать оставшуюся от первых двух область, независимо от высоты устройства пользователя. Задача может быть решена, установкой высоты третьей области равной 100% родительской высоты, минус высоты и отступы первых двух.
Примечание : Функция calc() имеет хорошую поддержку среди современных браузеров, даже в устаревшем Internet Explorer 9.
Хак чекбокса для отображение/скрытия
Затем скрываем настоящий чекбокс, избегая неразберихи в интерфейсе :
Наконец определяем правило при нажатии чекбокса . Когда он выбран (когда нажат элемент label ) соседний элемент получит значение горизонтального перехода и переместится в представление:
Основные настройки приложения
Для получения медиапотока, который нужно захватить используется метод getUserMedia() . Затем используется интерфейс MediaRecorder , для записи потока и вывода каждого отрывка записи в атрибут элемента для воспроизведения.
Объявим некоторые переменные для кнопок начала записи и остановки, а так же элемент , который будет содержать аудио плееры:
Наконец, для этого раздела создадим базовую структуру getUserMedia :
Все обёрнуто в условный тест, проверяющий поддержку getUserMedia до запуска чего нибудь. Затем вызываем getUserMedia() и внутри определяем:
- Ограничения : Приложению нужен только аудиопоток .
- Функция успешного вызова: Запускается при успешном получении потока функцией getUserMedia .
- Функция обработки ошибок: Если функция getUserMedia по какой либо причине завершиться с ошибкой.
Примечание : Весь код ниже помещается внутрь функции успешного вызова getUserMedia.
Захват медиа потока
Как только функция getUserMedia успешно получила медиапоток, создаём новый объект типа Media Recorder конструктором MediaRecorder() и передаём ему поток, полученный функцией. Это точка входа использования интерфейса MediaRecorder — теперь поток готов для захвата и упаковки в объект Blob , в формате по умолчанию, установленного для браузера.
Существуют несколько методов объекта MediaRecorder , позволяющие контролировать запись медиапотока; в приложении веб диктофон используется два и прослушиваем некоторые события. Прежде всего используем метод MediaRecorder.start() , для запуска записи потока, после нажатия кнопки старта:
Когда объект MediaRecorder приступает к записи его свойство MediaRecorder.state получает значение " recording ".
По мере записи, нам нужно получать аудио данные. Для этого регистрируем обработчик события mediaRecorder.ondataavailable :
Примечание : Браузер будет запускать события dataavailable по необходимости (когда внутренний буфер объекта будет переполняться), но если разработчику нужно вмешаться, в вызов метода start() можно включить параметр timeslice , определяющий диапазон захвата в миллисекундах — к примеру, start(10000) , или вызывать функцию запроса данных MediaRecorder.requestData() , запуская событие по необходимости.
Наконец используем метод MediaRecorder.stop() (en-US) при нажатии кнопки остановки записи и завершения упаковки объекта Blob для его использования в приложении.
Обратите внимание, что запись потока может остановиться естественно, если медиапоток кончился(к примеру, если захватывается музыкальный трек и он кончился, или пользователь отключил использование микрофона, чей поток захватывается).
Получение и использования объекта blob
Когда запись останавливается, свойство state получает значение " inactive ", и запускается событие stop . Мы устанавливаем обработчик этого события, используя свойство mediaRecorder.onstop (en-US), завершая запись всех полученных порций объекта blob на момент остановки:
Пройдём весь код выше и посмотрим, что он делает.
Сначала отображаем пользователю диалог с запросом имени будущей записи.
Затем создаём структуру HTML , вставляя её в контейнер, представляющийся элементом .
После этого создаём объект Blob с комбинацией наших записанных частей аудио и создаём объект URL ссылающийся на него, используя метод window.URL.createObjectURL(blob) . Затем устанавливаем значение атрибута src элемента в созданный объект URL, так, что бы при нажатии на кнопку воспроизведения объект Blob передал свои данные элементу.
Наконец, устанавливаем обработчик события onclick на кнопке удаления, для удаления всей структуры HTML проигрывания результата записи (элемент clip).
Продолжаю исследовать возможности по работе с медиа, предоставляемые современными браузерами, и в этой статье хочу рассказать вам о возможности захвата и записи медиаданных в процессе воспроизведения аудио и видеофайлов.
Мы разработаем простое приложение для сведения аудио и видео со следующим функционалом:
- пользователь выбирает одно видео и несколько аудио, хранящихся в его файловой системе;
- когда пользователь нажимает на кнопку для начала записи, запускается воспроизведение выбранных файлов, захватываются их медиапотоки;
- захваченные потоки объединяются в один и передаются для записи;
- в процессе записи пользователь может менять источник аудиоданных;
- пользователь может приостанавливать (например, для изменения источника аудиоданных) и продолжать запись;
- по окончанию записи генерируется видеофайл в формате WebM — превью сведенного контента и ссылка для его скачивания.
В качестве фреймворка для фронтенда я буду использовать React , однако все функции по работе с медиа будут автономными (сигнатура этих функций будет framework agnostic), так что вы можете использовать любой другой фреймворк или ограничиться чистым JavaScript .
О том, как разработать приложение для создания аудиозаметок, можно прочитать в этой статье, а о том, как разработать приложение для захвата и записи экрана — в этой.
Если вам это интересно, прошу под кат.
Если вы внимательно изучили функционал нашего будущего приложения, то могли заметить, что пользователь может выбрать только одно видео и имеет возможность менять только источник аудиоданных. Это связано с тем, что на сегодняшний день в процессе записи медиаданных возможна замена только аудиоисточника (без создания нового экземпляра MediaRecorder ).
В процессе разработки приложения мы будем опираться на следующие спецификации (черновики и рекомендацию):
-
— захват медиапотока из DOM элементов; — запись медиаданных; — интерфейс для объединения медиапотоков; — интерфейсы для создания динамического источника аудиоданных; — создание объекта Blob и ссылки на него.
Ссылки на соответствующие разделы MDN будут приводиться по мере необходимости.
Создаем шаблон React-приложения с помощью create-react-app :
Пока генерируется шаблон, поговорим об интерфейсах и методах, которые мы будем применять.
- Для захвата медиапотока в процессе воспроизведения аудио или видео, а также в процессе рендеринга canvas , используется метод captureStream . К сожалению, на сегодняшний день данный метод не поддерживается Safari, а в Firefox он поддерживается с префиксом ( mozCaptureStream ):
- Для объединения потоков используется интерфейс MediaStream :
- Для записи медиа данных используется интерфейс MediaRecorder :
- Для создания сведенного видеофайла мы будем использовать интерфейс Blob в сочетании с методом createObjectURL :
- Наконец, для создания динамического источника аудиоданных используется сочетание интерфейсов AudioContext , MediaStreamAudioDestinationNode и MediaStreamAudioSourceNode :
Шаблон готов. Приступим к разработке приложения.
Структура директории src будет следующей:
Как видите, для стилизации приложения я пользовался Sass :
Начнем с основного компонента приложения ( App.js ).
Импортируем компоненты, утилиту для создания хранилища состояния и стили:
Создаем хранилище и импортируем хуки:
С вашего позволения, я не буду останавливаться на утилите (почитать о ней можно здесь). Справедливости ради следует отметить, что в последнее время для создания хранилища состояния я все чаще прибегаю к помощи zustand .
Создаем и экспортируем компонент:
Компонент для выбора видео ( components/VideoSelector.js ):
Компонент для выбора аудио ( components/AudioSelector.js ). Сигнатура данной функции немного сложнее предыдущей, поскольку пользователь может выбрать несколько файлов, но, в целом, все тоже самое:
Кратко рассмотрим утилиту для определения поддержки используемых технологий ( utils/verifySupport.js ):
Переходим к самой интересной части.
Начнем с методов для записи и формирования ее результата ( utils/recording.js ).
Импортируем утилиту для определения поддержки и создаем глобальные (в пределах модуля) переменные:
Функция для начала записи:
Функция для остановки записи:
Функция замены источника аудиоданных:
Наконец, функции для приостановки и продолжения записи:
Компонент для записи ( components/Recorder.js ).
Импортируем хуки и утилиты:
Извлекаем сеттер и части состояния из хранилища, сохраняем ссылку на элемент audio и определяем локальное состояние для индикатора паузы и начала записи:
Определяем метод для управления воспроизведением аудио и видео:
Определяем метод для начала записи:
Определяем метод для приостановки/продолжения воспроизведения:
Наконец, определяем метод для остановки записи:
Ну и, конечно, разметка:
Последний компонент — результат записи ( components/Result.js ):
Думаю, тут все понятно.
Проверяем работоспособность нашего приложения.
Запускаем сервер для разработки с помощью yarn start или npm start :
Выбираем видео и аудиофайлы:
Нажимаем на кнопку Start recording :
Начинается воспроизведение и запись данных.
Нажимаем Pause , выбираем другой аудио файл и нажимаем Resume :
Генерируется сведенный контент, появляется превью и ссылка для скачивания файла.
Предлагаю читателям «Хабрахабра» перевод статьи «Using the Media Capture API in the Browser» авторства Dave Voyles.
Сегодня я хочу поэкспериментировать с Media Capture и Streams API, разработанные совместно в Web Real-Time Communications Working Group из W3C и Device APIs Working Group. Некоторые разработчики могут знать их как getUserMedia — главный интерфейс, который позврляет веб-страницам получать доступ к таким девайсам, как веб-камеры и микрофоны.
Вы можете найти исходный код проекта в моем GitHub. Здесь есть рабочие демо для ваших экспериментов. В последнем Windows 10 preview release Microsoft впервые добавила поддержку media capture APIs в Microsoft Edge. Значительная часть кода из примера взята с Photo Capture sample, который сделала команда разработчиков Edge на их тестовом сайте.
Для тех из вас, кто хочет узнать чуть болше, Eric Bidelman написал отличную статью на HTML5 rocks, которая рассказывает историю этих API.
Метод getUserMedia() — отличная стартовая точка в изучении Media Capture API. Вызвов getUserMedia() принимает MediaStreamConstraints как аргумент, который определяет настройки и/или требования к устройствам захвата и захваченным медиапотокам, такие как громкость микрофона, разрешение видео, какая камера включена (имеется ввиду передняя или задняя — прим. переводчика).
Через MediaStreamConstraints, вы так же можете использовать определенное устройство захвата, используя его ID, который может быть получен через enumerateDevices() метод. Когда пользователь дает разрешение, getUserMedia() может вернуть обещание вместе с MediaSteam объектом, если определенный MediaStreamConstraints будет найден.
И все это без необходимости загружать плагин! В этом примере мы узнаем больше про API и сделаем несколько замечательных фильтров для видео и изображений, которые мы получим. Поддерживается ли ваш браузер? getUserMedia() был доступен с Chrome 21, Opera 18, and Firefox 17, и сейчас в Edge.
Функция обнаружения — простоя проверка существования navigator.getUserMedia. Это большая работа — проверить каждый браузер. Я советую использовать Modernizr. Вот как это работает:
Без Modernizr, как в демонстрируемом примере, используйте это:
В нашем HTML вы можете поместить тег video вверху страницы. Вы можете заметить, что он стоит на autoplay. Без этого, зависнет на первом кадре.
Здесь еще нет медиаисточника, но мы сделаем с помощью Javascript.
Новая функциональность может дать разработчикам несколько новых возможностей, но тут есть риск в отношении безопасности пользователя, следовательно первое что происходит при запуске веб-приложения — запрос разрешения пользователя на доступ к устройству захвата. getUserMedia принимает несколько параметров. Первый — объект указывающий детали и требования для каждого типа медиа, к которым вы хотите получит доступ. Для доступа к камере первый параметр должен быть , для использования и камеры и микрофона .
В initalizeVideoStream функции вы можете увидеть, что мы берем video тег со страницы и устанавливаем его источником наш стрим. Стрим сам по себе — blob. Если браузер не поддерживает srcObject атрибут, надо сделать URL для медиастрима и установить его.
Я плохой фотограф, поэтому я всегда пологаюсь на фильтры Instagram. Но что если вы сможете применять свои фильтры к фото и видео? Что ж, вы можете!
Я сделал простую функию для видеопотока, котрый позволяет мне применять CSS-фильтры в реальном времени. Она почти идентична функции для изображений.
В начале класса есть массив с названиями фильтров. Они сохранены как строка и соответствуют по названию классам CSS.
// CSS-фильтры var index = 0; var filters = ['grayscale', 'sepia', 'blur', 'invert', 'brightness', 'contrast', '']; и CSS:
Вы можете увидеть больше примеров и поизменять значения в реальном времени на Edge test drive page.
Разбираясь в коде, вы могли заметить несколько вещей, которые вам не знакомы. Первой вещью, которая притянула мой взгляд была navigator.msSaveBlob. Blob конструктор позволяет вам легко создавать и манипулировать blob прямо на клиенте. Он поддерживается в IE 10+.
msSaveBlob позволяет вам сохранить объект blob (в данном случае наше фото) на диск. У него есть брат — метод msSaveOrOpenBlob, который позволяет вам открывать изображения изнутри браузера.
Если браузер поддерживает метод, то уменьшается количество кода, необходимого для сохранения изображения.
Это только начало. Вместе с этим мы можем использовать WebGL, который позволит применить больше фильтров, или в реальном времени помещать аудио/видеопоток в интерактивное окружение. Возможно это будет моим следующим проектом…
Дополнительно вы можете привязать Web Audio API, чтобы применить частотную модуляцию к аудио потоку. Этот пример из Web Audio tuner отлично это демонстрирует. Некоторым проще воспринимать визуально, так что проверьте пример от Microsoft.
Когда мобильные браузеры начнут поддерживать эту технологию, вы сможете использовать эти API, чтобы связать основные аппаратные средства и работать с ними не обращая внимание на платформу. Сейчас отличное время для того, чтобы быть веб-разработчиком, и надеюсь, после использования этого вы поймете, почему я так рад участвовать в этом.
Читайте также: