无法使用 ImageWriter 将图像传递到 RecordingSession Surface

问题描述 投票:0回答:1

我正在尝试创建一个使用 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 帧)

我非常感谢这里的任何指示/帮助!

java android image mediarecorder
1个回答
0
投票

当直接将相机连接到视频编码器时,缓冲区会分配有标志,指示它们将被编码器消耗。相机 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 中进行复制。

© www.soinside.com 2019 - 2024. All rights reserved.