Advertisement
Guest User

video creation 1

a guest
Jun 17th, 2019
72
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Swift 10.03 KB | None | 0 0
  1. import Foundation
  2. import AVFoundation
  3. import UIKit
  4. import Photos
  5.  
  6. struct RenderSettings {
  7.    
  8.     var width: CGFloat = 360
  9.     var height: CGFloat = 640
  10.     var fps: Int32 = 30   // frames per second
  11.     var avCodecKey = AVVideoCodecType.h264
  12.     var videoFilename = "part1"
  13.     var videoFilenameExt = "mp4"
  14.    
  15.     var size: CGSize {
  16.         return CGSize(width: width, height: height)
  17.     }
  18.    
  19.     var outputURL: NSURL {
  20.         // Use the CachesDirectory so the rendered video file sticks around as long as we need it to.
  21.         // Using the CachesDirectory ensures the file won't be included in a backup of the app.
  22.         let fileManager = FileManager.default
  23.         if let tmpDirURL = try? fileManager.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: true) {
  24.             return tmpDirURL.appendingPathComponent(videoFilename).appendingPathExtension(videoFilenameExt) as NSURL
  25.         }
  26.         fatalError("URLForDirectory() failed")
  27.     }
  28. }
  29.  
  30. class ImageAnimator {
  31.    
  32.     // Apple suggests a timescale of 600 because it's a multiple of standard video rates 24, 25, 30, 60 fps etc.
  33.     static let kTimescale: Int32 = 600
  34.    
  35.     let settings: RenderSettings
  36.     let videoWriter: VideoWriter
  37.     var images: [UIImage]!
  38.    
  39.     var frameNum = 0
  40.    
  41.     class func saveToLibrary(videoURL: NSURL) {
  42.         PHPhotoLibrary.requestAuthorization { status in
  43.             guard status == .authorized else { return }
  44.            
  45.             PHPhotoLibrary.shared().performChanges({
  46.                 PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: videoURL as URL)
  47.             }) { success, error in
  48.                 if !success {
  49.                     print("Could not save video to photo library:", error as Any)
  50.                 }
  51.             }
  52.         }
  53.     }
  54.    
  55.     class func removeFileAtURL(fileURL: NSURL) {
  56.         do {
  57.             try FileManager.default.removeItem(atPath: fileURL.path!)
  58.         }
  59.         catch _ as NSError {
  60.             // Assume file doesn't exist.
  61.         }
  62.     }
  63.    
  64.     init(renderSettings: RenderSettings) {
  65.         settings = renderSettings
  66.         videoWriter = VideoWriter(renderSettings: settings)
  67.         images = ViewController.uiImages // my array of images
  68.     }
  69.    
  70.     func render(completion: @escaping ()->Void) {
  71.        
  72.         // The VideoWriter will fail if a file exists at the URL, so clear it out first.
  73.         ImageAnimator.removeFileAtURL(fileURL: settings.outputURL)
  74.        
  75.         videoWriter.start()
  76.         videoWriter.render(appendPixelBuffers: appendPixelBuffers) {
  77.             //ImageAnimator.saveToLibrary(videoURL: self.settings.outputURL)
  78.             ViewController.finalUrl = self.settings.outputURL as URL
  79.             completion()
  80.         }
  81.        
  82.     }
  83.    
  84.    
  85.     // This is the callback function for VideoWriter.render()
  86.     func appendPixelBuffers(writer: VideoWriter) -> Bool {
  87.        
  88.         let frameDuration = CMTimeMake(value: Int64(ImageAnimator.kTimescale / settings.fps), timescale: ImageAnimator.kTimescale)
  89.        
  90.         while !images.isEmpty {
  91.            
  92.             if writer.isReadyForData == false {
  93.                 // Inform writer we have more buffers to write.
  94.                 return false
  95.             }
  96.            
  97.             let image = images.removeFirst()
  98.             let presentationTime = CMTimeMultiply(frameDuration, multiplier: Int32(frameNum))
  99.             let success = videoWriter.addImage(image: image, withPresentationTime: presentationTime)
  100.             if success == false {
  101.                 fatalError("addImage() failed")
  102.             }
  103.            
  104.             let newNum = frameNum + 1
  105.             frameNum = newNum
  106.         }
  107.        
  108.         // Inform writer all buffers have been written.
  109.         return true
  110.     }
  111.    
  112. }
  113.  
  114. class VideoWriter {
  115.    
  116.     let renderSettings: RenderSettings
  117.    
  118.     var videoWriter: AVAssetWriter!
  119.     var videoWriterInput: AVAssetWriterInput!
  120.     var pixelBufferAdaptor: AVAssetWriterInputPixelBufferAdaptor!
  121.    
  122.     var isReadyForData: Bool {
  123.         return videoWriterInput?.isReadyForMoreMediaData ?? false
  124.     }
  125.    
  126.     class func pixelBufferFromImage(image: UIImage, pixelBufferPool: CVPixelBufferPool, size: CGSize) -> CVPixelBuffer {
  127.        
  128.         var pixelBufferOut: CVPixelBuffer?
  129.        
  130.         let status = CVPixelBufferPoolCreatePixelBuffer(kCFAllocatorDefault, pixelBufferPool, &pixelBufferOut)
  131.         if status != kCVReturnSuccess {
  132.             fatalError("CVPixelBufferPoolCreatePixelBuffer() failed")
  133.         }
  134.        
  135.         let pixelBuffer = pixelBufferOut!
  136.        
  137.         CVPixelBufferLockBaseAddress(pixelBuffer, [])
  138.        
  139.         let data = CVPixelBufferGetBaseAddress(pixelBuffer)
  140.         let rgbColorSpace = CGColorSpaceCreateDeviceRGB()
  141.         let context = CGContext(data: data, width: Int(size.width), height: Int(size.height),
  142.                                 bitsPerComponent: 8, bytesPerRow: CVPixelBufferGetBytesPerRow(pixelBuffer), space: rgbColorSpace, bitmapInfo: CGImageAlphaInfo.premultipliedFirst.rawValue)
  143.        
  144.         if let cnt = context {
  145.             cnt.clear(CGRect(x: 0, y: 0, width: size.width, height: size.height))
  146.         }
  147.        
  148.        
  149.        
  150.         let horizontalRatio = size.width / image.size.width
  151.         let verticalRatio = size.height / image.size.height
  152.         //aspectRatio = max(horizontalRatio, verticalRatio) // ScaleAspectFill
  153.         let aspectRatio = min(horizontalRatio, verticalRatio) // ScaleAspectFit
  154.        
  155.         let newSize = CGSize(width: image.size.width * aspectRatio, height: image.size.height * aspectRatio)
  156.        
  157.         let x = newSize.width < size.width ? (size.width - newSize.width) / 2 : 0
  158.         let y = newSize.height < size.height ? (size.height - newSize.height) / 2 : 0
  159.        
  160.         //CGContextDrawImage(context, CGRect(x: x, y: y, width: newSize.width, height: newSize.height), image.cgImage)
  161.    
  162.         if let cnt = context {
  163.             if let img = image.cgImage {
  164.                 cnt.draw(img, in: CGRect(x: x, y: y, width: newSize.width, height: newSize.height))
  165.             }
  166.         }
  167.        
  168.         //context.draw(image.cgImage, in: CGRect(x: x, y: y, width: newSize.width, height: newSize.height))
  169.        
  170.         CVPixelBufferUnlockBaseAddress(pixelBuffer, [])
  171.        
  172.         return pixelBuffer
  173.     }
  174.    
  175.     init(renderSettings: RenderSettings) {
  176.         self.renderSettings = renderSettings
  177.     }
  178.    
  179.     func start() {
  180.        
  181.         let avOutputSettings: [String: AnyObject] = [
  182.             AVVideoCodecKey: renderSettings.avCodecKey as AnyObject,
  183.             AVVideoWidthKey: NSNumber(value: Float(renderSettings.width)),
  184.             AVVideoHeightKey: NSNumber(value: Float(renderSettings.height))
  185.         ]
  186.        
  187.         func createPixelBufferAdaptor() {
  188.             let sourcePixelBufferAttributesDictionary = [
  189.                 kCVPixelBufferPixelFormatTypeKey as String: NSNumber(value: kCVPixelFormatType_32ARGB),
  190.                 kCVPixelBufferWidthKey as String: NSNumber(value: Float(renderSettings.width)),
  191.                 kCVPixelBufferHeightKey as String: NSNumber(value: Float(renderSettings.height))
  192.             ]
  193.             pixelBufferAdaptor = AVAssetWriterInputPixelBufferAdaptor(assetWriterInput: videoWriterInput,
  194.                                                                       sourcePixelBufferAttributes: sourcePixelBufferAttributesDictionary)
  195.         }
  196.        
  197.         func createAssetWriter(outputURL: NSURL) -> AVAssetWriter {
  198.             guard let assetWriter = try? AVAssetWriter(outputURL: outputURL as URL, fileType: AVFileType.mp4) else {
  199.                 fatalError("AVAssetWriter() failed")
  200.             }
  201.            
  202.             guard assetWriter.canApply(outputSettings: avOutputSettings, forMediaType: AVMediaType.video) else {
  203.                 fatalError("canApplyOutputSettings() failed")
  204.             }
  205.            
  206.             return assetWriter
  207.         }
  208.        
  209.         videoWriter = createAssetWriter(outputURL: renderSettings.outputURL)
  210.         videoWriterInput = AVAssetWriterInput(mediaType: AVMediaType.video, outputSettings: avOutputSettings)
  211.        
  212.         if videoWriter.canAdd(videoWriterInput) {
  213.             videoWriter.add(videoWriterInput)
  214.         }
  215.         else {
  216.             fatalError("canAddInput() returned false")
  217.         }
  218.        
  219.         // The pixel buffer adaptor must be created before we start writing.
  220.         createPixelBufferAdaptor()
  221.        
  222.         if videoWriter.startWriting() == false {
  223.             fatalError("startWriting() failed")
  224.         }
  225.        
  226.         videoWriter.startSession(atSourceTime: CMTime.zero)
  227.        
  228.         precondition(pixelBufferAdaptor.pixelBufferPool != nil, "nil pixelBufferPool")
  229.     }
  230.    
  231.     func render(appendPixelBuffers: @escaping (VideoWriter)->Bool, completion: @escaping ()->Void) {
  232.        
  233.         precondition(videoWriter != nil, "Call start() to initialze the writer")
  234.        
  235.         let queue = DispatchQueue(label: "mediaInputQueue")
  236.         videoWriterInput.requestMediaDataWhenReady(on: queue) {
  237.             let isFinished = appendPixelBuffers(self)
  238.             if isFinished {
  239.                 self.videoWriterInput.markAsFinished()
  240.                 self.videoWriter.finishWriting() {
  241.                     DispatchQueue.main.async {
  242.                        completion()
  243.                     }
  244.                 }
  245.             }
  246.             else {
  247.                 // Fall through. The closure will be called again when the writer is ready.
  248.             }
  249.         }
  250.     }
  251.    
  252.     func addImage(image: UIImage, withPresentationTime presentationTime: CMTime) -> Bool {
  253.        
  254.         precondition(pixelBufferAdaptor != nil, "Call start() to initialze the writer")
  255.        
  256.         let pixelBuffer = VideoWriter.pixelBufferFromImage(image: image, pixelBufferPool: pixelBufferAdaptor.pixelBufferPool!, size: renderSettings.size)
  257.         return pixelBufferAdaptor.append(pixelBuffer, withPresentationTime: presentationTime)
  258.     }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement