利用ffmpeg將H264解碼為RGB
阿新 • • 發佈:2019-02-06
由於公司買到了一個不提供解碼器的裝置,我不得已還要做解碼的工作。在網上找了一圈,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);
}