фильтр ffmpeg, конечно, некоторые называют его фильтром ffmpeg. (Использование фильтров похоже на видео, поэтому это нехорошо, потому что аудио также можно использовать). В каталоге ffmpeg есть папка libavfilter, которую можно отдельно скомпилировать в библиотеку. Для чего он используется? Используется для фильтрации аудио и видео. Например, у меня есть mp4, и я хочу уменьшить его вдвое и вывести новый mp4. Затем сокращение выполняет libavfilter.
В этой статье проводится теоретическое исследование и практика использования фильтров ffmpeg.
Прежде чем изучать фильтр ffmpeg (фильтр), нам нужно сначала понять DirectShow, чтобы было удобнее и проще понять, когда мы позже изучим фильтр ffmpeg.
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 Следует использовать плеер и коллекцию цифрового видео.
Базовая структура программы DirectShow показана на рисунке ниже:
Архитектура DirectShow показана на рисунке ниже:
Source Filters
、Transform 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 соединены в определенном порядке, образуя линию"сборочная линия"совместная работа。
нравиться использовать теорию графов из описания терминов, граф фильтра и направленный、Нет цикла、Несвязный граф. Между фильтрами имеется поток в заданном направлении Нет; цикладаозначает, что нет пути, по которому можно идтиодинфильтры старт и возвратприезжатьсам;и Отключенода Укажи нетдавсеиз Фильтры могут достигатьприезжатьвсе Другие фильтры。
существоватьмультимедийная обработкасередина,Фильтр представляет собой программный инструмент для изменения содержимого входного файла перед кодированием выходного файла. нравиться: перевернуть видео,вращать,Зум и т. д.
Пример: [входная_ссылка_метка1]… имя_фильтра=параметры [выходная_ссылка_метка1]…
Например, видео input.mp4 поворачивается на 90 градусов по часовой стрелке.
ffplay -i input.mp4 -vf transpose=1
Например, видео input.mp4 перевернуто по горизонтали (влево и вправо).
ffplay -i input.mp4 -vf hflip
Достичь замедленного воспроизведения, скорость звука составляет 50% от исходной скорости.
ffplay input.mp3 -af atempo=0.5
Filterchain = набор фильтров, разделенных запятыми.
Перевод: «фильтр1,фильтр2,фильтр3,…фильтрN-2,фильтрN-1,фильтрN»
Поворот на 90 градусов по часовой стрелке и переворот по горизонтали
ffplay -i input.mp4 -vf transpose=1,hflip
Давайте сначала сделаем пример зеркально-симметричного видео. Конечный эффект будет следующим:
Шаг 1. Удвойте ширину исходного видео.
ffmpeg -i input.mp4 -t 10 -vf pad=2*iw output.mp4
Шаг 2. Переверните исходное видео по горизонтали.
ffmpeg -i input.mp4 -t 10 -vf hflip output2.mp4
Шаг 3. Переверните видео по горизонтали поверх файла output.mp4.
ffmpeg -i output.mp4 -i output2.mp4 -filter_complex overlay=w compare.mp4
Далее мы используем граф фильтра для достижения эффектов, достигаемых тремя вышеуказанными командами.
Filtergraph = Точка с запятой отделяет группу filterchain
“filterchain1;filterchain2;…filterchainN-1;filterchainN”
Чтобы реализовать три вышеуказанных шага с помощью графа фильтра с маркерами ссылок, вам понадобится всего одна команда:
ffmpeg -i test.mp4 -t 10 -vf "split[a][b];[a]pad=2*iw[1];[b]hflip[2];[1][2]overlay=w" output.mp4
Простой процесс обработки графа фильтра:
Схема обработки сложного графа фильтра:
Из рисунка видно, что сложных графов фильтров меньше, чем простых графов фильтров. 2 шаги, более эффективные, чем простые, ffmpeg Рекомендуется попробовать как можно большеиспользовать Сложный граф фильтра。
Структуры, участвующие в фильтре, в основном включают в себя:
Отношения классов между ними показаны на рисунке ниже:
На картинке выше вы можете видеть, как приезжать, FFmpeg Структура фильтра состоит из трех слоев:
FilterGraph,AVFilterGraph
состав;AVFilter,AVFilterContext,AVFilterLink,AVFilterPad
состав;где, AVFilterContext да AVFilter из Пример; AVFilterContext,AVFilterPad
состав;Чтосередина AVFilterContext да Реально выполнять обработку данных из объекта фильтра;Код ниже декодирует видеокадры и передает их в граф фильтра для обработки, а затем записывает обработанные кадры в файл. Строка описания фильтра filter_descr Указана операция фильтра, в данном случае используйте scale и hflip Приобретите правильный фильтрвидео выполнить масштабирование и горизонтальное переворот。финальный,Программа преобразует обработанные кадры в формат записи в файл YUV420P.
/**
* @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);
}
Распечатка выглядит следующим образом:
frame widht=640,frame height=480
format is yuv420p
Сгенерированный исходный файл видеоданных test.yuv намного больше, чем ранее сжатый файл test.flv.
использовать yuvplayer.exe Воспроизведение создано test.yuv Файл можно просмотреть ниже: