都内で働くSEの技術的なひとりごと

都内でサラリーマンやってます。マイクロソフト系(たまに、OSS系などマイクロソフト以外の技術も...)の技術的なことについて書いています。日々の仕事の中で、気になったことを技術要素関係なく気まぐれに選んでいるので記事内容は開発言語、インフラ等ばらばらです。なお、当ブログで発信、発言は私個人のものであり、所属する組織、企業、団体等とは何のかかわりもございません。ブログの内容もきちんと検証して使用してください。よろしくお願いします♪

DSC - QX10 をゲットしたので、カメラを制御するのに、SSDPとか、JSON-RPC over HTTP を利用した簡単なアプリケーションを C# で作ってみた

 最近、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 を使用しているんですね。呼び出し手順は下図の通りです。

Overview of how to access the camera functions.

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 をコールするんですね。大まかな流れは理解できました。 

Overview of device discovery and API calls.

 ここ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 等の情報が取得できました。

f:id:koogucc11:20140111231414p:plain

 http://10.0.0.1:64321/DmsRmtDesc.xml の内容を参照してみましょう。エンドポイント、API 情報などの情報が記載されています。カメラを制御するには、http://10.0.0.1:10000/sony/camera に対して、JSON のパラメータを HTTP POST で送出することで制御が可能だと判断できます。

f:id:koogucc11:20140112114926p:plain

 写真を QX10 に撮らせてみたいと思います。API 仕様は下図の通りです。

f:id:koogucc11:20140112143933p:plain

 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 

f:id:koogucc11:20140112170551p:plain

 デバイス制御プログラミング経験なかったのですが、JSON - RPC のような Web 技術をベースとしたものであれば、デバイス制御の経験がない技術者でも容易にデバイス制御のプログラムが書けます。( いいですね、こーゆーの。どんどん広まっていくことを望んでます。)

 今後も機会 ( 無駄遣いの機会? ) があれば、今回の記事のようなカテゴリの記事も投稿していきます。