2016-01-13 3 views
24

Ich arbeite derzeit an einem Rtsp-Player auf Android mit ffmpeg, um den Video-Stream zu verbinden und zu dekodieren. Ich würde gerne OpenGL es 2.0 verwenden, um den YUV-Frame in einen RGB-Frame umzuwandeln und anzuzeigen, aber ich bin blockiert (es ist das erste Mal, dass ich opengl benutze).YUV zu RGB Konvertierung und Anzeige mit Opengles 2.0 von android ndk mit Shadern

Ich werde versuchen, klar zu erklären, was mein Problem ist.

vom NDK android i einen OpenGL-Kontext initialisiert werden (aus dem Thread i Bilder verwenden möchten anzuzeigen) mit dieser Methode:

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

Dann i-Setup die Grafik mit dieser Methode:

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

Ich habe die Display-Größe (für jetzt) ​​hartcoded, bis ich etwas, das funktioniert.

Die createProgram-Methode, laden Sie die Shader, erstellen Sie das Programm, kompilieren und verknüpfen Sie die Shader erfolgreich.

Hier meine Shadern sind:

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

Dann werden die YUV-Texturen i-Setup und die zu Texturen, in diesem Moment yuv_width [i] und yuv_height [i] machen gesetzt Wert korrigieren:

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

zu beenden, meine Strecke Methode:

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

} 

ich initialisieren einmal die OpenGL-Texturen und andere Attribute, die notwendig sind, dann, wenn ein Rahmen dekodiert wird, rezykliere ich den Puffer in yuv_planes[0], den Puffer in yuv_planes[ 1] und den Puffer in yuv_planes[2].

Sobald ein Rahmen korrekt decodiert wird ffmpeg mit i in dieser Reihenfolge nennen:
- initContext()
- setupGraphics()
- setupYUVTexture()
- renderToTexture()

dann rufen i drawFrame. Natürlich, wenn alles initialisiert ist, rufe ich nach jedem decodierten Frame direkt drawFrame auf.

Dort ist der Ausgang, den ich jetzt habe.

Video display by opengl

Die Größe des Bildes ist richtig, aber jetzt bin ich hier blockieren. Ich verstehe nicht, warum die Bildanzeige grün ist! Irgendwelche Ideen

+0

Gibt der glTexSubImage2D einen Fehler bei jedem der 3 Anrufe aus? Beachten Sie, dass Sie (++ i) verwenden, das dann [1, 3] statt in [0, 2] verwendet. yuv_width [3] scheint nicht definiert zu sein, daher sollte der letzte Aufruf definitiv falsch sein (für i = 3). Sie sollten auch wissen, dass glGetError den obersten Wert aus dem Fehler-Stack zurückgibt und es öffnet. Das bedeutet, dass Sie normalerweise eine while-Schleife benötigen, um alle Fehler vom Stapel zu bekommen. Da Sie dies nicht tun, können Sie den Fehler beim nächsten glGetError-Aufruf melden, obwohl er von einer vorherigen Zeile generiert wurde. –

+0

Wenn ich mir den Shader ansehe, scheint es, dass Sie Farbkomponenten nicht als YUV, sondern als RGB verwenden und sie dann in RGB umwandeln. Das scheint mir ok zu sein, aber du solltest dann GL_RGB anstelle von GL_LUMINANCE verwenden und die RGB-Werte im Shader als YUV behandeln, was du bereits zu tun scheinst (oder nicht, der Fragment-Shader wirkt etwas komisch. Vielleicht würden einige Kommentare im Code Hilfe). –

+0

Ich habe den Beitrag bearbeitet, um Kommentare hinzuzufügen und die Ausgabe meines Android Monitors hinzuzufügen. Um zu beginnen, melden glTexSubImage2D Fehler beim ersten Anruf und beim letzten Anruf. Das Inkrement vor Anweisung, das ich in meiner for-Schleife verwende, ist korrekt. In diesem Fall wird es nicht von 1 bis 3, sondern von 0 bis 2 beginnen. Einige Kompilierungen verwenden ein temporäres Attribut, wenn Sie inkrement nach Anweisung verwenden, ich habe dafür verwendet, für eine Schleife zu schreiben, weil es kein temporäres Attribut verwendet ein bisschen schneller). Ich hatte ein Protokoll, bevor ich meine Android Monitor-Ausgabe bekomme, um Ihnen zu zeigen, dass es korrekt ist. –

Antwort

1

Das ist eine Menge Code zu durchlaufen und eine Menge Dinge, die schief gehen können;). Um diese Art von Problemen zu debuggen, würde ich Schritt für Schritt gehen.

  1. nur rot ausgeben (gl_FragColor = vec4(1.0, 0.5, 0.5, 1.0)), um sicherzustellen, dass Ihre Konfiguration ordnungsgemäß funktioniert.
  2. versuchen, jede Textur in Graustufen auszugeben.(gl_FragColor = vec4(y, y, y, 1.0))
  3. Wenn alles funktioniert, dann bedeutet es wahrscheinlich, dass deine yuv => rgb-Konvertierung irgendwo falsch ist.
  4. Wenn das nicht funktioniert, dann würde ich etwas im Textur Mapping vermuten. Überprüfen Sie Ihren glTexSubImage2D Anruf. Möglicherweise müssen Sie einen anderen Schritt ausführen oder ein anderes Koordinatensystem verwenden.