diff options
| author | Peter Bengtsson <mail@peterbe.com> | 2020-12-08 14:42:52 -0500 |
|---|---|---|
| committer | Peter Bengtsson <mail@peterbe.com> | 2020-12-08 14:42:52 -0500 |
| commit | 074785cea106179cb3305637055ab0a009ca74f2 (patch) | |
| tree | e6ae371cccd642aa2b67f39752a2cdf1fd4eb040 /files/ru/web/api/mediastream_recording_api | |
| parent | da78a9e329e272dedb2400b79a3bdeebff387d47 (diff) | |
| download | translated-content-074785cea106179cb3305637055ab0a009ca74f2.tar.gz translated-content-074785cea106179cb3305637055ab0a009ca74f2.tar.bz2 translated-content-074785cea106179cb3305637055ab0a009ca74f2.zip | |
initial commit
Diffstat (limited to 'files/ru/web/api/mediastream_recording_api')
3 files changed, 664 insertions, 0 deletions
diff --git a/files/ru/web/api/mediastream_recording_api/index.html b/files/ru/web/api/mediastream_recording_api/index.html new file mode 100644 index 0000000000..deafe13eed --- /dev/null +++ b/files/ru/web/api/mediastream_recording_api/index.html @@ -0,0 +1,129 @@ +--- +title: MediaStream Recording API +slug: Web/API/MediaStream_Recording_API +translation_of: Web/API/MediaStream_Recording_API +--- +<div>{{DefaultAPISidebar("MediaStream Recording")}}</div> + +<p><span class="seoSummary"><strong>MediaStream Recording API</strong>, иногда просто называемый <em>Media Recording API</em> или<em> MediaRecorder API</em>, тесно связан с <a href="/en-US/docs/Web/API/Media_Streams_API">Media Capture and Streams API</a> и <a href="/en-US/docs/Web/API/WebRTC_API">WebRTC API</a>. MediaStream Recording API делает возможным захват данных, сгенерированных {{domxref("MediaStream")}} или {{domxref("HTMLMediaElement")}} объектом для анализа, обработки или сохранения на дисковое пространство. С ним так же удивительно легко работать.</span></p> + +<h2 id="Базовая_концепция">Базовая концепция</h2> + +<p>MediaStream Recording API состоит из единого интерфейса, {{domxref("MediaRecorder")}}, который делает всю работу, принимая данные из {{domxref("MediaStream")}} и доставляя их обработчику. Данные поставляются с помощью серии {{event("dataavailable")}} событий, уже в том формате, который был указан, когда <code>MediaRecorder</code> был создан. Процесс записи потока прост:</p> + +<ol> + <li>Установите {{domxref("MediaStream")}} или {{domxref("HTMLMediaElement")}} (в виде {{HTMLElement("audio")}} или {{HTMLElement("video")}} элемента), чтобы тот служил в качестве источника медиа-данных.</li> + <li>Установите {{domxref("MediaRecorder.ondataavailable")}} обработчик событий для {{event("dataavailable")}} событий; он будет вызываться каждый раз, как данные будут доступны.</li> + <li>Создайте {{domxref("MediaRecorder")}} объект, указав исходный поток и любые опции по желанию (такие как MIME-тип контейнера или желаемый битрей его треков).</li> + <li>Как только исходная медиа запустится и будет достигнута точка, где вы будете готовы записать видео, вызовите {{domxref("MediaRecorder.start()")}} для начала записи.</li> + <li>Ваш {{event("dataavailable")}} обработчик будет вызываться каждый раз, как появлятся данные, готовые для выполнения вами над ними ожидаемых операций; событие имеет дата-атрибут, чье занчение {{domxref("Blob")}}, который содержит медиа-данные. Вы можете принудительно вызвать <code>dataavailable</code> событи, доставляя тем самым последние данные, чтобы вы могли отфильтровать их, сохранить и тд.</li> + <li>Запись останавливается автоматически, когда исходное медиа закончит проигрываться.</li> + <li>Вы можетет остановить запись в любое время, вызвав {{domxref("MediaRecorder.stop()")}}.</li> +</ol> + +<p>Вы можете так же использовать свойства MediaRecorder объекта для опредления состояния процесса записи и его {{domxref("MediaRecorder.pause", "pause()")}} и {{domxref("MediaRecorder.resume", "resume()")}} методы, чтобы остановить и возобновить запись исходной медиа.</p> + +<p>Если вам нужно или вы хотите проверить, поддерживается ли определенный MIME-тип, это так же возможно. Просто вызовите {{domxref("MediaRecorder.isMimeTypeSupported()")}}.</p> + +<p>Чтобы узнать больше о MediaStream Recording API, смотрите <a href="/en-US/docs/Web/API/MediaStream_Recording_API/Using_the_MediaStream_Recording_API">Using the MediaStream Recording API</a>, который показывает, как использовать API для записи аудио клипов. Другая статья, <a href="/en-US/docs/Web/API/MediaStream_Recording_API/Recording_a_media_element">Recording a media element</a>, описывает, как получить поток из {{HTMLElement("audio")}} или {{HTMLElement("video")}} элементов и использовать захватывающие потоки (в этом случае, запись и сохранение их на локальный диск).</p> + +<h2 id="Ссылки">Ссылки</h2> + +<p>{{domxref("BlobEvent")}}</p> + +<p>{{domxref("MediaRecorder")}}</p> + +<h2 id="Спецификация">Спецификация</h2> + +<table class="standard-table"> + <tbody> + <tr> + <th scope="col">Specification</th> + <th scope="col">Status</th> + <th scope="col">Comment</th> + </tr> + <tr> + <td>{{SpecName("MediaStream Recording", "#MediaRecorderAPI")}}</td> + <td>{{Spec2("MediaStream Recording")}}</td> + <td>Initial definition</td> + </tr> + </tbody> +</table> + +<h2 id="Поддержка_браузерами">Поддержка браузерами</h2> + +<p>{{CompatibilityTable}}</p> + +<div id="compat-desktop"> +<table class="compat-table"> + <tbody> + <tr> + <th>Feature</th> + <th>Chrome</th> + <th>Firefox (Gecko)</th> + <th>Internet Explorer</th> + <th>Microsoft Edge</th> + <th>Opera</th> + <th>Safari (WebKit)</th> + </tr> + <tr> + <td>Basic support</td> + <td>{{CompatChrome(47.0)}}</td> + <td>{{CompatGeckoDesktop("25.0")}}</td> + <td>{{CompatNo}}</td> + <td>{{CompatUnknown}}</td> + <td>{{CompatNo}}</td> + <td>{{CompatNo}}</td> + </tr> + </tbody> +</table> +</div> + +<div id="compat-mobile"> +<table class="compat-table"> + <tbody> + <tr> + <th>Feature</th> + <th>Android</th> + <th>Android Webview</th> + <th>Firefox Mobile (Gecko)</th> + <th>Firefox OS</th> + <th>IE Phone</th> + <th>Opera Mobile</th> + <th>Safari Mobile</th> + <th>Chrome for Android</th> + </tr> + <tr> + <td>Basic support</td> + <td>{{CompatNo}}</td> + <td>{{CompatChrome(47.0)}}</td> + <td>{{CompatGeckoDesktop("25.0")}}</td> + <td>1.3<sup>[1]</sup></td> + <td>{{CompatNo}}</td> + <td>{{CompatNo}}</td> + <td>{{CompatNo}}</td> + <td>{{CompatChrome(47.0)}}</td> + </tr> + </tbody> +</table> +</div> + +<p>[1] The initial Firefox OS implementation only supported audio recording.</p> + +<p>[2] To use {{domxref("MediaRecorder")}} in Chrome 47 and 48, enable <strong>experimental Web Platform features</strong> from the <code>chrome://flags</code> page.</p> + +<p>[3] Audio recording works in Chrome 49 and above; Chrome 47 and 48 only support video recording.</p> + +<h2 id="Смотри_так_же">Смотри так же</h2> + +<ul> + <li><a href="/en-US/docs/Web/API/MediaStream_Recording_API/Using_the_MediaStream_Recording_API">Using the MediaStream Recording API</a></li> + <li><a href="/en-US/docs/Web/API/MediaStream_Recording_API/Recording_a_media_element">Recording a media element</a></li> + <li><a href="https://simpl.info/mediarecorder/">simpl.info MediaStream Recording demo</a>, by <a href="https://twitter.com/sw12">Sam Dutton</a></li> + <li>{{domxref("navigator.mediaDevices.getUserMedia()")}}</li> + <li><a href="https://addpipe.com/blog/mediarecorder-api/">HTML5’s Media Recorder API in Action on Chrome and Firefox</a></li> + <li><a href="https://github.com/chrisjohndigital/TutorRoom">TutorRoom</a>: HTML5 video capture/playback/download using getUserMedia and the MediaRecorder API (<a href="https://github.com/chrisjohndigital/TutorRoom">source on GitHub</a>)</li> + <li><a href="https://www.fingerspell.org/">FingerSpell</a>: Sign Language Fingerspelling practice using getUserMedia and the MediaRecorder API to create and download recordings, MediaRecorder API supported desktop browsers only <a href="https://github.com/chrisjohndigital/CameraCaptureJS">(source on GitHub</a>)</li> + <li><a href="http://codepen.io/anon/pen/gpmPzm">Simple video recording demo</a></li> + <li><a href="https://quickblox.github.io/javascript-media-recorder/sample/">Advanced media stream recorder sample</a></li> +</ul> diff --git a/files/ru/web/api/mediastream_recording_api/recording_a_media_element/index.html b/files/ru/web/api/mediastream_recording_api/recording_a_media_element/index.html new file mode 100644 index 0000000000..7d3daa1d33 --- /dev/null +++ b/files/ru/web/api/mediastream_recording_api/recording_a_media_element/index.html @@ -0,0 +1,272 @@ +--- +title: Запись медиа элемента +slug: Web/API/MediaStream_Recording_API/Recording_a_media_element +translation_of: Web/API/MediaStream_Recording_API/Recording_a_media_element +--- +<div>{{DefaultAPISidebar("MediaStream Recording")}}</div> + +<p>В статье Использование интерфейса MediaStream Recording API демонстрируется использование объекта типа {{domxref("MediaRecorder")}} для захвата потока, представляющего объект типа {{domxref("MediaStream")}} , сгенерированного аппаратными средствами устройства и возвращаемого методом {{domxref("MediaDevices.getUserMedia()","navigator.mediaDevices.getUserMedia()")}}, но можно также использовать HTML медиа элемент (а именно {{HTMLElement("audio")}} или {{HTMLElement("video")}}) в качестве источника потока <code>MediaStream</code> для его записи. В этой статье рассматривается пример выполняющий это.</p> + +<div id="Example"> +<h2 id="HTML_содержимое">HTML содержимое</h2> + +<div class="hidden"> +<pre class="brush: html"><p>Click the "Start" button to begin video recording for a few seconds. You can stop + the video by clicking the creatively-named "Stop" button. The "Download" + button will download the received data (although it's in a raw, unwrapped form + that isn't very useful). +</p> +<br> +</pre> +</div> + +<p>Рассмотрим ключевые моменты кода HTML. Это только небольшой отрывок, относящийся к информационной части приложения.</p> + +<pre class="brush: html"><div class="left"> + <div id="startButton" class="button"> + Start + </div> + <h2>Preview</h2> + <video id="preview" width="160" height="120" autoplay muted></video> +</div> +</pre> + +<p>Основной интерфейс представляется в двух колонках. В левой находиться кнопка старта и элемент {{HTMLElement("video")}} , который отображает предварительный просмотр видео. Это видео, воспроизводится камерой устройства. Заметте, что используется атрибут {{htmlattrxref("autoplay", "video")}}, что бы поток начал воспроизводиться немедленно, снимаясь прямо с камеры. Атрибут {{htmlattrxref("muted", "video")}} гарантирует отключение звука с микрофона, для предотвращения цикличного эхо эффекта.</p> + +<pre class="brush: html"><div class="right"> + <div id="stopButton" class="button"> + Stop + </div> + <h2>Recording</h2> + <video id="recording" width="160" height="120" controls></video> + <a id="downloadButton" class="button"> + Download + </a> +</div> +</pre> + +<p>Справа мы видим кнопку остановки и элемент <code><video>,</code> который будет использоваться для воспроизведения записанного видео. Обратите внимание, что на панели воспроизведения не установлен режим автозапуска (поэтому воспроизведение не начинается сразу после поступления мультимедиа), а также установлен атрибут {{htmlattrxref ("controls", "video")}}, что говорит о необходимости показывать пользовательские элементы управления для воспроизведения, паузы и т. д.</p> + +<p>Под элементом воспроизведения находится кнопка для загрузки записанного видео.</p> + +<div class="hidden"> +<pre class="brush: html"><div class="bottom"> + <pre id="log"></pre> +</div></pre> +<strong>CSS файл</strong> + +<pre class="brush: css">body { + font: 14px "Open Sans", "Arial", sans-serif; +} + +video { + margin-top: 2px; + border: 1px solid black; +} + +.button { + cursor: pointer; + display: block; + width: 160px; + border: 1px solid black; + font-size: 16px; + text-align: center; + padding-top: 2px; + padding-bottom: 4px; + color: white; + background-color: darkgreen; + text-decoration: none; +} + +h2 { + margin-bottom: 4px; +} + +.left { + margin-right: 10px; + float: left; + width: 160px; + padding: 0px; +} + +.right { + margin-left: 10px; + float: left; + width: 160px; + padding: 0px; +} + +.bottom { + clear: both; + padding-top: 10px; +}</pre> +</div> + +<h2 id="JavaScript">JavaScript</h2> + +<p>Теперь давайте посмотрим на код JavaScript</p> + +<h3 id="Установка_глобальных_переменных">Установка глобальных переменных</h3> + +<p>Мы начнем с установления некоторых глобальных переменных, которые нам понадобятся.</p> + +<pre class="brush: js">let preview = document.getElementById("preview"); +let recording = document.getElementById("recording"); +let startButton = document.getElementById("startButton"); +let stopButton = document.getElementById("stopButton"); +let downloadButton = document.getElementById("downloadButton"); +let logElement = document.getElementById("log"); + +let recordingTimeMS = 5000; +</pre> + +<p>Большинство из них являются ссылками на элементы, с которыми нам нужно работать. Последняя, <code>recordingTimeMS</code>, установлена на 5000 миллисекунд (5 секунд);.</p> + +<h3 id="Используемые_функции">Используемые функции</h3> + +<p>Далее мы создадим несколько служебных функций, которые будут использованы позже.</p> + +<pre class="brush: js">function log(msg) { + logElement.innerHTML += msg + "\n"; +} +</pre> + +<p>Функция<code> log ()</code> используется для вывода текстовых строк в {{HTMLElement ("div")}}, чтобы мы могли делиться информацией с пользователем.</p> + +<pre class="brush: js">function wait(delayInMS) { + return new Promise(resolve => setTimeout(resolve, delayInMS)); +} +</pre> + +<p>The <code>wait()</code> function returns a new {{jsxref("Promise")}} which resolves once the specified number of milliseconds have elapsed. It works by using an <a href="/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions">arrow function</a> which calls {{domxref("window.setTimeout()")}}, 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.</p> + +<h3 id="Starting_media_recording">Starting media recording</h3> + +<p>The <code>startRecording()</code> function handles starting the recording process:</p> + +<pre class="brush: js">function startRecording(stream, lengthInMS) { + let recorder = new MediaRecorder(stream); + let data = []; + + recorder.ondataavailable = event => data.push(event.data); + recorder.start(); + log(recorder.state + " for " + (lengthInMS/1000) + " seconds..."); + + let stopped = new Promise((resolve, reject) => { + recorder.onstop = resolve; + recorder.onerror = event => reject(event.name); + }); + + let recorded = wait(lengthInMS).then( + () => recorder.state == "recording" && recorder.stop() + ); + + return Promise.all([ + stopped, + recorded + ]) + .then(() => data); +} +</pre> + +<p><code>startRecording()</code> takes two input parameters: a {{domxref("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, {{domxref("MediaRecorder")}} automatically stops recording as well.</p> + +<dl> + <dt>Line 2</dt> + <dd>Creates the <code>MediaRecorder</code> that will handle recording the input <code>stream</code>.</dd> + <dt>Line 3</dt> + <dd>Creates an empty array, <code>data</code>, which will be used to hold the {{domxref("Blob")}}s of media data provided to our {{domxref("MediaRecorder.ondataavailable", "ondataavailable")}} event handler.</dd> + <dt>Line 5</dt> + <dd>Sets up the handler for the {{event("dataavailable")}} event. The received event's <code>data</code> property is a {{domxref("Blob")}} that contains the media data. The event handler simply pushes the <code>Blob</code> onto the <code>data</code> array.</dd> + <dt>Lines 6-7</dt> + <dd>Starts the recording process by calling {{domxref("MediaRecorder.start", "recorder.start()")}}, and outputs a message to the log with the updated state of the recorder and the number of seconds it will be recording.</dd> + <dt>Lines 9-12</dt> + <dd>Creates a new {{jsxref("Promise")}}, named <code>stopped</code>, which is resolved when the <code>MediaRecorder</code>'s {{domxref("MediaRecorder.onstop", "onstop")}} event handler is called, and is rejected if its {{domxref("MediaRecorder.onerror", "onerror")}} event handler is called. The rejection handler receives as input the name of the error that occurred.</dd> + <dt>Lines 14-16</dt> + <dd>Creates a new <code>Promise</code>, named <code>recorded</code>, which is resolved when the specified number of milliseconds have elapsed. Upon resolution, it stops the <code>MediaRecorder</code> if it's recording.</dd> + <dt>Lines 18-22</dt> + <dd>These lines create a new <code>Promise</code> which is fulfilled when both of the two <code>Promise</code>s (<code>stopped</code> and <code>recorded</code>) have resolved. Once that resolves, the array data is returned by <code>startRecording()</code> to its caller.</dd> +</dl> + +<h3 id="Stopping_the_input_stream">Stopping the input stream</h3> + +<p>The <code>stop()</code> function simply stops the input media:</p> + +<pre class="brush: js">function stop(stream) { + stream.getTracks().forEach(track => track.stop()); +} +</pre> + +<p>This works by calling {{domxref("MediaStream.getTracks()")}}, using {{jsxref("Array.forEach", "forEach()")}} to call {{domxref("MediaStreamTrack.stop()")}} on each track in the stream.</p> + +<h3 id="Getting_an_input_stream_and_setting_up_the_recorder">Getting an input stream and setting up the recorder</h3> + +<p>Now let's look at the most intricate piece of code in this example: our event handler for clicks on the start button:</p> + +<pre class="brush: js">startButton.addEventListener("click", function() { + navigator.mediaDevices.getUserMedia({ + video: true, + audio: true + }).then(stream => { + preview.srcObject = stream; + downloadButton.href = stream; + preview.captureStream = preview.captureStream || preview.mozCaptureStream; + return new Promise(resolve => preview.onplaying = resolve); + }).then(() => startRecording(preview.captureStream(), recordingTimeMS)) + .then (recordedChunks => { + let recordedBlob = new Blob(recordedChunks, { type: "video/webm" }); + recording.src = URL.createObjectURL(recordedBlob); + downloadButton.href = recording.src; + downloadButton.download = "RecordedVideo.webm"; + + log("Successfully recorded " + recordedBlob.size + " bytes of " + + recordedBlob.type + " media."); + }) + .catch(log); +}, false);</pre> + +<p>When a {{event("click")}} event occurs, here's what happens:</p> + +<dl> + <dt>Lines 2-4</dt> + <dd>{{domxref("navigator.mediaDevices.getUserMedia()")}} is called to request a new {{domxref("MediaStream")}} that has both video and audio tracks. This is the stream we'll record.</dd> + <dt>Lines 5-9</dt> + <dd>When the Promise returned by <code>getUserMedia()</code> is resolved, the preview {{HTMLElement("video")}} element's {{domxref("HTMLMediaElement.srcObject","srcObject")}} 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 <code><video></code> 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 <code>preview.captureStream()</code> to call <code>preview.mozCaptureStream()</code> so that our code will work on Firefox, on which the {{domxref("MediaRecorder.captureStream()")}} method is prefixed. Then a new {{jsxref("Promise")}} which resolves when the preview video starts to play is created and returned.</dd> + <dt>Line 10</dt> + <dd>When the preview video begins to play, we know there's media to record, so we respond by calling the <code>{{anch("Starting media recording", "startRecording()")}}</code> function we created earlier, passing in the preview video stream (as the source media to be recorded) and <code>recordingTimeMS</code> as the number of milliseconds of media to record. As mentioned before, <code>startRecording()</code> returns a {{jsxref("Promise")}} whose resolution handler is called (receiving as input an array of {{domxref("Blob")}} objects containing the chunks of recorded media data) once recording has completed.</dd> + <dt>Lines 11-15</dt> + <dd>The recording process's resolution handler receives as input an array of media data <code>Blob</code>s locally known as <code>recordedChunks</code>. The first thing we do is merge the chunks into a single {{domxref("Blob")}} whose MIME type is <code>"video/webm"</code> by taking advantage of the fact that the {{domxref("Blob.Blob", "Blob()")}} constructor concatenates arrays of objects into one object. Then {{domxref("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 {{htmlattrxref("src", "video")}} attribute (so that you can play the video from the blob) as well as the target of the download button's link. + <p>Then the download button's {{htmlattrxref("download", "a")}} attribute is set. While the <code>download</code> 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 <code>download</code> attribute to "RecordedVideo.webm", we tell the browser that clicking the button should download a file named <code>"RecordedVideo.webm"</code> whose contents are the recorded video.</p> + </dd> + <dt>Lines 17-18</dt> + <dd>The size and type of the recorded media are output to the log area below the two videos and the download button.</dd> + <dt>Line 20</dt> + <dd>The <code>catch()</code> for all the <code>Promise</code>s outputs the error to the logging area by calling our <code>log()</code> function.</dd> +</dl> + +<h3 id="Handling_the_stop_button">Handling the stop button</h3> + +<p>The last bit of code adds a handler for the {{event("click")}} event on the stop button using {{domxref("EventTarget.addEventListener", "addEventListener()")}}:</p> + +<pre class="brush: js">stopButton.addEventListener("click", function() { + stop(preview.srcObject); +}, false);</pre> + +<p>This simply calls the <code>{{anch("Stopping the input stream", "stop()")}}</code> function we covered earlier.</p> +</div> + +<h2 id="Result">Result</h2> + +<p>When put all together with the rest of the HTML and the CSS not shown above, it looks and works like this:</p> + +<p>{{ EmbedLiveSample('Example', 600, 440, "", "", "", "camera;microphone") }}</p> + +<p>You can {{LiveSampleLink("Example", "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.</p> + +<h2 id="See_also">See also</h2> + +<ul> + <li><a href="/en-US/docs/Web/API/MediaStream_Recording_API">MediaStream Recording API</a></li> + <li><a href="/en-US/docs/Web/API/MediaStream_Recording_API/Using_the_MediaStream_Recording_API">Using the MediaStream Recording API</a></li> + <li><a href="/en-US/docs/Web/API/Media_Streams_API">Media Capture and Streams API</a></li> +</ul> diff --git a/files/ru/web/api/mediastream_recording_api/using_the_mediastream_recording_api/index.html b/files/ru/web/api/mediastream_recording_api/using_the_mediastream_recording_api/index.html new file mode 100644 index 0000000000..a054df7b8f --- /dev/null +++ b/files/ru/web/api/mediastream_recording_api/using_the_mediastream_recording_api/index.html @@ -0,0 +1,263 @@ +--- +title: Использование интерфейса записи медиапотока +slug: Web/API/MediaStream_Recording_API/Using_the_MediaStream_Recording_API +translation_of: Web/API/MediaStream_Recording_API/Using_the_MediaStream_Recording_API +--- +<p>{{DefaultAPISidebar("MediaStream Recording")}}</p> + +<div class="summary"> +<p><span class="seoSummary">Интерфейс <a href="/en-US/docs/Web/API/MediaStream_Recording_API">MediaStream Recording</a> позволяет записывать аудио и видео потоки. При использовании с методом {{domxref("MediaDevices.getUserMedia()","navigator.mediaDevices.getUserMedia()")}}, предлагает простой способ записи данных с устройств пользователя, и немедленное их использование в веб приложениях.И аудио и видео могут быть записаны вместе или по отдельности.Цель данной статьи - предоставить базовое руководство о том как использовать объект</span><code> MediaRecorder</code>, реализующий такой програмный интерфей.</p> +</div> + +<h2 id="Пример_приложени_Веб_диктофон">Пример приложени: Веб диктофон</h2> + +<p><img alt="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." src="https://mdn.mozillademos.org/files/7885/web-dictaphone.png" style="display: block; margin: 0px auto;"></p> + +<p>Для демонстрации основной функциональности интерфейса <code>MediaRecorder API</code>, мы создали веб диктофон, позволяющий записывать отрывки аудио и проигрывать их после записи. Он визуализирует устройства ввода звука, используя интерфейс <code> Web Audio API</code>. В этой статье будем концентрироваться на функциональности записи и воспроизведения.</p> + +<p>Посмотрите получившуюся <a href="https://mdn.github.io/web-dictaphone/">работающую демонстрацию</a>, или <a href="https://github.com/mdn/web-dictaphone">скачайте исходники</a> на GitHub.</p> + +<h2 id="CSS_плюшки">CSS плюшки</h2> + +<p>Разметка HTML довольно проста, поэтому не будем ее рассматривать подробно, но есть более интересные места в CSS, которые стоило бы отметить, и о которых поговорим ниже. Если вам не интересна тема CSS , и хотите сразу приступить к JavaScript, то перейдите к части основных настроек приложения {{anch("Basic app setup")}}.</p> + +<h3 id="Сохраняйте_интерфейс_ограниченным_областью_просмотра_независимо_от_высоты_устройства_с_функцией_calc">Сохраняйте интерфейс ограниченным областью просмотра, независимо от высоты устройства, с функцией calc()</h3> + +<p>Функция {{cssxref("calc")}} одна из полезных утилит возникших в CSS, которая не выглядет чем-то выдающимся, но в скоре заставит вас думать о том, почему вы не использовали её раньше?; и почему CSS2 макет такой неуклюжий? Она позволяет выполнять вычисления для определения значений из различных CSS единиц измерений, смешивая их в процессе вычисления.</p> + +<p>К примеру, в приложении веб диктафона мы создали три области интерфейса, расположенные вертикально. Первые две имеют фиксированную высоту (заголовок и элементы управления):</p> + +<pre class="brush: css">header { + height: 70px; +} + +.main-controls { + padding-bottom: 0.7rem; + height: 170px; +}</pre> + +<p>Третья область (содержащая записываемые образцы, которые можно воспроизвести) должна занимать оставшуюся от первых двух область, независимо от высоты устройства пользователя. Задача может быть решена, установкой высоты третьей области равной 100% родительской высоты, минус высоты и отступы первых двух.</p> + +<pre class="brush: css;highlight[4]">.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; +}</pre> + +<div class="note"> +<p><strong>Примечание </strong>: Функция <code>calc()</code> имеет хорошую поддержку среди современных браузеров, даже в устаревшем Internet Explorer 9.</p> +</div> + +<h3 id="Хак_чекбокса_для_отображениескрытия">Хак чекбокса для отображение/скрытия</h3> + +<p>Он уже хорошо документирован, но думаем его можно упоминуть, заключающийся в том, что можно кликнуть на элемент {{htmlelement("label")}} , содержащий элемент чекбокса для переключения самого чекбока. В нашем приложении веб диктафона он управляет отображением блока информации о приложении, при нажатии на иконку знака вопроса в правом верхнем углу. Сначала мы стилизуем элемент <code><label></code> , тем, что мы хотим, убеждаясь в том, что он имеет достаточно высокий <code>z-index</code> , всегда находящийся выше других элементов :</p> + +<pre class="brush: css">label { + font-family: 'NotoColorEmoji'; + font-size: 3rem; + position: absolute; + top: 2px; + right: 3px; + z-index: 5; + cursor: pointer; +}</pre> + +<p>Затем скрываем настоящий чекбокс, избегая неразберихи в интерфейсе :</p> + +<pre class="brush: css">input[type=checkbox] { + position: absolute; + top: -100px; +}</pre> + +<p>Затем стилизуем блок информации (обернутый в элемент {{htmlelement("aside")}}) по вкусу, давая ему фиксированную позицию, так что бы он не показывался в потоке разметки и влиял на основной интерфейс, трансформируем его позицию функцией трансформации, определяя его место по умолчанию, и меняем значение функции трансформации для плавного его отображения/скрытия:</p> + +<pre class="brush: css">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)); +}</pre> + +<p>Наконец определяем правило при нажатии чекбокса . Когда он выбран (когда нажат элемент <code>label</code>) соседний элемент <code><aside></code> получит значение горизонтального перехода и переместится в представление:</p> + +<pre class="brush: css">input[type=checkbox]:checked ~ aside { + transform: translateX(0); +}</pre> + +<h2 id="Основные_настройки_приложения">Основные настройки приложения</h2> + +<p>Для получения мелиапотока, который нужно захватить используется метод <code>getUserMedia()</code>. Затем используется интерфейс <code>MediaRecorder</code>, для записи потока и вывода каждого отрывка записи в атрибут элемента {{htmlelement("audio")}} для воспроизведения.</p> + +<p>Объявим некоторые переменные для кнопок начала записи и остановки, а так же элемент {{htmlelement("article")}} , который будет содержать аудио плееры:</p> + +<pre class="brush: js">const record = document.querySelector('.record'); +const stop = document.querySelector('.stop'); +const soundClips = document.querySelector('.sound-clips');</pre> + +<p>Наконец, для этого раздела создадим базовую структуру <code>getUserMedia</code> :</p> + +<pre class="brush: js">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 <code>getUserMedia</code> error occured: ' + err); + } + ); +} else { + console.log('getUserMedia not supported on your browser!'); +}</pre> + +<p>Все обернуто в условный тест, проверяющий поддержку <code>getUserMedia</code> до запуска чего нибудь. Затем вызываем <code>getUserMedia()</code> и внутри определяем:</p> + +<ul> + <li><strong>Ограничения </strong>: Приложению нужен только аудиопоток .</li> + <li><strong>Функция успешного вызова</strong>: Запускается при успешном получении потока функцией <code>getUserMedia</code> .</li> + <li><strong>Функция обработки ошибок</strong>: Если функция <code>getUserMedia</code> по какой либо причине завершиться с ошибкой.</li> +</ul> + +<div class="note"> +<p><strong>Примечание </strong>: Весь код ниже помещается внутрь функции успешного вызова <code>getUserMedia.</code></p> +</div> + +<h2 id="Захват_медиа_потока">Захват медиа потока</h2> + +<p>Как только функция <code>getUserMedia</code> успешно получила медиапоток, создаем новый объект типа Media Recorder конструктором <code>MediaRecorder()</code> и передаем ему поток, полученный функцией. Это точка входа использования интерфейса MediaRecorder — теперь поток готов для захвата и упаковки в объект {{domxref("Blob")}}, в формате по умолчанию, установленного для браузера.</p> + +<pre class="brush: js">const mediaRecorder = new MediaRecorder(stream);</pre> + +<p>Существуют несколько методов объекта {{domxref("MediaRecorder")}} , позволяющие контролировать запись медиапотока; в приложении веб диктофон используется два и прослушиваем некоторые события. Прежде всего используем метод {{domxref("MediaRecorder.start()")}} , для запуска записи потока, после нажатия кнопки старта:</p> + +<pre class="brush: js">record.onclick = function() { + mediaRecorder.start(); + console.log(mediaRecorder.state); + console.log("recorder started"); + record.style.background = "red"; + record.style.color = "black"; +}</pre> + +<p>Когда объект <code>MediaRecorder</code> приступает к записи его свойство {{domxref("MediaRecorder.state")}} получает значение "<code>recording</code>".</p> + +<p>По мере записи, нам нужно получать аудио данные. Для этого регистрируем обработчик события {{domxref("mediaRecorder.ondataavailable")}}:</p> + +<pre class="brush: js">let chunks = []; + +mediaRecorder.ondataavailable = function(e) { + <span class="pl-smi">chunks</span>.<span class="pl-c1">push</span>(<span class="pl-smi">e</span>.<span class="pl-c1">data</span>); +}</pre> + +<div class="blockIndicator note"> +<p><strong>Примечание </strong>: Браузер будет запускать события <code>dataavailable</code> по необходимости (когда внутренний буфер объекта будет переполняться), но если разработчику нужно вмешаться, в вызов метода <code>start()</code> можно включить параметр<code> timeslice</code> , определяющий диапазон захвата в миллисекундах — к примеру, <code>start(10000)</code> , или вызывать функцию запроса данных {{domxref("MediaRecorder.requestData()")}} , запуская событие по необходимости.</p> +</div> + +<p>Наконец используем метод {{domxref("MediaRecorder.stop()")}} при нажатии кнопки остановки записи и завершения упаковки объекта {{domxref("Blob")}} для его использования в приложении.</p> + +<pre class="brush: js">stop.onclick = function() { + mediaRecorder.stop(); + console.log(mediaRecorder.state); + console.log("recorder stopped"); + record.style.background = ""; + record.style.color = ""; +}</pre> + +<p>Обратите внимание, что запись потока может остановиться естественно, если медиапоток кончился(к примеру, если захватывается музыкальный трек и он кончился, или пользователь отключил использование микрофона, чей поток захватывается).</p> + +<h2 id="Получение_и_использования_объекта_blob">Получение и использования объекта blob</h2> + +<p>Когда запись останавливается, свойство <code>state</code> получает значение "<code>inactive</code>", и запускается событие <code>stop</code>. Мы устанавливаем обработчик этого события, используя свойство {{domxref("mediaRecorder.onstop")}}, завершая запись всех полученных порций объекта <code>blob</code> на момент остановки:</p> + +<pre class="brush: js">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); + } +}</pre> + +<p>Пройдем весь код выше и посмотрим, что он делает.</p> + +<p>Сначала отображаем пользователю диалог с запросом имени будущей записи.</p> + +<p>Затем создаем структуру HTML , вставляя её в контейнер, представляющийся элементом {{htmlelement("article")}} .</p> + +<pre class="brush: html"><article class="clip"> + <audio controls></audio> + <p><em>your clip name</em></p> + <button>Delete</button> +</article></pre> + +<p>После этого создаем объект {{domxref("Blob")}} с комбинацией наших записанных частей аудио и создаем объект URL ссылающийся на него, используя метод <code>window.URL.createObjectURL(blob)</code>. Затем устанавливаем значение атрибута {{htmlattrxref("src", "audio")}} элемента {{HTMLElement("audio")}} в созданный объект URL, так, что бы при нажатии на кнопку воспроизведения объект <code>Blob</code> передал свои данные элементу.</p> + +<p>Наконец, устанавливаем обработчик события <code>onclick</code> на кнопке удаления, для удаления всей структуры HTML проигрывания результата записи (элемент clip).</p> + +<h2 id="Спецификации">Спецификации</h2> + +<table class="standard-table"> + <tbody> + <tr> + <th scope="col">Specification</th> + <th scope="col">Status</th> + <th scope="col">Comment</th> + </tr> + <tr> + <td>{{SpecName("MediaStream Recording", "#MediaRecorderAPI")}}</td> + <td>{{Spec2("MediaStream Recording")}}</td> + <td>Initial definition</td> + </tr> + </tbody> +</table> + +<h2 id="Совместимость_с_браузерами">Совместимость с браузерами</h2> + +<h3 id="MediaRecorder"><code>MediaRecorder</code></h3> + + + +<p>{{Compat("api.MediaRecorder")}}</p> + +<h2 id="Смтотри_так_же">Смтотри так же</h2> + +<ul> + <li>Страница <a href="/en-US/docs/Web/API/MediaRecorder_API">MediaRecorder API</a> </li> + <li><code>{{domxref("Navigator.getUserMedia()")}}</code></li> + <li>Интерфейс <a href="https://addpipe.com/blog/media-recorder-api-is-now-supported-by-65-of-all-desktop-internet-users/">MediaRecorder теперь поддерживают 65% вебсайтов</a></li> +</ul> |
