视频音频

CPU与OpenGL ES实现YUV数据左右留白边

2017-04-23  本文已影响285人  熊皮皮

以YUV420p为例实现视频左右留白边功能。因YUV颜色空间的UV通道在转换成RGB时需进行偏置,偏置前范围为[-0.5, 0.5],即[-128, 127]。相应的,白色作为255(0xFF),偏置前是127(0x7F)。参考实现代码如下:

/// Y
for (int row = 0; row < dstHeight; ++row) {
    static uint8_t *tmpFrame = (uint8_t *)malloc(dstWidth);
    static int kCopyStartAddress = (dstWidth - scaledWidth) / 2;
    memset(tmpFrame, 0xFF, dstWidth);
    memcpy(tmpFrame + kCopyStartAddress, dstFrame + row * dstWidth, scaledWidth);

    memcpy(dstFrame + row * dstWidth, tmpFrame, dstWidth);
}

/// Cb
int kDstWidthU = dstWidth >> 1;
int kDstHeightU = dstHeight >> 1;
int scaledWidthU = scaledWidth / 2;
uint8_t *kDstFrameStartAddressU = dstFrame  + dstWidth * dstHeight;
for (int row = 0; row < kDstHeightU; ++row) {
    static uint8_t *tmpFrame = (uint8_t *)malloc(kDstWidthU);
    static int kCopyStartAddressU = (kDstWidthU - scaledWidthU) / 2;
    memset(tmpFrame, 0x7F, kDstWidthU);
    memcpy(tmpFrame + kCopyStartAddressU, kDstFrameStartAddressU + row * kDstWidthU, scaledWidthU);

    memcpy(kDstFrameStartAddressU + row * kDstWidthU, tmpFrame, kDstWidthU);
}

/// Cr
int kDstWidthV = dstWidth >> 1;
int kDstHeightV = dstHeight >> 1;
int scaledWidthV = scaledWidth / 2;
uint8_t *kDstFrameStartAddressV = dstFrame  + dstWidth * dstHeight * 5 / 4;
for (int row = 0; row < kDstHeightV; ++row) {
    static uint8_t *tmpFrame = (uint8_t *)malloc(kDstWidthV);
    static int kCopyStartAddressV = (kDstWidthV - scaledWidthV) / 2;
    memset(tmpFrame, 0x7F, kDstWidthV);
    memcpy(tmpFrame + kCopyStartAddressV, kDstFrameStartAddressV + row * kDstWidthV, scaledWidthV);
    
    memcpy(kDstFrameStartAddressV + row * kDstWidthV, tmpFrame, kDstWidthV);
}

如果用OpenGL (ES)实现,修改顶点坐标并设置glClearColor为白色即可,0.5应该是视频与glViewport中设置的宽度比例,示例如下:

GLfloat rect_vertices[] = {
    -0.5,  -1.0,
     0.5,  -1.0,
    -0.5,   1.0,
     0.5,   1.0,
};

另外,OpenGL (ES)实现剪裁图像,如16:9图像至1:1,通常变化的是纹理坐标,例如:

GLfloat texture_vertices[] = {
    0.0f, 0.0f,
    0.5f, 0.0f,
    0.0f, 0.5f,
    0.5f, 0.5f,
};

最终渲染出来的图像是符合预期的。然而,如果做多图像混合,比如在右下角叠加水印,为了适配不同分辨率的视频,通常会在片段着色器中计算纹理坐标。那么,上述修改会导致OpenGL插值时只处理指定的区域,多余的位置会被丢弃。按上面的坐标,当要计算textureCoordinate大于0.5的位置时,此时并没被插值出来,即得不到大于0.5的这些坐标。

影响片段着色器得到插值纹理坐标的顶点着色器,对于常规的数字图像处理,通常是这么实现的:

#version 300 es
in vec4 position; 
in vec2 inputTextureCoordinate;
out vec2 textureCoordinate;
void main()
{
    gl_Position = position;
    textureCoordinate = inputTextureCoordinate;
}

为了得到完整的纹理坐标,客户端也只能上传完整坐标点,然后在片段着色器中判断,这样的实现在一次滤波时最差的情况将进行glViewport指定的width x height次判断,严重影响性能。

上一篇下一篇

猜你喜欢

热点阅读