我使用GLSurfaceView
作为视图来显示相机预览数据。我使用createBitmapFromGLSurface
瞄准抓取像素并将其保存到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
...
答案 0 :(得分:2)
这不起作用。
要了解原因,请记住SurfaceView Surface是具有生产者 - 消费者关系的缓冲区队列。显示摄像机预览数据时,Camera是生产者,系统合成器(SurfaceFlinger)是消费者。只有生产者可以向Surface发送数据 - 一次只能有一个生产者 - 只有消费者可以从Surface检查缓冲区。
如果您自己在Surface上绘图,那么您的应用程序将成为制作人,您将能够看到您绘制的内容,但仅限于onDrawFrame()
。返回时,GLSurfaceView调用eglSwapBuffers()
,您绘制的帧将被发送给消费者。 (从技术上讲,因为缓冲区在池中并被重用,所以可以读取onDrawFrame()
之外的帧;但是你正在阅读的内容将是1-2帧之前的陈旧数据,而不是你刚刚画的那个。)
您在这里所做的是从EGLSurface读取数据,该数据从未被绘制过,并且未连接到SurfaceView。这就是它总是读黑的原因。相机不会“绘制”预览,它只需要一个YUV数据缓冲区并将其推入BufferQueue。
如果您想使用GLSurfaceView显示预览和捕捉帧,请参阅Grafika中的“show + capture camera”示例。您可以使用glReadPixels()
替换MediaCodec代码(请参阅EglSurfaceBase.saveFrame()
,其外观与您的内容非常相似)。