13

Bir AVIsset'e bir CIFilter uygulamaya çalışıyorum ve ardından uygulanan filtreyle kaydedin. Bunu yapma biçimim, AVVideoCompositing özel bir AVVideoCompositing sınıfına sahip AVMutableVideoComposition nesnesine ayarlanmış videoComposition ile AVAssetExportSession kullanmaktır.Özel AVVideoCompositing sınıfı beklendiği gibi çalışmıyor

AVMutableVideoComposition nesnesinin instructions nesnesini, özel bir bileşim talimat sınıfına (AVMutableVideoCompositionInstruction'a uygun) ayarlıyorum. Bu sınıf, diğer önemsiz değişkenlerle birlikte bir parça kimliğinden geçirilir.

Ne yazık ki, bir sorunla karşılaştım - özel video compositor sınıfımda (AVVideoCompositing'a uygun) startVideoCompositionRequest: işlevi doğru çağrılmıyor.

Özel talimat sınıfımın parça kimliğine passthroughTrackID değişkenini ayarladığımda, AVVideoCompositing numaralı telefonumdaki startVideoCompositionRequest(request) işlevi çağrılmıyor.

Oysa, benim özel talimat sınıfının passthroughTrackID değişkeni belirtilmiş, startVideoCompositionRequest(request)denilen olduğunu, ancak doğru değil - Boş dizide request.sourceTrackIDs sonuçları ve sıfır değerinde request.sourceFrameByTrackID(trackID) sonuçları baskı.

Bulduğum ilginç bir şey, videoyu filtrelerle dışa aktarmaya çalışırken her zaman iki kez cancelAllPendingVideoCompositionRequests: işlevinin çağrılmasıydı. startVideoCompositionRequest:'dan önce bir kez ya da bir kez ya da startVideoCompositionRequest: numaralı telefonun aranmadığı bir satırda sadece iki kez çağrılır.

Videoyu filtreli dışa aktarmak için üç sınıf oluşturdum. İşte temelde sadece bir export işlevi bulunmaktadır ve gerekli tüm kodu çağırır yarar sınıfı var

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) 
     } 
    } 
} 

Burada diğer iki sınıfları - Ben bu yazı yapmak için bir kod bloğu içine ikisini de koyacağım kısa

// 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") 
     } 
    } 
} 

Bu konuda rehberlik etmek için Apple's AVCustomEdit sample project numaralı telefonu inceledim, ancak bunun nedenini bulmamın bir nedeni bulamıyorum.

Doğru aramak için request.sourceFrameByTrackID: işlevini nasıl alabilirim ve her çerçeve için geçerli bir CVPixelBuffer sağlayabilir miyim?

cevap

6

Bu özel AVVideoCompositionInstruction sınıfında requiredSourceTrackIDs değişken (Söz konusu VideoFilterCompositionInstruction)

override var requiredSourceTrackIDs: [NSValue]{ 
    get{ 
    return [ 
     NSNumber(value: Int(self.trackID)) 
    ] 
    } 
} 

Yani nihai özel kompozisyon dersine sınıf

olan pist kimliklerini içeren bir diziye ayarlanmış olması gerekir çıkıyor
class VideoFilterCompositionInstruction : AVMutableVideoCompositionInstruction{ 
    let trackID: CMPersistentTrackID 
    let filters: [CIFilter] 
    let context: CIContext 

    override var passthroughTrackID: CMPersistentTrackID{get{return self.trackID}} 
    override var requiredSourceTrackIDs: [NSValue]{get{return [NSNumber(value: Int(self.trackID))]}} 
    override var containsTweening: Bool{get{return false}} 

    init(trackID: CMPersistentTrackID, filters: [CIFilter], context: CIContext){ 
     self.trackID = trackID 
     self.filters = filters 
     self.context = context 

     super.init() 

     self.enablePostProcessing = true 
    } 

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

bu programı is also on GitHub

+0

Merhaba Örnek projenizi github üzerinde denedim, ancak benim tarafımda çalışmaz. func startRequest (_ asyncVideoCompositionRequest: AVAsynchronousVideoCompositionRequest), AVVideoCompositing'de ( – Sam

+0

) çağrılmadı. Yanlış olanın ne olduğunu biliyor musunuz, önerilen her şeyi uygularım. – Sam

+0

Bunu Swift 3'te denediniz mi? – Sam

3

zamanda için kod tüm Filtre etmek istediğiniz parçayı passthroughTrackID döndürdüğünüze göre doğru bir yaklaşım değil - filtrelenecek parçayı requiredSourceTrackIDs yerine döndürmeniz gerekiyor. (Ve bir kez yaptığınızda, da'un passthroughTrackID'dan döndürmesi farketmez.) Kesinlikle passthroughTrackID ve requiredSourceTrackIDs için bu şekilde çalışır nedenkalan soru ...

belgeler cevaplamak için Apple'ın en net yazma hiç değildir.

öğretim süresince video kompozisyon sonuç biriyse ... bir ipucu (vurgu eklenmiştir) (File a bug about it ve onlar. Yükseltebilir) Ama eski açıklamasında yakından bakarsanız, orada Kaynak karelerin, bu özellik ilgili parça kimliğini döndürür. Kompozitör, talimatı boyunca çalıştırılmayacak ve bunun yerine uygun kaynak çerçevesi kullanılmayacaktır. Eğer
işlemeden içinden tek bir iz geçer bir talimat sınıfını yapıyoruz zaman

Yani, sadecepassthroughTrackID kullanın.

Görüntü işlemeyi gerçekleştirmeyi planlıyorsanız, yalnızca kompozisyon içermeyen tek bir parça olsa bile, bu parçayı requiredSourceTrackIDs yerine belirtin.

+0

Merhaba, lütfen detaylandırır mısınız? Github projesi nasıl swift3 ile çalışır? – Sam

İlgili konular