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?
Merhaba Örnek projenizi github üzerinde denedim, ancak benim tarafımda çalışmaz. func startRequest (_ asyncVideoCompositionRequest: AVAsynchronousVideoCompositionRequest), AVVideoCompositing'de ( – Sam
) çağrılmadı. Yanlış olanın ne olduğunu biliyor musunuz, önerilen her şeyi uygularım. – Sam
Bunu Swift 3'te denediniz mi? – Sam