💭

【Python】クラウド録画サービスを作る! [その1]システム構成の検討

2023/06/22に公開

https://zenn.dev/daddy_yukio/books/c117e5705d7e54

はじめに

こんにちは。Daddy's Officeの市川です。

近年、防犯カメラの映像をクラウドサーバー上に24時間録画するサービスが増えてきています。


サービス構成概要図

今回は、こうしたサービスを作ってみようと思います。
記事は複数回に分けて説明していく予定です。

作成するサービスは、最低限必要な以下の機能を実現したいと思います。

  • 防犯カメラの映像を24時間クラウドサーバーに録画する
  • 録画された映像を見たいときに、必要な区間を切り出して視聴する
  • 現在の映像をリアルタイムにライブで見る

こうした機能を実現するサービスをどのような構成で作成するか?

今回は、作成するシステムの構成を検討していきたいと思います。

カメラ側の構成

最近の防犯カメラは、ほとんどがネットワーク対応ですので、今回作るシステムでもネットワーク対応の防犯カメラが使える構成を考えていきます。

最近のネットワークカメラのほとんどがRTSP (Real Time Streaming Protocol)に対応しています。

RTSPとは、カメラと受信器(録画サーバーなど)との間で、取得したい映像や音声の情報を伝達したり再生開始・停止を行ったりする制御用のプロトコルです。
通信の中身はHTTP同様、ASCII文字列ベースのプロトコルで、TCPでの通信です。

RTSPは、映像/音声の取得に必要な情報をやり取りするプロトコルであり、実際の映像/音声データの転送はRTSPではなく、RTP(Real-time Transport Protocol)というUDPベースのプロトコルを使用して行われます。

ちなみに、ONVIF(Open Network Video Interface Forum)対応のカメラも多くありますが、ONVIFとは、カメラとの接続性を確保する為の規格で、実際の映像/音声の取得プロトコルには、ほとんどのカメラがRTSPを使用しています。その為、ONVIF対応カメラでもほぼ問題なくRTSPで接続ができると思います。

下図はRTSPで情報伝達をした後にRTPで通信を開始するまでのイメージ図です。


RTSP通信イメージ

RTSPでは、まず最初に映像受信サーバー側から、カメラに対してTCPで接続し、映像/音声のフォーマット情報等を問い合わせた後(DESCRIBE)、受信ポート番号等を伝えます(SETUP)。その後、開始の合図(PLAY)を通知すると、SETUPで指定されたポート番号に対して、RTPで映像と音声が別々に送信されてくる形です。

今回作成するシステムでは、このRTSP対応カメラを使っていきますが、この時に考えなければいけいないのが、通信の方向です。

家の中に置いてあるRTSPカメラの映像を、直接クラウドサーバーに保存する為には、家の中のカメラに、クラウドサーバー側からTCPで接続できなければいけません。
しかし通常の環境では、外部からの通信はルータで拒否されるので、クラウドサーバーからカメラにTCP接続することができません。


外からの通信はルータで遮断される

ルータの設定でポートマッピングを行えば、クラウドサーバーからの通信をカメラまで届けることは可能ですが、ルータの設定を変更する必要があり、設置場所が限定されてしまいます。

そこで今回は、カメラと同じネットワーク内に、中継プログラムを配置することで、ルータの設定を変更することなく、クラウドサーバーに映像を保存する方法で作っていきたいと思います。


家の中に中継プログラムを置いてクラウドサーバーにアップロード

カメラとのRTSP接続は、同じ家庭内のネットワーク上にある中継プログラムで行います。映像/音声も中継プログラムで受信し、中継プログラム側からクラウドサーバーに映像をアップロードする形です。
この形であれば、インターネット通信が可能な環境であれば、ほぼ問題なくカメラ映像をクラウドサーバーに録画することができます。

次に考えなければいけないのが、中継プログラムとクラウドサーバーとの間の通信プロトコルをどうするかですが、それは、動画視聴側の構成と合わせて考えていきます。

動画視聴側の構成

今回のシステムでは、ウェブブラウザを使用して、クラウドサーバーに保存された映像を視聴できるようにしたいと思います。

そうなると、使用するプロトコルは、現状ではHLS(HTTP Live Streaming)ほぼ一択になります。

HLS(HTTP Live Streaming)とは、Apple社が開発したHTTPベースの動画配信の規格です。

専用の動画配信サーバーを必要とせず、Apacheのような通常のHTTP配信機能があれば動画配信が実現できます。
また、CDN(Content Delivery Network)のキャッシュサーバーとも相性が良く、大量のユーザによる同時視聴時でも負荷分散を行うことが可能になります。

今回のシステムでは、負荷分散は不要ですが、専用の配信サーバーが不要、というHLSのメリットを生かして、クラウドサーバー上には配信サーバーを配置しない、いわゆるサーバーレス構造で作っていきたいと思います。

HTTP Live Streamingの仕組み

HLS(HTTP Live Streaming)では、M3U8というインデックスファイルと、実際の動画ファイルの組み合わせで動画配信を行います。

動画ファイルのコンテナフォーマットとしてはMPEG2-TSが使用されます。
(フラグメント形式のMPEG-4も使用可能です)

M3U8の中身は、再生するMPEG2-TSのURLのリストが記述された、単なるテキストファイルです。


M3U8例

#EXT-INFOというタグに動画の長さ(秒)を記載し、その下に動画ファイル(MPEG2-TS)の配信URLを記述します。

配信サーバー側では、このM3U8ファイルと、細切れにされたMPEG2-TSファイルを、HTTPの公開ディレクトリに置いておきます。

再生プレイヤーに、このM3U8のURLを指定し、再生を開始すると、再生プレイヤーはHTTP GETで指定されたM3U8を取得します。
M3U8を取得したら、最初に記載されたMPEG2-TSをHTTP GETで取得し、再生を開始します。


HTTP Live Streaming処理イメージ

一つ目のMPEG2-TSが終了する前に、次のMPEG2-TSをバックグラウンドで取得し、時間が来たら2つ目のMPEG2-TSを再生します。
これをリストが終わるまで繰り返すことで、1本の動画を再生しているのです。

HLSは、再生プレイヤー側は色々と処理しなければいけないことがありますが、配信サーバー側は、M3U8もMPEG2-TSも、通常のHTMLと変わらない、単なるHTTPコンテンツと同様です。
その為、HTTP GETでコンテンツを配信できれば、配信サーバーとしては基本OKとなり、非常にサーバー側の実装負荷が低い(=楽な)配信フォーマットと言えます。

今回作成するシステム構成

以上の検討を踏まえて、今回作るシステムは下図のような構成で行きたいと思います。


今回作成するシステム構成イメージ図

中継プログラム

中継プログラムはPythonで実装します。

カメラとのRTSP接続は、PythonからFFmpegを利用して接続し、映像を取得します。
せっかくFFmpegを使用するので、カメラ入力はRTSPだけでなく、USB接続のWebカメラにも対応してみたいと思います。

取得したカメラ映像は、MPEG2-TSコンテナに包んで、数十秒のMPEG2-TSファイルとして、クラウドサーバーにアップロードします。

クラウドサーバー

クラウドサーバーには、AWSを使用します。
使用するAWSのサービスは以下の2つです。

S3

AWSが提供するクラウドストレージ・サービスです。

コンテンツの保存だけでなく、HTTPでのコンテンツ公開も可能なので、簡易的なHTTPサーバーとしても利用できます。

今回は、中継プログラムからアップロードされてくるMPEG2-TSファイルの保存と配信の両方をS3で行います。

また、動画再生用のJavaScriptを含んだHTMLファイルも同じくS3に配置・配信します。

Lambda

いわゆるサーバーレスなシステムを実現する為の、イベント駆動型コンピューティングサービスです。

簡単に言うと、関数の中身を書くだけで、インフラやサーバーを気にせずにWebAPIが作れるサービスです。

今回のシステムでは、指定した区間の動画を再生する為のM3U8ファイルを、動的に作成する機能をLambdaを使って、Pythonで作ろうと思います。

動画再生プレイヤー

保存したカメラ映像の再生は、ウェブブラウザ上で行いますので、JavaScriptを使って動画再生プレイヤーを作ります。
JavaScriptで実装するので、HLSの再生はVideo.JSで行います。

また、保存した動画の再生だけでなく、簡易的なリアルタイム・ライブビューの機能も実装してみようと思います。

おわりに

色々と検討してきましたが、実際のシステムは非常に簡易な構成になります。
使用する言語もPythonとJavaScriptのみですので、興味を持たれた方はぜひ次回以降も読みに来てください!

Discussion