我有一个带 RTSP 流的 IP 摄像头。我决定使用 ffmpeg(版本 3.2.14-1~deb9u1)在装有 Armbian 的 Odroid-N2 设备上录制流。我创建了一个 .sh 脚本,该脚本每分钟由 Croon 启动一次。它会检查所选摄像头的 ffmpeg 录制是否处于活动状态,并删除超过 7 天的文件:
#!/bin/bash
RecordPathAndFind='/home/mona/CameraRecordings/Camera1/'
SegmentTime=900
MinutesAfterDeleteOldFiles=10080
DaysSecurityLimit=31
tempoutput=$(ps aux | grep ffmpeg | grep $RecordPathAndFind)
if [ ${#tempoutput} -ge 5 ];
then
echo "FFMPEG with record command is already running. Skipping.\n"
else
echo "FFMPEG with record command is not running. Starting now...\n"
FFMPEGSTART=$(su - mona -c "cd /home/mona; /usr/bin/screen -dmS ffmpegcamera1 ffmpeg -rtsp_transport udp -i 'rtsp://admin:[email protected]:554/onvif1' -vcodec copy -c:a aac -map 0 -f segment -segment_time $(echo $SegmentTime) -segment_format mp4 -strftime 1 $(echo $RecordPathAndFind)%Y-%m-%d_%H-%M-%S.mp4")
fi
currenthourminutes=$(date +%M)
if [ "$currenthourminutes" == "00" ]; then
echo "Current Minute is 00. Checking for old files to delete.\n"
FILESDELETECOMMAND=$(find $(echo $RecordPathAndFind) -maxdepth 1 -type f -mmin +$(echo $MinutesAfterDeleteOldFiles) -mtime -$(echo $DaysSecurityLimit) -name '*.mp4' -ls -exec rm {} \;)
else
echo "Current Minute is NOT 00. Skipping.\n"
fi
脚本运行良好,但我担心此设备中的 SD 卡的使用寿命。我检测到 ffmpeg 正在不断写入 mp4 文件(文件大小一直在增长)。我认为如果 ffmpeg 等待 ~1MiB 再将其刷新到磁盘会更好。
我尝试了不同的 ffmpeg 设置(添加“blocksize”、“flush_packets”、“reorder_queue_size”和其他一些我现在想不起来了的设置),不幸的是,它什么也没改变。mp4 文件大小一直在增加(甚至增加了几 KB)。
第一个问题是:当 ffmpeg 一直在写入文件(当前环境)时,我是否应该担心 microSD 卡的寿命?
第二个问题是:还有其他我遗漏的 ffmpeg 优化设置吗?
如果我对 SD 卡寿命的担心是正确的,您能否推荐一些其他可以添加到我的脚本中(或在系统中进行更改)以延长用于录制目的的微型 SD 卡(或 USB 记忆棒)的使用寿命?我正在考虑使用 ramdisk,然后手动移动文件,但是如果系统重启、挂起或断电,这可能会导致文件丢失。
编辑:这是我用来检查文件大小变化的命令:
root@odroidn2:/home/mona/CameraRecordings/Camera1# printf "`date +"%m-%d-%y_%H:%S:%N"` `echo "Bytes:"` `stat --printf "%s\n" 2019-07-10_18-01-00.mp4`\n"
07-10-19_18:28:616623697 Bytes: 28443805
07-10-19_18:29:497492966 Bytes: 28453943
07-10-19_18:29:811378969 Bytes: 28458099
07-10-19_18:30:162532642 Bytes: 28724960
07-10-19_18:30:513455929 Bytes: 28730146
07-10-19_18:30:832042221 Bytes: 28734694
07-10-19_18:31:204934593 Bytes: 28739202
07-10-19_18:31:587997659 Bytes: 28744450
07-10-19_18:32:056139192 Bytes: 28750415
07-10-19_18:32:490959812 Bytes: 28755253
07-10-19_18:32:836352316 Bytes: 28757934
07-10-19_18:33:209513158 Bytes: 28762371
07-10-19_18:33:595689070 Bytes: 29021552
07-10-19_18:33:970968755 Bytes: 29026202
07-10-19_18:34:412742761 Bytes: 29031995
07-10-19_18:34:825309867 Bytes: 29037579
07-10-19_18:35:219852935 Bytes: 29043515
07-10-19_18:35:598878409 Bytes: 29046993
07-10-19_18:35:994067048 Bytes: 29051547
07-10-19_18:36:383952621 Bytes: 29055235
07-10-19_18:36:822644535 Bytes: 29331875
07-10-19_18:37:233137175 Bytes: 29336695
07-10-19_18:37:617277956 Bytes: 29343653
07-10-19_18:38:022776087 Bytes: 29348487
07-10-19_18:38:630347795 Bytes: 29357002
07-10-19_18:39:304204108 Bytes: 29364381
我可以看到文件大小在 1 秒内增加了 10138 字节(10 千字节)。
答案1
我通过使用由 croon 执行的自写脚本解决了我的问题:
* * * * * /home/mona/mona_ffmpeg_camera1_record.sh
#!/bin/bash
RecordPathAndFind='/home/mona/CameraRecordings/Tmps_Camera1/'
RecordPathCopyTo='/srv/dev-disk-by-uuid-XXX/Camera1_Recordings/'
#900 = 15 mins
SegmentTime=900
#7 days = 10080
MinutesAfterDeleteOldFiles=10080
DaysSecurityLimit=31
DeleteOldFiles=false
MakeRamDisk=true
CopyFilesToHDD=true
tempoutput=$(ps aux | grep ffmpeg | grep $RecordPathAndFind)
if [ ${#tempoutput} -ge 5 ];
then
echo "FFMPEG with record command is already running. Skipping.\n"
#Check if ffmpeg didn't hang:
PIDOfFFmpeg=$(ps aux | grep ffmpeg | grep $RecordPathAndFind | grep -v 'SCREEN' | awk '{print $2}')
echo "Found existing ffmpeg PID: $PIDOfFFmpeg.\n"
#ModificationTime=$(stat -L -c %Y /proc/$PIDOfFFmpeg/fd/4);
ModificationTime=$(stat -L -c %Y /proc/$PIDOfFFmpeg/fd/2);
echo "Last File modification time: $ModificationTime.\n"
if [ $ModificationTime -le $(($EPOCHSECONDS - 60)) ];
then
echo "Very old modification time. Restarting ffmpeg at next run.\n"
kill $PIDOfFFmpeg
#Backup existing files:
FILES_TO_COPY=$(ls -lt "$RecordPathAndFind" | awk '{ print $9 }' | tail -n +2)
FILES_COUNT=0
for f in $FILES_TO_COPY; do
echo "Currently processing file: $f"
FILES_COUNT=$FILES_COUNT+1
if [[ $FILES_COUNT -gt 0 ]]; then
echo "File not skipped, copying..."
cp "$RecordPathAndFind$f" "$RecordPathCopyTo"
echo "Removing file..."
rm "$RecordPathAndFind$f"
fi
done
fi
#test `stat -L -c %Y /proc/$PIDOfFFmpeg/fd/4` -ge $(($EPOCHSECONDS - 60)) || kill $PIDOfFFmpeg
else
if [ "$MakeRamDisk" = true ] ; then
echo "Mounting Ramdisk...\n"
umount -A $(echo $RecordPathAndFind)
umount -A $(echo $RecordPathAndFind)
umount -A $(echo $RecordPathAndFind)
umount -A $(echo $RecordPathAndFind)
umount -A $(echo $RecordPathAndFind)
umount -A $(echo $RecordPathAndFind)
umount -A $(echo $RecordPathAndFind)
umount -A $(echo $RecordPathAndFind)
umount -A $(echo $RecordPathAndFind)
umount -A $(echo $RecordPathAndFind)
mount -t tmpfs -o size=1024m tmpfs $(echo $RecordPathAndFind)
fi
if ping -c 1 192.168.1.111 &> /dev/null
then
echo "FFMPEG with record command is not running. Starting now...\n"
FFMPEGSTART=$(su - mona -c "cd /home/mona; /usr/bin/screen -dmS ffmpegcamera1 ffmpeg -rtsp_transport udp -i 'rtsp://admin:[email protected]:554/onvif1' -vcodec copy -c:a aac -map 0 -use_wallclock_as_timestamps 1 -f segment -segment_time $(echo $SegmentTime) -segment_format mp4 -strftime 1 $(echo $RecordPathAndFind)%Y-%m-%d_%H-%M-%S.mp4")
#echo $FFMPEGSTART
else
echo "ERROR. Pinging 192.168.1.111 failed. Will retry in 1 minute...\n"
fi
fi
currenthourminutes=$(date +%M)
if [ "$DeleteOldFiles" = true ] ; then
if [ "$currenthourminutes" == "00" ]; then
echo "Current Minute is 00. Checking for old files to delete.\n"
FILESDELETECOMMAND=$(find $(echo $RecordPathAndFind) -maxdepth 1 -type f -mmin +$(echo $MinutesAfterDeleteOldFiles) -mtime -$(echo $DaysSecurityLimit) -name '*.mp4' -ls -exec rm {} \;)
else
echo "Current Minute is NOT 00. Skipping.\n"
fi
fi
if [ "$CopyFilesToHDD" = true ] ; then
SIZE=$(du -s -B1 "$RecordPathAndFind" | cut -f1)
echo "Folder size is: $SIZE"
#900MiB
COPY_WHEN_SIZE_BYTES=943718400
#COPY_WHEN_SIZE_BYTES=401219456
if [[ $SIZE -gt $COPY_WHEN_SIZE_BYTES ]]; then
echo 'Folder Size is greater than: '$COPY_WHEN_SIZE_BYTES' bytes. Copying...'
FILES_TO_COPY=$(ls -lt "$RecordPathAndFind" | awk '{ print $9 }' | tail -n +2)
FILES_COUNT=0
for f in $FILES_TO_COPY; do
echo "Currently processing file: $f"
FILES_COUNT=$FILES_COUNT+1
if [[ $FILES_COUNT -gt 1 ]]; then
echo "File not skipped, copying..."
cp "$RecordPathAndFind$f" "$RecordPathCopyTo"
echo "Removing file..."
rm "$RecordPathAndFind$f"
fi
done
fi
fi
其工作方式如下:
- 该脚本检测另一个实例是否已在运行(这应该改进,但现在对我来说已经足够了)。
- 它将 15 分钟的部分记录到 RAM 磁盘(我设置了大约 1GB,因为我的设备有 4GB RAM)。
- 当 RAM 磁盘几乎已满时,它会将文件从中复制到旧的 2.5 英寸 HDD(通过 USB 连接),并启用省电功能,因此它每隔几个小时仅旋转几秒钟。
- 如果发生断电,这可能会导致 RAM 磁盘中的数据丢失,但我更喜欢这种方法,因为它非常安静,并且旋转的磁盘不会在晚上打扰我:)
我已经使用这种方法大约 2 年了,我很高兴,因为我没有损坏任何 SD 卡。