美文网首页
多张图片转视频

多张图片转视频

作者: 失忆的程序员 | 来源:发表于2022-07-18 17:53 被阅读0次

    效果gif

    均为网络图片,如有侵权问题联系我删除。感谢图片模特们对技术的支持与奉献❤️❤️❤️❤️❤️

    图片转视频.gif

    这里传入的是 PHAsset *expAsset
    然后我处理,获取到image 数组
    通过图片数组 来搞

    苹果.jpg

    用到XPFFile

    super dev.jpg

    使用

    
                
                MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:[ViewTool topViewController].view animated:YES];
                // Set the label text.
                //self.hud.label.text = @"转码中...";//NSLocalizedString(@"Loading...", @"HUD loading title");
                
                WeakSelf;
                TXWXEditImageArys *imageAry = [[TXWXEditImageArys alloc] init];
                [imageAry onHandlePHAssetArys:assets url:^(NSURL * _Nonnull outurl) {
                    dispatch_async(dispatch_get_main_queue(), ^{
                        [hud hideAnimated:YES];
                        AVAsset *movieAsset = [AVURLAsset URLAssetWithURL:outurl options:nil];
                        XPFLog(@" =movieAsset=>  %@", movieAsset);
                        TXWXEditRecordVC *vc = [[TXWXEditRecordVC alloc] init];
                        //vc.renderRotation = _renderRotation;
                        vc.videoAsset = movieAsset;
                        //vc.videoPath = self.recordVideoPath;
                        vc.editType = RecordEditType_Video;
                        [weakSelf pushViewController:vc];
                        [vc setOnback:^(BOOL value) {
                            [weakSelf.navigationController dismissViewControllerAnimated:YES completion:nil];
                        }];
                        
                    });
                } vc:[ViewTool topViewController]];
                
    
    bug.gif

    模型1

    // 
    
    #import <Foundation/Foundation.h>
    
    NS_ASSUME_NONNULL_BEGIN
    
    
    typedef void(^editimageArys) (NSURL *outurl);
    
    @interface TXWXEditImageArys : NSObject
    
    - (void)onHandlePHAssetArys:(NSArray *)assets url:(editimageArys)url vc:(UIViewController *)vc;
    
    @end
    
    NS_ASSUME_NONNULL_END
    
    
    //
    
    #import "TXWXEditImageArys.h"
    #import "XPicToVideo.h" 
    
    @interface TXWXEditImageArys ()
    
    @property (nonatomic, strong) NSMutableArray *imagesToEdit;
    @property (nonatomic, assign) NSInteger indexs;
    
    @property (nonatomic, copy) editimageArys blockbackurl;
    
    @end
    
    @implementation TXWXEditImageArys
    
    // MARK: ----- 对外
    - (void)onHandlePHAssetArys:(NSArray *)assets url:(editimageArys)url vc:(UIViewController *)vc
    {
        self.blockbackurl = url;
        self.imagesToEdit = [[NSMutableArray array] init];
        self.indexs = 0;
        [self addEditImageArysindex:self.indexs PHAssetArys:assets vc:vc];
    }
    
    - (void)addEditImageArysindex:(NSInteger)index PHAssetArys:(NSArray *)assets vc:(UIViewController *)vcontroller;
    {
        if (assets.count == self.indexs)
        {
            
            [XPicToVideo compressImages:self.imagesToEdit completion:^(NSURL * _Nonnull outurl) {
                if (self.blockbackurl) {
                    self.blockbackurl(outurl);
                }
            }];
            
            return;
        }
        
        PHAsset *expAsset = assets[self.indexs];
        PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init];
        options.version = PHImageRequestOptionsVersionCurrent;
        options.networkAccessAllowed = YES;
        options.synchronous = YES;
        //sync requests are automatically processed this way regardless of the specified mode
        //originRequestOptions.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;
        options.progressHandler = ^(double progress, NSError *__nullable error, BOOL *stop, NSDictionary *__nullable info){
            
        };
        [[PHImageManager defaultManager] requestImageForAsset:expAsset targetSize:PHImageManagerMaximumSize contentMode:PHImageContentModeDefault options:options resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) {
            dispatch_async(dispatch_get_main_queue(), ^(void) {
                if (result) {
                    //这里做一次压缩,否则读入内存的图片数据会过大
                    UIImage *image = [self scaleImage:result scaleToSize:[self getVideoSize:result.size]];
                    if (image != nil)
                    {
                        [self.imagesToEdit addObject:image];
                        self.indexs ++;
                        [self addEditImageArysindex:self.indexs PHAssetArys:assets vc:vcontroller];
                    }
                }
            });
        }];
        
    }
    
    // MARK: ----- 扩展方法
    
    - (UIImage *)scaleImage:(UIImage *)image scaleToSize:(CGSize)size
    {
        UIGraphicsBeginImageContext(size);
        [image drawInRect:CGRectMake(0, 0, size.width, size.height)];
        UIImage* scaledImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        return scaledImage;
    }
    
    - (CGSize)getVideoSize:(CGSize)sourceSize
    {
        CGSize videoSize = CGSizeMake(sourceSize.width, sourceSize.height);
        if (videoSize.height >= videoSize.width)
        {
            if([self supportCompressSize:CGSizeMake(720, 1280) videoSize:videoSize])
            {
                videoSize = [self compress:CGSizeMake(720, 1280) videoSize:videoSize];
            }
        }
        else
        {
            if ([self supportCompressSize:CGSizeMake(1280, 720) videoSize:videoSize])
            {
                videoSize = [self compress:CGSizeMake(1280, 720) videoSize:videoSize];
            }
        }
        return videoSize;
    }
    ///< 判断是否需要压缩图片
    - (BOOL)supportCompressSize:(CGSize)compressSize videoSize:(CGSize)videoSize
    {
        if (videoSize.width >= compressSize.width && videoSize.height >= compressSize.height)
        {
            return YES;
        }
        if (videoSize.width >= compressSize.height && videoSize.height >= compressSize.width)
        {
            return YES;
        }
        return NO;
    }
    ///< 获得压缩后图片大小
    - (CGSize)compress:(CGSize)compressSize videoSize:(CGSize)videoSize
    {
        CGSize size = CGSizeZero;
        if (compressSize.height / compressSize.width >= videoSize.height / videoSize.width)
        {
            size.width = compressSize.width;
            size.height = compressSize.width * videoSize.height / videoSize.width;
        }
        else
        {
            size.height = compressSize.height;
            size.width = compressSize.height * videoSize.width / videoSize.height;
        }
        return size;
    }
    
    @end
    
    
    幼虎等嬉戏.jpg

    模型2

    // 
    
    #import <Foundation/Foundation.h>
    
    NS_ASSUME_NONNULL_BEGIN
    
    @interface XPicToVideo : NSObject
    
    /**
     *  多张图片合成视频
     *
     */
    + (void)compressImages:(NSArray <UIImage *> *)images completion:(void(^)(NSURL *outurl))block;
    
    @end
    
    NS_ASSUME_NONNULL_END
    
    
    // 
    
    #import "XPicToVideo.h"
    #import "XPFFile.h"
    
    @implementation XPicToVideo
    
    /**
     *  裁剪图片
     *
     *  @param image  图片
     *  @param bounds 大小
     *
     */
    + (UIImage *)croppedImage:(UIImage *)image bounds:(CGRect)bounds
    {
        CGImageRef imageRef = CGImageCreateWithImageInRect([image CGImage], bounds);
        UIImage *croppedImage = [UIImage imageWithCGImage:imageRef];
        CGImageRelease(imageRef);
        return croppedImage;
    }
    
    + (UIImage *)clipImage:(UIImage *)image ScaleWithsize:(CGSize)asize
    {
        UIImage *newimage;
        if (nil == image)
        {
            newimage = nil;
        }
        else
        {
            CGSize oldsize = image.size;
            CGRect rect;
            if (asize.width/asize.height > oldsize.width/oldsize.height)
            {
                rect.size.width = asize.width;
                rect.size.height = asize.width*oldsize.height/oldsize.width;
                rect.origin.x = 0;
                rect.origin.y = (asize.height - rect.size.height)/2;
            }
            else
            {
                rect.size.width = asize.height*oldsize.width/oldsize.height;
                rect.size.height = asize.height;
                rect.origin.x = (asize.width - rect.size.width)/2;
                rect.origin.y = 0;
            }
            UIGraphicsBeginImageContext(asize);
            CGContextRef context = UIGraphicsGetCurrentContext();
            CGContextClipToRect(context, CGRectMake(0, 0, asize.width, asize.height));
            CGContextSetFillColorWithColor(context, [[UIColor clearColor] CGColor]);
            UIRectFill(CGRectMake(0, 0, asize.width, asize.height));//clear background
            [image drawInRect:rect];
            newimage = UIGraphicsGetImageFromCurrentImageContext();
            UIGraphicsEndImageContext();
        }
        return newimage;
    }
    
    + (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();
        CGContextRef context = CGBitmapContextCreate(pxdata, size.width, size.height, 8, 4*size.width, rgbColorSpace, kCGImageAlphaPremultipliedFirst);
        NSParameterAssert(context);
        //CGSize drawSize = CGSizeMake(CGImageGetWidth(image), CGImageGetHeight(image));
        //BOOL baseW = drawSize.width < drawSize.height;
        
        CGContextDrawImage(context, CGRectMake(0, 0, CGImageGetWidth(image), CGImageGetHeight(image)), image);
    
        CGColorSpaceRelease(rgbColorSpace);
        CGContextRelease(context);
    
        CVPixelBufferUnlockBaseAddress(pxbuffer, 0);
    
        return pxbuffer;
    }
    
    /**
     *  多张图片合成视频
     *
     */
    + (void)compressImages:(NSArray <UIImage *> *)images completion:(void(^)(NSURL *outurl))block;
    {
        //先裁剪图片
        NSMutableArray *imageArray = [NSMutableArray array];
        for (UIImage *image in images)
        {
            CGRect rect = CGRectMake(0, 0,image.size.width, image.size.height);
            if (rect.size.width < rect.size.height)
            {
                rect.origin.y = (rect.size.height - rect.size.width)/2;
                rect.size.height = rect.size.width;
            }
            else
            {
                rect.origin.x = (rect.size.width - rect.size.height)/2;
                rect.size.width = rect.size.height;
            }
            //裁剪
            UIImage *newImage = [XPicToVideo croppedImage:image bounds:rect];
            /**
             *  缩放
             */
            UIImage *finalImage = [XPicToVideo clipImage:newImage ScaleWithsize:CGSizeMake(640, 640)];
            [imageArray addObject:finalImage];
        }
        
    //    NSDate *date = [NSDate date];
    //    NSString *string = [NSString stringWithFormat:@"%ld.mov", (unsigned long)(date.timeIntervalSince1970 * 1000)];
    //    NSString *cachePath = [NSTemporaryDirectory() stringByAppendingPathComponent:string];
        [XPFFile file_tmpRemoveFileName:@"imagevideo"];
        NSString *cachePath = [XPFFile file_tmpAddFileName:@"imagevideo" txt:[NSString stringWithFormat:@"%ld.mov", (unsigned long)([NSDate date].timeIntervalSince1970 * 1000)] content:@""];
        if ([[NSFileManager defaultManager] fileExistsAtPath:cachePath])
        {
            [[NSFileManager defaultManager] removeItemAtPath:cachePath error:nil];
        }
        NSURL *exportUrl = [NSURL fileURLWithPath:cachePath];
        CGSize size = CGSizeMake(640, 640);//定义视频的大小
        __block AVAssetWriter *videoWriter = [[AVAssetWriter alloc] initWithURL:exportUrl fileType:AVFileTypeQuickTimeMovie error:nil];
    
        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 *adaptor = [AVAssetWriterInputPixelBufferAdaptor assetWriterInputPixelBufferAdaptorWithAssetWriterInput:writerInput sourcePixelBufferAttributes:sourcePixelBufferAttributesDictionary];
        
        NSParameterAssert(writerInput);
        NSParameterAssert([videoWriter canAddInput:writerInput]);
    
        if ([videoWriter canAddInput:writerInput])
            NSLog(@"");
        else
            NSLog(@"");
    
        [videoWriter addInput:writerInput];
    
        [videoWriter startWriting];
        [videoWriter startSessionAtSourceTime:kCMTimeZero];
    
        //合成多张图片为一个视频文件
        dispatch_queue_t dispatchQueue = dispatch_queue_create("mediaInputQueue", NULL);
        int __block frame = 0;
        __weak typeof(self) ws = self;
    
        [writerInput requestMediaDataWhenReadyOnQueue:dispatchQueue usingBlock:^{
            while ([writerInput isReadyForMoreMediaData])
            {
                if(++frame > 8 * 30)
                {
                    [writerInput markAsFinished];
                    //[videoWriter_ finishWriting];
                    if (videoWriter.status == AVAssetWriterStatusWriting)
                    {
                        NSCondition *cond = [[NSCondition alloc] init];
                        [cond lock];
                        [videoWriter finishWritingWithCompletionHandler:^{
                            [cond lock];
                            [cond signal];
                            [cond unlock];
                        }];
                        [cond wait];
                        [cond unlock];
                        !block?:block(exportUrl);
                    }
                    break;
                }
                CVPixelBufferRef buffer = NULL;
    
                int idx = frame/30 * images.count/8;
                if (idx >= images.count)
                {
                    idx = images.count - 1;
                }
                buffer = (CVPixelBufferRef)[XPicToVideo pixelBufferFromCGImage:[[imageArray objectAtIndex:idx] CGImage] size:size];
                if (buffer)
                {
                    if(![adaptor appendPixelBuffer:buffer withPresentationTime:CMTimeMake(frame, 30)])
                    {
                        NSLog(@"fail");
                    }
                    else
                    {
                        NSLog(@"success:%d", frame);
                    }
                    CFRelease(buffer);
                }
            }
        }];
    
    }
    
    
    
    @end
    
    
    柴犬安详.jpg

    相关文章

      网友评论

          本文标题:多张图片转视频

          本文链接:https://www.haomeiwen.com/subject/yrsdirtx.html