为什么我用 GLSurfaceView 截屏总是得到一张全黑的图片?

Why do i always get a whole black picture which screenshoot by GLSurfaceView?

我使用GLSurfaceView作为显示相机预览数据的视图。我使用 createBitmapFromGLSurface 瞄准 grabPixels 并将其保存到 Bitmap.

但是,将位图保存到文件后,我总是得到一张全黑的图片。我哪里错了?

以下是我的代码片段。

@Override
public void onDrawFrame(GL10 gl) {
    if (mIsNeedCaptureFrame) {
        mIsNeedCaptureFrame = false;
        createBitmapFromGLSurface(width, height);
    }
}

private void createBitmapFromGLSurface(int w, int h) {
    ByteBuffer buf = ByteBuffer.allocateDirect(w * h * 4);
    buf.position(0);
    buf.order(ByteOrder.LITTLE_ENDIAN);
    GLES20.glReadPixels(0, 0, w, h,
            GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, buf);
    buf.rewind();

    Bitmap bmp = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
    bmp.copyPixelsFromBuffer(buf);

    Log.i(TAG, "createBitmapFromGLSurface w:" + w + ",h:" + h);
    mFrameCapturedCallback.onFrameCaptured(bmp);
}

更新:

public void captureFrame(FrameCapturedCallback callback) {
    mIsNeedCaptureFrame = true;
    mCallback = callback;
}

public void takeScreenshot() {
    final int width = mIncomingWidth;
    final int height = mIncomingHeight;
    EglCore eglCore = new EglCore(EGL14.eglGetCurrentContext(), EglCore.FLAG_RECORDABLE);
    OffscreenSurface surface = new OffscreenSurface(eglCore, width, height);
    surface.makeCurrent();
    Bitmap bitmap = surface.captureFrame();

    for (int x = 0, y = 0; x < 100; x++, y++) {
        Log.i(TAG, "getPixel:" + bitmap.getPixel(x, y));
    }
    surface.release();
    eglCore.release();
    mCallback.onFrameCaptured(bitmap);
}

@Override
public void onDrawFrame(GL10 gl) {

    mSurfaceTexture.updateTexImage();

    if (mIsNeedCaptureFrame) {
        mIsNeedCaptureFrame = false;
        takeScreenshot();
        return;
    }
    ....
 }

日志如下:

getPixel:0
getPixel:0
getPixel:0
getPixel:0
getPixel:0
getPixel:0
getPixel:0
getPixel:0
getPixel:0
getPixel:0
getPixel:0
...

这行不通。

要理解原因,请记住 SurfaceView Surface 是具有生产者-消费者关系的缓冲区队列。在显示相机预览数据时,Camera是生产者,系统合成器(SurfaceFlinger)是消费者。只有生产者可以向 Surface 发送数据——一次只能有一个生产者——并且只有消费者可以检查来自 Surface 的缓冲区。

如果您自己在 Surface 上绘图,那么您的应用程序就是制作者,您将能够看到您所画的内容,但只能在 onDrawFrame() 中看到。当它 returns 时,GLSurfaceView 调用 eglSwapBuffers(),并且您绘制的框架被发送给消费者。 (从技术上讲,因为缓冲区在池中并被重复使用,您 可以 读取 onDrawFrame() 之外的帧;但是您正在读取的是来自 1-后退 2 帧,不是你刚画的那个。)

您在这里所做的是从从未绘制过且未连接到 SurfaceView 的 EGLSurface 读取数据。这就是为什么它总是读黑色。 Camera 不 "draw" 预览,它只是获取 YUV 数据缓冲区并将其推入 BufferQueue。

如果您想使用 GLSurfaceView 显示预览和捕获帧,请参阅 Grafika 中的 "show + capture camera" 示例。您可以用 glReadPixels() 替换 MediaCodec 代码(参见 EglSurfaceBase.saveFrame(),它看起来非常像您所拥有的)。