在 Kotlin 多平台中使用 MLKit 条形码扫描时 iOS 相机冻结并崩溃

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

我正在开发一个使用 Kotlin Multiplatform 和 Google MLKit 进行条形码扫描的应用程序。我无法在 AVCaptureVideoDataOutputSampleBufferDelegate 的 captureOutput 方法中创建 VisionImage。尝试时,我遇到每 15 帧周期性冻结 10 秒的情况。

在我的 CameraViewController (iosMain) 中

class CameraViewController(
    private val onQRCodeDetected: (String) -> Unit
) : UIViewController(null, null), AVCaptureVideoDataOutputSampleBufferDelegateProtocol {

我覆盖 captureOutput

    override fun captureOutput(
        output: AVCaptureOutput,
        didOutputSampleBuffer: CMSampleBufferRef?,
        fromConnection: AVCaptureConnection
    ) {        
        frameCounter+=1
        logging("CViewC").i { "FRAME #$frameCounter" }

到目前为止一切都很完美。 captureOutput 会及时调用每一帧并记录帧计数。当我添加以下行时,事情就会中断。

val visionImage = MLKVisionImage(didOutputSampleBuffer)

现在 captureOutput 的前 15 次调用都可以了。在此期间,手机上的预览工作流畅。然后预览冻结并且不会调用 captureOutput。这持续 10 秒。 10 秒后,我再次正常收到 15 帧。这个循环持续下去,应用程序永远不会崩溃。

当我现在添加该行时

CFRelease(didOutputSampleBuffer)

我获得了 288 帧的连续流,直到崩溃。崩溃报告表明我尝试释放已释放的缓冲区(检测到 CFTypeRef %p (%lu / %s) 的过度释放)。

我尝试复制缓冲区:

val copiedBufferVar = nativeHeap.alloc<CMSampleBufferRefVar>()
CMSampleBufferCreateCopy(
    allocator = kCFAllocatorDefault,
    sbuf = didOutputSampleBuffer,
    sampleBufferOut = copiedBufferVar.ptr
)
val visionImage = MLKVisionImage(didOutputSampleBuffer.value)
CFRelease(copiedBufferVar.value)

因为我假设由于 VisionImage 对缓冲区的强引用,缓冲区没有从内存中释放。 (我不知道这是否有道理 - 我拼命地试图理解某些事情)。这导致了同样的事情。 15 帧 OK 和 10 秒冻结的循环。

我尝试通过将 VisionImage 设为可为空并在使用后将其设置为 null 来从内存中释放它。这也没有改变什么。 LLM 建议使用自动释放池,但这也没有帮助。

所以我猜这是一个记忆问题。我以为我的 AVCaptureSession 会自动处理这些事情。我是这样配置的。我删除了一些空检查以使其更短。

@OptIn(ExperimentalForeignApi::class)
private fun setupCaptureSession() {

    captureSession = AVCaptureSession().apply {
        beginConfiguration()
        sessionPreset = AVCaptureSessionPreset1280x720
    }

    val device = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo)
    val input = AVCaptureDeviceInput.deviceInputWithDevice(device, null)
    if (captureSession?.canAddInput(input) == true) {
        captureSession?.addInput(input)
    }
    videoOutput = AVCaptureVideoDataOutput().apply {
        setSampleBufferDelegate(this@CameraViewController, processingQueue)
        videoSettings = mapOf(
            kCVPixelBufferPixelFormatTypeKey to NSNumber(kCVPixelFormatType_32BGRA) 
        )
        alwaysDiscardsLateVideoFrames = true

    }
    if (captureSession?.canAddOutput(videoOutput!!) == true) {
        captureSession?.addOutput(videoOutput!!)
    }
    captureSession?.commitConfiguration()
    previewLayer = AVCaptureVideoPreviewLayer(session = captureSession!!).apply {
        videoGravity = AVLayerVideoGravityResizeAspectFill
    }
}
ios objective-c kotlin avfoundation google-mlkit
1个回答
0
投票

我也有同样的问题。对我有用的是,一旦不再需要 MLKVisionImage,就致电

kotlin.native.runtime.GC.collect()

var visionImage: MLKVisionImage? = MLKVisionImage(didOutputSampleBuffer)
// ...
recognizer.processImage(visionImage as MLKCompatibleImageProtocol) { mlkText, nsError ->
  visionImage = null
  kotlin.native.runtime.GC.collect()
 // ...
}

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