本稿で作成するカメラアプリのサンプルは、GitHubにて公開しています。
作成するサンプルアプリ
本稿では、HTML5と、Firefox OSのCamera APIを利用して、簡単なエフェクトつきのカメラアプリを作成します。
今回作成するカメラアプリは一画面のシンプルなアプリです。撮影ボタンのほか、左右の切替ボタンによって、様々なエフェクトを施した画像を撮影できます。下の画像は、アプリ上でエフェクトなし、エンボス加工、セピア風のエフェクトをそれぞれ表示したものです。
カメラアプリの各機能はそれぞれ以下のAPIを利用します。
機能 | API名称 | 簡単な実装方針 |
---|---|---|
カメラにアクセスする | CameraManager | CameraManager.getCamera() を利用し、カメラにアクセスします |
カメラを操作する | CameraControl | CameraControlの各メソッドで、フォーカスや撮影画像の取得を制御します |
カメラのデバイス情報を得る | CameraCapabilities | デバイスによって異なるカメラの情報はここから取得します |
カメラの映像にエフェクトをつける | CameraControl.effect | CameraCapabilities.effectsで取得できるエフェクトを指定することで、セピアやエンボスなどの画像効果をつけます |
撮影した写真をストレージに保存する | DeviceStorage | 撮影した写真は、デバイスの写真フォルダに格納します |
Firefox OSのアプリの種類
Firefox OSのアプリには、大きく分けて、Webから配信されるホスト型アプリと、デバイスにインストールされるパッケージ型アプリがあります。今回作成するカメラアプリはパッケージ型アプリです。
パッケージ型アプリは、権限によってさらに以下の3種類に分かれ、利用できるAPIが異なります。詳しくは、ドキュメントのパッケージ型アプリのページをご覧ください。
特権アプリ(Privileged App)
特権アプリは、特別なレビュープロセスを経てFirefox Marketplaceにより承認されたアプリです。アプリはFirefox Marketplaceに審査され、デジタル署名をされることで、デバイスの重要なAPIを利用することができるようになります。
認定アプリ(Certified App)
認定アプリは、電話やシステム設定など、Firefox OS端末上の重要な機能に使用されるものです。APIをユーザの明示的な承認なく利用できますが、OEMあるいはキャリアによって端末ごとに承認される必要があり、ほとんどのアプリ開発者は認定アプリを配布することができません。
単純なパッケージ型アプリ(Plain Packaged App)
単にひとつのZIPファイルにパッケージした通常のアプリです。特別な認証プロセスがない代わりに重要なAPIは利用できません。アプリをインストールした直後の状態で、アプリのリソースを全て端末上で利用可能にしておきたい場合に便利です。
また、WebIDEでは以下のように、それぞれの権限に許可されたAPIのテーブルを確認することができます。デバイスを接続した状態で、右上のデバイス選択メニューから[Permissions Table]を選択します。
Firefox OSで利用できるカメラ関連API
Firefox OSでカメラにアクセスするためのAPIには、以下の3種類があります。
API名 | 対応バージョン | 必要権限 |
---|---|---|
Web Activities | v1.0.1以上 | 特権(Privileged) |
Media Capture API | v1.4以上 | Web(Hosted) |
Camera API | v2.0以上 | 特権(Privileged) |
Web Activitiesは、Androidでいうインテントのような機能です。アプリから他のアプリを呼び出すことができる機能ですが、OS標準のカメラなど、Web Activitiesに対応したカメラアプリを呼び出し、結果の画像を取得することができます。他のカメラアプリを呼び出すだけなので、カメラUIの変更はできません。
Media Capture APIは、W3Cで標準化が進められているAPIです(2015年2月の現時点では、草案の段階です)。getUserMediaを使うことで、アプリ内でカメラの映像ソースにアクセスすることができます。標準APIとあって、ブラウザでも利用することができるので、触れたことのある方も多いかもしれません。また、このAPIは特権の権限が必要ないため、ホスト型のアプリでも利用できます。
Camera APIは、カメラを管理するためのFirefox OS独自のAPIです。Firefox OS 2.0以前は、認定アプリでのみ利用できるAPIでしたが、2.0から特権アプリでも利用できるようになりました。カメラの映像を取得するだけでなく、ズームやフォーカスを指定したり、フラッシュを利用したりといった豊富な機能を利用することができます。
プロジェクトの作成
WebIDEを利用したプロジェクトの準備
それでは早速アプリを制作していきましょう。まずは雛形となる空のプロジェクトを作ります。Firefox OSアプリの制作には、ブラウザ版のFirefoxに搭載されている、WebIDEを利用します。WebIDEについて詳しくは、第1回の内容をご覧ください。
[ツール] ‐[Web 開発] ‐[WebIDE] からWebIDEのウィンドウを開き、[アプリを開く] ‐[新規アプリ...] でテンプレートの選択画面を表示します。今回は特権アプリを作成するので、[Privileged Empty App] を選びます。今回作成するアプリの名前は「fxcam」としました。
アプリマニフェストの編集
Camera APIを利用できるようにするためには、アプリマニフェストにパーミッションを追加する必要があります。
プロジェクトルートにある「manifest.webapp」に"type"と"permissions"の項目を追加します。
...(略) "type": "privileged", "permissions": { "camera": {}, "device-storage:pictures":{ "access": "readwrite" } }, ...(略)
"permissions"の項目では、カメラへのアクセスを許可する "camera" の他に、撮影した写真を端末のストレージに保存するため、"device-storage:pictures" を指定します。
カメラ機能の作成(1)
さて、それではカメラアプリの機能を作成していきましょう。
Camera APIを利用したカメラへのアクセス
カメラにアクセスするには、navigator.mozCamera.getCamera()を利用します。第一引数に指定するオブジェクトは、navigator.mozCamera.getListOfCameras()で取得できるものを利用します。
// カメラリソース var camera; function releaseCamera() { console.log('releaseCamera'); if(camera) { camera.release(); } } function getCamera() { console.log('getCamera'); // カメラ取得時のオプション var options = { mode: 'picture', recorderProfile: 'jpg', previewSize: { width: 1280, height: 720 } }; // `getListOfCameras()`は背面カメラ、前面カメラの順に配列が返る var type = navigator.mozCameras.getListOfCameras()[0]; function onSuccess(success) { // スコープ外に値を保持 camera = success; console.log('getCamera:success', camera); // カメラ取得成功時の処理 } function onError(error) { console.warn('getCamera:error', error); // カメラ取得失敗時の処理 } // カメラがすでに取得されている場合はリリース releaseCamera(); navigator.mozCameras.getCamera(type, options, onSuccess, onError); }
カメラ取得に成功すると、成功時のコールバックonSuccess()の引数としてCameraControlオブジェクトが取得できます。
なお、初回のカメラアクセス時には、画面上にカメラへのアクセス許可を求める画面が出てきます。
カメラがすでに取得されている場合は、エラーとなってしまうので、取得前には必ず既存のCameraControlオブジェクトをリリースするようにします。カメラが取得できない場合、コンソールにはHardwareClosedと表示されます。こうなってしまったら、一度アプリを停止して再起動しましょう。
カメラ機能の作成(2)
プレビューの表示
次に、カメラのプレビューを画面に表示してみましょう。取得したCameraControlオブジェクトを<video>のストリームとしてmozSrcObjectプロパティにセットします。
<div id="previewContainer"> <video id="preview"></video> </div>
var previewVideo = document.getElementById('preview'); function onSuccess(success) { // スコープ外に値を保持 camera = success; console.log('getCamera:success', camera); // プレビューの再生 previewVideo.mozSrcObject = camera; previewVideo.play(); }
プレビュー映像は、実際のアプリでは、取得した状態だと横向きに表示されてしまうため、<div>で<video>を囲い、CSSのtransformで回転させて縦向きの画像として見えるようにしています。
カメラからの画像の取得・保存
実際にカメラの画像を保存するには、CameraControl.takePicture()を利用します。指定するオプションで画像サイズやファイルのフォーマット、EXIF情報などを指定することができます。
<span id="captureBtn" class="capture-btn"></span>
// ストレージ var storage = navigator.getDeviceStorage('pictures'); // キャプチャボタン var captureBtn = document.getElementById('captureBtn'); captureBtn.addEventListener('touchstart', captureStart, false); captureBtn.addEventListener('touchend', captureEnd, false); function captureStart(e) { console.log('captureStart', e); if(!camera) return; function onSuccess(success) { console.log('autoFocus:success', success); } function onError(error) { console.warn('autoFocus:error', error); } camera.autoFocus(onSuccess, onError); } function captureEnd(e) { console.log('captureEnd', e); if(!camera) return; var options = { pictureSize: camera.capabilities.pictureSizes[0], // 最大サイズ fileFormat: 'jpeg' }; function onSuccess(success) { console.log('takePicture:success', success); // 画像をストレージへ保存 var filename = 'fxcam_' + Date.now() + '.jpg'; storage.addNamed(success, filename); alert("画像を保存しました\n" + filename); // プレビューの再開 camera.resumePreview(); }; function onError(error) { console.log('takePicture:error', error); // カメラ取得失敗時の処理 }; camera.takePicture(options, onSuccess, onError); }
このコードでは、キャプチャボタンのtouchstart時にオートフォーカスで照準を合わせ、touchend時に撮影画像を取得します。
カメラの画像を保存する際、取得できる画像サイズは端末のスペックに依存するため、CameraControlオブジェクトからカメラに関する情報をCameraCapabilitiesオブジェクトで受け取ります。
画像が取得できると、成功時のコールバックにBlobオブジェクトとして画像が渡されます。BlobオブジェクトはDeviceStorage.addNamed()を利用してストレージに保存することができます。なお、ストレージへの初回アクセス時には、カメラへのアクセスと同様に、ユーザに対してAPI利用の承認画面が表示されます。
また、CameraControl.takePicture()を呼ぶと、一旦プレビューが停止するため、CameraControl.resumePreview()でプレビュー映像を再開します。
Camera APIで利用できる画像のエフェクト処理
CameraCapabilitiesオブジェクトには、画像に対するエフェクトも含まれています。
エフェクトは、OSのバージョンや端末によって数が変わりますが、今回利用した端末では、以下の12つのプロパティがありました。
- "none"(エフェクトなし)
- "mono"(モノクロ調)
- "negative"(ネガポジ反転)
- "solarize"(ソラリゼーション)
- "sepia"(セピア調)
- "posterize"(ポスター絵調)
- "whiteboard"(ホワイトボード:コントラスト強め)
- "blackboard"(ブラックボード:コントラスト弱め)
- "aqua"(アクア:青みがけ)
- "emboss"(エンボス加工)
- "sketch"(スケッチ風)
- "neon"(ネオン)
これらのプロパティをCameraControl.effectに指定することで、画像にエフェクトをかけることができます。プレビュー映像と、取得できる画像の両方にエフェクトが適用されます。
<span id="effectLabel" class="effect-label">none</span> <span id="effectRightBtn" class="effect-right-btn">›</span> <span id="effectLeftBtn" class="effect-left-btn">‹</span>
// エフェクト var effects = camera.capabilities.effects; var effectIndex = 0; // エフェクト名を表示するラベル var effectLabel = document.getElementById('effectLabel'); // エフェクト切替ボタン(右) var effectRightBtn = document.getElementById('effectRightBtn'); effectRightBtn.addEventListener('touchend', changeEffectRight, false); // エフェクト切替ボタン(左) var effectLeftBtn = document.getElementById('effectLeftBtn'); effectLeftBtn.addEventListener('touchend', changeEffectLeft, false); function changeEffectRight(e) { console.log('changeEffectRight', effectIndex); if(!camera) return; effectIndex = (effectIndex < effects.length-1) ? effectIndex + 1 : 0; effectLabel.innerHTML = camera.effect = effects[effectIndex]; } function changeEffectLeft(e) { console.log('changeEffectLeft', effectIndex); if(!camera) return; effectIndex = (effectIndex > 0) ? effectIndex - 1 : effects.length - 1; effectLabel.innerHTML = camera.effect = effects[effectIndex]; }
上記のコードでは、エフェクトの切替ボタンを左右に配置し、それぞれを押すとエフェクトが順番に切り替わるようにしました。
実際のコード
今回のアプリは、これだけのコードでカメラの制御とエフェクト加工が実現できます。もう少し整理したものをGitHubに公開していますので、参考にしてみてください。
また、OSに標準搭載されているカメラアプリもGitHub上で公開されています。こちらは少し読むのが難しいですが、Camera APIを余すところなく利用していますので、非常に参考になると思います。
アプリの申請フローについて
Firefox OSにも、Firefox Marketplaceというアプリのマーケットプレイスがあり、カメラ関連のものだけでもすでにたくさんのアプリが掲載されています。
Camera APIを利用するような特権アプリの場合、このマーケットプレイスにアプリを申請する際にはレビューを受けなくてはなりません。ここではその申請時の注意点について簡単に説明します。
アプリのレビュープロセス
Firefox Marketplaceでのレビュープロセスは非常にオープンなものとなっています。開発者は、Marketplaceレビュー要件に従ってアプリのレビューを受けます。このレビューチームは、Mozillaのメンバーとボランティアで構成されており、レビューチーム向けのガイドラインもWeb上で見ることができます。また、開発者であれば、ボランティアに応募することで、自分がレビュワーになることも可能です。
特権アプリの場合、セキュリティなどの項目についてもチェックされるため、アプリを申請してからレビュー完了までに数日、場合によっては数週間かかることもあります。
アプリのレビューで問題がある場合は、どこが問題だったのかをフィードバックしてもらうことができ、レビューした人と直接メールでやりとりすることができます。レビューに関して一般的な質問がある場合は、IRCやメーリングリストからレビューチームに連絡することも可能です。
セキュリティガイドライン
特権アプリの申請時にひっかかりやすいのが、セキュリティガイドラインでしょう。とりわけ、CSP(Content Security Policy)には注意が必要です。詳しくはMDNの解説ページを参照頂ければと思いますが、例えば以下のような制約があります。
- 外部JavaScript、CSSの禁止:別ドメインに置かれたJavaScriptファイルやCSSファイルを、アプリ内から参照することはできません。ライブラリなどは全てローカルに置く必要があります。
- インラインスクリプトの禁止:onclick()などのインラインスクリプトは利用できません。また、<script> タグを動的に生成することも禁止されます。
- eval()の利用禁止:eval()やeval演算子は、セキュリティエラーを投げます。特にテンプレートライブラリ等で利用されているケースがあるので注意して下さい。
KDDIのFirefox OS搭載端末「Fx0」のスペック
実際にカメラアプリを作る際に気になってくるのは端末のカメラのスペックでしょう。そこで、KDDIから発売されている世界初(※1)のハイエンドなFirefox OS搭載スマートフォン「Fx0」のスペックを見てみたいと思います。Fx0はFirefox OS 2.0を搭載しているので、Camera APIを使って開発者が自由にアプリを作ることができます。
このFx0は背面に約800万画素のCMOSカメラ、前面には約210万画素のCMOSカメラが搭載されており、他のOSのスマートフォンと比べても遜色ないスペックとなっています。カメラ背面には、ライトも搭載されているため、フラッシュ撮影も可能です。
ディスプレイは解像度1280×720ドットの4.7インチ IPS HDとなっています。他の廉価なFirefox OS端末に合わせて解像度は低めになっていますが、IPS液晶とあって画面の映りが非常に綺麗で見やすいものとなっています。
※1 Mozilla Corporation調べ、2014年12月現在
まとめ
本稿では、Firefox OSで利用できるCamera APIを紹介しました。OSバージョン1.xではパーミッションの制限があったCamera APIも、今では自由に開発が可能になったため、今後より多くのカメラ関連アプリが出てくるのではないでしょうか。同様に、他のAPIもOSバージョンが上がるにつれて開発が可能になることと思うと、とても楽しみですね。日本でもすでに「Fx0」のような端末が入手できるとあって、世界に先駆けてますますFirefox OSのアプリ開発が盛り上がれば良いなと思っています。本稿がそんな開発の一助になれば幸いです。
下記サイトにて、KDDIが開催を予定しているFirefox OSに関連するイベントや、最新のHTML5技術、Webプラットフォームを用いた新たな楽しみ方についての情報を発信しています。
「Fx0」の取扱い店舗についても下記サイトからご覧いただけます。