2014-12-23 27 views
1

我试图用一个表面纹理从我的设备的背面摄像头显示相机预览,但我不断收到错误相机预览使用表面纹理

bindTextureImage:错误结合外部纹理图像0X2:0x502

望着那说

SurfaceTexture.UpdateTexImage() 

纵观Android文档行,它好像这可能是CA通过在SurfaceTexture.OnFrameAvailableListener的OnFrameAvailable方法上调用UpdateTexImage来使用。根据文档“可以在任意线程上调用回调(SurfaceTexture.OnFrameAvailableListener),因此调用updateTexImage()时不先安全地将OpenGL ES上下文绑定到调用回调的线程“。

如何“将OpenGL ES上下文”绑定到调用回调的线程?

我一直试图密切关注Grafika项目中的“来自相机的纹理”活动,尝试获取有关我如何实现此任务的线索。任何帮助都将得到赞赏。

我完整的代码粘贴如下:

public class CameraPreviewFromTextureActivity extends Activity 
{ 
private static String TAG = "CameraPreviewFromTexture"; 

private static SurfaceHolder mySurfaceHolder = null;  
private SurfaceTexture mCameraTexture = null; 
private Camera mCamera = null; 
private EglCore mEglCore; 
private WindowSurface mWindowSurface; 
private int mWindowSurfaceWidth; 
private int mWindowSurfaceHeight; 
private int mCameraPreviewWidth, mCameraPreviewHeight; 
private float mCameraPreviewFps; 
private Texture2dProgram mTexProgram; 

@Override 
protected void onCreate(Bundle savedInstanceState) 
{ 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_camera_preview_from_texture); 

    mySurfaceHolder = ((SurfaceView)this.findViewById(R.id.surfaceView)).getHolder(); 
    mySurfaceHolder.addCallback(mySurfaceHolderCallback); 

    //Run Thread Methods 
    mEglCore = new EglCore(null, 0); 
    openCamera(328, 288, 30); 
} 

private SurfaceHolder.Callback mySurfaceHolderCallback = new SurfaceHolder.Callback() 
{ 

    @Override 
    public void surfaceDestroyed(SurfaceHolder holder) 
    { 
     releaseGl(); 
    } 

    @Override 
    public void surfaceCreated(SurfaceHolder holder) 
    { 
     surfaceAvailable(holder, true);   
    } 

    @Override 
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) 
    { 
     Log.d(TAG, "SurfaceChanged " + width + "x" + height); 

     mWindowSurfaceWidth = width; 
     mWindowSurfaceHeight = height; 
     finishSurfaceSetup(); 
    } 
}; 


private void surfaceAvailable(SurfaceHolder holder, boolean newSurface) 
{ 
    Surface surface = holder.getSurface(); 
    mWindowSurface = new WindowSurface(mEglCore, surface, false); 
    mWindowSurface.makeCurrent(); 

    // Create and configure the SurfaceTexture, which will receive frames from the 
    // camera. We set the textured rect's program to render from it. 
    mTexProgram = new Texture2dProgram(Texture2dProgram.ProgramType.TEXTURE_EXT); 
    int textureId = mTexProgram.createTextureObject(); 
    mCameraTexture = new SurfaceTexture(textureId); 
    //mRect.setTexture(textureId); 

    if (!newSurface) { 
     // This Surface was established on a previous run, so no surfaceChanged() 
     // message is forthcoming. Finish the surface setup now. 
     // 
     // We could also just call this unconditionally, and perhaps do an unnecessary 
     // bit of reallocating if a surface-changed message arrives. 
     mWindowSurfaceWidth = mWindowSurface.getWidth(); 
     mWindowSurfaceHeight = mWindowSurface.getHeight(); 
     finishSurfaceSetup(); 
    } 

    mCameraTexture.setOnFrameAvailableListener(myOnFrameListner); 
} 

private SurfaceTexture.OnFrameAvailableListener myOnFrameListner = new SurfaceTexture.OnFrameAvailableListener() 
{ 
    @Override 
    public void onFrameAvailable(SurfaceTexture surfaceTexture) 
    {   
     surfaceTexture.updateTexImage(); //Problem Occurs Here 
     draw(); 
    } 
}; 

private void draw() 
{ 
    GlUtil.checkGlError("draw start"); 

    GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); 
    GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); 
    //mRect.draw(mTexProgram, mDisplayProjectionMatrix); 
    mWindowSurface.swapBuffers(); 

    GlUtil.checkGlError("draw done"); 
} 
private void finishSurfaceSetup() 
{ 
    int width = mWindowSurfaceWidth; 
    int height = mWindowSurfaceHeight; 

    // Use full window. 
    GLES20.glViewport(0, 0, width, height); 

    // Ready to go, start the camera. 
    Log.d(TAG, "starting camera preview"); 
    try { 
     mCamera.setPreviewTexture(mCameraTexture); 
    } catch (IOException ioe) { 
     throw new RuntimeException(ioe); 
    } 
    mCamera.startPreview(); 
} 

private void openCamera(int desiredWidth, int desiredHeight, int desiredFps) 
{ 
    if (mCamera != null) 
    { 
     throw new RuntimeException("camera already initialized"); 
    } 

    Camera.CameraInfo info = new Camera.CameraInfo(); 

    try 
    { 
     mCamera = Camera.open(); 
    } 
    catch(Exception ex) 
    { 
     ex.printStackTrace(); 
    } 

    Camera.Parameters parms = mCamera.getParameters(); 

    // Try to set the frame rate to a constant value. 
    int thousandFps = chooseFixedPreviewFps(parms, desiredFps * 1000); 

    // Give the camera a hint that we're recording video. This can have a big 
    // impact on frame rate. 
    parms.setRecordingHint(true); 

    mCamera.setParameters(parms); 

    int[] fpsRange = new int[2]; 
    Camera.Size mCameraPreviewSize = parms.getPreviewSize(); 
    parms.getPreviewFpsRange(fpsRange); 
    String previewFacts = mCameraPreviewSize.width + "x" + mCameraPreviewSize.height; 
    if (fpsRange[0] == fpsRange[1]) { 
     previewFacts += " @" + (fpsRange[0]/1000.0) + "fps"; 
    } else { 
     previewFacts += " @[" + (fpsRange[0]/1000.0) + 
       " - " + (fpsRange[1]/1000.0) + "] fps"; 
    } 
    Log.i(TAG, "Camera config: " + previewFacts); 

    mCameraPreviewWidth = mCameraPreviewSize.width; 
    mCameraPreviewHeight = mCameraPreviewSize.height; 
    mCameraPreviewFps = desiredFps; 
} 


public static int chooseFixedPreviewFps(Camera.Parameters parms, int desiredThousandFps) 
{ 
     List<int[]> supported = parms.getSupportedPreviewFpsRange(); 

     for (int[] entry : supported) { 
      //Log.d(TAG, "entry: " + entry[0] + " - " + entry[1]); 
      if ((entry[0] == entry[1]) && (entry[0] == desiredThousandFps)) { 
       parms.setPreviewFpsRange(entry[0], entry[1]); 
       return entry[0]; 
      } 
     } 

     int[] tmp = new int[2]; 
     parms.getPreviewFpsRange(tmp); 
     int guess; 
     if (tmp[0] == tmp[1]) { 
      guess = tmp[0]; 
     } else { 
      guess = tmp[1]/2;  // shrug 
     } 

     Log.d(TAG, "Couldn't find match for " + desiredThousandFps + ", using " + guess); 
     return guess; 
    } 


@Override 
public boolean onCreateOptionsMenu(Menu menu) { 
    // Inflate the menu; this adds items to the action bar if it is present. 
    getMenuInflater().inflate(R.menu.camera_preview_from_texture, menu); 
    return true; 
} 

@Override 
public boolean onOptionsItemSelected(MenuItem item) { 
    // Handle action bar item clicks here. The action bar will 
    // automatically handle clicks on the Home/Up button, so long 
    // as you specify a parent activity in AndroidManifest.xml. 
    int id = item.getItemId(); 
    if (id == R.id.action_settings) { 
     return true; 
    } 
    return super.onOptionsItemSelected(item); 
} 

private void releaseGl() { 
    GlUtil.checkGlError("releaseGl start"); 

    if (mWindowSurface != null) { 
     mWindowSurface.release(); 
     mWindowSurface = null; 
    } 
    if (mTexProgram != null) { 
     mTexProgram.release(); 
     mTexProgram = null; 
    } 
    GlUtil.checkGlError("releaseGl done"); 

    mEglCore.makeNothingCurrent(); 
} 

private void releaseCamera() { 
    if (mCamera != null) { 
     mCamera.stopPreview(); 
     mCamera.release(); 
     mCamera = null; 
     Log.d(TAG, "releaseCamera -- done"); 
    } 
} 

@Override 
public void onDestroy() 
{ 
    releaseCamera(); 
    releaseGl(); 
    mEglCore.release(); 
} 

} 

回答

4

每个线程都有“当前”东瀛背景下,通过线程本地存储由GLES驾驶员参考。您发出的任何GLES命令(包括纹理的绑定)都在此上下文中运行。

快速浏览您的代码表明您正在尝试在UI线程上执行所有操作。如果您创建并制作自己的上下文,它将与UI代码战斗,UI代码将拥有自己的EGL上下文以进行硬件加速的视图渲染。如果你创建一个单独的线程,使你的EGL上下文在当前,然后就放置它,生活会更简单一些。

您可以看到“纹理从相机”活动的框架可用处理器就是:

public void onFrameAvailable(SurfaceTexture surfaceTexture) { 
     mHandler.sendFrameAvailable(); 
    } 

由于框架可用的消息可以在任意线程到达,你不知道什么是EGL上​​下文将会在那里(或者如果线程将有一个)。您可以拨打eglMakeCurrent()(通过EglCore#makeCurrent())使其成为最新版本,但不允许您从多个线程使用相同的EGL上下文,因此如果它在当前某处同时存在,则可能会出现问题。因此,只要将可用框架消息转发到您知道EGL上下文是最新的线程就更容易了。

FWIW,0x0502是GL_INVALID_OPERATION​

0

同样的错误,我已经通过在函数draw()的开头添加mWindowSurface.makeCurrent()来修复它。