我正在开发一个Android应用程序,它可以从外部摄像头渲染视频。当我们在ndk中与相机进行通信时,视频帧通过NDK层中的opengl呈现到屏幕,并且速度更快。我没有编写来自第三方(相机开发人员)的代码,他们已经在MyGLSurfaceView中实现了SurfaceDestroyed方法,如下所示:
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.d(TAG, "Inside GL surfaceDestroyed" );
// TODO Auto-generated method stub
myRenderer.surfaceDestroyed(); //call this to clean up the renderer.
super.surfaceDestroyed(holder);
}
myRenderer的位置
myRenderer = new MyGLRenderer(mContext);
现在在渲染器中编写了以下代码:
public class MyGLRenderer implements Renderer {
public void surfaceDestroyed()
{
Log.d(TAG, "Inside surfaceDestroyed" );
/* Note: As per doc, GLSurfaceView kills the renderer and deletes any textures associated with it.
* However, it does not clean up textures created in NDK. So, we need to do this explicitly.
*/
mGLAdapter.destroyGlTexturesJni(myRenderer);
}
请注意,NDK中创建的纹理不会被清除的注释。这是真的?我问的原因是我从这个电话中得到以下错误:
01-19 12:01:19.715: E/libEGL(27208): call to OpenGL ES API with no current context (logged once per thread)
我已经放了一些调试语句来确定错误被抛出的NDK代码的确切位置,并且发现它在一个名为ImageRender的类(它调用Clean)的析构函数中,这里是触发它的行:
void ImageRender::Clean()
{
// destruct texture
if (m_unTexture != -1)
glDeleteTextures( 1,&m_unTexture );
....
看起来他们在这里设置了m_unTexture(这是gl上下文吗?):
int ImageRender::InitTexture( const int nWidth, const int nHeight )
{
// Enable texture 2D first.
glEnable( GL_TEXTURE_2D );
// create texture
glGenTextures( 1,&m_unTexture );
// bind texture to target.
glBindTexture( GL_TEXTURE_2D,m_unTexture );
...
并且如果他们检查glGetError并且如果他们没有得到GL_NO_ERROR则通过代码将它们设置为-1,这样他们就知道它是无效的。
好的,我的问题是我真的需要清理在NDK中创建的纹理,如果是这样,我怎么能没有我看到的错误。我尝试使用:
//from MyGLSurfaceView
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.d(TAG, "Inside GL surfaceDestroyed" );
// TODO Auto-generated method stub
queueEvent(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
myRenderer.surfaceDestroyed(); //call this to clean up the renderer.
}
});
super.surfaceDestroyed(holder);
}
确保在opengl线程上调用渲染器,但是它有相同的错误。
有什么建议吗?顺便说一句代码似乎工作正常,如果我只是不调用这个方法,我已经做了很长一段时间,但我遇到的问题,当我开始一个使用opengl框架的活动时,它会被最后一帧初始化以前的opengl活动。似乎把这个电话回来解决问题只是在某些时候。
答案 0 :(得分:3)
你不能在UI线程上调用myRenderer.surfaceDestroyed,因为它没有OpenGL上下文。 您必须使用queueEvent在OpenGL线程上运行surfaceDestroyed。使用Runnable实现的问题如下:queueEvent是非阻塞的。因此,在调用任何本机代码之前,myRenderer.surfaceDestroyed()和GL上下文被销毁之前,很可能会调用super.surfaceDestroyed(holder)。
不确定这是否有效,但在queueEvent调用后尝试阻止UI线程并等待Runnable完成它的工作。像这样:
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.d(TAG, "Inside GL surfaceDestroyed" );
queueEvent(new Runnable() {
@Override
public void run() {
myRenderer.surfaceDestroyed();
synchronized (myRenderer) {
myRenderer.notify();
}
}
});
synchronized (myRenderer)
{
try { myRenderer.wait(100); }
catch (InterruptedException e) { }
}
super.surfaceDestroyed(holder);
}
这将阻止UI线程最多100毫秒,允许您的本机代码清理上下文。如果需要,可以增加100到更多。
在最坏的情况下 - 只是不要自己释放GL资源。让GL语境为你释放它们。
答案 1 :(得分:0)
回答你的另一个问题:m_unTexture是GL纹理名称(不是GL上下文)。 -1值似乎只是纹理名称的默认初始化值,如果glGenTextures()无法为纹理分配新名称,则不会触及该值。