在Docker Swarm里运行ZoneMinder

分别使用ZoneMinder和Docker有些时日了,最近把两者结合起来,在容器里运行ZoneMinder。为了日后迁移方便,又把ZoneMinder程序容器放在Docker Swarm里管理,ZoneMinder使用的文件存放在NFS服务器上。

下面是启动服务的命令示例:

export ZM_NFS_SERVER=192.168.1.2 
export EVENT_NFS_SERVER=192.168.1.3
docker stack deploy -c zm-stack.yml -c zm-stack.prod.yml zm

配置文件参见https://github.com/wlcasper/zmdocker

另外zm-stack.override.yml也提供了映射本地目录的配置,既可用于本地开发测试,又可作为NFS服务器宕机时的临时替代方案。

使用ffmpeg压缩电视录像

使用Plex和HDHomeRun录制电视节目,文件都非常大,动则数GB,甚至十几个GB。为了节省空间,决定把录制的mpeg2.ts文件转换为x264格式的mp4文件。方式如下:

ffmpeg -i input.ts -c:v libx264 -c:a copy output.mp4

然而当我打算批处理所有ts文件时,发现两个小问题。首先,Plex存储的目录和文件名里有空格和括号,如Premier League Soccer (2019)/Season 2019/Premier League Soccer (2019) – 2019-10-19 05 00 00 – Crystal Palace vs. Manchester City.ts,没办法用for循环加find的方式:for ts in $(find . -name “*.ts”); do echo “$ts”; done。

搜索后学了一招复杂的处理方法:

find . -name "*.ts" -print0 |
while IFS= read -r -d '' ts

do
mp4filename="$(basename "$ts" .ts).mp4"
relative_filename="$(dirname "$ts")/$mp4filename"
if [ -e "$relative_filename" ]
then
echo "find $relative_filename. skip conversion."
continue
fi
echo -n "converting '$ts' to '$mp4filename'…"
ffmpeg -loglevel quiet -i "$ts" -c:v libx264 -c:a copy "$relative_filename" >/dev/null 2>&1
echo "done"
done

然而发现一个诡异的现象,这段脚本能够正常处理第一个文件,但是第二个文件的路径名前面几个字符被截断了,ffmpeg报错说找不到文件。调试加搜索后得知,ffmpeg在转换视频格式的时候,还会读取stdin,导致下一个文件的路径名前面几个字符被吃掉了,需要使用-nostdin选项来禁止这种行为。

find . -name "*.ts" -print0 |
while IFS= read -r -d '' ts
do
mp4filename="$(basename "$ts" .ts).mp4"
relative_filename="$(dirname "$ts")/$mp4filename"
if [ -e "$relative_filename" ]
then
echo "find $relative_filename. skip conversion."
continue
fi
echo -n "converting '$ts' to '$mp4filename'…"
ffmpeg -nostdin -loglevel quiet -i "$ts" -c:v libx264 -c:a copy "$relative_filename" >/dev/null 2>&1
echo "done"
done

压缩率因文件而异,总体来讲,从1.2TB降到400GB以下,约为原来的1/3。