简体   繁体   English

使用着色器从android ndk使用opengl es 2.0进行YUV到RGB的转换和显示

[英]YUV to RGB conversion and display using opengl es 2.0 from android ndk using shaders

I am currently working on a rtsp player on android using ffmpeg to connect and decode the video stream. 我目前正在使用ffmpeg在Android上使用rtsp播放器来连接和解码视频流。 I would like to use OpenGL es 2.0 to convert the YUV frame to RGB frame and display it but i am blocked (it's the first time i use opengl). 我想使用OpenGL es 2.0将YUV帧转换为RGB帧并显示它但我被阻止(这是我第一次使用opengl)。

I will try to explain clearly what is my problem. 我会试着清楚地解释一下我的问题是什么。

From the NDK android i initialize an opengl context (from the thread i want to use to display images) using this method : 从NDK android我使用这个方法初始化一个opengl上下文(从我想用来显示图像的线程):

    //
EGLint attribs[] = {
        EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
        EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
        EGL_BLUE_SIZE, 8,
        EGL_GREEN_SIZE, 8,
        EGL_RED_SIZE, 8,
        EGL_ALPHA_SIZE, 8,
        EGL_NONE
};
EGLint contextAttrs[] = {
        EGL_CONTEXT_CLIENT_VERSION, 2,
        EGL_NONE
};

LOGI("Initializing context");

if((display = eglGetDisplay(EGL_DEFAULT_DISPLAY)) == EGL_NO_DISPLAY)
{
    closeContext();
    return;
}

if(!eglInitialize(display, 0, 0))
{
    closeContext();
    return;
}

if(!eglChooseConfig(display, attribs, &config, 1, &numConfigs))
{
    closeContext();
    return;
}

if(!eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format))
{
    closeContext();
    return;
}

ANativeWindow_setBuffersGeometry(window, 0, 0, format);

if(!(surface = eglCreateWindowSurface(display, config, window, 0)))
{
    closeContext();
    return;
}

if(!(context = eglCreateContext(display, config, 0, contextAttrs)))
{
    closeContext();
    return;
}

if(!eglMakeCurrent(display, surface, surface, context))
{
    closeContext();
    return;
}

if(!eglQuerySurface(display, surface, EGL_WIDTH, &width) || !eglQuerySurface(display, surface, EGL_HEIGHT, &height))
{
    closeContext();
    return;
}

LOGI("EGLWIDTH : %d EGLHEIGHT : %d ", (int)width, (int)height);

isInitEGLContext = 1;

Then i setup the graphics using this method : 然后我使用这种方法设置图形:

    //
//Load Vertex and Fragment Shader, attach shader and link program
 programId = createProgram(kVertexShader, kFragmentShader);
 LOGI("Program id : %d error :  %d",(int) programId, glGetError());


if(!programId)
{
    LOGI("Could not create program");
    return;
}
// get index of the generic vertex attribute bound to vPosition
positionObject = (int) glGetAttribLocation(programId, "vPosition");

 // get index of the generic vertex attribute bound to vTexCoord
texturePosition = (int) glGetAttribLocation(programId, "vTexCoord");

// get the location of yTexture within the program (corresponding to   program id)
yuv_texture_object[0] = glGetUniformLocation(programId, "yTexture");

// get the location of uTexture within the program
yuv_texture_object[1] = glGetUniformLocation(programId, "uTexture");

// get the location of vTexture within the program
yuv_texture_object[2] = glGetUniformLocation(programId, "vTexture");

// Setup width of each planes (display size)
stream_yuv_width[0] = 800;
stream_yuv_width[1] = 400;
stream_yuv_width[2] = 400;

// Setup height of each planes (display size)
stream_yuv_height[0] = 600;
stream_yuv_height[1] = 300;
stream_yuv_height[2] = 300;

//set the view port
glViewport(0,0,stream_yuv_width[0],stream_yuv_height[0]);
LOGI("glViewPort() %d ", glGetError());

I have hardcoded the display size (for now) until i get something that work. 我已经硬编码显示尺寸(现在),直到我得到一些有用的东西。

The createProgram method, load the shaders, create the program, compile and link the shaders successfully. createProgram方法,加载着色器,创建程序,成功编译和链接着色器。

Here are my shaders : 这是我的着色器:

const char kVertexShader[] =
"attribute vec4 vPosition;\n"
"attribute vec2 vTexCoord;\n"
"varying vec2 v_vTexCoord;\n"
"void main() {\n"
"gl_Position = vPosition;\n"
"v_vTexCoord = vTexCoord;\n"
"}\n";

const char kFragmentShader[] =
"precision mediump float; \n"
"varying vec2 v_vTexCoord;\n"
"uniform sampler2D yTexture;\n"
"uniform sampler2D uTexture;\n"
"uniform sampler2D vTexture;\n"
"void main() {\n"
"float nx, ny; \n"
"nx = v_vTexCoord.x; \n"
"ny = v_vTexCoord.y; \n"
"float y=texture2D(yTexture, v_vTexCoord).r;\n"
"float u=texture2D(uTexture, vec2(nx / 2.0, ny / 2.0)).r;\n"
"float v=texture2D(vTexture, vec2(nx / 2.0, ny / 2.0)).r;\n"
"y = 1.1643 * (y - 0.0625);\n"
"u = u - 0.5; \n"
"v = v - 0.5; \n"
"float r=y + 1.5958 * v;\n"
"float g=y - 0.39173 * u - 0.81290 * v;\n"
"float b=y + 2.017 * u;\n"
"gl_FragColor = vec4(r, g, b, 1.0);\n"
"}\n";

const GLfloat kVertexInformation[] = {
    -1.0f, 1.0f,           // TexCoord 0 top left
    -1.0f,-1.0f,           // TexCoord 1 bottom left
    1.0f,-1.0f,           // TexCoord 2 bottom right
    1.0f, 1.0f            // TexCoord 3 top right
};
const GLshort kTextureCoordinateInformation[] = {
    0, 0,         // TexCoord 0 top left
    0, 1,         // TexCoord 1 bottom left
    1, 1,         // TexCoord 2 bottom right
    1, 0          // TexCoord 3 top right  
};
const GLuint kStride = 0;//COORDS_PER_VERTEX * 4;
const GLshort kIndicesInformation[] = {
    0, 1, 2,
    0, 2, 3
};

Then i setup the yuv textures and the render to textures, at this moment yuv_width[i] and yuv_height[i] are set to correct value: 然后我设置了yuv纹理和渲染到纹理,此时yuv_width [i]和yuv_height [i]被设置为正确的值:

void setupYUVTexture()
{
//Setup the pixel alignement
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
LOGI("glPixelStorei() : %d ", glGetError());
int i = 0;

for(i = 0 ; i < 3 ; ++i)
{
    //Check if the texture already setup
    if(yuv_texture_id[i] != 0)
    {
        glDeleteTextures(1, &yuv_texture_id[i]);
        yuv_texture_id[i] = 0;
    }
    // Active the i texture
    glActiveTexture(GL_TEXTURE0 + i);

    //Generate the texture name
    glGenTextures(1, &yuv_texture_id[i]);

    // Bind the texture
    glBindTexture(GL_TEXTURE_2D, yuv_texture_id[i]);

   // Setup the texture parameters
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);  
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);   
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);


    //Define the texture image
    glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, yuv_width[i], yuv_height[i], 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, NULL);
    LOGI("glTexImage2D() %d ", glGetError());
}
}

void renderToTexture()
{
 // Generate framebuffer object name
    glGenFramebuffers(1, &frameBufferObject);
    //Bind the framebuffer
    glBindFramebuffer(GL_FRAMEBUFFER, frameBufferObject);


    //Generate render buffer object name
    glGenRenderbuffers(1, &renderBufferObject);

    //Bind render buffer
    glBindRenderbuffer(GL_RENDERBUFFER, renderBufferObject);

   //Create and initialize render buffer for display RGBA with the same size of the viewport
    glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, 800, 600);

    //Attach render buffer to frame buffer object
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderBufferObject);

    //Attach y plane to frame buffer object
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, yuv_texture_id[0], 0);

   //Attach u plane to frame buffer object
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, yuv_texture_id[1], 0);

     //Attach v plane to frame buffer object
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, yuv_texture_id[2], 0);

    // Bind the framebuffer
    glBindFramebuffer(GL_FRAMEBUFFER, 0);

    //Check if the framebuffer is correctly setup
    GLint status = glCheckFramebufferStatus(GL_FRAMEBUFFER);

    if(status != GL_FRAMEBUFFER_COMPLETE)
    {
        LOGI(" FBO setting fault : %d ", status);
        return;
    }
}

To finish, my draw frame method : 为了完成,我的画框方法:

void drawFrame()
{
LOGI("DrawFrame");
glBindFramebuffer(GL_FRAMEBUFFER, frameBufferObject);
printGLError("glBindFramebuffer");
glUseProgram(programId);
printGLError("glUseProgram");
int i = 0;
for(i = 0 ; i < 3 ; ++i)
{
    glActiveTexture(GL_TEXTURE0 + i);
    printGLError("glActiveTexture");

    glBindTexture(GL_TEXTURE_2D, yuv_texture_object[i]);
    printGLError("glBindTexture");

    glUniform1i(yuv_texture_object[i], i);
    printGLError("glUniform1i");            
    LOGI("Plan : %d Largeur : %d Hauteur : %d ", i, yuv_width[i], yuv_height[i]);
    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,yuv_width[i], yuv_height[i], GL_LUMINANCE, GL_UNSIGNED_BYTE, yuv_planes[i]);
            printGLError("glTexSubImage2D");

    }

    glVertexAttribPointer(positionObject, 2, GL_FLOAT, GL_FALSE, kStride, kVertexInformation);
    printGLError("glVertexAttribPointer");

    glVertexAttribPointer(texturePosition, 2, GL_SHORT, GL_FALSE, kStride, kTextureCoordinateInformation);
    printGLError("glVertexAttribPointer");

    glEnableVertexAttribArray(positionObject);      
    printGLError("glVertexAttribArray");

    glEnableVertexAttribArray(texturePosition);
    printGLError("glVertexAttribArray");

    glBindFramebuffer(GL_FRAMEBUFFER, 0);
    printGLError("glBindFramebuffer");

    glDrawElements(GL_TRIANGLE_STRIP, 6, GL_UNSIGNED_SHORT, kIndicesInformation);
    printGLError("glDrawElements");

    eglSwapBuffers(display, surface);
    printGLError("eglSwapBuffers");

}

I initialize once the opengl textures and other attributes which are necessary, then when a frame is decode i recopy y buffer into yuv_planes[0] , u buffer in yuv_planes[ 1] and v buffer in yuv_planes[2] . 我初始化一次opengl纹理和其他必要的属性,然后当一个帧解码时,我将yuv_planes[0] y buffer重新复制到yuv_planes[0]yuv_planes[ 1] u缓冲区和yuv_planes[2] v buffer。

Once a frame is correctly decoded using ffmpeg i call in this order : 使用ffmpeg正确解码帧后,我按此顺序调用:
- initContext() - initContext()
- setupGraphics() - setupGraphics()
- setupYUVTexture() - setupYUVTexture()
- renderToTexture() - renderToTexture()

then i call drawFrame . 然后我调用drawFrame Of course, when everything is initialize i call directly drawFrame after each decoded frame. 当然,当一切都初始化时,我会在每个解码帧之后直接调用drawFrame。

There is the output i have now. 我现在有输出。

视频显示由opengl

The size of the image is correct, but now i am block here. 图像的大小是正确的,但现在我在这里阻止。 I don't understand why the image display is green ! 我不明白为什么图像显示是绿色的! Any ideas 有任何想法吗

That's a lot of code to go through and a lot of things that can go wrong ;). 这是需要经过的大量代码以及许多可能出错的问题;)。 To debug these kind of issues, I would go step by step. 为了调试这些问题,我会一步一步走。

  1. just output red ( gl_FragColor = vec4(1.0, 0.5, 0.5, 1.0) ) to make sure your configuration is working properly. 只输出红色( gl_FragColor = vec4(1.0, 0.5, 0.5, 1.0)gl_FragColor = vec4(1.0, 0.5, 0.5, 1.0) )以确保您的配置正常工作。
  2. try to output every texture in grayscale. 尝试以灰度输出每个纹理。 ( gl_FragColor = vec4(y, y, y, 1.0) ) gl_FragColor = vec4(y, y, y, 1.0)
  3. If all that is working then it most likely means your yuv => rgb conversion is wrong somewhere. 如果一切正常,则很可能意味着您的yuv => rgb转换在某处出错。
  4. If that's not working, then I would suspect something in the texture mapping. 如果那不起作用,那么我会怀疑纹理映射中的某些东西。 Double check your glTexSubImage2D call. 仔细检查你的glTexSubImage2D电话。 You might need to pass a different stride or use a different coordinate system. 您可能需要传递不同的步幅或使用不同的坐标系。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM