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