13

मैं एक एवीएसेट में एक सीआईएफलेटर लागू करने का प्रयास कर रहा हूं, और उसके बाद इसे फ़िल्टर के साथ सहेजता हूं। जिस तरह से मैं यह कर रहा हूं AVAssetExportSessionvideoCompositionAVMutableVideoComposition ऑब्जेक्ट पर कस्टम AVVideoCompositing क्लास के साथ सेट करके है।कस्टम एवीवीडियो कॉम्पोजिटिंग क्लास अपेक्षित काम नहीं कर रहा है

मैं अपने AVMutableVideoComposition ऑब्जेक्ट की कस्टम रचना निर्देश कक्षा (AVMutableVideoCompositionInstruction के अनुरूप) instructions भी सेट कर रहा हूं। इस वर्ग को कुछ अन्य महत्वहीन चर के साथ एक ट्रैक आईडी पारित किया गया है।

दुर्भाग्यवश, मैंने एक समस्या में भाग लिया है - startVideoCompositionRequest: मेरे कस्टम वीडियो कंपोजिटर क्लास (AVVideoCompositing के अनुरूप) में सही ढंग से नहीं कहा जा रहा है।

जब मैं अपने कस्टम निर्देश वर्ग के passthroughTrackID चर को ट्रैक आईडी पर सेट करता हूं, तो startVideoCompositionRequest(request) मेरे AVVideoCompositing में फ़ंक्शन नहीं कहा जाता है।

फिर भी, जब मैं अपने कस्टम अनुदेश वर्ग के passthroughTrackID चर सेट नहीं है, startVideoCompositionRequest(request) कहा जाता है, लेकिन सही ढंग से नहीं - मुद्रण एक खाली सरणी में request.sourceTrackIDs परिणाम है, और request.sourceFrameByTrackID(trackID) परिणाम एक नहीं के बराबर मूल्य में।

कुछ दिलचस्प जो मैंने पाया था कि फिल्टर के साथ वीडियो निर्यात करने का प्रयास करते समय cancelAllPendingVideoCompositionRequests: फ़ंक्शन को हमेशा दो बार बुलाया जाता है। इसे या तो startVideoCompositionRequest: से पहले और एक बार बाद में, या startVideoCompositionRequest: के मामले में पंक्ति में केवल दो बार बुलाया जाता है।

मैंने वीडियो के साथ वीडियो निर्यात करने के लिए तीन कक्षाएं बनाई हैं। यहाँ उपयोगिता वर्ग, जो मूल रूप से सिर्फ एक export समारोह भी शामिल है और आवश्यक कोड के सभी कॉल है

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

यहाँ अन्य दो वर्गों है - मैं इस पोस्ट बनाने के लिए एक कोड ब्लॉक में उन दोनों को डाल देता हूँ कम

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

मैं इस पर मार्गदर्शन के लिए Apple's AVCustomEdit sample project पर बहुत कुछ देख रहा हूं, लेकिन मुझे ऐसा क्यों लगता है कि ऐसा क्यों हो रहा है।

मैं सही ढंग से कॉल करने के लिए request.sourceFrameByTrackID: फ़ंक्शन कैसे प्राप्त कर सकता हूं, और प्रत्येक फ्रेम के लिए वैध CVPixelBuffer प्रदान कर सकता हूं?

उत्तर

6

यह पता चला कस्टम AVVideoCompositionInstruction कक्षा में requiredSourceTrackIDs चर (प्रश्न में VideoFilterCompositionInstruction) युक्त ट्रैक आईडी

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

तो अंतिम कस्टम संगठन शिक्षा वर्ग

है एक सरणी के लिए सेट किया है
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") 
    } 
} 

इस उपयोगिता is also on GitHub

+0

हैलो मैंने अभी आपके नमूना प्रोजेक्ट को जिथब पर आजमाया है लेकिन यह मेरी तरफ काम नहीं करता है। func startRequest (_ asyncVideoCompositionRequest: AVAsynchronousVideoCompositionRequest) AVVideoCompositing – Sam

+0

में नहीं कहा जाता है क्या आपको पता है कि क्या गलत है, मैंने सुझाए गए सबकुछ को लागू किया है। – Sam

+0

क्या आपने स्विफ्ट 3 पर यह कोशिश की है? – Sam

3

के रूप में के लिए कोड के सभी आपने ध्यान दिया है कि passthroughTrackID वह ट्रैक लौटाएं जिसे आप फ़िल्टर करना चाहते हैं, सही दृष्टिकोण नहीं है - आपको ट्रैक को requiredSourceTrackIDs से फ़िल्टर करने के लिए वापस करने की आवश्यकता है। (और ऐसा लगता है कि एक बार ऐसा करने के बाद, इससे कोई फर्क नहीं पड़ता कि आप भी इसे passthroughTrackID से वापस कर दें।) निश्चित रूप से passthroughTrackID और requiredSourceTrackIDs के लिए के शेष सवाल क्यों यह इस तरह से काम करता है ...

डॉक्स उत्तर देने के लिए एप्पल के स्पष्ट लेखन कभी नहीं कर रहे हैं। (File a bug about it और वे सुधार हो सकता है।) लेकिन अगर आप पूर्व के विवरण में बारीकी से देखें, वहाँ एक संकेत (जोर जोड़ा) ...

तो शिक्षा की अवधि के लिए, वीडियो रचना परिणाम एक है स्रोत फ्रेम के, यह संपत्ति संबंधित ट्रैक आईडी देता है। कंपोजिटर निर्देश की अवधि के लिए नहीं चलाया जाएगा और उचित स्रोत फ्रेम का उपयोग इसके बजाय किया जाता है।

तो, आप passthroughTrackIDकेवल का उपयोग करके ऐसी प्रसंस्करण बिना के माध्यम से एक निर्देश वर्ग है कि एक ही ट्रैक गुजरता बना रहे हैं।

यदि आप कोई छवि प्रसंस्करण करने की योजना बना रहे हैं, भले ही यह किसी भी कंपोजिटिंग के साथ एक ट्रैक पर न हो, तो उस ट्रैक को requiredSourceTrackIDs में निर्दिष्ट करें।

+0

हैलो, क्या आप विस्तृत कर सकते हैं। Github परियोजना को swift3 के साथ कैसे काम करें? – Sam

संबंधित मुद्दे