我正在尝试使用MediaCodec和Surfaces对电影进行编码(像素缓冲模式有效,但性能不够好)。但是,每次我尝试调用eglSwapBuffers()
时,都会失败并显示EGL_BAD_SURFACE
,因此dequeueOutputBuffer()
总是返回-1(INFO_TRY_AGAIN_LATER
)
我已经看到了Bigflake和Grafika上的示例,并且我有另一个可以正常工作的项目,但是我需要在另一个稍有不同的设置中使它工作。
我目前有一个GLSurfaceView,它可以进行屏幕渲染,并带有自定义EGLContextFactory / EGLConfigChooser。这使我可以创建共享上下文,以用于本机库中的单独OpenGL渲染。它们是使用EGL10创建的,但这不应该成为问题,因为据我所知,底层上下文仅关心客户端版本。
我已使用以下配置确保上下文可记录:
private android.opengl.EGLConfig chooseConfig14(android.opengl.EGLDisplay display) {
// Configure EGL for recording and OpenGL ES 3.x
int[] attribList = {
EGL14.EGL_RED_SIZE, 8,
EGL14.EGL_GREEN_SIZE, 8,
EGL14.EGL_BLUE_SIZE, 8,
EGL14.EGL_ALPHA_SIZE, 8,
EGL14.EGL_RENDERABLE_TYPE, EGLExt.EGL_OPENGL_ES3_BIT_KHR,
EGLExt.EGL_RECORDABLE_ANDROID, 1,
EGL14.EGL_NONE
};
android.opengl.EGLConfig[] configs = new android.opengl.EGLConfig[1];
int[] numConfigs = new int[1];
if (!EGL14.eglChooseConfig(display, attribList, 0, configs, 0,
configs.length, numConfigs, 0)) {
return null;
}
return configs[0];
}
现在,我试图简化场景,因此在开始记录时,我将MediaCodec的实例初始化为编码器,并在GLSurfaceView的线程上调用createInputSurface()
。有了曲面后,将其转换为EGLSurface(EGL14),如下所示:
EGLSurface createEGLSurface(Surface surface) {
if (surface == null) return null;
int[] surfaceAttribs = { EGL14.EGL_NONE };
android.opengl.EGLDisplay display = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
android.opengl.EGLConfig config = chooseConfig14(display);
EGLSurface eglSurface = EGL14.eglCreateWindowSurface(display, config, surface, surfaceAttribs, 0);
return eglSurface;
}
从相机收到新帧时,我将其发送到屏幕和另一个处理记录的类。该类仅将其呈现到从MediaCodec的输入表面构建的EGLSurface
中,如下所示:
public void drawToSurface(EGLSurface targetSurface, int width, int height, long timestampNano, boolean ignoreOrientation) {
if (mTextures[0] == null) {
Log.w(TAG, "Attempting to draw without a source texture");
return;
}
EGLContext currentContext = EGL14.eglGetCurrentContext();
EGLDisplay currentDisplay = EGL14.eglGetCurrentDisplay();
EGL14.eglMakeCurrent(currentDisplay, targetSurface, targetSurface, currentContext);
int error = EGL14.eglGetError();
ShaderProgram program = getProgramForTextureType(mTextures[0].getTextureType());
program.draw(width, height, TextureRendererView.LayoutType.LINEAR_HORIZONTAL, 0, 1, mTextures[0]);
error = EGL14.eglGetError();
EGLExt.eglPresentationTimeANDROID(currentDisplay, targetSurface, timestampNano);
error = EGL14.eglGetError();
EGL14.eglSwapBuffers(currentDisplay, targetSurface);
error = EGL14.eglGetError();
Log.d(TAG, "drawToSurface");
}
由于某种原因,eglSwapBuffers()
失败并报告EGL_BAD_SURFACE
,但我还没有找到进一步调试方法。
更新
我尝试在使当前表面成为当前调用之后查询当前表面,并且该表面总是返回格式错误的表面(从内部看,我可以看到句柄为0
,并且在查询时始终失败)。似乎eglMakeCurrent()
默默地未能将绑定表面设置为上下文。
此外,我确定此问题出现在高通公司的芯片(Adreno)上,而不是麒麟上,所以它肯定与本机OpenGL实现有关(这很有趣,因为我一直注意到Adreno在处理问题时更加宽容“错误的” OpenGL配置)
答案 0 :(得分:0)
已修复!事实证明,EGL10和EGL14看起来可以很好地配合使用,但是在某些情况下会以非常微妙的方式失败,例如我遇到的那种方式。
就我而言,我编写的EGLContextFactory是使用EGL10创建基本的OpenGL ES上下文,然后根据需要再次使用EGL10创建更多的共享上下文。虽然我可以使用EGL14(在Java或C语言中)检索它们,并且上下文句柄始终是正确的(在上下文之间共享纹理就像一个咒语一样工作),但是当尝试使用从上下文或EGL10起源创建的EGLSurface时,它神秘地失败了...在Adreno芯片上。
解决方案是将EGLContextFactory切换为从使用EGL14创建的上下文开始,然后继续使用EGL14创建共享上下文。对于仍然需要EGL10的GLSurfaceView,我不得不使用一个hack
@Override
public javax.microedition.khronos.egl.EGLContext createContext(EGL10 egl10, javax.microedition.khronos.egl.EGLDisplay eglDisplay, javax.microedition.khronos.egl.EGLConfig eglConfig) {
EGLContext context = createContext();
boolean success = EGL14.eglMakeCurrent(mBaseEGLDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, context);
if (!success) {
int error = EGL14.eglGetError();
Log.w(TAG, "Failed to create a context. Error: " + error);
}
javax.microedition.khronos.egl.EGLContext egl14Context = egl10.eglGetCurrentContext(); //get an EGL10 context representation of our EGL14 context
javax.microedition.khronos.egl.EGLContext trueEGL10Context = egl10.eglCreateContext(eglDisplay, eglConfig, egl14Context, glAttributeList);
destroyContext(context);
return trueEGL10Context;
}
这是用EGL14创建一个新的共享上下文,使其成为最新的,然后检索其EGL10版本。该版本不能直接使用(由于我无法完全理解的原因),但是该版本的另一个共享上下文也可以正常使用。然后可以破坏开始的EGL14上下文(在我的情况下,将其放入堆栈中,稍后再使用)。
我真的很想知道为什么需要这种hack,但我很高兴能有一个可行的解决方案。