1. 相关名词解释

1.1      什么是YUV编码

YUV主要用于优化彩色视频信号的传输,使其向后相容老式黑白电视。与RGB视频信号传输相比,它最大的优点在于只需占用极少的频宽(RGB要求三个独立的视频信号同时传输)。其中“Y”表示明亮度,也就是灰阶值;而“U”和“V”表示的则是色度,作用是描述影像色彩及饱和度,用于指定像素的颜色。“亮度”是透过RGB输入信号来建立的,方法是将RGB信号的特定部分叠加到一起。“色度”则定义了颜色的两个方面─色调与饱和度,分别用CrCb来表示。其中,Cr反映了RGB输入信号红色部分与RGB信号亮度值之间的差异。而Cb反映的是RGB输入信号蓝色部分与RGB信号亮度值之间的差异。

采用YUV色彩空间的重要性是它的亮度信号Y和色度信号UV是分离的。如果只有Y信号分量而没有UV分量,那么这样表示的图像就是黑白灰度图像。彩色电视采用YUV空间正是为了用亮度信号Y解决彩色电视机与黑白电视机的兼容问题,使黑白电视机也能接收彩色电视信号。

 

1.2      什么是YUV420格式

YUV420 格式即YUV 4:2:0格式。4:2:0并不意味着只有YCb而没有Cr分量。它指得是对每行扫描线来说,只有一种色度分量以2:1的抽样率存储。相邻的扫描行存储不同的色度分量,也就是说,如果一行是4:2:0的话,下一行就是4:0:2,再下一行是4:2:0...以此类推。对每个色度分量来说,水平方向和竖直方向的抽样率都是2:1,所以可以说色度的抽样率是4:1。对非压缩的8比特量化的视频来说,每个由2*222列相邻的像素组成的宏像素需要占用6字节内存。

2. 视频播放的实现

2.1      读取YUV420格式帧

本文使用的YUV420格式视频分辨率为:x86架构下SylixOS系统的默认的分辨率640*480,每秒的帧数为25帧。

所以视频的每一帧宽度为640,高度为480,共640*480个像素点。在YUV420格式中,一个像素点对应一个Y,一个2*2的小方块对应一个UV。可以准确的计算出一帧YUV420图像大小为:

Y+U+V

其中:

Y = 640(width) * 480(height);

U = Y / 4;

V = Y / 4;

则一帧图像的总大小为:width * height * 3/2 ,单位是字节。

 

读入函数如程序清单2.1所示:

 

程序清单2.1 读入一帧YUV图像

#define nWidth     (640)                     /*  帧宽度                    */

#define nHeight    (480)                     /*  帧高度                    */

#define FrameSize  (nWidth*nHeight*3/2)      /*  帧大小                    */

 

fread(pBuf, 1, FrameSize, fp);               /*  读入一帧YUV               */

 

2.2      显示一帧画面

要将每一帧画面显示到屏幕上,需要打开系统的framebuffer设备,将framebuffer物理内存映射到用户空间,并在对应区域写入颜色值,对应的颜色会自动在屏幕上显示。只要将一帧所有的像素点颜色写入对应区域,完整的一帧画面就会在屏幕上显示。

源视频帧数为每秒25帧,即每一帧间隔时间为0.04秒,为了保证播放的流畅度,加入定时器控制显示帧间隔。

显示一帧的代码实现如程序清单2.2 所示:

 

程序清单2.2 显示一帧YUV图像

#define FrameInterval   (40000)              /*  每帧间隔时间,单位微秒       */

 

gettimeofday(&start,NULL);

for (j = 0; j< nHeight;j++) {

    for (i = 0;i < nWidth;i++) {

        Color = pointXY[j][i][iPlayFrame];

 

        draw_pixel(pframebuffer, &scr_info, &var_info, i, j, Color);

    }

}                                            /*  整个循环为一帧画面的输出     */

 

do {

    gettimeofday(&end,NULL);

 

    time_use=(end.tv_sec-start.tv_sec)*1000000+(end.tv_usec-start.tv_usec);

}while(time_use <= FrameInterval);           /*  控制帧间隔时间              */

 

 

2.3      YUV420格式帧转换为RGB888格式帧

SylixOS系统提供接口所接受的颜色信息格式为RGB格式,形如”#FF0000”为红色的颜色值。

需要将读入的YUV420信息转码为RGB信息,RGBYUV的变换公式如图2.3所示:

SylixOS中视频播放的实现_第1张图片

2.3  RGBYUV变换公式

 

RGB 也可以直接从YUV (256级别) 计算:

R = Y + 1.402 (Cr-128)

G = Y - 0.34414 (Cb-128) - 0.71414 (Cr-128)

B = Y + 1.772 (Cb-128)

 

但是直接计算涉及浮点运算,影响转码效率,所以利用空间换时间思路,以查找表来替代转换过程中的一些计算。

查表法转换的实现函数如程序清单2.4所示,由于表数据较多,所以未列出具体的表信息。

 

程序清单2.4 转码一帧YUV图像

for (int i = 0;i < height;i++){

    for (int j = 0;j < width;j++){

    yIdx = i * width + j;

    vIdx = (i/2) * (width/2) + (j/2);

    uIdx = vIdx;

 

    rdif = Table_fv1[vData[vIdx]];

    invgdif = Table_fu1[uData[uIdx]] + Table_fv2[vData[vIdx]];

    bdif = Table_fu2[uData[uIdx]];

 

    bgr[0] = yData[yIdx] + bdif;       //B

    bgr[1] = yData[yIdx] - invgdif;    //G

    bgr[2] = yData[yIdx] + rdif;       //R

 

    for (int e = 0;e < 3; e++)

    {

    if(bgr[e] < 0 || bgr[e] > 255)

        bgr[e] = (bgr[e] < 0)?0:255;

    }

 

    pointXY[i][j][iReadFrame] = rgbColor(bgr[0], bgr[1], bgr[2]);

    for (int k = 0;k < 3;k++){

        idx = (i * width + j) * 3 + k;

        pBGR24[idx] = bgr[k];

        }

    }

}

iReadFrame++;

 

3. 总结

SylixOS中我们可以利用系统提供的framebuffer设备接口,读入文件,转码,写入内存,从而实现图像、视频的播放。

在转码过程中,需要考虑转码效率,采取更加节约时间的方式转换。同时要考虑系统的性能,适当减少帧数以达到视频更加流畅的目的。