【问题标题】:ffmpeg: check video folder conformance with bash fileffmpeg:检查视频文件夹与 bash 文件的一致性
【发布时间】:2018-02-01 08:19:31
【问题描述】:

我写了一个小的 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 ($1 * 3600) + ($2 * 60) + $3 }')
    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 ($1 * 3600) + ($2 * 60) + $3 }')
    times+=("$_t")
done
TOTALDURATION=$( echo "${times[@]}" | sed 's/ /+/g' | bc )

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

【问题讨论】:

    标签: bash video ffmpeg mpeg-dash


    【解决方案1】:

    重击

    有人在这个问题中发布了一个获取视频时长的示例: 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 脚本运行您的脚本。

    从视频文件中查找所需信息的 sn-p 如下所示:

    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,我多次听说过 node.js,但我从未研究过它,正如您所知,我想将所有必要的操作集成到 bash 文件中,以使视频 concat 准备好破折号,但我首先有关于音频视频同步的问题:因为并非所有视频都具有与视频一样长的音轨,所以我在 ffmpeg 上使用 -t 剪切它们以使其以 i 帧结束,但如果有更多比一个有这个问题的视频 concat 一起出现了音频视频同步的故障,我希望能解释一下我自己.. 出于这个原因,我认为暂时离开意图,对我来说太难自动化了
    • @MassimoVantaggio 不要放弃,这并不像你想象的那么难。无需验证相同的参数,只需重新编码所有视频。我发布了一个完整的示例 bash 脚本,请参阅更新的答案。用您提供的两个视频进行了测试,效果很好。只需在包含输入视频的目录中运行脚本,您就会在 output 文件夹中获得一个 concat.mp4。重新编写需要一些时间...
    • 你要我学习 node.js,我一完成媒体服务器的调试。谢谢 Uwe!
    【解决方案2】:

    嗨@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 ($1 * 3600) + ($2 * 60) + $3 }')
        times+=("$_t")
    
        _f=$(ffmpeg -i "$f" 2>&1 | sed -n "s/.*, \(.*\) fp.*/\1/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\0" "${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($1, A, ":"); split(A[3], B, "."); print 3600*A[1] + 60*A[2] + B[1] }')"
    
    FPS="$(ffmpeg -i "$f" 2>&1 | sed -n "s/.*, \(.*\) fp.*/\1/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
    

    【讨论】:

    • 您应该使用ShellCheck 检查您的脚本。它将帮助您发现问题。
    • 谢谢,我试了一下
    猜你喜欢
    • 2019-07-05
    • 1970-01-01
    • 2020-07-30
    • 2021-05-10
    • 2013-05-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多