使用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。