Created
April 12, 2018 23:51
-
-
Save uruly/ea31e44db35a4b2575d2be01f4dc86e4 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
import AVFoundation | |
import UIKit | |
class MovieCreator: NSObject { | |
func create(images:[UIImage],size:CGSize,time:Int,completion:(URL)->()){ | |
//保存先のURL | |
guard let url = NSURL(fileURLWithPath:NSTemporaryDirectory()).appendingPathComponent("\(NSUUID().uuidString).mp4") else { | |
fatalError("URL error") | |
} | |
// AVAssetWriter | |
guard let videoWriter = try? AVAssetWriter(outputURL: url, fileType: .mp4) else { | |
fatalError("AVAssetWriter error") | |
} | |
//画像サイズを変える | |
let width = size.width | |
let height = size.height | |
// AVAssetWriterInput | |
let outputSettings = [ | |
AVVideoCodecKey: AVVideoCodecH264, | |
AVVideoWidthKey: width, | |
AVVideoHeightKey: height | |
] as [String : Any] | |
let writerInput = AVAssetWriterInput(mediaType: AVMediaType.video, outputSettings: outputSettings as [String : AnyObject]) | |
videoWriter.add(writerInput) | |
// AVAssetWriterInputPixelBufferAdaptor | |
let adaptor = AVAssetWriterInputPixelBufferAdaptor( | |
assetWriterInput: writerInput, | |
sourcePixelBufferAttributes: [ | |
kCVPixelBufferPixelFormatTypeKey as String: Int(kCVPixelFormatType_32ARGB), | |
kCVPixelBufferWidthKey as String: width, | |
kCVPixelBufferHeightKey as String: height, | |
] | |
) | |
writerInput.expectsMediaDataInRealTime = true | |
// 動画の生成開始 | |
// 生成できるか確認 | |
if (!videoWriter.startWriting()) { | |
// error | |
print("error videoWriter startWriting") | |
} | |
// 動画生成開始 | |
videoWriter.startSession(atSourceTime: kCMTimeZero) | |
// pixel bufferを宣言 | |
var buffer: CVPixelBuffer? = nil | |
// 現在のフレームカウント | |
var frameCount = 0 | |
// 各画像の表示する時間 | |
let durationForEachImage = time | |
// FPS | |
let fps: __int32_t = 60 | |
// 全画像をbufferに埋め込む | |
for image in images { | |
if (!adaptor.assetWriterInput.isReadyForMoreMediaData) { | |
break | |
} | |
// 動画の時間を生成(その画像の表示する時間/開始時点と表示時間を渡す) | |
let frameTime: CMTime = CMTimeMake(Int64(__int32_t(frameCount) * __int32_t(durationForEachImage)), fps) | |
//時間経過を確認(確認用) | |
let second = CMTimeGetSeconds(frameTime) | |
print(second) | |
guard let resize = image.resize(size: size) else { return } | |
buffer = self.pixelBufferFromCGImage(cgImage: resize.cgImage!) | |
// 生成したBufferを追加 | |
if (!adaptor.append(buffer!, withPresentationTime: frameTime)) { | |
// Error! | |
print("adaptError") | |
print(videoWriter.error!) | |
} | |
frameCount += 1 | |
} | |
// 動画生成終了 | |
writerInput.markAsFinished() | |
videoWriter.endSession(atSourceTime: CMTimeMake(Int64((__int32_t(frameCount)) * __int32_t(durationForEachImage)), fps)) | |
videoWriter.finishWriting(completionHandler: { | |
// Finish! | |
print("movie created.") | |
}) | |
completion(url) | |
} | |
func pixelBufferFromCGImage(cgImage: CGImage) -> CVPixelBuffer { | |
let options = [ | |
kCVPixelBufferCGImageCompatibilityKey as String: true, | |
kCVPixelBufferCGBitmapContextCompatibilityKey as String: true | |
] | |
var pxBuffer: CVPixelBuffer? = nil | |
let width = cgImage.width | |
let height = cgImage.height | |
CVPixelBufferCreate(kCFAllocatorDefault, | |
width, | |
height, | |
kCVPixelFormatType_32ARGB, | |
options as CFDictionary?, | |
&pxBuffer) | |
CVPixelBufferLockBaseAddress(pxBuffer!, CVPixelBufferLockFlags(rawValue: 0)) | |
let pxdata = CVPixelBufferGetBaseAddress(pxBuffer!) | |
let bitsPerComponent: size_t = 8 | |
let bytesPerRow: size_t = 4 * width | |
let rgbColorSpace: CGColorSpace = CGColorSpaceCreateDeviceRGB() | |
let context = CGContext(data: pxdata, | |
width: width, | |
height: height, | |
bitsPerComponent: bitsPerComponent, | |
bytesPerRow: bytesPerRow, | |
space: rgbColorSpace, | |
bitmapInfo: CGImageAlphaInfo.noneSkipFirst.rawValue) | |
context?.draw(cgImage, in: CGRect(x:0, y:0, width:CGFloat(width),height:CGFloat(height))) | |
CVPixelBufferUnlockBaseAddress(pxBuffer!, CVPixelBufferLockFlags(rawValue: 0)) | |
return pxBuffer! | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
extension UIImage { | |
func resize(size:CGSize) -> UIImage?{ | |
//縦横がおかしくなる時は一度書き直すと良いらしい | |
UIGraphicsBeginImageContextWithOptions(self.size, false, 0.0) | |
self.draw(in:CGRect(x:0,y:0,width:self.size.width,height:self.size.height)) | |
let image = UIGraphicsGetImageFromCurrentImageContext() | |
UIGraphicsEndImageContext() | |
// リサイズ処理 | |
let origWidth = self.size.width | |
let origHeight = self.size.height | |
var resizeWidth:CGFloat = 0 | |
var resizeHeight:CGFloat = 0 | |
if (origWidth < origHeight) { | |
resizeWidth = size.width | |
resizeHeight = origHeight * resizeWidth / origWidth | |
} else { | |
resizeHeight = size.height | |
resizeWidth = origWidth * resizeHeight / origHeight | |
} | |
let resizeSize = CGSize(width:CGFloat(resizeWidth), height:CGFloat(resizeHeight)) | |
UIGraphicsBeginImageContext(resizeSize) | |
self.draw(in: CGRect(x:0,y: 0,width: resizeWidth, height: resizeHeight)) | |
let resizeImage = UIGraphicsGetImageFromCurrentImageContext() | |
UIGraphicsEndImageContext() | |
// 切り抜き処理 | |
let cropRect = CGRect(x:( resizeWidth - size.width ) / 2, | |
y:( resizeHeight - size.height) / 2, | |
width:size.width, | |
height:size.height) | |
if let cropRef = resizeImage?.cgImage { | |
cropRef.cropping(to: cropRect) | |
let cropImage = UIImage(cgImage: cropRef) | |
return cropImage | |
}else { | |
print("error!") | |
return nil | |
} | |
} | |
func resizeMaintainDirection(size:CGSize) -> UIImage?{ | |
//縦横がおかしくなる時は一度書き直すと良いらしい | |
UIGraphicsBeginImageContextWithOptions(self.size, false, 0.0) | |
self.draw(in:CGRect(x:0,y:0,width:self.size.width,height:self.size.height)) | |
guard let image = UIGraphicsGetImageFromCurrentImageContext() else { return nil } | |
UIGraphicsEndImageContext() | |
// リサイズ処理 | |
let origWidth = image.size.width | |
let origHeight = image.size.height | |
var resizeWidth:CGFloat = 0 | |
var resizeHeight:CGFloat = 0 | |
if (origWidth < origHeight) { | |
resizeWidth = size.width | |
resizeHeight = origHeight * resizeWidth / origWidth | |
} else { | |
resizeHeight = size.height | |
resizeWidth = origWidth * resizeHeight / origHeight | |
} | |
let resizeSize = CGSize(width:resizeWidth, height:resizeHeight) | |
UIGraphicsBeginImageContext(resizeSize) | |
image.draw(in: CGRect(x:0,y: 0,width: resizeWidth, height: resizeHeight)) | |
let resizeImage = UIGraphicsGetImageFromCurrentImageContext() | |
UIGraphicsEndImageContext() | |
// 切り抜き処理 | |
let cropRect = CGRect(x:( resizeWidth - size.width ) / 2, | |
y:( resizeHeight - size.height) / 2, | |
width:size.width, | |
height:size.height) | |
if let cropRef = resizeImage?.cgImage { | |
cropRef.cropping(to: cropRect) | |
let cropImage = UIImage(cgImage: cropRef) | |
return cropImage | |
}else { | |
print("error!") | |
return nil | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import UIKit | |
import AVFoundation | |
import ImageIO | |
import MobileCoreServices | |
import WebKit | |
class ViewController: UIViewController { | |
var images:[UIImage]! | |
override func viewDidLoad() { | |
super.viewDidLoad() | |
imageArray = [] | |
images = [] | |
//適当に入れておく | |
for i in 1 ..< 3 { | |
if let image = UIImage(named:"image_\(i).jpeg"){ | |
imageArray.append(image.cgImage!) | |
images.append(image) | |
} | |
} | |
let movie = MovieCreator() | |
//16の倍数 | |
let movieSize = CGSize(width:480,height:640) | |
// time / 60 秒表示する | |
let time = 1 | |
//動画を生成 | |
movie.create(images:images,size:movieSize,time:time, completion: { url in | |
//作成した動画を表示 | |
let mp4View = WKWebView(frame: CGRect(x:0,y:300,width:480,height:640)) | |
mp4View.backgroundColor = UIColor.lightGray | |
mp4View.load(URLRequest(url: url)) | |
self.view.addSubview(mp4View) | |
}) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment