使用ImageWriter无法将图像传入RecordingSession Surface的问题排查
我正在开发一款基于Camera2的应用,需要同时实现二维码检测和录像功能。由于Camera2无法同时挂载多个输出,我采用ImageReader接收相机帧→ML处理(二维码检测)→ImageWriter传递到MediaRecorder的Surface的流程,把帧处理和录像整合到单个相机输出。
当前实现代码
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) // 省略MediaRecorder其他配置代码 recorder.setInputSurface(surface) recorder.prepare() recorder.start() passThroughImageWriter = ImageWriter.newInstance(recorderSurface, 5) } override fun onImageAvailable(reader: ImageReader) { val image = reader.acquireLatestImage() if (passThroughImageWriter != null) { // 将Image传递给ImageWriter,无需手动关闭 passThroughImageWriter?.queueInputImage(image) } else { // 无录制任务时关闭图像 image.close() } } }
报错信息
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)
已尝试ImageFormat.YUV_420_888、ImageFormat.PRIVATE、PixelFormat.RGB888三种格式,均失败。之前用OpenGL管线将画面流传输到MediaRecorder的Surface完全正常,因此MediaRecorder配置没问题,问题出在ImageWriter的使用逻辑上。
排查思路与解决方案
1. 严格匹配格式与数据空间
MediaRecorder的Surface仅支持特定格式,尤其是使用VideoSource.SURFACE时,必须与相机输出的格式、dataSpace完全对齐。可通过Image.getFormat()和Image.getPlanes()[0].getBuffer()验证传递给ImageWriter的帧属性是否符合要求,避免跨格式直接传递。
2. 调整ImageWriter初始化时机
当前先启动MediaRecorder再初始化ImageWriter,会导致MediaRecorder无法提前感知输入Surface的格式信息。应先初始化ImageWriter,再调用MediaRecorder的prepare()和start():
fun startRecording() { val recorderSurface = MediaCodec.createPersistentInputSurface() // 先初始化ImageWriter passThroughImageWriter = ImageWriter.newInstance(recorderSurface, 5) val recorder = MediaRecorder(...) recorder.setVideoSource(MediaRecorder.VideoSource.SURFACE) // 省略其他配置 recorder.setInputSurface(recorderSurface) recorder.prepare() recorder.start() }
3. 确保时间戳连续有效
Camera2输出的帧带有时间戳,直接传递时需保证时间戳递增且符合MediaRecorder要求。可通过Image.getTimestamp()获取时间戳,在queueInputImage前调用ImageWriter.setTimestamp()(API 26+),避免因时间戳异常触发报错。
4. 统一缓冲区队列配置
报错中的released unpopulated slots提示缓冲区队列异常,建议将ImageReader和ImageWriter的缓冲区数量设为相同值(比如都设为3),同时确保相机输出帧速率与MediaRecorder配置一致,避免队列溢出或空转。
5. 避免直接传递PRIVATE格式帧
ImageFormat.PRIVATE是厂商自定义格式,可能无法直接通过ImageWriter传递给MediaRecorder。建议将PRIVATE格式帧转换为YUV_420_888格式后再传递,或直接让Camera2输出YUV_420_888格式的帧。
内容的提问来源于stack exchange,提问作者mrousavy

