Sony Camera Remote APIを使ってカメラを遠隔操作する -撮影編-


※この記事の続編を書きました！**[こちら](http://qiita.com/kota65535/items/d51cac913e6b538c0eca)**からどうぞ。

ソニーの **カメラリモートAPI** を利用して、アクションカム([HDR-AZ1](http://www.sony.jp/actioncam/products/HDR-AZ1/))を自作プログラムから遠隔操作してみました。

![DSC04449.edit.jpeg](https://qiita-image-store.s3.amazonaws.com/0/69146/deedbd31-6ce9-3afd-ab3f-46104ab30560.jpeg "DSC04449.edit.jpeg")
 

## カメラリモートAPIとは

最近のカメラはWi-Fi接続で遠隔操作できるものが増えています。たいていは各カメラメーカーの専用のアプリからのみ利用可能ですが、ソニーはその通信仕様を公開しており、 **誰でも自由にカメラを利用したアプリケーションを作成することが可能** です。

### どのカメラだったら使えるの？

残念ながら、カメラリモートAPIはソニー製の全てのデジタルカメラで使えるわけではありません。[こちらのページ](https://developer.sony.com/develop/cameras/device-support/)から、リモートAPIに対応している機種を確認することができます。アクションカムは全機種対応で、その他のデジカメも2013年頃の機種から対応しています。

### どうやって使うの？

まず、ソニーの[開発者向けサイト](https://developer.sony.com/ja/camera-remote-api-beta-sdk-page/)からカメラリモートAPI SDKをダウンロードしておきましょう。チュートリアル、APIリファレンス、サンプルコードが同梱されています。
カメラリモートAPIの使い方の流れは以下の通りです。

1. PCからアクションカムにWi-Fi接続
2. [SSDP](https://en.wikipedia.org/wiki/Simple_Service_Discovery_Protocol)でアクションカムのデバイス情報を取得
3. [JSON-RPC](http://www.jsonrpc.org/)でアクションカムにHTTP POSTし、APIを呼び出す

それでは、これから[こちらのサンプルコード](https://gist.github.com/kota65535/5d69d2b86563fee8b565#file-camera_remote_test-rb)を使って **「リモートで動画撮影の開始・終了を行う」** ところまで説明していきます。言語はRubyを使いますので、環境の無い方は[公式サイト](https://www.ruby-lang.org/ja/documentation/installation/)などを参考にインストールしてください。バージョンは2.2.3で動作確認しましたが、おそらく1.9.3以上なら動くと思います。


## 1. PCとカメラをWi-Fi接続する

まずは[こちらの方法で](http://qa.support.sony.jp/solution/S1301309006233/)アクションカムのSSIDとパスワードを調べ、PCからアクションカムにWi-Fi接続します。
GUIのメニューから設定してもOKですが、自作プログラムに組み込むことを考慮するとスクリプト化しておくのが理想です。[Linux用(Raspberry Pi対応)](http://qiita.com/kota65535/items/9210727d807a31054842)、[Mac用](http://qiita.com/kota65535/items/4cd13ac28de51648e370)を用意しましたので、良かったら使ってみてください。下記のようにコマンドラインから接続できます。

```Linux用
$ bash wificonn_linux.sh <interface-name> <network-SSID> <password>
```

```Mac用
$ bash wificonn_mac.sh <network-SSID> <password>
```


## 2. SSDPでカメラのデバイス情報を取得する

[SSDP](https://en.wikipedia.org/wiki/Simple_Service_Discovery_Protocol)とは、同一ネットワーク上にある[UPnP](https://ja.wikipedia.org/wiki/Universal_Plug_and_Play)対応デバイスの検索を行うための規格です。詳しくは[このサイト](http://wa3.i-3-i.info/word12619.html)の説明がわかりやすくて参考になります。
Rubyでは[frisky](https://github.com/MrJoy/frisky)というgemをインストールすればSSDPを使えるようになります。
ついでに[httpclient](https://github.com/nahi/httpclient)と[nokogiri](http://www.nokogiri.org/)も入れておきましょう。

```
$ gem install frisky httpclient nokogiri
```

まず、以下のようにしてSSDPでデバイス検索を行います。

```ruby:camera_remote_test.rb
require 'frisky/ssdp'
require 'httpclient'
require 'nokogiri'
require 'json'
require 'pp'

# SSDPで所定のデバイスを検索
SSDP_SEARCH_TARGET = 'urn:schemas-sony-com:service:ScalarWebAPI:1'
SSDP_SEARCH_RETRY   = 3
SSDP_RETRY_INTERVAL = 5
try = 1
while true do
  response = Frisky::SSDP.search SSDP_SEARCH_TARGET 
  if ! response.empty?
    break
  elsif try < SSDP_SEARCH_RETRY
    try += 1
    puts "SSDP discover failed, retrying... (#{try}/#{SSDP_SEARCH_RETRY})" 
    sleep(SSDP_RETRY_INTERVAL)
  else
    puts "The device not found."
    exit 3
  end
end
pp response
puts 'SSDP discover succeeded.'
```

デバイス検索は一回だけだと失敗することもあるので、念のため何度かリトライしてやります。
`Frisky::SSDP.search`メソッドの引数は検索ターゲットで、カメラリモートAPI特有の文字列を指定してやります。何も指定しない場合は同一ネットワークの全てのデバイスが得られます。
これを実行するとアクションカムより以下のようなレスポンスが返ってきます。

```ruby:実行結果
[{:cache_control=>"max-age=1800",
  :ext=>"",
  :location=>"http://192.168.122.1:64321/dd.xml",
  :server=>"UPnP/1.0 MINT-X/1.8.1",
  :st=>"urn:schemas-sony-com:service:ScalarWebAPI:1",
  :usn=>
   "uuid:000000001000-1010-8000-FEC2DEEB77DC::urn:schemas-sony-com:service:ScalarWebAPI:1"}]
```

最も重要なのは`:location`をキーとする`http://192.168.122.1:64321/dd.xml` です。
このdd.xmlはデバイスデスクリプションというもので、カメラリモートAPIを利用するために必要な情報が書かれています。これをHTTP GETで取得し、nokogiriを使ってパースしてやります。

		
```ruby:camera_remote_test.rb（続き）
# デバイスデスクリプション解析
dd_xml = Nokogiri::XML(dd)
if dd_xml.nil?
  puts 'Failed to parse XML.'
  exit 1
end
dd_xml.remove_namespaces!
camera_name = dd_xml.css('device friendlyName')
services = dd_xml.css('device X_ScalarWebAPI_Service')
endpoint_urls = {}
services.each do |sv|
  service_type = sv.css('X_ScalarWebAPI_ServiceType').inner_text
  endpoint_urls[service_type] = File.join(sv.css('X_ScalarWebAPI_ActionList_URL').inner_text, service_type) 
end

puts 'Endpoint URLs are:'
pp endpoint_urls
```

デバイスデスクリプションを取得すると、中身は[このようなXML](https://gist.github.com/kota65535/5d69d2b86563fee8b565#file-dd-xml)になっています。
特に重要なエレメントは`X_ScalarWebAPI_ServiceType`と`X_ScalarWebAPI_ActionList_URL`です。
ServiceTypeはカメラリモートAPI群の分類で、以下の様なものがあります。

| 種別      | 説明                                             |
|-----------|--------------------------------------------------|
| guide     | サポートするAPIサービスの取得                    |
| **camera**    | カメラ設定の取得変更、および撮影機能へのアクセス（最も使う） |
| system    | システム設定の取得変更                           |
| avContent | メディアに記録された静止画・動画へのアクセス     |

ServiceTypeと対応するActionList_URLを`/`で繋げて`<ActionList_URL>/<ServiceType>` とすると、次節で使用する **エンドポイントURL** になります。
先ほどのコードを実行すると、以下のようにサービスタイプをキーとしたエンドポイントURLの連想配列が得られます。


```ruby:実行結果
Endpoint URLs are:
{"guide"=>"http://192.168.122.1:8080/sony/guide",
 "accessControl"=>"http://192.168.122.1:8080/sony/accessControl",
 "camera"=>"http://192.168.122.1:8080/sony/camera",
 "system"=>"http://192.168.122.1:8080/sony/system",
 "avContent"=>"http://192.168.122.1:8080/sony/avContent"}
```

### デバイス検索に失敗する場合は

SSDPはブロードキャストアドレスとして`239.255.255.250`のアドレスを使用しますが、ホストが複数のネットワークに接続している場合、ルーティングに問題がある可能性があります。仮に、ホストがwlan0で家庭内LANに接続し、wlan1でアクションカムに接続しているとしましょう。
`239.255.255.250`へのルートを調べるには、`ip route get`コマンドを使用します。

```bash:
$ ip route get 239.255.255.250
multicast 239.255.255.250 dev wlan0  src 192.168.1.10
    cache <mc>
```

このように **dev wlan0** と表示された場合、`239.255.255.250`にはwlan0を介した経路が設定されているため、デバイス検索はwlan0の接続しているネットワーク（家庭内LAN）内で行われます。そのため、wlan1のネットワークに接続しているアクションカムはSSDPの検索にヒットしなくなってしまいます。
wlan1を介した経路を追加するには、`route add`コマンドを使用します。

```bash:
$ sudo route add -host 239.255.255.250 dev wlan1
```

再度`ip route get`コマンドを実行して、 **dev wlan1** と表示されていればOKです。
こちらもスクリプトを用意しましたので、困ったら活用してください。

* [Linux用(Raspberry Pi対応)](https://gist.github.com/kota65535/e4717e61df1df33e49c1)
* [Mac用](https://gist.github.com/kota65535/fb66270cf030ea84bad3)


## 3. カメラにJSON-RPCでコマンドを送信する

[JSON-RPC](http://www.jsonrpc.org/)とはJSONを用いてリモートプロシージャコールを行う規格で、カメラリモートAPIの呼び出しにはこのJSON-RPCを使用します。
決められたフォーマットでJSONデータを作成し、前節で調べたエンドポイントURLに対してHTTP POSTを実行することでAPIの呼び出しが可能です。JSONデータのフォーマットは以下のように定められています。

| キー      | 値                                        |
|-----------|-------------------------------------------|
| method  | 呼びだすAPIの名前                         |
| params  | APIに渡すパラメータの連想配列             |
| id      | ユーザーがAPI呼び出しの識別に利用できる番号 |
| version | APIのバージョン                           |


APIリファレンスによると、例えば **"setShootMode"** APIを用いて撮影モードを動画撮影に切り替えるJSONは以下のように書けます。静止画撮影やインターバル静止画撮影モードにするなら、paramsに渡す文字列をそれぞれ`"still"`, `"intervalstill"`というように変えてやります。

```ruby:setShootModeのJSON
{
    "method": "setShootMode",
    "params": ["movie"],
    "id": 1,
    "version": "1.0"
}
```


"setShootMode" APIのサービスタイプは"camera"なので、エンドポイントURLは`http://192.168.122.1:8080/sony/camera`となります。このアドレスに対し上記のJSONをPOSTすると、アクションカムのリモート手続完了後にレスポンスのJSONが返され、実行結果を確認することができます。￼
たいていのAPIは成功時に"result"キーと0の値のペアを返しますが、エラーの際には"result"は返されず、代わりに”error"キーとエラーコード・メッセージの値が返ってきます。

```ruby:成功時
{
    "result": [0],
	"id": 1
}
```

```ruby:失敗時
{
    "error": [2, "Timeout"],
	"id": 1
}
```

それでは、"setShootMode"に加えて"startMovieRec"と”stopMovieRec"という3つのAPIを使って、録画の開始・停止を実行してみます。"id"キーの値はAPIの実行順ごとに連番にしていますが、単純に同じ値を使っても大丈夫です。

```ruby:camera_remote_test.rb（続き）
setShootMode_request = {
  'method' => 'setShootMode',
  'params' => ['movie'],
  'id' => 1,
  'version' => '1.0'
}
startMovieRec_request = {
  'method' => 'startMovieRec',
  'params' => [],
  'id' => 2,
  'version' => '1.0'
}
stopMovieRec_request = {
  'method' => 'stopMovieRec',
  'params' => [],
  'id' => 3,
  'version' => '1.0'
}

# 撮影モード切り替え->録画開始->録画停止
res = cli.post_content(endpoint_urls['camera'], setShootMode_request.to_json)
pp JSON.parse(res)
res = cli.post_content(endpoint_urls['camera'], startMovieRec_request.to_json)
pp JSON.parse(res)
sleep(5)
res = cli.post_content(endpoint_urls['camera'], stopMovieRec_request.to_json)
pp JSON.parse(res)
```

実行すると、録画が始まって5秒間経過したのち停止します。画面には下記のような出力が出て、APIが正常に終了したことを確認できます（stopMovieRecは、現状では成功時に空文字列を返すようです）。

```ruby:実行結果
{"result"=>[0], "id"=>1}
{"result"=>[0], "id"=>2}
{"result"=>[""], "id"=>3}
```

## 終わりに

今回はソニーのカメラリモートAPIを利用して、PCから録画の開始・終了を実行してみました。
他にも、静止画撮影や撮影したコンテンツの取得、ライブビューの取得など、やれることは様々です。
今後も機会がありましたら解説記事を上げていこうと思います。
ちなみに、CanonやNikon、GoProも同様のAPIを公開しています。こちらの製品をお持ちの方は、是非トライしてみてください。

* [Canon Digital Camera Software Development Kit](http://www.canon-asia.com/personal/web/developerresource/digital-imaging?languageCode=EN#ED)（要登録）
* [Nikon デジタル画像プロダクト用SDK](https://sdk.nikonimaging.com/apply/)（要登録）
* [GoProController](https://github.com/joshvillbrandt/GoProController)