为什么我用 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()
,它看起来非常像您所拥有的)。
我使用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()
,它看起来非常像您所拥有的)。