10
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

ブラウザでWebcamを扱えるテストツールを作った話。キャプチャも可。

Last updated at Posted at 2020-02-11

サマリ

ブラウザでアクセスするだけで、Webcamの動画ストリーミングにアクセスできるツールを作成しました。任意のタイミングでキャプチャし、PNG画像として保存することもできます。
screenShot.png

成果物

こちらにアクセスするだけで使えます。ソースコードもOSSでどうぞ。

開発モチベーション

日頃開発をしていると、机の上の状況をPC上で画像として貼り付けたくなる事が多々あります。当然スマホで撮影することが多いのですが、PC側で扱うにはクラウドサービスを経由したり、Directな無線通信やケーブルで接続したりと転送に少し手間がかかります。
そんな時Webcamが常設されている環境であればキャプチャが出来るはずですが、気軽にキャプチャできるツールが無かったので、作成した次第です。

使い方

  1. PCにWebcamを接続します。
  2. このツールを開いてください. Google Chromeを強く推奨します。
  3. 自動的に以下のダイアログがでてくるので、カメラを使うことを許可してください。(「許可」を押します。)
permission.png 4. 動画のストリーミングが始まった後に`CAPTURE`ボタンを押すと、その時点の画像データがPNGファイルとして即ダウンロードされます。

SWのポイント

HTML5 ビデオストリーミングの開始

10数行で実現出来る。いい世の中になりまりました。

index.html
<video autoplay="true" id="video"></video>
index.js
// On Streaming
const startStreamingVideo = () => {
  const video = document.querySelector( "#video" );
  if( navigator.mediaDevices.getUserMedia ){
    navigator.mediaDevices.getUserMedia( { video: true } )
    .then( ( stream ) => {
        video.srcObject = stream;
    } );
  }
}
startStreamingVideo();

静止画のキャプチャ

少しトリッキーな事をしています。
CAPUTREボタンが押されたら、見えないcanvasにその時点のデータを描画します。その後canvasの内容をDataURL形式に変換しlinkと紐付け、ダウンロード可能にした状態で最後にlinkclick()します。
このあたりは、こちらの記事を参考にさせていただきました。

index.html
<div id="hiddenContainer">
  <a id="hiddenLink"></a>
  <canvas id="hiddenCanvas" width="500" height="375"></canvas>
</div>
index.js
const btCapture = document.getElementById( 'btCapture' );

btCapture.addEventListener( 'click', () => {
  
  // Capture: draw to hidden canvas
  const hiddenCanvas = document.getElementById( 'hiddenCanvas' );
  const ctx = hiddenCanvas.getContext('2d');
  const WIDTH = 500;
  const HEIGHT = 375;
  ctx.drawImage( video, 0, 0, WIDTH, HEIGHT );
  
  // Download: load DataURL and convert to png
  const link = document.getElementById( 'hiddenLink' );
  link.href = hiddenCanvas.toDataURL();
  // document.getElementById('hiddenCanvas').src = hiddenCanvas.toDataURL();
  link.download = getYYYYMMDD_hhmmss( true ) + ".png";
  link.click();

});

btCapture.disabled = false;

JavascriptでYYYYMMDD_hhmmss

なんか毎回こんな関数をつくっているような気がしますが、今回は下記の通りで実装。
こちらを参考にさせていただきました。

index.js
const getYYYYMMDD_hhmmss = ( isNeedUS ) => {

 const now = new Date();
 let retVal = '';

 // YYMMDD
 retVal += now.getFullYear();
 retVal += padZero2Digit( now.getMonth() + 1 );
 retVal += padZero2Digit( now.getDate() );
 
 if( isNeedUS ){ retVal += '_'; }
 
 // hhmmss
 retVal += padZero2Digit( now.getHours() );
 retVal += padZero2Digit( now.getMinutes() );
 retVal += padZero2Digit( now.getSeconds() );

 return retVal;

}

// padding function
const padZero2Digit = ( num ) => {
 return ( num < 10 ? "0" : "" ) + num;
}

所感と考察

  • Videoデータの取り扱い、楽になったものだなぁ。
  • Captureした後のデータサイズは決め打ちにしてしまっているのですが、videoのMAX解像度を知ることができると画像もキレイに保存できて良いなと。ただ、ちょっと調べたくらいでは出来なかったので今回は断念しています。 Version1.1.0で解消しました。単にWidth/Heightをこちらで指定してしまっているのが原因でした。指定がなければブラウザ側でよきに計らってサイズ設定されました。(2020/2/16追記)
  • 今回の本筋とは関係ないのですが、本ツールはVSCodeのLiveServerを使って実装しました。めちゃめちゃ楽でQoD爆上がり。詳しくはこちらの記事をどうぞ。

追記(20200216)

Version 1.1.0および1.2.0をリリースしました。1.2.0の機能については別途記事書きます。
1.1.0では、下記の機能を追加しています。

  • iOS(Safariのみ)からも使えるように。
  • リア/フロント カメラの切り替え

iOSで使えるように

残念ながら本日(20200216)時点ではChromeでは非対応で、Safariでしか確認できませんが、iOSでも動作するようになりました。具体的には、iOSでautoplayとするには下記のようにplaysinline及びmutedを入れる必要がありました。これで自動再生できるようになりました。

index.html
<video playsinline muted autoplay id="video"></video>

リア/フロントカメラの切り替え

そもそも何も設定しないとセルフィ―スタイルの方のカメラ(facingMode: user)しか起動せず困っていたので、初期設定を外側を向いているカメラ(facingMode: environment)との切り替えを用意しました。
解決方法は簡単で、getUserMediaしたときの引数に、facingMode: environmentなどを指定してあげるだけで終了です。

index.js
// Camera facing mode = flip mode
const FACING_MODE_ENVIRONMENT = "environment";
const FACING_MODE_USER = "user";

let gCurrentCameraFacingMode = FACING_MODE_ENVIRONMENT;
navigator.mediaDevices.getUserMedia( 
  { video: { facingMode: gCurrentCameraFacingMode } } 
)

// Flip Cmera
filpCameraElem.addEventListener( "click", async ev => {
  switchCamera();
});

// Flip camera
const switchCamera = () => {

  if( gCurrentCameraFacingMode === FACING_MODE_ENVIRONMENT ){
    gCurrentCameraFacingMode = FACING_MODE_USER;
  }else{
    gCurrentCameraFacingMode = FACING_MODE_ENVIRONMENT;
  }
  startStreamingVideo();

}

10
15
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?