From 5f963736c1889709be4c69ff18084e6b6baa0255 Mon Sep 17 00:00:00 2001 From: Romain <> Date: Sat, 20 Mar 2021 17:12:55 +0100 Subject: [PATCH] Snapshot 0a563dd372 --- Licence note | 1 + README.md | 92 +++++++++++++++++++ ffmpeg_ein.sh | 241 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 334 insertions(+) create mode 100644 Licence note create mode 100644 README.md create mode 100644 ffmpeg_ein.sh diff --git a/Licence note b/Licence note new file mode 100644 index 0000000..01464de --- /dev/null +++ b/Licence note @@ -0,0 +1 @@ +If you find software that doesn’t have a license, that means you have no permission from the creators of the software to use, modify, or share the software. Although a code host such as GitHub may allow you to view and fork the code, this does not imply that you are permitted to use, modify, or share the software for any purpose. diff --git a/README.md b/README.md new file mode 100644 index 0000000..92b304c --- /dev/null +++ b/README.md @@ -0,0 +1,92 @@ +# FFmpeg Encode If Needed + +FFmpeg wrapper to encode upon some conditions. + +## Installation + +The script can be use "as is" with a command like `./ffmpeg_ein.sh` but +you could also "install" it with something like: + +```bash +mv ffmpeg_ein.sh /usr/bin/ffmpeg_ein +``` + +## Usage + +```bash +ffmpeg_ein [OPTION] [INPUT] +```` + +Refer to the `-h` argument to get the full list of options. + +## Description + +By default, FFmpeg encode a file regardless of the fact that the +original and selected codec are the same. Although it may be be a +desired outcome, it can lead to unnecessary operations losing time +and quality. + +This script allow the user to specified a desired codec for audio +and video, only if either of those are not present an encoding will be +triggered. + +For example if the desired codecs are aac/h264 and the source file is +wmav2/h264 then the file will be encoded with aac and the h264 stream +copied. + +| Desired ( `-a aac` ) | Source | FFmpeg option | +|:--------------------:|:------:|:----------------:| +| aac | aac | `-c:a copy` | +| aac | X | content of `-b` | + +**Warning**, by default no output is set, use the `-d` option or specify +the output file with `-o`. + +### Batch friendly + +This script is designed to be batch friendly allowing the sorting of +processed and sources files within multiple directories. The following +points also help in this endeavor: + ++ INPUT can be multiple files and/or directories. ++ The output files will be automatically named with the `-d` option. ++ Errors generated by FFmepg are stored in a *[FILE].error.txt* file +alongside the outputted file. ++ Friendly statistics available if the verbose option is set to default +value. + +## Example + +```bash +ffmpeg_ein \ +-a aac -b "-c:a aac -b:a 48k" \ +-v h264 -u "-c:v libx264 -crf 23" \ +-d /tmp/ \ +[FILE] +``` + +Will result in the encoding of the file(s) [FILE] if either their audio +codec is not "aac" or their video codec is not "h264". The result +encoded file(s) (if created) will have the same name as the original(s) +and be placed in the "/tmp" folder. + +| Desired | Source | FFmpeg option | +|:---------:|:---------:|:--------------------------------------:| +| aac/h264 | aac/h264 | -c:a copy -c:v copy | +| aac/h264 | X/h264 | -c:a aac -b:a 48k -c:v copy | +| aac/h264 | aac/X | -c:a copy -c:v libx264 -crf 23 | +| aac/h264 | X/X | -c:a aac -b:a 48k -c:v libx264 -crf 23 | + +## Limitations / Future roadmap + ++ Only the first audio and video streams are accounted for during codec +comparaison. ++ Only the codecs are checked so it's not possible to trigger encoding +on other parameters like bitrate or length. ++ The comparaison doesn't allow for multiple value (example compare +source against h264 and mpeg4) ++ No "-n, --dry-run" option exist to perform a trial run with no +changes made. ++ Some assumptions are made regarding the fact that the user will +provide valid input (valid FFmpeg arguments; read/write access to +specified files and folders; ...) diff --git a/ffmpeg_ein.sh b/ffmpeg_ein.sh new file mode 100644 index 0000000..e5353d9 --- /dev/null +++ b/ffmpeg_ein.sh @@ -0,0 +1,241 @@ +#!/bin/bash +function displayHelp { + echo -e "Encode If Needed is a warper for FFmpeg to encode upon some conditions." + echo -e "USAGE: ein [OPTION] [INPUT]" + echo -e "DESCRIPTION:" + echo -e "By default, FFmpeg encode a file regardless of the fact that the" + echo -e "original and selected codec are the same. Although it may be be a" + echo -e "desired outcome, it can lead to unnecessary operations losing time" + echo -e "and quality." + echo -e "This script allow the user to specified a desired codec for audio" + echo -e "and video, only if either of those are not present an encoding will be" + echo -e "triggered." + echo -e "Example if the desired codecs are aac/h264 and the source file is wmav2" + echo -e "/h264 then the file will be encoded with aac and the h264 stream copied." + echo -e "Warning, by default no output is set, use the \"-d\" option or specify" + echo -e "the output file with \"-o\"." + echo -e "Errors generated by FFmpeg will be put in [INPUT].error.txt" + echo -e "[INPUT] can be one or multiple file(s) or directory(ies)." + echo -e "OPTION:" + echo -e "-a\tDesited audio codec like aac." + echo -e "-v\tDesited video codec like aac." + echo -e "-b\tArgument pass to FFmpeg if the audio codec specified by \"-a\"" + echo -e "\tdon't match." + echo -e "-b\tArgument pass to FFmpeg if the video codec specified by \"-a\"" + echo -e "\tdon't match." + echo -e "-d\tOptional output directory for the encoded file(s)." + echo -e "\tThe original filename will be used." + echo -e "\tMake sure the directory exist and that you have write access." + echo -e "-e\tUsed with \"-d\" to specified the container output file." + echo -e "\tBy default mkv will be used." + echo -e "-k\tOptional directory for original files kept \"as is\"." + echo -e "-f\tOptional directory for original files processed." + echo -e "-q\tReduce the verbosity." + echo -e "-h\tDisplay this help." + echo -e "EXAMPLE:" + echo -e "\t-a aac -b \"-c:a aac -b:a 48k\" \\" + echo -e "\t-v h264 -u \"-c:v libx264 -crf 23\" \\" + echo -e "\t-d /tmp/ \\" + echo -e "\t[FILE]" + echo -e "Will result in the encoding of the file(s) [FILE] if either their audio" + echo -e "codec is not \"aac\" or their video codec is not \"h264\". The result" + echo -e "encoded file(s) (if created) will have the same name as the original(s)" + echo -e "and be placed in the \"/tmp\" folder." +} + +#"codec" will always matching thus allowing to specified only audio or video codec +audioCodecDesired="codec" +videoCodecDesired="codec" +audioParameter="" +videoParameter="" +outputDirectory="" +outputExtension="mkv" +goodAsIsDirectory="" +processedDirectory="" +optionalParameter="" +verbose=1 +fileCount=0 +errorCount=0 +directoryCount=0 +unencodedCount=0 + +no_args=1 +while getopts a:v:b:u:d:e:o:k:f:qh option +do + case $option in + (a) + audioCodecDesired=$OPTARG;; + (v) + videoCodecDesired=$OPTARG;; + (b) + audioParameter=$OPTARG;; + (u) + videoParameter=$OPTARG;; + (d) + outputDirectory=$OPTARG;; + (e) + outputExtension=$OPTARG;; + (k) + goodAsIsDirectory=$OPTARG;; + (f) + processedDirectory=$OPTARG;; + (o) + optionalParameter=$OPTARG;; + (q) + verbose=0;; + (h) + (*) + displayHelp + exit;; + esac + no_args=0 +done +shift $((OPTIND -1)) + +function processFile { + basename=$(basename "$@") + verbose $basename + errorFile="$basename.error.txt" + #Retrieve audio and video codec for the first stream of each + aCodec=$(ffprobe -select_streams a:0 -show_entries stream=codec_name -i "$@" -v error|head -n 2|tail -n 1) + vCodec=$(ffprobe -select_streams v:0 -show_entries stream=codec_name -i "$@" -v error|head -n 2|tail -n 1) + + prefix="codec_name=" + + verboseAudio=$(echo -e "\t"$aCodec | sed -e "s/$prefix/Audio /") + verboseVideo=$(echo -e "\t"$vCodec | sed -e "s/$prefix/Video /") + + encode=0 + ffmpegCmd="" + if echo $aCodec|grep --quiet $audioCodecDesired; then + ffmpegCmd=$ffmpegCmd" -c:a copy" + verboseAudio=$verboseAudio" -> copy" + else + ffmpegCmd=$ffmpegCmd" "$audioParameter + encode=1 + verboseAudio=$verboseAudio" -> "$audioCodecDesired + fi + if echo $vCodec|grep --quiet $videoCodecDesired; then + ffmpegCmd=$ffmpegCmd" -c:v copy" + verboseVideo=$verboseVideo" -> copy" + else + ffmpegCmd=$ffmpegCmd" "$videoParameter + encode=1 + verboseVideo=$verboseVideo" -> "$videoCodecDesired + fi + + verbose "$verboseAudio" + verbose "$verboseVideo" + + if [[ $encode == 1 ]]; then + #Add optional arguments passed by user + if [ ! -z "$optionalParameter" ]; then + ffmpegCmd="$ffmpegCmd $optionalParameter" + fi + #Add directory "-d" if selected + if [ ! -z "$outputDirectory" ]; then + ffmpegCmd="$ffmpegCmd $outputDirectory/${basename%.*}.$outputExtension" + fi + + verbose "\tffmpeg arguments:"$ffmpegCmd + + FFREPORT=file="$errorFile":level=24 + ffmpeg -hide_banner -i "$@" $(echo "$ffmpegCmd") + + if [ -f "$errorFile" ]; then + errorCount=$((errorCount+1)) + fi + + # 2> "$basename".error.txt + if [ ! -z "$processedDirectory" ]; then + mv "$@" "$processedDirectory/" -v + if [ -f "$errorFile" ]; then + mv "$errorFile" "$processedDirectory/" -v + fi + fi + else + verbose "\tKeep original source." + unencodedCount=$((unencodedCount+1)) + if [ ! -z "$goodAsIsDirectory" ]; then + mv "$@" "$goodAsIsDirectory/" -v + fi + fi +} + +#Process user input that could be a file, a directory or anything really. +function processInput { + verbose "Input \"$@\"" + if [ -d "$@" ]; then + directoryCount=$((directoryCount+1)) + for inputInDir in "$@"/*; do + processInput "$inputInDir" + done + fi + if [ -f "$@" ]; then + fileCount=$((fileCount+1)) + processFile "$@" + fi +} + +function verbose { + if [[ $verbose == 1 ]]; then + echo -e "$@" + fi +} + +if [[ $no_args == 1 ]]; then + displayHelp +else + verbose "Configuration:" + verbose "\tAudio codec desired="$audioCodecDesired + verbose "\tVideo codec desired="$videoCodecDesired + verbose "\tAudio parameter="$audioParameter + verbose "\tVideo parameter="$videoParameter + verbose "\tOptional parameter="$optionalParameter + verbose "\tDirectory encoded file="$outputDirectory + verbose "\tEncoded file extension="$outputExtension + verbose "\tDirectory processed file="$processedDirectory + verbose "\tDirectory unmodified file="$goodAsIsDirectory + verbose "Number of inputs "$# + + if + ([ $audioCodecDesired = "codec" ] && [ $videoCodecDesired = "codec" ]) || + ([ -z "$audioParameter" ] && [ -z "$videoParameter" ]) + then + echo "At least one codec must be choosed." + exit + fi + + for input in "$@"; do + processInput "$input" + done + + #Display statistics + message="Processed "$fileCount" file" + if [ $fileCount -gt 1 ]; then + message=$message"s" + fi + if [ $directoryCount -gt 0 ]; then + message=$message" in $directoryCount director" + if [ $directoryCount -gt 1 ]; then + message=$message"ies." + else + message=$message"y." + fi + fi + encodedCount=$((fileCount-unencodedCount)) + if [ $encodedCount -gt 0 ]; then + message=$message"\n"$encodedCount" encoded." + fi + if [ $unencodedCount -gt 0 ]; then + message=$message"\n"$unencodedCount" kept \"as is\"." + fi + if [ $errorCount -gt 0 ]; then + message=$message"\n"$errorCount" error" + if [ $fileCount -gt 1 ]; then + message=$message"s" + fi + message=$message" reported by FFmpeg." + fi + verbose $message +fi \ No newline at end of file