利用ffmpeg将H264解码为RGB
因为公司买到了一个不提供解码器的设备,我不得已还要做解码的工作。在网上找了一圈,H264解码比較方便的也就是ffmpeg一系列的函数库了,原本设备中也是用这套函数库解码,但厂家不给提供,没办法,仅仅得自己搞了。
利用H264解码分为几个步骤:
注意一点在加入头文件的时候要加入extern "C",不然会出现错误
extern "C" { #include <avcodec.h> #include <avformat.h> #include <avutil.h> #include <swscale.h> };
这里申明了几个全局变量
AVCodec *pCodec = NULL; AVCodecContext *pCodecCtx = NULL; SwsContext *img_convert_ctx = NULL; AVFrame *pFrame = NULL; AVFrame *pFrameRGB = NULL;
1. 初始化
int H264_Init(void) { /* must be called before using avcodec lib*/ avcodec_init(); /* register all the codecs */ avcodec_register_all(); /* find the h264 video decoder */ pCodec = avcodec_find_decoder(CODEC_ID_H264); if (!pCodec) { fprintf(stderr, "codec not found\n"); } pCodecCtx = avcodec_alloc_context(); /* open the coderc */ if (avcodec_open(pCodecCtx, pCodec) < 0) { fprintf(stderr, "could not open codec\n"); } // Allocate video frame pFrame = avcodec_alloc_frame(); if(pFrame == NULL) return -1; // Allocate an AVFrame structure pFrameRGB=avcodec_alloc_frame(); if(pFrameRGB == NULL) return -1; return 0; }
在最早使用的时候没有使用全局变量,初始化中也就仅仅有init和regisger这两个函数,而这样做的下场是,非关键帧所有无法解码,仅仅有关键帧才有办法解码。
2. 解码
解码的时候avcodec_decode_video函数是进行解码操作,在外部定义outputbuf的大小时,pixes*3,outsize是返回的outputbuf的size,值也是pixes*3。
在解码的时候这几句话的意义是将YUV420P的数据倒置。在原先使用中,发现解出来的图像竟然是中心旋转图,后面在网上找了些办法,认为这个比較有用。解码实时是非常重要的,图像转化完之后也能够讲RGB图再次转化,那样也能成为一个正的图,可是那样效率就明显低了。
pFrame->data[0] += pFrame->linesize[0] * (pCodecCtx->height-1); pFrame->linesize[0] *= -1; pFrame->data[1] += pFrame->linesize[1] * (pCodecCtx->height/2 - 1);; pFrame->linesize[1] *= -1; pFrame->data[2] += pFrame->linesize[2] * (pCodecCtx->height/2 - 1);; pFrame->linesize[2] *= -1;
int H264_2_RGB(unsigned char *inputbuf, int frame_size, unsigned char *outputbuf, unsigned int*outsize) { int decode_size; int numBytes; int av_result; uint8_t *buffer = NULL; printf("Video decoding\n"); av_result = avcodec_decode_video(pCodecCtx, pFrame, &decode_size, inputbuf, frame_size); if (av_result < 0) { fprintf(stderr, "decode failed: inputbuf = 0x%x , input_framesize = %d\n", inputbuf, frame_size); return -1; } // Determine required buffer size and allocate buffer numBytes=avpicture_get_size(PIX_FMT_BGR24, pCodecCtx->width, pCodecCtx->height); buffer = (uint8_t*)malloc(numBytes * sizeof(uint8_t)); // Assign appropriate parts of buffer to image planes in pFrameRGB avpicture_fill((AVPicture *)pFrameRGB, buffer, PIX_FMT_BGR24, pCodecCtx->width, pCodecCtx->height); img_convert_ctx = sws_getCachedContext(img_convert_ctx,pCodecCtx->width,pCodecCtx->height, //PIX_FMT_YUV420P,pCodecCtx->width,pCodecCtx->height,pCodecCtx->pix_fmt, pCodecCtx->pix_fmt,pCodecCtx->width,pCodecCtx->height,PIX_FMT_RGB24 , SWS_X ,NULL,NULL,NULL) ; if (img_convert_ctx == NULL) { printf("can't init convert context!\n") ; return -1; } pFrame->data[0] += pFrame->linesize[0] * (pCodecCtx->height-1); pFrame->linesize[0] *= -1; pFrame->data[1] += pFrame->linesize[1] * (pCodecCtx->height/2 - 1);; pFrame->linesize[1] *= -1; pFrame->data[2] += pFrame->linesize[2] * (pCodecCtx->height/2 - 1);; pFrame->linesize[2] *= -1; sws_scale(img_convert_ctx, pFrame->data, pFrame->linesize, 0, 0 - pCodecCtx->width, pFrameRGB->data, pFrameRGB->linesize); if (decode_size) { *outsize = pCodecCtx->width * pCodecCtx->height * 3; memcpy(outputbuf, pFrameRGB->data[0], *outsize); } free(buffer); return 0; }
3. 释放资源
资源的回收。
void H264_Release(void) { avcodec_close(pCodecCtx); av_free(pCodecCtx); av_free(pFrame); av_free(pFrameRGB); }