Всем привет! Сегодня я хочу открыть
серию статей по изучению FFmpeg libav с нуля.
Сразу уточню, что в основном статьи направлены на программирование, используя библиотеки libav*, где в качестве языка выступит С++.
Отмечу то, что я не являюсь
профессионалом в данной теме и моей целью является просто облегчить жизнь тем, кто, как и я, делает первые шаги в данном направлении.
Знакомство (небольшая предыстория)
Несколько месяцев назад у меня появилась необходимость познакомиться с основами работы с аудио и видео файлами посредством языка С++ и библиотек libav*. Под libav* я подразумеваю libavformat, libavcodec, libavutil и другие.
За время, посвящённое изучению, я понял, что в интернете довольно мало актуальной информации на данную тему. Мне удалось найти лишь несколько статей, которые более менее раскрывают природу библиотек, но и они уже успели устареть, поэтому я хочу делиться собственным опытом со всеми, кто только ступает на эту тернистую тропу.
Важные основы
Перед началом чтения моих статей, настоятельно прошу ознакомиться с основами, которые освещаются здесь: github.com/leandromoreira/ffmpeg-libav-tutorial
Подготовка рабочего окружения (по желанию)
Данный раздел предназначен для каждого, кого интересует простая настройка рабочего окружения на Windows. Я пользуюсь редактором кода Visual Studio Code, менеджером пакетов MSYS2 и компилятором GCC из группы компиляторов mingw-w64.
Скачиваем и устанавливаем менеджер пакетов с официального сайта: http://www.msys2.org
Запускаем MSYS2 и обновляем основные системные пакеты:
$ pacman -Syu
Перезапускаем MSYS2 и обновляем остальные пакеты:
$ pacman -Su
Устанавливаем mingw-w64 и некоторые вспомогательные пакеты (make, pkgconf):
$ pacman -S --needed base-devel mingw-w64-x86_64-toolchain
Устанавливаем пакет FFmpeg:
$ pacman -S mingw-w64-x86_64-ffmpeg
Настраиваем Visual Studio Code. По его настройке довольно много информации на официальном сайте: https://code.visualstudio.com/docs/cpp/config-mingw, поэтому покажу лишь основное:
1. Добавить путь к заголовочным файлам (FFmpeg libav*) в
"includePath"
.
2. Добавить путь к компилятору (mingw-w64 gcc) в"compilerPath"
.
Начало работы с libav*
В качестве ознакомительного примера мы напишем простую программу для отображения информации о медиафайле.
Полный код программы можно найти здесь: https://github.com/Meystergod/libavtutorial
Первым делом необходимо открыть входной файл, путь к которому мы укажем аргументом в командной строке.
int main(int argc, char* argv[])
{
if(argc != 2)
{
av_log(NULL, AV_LOG_ERROR, "Usage: <input file>\n");
return ARGUMENTS_ERROR;
}
const char* input_file = argv[1];
return EXIT_SUCCESS;
}
Далее прочитаем файловый заголовок и сохраним информацию о найденных форматах в структуру AVFormatContext
. Для удобства все манипуляции будут производиться в отдельной функции.
Подробнее ознакомиться с AVFormatContext
и его полями можно на официальном сайте: https://libav.org/documentation/doxygen/master/structAVFormatContext.html
Итак, для начала выделим память для нашего format_context
, сделать это можно с помощью функции avformat_alloc_context()
. Далее посредством функции avformat_open_input()
прочитаем заголовок и заполним наш компонент минимальной информацией о формате. Следующим шагом получим информацию обо всех потоках в файле, это делает функция avformat_find_stream_info()
.
int open_input_media_file(AVFormatContext** format_context, const char* input_file)
{
int response;
*format_context = avformat_alloc_context();
if(!(*format_context))
{
av_log(NULL, AV_LOG_ERROR, "Could not allocate memory for the input format context.\n");
return ALLOCATE_FORMAT_CONTEXT_ERROR;
}
response = avformat_open_input(format_context, input_file, NULL, NULL);
if(response != 0)
{
av_log(NULL, AV_LOG_ERROR, "Could not open the input file: %s.\n", input_file);
return OPEN_INPUT_FILE_ERROR;
}
response = avformat_find_stream_info(*format_context, NULL);
if(response < 0)
{
av_log(NULL, AV_LOG_ERROR, "Could not find the input stream information.\n");
return FIND_STREAM_INFO_ERROR;
}
return EXIT_SUCCESS;
}
Для вывода информации можно воспользоваться стандартной функцией av_dump_format()
, которая принимает структуру AVFormatContext
в качестве первого входного параметра.
Подробнее ознакомиться с av_dump_format()
и её параметрами можно на официальном сайте: https://libav.org/documentation/doxygen/master/group__lavf__misc.html#gae2645941f2dc779c307eb6314fd39f10
Осталось инициализировать структуру AVFormatContext
и добавить вызов ранее созданной функции open_input_media_file()
в main()
.
Ну и, конечно же, не стоит забывать про очистку памяти. Для этого мы напишем отдельную функцию clean_memory()
и также добавим её вызов в main()
.
void clean_memory(AVFormatContext* format_context)
{
if(format_context != NULL)
{
avformat_close_input(&format_context);
avformat_free_context(format_context);
}
}
int main(int argc, char* argv[])
{
AVFormatContext* format_context = NULL;
if(argc != 2)
{
av_log(NULL, AV_LOG_ERROR, "Usage: <input file>\n");
return ARGUMENTS_ERROR;
}
const char* input_file = argv[1];
int response = open_input_media_file(&format_context, input_file);
if(response != EXIT_SUCCESS)
{
clean_memory(format_context);
return response;
}
av_dump_format(format_context, 0, input_file, 0);
clean_memory(format_context);
return EXIT_SUCCESS;
}
Сборку и запуск программы можно осуществить как с помощью командной строки:g++ 1.cpp -o out -lavformat -lavutil
, так и с использованием Makefile (об этом довольно много информации в интернете).
После запуска программы мы увидим информацию, полученную из нашего медиафайла, обо всех его потоках.
Заключение
В рамках данной статьи мы немного познакомились с libav* и написали вводный пример. Далее будем переходить к более реальным вещам, например, декодированию, транскодированию, использованию фильтров и многому другому.