Skip to content

Instantly share code, notes, and snippets.

@uruly
Created April 12, 2018 23:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save uruly/ea31e44db35a4b2575d2be01f4dc86e4 to your computer and use it in GitHub Desktop.
Save uruly/ea31e44db35a4b2575d2be01f4dc86e4 to your computer and use it in GitHub Desktop.
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!
}
}
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
}
}
}
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