我正在通过视频通话的应用程序中使用QuickBlox,该视频通话需要与智能眼镜设备Moverio BT-300配合使用。我需要能够将一个帧作为位图并将其保存在设备中。经过一番研究,我发现有多种方法可以做到这一点,但是每种方法都有不同的问题:
停止允许访问摄像头的视频流,以便可以按意图调用摄像头应用程序似乎适用于大多数设备。 Moverio在这里显示问题。摄像头会打开,并且似乎可以正常工作,但是当返回图像时,不会调用onActivityResult方法,并且在调用过程中任何后续尝试访问摄像头应用程序的尝试都会导致摄像头错误。此方法似乎可以在Moverio的调用之外正常工作。
QuickBlox允许使用frameListeners,但是即使将其正确添加到SurfaceViewRenderer之后,也从未在视频通话期间调用过onFrame(bitmap:Bitmap)方法。
QuickBlox使用WebRTC中的帧定义(I420Frame),并带有允许构建YuvImage的信息。该框架包含一个标志(yuvFrame),该标志指示YUV信息是否可用于此框架。对于大多数设备而言,此信息就足够了,但对于Moverio的相机,此标志为false,YUV信息为空。
帧定义还包含opengl-es纹理名称,如果YUV信息不可用,则视频调用使用该名称,因此应该可以在缓冲区中渲染该纹理并从此处获取位图。这是最有前途的方法,因为视频通话可与Moverio正常工作,这意味着纹理信息可用于帧。但是我在这里面临的问题是在视频通话渲染的同时使用openGL。我能够渲染纹理并访问位图,但是只要调用尝试在屏幕上渲染下一帧,应用程序就会崩溃。
我当前尝试实现的方法是4。这是用于创建位图的代码:
val drawer = GlRectDrawer()
val texMatrix = RendererCommon.rotateTextureMatrix(frame.samplingMatrix, frame.rotationDegree.toFloat())
val bitmapMatrix = RendererCommon.multiplyMatrices(texMatrix, RendererCommon.verticalFlipMatrix())
val buffers = IntArray(1)
GLES20.glGenFramebuffers(1, buffers, 0)
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, buffers[0])
GLES20.glFramebufferTexture2D(
GLES20.GL_FRAMEBUFFER,
GLES20.GL_COLOR_ATTACHMENT0,
GLES20.GL_TEXTURE_2D,
buffers[0], 0)
drawer.drawOes(textureId, bitmapMatrix, rotatedWidth(), rotatedHeight(), 0, 0, rotatedWidth(), rotatedHeight())
val bitmapBuffer = ByteBuffer.allocateDirect(rotatedWidth() * rotatedHeight() * 4)
GLES20.glReadPixels(0, 0, rotatedWidth(), rotatedHeight(), GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, bitmapBuffer)
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0)
GLES20.glDeleteFramebuffers(1, buffers, 0)
val bitmap = Bitmap.createBitmap(rotatedWidth(), rotatedHeight(), Bitmap.Config.ARGB_8888)
bitmap.copyPixelsFromBuffer(bitmapBuffer)
这是例外日志:
08-17 17:25:26.740 5288-5486/? W/System.err: java.lang.RuntimeException: java.lang.RuntimeException: YuvConverter.convert: GLES20 error: 1282
at org.webrtc.GlUtil.checkNoGLES2Error(GlUtil.java:29)
at org.webrtc.YuvConverter.convert(YuvConverter.java:239)
at org.webrtc.SurfaceTextureHelper$7.run(SurfaceTextureHelper.java:234)
at org.webrtc.ThreadUtils$5.call(ThreadUtils.java:208)
at org.webrtc.ThreadUtils$5.call(ThreadUtils.java:205)
at org.webrtc.ThreadUtils$4.run(ThreadUtils.java:182)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
这是发生异常的代码(来自WebRTC包):
GLES20.glBindFramebuffer(36160, this.frameBufferId);
GlUtil.checkNoGLES2Error("glBindFramebuffer");
if (this.frameBufferWidth != stride / 4 || this.frameBufferHeight != total_height) {
this.frameBufferWidth = stride / 4;
this.frameBufferHeight = total_height;
GLES20.glActiveTexture(33984);
GLES20.glBindTexture(3553, this.frameTextureId);
GLES20.glTexImage2D(3553, 0, 6408, this.frameBufferWidth, this.frameBufferHeight, 0, 6408, 5121, (Buffer)null);
int status = GLES20.glCheckFramebufferStatus(36160);
if (status != 36053) {
throw new IllegalStateException("Framebuffer not complete, status: " + status);
}
}
GLES20.glActiveTexture(33984);
GLES20.glBindTexture(36197, srcTextureId);
...
GLES20.glReadPixels(0, 0, this.frameBufferWidth, this.frameBufferHeight, 6408, 5121, buf);
GlUtil.checkNoGLES2Error("YuvConverter.convert");
GLES20.glBindFramebuffer(36160, 0);
GLES20.glBindTexture(3553, 0);
GLES20.glBindTexture(36197, 0);
取决于我渲染帧的确切时间,有时错误会发生在glBindFramebuffer(36160,this.frameBufferId)或GLES20.glReadPixels上。几次,该时间允许在下一帧被调用并导致应用崩溃之前的一秒钟内将图像显示在屏幕上(在用于确认要保存图像的对话框中)。
如OpenGL reference for glBindFramebuffer中所述,错误代码表示所提供的值不是生成的帧缓冲区的名称,而是正确生成了帧缓冲区,并且该代码(来自WebRTC包)在任何其他时间都可以正常工作。
我是否忘记了导致此问题的代码?