ffmpeg - 如何并行转换大量文件?

ffmpeg - How to convert massive amounts of files in parallel?

我需要转换大约 1.5TiB 或 flac 或 wav 格式的音频文件。它们需要转换成mp3文件,保留重要的元数据和封面艺术等,比特率需要320k。

仅此一项就很简单:

ffmpeg -i "$flacFile" -ab 320k -map_metadata 0 -id3v2_version 3 -vsync 2 "$mp3File" < /dev/null

但问题是让它变得更快。上面的命令只使用了 CPU 的 12.5%。我宁愿使用 80%。所以我玩弄了线程标志,但它并没有使它变快或变慢:

ffmpeg -i "$flacFile" -ab 320k -map_metadata 0 -id3v2_version 3 -vsync 2 -threads 4 "$mp3File" < /dev/null

但它只使用了我的 CPU 13%。我认为它只使用一个线程。我的 CPU 有 8 个物理内核顺便说一下(+ 超线程)。

所以我现在的想法是以某种方式同时拥有多个 ffmpeg 运行ning 实例,但我不知道如何正确地做到这一点。

这是我当前的脚本,用于从一个目录(递归地)获取所有 flac/wav 文件,并将它们转换为具有完全相同结构的新目录中的 mp3 文件:

#!/bin/bash

SOURCE_DIR="/home/fedora/audiodata_flac"
TARGET_DIR="/home/fedora/audiodata_mp3"

echo "FLAC/WAV files will be read from '$SOURCE_DIR' and MP3 files will be written to '$TARGET_DIR'!"
read -p "Are you sure? (y/N)" -n 1 -r
echo    # (optional) move to a new line
if [[ $REPLY =~ ^[Yy]$ ]] ; then # Continue if user enters "y"

    # Find all flac/wav files in the given SOURCE_DIR and iterate over them:
    find "${SOURCE_DIR}" -type f \( -iname "*.flac" -or -iname "*.wav" \) -print0 | while IFS= read -r -d '' flacFile; do
        if [[ "$(basename "${flacFile}")" != ._* ]] ; then # Skip files starting with "._"
            tmpVar="${flacFile%.*}.mp3"
            mp3File="${tmpVar/$SOURCE_DIR/$TARGET_DIR}"
            mp3FilePath=$(dirname "${mp3File}")
            mkdir -p "${mp3FilePath}"
            if [ ! -f "$mp3File" ]; then # If the mp3 file doesn't exist already
                echo "Input: $flacFile"
                echo "Output: $mp3File"
                ffmpeg -i "$flacFile" -ab 320k -map_metadata 0 -id3v2_version 3 -vsync 2 "$mp3File" < /dev/null
            fi
        fi
    done
fi

我的意思是我想我可以将 & 附加到 ffmpeg 命令,但这会导致成千上万的 ffmpeg 实例同时 运行,这太多了。

像这样:

#!/bin/bash

SOURCE_DIR="/home/fedora/audiodata_flac"
TARGET_DIR="/home/fedora/audiodata_mp3"
export SOURCE_DIR
export TARGET_DIR

doone() {
    flacFile=""
    if [[ "$(basename "${flacFile}")" != ._* ]] ; then # Skip files starting with "._"
        tmpVar="${flacFile%.*}.mp3"
        mp3File="${tmpVar/$SOURCE_DIR/$TARGET_DIR}"
        mp3FilePath=$(dirname "${mp3File}")
        mkdir -p "${mp3FilePath}"
        if [ ! -f "$mp3File" ]; then # If the mp3 file doesn't exist already
            echo "Input: $flacFile"
            echo "Output: $mp3File"
            ffmpeg -i "$flacFile" -ab 320k -map_metadata 0 -id3v2_version 3 -vsync 2 "$mp3File" < /dev/null
        fi
    fi
}

export -f doone

# Find all flac/wav files in the given SOURCE_DIR and iterate over them:
find "${SOURCE_DIR}" -type f \( -iname "*.flac" -or -iname "*.wav" \) -print0 |
  parallel -0 doone