I am working with Video, I create video from array of Uimage. Its works fine with low quantity of images, but with big quantity (ex: 100+ images) it crash because of memory Issue.
I have tested when video is creating, memory is growing big but after the process completed memory is still size, not release.
Any help will be appreciated thanks
here is my code:
func buildVideoFromImageArray(completed:@escaping (String)->Void) {
selectedPhotosArray = arrimageVideo
imageArrayToVideoURL = NSURL(fileURLWithPath: NSHomeDirectory() + "/Documents/video1.MP4")
removeFileAtURLIfExists(url: imageArrayToVideoURL)
guard let videoWriter = try? AVAssetWriter(outputURL: imageArrayToVideoURL as URL, fileType: AVFileType.mp4) else {
fatalError("AVAssetWriter error")
}
let outputSettings = [AVVideoCodecKey : AVVideoCodecH264, AVVideoWidthKey : NSNumber(value: Float(outputSize.width)), AVVideoHeightKey : NSNumber(value: Float(outputSize.height))] as [String : Any]
guard videoWriter.canApply(outputSettings: outputSettings, forMediaType: AVMediaType.video) else {
fatalError("Negative : Can't apply the Output settings...")
}
let videoWriterInput = AVAssetWriterInput(mediaType: AVMediaType.video, outputSettings: outputSettings)
let sourcePixelBufferAttributesDictionary = [kCVPixelBufferPixelFormatTypeKey as String : NSNumber(value: kCVPixelFormatType_32ARGB), kCVPixelBufferWidthKey as String: NSNumber(value: Float(outputSize.width)), kCVPixelBufferHeightKey as String: NSNumber(value: Float(outputSize.height))]
let pixelBufferAdaptor = AVAssetWriterInputPixelBufferAdaptor(assetWriterInput: videoWriterInput, sourcePixelBufferAttributes: sourcePixelBufferAttributesDictionary)
if videoWriter.canAdd(videoWriterInput) {
videoWriter.add(videoWriterInput)
}
if videoWriter.startWriting() {
videoWriter.startSession(atSourceTime: kCMTimeZero)
assert(pixelBufferAdaptor.pixelBufferPool != nil)
let media_queue = DispatchQueue(label: "mediaInputQueue")
videoWriterInput.requestMediaDataWhenReady(on: media_queue, using: { () -> Void in
let fps: Int32 = 1000
var frameCount: Int64 = 0
var appendSucceeded = true
var lastTimeVl :Int64 = 0
var framePerSecond: Int64 = Int64(0)
for nextDicData in self.selectedPhotosArray{
if (videoWriterInput.isReadyForMoreMediaData) {
if let nextImage = nextDicData["img"] as? UIImage
{
var frameDuration = CMTimeMake(Int64(0), fps)
if let timeVl = nextDicData["time"] as? Float{
framePerSecond = Int64(timeVl * 1000)
print("TIME FRAME : \(timeVl)")
}else{
framePerSecond = Int64(0.1 * 1000)
}
frameDuration = CMTimeMake(framePerSecond ,fps)
let lastFrameTime = CMTimeMake(Int64(lastTimeVl), fps)
let presentationTime = lastFrameTime
if !self.appendPixelBufferForImage(image: nextImage, pixelBufferAdaptor: pixelBufferAdaptor, presentationTime: presentationTime){
print("AVAssetWriterInputPixelBufferAdapter failed to append pixel buffer")
break
}
if let index = self.selectedPhotosArray.firstIndex(where: { NSDictionary(dictionary: $0).isEqual(to: nextDicData)}){
self.selectedPhotosArray.remove(at: index)
}
}
}else{
//not ready
print("write is Not Ready: \(lastTimeVl)")
}
if !appendSucceeded {
break
}
frameCount += 1
lastTimeVl += framePerSecond
usleep(100000)
}
videoWriterInput.markAsFinished()
videoWriter.endSession(atSourceTime: CMTimeMake(lastTimeVl, fps))
videoWriter.finishWriting { () -> Void in
print("-----video1 url = \(self.imageArrayToVideoURL)")
self.urlString = "\(self.imageArrayToVideoURL)"
self.asset = AVAsset(url: self.imageArrayToVideoURL as URL)
DispatchQueue.main.async {
//code that caused error goes here
completed(self.urlString)
self.setupPlayerVideoWithUrl(strUrl: self.urlString)
}
}
})
}
}
//create pixel Buffer
func appendPixelBufferForImage(image: UIImage, pixelBufferAdaptor: AVAssetWriterInputPixelBufferAdaptor, presentationTime: CMTime) -> Bool {
var appendSucceeded = false
autoreleasepool {
if let pixelBufferPool = pixelBufferAdaptor.pixelBufferPool {
let pixelBufferPointer = UnsafeMutablePointer<CVPixelBuffer?>.allocate(capacity: 1)
let status: CVReturn = CVPixelBufferPoolCreatePixelBuffer(
kCFAllocatorDefault,
pixelBufferPool,
pixelBufferPointer
)
if let pixelBuffer = pixelBufferPointer.pointee, status == 0 {
fillPixelBufferFromImage(image, pixelBuffer: pixelBuffer)
appendSucceeded = pixelBufferAdaptor.append(
pixelBuffer,
withPresentationTime: presentationTime
)
pixelBufferPointer.deinitialize(count: 1)
} else {
NSLog("error: Failed to allocate pixel buffer from pool")
}
pixelBufferPointer.deallocate()
}
}
return appendSucceeded
}
func fillPixelBufferFromImage(_ image: UIImage, pixelBuffer: CVPixelBuffer) {
CVPixelBufferLockBaseAddress(pixelBuffer, CVPixelBufferLockFlags(rawValue: 0))
let pixelData = CVPixelBufferGetBaseAddress(pixelBuffer)
let rgbColorSpace = CGColorSpaceCreateDeviceRGB()
let context = CGContext(
data: pixelData,
width: Int(image.size.width),
height: Int(image.size.height),
bitsPerComponent: 8,
bytesPerRow: CVPixelBufferGetBytesPerRow(pixelBuffer),
space: rgbColorSpace,
bitmapInfo: CGImageAlphaInfo.premultipliedFirst.rawValue
)
context!.clear(CGRect(x: 0, y: 0, width: CGFloat(self.outputSize.width), height: CGFloat(self.outputSize.height)))
context?.draw(image.cgImage!, in: CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height))
CVPixelBufferUnlockBaseAddress(pixelBuffer, CVPixelBufferLockFlags(rawValue: 0))
}