Как стать автором
Обновить

Сжатие медиа для экономии места

Уровень сложностиПростой
Время на прочтение51 мин
Количество просмотров5.2K
Постер
Постер

Дисклеймер

  • Я не аудио-, видео-, картино-фил.

  • Методы сжатия используемые в этой статье с потерей качества. Но на мой личный взгляд почти незаметно. У вас может быть на этот счет другое мнение.

  • В статье могут присутствовать не точности.

  • Статья не покрывает все параметры и тонкости использования ffmpeg, magick и fish, за дополнительной информацией обращайтесь к их документациям или другим ресурсам.

Предисловие

Моя мотивация. Со временем у меня собралось огромное количество видео, картинок и музыки >100 ГБ и мне нужно это хранить и перемещать. И так как для меня увеличить электронное пространство не вариант, следовательно я решил сжать сами медиа файлы.

TL;DR

Вы наверняка имеете медиа файлы закодированные не самым эффективным кодеком (H.264, JPEG, MP3). И вы можете сократить их размер вплоть до 75% перекодировав медиа современными альтернативами (H.265, AVIF, OPUS).

Использовать для этого можно консольные утилиты ffmpeg и magick.

Вот скрипт который перекодирует все медиа файлы в директории современными кодеками.

Скрипт для конвертации медиа файлов
# ===================== Утилита сжатия медиа =============================
# Полнофункциональный инструмент для сжатия медиафайлов, поддерживающий аудио, видео и изображения
# Использует современные кодеки (Opus, HEVC, AVIF) с разумными настройками по умолчанию для качества/размера
# ==============================================================================

function compress_media
    # ===================== КОНСТАНТЫ ============================
    # ВНИМАНИЕ: Порядок имеет значение - индексы должны совпадать между этими двумя массивами
    set FILE_TYPES audio video image # Поддерживаемые типы медиа
    set FILE_TYPE_EXTENSIONS opus mp4 avif # Соответствующие расширения файлов для типов медиа

    # ПРИМЕЧАНИЕ: Вы можете использовать разные команды для ffmpeg
    # - ffmpeg - подробный вывод. Измените функцию compress_file для управления уровнем логирования
    # - ffmpeg-bar - индикатор прогресса
    set -g ffmpeg ffmpeg-bar
    # ===================== ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ ============================

    function bold -d "Применить жирное оформление к тексту"

        if not count $argv >/dev/null # Хак: Чтение из pipe в argv
            read --null --list --delimiter=\n argv
        end
        echo "\e[1m$argv\e[22m"
    end

    function yellow -d "Применить желтый цвет к тексту"
        if not count $argv >/dev/null # Хак: Чтение из pipe в argv
            read --null --list --delimiter=\n argv
        end
        echo "\e[0;33m$argv\e[39m"
    end
    function red -d "Применить красный цвет к тексту"
        if not count $argv >/dev/null # Хак: Чтение из pipe в argv
            read --null --list --delimiter=\n argv
        end
        echo "\e[0;31m$argv\e[39m"
    end

    function file_type -a file -d "Определить категорию типа файла из mime-типа"
        set -l mime_type (file --mime-type -b "$file")
        echo (string split '/' $mime_type)[1]
    end

    function compress_file -a input -a output -d "Сжать файл"

        switch (file_type $input)
            case audio
                # Сжатие аудио Opus с переменным битрейтом
                # ПРИМЕЧАНИЕ: Битрейт 64k обеспечивает хорошее качество для большинства контента
                $ffmpeg -i "$input" \
                    -c:a libopus -b:a 64k -vbr on \
                    -y "$output"

            case video
                # ПРОИЗВОДИТЕЛЬНОСТЬ:
                # - `nice -n 20 ` - снижение приоритета процесса
                # - `cpulimit -l 2` - ограничение использования ЦП. Для меня это занимает 90% ЦП
                sudo nice -n 20 cpulimit -l 2 -i \
                    $ffmpeg -i "$input" \
                    -c:v libx265 -crf 31 -vf "scale='min(iw,1024)':-2" \
                    -pix_fmt yuv420p \
                    -c:a aac -b:a 128k -vtag hvc1 \
                    -y "$output"
                # Если вы используете ffmpeg вместо ffmpeg-bar.
                # Добавьте эти опции для уменьшения вывода логов
                #  -loglevel error -x265-params log-level=error

            case image
                magick $input $output
        end

        tput smam # ВНИМАНИЕ: Ручной вызов tput для исправления отключения обертывания терминала из ffmpeg-bar
    end

    function is_compressed -a item -d "Проверить, содержит ли имя файла маркер '.compressed.'"
        string match -q '*.compressed.*' (path basename $item)
    end

    # ===================== Аргументы командной строки ============================
    argparse \
        h/help \
        'm/media-type=+' \
        't/target=' \
        k/keep-original \
        only-media \
        -- $argv

    if not string match -rq "GNU findutils" (find --version 2>/dev/null) # Требуется GNU find, а не BSD
        echo -e (red "Требуется GNU find, а не BSD.")
        return 1
    end
    if set -q _flag_h # Отобразить сообщение справки, если true
        echo "\
compress_media - Сжатие аудио, видео или изображений в указанных директориях

Использование:
  compress_media [ОПЦИИ]... [ИСТОЧНИКИ]...

Описание:
  Рекурсивно сжимает медиафайлы (аудио/видео/изображения) в указанных директориях.
  По умолчанию заменяет оригиналы сжатыми версиями (перемещает в корзину, если не используется -k).
  Используйте --target для сохранения структуры директорий в другом месте.

Опции:
  -h, --help                    Показать это сообщение справки и выйти
  -m, --media-type TYPE         ОБЯЗАТЕЛЬНО. Тип медиа для обработки (audio|video|image|all)
  -t, --target DIR              Директория вывода для сжатых файлов (сохраняет структуру). Относительный путь должен начинаться с `./`, иначе это будет `/`.
  -k, --keep-original           Сохранить оригинальные файлы (не перемещать в корзину)
  --only-media                  Обрабатывать только медиафайлы (не копировать другие файлы в целевую директорию)

Поведение:
  - При использовании --target:
    * Создает зеркальную структуру директорий в целевом месте
    * Копирует немедиафайлы без изменений, если не указано --only-media
    * Хранит сжатые версии медиафайлов в соответствующих директориях

  - Без --target:
    * Сжимает файлы на месте
    * Оригинальные файлы перемещаются в корзину, если не указано --keep-original

Поддерживаемые форматы:
  - audio: конвертируется в OPUS (64kbit/s, VBR)
  - video: конвертируется в H.265 MP4 (CRF 31, ширина 1024px)
  - image: конвертируется в AVIF с использованием настроек ImageMagick по умолчанию

Примеры:
  1. Сжатие аудиофайлов в текущей директории:
     compress_media -m audio

  2. Сжатие видео в ~/Videos, сохранение оригиналов, вывод в ~/Compressed:
     compress_media -m video -t ~/Compressed -k ~/Videos

  3. Сжатие изображений в ~/Pictures, обработка только медиафайлов:
     compress_media -m image --only-media -t ~/Compressed_Images ~/Pictures

Примечания:
  - Требуется ffmpeg, ImageMagick, trash и coreutils (для определения типа файла)
  - Вы должны использовать GNU find на MacOS. Сделайте `alias find=gfind` перед
    запуском скрипта
  - Сжатие видео использует ограничение ЦП для стабильности
  - Сжатые файлы получают расширение .compressed перед фактическим расширением
        "
        return 0
    end

    if not set -q _flag_m # Флаг media-type обязателен
        echo (red Ошибка: опция m/media-type не указана)
        return 1
    else
        for v in $_flag_m # Проверка значения флага media-type
            set ALLOWED_VALUES $FILE_TYPES all
            if not contains $v $ALLOWED_VALUES
                echo (red "Флаг media-type может содержать только значения \
                    из \"$ALLOWED_VALUES\", но не \"$v\".")
                return 1
            end
        end
        contains all "$_flag_m" # Если одно из значений "all"
        and set _flag_m $FILE_TYPES # то установить все типы файлов
    end

    # ===================== Обработка источника и цели ===================================
    if set -q argv[1] # Если есть позиционные аргументы, то они являются источниками
        set SOURCES $argv
    else # Иначе текущая директория является источником
        set SOURCES .
    end

    for source in $SOURCES # Проверка существования исходной директории
        if not test -d $source
            echo Исходная директория $source не существует.
            exit 1
        end
    end

    if set -q _flag_t # Создать целевую директорию, если указана и не существует
        set TARGET $_flag_t

        if not test -d $TARGET
            mkdir $TARGET
        end
    end

    # ===================== Основной цикл обработки ==============================
    for source in $SOURCES

        # Найти все файлы, исключив целевую директорию
        # Хак: `tail -n +2` обрезает исходную директорию (первую строку)
        for item in (find $source -path $TARGET -prune -o -print | tail -n +2)

            # ============= Обработка целевой директории =======================
            if set -q TARGET
                set target_item (string replace -r "^$source/?" "$TARGET/" "$item") # Путь к элементу относительно целевой директории вместо исходной

                test $item -ef $TARGET; and continue # Хак: `find` также перечисляет целевую директорию, если она в источнике, поэтому пропустить её

                if test -d "$item" # Директория
                    and not set -q _flag_only_media
                    echo -en "Создание директории: $(bold $target_item)"

                    test -d $target_item # Создать директорию, если не существует
                    and echo -e \t(yellow Директория (bold $target_item) уже создана)
                    or begin
                        echo
                        mkdir -p $target_item
                    end

                else if contains (file_type $item) $_flag_m # Не сжатый медиафайл
                    and not is_compressed $item

                    set output_extension compressed.$FILE_TYPE_EXTENSIONS[(contains -i (file_type $item) $FILE_TYPES)]
                    set output (path change-extension $output_extension $target_item)

                    set -q _flag_only_media # Хак: Убедиться, что целевая директория существует, если установлен флаг only media. Поскольку изначально структура директорий не создается
                    and mkdir -p (path dirname $output)

                    echo -en Обработка файла (bold $item) в (bold $output)
                    test -e $output
                    and echo -e \t(yellow Файл (bold $output) уже существует)
                    or begin
                        echo
                        compress_file $item $output
                    end

                else if not set -q _flag_only_media # Немедиа или сжатый медиафайл
                    echo -ne "Копирование файла: $(bold $item) в $(path dirname $target_item | bold)"

                    test -e $target_item
                    and echo -e \t(yellow Файл (bold $target_item) уже существует)
                    or begin
                        echo
                        cp "$item" "$target_item"
                    end
                end

                # ============= Обработка на месте ===============================
            else if contains (file_type $item) $_flag_m # Не сжатый медиафайл
                and not is_compressed $item
                set output_extension compressed.$FILE_TYPE_EXTENSIONS[(contains -i (file_type $item) $FILE_TYPES)]
                set output (path change-extension $output_extension $item)

                echo -ne Обработка файла (bold $item) в (path basename $output | bold)
                test -e $output
                and echo -e \t(yellow Файл (bold $item) уже существует)
                or begin
                    echo
                    compress_file $item $output
                    and not set -q _flag_k
                    and begin
                        echo Перемещение $item в корзину
                        trash $item # MacOS
                        # trash-put $item # Linux
                    end
                end
            end
        end
    end
end
На английском
# ===================== Media Compression Utility =============================
# A comprehensive media compression tool supporting audio, video and image files
# Uses modern codecs (Opus, HEVC, AVIF) with sensible defaults for quality/size
# ==============================================================================

function compress_media
    # ===================== CONSTANTS ============================
    # WARN: Order matters - indexes must match between these two arrays
    set FILE_TYPES audio video image # Supported media types 
    set FILE_TYPE_EXTENSIONS opus mp4 avif # Corresponding to media types file extensions

    # NOTE: You can use different command for ffmpeg
    # - ffmpeg - verbose output. Change compress_file function to controll loglevel
    # - ffmpeg-bar - progress bar
    set -g ffmpeg ffmpeg-bar
    # ===================== HELPER FUNCTIONS ============================

    function bold -d "Apply bold styling to text"

        if not count $argv >/dev/null # HACK: Read pipe to argv
            read --null --list --delimiter=\n argv
        end
        echo "\e[1m$argv\e[22m"
    end

    function yellow -d "Apply yellow color to text"
        if not count $argv >/dev/null # HACK: Read pipe to argv
            read --null --list --delimiter=\n argv
        end
        echo "\e[0;33m$argv\e[39m"
    end
    function red -d "Apply red color to text"
        if not count $argv >/dev/null # HACK: Read pipe to argv
            read --null --list --delimiter=\n argv
        end
        echo "\e[0;31m$argv\e[39m"
    end

    function file_type -a file -d "Determine file type category from mime-type"
        set -l mime_type (file --mime-type -b "$file")
        echo (string split '/' $mime_type)[1]
    end

    function compress_file -a input -a output -d "Compress file"


        switch (file_type $input)
            case audio
                # Opus audio compression with variable bitrate
                # NOTE: 64k bitrate provides good quality for most content
                $ffmpeg -i "$input" \
                    -c:a libopus -b:a 64k -vbr on \
                    -y "$output"

            case video
                # PERF:
                # - `nice -n 20 ` - lower priority of process
                # - `cpulimit -l 2` - limit CPU usage. For me it take 90% of CPU
                sudo nice -n 20 cpulimit -l 2 -i \
                    $ffmpeg -i "$input" \
                    -c:v libx265 -crf 31 -vf "scale='min(iw,1024)':-2" \
                    -pix_fmt yuv420p \
                    -c:a aac -b:a 128k -vtag hvc1 \
                    -y "$output"
                # If you use use ffmpeg instead ffmpeg-bar.
                # Add this options to reduce log output
                #  -loglevel error -x265-params log-level=error

            case image
                magick $input $output
        end

        tput smam # WARN: Manual tput call to fix disabling terminal wrapping from ffmpeg-bar
    end

    function is_compressed -a item -d "Check if filename contains '.compressed.' marker"
        string match -q '*.compressed.*' (path basename $item)
    end

    # ===================== Command Line Arguments ============================
    argparse \
        h/help \
        'm/media-type=+' \
        't/target=' \
        k/keep-original \
        only-media \
        -- $argv

    if not string match -rq "GNU findutils" (find --version 2>/dev/null) # Require GNU find not BSD
        echo -e (red "Required GNU find not BSD.")
        return 1
    end
    if set -q _flag_h # Display help message if true
        echo "\
compress_media - Compress audio, video, or image files in specified directories

Usage:
  compress_media [OPTIONS]... [SOURCES]...

Description:
  Recursively compresses media files (audio/video/image) in specified directories.
  By default replaces originals with compressed versions (moved to trash unless -k is used).
  Use --target to preserve directory structure in a different location.

Options:
  -h, --help                    Show this help message and exit
  -m, --media-type TYPE         REQUIRED. Media type to process (audio|video|image|all)
  -t, --target DIR              Output directory for compressed files (preserves structure). Relative path must start with `./` otherwise it will be `/`.
  -k, --keep-original           Keep original files (don't move to trash)
  --only-media                  Only process media files (don't copy other files to target)

Behavior:
  - When using --target:
    * Creates mirrored directory structure in target location
    * Copies non-media files unchanged unless --only-media is specified
    * Stores compressed versions of media files in corresponding directories
  
  - Without --target:
    * Compresses files in-place
    * Original files are moved to trash unless --keep-original is specified

Supported Formats:
  - audio: converts to OPUS (64kbit/s, VBR)
  - video: converts to H.265 MP4 (CRF 31, 1024px width)
  - image: converts to AVIF using ImageMagick default settings

Examples:
  1. Compress audio files in current directory:
     compress_media -m audio

  2. Compress videos in ~/Videos, keep originals, output to ~/Compressed:
     compress_media -m video -t ~/Compressed -k ~/Videos

  3. Compress images in ~/Pictures, only process media files:
     compress_media -m image --only-media -t ~/Compressed_Images ~/Pictures

Notes:
  - Requires ffmpeg, ImageMagick, trash, and coreutils (for file type detection)
  - You must use GNU find on MacOS. Make `alias find=gfind` before 
    running script
  - Video compression uses CPU limiting for stability
  - Compressed files get .compressed extension before the actual extension
        "
        return 0
    end


    if not set -q _flag_m # Flag media-type required
        echo (red Error: option m/media-type not specified)
        return 1
    else
        for v in $_flag_m # Validate value of flag media-type
            set ALLOWED_VALUES $FILE_TYPES all
            if not contains $v $ALLOWED_VALUES
                echo (red Flag media-type can contain only values \
                    from \"$ALLOWED_VALUES\", but not \"$v\".)
                return 1
            end
        end
        contains all "$_flag_m" # If one of the value "all" 
        and set _flag_m $FILE_TYPES # then set to all file types
    end

    # ===================== Source and Target Handling ===================================
    if set -q argv[1] # If there are positional args, then they are sources
        set SOURCES $argv
    else # Else current dir is source
        set SOURCES .
    end

    for source in $SOURCES # Verify source dir existance
        if not test -d $source
            echo Source directory $source not exist.
            exit 1
        end
    end

    if set -q _flag_t # Create target directory if specified and doesn't exist
        set TARGET $_flag_t

        if not test -d $TARGET
            mkdir $TARGET
        end
    end

    # ===================== Main Processing Loop ==============================
    for source in $SOURCES

        # Find all files while excluding target directory
        # HACK: `tail -n +2` trim source dir (first line)
        for item in (find $source -path $TARGET -prune -o -print | tail -n +2)

            # ============= Target Directory Processing =======================
            if set -q TARGET
                set target_item (string replace -r "^$source/?" "$TARGET/" "$item") # Path to item relative to target dir instead of source


                test $item -ef $TARGET; and continue # HACK: `find` also list target dir if it in source, so skip it


                if test -d "$item" # Dir
                    and not set -q _flag_only_media
                    echo -en "Creating directory: $(bold $target_item)"

                    test -d $target_item # Create dir if not exist
                    and echo -e \t(yellow Directory (bold $target_item) already created)
                    or begin
                        echo
                        mkdir -p $target_item
                    end


                else if contains (file_type $item) $_flag_m # Not compressed Media
                    and not is_compressed $item

                    set output_extension compressed.$FILE_TYPE_EXTENSIONS[(contains -i (file_type $item) $FILE_TYPES)]
                    set output (path change-extension $output_extension $target_item)


                    set -q _flag_only_media # HACK: Ensure output directory exists, when flag only media. Cause initialy dir structure not created
                    and mkdir -p (path dirname $output)


                    echo -en Processing file (bold $item) into (bold $output)
                    test -e $output
                    and echo -e \t(yellow File (bold $output) already exist)
                    or begin
                        echo
                        compress_file $item $output
                    end


                else if not set -q _flag_only_media # Non-media or compressed media
                    echo -ne "Copying file: $(bold $item) to $(path dirname $target_item | bold)"

                    test -e $target_item
                    and echo -e \t(yellow File (bold $target_item) already exist)
                    or begin
                        echo
                        cp "$item" "$target_item"
                    end
                end

                # ============= In-place Processing ===============================
            else if contains (file_type $item) $_flag_m # Not compressed media
                and not is_compressed $item
                set output_extension compressed.$FILE_TYPE_EXTENSIONS[(contains -i (file_type $item) $FILE_TYPES)]
                set output (path change-extension $output_extension $item)

                echo -ne Processing file (bold $item) into (path basename $output | bold)
                test -e $output
                and echo -e \t(yellow File (bold $item) already exist)
                or begin
                    echo
                    compress_file $item $output
                    and not set -q _flag_k
                    and begin
                        echo Move $item to trash
                        trash $item # MacOS
                        # trash-put $item # Linux
                    end
                end
            end
        end
    end
end

Установка зависимостей

### Debian/Ubuntu (apt)
sudo apt update && sudo apt install -y \
  ffmpeg imagemagick file trash-cli cpulimit findutils coreutils \
  ffmpeg-bar fish

### macOS (Homebrew)
brew install \
  ffmpeg imagemagick findutils coreutils cpulimit trash \
  ffmpeg-bar fish
# For GNU find (required)
echo "alias find=gfind" >> ~/.config/fish/config.fish

Введение

В настоящее время до сих пор распространены не эффективные медиа форматы, которым больше 20 лет. Такие как: H.264 (AVC), JPEG, MP3. Хотя есть современные более эффективные альтернативы, с хорошей совместимостью. Такие как: H.265 (HEVC), AVIF, AAC.

В данной статье представлены способы перекодирования медиа файлов в более современные с потерей качества (на мой глаз и слух не заметных), используя утилиты командной строки, такие как ffmpeg и magick. Так же написан скрипт для оболочки fish для конвертации всего медиа из директорий.

Сведения об ffmpeg

FFmpeg (Fast Forward Moving Picture Experts Group) — инструмент для обработки мультимедийных данных, который позволяет кодировать, декодировать, транскодировать, мультиплексировать, демультиплексировать, потоково передавать, фильтровать и проигрывать практически любые аудио- и видео-форматы.

Установка:

Синопсис:

ffmpeg [global_options] {[input_file_options] -i input_url} ... {[output_file_options] output_url} ...

Общие параметры

  • -i url - Указывает URL входного файла.

  • -y - Перезаписывает выходные файлы без запроса.

  • -n - Не перезаписывает выходные файлы и немедленно завершает работу, если указанный выходной файл уже существует.

  • -c[:stream_specifier] codec или -codec[:stream_specifier] codec - Выбирает кодек (кодировщик при использовании перед выходным файлом или декодер при использовании перед входным файлом) для одного или нескольких потоков. codec — это имя декодера/кодировщика или специальное значение copy (только для выхода), указывающее, что поток не должен быть перекодирован. Для каждого потока применяется последняя соответствующая опция -c.

  • -filter[:stream_specifier] filtergraph - Создает фильтр-граф, указанный в filtergraph, и использует его для фильтрации потока.

Фильтры:

  • scale - Изменение размера (масштабирование) входного видео. Этот фильтр обеспечивает соответствие соотношения сторон отображения выходного видео входному видео, корректируя соотношение сторон выходного образца.

    • Опции:

      • width, w: Установить ширину выходного видео.

      • height, h: Установить высоту выходного видео.

      • Значения по умолчанию — это размеры входного изображения. Если width (w) или height (h) установлены в 0, используется ширина или высота входного изображения для выходного изображения. Если одно из значений установлено в -n (где n >= 1), фильтр сохранит соотношение сторон входного изображения на основе другого указанного размера и обеспечит, чтобы рассчитанный размер был кратен n. Если оба значения равны -n, поведение такое же, как если бы оба были установлены в 0.

    • Константы для выражений размеров: Значения опций w и h являются выражениями, содержащими следующие константы:

      • in_w, in_h: Ширина и высота входного изображения.

      • iw, ih: То же, что и in_w и in_h.

      • out_w, out_h: Ширина и высота выходного (масштабированного) изображения.

      • ow, oh: То же, что и out_w и out_h.

      • a: То же, что и iw / ih.

    • Примеры:

      # Масштабирование до определенного размера. Масштабировать входное видео до размера 200x100:
      scale=w=200:h=100
      # Или
      scale=200:100
      # Или
      scale=200x100
      
      # Аббревиатура размера. Указать аббревиатуру размера для выходного размера:
      scale=qcif
      # Или
      scale=size=qcif
      
      # Масштабирование в 2 раза. Масштабировать входное изображение в 2 раза:
      scale=w=2*iw:h=2*ih
      # Или
      scale=2*in_w:2*in_h
      
      # Масштабирование до половины размера. Масштабировать входное изображение до половины размера:
      scale=w=iw/2:h=ih/2
      
      # Увеличение ширины и установка высоты в тот же размер:
      scale=3/2*iw:ow
      
      # Увеличение высоты и установка ширины в 3/2 от высоты:
      scale=w=3/2*oh:h=3/5*ih
      
      # Сохранение соотношения сторон. Увеличить ширину до максимум 500 пикселей, сохраняя то же соотношение сторон, что и у входного изображения:
      scale=w='min(500\, iw*3/2):h=-1'
  • fps - Преобразует видео в указанную постоянную частоту кадров, дублируя или удаляя кадры по мере необходимости.

    • Параметры:

      • fps - Желаемая частота кадров выходного видео. Принимает выражения, содержащие следующие константы:

        • source_fps: Частота кадров входного видео.

        • ntsc: Частота кадров NTSC 30000/1001.

        • pal: Частота кадров PAL 25.0.

        • film: Частота кадров фильма 24.0.

        • ntsc_film: Частота кадров NTSC-фильма 24000/1001.

        • По умолчанию используется значение 25.

    • Примеры:

      # Установить FPS в 25
      fps=fps=25

Примеры использования:

ffmpeg -i input.mp4 -c:v libx264 -c:a aac output.mkv  # Конвертация MP4 в MKV с использованием кодеков libx264 и aac

ffmpeg -i input.mp4 -q:a 0 -map a output.mp3 # Извлечение аудиодорожки в формате MP3 с высоким качеством
ffmpeg -i input.mp4 -ss 00:01:00 -to 00:02:00 -c copy output.mp4 # Обрезка видео с 1 минуты до 2 минут без перекодирования
ffmpeg -i input.mp4 -vf "scale=1280:720" output.mp4 # Изменение разрешения видео до 1280x720
ffmpeg -i input.mp4 -i watermark.png -filter_complex "overlay=10:10" output.mp4 # Наложение водяного знака на видео в позиции (10,10)

Сведения об magick

magick — инструмент для обработки изображений, который позволяет выполнять широкий спектр операций с изображениями, включая конвертацию форматов, редактирование, применение фильтров, создание анимаций и многое другое.

Установка:

  • Windows: Скачайте установочный файл с официального сайта ImageMagick и следуйте инструкциям установки.

  • macOS: Используйте Homebrew для установки:

    brew install imagemagick
  • Linux: Установите из пакетного менеджера вашего дистрибутива. Например, для Ubuntu:

    sudo apt-get install imagemagick

Синопсис:

magick [input-filename...] [settings...] [operators...] [sequence-operators...] [stacks...] [output-filename]

Общие параметры:

  • input-filename: Указывает входной файл или файлы.

  • settings: Настройки, которые применяются к изображению.

  • operators: Операторы, которые выполняют различные операции с изображением.

  • sequence-operators: Операторы, которые применяются к последовательности изображений.

  • stacks: Операторы, которые работают с наборами изображений.

  • output-filename: Указывает выходной файл.

Примеры использования:

magick input.jpg -resize 800x600 output.jpg  # Изменение размера изображения до 800x600 пикселей

magick input.png -rotate 90 output.png  # Поворот изображения на 90 градусов

magick input.jpg -gravity center -crop 300x300+0+0 +repage output.jpg  # Обрезка изображения до размера 300x300 пикселей с центрированием

magick input.jpg -blur 0x8 output.jpg  # Применение размытия к изображению

magick input1.jpg input2.jpg -append output.jpg  # Объединение двух изображений по вертикали

magick input.gif -coalesce -gravity center -background none -extent 320x240 output.gif  # Изменение размера анимации GIF до 320x240 пикселей с центрированием

Сведения об fish

Fish (Friendly Interactive SHell) — современная интерактивная оболочка командной строки с подсветкой синтаксиса, умным автодополнением, встроенной справкой и удобным скриптовым языком. Отличается дружелюбным интерфейсом и простотой настройки.

Установка:

  • Windows:

    • Через WSL (рекомендуется):

      sudo apt-get install fish
    • Нативно: через Chocolatey (требует прав администратора):

      choco install fish
  • macOS:

    brew install fish
  • Linux:

    sudo apt-get install fish  # Ubuntu/Debian
    sudo dnf install fish      # Fedora
    sudo pacman -S fish        # Arch

Синопсис:

fish [options] [cmd]

Основные параметры:

  • -c "cmd" — выполнить команду и завершить работу.

  • -d level — включить отладку (например, -d 3).

  • -i — запустить интерактивный режим (по умолчанию).

  • -v или --version — вывести версию.

  • -N — отключить автодополнение.

  • --profile=file — записать статистику производительности в файл.

Примеры использования:

# Запуск интерактивной оболочки
fish

# Выполнение одной команды
fish -c "echo Hello World && ls -l"

# Настройка fish как оболочки по умолчанию
chsh -s (which fish)

# Создание функции в fish
function greet
  echo "Привет, $argv!"
end
greet "Пользователь"  # Вывод: Привет, Пользователь!

# Работа с переменными
set name "Fish"
echo $name  # Вывод: Fish

Конфигурация. Файл настроек: ~/.config/fish/config.fish.
Для быстрой настройки используйте веб-интерфейс:

fish_config

Как использовать методы в этой статье и теория

Вкратце: Если не обязательно сохранять исходное качества, можно перекодировать с в более высоком качестве и удалить исходные файлы. Иначе кодируйте как хотите и переносите исходники в холодное хранилище.

Форматы с потерями и без потерь

Форматы без потерь сохраняют все исходные данные из исходного файла. Это означает, что при сжатии не теряется никакая информация, что гарантирует возможность точного восстановления файла в его первоначальном виде. Распространенные форматы без потерь включают:

  • Изображения: PNG, BMP, TIFF

  • Аудио: WAV, FLAC, ALAC

  • Видео: AVI (несжатый), Huffyuv

Используются для:

  • Сохранения качества. Исходные файлы без потерь обеспечивают сохранение первоначального качества медиа. Это критично для профессиональной работы, где любое ухудшение качества может быть недопустимым.

  • Гибкости при редактировании. Файлы без потерь обеспечивают большую гибкость при редактировании. Например, при редактировании видео форматы без потерь позволяют проводить несколько раундов редактирования без накопления артефактов сжатия.

  • Архивных целей. Для долговременного хранения и архивирования предпочтительны форматы без потерь, так как они гарантируют, что медиа можно будет восстановить в первоначальном качестве даже спустя годы.

Форматы с потерями достигают более высоких степеней сжатия, отбрасывая часть исходных данных. Хотя это приводит к уменьшению размера файлов, оно также вносит определенный уровень ухудшения качества. Распространенные форматы с потерями включают:

  • Изображения: JPEG, GIF

  • Аудио: MP3, AAC, OGG

  • Видео: MP4 (H.264), MKV (H.265), WebM (VP9)

Используются для:

  • Распространения: Форматы с потерями идеально подходят для распространения медиа через интернет или на физических носителях благодаря их меньшему размеру. Это делает их более доступными и быстрыми для загрузки или потоковой передачи.

  • Эффективности хранения: Для личного или некритического использования форматы с потерями могут значительно экономить место на хранилище. Это особенно полезно для устройств с ограниченной емкостью хранения.

  • Совместимости: Некоторые устройства и платформы могут не поддерживать форматы без потерь, делая форматы с потерями необходимым выбором по соображениям совместимости.

Плохие практики перекодирования

  • Отсутствие тестирования закодированных файлов: поздно и не вовремя выявляются проблемы.

  • Перекодирование из в формат с потерями: деградация качества файла усугубляется с каждым перекодированием.

  • Чрезмерное сжатие: значительное ухудшение качества.

  • Использование форматов с потерями для профессиональной работы: Форматы с потерями (такие как MP3, AAC, JPEG) подходят для потребителей, но не для профессионального редактирования или архивирования, так как удаляют часть данных. Для этих целей лучше использовать форматы без потерь (такие как FLAC, PNG)

  • Игнорирование совместимости с целевой платформой: использование на разных платформах может быть затруднено или невозможно.

Сжатие аудио

Есть два кодека эффективнее чем MP3 это AAC и OPUS.

Общие параметры аудио кодировщиков

  • -b:a - Устанавливает битрейт в битах/с. Если включен режим VBR, эта опция игнорируется.

  • -c:a - Устанавливает аудио-кодек.

  • -q:a - Устанавливает качество для режима переменного битрейта (специфично для кодека, VBR).

Перекодирование в AAC

Кодировщики:

  • Fraunhofer FDK AAC libfdk_aac

  • audiotoolbox Encoder aac_at (обычно доступен только на MacOS)

  • Native FFmpeg AAC Encoder aac (По умолчанию)

Профили:

  • aac_low (1): Low Complexity AAC (LC-AAC) (По умолчанию): Высокая совместимость, низкое потребление процессорной мощности. Аудио приемлемо звучит с битрейтом 128k.

  • aac_he (4): High Efficiency AAC (HE-AAC): Низкая совместимость, высокое потребление процессорной мощности, хорошо звучит на низких битрейтах. Аудио приемлемо звучит с битрейтом 64k. Доступный диапазон битрейта 20 КБИТ/СЕК ≤ 80 КБИТ/СЕК.

  • aac_he_v2 (28): High Efficiency AAC version 2 (HE-AACv2): Улучшенная версия HE-AAC, лучшее качество звука на низких битрейтах. Аудио приемлемо звучит с битрейтом 32k. Доступный диапазон битрейта 20 КБИТ/СЕК ≤ 48 КБИТ/СЕК.

Параметры

  • -profile:a - Устанавливает аудио профиль.

  • -b:a. Если битрейт не указан явно, он автоматически устанавливается на подходящее значение в зависимости от выбранного профиля.

Параметры aac_at

  • -aac_at_mode - Устанавливает режим управления битрейтом. Возможные значения варьируются от -1 до 3:

    • auto (-1): VBR, если указано глобальное качество; CBR в противном случае (По умолчанию)

    • cbr (0): Постоянный битрейт

    • abr (1): Средний битрейт в долгосрочной перспективе

    • cvbr (2): Ограниченный переменный битрейт

    • vbr (3): Переменный битрейт

Параметры libfdk_aac

  • -vbr - Устанавливает режим VBR, от 1 (низшее качество) до 5 (высокое качество). Значение 0 отключает VBR, и включается CBR (постоянный битрейт). В настоящее время только профиль aac_low поддерживает кодирование VBR.

    • 0: отключить VBR (По умолчанию)

    • 1: 32 кбит/с на канал

    • 2: 40 кбит/с на канал

    • 3: 48-56 кбит/с на канал

    • 4: 64 кбит/с на канал

    • 5: около 80-96 кбит/с на канал

Перекодирование в OPUS

При малых битрейтах OPUS звучит хорошо только в стерео, в моно звучит хуже чем AAC.

Кодировщик доступен только один libopus. Он не поддерживает кодирование, основанное на качестве.

Параметры libopus

  • -vbr - Устанавливает режим VBR. Опция -vbr в FFmpeg имеет следующие допустимые аргументы, с эквивалентными опциями в opusenc в скобках:

    • off (hard-cbr): Использует кодирование с постоянным битрейтом.

    • on (vbr): Использует кодирование с переменным битрейтом (по умолчанию).

    • constrained (cvbr): Использует кодирование с ограниченным переменным битрейтом.

Пример команд

# Явное указание кодека aac (aac), который используется по умолчанию
ffmpeg -i input.mp3 -c:a aac output.m4a
# Использование кодека libopus (opus)
ffmpeg -i input.mp3 -c:a libopus output.m4a
# Использование кодека libfdk_aac (aac)
ffmpeg -i input.mp3 -c:a libfdk_aac output.m4a
# Использование профиля AAC HE первой версии
ffmpeg -i input.mp3 -c:a libfdk_aac -profile:a aac_he -b:a 64k output.m4a
# Использование профиля AAC HE второй версии
ffmpeg -i input.mp3 -c:a libfdk_aac -profile:a aac_he_v2 -b:a 32k output.m4a
# Использование профиля AAC HE первой версии для кодека aac_at (aac)
ffmpeg -i input.mp3 -c:a aac_at -profile:a 4 -b:a 64k output.m4a
# Использование профиля AAC HE второй версии для кодека aac_at (aac)
ffmpeg -i input.mp3 -c:a aac_at -profile:a 28 -b:a 32k output.m4a
# Указание качества 10 (0-14, 0 - самое высокое качество)
ffmpeg -i input.mp3 -q:a 10 output.m4a
# Указание битрейта
ffmpeg -i input.mp3 -b:a 128k output.m4a

Таблица сравнения кодировщиков

Оригинальный файл

Кодек

Качество

Битрейт (КБИТ/СЕК)

Режим кодирования

Размер (МБ)

Относительный размер (%)

Качество на мой слух

11 - Drop Dead.mp3

mp3

320

7.221

aac

20.7

cbr

0.468

6

Ужасно

aac

20.7

abr

0.468

6

Ужасно

aac

20.7

cvbr

0.468

6

Плохо

opus

15.3

cbr

0.346

4

Плохо

opus

15.3

cvbr

0.346

4

Плохо

opus

15.3

vbr

0.346

4

Плохо

aac

32.7

cbr

0.739

10

Средне

aac

32.7

abr

0.739

10

Лучше среднего

aac

32.7

cvbr

0.739

10

Похоже на оригинал

opus

31.9

cbr

0.720

9

Лучше среднего

opus

31.9

cvbr

0.720

9

Похоже на оригинал

opus

31.9

vbr

0.720

9

Похоже на оригинал

aac

48.7

cbr

1.100

15

Похоже на оригинал

aac

48.7

abr

1.100

15

Похоже на оригинал

aac

48.7

cvbr

1.100

15

Похоже на оригинал

opus

48.1

cbr

1.085

15

Похоже на оригинал

opus

48.1

cvbr

1.085

15

Похоже на оригинал

opus

48.1

vbr

1.085

15

Похоже на оригинал

aac

48.7

cbr

1.100

15

Идентично

aac

48.7

abr

1.100

15

Идентично

aac

48.7

cvbr

1.100

15

Идентично

opus

64.0

cbr

1.443

19

Идентично

opus

64.0

cvbr

1.443

19

Идентично

opus

64.0

vbr

1.443

19

Идентично

aac

48.7

cbr

1.100

15

Идентично

aac

48.7

abr

1.100

15

Идентично

aac

48.7

cvbr

1.100

15

Идентично

opus

96.2

cbr

2.170

30

Идентично

opus

96.2

cvbr

2.170

30

Идентично

opus

96.2

vbr

2.170

30

Идентично

aac

0

118

vbr

2.660

36

Идентично

aac

7

52.4

vbr

1.184

16

Похоже на оригинал

aac

14

19.8

vbr

0.448

6

Ужасно

3 - This Is Halloween.mp3

mp3

192

4.647

aac

20.7

cbr

0.502

10

Ужасно

aac

20.7

abr

0.502

10

Ужасно

aac

20.7

cvbr

0.502

10

Плохо

opus

14.4

cbr

0.349

7

Плохо

opus

14.4

cvbr

0.349

7

Плохо

opus

14.4

vbr

0.349

7

Плохо

aac

32.7

cbr

0.793

17

Средне

aac

32.7

abr

0.793

17

Лучше среднего

aac

32.7

cvbr

0.793

17

Похоже на оригинал

opus

30.6

cbr

0.741

15

Лучше среднего

opus

30.6

cvbr

0.741

15

Похоже на оригинал

opus

30.6

vbr

0.741

15

Похоже на оригинал

aac

48.7

cbr

1.180

25

Похоже на оригинал

aac

48.7

abr

1.180

25

Похоже на оригинал

aac

48.7

cvbr

1.180

25

Похоже на оригинал

opus

46.3

cbr

1.120

24

Похоже на оригинал

opus

46.3

cvbr

1.120

24

Похоже на оригинал

opus

46.3

vbr

1.120

24

Похоже на оригинал

aac

48.7

cbr

1.180

25

Идентично

aac

48.7

abr

1.180

25

Идентично

aac

48.7

cvbr

1.180

25

Идентично

opus

61.9

cbr

1.497

32

Идентично

opus

61.9

cvbr

1.497

32

Идентично

opus

61.9

vbr

1.497

32

Идентично

aac

48.7

cbr

1.180

25

Идентично

aac

48.7

abr

1.180

25

Идентично

aac

48.7

cvbr

1.180

25

Идентично

opus

94.3

cbr

2.282

49

Идентично

opus

94.3

cvbr

2.282

49

Идентично

opus

94.3

vbr

2.282

49

Идентично

aac

0

117

vbr

2.844

61

Идентично

aac

7

51.4

vbr

1.244

26

Похоже на оригинал

aac

14

19.9

vbr

0.483

10

Ужасно

Спектрограмма (Рекомендую смотреть в отдельном окне)
Спектрограмма
Спектрограмма

Скрипт использованный для тестирования
#!/usr/bin/env fish

# ========== Configuration Variables ==========
set COMPRESSED ./compressed # Output directory for processed files
set SOURCE ./original # Input directory with source audio files
set CSV_TABLE encoding_info.csv # CSV table for storing info about source and compressed files
set BITRATES 16k 32k 48k 64k 96k # Target bitrates for encoding
set AAC_MODES cbr abr cvbr # AAC encoding modes
set AAC_QUALITY_VALUES 0 7 14 # AAC quality levels (0-14 scale)
set OPUS_MODES off constrained on # Opus encoding modes
set OPUS_MODES_STR cbr cvbr vbr # Human-friendly Opus mode names

# ========== Helper Functions ==========

function get_file_size -d "Calculate file size in MB"
    math (stat -f %z "$argv") / 1024 / 1024
end

function get_codec -d "Detect audio codec"
    set file $argv
    ffprobe -v error -select_streams a:0 -show_entries stream=codec_name \
        -of default=noprint_wrappers=1:nokey=1 $file
end

function get_bitrate -a file -d "Get audio bitrate in kbps"
    set bitrate (mediainfo "$file" | grep "Overall bit rate" | string split : | string trim | tail -n 1)
    echo "$bitrate"
end

# ========== Main Encoding Function ==========
function my_ffmpeg -d "Convert input and write record in csv table. -f <aac | opus>, only one option allowed -b or -a"
    # Parse command-line arguments for encoding parameters
    argparse \
        'i/input=' \
        'f/format=' \
        'p/profile=' \
        'c/codec=' \
        'b/bitrate=' \
        'q/quality=' \
        'm/mode=' \
        -- $argv
    or return 1 # Exit if argument parsing fails

    # Generate output filename with parameters
    set output (path change-extension '' $_flag_i)
    if set -q _flag_b
        set output "$output"_b=$_flag_b
        set option_b_q -b:a $_flag_b
    else if set -q _flag_q
        set output "$output"_q=$_flag_q
        set option_b_q -q:a $_flag_q
    end
    set output "$output"_mode="$_flag_m"

    # Format-specific configuration and output file extension
    switch $_flag_f
        case opus
            set output "$output".opus
            set option_mode -aac_at_mode $_flag_m
            set field_mode $OPUS_MODES_STR[(contains -i $_flag_m $OPUS_MODES)]
        case aac
            set output "$output".m4a
            set option_mode -vbr $_flag_m
            set option_profile -profile:a $_flag_p
            set field_mode $_flag_m
    end

    # Execute FFmpeg encoding command
    echo "Conversion of $_flag_i to format $_flag_f"
    ffmpeg -loglevel error -i "$_flag_i" \
        -c:a $_flag_c $option_profile $option_b_q $option_mode \
        -y \
        $output
    and begin # Record encoding results if successful
        set size (get_file_size $output)
        set source_size (get_file_size $_flag_i)
        set relative_size (math -s 0 $size / $source_size \* 100)%

        set target_bitrate (get_bitrate $output)
        or set target_bitrate $_flag_b

        if set -q _flag_b # Handle different quality/bitrate displays
            set field_b_q $target_bitrate
        else if set -q _flag_q
            set field_b_q $_flag_q \($target_bitrate\)
        end

        # Append results to CSV file
        echo ",$_flag_f,$field_b_q,$field_mode,$size,$relative_size,$output" >>$CSV_TABLE
    end
    or echo "Conversion of $_flag_i failed" # Error handling
end

# ========== Main Script Execution ==========
cd (dirname (status -f)) # Switch to script directory

# Validate directory structure
if not test -d $SOURCE
    echo "Original directory not exist."
    exit 1
end
if not test -d $COMPRESSED
    mkdir $COMPRESSED
end

# Clean and prepare output directory
rm -r $COMPRESSED/*
cp -a $SOURCE/. $COMPRESSED

# Initialize CSV report
set files (find $COMPRESSED -type f -name '*.mp3')
echo "Original File,Codec,Bitrate or Quality,Encoding Mode,File Size,Relative size,Output File" >$CSV_TABLE

# Process all audio files
for file in $files
    # Record source file metadata
    set source_name (path basename $file)
    set source_bitrate (get_bitrate $file)
    set source_codec (get_codec $file)
    set source_size (get_file_size $file)
    echo "$source_name,$source_codec,$source_bitrate,,$source_size,," >>$CSV_TABLE

    # Encode with different bitrates and modes
    for bitrate in $BITRATES
        # AAC encoding variants
        for mode in $AAC_MODES
            my_ffmpeg -i "$file" -f aac -c aac_at -b $bitrate -m $mode -p 28
        end

        # Opus encoding variants
        for mode in $OPUS_MODES
            my_ffmpeg -i "$file" -f opus -c libopus -b $bitrate -m $mode
        end
    end

    # Additional AAC quality-based encodings
    for q in $AAC_QUALITY_VALUES
        my_ffmpeg -i "$file" -f aac -c aac_at -q $q -m vbr -p 28
    end
end

# ========== Spectrogram Generation ==========
for audio in (find $COMPRESSED -type f \( -name '*.mp3' -o -name '*.m4a' -o -name '*.opus' \))
    set spectogram $audio.png
    echo "Generating spectrogram for $audio"
    set spectrogram_options -n remix 1,2 spectrogram -x 200 -Y 130 -t "$(path basename $audio)"

    # Try direct conversion first, fallback to WAV if needed
    sox "$audio" $spectrogram_options -o "$spectogram" || begin
        set tmp_wav (path change-extension wav $audio)
        ffmpeg -loglevel error -i "$audio" "$tmp_wav"
        sox "$tmp_wav" $spectrogram_options -o "$spectogram"
        rm $tmp_wav
    end
end

# ========== Spectrogram Aggregation ==========
# Combine individual spectrograms into composite images
set source_files (csvcut -x -c "Original File" $CSV_TABLE | tail -n +2)

for source_file in $source_files
    set file (path change-extension '' $source_file)
    set spectograms (gfind $COMPRESSED/ -type f -iregex ".*/"$file"\(_.*\|\.mp3\)?.png" | sort -V)
    magick $spectograms -append "$COMPRESSED/spectogram_$file.png"
    rm $spectograms
end

# Create final combined visualization
set spectograms (gfind $COMPRESSED/ -type f -name "spectogram_*.png" | sort -V)
magick $spectograms +append "$COMPRESSED/spectogram.avif"
rm $spectograms

Заключение

Какой формат выбрать? Выбирайте OPUS для наивысшей эффективности, а AAC для совместимости.

Какой битрейт выбрать?

  • Для HE-AACv2 и OPUS: От 32k до 96k. Рекомендую 64k, но 48k тоже может звучать приемлемо.

  • Для HE-AAC: От 64k до 160k. Рекомендую 128k.

MP3

AAC

HE-AAC

HE-AAC v2

Opus

32 кбит/с

32 кбит/с

16 кбит/с

8 кбит/с

8 кбит/с

64 кбит/с

64 кбит/с

32 кбит/с

16 кбит/с

16 кбит/с

96 кбит/с

96 кбит/с

48 кбит/с

24 кбит/с

24 кбит/с

128 кбит/с

128 кбит/с

64 кбит/с

32 кбит/с

32 кбит/с

160 кбит/с

160 кбит/с

80 кбит/с

40 кбит/с

40 кбит/с

192 кбит/с

192 кбит/с

96 кбит/с

48 кбит/с

48 кбит/с

256 кбит/с

256 кбит/с

128 кбит/с

64 кбит/с

64 кбит/с

320 кбит/с

320 кбит/с

160 кбит/с

80 кбит/с

80 кбит/с

Таблица. Приблизительное сопоставление битрейта MP3 к AAC и OPUS

Какой кодек AAC выбрать? Если доступен выбирайте aac_at, иначе libfdk_aac.

  • Для AAC-LC: aac_atlibfdk_aac > (aac).

  • Для HE-AAC неясно, что лучше: aac_at или libfdk_aac.

Какой профиль AAC выбрать? Выбирайте HE-AACv2.

Итог: Сжал 41 ГБ аудио в 8 ГБ (в 5 раз), при этом без заметной потери качества, переместив оригиналы в холодное хранилище.

Использованная команда:

                # Сжатие аудио Opus с переменным битрейтом
                # ПРИМЕЧАНИЕ: Битрейт 64k обеспечивает хорошее качество для большинства контента
                $ffmpeg -i "$input" \
                    -c:a libopus -b:a 64k -vbr on \
                    -y "$output"

Сжатие видео

Есть два кодека эффективнее чем H.264/AVC это AV1 и H.265/HEVC.

Общие параметры видео кодеков

  • -c:v - Устанавливает видео-кодек.

  • -crf - Устанавливает постоянный коэффициент скорости (Constant Rate Factor), где меньшие значения значат большее качество, а большие худшее.

  • -preset - Устанавливает пресет, балансирующую скорость кодирования и эффективность сжатия.

  • -filter:v (-vf) - Фильтры:

    • scale - Изменяет разрешение видео.

    • fps - Изменяет количество кадров видео.

Перекодирование в AV1

Кодировщик доступен только один SVT-AV1 Encoder libsvtav1.

Параметры libsvtav1

  • -preset <0..13> (По умолчанию 0)

  • -crf <0..63.0> (По умолчанию 0)

Перекодирование в H.265/HEVC

Кодировщик доступен только один H.265/HEVC Encoder libx265.

Параметры libx265

  • -preset

    • ultrafast

    • superfast

    • veryfast

    • faster

    • fast

    • medium (по умолчанию)

    • slow

    • slower

    • veryslow

    • placebo

  • -crf <0..51.0> (По умолчанию 28)

Пример команд

# Использование кодека libsvtav1
ffmpeg -i input.mp4 -c:v libsvtav1 output.mkv
# Использование значение пресета 8
ffmpeg -i input.mp4 -c:v libsvtav1 -preset 8 output.mkv
# Использование значение CRF 28
ffmpeg -i input.mp4 -c:v libsvtav1 -crf 28 output.mkv
# Использование кодека libx265
ffmpeg -i input.mp4 -c:v libx265 output.mkv
# Использование значение пресета slow
ffmpeg -i input.mp4 -c:v libx265 -preset slow output.mkv
# Использование значение CRF 18
ffmpeg -i input.mp4 -c:v libx265 -preset veryslow -crf 18 output.mkv
# Изменение разрешения видео до 1280x720 и частоты кадров до 30 fps с
# использованием кодека libx265 и пресета slow
ffmpeg -i input.mp4 -vf "scale=1280:720,fps=30" -c:v libx265 -preset slow output.mkv
# Изменение разрешения видео до половины исходного разрешения
ffmpeg -i input.mp4 -vf "scale=iw/2:ih/2" output.mkv
# Изменение разрешения видео до максимальной ширины 1280, сохраняя соотношение сторон
ffmpeg -i input.mp4 -vf "scale='if(gt(iw,1280),1280,-1)':'if(gt(ih,720),720,-1)'" output.mkv
# Изменение частоты кадров до половины исходной частоты кадров
ffmpeg -i input.mp4 -vf "fps=source_fps/2" output.mkv
# Изменение разрешения видео до минимальной ширины 640 и частоты кадров до 24 fps
ffmpeg -i input.mp4 -vf "scale='if(lt(iw,640),640,iw)':'if(lt(ih,480),480,ih)',fps=24" output.mkv

Таблица сравнения кодеков

Имя

Кодек

Пресет

CRF

Масштаб

FPS

Время перекодирования (сек)

Размер (МБ)

Относительный размер (%)

PSNR

SSIM

VMAF

real ai spirited away

h264

720x1280

30/1

5.888

100

h265

fast

28

720x1280 (1)

30/1 (1)

2

0.567

9

42.186

0.982

87.448

av1

10

34

720x1280 (1)

30/1 (1)

1

0.689

11

43.101

0.984

89.311

h265

fast

28

720x1280 (1)

15/1 (0.5)

1

0.557

9

19.990

0.655

17.964

av1

10

34

720x1280 (1)

15/1 (0.5)

1

0.523

8

18.815

0.632

13.923

h265

medium

28

720x1280 (1)

30/1 (1)

2

0.600

10

42.569

0.983

88.203

av1

8

34

720x1280 (1)

30/1 (1)

2

0.744

12

42.214

0.967

86.266

h265

medium

28

720x1280 (1)

15/1 (0.5)

2

0.575

9

20.007

0.655

18.118

av1

8

34

720x1280 (1)

15/1 (0.5)

1

0.538

9

19.572

0.646

16.711

h265

slow

28

720x1280 (1)

30/1 (1)

4

0.639

10

43.313

0.985

90.463

av1

6

34

720x1280 (1)

30/1 (1)

3

0.761

12

43.542

0.981

89.484

h265

slow

28

720x1280 (1)

15/1 (0.5)

3

0.605

10

19.814

0.650

17.502

av1

6

34

720x1280 (1)

15/1 (0.5)

2

0.541

9

19.093

0.636

15.308

h265

fast

31

720x1280 (1)

30/1 (1)

2

0.419

7

40.614

0.975

83.179

av1

10

38

720x1280 (1)

30/1 (1)

0

0.553

9

41.504

0.973

85.313

h265

fast

31

720x1280 (1)

15/1 (0.5)

2

0.410

6

19.925

0.655

17.683

av1

10

38

720x1280 (1)

15/1 (0.5)

1

0.420

7

19.875

0.653

17.371

h265

medium

31

720x1280 (1)

30/1 (1)

2

0.436

7

40.824

0.977

83.759

av1

8

38

720x1280 (1)

30/1 (1)

1

0.599

10

43.170

0.985

89.948

h265

medium

31

720x1280 (1)

15/1 (0.5)

1

0.420

7

19.870

0.653

17.678

av1

8

38

720x1280 (1)

15/1 (0.5)

1

0.430

7

18.906

0.635

14.490

h265

slow

31

720x1280 (1)

30/1 (1)

5

0.463

7

41.522

0.979

86.903

av1

6

38

720x1280 (1)

30/1 (1)

3

0.609

10

43.476

0.986

90.413

h265

slow

31

720x1280 (1)

15/1 (0.5)

3

0.439

7

19.920

0.655

17.963

av1

6

38

720x1280 (1)

15/1 (0.5)

2

0.437

7

19.327

0.641

15.881

h265

fast

28

360x640 (0.5)

30/1 (1)

1

0.263

4

37.427

0.960

70.115

av1

10

34

360x640 (0.5)

30/1 (1)

0

0.372

6

38.761

0.970

77.434

h265

fast

28

360x640 (0.5)

15/1 (0.5)

1

0.259

4

19.747

0.656

16.204

av1

10

34

360x640 (0.5)

15/1 (0.5)

1

0.286

4

19.765

0.655

16.061

h265

medium

28

360x640 (0.5)

30/1 (1)

1

0.278

4

37.619

0.962

71.487

av1

8

34

360x640 (0.5)

30/1 (1)

1

0.363

6

38.978

0.972

78.936

h265

medium

28

360x640 (0.5)

15/1 (0.5)

1

0.272

4

19.696

0.654

16.120

av1

8

34

360x640 (0.5)

15/1 (0.5)

1

0.275

4

18.785

0.637

13.222

h265

slow

28

360x640 (0.5)

30/1 (1)

3

0.290

4

38.035

0.964

74.995

av1

6

34

360x640 (0.5)

30/1 (1)

1

0.360

6

39.145

0.974

79.656

h265

slow

28

360x640 (0.5)

15/1 (0.5)

1

0.278

4

19.666

0.653

16.152

av1

6

34

360x640 (0.5)

15/1 (0.5)

1

0.272

4

19.637

0.653

16.043

h265

fast

31

360x640 (0.5)

30/1 (1)

1

0.209

3

36.289

0.949

63.080

av1

10

38

360x640 (0.5)

30/1 (1)

0

0.319

5

38.362

0.967

75.674

h265

fast

31

360x640 (0.5)

15/1 (0.5)

1

0.207

3

19.591

0.654

15.332

av1

10

38

360x640 (0.5)

15/1 (0.5)

1

0.244

4

19.732

0.655

15.904

h265

medium

31

360x640 (0.5)

30/1 (1)

1

0.216

3

36.408

0.951

64.262

av1

8

38

360x640 (0.5)

30/1 (1)

1

0.308

5

38.502

0.969

77.035

h265

medium

31

360x640 (0.5)

15/1 (0.5)

1

0.212

3

19.713

0.657

15.761

av1

8

38

360x640 (0.5)

15/1 (0.5)

1

0.234

3

19.805

0.659

16.189

h265

slow

31

360x640 (0.5)

30/1 (1)

2

0.223

3

36.799

0.953

68.217

av1

6

38

360x640 (0.5)

30/1 (1)

2

0.304

5

38.762

0.971

77.960

h265

slow

31

360x640 (0.5)

15/1 (0.5)

1

0.216

3

19.590

0.654

15.642

av1

6

38

360x640 (0.5)

15/1 (0.5)

1

0.234

3

19.228

0.644

14.684

mini hogwarts

h264

832x1278

30/1

7.486

100

h265

fast

28

832x1278 (1)

30/1 (1)

3

0.641

8

43.339

0.985

84.796

av1

10

34

832x1278 (1)

30/1 (1)

1

0.871

11

44.324

0.987

89.931

h265

fast

28

832x1278 (1)

15/1 (0.5)

2

0.594

7

23.479

0.870

4.036

av1

10

34

832x1278 (1)

15/1 (0.5)

1

0.633

8

22.457

0.844

3.272

h265

medium

28

832x1278 (1)

30/1 (1)

4

0.671

8

43.694

0.986

86.123

av1

8

34

832x1278 (1)

30/1 (1)

4

0.811

10

45.010

0.988

90.853

h265

medium

28

832x1278 (1)

15/1 (0.5)

2

0.606

8

23.475

0.870

4.008

av1

8

34

832x1278 (1)

15/1 (0.5)

3

0.570

7

22.650

0.852

3.482

h265

slow

28

832x1278 (1)

30/1 (1)

10

0.749

9

44.527

0.987

89.510

av1

6

34

832x1278 (1)

30/1 (1)

10

0.823

10

44.039

0.974

85.906

h265

slow

28

832x1278 (1)

15/1 (0.5)

7

0.663

8

23.369

0.868

4.015

av1

6

34

832x1278 (1)

15/1 (0.5)

7

0.572

7

22.755

0.855

3.575

h265

fast

31

832x1278 (1)

30/1 (1)

4

0.451

6

41.952

0.981

79.255

av1

10

38

832x1278 (1)

30/1 (1)

2

0.652

8

43.347

0.984

86.802

h265

fast

31

832x1278 (1)

15/1 (0.5)

2

0.425

5

23.370

0.869

3.884

av1

10

38

832x1278 (1)

15/1 (0.5)

1

0.482

6

23.554

0.871

4.061

h265

medium

31

832x1278 (1)

30/1 (1)

3

0.467

6

41.869

0.981

79.842

av1

8

38

832x1278 (1)

30/1 (1)

5

0.611

8

44.064

0.987

88.228

h265

medium

31

832x1278 (1)

15/1 (0.5)

2

0.429

5

23.280

0.867

3.824

av1

8

38

832x1278 (1)

15/1 (0.5)

3

0.434

5

23.443

0.872

4.000

h265

slow

31

832x1278 (1)

30/1 (1)

10

0.518

6

42.844

0.983

84.439

av1

6

38

832x1278 (1)

30/1 (1)

13

0.621

8

44.742

0.988

89.565

h265

slow

31

832x1278 (1)

15/1 (0.5)

6

0.466

6

23.346

0.868

3.914

av1

6

38

832x1278 (1)

15/1 (0.5)

7

0.438

5

22.750

0.855

3.544

h265

fast

28

416x640 (0.5)

30/1 (1)

2

0.265

3

39.338

0.974

64.721

av1

10

34

416x640 (0.5)

30/1 (1)

0

0.388

5

40.029

0.977

71.285

h265

fast

28

416x640 (0.5)

15/1 (0.5)

1

0.252

3

23.366

0.871

3.197

av1

10

34

416x640 (0.5)

15/1 (0.5)

1

0.288

3

23.450

0.872

3.385

h265

medium

28

416x640 (0.5)

30/1 (1)

1

0.268

3

39.197

0.973

64.748

av1

8

34

416x640 (0.5)

30/1 (1)

2

0.335

4

40.391

0.979

72.749

h265

medium

28

416x640 (0.5)

15/1 (0.5)

1

0.255

3

23.272

0.869

3.132

av1

8

34

416x640 (0.5)

15/1 (0.5)

1

0.254

3

23.454

0.874

3.443

h265

slow

28

416x640 (0.5)

30/1 (1)

3

0.290

3

39.804

0.976

69.580

av1

6

34

416x640 (0.5)

30/1 (1)

4

0.332

4

40.611

0.980

74.124

h265

slow

28

416x640 (0.5)

15/1 (0.5)

2

0.271

3

23.343

0.870

3.252

av1

6

34

416x640 (0.5)

15/1 (0.5)

2

0.252

3

23.450

0.874

3.450

h265

fast

31

416x640 (0.5)

30/1 (1)

1

0.204

2

38.239

0.968

57.526

av1

10

38

416x640 (0.5)

30/1 (1)

1

0.304

4

39.529

0.975

67.564

h265

fast

31

416x640 (0.5)

15/1 (0.5)

1

0.195

2

23.364

0.872

3.064

av1

10

38

416x640 (0.5)

15/1 (0.5)

1

0.229

3

23.364

0.871

3.192

h265

medium

31

416x640 (0.5)

30/1 (1)

1

0.205

2

38.064

0.967

57.008

av1

8

38

416x640 (0.5)

30/1 (1)

2

0.267

3

39.797

0.977

69.214

h265

medium

31

416x640 (0.5)

15/1 (0.5)

1

0.195

2

23.277

0.870

2.930

av1

8

38

416x640 (0.5)

15/1 (0.5)

1

0.206

2

23.455

0.875

3.334

h265

slow

31

416x640 (0.5)

30/1 (1)

2

0.216

2

38.754

0.970

63.000

av1

6

38

416x640 (0.5)

30/1 (1)

3

0.267

3

40.139

0.978

71.137

h265

slow

31

416x640 (0.5)

15/1 (0.5)

2

0.206

2

23.336

0.871

3.073

av1

6

38

416x640 (0.5)

15/1 (0.5)

3

0.205

2

23.363

0.873

3.323

Как интерпретировать метрики
  • PSNR (Peak Signal-to-Noise Ratio) — метрика, измеряющая отношение максимального возможного значения сигнала к мощности коррумпирующего шума. Большие значения указывают на лучшее качество сжатия.

  • SSIM (Structural Similarity Index) — метрика, измеряющая структурное сходство между оригинальным и сжатым изображением. Значения ближе к 1 указывают на большее сходство, то есть лучшее качество сжатия.

  • VMAF (Video Multimethod Assessment Fusion) — метрика, объединяющая несколько методов оценки качества видео для получения более точной оценки. Большие значения указывают на лучшее качество сжатия.

Скрипт использованный для тестирования
#!/usr/bin/env fish

# Переход в директорию, где находится скрипт
cd (dirname (status -f))

# Установка переменных для сжатых и оригинальных файлов
set COMPRESSED ./compressed
set ORIGINAL ./original

# Проверка существования оригинальной директории
if not test -d $ORIGINAL
    echo "Оригинальная директория не существует."
    exit 1
end

# Создание директории для сжатых файлов, если она не существует
if not test -d $COMPRESSED
    mkdir $COMPRESSED
end

# Очистка директории для сжатых файлов
rm -r $COMPRESSED/*

# Копирование всех файлов из оригинальной директории в директорию для сжатых файлов
cp -a $ORIGINAL/. $COMPRESSED

# Установка значений CRF для кодеков H.265 и AV1
set crf_h265 28 31
set crf_av1 34 38

# Установка значений масштабирования
set scale_values 1 0.5

# Установка пресетов для кодеков H.265 и AV1
set preset_h265 fast medium slow # veryslow
set preset_av1 10 8 6 # 2 0

# Установка кодеков и их соответствующих энкодеров
set codec h265 av1
set encoder libx265 libsvtav1

# Установка значений масштабирования FPS
set fps_scales 1 0.5

# Установка времени для кодирования
set time 0:05

# Создание псевдонимов для получения FPS, кодека и разрешения видео
alias get_fps="ffprobe -v error -select_streams v:0 -show_entries stream=r_frame_rate -of default=noprint_wrappers=1:nokey=1"
alias get_codec="ffprobe -v error -select_streams v:0 -show_entries stream=codec_name -of default=noprint_wrappers=1:nokey=1"
alias get_resolution="ffprobe -v error -select_streams v:0 -show_entries stream=width,height -of csv=s=x:p=0"

# Функция для получения размера файла в МБ
function get_file_size
    math (stat -f %z "$argv") / 1024 / 1024
end

# Подготовка заголовка для CSV файла с логами сжатия
echo "Name,Codec,Preset,CRF,Scale,FPS,Duration (s),Size (MB),Relative file size (%), PSNR,SSIM,VMAF,Filename" >compression_log.csv

# Цикл по расширениям видеофайлов
for ext in mov mp4
    # Цикл по файлам в директории для сжатых файлов
    for file in (gfind $COMPRESSED -type f -not -name "*_*.*" -iname "*.$ext")
        # Получение FPS, кодека, разрешения и размера исходного файла
        set source_fps (get_fps "$file")
        set source_codec (get_codec "$file")
        set source_resolution (get_resolution "$file")
        set source_file_size (get_file_size "$file")
        set name (path basename $file | path change-extension '')

        # Запись информации об исходном файле в CSV файл
        echo "$name,$source_codec,,,$source_resolution,$source_fps,,$source_file_size,100%,,,,$file" >>compression_log.csv

        # Цикл по значениям масштабирования
        for scale in $scale_values
            # Цикл по значениям CRF для H.265
            for crf_idx in (seq 1 (count $crf_h265))
                # Цикл по пресетам для H.265
                for preset_idx in (seq 1 (count $preset_h265))
                    # Цикл по значениям масштабирования FPS
                    for fps_scale in $fps_scales
                        # Вычисление нового FPS
                        set fps (math $source_fps \* $fps_scale)

                        # Цикл по кодекам
                        for encoder_idx in (seq 1 (count $codec))
                            # Установка кодека и энкодера
                            set cod $codec[$encoder_idx]
                            set enco $encoder[$encoder_idx]

                            # Установка параметров в зависимости от кодека
                            switch $cod
                                case h265
                                    set crf $crf_h265[$crf_idx]
                                    set preset $preset_h265[$preset_idx]
                                    set vtag -vtag hvc1
                                case av1
                                    set crf $crf_av1[$crf_idx]
                                    set preset $preset_av1[$preset_idx]
                                    set vtag
                            end

                            # Формирование нового имени файла
                            set new_file (path change-extension '' $file)_"$cod"_crf="$crf"_preset="$preset"_scale="$scale"_fps="$fps".mp4
                            set start_time (date +%s)

                            # Кодирование видео с указанными параметрами
                            ffmpeg-bar -i "$file" -t $time \
                                -c:v "$enco" -crf $crf \
                                -vf "fps=source_fps*$fps_scale,scale=iw*$scale:-2" \
                                $vtag -preset "$preset" \
                                -hide_banner -loglevel error \
                                "$new_file"

                            # Вычисление времени кодирования
                            set end_time (date +%s)
                            set duration (math $end_time-$start_time)

                            # Получение размера нового файла и относительного размера
                            set file_size (get_file_size "$new_file")
                            set relative_file_size (math -s 0  $file_size / $source_file_size \* 100)%

                            # Получение разрешения и FPS нового файла
                            set output_resolution (get_resolution "$new_file")
                            set output_fps (get_fps "$new_file")

                            # Вычисление метрик качества
                            ffmpeg-quality-metrics $new_file $file --metrics psnr ssim vmaf -p -r (string sub $output_fps -e -2) >log.json

                            # Получение значений метрик качества
                            set psnr_value (jq '.global.psnr.psnr_avg.average' log.json)
                            set ssim_value (jq '.global.ssim.ssim_avg.average' log.json)
                            set vmaf_value (jq '.global.vmaf.vmaf.average' log.json)

                            # Запись результатов в CSV файл
                            echo ",$cod,$preset,$crf,$output_resolution ($scale),$output_fps ($fps_scale),$duration,$file_size,$relative_file_size,$psnr_value,$ssim_value,$vmaf_value,$new_file" >>compression_log.csv
                        end
                    end
                end
            end
        end

        # Удаление временных файлов
        rm {psnr,ssim,vmaf}_stats.log "$file"
    end
end

Заключение

Какой кодек выбрать? Если ваша целевая платформа поддерживает AV1, то выбирайте его. Так как формат AV1 имеет меньшую поддержку, но большую эффективность сжатия чем H.265/HEVC.

Параметр

libx265

libsvtav1

Уменьшение fps в n раз

1 + 0.05n

1 + [0.05, 0.15]n

Уменьшение scale в n раз

n

n

Увеличение crf на n

1 + ≈0.025n

1 + ≈0.025n

Увеличение preset на n

1 - ≈0.025n

1 - ≈0.025n

Таблица: Насколько сильно изменение параметра влияет на результирующий размер файла (От самого качественного до не качественного). Как понимать? В ячейке написано число на которое нужно делить размер файла, то есть $\frac {\text{размер файла}}{\text{значение в ячейке или } f(n)}$

Какое разрешение scale выбрать? Самое маленькое, которое вас удовлетворяет. Так его оно прямо пропорционально размеру файла.

Какое количество кадров fps выбрать? Исходное, так как имеет минимальное влияние на размер файла.

Какой пресет -preset выбрать? medium (libx265) или 8 (libsvtav1). Но можете выбрать самый медленный, который способны терпеть. Так как время перекодирования быстро увеличивается в несколько раз. Игнорируйте placebo (libx265) или 0 (libsvtav1), так как они дают незначительные улучшения за значительное увеличение времени кодирования.

Какое значение CRF -crf выбрать? Значения CRF различаются в зависимости от выбранного пресета. Например для кодировщика libx265 пресет slower генерирует больше сжатия/бит, но может увеличить размер файла по сравнению с medium с одним и тем же значением CRF. Если сравнить ultrafast с veryslow при одинаковом значении CRF, veryslow может создать файл большего размера с лучшим общим сжатием. Например, если -preset ultrafast с -crf 15 генерирует файл сравнимого размера с -preset veryslow с -crf 20, файл с пресетом veryslow будет иметь лучшее качество при том же размере файла.

  • libx265: от 23 до 31 при medium.

  • libsvtav1: от 30 до 38 при 8.

В итоге: Я ужал видео разной степени сжатости в сумме на 30 ГБ в 12 ГБ (почти в 3 раза), без заметной потери в качестве, не сохраняя оригиналы.

Использованная команда:

                # ПРОИЗВОДИТЕЛЬНОСТЬ:
                # - `nice -n 20 ` - снижение приоритета процесса
                # - `cpulimit -l 2` - ограничение использования ЦП. Для меня это занимает 90% ЦП
                sudo nice -n 20 cpulimit -l 2 -i \
                    $ffmpeg -i "$input" \
                    -c:v libx265 -crf 31 -vf "scale='min(iw,1024)':-2" \
                    -pix_fmt yuv420p \
                    -c:a aac -b:a 128k -vtag hvc1 \
                    -y "$output"
                # Если вы используете ffmpeg вместо ffmpeg-bar.
                # Добавьте эти опции для уменьшения вывода логов
                #  -loglevel error -x265-params log-level=error

Сжатие изображений

Есть один кодек эффективнее JPG это AVIF.

Общие параметры для изображений

  • -quality <0-100> - Устанавливает качество изображения, где меньшие значения означают меньшее качество, а большие — лучшее.

Примеры команд

# Конвертация JPG в AVIF
magick input.jpg output.avif
# Конвертация BMP в AVIF с качеством 80
magick input.bmp -quality 80 output.avif

Таблица сравнения кодеков

Имя

Формат

Качество

Размер (МБ)

Относительный размер (%)

AE

DSSIM

FUZZ

MAE

MEPP

MSE

NCC

PAE

PHASH

PSNR

RMSE

SSIM

eye

avif

0,068

14

503 953

0,044

1 570,390

1 178,290

1 791 120 000

37,631

0,994

13 107

1,591

32,409

1 570,390

0,912

eye

avif

0

0,068

14

503 953

0,044

1 570,390

1 178,290

1 791 120 000

37,631

0,994

13 107

1,591

32,409

1 570,390

0,912

eye

avif

1

0,005

1

506 565

0,239

5 232,840

3 679,830

5 593 710 000

417,832

0,935

53 713

13,912

21,955

5 232,840

0,522

eye

avif

25

0,024

5

504 862

0,110

2 833,160

2 066,130

3 140 730 000

122,481

0,981

25 957

3,983

27,284

2 833,160

0,780

eye

avif

50

0,068

14

503 953

0,044

1 570,390

1 178,290

1 791 120 000

37,631

0,994

13 107

1,591

32,409

1 570,390

0,912

eye

avif

75

0,140

29

497 317

0,017

909,873

678,263

1 031 030 000

12,632

0,998

8 224

1,041

37,150

909,873

0,966

eye

avif

100

0,438

92

377 031

0,003

453,631

262,135

398 471 000

3,140

0,999

5 911

1,313

43,195

453,631

0,994

eye

webp

0,676

142

0

0,000

0,000

0,000

0

0,000

1,000

0

0,000

0,000

0,000

1,000

eye

webp

0

0,097

20

503 328

0,033

1 389,280

1 034,390

1 572 370 000

29,451

0,995

10 023

2,106

33,474

1 389,280

0,935

eye

webp

1

0,018

3

506 333

0,162

3 744,970

2 637,210

4 008 830 000

214,005

0,967

32 639

4,981

24,860

3 744,970

0,677

eye

webp

25

0,046

9

505 148

0,077

2 379,720

1 717,390

2 610 600 000

86,412

0,987

20 560

2,679

28,799

2 379,720

0,846

eye

webp

50

0,071

14

504 073

0,050

1 782,190

1 314,580

1 998 290 000

48,466

0,992

14 649

1,538

31,310

1 782,190

0,901

eye

webp

75

0,097

20

503 328

0,033

1 389,280

1 034,390

1 572 370 000

29,451

0,995

10 023

2,106

33,474

1 389,280

0,935

eye

webp

100

0,676

142

0

0,000

0,000

0,000

0

0,000

1,000

0

0,000

0,000

0,000

1,000

eye

jxl

0,149

31

500 688

0,022

1 129,770

771,809

1 173 230 000

19,476

0,996

18 247

0,646

35,270

1 129,770

0,957

eye

jxl

0

0,149

31

500 688

0,022

1 129,770

771,809

1 173 230 000

19,476

0,996

18 247

0,646

35,270

1 129,770

0,957

eye

jxl

1

0,149

31

500 688

0,022

1 129,770

771,809

1 173 230 000

19,476

0,996

18 247

0,646

35,270

1 129,770

0,957

eye

jxl

25

0,032

6

503 549

0,109

2 964,920

2 048,390

3 113 760 000

134,138

0,978

33 924

1,670

26,889

2 964,920

0,783

eye

jxl

50

0,043

9

503 385

0,086

2 537,160

1 775,410

2 698 800 000

98,225

0,984

24 672

4,479

28,242

2 537,160

0,829

eye

jxl

75

0,077

16

502 710

0,050

1 827,870

1 294,900

1 968 380 000

50,982

0,991

19 275

2,444

31,091

1 827,870

0,900

eye

jxl

100

0,613

129

0

0,000

0,000

0,000

0

0,000

1,000

0

0,000

0,000

0,000

1,000

eye

heic

0,106

22

499 596

0,024

1 155,190

857,095

1 302 870 000

20,363

0,997

12 079

3,690

35,076

1 155,190

0,952

eye

heic

0

0,106

22

499 596

0,024

1 155,190

857,095

1 302 870 000

20,363

0,997

12 079

3,690

35,076

1 155,190

0,952

eye

heic

1

0,004

0

505 350

0,256

5 602,910

3 890,270

5 913 600 000

479,020

0,925

53 456

6,704

21,361

5 602,910

0,487

eye

heic

25

0,026

5

504 647

0,104

2 785,230

2 016,690

3 065 580 000

118,372

0,982

23 901

9,343

27,432

2 785,230

0,791

eye

heic

50

0,106

22

499 596

0,024

1 155,190

857,095

1 302 870 000

20,363

0,997

12 079

3,690

35,076

1 155,190

0,952

eye

heic

75

0,275

58

458 907

0,005

547,403

369,373

561 484 000

4,572

0,999

7 196

0,950

41,563

547,403

0,989

eye

heic

100

0,461

97

377 031

0,003

453,631

262,135

398 471 000

3,140

0,999

5 911

1,313

43,195

453,631

0,994

Как интерпретировать метрики
  • AE (Absolute Error) — абсолютная ошибка между оригинальным и сжатым изображением. Меньшие значения указывают на лучшее качество сжатия.

  • DSSIM (Structural Dissimilarity Index) — метрика, измеряющая структурное несходство между изображениями. Значения ближе к 0 указывают на большее сходство, то есть лучшее качество сжатия.

  • FUZZ — метрика, измеряющая степень “размытости” или нечеткости изображения. Меньшие значения указывают на меньшую размытость и лучшее качество сжатия.

  • MAE (Mean Absolute Error) — средняя абсолютная ошибка между пикселями оригинального и сжатого изображений. Меньшие значения указывают на лучшее качество сжатия.

  • MEPP (Mean Edge Pixel Percentage) — средний процент пикселей, расположенных на границах объектов. Значения ближе к оригинальному изображению указывают на лучшее сохранение деталей.

  • MSE (Mean Squared Error) — средняя квадратичная ошибка между пикселями оригинального и сжатого изображений. Меньшие значения указывают на лучшее качество сжатия.

  • NCC (Normalized Cross-Correlation) — нормализованная кросс-корреляция между оригинальным и сжатым изображениями. Значения ближе к 1 указывают на большее сходство и лучшее качество сжатия.

  • PAE (Peak Absolute Error) — максимальная абсолютная ошибка между пикселями оригинального и сжатого изображений. Меньшие значения указывают на лучшее качество сжатия.

  • PHASH (Perceptual Hash) — перцептивный хеш, измеряющий визуальное сходство между изображениями. Меньшие значения указывают на большее сходство и лучшее качество сжатия.

  • PSNR (Peak Signal-to-Noise Ratio) — пиковое соотношение сигнал/шум, измеряющее качество сжатия. Большие значения указывают на лучшее качество сжатия.

  • RMSE (Root Mean Squared Error) — среднеквадратичная ошибка между пикселями оригинального и сжатого изображений. Меньшие значения указывают на лучшее качество сжатия.

  • SSIM (Structural Similarity Index) — структурный индекс сходства, измеряющий визуальное качество сжатия. Значения ближе к 1 указывают на лучшее качество сжатия.

Скрипт использованный для тестирования
#!/usr/bin/env fish

# Переход в директорию, где находится скрипт
cd (dirname (status -f))

# Установка переменных для сжатых и оригинальных файлов
set COMPRESSED ./compressed
set ORIGINAL ./original

# Проверка существования оригинальной директории
if not test -d $ORIGINAL
    echo "Оригинальная директория не существует."
    exit 1
end

# Создание директории для сжатых файлов, если она не существует
if not test -d $COMPRESSED
    mkdir $COMPRESSED
end

# Очистка директории для сжатых файлов
rm -r $COMPRESSED/*

# Копирование всех файлов из оригинальной директории в директорию для сжатых файлов
cp -a $ORIGINAL/. $COMPRESSED

# Установка значений качества для сжатия
set quality_values "" 0 1 25 50 75 100

# Установка форматов изображений для сжатия
set formats avif webp jxl heic

# Функция для получения размера файла в МБ
function get_file_size
    math (stat -f %z "$argv") / 1024 / 1024
end

# Установка метрик для оценки качества сжатия
set metrics AE DSSIM FUZZ MAE MEPP MSE NCC PAE PHASH PSNR RMSE SSIM

# Объединение метрик в одну строку, разделенную запятыми
set joined_metrics (string join "," $metrics)

# Подготовка заголовка для CSV файла с логами сжатия
echo "Name,Format,Quality,Size (MB),Relative Size,$joined_metrics,Filename" >compression_log.csv

# Цикл по расширениям изображений
for ext in png jpg jpeg
    # Цикл по файлам в директории для сжатых файлов
    for file in (gfind $COMPRESSED -type f -not -name "*_*.*" -iname "*.$ext")
        # Получение размера оригинального файла
        set original_size (get_file_size "$file")

        # Цикл по форматам изображений
        for format in $formats
            # Цикл по значениям качества
            for quality in $quality_values
                # Формирование нового имени файла
                set new_file (path change-extension '' $file)_"$format"_quality="$quality".$format
                set start_time (date +%s)

                # Сжатие изображения с указанным качеством
                if test -n "$quality"
                    magick "$file" -quality $quality "$new_file"
                else
                    magick "$file" "$new_file"
                end

                # Вычисление времени сжатия
                set end_time (date +%s)
                set duration (math $end_time-$start_time)

                # Получение размера нового файла
                set file_size (get_file_size "$new_file")

                # Вычисление относительного размера нового файла
                set relative_size (math -s 0 $file_size / $original_size \* 100)%

                # Вычисление метрик качества
                set -e metrics_value
                for metric in $metrics
                    set metric_value (compare -metric $metric "$new_file" "$file" null: 2>&1 | awk '{print $1}')
                    set metrics_value $metrics_value $metric_value
                end

                # Запись результатов в CSV файл
                set name (path basename $file | path change-extension '')
                set metrics_value_joined (string join "," $metrics_value)
                echo "$name,$format,$quality,$file_size,$relative_size,$metrics_value_joined,$new_file" >>compression_log.csv
            end
        end
    end
end

Заключение

Какое качество -quality выбрать? Никакое. Качество картинки и так нормальное.

В итоге: Я ужал изображения в сумме на 10 ГБ в 1 ГБ (в 10 раз), без заметной потери в качестве, без сохранения оригиналов.

Использованная команда:

                magick $input $output

Вывод

Существуют более эффективные альтернативы распространенным сейчас кодекам, которыми можно уже пользоваться или ждать пока они распространятся.

Используя их я смог уменьшить размер медиа файлов на горячем хранилище с ≈150 ГБ в ≈30 ГБ (5 раз). Уменьшив головную боль из за неопределенности определения, что мне нужно, а что можно переместить в холодное хранилище.

            case audio
                # Сжатие аудио Opus с переменным битрейтом
                # ПРИМЕЧАНИЕ: Битрейт 64k обеспечивает хорошее качество для большинства контента
                $ffmpeg -i "$input" \
                    -c:a libopus -b:a 64k -vbr on \
                    -y "$output"

            case video
                # ПРОИЗВОДИТЕЛЬНОСТЬ:
                # - `nice -n 20 ` - снижение приоритета процесса
                # - `cpulimit -l 2` - ограничение использования ЦП. Для меня это занимает 90% ЦП
                sudo nice -n 20 cpulimit -l 2 -i \
                    $ffmpeg -i "$input" \
                    -c:v libx265 -crf 31 -vf "scale='min(iw,1024)':-2" \
                    -pix_fmt yuv420p \
                    -c:a aac -b:a 128k -vtag hvc1 \
                    -y "$output"
                # Если вы используете ffmpeg вместо ffmpeg-bar.
                # Добавьте эти опции для уменьшения вывода логов
                #  -loglevel error -x265-params log-level=error

            case image
                magick $input $output

Как можно использовать это

  • Хранение медиа на устройствах с ограниченным цифровым хранилищем (такие как телефоны)

  • Оттягивание той черты когда придется выгружать данные с горячего хранилища в холодное

  • Дальше сами…

Скрипт для конвертации медиа файлов

# ===================== Утилита сжатия медиа =============================
# Полнофункциональный инструмент для сжатия медиафайлов, поддерживающий аудио, видео и изображения
# Использует современные кодеки (Opus, HEVC, AVIF) с разумными настройками по умолчанию для качества/размера
# ==============================================================================

function compress_media
    # ===================== КОНСТАНТЫ ============================
    # ВНИМАНИЕ: Порядок имеет значение - индексы должны совпадать между этими двумя массивами
    set FILE_TYPES audio video image # Поддерживаемые типы медиа
    set FILE_TYPE_EXTENSIONS opus mp4 avif # Соответствующие расширения файлов для типов медиа

    # ПРИМЕЧАНИЕ: Вы можете использовать разные команды для ffmpeg
    # - ffmpeg - подробный вывод. Измените функцию compress_file для управления уровнем логирования
    # - ffmpeg-bar - индикатор прогресса
    set -g ffmpeg ffmpeg-bar
    # ===================== ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ ============================

    function bold -d "Применить жирное оформление к тексту"

        if not count $argv >/dev/null # Хак: Чтение из pipe в argv
            read --null --list --delimiter=\n argv
        end
        echo "\e[1m$argv\e[22m"
    end

    function yellow -d "Применить желтый цвет к тексту"
        if not count $argv >/dev/null # Хак: Чтение из pipe в argv
            read --null --list --delimiter=\n argv
        end
        echo "\e[0;33m$argv\e[39m"
    end
    function red -d "Применить красный цвет к тексту"
        if not count $argv >/dev/null # Хак: Чтение из pipe в argv
            read --null --list --delimiter=\n argv
        end
        echo "\e[0;31m$argv\e[39m"
    end

    function file_type -a file -d "Определить категорию типа файла из mime-типа"
        set -l mime_type (file --mime-type -b "$file")
        echo (string split '/' $mime_type)[1]
    end

    function compress_file -a input -a output -d "Сжать файл"

        switch (file_type $input)
            case audio
                # Сжатие аудио Opus с переменным битрейтом
                # ПРИМЕЧАНИЕ: Битрейт 64k обеспечивает хорошее качество для большинства контента
                $ffmpeg -i "$input" \
                    -c:a libopus -b:a 64k -vbr on \
                    -y "$output"

            case video
                # ПРОИЗВОДИТЕЛЬНОСТЬ:
                # - `nice -n 20 ` - снижение приоритета процесса
                # - `cpulimit -l 2` - ограничение использования ЦП. Для меня это занимает 90% ЦП
                sudo nice -n 20 cpulimit -l 2 -i \
                    $ffmpeg -i "$input" \
                    -c:v libx265 -crf 31 -vf "scale='min(iw,1024)':-2" \
                    -pix_fmt yuv420p \
                    -c:a aac -b:a 128k -vtag hvc1 \
                    -y "$output"
                # Если вы используете ffmpeg вместо ffmpeg-bar.
                # Добавьте эти опции для уменьшения вывода логов
                #  -loglevel error -x265-params log-level=error

            case image
                magick $input $output
        end

        tput smam # ВНИМАНИЕ: Ручной вызов tput для исправления отключения обертывания терминала из ffmpeg-bar
    end

    function is_compressed -a item -d "Проверить, содержит ли имя файла маркер '.compressed.'"
        string match -q '*.compressed.*' (path basename $item)
    end

    # ===================== Аргументы командной строки ============================
    argparse \
        h/help \
        'm/media-type=+' \
        't/target=' \
        k/keep-original \
        only-media \
        -- $argv

    if not string match -rq "GNU findutils" (find --version 2>/dev/null) # Требуется GNU find, а не BSD
        echo -e (red "Требуется GNU find, а не BSD.")
        return 1
    end
    if set -q _flag_h # Отобразить сообщение справки, если true
        echo "\
compress_media - Сжатие аудио, видео или изображений в указанных директориях

Использование:
  compress_media [ОПЦИИ]... [ИСТОЧНИКИ]...

Описание:
  Рекурсивно сжимает медиафайлы (аудио/видео/изображения) в указанных директориях.
  По умолчанию заменяет оригиналы сжатыми версиями (перемещает в корзину, если не используется -k).
  Используйте --target для сохранения структуры директорий в другом месте.

Опции:
  -h, --help                    Показать это сообщение справки и выйти
  -m, --media-type TYPE         ОБЯЗАТЕЛЬНО. Тип медиа для обработки (audio|video|image|all)
  -t, --target DIR              Директория вывода для сжатых файлов (сохраняет структуру). Относительный путь должен начинаться с `./`, иначе это будет `/`.
  -k, --keep-original           Сохранить оригинальные файлы (не перемещать в корзину)
  --only-media                  Обрабатывать только медиафайлы (не копировать другие файлы в целевую директорию)

Поведение:
  - При использовании --target:
    * Создает зеркальную структуру директорий в целевом месте
    * Копирует немедиафайлы без изменений, если не указано --only-media
    * Хранит сжатые версии медиафайлов в соответствующих директориях

  - Без --target:
    * Сжимает файлы на месте
    * Оригинальные файлы перемещаются в корзину, если не указано --keep-original

Поддерживаемые форматы:
  - audio: конвертируется в OPUS (64kbit/s, VBR)
  - video: конвертируется в H.265 MP4 (CRF 31, ширина 1024px)
  - image: конвертируется в AVIF с использованием настроек ImageMagick по умолчанию

Примеры:
  1. Сжатие аудиофайлов в текущей директории:
     compress_media -m audio

  2. Сжатие видео в ~/Videos, сохранение оригиналов, вывод в ~/Compressed:
     compress_media -m video -t ~/Compressed -k ~/Videos

  3. Сжатие изображений в ~/Pictures, обработка только медиафайлов:
     compress_media -m image --only-media -t ~/Compressed_Images ~/Pictures

Примечания:
  - Требуется ffmpeg, ImageMagick, trash и coreutils (для определения типа файла)
  - Вы должны использовать GNU find на MacOS. Сделайте `alias find=gfind` перед
    запуском скрипта
  - Сжатие видео использует ограничение ЦП для стабильности
  - Сжатые файлы получают расширение .compressed перед фактическим расширением
        "
        return 0
    end

    if not set -q _flag_m # Флаг media-type обязателен
        echo (red Ошибка: опция m/media-type не указана)
        return 1
    else
        for v in $_flag_m # Проверка значения флага media-type
            set ALLOWED_VALUES $FILE_TYPES all
            if not contains $v $ALLOWED_VALUES
                echo (red "Флаг media-type может содержать только значения \
                    из \"$ALLOWED_VALUES\", но не \"$v\".")
                return 1
            end
        end
        contains all "$_flag_m" # Если одно из значений "all"
        and set _flag_m $FILE_TYPES # то установить все типы файлов
    end

    # ===================== Обработка источника и цели ===================================
    if set -q argv[1] # Если есть позиционные аргументы, то они являются источниками
        set SOURCES $argv
    else # Иначе текущая директория является источником
        set SOURCES .
    end

    for source in $SOURCES # Проверка существования исходной директории
        if not test -d $source
            echo Исходная директория $source не существует.
            exit 1
        end
    end

    if set -q _flag_t # Создать целевую директорию, если указана и не существует
        set TARGET $_flag_t

        if not test -d $TARGET
            mkdir $TARGET
        end
    end

    # ===================== Основной цикл обработки ==============================
    for source in $SOURCES

        # Найти все файлы, исключив целевую директорию
        # Хак: `tail -n +2` обрезает исходную директорию (первую строку)
        for item in (find $source -path $TARGET -prune -o -print | tail -n +2)

            # ============= Обработка целевой директории =======================
            if set -q TARGET
                set target_item (string replace -r "^$source/?" "$TARGET/" "$item") # Путь к элементу относительно целевой директории вместо исходной

                test $item -ef $TARGET; and continue # Хак: `find` также перечисляет целевую директорию, если она в источнике, поэтому пропустить её

                if test -d "$item" # Директория
                    and not set -q _flag_only_media
                    echo -en "Создание директории: $(bold $target_item)"

                    test -d $target_item # Создать директорию, если не существует
                    and echo -e \t(yellow Директория (bold $target_item) уже создана)
                    or begin
                        echo
                        mkdir -p $target_item
                    end

                else if contains (file_type $item) $_flag_m # Не сжатый медиафайл
                    and not is_compressed $item

                    set output_extension compressed.$FILE_TYPE_EXTENSIONS[(contains -i (file_type $item) $FILE_TYPES)]
                    set output (path change-extension $output_extension $target_item)

                    set -q _flag_only_media # Хак: Убедиться, что целевая директория существует, если установлен флаг only media. Поскольку изначально структура директорий не создается
                    and mkdir -p (path dirname $output)

                    echo -en Обработка файла (bold $item) в (bold $output)
                    test -e $output
                    and echo -e \t(yellow Файл (bold $output) уже существует)
                    or begin
                        echo
                        compress_file $item $output
                    end

                else if not set -q _flag_only_media # Немедиа или сжатый медиафайл
                    echo -ne "Копирование файла: $(bold $item) в $(path dirname $target_item | bold)"

                    test -e $target_item
                    and echo -e \t(yellow Файл (bold $target_item) уже существует)
                    or begin
                        echo
                        cp "$item" "$target_item"
                    end
                end

                # ============= Обработка на месте ===============================
            else if contains (file_type $item) $_flag_m # Не сжатый медиафайл
                and not is_compressed $item
                set output_extension compressed.$FILE_TYPE_EXTENSIONS[(contains -i (file_type $item) $FILE_TYPES)]
                set output (path change-extension $output_extension $item)

                echo -ne Обработка файла (bold $item) в (path basename $output | bold)
                test -e $output
                and echo -e \t(yellow Файл (bold $item) уже существует)
                or begin
                    echo
                    compress_file $item $output
                    and not set -q _flag_k
                    and begin
                        echo Перемещение $item в корзину
                        trash $item # MacOS
                        # trash-put $item # Linux
                    end
                end
            end
        end
    end
end
На английском
# ===================== Media Compression Utility =============================
# A comprehensive media compression tool supporting audio, video and image files
# Uses modern codecs (Opus, HEVC, AVIF) with sensible defaults for quality/size
# ==============================================================================

function compress_media
    # ===================== CONSTANTS ============================
    # WARN: Order matters - indexes must match between these two arrays
    set FILE_TYPES audio video image # Supported media types 
    set FILE_TYPE_EXTENSIONS opus mp4 avif # Corresponding to media types file extensions

    # NOTE: You can use different command for ffmpeg
    # - ffmpeg - verbose output. Change compress_file function to controll loglevel
    # - ffmpeg-bar - progress bar
    set -g ffmpeg ffmpeg-bar
    # ===================== HELPER FUNCTIONS ============================

    function bold -d "Apply bold styling to text"

        if not count $argv >/dev/null # HACK: Read pipe to argv
            read --null --list --delimiter=\n argv
        end
        echo "\e[1m$argv\e[22m"
    end

    function yellow -d "Apply yellow color to text"
        if not count $argv >/dev/null # HACK: Read pipe to argv
            read --null --list --delimiter=\n argv
        end
        echo "\e[0;33m$argv\e[39m"
    end
    function red -d "Apply red color to text"
        if not count $argv >/dev/null # HACK: Read pipe to argv
            read --null --list --delimiter=\n argv
        end
        echo "\e[0;31m$argv\e[39m"
    end

    function file_type -a file -d "Determine file type category from mime-type"
        set -l mime_type (file --mime-type -b "$file")
        echo (string split '/' $mime_type)[1]
    end

    function compress_file -a input -a output -d "Compress file"


        switch (file_type $input)
            case audio
                # Opus audio compression with variable bitrate
                # NOTE: 64k bitrate provides good quality for most content
                $ffmpeg -i "$input" \
                    -c:a libopus -b:a 64k -vbr on \
                    -y "$output"

            case video
                # PERF:
                # - `nice -n 20 ` - lower priority of process
                # - `cpulimit -l 2` - limit CPU usage. For me it take 90% of CPU
                sudo nice -n 20 cpulimit -l 2 -i \
                    $ffmpeg -i "$input" \
                    -c:v libx265 -crf 31 -vf "scale='min(iw,1024)':-2" \
                    -pix_fmt yuv420p \
                    -c:a aac -b:a 128k -vtag hvc1 \
                    -y "$output"
                # If you use use ffmpeg instead ffmpeg-bar.
                # Add this options to reduce log output
                #  -loglevel error -x265-params log-level=error

            case image
                magick $input $output
        end

        tput smam # WARN: Manual tput call to fix disabling terminal wrapping from ffmpeg-bar
    end

    function is_compressed -a item -d "Check if filename contains '.compressed.' marker"
        string match -q '*.compressed.*' (path basename $item)
    end

    # ===================== Command Line Arguments ============================
    argparse \
        h/help \
        'm/media-type=+' \
        't/target=' \
        k/keep-original \
        only-media \
        -- $argv

    if not string match -rq "GNU findutils" (find --version 2>/dev/null) # Require GNU find not BSD
        echo -e (red "Required GNU find not BSD.")
        return 1
    end
    if set -q _flag_h # Display help message if true
        echo "\
compress_media - Compress audio, video, or image files in specified directories

Usage:
  compress_media [OPTIONS]... [SOURCES]...

Description:
  Recursively compresses media files (audio/video/image) in specified directories.
  By default replaces originals with compressed versions (moved to trash unless -k is used).
  Use --target to preserve directory structure in a different location.

Options:
  -h, --help                    Show this help message and exit
  -m, --media-type TYPE         REQUIRED. Media type to process (audio|video|image|all)
  -t, --target DIR              Output directory for compressed files (preserves structure). Relative path must start with `./` otherwise it will be `/`.
  -k, --keep-original           Keep original files (don't move to trash)
  --only-media                  Only process media files (don't copy other files to target)

Behavior:
  - When using --target:
    * Creates mirrored directory structure in target location
    * Copies non-media files unchanged unless --only-media is specified
    * Stores compressed versions of media files in corresponding directories
  
  - Without --target:
    * Compresses files in-place
    * Original files are moved to trash unless --keep-original is specified

Supported Formats:
  - audio: converts to OPUS (64kbit/s, VBR)
  - video: converts to H.265 MP4 (CRF 31, 1024px width)
  - image: converts to AVIF using ImageMagick default settings

Examples:
  1. Compress audio files in current directory:
     compress_media -m audio

  2. Compress videos in ~/Videos, keep originals, output to ~/Compressed:
     compress_media -m video -t ~/Compressed -k ~/Videos

  3. Compress images in ~/Pictures, only process media files:
     compress_media -m image --only-media -t ~/Compressed_Images ~/Pictures

Notes:
  - Requires ffmpeg, ImageMagick, trash, and coreutils (for file type detection)
  - You must use GNU find on MacOS. Make `alias find=gfind` before 
    running script
  - Video compression uses CPU limiting for stability
  - Compressed files get .compressed extension before the actual extension
        "
        return 0
    end


    if not set -q _flag_m # Flag media-type required
        echo (red Error: option m/media-type not specified)
        return 1
    else
        for v in $_flag_m # Validate value of flag media-type
            set ALLOWED_VALUES $FILE_TYPES all
            if not contains $v $ALLOWED_VALUES
                echo (red Flag media-type can contain only values \
                    from \"$ALLOWED_VALUES\", but not \"$v\".)
                return 1
            end
        end
        contains all "$_flag_m" # If one of the value "all" 
        and set _flag_m $FILE_TYPES # then set to all file types
    end

    # ===================== Source and Target Handling ===================================
    if set -q argv[1] # If there are positional args, then they are sources
        set SOURCES $argv
    else # Else current dir is source
        set SOURCES .
    end

    for source in $SOURCES # Verify source dir existance
        if not test -d $source
            echo Source directory $source not exist.
            exit 1
        end
    end

    if set -q _flag_t # Create target directory if specified and doesn't exist
        set TARGET $_flag_t

        if not test -d $TARGET
            mkdir $TARGET
        end
    end

    # ===================== Main Processing Loop ==============================
    for source in $SOURCES

        # Find all files while excluding target directory
        # HACK: `tail -n +2` trim source dir (first line)
        for item in (find $source -path $TARGET -prune -o -print | tail -n +2)

            # ============= Target Directory Processing =======================
            if set -q TARGET
                set target_item (string replace -r "^$source/?" "$TARGET/" "$item") # Path to item relative to target dir instead of source


                test $item -ef $TARGET; and continue # HACK: `find` also list target dir if it in source, so skip it


                if test -d "$item" # Dir
                    and not set -q _flag_only_media
                    echo -en "Creating directory: $(bold $target_item)"

                    test -d $target_item # Create dir if not exist
                    and echo -e \t(yellow Directory (bold $target_item) already created)
                    or begin
                        echo
                        mkdir -p $target_item
                    end


                else if contains (file_type $item) $_flag_m # Not compressed Media
                    and not is_compressed $item

                    set output_extension compressed.$FILE_TYPE_EXTENSIONS[(contains -i (file_type $item) $FILE_TYPES)]
                    set output (path change-extension $output_extension $target_item)


                    set -q _flag_only_media # HACK: Ensure output directory exists, when flag only media. Cause initialy dir structure not created
                    and mkdir -p (path dirname $output)


                    echo -en Processing file (bold $item) into (bold $output)
                    test -e $output
                    and echo -e \t(yellow File (bold $output) already exist)
                    or begin
                        echo
                        compress_file $item $output
                    end


                else if not set -q _flag_only_media # Non-media or compressed media
                    echo -ne "Copying file: $(bold $item) to $(path dirname $target_item | bold)"

                    test -e $target_item
                    and echo -e \t(yellow File (bold $target_item) already exist)
                    or begin
                        echo
                        cp "$item" "$target_item"
                    end
                end

                # ============= In-place Processing ===============================
            else if contains (file_type $item) $_flag_m # Not compressed media
                and not is_compressed $item
                set output_extension compressed.$FILE_TYPE_EXTENSIONS[(contains -i (file_type $item) $FILE_TYPES)]
                set output (path change-extension $output_extension $item)

                echo -ne Processing file (bold $item) into (path basename $output | bold)
                test -e $output
                and echo -e \t(yellow File (bold $item) already exist)
                or begin
                    echo
                    compress_file $item $output
                    and not set -q _flag_k
                    and begin
                        echo Move $item to trash
                        trash $item # MacOS
                        # trash-put $item # Linux
                    end
                end
            end
        end
    end
end

Установка зависимостей

### Debian/Ubuntu (apt)
sudo apt update && sudo apt install -y \
  ffmpeg imagemagick file trash-cli cpulimit findutils coreutils \
  ffmpeg-bar fish

### macOS (Homebrew)
brew install \
  ffmpeg imagemagick findutils coreutils cpulimit trash \
  ffmpeg-bar fish
# For GNU find (required)
echo "alias find=gfind" >> ~/.config/fish/config.fish

Источники

Отзывы, предложения и размышления

Можете использовать для обратной связи мою почту dalokoschestmat@gmail.com или комментарии

  • Обнаружили ошибку? Если вы заметили ошибку или неточность, пожалуйста, сообщите нам об этом в комментариях.

  • Что то было непонятно и размыто? Если в статье что-то осталось неясным или упомянуто вкратце из за чего трудно понятен смысл, дайте нам знать.

  • Код может более элегантным? Если вы можете написать более элегантное решение — пишите!

  • Общие мысли? Если у вас есть какие-то идеи, мысли или предложения, которые помогут статье, поделитесь ими.

Важно: Если у вас есть какое-то негативное ощущение от статьи выражайте это конструктивной критикой, если решились выражать. От 3 язвительных слов мало кто-то что осознает. Так же постарайтесь не писать в стиле претензии-вопроса, а больше в стиле предложения или вопроса.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Кодирование медиа файлов
50% Я знаю какой используется кодек1
50% Я сам кодировал их1
100% Я предпочитаю использовать lossless2
50% Использую самые эффективные кодеки1
Проголосовали 2 пользователя. Воздержавшихся нет.
Теги:
Хабы:
Всего голосов 13: ↑12 и ↓1+14
Комментарии20

Публикации

Ближайшие события