Conversion YUV en RGB et affichage utilisant OpenGLES 2.0 de android ndk en utilisant des shaders

je travaille actuellement sur un lecteur rtsp sur android en utilisant ffmpeg pour connecter et décoder le flux vidéo. Je voudrais utiliser OpenGL es 2.0 pour convertir le cadre YUV en cadre RGB et l'afficher mais je suis bloqué (c'est la première fois que j'utilise opengl).

je vais essayer d'expliquer clairement ce qu'est mon problème.

à partir de L'android NDK j'initialise un contexte opengl (à partir du thread que je veux utiliser pour afficher des images) en utilisant cette méthode :

    //
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;

puis je configure les graphiques en utilisant cette méthode:

    //
//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());

j'ai codé en dur la taille de l'affichage (pour l'instant) jusqu'à ce que j'obtienne quelque chose qui fonctionne.

la méthode createProgram, chargez les shaders, créez le programme, compilez et liez les shaders avec succès.

Voici mes 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
};

puis je configure les textures yuv et le rendu aux textures, en ce moment yuv_width[i] et yuv_height[i] sont définis à valeur correcte:

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;
    }
}

pour finir, mon draw frame méthode:

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");

}

j'initialise une fois que les textures opengl et les autres attributs nécessaires sont activés, puis quand un cadre est décodé, je recopie y buffer dans yuv_planes[0], u le tampon yuv_planes[ 1] et v de la mémoire tampon dans yuv_planes[2].

une fois qu'un cadre est correctement décodé en utilisant ffmpeg j'appelle dans cet ordre:

- initContext()

- setupGraphics()

- setupYUVTexture()

- renderToTexture()

puis-je appeler drawFrame. Bien sûr, quand tout est initialiser j'appelle directement étirage après chaque trame décodée.

il y a la sortie que j'ai maintenant.

Video display by opengl

La taille de l'image est correcte, mais maintenant je suis bloquer ici. Je ne comprends pas pourquoi l'affichage est vert ! Toutes les idées

24
demandé sur grunk 2016-01-13 13:29:03

1 réponses

C'est beaucoup de code à faire et beaucoup de choses qui peuvent mal se passer ;). Pour réfuter ce genre de problèmes, j'irais pas à pas.

  1. juste à la sortie rouge (gl_FragColor = vec4(1.0, 0.5, 0.5, 1.0)) pour vous assurer que votre configuration fonctionne correctement.
  2. essayez de sortir chaque texture en échelle de gris. (gl_FragColor = vec4(y, y, y, 1.0))
  3. si tout cela fonctionne, alors cela signifie très probablement que votre conversion yuv => rgb est erronée quelque part.
  4. si cela ne fonctionne pas, alors je suspecterais quelque chose dans la cartographie des textures. Vérifiez votre glTexSubImage2D appel. Vous pourriez avoir besoin de passer une foulée différente ou utiliser un système de coordonnées différent.
1
répondu mbonnin 2016-03-23 22:04:02