Skip to content

Instantly share code, notes, and snippets.

@plutovman
Created August 16, 2015 00:07
Show Gist options
  • Save plutovman/bc6f2f43794d924def30 to your computer and use it in GitHub Desktop.
Save plutovman/bc6f2f43794d924def30 to your computer and use it in GitHub Desktop.
UIImages to Video Swift files
//
// TimeLapseBuilder.swift
//
// Created by Adam Jensen on 5/10/15.
// Copyright (c) 2015 Adam Jensen. All rights reserved.
//
import AVFoundation
import UIKit
let kErrorDomain = "TimeLapseBuilder"
let kFailedToStartAssetWriterError = 0
let kFailedToAppendPixelBufferError = 1
class TimeLapseBuilder: NSObject {
let photoURLs: [String]
var videoWriter: AVAssetWriter?
init(photoURLs: [String]) {
self.photoURLs = photoURLs
}
func build(progress: (NSProgress -> Void), success: (NSURL -> Void), failure: (NSError -> Void)) {
let inputSize = CGSize(width: 4000, height: 3000)
let outputSize = CGSize(width: 1280, height: 720)
var error: NSError?
let documentsPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as! NSString
let videoOutputURL = NSURL(fileURLWithPath: documentsPath.stringByAppendingPathComponent("AssembledVideo.mov"))!
NSFileManager.defaultManager().removeItemAtURL(videoOutputURL, error: nil)
videoWriter = AVAssetWriter(URL: videoOutputURL, fileType: AVFileTypeQuickTimeMovie, error: &error)
if let videoWriter = videoWriter {
let videoSettings: [NSObject : AnyObject] = [
AVVideoCodecKey : AVVideoCodecH264,
AVVideoWidthKey : outputSize.width,
AVVideoHeightKey : outputSize.height,
// AVVideoCompressionPropertiesKey : [
// AVVideoAverageBitRateKey : NSInteger(1000000),
// AVVideoMaxKeyFrameIntervalKey : NSInteger(16),
// AVVideoProfileLevelKey : AVVideoProfileLevelH264BaselineAutoLevel
// ]
]
let videoWriterInput = AVAssetWriterInput(mediaType: AVMediaTypeVideo, outputSettings: videoSettings)
let pixelBufferAdaptor = AVAssetWriterInputPixelBufferAdaptor(
assetWriterInput: videoWriterInput,
sourcePixelBufferAttributes: [
kCVPixelBufferPixelFormatTypeKey : kCVPixelFormatType_32ARGB,
kCVPixelBufferWidthKey : inputSize.width,
kCVPixelBufferHeightKey : inputSize.height,
]
)
assert(videoWriter.canAddInput(videoWriterInput))
videoWriter.addInput(videoWriterInput)
if videoWriter.startWriting() {
videoWriter.startSessionAtSourceTime(kCMTimeZero)
assert(pixelBufferAdaptor.pixelBufferPool != nil)
let media_queue = dispatch_queue_create("mediaInputQueue", nil)
videoWriterInput.requestMediaDataWhenReadyOnQueue(media_queue, usingBlock: { () -> Void in
let fps: Int32 = 30
let frameDuration = CMTimeMake(1, fps)
let currentProgress = NSProgress(totalUnitCount: Int64(self.photoURLs.count))
var frameCount: Int64 = 0
var remainingPhotoURLs = [String](self.photoURLs)
while (videoWriterInput.readyForMoreMediaData && !remainingPhotoURLs.isEmpty) {
let nextPhotoURL = remainingPhotoURLs.removeAtIndex(0)
let lastFrameTime = CMTimeMake(frameCount, fps)
let presentationTime = frameCount == 0 ? lastFrameTime : CMTimeAdd(lastFrameTime, frameDuration)
if !self.appendPixelBufferForImageAtURL(nextPhotoURL, pixelBufferAdaptor: pixelBufferAdaptor, presentationTime: presentationTime) {
error = NSError(
domain: kErrorDomain,
code: kFailedToAppendPixelBufferError,
userInfo: [
"description": "AVAssetWriterInputPixelBufferAdapter failed to append pixel buffer",
"rawError": videoWriter.error ?? "(none)"
]
)
break
}
frameCount++
currentProgress.completedUnitCount = frameCount
progress(currentProgress)
}
videoWriterInput.markAsFinished()
videoWriter.finishWritingWithCompletionHandler { () -> Void in
if error == nil {
success(videoOutputURL)
}
}
})
} else {
error = NSError(
domain: kErrorDomain,
code: kFailedToStartAssetWriterError,
userInfo: ["description": "AVAssetWriter failed to start writing"]
)
}
}
if let error = error {
failure(error)
}
}
func appendPixelBufferForImageAtURL(url: String, pixelBufferAdaptor: AVAssetWriterInputPixelBufferAdaptor, presentationTime: CMTime) -> Bool {
var appendSucceeded = true
autoreleasepool {
if let url = NSURL(string: url),
let imageData = NSData(contentsOfURL: url),
let image = UIImage(data: imageData) {
var pixelBuffer: Unmanaged<CVPixelBuffer>?
let status: CVReturn = CVPixelBufferPoolCreatePixelBuffer(
kCFAllocatorDefault,
pixelBufferAdaptor.pixelBufferPool,
&pixelBuffer
)
if let pixelBuffer = pixelBuffer where status == 0 {
let managedPixelBuffer = pixelBuffer.takeRetainedValue()
fillPixelBufferFromImage(image, pixelBuffer: managedPixelBuffer)
appendSucceeded = pixelBufferAdaptor.appendPixelBuffer(
managedPixelBuffer,
withPresentationTime: presentationTime
)
} else {
NSLog("error: Failed to allocate pixel buffer from pool")
}
}
}
return appendSucceeded
}
func fillPixelBufferFromImage(image: UIImage, pixelBuffer: CVPixelBufferRef) {
let imageData = CGDataProviderCopyData(CGImageGetDataProvider(image.CGImage))
let lockStatus = CVPixelBufferLockBaseAddress(pixelBuffer, 0)
let pixelData = CVPixelBufferGetBaseAddress(pixelBuffer)
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.PremultipliedFirst.rawValue)
let rgbColorSpace = CGColorSpaceCreateDeviceRGB()
let context = CGBitmapContextCreate(
pixelData,
Int(image.size.width),
Int(image.size.height),
8,
Int(4 * image.size.width),
rgbColorSpace,
bitmapInfo
)
CGContextDrawImage(context, CGRectMake(0, 0, image.size.width, image.size.height), image.CGImage)
CVPixelBufferUnlockBaseAddress(pixelBuffer, 0)
}
}
//
// ViewController.swift
// Mod of Adam Jensen's movie creator code
import UIKit
import AVFoundation
class ViewController: UIViewController {
@IBOutlet weak var previewView: UIView!
@IBOutlet weak var capturedImage: UIImageView!
@IBAction func PlayImageSequence(sender: UIButton) {
toggleAnimationSequence()
}
@IBAction func EncodeVideoFromImageSequence(sender: UIButton) {
println("...placeholder for encode")
encodeVideo()
}
@IBOutlet weak var ImageSequenceView: UIImageView!
var captureSession: AVCaptureSession?
var stillImageOutput: AVCaptureStillImageOutput?
var previewLayer: AVCaptureVideoPreviewLayer?
var imageSequenceList = [UIImage]()
var rawImageList = [String]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let formatter = NSNumberFormatter()
formatter.minimumIntegerDigits = 4
for i in 0 ... 535 {
let imageName = ("scenes." + formatter.stringFromNumber(i)! + ".jpg")
println (imageName)
imageSequenceList.append(UIImage(named: imageName)!)
rawImageList.append(imageName)
}
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
captureSession = AVCaptureSession()
captureSession!.sessionPreset = AVCaptureSessionPresetPhoto
var backCamera = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo)
var error: NSError?
var input = AVCaptureDeviceInput(device: backCamera, error: &error)
if error == nil && captureSession!.canAddInput(input) {
captureSession!.addInput(input)
stillImageOutput = AVCaptureStillImageOutput()
stillImageOutput!.outputSettings = [AVVideoCodecKey: AVVideoCodecJPEG]
if captureSession!.canAddOutput(stillImageOutput) {
captureSession!.addOutput(stillImageOutput)
previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
previewLayer!.videoGravity = AVLayerVideoGravityResizeAspect
previewLayer!.connection?.videoOrientation = AVCaptureVideoOrientation.Portrait
previewView.layer.addSublayer(previewLayer)
captureSession!.startRunning()
}
}
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
previewLayer!.frame = previewView.bounds
}
@IBAction func didPressTakePhoto(sender: UIButton) {
if let videoConnection = stillImageOutput!.connectionWithMediaType(AVMediaTypeVideo) {
videoConnection.videoOrientation = AVCaptureVideoOrientation.Portrait
stillImageOutput?.captureStillImageAsynchronouslyFromConnection(videoConnection, completionHandler: {(sampleBuffer, error) in
if (sampleBuffer != nil) {
var imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(sampleBuffer)
var dataProvider = CGDataProviderCreateWithCFData(imageData)
var cgImageRef = CGImageCreateWithJPEGDataProvider(dataProvider, nil, true, kCGRenderingIntentDefault)
var image = UIImage(CGImage: cgImageRef, scale: 1.0, orientation: UIImageOrientation.Right)
self.capturedImage.image = image
}
})
}
}
@IBAction func didTakeScreenGrab(sender: AnyObject) {
let screenshot = self.view?.pb_takeSnapshot()
UIImageWriteToSavedPhotosAlbum(screenshot, nil, nil, nil)
}
func toggleAnimationSequence(){
if (!ImageSequenceView.isAnimating()){
ImageSequenceView.animationImages = imageSequenceList
ImageSequenceView.startAnimating()
} else {
ImageSequenceView.stopAnimating()
}
}
func encodeVideo(){
//func build(progress: (NSProgress -> Void), success: (NSURL -> Void), failure: (NSError -> Void)) {
let createVideo = TimeLapseBuilder(photoURLs: rawImageList)
createVideo.build(
{ (progress: NSProgress) in
NSLog("Progress: \(progress.completedUnitCount) / \(progress.totalUnitCount)")
},
success: { url in
NSLog("Output written to \(url)")
},
failure: { error in
NSLog("failure: \(error)")
}
)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment