最近、DSC - QX10 をゲットしましたー♪
ゲットした理由は、これです。カメラ自体を制御する API が既に公開されています。 API でカメラを制御してみたくなったので..... 動機が変ですね...
The Camera Remote API uses JSON-RPC over HTTP. You can therefore use the Camera Remote APIs with any operating system, such as Android, IOS or Microsoft® Windows®.
カメラを制御するのに、 JSON-RPC over HTTP を使用しているんですね。呼び出し手順は下図の通りです。
Any camera supporting the Camera Remote API can be discovered using SSDP. The smartphone or tablet can get the endpoint URL of the API, and information about supported functionalities, over device discovery according to the below illustration. For details please see the development guide and the sample code included in the Camera Remote API beta SDK.
SSDP を使用しています。SSDP とは、UPnP のアーキテクチャの一部です。
UPnP Device Architecture
UPnP DAの仕様は、大きく分けて、アドレッシング、ディスカバリー、デスクリプション、コントロール、イベント通知、プレゼンテーションが定義されている。
UPnP では、各種機能を持ったデバイスと、そのデバイスに対して制御を行なうコントロールポイントの2者を定義している。デバイスは、自身の持つ機能をXML形式で公開しており、コントロールポイントはそれを参照して、必要な機能を利用できる。
アドレッシング
機器がネットワークに参加した際に、アドレスを取得する方法について規定されている。通常はDHCPを用いるが、DHCPによるアドレス配布を利用できない場合、AutoIPの仕組みを用いてアドレスを決定する。
ディスカバリー
SSDPというUDPを利用したプロトコルを用いて、ネットワーク上のデバイスの検出を行なう。具体的には、ネットワークに参加したデバイスは、マルチキャストパケット(NOTIFYメソッド)の送出を行ない、コントロールポイントはそれを受け取り、デバイスを検出する。また、コントロールポイント側からマルチキャストパケット(M_SEARCHメソッド)を用いて問い合わせを行ない、デバイスが応答するモデルもある。
デスクリプション
デバイスが提供できる機能や情報を記述したXMLファイルである。デバイス自身が持つサービスなどについて記述したデバイスデスクリプションと、各サービスが持つアクションなどについて記述したサービスデスクリプションの2種類がある。
コントロール
サービスの持つ機能を呼び出すアクションと、デバイスの状態変数を問合せるクエリーがある。 これらのメッセージは、XMLによって記述されたSOAPが使われる。
イベント通知
デバイスに対して、特定の状態変数を指定してイベント購読要求を行なうと、その状態変数の値が変化するたびに、イベントが通知される。
プレゼンテーション
ウェブブラウザから、デバイスの状態の確認や、制御ができる。
デバイスに対して、LOCATION を要求し、仕様の記述された XML に基づき HTTP POST で JSON を用いて各 API をコールするんですね。大まかな流れは理解できました。
ここに API ドキュメントおよびサンプルソースがあります。サンプルソースは Java です。このソースを参考に C# で実装したいと思います。
まずは、SSDP の要求フォーマットはどうなるんでしょうか。Java のサンプルソースをみてみましょう。( ソースではなく、ドキュメントを見るべき... どうしても先にソースが見たくなるんですよね.... )
final String ssdpRequest = "M-SEARCH * HTTP/1.1\r\n"
+ String.format("HOST: %s:%d\r\n", SSDP_ADDR, SSDP_PORT)
+ String.format("MAN: \"ssdp:discover\"\r\n")
+ String.format("MX: %d\r\n", SSDP_MX)
+ String.format("ST: %s\r\n", SSDP_ST) + "\r\n";
final byte sendData = ssdpRequest.getBytes();
定数の定義は下記の通りです。
private final static int SSDP_PORT = 1900; private final static int SSDP_MX = 1; private final static String SSDP_ADDR = "239.255.255.250"; private final static String SSDP_ST = "urn:schemas-sony-com:service:ScalarWebAPI:1";
SSDP による要求フォーマットは下記のようになりますね。
M-SEARCH * HTTP/1.1 HOST: 239.255.255.250:1900 MAN: "ssdp:discover" MX: 1 ST: urn:schemas-sony-com:service:ScalarWebAPI:1
SSDP の依頼および LOCATION の結果取得は、ソケットを使用します。ソースはかなり手抜きです。LOCATION さえ取れればいいかなと... 実行前に、無線 LAN で、QX10 に接続する必要があります。
using System; using System.Net; using System.Net.Http; using System.Net.Sockets; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace WindowsFormsApplication1 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) {
// ローカルIPのEndPointの作成 var localEP = new IPEndPoint(IPAddress.Any, 1900);
// マルチキャストのEndPointの作成 var multicastEP = new IPEndPoint(IPAddress.Parse("239.255.255.250"), 1900); // データグラム、マルチキャスト通信
var udpsocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); // ローカルIPにバインド udpsocket.Bind(localEP); udpsocket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(multicastEP.Address, IPAddress.Any)); // SSDPのフォーマット string searchstr = "M-SEARCH * HTTP/1.1\r\n" + "HOST: 239.255.255.250:1900\r\n" + "MAN:\"ssdp:discover\"\r\n" + "MX:1\r\n" + "ST:urn:schemas-sony-com:service:ScalarWebAPI:1\r\n"; // マルチキャストで送信 udpsocket.SendTo(Encoding.UTF8.GetBytes(searchstr), SocketFlags.None, multicastEP); // バッファサイズは1024 byte recbuff = new byte[1024]; int recbytes = 0; bool loop = true; while (loop) { if (udpsocket.Available > 0) {
// データ受信 recbytes = udpsocket.Receive(recbuff, SocketFlags.None); if (recbytes > 0) {
// 内容のエンコード string str = Encoding.UTF8.GetString(recbuff, 0, recbytes).ToString(); if (MessageBox.Show(str, "抜ける?", MessageBoxButtons.YesNo) == System.Windows.Forms.DialogResult.Yes) { loop = false; udpsocket.Close(); } } } } } } }
LOCATION 等の情報が取得できました。
http://10.0.0.1:64321/DmsRmtDesc.xml の内容を参照してみましょう。エンドポイント、API 情報などの情報が記載されています。カメラを制御するには、http://10.0.0.1:10000/sony/camera に対して、JSON のパラメータを HTTP POST で送出することで制御が可能だと判断できます。
写真を QX10 に撮らせてみたいと思います。API 仕様は下図の通りです。
POST でリクエストするなら、私の過去の投稿にある通り、HttpClient を使って実装するのが一番楽ですね。しかし、今回は JSON の文字列を送出するため、FormUrlEncodedContent クラスは使用できません。代わりに、StringContent クラスを使用します。ソースは下記の通りです。( 汎用性はまったくないメソッドです。 )
static async void DoPost() {
// 撮影する場合の JSON フォーマット string jsonparams = "{\"method\": \"actTakePicture\"," + "\"params\": []," + "\"id\": 1," + "\"version\": \"1.0\"}";
// サービスのURL string url = "http://10.0.0.1:10000/sony/camera"; var httpclient = new HttpClient();
// content の作成 var jsoncontent = new StringContent(jsonparams, Encoding.UTF8, "application/json"); httpclient.MaxResponseContentBufferSize = int.MaxValue; // POSTする var response = await httpclient.PostAsync(url, jsoncontent); // 結果の読み込み
String text = await response.Content.ReadAsStringAsync(); }
上記のメソッドを呼び出します。
private void button2_Click(object sender, EventArgs e) {
// 手抜き呼び出しww Task t = new Task(DoPost); t.Start(); }
t.Start() をコール後、QX10から『ピッ!』と音がなり、写真を撮ることができます。戻り値は下記の通りです。
{"id":1, "result":http://10.0.0.1:60152/pict140112_1702300000.JPG?%211234%21http%2dget%3a%2a%3aimage%2fjpeg%3a%2a%21%21%21%21%21
result 部分の URL をブラウザで参照すると、撮った写真を参照することが可能です。( 写真は、DSC - QX10 の専用ケースです。 )
http://10.0.0.1:60152/pict140112_1702300000.JPG?%211234%21http%2dget%3a%2a%3aimage%2fjpeg%3a%2a%21%21%21%21%21
デバイス制御プログラミング経験なかったのですが、JSON - RPC のような Web 技術をベースとしたものであれば、デバイス制御の経験がない技術者でも容易にデバイス制御のプログラムが書けます。( いいですね、こーゆーの。どんどん広まっていくことを望んでます。)
今後も機会 ( 無駄遣いの機会? ) があれば、今回の記事のようなカテゴリの記事も投稿していきます。