我正在尝试使用SurfaceTexture从我的设备的后置摄像头显示摄像头预览,但我一直收到错误
bindTextureImage : error binding external texture image 0x2:0x502
看着说的那条线
SurfaceTexture.UpdateTexImage()
看看androID文档,似乎这可能是由于在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;@OverrIDeprotected 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; }@OverrIDepublic 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;}@OverrIDepublic 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"); } }@OverrIDepublic voID onDestroy(){ releaseCamera(); releaseGl(); mEglCore.release();}}
解决方法:
每个线程都有一个“当前”EGL上下文,由GLES驱动程序通过线程本地存储引用.您发出的任何GLES命令(包括纹理绑定)都在此上下文中运行.
快速查看代码表明您正在尝试在UI线程上执行所有 *** 作.如果您创建并创建当前自己的上下文,它将与UI代码作斗争,UI代码将具有自己的EGL上下文,用于硬件加速的VIEw渲染.如果你创建一个单独的线程,让你的EGL上下文在那里流动,然后不管它,生活会有点简单.
你可以看到“相机纹理”活动的框架可用处理程序只是:
public voID onFrameAvailable(SurfaceTexture surfaceTexture) { mHandler.sendFrameAvailable(); }
因为帧可用消息可以到达任意线程,所以你不知道那里的EGL上下文是什么(或者线程是否会有一个).你可以调用eglMakeCurrent()(通过EglCore#makeCurrent())使你的当前,但你不允许从多个线程使用相同的EGL上下文,所以如果它在同一时间的其他地方你可能有一个问题.因此,将帧可用消息转发到您知道EGL上下文是最新的线程更容易.
FWIW,0x0502是GL_INVALID_OPERATION.
总结以上是内存溢出为你收集整理的android – 使用表面纹理的相机预览全部内容,希望文章能够帮你解决android – 使用表面纹理的相机预览所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)