第14回 Raspberry Pi で監視カメラを作ろう! ~カメラの遠隔操作(ONVIF)編~


Raspberry Piと専用のカメラモジュールを使用し、ONVIF対応の監視カメラを作成するシリーズ記事です。
本記事はリンク情報システム株式会社の有志が作成しています。


カメラから動画や静止画を取得できるようになったので、監視カメラとして機器間の接続性を高めるために、業界標準のI/Fに合わせるために、ONVIFのプロトコルをサポートしてみたいと思います。

カメラの遠隔操作 (ONVIF)

はじめに

ONVIFについて、wikiでは以下のように説明されてます。

ONVIF(オーエヌブイアイエフ、Open Network Video Interface Forum)は、アクシス、ボッシュ、ソニーが立ち上げたネットワークカメラ製品のインターフェースの規格標準化フォーラムである。

ネットワークカメラの機能をWebサービスとして公開するのが特徴。クライアントはカメラからWSDLを取得してそのカメラが持つ機能の利用方法を動的に入手する。

カメラの動的な発見、カメラ情報の設定および取得、カメラの光学制御およびPTZ (Pan, Tilt, Zoom) 制御、イベントハンドリング、Video Analytics, ストリーミング、セキュリティといったネットワークカメラの利用に必要な一通りのインターフェイスを定義している。
参考元:ONVIF - Wikipedia

簡単にまとめると、端末アプリから自動でカメラを検出。動画の再生やカメラ向きを切り替える事ができる。という事だと思います。

ONVIFでの通信方法

ONVIFは、以下のプロトコルを駆使して通信を行います。

機能 プロトコル
カメラ制御 SOAP
動画 RTSP や HTTP ※今回はRTSPを対象に作成
静止画 HTTP

動画のRTSPや静止画のHTTPは、これまでに述べてきた事ですので、今回は、カメラ制御のSOAPの部分に関して実装を進めて行きます。

gsoapを使用した ONVIF I/F

soap通信を実現するために、linux上で使用できるオープンソースのライブラリとしては、gSoapが存在しています。
gSoapを使用することで、ONVIFのサイトで公開されている WSDL ファイルより、プログラムのスケルトンを生成することができます。
ONVIFクライアントから、RaspberryPi上の監視カメラアプリに対して命令が実行されると、gSoapで生成したスケルトンの処理が実行されます。

その為、このスケルトンに処理を実装していくことで、ONVIFクライアントの要求に応える事ができます。

まずは、以下のコマンドを実行して、WSDLよりスケルトンのヘッダファイル(onvif.h)を生成します

wsdl2h -c -o onvif.h http://www.onvif.org/onvif/ver10/device/wsdl/devicemgmt.wsdl http://www.onvif.org/onvif/ver10/media/wsdl/media.wsdl https://www.onvif.org/ver10/events/wsdl/event.wsdl https://www.onvif.org/ver10/recording.wsdl https://www.onvif.org/ver20/ptz/wsdl/ptz.wsdl

次に、ONVIFでは認証にWSSEを使用するため、生成された onvif.h にWSSE機能を追加するために、importの記述を追加します。

#import "xop.h"    // xop = <http://www.w3.org/2004/08/xop/include>
#import "wsa5.h"   // wsa5 = <http://www.w3.org/2005/08/addressing>
#import "wsse.h"   // WSSEを追加

最後に以下のコマンドを実行して、スケルトンのソースを出力します。

soapcpp2 -I/usr/local/share/gsoap onvif.h

ここまでの手順で、以下のソースファイルが作成されます。
・soapC.c
・soapClient.c
・soapClientLib.c
・soapServer.c
・soapServerLib.c
・soapH.h
・soapStub.h

これらのソースは、ONVIFクライアントと接続・通信を行い、処理を要求されるまでのスケルトンになっています。
(※soapClient.c と soapClientLib.c はクライアント用のソースなので、使用しません。)

処理の実装

次に、実装すべき関数は、以下のようにsoapStub.hに宣言されています。

/******************************************************************************\
*                                                                            *
* Server-Side Operations                                                     *
*                                                                            *
\******************************************************************************/

    /** Web service operation 'SOAP_ENV__Fault' (returns SOAP_OK or error code) */
    SOAP_FMAC5 int SOAP_FMAC6 SOAP_ENV__Fault(struct soap*, char *faultcode, char *faultstring, char *faultactor, struct SOAP_ENV__Detail *detail, struct SOAP_ENV__Code *SOAP_ENV__Code, struct SOAP_ENV__Reason *SOAP_ENV__Reason, char *SOAP_ENV__Node, char *SOAP_ENV__Role, struct SOAP_ENV__Detail *SOAP_ENV__Detail);
    /** Web service operation '__tds__GetServices' (returns SOAP_OK or error code) */
    SOAP_FMAC5 int SOAP_FMAC6 __tds__GetServices(struct soap*, struct _tds__GetServices *tds__GetServices, struct _tds__GetServicesResponse *tds__GetServicesResponse);
    /** Web service operation '__tds__GetServiceCapabilities' (returns SOAP_OK or error code) */
    SOAP_FMAC5 int SOAP_FMAC6 __tds__GetServiceCapabilities(struct soap*, struct _tds__GetServiceCapabilities *tds__GetServiceCapabilities, struct _tds__GetServiceCapabilitiesResponse *tds__GetServiceCapabilitiesResponse);
    /** Web service operation '__tds__GetDeviceInformation' (returns SOAP_OK or error code) */
    SOAP_FMAC5 int SOAP_FMAC6 __tds__GetDeviceInformation(struct soap*, struct _tds__GetDeviceInformation *tds__GetDeviceInformation, struct _tds__GetDeviceInformationResponse *tds__GetDeviceInformationResponse);
                            

例えば、カメラの情報を取得するというI/F (devicemgmt.wsdl で定義されていた、GetDeviceInformation) を実装した場合、以下のようになります。

/** Web service operation '__tds__GetDeviceInformation' (returns SOAP_OK or error code) */
SOAP_FMAC5 int SOAP_FMAC6 __tds__GetDeviceInformation(struct soap* soap, struct _tds__GetDeviceInformation *tds__GetDeviceInformation, struct _tds__GetDeviceInformationResponse *tds__GetDeviceInformationResponse)
{
    // 実際は、設定ファイルやROM等から情報を取得して通知する。
    tds__GetDeviceInformationResponse->Manufacturer    = soap_strdup(soap, "製造者名");
    tds__GetDeviceInformationResponse->Model           = soap_strdup(soap, "モデル名");
    tds__GetDeviceInformationResponse->FirmwareVersion = soap_strdup(soap, "バージョン番号");
    tds__GetDeviceInformationResponse->SerialNumber    = soap_strdup(soap, "シリアル番号");
    tds__GetDeviceInformationResponse->HardwareId      = soap_strdup(soap, "ハードウェアID");
    return 0;
}

実装すべき関数は沢山あるのですが、gSoapを使用することで、SOAP通信などの難しい部分の処理は、すべて自動で作成されるので、肝心の監視カメラの制御に専念するる事ができます。

対応アプリとの通信

このようにONVIF対応として監視カメラを実装すれば、今回自作するWindows用の監視カメラ映像再生用アプリからのみではなく、一般に公開されているONVIF対応アプリから、カメラの映像が再生できるようになります。

例えば、GooglePlayにあるBiyee SciTech, Inc.の "ONVIF IP Camera Monitor (Onvifer)" で試したら、接続できる事が確認できました。

※2020/12/30追記 現状、iPhoneアプリでは動作確認がとれていません。ONVIF対応アプリと謳っている数点のiPhoneアプリで確認してみましたが、何れも映像の再生(通信)が出来ていません。


インデックス記事へ
第13回記事へ
第15回記事へ


リンク情報システム株式会社では一緒に働く仲間を随時募集しています!
また、お仕事のご依頼、ビジネスパートナー様も募集しております。お気軽にご連絡ください。