AVAsset에 CIFilter를 적용한 다음 필터를 적용한 상태로 저장하려고합니다. 내가이 작업을 수행하는 방법은 AVVideoCompositing
클래스의 객체로 설정된 videoComposition
으로 AVAssetExportSession
을 사용하는 것입니다.사용자 지정 AVVideoCompositing 클래스가 예상대로 작동하지 않습니다.
내 AVMutableVideoComposition
개체는 사용자 지정 합성 명령 클래스 (AVMutableVideoCompositionInstruction
준수)로 설정됩니다. 이 클래스에는 몇 가지 중요하지 않은 변수와 함께 트랙 ID가 전달됩니다.
불행히도 문제가 생겼습니다. 맞춤 비디오 컴포저 클래스 (AVVideoCompositing
준수)의 startVideoCompositionRequest:
함수가 올바르게 호출되지 않았습니다.
사용자 정의 명령어 클래스의 passthroughTrackID
변수를 트랙 ID로 설정하면 AVVideoCompositing
에있는 startVideoCompositionRequest(request)
함수가 호출되지 않습니다.
그러나, 나는 내 사용자 지정 명령 클래스의 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")
}
}
}
나는 이것에 관해서 많은 조언을 해왔다. 그러나 나는 왜 이런 일이 일어나고 있는지 발견 할 수 없다.
request.sourceFrameByTrackID:
함수를 올바르게 호출하여 각 프레임에 대해 유효한 CVPixelBuffer
을 제공하려면 어떻게해야합니까?
안녕하세요. github에서 샘플 프로젝트를 시도했지만 제 쪽에서 작동하지 않습니다. func startRequest (_ asyncVideoCompositionRequest : AVAsynchronousVideoCompositionRequest)가 AVVideoCompositing에서 호출되지 않습니다. – Sam
잘못된 점을 알고 있습니까? 제안 된대로 모든 것을 구현했습니다. – Sam
스위프트 3에서이 기능을 사용해 보셨습니까? – Sam