Benutzerdefinierte AVVideoCompositing-Klasse funktioniert nicht wie erwartet

Ich versuche, einen CIFilter auf ein AVAsset anzuwenden und ihn dann mit dem angewendeten Filter zu speichern. Die Art, wie ich das mache, ist die Verwendung einesAVAssetExportSession mitvideoComposition auf ein @ setzAVMutableVideoComposition Objekt mit einem benutzerdefiniertenAVVideoCompositing Klasse.

Ich stelle auch das @ einstructions von meinenAVMutableVideoComposition -Objekt zu einer benutzerdefinierten Kompositionsanweisungsklasse (gemäßAVMutableVideoCompositionInstruction). Dieser Klasse wird zusammen mit einigen anderen unwichtigen Variablen eine Track-ID übergeben.

Leider bin ich auf ein Problem gestoßen - dasstartVideoCompositionRequest: -Funktion in meiner benutzerdefinierten Video-Compositor-Klasse (gemäßAVVideoCompositing) wird nicht korrekt aufgerufen.

Wenn ich das @ einstelpassthroughTrackID -Variable meiner benutzerdefinierten Anweisungsklasse für die Spur-ID, diestartVideoCompositionRequest(request) Funktion in meinemAVVideoCompositing wird nicht aufgerufen.

Yet, wenn ich das @ nicht einstelpassthroughTrackID -Variable meiner benutzerdefinierten Anweisungsklasse, diestartVideoCompositionRequest(request) ist angerufen, aber nicht richtig - drucktrequest.sourceTrackIDs ergibt ein leeres Array undrequest.sourceFrameByTrackID(trackID) ergibt einen Nullwert.

Etwas Interessantes, das ich fand, war, dass diecancelAllPendingVideoCompositionRequests:ie @ -Funktion wird immer zweimal aufgerufen, wenn versucht wird, das Video mit Filtern zu exportieren. Es wird entweder einmal vor @ aufgerufstartVideoCompositionRequest: und einmal danach oder nur zweimal hintereinander, wennstartVideoCompositionRequest: wird nicht aufgerufen.

Ich habe drei Klassen zum Exportieren des Videos mit Filtern erstellt. Hier ist die Utility-Klasse, die im Grunde nur ein @ enthäexport Funktion und ruft den gesamten erforderlichen Code auf

class VideoFilterExport{

    let asset: AVAsset
    init(asset: AVAsset){
        self.asset = asset
    }

    func export(toURL url: NSURL, callback: (url: NSURL?) -> Void){
        guard let track: AVAssetTrack = self.asset.tracksWithMediaType(AVMediaTypeVideo).first else{callback(url: nil); return}

        let composition = AVMutableComposition()
        let compositionTrack = composition.addMutableTrackWithMediaType(AVMediaTypeVideo, preferredTrackID: kCMPersistentTrackID_Invalid)

        do{
            try compositionTrack.insertTimeRange(track.timeRange, ofTrack: track, atTime: kCMTimeZero)
        }
        catch _{callback(url: nil); return}

        let videoComposition = AVMutableVideoComposition(propertiesOfAsset: composition)
        videoComposition.customVideoCompositorClass = VideoFilterCompositor.self
        videoComposition.frameDuration = CMTimeMake(1, 30)
        videoComposition.renderSize = compositionTrack.naturalSize

        let instruction = VideoFilterCompositionInstruction(trackID: compositionTrack.trackID)
        instruction.timeRange = CMTimeRangeMake(kCMTimeZero, self.asset.duration)
        videoComposition.instructions = [instruction]

        let session: AVAssetExportSession = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetMediumQuality)!
        session.videoComposition = videoComposition
        session.outputURL = url
        session.outputFileType = AVFileTypeMPEG4

        session.exportAsynchronouslyWithCompletionHandler(){
            callback(url: url)
        }
    }
}

Hier sind die anderen beiden Klassen - ich werde sie beide in einen Codeblock setzen, um diesen Beitrag zu verkürzen

// Video Filter Composition Instruction Class - from what I gather,
// AVVideoCompositionInstruction is used only to pass values to
// the AVVideoCompositing class

class VideoFilterCompositionInstruction : AVMutableVideoCompositionInstruction{

    let trackID: CMPersistentTrackID
    let filters: ImageFilterGroup
    let context: CIContext


    // When I leave this line as-is, startVideoCompositionRequest: isn't called.
    // When commented out, startVideoCompositionRequest(request) is called, but there
    // are no valid CVPixelBuffers provided by request.sourceFrameByTrackID(below value)
    override var passthroughTrackID: CMPersistentTrackID{get{return self.trackID}}
    override var requiredSourceTrackIDs: [NSValue]{get{return []}}
    override var containsTweening: Bool{get{return false}}


    init(trackID: CMPersistentTrackID, filters: ImageFilterGroup, context: CIContext){
        self.trackID = trackID
        self.filters = filters
        self.context = context

        super.init()

        //self.timeRange = timeRange
        self.enablePostProcessing = true
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

}


// My custom AVVideoCompositing class. This is where the problem lies -
// although I don't know if this is the root of the problem

class VideoFilterCompositor : NSObject, AVVideoCompositing{

    var requiredPixelBufferAttributesForRenderContext: [String : AnyObject] = [
        kCVPixelBufferPixelFormatTypeKey as String : NSNumber(unsignedInt: kCVPixelFormatType_32BGRA), // The video is in 32 BGRA
        kCVPixelBufferOpenGLESCompatibilityKey as String : NSNumber(bool: true),
        kCVPixelBufferOpenGLCompatibilityKey as String : NSNumber(bool: true)
    ]
    var sourcePixelBufferAttributes: [String : AnyObject]? = [
        kCVPixelBufferPixelFormatTypeKey as String : NSNumber(unsignedInt: kCVPixelFormatType_32BGRA),
        kCVPixelBufferOpenGLESCompatibilityKey as String : NSNumber(bool: true),
        kCVPixelBufferOpenGLCompatibilityKey as String : NSNumber(bool: true)
    ]

    let renderQueue = dispatch_queue_create("co.getblix.videofiltercompositor.renderingqueue", DISPATCH_QUEUE_SERIAL)

    override init(){
        super.init()
    }

    func startVideoCompositionRequest(request: AVAsynchronousVideoCompositionRequest){
       // This code block is never executed when the
       // passthroughTrackID variable is in the above class  

        autoreleasepool(){
            dispatch_async(self.renderQueue){
                guard let instruction = request.videoCompositionInstruction as? VideoFilterCompositionInstruction else{
                    request.finishWithError(NSError(domain: "getblix.co", code: 760, userInfo: nil))
                    return
                }
                guard let pixels = request.sourceFrameByTrackID(instruction.passthroughTrackID) else{
                    // This code block is executed when I comment out the
                    // passthroughTrackID variable in the above class            

                    request.finishWithError(NSError(domain: "getblix.co", code: 761, userInfo: nil))
                    return
                }
                // I have not been able to get the code to reach this point
                // This function is either not called, or the guard
                // statement above executes

                let image = CIImage(CVPixelBuffer: pixels)
                let filtered: CIImage = //apply the filter here

                let width = CVPixelBufferGetWidth(pixels)
                let height = CVPixelBufferGetHeight(pixels)
                let format = CVPixelBufferGetPixelFormatType(pixels)

                var newBuffer: CVPixelBuffer?
                CVPixelBufferCreate(kCFAllocatorDefault, width, height, format, nil, &newBuffer)

                if let buffer = newBuffer{
                    instruction.context.render(filtered, toCVPixelBuffer: buffer)
                    request.finishWithComposedVideoFrame(buffer)
                }
                else{
                    request.finishWithComposedVideoFrame(pixels)
                }
            }
        }
    }

    func renderContextChanged(newRenderContext: AVVideoCompositionRenderContext){
        // I don't have any code in this block
    }

    // This is interesting - this is called twice,
    // Once before startVideoCompositionRequest is called,
    // And once after. In the case when startVideoCompositionRequest
    // Is not called, this is simply called twice in a row
    func cancelAllPendingVideoCompositionRequests(){
        dispatch_barrier_async(self.renderQueue){
            print("Cancelled")
        }
    }
}

Ich habe auf @ gesuc Apples AVCustomEdit Beispielprojekt viel zur Orientierung, aber ich kann anscheinend keinen Grund finden, warum dies geschieht.

Wie kann ich das @ bekommrequest.sourceFrameByTrackID: -Funktion zum korrekten Aufrufen und Bereitstellen eines gültigenCVPixelBuffer für jeden Frame?

Antworten auf die Frage(4)

Ihre Antwort auf die Frage