Android GLSurfaceView停止调用onDrawFrame

Android GLSurfaceView停止调用onDrawFrame,android,android-camera,glsurfaceview,Android,Android Camera,Glsurfaceview,我使用GLSurfaceView来显示Camera提要,因为当我在设备上使用外部摄像头(通过USB插入)时,预览是镜像的,我只能使用GLSurfaceView来“取消”它。它工作得很好,但一段时间后(可能是2分钟或10分钟),视图不再显示任何更新,只是冻结。我将日志放在public void onDrawFrame(GL10 gl)方法中,我可以看到它经常被调用,直到它冻结,然后再也没有调用。这些是我在它冻结时得到的唯一日志: 11-29 11:01:39.761 2431-2506/com.x

我使用
GLSurfaceView
来显示
Camera
提要,因为当我在设备上使用外部摄像头(通过USB插入)时,预览是镜像的,我只能使用
GLSurfaceView
来“取消”它。它工作得很好,但一段时间后(可能是2分钟或10分钟),视图不再显示任何更新,只是冻结。我将日志放在
public void onDrawFrame(GL10 gl)
方法中,我可以看到它经常被调用,直到它冻结,然后再也没有调用。这些是我在它冻结时得到的唯一日志:

11-29 11:01:39.761 2431-2506/com.xxx D/MyGLSurfaceView: #onDrawFrame
11-29 11:01:58.154 2431-2446/com.xxx I/art: Background partial concurrent mark sweep GC freed 9(288B) AllocSpace objects, 3(3MB) LOS objects, 39% free, 6MB/10MB, paused 6.019ms total 25.478ms
11-29 11:02:05.265 2431-2446/com.xxx I/art: Background partial concurrent mark sweep GC freed 6(192B) AllocSpace objects, 5(6MB) LOS objects, 39% free, 7MB/12MB, paused 5.228ms total 49.221ms
11-29 11:02:36.496 2431-2446/com.xxx I/art: Background partial concurrent mark sweep GC freed 11(336B) AllocSpace objects, 3(3MB) LOS objects, 39% free, 6MB/10MB, paused 7.752ms total 28.387ms
11-29 11:03:10.488 2431-2446/com.xxx I/art: Background partial concurrent mark sweep GC freed 242(12KB) AllocSpace objects, 3(3MB) LOS objects, 39% free, 7MB/12MB, paused 12.263ms total 41.651ms
11-29 11:03:24.016 2431-2446/com.xxx I/art: Background partial concurrent mark sweep GC freed 18(720B) AllocSpace objects, 5(6MB) LOS objects, 39% free, 6MB/10MB, paused 7.029ms total 26.553ms
11-29 11:03:58.696 2431-2446/com.xxx I/art: Background partial concurrent mark sweep GC freed 18(640B) AllocSpace objects, 3(3MB) LOS objects, 39% free, 6MB/10MB, paused 6.227ms total 27.462ms
11-29 11:04:44.049 2431-2446/com.xxx I/art: Background sticky concurrent mark sweep GC freed 234(12KB) AllocSpace objects, 3(3MB) LOS objects, 29% free, 8MB/12MB, paused 8.683ms total 17.247ms
我在某处使用了一些示例代码来使用
GLSurfaveView
,因为我不知道它是如何工作的,代码如下:

import android.content.Context;
import android.graphics.SurfaceTexture;
import android.hardware.Camera;
import android.opengl.GLES11Ext;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.util.AttributeSet;
import android.util.Log;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

class MyGLSurfaceView extends GLSurfaceView implements GLSurfaceView.Renderer, SurfaceTexture.OnFrameAvailableListener, Camera.PreviewCallback {
  private static final String TAG = MyGLSurfaceView.class.getSimpleName();

  private static final String vss = "attribute vec2 vPosition;\n" +
    "attribute vec2 vTexCoord;\n" +
    "varying vec2 texCoord;\n" +
    "void main() {\n" +
    "  texCoord = vTexCoord;\n" +
    "  gl_Position = vec4 ( vPosition.x, vPosition.y, 0.0, 1.0 );\n" +
    "}";

  private static final String fss = "#extension GL_OES_EGL_image_external : require\n" +
    "precision mediump float;\n" +
    "uniform samplerExternalOES sTexture;\n" +
    "varying vec2 texCoord;\n" +
    "void main() {\n" +
    "  gl_FragColor = texture2D(sTexture,texCoord);\n" +
    "}";

  private SurfaceTexture mSurfaceTexture;
  private boolean mUpdateTexture = false;
  private FloatBuffer mVertex;
  private FloatBuffer mTextCoordinates;
  private int[] mTexture;
  private int hProgram;

  private Camera mCamera;
  private Camera.Size cameraSize;

  public MyGLSurfaceView(Context context) {
    this(context, null);
  }

  public MyGLSurfaceView(Context context, AttributeSet attrs) {
    super(context, attrs);
    this.init();
  }

  private void init() {
    setEGLContextClientVersion(2);
    setRenderer(this);
    setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);

    float[] vtmp = {-1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f};
    this.mVertex = ByteBuffer.allocateDirect(8 * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
    this.mVertex.put(vtmp);
    this.mVertex.position(0);

    float[] ttmp = {1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f};
    this.mTextCoordinates = ByteBuffer.allocateDirect(8 * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
    this.mTextCoordinates.put(ttmp);
    this.mTextCoordinates.position(0);
  }

  @Override
  public void onResume() {
    Log.d(TAG, "#onResume");
    super.onResume();
  }

  @Override
  public void onPause() {
    Log.d(TAG, "#onPause");
    super.onPause();
    this.releaseCamera();
  }

  @Override
  public void onFrameAvailable(SurfaceTexture surfaceTexture) {
    this.mUpdateTexture = true;
    this.requestRender();
  }

  @Override
  public void onPreviewFrame(byte[] data, Camera camera) {
    // process bytes here
  }

  @Override
  public void onSurfaceCreated(GL10 gl, EGLConfig config) {
    Log.d(TAG, "#onSurfaceCreated");

    this.initTexture();
    this.mSurfaceTexture = new SurfaceTexture(mTexture[0]);
    this.mSurfaceTexture.setOnFrameAvailableListener(this);
    this.mSurfaceTexture.getTransformMatrix(new float[16]);

    try {
      this.openCamera();
    }
    catch (IOException e) {
      e.printStackTrace();
      this.releaseCamera();
    }

    GLES20.glClearColor(1.0f, 1.0f, 0.0f, 1.0f);
  }

  @Override
  public void onSurfaceChanged(GL10 gl, int width, int height) {
    Log.d(TAG, "#onSurfaceChanged");

    GLES20.glViewport(0, 0, width, height);

    this.mCamera.startPreview();
    this.mCamera.setPreviewCallback(this);

    this.hProgram = loadShader();
  }

  @Override
  public void onDrawFrame(GL10 gl) {
    Log.d(TAG, "#onDrawFrame");

    GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);

    synchronized (this) {
      if (this.mUpdateTexture) {
        this.mSurfaceTexture.updateTexImage();
        this.mUpdateTexture = false;
      }
    }

    GLES20.glUseProgram(this.hProgram);

    GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
    GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, this.mTexture[0]);

    int th = GLES20.glGetUniformLocation(this.hProgram, "sTexture");
    GLES20.glUniform1i(th, 0);

    int ph = GLES20.glGetAttribLocation(this.hProgram, "vPosition");
    GLES20.glVertexAttribPointer(ph, 2, GLES20.GL_FLOAT, false, 4 * 2, this.mVertex);
    GLES20.glEnableVertexAttribArray(ph);

    int tch = GLES20.glGetAttribLocation(this.hProgram, "vTexCoord");
    GLES20.glVertexAttribPointer(tch, 2, GLES20.GL_FLOAT, false, 4 * 2, this.mTextCoordinates);
    GLES20.glEnableVertexAttribArray(tch);

    GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);

    GLES20.glFlush();
  }

  private void initTexture() {
    this.mTexture = new int[1];
    GLES20.glGenTextures(1, this.mTexture, 0);
    GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, this.mTexture[0]);
    GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
    GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
    GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
    GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
  }

  private static int loadShader() {
    int vshader = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER);
    GLES20.glShaderSource(vshader, vss);
    GLES20.glCompileShader(vshader);
    int[] compiled = new int[1];
    GLES20.glGetShaderiv(vshader, GLES20.GL_COMPILE_STATUS, compiled, 0);
    if (compiled[0] == 0) {
      Log.e("Shader", "Could not compile vshader");
      Log.v("Shader", "Could not compile vshader:" + GLES20.glGetShaderInfoLog(vshader));
      GLES20.glDeleteShader(vshader);
      vshader = 0;
    }

    int fshader = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER);
    GLES20.glShaderSource(fshader, fss);
    GLES20.glCompileShader(fshader);
    GLES20.glGetShaderiv(fshader, GLES20.GL_COMPILE_STATUS, compiled, 0);
    if (compiled[0] == 0) {
      Log.e("Shader", "Could not compile fshader");
      Log.v("Shader", "Could not compile fshader:" + GLES20.glGetShaderInfoLog(fshader));
      GLES20.glDeleteShader(fshader);
      fshader = 0;
    }

    int program = GLES20.glCreateProgram();
    GLES20.glAttachShader(program, vshader);
    GLES20.glAttachShader(program, fshader);
    GLES20.glLinkProgram(program);

    return program;
  }

  private void openCamera() throws IOException {
    this.mCamera = Camera.open();

    final Camera.Parameters parameters = this.mCamera.getParameters();
    parameters.setPreviewSize(1280, 720);
    this.mCamera.setParameters(parameters);

    this.mCamera.setPreviewTexture(this.mSurfaceTexture);
  }

  private void releaseCamera() {
    synchronized (this) {
      try {
        if (this.mCamera != null) {
          this.mCamera.stopPreview();
          this.mCamera.setPreviewCallback(null);
          this.mCamera.release();
          this.mCamera = null;
        }
      }
      catch (Exception e) {
        Log.e(TAG, e.getMessage());
      }
    }
  }
}

因此,看起来核心问题是图像处理(从
onPreviewFrame()
callback启动)在主线程上造成了拥塞

在主线程上运行摄像头是一种糟糕的做法,可能会在不同的设备上造成许多奇怪的效果。新的camera2API正确地处理了这个问题,但是如果出于任何原因您必须使用不推荐使用的Camera API,请将
Camera.open()
和所有回调移到一个新的位置


另一个优化:切换到以减少摄像头缓冲区分配和垃圾收集成本。

不,垃圾收集器看起来并没有占用您所有的CPU。没有理由怀疑OpenGL。可能是视频卡在了管道的某个地方。USB视频在Android上不是很可靠,甚至一些硬件部件可能会过热而出现故障。您可以尝试关闭显示并只记录即将到来的帧:这会持续更长时间吗?一点精度:在
公共void onPreviewFrame(byte[]数据,摄像头)
中,如果我放置日志,它仍然会被调用。因此,预览版似乎仍在运行。也许你根本不需要预览回调。它毫无用处地浪费电子。另一个注意事项是,在UI线程上打开camera不是一个好的做法。为了让所有的摄像头回调都远离UI线程,你需要一个单独的循环器,请看,虽然我确实需要使用previewCallback,因为我正在处理帧,但是按照你的建议使用另一个线程会使事情更快,而且到目前为止还没有看到一个冻结。。。