■概略
◯RTSPサーバとRTSPクライアント(ffmpeg)
最近のネットワークカメラはRTSPサーバになれる
※今回はPanasonic WV-S1536Lを使用
Linux(MA-X300を使用)でffmpegを使えばRTSPクライアントになれる
これでカメラ画像をMP4で記録することができる
◯ネットワークカメラの動体検知
最近のネットワークカメラには動体検知機能がある
※画像内で動きが発生したら通知してくれる
1)通知をLinuxにRESTで通知する
2)MP4を通知の前後10秒の合計20秒の動画にして保存する
これで動体検知した画像だけを残すことができる
■カメラ側
◯概略とカメラ設定
1)ネットワークカメラをRTSPサーバにして、MA-X300で常時受信する
受信したmp4は/ramdiskに10秒ごとに別ファイルで保存する
/ramdiskの容量は少ないので10分経過で削除する
2)RESTサーバで動体検知通知を受信する
10秒後に最新3のmp4を1つのmp4にして/mp4に保存する
◯認証方法
rtspをID/PW認証する場合は以下のようにURLにID/PWを記載する
rtsp://{ネットワークカメラのIP}:554/Src/MediaInput/stream_1
↓
rtsp://{ID}:{パスワード}@{ネットワークカメラのIP}:554/Src/MediaInput/stream_1
しかしMA-X300のUbuntu22.04(ARM64)のffmpegではID/PWの指定をするとエラーになる
そのためネットワークカメラはID/PW認証を止め、IP認証にする
ネットワークカメラのWebI/Fにログインし、
ユーザ管理-ユーザ認証をoff
ユーザ管理-ホスト認証をonにして必要なIP(ここではMA-X300のIP)を追加
◯カメラ設定
歯車マークの[設定]-[詳細設定]-[カメラの詳細設定]-[映像/音声]-[映像]
以下変更点と重要点のみ
初期表示ストリーム | ストリーム(1) |
JPEG画像更新速度(動画時) | 5fps |
ストリーム配信 | On |
JPEG(1) 解像度 | 1280x720 |
ストリーム配信 | ◎ON |
圧縮方式 | H.264 ※H.265だとWindows Media Playerで再生 できない |
解像度 | 1280x720 ※FD。FullHD(1920x1024)・15fpsにするかも |
配信モード | ベストエフォート配信 |
フレームレート | 5fps ※6.1MB/分 ramdiskに保存できるように 容量は少なくする |
1クライアントあたりのビットレート | 1024kbps |
◯カメラ動体検知設定
歯車マークの[設定]-[詳細設定]-[カメラの詳細設定]-[アラーム]-[動作検知エリア]
[全領域]をクリックする
歯車マークの[設定]-[詳細設定]-[カメラの詳細設定]-[アラーム]-[通知]-[HTTPアラーム通知]
通知先1 : http://{ネットワークカメラのIP}
アラーム(左のチェックボックス) : x
通知データ : /notice
※http://{ネットワークカメラのIP}/api/v1/notice にRESTサーバを用意する
■MA-X300側
◯処理の流れ
1) カメラからRTSP受信常駐プログラム
プログラム名 : restserver_local/batch/systemd_rtspRecording1.py
ffmpegコマンドでeth1のLAN側にあるカメラにRTSP接続し
10秒毎のmp4ファイルを/ramdiskに
{カメラ名}_yyyymmdd_HHMMSS.mp4の形式で作成しつづけている
MA-X300が複数台のカメラを受信する場合複数の常駐プログラムが必要
◯ffmpeg他インストール
# apt install -y ffmpeg
# apt install -y python3-venv
# cd /var/www
# python3 -m venv python3-venv
# source /var/www/python3-venv/bin/activate
(python3-venv) root@netcam:~# pip3 install requests
◯ffmpeg常駐スクリプト
MA-X300上のffmpegで以下のオプションで受信ができる
-c:v copy を指定しないと再度エンコードを行おうとしてCPU稼働率が激上がりする
pythonの設定はRESTサーバもcronもsystemdも全てconfg.pyで設定する
/var/www/ismscam/batch/config.py
device_name = 'emboma-matsuyama'
camera1_url = 'rtsp://{ネットワークカメラのIP}:554/Src/MediaInput/stream_1'
camera1_name = 'camera1'
/var/www/ismscam/batch/systemd_rtspRecording1.py
import config
…
cmd = 'ffmpeg -rtsp_transport tcp -i "{config.camera1_url}" \
-f segment -segment_time "10" -segment_format_options movflags=+faststart \
-c:v copy \
-reset_timestamps "1" -strftime "1" /ramdisk/{config.camera1_name}_%Y%m%d_%H%M%S.mp4'
…
○ffmepg起動スクリプト
/etc/systemd/system
[Unit]
Description=RTSP Recording 1
After=network.target
[Service]
Type=simple
PIDFile=/var/run/systemd_rtspRecording1.pid
RemainAfterExit=no
Restart=on-failure
RestartSec=10
ExecStart=/var/www/ismscam/batch/systemd_rtspRecording1.py
[Install]
WantedBy=multi-user.target
# systemctl enable rtsprecording1
# systemctl start rtsprecording1
/ramdiskに10秒ごとにmp4が書き出されれば成功
2) 10分経った/ramdiskの古いmp4を削除するcronプログラム
プログラム名 : restserver_local/batch/cron_rmOldMP4_1.bash
cronで1分毎に起動し、10分経過した特定のカメラが受信したmp4を削除している
複数台のカメラを受信する場合は複数の削除プログラムが必要
削除は他に、以下がある
・/mp4のn日経ったmp4を削除する
・DISKが90%超で最古のMP4を1つ強制削除する
◯/ramdiskの10分以上経ったMP4を削除するcron
カメラ毎に下記cronをrootで登録し、/ramdiskの10分以上経ったmp4は自動的に削除されるように設定する
config.pyのcamera_nameを設定する
# crontab -e
*/1 * * * * /var/www/ismscam/batch/cron_rmOldMP4_1.bash > /dev/null 2>&1
---
find /ramdisk -mmin +10 -name "camera1*.mp4" | xargs rm -f
---
3) カメラのHTTP通知をWebサーバで受信しMP4を合成する
カメラで動体検知をするとWebサーバにHTTP通知する
10秒毎に分割されているMP4を必要な本数結合し/mp4に保存する
◯bottle.pyでRESTサーバを作成する
/api/v1/notice部分
### REST API
# mp4ファイルを結合し/mp4に保存する
@route('/api/v1/notice', method='GET')
@route('/api/v1/notice/<camera>', method='GET')
def notice(camera=''):
forms = getUTF8forms(request.forms)
result = {}
result['camera'] = camera
if camera == '':
result['message'] = 'No camera name!'
return template('notice', dict(forms=forms, result=result))
nowtime = datetime.now().strftime('%Y%m%d%H%M%S')
# ローカル出力ファイル
# mp4結合ファイル:{device_name}_{camera}_yyyymmddHHMMSS.mp4
localout_filename = '{0}_{1}_{2}.mp4'.format(config.device_name, camera, nowtime)
localout_filepath = '/mp4/{0}'.format(localout_filename)
# ローカル入力ファイル 呼び出しから10秒後の最新mp4を3つ取得する
# カメラからのmp4:{camera}_yyyymmddHHMMSS.mp4
time.sleep(10)
files = glob.glob("/ramdisk/{0}*mp4".format(camera))
files.sort()
files = files[-3:]
# 結合対象を書いたファイル
target_file = "{0}_targets".format(localout_filepath)
fileobj = open(target_file, "w", encoding = "utf_8")
for i in range(3):
fileobj.write("file {0}\n".format(files[i]))
fileobj.close()
# 3つのmp4を結合し結合対象を書いたファイルを削除
cmd = 'ffmpeg -f concat -safe 0 -i {0} -c copy {1}'
cmd = cmd.format(target_file, localout_filepath)
ret = subprocess.run(cmd, shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
os.remove(target_file)
# 画面表示用にファイル名とファイルサイズを取得
result['mp4file_name'] = localout_filename
if os.path.exists(localout_filepath):
result['mp4file_size'] = os.path.getsize(localout_filepath)
return template('notice', dict(forms=forms, result=result))
◯/mp4の7日以上経ったMP4を削除するcron
カメラ毎に下記cronをrootで登録し、/mp4の7日以上経ったmp4は自動的に削除されるように設定する
config.pyのcamera_nameを設定する
---
# crontab -e
*/1 * * * * /var/www/ismscam/batch/cron_rmOldStorageMP4_1.bash
---
find /mp4 -mtime 7 -name "camera1*.mp4" | xargs rm -f
---
◯DISK90%超で最古のMP4を強制的に削除するcron(毎分)
# crontab -e
*/1 * * * * /var/www/ismscam/batch/cron_rmOldestMP4.bash > /dev/null 2>&1
---
#!/bin/bash
# DISK利用率90%を超えたら最も古いMP4を1つ削除する
use=`df -h|grep root|awk '{print $5}'|sed "s/%//"`
if [ $use -gt 90 ]; then
mp4file=`ls -ltr /mp4| tail -n +2 | head -1|awk '{print $9}'`
/bin/rm '/mp4/'$mp4file
fi
---
4) その他
◯ Scoreboard is Fullエラーを抑える
MX-X300でWebアプリを動かすと、半日程度稼働させると、apache2のerror_logに下記のエラーが頻発し、
Webアクセスができなくなる
AH03490: scoreboard is full, not at MaxRequestWorkers.Increase ServerLimit .
下記のファイルの以下をコメントアウトし、apache2を再起動する
/etc/apache2/mods-available/mpm_event.conf
/etc/apache2/mods-available/mpm_prefork.conf
/etc/apache2/mods-available/mpm_worker.conf
# MaxConnectionsPerChild 0