Search     or:     and:
 LINUX 
 Language 
 Kernel 
 Package 
 Book 
 Test 
 OS 
 Forum 
 iakovlev.org 
 Packages
 Make 
 Iptables 
 Nmap 
 Apache 
 LFS 
 TUX 
 cURL 
 libpcap 
 Parted 
 Httpd 
 File managers 
 FFMPEG 
 RTMP 
 SQL 
 Test 
 Git 
NEWS
Последние статьи :
  Тренажёр 16.01   
  Эльбрус 05.12   
  Алгоритмы 12.04   
  Rust 07.11   
  Go 25.12   
  EXT4 10.11   
  FS benchmark 15.09   
  Сетунь 23.07   
  Trees 25.06   
  Apache 03.02   
 
TOP 20
 MINIX...3057 
 Solaris...2933 
 LD...2904 
 Linux Kernel 2.6...2470 
 William Gropp...2181 
 Rodriguez 6...2012 
 C++ Templates 3...1945 
 Trees...1937 
 Kamran Husain...1866 
 Secure Programming for Li...1792 
 Максвелл 5...1710 
 DevFS...1694 
 Part 3...1684 
 Stein-MacEachern-> Час...1632 
 Go Web ...1624 
 Ethreal 4...1618 
 Arrays...1607 
 Стивенс 9...1603 
 Максвелл 1...1592 
 FAQ...1538 
 
  01.01.2024 : 3621733 посещений 

iakovlev.org

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