QNAP の監視カメラサービス QVR Pro のクライアントソフトには、任意の時間の録画を取得して mp4 にエクスポートする機能がある。これを自動化したい。
概要
QVR Pro には HTTP API があり、下記の "QVR Pro API を申請" から申込むと見ることができる (自動応答で URL 送られてくる)。
これを利用して、下記の手順を踏む事で期待する結果が得られた。
- ログイン
- カメラの id を取得
- 取得したい録画の情報を指定
- ダウンロード
- .264 に変換
- .mp4 に変換
1-4 は QVR Pro の HTTP API 呼び出し。
5 は何らかのスクリプト (ここでは Ruby を使用。
6 は ffmpeg を使う。
QVR Pro の API 呼び出し
ここでは curl を使う。
ログイン
# パスワードは Base64 encode して percent encoding する
$ curl -k 'https://[host:port]/cgi-bin/authLogin.cgi?user=[username]&serviceKey=1&pwd=[encoded_password]'
<?xml version="1.0" encoding="UTF-8"?>
<QDocRoot version="1.0">
...
<authSid><![CDATA[xxxxxxxx]]></authSid>
...
</QDocRoot>
認証に成功すれば /QDocRoot/authSid
にセッション ID が入ってくる。今後のリクエストにはこのセッション ID が必要になる。
カメラの ID を取得
# カメラの一覧
# sid: "ログイン" で取得したセッション ID を指定
curl -k 'https://[host:port]/qvrpro/camera/list?sid=[sid]&ver=1.0.0'
{
"datas": [
{
"channel_index": 0,
"name": "Camera",
"umsid": "XXXXXXXXXXXXXXXX",
"guid": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
"brand": "Panasonic",
"model": "BB-ST165",
"mac": "XX:XX:XX:XX:XX:XX",
"ver": "4.00",
"ip": "X.X.X.X",
"port": "80",
"video_codec_setting": "H.264",
"video_resolution_setting": "1280x960",
"frame_rate_setting": "10",
"video_quality_setting": "8192kbps",
"stream_state": [],
"status": "NVR_CAM_CONNECT_IDLE",
"rec_state": "RECORDING",
"rec_state_err_code": 0,
"frame_rate": "-1",
"bit_rate": -1
},
...
}
datas[n].guid
で得られるカメラの ID が必要になる。
取得したい録画の情報を指定
# sid: "ログイン" で取得したセッション ID を指定
# ch_sid: "カメラの ID を取得" で取得したカメラの ID を指定
# start_time: 録画を取得したい開始日時を Unix time でミリ秒単位で指定
# end_time: 録画を取得したい開始日時を Unix time でミリ秒単位で指定
# gop_mode: 1 を指定するとストリーミングでなく、ダウンロードできる (ドキュメント未記載)
# ほかのオプションはだいたいこれでよいはず (詳しくはAPI ドキュメント参照)
curl -k 'https://[host:port]/qvrpro/apis/qplay.cgi?ver=v1&cmd=open&sid=[sid]&ch_sid=[ch_sid]&query_type=0&start_time=1541486138332&end_time=1541486148332&recording_type=0&stream_id=255&alarm_stream_id=255&data_type=1&gop_mode=1'
0
PSXXXXXXXXXXXXXXXXXXXXXXXXXX__
レスポンスは 0 なら成功。そのあとにプレイバックセッション ID が続く。
次のステップでプレイバックセッション ID が必要。
gop_mode=1
の指定をしない場合、レスポンスに end_time - start_time
だけ時間がかかる。後述の "既知の問題" も参照。
ダウンロード
# sid: "ログイン" で取得したセッション ID を指定
# session: "取得したい録画の情報を指定" で取得したプレイバックセッション ID を指定
curl -k 'https://[host:port]/qvrpro/apis/qplay.cgi?ver=v1&cmd=get&sid=[sid]&session=[session]' > out.q264
レスポンスは最初の 3 bytes が 0x0A300A
なら成功。
その後にビデオフレームが続く。
ここでは最初の 3 bytes も含めてファイルに書き出している。
.264 に変換
(たぶん) "取得したい録画の情報を指定" で gop_mode=1
を指定した場合、ビデオフレームの形式はドキュメントされているものと異なる。
下記のような形式と思われる。
# リトルエンディアン
# 下記の繰り返し
# 最後に 0x00 の padding がつく
uint8 0x0a
string タイムスタンプ (文字列)
uint8 0x0a
string この次の次からフレームの終わりまでのサイズ (文字列)
uint8 0x0a
uint32 0x00
uint32 0x0a なら最後以外のフレーム、0x0b なら最後のフレーム
uint32 0x02
uint8[4] "q264"
uint32 画像幅
uint32 画像高さ
uint8[24] 0x00 * 24
uint8[4] "G276"
uint32 0x1f40
uint32 0x04
uint32 0x01
uint32 0x00
uint64 このあとに続くデータのサイズ (バイト単位)
string H.264 の Raw データ
次の処理に必要なのは、最後の H.264 の Raw データ
なのでこれだけ抽出するスクリプトを書いた。
$ ruby q264to264.rb out.q264 > out.264
.mp4 に変換
# -r はフレームレート。"カメラの ID を取得" の frame_rate_setting と同じ値でいいはず
$ ffmpeg -r 10 -i out.264 -c copy out.mp4
既知の問題
"取得したい録画の情報を指定" で gop_mode=1
を指定した場合、音声のデータが含まれない。そもそもクライアントソフトのエクスポート機能でも音声を含めるよう設定すると、エクスポートが終わらない問題がある (Windows 版で mp4 指定時はエクスポートが完了するが音声は含まれない)。
gop_mode=1
を指定すると、音声のデータが含まれるようになる (形式は公式のドキュメント参照) が、レスポンスに取得したい録画と同じだけの時間がかかるようになってしまう。