꽃삽질 : ffmpeg 를 이용한 rtsp stream 녹화

** 이 계획(?)의 최신 소식은 이 글 아래 쪽에.


꽃삽질 자취 기록:

며칠 이걸로 고생했는데, 답을 찾아내진 못했다. 이건, 녹화를 시행하는 쪽 기기 문제도 있겠지만, 영상을 날려주는 rtsp 서버 쪽에 더 큰 문제가 있는 듯 하다.

Xiaomi Dafang 을 활용(?)하여 rtsp 서버를 만들었다. tinyCam 등을 활용하면, 적어도 ‘보는 데’는 지장이 없다. 다만, 이걸 녹화하여 보존하려하니 무리가 따른다.

Dafang Custom Firmware 에 녹화 기능이 있긴 하다. 그런데 이 기능을 사용하면 microSD 를 이용해야만 하고, 수명 문제도 있고, 영상을 확인할 때 좀 애매한 면이 있다.

따라서, 다른 기기에서 이 영상을 실시간으로 받아 저장하는 방식을 택했는데..
구글의 뒤져 구한 코드들을 짜깁기 해서 다음과 같은 ffmpeg 명령을 만들었다.

ffmpeg -stimeout 1000000 \
 -abort_on empty_output \
 -rtsp_transport tcp \
 -fflags nobuffer \
 -an \
 -i rtsp://ip:port/unicast \
 -map 0:v \
 -c copy \
 -metadata title="" \
 -f segment \
 -segment_time 600 \
 -segment_wrap 100 \
 -reset_timestamps 1 \
 -strftime 1 \
 "file-%Y%m%d-%H%M.mp4"

그런데.. 이거 어떨 땐 40분쯤 잘 버티기도 하지만, 대부분은 수분 이내에 죽어버린다. 이유는 명확하질 않다. 정확한 오류가 나타날 때도 있지만(서버 연결 불가 등), 그냥 멈추는 경우가 대부분이다.

이걸 해결하기 위해 구글을 뛰어다니고(?), Dafang C.F 에도 문의를 해봤는데, 아직 명쾌한 해답은 찾질 못했다. 막연한 내 짐작으로는, 서버, 즉 Dafang 의 하드웨어 자체가 단단하질 못해서, 즉, 영상을 오류없이 잘 제공해줄만큼 견고한 상태가 아니기에 이런 문제가 발생하는 듯 하다. Wifi 시그널 문제도 있을테고, 서버 자체도 조금 불안한 면이 있을테고.

따라서, 그야말로 24/7 영상을 저장하기란 문제가 있어 보인다.

하여.. 고민하다가, 그냥 ffmpeg 를 무한루프로 돌리기로 했다.

while :
do
	# Start FFmpeg hourly recordings
	#ffmpeg 
	#ffmpeg -stimeout 2000 \ 
	#ffmpeg -stimeout 300000 \
	ffmpeg -stimeout 1000000 \
	 -abort_on empty_output \
	 -rtsp_transport tcp \
	 -fflags nobuffer \
	 -an \
	 -i rtsp://ip:port/unicast \
	 -map 0:v \
	 -c copy \
	 -metadata title="" \
	 -f segment \
	 -segment_time 600 \
	 -segment_wrap 100 \
	 -reset_timestamps 1 \
	 -strftime 1 \
	 "$BASEDIRECTORY/$YEAR/$MONTH/$DAY/file-%Y%m%d-%H%M.mp4"
	 echo "$(date): 왜 그런진 모르지만, ffmpeg 가 죽어서 재시작!"
done

이제 막 시험해봤으니.. 결과는 오늘 내로 나올테지만, 무한루프로 돌리고 있으니 뭐 어쨌든 소기의 목적은 달성할 수 있으리라 생각한다.

다만, 연결이 끊어지는 순간 순간마다 영상 녹화가 누락되는 상황은 어쩔 수가 없다. (그게 수초에서 길어야 1분 정도일테지만, 그 순간에 뭔 일이 날까봐 녹화를 하는건데..)
이 부분이 조금 아쉽다.

그건 어쩔 수 없다 치고, 시험해보고 별 문제가 없다면, 이제 systemd 로 만들어 서비스화하는 작업만 해주면 녹화 시스템에 문제가 생기거나 정기 재부팅을 하더라도 자동으로 재시동되도록 해줄 수 있겠다.

오픈 소스가 이래서 좋긴 한데, 어쨌든 한계는 또 명확하고..
완벽하게 하려면, 돈 들여 제대로된 장비를 들여야겠지.
허나, 거의 돈을 들이지 않고도 이만큼이라도 할 수 있다는게 어딘지.


여기 저기 뒤져보고, 문의도 해봤지만, 내가 원하는 답을 찾지는 못했다.
그래도, 이리 저리 뒤진 결과로, 대충 결과는 낼 수 있게 됐다.

위 명령대로 ffmpeg 를 실행하면, 중간 중간 끊기는 현상이 발생한다. 서버, 즉 Dafang 카메라의 문제로 보이는데, 이걸 현재 상황으로(물론 내 수준에서) 해결할 방법이 없다. 수많은 ffmpeg 의 옵션을 적절히 조합하면 될 수도 있을 듯 한데.. 관련 지식이 없다보니 어쩔 도리가 없다.

무한 영역으로!

위에도 적었지만, 내가 생각한 대안은 무한 루프였다. ffmpeg 를 돌리되, ‘멈춤 현상’을 인정하고, 그걸 프로그램으로 해결한다.
최종 결과는 다음과 같다.

#!/usr/bin/bash

BASEDIRECTORY="$HOME/카메라 녹화"

YEAR=$(date +"%Y");
MONTH=$(date +"%m");
DAY=$(date +"%d");

mkdir -p "$BASEDIRECTORY/$YEAR/$MONTH/$DAY"


# 자꾸 죽는 현상을 대비해 무한루프로.

while :
do
	ffmpeg -stimeout 2000 \
	 -abort_on empty_output \
	 -rtsp_transport tcp \
	 -an \
	 -i rtsp://ip:port \
	 -map 0:v \
	 -b:v 800k \
	 -bufsize 600k \
	 -r 24 \
	 -c copy \
	 -metadata title="" \
	 -f segment \
	 -segment_time 300 \
	 -reset_timestamps 1 \
	 -strftime 1 \
	 "$BASEDIRECTORY/$YEAR/$MONTH/$DAY/file-%Y%m%d-%H%M.mp4" | systemd-cat -p info	
	 echo "$(date): 왜 그런진 모르지만, ffmpeg 가 죽어서 재시작!"

done

while 에만 넣었고, 별다른 변동 사항은 없다. 딱 하나, systemd-cat 을 빼고는.

systemd 에 참가

저 스크립트의 한가지 문제점은, 바로 날짜, 그리고 그 날짜에 입각한 디렉토리 생성에 있다. 오류가 발생하지 않는다고 가정하면, 모든 파일은 스크립트를 처음 실행한 날짜의 디렉토리 (/home/user/카메라 녹화/2020/10/02/ 등)에 저장된다.

굳이 디렉토리에 날짜를 넣은 이유는, 하루치만큼의 파일을 동일한 디렉토리에 넣어놓고자 함이었는데, 이렇게 되면 의도와는 전혀 다른 결과를 받게 된다. (물론, 수시로 ffmpeg 가 알아서(?) 죽기 때문에 그럴 염려는 안해도 되지만..)

아무튼, 원래 의도대로 하루치 파일을 한 디렉토리에 넣어놓으려면, 매일 밤 12:00 에 저 스크립트를 시작해줘야 한다. 매번 수동으로 돌려줘도 물론 된다.(그러려면 tmux 등을 써야만 한다.)

하지만 그럴 수는 없는 터. 자동!하면 생각나는게 cron/crontab 인데, 매일 특정 시간에 실행되게끔 할 수는 있다. 예를 들어, 매일 밤 12:00 에 실행되게끔.

그러나 여기도 문제가 있다. 만약, 뭔가 일이 있어서 시스템 재부팅을 했을 경우, 저 스크립트는 밤 12시가 되기 전까진 다시 시작되지 않는다. 따라서, systemd 를 이용하여, 시스템이 시작될 때, 자동으로 실행되게끔 만들어야 한다.

/etc/systemd/system/cctv-record.service 를 만들고, 다음과 같은 내용을 넣는다.

[Unit]
Description=Dafang Cam record
Wants=network.target
After=network-online.target

[Service]
User=username
Group=groupname
Type=simple
ExecStart=/home/username/bin/cctv-rec.sh
Restart=on-failure
RestartSec=10
KillMode=process

[Install]
WantedBy=multi-user.target

User/Group 은 넣지 않아도 된다. 그러면 root 권한으로 실행이 되고, 이 스크립트로 생성된 파일도 역시 root 소유가 된다. 이를 방지하고자 User/Group 을 넣어줬다.
혹은, 아예 이 파일을 개인영역에 만들고 사용자 권한으로 실행하게끔 할 수도 있긴 하다.

ExecStart 도 그냥 /home 경로를 사용했는데, 보통은 전역(/usr/local/bin/ 등)으로 복사를 해놓는 편이다.

이 내용은 MCKAY 선생님의 도움을 받아 작성할 수 있었다.

마지막으로 enable and start.

sudo systemctl enable --now cctv-record.service

systemd 와 journal 을 위해서, 원 스크립트의 마지막에 | systemd-cat -p info 를 넣었다. 이로 인해, journalctl 에서 cctv 스크립트의 문구를 볼 수 있게 된다.

crontab 으로, 매일 자정에 재시작

cctv-record 서비스만 재시작하게 할 수도 있지만(systemd restart cctv-record), 그냥 기기 자체를 매일 밤에 재부팅하게끔 했다. 그러면 서비스도 당연히 재시작되고, 새로운 날짜에 새 디렉토리가 자연스레 만들어진다.

….

썩 맘에 들진 않지만, 이걸로 당분간은 만족하는 수밖에.

안녕하세요. 글 남겨주셔서 고맙습니다.