圖片合成視頻
其實(shí)肃叶,視頻的畫面就是一幀幀的圖片,當(dāng)幀率大于16也就是一秒鐘有16張以上的圖片時(shí)十嘿,人類的視覺就覺得它是連續(xù)的因惭。所以,視頻畫面可以分解成圖片绩衷,圖片也可以合成視頻畫面筛欢。 AVFoundation庫可以很方便的操作多媒體設(shè)備,AVAssetWriter這個(gè)類可以方便的將圖像和音頻寫成一個(gè)完整的視頻文件唇聘。代碼如下:
NSArray *imageArray =@[[UIImage imageNamed:@"avatar"],[UIImage imageNamed:@"beauty_off"],[UIImage imageNamed:@"demo"],[UIImage imageNamed:@"recordBefore"],[UIImage imageNamed:@"recording"],[UIImage imageNamed:@"watermark"]];
CGSize size =CGSizeMake(720, 1280);
//設(shè)置mov路徑
NSArray *paths =NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);
NSString *moviePath =[[paths objectAtIndex:0]stringByAppendingPathComponent:[NSString stringWithFormat:@"test.mov"]];
unlink([moviePath UTF8String]);
NSError *error;
// AVFoundation庫來方便的操作多媒體設(shè)備,AVAssetWriter這個(gè)類可以方便的將圖像和音頻寫成一個(gè)完整的視頻文件
AVAssetWriter *videoWriter =[[AVAssetWriter alloc]initWithURL:[NSURL fileURLWithPath:moviePath] fileType:AVFileTypeQuickTimeMovie error:&error];
//設(shè)置視頻的格式 編碼 尺寸
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實(shí)例,
// 可以使用分配像素緩沖區(qū)寫入輸出文件柱搜。使用提供的像素為緩沖池分配通常
// 是更有效的比添加像素緩沖區(qū)分配使用一個(gè)單獨(dú)的池
AVAssetWriterInputPixelBufferAdaptor *adaptor =[AVAssetWriterInputPixelBufferAdaptor assetWriterInputPixelBufferAdaptorWithAssetWriterInput:writerInput sourcePixelBufferAttributes:sourcePixelBufferAttributesDictionary];
//先判斷下
if ([videoWriter canAddInput:writerInput]) {
[videoWriter addInput:writerInput];
[videoWriter startWriting];
[videoWriter startSessionAtSourceTime:kCMTimeZero];
}
__block int i=0;
[writerInput requestMediaDataWhenReadyOnQueue:dispatch_queue_create("mediaInputQueue", NULL)
usingBlock:^{
while ([writerInput isReadyForMoreMediaData]) {
if (++i>=imageArray.count*10) {
[writerInput markAsFinished];
[videoWriter finishWritingWithCompletionHandler:^{
NSLog(@"合并視頻成功");
//保存到手機(jī)相冊
}];
break;
}
CVPixelBufferRef buffer =NULL;
int idx =i/10;
if (idx <imageArray.count) {
buffer =(CVPixelBufferRef )[self pixelBufferFromCGImage:[imageArray[idx] CGImage] size:size];
}
if (buffer) {
if (![adaptor appendPixelBuffer:buffer withPresentationTime:CMTimeMake(i, 30)]) {
NSLog(@"合成fail");
}else{
NSLog(@"合成成功");
}
CFRelease(buffer);
}
}
}];
其中繪制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();
// 當(dāng)你調(diào)用這個(gè)函數(shù)的時(shí)候迟郎,Quartz創(chuàng)建一個(gè)位圖繪制環(huán)境,也就是位圖上下文聪蘸。當(dāng)你向上下文中繪制信息時(shí)宪肖,Quartz把你要繪制的信息作為位圖數(shù)據(jù)繪制到指定的內(nèi)存塊。一個(gè)新的位圖上下文的像素格式由三個(gè)參數(shù)決定:每個(gè)組件的位數(shù)健爬,顏色空間控乾,alpha選項(xiàng)
CGContextRef context =CGBitmapContextCreate(pxdata,size.width,size.height,8,4*size.width,rgbColorSpace,kCGImageAlphaPremultipliedFirst);
NSParameterAssert(context);
//使用CGContextDrawImage繪制圖片 這里設(shè)置不正確的話 會導(dǎo)致視頻顛倒
// 當(dāng)通過CGContextDrawImage繪制圖片到一個(gè)context中時(shí),如果傳入的是UIImage的CGImageRef娜遵,因?yàn)閁IKit和CG坐標(biāo)系y軸相反蜕衡,所以圖片繪制將會上下顛倒
CGContextDrawImage(context,CGRectMake(0,0,CGImageGetWidth(image),CGImageGetHeight(image)), image);
// 釋放色彩空間
CGColorSpaceRelease(rgbColorSpace);
// 釋放context
CGContextRelease(context);
// 解鎖pixel buffer
CVPixelBufferUnlockBaseAddress(pxbuffer,0);
return pxbuffer;
}
圖片壓縮
兩種壓縮圖片的方法:壓縮圖片質(zhì)量(Quality),壓縮圖片尺寸(Size)设拟。
1.壓縮圖片質(zhì)量
NSData *data = UIImageJPEGRepresentation(image, compression);
UIImage *resultImage = [UIImage imageWithData:data];
通過UIImage和NSData的相互轉(zhuǎn)化慨仿,減小 JPEG 圖片的質(zhì)量來壓縮圖片久脯。UIImageJPEGRepresentation::第二個(gè)參數(shù)compression 取值 0.0~1.0,值越小表示圖片質(zhì)量越低镰吆,圖片文件自然越小帘撰。
1.2壓縮圖片尺寸
UIGraphicsBeginImageContext(size);
[image drawInRect:CGRectMake(0, 0, size.width, size.height)];
resultImage=UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
給定所需的圖片尺寸 size,resultImage 即為原圖 image 繪制為 size 大小后的圖片万皿。壓縮圖片質(zhì)量的優(yōu)點(diǎn)在于摧找,盡可能保留圖片清晰度,圖片不會明顯模糊牢硅;缺點(diǎn)在于蹬耘,不能保證圖片壓縮后小于指定大小。壓縮圖片尺寸可以使圖片小于指定大小唤衫,但會使圖片明顯模糊(比壓縮圖片質(zhì)量模糊)婆赠。所以在實(shí)際運(yùn)用中,可以兩者結(jié)合起來使用佳励,給UIImage添加一個(gè)分類休里,上代碼:
-(NSData *)compressWithMaxLength:(NSUInteger)maxLength{
// Compress by quality
CGFloat compression = 1;
NSData *data = UIImageJPEGRepresentation(self, compression);
//NSLog(@"Before compressing quality, image size = %ld KB",data.length/1024);
if (data.length < maxLength) return data;
CGFloat max = 1;
CGFloat min = 0;
for (int i = 0; i < 6; ++i) {
compression = (max + min) / 2;
data = UIImageJPEGRepresentation(self, compression); //NSLog(@"Compression = %.1f", compression); //NSLog(@"In compressing quality loop, image size = %ld KB", data.length / 1024);
if (data.length < maxLength * 0.9)
{
min = compression;
} else if (data.length > maxLength) {
max = compression;
} else {
break;
}
}
//NSLog(@"After compressing quality, image size = %ld KB", data.length / 1024);
if (data.length < maxLength) return data;
UIImage *resultImage = [UIImage imageWithData:data];
// Compress by size
NSUInteger lastDataLength = 0;
while (data.length > maxLength && data.length != lastDataLength) {
lastDataLength = data.length;
CGFloat ratio = (CGFloat)maxLength / data.length; //NSLog(@"Ratio = %.1f", ratio);
CGSize size = CGSizeMake((NSUInteger)(resultImage.size.width * sqrtf(ratio)), (NSUInteger)(resultImage.size.height * sqrtf(ratio)));
// Use NSUInteger to prevent white blank UIGraphicsBeginImageContext(size);
[resultImage drawInRect:CGRectMake(0, 0, size.width, size.height)];
resultImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext();
data = UIImageJPEGRepresentation(resultImage, compression);
//NSLog(@"In compressing size loop, image size = %ld KB", data.length / 1024);
}
//NSLog(@"After compressing size loop, image size = %ld KB", data.length / 1024);
return data;
}
有什么問題,歡迎留言討論赃承。