Ghi hình chuyển động #2: ffmpeg

FFmpeg là một framework hàng đầu về đa phương tiện (xử lý audio, video). Nó có thể decode (giải mã), encode (mã hóa), transcode (chuyển mã), mux (ghép kênh), demux (phân kênh, tách kênh), stream (ví dụ như livestream trên youtube, facebook,..), filter (lọc) và play (chạy, phát video) rất nhiều thứ mà con người hay máy móc tạo ra.

ffmpeg đã bao gồm trong motion. Tuy nhiên chúng ta có thể chỉ dùng ffmpeg để ghi hình.

# cài đặt
apt install ffmpeg

# Cú pháp
ffmpeg [global_options] {[input_file_options] -i input_url} {[output_file_options] output_url}

Kế hoạch sử dụng ffmpeg

  1. Ghi hình từ rtsp ra clip dài 30 giây. Việc ghi ra file nhỏ giúp tải về xem nhanh hơn và ít lãng phí không gian đĩa theo bước tiếp theo.
  2. Tìm sự thay đổi trong khung hình theo ngưỡng cho trước trong từng video đã ghi. Nếu tìm thấy ghi ra file ảnh các thay đổi, nếu không thì xóa video tương ứng.

Để ghi hình từ rtsp, chúng ta cần một đường truyền ổn định, tốt nhất là camera dùng cáp LAN thay vì wifi.

Bước 1: Ghi video bằng script vrec

+ vrec là một script giúp bắt luồng RTSP từ camera và ghi thành các clip 30 giây tại thư mục tạm (mặc định là /tmp/vrec.clip), kết thúc tại thời điểm định trước. Thời điểm gọi chạy được đặt qua crontab. Thí dụ từ 20g đến 6g hôm sau.

# sử dụng
vrec -url 'rtsp_url' [-cam camera_name][-tmp tmp_dir][-end HH:MM]

# thí dụ
vrec -url 'rtsp://admin:<password>@<cam_ip>/cam/realmonitor?channel=1&subtype=0' \
     -cam floor \
     -tmp /tmp/vrec.clip \
     -end 6:00

# Chú ý giá trị của option url được bọc trong cặp '

+ Nội dung script vrec

#!/bin/bash
echo "Record video from RTSP, $(basename $0) - version 20220129"
echo -e "LNT <lnt@lyle.info>\n"
eMsg(){ echo -e "Ghi video từ RTSP\n$(basename $0) -url 'rtsp_url' [-cam camera_name][-tmp save_clip_to][-end HH:MM]"; exit 1; }
while [ "$1" != "" ]; do
  case $1 in
  -url) shift;url=$1;;
  -tmp) shift;tmp=$1;;
  -cam) shift;cam=$1;;
  -end) shift;ti=$1;;
     *) eMsg
  esac
  shift
done
[ ! "$url" ] && eMsg
[ ! "$tmp" ] && tmp="/tmp/$(basename $0).clip"; [ ! -d "$tmp" ] && mkdir -p "$tmp"
[ ! "$cam" ] && cam=camera
if [ "$ti" ]; then
  [[ "$ti" =~ ^(2[0-3]|1[0-9]|0?[0-9])\:(0?|[1-5])[0-9]$ ]] || eMsg
  b=$(date +%s)
  e=$(date --date "$ti:00" +%s)
  d=$(($e - $b))
  [ $d -lt 0 ] && d=$(($d + 86400))
  ( sleep $d; killall --user $USER  --ignore-case  --signal INT ffmpeg; ) &
fi

# ffmpeg options
gopt='-hide_banner -loglevel quiet -fflags +igndts -err_detect aggressive -fflags discardcorrupt -use_wallclock_as_timestamps 1 -rtsp_transport tcp'
oopt='-map 0 -c:v copy -an -f segment -segment_time 30 -reset_timestamps 1 -r 7 -strftime 1'
while true; do
  ffmpeg $gopt -i "$url" $oopt "$tmp/$cam"_%Y%m%d%H%M%S.mkv
  # Hêt giờ hoặc Lỗi, chờ 15 giây
  [ $? -eq 255 ] && break || sleep 15
done

Các option của ffmpeg

  • y: đồng ý với mọi câu hỏi đặt ra khi ffmpeg làm việc
  • hide_banner: không hiện banner của ffmpeg
  • rtsp_transport: rtsp dùng giao thức tcp
  • i: input file
  • c:v video codec
  • an: không dùng audio
  • segment_time: thời lượng mỗi phân đoạn
  • r: số khung hình mỗi giây (10 trở xuống để giảm tãi CPU)

+ Ghi các lệnh trên vào file vrec

# đặt thuộc tinh thực thi
chmod +x /path/to/vrec

Bước 2: Tìm chuyển động bằng script mdet

+ Script mdet đọc file video từ thư mục tạm (mặc định là /tmp/vrec.clip), dùng filter scdet để phát hiện có thay đổi trong các khung hình vượt ngưỡng định trước, từ đó giả định là có chuyển động, sau đó dời clip đến thư mục lưu trữ hoặc xóa đi nếu không phát hiện được chuyển động. mdet có thể chạy song song nhiều bản sao tùy vào tài nguyên của máy, mặc định chỉ chạy một instant.

# sử dụng
mdet -dir move_clip_to [-tmp temp_dir][-one 1]

# thí dụ
mdet -dir /mnt

+ Nội dung clip

#!/bin/bash
echo "Motion detection, $(basename $0) - version 20220131"
echo -e "LNT <lnt@lyle.info>\n"
eMsg(){ echo -e "Tìm chuyển động từ video clip\n$(basename $0) -dir move_clip_to [-tmp temp_dir] [-one 1]"; exit 1; }
while [ "$1" != "" ]; do
 case $1 in
  -dir) shift;dir="$1";;
  -tmp) shift;tmp="$1";;
  -one) shift;one="$1";;
     *) eMsg
 esac
 shift
done
[ ! "$tmp" ] && tmp="/tmp/vrec.clip"
[ ! -e "$tmp" ] && eMsg
[ -z "$one" ] && one=1
if [[ "$one" ]]; then
  PID="/var/lock/$(basename $0).pid"
  rm_pid(){ rm -f "$PID"; }
  [ -f "$PID" ] && kill -0 "$(cat $PID)" &> /dev/null && exit 2
  trap rm_pid EXIT
  echo $$ > "$PID"
fi
while true; do
  [ -z "$(ls -A $tmp/*.mkv)" ] && { echo '» No more files, wait a few seconds...'; sleep 30; continue; }
  for f in $tmp/*.mkv; do
    n=$(basename $f)
    echo -n "» $n: "
    [ -r $f ] || { echo '- skip'; continue; }
    prx=${n%%_*}; [ "$n" = "$prx" ] && prx=camera; target="${dir}/${prx}"; [ -d $target ] || mkdir -p $tatget
    ffmpeg -hide_banner -threads 2 -r 7 -i $f -vf scdet=s=1:t=1.03,scale=320:-1,tile=2x2:padding=3 -frames:v 1 -q:v 2 $target/${n%.*}.jpg 2>&1 | grep -q 'nothing' && { rm -f $f; echo $n >> $target/rm.txt; echo '- deleted'; } || { mv -f $f $target; echo '+ motion detected'; }
  done
done

Các option của ffmpeg

  • vf: video filter.
  • filter scdet: t ngưỡng thay đổi của khung hình, từ 0.6 đến 2
  • filter scale: thay đổi kích thước khung hình
  • filter title: ghép các khung hình liền nhau

+ Ghi các lệnh trên vào file mdet

# đặt thuộc tinh thực thi
chmod +x /path/to/mdet

Chú thích

  • Gọi các script trên khi khởi động. Clip từ camera được tạo liên tục. Nếu phát hiện khung hình có thay đổi, chúng ta có một ảnh thunbnail cùng tên file clip, nếu không thì clip bị xóa. Tên clip bị xóa được ghi vào file rm.txt tại thư mục chứa các clip.
  • Mặc định trong script mdet, ffmpeg sử dụng tất cả CPU của máy, số thread chạy có thể lớn hơn 4 ở RPi 4. Chúng ta có thể giới hạn số thread chay dưới 4 để tránh quá tải cho RPi, tất nhiên công việc có thể chậm hơn một chút. Tuy nhiên khi CPU quá tải công việc lại chậm hơn bình thường!
# thời gian tìm chuyển động trong 10 clip
real    1m18.808s
user    2m27.286s
sys     0m1.452s
Giới hạn threads=2
  • Đặt lịch để script vrec ghi hình giúp giảm số lượng clip ghi trên đĩa, đồng thời giúp script mdet có thời gian xử lý hết các clip tồn đọng, tuy rằng mdet làm việc nhanh hơn vrec vài lần.
# crontab
@reboot /path/to/vrec -url '<rstp_url_imou>' -cam floor
@reboot /path/to/mdet -dir /mnt
# ghi hình từ camera imou, từ 20:00 đến 6:00
00 20 * * * /path/to/vrec -url '<rstp_url_imou>' -cam floor -end 6:00
# ghi hình từ camera hik, từ 6:00 đến 20:00
00 06 * * * /path/to/vrec -url '<rstp_url_hik>' -cam office -end 20:00
# phát hiện chuyển động
@reboot /path/to/mdet -dir /mnt
  • Trong 2 bước trên chỉ có bước 2 chạy mdet tốn nhiều CPU nhưng làm việc tuần tự trên từng clip. Điều này cho phép RPi 4 quản lý đến trên chục camera nếu đặt lịch thích hợp.

Comments Off on Ghi hình chuyển động #2: ffmpeg

Filed under Software

Comments are closed.