美文网首页音视频开发
从 AVFrame 到渲染成纹理我趟过的坑

从 AVFrame 到渲染成纹理我趟过的坑

作者: zhonglaoban | 来源:发表于2023-07-06 19:42 被阅读0次

    FFmpeg 解码出来 AVFrame 后,要渲染出来正确的图像,还是有不少问题的,下面来看看几种常见的渲染方式吧(以yuv格式为例)。

    使用 AVFrame 的 width,height 直接渲染

    在 width 和 lineSize 不一样的情况下,渲染出来的图像会有乱码。

    int width - frame->width;
    int width = frame->height;
    glTexImage2D(texture_y, 0, GL_LUMINANCE, width, height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, frame->data[0]);
    glTexImage2D(texture_u, 0, GL_LUMINANCE, width/2, height/2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, frame->data[1]);
    glTexImage2D(texture_v, 0, GL_LUMINANCE, width/2, height/2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, frame->data[2]);
    

    使用 AVFrame 的 lineSize,height 直接渲染

    在 width 和 lineSize 不一样的情况下,渲染出来的图像会有绿边。

    int width - frame->width;
    int width = frame->height;
    glTexImage2D(texture_y, 0, GL_LUMINANCE, frame->linesize[0], height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, frame->data[0]);
    glTexImage2D(texture_u, 0, GL_LUMINANCE, frame->linesize[1], height/2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, frame->data[1]);
    glTexImage2D(texture_v, 0, GL_LUMINANCE, frame->linesize[2], height/2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, frame->data[2]);
    

    裁剪有效数据后渲染

    直接裁剪内存,性能开销比较大。

    int w - frame->width;
    int h = frame->height;
    if(y == nullptr){
      y = new unsigned char[w*h];
    }
    if(u == nullptr){
      u = new unsigned char[w*h/4];
    }
    if(v == nullptr){
      v = new unsigned char[w*h/4];
    }}
    int l1 = frame->linesize[0];
    int l2 = frame->linesize[1];
    int l3 = frame->linesize[2];
    for(int i= 0; i < h ; i++)
    {
      memcpy(y + w*i,frame->data[0] + l1* i, sizeof( unsigned char)*w);
    }
    for(int i= 0 ; i < h/2 ; i++)
    {
      memcpy(u + w/2*i,frame->data[1] + l2 * i, sizeof(unsigned char)*w/2);
      memcpy(v + w/2*i,frame->data[2] + l3 * i, sizeof(unsigned char)*w/2);
    }
    glTexImage2D(texture_y, 0, GL_LUMINANCE, w, h, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, y);
    glTexImage2D(texture_u, 0, GL_LUMINANCE, w/2, h/2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, u);
    glTexImage2D(texture_v, 0, GL_LUMINANCE, w/2, h/2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, v);
    

    渲染后再进行裁剪

    按 lineSize 去渲染,渲染的时候需要改变纹理坐标。

    int width - frame->width;
    int width = frame->height;
    glTexImage2D(texture_y, 0, GL_LUMINANCE, frame->linesize[0], height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, frame->data[0]);
    glTexImage2D(texture_u, 0, GL_LUMINANCE, frame->linesize[1], height/2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, frame->data[1]);
    glTexImage2D(texture_v, 0, GL_LUMINANCE, frame->linesize[2], height/2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, frame->data[2]);
    float textureCoords[] = {
            0.0f, 0.0f, // bottom left
            0.0f, 1.0f, // top left
            1.0f, 1.0f, // top right
            1.0f, 0.0f, // bottom right
    };
    textureCoords[5] = 1.0f * width/frame->linesize[0];
    textureCoords[7] = 1.0f * width/frame->linesize[0];
    glBindBuffer(GL_ARRAY_BUFFER, textureVBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(textureCoords), textureCoords, GL_STATIC_DRAW);
    glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
    

    通过对比上面的四种渲染方式,我们发现第四种不仅最高效,而且没有异常问题,因此值得推荐。

    相关文章

      网友评论

        本文标题:从 AVFrame 到渲染成纹理我趟过的坑

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