美文网首页视频音频
CPU与OpenGL ES实现YUV数据左右留白边

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

作者: 熊皮皮 | 来源:发表于2017-04-23 21:09 被阅读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次判断,严重影响性能。

    相关文章

      网友评论

      • ai___believe:皮,我觉得你可以给libyuv写个opengles的实现,分享出来,或者搞个开源,大家一起写
        熊皮皮:@ai___believe 格式转换虽然可以用OpenGL ES实现,从GPU读回来还是挺耗时的,主要是代码会比libyuv多:joy:

      本文标题:CPU与OpenGL ES实现YUV数据左右留白边

      本文链接:https://www.haomeiwen.com/subject/jynyzttx.html