मैं एक एवीएसेट में एक सीआईएफलेटर लागू करने का प्रयास कर रहा हूं, और उसके बाद इसे फ़िल्टर के साथ सहेजता हूं। जिस तरह से मैं यह कर रहा हूं AVAssetExportSession
videoComposition
AVMutableVideoComposition
ऑब्जेक्ट पर कस्टम 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
प्रदान कर सकता हूं?
हैलो मैंने अभी आपके नमूना प्रोजेक्ट को जिथब पर आजमाया है लेकिन यह मेरी तरफ काम नहीं करता है। func startRequest (_ asyncVideoCompositionRequest: AVAsynchronousVideoCompositionRequest) AVVideoCompositing – Sam
में नहीं कहा जाता है क्या आपको पता है कि क्या गलत है, मैंने सुझाए गए सबकुछ को लागू किया है। – Sam
क्या आपने स्विफ्ट 3 पर यह कोशिश की है? – Sam