Теория и практика фильтров ffmpeg
Теория и практика фильтров ffmpeg
Каталог статей
  • Предисловие
  • один, DirectShow
    • 1. Введение
    • 2. Базовая структура программы
    • 3. Архитектура
  • 2. Фильтр
    • 1. Видеофильтр -vf
    • 2. Аудио фильтр -af
    • 3. Цепочка фильтров
    • 4. График фильтра
      • ①.Основная грамматика
      • ② Классификация фильтрграфа.
    • 5. Схема взаимоотношений между структурами
  • 3. Фильтрация практических примеров
    • 1. Пример исходного кода
    • 2. Результаты операции

Предисловие

фильтр ffmpeg, конечно, некоторые называют его фильтром ffmpeg. (Использование фильтров похоже на видео, поэтому это нехорошо, потому что аудио также можно использовать). В каталоге ffmpeg есть папка libavfilter, которую можно отдельно скомпилировать в библиотеку. Для чего он используется? Используется для фильтрации аудио и видео. Например, у меня есть mp4, и я хочу уменьшить его вдвое и вывести новый mp4. Затем сокращение выполняет libavfilter.

В этой статье проводится теоретическое исследование и практика использования фильтров ffmpeg.


один, DirectShow

Прежде чем изучать фильтр ffmpeg (фильтр), нам нужно сначала понять DirectShow, чтобы было удобнее и проще понять, когда мы позже изучим фильтр ffmpeg.

1. Введение

DirectShow (сокращенно DShow) — это платформа потокового мультимедиа на платформе Windows.,Обеспечивает высокое качествоизФункция сбора и воспроизведения мультимедийного потока。Он поддерживает различныеизформат медиа-файла,включать ASF、MPEG、AVI、MP3 и WAV файлы, поддерживая при этом использование WDM водитель или рано VFW Драйвер для сбора мультимедийных потоков.

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

DirectShow разработан для C++. Microsoft не предоставляет управляемый API для DirectShow.

DirectShow основан на объектной модели компонентов (COM), поэтому при написании приложения DirectShow вам необходимо обладать знаниями в области программирования COM-клиентов. Для большинства приложений вам не нужно реализовывать свои собственные COM-объекты. DirectShow предоставляет большинство необходимых вам компонентов DirectShow. Однако если вам нужно написать свои собственные компоненты DirectShow для расширения, вам придется написать и реализовать COM-объекты.

использовать DirectShow Типичные написанные приложения включают в себя:DVD Плееры, программы для редактирования видео, AVI приезжать ASF Конвертер, MP3 Следует использовать плеер и коллекцию цифрового видео.

2. Базовая структура программы

Базовая структура программы DirectShow показана на рисунке ниже:

3. Архитектура

Архитектура DirectShow показана на рисунке ниже:

  • DirectShow Расположен в Иниспользуйтеслой. Это использование называется Filter Graph из модели для управления всем потоком данных из процесса обработки см. и обработка данных из каждого функционального модуля называется; Filter;<font color=>каждый Filter существовать Filter Graph соединены в определенном порядке, образуя линию «сборочная линия» Работайте совместно. (Это можно увидеть FilterGraph да Filter контейнер )
  • Разделено по функциям, Фильтр условно разделен на три категории:Source FiltersTransform Filters и Rendering(sink) Filters
    • Source Filters Основная ответственность за получение данных,Источниками могут быть файлы, Интернет или компьютеры, карты захвата, цифровые камеры и т. д.,Затем передайте данные вниз;
    • Transform Fitlers В основном отвечает за преобразование и передачу формата данных;
    • Rendering Filtes Основная ответственность за окончательное местонахождение данных.,Мы можем отправлять данные на звуковые карты и видеокарты для мультимедийных демонстраций.,Вы также можете экспортировать файлы для хранения.

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

существовать DirectShow В системе мы смотрим на приезжатьиз, то есть на то, чтобы мы использовали программу (приложение). Прикладная программа должна установить соответствующее приложение в соответствии с определенным намерением. Filter График, затем передать Filter Graph Manager контролировать весь процесс обработки данных. Директ Шоу способныйсуществовать Filter Graph При запуске мы получаем различные события и передаем сообщения, отправленные в нашу программу прибытия. Таким образом реализуется прикладная программаuse. DirectShow взаимодействия между системами.

DirectShow использование называется Filter Graph модель управления всем процессом обработки потока данных называется каждым функциональным модулем, участвующим в обработке данных; Фильтровать каждый; Filter существовать Filter Graph соединены в определенном порядке, образуя линия «сборочного конвейера» совместной работы. Фильтр, который является самым основным компонентом программного обеспечения, обычно выполняет операцию в мультимедийном потоке. каждый Filterсуществовать Filter Graph соединены в определенном порядке, образуя линию"сборочная линия"совместная работа。нравиться использовать теорию графов из описания терминов, граф фильтра и направленный、Нет цикла、Несвязный граф. Между фильтрами имеется поток в заданном направлении Нет; цикладаозначает, что нет пути, по которому можно идтиодинфильтры старт и возвратприезжатьсам;и Отключенода Укажи нетдавсеиз Фильтры могут достигатьприезжатьвсе Другие фильтры。

2. Фильтр

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

Пример: [входная_ссылка_метка1]… имя_фильтра=параметры [выходная_ссылка_метка1]…

1. Видеофильтр -vf

Например, видео input.mp4 поворачивается на 90 градусов по часовой стрелке.

Язык кода:javascript
копировать
ffplay -i input.mp4 -vf transpose=1

Например, видео input.mp4 перевернуто по горизонтали (влево и вправо).

Язык кода:javascript
копировать
ffplay -i input.mp4 -vf hflip

2. Аудио фильтр -af

Достичь замедленного воспроизведения, скорость звука составляет 50% от исходной скорости.

Язык кода:javascript
копировать
ffplay input.mp3 -af atempo=0.5

3. Цепочка фильтров

Filterchain = набор фильтров, разделенных запятыми.

Перевод: «фильтр1,фильтр2,фильтр3,…фильтрN-2,фильтрN-1,фильтрN»

Поворот на 90 градусов по часовой стрелке и переворот по горизонтали

Язык кода:javascript
копировать
ffplay -i input.mp4 -vf transpose=1,hflip

4. График фильтра

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

Шаг 1. Удвойте ширину исходного видео.

Язык кода:javascript
копировать
ffmpeg -i input.mp4 -t 10 -vf pad=2*iw output.mp4

Шаг 2. Переверните исходное видео по горизонтали.

Язык кода:javascript
копировать
ffmpeg -i input.mp4 -t 10 -vf hflip output2.mp4

Шаг 3. Переверните видео по горизонтали поверх файла output.mp4.

Язык кода:javascript
копировать
ffmpeg -i output.mp4 -i output2.mp4 -filter_complex overlay=w compare.mp4

Далее мы используем граф фильтра для достижения эффектов, достигаемых тремя вышеуказанными командами.

①.Основная грамматика
Язык кода:javascript
копировать
Filtergraph = Точка с запятой отделяет группу filterchain
“filterchain1;filterchain2;…filterchainN-1;filterchainN”

Чтобы реализовать три вышеуказанных шага с помощью графа фильтра с маркерами ссылок, вам понадобится всего одна команда:

Язык кода:javascript
копировать
ffmpeg -i test.mp4 -t 10 -vf "split[a][b];[a]pad=2*iw[1];[b]hflip[2];[1][2]overlay=w" output.mp4
  • Разделительный фильтр создает две копии входного файла с метками [a],[b].
  • [a] как pad входной фильтр, пэд фильтр генерирует 2 Двойная ширина и мощность проживания [1]
  • [b] как hflip входной фильтр, vflip Фильтр переворачивает видео по горизонтали и выводит: приезжать. [2]
  • использовать overlay ручка фильтра [2] крышкаприезжать [1] рядом с
② Классификация фильтрграфа.
  • Просто: один в один
  • Комплекс: многие-к-одному, многие-ко-многим.

Простой процесс обработки графа фильтра:

Схема обработки сложного графа фильтра:

Из рисунка видно, что сложных графов фильтров меньше, чем простых графов фильтров. 2 шаги, более эффективные, чем простые, ffmpeg Рекомендуется попробовать как можно большеиспользовать Сложный граф фильтра。

5. Схема взаимоотношений между структурами

Структуры, участвующие в фильтре, в основном включают в себя:

  • FilterGraph, AVFilterGraph
  • InputFilter, InputStream, OutputFilter, OutputStream
  • AVFilter,AVFilterContext
  • AVFilterLink
  • AVFilterPad

Отношения классов между ними показаны на рисунке ниже:

На картинке выше вы можете видеть, как приезжать, FFmpeg Структура фильтра состоит из трех слоев:

  • слой фильтрграфа
    • по структуре FilterGraph,AVFilterGraph состав;
    • Среди них FilterGraph:
      • содержит InputFilter, который указывает весь Graph первый фильтр и указывает InputStream, весь из икак Graph из ввода;
      • содержит OutputFilter, который указывает весь Graph последний фильтр и указывает OutputStream, из икак всего Graph из Выход;
      • содержит AVFilterGraph экземпляр Указывает стоимость группы изда graph из filter;
  • слой цепочки фильтров
    • он состоит из AVFilter,AVFilterContext,AVFilterLink,AVFilterPad состав;где, AVFilterContext да AVFilter из Пример;
      • и filter между даиспользовать AVFilterLink Подключить, то есть да, фильтры не подключаются напрямую даиз, дапроходить AVFilterLink Установить связь;
      • AVFilterContext проходить AVFilterLink После установления соединения образуется Filterchain。
      • и AVFilterContext и AVFilterLink между AVFilterPad да напрямую связано с из, что соответствует отношению из да: AVFilterContext из output_pad подключите его ниже по течению AVFilterLink из srcpad;AVFilterContext из input_pad Подключите его выше по течению AVFilterLink из dstpad;
  • фильтрующий слой
    • Зависит от AVFilterContext,AVFilterPad состав;Чтосередина AVFilterContext да Реально выполнять обработку данных из объекта фильтра;
    • AVFilterPad использовать В AVFilterContext между перезвонить:
      • первый AVFilterContext из outputs[0] указатель,ориентированныйпервый AVFilterLink, этот AVFilterLink из dst указатель,укажи на второй AVFilterContext。
      • нравитьсяфруктысуществоватьвпередодининдивидуальный AVFilterContext настраиватьиспользовать outputs[0]->dstpad->filter_frame(Frame* input_frame1), Фактически это означает, что первый фильтр хорошо справляется с кадр (имя input_frame1), следуетэтотиспользование передается второму фильтру из input_pads из filter_frame функция. и Второй фильтр, который реализуется самим пользователем из filter_frame(), для обработки данных;

3. Фильтрация практических примеров

Код ниже декодирует видеокадры и передает их в граф фильтра для обработки, а затем записывает обработанные кадры в файл. Строка описания фильтра filter_descr Указана операция фильтра, в данном случае используйте scale и hflip Приобретите правильный фильтрвидео выполнить масштабирование и горизонтальное переворот。финальный,Программа преобразует обработанные кадры в формат записи в файл YUV420P.

1. Пример исходного кода

Язык кода:javascript
копировать
/**
 * @file
 * API example for decoding and filtering
 * @example filtering_video.c
 */

#define _XOPEN_SOURCE 600 /* for usleep */
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h>
#include <libavutil/opt.h>

const char *filter_descr = "scale=640:480,hflip";
/* other way:
   scale=78:24 [scl]; [scl] transpose=cclock // assumes "[in]" and "[out]" to be input output pads respectively
 */

static AVFormatContext *fmt_ctx;
static AVCodecContext *dec_ctx;
AVFilterContext *buffersink_ctx;
AVFilterContext *buffersrc_ctx;
AVFilterGraph *filter_graph;
static int video_stream_index = -1;
static int64_t last_pts = AV_NOPTS_VALUE;

static int open_input_file(const char *filename)
{
    int ret;
    AVCodec *dec;

    if ((ret = avformat_open_input(&fmt_ctx, filename, NULL, NULL)) < 0) {
        av_log(NULL, AV_LOG_ERROR, "Cannot open input file\n");
        return ret;
    }

    if ((ret = avformat_find_stream_info(fmt_ctx, NULL)) < 0) {
        av_log(NULL, AV_LOG_ERROR, "Cannot find stream information\n");
        return ret;
    }

    /* select the video stream */
    ret = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &dec, 0);
    if (ret < 0) {
        av_log(NULL, AV_LOG_ERROR, "Cannot find a video stream in the input file\n");
        return ret;
    }
    video_stream_index = ret;

    /* create decoding context */
    dec_ctx = avcodec_alloc_context3(dec);
    if (!dec_ctx)
        return AVERROR(ENOMEM);
    avcodec_parameters_to_context(dec_ctx, fmt_ctx->streams[video_stream_index]->codecpar);

    /* init the video decoder */
    if ((ret = avcodec_open2(dec_ctx, dec, NULL)) < 0) {
        av_log(NULL, AV_LOG_ERROR, "Cannot open video decoder\n");
        return ret;
    }

    return 0;
}

static int init_filters(const char *filters_descr)
{
    char args[512];
    int ret = 0;
    const AVFilter *buffersrc  = avfilter_get_by_name("buffer");
    const AVFilter *buffersink = avfilter_get_by_name("buffersink");
    AVFilterInOut *outputs = avfilter_inout_alloc();
    AVFilterInOut *inputs  = avfilter_inout_alloc();
    AVRational time_base = fmt_ctx->streams[video_stream_index]->time_base;
    enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_GRAY8, AV_PIX_FMT_NONE };

    filter_graph = avfilter_graph_alloc();
    if (!outputs || !inputs || !filter_graph) {
        ret = AVERROR(ENOMEM);
        goto end;
    }

    /* buffer video source: the decoded frames from the decoder will be inserted here. */
    snprintf(args, sizeof(args),
            "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
            dec_ctx->width, dec_ctx->height, dec_ctx->pix_fmt,
            time_base.num, time_base.den,
            dec_ctx->sample_aspect_ratio.num, dec_ctx->sample_aspect_ratio.den);

    ret = avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in",
                                       args, NULL, filter_graph);
    if (ret < 0) {
        av_log(NULL, AV_LOG_ERROR, "Cannot create buffer source\n");
        goto end;
    }

    /* buffer video sink: to terminate the filter chain. */
    ret = avfilter_graph_create_filter(&buffersink_ctx, buffersink, "out",
                                       NULL, NULL, filter_graph);
    if (ret < 0) {
        av_log(NULL, AV_LOG_ERROR, "Cannot create buffer sink\n");
        goto end;
    }

    /*
     * Set the endpoints for the filter graph. The filter_graph will
     * be linked to the graph described by filters_descr.
     */

    /*
     * The buffer source output must be connected to the input pad of
     * the first filter described by filters_descr; since the first
     * filter input label is not specified, it is set to "in" by
     * default.
     */
    outputs->name       = av_strdup("in");
    outputs->filter_ctx = buffersrc_ctx;
    outputs->pad_idx    = 0;
    outputs->next       = NULL;

    /*
     * The buffer sink input must be connected to the output pad of
     * the last filter described by filters_descr; since the last
     * filter output label is not specified, it is set to "out" by
     * default.
     */
    inputs->name       = av_strdup("out");
    inputs->filter_ctx = buffersink_ctx;
    inputs->pad_idx    = 0;
    inputs->next       = NULL;

    if ((ret = avfilter_graph_parse_ptr(filter_graph, filters_descr,
                                    &inputs, &outputs, NULL)) < 0)
        goto end;

    if ((ret = avfilter_graph_config(filter_graph, NULL)) < 0)
        goto end;

end:
    avfilter_inout_free(&inputs);
    avfilter_inout_free(&outputs);

    return ret;
}

/// Запись кадров yuv в файл: формат yuv420p.
FILE * g__file_fd;
static void write_frame(const AVFrame *frame)
{
    static int printf_flag = 0;
    if(!printf_flag){
        printf_flag = 1;
        printf("frame widht=%d,frame height=%d\n",frame->width,frame->height);

        if(frame->format==AV_PIX_FMT_YUV420P){
            printf("format is yuv420p\n");
        }
        else{
            printf("formet is = %d \n",frame->format);
        }

    }

    fwrite(frame->data[0],1,frame->width*frame->height,g__file_fd);
    fwrite(frame->data[1],1,frame->width/2*frame->height/2,g__file_fd);
    fwrite(frame->data[2],1,frame->width/2*frame->height/2,g__file_fd);
}

int main(int argc, char **argv)
{
    int ret;
    AVPacket packet;
    AVFrame *frame;
    AVFrame *filt_frame;

    g__file_fd = fopen("./debug/test.yuv", "w");
    frame = av_frame_alloc();
    filt_frame = av_frame_alloc();
    if (!frame || !filt_frame) {
        perror("Could not allocate frame");
        exit(1);
    }

    if ((ret = open_input_file("./debug/test.flv")) < 0)
        goto end;
    if ((ret = init_filters(filter_descr)) < 0)
        goto end;

    /* read all packets */
    while (1) {
        if ((ret = av_read_frame(fmt_ctx, &packet)) < 0)
            break;

        if (packet.stream_index == video_stream_index) {
            ret = avcodec_send_packet(dec_ctx, &packet);
            if (ret < 0) {
                av_log(NULL, AV_LOG_ERROR, "Error while sending a packet to the decoder\n");
                break;
            }

            while (ret >= 0) {
                ret = avcodec_receive_frame(dec_ctx, frame);
                if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
                    break;
                } else if (ret < 0) {
                    av_log(NULL, AV_LOG_ERROR, "Error while receiving a frame from the decoder\n");
                    goto end;
                }

                frame->pts = frame->best_effort_timestamp;  // Установите эту временную метку на кадр из временной метки дисплея.

                /* push the decoded frame into the filtergraph */
                if (av_buffersrc_add_frame_flags(buffersrc_ctx, frame, AV_BUFFERSRC_FLAG_KEEP_REF) < 0) {
                    av_log(NULL, AV_LOG_ERROR, "Error while feeding the filtergraph\n");
                    break;
                }

                /* pull filtered frames from the filtergraph */
                while (1) {
                    ret = av_buffersink_get_frame(buffersink_ctx, filt_frame);
                    if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
                        break;
                    if (ret < 0)
                        goto end;

                    write_frame(filt_frame);
                    av_frame_unref(filt_frame);
                }
                av_frame_unref(frame);
            }
        }
        av_packet_unref(&packet);
    }
end:
    avfilter_graph_free(&filter_graph);
    avcodec_free_context(&dec_ctx);
    avformat_close_input(&fmt_ctx);
    av_frame_free(&frame);
    av_frame_free(&filt_frame);
    fclose(g__file_fd);
    if (ret < 0 && ret != AVERROR_EOF) {
        fprintf(stderr, "Error occurred: %s\n", av_err2str(ret));
        exit(1);
    }

    exit(0);
}

2. Результаты операции

Распечатка выглядит следующим образом:

Язык кода:javascript
копировать
frame widht=640,frame height=480
format is yuv420p

Сгенерированный исходный файл видеоданных test.yuv намного больше, чем ранее сжатый файл test.flv.

использовать yuvplayer.exe Воспроизведение создано test.yuv Файл можно просмотреть ниже:


boy illustration
Учебное пособие по Jetpack Compose для начинающих, базовые элементы управления и макет
boy illustration
Код js веб-страницы, фон частицы, код спецэффектов
boy illustration
【новый! Суперподробное】Полное руководство по свойствам компонентов Figma.
boy illustration
🎉Обязательно к прочтению новичкам: полное руководство по написанию мини-программ WeChat с использованием программного обеспечения Cursor.
boy illustration
[Забавный проект Docker] VoceChat — еще одно приложение для мгновенного чата (IM)! Может быть встроен в любую веб-страницу!
boy illustration
Как реализовать переход по странице в HTML (html переходит на указанную страницу)
boy illustration
Как решить проблему зависания и низкой скорости при установке зависимостей с помощью npm. Существуют ли доступные источники npm, которые могут решить эту проблему?
boy illustration
Серия From Zero to Fun: Uni-App WeChat Payment Practice WeChat авторизует вход в систему и украшает страницу заказа, создает интерфейс заказа и инициирует запрос заказа
boy illustration
Серия uni-app: uni.navigateЧтобы передать скачок значения
boy illustration
Апплет WeChat настраивает верхнюю панель навигации и адаптируется к различным моделям.
boy illustration
JS-время конвертации
boy illustration
Обеспечьте бесперебойную работу ChromeDriver 125: советы по решению проблемы chromedriver.exe не найдены
boy illustration
Поле комментария, щелчок мышью, специальные эффекты, js-код
boy illustration
Объект массива перемещения объекта JS
boy illustration
Как открыть разрешение на позиционирование апплета WeChat_Как использовать WeChat для определения местонахождения друзей
boy illustration
Я даю вам два набора из 18 простых в использовании фонов холста Power BI, так что вам больше не придется возиться с цветами!
boy illustration
Получить текущее время в js_Как динамически отображать дату и время в js
boy illustration
Вам необходимо изучить сочетания клавиш vsCode для форматирования и организации кода, чтобы вам больше не приходилось настраивать формат вручную.
boy illustration
У ChatGPT большое обновление. Всего за 45 минут пресс-конференция показывает, что OpenAI сделал еще один шаг вперед.
boy illustration
Copilot облачной разработки — упрощение разработки
boy illustration
Микросборка xChatGPT с низким кодом, создание апплета чат-бота с искусственным интеллектом за пять шагов
boy illustration
CUDA Out of Memory: идеальное решение проблемы нехватки памяти CUDA
boy illustration
Анализ кластеризации отдельных ячеек, который должен освоить каждый&MarkerгенетическийВизуализация
boy illustration
vLLM: мощный инструмент для ускорения вывода ИИ
boy illustration
CodeGeeX: мощный инструмент генерации кода искусственного интеллекта, который можно использовать бесплатно в дополнение к второму пилоту.
boy illustration
Машинное обучение Реальный бой LightGBM + настройка параметров случайного поиска: точность 96,67%
boy illustration
Бесшовная интеграция, мгновенный интеллект [1]: платформа больших моделей Dify-LLM, интеграция без кодирования и встраивание в сторонние системы, более 42 тысяч звезд, чтобы стать свидетелями эксклюзивных интеллектуальных решений.
boy illustration
LM Studio для создания локальных больших моделей
boy illustration
Как определить количество слоев и нейронов скрытых слоев нейронной сети?
boy illustration
[Отслеживание целей] Подробное объяснение ByteTrack и детали кода