Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1292809
  • 博文数量: 79
  • 博客积分: 1959
  • 博客等级: 上尉
  • 技术积分: 2719
  • 用 户 组: 普通用户
  • 注册时间: 2007-11-19 12:07
个人简介

樽中酒不空

文章分类

全部博文(79)

文章存档

2024年(3)

2020年(4)

2019年(1)

2017年(2)

2016年(2)

2015年(7)

2014年(11)

2013年(13)

2012年(18)

2011年(2)

2010年(16)

分类: C/C++

2014-05-27 15:26:55

ffmpeg解码视频的例子可以看官方自带的encode_decode.c。
官方解码保存成ppm,这里接下来保存成BMP或JPG。
原理:
保存BMP是解码成功后,从YUV420转成RGB24,然后构造文件头,BITMAPINFOHEADER,再写入图片数据。
保存JPG是解码成功后,得到的是YUV420, 然后再重编码成JPG (这个重编码过程几乎不占CPU资源,可以接受)。

(以下代码只是演示怎么处理,不一定能直接运行。感觉渔比鱼更重要,学会解决思路比直接抄代码更有利于提高)

....
//之前略,从解码成功开始,之前的见encode_decode。
AVFrame *decode_picture;
decode_picture= avcodec_alloc_frame();//av_frame_alloc();
int len = avcodec_decode_video2(decode_c, decode_picture, &got_picture, &pkt);

if (got_picture)
{
//解码成功,开始保存,下面两个分支:
1  saveBMP()
2  saveJPG



1 save BMP:
首先定义两个结构体,从MSDN里抄过来就行。(当前运行环境是Linux,如果是VC开发,系统自带这两个结构体)
typedef struct tagBITMAPFILEHEADER {
  WORD  bfType;
  DWORD bfSize;
  WORD  bfReserved1;
  WORD  bfReserved2;
  DWORD bfOffBits;
} BITMAPFILEHEADER, *PBITMAPFILEHEADER;

typedef struct tagBITMAPINFOHEADER {
  DWORD biSize;
  LONG  biWidth;
  LONG  biHeight;
  WORD  biPlanes;
  WORD  biBitCount;
  DWORD biCompression;
  DWORD biSizeImage;
  LONG  biXPelsPerMeter;
  LONG  biYPelsPerMeter;
  DWORD biClrUsed;
  DWORD biClrImportant;
} BITMAPINFOHEADER, *PBITMAPINFOHEADER;


WORD 和DWORD就是uint16_t和uint32_t。


具体保存过程:
saveBMP()
{
//接着if (got_picture):
//1 先进行转换,  YUV420=>RGB24:
int w = decode_c->width;
int h = decode_c->height;

int numBytes=avpicture_get_size(PIX_FMT_RGB24, w,h);
uint8_t * buffer=(uint8_t *)av_malloc(numBytes*sizeof(uint8_t));

AVFrame *pFrameRGB;
pFrameRGB = avcodec_alloc_frame();
avpicture_fill((AVPicture *)pFrameRGB, buffer,PIX_FMT_RGB24,  w, h);

img_convert_ctx = sws_getCachedContext(img_convert_ctx,
w, h, (PixelFormat)(decode_picture->format), w, h,PIX_FMT_BGR24, sws_flags, NULL, NULL, NULL);

if (img_convert_ctx == NULL) 
{
fprintf(stderr, "Cannot initialize the conversion context\n");
exit(1);
}
sws_scale(img_convert_ctx, decode_picture->data, decode_picture->linesize,
0, h, pFrameRGB->data, pFrameRGB->linesize);


   //2 构造 BITMAPINFOHEADER

BITMAPINFOHEADER header;
header.biSize = sizeof(BITMAPINFOHEADER);

header.biWidth = w;
header.biHeight = h*(-1);
header.biBitCount = 24;
header.biCompression = 0;
header.biSizeImage = 0;
header.biClrImportant = 0;
header.biClrUsed = 0;
header.biXPelsPerMeter = 0;
header.biYPelsPerMeter = 0;
header.biPlanes = 1;


//3 构造文件头
BITMAPFILEHEADER bmpFileHeader;
HANDLE hFile = NULL;
DWORD dwTotalWriten = 0;
DWORD dwWriten;

bmpFileHeader.bfType = 0x4d42; //'BM';
bmpFileHeader.bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);
bmpFileHeader.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)+ numBytes;


FILE* pf = fopen("test.bmp", "wb");
fwrite(&bmpFileHeader, sizeof(BITMAPFILEHEADER), 1, pf);
fwrite(&header, sizeof(BITMAPINFOHEADER), 1, pf);
fwrite(pFrameRGB->data[0], 1, numBytes, pf);
fclose(pf);

//释放资源
av_free(buffer);
av_free(pFrameRGB);
}


如果感觉与原图色彩不一样,分别试试PIX_FMT_BGR24和PIX_FMT_RGB24。
如果图像是倒置的,自己写个方法正过来就行了。就是矩形上下对调。




2 saveJPG


//接着if (got_picture)
int numBytes=avpicture_get_size(PIX_FMT_YUVJ420P, decode_c->width, decode_c->height);
uint8_t *buffer=(uint8_t *)av_malloc(numBytes*sizeof(uint8_t));
bool bret = WriteJPEG(decode_c, decode_picture, "test.jpg", PIX_FMT_YUVJ420P, buffer, numBytes);

//以下内容是以前google出来的,不过这几天google被墙,等以后找到后再补上具体出处。

bool WriteJPEG (AVCodecContext *pCodecCtx, AVFrame *pFrame, char cFileName[], PixelFormat pix, uint8_t *buffer, int numBytes)
{
   bool bRet = false;
   AVCodec *pMJPEGCodec=NULL;
   AVCodecContext *pMJPEGCtx = avcodec_alloc_context();
   if( pMJPEGCtx )
   {
      pMJPEGCtx->bit_rate = pCodecCtx->bit_rate;
      pMJPEGCtx->width = pCodecCtx->width;
      pMJPEGCtx->height = pCodecCtx->height;
      pMJPEGCtx->pix_fmt = pix;
      pMJPEGCtx->codec_id = CODEC_ID_MJPEG;
      pMJPEGCtx->codec_type = AVMEDIA_TYPE_VIDEO;
      pMJPEGCtx->time_base.num = pCodecCtx->time_base.num;
      pMJPEGCtx->time_base.den = pCodecCtx->time_base.den;
      pMJPEGCodec = avcodec_find_encoder(pMJPEGCtx->codec_id );


      if( pMJPEGCodec && (avcodec_open( pMJPEGCtx, pMJPEGCodec) >= 0) )
      {
         pMJPEGCtx->qmin = pMJPEGCtx->qmax = 3;
         pMJPEGCtx->mb_lmin = pMJPEGCtx->lmin = pMJPEGCtx->qmin * FF_QP2LAMBDA;
         pMJPEGCtx->mb_lmax = pMJPEGCtx->lmax = pMJPEGCtx->qmax * FF_QP2LAMBDA;
         pMJPEGCtx->flags |= CODEC_FLAG_QSCALE;
         pFrame->quality = 10;
         pFrame->pts = 0;
         int szBufferActual = avcodec_encode_video(pMJPEGCtx, buffer, numBytes, pFrame);
            
         if( SaveFrame(szBufferActual, buffer, cFileName ) )
            bRet = true;

         avcodec_close(pMJPEGCtx);
      }
   }
   return bRet;


bool SaveFrame(int nszBuffer, uint8_t *buffer, char cOutFileName[])
{
//printf("SaveFrame nszBuffer = %d, cOutFileName = %s\n", nszBuffer, cOutFileName);
bool bRet = false;

if( nszBuffer > 0 )
{
FILE *pFile = pFile = fopen(cOutFileName, "wb");
if(pFile)
{
fwrite(buffer, sizeof(uint8_t), nszBuffer, pFile);
bRet = true;
fclose(pFile);
}
}
   return bRet;
}


阅读(22447) | 评论(4) | 转发(2) |
给主人留下些什么吧!~~

sxcong2017-01-19 16:08:00

修改一个错误:iBufLeng应该是numBytes。

sxcong2014-06-23 09:07:37

liuxuejin:ffmpeg 本身就能截图出来 jpg  为什么不参考那段代码呢??

哦,在哪个模块里?我还真没注意到,请指教

回复 | 举报

liuxuejin2014-06-22 09:27:54

ffmpeg 本身就能截图出来 jpg  为什么不参考那段代码呢??

liuxuejin2014-06-22 09:26:59

不错啊啊