IOS多张图片合成一个视频

图片合成视频

首先需要导入的三个依赖库

AVKit.framework    MediaPlayer.framework    AVFoundation.framework

下面代码中的注释很清楚, 就不做多解释, 直接上代码了

#import "ViewController.h"
#import <AVFoundation/AVFoundation.h>
#import <MediaPlayer/MediaPlayer.h>
#import <AVKit/AVKit.h>

@interface ViewController ()

{
    NSMutableArray *imageArr;//未压缩的图片
    NSMutableArray *imageArray;//经过压缩的图片
}

@property (nonatomic, strong) NSString *theVideoPath;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    imageArray = [[NSMutableArray alloc] init];
    imageArr =[[NSMutableArray alloc]initWithObjects:
               [UIImage imageNamed:@"IMG_3811.jpg"],[UIImage imageNamed:@"IMG_3812.jpg"],[UIImage imageNamed:@"IMG_3813.jpg"],[UIImage imageNamed:@"IMG_3814.jpg"],[UIImage imageNamed:@"IMG_3815.jpg"],[UIImage imageNamed:@"IMG_3816.jpg"],[UIImage imageNamed:@"IMG_3817.jpg"],[UIImage imageNamed:@"IMG_3818.jpg"],[UIImage imageNamed:@"IMG_3820.jpg"],[UIImage imageNamed:@"IMG_3821.jpg"],[UIImage imageNamed:@"IMG_3822.jpg"],[UIImage imageNamed:@"IMG_3823.jpg"],[UIImage imageNamed:@"IMG_3824.jpg"],[UIImage imageNamed:@"IMG_3825.jpg"],[UIImage imageNamed:@"IMG_3826.jpg"],[UIImage imageNamed:@"IMG_3827.jpg"],[UIImage imageNamed:@"IMG_3828.jpg"],[UIImage imageNamed:@"IMG_3829.jpg"],[UIImage imageNamed:@"IMG_3830.jpg"],[UIImage imageNamed:@"IMG_3831.jpg"],[UIImage imageNamed:@"IMG_3832.jpg"],[UIImage imageNamed:@"IMG_3833.jpg"],[UIImage imageNamed:@"IMG_3834.jpg"],[UIImage imageNamed:@"IMG_3835.jpg"],[UIImage imageNamed:@"IMG_3836.jpg"],[UIImage imageNamed:@"IMG_3837.jpg"],[UIImage imageNamed:@"IMG_3838.jpg"],[UIImage imageNamed:@"IMG_3839.jpg"],[UIImage imageNamed:@"IMG_3840.jpg"],[UIImage imageNamed:@"IMG_3841.jpg"],[UIImage imageNamed:@"IMG_3842.jpg"],[UIImage imageNamed:@"IMG_3843.jpg"],[UIImage imageNamed:@"IMG_3844.jpg"],[UIImage imageNamed:@"IMG_3845.jpg"],[UIImage imageNamed:@"IMG_3846.jpg"],nil];
    
    for (int i = 0; i<imageArr.count; i++) {
        UIImage *imageNew = imageArr[i];
        //设置image的尺寸
        CGSize imagesize = imageNew.size;
        imagesize.height =408;
        imagesize.width =306;   
        //对图片大小进行压缩--
        imageNew = [self imageWithImage:imageNew scaledToSize:imagesize];
        [imageArray addObject:imageNew];
    }
    
    UIButton * button =[UIButton buttonWithType:UIButtonTypeRoundedRect];
    [button setFrame:CGRectMake(100,100, 100,50)];
    [button setTitle:@"视频合成"forState:UIControlStateNormal];
    [button addTarget:self action:@selector(testCompressionSession)forControlEvents:UIControlEventTouchUpInside];
    button.backgroundColor = [UIColor redColor];
    [self.view addSubview:button];
    
    UIButton * button1 =[UIButton buttonWithType:UIButtonTypeRoundedRect];
    [button1 setFrame:CGRectMake(100,200, 100,50)];
    [button1 setTitle:@"视频播放"forState:UIControlStateNormal];
    [button1 addTarget:self action:@selector(playAction)forControlEvents:UIControlEventTouchUpInside];
    button1.backgroundColor = [UIColor redColor];
    [self.view addSubview:button1];
}

//对图片尺寸进行压缩--

-(UIImage*)imageWithImage:(UIImage*)image scaledToSize:(CGSize)newSize
{
//    新创建的位图上下文 newSize为其大小
    UIGraphicsBeginImageContext(newSize);
//    对图片进行尺寸的改变
    [image drawInRect:CGRectMake(0,0,newSize.width,newSize.height)];
//    从当前上下文中获取一个UIImage对象  即获取新的图片对象
    UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    // Return the new image.
    return newImage;
}

-(void)testCompressionSession
{
    //设置mov路径
    NSArray *paths =NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);
    NSString *moviePath =[[paths objectAtIndex:0]stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.mov",@"test"]];
    self.theVideoPath=moviePath;
    
    //定义视频的大小320 480 倍数
    CGSize size =CGSizeMake(320,480);
    
//        [selfwriteImages:imageArr ToMovieAtPath:moviePath withSize:sizeinDuration:4 byFPS:30];//第2中方法
    
    NSError *error =nil;
//    转成UTF-8编码
    unlink([moviePath UTF8String]);
    NSLog(@"path->%@",moviePath);
//     iphone提供了AVFoundation库来方便的操作多媒体设备,AVAssetWriter这个类可以方便的将图像和音频写成一个完整的视频文件
    AVAssetWriter *videoWriter = [[AVAssetWriter alloc] initWithURL:[NSURL fileURLWithPath:moviePath] fileType:AVFileTypeQuickTimeMovie error:&error];
    
    NSParameterAssert(videoWriter);
    if(error)
        NSLog(@"error =%@", [error localizedDescription]);
    //mov的格式设置 编码格式 宽度 高度
    NSDictionary *videoSettings =[NSDictionary dictionaryWithObjectsAndKeys:AVVideoCodecH264,AVVideoCodecKey,
                                  [NSNumber numberWithInt:size.width],AVVideoWidthKey,
                                  [NSNumber numberWithInt:size.height],AVVideoHeightKey,nil];
    
    AVAssetWriterInput *writerInput =[AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:videoSettings];

    NSDictionary*sourcePixelBufferAttributesDictionary =[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:kCVPixelFormatType_32ARGB],kCVPixelBufferPixelFormatTypeKey,nil];
//    AVAssetWriterInputPixelBufferAdaptor提供CVPixelBufferPool实例,
//    可以使用分配像素缓冲区写入输出文件。使用提供的像素为缓冲池分配通常
//    是更有效的比添加像素缓冲区分配使用一个单独的池
    AVAssetWriterInputPixelBufferAdaptor *adaptor =[AVAssetWriterInputPixelBufferAdaptor assetWriterInputPixelBufferAdaptorWithAssetWriterInput:writerInput sourcePixelBufferAttributes:sourcePixelBufferAttributesDictionary];
    
    NSParameterAssert(writerInput);
    NSParameterAssert([videoWriter canAddInput:writerInput]);
    
    if ([videoWriter canAddInput:writerInput])
    {
        NSLog(@"11111");
    }
    else
    {
        NSLog(@"22222");        
    }
    
    [videoWriter addInput:writerInput];
    
    [videoWriter startWriting];
    [videoWriter startSessionAtSourceTime:kCMTimeZero];
    
    //合成多张图片为一个视频文件
    dispatch_queue_t dispatchQueue =dispatch_queue_create("mediaInputQueue",NULL);
    int __block frame =0;
    [writerInput requestMediaDataWhenReadyOnQueue:dispatchQueue usingBlock:^{

        while([writerInput isReadyForMoreMediaData])
        {
            if(++frame >=[imageArray count]*10)
            {
                [writerInput markAsFinished];
                [videoWriter finishWriting];
                //              [videoWriterfinishWritingWithCompletionHandler:nil];
                break;
            }
            CVPixelBufferRef buffer =NULL;
            int idx =frame/10;
            NSLog(@"idx==%d",idx);
            buffer = (CVPixelBufferRef)[self pixelBufferFromCGImage:[[imageArray objectAtIndex:idx] CGImage] size:size];
            
            if (buffer)
            {
                if(![adaptor appendPixelBuffer:buffer withPresentationTime:CMTimeMake(frame,30)])//设置每秒钟播放图片的个数
                {
                    NSLog(@"FAIL");
                }
                else
                {
                     NSLog(@"OK");
                }
                
                CFRelease(buffer);
            }
        }
    }];

}

- (CVPixelBufferRef)pixelBufferFromCGImage:(CGImageRef)image size:(CGSize)size
{
    NSDictionary *options =[NSDictionary dictionaryWithObjectsAndKeys:
                            [NSNumber numberWithBool:YES],kCVPixelBufferCGImageCompatibilityKey,
                            [NSNumber numberWithBool:YES],kCVPixelBufferCGBitmapContextCompatibilityKey,nil];
    CVPixelBufferRef pxbuffer =NULL;
    CVReturn status =CVPixelBufferCreate(kCFAllocatorDefault,size.width,size.height,kCVPixelFormatType_32ARGB,(__bridge CFDictionaryRef) options,&pxbuffer);
    
    NSParameterAssert(status ==kCVReturnSuccess && pxbuffer !=NULL);
    
    CVPixelBufferLockBaseAddress(pxbuffer,0);
    
    void *pxdata =CVPixelBufferGetBaseAddress(pxbuffer);
    NSParameterAssert(pxdata !=NULL);
    CGColorSpaceRef rgbColorSpace=CGColorSpaceCreateDeviceRGB();
//    当你调用这个函数的时候,Quartz创建一个位图绘制环境,也就是位图上下文。当你向上下文中绘制信息时,Quartz把你要绘制的信息作为位图数据绘制到指定的内存块。一个新的位图上下文的像素格式由三个参数决定:每个组件的位数,颜色空间,alpha选项
    CGContextRef context =CGBitmapContextCreate(pxdata,size.width,size.height,8,4*size.width,rgbColorSpace,kCGImageAlphaPremultipliedFirst);
    NSParameterAssert(context);
    
    //使用CGContextDrawImage绘制图片  这里设置不正确的话 会导致视频颠倒
//    当通过CGContextDrawImage绘制图片到一个context中时,如果传入的是UIImage的CGImageRef,因为UIKit和CG坐标系y轴相反,所以图片绘制将会上下颠倒
    CGContextDrawImage(context,CGRectMake(0,0,CGImageGetWidth(image),CGImageGetHeight(image)), image);
    // 释放色彩空间
    CGColorSpaceRelease(rgbColorSpace);
    // 释放context
    CGContextRelease(context);
    // 解锁pixel buffer
    CVPixelBufferUnlockBaseAddress(pxbuffer,0);
    
    return pxbuffer;
}

//播放
-(void)playAction
{
    NSLog(@"************%@",self.theVideoPath);
    NSURL *sourceMovieURL = [NSURL fileURLWithPath:self.theVideoPath];
    AVAsset *movieAsset = [AVURLAsset URLAssetWithURL:sourceMovieURL options:nil];
    AVPlayerItem *playerItem = [AVPlayerItem playerItemWithAsset:movieAsset];
    AVPlayer *player = [AVPlayer playerWithPlayerItem:playerItem];
    AVPlayerLayer *playerLayer = [AVPlayerLayer playerLayerWithPlayer:player];
    playerLayer.frame = self.view.layer.bounds;
    playerLayer.videoGravity = AVLayerVideoGravityResizeAspect;
    [self.view.layer addSublayer:playerLayer];
    [player play];
}

注意事项:  我这里的图片是手机直接进行拍的, 直接拍的图片不进行压缩的话, 合成视频的时间会很长,

这里我进行压缩后合成, 时间会很短   35张图片进行合成  差不多3秒左右

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值