usar - ndk para android
Conversión y visualización de YUV a RGB usando opengl es 2.0 desde android ndk usando shaders (1)
Eso es un montón de código por recorrer y muchas cosas que pueden salir mal;). Para depurar este tipo de problemas, iría paso a paso.
- simplemente
gl_FragColor = vec4(1.0, 0.5, 0.5, 1.0)
rojo (gl_FragColor = vec4(1.0, 0.5, 0.5, 1.0)
) para asegurarse de que su configuración funciona correctamente. - Intenta dar salida a cada textura en escala de grises. (
gl_FragColor = vec4(y, y, y, 1.0)
) - Si todo eso funciona, lo más probable es que tu conversión de yuv => rgb esté mal en algún lugar.
- Si eso no funciona, entonces sospecharía algo en el mapeo de texturas. Comprueba tu llamada
glTexSubImage2D
. Es posible que deba pasar un paso diferente o usar un sistema de coordenadas diferente.
Actualmente estoy trabajando en un reproductor rtsp en Android usando ffmpeg para conectar y decodificar el flujo de video. Me gustaría usar OpenGL es 2.0 para convertir el marco YUV a un marco RGB y mostrarlo, pero estoy bloqueado (es la primera vez que uso opengl).
Intentaré explicar claramente cuál es mi problema.
Desde el NDK android, inicializo un contexto de opengl (del hilo que quiero usar para mostrar imágenes) usando este método:
//
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;
Luego instalo los gráficos usando este método:
//
//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());
He codificado el tamaño de la pantalla (por ahora) hasta que obtengo algo que funciona.
El método createProgram, carga los shaders, crea el programa, compila y vincula los shaders con éxito.
Aquí están mis 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
};
Luego configuro las texturas yuv y el renderizado a las texturas, en este momento yuv_width [i] y yuv_height [i] están configurados en el valor correcto:
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;
}
}
Para finalizar, mi método dibujar marco:
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");
}
Inicializo una vez las texturas opengl y otros atributos que son necesarios, luego, cuando una trama se decodifica, yuv_planes[0]
búfer en yuv_planes[0]
, u buffer en yuv_planes[ 1]
yv buffer en yuv_planes[2]
.
Una vez que un cuadro se decodifica correctamente usando ffmpeg, llamo en este orden:
- initContext()
- setupGraphics()
- setupYUVTexture()
- renderToTexture()
entonces llamo a drawFrame
. Por supuesto, cuando todo está inicializado, llamo a drawFrame directamente después de cada marco decodificado.
Hay la salida que tengo ahora.
El tamaño de la imagen es correcto, pero ahora estoy bloqueado aquí. ¡No entiendo por qué la imagen es verde! Algunas ideas