【问题标题】:Why do i always get a whole black picture which screenshoot by GLSurfaceView?为什么我总是得到一张由 GLSurfaceView 截屏的全黑图片?
【发布时间】:2015-08-13 16:50:48
【问题描述】:

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

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

以下是我的代码 sn-p。

@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 进行屏幕截图”的问题,以为这是使用 View 渲染缓存抓取屏幕的常用尝试。但这不是你正在做的事情,它失败的原因完全不同。我给 +1 是因为我提出了一些新的问题。 :-)

标签: android bitmap android-bitmap glsurfaceview egl


【解决方案1】:

这行不通。

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

如果您自己在 Surface 上绘图,那么您的应用将成为生产者,您将能够看到您所绘制的内容,但仅限于在onDrawFrame() 中。当它返回时,GLSurfaceView 会调用eglSwapBuffers(),并将您绘制的帧发送给消费者。 (从技术上讲,由于缓冲区位于池中并被重复使用,您 可以 读取 onDrawFrame() 之外的帧;但您正在读取的将是 1-2 帧之前的陈旧数据,而不是你刚刚画的那个。)

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

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

【讨论】:

  • 顺便说一句,MediaCodec代码和Frame capture code是否可能同时存在?在Grafika中,我无法捕获帧,因为编码器将在stopRecording后释放。我想在相机活动期间一直捕捉帧。
  • “show + capture camera”示例是从带有 SurfaceTexture 的 Camera 接收帧。然后它将 SurfaceTexture 呈现给 GLSurfaceView,如果处于活动状态,则呈现给视频编码器。您可以将该帧发送到任意数量的目的地。对于简单的帧抓取,您可以使用屏幕外 pbuffer - 渲染到它,然后从它glReadPixels()。 (参见 gles/OffscreenSurface.java)
  • 如果我使用OffscreenSurface 抓取框架并保存到文件,我应该强调什么?我按照以下步骤进行操作:1.构造EglCore:eglCore = new EglCore(null, 0); 2.构造OffscreenSurface:surface = new OffscreenSurface(eglCore, mWidth, mHeight); 3.保存帧:surface.saveFrame()。但我得到了一张全白的照片。看来我错过了什么。
  • surface.makeCurrent()surface.saveFrame()之间,你需要在表面上画一些东西。如果您正在关注“显示 + 捕获相机”,那将是 mSurfaceTexture.getTransformMatrix(mSTMatrix); mFullScreen.drawFrame(mTextureId, mSTMatrix);。这假设在进入时,EGL 上下文是来自 SurfaceTexture 的上下文。保存完帧后,您可能还想切换回 EGL 上下文。
  • 在另一面致电makeCurrent()。如果您要切换回 GLSurfaceView 的表面,您可以按照 Android Breakout 补丁 (bigflake.com/mediacodec/0001-Record-game-into-.mp4.patch) 的方式保存/恢复它 - 请参阅 saveRenderState()restoreRenderState()
猜你喜欢
  • 1970-01-01
  • 2014-03-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-10
  • 1970-01-01
  • 2013-04-03
  • 1970-01-01
相关资源
最近更新 更多