はりきらないひと

2014/12/12

Jolla: Jolla向けアプリ「Qippis」に簡易カメラ機能を追加してみた。

この投稿は"Qt Advent Calendar 2014 - Qiita"の12日目(12/12分)の記事だ。

なんか他のメンバの記事のレベルが高くてアレだけど、とりあえず素人なりに素人っぽいものを書いてみる。Qtの話ってよりJollaの話っぽい感じがするけど、まぁ気にしない。


おいらはホビーヘッポコプログラマだけど、Jolla向けにQippisっていうアプリを作ってみた。
はりきらないひと: Jolla向けのアプリを作ってみた。
簡単に言うと、ビールデータベース"BreweryDB.com"のビューア。
ちゃんとストアで公開してるよ。
ソースはこの辺で公開してある。
http://git.qtquick.me/?p=sailfishos/qippis.git;a=tree
JollaはMeeGo由来の「SailfishOS」で動いていて、全面的にQtが採用されてて、UI周りや標準アプリの大部分がQtQuickで書かれている。あとUI作りを簡便化および一貫化するために"Sailfish Silica"っていうライブラリが用意されていて開発者向けにオープンにされている。

ここで告白すると、おいらはC++なプログラムが書けない(←威張って言うな!ww)。ということでQippisもsubmoduleのtwitter4qmlとそのつなぎ部分を除けば、ほぼQtQuickのみで書かれている。(twitter4qmlはおいらが作ったワケではないからノーカウントw)

ビバ QtQuick !

って事で、今回はQippisに新しい機能を付けた件について書いてみる。

○とりあえずJollaでカメラを使う

Qippisはビール情報をTwitterでシェアできるんだけど、飲んでるビール等の写真を添付するにはギャラリーから写真を選択するしかなかったので、撮影にはカメラアプリを別に起動する必要があった。

ホントはこーゆーのはQippisからカメラアプリを呼び出して・・・みたいな形が望ましいワケだけど、importできるモジュールに限りがあったりしてなかなか難しい(単なる勉強不足とも言う)。そんな中よく見たらQtMultimediaが使ってもいいモジュールのリストに入っていたので、自力で簡易的な撮影→投稿の機能を追加してみることにした。

まず、QtMultimediaを使ってカメラで写真を撮る時の基本的なコードは概ねこれだけ。
import QtQuick 2.0
import Sailfish.Silica 1.0
import QtMultimedia 5.0

Page {
    id: page

    Camera {
        id: camera
    }

    VideoOutput {
        anchors.fill: parent
        source: camera
        focus: visible
        MouseArea {
            anchors.fill: parent
            onClicked: camera.imageCapture.capture()
        }
    }
}
・・・たったこんだけ。
非常にシンプルでわかりやすい。ビューファインダーをタップすると写真が撮れる。撮った写真は端末のデフォルトの保存場所に落ちる。
Androidの場合はSailfish.Silicaの代わりにQtQuick.WindowをimportしてPage{}の代わりにWindow{}を使えばOK(あ、"visible: true"を入れないとダメかな)。WindowsPhone8.1は・・・今のところうまく動かせてない。5.4がリリースされたばかりなので、これからゆっくり確認してみるつもり。

ともあれQippisへの機能追加は、これをベースにした写真撮影用のページを一つ作り、保存された画像ファイルのパスをツイートのパラメータに取り込めば完了。

Σ(゜Д゜) スゲー簡単ジャン!!!

・・・とココまではサクサクだったのに、この後しばらく悩むことになる。

― 悩みその1:『保存される画像の向き』

Jollaのカメラのセンサーは端末の右側を上にして配置されてるっぽい。
つまり端末を左に90度回転させた横向きで写真を撮ると、ファインダーで見た通りの画像が保存される。

端末は基本縦持ちだし、アプリも縦画面のUIしか持ってないし、なによりビールの缶・ボトル・グラス・ジョッキは基本縦長なので、縦向きに撮影・保存したいのに、どうやっても向きが変わらない。

最初はカメラ側の機能で回転させて保存するんだとばかり思っていたから、あーでもないこーでもないといろいろ悩んでたんだけど、結局「画像を回転させて保存する」のではなく、『どっちが上か(撮影方向)の情報を画像ファイルに付与する』のが正解らしい。即ち、
camera.imageCapture.setMetadata("Orientation", 270)
ってな形でメタデータをセットしておけばいいらしい。

ホントは端末の向きに応じてメタデータをセットするのが正しいけど、今回qippisは縦持ち決め打ちすることにしたので、撮影用のページを呼び出した時点で270度に固定するよう設定した。

ちなみにSailfish.SilicaのOrientationは0-4の数値で管理してるっぽいけど、QtMultimediaのは90の倍数(0,90,180,270)で管理してるので、Silicaで得られる値をそのまま突っ込んではいけない。はず。

― 悩みその2:Jolla特有の問題(1)「onImageCapturedがCallされない」

Qtのドキュメントにあるサンプルコードの挙動がJollaとAndroidで違ったのでしばらく悩んでたんだけど、どうやらJollaではCamera.imageCapture.capture()を呼んでもonImageCapturedがうまくCallされない問題があるらしいことがわかった。そのせいでpreviewプロパティもちゃんとセットされないので、サンプルコードのようにpreviewをImage.sourceにツッコむこともできない。

ということで、撮影時にその場で撮ったものを確認するような機能を実装する時は、onImageSavedを使って保存済みの画像ファイルをImage.sourceに持ってく様にするといいっぽい(ってか、それしか手段がなさそうに見える)。
で、qippisはそういう風に実装したんだけど、ここで次の問題に遭遇する。

― 悩みその3:Jolla特有の問題(2)「Image{}でMetadetaのOrientationが反映されない?」

縦持ちで撮影して、ギャラリーで確認する時はちゃんと縦画像になっていて、Twitterに投稿した画像もちゃんと縦画像になっているにも関わらず、Image{}で表示させると横になってしまう・・・
・・・なんでやねん。

いろいろググってみたけど、「(Jollaで)そういう問題がある」ってことしか今のところ判らない。で、今回は上にも書いた通り縦画像で決め打ちだから、Image{}にrotation: 90を指定して無理やり回転させている。本当はメタデータを読みだして、データに合わせて回転させないとダメだけどさ。

― 悩みその4:実装上の問題「High Power Consumption」

とりあえずやりたいことはできるようになったのでJolla HarbourのQAにアプリを投げたら、「High Power Consumptionで受容不可」とのこと。どうやら撮影用のページを開いたままで端末がスリープに入るとmm-qcamera-daemonがアイドル状態にならず、結果としてqippisがActiveの状態を維持してしまうようで、「これだとバッテリのもちに影響しちゃう」ってことらしい。
JollaのQAのヒトはすごく親切で、状態の説明、確認方法(彼/彼女らがテストした方法)を詳しく解説してくれた上で、「I believe releasing the camera in this case can be a good solution to fix this issue.」というアドバイスをくれた。

なんとなく「スリープした時にcamera.stop()、復帰した時にcamera.start()」を呼べばいいというところまではピンと来たけど、肝心の「端末がスリープしてるかどうか」を知る方法が判らない・・・

悩んだ末、JollaのQRコードリーダーである"CodeReader"のソースを参考にさせてもらい、多分この辺だろうと思しきコードを貼り付けてみた。
Connections {
    target: Qt.application
    onActiveChanged: Qt.application.active ? camera.start() : camera.stop()
}
一応、回避できたっぽい感じだったので改めてsubmitしたところ、どうやらこれでOKっぽかった。
Jollaは意外にバッテリの持ちがいい印象なんだけど、こうやってアプリの電力的な挙動も一通り確認してあるんだね。

○そしてできたもの。

ってな感じで、最終的にできたのがコレ。


これを下のようにして呼び出して、tweet用のデータに組み込んでる。
var cameraPicker = pageStack.push(Qt.resolvedUrl("Camera.qml"));
cameraPicker.tookPictureChanged.connect(function() {
    _image = cameraPicker.tookPicture;
    media.push(_image);
});

正直なところ良く言えば「割り切って」、悪く言えば「手を抜いて」って感じではあるんだけど、今回はカメラアプリを作りたかったワケじゃないので、「キレイな写真が撮りたければカメラアプリ使ってね」ってことでOKにしてほしい(笑)

とりあえず「カメラで写真撮って画像ファイルを取得する」のに、へっぽこスキルでも80行強というわずかなコード量で書けているので、これはやはりQtQuickすごいよね(笑)

とゆーことで、Jolla用アプリにカメラ機能を追加してみた話はこれでおしまい。

○最後に

ま、コードの方は見る人が見れば「おまえそれはないだろう」的なものが多いだろうとは思うんだけど、「何かやってみたいけど素人だからなかなか手が出ない」ってヒトにとっては「この程度のスキルでもこのぐらいはやれる」っていういいサンプルになるんじゃなかろーか(ぉぃ

フレームワーク自体が便利なだけじゃなくてQtCreatorっていう強力なIDEもあるし、Qtのコミュニティはこんなレベルでもちゃんと歓迎してくれる温かい人たちで溢れているので、「これからプログラミングはじめます」ってヒトは是非とっかかりにQtを使う&日本Qtユーザー会に参加することおススメするのである。←それっぽく宣伝してみる

さて、肝心のアプリ、ホントなら「みなさんもちょっと試してみてね」って言いたいところなんだけど、残念ながらものすごくマイナーな環境なので簡単にはお試し頂けない。
大半のところはSailfishOS SDKでエミュレータを使えば体験できるけど、今回のカメラのところは実機じゃないと・・・
ってことで、Jollaのスマートフォンは今はかなり値下がりしてるし、来年5月にはタブレットも出るし、なによりQtでアプリを作りやすいから、みなさん1台ずつどうっすかね(笑)


0 件のコメント:

コメントを投稿