上一章写了如何通过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,欢迎下载查看。