上一章寫了如何通過SPPV210芯片H264硬件解碼生存yuv格式視頻文件,yuv格式是由一幀幀的圖像組成,做一下格式轉換寫到framebuffer上即可實現顯示了。
首先看上一章中關於解碼後獲得YUV幀數據的處理方法,下面是解碼部分代碼。
if(status==MFC_GETOUTBUF_DISPLAY_DECODING || status==MFC_GETOUTBUF_DISPLAY_ONLY) {
if(!ylin)
ylin = (uint8_t *)malloc(oinfo.img_width*oinfo.img_height);
if(!ylin) {
fprintf(stderr,"Out of memory.\n");
break;
}
// converted tiled to linear nv12 format - Y plane
csc_tiled_to_linear(ylin, (uint8_t *)oinfo.YVirAddr, oinfo.img_width, oinfo.img_height);
fwrite(ylin,1, oinfo.img_width*oinfo.img_height, fpo);
if(!clin)
clin = (uint8_t *)malloc(oinfo.img_width*oinfo.img_height/2);
if(!clin) {
fprintf(stderr,"Out of memory.\n");
break;
}
p_U = (uint8_t *)clin;
p_V = (uint8_t *)clin;
p_V += ((oinfo.img_width * oinfo.img_height) >> 2);
// converted tiled to linear uv format - C plane
csc_tiled_to_linear_deinterleave(p_U, p_V, (uint8_t *)oinfo.CVirAddr, oinfo.img_width, oinfo.img_height/2);
fwrite(clin,1,oinfo.img_width*oinfo.img_height/2,fpo);
show_cnt++;
}
解碼完成時,獲得到一幀YUV格式數據後,會將status設置爲MFC_GETOUTBUF_DISPLAY_DECODING ,從而進入下面的程序。ylin、clin分別是存儲Y分量數據和UV分量數據的地址,主要到爲其申請到的地址空間長度分別爲img_width*img_height和img_width*img_height/2,由於解碼器輸出數據爲tiled格式,需要將其轉爲line格式,這裏分別調用了csc_tiled_to_linear()函數和csc_tiled_to_linear_deinterleave()函數對其進行格式轉換,轉換後通過fwrite()函數將其寫入到輸出文件即可。
這裏要對解碼後的數據進行顯示,由於液晶屏幕顯示的數據格式爲rgb格式的,所以就要多一次格式轉換。這次解碼後輸出的YUV數據與之前寫的一篇顯示攝像頭畫面程序中的NV12格式數據類似,很多函數直接使用即可。
首先看屏幕的初始化,代碼如下。
Fb *fb;
char *fb_dev = "/dev/fb0";
unsigned char* yuv420p = NULL;
unsigned char* rgb = NULL;
// 屏幕初始化
fb = new Fb(fb_dev, 80, 0, 640, 480);
if(!fb->OpenDevice()){
printf("Fb Open error\n");
return -1;
}
fb->Trans(&rgb);
這裏定義了液晶屏控制類,在其構造函數中定義了顯示圖像的位置(80,0),顯示圖像的大小(640,480),這裏圖像的大小要與解碼的h264文件的圖像格式大小一致,不然會顯示圖像不正常。然後調用Trans()函數來取到顯示區buff 的地址,需要顯示圖像時,往這個地址寫rgb圖像格式數據就行。
接下來看顯示部分,代碼如下。
if(status==MFC_GETOUTBUF_DISPLAY_DECODING || status==MFC_GETOUTBUF_DISPLAY_ONLY) {
if(!ylin)
ylin = (uint8_t *)malloc(oinfo.img_width*oinfo.img_height);
if(!ylin) {
fprintf(stderr,"Out of memory.\n");
break;
}
if(!yuv420p)
yuv420p = (uint8_t *)malloc(oinfo.img_width*oinfo.img_height*3/2);
if(!yuv420p) {
fprintf(stderr,"Out of memory.\n");
break;
}
// converted tiled to linear nv12 format - Y plane
csc_tiled_to_linear(ylin, (uint8_t *)oinfo.YVirAddr, oinfo.img_width, oinfo.img_height);
memcpy(yuv420p, ylin, oinfo.img_width*oinfo.img_height);
if(!clin)
clin = (uint8_t *)malloc(oinfo.img_width*oinfo.img_height/2);
if(!clin) {
fprintf(stderr,"Out of memory.\n");
break;
}
// converted tiled to linear uv format - C plane
csc_tiled_to_linear(clin, (uint8_t *)oinfo.CVirAddr, oinfo.img_width, oinfo.img_height/2);
memcpy(yuv420p+oinfo.img_width*oinfo.img_height, clin, oinfo.img_width*oinfo.img_height/2);
decodeYUV420SP((unsigned int*)rgb, yuv420p, oinfo.img_width, oinfo.img_height);
fb->Draw();
show_cnt++;
}
這裏多了申請了一個地址yuv420p,用來存放完整的一幀yuv格式數據,之前是通過ylin,clin兩個地址存放。注意到這裏UV分量數據轉換時時調用的是csc_tiled_to_linear()函數,輸出的是NV12格式數據。YUV與NV12格式數據的區別在於,UV分量排列時,YUV是先存放U分量,然後存放V分量,而NV12是UV交叉排列。調用decodeYUV420SP()函數來完成NV12格式數據到rgb格式數據的轉換,然後調用Draw()函數就完成了一幀圖像的顯示。
具體程序我上傳到了http://download.csdn.net/detail/westlor/9403775,歡迎下載查看。