WebRTC (getUserMedia())

Webで”WebRTC samples”を見つけました。サンプルと共に”WebRTC”について説明しているHPです。興味が有ったので幾つかのサンプルを読んでみました。

(1)Basic getUserMedia demo

画面左下に有るOpen cameraボタン(赤いやつ)を押すと画面にカメラのライブ動画が表示されます。

このHPのソースは、


<!DOCTYPE html>
<!--
 *  Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree.
-->
<html>
<head>

    <meta charset="utf-8">
    <meta name="description" content="WebRTC code samples">
    <meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1, maximum-scale=1">
    <meta itemprop="description" content="Client-side WebRTC code samples">
    <meta itemprop="image" content="../../../images/webrtc-icon-192x192.png">
    <meta itemprop="name" content="WebRTC code samples">
    <meta name="mobile-web-app-capable" content="yes">
    <meta id="theme-color" name="theme-color" content="#ffffff">

    <base target="_blank">

    <title>getUserMedia</title>

    <link rel="icon" sizes="192x192" href="../../../images/webrtc-icon-192x192.png">
    <link href="//fonts.googleapis.com/css?family=Roboto:300,400,500,700" rel="stylesheet" type="text/css">
    <link rel="stylesheet" href="../../../css/main.css">

</head>

<body>

<div id="container">
    <h1><a href="//webrtc.github.io/samples/" title="WebRTC samples homepage">WebRTC samples</a>
        <span>getUserMedia</span></h1>

    <video id="gum-local" autoplay playsinline></video>
    <button id="showVideo">Open camera</button>

    <div id="errorMsg"></div>

    <p class="warning"><strong>Warning:</strong> if you're not using headphones, pressing play will cause feedback.</p>

    <p>Display the video stream from <code>getUserMedia()</code> in a video element.</p>

    <p>The <code>MediaStream</code> object <code>stream</code> passed to the <code>getUserMedia()</code> callback is in
        global scope, so you can inspect it from the console.</p>

    <a href="https://github.com/webrtc/samples/tree/gh-pages/src/content/getusermedia/gum"
       title="View source for this page on GitHub" id="viewSource">View source on GitHub</a>
</div>

<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
<script src="js/main.js"></script>

<script src="../../../js/lib/ga.js"></script>

</body>
</html>

ここからカメラの動画表示のみ取り出して見ました。

samp01.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>getUserMedia</title>
    </head>
    <body>
        <div id="container">
            <h1>WebRTC samples getUserMedia</h1>
            <video autoplay playsinline></video><br>
            <button id="showVideo" onclick="init()">Open camera</button>
        </div>
        <script>
            function init() {
                navigator.mediaDevices.getUserMedia({audio: false, video: true})
                .then(function(stream) { 
                    document.querySelector('video').srcObject = stream; })
                .catch(function(err) { 
                    console.error(err); } );
            }
        </script>
    </body>
</html>
  • 10行 videoタグ要素を用いて映像表示箇所を作成。この時必要に応じて、autoplay、 playsinline等を指定
  • 11行 Open cameraボタンが押されると関数 “init()”が実行されます。
  • 14行 function init()の定義
  • 15行 navigator.mediaDevices.getUserMedia({audio: false, video: true})
    • この関数が今回のキーです。Webカメラやマイクのメディアストリームを取得する時に使います。
    • 引数で取得したいメディアで指定します。{audio: false, video: true}
    • メディアの取得が成功すれば .then以降が、失敗すれば .catch以降が実行されます。
  • 17行 成功すると、画面とStreamを関連づけ動画を開始。

これだけを見るとWebでの動画の再生が簡単に出来そうです。

(2)Use getUserMedia with canvas

 ボタンを押した瞬間の映像を表示する(写真に撮る)コードです。これも簡単にして見ました。

samp02.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>getUserMedia to canvas</title>
    </head>

    <body>
        <center>
            <h1>WebRTC samples getUserMedia ⇒ canvas</h1>
            <video playsinline autoplay></video><br>
            <button>Take snapshot</button><br>
            <canvas></canvas>
        </center>

        <script>
            'use strict';

            const video = document.querySelector('video');
            const canvas = document.querySelector('canvas');
            const button = document.querySelector('button');
            const constraints = {
                  audio: false,
                  video: true
            };

            navigator.mediaDevices.getUserMedia(constraints).then(handleSuccess).catch(handleError);

            function handleSuccess(stream) {
                  video.srcObject = stream;
            }

            function handleError(error) {
                  console.log('navigator.MediaDevices.getUserMedia error: ', error.message, error.name);
            }

            button.onclick = function() {
                  canvas.width = video.videoWidth;
                  canvas.height = video.videoHeight;
                  canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height);
            };

        </script>
    </body>
</html>
  • 13行: 写真表示用canvas 要素を追加。
  • 27行: navigator.mediaDevices.getUserMedia(constraints).then(handleSuccess).catch(handleError);
    • 実行はこの部分(カメラ使用許可)から。
    • navigator.mediaDevices.getUserMedia関数の引数、完了、失敗処理の指定の書き方が前回と違います。
      • 引数: constraints
        • この変数は22行で設定されています。
        • audio: false/video: true 音ナシ/映像アリ
      • 完了の場合: .then(handleSuccess)
        • 29行に規定されています。video要素srcObject属性にstreamを代入。
        • Streamが開始される。
      • 失敗の場合: catch(handleError)
        • 33行で規定されています。エラー表示。
  • 37行: button.onclick = function() {
    • ボタンを押すとここが実行。
    • 縦、横を動画のサイズに合わせてcanvas.getContext(‘2d’).drawImage()関数で写真を表示。
    • canvas.getContext(‘2d’).drawImage()の引数は、
      • 第1引数:video要素
      • 第2,3引数:画像左上の座標
      • 第4,5引数:画像の幅と高さ

写真は、navigator.mediaDevices.getUserMedia()でMediaStreameを取得し、canvas要素のgetContext(‘2d’).drawImage()関数を使う事により簡単に表示出来ます。

(3)Use getUserMedia with canvas and CSS filters

これは動画、写真に映像効果(Filter)をかけるサンプル。いつもの通りコード簡略化します。

samp03.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>getUserMedia + CSS filters</title>
        <style>
            .none {
                -webkit-filter: none;
                filter: none;
            }

            .blur {
                -webkit-filter: blur(3px);
                filter: blur(3px);
            }

            .grayscale {
                -webkit-filter: grayscale(1);
                filter: grayscale(1);
            }
    
            .invert {
                -webkit-filter: invert(1);
                filter: invert(1);
            }
    
            .sepia {
                -webkit-filter: sepia(1);
                filter: sepia(1);
            }

            .opacity {
                -webkit-filter: opacity(40%);
                filter: opacity(40%);
            }

            button#snapshot {
                margin: 0 10px 25px 0;
                width: 110px;
            }
    
            video {
                object-fit: cover;
            }
        </style>
    </head>
    <body>
        <center>
            <h1>WebRTC samples getUserMedia + CSS filters</h1>
            <video playsinline autoplay></video><br>

            <label for="filter">Filter: </label>
            <select id="filter">
                <option value="none">None</option>
                <option value="blur">Blur</option>
                <option value="grayscale">Grayscale</option>
                <option value="invert">Invert</option>
                <option value="sepia">Sepia</option>
                <option value="opacity">opacity</option>
            </select>

            <button id="snapshot">Take snapshot</button><br>

            <canvas></canvas>
        </center>

        <script>
            'use strict';

            const snapshotButton = document.querySelector('button#snapshot');
            const filterSelect = document.querySelector('select#filter');
            const video = document.querySelector('video');
            const canvas = document.querySelector('canvas');
            const constraints = {
                  audio: false,
                  video: true
            };

            navigator.mediaDevices.getUserMedia(constraints).then(handleSuccess).catch(handleError);

            function handleSuccess(stream) {
                  window.stream = stream; // make stream available to browser console
                  video.srcObject = stream;
            }

            function handleError(error) {
                  console.log('navigator.MediaDevices.getUserMedia error: ', error.message, error.name);
            }

            snapshotButton.onclick = function() {
                  canvas.className = filterSelect.value;
                  canvas.width = video.videoWidth;
                  canvas.height = video.videoHeight;
                  canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height);
            };

            filterSelect.onchange = function() {
                  video.className = filterSelect.value;
            };

        </script>
    </body>
</html>
  • ”Use getUserMedia with canvas”のサンプルにプルダウンメニューがついた様なHP
  • Filterをプルダウンメニューから選択し画像と動画にFilterを掛ける。
  • 6から45行: CSSの指定。Filterの指定が主です。
  • 48から65行: HPの本体。52から60行がプルダウンメニュー
  • 97行: プルダウンメニューが変更されるとこの関数が実行する。
  • 98行: この部分でFilterを掛けている。video要素のclassName属性にFilterの値(filterSelect.value;)を代入しているのみ

98行の、”video.className = filterSelect.value;”のみでFilter効果が得られる様です。

(4)Use getUserMedia with canvas and CSS filters

画像解像度を指定するサンプルです。今回は、QVGA,VGA,HDの3解像度に変更してコードを簡略化しました。

samp04.html

<html>
    <head>
        <meta charset="utf-8">
        <title>getUserMedia: select resolution</title>
    </head>

    <body>

        <center>
            <h1>getUserMedia: select resolution</h1>
            <p></p>
            <p>Click a button to call <code>getUserMedia()</code> with appropriate resolution.</p>

            <div id="buttons">
                <button id="qvga">QVGA</button>
                <button id="vga">VGA</button>
                <button id="hd">HD</button>
            </div>

            <div id="videoblock">
                <p id="dimensions"></p>
                <video playsinline autoplay></video>
           </div>
        </center>

        <script>
            'use strict';
    
            const videoblock = document.querySelector('#videoblock');
            const dimensions = document.querySelector('#dimensions');
            const video = document.querySelector('video');
            const vgaButton = document.querySelector('#vga');
            const qvgaButton = document.querySelector('#qvga');
            const hdButton = document.querySelector('#hd');
            const qvgaConstraints = {
                    video: {width: {exact: 320}, height: {exact: 240}}
            };

            const vgaConstraints = {
                    video: {width: {exact: 640}, height: {exact: 480}}
            };

            const hdConstraints = {
                    video: {width: {exact: 1280}, height: {exact: 720}}
            };

            vgaButton.onclick = () => {
                    getMedia(vgaConstraints);
            };

            qvgaButton.onclick = () => {
                    getMedia(qvgaConstraints);
            };

            hdButton.onclick = () => {
                    getMedia(hdConstraints);
            };
    
            function getMedia(constraints) {
                    videoblock.style.display = 'none';
                    navigator.mediaDevices.getUserMedia(constraints)
                        .then(gotStream)
                        .catch(err => {
                            videoblock.style.display = 'block';
                            dimensions.innerText = 'Video not ready';
                        });
            }

            function gotStream(mediaStream) {
                    video.srcObject = mediaStream;
                    videoblock.style.display = 'block';
            }

            video.onloadedmetadata = () => {
                    displayVideoDimensions('loadedmetadata');
            };
    
            function displayVideoDimensions(whereSeen) {
                    if (video.videoWidth) {
                        dimensions.innerText = 'Actual video dimensions: ' + video.videoWidth +
                        'x' + video.videoHeight + 'px.';
                    } else {
                        dimensions.innerText = 'Video not ready';
                    }
            }

        </script>
    </body>
</html>
  • navigator.mediaDevices.getUserMedia()の引数で映像の解像度の指定が出来る。
  • QVGAを指定する場合の引数は、qvgaConstraints = {video: {width: {exact: 320}, height: {exact: 240}}}; の様に高さと幅の情報を持って、navigator.mediaDevices.getUserMedia() を実行すれば良い。

(5)Audio-only getUserMedia() output to local audio element

navigator.mediaDevices.getUserMedia()の引数で、映像をオフに音声をオンにしたバージョンです。引数の定義は、23から25行で行っています。

samp05.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>gUM audio</title>
    </head>

    <body>

        <center>
            <h1>WebRTC samples getUserMedia, audio only</h1>
            <audio controls autoplay></audio>
            <div>
                <span id="errorMsg"></span>
            </div>
        </center>

        <script>
            'use strict';

            // Put variables in global scope to make them available to the browser console.
            const audio = document.querySelector('audio');
            const constraints = window.constraints = {
                  audio: true,
                  video: false
            };

            function handleSuccess(stream) {
                  audio.srcObject = stream;
            }

            function handleError(error) {
                  const errorMessage = 'navigator.MediaDevices.getUserMedia error: ' + error.message + ' ' + error.name;
                  document.getElementById('errorMsg').innerText = errorMessage;
                  console.log(errorMessage);
            }

            navigator.mediaDevices.getUserMedia(constraints).then(handleSuccess).catch(handleError);
        </script>

    </body>
</html>

(6)Audio-only getUserMedia() displaying volume

音の大きさをメータに表示するサンプル。navigator.mediaDevices.getUserMedia()で得たStream(今回は音声のみ)の値を140行に有る関数 meterRefresh = setInterval(() でその値とバーレベルを表示している様です。

samp06.html

<!DOCTYPE html>
<html>
    <head>
        <title>Audio stream volume</title>
        <style>
            div#meters > div { margin: 0 0 1em 0; }
            div#meters div.label {
                display: inline-block;
                font-weight: 400;
                margin: 0 0.5em 0 0;
                width: 3.5em;
            }
            div#meters div.value { display: inline-block; }
            meter { width: 50%; }
            meter#clip { color: #db4437; }
            meter#slow { color: #f4b400; }
            meter#instant { color: #0f9d58; }
        </style>
    </head>
    <body>
        <h1>
            <a href="//webrtc.github.io/samples/" title="WebRTC samples homepage">WebRTC samples</a>
            <span>Audio stream volume</span>
        </h1>
        <p>Measure the volume of a local media stream using WebAudio.</p>

        <div id="meters">
            <div id="instant">
                <div class="label">Instant:</div>
                <meter high="0.25" max="1" value="0"></meter>
                <div class="value"></div>
            </div>
            <div id="slow">
                <div class="label">Slow:</div>
                <meter high="0.25" max="1" value="0"></meter>
                <div class="value"></div>
            </div>
            <div id="clip">
                <div class="label">Clip:</div>
                <meter max="1" value="0"></meter>
                <div class="value"></div>
            </div>
        </div>

        <div>
            <button type="button" id="startButton">Start</button>
            <button type="button" id="stopButton" disabled>Stop</button>
        </div>

        <script>
        'use strict';
        // =====================  soundmeter.js  ========================================================
            function SoundMeter(context)
            {
                this.context = context;
                this.instant = 0.0;
                this.slow = 0.0;
                this.clip = 0.0;
                this.script = context.createScriptProcessor(2048, 1, 1);
                const that = this;
                this.script.onaudioprocess = function(event)
                {
                    const input = event.inputBuffer.getChannelData(0);
                    let i;
                    let sum = 0.0;
                    let clipcount = 0;
                    for (i = 0; i < input.length; ++i)
                    {
                        sum += input[i] * input[i];
                        if (Math.abs(input[i]) > 0.99) clipcount += 1;
                    }
                    that.instant = Math.sqrt(sum / input.length);
                    that.slow = 0.95 * that.slow + 0.05 * that.instant;
                    that.clip = clipcount / input.length;
                };
            }

            SoundMeter.prototype.connectToSource = function(stream, callback)
            {
                console.log('SoundMeter connecting');
                try
                {
                    this.mic = this.context.createMediaStreamSource(stream);
                    this.mic.connect(this.script);
                    // necessary to make sample run, but should not be.
                    this.script.connect(this.context.destination);
                    if (typeof callback !== 'undefined') callback(null);
                }
                catch (e)
                {
                    console.error(e);
                    if (typeof callback !== 'undefined') callback(e);
                }
            };

            SoundMeter.prototype.stop = function()
            {
                console.log('SoundMeter stopping');
                this.mic.disconnect();
                this.script.disconnect();
            };

            // =====================  main.js  ========================================================
            const startButton = document.getElementById('startButton');
            const stopButton = document.getElementById('stopButton');
            startButton.onclick = start;
            stopButton.onclick = stop;

            const instantMeter = document.querySelector('#instant meter');
            const slowMeter = document.querySelector('#slow meter');
            const clipMeter = document.querySelector('#clip meter');

            const instantValueDisplay = document.querySelector('#instant .value');
            const slowValueDisplay = document.querySelector('#slow .value');
            const clipValueDisplay = document.querySelector('#clip .value');

            // Put variables in global scope to make them available to the browser console.
            const constraints = window.constraints =
            {
              audio: true,
              video: false
            };

            let meterRefresh = null;

            function handleSuccess(stream)
            {
                // Put variables in global scope to make them available to the
                // browser console.
                window.stream = stream;
                const soundMeter = window.soundMeter = new SoundMeter(window.audioContext);
                soundMeter.connectToSource(stream, function(e)
                {
                    if (e)
                    {
                        alert(e);
                        return;
                    }

                    meterRefresh = setInterval(() =>
                    {
                        instantMeter.value = instantValueDisplay.innerText = soundMeter.instant.toFixed(2);
                        slowMeter.value = slowValueDisplay.innerText = soundMeter.slow.toFixed(2);
                        clipMeter.value = clipValueDisplay.innerText = soundMeter.clip;
                    }, 200);
                });
            }

            function handleError(error)
            {
                console.log('navigator.MediaDevices.getUserMedia error: ', error.message, error.name);
            }

            function start()
            {
                console.log('Requesting local stream');
                startButton.disabled = true;
                stopButton.disabled = false;

                try
                {
                    window.AudioContext = window.AudioContext || window.webkitAudioContext;
                    window.audioContext = new AudioContext();
                }
                catch (e)
                {
                    alert('Web Audio API not supported.');
                }

                navigator.mediaDevices
                  .getUserMedia(constraints)
                  .then(handleSuccess)
                  .catch(handleError);
            }

            function stop()
            {
                console.log('Stopping local stream');
                startButton.disabled = false;
                stopButton.disabled = true;

                window.stream.getTracks().forEach(track => track.stop());
                window.soundMeter.stop();
                window.audioContext.close();
                clearInterval(meterRefresh);
                instantMeter.value = instantValueDisplay.innerText = '';
                slowMeter.value = slowValueDisplay.innerText = '';
                clipMeter.value = clipValueDisplay.innerText = '';
            }

        </script>
    </body>
</html>

(7)Record stream

動画の録画、再生、保存の3つ機能についてのサンプルです。

samp07.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>MediaStream Recording</title>
    </head>

    <style>
        video {
              background: #222;
              vertical-align: top;
              --width: 25vw;
              width: var(--width);
              height: calc(var(--width) * 0.5625);
       }
    </style>

    <body>
        <center>

            <h1>WebRTC samples MediaRecorder</h1>
            <video id="gum" playsinline autoplay muted></video>
            <video id="recorded" playsinline loop></video>

            <div>
                <button id="start">Start camera</button>
                <button id="record" disabled>Start Recording</button>
                <button id="play" disabled>Play</button>
                <button id="download" disabled>Download</button>
            </div>

            <div>
                Recording format: <select id="codecPreferences" disabled></select>
            </div>
            <div>
                <h4>Media Stream Constraints options</h4>
                <p>Echo cancellation: <input type="checkbox" id="echoCancellation"></p>
            </div>

            <div>
                <span id="errorMsg"></span>
            </div>

        </center>

        <script>
            'use strict';

            /* globals MediaRecorder */

            let mediaRecorder;
            let recordedBlobs;

            const codecPreferences = document.querySelector('#codecPreferences');

            const errorMsgElement = document.querySelector('span#errorMsg');
            const recordedVideo = document.querySelector('video#recorded');
            const recordButton = document.querySelector('button#record');
            recordButton.addEventListener('click', () => {
                      if (recordButton.textContent === 'Start Recording') {
                        startRecording();
                      } else {
                        stopRecording();
                        recordButton.textContent = 'Start Recording';
                        playButton.disabled = false;
                        downloadButton.disabled = false;
                        codecPreferences.disabled = false;
                      }
            });

            const playButton = document.querySelector('button#play');
            playButton.addEventListener('click', () => {
                      const mimeType = codecPreferences.options[codecPreferences.selectedIndex].value.split(';', 1)[0];
                      const superBuffer = new Blob(recordedBlobs, {type: mimeType});
                      recordedVideo.src = null;
                      recordedVideo.srcObject = null;
                      recordedVideo.src = window.URL.createObjectURL(superBuffer);
                      recordedVideo.controls = true;
                      recordedVideo.play();
            });

            const downloadButton = document.querySelector('button#download');
            downloadButton.addEventListener('click', () => {
                      const blob = new Blob(recordedBlobs, {type: 'video/webm'});
                      const url = window.URL.createObjectURL(blob);
                      const a = document.createElement('a');
                      a.style.display = 'none';
                      a.href = url;
                      a.download = 'test.webm';
                      document.body.appendChild(a);
                      a.click();
                      setTimeout(() => {
                        document.body.removeChild(a);
                        window.URL.revokeObjectURL(url);
                      }, 100);
            });

            function handleDataAvailable(event) {
                      console.log('handleDataAvailable', event);
                      if (event.data && event.data.size > 0) {
                        recordedBlobs.push(event.data);
                      }
            }

            function getSupportedMimeTypes() {
                     const possibleTypes = [
                            'video/webm;codecs=vp9,opus',
                            'video/webm;codecs=vp8,opus',
                            'video/webm;codecs=h264,opus',
                            'video/mp4;codecs=h264,aac',
                    ];
                  return possibleTypes.filter(mimeType => {
                        return MediaRecorder.isTypeSupported(mimeType);
                    });
            }

            function startRecording() {
                      recordedBlobs = [];
                      const mimeType = codecPreferences.options[codecPreferences.selectedIndex].value;
                      const options = {mimeType};

                      try {
                        mediaRecorder = new MediaRecorder(window.stream, options);
                      } catch (e) {
                        console.error('Exception while creating MediaRecorder:', e);
                        errorMsgElement.innerHTML = `Exception while creating MediaRecorder: ${JSON.stringify(e)}`;
                        return;
                        }

                      console.log('Created MediaRecorder', mediaRecorder, 'with options', options);
                      recordButton.textContent = 'Stop Recording';
                      playButton.disabled = true;
                      downloadButton.disabled = true;
                      codecPreferences.disabled = true;
                      mediaRecorder.onstop = (event) => {
                                console.log('Recorder stopped: ', event);
                                console.log('Recorded Blobs: ', recordedBlobs);
                      };
                      mediaRecorder.ondataavailable = handleDataAvailable;
                      mediaRecorder.start();
                      console.log('MediaRecorder started', mediaRecorder);
            }

            function stopRecording() {
                      mediaRecorder.stop();
            }

            function handleSuccess(stream) {
                      recordButton.disabled = false;
                      console.log('getUserMedia() got stream:', stream);
                      window.stream = stream;

                      const gumVideo = document.querySelector('video#gum');
                      gumVideo.srcObject = stream;

                      getSupportedMimeTypes().forEach(mimeType => {
                        const option = document.createElement('option');
                        option.value = mimeType;
                        option.innerText = option.value;
                        codecPreferences.appendChild(option);
                      });
                      codecPreferences.disabled = false;
            }

            async function init(constraints) {
                      try {
                        const stream = await navigator.mediaDevices.getUserMedia(constraints);
                        handleSuccess(stream);
                      } catch (e) {
                        console.error('navigator.getUserMedia error:', e);
                        errorMsgElement.innerHTML = `navigator.getUserMedia error:${e.toString()}`;
                      }
            }

            document.querySelector('button#start').addEventListener('click', async () => {
                      document.querySelector('button#start').disabled = true;
                      const hasEchoCancellation = document.querySelector('#echoCancellation').checked;
                      const constraints = {
                        audio: {
                          echoCancellation: {exact: hasEchoCancellation}
                        },
                        video: {
                          width: 1280, height: 720
                        }
                      };
                      console.log('Using media constraints:', constraints);
                      await init(constraints);
            });
        </script>
    </body>
</html>

HTMLの流れ

  1.  Start camera ボタンを押してStreamingを開始。
  2.  ブラウザでコードを読み込む。
  3.  Start Recording ボタンを押すと録画が開始されます。
  4.  Stop Recording ボタンで録画終了。録画したデータが再生用画面に表示。
  5.  ”Download”ボタンを押すと、録画したデータのダウンロード開始。
  • ========= ”Start camera”ボタンが押されと以下が実行される ==========
  • 175行: カメラ使用許可の初期設定。
    • ”Start camera”の無効化
    • ”echoCancellation”チェックボックスのチェックの有無
    • navigator.mediaDevices.getUserMedia()の引数の設定
    • 上記の操作後、165行の ”init()” へ飛びます
  • 165行: カメラ使用許可依頼
    • navigator.mediaDevices.getUserMedia()を実行してカメラ使用許可依頼
    • OKなら148行、handleSuccess()へ
    • Errorならエラー処理を行います。
  • 148行: Streamingの開始
    • ”Start Recording”ボタンを有効にする。
    • 151行: window.stream = stream;
      •  window要素のstream属性にnavigator.mediaDevices.getUserMedia()からのstreamを代入。
      • これは、この後でStreamingを録画する時に使用します。
    • 156から161行: 対応するフォーマットの確認
      • 対応フォーマットが、プルダウンメニュー、”Recording format”に追加されます。
      • プルダウンメニュー、”Recording format”を有効にする。
  • ========= ”Start Recording”ボタンが押されと以下が実行される ==========
  • 59行: ”record”ボタンクリックイベントの追加
    • ボタンが、’Start Recording’ならstartRecording(); 117行を実行
    • それ以外は、stopRecording();を実行
  • 117行: startRecording() 録画開始
    • 118行: 録画用配列の宣言
    • 120行: 画像タイプの取得
    • 123行: MediaRecorder()の定義。引数は、window.streamとフォーマット
    • 135行: ストップイベント処理の登録
    • 139行: 画像データレディーイベントの登録。イベントで、handleDataAvailable()が実行
    • 140行: 録画の開始
    • 98行: handleDataAvailable()
      • 100行: データの有無を確認して
      • 101行: データが有れば、録画用配列(recordedBlobs)に書き込む。
  • 144行: stopRecording() 録画終了。
    • この時、135行のストップイベント(コンソールに表示)が実行される。
  • ========= ”Play”ボタンが押されと以下が実行される ==========
  • 72行: ”play”ボタンクリックイベントの追加
    • 73行: データのタイプ取得
    • 74行: 録画したデータとタイプで新しい配列を宣言
    • 77行: 録画表示用の画面(recordedVideo)要素のsrc属性に配列データを代入
    • 78行: ビデオコントロールをオンに設定
    • 79行: 再生開始
    • 23行: videoタグで、”loop”と定義されているので再生は繰り返される。
  • ========= ”download”ボタンが押されと以下が実行される ==========
  • 83行: ”download”ボタンクリックイベントの追加
    • 84行: 録画データとデータタイプを合わせてダウンロード用配列を定義
    • 85行: 配列のURLを取得
    • 86行: ”a”タグ要素の製作
    • 90行: 要素をbodyに追加
    • 91行: ダウンロードの開始
    • 92から95行: ダウンロード後、100ミリセックで下記を行う
      • ダウンロード要素の削除
      • ダウンロード用配列の削除

次回は

今回はカメラの使い方、画像の再生、録画用のコードの説明でした。”WebRTC samples”にはこれ以外に下記の項目についてコードの説明が有ります。今後これらにつても見て行く予定です。

<<Before