ffmpeg:检查视频文件夹与 bash 文件的一致性

ffmpeg: check video folder conformance with bash file

我写了一个小 bash 文件来读取文件夹、生成播放列表、连接、添加徽标并对大视频结果进行编码以准备好破折号,我想通过在所有视频之前检查来实现它一致性:如果它们具有相同的 fps、相同的分辨率、相同的时基等。 下面我的情况:

#!/bin/bash
# CONCAT DEMUXER
#This demuxer reads a list of #files and other directives from a text file and demuxes them one after the other, as if #all their packets had been muxed together. All files must have the same streams (same #codecs, same time base, etc.) but can be wrapped in different container formats.

times=()
for f in *.mp4; do
    _t=$(ffmpeg -i "$f" 2>&1 | grep "Duration" | grep -o " [0-9:.]*, " | head -n1 | tr ',' ' ' | awk -F: '{ print ( * 3600) + ( * 60) +  }')
    times+=("$_t")
done
TOTALDURATION=$( echo "${times[@]}" | sed 's/ /+/g' | bc )


printf "file '%s'\n" *.mp4 > playlist.txt
ffmpeg -auto_convert 1 -f concat -safe 0 -i playlist.txt -c:a aac -b:a 384k -ar 48000 -ac 2 -async 1 -c:v libx264 -x264opts 'keyint=50:min-keyint=50:no-scenecut' -r 25 -b:v 2400k -maxrate 2400k -bufsize 1200k -vf "scale=-1:432" -vf "movie=stable.png [watermark]; [in][watermark] overlay=main_w-overlay_w-10:10 [out]" -t $TOTALDURATION out.mp4
#clear
echo “VIDEO CONCAT COMPLETED”

例如下面我发现这个 bash 计算文件夹视频的总持续时间

times=()
for f in *.mp4; do
    _t=$(ffmpeg -i "$f" 2>&1 | grep "Duration" | grep -o " [0-9:.]*, " | head -n1 | tr ',' ' ' | awk -F: '{ print ( * 3600) + ( * 60) +  }')
    times+=("$_t")
done
TOTALDURATION=$( echo "${times[@]}" | sed 's/ /+/g' | bc )

我想在处理之前检查视频是否具有相同的 fps 和相同的分辨率 谢谢 马西莫

Bash

有人在此问题中发布了一个获取视频时长的示例: ffmpeg how do I get the duration onto the node.js?

他的做法是:

ffmpeg -i input.mp4 2>&1 | grep Duration | cut -d ' ' -f 4 | sed s/,//

在我看来你应该使用 ffprobe 而不是 ffmpeg。更好的语法:

ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 input.mp4

可以在这里找到很好的例子:https://trac.ffmpeg.org/wiki/FFprobeTips

您可以将第一个视频的结果保存在一个变量中,并将所有其他视频与第一个视频进行比较。 如果需要可以给你更多的例子。

但我可以推荐另一种方式:nodejs

NodeJS

如果你熟悉JavaScript,写一个NodeJS脚本就可以了。我认为代码更容易编写。

您需要安装NodeJS。在您的项目中使用 npm install --save fluent-ffmpeg.

安装 fluent-ffmpeg

https://github.com/fluent-ffmpeg/node-fluent-ffmpeg

在您选择的 js 文件中编写您的脚本,并使用 node script.js.

从您的 bash 脚本中 运行 您的脚本

从您的视频文件中查找所需信息的代码片段可能如下所示:

var ffmpeg = require('fluent-ffmpeg');

ffmpeg.ffprobe('./input.mp4', function(err, metadata) {
    //console.dir(metadata); // all metadata
    console.log(metadata.format.duration);
    console.log(metadata.streams[0].width);
    console.log(metadata.streams[0].height);
    console.log(metadata.streams[0].r_frame_rate);
    console.log(metadata.streams[0].time_base);
    //... use first console.dir to get all possible informations
});

更新BASH

这里是一个完整的例子,将一个目录中的所有 mp4 视频转换到一个 output 目录并在之后连接它们:

#!/bin/bash

SCALE="768:432"
FPS="25"

mkdir -p ./output/

for i in *.mp4;
  do name=`echo $i | cut -d'.' -f1`;
  echo $name;
  ffmpeg -i "$i" -c:v libx264 -preset slow -crf 22 -vf scale=$SCALE -framerate $FPS -c:a aac -b:a 128k -f mp4 "output/${name}.mp4";
done

printf "file '%s'\n" ./output/*.mp4 > ./playlist.txt

ffmpeg -f concat -safe 0 -i ./playlist.txt -c copy ./output/concat.mp4
  • 宽度、高度和帧率被设置为一个定义的值以使连接成为可能
  • 视频编码的预设是,所以重新编码真的很慢;)
  • 必要时可以增加音频比特率,例如增加到 192k

嗨@Uwe 你在想什么?是原始的,可以在代码经济中编写更多但似乎有效:

#!/bin/bash

times=()
fps=()
for f in *.mp4; do

   _t=$(ffmpeg -i "$f" 2>&1 | grep "Duration" | grep -o " [0-9:.]*, " | head -n1 | tr ',' ' ' | awk -F: '{ print ( * 3600) + ( * 60) +  }')
    times+=("$_t")

    _f=$(ffmpeg -i "$f" 2>&1 | sed -n "s/.*, \(.*\) fp.*//p")
    fps+=(“$_f”)
done

TOTALDURATION=$( echo "${times[@]}" | sed 's/ /+/g' | bc )

tLen=${#fps[@]}

for tLen in "${fps[@]:1}"; do
    if [[ $tLen != ${fps[0]} ]]; then
        printf “WARNING: VIDEO’S FRAME-RATE (fps) ARE NOT EQUALS, THE PROCESS CAN’T START.”
        printf "%s[=10=]" "${fps[@]}" |
            sort -zu |
            xargs -0 printf " %s"
        printf "\n"
       exit 1
    fi
done

for f in *.mp4; do

DUR="$(ffmpeg -i "$f" 2>&1 | grep "Duration"| cut -d ' ' -f 4 | sed s/,// | sed 's@\..*@@g' | awk '{ split(, A, ":"); split(A[3], B, "."); print 3600*A[1] + 60*A[2] + B[1] }')"

FPS="$(ffmpeg -i "$f" 2>&1 | sed -n "s/.*, \(.*\) fp.*//p")"
FPSC=$( echo "($FPS+0.5)/1" | bc )

NAME=$(echo "$f" | cut -d'.' -f1)

GOP="$((FPSC*2))"
DURM="$((DUR%2))"
FDUR="$(($DUR-$DURM))"

ffmpeg -y -i "$f" -i logo.png -c:a aac -b:a 384k -ar 48000 -ac 2 -async 1 -c:v libx264 -x264opts keyint=$GOP:min-keyint=$GOP:no-scenecut -bf 0 -r $FPSC -b:v 4800k -maxrate 4800k -bufsize 3000k -profile:v main -t $FDUR -filter_complex "[0:v][1:v]overlay=main_w-overlay_w-10:10,scale=1920:1080,setsar=1" ${NAME}-1080.mp4


ffmpeg -y -i ${NAME}-720.mp4 -c:a aac -b:a 128k -ar 48000 -ac 2 -async 1 -c:v libx264 -x264opts keyint=$GOP:min-keyint=$GOP:no-scenecut -bf 0 -s 640x360 -r $FPSC -b:v 800k -maxrate 800k -bufsize 500k -profile:v main -t $FDUR ${NAME}-360.mp4
done

# with a bash for loop
for f in ./*1080.mp4; do echo "file '$f'" >> 1080list.txt; done
# or with printf
printf "file '%s'\n" ./*1080.mp4 > 1080list.txt

ffmpeg -f concat -safe 0 -i 1080list.txt -c copy 1080set.mp4

# with a bash for loop
for f in ./*360.mp4; do echo "file '$f'" >> 360list.txt; done
# or with printf
printf "file '%s'\n" ./*360.mp4 > 360list.txt

ffmpeg -f concat -safe 0 -i 360list.txt -c copy 360set.mp4

rm *1080.mp4
rm *.txt
rm *360.mp4

echo $TOTALDURATION