LINUX.ORG.RU

[ffmpeg] AVFrame to jpeg file

 


0

0

В общем, есть задача нарезать скриншотов с видео файла.
Все как бы получилось, но вот проблемка есть с тем, что файлы исходные нужно сохранять в jpeg формате,а  я добился только сохранения в ppm.
Вот мой код, после инициализации, загрузки кодека и прочей не относящейся к делу лабуды (большая часть кода на основе статьи Martin'a Böhme):
      //Фреймы
      AVFrame *pFrame;
      AVFrame *pFrameRGB;

      pFrame=avcodec_alloc_frame();
      pFrameRGB=avcodec_alloc_frame();

      int             frameFinished;
      int             numBytes;
      
      numBytes = avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width,pCodecCtx->height);
      uint8_t *buffer = new uint8_t[numBytes];

     int h = pCodecCtx->height;
     int w = pCodecCtx->width;

      // Assign appropriate parts of buffer to image planes in pFrameRGB
      avpicture_fill((AVPicture *)pFrameRGB, buffer,PIX_FMT_RGB24,pCodecCtx->width, pCodecCtx->height);

      // Read frames and save first five frames to disk

      AVPacket        packet;
      i=0;
      struct SwsContext *img_convert_ctx = NULL;

      while(av_read_frame(pFormatCtx, &packet)>=0)
      {
          // Is this a packet from the video stream?
          if(packet.stream_index==videoStream)
          {
              // Decode video frame
              avcodec_decode_video(pCodecCtx, pFrame, &frameFinished,
                  packet.data, packet.size);

              // Did we get a video frame?
              if(frameFinished)
              {
                   if(img_convert_ctx == NULL) //Создаем контекст для конвертирования
                      img_convert_ctx = sws_getContext(w, h,pCodecCtx->pix_fmt, w, h,PIX_FMT_RGB24,SWS_BICUBIC, NULL, NULL, NULL);

		    //Конвертируем	
                    sws_scale(img_convert_ctx, pFrame->data, pFrame->linesize,
                              0, h , pFrameRGB->data, pFrameRGB->linesize);
		    // Сохраняем на диск
	            if(++i<=atoi(argv[2]))
        	              SaveFrame(pFrameRGB, pCodecCtx->width, pCodecCtx->height,i);
		}
	  }
   	av_free_packet(&packet);

	}

Вот так сохраняю в файл:

void SaveFrame(AVFrame *pFrame, int width, int height, int iFrame)
 {
     FILE *pFile;
     char szFilename[32];
     int  y;

     // Open file
     sprintf(szFilename, "frame%d.ppm", iFrame);
     pFile=fopen(szFilename, "wb");
     if(pFile==NULL)
         return;

     // Write header
     fprintf(pFile, "P6\n%d %d\n255\n", width, height);

     // Write pixel data
     for(y=0; y<height; y++)
         fwrite(pFrame->data[0]+y*pFrame->linesize[0], 1, width*3, pFile);

     // Close file
     fclose(pFile);
 }


Подскажите пожалуйста, как я могу запихнуть данные, которые в pFrame->data[0] не в ppm, а в jpeg формат?  Или возможно есть другой способ? 

> while(av_read_frame(

Это очень старый пример, сейчас так делают:

if (av_read_frame(pFormatCtx, &packet) >= 0) {
   avcodec_decode_video(pCodecCtx, pFrame, &frameFinished, packet.data, packet.size);
   av_free_packet(&packet);
}

> Подскажите пожалуйста, как я могу запихнуть данные, которые в pFrame->data[0] не в ppm, а в jpeg формат?  Или возможно есть другой способ? 

Можно жать в jpeg с помощью самого же ffmpeg:

AVCodecContext *c = avcodec_alloc_context();
c->pix_fmt = PIX_FMT_YUVJ420P;
c->width = width;
c->height = height;
c->flags |= CODEC_FLAG_QSCALE;
c->time_base = av_d2q(1, 1);
c->qmin = 30;
c->qmax = 31;
picture = avcodec_alloc_frame();
dest_size = width * 3 * height;
dest = av_malloc(dest_size);

AVPicture picture
picture.quality = 4;

int size = avcodec_encode_video(c, dest, dest_size, picture);

FFmpeg внутри работает только с форматом YUV420P, только для джпега нужно YUVJ420P, а у вас данные в RGB24. Поэтому кадр предварительно нужно преобразовать с помощью img_convert (или чем-нибудь другим, если img_convert уже выбросили). Собственно, в dest будет пожатый в джпег кадр.

mv ★★★★★
()

Насколько помню - после декодирования получается YUV, зачем его конвертировать в RGB, чтобы потом опять в YV?

Возьми исходники mplayer - ключ "-vo jpeg" заставляет сохранять фреймы в jpeg файлы - и посмотри как это там сделано Ну или просто используй mplayer

anonymous
()
Ответ на: комментарий от anonymous

Это был пример записи в ppm-файл.
Смотрел исходники ffmpeg - там чуть обе ноги не поламал(
Сейчас разбираюсь с avcodec_encode_video за советом mv, но у меня она по сегфолту вылетает, если до чего-то дойду или не дойду - отпишусь.

dont
() автор топика
Ответ на: комментарий от mv

2 mv:
Спасибо большое за настановление на путь истинный. Все-таки заработало!)

Только фреймы получаются не все идеальны, сливаются на некоторых рисунках предыдущие и следующие фреймы, получается немножко приторможено... Это моя проблема, проблема кодека или вся беда в самом видеофайле?

dont
() автор топика
Ответ на: комментарий от dont

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

extern "C"
{
#include <ffmpeg/avcodec.h>
#include <ffmpeg/avformat.h>
#include <ffmpeg/swscale.h>
}

#include <iostream>
#include <stdio.h>


int main(int argc, char *argv[])
{
    if(argc < 3)
    {
        std::cerr << "Not enouth parameters\n";
        return 1;
    }
    // Register all formats and codecs
    av_register_all();
    
    AVFormatContext *pFormatCtx;
    
    const char      *filename=argv[1];

    // Open video file
     if(av_open_input_file(&pFormatCtx, filename, NULL, 0, NULL)!=0)
     {
            std::cerr << "Can't open video file \"" << filename << "\"\n";
            return 1;
     }
     else
        std::cout << "Video file was opened\n";
     
     if(av_find_stream_info(pFormatCtx)<0)
     {
         std::cerr << "Couldn't find stream information\n";
         return 1;
     }    

    dump_format(pFormatCtx, 0, filename, false);
    
    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)
    {   
        std::cerr << "Didn't find a video stream\n";
        return 1;
    }
    else
        std::cout << "Finded video stream with number #" <<i << std::endl; 

    // Get a pointer to the codec context for the video stream
    pCodecCtx = pFormatCtx->streams[videoStream]->codec;
    

    AVCodec *pCodec = NULL;

    // Find the decoder for the video stream
     pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
     if(pCodec==NULL)
     {
         std::cerr << "Codec not found\n";
         return 1;
     }
     else
        std::cout << "Codec was finded\n";
     
     // 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)
      {
        std::cerr << " Could not open codec\n";
        return 1;
      }  
   
      // 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;
      AVFrame *pFrameRGB;

      pFrame=avcodec_alloc_frame();

      if(!pFrame)
      {
            std::cerr << "Can't allocate memory for AVFrame\n";
            return 1;
      }

      int             frameFinished;      
      int             numBytes;      

      // Determine required buffer size and allocate buffer               
      numBytes = avpicture_get_size(PIX_FMT_YUVJ420P, pCodecCtx->width,pCodecCtx->height);
      uint8_t *buffer = new uint8_t[numBytes];

     int h = pCodecCtx->height;
     int w = pCodecCtx->width;

      // Assign appropriate parts of buffer to image planes in pFrameRGB
      avpicture_fill((AVPicture *)pFrame, buffer,PIX_FMT_YUVJ420P ,pCodecCtx->width, pCodecCtx->height);                            

      // Read frames and save first five frames to disk

      AVPacket        packet;
      i=0;

      AVCodec *pOCodec;  
      AVCodecContext *pOCodecCtx = avcodec_alloc_context();
      if (!pOCodecCtx) {
         fprintf(stderr, "Could not allocate codec\n");
         return 1;
      }

    pOCodecCtx->bit_rate = pCodecCtx->bit_rate;
    pOCodecCtx->width = pCodecCtx->width;
    pOCodecCtx->height = pCodecCtx->height;
    pOCodecCtx->pix_fmt = PIX_FMT_YUVJ420P;
    pOCodecCtx->codec_id = CODEC_ID_MJPEG;
    pOCodecCtx->codec_type = CODEC_TYPE_VIDEO;
    pOCodecCtx->time_base.num = pCodecCtx->time_base.num; 
    pOCodecCtx->time_base.den = pCodecCtx->time_base.den;
                                                                 
    pOCodec = avcodec_find_encoder(pOCodecCtx->codec_id);
    if(!pOCodec)
    {
        fprintf(stderr, "Codec not found\n");
        return 1;
        
    }
    else
        fprintf(stderr, "Codec with id CODEC_ID_MJPEG found\n");
        
    if (avcodec_open(pOCodecCtx, pOCodec) < 0) {
    fprintf(stderr, "Could not open codec\n");
    return 1;
    }
    else
    fprintf(stderr, "Codec was opened\n");


      while(av_read_frame(pFormatCtx, &packet)>=0)
      {
          // Is this a packet from the video stream?
          if(packet.stream_index==videoStream)
          {
              // Decode video frame
              avcodec_decode_video(pCodecCtx, pFrame, &frameFinished, 
                  packet.data, packet.size);
                                                                                 
              // Did we get a video frame?
              if(frameFinished)
              {

                pOCodecCtx->qmin = pOCodecCtx->qmax = 3;
                pOCodecCtx->mb_lmin = pOCodecCtx->lmin = pOCodecCtx->qmin * FF_QP2LAMBDA;
                pOCodecCtx->mb_lmax = pOCodecCtx->lmax = pOCodecCtx->qmax * FF_QP2LAMBDA;
                pOCodecCtx->flags |= CODEC_FLAG_QSCALE;




                
                pFrame->quality = 4;
                pFrame->pts = i;
                    


                int szBufferActual = avcodec_encode_video(pOCodecCtx, buffer, numBytes, pFrame);
                if(szBufferActual < 0)
                {
                        fprintf(stderr, "avcodec_encode_video error. return value = %d\n",szBufferActual);
                        return -1;
                }        
                    
                if(++i < atoi(argv[2]))
                {
                    /* Write JPEG to file */
                    char buf[32] = {0};
                    snprintf(buf,30,"fram%d.jpeg",i);
                    FILE *fdJPEG = fopen(buf, "wb");
                    int bRet = fwrite(buffer, sizeof(uint8_t), szBufferActual, fdJPEG);
                    fclose(fdJPEG);

                    if (bRet != szBufferActual)
                    { 
                        fprintf(stderr, "Error writing jpeg file\n");
                        return 1;
                    }
                    else
                        fprintf(stderr, "jpeg file was writed\n");
                }       
                else
                    break;
                
              }
          }
                                                                                 
          // Free the packet that was allocated by av_read_frame
          av_free_packet(&packet);
      }

     // 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);
                                      
     return 0;
}

dont
() автор топика
Ответ на: комментарий от dont

Что ты имеешь в виду "под сливаются фреймы"? Если похоже на эффект morphing или motion blur, то это в самом видео.

mv ★★★★★
()
Ответ на: комментарий от dont

Хм, и без av_codec_init() работает? Год назад стабильно в сегфолт ложилось, т.к. табличка одна не инитились.

mv ★★★★★
()
Ответ на: комментарий от mv

"Сливались фреймы" это я наверное некоректно выразился. Просто пакеты которые на границе между фреймами обрабатываются при установленом флаге CODEC_FLAG_TRUNCATED, в результате некоторые картинки получались искаженными. Сброс флага все решил.
Функции av_codec_init в сорцах ffmpeg вообще нет. Память под кодек выделется функцией avcodec_find_decoder на сколько я понимаю.

dont
() автор топика
Ответ на: комментарий от mv

А, ты наверное имелл ввиду функцию avcodec_init. Да, работает без нее. Наверное кодеки инитятся еще в av_register_all()

dont
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.