MTTransitions
MTTransitions copied to clipboard
Cannot Provide Dynamic duration to each image.
Hi , i am using this library and its way too good but i am stuck when trying to add dynamic duration to each image in array for example there are 6 images and adding duration to each image like [0.0,4.0,2.0,0.5,5.0,6.0]. it only works fine when i am adding static duration for all images like [2.0], then all images duration will be 2.0.
Pleas help me to provide flexible duration for each image while creating video of images.
Below is the Error:-
Error Domain=AVFoundationErrorDomain Code=-11800 "The operation could not be completed" UserInfo={NSLocalizedFailureReason=An unknown error occurred (-16364), NSLocalizedDescription=The operation could not be completed, NSUnderlyingError=0x28023c5a0 {Error Domain=NSOSStatusErrorDomain Code=-16364 ["(null)"}}``](url
)
I think you can create extension of MTMovieMaker
and pass an array of transition durations. The count of the array should equal to effects.
There are two duration:
- frameDuration
- transitionDuration
Something like following (not tested, do not know if this works)
extension MTMovieMaker {
public func createVideo(with images: [MTIImage],
effects: [MTTransition.Effect],
frameDurations: [TimeInterval] = [1],
transitionDuration: TimeInterval = 0.8,
audioURL: URL? = nil,
completion: MTMovieMakerCompletion? = nil) throws {
guard images.count >= 2 else {
throw MTMovieMakerError.imagesMustMoreThanTwo
}
guard effects.count == images.count - 1 else {
throw MTMovieMakerError.imagesAndEffectsDoesNotMatch
}
guard frameDurations.count == effects.count else {
throw MTMovieMakerError.imagesAndEffectsDoesNotMatch
}
if FileManager.default.fileExists(atPath: outputURL.path) {
try FileManager.default.removeItem(at: outputURL)
}
writer = try AVAssetWriter(outputURL: outputURL, fileType: .mp4)
let outputSize = images.first!.size
let videoSettings: [String: Any] = [
AVVideoCodecKey: AVVideoCodecH264,
AVVideoWidthKey: outputSize.width,
AVVideoHeightKey: outputSize.height
]
let writerInput = AVAssetWriterInput(mediaType: .video, outputSettings: videoSettings)
let attributes = sourceBufferAttributes(outputSize: outputSize)
let pixelBufferAdaptor = AVAssetWriterInputPixelBufferAdaptor(assetWriterInput: writerInput,
sourcePixelBufferAttributes: attributes)
writer?.add(writerInput)
guard let success = writer?.startWriting(), success == true else {
fatalError("Can not start writing")
}
guard let pixelBufferPool = pixelBufferAdaptor.pixelBufferPool else {
fatalError("AVAssetWriterInputPixelBufferAdaptor pixelBufferPool empty")
}
self.writer?.startSession(atSourceTime: .zero)
writerInput.requestMediaDataWhenReady(on: self.writingQueue) {
var index = 0
while index < (images.count - 1) {
let frameDuration = frameDurations[index]
var presentTime = CMTimeMake(value: Int64(frameDuration * Double(index) * 1000), timescale: 1000)
let transition = effects[index].transition
transition.inputImage = images[index]
transition.destImage = images[index + 1]
transition.duration = transitionDuration
let frameBeginTime = presentTime
let frameCount = 29
for counter in 0 ... frameCount {
autoreleasepool {
while !writerInput.isReadyForMoreMediaData {
Thread.sleep(forTimeInterval: 0.01)
}
let progress = Float(counter) / Float(frameCount)
transition.progress = progress
let frameTime = CMTimeMake(value: Int64(transitionDuration * Double(progress) * 1000), timescale: 1000)
presentTime = CMTimeAdd(frameBeginTime, frameTime)
var pixelBuffer: CVPixelBuffer?
CVPixelBufferPoolCreatePixelBuffer(kCFAllocatorDefault, pixelBufferPool, &pixelBuffer)
if let buffer = pixelBuffer, let frame = transition.outputImage {
try? MTTransition.context?.render(frame, to: buffer)
pixelBufferAdaptor.append(buffer, withPresentationTime: presentTime)
}
}
}
index += 1
}
writerInput.markAsFinished()
self.writer?.finishWriting {
if let audioURL = audioURL, self.writer?.error == nil {
do {
let audioAsset = AVAsset(url: audioURL)
let videoAsset = AVAsset(url: self.outputURL)
try self.mixAudio(audioAsset, video: videoAsset, completion: completion)
} catch {
completion?(.failure(error))
}
} else {
DispatchQueue.main.async {
if let error = self.writer?.error {
completion?(.failure(error))
} else {
completion?(.success(self.outputURL))
}
}
}
}
}
}
}