AVFoundation|AVFoundation - 根据图像生成视频

先给一个可以直接拿过去用的代码,然后我再解释一下.

+ (void)compressImage:(NSURL *)imageUrl duration:(CGFloat)duration size:(CGSize)imageSize outputUrl:(NSURL *)exportUrl completion:(void(^)(void))block {//get source CGImageRef image = NULL; CGImageSourceRef src = https://www.it610.com/article/CGImageSourceCreateWithURL((CFURLRef)imageUrl, NULL); // 1 if (src) { image = CGImageSourceCreateImageAtIndex(src, 0, NULL); //config size_t width = CGImageGetWidth(image); size_t height = CGImageGetHeight(image); CFRelease(src); CGRect dstRect = AVMakeRectWithAspectRatioInsideRect(CGSizeMake(width, height), CGRectMake(0, 0, imageSize.width, imageSize.height)); //2 NSImage *scaleImage = [[NSImage alloc] initWithCGImage:image size:dstRect.size]; CVPixelBufferRef buffer = [ExportMovieUtil getBufferFromNSImage:scaleImage]; // 3 if (buffer) { //config output setting __block AVAssetWriter *videoWriter = [[AVAssetWriter alloc] initWithURL:exportUrl fileType:AVFileTypeQuickTimeMovie error:nil]; NSDictionary *videoSettings = [NSDictionary dictionaryWithObjectsAndKeys:AVVideoCodecTypeH264, AVVideoCodecKey, [NSNumber numberWithInt:dstRect.size.width], AVVideoWidthKey, [NSNumber numberWithInt:dstRect.size.height], AVVideoHeightKey, nil]; // 4 AVAssetWriterInput *writerInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:videoSettings]; NSDictionary *sourcePixelBufferAttributesDictionary = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:kCVPixelFormatType_32ARGB], kCVPixelBufferPixelFormatTypeKey, nil]; AVAssetWriterInputPixelBufferAdaptor *adaptor = [AVAssetWriterInputPixelBufferAdaptor assetWriterInputPixelBufferAdaptorWithAssetWriterInput:writerInput sourcePixelBufferAttributes:sourcePixelBufferAttributesDictionary]; NSParameterAssert(writerInput); NSParameterAssert([videoWriter canAddInput:writerInput]); if ([videoWriter canAddInput:writerInput]) { [videoWriter addInput:writerInput]; }[videoWriter startWriting]; [videoWriter startSessionAtSourceTime:kCMTimeZero]; //start to write dispatch_queue_t dispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); int __block frame = 0; [writerInput requestMediaDataWhenReadyOnQueue:dispatchQueue usingBlock:^{ while ([writerInput isReadyForMoreMediaData]) { if(++frame> duration * 30) { [writerInput markAsFinished]; if(videoWriter.status == AVAssetWriterStatusWriting){ CFRelease(buffer); [videoWriter finishWritingWithCompletionHandler:^{ !block?:block(); }]; } break; }if(![adaptor appendPixelBuffer:buffer withPresentationTime:CMTimeMake(frame, 30)]) { NSLog(@"fail"); } else { NSLog(@"success:%ld",(long)frame); } } }]; } } }

【AVFoundation|AVFoundation - 根据图像生成视频】应该能看到我的代码里除了简单的注释还有1,2,3,4这样的小注释
  • 1 .CGImageSourceCreateWithURL 因为这里我需要的CGImage,所以就不先生成UIImage 或者 NSImage了.
  • 2 . AVMakeRectWithAspectRatioInsideRect 这个api真的是提供很多便利.这个api可以根据你提供的size,然后自动计算当前size按比例能缩放成多大的,然后返回.返回的Rect是一个居中显示的矩形.具体的可以自己试一试,可能我说的比较乱
  • 3 .这是另一个函数了,不是系统的api,我会在下面po出,是将NSImage 转换为 CVPixelBufferRef .
  • 4 .这边我们用AVAssetWriter 结合 AVAssetWriterInputPixelBufferAdaptor的方式来将图片写成视频,我这里默认写成一秒30帧.其实由于是静态图片,不用写得这么久,甚至可以一秒一帧.只要算好时间,算好关键帧,就ok.
**这里po出另外一个函数的实现 **
+ (CVPixelBufferRef)getBufferFromNSImage:(NSImage *)image { CVPixelBufferRef buffer = NULL; // config size_t width = [image size].width; size_t height = [image size].height; size_t bitsPerComponent = 8; // set 8 bit per component CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], kCVPixelBufferCGImageCompatibilityKey, [NSNumber numberWithBool:YES], kCVPixelBufferCGBitmapContextCompatibilityKey, nil]; // create pixel buffer CVPixelBufferCreate(kCFAllocatorDefault, width, height, k32ARGBPixelFormat, (__bridge CFDictionaryRef)options, &buffer); CVPixelBufferLockBaseAddress(buffer, 0); void *rasterData = https://www.it610.com/article/CVPixelBufferGetBaseAddress(buffer); size_t bytesPerRow = CVPixelBufferGetBytesPerRow(buffer); // context to draw in, set to pixel buffer's address CGContextRef ctxt = CGBitmapContextCreate(rasterData, width, height, bitsPerComponent, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedFirst); if(ctxt == NULL){ NSLog(@"could not create context"); return NULL; } // draw NSGraphicsContext *nsctxt = [NSGraphicsContext graphicsContextWithGraphicsPort:ctxt flipped:NO]; [NSGraphicsContext saveGraphicsState]; [NSGraphicsContext setCurrentContext:nsctxt]; [image drawInRect:NSMakeRect(0, 0, width, height)]; [NSGraphicsContext restoreGraphicsState]; CVPixelBufferUnlockBaseAddress(buffer, 0); CFRelease(ctxt); return buffer; }

    推荐阅读