Differences

This shows you the differences between two versions of the page.


Previous revision
linux_faq:bash_script_to_batch_convert_video_files_to_h265 [2019/11/15 13:00] (current) – [Описание скрипта] admin
Line 1: Line 1:
 +====== to do ======
 +1. добавить вывод даты/времени в начале конвертирования каждого файла. бывает, что handbrake зависает и непонятно долго ли он уже мурыжит этот файл.\\
 + 
 +3. Сделать проверку/сравнение длительности потока исходного и конечного файла. Просто я столкнулся с тем, что в отсутствие кодеков, handbrake создает файл без видеопотока, но ошибок не сообщает. 
 +  ffprobe videofile.avi -v error -select_streams 0 -show_entries stream=duration
 +4. Сделать проверку и исправление исходного файла, в случае, когда не совпадает длительность потока. \\
 +5.Сделать автоматическое определение необходимого уровня качества, в зависимости от разрешения и частоты кадров потока. https://handbrake.fr/docs/en/latest/workflow/adjust-quality.html . Для ролика 1920*1080*60 мне кажется хорошим значение параметра -q=24. Битрейт в этом случае составляет 9-10 мегабит в секунду. При больших значниях - становятся заметны артефакты (на паузе на границах объектов, при воспроизведении - меньше). Короче - надо тестировать.  
 +====== Для чего ======
 +Мне захотелось оптимизировать свой видеоархив при помощи кодека **h265**, который позволяет существенно сэкономить место на диске практически без потери качества видеоматериалов. \\
 +
 +====== Применяемые средства ======
 +Для конвертации я буду использовать **HandBrakeCLI** под **Ubuntu 16.04**. \\
 +Устанавливать **HandBrakeCLI** лучше из [[https://launchpad.net/~stebbins/+archive/ubuntu/handbrake-releases|репозитория разработчиков]]. Версия, которая есть в репозиториях **Ubuntu 16.04** старая и некорректно обрабатывает звук (он просто пропадает после конвертации). \\
 +Также понадобится **ffmpeg** для определения кодека (чтобы повторно не конвертировать файлы уже сконвертированные в **h265**) и работы с данными **EXIF**.
 +  sudo add-apt-repository ppa:stebbins/handbrake-releases
 +  sudo apt-get update
 +  sudo apt-get install handbrake-cli ffmpeg
 +
 +====== Описание скрипта ======
 +Скрипт адаптирован к именам файлов и папок, содержащим пробелы. \\
 +Скрипт формирует список файлов в папке и вложенных папках по типу **MIME** - **video**. \\
 +Затем, у каждого файла проверяется текущий видеокодек и если он не **HEVC (h265)**, то файл проверяется на наличие ошибок и если все все хорошо - конвертируется. Конвертированный  файл кладется с именем оригинального файла на то же место, где лежал оригинальный файл. \\
 +После конвертации в файле меняется значение **EXIF**-аттрибута **creation_date** на значение извлеченное из оригинального файла. \\
 +В качестве бонуса подсчитывается количество сэкономленного места. \\
 +Для работы нужно задать параметры - папку с исходными файлами, папку куда будут положены исходные файлы после конвертации, а также количество потоков для **HandBrakeCLI**, чтобы система оставалась юзабельной во время конвертации.
 +<code>
 +#!/bin/bash
 +
 +srcdir="/home/AUTOSYS/mike/Downloads/paan/"
 +oldfilesdir=`dirname "$srcdir"`/Unconverted_Video_`basename "$srcdir"`
 +filelist=`find $srcdir -type f -exec file -N -i -- {} + | sed -n 's!: video/[^:]*$!!p'`
 +logfile=$oldfilesdir/convertation_`date +"%d.%m.%y_%H-%M"`.log
 +threads=3
 +quality=30
 +
 +mkdir $oldfilesdir
 +oldsize=0; newsize=0; filesleft=0; corruptedfiles=0
 +filesleft=`echo "$filelist" | wc -l`
 +echo $filesleft "video files detected" | tee -a $logfile
 +while read -r file
 +do
 +echo "FILE - $file" | tee -a $logfile
 +echo "ffprobe -show_format \"$file\" 2>&1 | grep Video: | grep hevc" | /bin/bash 1>/dev/null 2>/dev/null
 +if [ $? -eq 0 ]; then echo "This File is already in HEVC (h265)" | tee -a $logfile
 +else
 +        echo "Checking file for errors..." | tee -a $logfile
 +        echo "ffmpeg -i \"$file\" -v error -f null - 2>/dev/null 1>/dev/null" | /bin/bash 1>/dev/null 2>/dev/null
 +        if [ $? -ne 0 ]; then echo "File corrupted. Skipping..." | tee -a $logfile; (( corruptedfiles++ ))
 +        else
 +        filedir=`dirname "$file"`
 +        filesize=`du -k "$file" | cut -f1`
 +        filename=`basename "$file"`
 +        creationtime=`echo "ffprobe \"$file\" 2>&1 | grep -m 1 creation_time | sed -n -e 's/^.*creation_time\ *:\ *//p'" | /bin/bash`
 +        oldsize=$(( oldsize + filesize ))
 +        mkdir --parents "$oldfilesdir"/"$filedir"
 +        mv "$file" "$oldfilesdir"/"$filedir"
 +        echo "HandBrakeCLI -i \"$oldfilesdir\"/\"$file\" -o \"$filedir\"/\"$filename\" -e x265 -q $quality -E av_aac \
 +        --custom-anamorphic --keep-display-aspect -O -x threads=$threads 2> /dev/null" | /bin/bash
 +        handbrakereturn=$?
 +        if [ "$handbrakereturn" -eq 0 ]
 +                then echo "Convertation Done." | tee -a $logfile
 +                if [ "$creationtime" ]
 +                        then echo "Preserving Creation Time..." | tee -a $logfile
 +                        echo "ffmpeg -i \"$filedir\"/\"$filename\" -metadata:s:v:0 creation_time=\"$creationtime\" \
 +                        -metadata:s:a:0 creation_time=\"$creationtime\" -metadata creation_time=\"$creationtime\" \
 +                        -c copy \"$filedir\"/\"new_date_$filename\" 2> /dev/null" | /bin/bash
 +                        if [ $? -eq 0 ]; then echo "Done." | tee -a $logfile; else echo "Preservation of Creation Time metadata failed..." | tee -a $logfile; fi 
 +                        mv "$filedir"/"new_date_$filename" "$filedir"/"$filename"
 +                        else echo "Creation Time metadata not found" | tee -a $logfile
 +                fi
 +                else echo "Convertation ERROR. HandBrakeCLI Exit Code: $handbrakereturn" | tee -a $logfile
 +                mv "$oldfilesdir"/"$file" "$file"
 +        fi
 +        sync
 +        filesize=`du -k "$file" | cut -f1`
 +        newsize=$(( newsize + filesize ))
 +fi
 +fi
 +        (( filesleft-- ))
 +        echo $filesleft "files left to convert" | tee -a $logfile
 +done < <(echo "$filelist")
 +echo "Converted files old size - $oldsize Kb" | tee -a $logfile
 +echo "Converted files new size - $newsize Kb" | tee -a $logfile
 +echo "Saved Space - $(( $oldsize - $newsize )) Kb" | tee -a $logfile
 +echo "Corrupted files: $corruptedfiles" | tee -a $logfile
 +</code>
 +
 +Вот вариант, еоторый кладет новые файлы на место старых:
 +<code>
 +#!/bin/bash
 +
 +srcdir=/home/valusik/Изображения
 +#srcdir=/home/valusik/Изображения/test
 +filelist=`find $srcdir -type f -exec file -N -i -- {} + | sed -n 's!: video/[^:]*$!!p'`
 +logfile=$srcdir/convertation_`date +"%d.%m.%y_%H-%M"`.log
 +threads=3
 +
 +#mkdir $oldfilesdir
 +oldsize=0; newsize=0; filesleft=0; corruptedfiles=0
 +filesleft=`echo "$filelist" | wc -l`
 +echo $filesleft "video files detected" | tee -a $logfile
 +while read -r file
 +do
 +echo "FILE - $file" | tee -a $logfile
 +echo "ffprobe -show_format \"$file\" 2>&1 | grep Video: | grep hevc" | /bin/bash 1>/dev/null 2>/dev/null
 +if [ $? -eq 0 ]; then echo "This File is already in HEVC (h265)" | tee -a $logfile
 +else
 +        echo "Checking file for errors..." | tee -a $logfile
 +        echo "ffmpeg -i \"$file\" -v error -f null - 2>/dev/null 1>/dev/null" | /bin/bash 1>/dev/null 2>/dev/null
 +        if [ $? -ne 0 ]; then echo "File corrupted. Skipping..." | tee -a $logfile; (( corruptedfiles++ ))
 +        else
 +        filedir=`dirname "$file"`
 +        filesize=`du -k "$file" | cut -f1`
 +        filename=`basename "$file"`
 +        creationtime=`echo "ffprobe \"$file\" 2>&1 | grep -m 1 creation_time | sed -n -e 's/^.*creation_time\ *:\ *//p'" | /bin/bash`
 +        oldsize=$(( oldsize + filesize ))
 +        echo "Creation time - \"$creationtime\"; Filesize - \"$filesize\""
 +        mv "$file" "$file".old
 +        echo "HandBrakeCLI -i \"$file\".old -o \"$file\" -e x265 -q 20 -E av_aac \
 +        --custom-anamorphic --keep-display-aspect -O -x threads=$threads 4> /dev/null" | /bin/bash
 +        handbrakereturn=$?
 +        if [ "$handbrakereturn" -eq 0 ]
 +                then echo "Convertation Done." | tee -a $logfile
 +                rm -f "$file".old
 +                if [ "$creationtime" ]
 +                        file_extension="${file##*.}"
 +                        new_date_file="$file"_new_date."$file_extension"
 +                        then echo "Preserving Creation Time..." | tee -a $logfile
 +                        preservation_time_cmd="ffmpeg -i \"$file\" -metadata:s:v:0 creation_time=\"$creationtime\" \
 +                        -metadata:s:a:0 creation_time=\"$creationtime\" -metadata creation_time=\"$creationtime\" \
 +                        -c copy \"$new_date_file\" 2> /dev/null"
 +                        echo $preservation_time_cmd | /bin/bash
 +                        if [ $? -eq 0 ]; then echo "Done." | tee -a $logfile; mv "$new_date_file" "$file"; else echo "Preservation of Creation Time metadata failed..." | tee -a $logfile; fi
 +                else echo "Creation Time metadata not found" | tee -a $logfile
 +                fi
 +        else
 +                echo "Convertation ERROR. HandBrakeCLI Exit Code: $handbrakereturn" | tee -a $logfile
 +                mv "$file".old "$file"
 +        fi
 +        sync
 +        filesize=`du -k "$file" | cut -f1`
 +        newsize=$(( newsize + filesize ))
 +fi
 +fi
 +        (( filesleft-- ))
 +        echo $filesleft "files left to convert" | tee -a $logfile
 +done < <(echo "$filelist")
 +echo "Converted files old size - $oldsize Kb" | tee -a $logfile
 +echo "Converted files new size - $newsize Kb" | tee -a $logfile
 +echo "Saved Space - $(( $oldsize - $newsize )) Kb" | tee -a $logfile
 +echo "Corrupted files: $corruptedfiles" | tee -a $logfile
 +</code>