Using libavformat and libavcodec
Martin
Boehme (boehme@inb.uni-luebeckREMOVETHIS.de)
February 18, 2004
Библиотеки libavformat и libavcodec входят в состав
ffmpeg
и позволяют получить доступ к большинству видео-форматов.
К сожалению , документации по эти библиотекам очень мало , как и примеров их использования.
При использовании библиотек libavformat/libavcodec часто приходится прибегать
к методу тыка .
Вот пример небольшой программы
demo program ,
который работает с libavformat/libavcodec версии 0.4.8 .
В этом документе будет описано , как прочитать видео-поток из файла ,
для чтения аудио нужно искать другие примеры .
Многие форматы (AVI например) не конкретизируют конкретный кодек ,
они устанавливают правила упаковки видео и аудио-потоков ,
вот почему например при открытии таких файлов у вас может не показываться картинка ,
поскольку на вашей системе отсутствует нужный кодек.
Библиотека libavformat делает первичную работу и парсит контейнеры и разделяет потоки в контейнере ,
затем libavcodec уже непосредственно декодирует сами audio и video потоки.
Открытие видео файла
Давайте глянем , как открыть видео-файл и получить доступ к потокам - streams .
Инициализация libavformat/libavcodec:
av_register_all();
Регистрируются все доступные файловые форматы и кодеки .
Вызов av_register_all() делается 1 раз .
В принципе , можно открыть не все , а какой-то один формат.
Открываем файл :
AVFormatContext *pFormatCtx;
const char *filename="myvideo.mpg";
// Open video file
if(av_open_input_file(&pFormatCtx, filename, NULL, 0, NULL)!=0)
handle_error(); // Couldn't open file
Последние 3 параметра задают : file format, buffer size , format parameters.
Мы устанавливаем их в NULL , тем самым заставляя libavformat автоматически определить формат файла.
Теперь получаем информацию о потоках - streams :
// Retrieve stream information
if(av_find_stream_info(pFormatCtx)<0)
handle_error(); // Couldn't find stream information
Заполняется поле streams в структуре AVFormatContext.
В этом примере мы будем работать только с видео-потоком.
Прочитаем первый видео-поток , который найден в контейнере :
int i, videoStream;
AVCodecContext *pCodecCtx;
// Find the first video stream
videoStream=-1;
for(i=0; i<pFormatCtx->nb_streams; i++)
if(pFormatCtx->streams[i]->codec.codec_type==CODEC_TYPE_VIDEO)
{
videoStream=i;
break;
}
if(videoStream==-1)
handle_error(); // Didn't find a video stream
// Get a pointer to the codec context for the video stream
pCodecCtx=&pFormatCtx->streams[videoStream]->codec;
Идентифицируем кодек :
AVCodec *pCodec;
// Find the decoder for the video stream
pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
if(pCodec==NULL)
handle_error(); // Codec not found
// Inform the codec that we can handle truncated bitstreams -- i.e.,
// bitstreams where frame boundaries can fall in the middle of packets
if(pCodec->capabilities & CODEC_CAP_TRUNCATED)
pCodecCtx->flags|=CODEC_FLAG_TRUNCATED;
// Open codec
if(avcodec_open(pCodecCtx, pCodec)<0)
handle_error(); // Could not open codec
Зачем нужен т.н. "truncated bitstreams" ?
Данные в видео-потоке разбиты на пакеты.
Поскольку количество данных для видео-фрейма - величина не постоянная ,
граница между двумя фреймами может не совпадать с границей пакетов .
Поэтому мы обрабатываем эту ситуацию и сообщаем об этом кодеку .
frame rate - одна из наиболее важных информаций , которая хранится
в структуре AVCodecContext.
frame rates (типа NTSC's 29.97 fps) может быть с дробной частью ,
он хранится по частям в pCodecCtx->frame_rate плюс в pCodecCtx->frame_rate_base.
Некоторые кодеки (напр. ASF) некорректно инициализируют frame rate ,
и мы фиксируем это :
// Hack to correct wrong frame rates that seem to be generated by some
// codecs
if(pCodecCtx->frame_rate>1000 && pCodecCtx->frame_rate_base==1)
pCodecCtx->frame_rate_base=1000;
Выделяем память для фреймов :
AVFrame *pFrame;
pFrame=avcodec_alloc_frame();
Теперь можно приступить к декодированию видео .
Декодирование Video Frames
Контейнер может включать несколько аудио и видео потоков ,
каждый из которых разбит на пакеты определенного размера.
Нужно прочитать эти пакеты с помощью libavformat,
выбрать из них нужные нам и передать для декодирования с помощью libavcodec.
Напишем процедуру , которая будет возвращать очередной фрейм :
bool GetNextFrame(AVFormatContext *pFormatCtx, AVCodecContext *pCodecCtx,
int videoStream, AVFrame *pFrame)
{
static AVPacket packet;
static int bytesRemaining=0;
static uint8_t *rawData;
static bool fFirstTime=true;
int bytesDecoded;
int frameFinished;
// First time we're called, set packet.data to NULL to indicate it
// doesn't have to be freed
if(fFirstTime)
{
fFirstTime=false;
packet.data=NULL;
}
// Decode packets until we have decoded a complete frame
while(true)
{
// Work on the current packet until we have decoded all of it
while(bytesRemaining > 0)
{
// Decode the next chunk of data
bytesDecoded=avcodec_decode_video(pCodecCtx, pFrame,
&frameFinished, rawData, bytesRemaining);
// Was there an error?
if(bytesDecoded < 0)
{
fprintf(stderr, "Error while decoding frame\n");
return false;
}
bytesRemaining-=bytesDecoded;
rawData+=bytesDecoded;
// Did we finish the current frame? Then we can return
if(frameFinished)
return true;
}
// Read the next packet, skipping all packets that aren't for this
// stream
do
{
// Free old packet
if(packet.data!=NULL)
av_free_packet(&packet);
// Read new packet
if(av_read_packet(pFormatCtx, &packet)<0)
goto loop_exit;
} while(packet.stream_index!=videoStream);
bytesRemaining=packet.size;
rawData=packet.data;
}
loop_exit:
// Decode the rest of the last frame
bytesDecoded=avcodec_decode_video(pCodecCtx, pFrame, &frameFinished,
rawData, bytesRemaining);
// Free last packet
if(packet.data!=NULL)
av_free_packet(&packet);
return frameFinished!=0;
}
теперь нужно просто в цикле вызывать GetNextFrame().
Многие кодеки возвращают картинку в формате YUV 420
(одной яркостью и 2 каналами цветов ), который можно сконвертироватьв в RGB.
В libavcodec есть процедура img_convert, которая делает такую конверсию :
while(GetNextFrame(pFormatCtx, pCodecCtx, videoStream, pFrame))
{
img_convert((AVPicture *)pFrameRGB, PIX_FMT_RGB24, (AVPicture*)pFrame,
pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);
// Process the video frame (save to disk etc.)
DoSomethingWithTheImage(pFrameRGB);
}
Картинка в формате RGB pFrameRGB :
AVFrame *pFrameRGB;
int numBytes;
uint8_t *buffer;
// Allocate an AVFrame structure
pFrameRGB=avcodec_alloc_frame();
if(pFrameRGB==NULL)
handle_error();
// Determine required buffer size and allocate buffer
numBytes=avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width,
pCodecCtx->height);
buffer=new uint8_t[numBytes];
// Assign appropriate parts of buffer to image planes in pFrameRGB
avpicture_fill((AVPicture *)pFrameRGB, buffer, PIX_FMT_RGB24,
pCodecCtx->width, pCodecCtx->height);
Итак , мы прочитали и извлекли видео
:
// Free the RGB image
delete [] buffer;
av_free(pFrameRGB);
// Free the YUV frame
av_free(pFrame);
// Close the codec
avcodec_close(pCodecCtx);
// Close the video file
av_close_input_file(pFormatCtx);
|
achuglazov@yandex.ru | Здравствуйте.
Я пытался разобраться в ffmpeg.
Пытался откомпилить ваш пример в Microsoft Visual C++ 2005 ,
а он мне выдаёт ошибку "error LNK2019: unresolved external symbol _avcodec_decode_video referenced in function _main Work_2.obj".
Я почитал на форуме про эту ошибку, там говорится что методы не прописаны в .h файлах.
Так вот у меня вопрос как всё таки запустить ваш пример, точнее как правильно подключить библиотеки.
Спасибо.
2010-05-25 18:08:42 | Яковлев Сергей | Если честно, то я не пытался это собирать в винде и смутно представляю, как это можно сделать.
Скорее всего, в солюшине виндового проекта нужно добавить ссыллку либо на библиотеку,
либо dll-ку, которая идет вместе с исполняемым файлом. 2010-05-25 19:26:24 | achuglazov@yandex.ru | Ладно я попробую. Спасибо. 2010-05-28 18:34:47 | achuglazov@yandex.ru | поставил Mac OS
ну и вобщем:
Alexs-Mac:Tuttorial№1 alex$ gcc -o tutorial01 tutorial01.c
Undefined symbols:
"_avcodec_find_decoder", referenced from:
_main in ccFjcPkk.o
"_avcodec_alloc_frame", referenced from:
_main in ccFjcPkk.o
_main in ccFjcPkk.o
"_avpicture_get_size", referenced from:
_main in ccFjcPkk.o
"_av_close_input_file", referenced from:
_main in ccFjcPkk.o
"_av_malloc", referenced from:
_main in ccFjcPkk.o
"_av_read_frame", referenced from:
_main in ccFjcPkk.o
"_av_register_all", referenced from:
_main in ccFjcPkk.o
"_av_find_stream_info", referenced from:
_main in ccFjcPkk.o
"_avpicture_fill", referenced from:
_main in ccFjcPkk.o
"_av_free", referenced from:
_main in ccFjcPkk.o
_main in ccFjcPkk.o
_main in ccFjcPkk.o
"_av_open_input_file", referenced from:
_main in ccFjcPkk.o
"_avcodec_open", referenced from:
_main in ccFjcPkk.o
"_avcodec_decode_video", referenced from:
_main in ccFjcPkk.o
"_avcodec_close", referenced from:
_main in ccFjcPkk.o
"_img_convert", referenced from:
_main in ccFjcPkk.o
"_dump_format", referenced from:
_main in ccFjcPkk.o
ld: symbol(s) not found
collect2: ld returned 1 exit status
Alexs-Mac:Tuttorial№1 alex$
Как можно с этим побороться?) 2010-05-28 19:38:53 | Яковлев Сергей | >> Alexs-Mac:Tuttorial№1 alex$ gcc -o tutorial01 tutorial01.c
А либы кто будет подключать ?
Я не знаю, как там на маке, но по иднее нужно так:
gcc -o tutorial01 tutorial01.c -lavutil -lavformat -lavcodec -lz -lavutil -lm
2010-05-28 19:58:22 | achuglazov@yandex.ru | Я запустил так как вы сказали...
library not found for -lavutil
Вот а гугл зараза ничего не выдает по этой ошибке...
2010-05-31 13:01:36 | Яковлев Сергей | А библиотека lavutil тут не нужна.
Для примера, который описан на этой странице, правильная команда:
g++ -o avcodec_sample avcodec_sample.cpp -lavformat -lavcodec -lz 2010-05-31 13:48:21 | achuglazov@yandex.ru | Во вот так:
gcc -L"UsersDimaDesktopLibraryffmpeg"libavdevice
-L"UsersDimaDesktopLibraryffmpeg"libavformat
-L"UsersDimaDesktopLibraryffmpeg"libavcodec
-L"UsersDimaDesktopLibraryffmpeg"libavutil -Wl,-dynamic,-search_paths_first -o tutorial01 tutorial01.c -lavutil -lavformat -lavcodec -lbz2 -lavutil -lz
а параметры которые передаются через -L типа пути к этип библиотекам...вот)
теперь работает, и можно дальше изучать...)) 2010-05-31 16:31:15 | achuglazov@yandex.ru | Снова привет.
А как считать картинку, например JPEG в фрейм или пакет?
вот...а то как достать картинку из видео понятно, а как создать своё видео из картинок пока неочень... 2010-06-03 14:28:28 | Яковлев Сергей | Как-то так:
ffmpeg -r 10 -b 1800 -i *.jpg -y test.avi 2010-06-04 23:36:37 | user | По ссылке с демо-программой открывается неформатированный листинг - читать тяжело. 2016-04-28 00:40:41 | Яковлев Сергей | Поправил 2016-04-28 09:00:17 | |
|