ソラカメ API を使いこなしてタイムラプス映像を作成する

みなさんこんにちは!

早いものでもう 12 月、今年もアドベントカレンダーの季節がやってきましたね。

この記事は SORACOM Advent Calendar 2022 の初日の記事です。


目次


ソラカメとソラカメ API

みなさん ソラカメ はご存知ですか?

soracom.jp

ソラカメはソラコムが提供を開始した "クラウドカメラサービス" です。詳しいことはリンク先をご覧いただきたいのですが、クラウドに映像を常時録画しておいて後からその映像を確認できるなどの特徴があります。しかしなんと言ってもそのクラウドに記録された映像を API でダウンロードできたりするという点が私たちプログラマーにとっては最大の特徴と言えるでしょう。

今日は、その ソラカメ API の活用例をご紹介したいと思います!

soracom.jp

細かい説明はすっ飛ばしてとにかく手っ取り早くタイムラプス映像を作成する方法を知りたい方は ソラカメ API を使ってタイムラプス映像を作るためのスクリプト まで一気に飛んでください。

ソラカメ API でできること

2022 年 12 月 1 日現在では、ソラカメ API を使って以下のようなことができます。

  • カメラ一覧の取得
  • カメラ詳細情報の取得
  • リアルタイム映像の閲覧(ストリーミング形式)
  • 常時録画の閲覧(ストリーミング形式)
  • 常時録画データのエクスポート
  • 常時録画データから切り出した静止画像のエクスポート
  • 常時録画データもしくは静止画像のエクスポートの進捗状況の取得

API は今後も随時拡充される予定ですのでぜひお楽しみになさっていてください!

ソラカメ API の使い方

「ソラカメ API」と言っても、実はこれまでソラコムが提供してきた SORACOM API の一部です。

つまり、https://api.soracom.io などのエンドポイント経由で呼び出すことができますし、soracom-cli を使って呼び出すことももちろんできます。

soracom-cli をインストールしてセットアップ済みであれば soracom-cli を使ってソラカメ API を呼び出すのが一番簡単なので、この記事ではここから先の説明では soracom-cli を使います。

soracom-cli をまだインストールしていないという方は、README (英語 / 日本語) に書かれた手順にしたがってインストールし、初期設定を行ってください。

soracom-cli を使って、試しにいくつかのソラカメ API を実行してみましょう。

カメラ一覧を取得する

アカウント(SORACOM オペレーター)に登録されているカメラの一覧を取得するには、soracom sora-cam devices list コマンドを実行します。 カメラが登録済みでしたら、以下のように一覧が表示されるはずです。

$ soracom sora-cam devices list
[
    {
        "configuration": {
            "audioAlarmEnabled": false,
            "motionDetectionEnabled": true,
            "smokeAlarmEnabled": false
        },
        "connected": false,
        "deviceCategory": "Camera",
        "deviceId": "7CDDxxxxxxxx",
        "firmwareVersion": "4.37.1.101",
        "lastConnectedTime": 1667896875949,
        "name": "ATOM Cam Swing",
        "productDisplayName": "ATOM Cam Swing"
    },
       ...
    {
        "configuration": {
            "audioAlarmEnabled": false,
            "motionDetectionEnabled": true,
            "smokeAlarmEnabled": false
        },
        "connected": true,
        "deviceCategory": "Camera",
        "deviceId": "7CDDxxxxxxxx",
        "firmwareVersion": "4.58.0.100",
        "lastConnectedTime": 1667971338458,
        "name": "ATOM Cam 2 Wood deck ",
        "productDisplayName": "ATOM Cam 2"
    }
]

常時録画データのエクスポートを開始する

クラウドに常時録画されている映像から一部を切り出してダウンロードできる形にする(=エクスポートする)には時間がかかるため、いったんエクスポート処理の開始を指示したら、あとはエクスポート処理が完了するまで定期的に処理の進捗状況をポーリングして確認する必要があります。エクスポート処理が完了すると映像データをダウンロードするための URL が入手できます。

このとき利用する API は、エクスポート処理の開始を指示する API と、エクスポート処理の進捗状況を確認する API の 2 つです。

ここではまずエクスポート処理の開始を指示する API を呼んでみましょう。

$ now="$( date +%s )"; soracom sora-cam devices videos export --device-id 7CDDxxxxxxxx --from "$(( now - 60 ))000" --to "${now}000"
{
    "deviceId": "7CDDxxxxxxxx",
    "exportId": "6e061429-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    "operatorId": "OP00xxxxxxxx",
    "requestedTime": 1668342217441,
    "status": "initializing"
}

ちょっとコマンドが複雑なようにみえますが、順番に紐解いていきましょう。

まず now="$( date +%s )" の部分 ですが、これは now という名前の変数に、現在時刻の Unix time 表現を格納しています。 これにより、now にはたとえば 1668341455 といったような数が入ります。

続いて soracom sora-cam devices videos export コマンドを呼び出しています。こちらのコマンドの引数は --device-id --from --to の 3 つが必須です。

--device-id はカメラのデバイス ID です。先ほどカメラの一覧を取得したときに "deviceId" フィールドとして格納されていた値を使います。

--from--to は、常時録画されている映像のどこからどこまでの区間をエクスポートするかを指示するための開始時刻と終了時刻です。

こちらの引数は、ミリ秒単位の Unix time で指定する必要があります。date コマンドで取得できる Unix time は秒単位のため 1、末尾に 000 を付与してミリ秒単位にしています。

--from "$(( now - 60 ))000" は現在時刻の 60 秒前を指定していて、--to "${now}000" は現在時刻を指定しています。したがって、現在時刻の 1 分前からの 1 分間の映像データのエクスポートを指示したことになります。

API のレスポンスには exportIdstatus が入っています。 exportId はエクスポートの進捗状況を確認する際に必要になりますので控えておきましょう。

エクスポート処理の進捗状況を確認する

エクスポート処理の進捗状況を確認するためには以下のコマンドを実行します。

$ soracom sora-cam devices videos get-exported --device-id 7CDDxxxxxxxx --export-id 6e061429-xxxx-xxxx-xxxx-xxxxxxxxxxxx
{
    "deviceId": "7CDDE9028C51",
    "exportId": "6e061429-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    "operatorId": "OP00xxxxxxxx",
    "requestedTime": 1668342217441,
    "status": "processing"
}

--export-id には先ほどメモした exportId の値を指定してください。

先ほどエクスポート開始を指示した際は statusinitializing でしたが、今は processing になっています。これはエクスポート処理を開始する指示が受け入れられ、エクスポート処理が実際に進行中であることを示すステータスです。

数秒おきに同じコマンドを繰り返し実行してみてください。 何度か同じ processing 状態のままレスポンスが返ってくると思いますが、そのうち以下のように statuscompleted となり、url フィールドが追加されるはずです。

$ soracom sora-cam devices videos get-exported --device-id 7CDDxxxxxxxx --export-id 6e061429-xxxx-xxxx-xxxx-xxxxxxxxxxxx
{
    "deviceId": "7CDDxxxxxxxx",
    "expiryTime": 1668342854000,
    "exportId": "6e061429-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    "operatorId": "OP00xxxxxxxx",
    "requestedTime": 1668342217441,
    "status": "completed",
    "url": "https://soracom-sora-cam-devices-api-export-file-prod.s3.amazonaws.com/2022-11-13-12-23-37_OP00xxxxxxxx_7CDDxxxxxxxx_ATOM%20Cam%202%20Wood%20deck%20_6e061429-xxxx-xxxx-xxxx-xxxxxxxxxxxx.zip?AWSAccessKeyId=ASIA...&Signature=1aqj...."
}

curl コマンドなどでこの URL にアクセスすることでファイルをダウンロードすることができます。映像のフォーマットは MP4(動画は AVC/H.264 で圧縮されています)ですが、指定した期間に複数の映像が含まれる可能性があるため、zip 形式でまとめられています。(動画ファイルが一つだけでも .zip ファイルとしてダウンロードされます)

この URL には有効期限があります。有効期限はエクスポートが完了してから 10 分間です。なのでエクスポートが完了したら速やかにダウンロードを開始してください。

有効期限が切れた場合は status の値が "expired" に変化します。

静止画像データのエクスポートを開始する

ソラカメ API を使うと、動画だけでなく指定した日時の静止画像もダウンロードできます。

$ now="$( date +%s )"; soracom sora-cam devices images export --device-id 7CDDxxxxxxxx --time "$(( now - 60 ))000"
{
        "deviceId": "7CDDxxxxxxxx",
        "exportId": "369b957a-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
        "operatorId": "OP00xxxxxxxx",
        "requestedTime": 1668408035409,
        "status": "initializing"
}

映像をエクスポートしたときとの違いは、サブコマンドの一部の videos だった箇所が images に変わっていることと、引数のうち時間の範囲を指定する --from--to の代わりにピンポイントで取得したい静止画の時刻を指定する --time に変わっていることです。

--device-id に指定している値は先ほど映像をエクスポートしたときと同じで、対象となるカメラの deviceId です。

--time に指定するタイムスタンプはミリ秒単位の Unix time なので、映像のエクスポート時と同じように現在時刻の 1 分前の Unix time(秒単位)に 000 を追加してミリ秒単位に変換しています。

静止画のエクスポートは通常はほぼ瞬時に完了しますので、以下のようにエクスポート状況を確認すると status がすでに "complete" になっていることが多いと思います。

$ soracom sora-cam devices images get-exported --device-id 7CDDxxxxxxxx --export-id 369b957a-xxxx-xxxx-xxxx-xxxxxxxxxxxx
{
        "deviceId": "7CDDxxxxxxxx",
        "expiryTime": 1668408637000,
        "exportId": "369b957a-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
        "operatorId": "OP00xxxxxxxx",
        "requestedTime": 1668408035409,
        "status": "completed",
        "url": "https://soracom-sora-cam-devices-api-export-file-prod.s3.amazonaws.com/2022-11-14-06-40-35_OP00xxxxxxxx_7CDDxxxxxxxx_ATOM%20Cam%202%20Wood%20deck%20_369b957a-xxxx-xxxx-xxxx-xxxxxxxxxxxx.jpg?AWSAccessKeyId=ASIA...&Signature=DMeoj...
}

映像のときと同じように、こちらの URL にアクセスすると静止画がダウンロードできます。(動画とは異なり、複数の画像が取得されることはありませんので zip にはなっておらず、JPEG ファイルが直接ダウンロードできます)

ここまでで、ソラカメ API を使って動画および静止画をダウンロードできるようになりました。

次はこれらの API を活用してタイムラプス映像を作っていきましょう。

タイムラプス映像とは?

最近はスマホのカメラ機能でも簡単にタイムラプスが撮影できたりすると思いますので皆さんすでにご存知だとは思いますが、一定の時間間隔で長時間にわたって写真を撮り、その写真をつなぎ合わせて映像にするというものです。空の雲が流れたり、星空がぐるっと回転していく様子がタイムラプスでダイナミックに表現された動画を皆さんも一度はご覧になったことがあるのではないでしょうか。

普段は動きや変化の少ない、でも長時間でみると確実に変わっている、そんなものを撮影して手早く確認するためにタイムラプス映像を作成することはひとつの有効な手段です。

タイムラプス映像の作り方

タイムラプス映像を作るには、大きく分けて以下の 2 つの作業が必要です。

  1. 一定の時間間隔で写真を撮り続ける
  2. それらの写真をつなぎ合わせて動画にする

実はこの 2 番めの作業に関しては、ffmpeg などのツールを使うと誰でも簡単にできてしまいます。

というわけで、タイムラプス映像を作る上でやらなければならないことは実質的には 1 番の作業です。

タイムラプス映像を作る上での困りごと

通常は「この場所のこの時間のタイムラプス映像を作りたい」と思ったら 事前に 計画を練り、撮影用の機器を設置したりといった準備を入念に行う必要があります。 たとえば、何月何日の何時から何時までの○時間分をもとに最終成果物の動画としては○秒くらいになるようにしたいと思ったら、そこから逆算して何秒間隔で何枚の写真を撮らなければいけないかということを計算して撮影する必要があります。そのパラメーターで満足の行く結果が得られるかどうかは実際に映像を作ってみないとわかりません。たとえばタイムラプス動画ができてから尺をもうちょっと長くしたいとか、もっと細かい間隔で写真を撮っておけばよかったと思っても後の祭りです。そうならないためには予定よりも短めの間隔でたくさん写真を撮っておくとか、予定の開始終了時刻に余裕をもたせておくといったことも必要になります。いずれにせよ事前に考えなければならないことが多く、私のような心配性にはとても億劫な作業になります。

ソラカメでタイムラプス映像を作ると嬉しいこと

一方ソラカメがすでに設置済みでクラウド常時録画をするように設定してあれば、動画はもう存分に存在しているわけなので、その動画を元に「あの時間帯のタイムラプス映像を作ろう」と後から思ったとしてもそれができてしまいます。パラメータの試行錯誤も比較的自由自在です。

通常のタイムラプス作成時も、最初から写真で撮るのではなく動画として撮影しておけば、そのような試行錯誤ができるかもしれません。しかし数時間にも渡る映像ファイルを取り扱うのは若干の不便が伴います。ファイルサイズが大きくなりがちですし、そもそも撮影していない時間帯の画像を使いたくなってしまったらそれはやはり不可能です。クラウドに常時録画されているというのはそういう面でとても便利です。

実は私がこの後ご紹介するサンプルのタイムラプス映像も「あの場所のあの時間帯のタイムラプス映像を作ったら面白いかもな」と後になって思って作ってみたものになります。時間帯も「たしか○月○日の午前 9 時から11時くらいの間だったかな?」というような曖昧な記憶から、試しにその前後の動画を確認して正確な開始・終了時刻を決めることができました。これは常時録画の強みですね。

ソラカメ API を使ってタイムラプス映像を作るためのスクリプト

前置きが長くなりましたが、ようやくスクリプトのご紹介です。

スクリプトの全体をここに掲載するとそれだけで結構なボリュームになってしまいますので GitHub に置きました。

github.com

スクリプトの説明

このスクリプトを実行するには ffmpeg soracom jq curl の各コマンドが必要ですので事前にインストールしておいてください。 (需要があれば、これらのコマンドやスクリプト自体を事前にインストールした Docker コンテナを作ってそれを使っていただけるようにしたりしても良いかもしれませんね)

# パラメータの設定(ご利用になる環境や目的に応じて変更してください)
device_id=7CDDxxxxxxxx                 # => カメラのデバイス ID に置き換えてください。デバイス ID は `soracom sora-cam devices list` コマンドで取得できます。
work_dir=/tmp/sora-cam-timelapse-work  # => ダウンロードした静止画ファイルを一時的に保存しておくディレクトリです。好きなディレクトリを指定してください。
now="$( date +%s )"
start="$(( now - 7200 ))000"           # => 開始時刻を現在時刻の 2 時間前に設定します。
end="$(( now - 3600 ))000"             # => 終了時刻を現在時刻の 1 時間前に設定します。つまり、この start と end によって、現在時刻の 2 時間前から 1 時間前までの 1 時間分の映像を使ってタイムラプスを作成します。実際には好きな時間を指定してください。

# スクリプトの実行
./sora-cam-timelapse.sh --work-dir "$work_dir" --device-id "$device_id" --start "$start" --end "$end"

スクリプトに指定するパラメータが少し多めですね。必須オプションは上で指定している 4 つです。これ以外にも指定可能なオプションがありますので、詳しいことはヘルプをご参照ください(スクリプトに引数を指定せずに実行するとヘルプが表示されます)

出来上がったタイムラプス映像

実際にこのスクリプトを使って私が作ったタイムラプス映像がこちらです。

youtu.be

休日にウッドデッキの汚れを高圧洗浄で落とす作業をしている最中に「この様子をタイムラプスで見たら面白そうだな?」と思って、翌日に雑なスクリプトを書いたら自分でも思っていた以上に簡単にタイムラプス映像が作れてしまいました。

みなさんもぜひソラカメ常時録画でタイムラプスを作って面白い映像が撮れたらシェアしてください!

同僚に教えてもらった Reddit Power Washing Porn

当初このタイムラプス映像は社内の Slack で仲間内だけでシェアしていたのですが、同僚のうちの一人が以下の URL を教えてくれました。

www.reddit.com

なんかわかる気がするというか、キレイになっていく様子を早送りで観察できるのは気持ちがいいですね。

新しい趣味を開拓されてしまった気分です。

(私の映像を reddit に投稿する勇気はさすがに無かったですがw)

宣伝

ところでみなさん、スクリプトstart とか endUnix time をミリ秒単位で指定するの、なんか面倒だな〜と思いませんでしたか?

思いますよね。

私はついカッとなって github.com/bitbears-dev/dq というツールを作ってしまいました。

github.com

これを使うとたとえば以下のように比較的簡単に目的の Unix time(ミリ秒単位)を取得できます。

$ dq 'guess("2022-12-01T01:02:03Z") | .unixMilli'
1669856523000

ソラカメ API に限らず、SORACOM APIUnix time をミリ秒単位で指定しなければならない場合が多いですし、逆に API のレスポンスにミリ秒単位の Unix time が含まれていることも多いです。そんなとき簡単に Unix time と Human friendly な表記を相互変換できるのでとても便利です。

よろしかったらこちらもぜひ合わせてご利用ください。


  1. GNU date コマンドでは書式指定子に +%s%3N と書くとミリ秒単位の Unix time を取得できるのですが、Mac などにインストールされている BSD 版の date コマンドはこの記法には対応していないようです。