みかづきブログ その3

本ブログは更新を終了しました。通算140万ユーザーの方に観覧頂くことができました。長い間、ありがとうございました。

👆

引越し先はこちらです!

iOS11のSafariからカメラとマイクにアクセスするシンプルでサンプルなコードを書きました。

iOS11がリリース(9月20日)されてから、はや1ヶ月半弱、
iPhone8が発売(9月22日)されてからも、はや1ヶ月半弱、
iPhoneXが発売(11月3日)されてしてから、はや3日が立ち、

32ビットアプリが動かなくなるという情報からか、リリース直後は若干苦戦していたiOS11のシェアも、
アナリティクスを確認する限りでは、着々と増えてきております。


さてさて、

New in Safari 11.0 – Camera and microphone access.
 Added support for the Media Capture API.
 Websites can access camera and microphone streams from a user's device (user permission is required.)

https://developer.apple.com/library/content/releasenotes/General/WhatsNewInSafari/Safari_11_0/Safari_11_0.html より引用

とあるように、iOS11からはウェブサイトからカメラマイクにアクセスできるようになりました。
iOS11のシェアが増えてきたいまこそ、ウェブサイトからカメラマイクにアクセスするサイトをつくるチャンスなのではないかと思い、
シンプルなサンプルを書いてみました。ソースはリポジトリにアップしております。

カメラにアクセス
github.com

マイクにアクセス
github.com





はじめに

  1. マイク、カメラにアクセスするためにはhttpsのサイトであることが必須条件です。
  2. Androidでは動作確認しておりません。



カメラにアクセス編

フロントカメラにアクセス

f:id:kimizuka:20171106161950p:plain

https://kimizuka.github.io/webcamera-preview-for-ios11/front-camera/

明示的にフロントカメラを渡していますが、

medias = {audio : false, video : true}

という感じで、明示的に渡さなくても基本的にフロントカメラが立ち上がります。

ソースコード

JavaScript

const medias = {
  audio: false,
  video: {
    facingMode: "user" // フロントカメラにアクセス
  }
};
const video = document.getElementById("video");
const promise = navigator.mediaDevices.getUserMedia(medias);

promise.then(successCallback)
       .catch(errorCallback);


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

function errorCallback(err) {
  alert(err);
};

HTML

<video id="video" autoplay playsinline></video><!--インライン再生を可能にしておく-->

CSS

body {
  margin: 0;
  background: #000;
}

#video {
  display: block;
  width: 100%;



リアカメラにアクセス

f:id:kimizuka:20171106162015p:plain

https://kimizuka.github.io/webcamera-preview-for-ios11/rear-camera/

ソースコード

JavaScript

const medias = {
  audio: false,
  video: {
    facingMode: {
      exact: "environment" // リアカメラにアクセス
    }
  }
};
const video = document.getElementById("video");
const promise = navigator.mediaDevices.getUserMedia(medias);

promise.then(successCallback)
       .catch(errorCallback);

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

function errorCallback(err) {
  alert(err);
};

HTML

<video id="video" autoplay playsinline></video><!--インライン再生を可能にしておく-->

CSS

body {
  margin: 0;
  background: #000;
}

#video {
  display: block;
  width: 100%;



応用例

f:id:kimizuka:20171106162042p:plain

https://kimizuka.github.io/webcamera-preview-for-ios11/carnvas-demo/

f:id:kimizuka:20171106133159g:plain

車窓とあわせる用の簡易ARのデモです。
すべてcanvasにレンダリングしても良かったのですが、カメラから取り込んだ映像以外はCSSで描画しています。
画面をタップするとジャンプします。
折角CSSを使っているので、targetセレクタをつかって、#reverseで反転させようと思ったのですが、
何故かiOSでうまくいきません。(原因調査中)
ソースはリポジトリを直接見ていただいたほうが早いと思います。



マイクにアクセス編

マイク入力をビジュアライズ

f:id:kimizuka:20171106162110p:plain

https://kimizuka.github.io/mic-preview-for-ios11/analyse/

f:id:kimizuka:20171106135846g:plain

iOSの制約で「ユーザージェスチャーきっかけで無いとオーディオを再生できない」という点に苦戦しました、
はじめに画面をタップさせることで制約を回避しました。
FFTを掛けて画面上にビジュアライスしています。

ソースコード

JavaScript

(function() {

  "use strict";

  const btn = document.getElementById("btn");
  const canvas = document.getElementById("canvas");
  const ctx = canvas.getContext("2d");

  navigator.mediaDevices.getUserMedia({
    audio: true,
    video: false
  }).then(_handleSuccess).catch(_handleError);
  
  function _handleSuccess(stream) {
    btn.addEventListener("click", () => {
      _handleClick(stream);
    }, false);
  }

  function _handleError() {
    alert("Error!");
  }

  function _handleClick(stream) {
    const LENGTH = 16;
    const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
    const options  = {
      mediaStream : stream
    };
    const src = audioCtx.createMediaStreamSource(stream);
    const analyser = audioCtx.createAnalyser(stream);
    const data = new Uint8Array(LENGTH);
    let w = 0;

    btn.classList.add("off");
    analyser.fftSize = 1024;
    src.connect(analyser);

    setInterval(() => {
      canvas.width  = window.innerWidth;
      canvas.height = window.innerHeight;

      ctx.fillStyle = "#3e3e3e";

      w = canvas.width / LENGTH,

      analyser.getByteFrequencyData(data);

      for (let i = 0; i < LENGTH; ++i) {
        ctx.rect(i * w, canvas.height - data[i] * 2, w, data[i] * 2);
      }

      ctx.fill();
    }, 20);
  }
})();

HTML

<canvas id="canvas"></canvas>
<div id="btn">TAP TO START</div>

CSS

body {
  font: 20px "Rubik Mono One", sans-serif;;
  background: #e3e3e3;
  overflow: hidden;
}

#btn {
  display: flex;
  position: absolute;
  top: 0; bottom: 0;
  left: 0; right: 0;
  color: #3e3e3e;
  align-items: center;
  justify-content: center;
  opacity: 1;
  transition: opacity .2s ease-in-out;
}

#btn.off {
  opacity: 0;
  pointer-events: none;
}

#canvas {
  display: block;
}



音声認識(失敗)

f:id:kimizuka:20171106162152p:plain

こちらでつくったサンプルをiOS11で試してみましたが、

kimizuka.hatenablog.com

残念ながら、動作しませんでした。
それもそのはず、SpeechRecognitionはまだ未実装ですね。

developer.mozilla.org


今回はiOS11に限ったコードを書きましたが、
実践に投入するにはAndroidやPCでも動くようにしなければならないですね。
それは、そのうち頑張って書こうと思います。今回は以上となります。



追記: 2018/02/17
Navigator.getUserMediaが非推奨となったため、MediaDevices.getUserMediaを使うように修正しました。