MTLTexture.getbytes() returning EXC_BAD_ACCESS - Possible reasons?

安稳与你 提交于 2020-07-10 06:48:07

问题


I am using ARKit3 body tracking capabilities to overlay a 3D model on top of the camera feed and allow the user to capture the feed as an MP4 with audio. I have been able to draw to the camera preview to the MTLView successfully. However, am receiving a EXC_BAD_ACCESS error which I cannot seem to debug when trying to write to my asset writer. My App has render class which is responsible for rendering the Metal textures from the feed, and a MetalVideoRecorder class which is used to write the frames to the mp4 file. I have highlighted the line generating the error below:

Relevant code from my MetalVideoWriter class:

func writeFrame(forTexture texture: MTLTexture) {

    //Only if recording and our asset writer has been initialized
    if (assetWriter == nil) { return; }
    if !isRecording { return; }
    while !assetWriterVideoInput!.isReadyForMoreMediaData {}

    //Get pixer buffer pool from the asset writer (to write to)
    guard let pixelBufferPool = assetWriterPixelBufferInput!.pixelBufferPool else { return; }

    var maybePixelBuffer: CVPixelBuffer? = nil
    let status  = CVPixelBufferPoolCreatePixelBuffer(nil, pixelBufferPool, &maybePixelBuffer)
    if status != kCVReturnSuccess { return; }
    guard let pixelBuffer = maybePixelBuffer else { return }

    CVPixelBufferLockBaseAddress(pixelBuffer, [])
    let pixelBufferBytes = CVPixelBufferGetBaseAddress(pixelBuffer)!
    let bytesPerRow = CVPixelBufferGetBytesPerRow(pixelBuffer)
    let region = MTLRegionMake2D(0, 0, texture.width, texture.height)

    //########################################
    //#### This line returns the error... ####
    //########################################
    texture.getBytes(pixelBufferBytes, bytesPerRow: bytesPerRow, from: region, mipmapLevel: 0)

    //Timestamp of the frame
    let frameTime = CACurrentMediaTime() - recordingStartTime
    let presentationTime = CMTimeMakeWithSeconds(frameTime, 240)
    assetWriterPixelBufferInput!.append(pixelBuffer, withPresentationTime: presentationTime)

    CVPixelBufferUnlockBaseAddress(pixelBuffer, [])        
    firstFrameWritten = true;
}

And this is how a frame is handled by my Renderer class:

func update() {

    if (self.metalVideoRecorder == nil) {
        print("Failed - Metal video recorder is nil")
        return;
    }
    // Wait to ensure only kMaxBuffersInFlight are getting proccessed by any stage in the Metal
    //   pipeline (App, Metal, Drivers, GPU, etc)
    let _ = inFlightSemaphore.wait(timeout: DispatchTime.distantFuture)

    // Create a new command buffer for each renderpass to the current drawable

    if let commandBuffer = commandQueue.makeCommandBuffer() {
        commandBuffer.label = "MyCommand"


        // Retain our CVMetalTextures for the duration of the rendering cycle. The MTLTextures
        //   we use from the CVMetalTextures are not valid unless their parent CVMetalTextures
        //   are retained. Since we may release our CVMetalTexture ivars during the rendering
        //   cycle, we must retain them separately here.
        var textures = [capturedImageTextureY, capturedImageTextureCbCr]

        updateBufferStates()
        updateGameState()

        guard let renderDescriptor = renderDestination.currentRenderPassDescriptor else {return}
        renderDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0, 0, 0, 1) //Black background

        guard let currentDrawable = renderDestination.currentDrawable else {return}

        guard let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderDescriptor) else {return}
        renderEncoder.label = "MyRenderEncoder"

        let texture = currentDrawable.texture

        // Add completion hander which signal _inFlightSemaphore when Metal and the GPU has fully
        //   finished proccssing the commands we're encoding this frame.  This indicates when the
        //   dynamic buffers, that we're writing to this frame, will no longer be needed by Metal
        //   and the GPU.
        commandBuffer.addCompletedHandler{ [weak self] commandBuffer in
            if let strongSelf = self {
                strongSelf.inFlightSemaphore.signal()

                if (strongSelf.metalVideoRecorder!.isRecording == true)
                {
                    strongSelf.metalVideoRecorder!.writeFrame(forTexture: texture)
                }
            }
            textures.removeAll()
        }

        //Draw camera image
        drawCapturedImage(renderEncoder: renderEncoder)

        //Draw additional items on top (cube etc.)
        drawAnchorGeometry(renderEncoder: renderEncoder)

        // We're done encoding commands
        renderEncoder.endEncoding()

        // Schedule a present once the framebuffer is complete using the current drawable
        commandBuffer.present(currentDrawable)

        // Finalize rendering here & push the command buffer to the GPU
        commandBuffer.commit()

    }
}

What are some of the potential reasons for this error? And what is the best way to go about debugging the error?

Note my approach to recording frames was inspired by this answer: Capture Metal MTKView as Movie in realtime?

UPDATE

The method above assumed your texture and output video are the same size. In my case, I was trying to generate a 1280x720 video, but the MTLView I was trying to record is different in size (its a full screen view, so it is whatever the size display of the phone would be). My options are either to record in a non-standard aspect ratio (yuck) or determine a way to crop. I'll keep looking for a way to crop the frames.

来源:https://stackoverflow.com/questions/62359253/mtltexture-getbytes-returning-exc-bad-access-possible-reasons

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!