2017-04-21 4 views
9

Ich bin ein MTLTexture von CVImageBuffer s (von der Kamera und Spielern) zu schaffen CVMetalTextureCacheCreateTextureFromImage mit den MTLTexture ein CVMetalTexture und dann bekommen CVMetalTextureGetTexture zu bekommen.Holding auf einen MTLTexture von einem CVImageBuffer verursacht Stottern

Das Problem, das ich sehe, ist, wenn ich später die Textur mit Metall rendern, sehe ich gelegentlich Video Frames gerendert gerendert (visuell stottert hin und her in der Zeit), vermutlich weil CoreVideo den zugrunde liegenden CVImageBuffer Speicher ändert und die MTLTexture zeigt nur dort.

Gibt es eine Möglichkeit, dass CoreVideo diesen Puffer nicht berührt und einen anderen aus seinem Pool verwendet, bis ich das Objekt MTLTexture freigebe?

Meine aktuelle Workaround ist das Blitting der Textur mit einem MTLBlitCommandEncoder, aber da ich nur auf die Textur für ~ 30 Millisekunden halten muss, scheint das unnötig.

+0

Behalten Sie einen starken Bezug auf das' CVMetalTexture' bis zu dem Punkt, an dem Sie mit der Metal-Textur fertig sind? Oder haben Sie nur einen starken Bezug zum 'MTLTexture' Objekt? –

+0

Ich halte eine starke Bezugnahme auf die 'MTLTexture' nur aufgrund einiger Implementierungsdetails. Würden die Objekte 'CVMetalTexture' oder' CVImageBuffer' beibehalten, um mein Problem zu lösen? – Blixt

+0

Ich weiß es nicht. Es könnte. Es ist nur eine Vermutung von meiner Seite. Wenn Sie es einfach versuchen können, sollten Sie. :) –

Antwort

0

Scheint, dass Ihr Problem davon abhängt, wie Sie eine Sitzung verwalten, um rohe Kameradaten zu erhalten.

Ich glaube, Sie können die Kamera-Sitzung in tiefen und in Echtzeit analysieren den aktuellen Status Ihrer Sitzung mit dieser Klasse wissen (MetalCameraSession):

import AVFoundation 
import Metal 
public protocol MetalCameraSessionDelegate { 
    func metalCameraSession(_ session: MetalCameraSession, didReceiveFrameAsTextures: [MTLTexture], withTimestamp: Double) 
    func metalCameraSession(_ session: MetalCameraSession, didUpdateState: MetalCameraSessionState, error: MetalCameraSessionError?) 
} 
public final class MetalCameraSession: NSObject { 
    public var frameOrientation: AVCaptureVideoOrientation? { 
     didSet { 
      guard 
       let frameOrientation = frameOrientation, 
       let outputData = outputData, 
       outputData.connection(withMediaType: AVMediaTypeVideo).isVideoOrientationSupported 
      else { return } 

      outputData.connection(withMediaType: AVMediaTypeVideo).videoOrientation = frameOrientation 
     } 
    } 
    public let captureDevicePosition: AVCaptureDevicePosition 
    public var delegate: MetalCameraSessionDelegate? 
    public let pixelFormat: MetalCameraPixelFormat 
    public init(pixelFormat: MetalCameraPixelFormat = .rgb, captureDevicePosition: AVCaptureDevicePosition = .back, delegate: MetalCameraSessionDelegate? = nil) { 
     self.pixelFormat = pixelFormat 
     self.captureDevicePosition = captureDevicePosition 
     self.delegate = delegate 
     super.init(); 
     NotificationCenter.default.addObserver(self, selector: #selector(captureSessionRuntimeError), name: NSNotification.Name.AVCaptureSessionRuntimeError, object: nil) 
    } 
    public func start() { 
     requestCameraAccess() 
     captureSessionQueue.async(execute: { 
      do { 
       self.captureSession.beginConfiguration() 
       try self.initializeInputDevice() 
       try self.initializeOutputData() 
       self.captureSession.commitConfiguration() 
       try self.initializeTextureCache() 
       self.captureSession.startRunning() 
       self.state = .streaming 
      } 
      catch let error as MetalCameraSessionError { 
       self.handleError(error) 
      } 
      catch { 
       print(error.localizedDescription) 
      } 
     }) 
    } 
    public func stop() { 
     captureSessionQueue.async(execute: { 
      self.captureSession.stopRunning() 
      self.state = .stopped 
     }) 
    } 
    fileprivate var state: MetalCameraSessionState = .waiting { 
     didSet { 
      guard state != .error else { return } 

      delegate?.metalCameraSession(self, didUpdateState: state, error: nil) 
     } 
    } 
    fileprivate var captureSession = AVCaptureSession() 
    internal var captureDevice = MetalCameraCaptureDevice() 
    fileprivate var captureSessionQueue = DispatchQueue(label: "MetalCameraSessionQueue", attributes: []) 
#if arch(i386) || arch(x86_64) 
#else 
    /// Texture cache we will use for converting frame images to textures 
    internal var textureCache: CVMetalTextureCache? 
#endif 
    fileprivate var metalDevice = MTLCreateSystemDefaultDevice() 
    internal var inputDevice: AVCaptureDeviceInput? { 
     didSet { 
      if let oldValue = oldValue { 
       captureSession.removeInput(oldValue) 
      } 
      captureSession.addInput(inputDevice) 
     } 
    } 
    internal var outputData: AVCaptureVideoDataOutput? { 
     didSet { 
      if let oldValue = oldValue { 
       captureSession.removeOutput(oldValue) 
      } 
      captureSession.addOutput(outputData) 
     } 
    } 
    fileprivate func requestCameraAccess() { 
     captureDevice.requestAccessForMediaType(AVMediaTypeVideo) { 
      (granted: Bool) -> Void in 
      guard granted else { 
       self.handleError(.noHardwareAccess) 
       return 
      } 

      if self.state != .streaming && self.state != .error { 
       self.state = .ready 
      } 
     } 
    } 
    fileprivate func handleError(_ error: MetalCameraSessionError) { 
     if error.isStreamingError() { 
      state = .error 
     } 

     delegate?.metalCameraSession(self, didUpdateState: state, error: error) 
    } 
    fileprivate func initializeTextureCache() throws { 
#if arch(i386) || arch(x86_64) 
     throw MetalCameraSessionError.failedToCreateTextureCache 
#else 
     guard 
      let metalDevice = metalDevice, 
      CVMetalTextureCacheCreate(kCFAllocatorDefault, nil, metalDevice, nil, &textureCache) == kCVReturnSuccess 
     else { 
      throw MetalCameraSessionError.failedToCreateTextureCache 
     } 
#endif 
    } 
    fileprivate func initializeInputDevice() throws { 
     var captureInput: AVCaptureDeviceInput! 
     guard let inputDevice = captureDevice.device(mediaType: AVMediaTypeVideo, position: captureDevicePosition) else { 
      throw MetalCameraSessionError.requestedHardwareNotFound 
     } 
     do { 
      captureInput = try AVCaptureDeviceInput(device: inputDevice) 
     } 
     catch { 
      throw MetalCameraSessionError.inputDeviceNotAvailable 
     } 
     guard captureSession.canAddInput(captureInput) else { 
      throw MetalCameraSessionError.failedToAddCaptureInputDevice 
     } 
     self.inputDevice = captureInput 
    } 
    fileprivate func initializeOutputData() throws { 
     let outputData = AVCaptureVideoDataOutput() 

     outputData.videoSettings = [ 
      kCVPixelBufferPixelFormatTypeKey as AnyHashable : Int(pixelFormat.coreVideoType) 
     ] 
     outputData.alwaysDiscardsLateVideoFrames = true 
     outputData.setSampleBufferDelegate(self, queue: captureSessionQueue) 

     guard captureSession.canAddOutput(outputData) else { 
      throw MetalCameraSessionError.failedToAddCaptureOutput 
     } 

     self.outputData = outputData 
    } 
    @objc 
    fileprivate func captureSessionRuntimeError() { 
     if state == .streaming { 
      handleError(.captureSessionRuntimeError) 
     } 
    } 
    deinit { 
     NotificationCenter.default.removeObserver(self) 
    } 
} 
extension MetalCameraSession: AVCaptureVideoDataOutputSampleBufferDelegate { 
#if arch(i386) || arch(x86_64) 
#else 
    private func texture(sampleBuffer: CMSampleBuffer?, textureCache: CVMetalTextureCache?, planeIndex: Int = 0, pixelFormat: MTLPixelFormat = .bgra8Unorm) throws -> MTLTexture { 
     guard let sampleBuffer = sampleBuffer else { 
      throw MetalCameraSessionError.missingSampleBuffer 
     } 
     guard let textureCache = textureCache else { 
      throw MetalCameraSessionError.failedToCreateTextureCache 
     } 
     guard let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { 
      throw MetalCameraSessionError.failedToGetImageBuffer 
     } 
     let isPlanar = CVPixelBufferIsPlanar(imageBuffer) 
     let width = isPlanar ? CVPixelBufferGetWidthOfPlane(imageBuffer, planeIndex) : CVPixelBufferGetWidth(imageBuffer) 
     let height = isPlanar ? CVPixelBufferGetHeightOfPlane(imageBuffer, planeIndex) : CVPixelBufferGetHeight(imageBuffer) 
     var imageTexture: CVMetalTexture? 
     let result = CVMetalTextureCacheCreateTextureFromImage(kCFAllocatorDefault, textureCache, imageBuffer, nil, pixelFormat, width, height, planeIndex, &imageTexture) 
     guard 
      let unwrappedImageTexture = imageTexture, 
      let texture = CVMetalTextureGetTexture(unwrappedImageTexture), 
      result == kCVReturnSuccess 
     else { 
      throw MetalCameraSessionError.failedToCreateTextureFromImage 
     } 
     return texture 
    } 
    private func timestamp(sampleBuffer: CMSampleBuffer?) throws -> Double { 
     guard let sampleBuffer = sampleBuffer else { 
      throw MetalCameraSessionError.missingSampleBuffer 
     } 

     let time = CMSampleBufferGetPresentationTimeStamp(sampleBuffer) 

     guard time != kCMTimeInvalid else { 
      throw MetalCameraSessionError.failedToRetrieveTimestamp 
     } 

     return (Double)(time.value)/(Double)(time.timescale); 
    } 
    @objc public func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputSampleBuffer sampleBuffer: CMSampleBuffer!, from connection: AVCaptureConnection!) { 
     do { 
      var textures: [MTLTexture]! 

      switch pixelFormat { 
      case .rgb: 
       let textureRGB = try texture(sampleBuffer: sampleBuffer, textureCache: textureCache) 
       textures = [textureRGB] 
      case .yCbCr: 
       let textureY = try texture(sampleBuffer: sampleBuffer, textureCache: textureCache, planeIndex: 0, pixelFormat: .r8Unorm) 
       let textureCbCr = try texture(sampleBuffer: sampleBuffer, textureCache: textureCache, planeIndex: 1, pixelFormat: .rg8Unorm) 
       textures = [textureY, textureCbCr] 
      } 

      let timestamp = try self.timestamp(sampleBuffer: sampleBuffer) 

      delegate?.metalCameraSession(self, didReceiveFrameAsTextures: textures, withTimestamp: timestamp) 
     } 
     catch let error as MetalCameraSessionError { 
      self.handleError(error) 
     } 
     catch { 
      print(error.localizedDescription) 
     } 
    } 
#endif 
} 

Mit dieser Klasse die anderen Sitzung wissen Typen und die Fehler, die occours (MetalCameraSessionTypes):

import AVFoundation 
public enum MetalCameraSessionState { 
    case ready 
    case streaming 
    case stopped 
    case waiting 
    case error 
} 
public enum MetalCameraPixelFormat { 
    case rgb 
    case yCbCr 
    var coreVideoType: OSType { 
     switch self { 
     case .rgb: 
      return kCVPixelFormatType_32BGRA 
     case .yCbCr: 
      return kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange 
     } 
    } 
} 
public enum MetalCameraSessionError: Error { 
    case noHardwareAccess 
    case failedToAddCaptureInputDevice 
    case failedToAddCaptureOutput 
    case requestedHardwareNotFound 
    case inputDeviceNotAvailable 
    case captureSessionRuntimeError 
    case failedToCreateTextureCache 
    case missingSampleBuffer 
    case failedToGetImageBuffer 
    case failedToCreateTextureFromImage 
    case failedToRetrieveTimestamp 
    public func isStreamingError() -> Bool { 
     switch self { 
     case .noHardwareAccess, .failedToAddCaptureInputDevice, .failedToAddCaptureOutput, .requestedHardwareNotFound, .inputDeviceNotAvailable, .captureSessionRuntimeError: 
      return true 
     default: 
      return false 
     } 
    } 
    public var localizedDescription: String { 
     switch self { 
     case .noHardwareAccess: 
      return "Failed to get access to the hardware for a given media type." 
     case .failedToAddCaptureInputDevice: 
      return "Failed to add a capture input device to the capture session." 
     case .failedToAddCaptureOutput: 
      return "Failed to add a capture output data channel to the capture session." 
     case .requestedHardwareNotFound: 
      return "Specified hardware is not available on this device." 
     case .inputDeviceNotAvailable: 
      return "Capture input device cannot be opened, probably because it is no longer available or because it is in use." 
     case .captureSessionRuntimeError: 
      return "AVCaptureSession runtime error." 
     case .failedToCreateTextureCache: 
      return "Failed to initialize texture cache." 
     case .missingSampleBuffer: 
      return "No sample buffer to convert the image from." 
     case .failedToGetImageBuffer: 
      return "Failed to retrieve an image buffer from camera's output sample buffer." 
     case .failedToCreateTextureFromImage: 
      return "Failed to convert the frame to a Metal texture." 
     case .failedToRetrieveTimestamp: 
      return "Failed to retrieve timestamp from the sample buffer." 
     } 
    } 
} 

Dann können Sie einen Wrapper für dies AVFoundation‘verwendendie Instanzmethoden anstelle der Klasse Einsen (MetalCameraCaptureDevice) hat:

import AVFoundation 
internal class MetalCameraCaptureDevice { 
    internal func device(mediaType: String, position: AVCaptureDevicePosition) -> AVCaptureDevice? { 
     guard let devices = AVCaptureDevice.devices(withMediaType: mediaType) as? [AVCaptureDevice] else { return nil } 

     if let index = devices.index(where: { $0.position == position }) { 
      return devices[index] 
     } 
     return nil 
    } 
    internal func requestAccessForMediaType(_ mediaType: String!, completionHandler handler: ((Bool) -> Void)!) { 
     AVCaptureDevice.requestAccess(forMediaType: mediaType, completionHandler: handler) 
    } 
} 

Dann könnten Sie haben eine benutzerdefinierte Viewcontroller Klasse zur Steuerung der Kamera wie diese (CameraViewController): Schließlich

import UIKit 
import Metal 
internal final class CameraViewController: MTKViewController { 
    var session: MetalCameraSession? 
    override func viewDidLoad() { 
     super.viewDidLoad() 
     session = MetalCameraSession(delegate: self) 
    } 
    override func viewWillAppear(_ animated: Bool) { 
     super.viewWillAppear(animated) 
     session?.start() 
    } 
    override func viewDidDisappear(_ animated: Bool) { 
     super.viewDidDisappear(animated) 
     session?.stop() 
    } 
} 
// MARK: - MetalCameraSessionDelegate 
extension CameraViewController: MetalCameraSessionDelegate { 
    func metalCameraSession(_ session: MetalCameraSession, didReceiveFrameAsTextures textures: [MTLTexture], withTimestamp timestamp: Double) { 
     self.texture = textures[0] 
    } 
    func metalCameraSession(_ cameraSession: MetalCameraSession, didUpdateState state: MetalCameraSessionState, error: MetalCameraSessionError?) { 
     if error == .captureSessionRuntimeError { 
      print(error?.localizedDescription ?? "None") 
      cameraSession.start() 
     } 
     DispatchQueue.main.async { 
      self.title = "Metal camera: \(state)" 
     } 
     print("Session changed state to \(state) with error: \(error?.localizedDescription ?? "None").") 
    } 
} 

Ihre Klasse könnte wie diese sein (MTKViewController), wo Sie das haben, wo Sie die haben erhalten Sie genau die MTLTexture, die Sie aus dem Puffer Kamera erwarten:

import UIKit 
import Metal 
#if arch(i386) || arch(x86_64) 
#else 
    import MetalKit 
#endif 
open class MTKViewController: UIViewController { 
    open var texture: MTLTexture? 
    open func willRenderTexture(_ texture: inout MTLTexture, withCommandBuffer commandBuffer: MTLCommandBuffer, device: MTLDevice) { 
    } 
    open func didRenderTexture(_ texture: MTLTexture, withCommandBuffer commandBuffer: MTLCommandBuffer, device: MTLDevice) { 
    } 
    override open func loadView() { 
     super.loadView() 
#if arch(i386) || arch(x86_64) 
     NSLog("Failed creating a default system Metal device, since Metal is not available on iOS Simulator.") 
#else 
     assert(device != nil, "Failed creating a default system Metal device. Please, make sure Metal is available on your hardware.") 
#endif 
     initializeMetalView() 
     initializeRenderPipelineState() 
    } 
    fileprivate func initializeMetalView() { 
#if arch(i386) || arch(x86_64) 
#else 
     metalView = MTKView(frame: view.bounds, device: device) 
     metalView.delegate = self 
     metalView.framebufferOnly = true 
     metalView.colorPixelFormat = .bgra8Unorm 
     metalView.contentScaleFactor = UIScreen.main.scale 
     metalView.autoresizingMask = [.flexibleWidth, .flexibleHeight] 
     view.insertSubview(metalView, at: 0) 
#endif 
    } 
#if arch(i386) || arch(x86_64) 
#else 
    internal var metalView: MTKView! 
#endif 
    internal var device = MTLCreateSystemDefaultDevice() 
    internal var renderPipelineState: MTLRenderPipelineState? 
    fileprivate let semaphore = DispatchSemaphore(value: 1) 
    fileprivate func initializeRenderPipelineState() { 
     guard 
      let device = device, 
      let library = device.newDefaultLibrary() 
     else { return } 

     let pipelineDescriptor = MTLRenderPipelineDescriptor() 
     pipelineDescriptor.sampleCount = 1 
     pipelineDescriptor.colorAttachments[0].pixelFormat = .bgra8Unorm 
     pipelineDescriptor.depthAttachmentPixelFormat = .invalid 
     pipelineDescriptor.vertexFunction = library.makeFunction(name: "mapTexture") 
     pipelineDescriptor.fragmentFunction = library.makeFunction(name: "displayTexture") 
     do { 
      try renderPipelineState = device.makeRenderPipelineState(descriptor: pipelineDescriptor) 
     } 
     catch { 
      assertionFailure("Failed creating a render state pipeline. Can't render the texture without one.") 
      return 
     } 
    } 
} 
#if arch(i386) || arch(x86_64) 
#else 
extension MTKViewController: MTKViewDelegate { 
    public func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) { 
     NSLog("MTKView drawable size will change to \(size)") 
    } 
    public func draw(in: MTKView) { 
     _ = semaphore.wait(timeout: DispatchTime.distantFuture) 
     autoreleasepool { 
      guard 
       var texture = texture, 
       let device = device 
      else { 
       _ = semaphore.signal() 
       return 
      } 
      let commandBuffer = device.makeCommandQueue().makeCommandBuffer() 
      willRenderTexture(&texture, withCommandBuffer: commandBuffer, device: device) 
      render(texture: texture, withCommandBuffer: commandBuffer, device: device) 
     } 
    } 
    private func render(texture: MTLTexture, withCommandBuffer commandBuffer: MTLCommandBuffer, device: MTLDevice) { 
     guard 
      let currentRenderPassDescriptor = metalView.currentRenderPassDescriptor, 
      let currentDrawable = metalView.currentDrawable, 
      let renderPipelineState = renderPipelineState 
     else { 
      semaphore.signal() 
      return 
     } 
     let encoder = commandBuffer.makeRenderCommandEncoder(descriptor: currentRenderPassDescriptor) 
     encoder.pushDebugGroup("RenderFrame") 
     encoder.setRenderPipelineState(renderPipelineState) 
     encoder.setFragmentTexture(texture, at: 0) 
     encoder.drawPrimitives(type: .triangleStrip, vertexStart: 0, vertexCount: 4, instanceCount: 1) 
     encoder.popDebugGroup() 
     encoder.endEncoding() 
     commandBuffer.addScheduledHandler { [weak self] (buffer) in 
      guard let unwrappedSelf = self else { return } 

      unwrappedSelf.didRenderTexture(texture, withCommandBuffer: buffer, device: device) 
      unwrappedSelf.semaphore.signal() 
     } 
     commandBuffer.present(currentDrawable) 
     commandBuffer.commit() 
    } 
} 
#endif 

Jetzt Sie alle Quellen haben, aber Sie können auch alle navoshta (der Autor) GitHub Projekts here komplette aller Kommentare und Beschreibungen finden über Code und ein großes Tutorial über dieses Projekt here vor allem der zweite Teil, in dem Sie die Textur erhalten können (Sie diesen Code in der unten stehenden MetalCameraSession Klasse finden):

guard 
    let unwrappedImageTexture = imageTexture, 
    let texture = CVMetalTextureGetTexture(unwrappedImageTexture), 
    result == kCVReturnSuccess 
else { 
    throw MetalCameraSessionError.failedToCreateTextureFromImage 
} 
+0

Dies ist eine Menge Quellcode aus dem Repo, aber sagt mir nicht wirklich, wo ich anfangen soll, nach dem Fehler in meinem Code zu suchen, selbst wenn dieser Quellcode das Problem beheben würde. Auf den ersten Blick scheint mir ziemlich genau zu sein, was dieser Quellcode tut, haben Sie irgendwelche Hinweise darauf, welcher Teil wichtig ist, um das flackernde Problem zu vermeiden? – Blixt

+0

Ich denke, Sie sollten Ihre Aufmerksamkeit auf die Capture-Sitzung konzentrieren, um sicherzustellen, dass Ihr Puffer nicht durch falsche Thread-Zeiten über zum Beispiel den Start der Erfassung und den tatsächlichen Status des Geräts kompromittiert wurde: das Vorhandensein eines Semaphors (MTKViewController) Kontrollieren Sie den Fluss des Puffers ist hervorragend, stellen Sie sicher, dass die Pipeline richtig gebaut. –

+0

Haben Sie diese Bibliothek versuchen? –

0

die Problem kann aufgrund Ihrer Kamera-Eingabe auftreten. Wenn Ihr Filmmaterial nicht genau die gleiche Bildrate wie Ihre beabsichtigte Ausgabe ist, wird Frame-Rate Diskrepanz seltsame Ghosting verursachen. Versuchen Sie, die Auto-Bildrate zu deaktivieren.

DREHZAHLAUSBLEND:

Andere Ursachen dieses Problems kann das folgende Ursachen Es gibt bestimmte Geschwindigkeiten, die mit Frameraten synchronisieren, so dass sie das Stottern verursachen. Je niedriger die Bildrate ist, desto offensichtlicher ist das Problem.

SUB-PIXEL-INTERPOLATION: Es gibt auch andere Fälle, in denen die Sub-Pixel-Interpolation zwischen Frames dazu führt, dass Detailbereiche zwischen Frames flackern.

Die Lösung für erfolgreiches Rendering besteht darin, die richtige Geschwindigkeit (Pixel pro Sekunde) für die Bildrate zu verwenden, genügend Bewegungsunschärfe hinzuzufügen, um das Problem zu verbergen oder die Detailgenauigkeit im Bild zu reduzieren.

+0

Die Eingabe kann nicht wirklich das Problem sein, denn wenn ich den Puffer in den Rückruf kopieren, ist alles in Ordnung. Das Problem manifestiert sich erst, wenn ich eine 'MTLTexture' aus dem Puffer bekomme und versuche, sie später (außerhalb des Callbacks) zu rendern. Ich sehe keine Artefakte in den mir zur Verfügung gestellten Videodaten. – Blixt

4

Ich vor kurzem in genau diesem gleichen Problem lief. Das Problem ist, dass die MTLTexture nicht gültig ist, es sei denn CVMetalTextureRef ist noch am Leben. Sie müssen den gesamten Zeitraum, in dem Sie MTLTexture verwenden (bis zum Ende des aktuellen Rendering-Zyklus), einen Verweis auf CVMetalTextureRef beibehalten.

+0

Dies ist der Schlüssel zum erfolgreichen Texturing von CMSampleBufferRefs unter Metal, danke! – C0C0AL0C0

+0

C0C0AL0C0 Ich nehme an, dass dies auch die Lösung zum Aufreißen in Apples Beispielcode MetalVideoCapture ist? https://stackoverflow.com/questions/38879518/screen-tearing-and-camera-capture-with-metal (da du diese Frage beantwortet hast, glaube ich, habe sie aber seitdem entfernt) – Gary

1

Ich lief das gleiche Problem, aber mit einem zusätzlichen Verweis auf CVMetalTexture-Objekt löste dieses Problem nicht in meinem Fall.

Soweit ich das beurteilen kann, passiert es nur, wenn ich einen neuen Frame von der Kamera erhalte, bevor mein Metallcode die Verarbeitung des vorherigen Frames abgeschlossen hat.

Es scheint, dass CVMetalTextureCacheCreateTextureFromImage einfach eine Textur über dem Pixelpuffer erstellt, in den die Kamera Daten hineinspeist. Daher ist der asynchrone Zugriff auf diesen Code einige Probleme.

Ich habe mich entschieden, eine Kopie von MTLTexture zu erstellen (die auch asynchron ist, aber schnell genug ist).

Hier wird eine Beschreibung CVMetalTextureCacheCreateTextureFromImage()

„Diese Funktion erzeugt oder liefert ein zwischengespeicherten Corevideo Metalltexturpuffer zu einem Bildpuffer zugeordnet entsprechend den angegebenen, eine Live zwischen einem gerätebasierten Bildpuffer Bindung zu schaffen und einem MTLTexture Objekt. ",

Verwandte Themen