我正在尝试创建一个使用 Camera2 来检测 QR 码并录制视频的应用程序。
我有很多输出,并且由于我无法一次将所有内容连接到相机,因此我需要在一个相机输出中进行帧处理(QR 码或其他)和媒体记录。 我决定选择
ImageReader
,在那里我收到我可以处理的 Image
,然后将它们传递到另一个输出表面,在本例中是 MediaRecorder
的表面,使用 ImageWriter
。所以:
ImageReader
-> 对 Image
进行一些机器学习处理 -> ImageWriter
-> MediaRecorder
我当前的设置如下所示:
class VideoPipeline(width: Int,
height: Int,
format: Int): ImageReader.OnImageAvailableListener {
private var passThroughImageWriter: ImageWriter? = null
init {
imageReader = ImageReader.newInstance(width, height, format, 5)
imageReader.setOnImageAvailableListener(this, videoThreadHandler)
setupCamera(imageReader.surface, previewSurface, photoOutputSurface)
}
fun startRecording() {
val recorderSurface = MediaCodec.createPersistentInputSurface()
val recorder = MediaRecorder(...)
recorder.setVideoSource(MediaRecorder.VideoSource.SURFACE)
// a bunch of other setup code for media recorder
recorder.setInputSurface(surface)
recorder.prepare()
recorder.start()
passThroughImageWriter = ImageWriter.newInstance(recorderSurface, 5)
}
override fun onImageAvailable(reader: ImageReader) {
val image = reader.acquireLatestImage()
if (passThroughImageWriter != null) {
// Moves the Image to the ImageWriter, no need to close
passThroughImageWriter?.queueInputImage(image)
} else {
// Nothing else happening here, close image.
image.close()
}
}
}
但是每次都会失败,无论我使用什么
format
(尝试过ImageFormat.YUV_420_888
,ImageFormat.PRIVATE
和PixelFormat.RGB888
):
VideoPipeline D --> writing 1280 x 960 34 frame..
GraphicBufferSource D got buffer with new dataSpace #104
GraphicBufferSource W released unpopulated slots: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63]
VideoPipeline D --> writing 1280 x 960 34 frame..
GraphicBufferSource W released unpopulated slots: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63]
RecordingSession E MediaRecorder Error: 268435556 (-1007)
RecordingSession I Stopping RecordingSession..
VideoPipeline D --> writing 1280 x 960 34 frame..
GraphicBufferSource W released unpopulated slots: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63]
MediaRecorder E stop failed: -1007
AndroidRuntime D Shutting down VM
AndroidRuntime E FATAL EXCEPTION: main
Process: com.mrousavy.camera.example, PID: 6406
java.lang.RuntimeException: stop failed.
at android.media.MediaRecorder.stop(Native Method)
at com.mrousavy.camera.core.RecordingSession.stop(RecordingSession.kt:103)
at com.mrousavy.camera.core.RecordingSession._init_$lambda$0(RecordingSession.kt:75)
at com.mrousavy.camera.core.RecordingSession.$r8$lambda$enBPf4dA0r-MTevnd6ChW75ZCDg(Unknown Source:0)
at com.mrousavy.camera.core.RecordingSession$$ExternalSyntheticLambda0.onError(Unknown Source:2)
at android.media.MediaRecorder$EventHandler.handleMessage(MediaRecorder.java:1615)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7872)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)
有什么想法吗?
完整代码:
JFYI:在使用
ImageWriter
之前,我有一个 OpenGL 管道,它使用 OpenGL 表面流式传输到 MediaRecorder
的表面,工作得非常好。所以我认为 MediaRecorder
设置正确,在我看来,它必须与 ImageWriter
相关。
这是我从 OpenGL 管道切换到
ImageWriter
的差异:4e96eb7
(原因是 OpenGL 始终是 RGB,而我想要 PRIVATE/YUV 帧)
我非常感谢这里的任何指示/帮助!
当直接将相机连接到视频编码器时,缓冲区会分配有标志,指示它们将被编码器消耗。相机 HAL 和图形缓冲区分配器使用此标志来为缓冲区选择适当的格式/布局/等。
当您像这样使用 ImageReader 时,只会设置“CPU 使用”标志;当您尝试将缓冲区发送到编码器时,尝试从硬件编码器访问缓冲区时可能会出错。
您可以尝试使用 ImageReader 构造函数的这种变体: https://developer.android.com/reference/android/media/ImageReader?hl=en#newInstance(int,%20int,%20int,%20int,%20long)
并传入
USAGE_VIDEO_ENCODE
和 USAGE_CPU_READ_OFTEN
标志。请注意,不幸的是,这并不是一个保证有效的组合。也就是说,我希望它可以使用 YUV_420_888 作为格式。 (私有格式意味着您无法在应用程序代码中读取它,因为您不知道布局)。
最兼容的选项是有两个输出流,一个输出到编码器,另一个输出到用于机器学习的 ImageReader。或者像以前一样在 GPU 中进行复制。