diff options
| author | Peter Bengtsson <mail@peterbe.com> | 2020-12-08 14:40:17 -0500 |
|---|---|---|
| committer | Peter Bengtsson <mail@peterbe.com> | 2020-12-08 14:40:17 -0500 |
| commit | 33058f2b292b3a581333bdfb21b8f671898c5060 (patch) | |
| tree | 51c3e392513ec574331b2d3f85c394445ea803c6 /files/ja/web/api/mediastream_recording_api | |
| parent | 8b66d724f7caf0157093fb09cfec8fbd0c6ad50a (diff) | |
| download | translated-content-33058f2b292b3a581333bdfb21b8f671898c5060.tar.gz translated-content-33058f2b292b3a581333bdfb21b8f671898c5060.tar.bz2 translated-content-33058f2b292b3a581333bdfb21b8f671898c5060.zip | |
initial commit
Diffstat (limited to 'files/ja/web/api/mediastream_recording_api')
3 files changed, 726 insertions, 0 deletions
diff --git a/files/ja/web/api/mediastream_recording_api/index.html b/files/ja/web/api/mediastream_recording_api/index.html new file mode 100644 index 0000000000..ef3408d76b --- /dev/null +++ b/files/ja/web/api/mediastream_recording_api/index.html @@ -0,0 +1,170 @@ +--- +title: MediaStream Recording API +slug: Web/API/MediaStream_Recording_API +tags: + - API + - Audio + - Media + - Media Capture and Streams + - MediaStream Recording + - MediaStream Recording API + - Overview + - Reference + - Video +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="/ja/docs/Web/API/Media_Streams_API">Media Capture and Streams API</a> および <a href="/ja/docs/Web/API/WebRTC_API">WebRTC API</a> と密接に関係しています。 MediaStream Recording API を使用すると、{{domxref("MediaStream")}} オブジェクトまたは {{domxref("HTMLMediaElement")}} オブジェクトによって生成されたデータを分析、処理、またはディスクへの保存のためにキャプチャすることができます。 また、驚くほど簡単に作業できます。</span></p> + +<h2 id="Basic_concepts" name="Basic_concepts">基本概念</h2> + +<p>MediaStream Recording API は、{{domxref("MediaRecorder")}} という1つの主要なインターフェイスで構成されています。 <code>MediaRecorder</code> は、{{domxref("MediaStream")}} からデータを取得して処理のためにユーザーに配信するというすべての作業を行います。 データは、<code>MediaRecorder</code> の作成時にすでに指定した形式で、一連の {{event("dataavailable")}} イベントによって配信されます。 その後、データをさらに処理するか、必要に応じてファイルに書き込むことができます。</p> + +<h3 id="Overview_of_the_recording_process" name="Overview_of_the_recording_process">記録プロセスの概要</h3> + +<p>ストリームを記録(recording、録音、録画)するプロセスは簡単です。</p> + +<ol> + <li>メディアデータのソースとして機能する {{domxref("MediaStream")}} または {{domxref("HTMLMediaElement")}}({{HTMLElement("audio")}} 要素または {{HTMLElement("video")}} 要素の形式)を設定します。</li> + <li>ソースストリームと必要なオプション(コンテナの MIME タイプやトラックの必要なビットレートなど)を指定して、{{domxref("MediaRecorder")}} オブジェクトを作成します。</li> + <li>{{domxref("MediaRecorder.ondataavailable")}} に {{event("dataavailable")}} イベントのイベントハンドラを設定します。 データが利用可能になるたびにこれが呼び出されます。</li> + <li>ソースメディアが再生され、動画を録画する準備が整ったら、{{domxref("MediaRecorder.start()")}} を呼び出して録画を開始します。</li> + <li>{{event("dataavailable")}} イベントハンドラは準備ができたデータがあるたびに呼ばれます。 イベントは、<code>data</code> 属性を持ち、その値はメディアデータを含む {{domxref("Blob")}} です。 あなたは <code>dataavailable</code> イベントを発生させることができ、それによって最新のサウンドをあなたに届けるので、それをフィルターにかけたり、それを保存したりすることができます。</li> + <li>ソースメディアの再生が停止すると、録画は自動的に停止します。</li> + <li>{{domxref("MediaRecorder.stop()")}} を呼び出すことで、いつでも録画を停止できます。</li> +</ol> + +<div class="note"> +<p><strong>注</strong>: 記録されたメディアのスライスを含む個々の {{domxref("Blob")}} は、必ずしも個別に再生できるわけではありません。 再生する前にメディアを組み立て直す必要があります。</p> +</div> + +<p>記録中に問題が発生した場合は、{{event("error")}} イベントが <code>MediaRecorder</code> に送られます。 {{domxref("MediaRecorder.onerror", "onerror")}} イベントハンドラを設定することで <code>error</code> イベントを監視できます。</p> + +<p>ここでの例では、{{domxref("MediaStream")}}のソースとしてHTML Canvasを利用し、9秒後に録画を停止します。</p> + +<pre class="notranslate">var canvas = document.querySelector("canvas"); + +// Optional frames per second argument. +var stream = canvas.captureStream(25); +var recordedChunks = []; + +console.log(stream); +var options = { mimeType: "video/webm; codecs=vp9" }; +mediaRecorder = new MediaRecorder(stream, options); + +mediaRecorder.ondataavailable = handleDataAvailable; +mediaRecorder.start(); + +function handleDataAvailable(event) { + console.log("data-available"); + if (event.data.size > 0) { + recordedChunks.push(event.data); + console.log(recordedChunks); + download(); + } else { + // ... + } +} +function download() { + var blob = new Blob(recordedChunks, { + type: "video/webm" + }); + var url = URL.createObjectURL(blob); + var a = document.createElement("a"); + document.body.appendChild(a); + a.style = "display: none"; + a.href = url; + a.download = "test.webm"; + a.click(); + window.URL.revokeObjectURL(url); +} + +// demo: to download after 9sec +setTimeout(event => { + console.log("stopping"); + mediaRecorder.stop(); +}, 9000);</pre> + +<h3 id="Examining_and_controlling_the_recorder_status" name="Examining_and_controlling_the_recorder_status">レコーダーの状態を調べて制御する</h3> + +<p><code>MediaRecorder</code> オブジェクトのプロパティを使用して記録プロセスの状態を決定したり、{{domxref("MediaRecorder.pause", "pause()")}} および {{domxref("MediaRecorder.resume", "resume()")}} メソッドを使用してソースメディアの記録を一時停止したり再開したりすることもできます。</p> + +<p>特定の MIME タイプがサポートされているかどうかを確認する必要がある場合は、それも可能です。 {{domxref("MediaRecorder.isTypeSupported()")}} を呼び出すだけです。</p> + +<h3 id="Examining_potential_input_sources" name="Examining_potential_input_sources">見込みがある入力ソースの調査</h3> + +<p>カメラやマイクの入力を記録することが目的の場合は、<code>MediaRecorder</code> の構築プロセスを開始する前に、使用可能な入力デバイスを調べてください。 そのためには、{{domxref("MediaDevices.enumerateDevices", "navigator.mediaDevices.enumerateDevices()")}} を呼び出して利用可能なメディアデバイスのリストを取得する必要があります。 その後、そのリストを調べて見込みがある入力ソースを特定し、さらに必要な基準に基づいてリストをフィルタリングすることもできます。</p> + +<p>このコードスニペットでは、<code>enumerateDevices()</code> を使用して使用可能な入力デバイスを調べ、音声入力デバイスであるものを見つけて、{{HTMLElement("option")}} 要素を作成し、それを入力ソースピッカーを表す {{HTMLElement("select")}} 要素に追加します。</p> + +<pre class="brush: js notranslate">navigator.mediaDevices.enumerateDevices() +.then(function(devices) { + devices.forEach(function(device) { + let menu = document.getElementById("inputdevices"); + if (device.kind == "audioinput") { + let item = document.createElement("option"); + item.innerHTML = device.label; + item.value = device.deviceId; + menu.appendChild(item); + } + }); +});</pre> + +<p>これと同じようなコードを使用して、ユーザーが使用したいデバイスのセットを制限することができます。</p> + +<h3 id="For_more_information" name="For_more_information">詳細については</h3> + +<p>MediaStream Recording API の使用方法の詳細については、<a href="/ja/docs/Web/API/MediaStream_Recording_API/Using_the_MediaStream_Recording_API">MediaStream Recording API の使用</a>を参照してください。 これは、API を使用してオーディオクリップを記録する方法を示します。 2番目の記事の<a href="/ja/docs/Web/API/MediaStream_Recording_API/Recording_a_media_element">メディア要素の記録</a>では、{{HTMLElement("audio")}} 要素または {{HTMLElement("video")}} 要素からストリームを受信してキャプチャしたストリームを使用する方法(この場合は記録してローカルディスクに保存する方法)について説明します。</p> + +<h2 id="Reference" name="Reference">参照</h2> + +<dl> + <dt>{{domxref("BlobEvent")}}</dt> + <dd>メディアデータのチャンク(chunk、大きな塊)が記録され終えるたびに、<code>dataavailable</code> 型の {{domxref("BlobEvent")}} を使用して {{domxref("Blob")}} 形式で消費者に配信されます。</dd> + <dt>{{domxref("MediaRecorder")}}</dt> + <dd>MediaStream Recording API を実装する主要インタフェース。</dd> + <dt>{{domxref("MediaRecorderErrorEvent")}}</dt> + <dd>MediaStream Recording API によって投げられたエラーを表すインターフェース。 その {{domxref("MediaRecorderErrorEvent.error", "error")}} プロパティは、エラーが発生したことを示す {{domxref("DOMException")}} です。</dd> +</dl> + +<h2 id="Specifications" name="Specifications">仕様</h2> + +<table class="standard-table"> + <tbody> + <tr> + <th scope="col">仕様</th> + <th scope="col">状態</th> + <th scope="col">コメント</th> + </tr> + <tr> + <td>{{SpecName("MediaStream Recording", "#MediaRecorderAPI")}}</td> + <td>{{Spec2("MediaStream Recording")}}</td> + <td>初期定義</td> + </tr> + </tbody> +</table> + +<h2 id="Browser_compatibility" name="Browser_compatibility">ブラウザー実装状況</h2> + +<h3 id="MediaRecorder" name="MediaRecorder"><code>MediaRecorder</code></h3> + + + +<p>{{Compat("api.MediaRecorder")}}</p> + +<h2 id="See_also" name="See_also">関連情報</h2> + +<ul> + <li><a href="/ja/docs/Web/API/MediaStream_Recording_API/Using_the_MediaStream_Recording_API">MediaStream Recording API の使用</a></li> + <li><a href="/ja/docs/Web/API/MediaStream_Recording_API/Recording_a_media_element">メディア要素の記録</a></li> + <li><a href="https://simpl.info/mediarecorder/">simpl.info の MediaStream Recording のデモ</a>、<a href="https://twitter.com/sw12">Sam Dutton</a> 著(英語)</li> + <li>{{domxref("MediaDevices.getUserMedia()","navigator.mediaDevices.getUserMedia()")}}</li> + <li><a href="https://addpipe.com/blog/mediarecorder-api/">Chrome および Firefox で動作中の HTML5 の Media Recorder API</a>(英語)</li> + <li>Safari および Edge 用の <a href="https://github.com/ai/audio-recorder-polyfill">MediaRecorder ポリフィル</a>(英語)</li> + <li><a href="https://github.com/chrisjohndigital/TutorRoom">TutorRoom</a>: getUserMedia および MediaRecorder APIを使用した HTML5 動画のキャプチャ/再生/ダウンロード(<a href="https://github.com/chrisjohndigital/TutorRoom">GitHub のソース</a>)(英語)</li> + <li><a href="http://codepen.io/anon/pen/gpmPzm">簡単な動画録画デモ</a></li> + <li><a href="https://quickblox.github.io/javascript-media-recorder/sample/">高度なメディアストリームレコーダーのサンプル</a>(英語)</li> + <li><a href="https://github.com/chrisjohndigital/OpenLang">OpenLang</a>: MediaDevices と MediaStream Recording API を動画の録画に使用する HTML5 動画言語ラボウェブアプリ(<a href="https://github.com/chrisjohndigital/OpenLang">GitHub のソース</a>)(英語)</li> + <li><a href="https://addpipe.com/blog/safari-technology-preview-73-adds-limited-mediastream-recorder-api-support/">Safari 技術プレビュー 73 で MediaStream Recorder API が利用可能に</a>(英語)</li> +</ul> diff --git a/files/ja/web/api/mediastream_recording_api/recording_a_media_element/index.html b/files/ja/web/api/mediastream_recording_api/recording_a_media_element/index.html new file mode 100644 index 0000000000..6764984d10 --- /dev/null +++ b/files/ja/web/api/mediastream_recording_api/recording_a_media_element/index.html @@ -0,0 +1,282 @@ +--- +title: メディア要素の記録 +slug: Web/API/MediaStream_Recording_API/Recording_a_media_element +tags: + - API + - Audio + - Example + - Guide + - Media + - Media Recording + - MediaStream Recording + - Tutorial + - Video +translation_of: Web/API/MediaStream_Recording_API/Recording_a_media_element +--- +<div>{{DefaultAPISidebar("MediaStream Recording")}}</div> + +<p>MediaStream Recording API の使用の記事では、 {{domxref("MediaDevices.getUserMedia()","navigator.mediaDevices.getUserMedia()")}} から返されるように、{{domxref("MediaRecorder")}} インターフェイスを使用してハードウェアデバイスによって生成された {{domxref("MediaStream")}} をキャプチャする方法について説明しましたが、記録する <code>MediaStream</code> のソースとして HTML メディア要素({{HTMLElement("audio")}} または {{HTMLElement("video")}})も使用できます。 この記事では、それを実現する例を見ていきます。</p> + +<div id="Example"> +<h2 id="HTML_content" name="HTML_content">HTML の内容</h2> + +<div class="hidden"> +<pre class="brush: html notranslate"><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 notranslate"><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>2つの欄で主要なインターフェースを提示します。 左欄には、Start(開始)ボタンと動画プレビューを表示する {{HTMLElement("video")}} 要素があります。 これは、ユーザーのカメラが見ている動画です。 {{htmlattrxref("autoplay", "video")}} 属性は、カメラからストリームが到着したらすぐに表示するために使用し、{{htmlattrxref("muted", "video")}} 属性は、ユーザーのマイクからの音声をスピーカーに出力しないように使用していることに注意してください。 出力すると醜いフィードバックループ(ハウリング)を引き起こします。</p> + +<pre class="brush: html notranslate"><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>右欄には、Stop(停止)ボタンと録画された動画の再生に使用する <code><video></code> 要素があります。 再生パネルには <code>autoplay</code> を設定せずに(メディアが到着しても再生が開始されない)、{{htmlattrxref("controls", "video")}} を設定して、再生や一時停止などのユーザーコントロールを表示するように指示しています。</p> + +<p>再生要素の下には、録画した動画をダウンロードするためのボタンがあります。</p> + +<div class="hidden"> +<pre class="brush: html notranslate"><div class="bottom"> + <pre id="log"></pre> +</div></pre> +<strong>CSS content</strong> + +<pre class="brush: css notranslate">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_content" name="JavaScript_content">JavaScript の内容</h2> + +<p>それでは、JavaScript コードを見てみましょう。 結局のところ、これがアクションの大部分が起こるところです!</p> + +<h3 id="Setting_up_global_variables" name="Setting_up_global_variables">グローバル変数の設定</h3> + +<p>必要なグローバル変数をいくつか設定することから始めます。</p> + +<pre class="brush: js notranslate">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="Utility_functions" name="Utility_functions">ユーティリティ関数</h3> + +<p>次に、後で使用するユーティリティ関数をいくつか作成します。</p> + +<pre class="brush: js notranslate">function log(msg) { + logElement.innerHTML += msg + "\n"; +} +</pre> + +<p><code>log()</code> 関数は、ユーザーと情報を共有できるように、テキスト文字列を {{HTMLElement("div")}} に出力するために使用します。 それほどきれいではありませんが、この仕事は私たちの目的のために行われます。</p> + +<pre class="brush: js notranslate">function wait(delayInMS) { + return new Promise(resolve => setTimeout(resolve, delayInMS)); +} +</pre> + +<p><code>wait()</code> 関数は、指定したミリ秒数が経過すると解決する新しい {{jsxref("Promise")}} を返します。 タイムアウトハンドラ関数として promise の解決ハンドラを指定して、{{domxref("window.setTimeout()")}} を呼び出す<a href="/ja/docs/Web/JavaScript/Reference/Functions/Arrow_functions">アロー関数</a>を使用して動作します。 これにより、タイムアウトを使用するときに promise 構文を使用できます。 これは、後で説明するように、promise を連鎖させるときに非常に便利です。</p> + +<h3 id="Starting_media_recording" name="Starting_media_recording">メディア録画の開始</h3> + +<p><code>startRecording()</code> 関数は録画プロセスの開始を処理します。</p> + +<pre class="brush: js notranslate">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> は2つの入力パラメータを取ります。 録画元の {{domxref("MediaStream")}} と記録するミリ秒単位の長さです。 指定されたミリ秒数以下のメディアを常に録画しますが、その時間に達する前にメディアが停止すると、{{domxref("MediaRecorder")}} も自動的に録画を停止します。</p> + +<dl> + <dt>2行目</dt> + <dd>入力ストリーム(<code>stream</code>)の録画を処理する <code>MediaRecorder</code> を作成します。</dd> + <dt>3行目</dt> + <dd>空の配列 <code>data</code> を作成します。 これは、{{domxref("MediaRecorder.ondataavailable", "ondataavailable")}} イベントハンドラに提供されたメディアデータの {{domxref("Blob")}} を保持するために使用します。</dd> + <dt>5行目</dt> + <dd>{{event("dataavailable")}} イベントのハンドラを設定します。 受信したイベントの <code>data</code> プロパティはメディアデータを含む {{domxref("Blob")}} です。 イベントハンドラは単純に <code>Blob</code> を <code>data</code> 配列にプッシュ(末尾に追加)します。</dd> + <dt>6〜7行目</dt> + <dd>{{domxref("MediaRecorder.start", "recorder.start()")}} を呼び出して録画処理を開始し、<code>recorder</code> の更新された状態と録画される秒数とともにメッセージをログに出力します。</dd> + <dt>9〜12行目</dt> + <dd><code>stopped</code> という名前の新しい {{jsxref("Promise")}} を作成します。 これは、<code>MediaRecorder</code> の {{domxref("MediaRecorder.onstop", "onstop")}} イベントハンドラが呼び出されると解決し、<code>MediaRecorder</code> の {{domxref("MediaRecorder.onerror", "onerror")}} イベントハンドラが呼び出されると拒否します。 拒否ハンドラは、発生したエラーの名前を入力として受け取ります。</dd> + <dt>14〜16行目</dt> + <dd><code>recorded</code> という名前の新しい <code>Promise</code> を作成します。 これは、指定されたミリ秒数が経過すると解決します。 解決すると、<code>MediaRecorder</code> が録画中の場合は停止します。</dd> + <dt>18〜22行目</dt> + <dd>これらの行は、2つの <code>Promise</code>(<code>stopped</code> と <code>recorded</code>)の両方が解決したときに満たされる新しい <code>Promise</code> を作成します。 それが解決すると、配列データは <code>startRecording(</code>) によってその呼び出し元に返されます。</dd> +</dl> + +<h3 id="Stopping_the_input_stream" name="Stopping_the_input_stream">入力ストリームの停止</h3> + +<p><code>stop()</code> 関数は単に入力メディアを停止します。</p> + +<pre class="brush: js notranslate">function stop(stream) { + stream.getTracks().forEach(track => track.stop()); +} +</pre> + +<p>これは {{domxref("MediaStream.getTracks()")}} を呼び出し、{{jsxref("Array.forEach", "forEach()")}} を使用してストリーム内の各トラックの {{domxref("MediaStreamTrack.stop()")}} を呼び出すことによって機能します。</p> + +<h3 id="Getting_an_input_stream_and_setting_up_the_recorder" name="Getting_an_input_stream_and_setting_up_the_recorder">入力ストリームを取得してレコーダーを設定</h3> + +<p>それでは、この例で最も複雑なコードを見てみましょう。 開始ボタンをクリックしたときのイベントハンドラです。</p> + +<pre class="brush: js notranslate">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>{{event("click")}} イベントが発生すると、次のようになります。</p> + +<dl> + <dt>2〜4行目</dt> + <dd>{{domxref("MediaDevices.getUserMedia()","navigator.mediaDevices.getUserMedia()")}} は、動画トラックと音声トラックの両方を持つ新しい {{domxref("MediaStream")}} を要求するために呼び出します。 これが録画するストリームです。</dd> + <dt>5〜9行目</dt> + <dd><code>getUserMedia()</code> から返された Promise が解決すると、プレビューの {{HTMLElement("video")}} 要素の {{domxref("HTMLMediaElement.srcObject","srcObject")}} プロパティを入力ストリームに設定し、ユーザーのカメラでキャプチャしている動画をプレビューボックスに表示します。 <code><video></code> 要素はミュートしているので、音声は再生しません。 Download(ダウンロード)ボタンのリンクも、ストリームを参照するように設定します。 次に、8行目で、{{domxref("MediaRecorder.captureStream()")}} メソッドがなければ接頭辞が付いた <code>preview.mozCaptureStream()</code> を呼び出すように <code>preview.captureStream()</code> を設定して、コードが Firefox で機能するようにします。 その後、プレビュー動画の再生開始時に解決する新しい {{jsxref("Promise")}} を作成して返します。</dd> + <dt>10行目</dt> + <dd>プレビュー動画の再生が開始されると、録画するメディアがあることがわかります。 したがって、先ほど作成した <code>{{anch("Starting media recording", "startRecording()")}}</code> 関数を呼び出し、プレビュー動画ストリーム(録画するソースメディアとして)と、<code>recordingTimeMS</code>(録画するメディアのミリ秒数として)を渡します。 前述のように、<code>startRecording()</code> は、録画が完了すると、解決ハンドラが呼び出される {{jsxref("Promise")}}(録画されたメディアデータのチャンクを含む {{domxref("Blob")}} オブジェクトの配列を入力として受け取る)を返します。</dd> + <dt>11〜15行目</dt> + <dd>録画プロセスの解決ハンドラは、ローカルに <code>recordedChunks</code> として知られるメディアデータの <code>Blob</code> の配列を入力として受け取ります。 最初にすることは、{{domxref("Blob.Blob", "Blob()")}} コンストラクターがオブジェクトの配列を1つのオブジェクトに連結するという事実を利用して、チャンクを MIME タイプが <code>"video/webm"</code> の単一の {{domxref("Blob")}} にマージすることです。 次に、{{domxref("URL.createObjectURL()")}} を使用して <code>Blob</code> を参照する URL を作成します。 これは、ダウンロードされた動画再生要素の {{htmlattrxref("src", "video")}} 属性の値(<code>Blob</code> から動画を再生できるようにする)とダウンロードボタンのリンクのターゲットになります。 + <p>その後、ダウンロードボタンの <code>download</code> 属性が設定されます。 {{htmlattrxref("download", "a")}} 属性は <code>Boolean</code> にすることができますが、ダウンロードするファイルの名前として使用する文字列に設定することもできます。 そのため、ダウンロードリンクの <code>download</code> 属性を <code>"RecordedVideo.webm"</code> に設定することで、ボタンをクリックすると内容が録画された動画である <code>"RecordedVideo.webm"</code> という名前のファイルをダウンロードするようにブラウザーに指示します。</p> + </dd> + <dt>17〜18行目</dt> + <dd>記録されたメディアのサイズと種類は、2つの動画とダウンロードボタンの下のログ領域に出力されます。</dd> + <dt>20行目</dt> + <dd>すべての <code>Promise</code> の <code>catch()</code> は、<code>log()</code> 関数を呼び出すことによってエラーをロギング領域に出力します。</dd> +</dl> + +<h3 id="Handling_the_stop_button" name="Handling_the_stop_button">停止ボタンの取り扱い</h3> + +<p>最後のコードでは、{{domxref("EventTarget.addEventListener", "addEventListener()")}} を使用して停止ボタンの {{event("click")}} イベントのハンドラを追加します。</p> + +<pre class="brush: js notranslate">stopButton.addEventListener("click", function() { + stop(preview.srcObject); +}, false);</pre> + +<p>これは先ほど説明した <code>{{anch("Stopping the input stream", "stop()")}}</code> 関数を呼び出すだけです。</p> +</div> + +<h2 id="Result" name="Result">結果</h2> + +<p>残りの HTML と上に示されていない CSS をすべてまとめると、次のようになり、動作します。</p> + +<p>{{ EmbedLiveSample('Example', 600, 440, "", "", "", "camera;microphone") }}</p> + +<p>API がどのように使用されているかの説明には重要ではないため上で隠されている部分も含めて、{{LiveSampleLink("Example", "すべてのコードを見る")}}ことができます。</p> + +<h2 id="See_also" name="See_also">関連情報</h2> + +<ul> + <li><a href="/ja/docs/Web/API/MediaStream_Recording_API">MediaStream Recording API</a></li> + <li><a href="/ja/docs/Web/API/MediaStream_Recording_API/Using_the_MediaStream_Recording_API">MediaStream Recording API の使用</a></li> + <li><a href="/ja/docs/Web/API/Media_Streams_API">Media Capture and Streams API</a></li> +</ul> diff --git a/files/ja/web/api/mediastream_recording_api/using_the_mediastream_recording_api/index.html b/files/ja/web/api/mediastream_recording_api/using_the_mediastream_recording_api/index.html new file mode 100644 index 0000000000..125ec7f6a6 --- /dev/null +++ b/files/ja/web/api/mediastream_recording_api/using_the_mediastream_recording_api/index.html @@ -0,0 +1,274 @@ +--- +title: MediaStream Recording API の使用 +slug: Web/API/MediaStream_Recording_API/Using_the_MediaStream_Recording_API +tags: + - API + - Example + - Guide + - MediaRecorder + - MediaStream Recording API + - Tutorial +translation_of: Web/API/MediaStream_Recording_API/Using_the_MediaStream_Recording_API +--- +<div>{{DefaultAPISidebar("MediaStream Recording")}}</div> + +<div class="summary"> +<p><span class="seoSummary"><a href="/ja/docs/Web/API/MediaStream_Recording_API">MediaStream Recording API</a> を使用すると、音声や動画のストリームを簡単に記録できます。 {{domxref("MediaDevices.getUserMedia()","navigator.mediaDevices.getUserMedia()")}} と一緒に使用すると、ユーザーの入力デバイスから記録して結果を即座にウェブアプリで使用するための簡単な方法が提供されます。</span> 音声と動画の両方を別々にまたは一緒に記録することができます。 この記事では、この API を提供する <code>MediaRecorder</code> インターフェースの使用方法に関する基本的なガイドを提供することを目的としています。</p> +</div> + +<h2 id="A_sample_application_Web_Dictaphone" name="A_sample_application_Web_Dictaphone">サンプルアプリ: ウェブディクタフォン</h2> + +<p><img alt="ウェブディクタフォンのサンプルアプリの画像 - 正弦波のサウンドの視覚化、次に録音と停止ボタン、そして再生可能な録音済みトラックの音声ジュークボックス。" src="https://mdn.mozillademos.org/files/7885/web-dictaphone.png" style="display: block; margin: 0px auto;"></p> + +<p>MediaRecorder API の基本的な使い方を説明するために、ウェブベースのディクタフォン(dictaphone)を作りました。 それは音声の断片を録音してからそれらを再生することを可能にします。 Web Audio API を使用して、デバイスのサウンド入力を視覚化することもできます。 この記事では録音と再生の機能に集中します。</p> + +<p>この<a href="https://mdn.github.io/web-dictaphone/">デモがライブで実行</a>されているのを見ることも、GitHub で<a href="https://github.com/mdn/web-dictaphone">ソースコードを入手</a>することもできます。</p> + +<h2 id="CSS_goodies" name="CSS_goodies">CSS のおいしいところ</h2> + +<p>このアプリでは HTML は非常に単純なので、ここでは説明しません。 言及する価値がある、もう少し興味深い CSS がいくつかありますので、それらについて以下で説明します。 CSS に興味がなく、JavaScript に直行したいのであれば、{{anch("Basic app setup","基本的なアプリの設定")}}のセクションに進んでください。</p> + +<h3 id="Keeping_the_interface_constrained_to_the_viewport_regardless_of_device_height_with_calc" name="Keeping_the_interface_constrained_to_the_viewport_regardless_of_device_height_with_calc">calc() で、デバイスの高さに関係なく、インタフェースをビューポートに制限</h3> + +<p>{{cssxref("calc")}} 関数は、CSS でまとめられた便利で小さなユーティリティ機能の1つで、最初はあまり見かけませんが、すぐに「うわー、以前これがなかったのはなぜ? CSS2 のレイアウトが厄介だったのはなぜ?」とあなたに考えさせ始めます。 それはプロセスで異なる単位を混合して、CSS 単位の計算値を決定するための計算をすることを可能にします。</p> + +<p>例えば、ウェブディクタフォンには、3つの主要な UI 領域が縦に積み重ねられています。 最初の2つ(ヘッダーとコントロール)の高さを固定したいと思いました。</p> + +<pre class="brush: css notranslate">header { + height: 70px; +} + +.main-controls { + padding-bottom: 0.7rem; + height: 170px; +}</pre> + +<p>ただし、デバイスの高さに関係なく、3番目の領域(再生できる録音済みサンプルが含まれている領域)に、残っているスペースをすべて確保したいと考えました。 フレックスボックスがここでの答えかもしれませんが、それはそのような単純なレイアウトのために少しやり過ぎです。 代わりに、次のように3番目のコンテナーの高さを親の高さの 100% から、他の2つの高さとパディングを引いたものに等しくすることで、問題を解決しました。</p> + +<pre class="brush: css;highlight[4] notranslate">.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="Checkbox_hack_for_showinghiding" name="Checkbox_hack_for_showinghiding">表示/非表示のチェックボックスのハック</h3> + +<p>これはすでにかなりよく文書化されていますが、チェックボックスのハックについて言及したいと思います。 これは、チェックボックスの {{htmlelement("label")}} をクリックしてオン/オフを切り替えることができるという事実を乱用します。 ウェブディクタフォンでは、これにより情報画面が表示され、この画面は、右上隅にある疑問符アイコンをクリックすると表示/非表示になります。 まず最初に、次のように <code><label></code> を好きなようにスタイルして、他の要素の上に常に収まるように十分な <code>z-index</code> があり、したがってフォーカス可能/クリック可能になるようにします。</p> + +<pre class="brush: css notranslate">label { + font-family: 'NotoColorEmoji'; + font-size: 3rem; + position: absolute; + top: 2px; + right: 3px; + z-index: 5; + cursor: pointer; +}</pre> + +<p>次に、次のように実際のチェックボックスを非表示にします。 これは、UI を混乱させたくないためです。</p> + +<pre class="brush: css notranslate">input[type=checkbox] { + position: absolute; + top: -100px; +}</pre> + +<p>次に、({{htmlelement("aside")}} 要素で囲まれた)情報画面に希望のスタイルを設定し、レイアウトフローに表示されずメイン UI に影響しないように固定の位置を指定し、デフォルトで収まって欲しい位置に変換し、スムーズな表示/非表示のための遷移を与えます。</p> + +<pre class="brush: css notranslate">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><aside></code> 要素の水平方向の平行移動値が変更され、スムーズにビューに遷移するという規則を書きます。</p> + +<pre class="brush: css notranslate">input[type=checkbox]:checked ~ aside { + transform: translateX(0); +}</pre> + +<h2 id="Basic_app_setup" name="Basic_app_setup">基本的なアプリの設定</h2> + +<p>キャプチャしたいメディアストリームを入手するには、<code>getUserMedia()</code> を使用します。 その後、MediaRecorder API を使用してストリームを記録し、記録された各スニペットを生成された {{htmlelement("audio")}} 要素のソースに出力して、再生できるようにします。</p> + +<p>次のように録音ボタンと停止ボタン、および生成された音声プレーヤーを含む {{htmlelement("article")}} の変数をいくつか宣言します。</p> + +<pre class="brush: js notranslate">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 notranslate">if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) { + console.log('getUserMedia supported.'); + navigator.mediaDevices.getUserMedia ( + // constraints - only audio needed for this app + { + audio: true + }) + + // Success callback + .then(function(stream) { + + + }) + + // Error callback + .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>constraints</strong>: 音声だけがディクタフォンにキャプチャされます。</li> + <li><strong>Success callback</strong>: このコードは、<code>getUserMedia</code> の呼び出しが正常に完了した後に実行されます。</li> + <li><strong>Error callback</strong>: 何らかの理由で <code>getUserMedia</code> の呼び出しが失敗した場合、このコードが実行されます。</li> +</ul> + +<div class="note"> +<p><strong>注</strong>: 以下のコードはすべて <code>getUserMedia</code> の Success callback 内にあります。</p> +</div> + +<h2 id="Capturing_the_media_stream" name="Capturing_the_media_stream">メディアストリームのキャプチャ</h2> + +<p><code>getUserMedia</code> がメディアストリームを正常に作成したら、<code>MediaRecorder()</code> コンストラクタを使用して新しい Media Recorder インスタンスを作成し、それに直接ストリーム(<code>stream</code>)を渡します。 これが MediaRecorder API を使用するためのエントリポイントです。 これで、ストリームをブラウザーのデフォルトのエンコード形式で {{domxref("Blob")}} にキャプチャする準備ができました。</p> + +<pre class="brush: js notranslate">const mediaRecorder = new MediaRecorder(stream);</pre> + +<p>{{domxref("MediaRecorder")}} インターフェイスには、メディアストリームの記録を制御できる一連のメソッドがあります。 ウェブディクタフォンでは、その内の2つを利用して、いくつかのイベントをリスンしています。 まず、録音ボタンを押すと {{domxref("MediaRecorder.start()")}} を使用してストリームの録音を開始します。</p> + +<pre class="brush: js notranslate">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 notranslate">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>start(10000)</code> )を含めることも、必要なときに {{domxref("MediaRecorder.requestData()")}} を呼び出してイベントを発生させることもできます。</p> +</div> + +<p>最後に、停止ボタンが押されたときに {{domxref("MediaRecorder.stop()")}} メソッドを使用して録音を停止し、アプリの他の場所で使用できるように {{domxref("Blob")}} を完成させます。</p> + +<pre class="brush: js notranslate">stop.onclick = function() { + mediaRecorder.stop(); + console.log(mediaRecorder.state); + console.log("recorder stopped"); + record.style.background = ""; + record.style.color = ""; +}</pre> + +<p>メディアストリームが終了すると(例えば、曲のトラックを入手してトラックが終了した場合や、ユーザーがマイクの共有を停止した場合)、録音も自然に停止することがあることに注意してください。</p> + +<h2 id="Grabbing_and_using_the_blob" name="Grabbing_and_using_the_blob">blob を入手して使う</h2> + +<p>録音が停止すると、<code>state</code> プロパティは <code>"inactive"</code> の値を返し、<code>stop</code> イベントが発生します。 {{domxref("mediaRecorder.onstop")}} を使用してこれのイベントハンドラーを登録し、受け取ったすべてのチャンク(<code>chunks</code>)から <code>blob</code> を確定します。</p> + +<pre class="brush: js notranslate">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 notranslate"><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> を使用して作成します。 次に、{{HTMLElement("audio")}} 要素の {{htmlattrxref("src", "audio")}} 属性の値をオブジェクト URL に設定して、音声プレーヤーの再生ボタンが押されたときに <code>Blob</code> を再生するようにします。</p> + +<p>最後に、削除ボタンに <code>onclick</code> ハンドラーを設定して、クリップの HTML 構造全体を削除する関数にします。</p> + +<h2 id="Specifications" name="Specifications">仕様書</h2> + +<table class="standard-table"> + <thead> + <tr> + <th scope="col">仕様書</th> + <th scope="col">状態</th> + <th scope="col">備考</th> + </tr> + </thead> + <tbody> + <tr> + <td>{{SpecName("MediaStream Recording", "#MediaRecorderAPI")}}</td> + <td>{{Spec2("MediaStream Recording")}}</td> + <td>初回定義</td> + </tr> + </tbody> +</table> + +<h2 id="Browser_compatibility" name="Browser_compatibility">ブラウザーの互換性</h2> + +<h3 id="MediaRecorder" name="MediaRecorder"><code>MediaRecorder</code></h3> + +<div class="hidden">このページの互換性一覧表は構造化データから生成されています。データに協力していただけるのであれば、 <a class="external" href="https://github.com/mdn/browser-compat-data">https://github.com/mdn/browser-compat-data</a> をチェックアウトしてプルリクエストを送信してください。</div> + +<p>{{Compat("api.MediaRecorder")}}</p> + +<h2 id="See_also" name="See_also">関連情報</h2> + +<ul> + <li><a href="/ja/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 API がウェブサイトのユーザーの 65% でサポートされるようになりました</a>(英語)</li> +</ul> |
