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 лучше из репозитория разработчиков. Версия, которая есть в репозиториях 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, чтобы система оставалась юзабельной во время конвертации.
#!/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
Вот вариант, еоторый кладет новые файлы на место старых:
#!/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
Discussion