如何使用ImageReader读取OpenGL表面?

How to use ImageReader to read OpenGL surface?

我正在尝试使用 OpenGL ES 1.0 绘制一些东西,屏幕渲染效果很好。然后我想使用 ImageReader 从表面获取图像数据,但是它的回调 ImageReader.OnImageAvailableListener 在渲染时根本没有被调用,我的代码有什么问题?

public class ImageReaderActivity extends ActionBarActivity implements SurfaceHolder.Callback, Runnable {

private SurfaceView mSurfaceView;
private SurfaceHolder mSurfaceHolder;
private Surface mSurface;
private int mWidth, mHeight;
private boolean mRunning = false;

private ImageReader mImageReader;

private OpenGLDrawer mOpenGLDrawer;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_imagereader);
    mSurfaceView = findViewById(R.id.surface_view);
    mSurfaceHolder = mSurfaceView.getHolder();
    mSurfaceHolder.addCallback(this);
}

@Override
public void surfaceCreated(SurfaceHolder holder) {

}

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    mWidth = width;
    mHeight = height;
    mImageReader = ImageReader.newInstance(width, height, PixelFormat.RGBA_8888, 1);
    mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, null);
    mSurface = mImageReader.getSurface();
    new Thread(this).start();
}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
    mRunning = false;
}

@Override
public void run() {
    EGL10 egl = (EGL10) EGLContext.getEGL();
    EGLDisplay dpy = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
    int[] version = new int[2];
    egl.eglInitialize(dpy, version);

    int[] configSpec = {
            EGL10.EGL_RED_SIZE,      5,
            EGL10.EGL_GREEN_SIZE,    6,
            EGL10.EGL_BLUE_SIZE,     5,
            EGL10.EGL_DEPTH_SIZE,   16,
            EGL10.EGL_NONE
    };

    EGLConfig[] configs = new EGLConfig[1];
    int[] num_config = new int[1];

    egl.eglChooseConfig(dpy, configSpec, configs, 1, num_config);
    EGLConfig config = configs[0];

    EGLContext context = egl.eglCreateContext(dpy, config,
            EGL10.EGL_NO_CONTEXT, null);

    EGLSurface drawSurface = egl.eglCreateWindowSurface(dpy, config, mSurfaceHolder, null);
    EGLSurface readSurface = egl.eglCreateWindowSurface(dpy, config, mSurface, null);

    egl.eglMakeCurrent(dpy, drawSurface, readSurface, context);

    GL10 gl = (GL10)context.getGL();

    mOpenGLDrawer = new OpenGLDrawer();
    mOpenGLDrawer.setSize(gl, mWidth, mHeight);

    mRunning = true;
    while (mRunning) {
        SystemClock.sleep(333);
        synchronized (mSurfaceHolder) {
            render(gl);

            egl.eglSwapBuffers(dpy, drawSurface);
        }
    }

    egl.eglMakeCurrent(dpy, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
    egl.eglDestroySurface(dpy, drawSurface);
    egl.eglDestroySurface(dpy, readSurface);
    egl.eglDestroyContext(dpy, context);
    egl.eglTerminate(dpy);
}

private void render(GL10 gl) {
    gl.glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
    gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
    mOpenGLDrawer.draw(gl);
}

private ImageReader.OnImageAvailableListener mOnImageAvailableListener = new ImageReader.OnImageAvailableListener() {
    @Override
    public void onImageAvailable(ImageReader reader) {
        Image image = reader.acquireLatestImage();
        if (image != null) {
            image.close();
        }
    }
};

}

经过长时间的调试,这里是最终的工作代码:

public class ImageReaderActivity extends ActionBarActivity 
        implements SurfaceHolder.Callback, Runnable {
    private static final int MSG_SHOW_SCREENSHOT = 0;
    private static final int MSG_HIDE_SCREENSHOT = 1;

    private SurfaceView mSurfaceView;
    private SurfaceHolder mSurfaceHolder;
    private Surface mSurface;
    private int mWidth, mHeight;
    private boolean mRunning = false;

    private ImageReader mImageReader;

    private OpenGLDrawer mOpenGLDrawer;

    private EGLSurface mDrawSurface;
    private EGLSurface mImageReaderSurface;
    private EGLDisplay mEGLDisplay;
    private EGLContext mEGLContext;

    private FrameLayout mFrameView;
    private ImageView mScreenshotView;

    private volatile boolean mIsCapturing = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_imagereader);
        mSurfaceView = findViewById(R.id.surface_view);
        mSurfaceHolder = mSurfaceView.getHolder();
        mSurfaceHolder.addCallback(this);
        mFrameView = findViewById(R.id.frame);
        mScreenshotView = findViewById(R.id.screenshot);
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {

    }

    @SuppressLint("WrongConstant")
    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        mWidth = width;
        mHeight = height;
        mImageReader = ImageReader.newInstance(width, height, PixelFormat.RGBA_8888,1);
        mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, null);
        mSurface = mImageReader.getSurface();
        new Thread(this).start();
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        mRunning = false;
    }

    @Override
    public void run() {
        // 创建一个EGL实例
        EGL10 egl = (EGL10) EGLContext.getEGL();
        // 传教一个EGLDisplay实例
        mEGLDisplay = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
        // 初始化EGLDisplay实例
        int[] version = new int[2];
        egl.eglInitialize(mEGLDisplay, version);

        int[] configSpec = {
                EGL14.EGL_RED_SIZE, 8,
                EGL14.EGL_GREEN_SIZE, 8,
                EGL14.EGL_BLUE_SIZE, 8,
                EGL14.EGL_ALPHA_SIZE, 8,
                EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
                EGL14.EGL_SURFACE_TYPE, EGL14.EGL_WINDOW_BIT,
                EGL_RECORDABLE_ANDROID, 1,
                EGL14.EGL_NONE
        };

        EGLConfig[] configs = new EGLConfig[1];
        int[] num_config = new int[1];
        // 选择config创建OpenGL运行环境
        egl.eglChooseConfig(mEGLDisplay, configSpec, configs, 1, num_config);
        EGLConfig config = configs[0];

        int ctxAttr[] = {
                EGL14.EGL_CONTEXT_CLIENT_VERSION, 1,// 0x3098
                EGL14.EGL_NONE
        };

        mEGLContext = egl.eglCreateContext(mEGLDisplay, config,
                EGL10.EGL_NO_CONTEXT, ctxAttr);
        // 创建新的surface
        mDrawSurface = egl.eglCreateWindowSurface(mEGLDisplay, config, mSurfaceHolder, null);
        mImageReaderSurface = egl.eglCreateWindowSurface(mEGLDisplay, config, mSurface, null);

        // 将OpenGL环境设置为当前
        egl.eglMakeCurrent(mEGLDisplay, mDrawSurface, mDrawSurface, mEGLContext);
        // 获取当前OpenGL画布
        GL10 gl = (GL10)mEGLContext.getGL();

        mOpenGLDrawer = new OpenGLDrawer();
        mOpenGLDrawer.setSize(gl, mWidth, mHeight);

        mRunning = true;
        while (mRunning) {
            synchronized (mSurfaceHolder) {
                render(gl);

                // 显示绘制结果到屏幕上
                egl.eglSwapBuffers(mEGLDisplay, mDrawSurface);

                egl.eglMakeCurrent(mEGLDisplay, mImageReaderSurface, mImageReaderSurface, mEGLContext);

                render(gl);

                egl.eglSwapBuffers(mEGLDisplay, mImageReaderSurface);

                egl.eglMakeCurrent(mEGLDisplay, mDrawSurface, mDrawSurface, mEGLContext);
            }
            SystemClock.sleep(333);
        }

        egl.eglMakeCurrent(mEGLDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
        egl.eglDestroySurface(mEGLDisplay, mDrawSurface);
        egl.eglDestroyContext(mEGLDisplay, mEGLContext);
        egl.eglTerminate(mEGLDisplay);
    }

    public void onTakeScreenshotClick(View view) {
        synchronized (this) {
            mIsCapturing = true;
        }
    }

    private void render(GL10 gl) {
        gl.glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
        gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
        mOpenGLDrawer.draw(gl);
    }

    private ImageReader.OnImageAvailableListener mOnImageAvailableListener = new ImageReader.OnImageAvailableListener() {
        @Override
        public void onImageAvailable(ImageReader reader) {
            Log.d("reader", "onImageAvailable(ImageReader reader)");

            Image image = reader.acquireNextImage();
            if (image != null) {
                Log.d("reader", "acquireLatestImage()");

                synchronized (this) {
                    if (mIsCapturing) {
                        mIsCapturing = false;

                        int width = image.getWidth();
                        int height = image.getHeight();
                        final Image.Plane[] planes = image.getPlanes();
                        final ByteBuffer buffer = planes[0].getBuffer();
                        int pixelStride = planes[0].getPixelStride();
                        int rowStride = planes[0].getRowStride();
                        int rowPadding = rowStride - pixelStride * width;
                        Bitmap bitmap = Bitmap.createBitmap(width + rowPadding / pixelStride, height, Bitmap.Config.ARGB_8888);
                        bitmap.copyPixelsFromBuffer(buffer);
                        final Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
                        Canvas canvas = new Canvas(bmp);
                        final Rect src = new Rect(0,0, bitmap.getWidth(), bitmap.getHeight());
                        final RectF dst = new RectF(0,0, width, height);
                        canvas.drawBitmap(bitmap, src, dst, null);

                        mScreenshotView.post(() -> mScreenshotView.setImageBitmap(bmp));
                        mHandler.sendEmptyMessage(MSG_SHOW_SCREENSHOT);
                    }
                }

                image.close();
            }
        }
    };

    private Handler mHandler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(@NonNull Message msg) {
            switch (msg.what) {
                case MSG_SHOW_SCREENSHOT:
                    mFrameView.setVisibility(View.VISIBLE);
                    mHandler.sendEmptyMessageDelayed(MSG_HIDE_SCREENSHOT, 3000);
                    break;

                case MSG_HIDE_SCREENSHOT:
                    mFrameView.setVisibility(View.GONE);
                    break;

                default:
                    break;
            }

            return false;
        }
    });
}