- (void)writeImagesAsMovie:(NSArray *)images
toPath:(NSString *)path
{
NSParameterAssert(images);
NSParameterAssert(path);
NSAssert((images.count > 0), @"Set least one image.");
NSFileManager *fileManager = [NSFileManager defaultManager];
// 既にファイルがある場合は削除する
if ([fileManager fileExistsAtPath:path]) {
[fileManager removeItemAtPath:path error:nil];
}
// 最初の画像から動画のサイズ指定する
CGSize size = ((UIImage *)images[0]).size;
NSError *error = nil;
self.videoWriter = [[AVAssetWriter alloc] initWithURL:[NSURL fileURLWithPath:path]
fileType:AVFileTypeQuickTimeMovie
error:&error];
if (error) {
NSLog(@"%@", [error localizedDescription]);
return;
}
NSDictionary *outputSettings =
@{
AVVideoCodecKey : AVVideoCodecH264,
AVVideoWidthKey : @(size.width),
AVVideoHeightKey : @(size.height),
};
AVAssetWriterInput *writerInput = [AVAssetWriterInput
assetWriterInputWithMediaType:AVMediaTypeVideo
outputSettings:outputSettings];
[self.videoWriter addInput:writerInput];
NSDictionary *sourcePixelBufferAttributes =
@{
(NSString *)kCVPixelBufferPixelFormatTypeKey : @(kCVPixelFormatType_32ARGB),
(NSString *)kCVPixelBufferWidthKey : @(size.width),
(NSString *)kCVPixelBufferHeightKey : @(size.height),
};
AVAssetWriterInputPixelBufferAdaptor *adaptor = [AVAssetWriterInputPixelBufferAdaptor
assetWriterInputPixelBufferAdaptorWithAssetWriterInput:writerInput
sourcePixelBufferAttributes:sourcePixelBufferAttributes];
writerInput.expectsMediaDataInRealTime = YES;
// 動画生成開始
if (![self.videoWriter startWriting]) {
NSLog(@"Failed to start writing.");
return;
}
[self.videoWriter startSessionAtSourceTime:kCMTimeZero];
CVPixelBufferRef buffer = NULL;
int frameCount = 0;
double durationForEachImage = 0.05;
int32_t fps = 20;
for (UIImage *image in images) {
if (adaptor.assetWriterInput.readyForMoreMediaData) {
//CMTime frameTime = CMTimeMake((int64_t)frameCount * fps * durationForEachImage, fps);
//CMTime frameTime = CMTimeMake((int64_t)frameCount * 60, 600);
CMTime frameTime = CMTimeMake(1, 10);
CMTime lastTime=CMTimeMake(frameCount, 10);
CMTime presentTime=CMTimeAdd(lastTime, frameTime);
buffer = [self pixelBufferFromCGImage:image.CGImage];
if (![adaptor appendPixelBuffer:buffer withPresentationTime:presentTime]) {
NSLog(@"Failed to append buffer. [image : %@]", image);
}
if(buffer) {
CVBufferRelease(buffer);
}
frameCount++;
NSLog(@"%d",frameCount);
}
}
// 動画生成終了
[writerInput markAsFinished];
[self.videoWriter finishWritingWithCompletionHandler:^{
NSLog(@"Finish writing!");
NSURL *url = [NSURL URLWithString:path];
if ([library videoAtPathIsCompatibleWithSavedPhotosAlbum:url])
{
[library writeVideoAtPathToSavedPhotosAlbum:url
completionBlock:^(NSURL *assetURL, NSError *assetError)
{
if (assetError) { }
}];
}
}];
CVPixelBufferPoolRelease(adaptor.pixelBufferPool);
}
- (CVPixelBufferRef)pixelBufferFromCGImage:(CGImageRef)image
{
NSDictionary *options = @{
(NSString *)kCVPixelBufferCGImageCompatibilityKey : @(YES),
(NSString *)kCVPixelBufferCGBitmapContextCompatibilityKey: @(YES),
};
CVPixelBufferRef pxbuffer = NULL;
CVPixelBufferCreate(kCFAllocatorDefault,
CGImageGetWidth(image),
CGImageGetHeight(image),
kCVPixelFormatType_32ARGB,
(__bridge CFDictionaryRef)options,
&pxbuffer);
CVPixelBufferLockBaseAddress(pxbuffer, 0);
void *pxdata = CVPixelBufferGetBaseAddress(pxbuffer);
CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(pxdata,
CGImageGetWidth(image),
CGImageGetHeight(image),
8,
4 * CGImageGetWidth(image),
rgbColorSpace,
(CGBitmapInfo)kCGImageAlphaNoneSkipFirst);
CGContextConcatCTM(context, CGAffineTransformMakeRotation(0));
CGAffineTransform flipVertical = CGAffineTransformMake(1, 0, 0, -1, 0, CGImageGetHeight(image));
CGContextConcatCTM(context, flipVertical);
CGAffineTransform flipHorizontal = CGAffineTransformMake(-1.0, 0.0, 0.0, 1.0, CGImageGetWidth(image), 0.0);
CGContextConcatCTM(context, flipHorizontal);
CGContextDrawImage(context, CGRectMake(0, 0, CGImageGetWidth(image), CGImageGetHeight(image)), image);
CGColorSpaceRelease(rgbColorSpace);
CGContextRelease(context);
CVPixelBufferUnlockBaseAddress(pxbuffer, 0);
return pxbuffer;
}