引言:随着多媒体的普及,越来越多的短视频被人们所喜爱,接受,传播,所以快速而有效的合成高质量的视频成为刚需。
本文参考Skylpy作者,在原有基础上进行扩展,编码整理。
1.因为涉及到视频合成和播放,所以需求先引入一些和视频相关的资源库
#import <AVKit/AVKit.h>
#import <MediaPlayer/MediaPlayer.h>
#import <AVFoundation/AVFoundation.h>
然后定义一些宏,方便UI布局
#define WWScreamW [UIScreen mainScreen].bounds.size.width
#define WWScreamH [UIScreen mainScreen].bounds.size.height
接着定义一些变量
@interface ViewController () {
NSMutableArray*imageArr; //未压缩的图片
NSMutableArray*imageArray; //经过压缩的图片
}
//视频地址
@property(nonatomic,strong)NSString*theVideoPath;
//合成进度
@property(nonatomic,strong)UILabel *ww_progressLbe;
2.定义一个方法用于视图布局
- (void)ww_setupView {
//视频合成按钮
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button setBounds:CGRectMake(0,0,WWScreamW * 0.25,50)];
button.center = CGPointMake(WWScreamW * 0.25, WWScreamH * 0.15);
[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 setBounds:CGRectMake(0,0,WWScreamW * 0.25,50)];
button1.center = CGPointMake(WWScreamW * 0.75, WWScreamH * 0.15);
[button1 setTitle:@"视频播放"forState:UIControlStateNormal];
[button1 addTarget:self action:@selector(playAction)forControlEvents:UIControlEventTouchUpInside];
button1.backgroundColor = [UIColor redColor];
[self.view addSubview:button1];
//视频合成播放进度提示文本框
UILabel *lbe = [[UILabel alloc]init];
lbe.frame = CGRectMake(0, 0, WWScreamW * 0.25, 25);
lbe.center = CGPointMake(WWScreamW * 0.5, WWScreamH * 0.15);
lbe.textColor = [UIColor blackColor];
lbe.textAlignment = NSTextAlignmentCenter;
lbe.text = @"准备就绪";
lbe.font = [UIFont systemFontOfSize:12];
self.ww_progressLbe = lbe;
[self.view addSubview:lbe];
}
3.定义一个方法用于赋值数据
- (void)ww_setupInit {
imageArray = [[NSMutableArray alloc]init];
imageArr = [[NSMutableArray alloc]init];
NSString *name = @"";
UIImage *img = nil;
//实先准备21张图片,命名为0.jpg至21.jpg
for (int i = 0; i < 22; i++) {
name = [NSString stringWithFormat:@"%d",i];
img = [UIImage imageNamed:name];
[imageArr addObject:img];
}
//对图片进行裁剪,方便合成等比例视频
for (int i = 0; i < imageArr.count; i++) {
UIImage *imageNew = imageArr[i];
//设置image的尺寸
CGSize imgeSize = CGSizeMake(320, 480);
//对图片大小进行压缩--
imageNew = [self imageWithImage:imageNew scaledToSize:imgeSize];
[imageArray addObject:imageNew];
}
}
对图片进行压缩方法如下
-(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 newImage;
}
4.视频合成按钮点击操作事件
//视频合成按钮点击操作
- (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);
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]);
return;
}
//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 finishWritingWithCompletionHandler:^{
NSLog(@"完成");
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.ww_progressLbe.text = @"视频合成完毕";
}];
}];
break;
}
CVPixelBufferRef buffer = NULL;
int idx = frame / 10;
NSLog(@"idx==%d",idx);
NSString *progress = [NSString stringWithFormat:@"%0.2lu",idx / [imageArr count]];
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.ww_progressLbe.text = [NSString stringWithFormat:@"合成进度:%@",progress];
}];
buffer = (CVPixelBufferRef)[self pixelBufferFromCGImage:[[imageArray objectAtIndex:idx]CGImage]size:size];
if(buffer){
//设置每秒钟播放图片的个数
if(![adaptor appendPixelBuffer:buffer withPresentationTime:CMTimeMake(frame,10)]) {
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;
}
5.视频播放按钮点击操作事件
//视频播放按钮点击操作
- (void)playAction {
NSLog(@"************%@",self.theVideoPath);
// 文件管理器
NSFileManager *fileManager = [[NSFileManager alloc]init];
if (![fileManager fileExistsAtPath:self.theVideoPath]) {
self.ww_progressLbe.text = @"文件不存在";
return;
}
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 = CGRectMake(0, WWScreamH * 0.25, WWScreamW, WWScreamH * 0.65);
playerLayer.videoGravity = AVLayerVideoGravityResizeAspect;
[self.view.layer addSublayer:playerLayer];
[player play];
}
项目连接地址
效果图如下
视频布局
视频播放 WechatIMG42.jpeg
网友评论