FFmpeg可以一次编码多个文件吗?

时间:2016-12-27 13:37:38

标签: ffmpeg

我正在使用shell脚本对给定目录中的音频文件进行编码,并将.flac文件输出到另一个目录。我的概念证明逐个循环遍历每个文件并通过ffmpeg运行。我怀疑有一种方法可以在一个命令中将整个输入文件列表传递给ffmpeg,但是还没有能够弄明白。

我当前的版本如下:

#!/bin/sh

encode_album() {
    local artist=$1; shift
    local album=$1; shift
    local in_dir="${HOME}/Music/raw-imports/${artist}/${album}"
    local out_dir="${HOME}/Music/library/${artist}/${album}"

    mkdir -p "${out_dir}"

    find "${in_dir}" -type f -name *.aiff | while IFS= read -r in_file; do
        local track_name=$(basename "${in_file}" .aiff)
        local out_file="${out_dir}/${track_name}.flac"

        encode_track "${in_file}" "${out_file}"
    done
}

encode_track() {
    local in_file=$1; shift
    local out_file=$1; shift

    </dev/null ffmpeg -i "${in_file}" -codec:a flac "${out_file}"
}

encode_album "Rolf Lislevand" "La Mascarade"

这样可行,但是我需要逐个将这些文件提供给ffmpeg,还是能够接受一批文件并进行处理?

3 个答案:

答案 0 :(得分:3)

基本语法是

ffmpeg -i input1
       -i input2
       ...
       -i inputn
       -map 0 -f flac out1.flac
       -map 1 -f flac out2.flac
       ...
       -map n-1 -f flac outn.flac

答案 1 :(得分:-1)

如果有帮助,您可以使用或修改以下脚本。 ffmpeg内置的批处理语法对于大量文件来说可能会有点笨拙,所以我在尝试使用它时不会看到很多好处而不是逐个循环,这样脚本执行一些默认情况下无法在ffmpeg中完成的操作,包括目标位置的自动递归和目录结构重新创建。

完全披露,我是作者。

https://github.com/clone206/ffmpeg-batch-audio-resampler

发布时的代码:

#!/bin/bash

########################
# CONSTANTS
########################
# The requested output sample rate.
MAX_SR=$1
# The specified output file extension
OUTFILE_EXT=$2
# The specified volume boost for output file
VOL_BOOST=$3
# Don't go below this sample rate:
MIN_SR=44100
# The directory to output converted files to. Use trailing slash. Needs manual escaping (not quotes)
# if this is customized and there are special chars (spaces, etc) in the pathname supplied, eg: 
# OUT_DIR=~/Music/iTunes/iTunes\ Media/Automatically\ Add\ to\ iTunes.localized/
OUT_DIR=~/batch_resampled/

########################
# UTILITY FUNCTIONS
########################
# Send string to stderr
err() {
    >&2 echo "$1"
}

# Display usage info
print_usage() {
    err ""
    err "USAGE: "$(basename $0)" <sample_rate> <outfile_extension> [vol_adjust_db]"
    err ""
    err "Converts all supported audio files in the current directory to the format corresponding "
    err "to the given file extension (don't include the dot), at the speciied sample rate (in Hz). "
    err "To specify a maximum output sample rate, where any input file of a greater rate gets downsampled "
    err "to the nearest even multiple of either 44100 or 48000, add an 'm' to the end of the number, "
    err "eg. '96000m'. If an input file has a sample rate that is already below this, it will not be upsampled. "
    err ""
    err "An optional volume adjust (in dB) can be given (positive number for boost, "
    err "negative for cut). "
    err ""
    err "Renames file basenames on conversion and doesn't re-convert already "
    err "converted files on subsequent runs."
    err ""
    err "Supported infile types: flac,dsf,dff,wav,aiff,m4a,mp3"
    err "Supported outfile types: flac,wav,aiff,m4a(alac),mp3"
    err ""
}

########################
# ARGS CHECKING
########################
if [[ -z "$OUTFILE_EXT" || $OUTFILE_EXT == "dsf" || $OUTFILE_EXT == "dff" ]]
then
    print_usage
    exit 1
fi

########################
# GLOBALS
########################
# Added to the end of the file basename
suffix=""
# Args to pass to the resampler
filter_args=""
pre_filter_args=""
# Args for the output file, like codec
out_args=""
# Volume filter level. Setting to 0dB explicitly seems to prevent replaygain-related clipping.
# Also comes in handy for boosting DSD file levels on conversion to PCM.
vol_level=""
# The sample rate of each input file
infile_sr=""
# The sample format of each input file
infile_sfmt=""
# Output file name
output_file=""
# Level of any errors encountered.
error_level=0
# Lowest factor of input file
lowest_factor=0
# Destination sample rate of a given output file
dest_sr=$MAX_SR

########################
# FUNCTIONS
########################
# Recalculate sample rate to nearest even multiple
# of 44.1/48K, depending on the input file's sample rate and the max sample rate given
recalc_sr() {
    infile_sr=$1
    lowest_factor=$2
    max_sr=$(echo $MAX_SR | sed 's/m$//')
    dest_sr=0

    # Make sure our output sample rate is a multiple of either 44.1K or 48K
    if [[ ! $(($max_sr % 44100)) -eq 0 && ! $(($max_sr % 48000)) -eq 0 ]]
    then
        err "ERROR: Only even multiples of 44100 and 48000 are allowed to be specified for maximum output sample rates!"
        return 2
    fi

    # If the input is already at or below the specified max...
    if [[ $infile_sr -le $max_sr ]]
    then
        # Nothing to calculate. Keep sr the same
        dest_sr=$infile_sr

        err "INFO: Input sample rate <= output rate. Will not be upsampled."
        echo "$dest_sr"
        return 0
    # If user specified the min sr as the max
    elif [[ $max_sr -eq $MIN_SR ]]
    then
        # Downsample even if not even multiple
        dest_sr=$max_sr

        err "INFO: No even multiple available below minimum rate of ${MIN_SR}, so downsampling to ${MIN_SR}."
        echo "$dest_sr"
        return 0
    else
        # Find closest even multiple below the max. Takes advantage of the dropping of decimals by bash for easy math.
        dest_sr=$(( ($max_sr/$lowest_factor)*$lowest_factor ))

        err "INFO: Downsampling to even multiple of ${lowest_factor}"
        echo "$dest_sr"
        return 0
    fi
}

# Takes input file sample rate as the only param.
# Determines whether this is an even multiple or 44.1K or 48K
# Delegates to the recalc_sr function and passes through the results via echo's
find_lowest_factor() {
    infile_sr=$1

    if [[ $infile_sr && $(($infile_sr % 44100)) -eq 0 ]]
    then
        echo "44100"
        return 0
    elif [[ $infile_sr && $(($infile_sr % 48000)) -eq 0 ]]
    then
        echo "48000"
        return 0
    else
        err "ERROR: Only multiples of 44100 and 48000 are allowed for input sample rates when in maximum mode!"
        return 1
    fi
}

# Takes an input file as the only argument and converts it
# based on certain conditions, or returns an error if necessary
conv() {
    # The input file with "./" stripped from the beginning
    item=${1#./}

    err "item: $item"

    # Get some specifics about this input file
    # To see all info about a particular input file on the command line, type ffprobe -v error -show_streams <file>
    infile_sfmt=$(ffprobe -v error -select_streams a:0 -show_entries stream=sample_fmt -of default=noprint_wrappers=1:nokey=1 "$item" \
        | xargs echo -n)
    infile_sr=$(ffprobe -v error -select_streams a:0 -show_entries stream=sample_rate -of default=noprint_wrappers=1:nokey=1 "$item" \
        | xargs echo -n)

    # If user appended an "m" for "maximum" to the end of
    # the sample rate param, find nearest even multiple of
    # this infile's sample rate
    if [[ $MAX_SR =~ m$ ]]
    then
        err "INFO: Maximum sample rate specified for output. Recalculating destination sample rate"

        # Get lowest factor from infile sr and skip this file on error
        if ! lowest_factor=$(find_lowest_factor $infile_sr)
        then
            error_level=1
            err "SKIPPING ${item}..."
            return 0
        fi

        # Get destination sample rate for this file. Stop all conversion on error
        if ! dest_sr=$(recalc_sr $infile_sr $lowest_factor)
        then
            error_level=2
            err "FATAL"
            return 1
        fi
    fi

    # Append a marker to the end of the output filename indicating new sample rate
    suffix="_ff"$(($dest_sr / 1000))"k"
    filter_args="aresample=resampler=soxr:precision=32:dither_method=triangular:osr=${dest_sr}"

    # Double pass for signed to signed resampling, as ffmpeg has some kind of problem
    # with this while setting the output sample rate of a signed audio codec type w sox. 
    # This way it gets double dithered with sox, which seems to be the best way of 
    # avoiding the bug, which causes pops in the converted audio.
    if [[ $infile_sfmt =~ ^s[0-9]+ && ($item =~ .flac$ || $item =~ .m[^\.]+$) ]]
    then
        pre_filter_args="aresample=resampler=soxr:precision=32:dither_method=triangular,"
    fi

    # Set the right output codec/sfmt for m4a, wav, aif, flac
    if [[ $OUTFILE_EXT == "aiff" ]]
    then
        out_args="-acodec pcm_${infile_sfmt}be"
    elif [[ $OUTFILE_EXT == "wav" ]]
    then
        out_args="-acodec pcm_${infile_sfmt}le"
    elif [[ $OUTFILE_EXT == "m4a" ]]
    then
        out_args="-acodec alac"
    # For some reason (perhaps bc of the precision set in sox resampler),
    # signed pcm files are having their bit depth increased from 16 to 24 if we
    # don't specify to keep the sample format the same.
    elif [[ $OUTFILE_EXT == "flac" && $infile_sfmt =~ ^s[0-9]+ ]]
    then
        if [[ $infile_sfmt =~ p$ ]]
        then
            # Flac doesn't support the "p" variants of signed sample fmts
            infile_sfmt=${infile_sfmt%p}
        fi
        out_args="-sample_fmt ${infile_sfmt}"
    fi

    # Prepend output directory, strip infile extension, add suffix, add outfile extension
    output_file=${OUT_DIR}${item%.*}${suffix}.${OUTFILE_EXT}

    # Skip if we've already created this output file and it's not zero-size
    if [[ -e $output_file && -s $output_file ]]
    then
        err "INFO: Output file already exists!"
        err "SKIPPING ${output_file}"
        return 0
    fi

    # Print some info about the output file to be created
    err "filter_args: $pre_filter_args$filter_args$vol_level"
    err "out_args: $out_args"
    err "output_file: $output_file"

    # Where the magic happens
    ffmpeg -y -i "$item" -af "$pre_filter_args$filter_args$vol_level" $out_args "$output_file"

    return 0
}

########################
# DRIVER CODE
########################
# Set volume boost/cut if given as argument
if [[ $VOL_BOOST ]]
then
    vol_level=",volume=${3}dB"
fi

# Recreate the directory structure from the input dir in the output dir
# First create the output directory in case there are no subdirectories in CWD
mkdir "$OUT_DIR"
find . -mindepth 1 -type d -exec mkdir -p -- "${OUT_DIR}{}" \;

# Recursively loop through all supported audio files, call the conv function on each
# Using brace expansion to search both the current dir, and subdirs
for item in ./{*,**/*}.{flac,dsf,dff,wav,aiff,m4a,mp3}
do 
    # Skip unexpanded globs
    if [[ -e $item ]] 
    then 
        # Reset some file-specific resampler args
        pre_filter_args=""
        out_args=""

        # If we got a fatal error on trying to convert this file...
        if ! conv "$item"
        then
            # Exit loop
            break
        fi
    fi
done

# Not doing anything specific with the various error levels for now. Just checking for existence.
if [[ $error_level -ne 0 ]]
then
    err ""
    err "WARNING: Errors were encountered. Some or all files may not have been converted"
    print_usage
    exit 1
else
    exit 0
fi

答案 2 :(得分:-2)

是的,您可以使用批处理。 有关详细信息,请参阅此处:

http://forum.videohelp.com/threads/356314-How-to-batch-convert-multiplex-any-files-with-ffmpeg