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;
}
阅读(21912) | 评论(4) | 转发(2) |