美文网首页视频音频
『ios』视频编辑入门【画中画实现】改变视频尺寸与图片并排放置

『ios』视频编辑入门【画中画实现】改变视频尺寸与图片并排放置

作者: butterflyer | 来源:发表于2019-03-12 18:34 被阅读0次

    视频相关入门
    『ios』视频编辑入门 【视频背景音乐合成】
    『ios』视频编辑入门【添加水印】
    『ios』视频编辑入门【画中画实现】改变视频尺寸
    画中画这个功能,其实自己可以尝试搜一下,资料真的很少。这是最中的效果

    Simulator Screen Shot - iPhone 8 - 2019-03-12 at 18.13.41.png

    其实这个也是拿AVFoundation来实现的。下面我说下具体的步骤,因为有些东西跟前两篇是一样的,比如开头的声音采集了,视频采集,所以我就不拆分成不同的步骤了。

    1.拿到资源 视频采集 音频采集

     NSDictionary *opts = [NSDictionary dictionaryWithObject:@(YES) forKey:AVURLAssetPreferPreciseDurationAndTimingKey];
        videoAsset = [AVURLAsset URLAssetWithURL:videoPath options:opts];     //初始化视频媒体文件
        CMTime startTime = CMTimeMakeWithSeconds(0.2, 600);
        CMTime endTime = CMTimeMakeWithSeconds(videoAsset.duration.value/videoAsset.duration.timescale-0.2, videoAsset.duration.timescale);
    
        AVMutableComposition *mixComposition = [[AVMutableComposition alloc] init];
        
        // 视频通道  工程文件中的轨道,有音频轨、视频轨等,里面可以插入各种对应的素材
        AVMutableCompositionTrack *videoTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo
                                                                            preferredTrackID:kCMPersistentTrackID_Invalid];
        //把视频轨道数据加入到可变轨道中 这部分可以做视频裁剪TimeRange
        [videoTrack insertTimeRange:CMTimeRangeFromTimeToTime(startTime, endTime)
                            ofTrack:[[videoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0]
                             atTime:kCMTimeZero error:nil];
    
    //声音采集
       AVURLAsset * audioAsset = [[AVURLAsset alloc] initWithURL:videoPath options:opts];
      //音频通道
        AVMutableCompositionTrack * audioTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
        //音频采集通道
        AVAssetTrack * audioAssetTrack = [[audioAsset tracksWithMediaType:AVMediaTypeAudio] firstObject];
        [audioTrack insertTimeRange:CMTimeRangeFromTimeToTime(startTime, endTime) ofTrack:audioAssetTrack atTime:kCMTimeZero error:nil];
    
       //3.1 AVMutableVideoCompositionInstruction 视频轨道中的一个视频,可以缩放、旋转等
        AVMutableVideoCompositionInstruction *mainInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
        mainInstruction.timeRange = CMTimeRangeFromTimeToTime(kCMTimeZero, videoTrack.timeRange.duration);
    
        // 3.2 AVMutableVideoCompositionLayerInstruction 一个视频轨道,包含了这个轨道上的所有视频素材
        AVMutableVideoCompositionLayerInstruction *videolayerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:videoTrack];
        AVAssetTrack *videoAssetTrack = [[videoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
    [videolayerInstruction setOpacity:0.0 atTime:endTime];
    

    上面这一些列操作都是做了准备工作,实际上最重要的地方就是下面的这些。
    其实如果你真的来自己操作几遍视频编辑类的操作,你就会发现,明明视频尺寸是1080 * 1920 但是确实横屏的。其实这都是因为rotation这个东西。具体的可以看这篇文章
    关于视频方向的若干问题.

    image.png

    所以这里要做的就是判断旋转方向

    2.判断旋转方向 给视频设置正确的播放尺寸

    封装代码

    typedef enum {
        LBVideoOrientationUp,               //Device starts recording in Portrait
        LBVideoOrientationDown,             //Device starts recording in Portrait upside down
        LBVideoOrientationLeft,             //Device Landscape Left  (home button on the left side)
        LBVideoOrientationRight,            //Device Landscape Right (home button on the Right side)
        LBVideoOrientationNotFound = 99     //An Error occurred or AVAsset doesn't contains video track
    } LBVideoOrientation;
    -(LBVideoOrientation)videoOrientationWithAsset:(AVAsset *)asset
    {
        NSArray *videoTracks = [asset tracksWithMediaType:AVMediaTypeVideo];
        if ([videoTracks count] == 0) {
            return LBVideoOrientationNotFound;
        }
        
        AVAssetTrack* videoTrack    = [videoTracks objectAtIndex:0];
        CGAffineTransform txf       = [videoTrack preferredTransform];
        CGFloat videoAngleInDegree  = RadiansToDegrees(atan2(txf.b, txf.a));
        
        LBVideoOrientation orientation = 0;
        switch ((int)videoAngleInDegree) {
            case 0:
                orientation = LBVideoOrientationRight;
                break;
            case 90:
                orientation = LBVideoOrientationUp;
                break;
            case 180:
                orientation = LBVideoOrientationLeft;
                break;
            case -90:
                orientation     = LBVideoOrientationDown;
                break;
            default:
                orientation = LBVideoOrientationNotFound;
                break;
        }
        
        return orientation;
    }
    

    当LBVideoOrientationUp LBVideoOrientationDown的时候和当为LBVideoOrientationRight和LBVideoOrientationLeft的时候分别进行不同的设置

    当时竖直的时候把宽高调换。
       if(isVideoAssetPortrait_){
            naturalSize = CGSizeMake( videoAssetTrack.naturalSize.height,videoAssetTrack.naturalSize.width+500);
        } else {
            naturalSize =  CGSizeMake( videoAssetTrack.naturalSize.width,videoAssetTrack.naturalSize.height+500);;
        }
    //CGFloat scaleValue = screenSize.height/naturalSize.height;
    CGFloat scaleValue = scaleValue = 1;
    

    进行处理旋转方向 重点

     LBVideoOrientation  videoOrientation = [self videoOrientationWithAsset:videoAsset];
        
        CGAffineTransform t1 = CGAffineTransformIdentity;
        CGAffineTransform t2 = CGAffineTransformIdentity;
        CGAffineTransform t3 = CGAffineTransformIdentity;
        NSLog(@" --- 视频转向 -- %ld",(long)videoOrientation);
        switch (videoOrientation)
        {
            case LBVideoOrientationUp:
               
                t1 = CGAffineTransformMakeTranslation(videoTrack.naturalSize.height - 0, 0 - 0);
    //            t1 = CGAffineTransformScale(originTransform, scaleValue, scaleValue);
                t2 = CGAffineTransformRotate(t1, M_PI_2);
                t3 = CGAffineTransformScale(t2, scaleValue, scaleValue);
                break;
            case LBVideoOrientationDown:
                t1 = CGAffineTransformMakeTranslation(
                                                      0 - 0,
                                                      videoTrack.naturalSize.width - 0);  // not fixed width is the real height in upside down
                t2 = CGAffineTransformRotate(t1, -M_PI_2);
                 t3 = CGAffineTransformScale(t2, scaleValue, scaleValue);
                break;
            case  LBVideoOrientationRight:
                t1 = CGAffineTransformMakeTranslation(0 - 0, 0 - 0);
                t2 = CGAffineTransformRotate(t1, 0);
                 t3 = CGAffineTransformScale(t2, scaleValue, scaleValue);
                break;
            case LBVideoOrientationLeft:
                t1 = CGAffineTransformMakeTranslation(videoTrack.naturalSize.width - 0,
                                                      videoTrack.naturalSize.height - 0);
                t2 = CGAffineTransformRotate(t1, M_PI);
                 t3 = CGAffineTransformScale(t2, scaleValue, scaleValue);
                break;
            default:
                NSLog(@"【该视频未发现设置支持的转向】");
                break;
        }
    [videolayerInstruction setTransform:t3 atTime:kCMTimeZero];
    

    经过上面的操作,视频的方向尺寸基本搞定,下面需要做的就是设置图片的位置。其实这个就是水印的操作罢了。但是需要主要的地方。就是水印的布局。一般ios的布局都是从左上方开始,但是在这里水印的布局要从左下方开始。

    3.水印图片布局相关

        mainInstruction.layerInstructions = [NSArray arrayWithObjects:videolayerInstruction,nil];
        //AVMutableVideoComposition:管理所有视频轨道,可以决定最终视频的尺寸,裁剪需要在这里进行
        AVMutableVideoComposition *mainCompositionInst = [AVMutableVideoComposition videoComposition];
        float renderWidth, renderHeight;
        renderWidth = naturalSize.width;
        renderHeight = naturalSize.height;
        mainCompositionInst.renderSize = CGSizeMake(renderWidth, renderHeight);
        mainCompositionInst.renderSize = CGSizeMake(renderWidth, renderHeight);
        mainCompositionInst.instructions = [NSArray arrayWithObject:mainInstruction];
        mainCompositionInst.frameDuration = CMTimeMake(1, 25);
        [self applyVideoEffectsToComposition:mainCompositionInst WithWaterImg:img   size:CGSizeMake(renderWidth, renderHeight)];
    
    - (void)applyVideoEffectsToComposition:(AVMutableVideoComposition *)composition WithWaterImg:(UIImage*)img   size:(CGSize)size {
        
        //水印
        CALayer *imgLayer = [CALayer layer];
        imgLayer.contents = (id)img.CGImage;    
        imgLayer.frame  = CGRectMake(0, 0, size.width, 500);
    
        // 2 - The usual overlay
        CALayer *overlayLayer = [CALayer layer];
        [overlayLayer addSublayer:imgLayer];
        overlayLayer.frame = CGRectMake(0, 0,size.width, size.height);
        [overlayLayer setMasksToBounds:YES];
        
        CALayer *parentLayer = [CALayer layer];
        CALayer *videoLayer = [CALayer layer];
        parentLayer.frame = CGRectMake(0, 0, size.width, size.height);
        videoLayer.frame = CGRectMake(0, 0, size.width, size.height);
        [parentLayer addSublayer:videoLayer];
        [parentLayer addSublayer:overlayLayer];
       
        composition.animationTool = [AVVideoCompositionCoreAnimationTool
                                     videoCompositionCoreAnimationToolWithPostProcessingAsVideoLayer:videoLayer inLayer:parentLayer];
        
    }
    

    4.导出视频

       NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        NSString *documentsDirectory = [paths objectAtIndex:0];
        
        NSString *myPathDocs =  [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.mp4",fileName]];
        unlink([myPathDocs UTF8String]);
        NSURL* videoUrl = [NSURL fileURLWithPath:myPathDocs];
        
        dlink = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateProgress)];
        [dlink setFrameInterval:15];
        [dlink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
        [dlink setPaused:NO];
        // 5 - 视频文件输出
        
        exporter = [[AVAssetExportSession alloc] initWithAsset:mixComposition
                                                    presetName:AVAssetExportPresetHighestQuality];
        exporter.outputURL=videoUrl;
        exporter.outputFileType = AVFileTypeQuickTimeMovie;
        exporter.shouldOptimizeForNetworkUse = YES;
        exporter.videoComposition = mainCompositionInst;
        [exporter exportAsynchronouslyWithCompletionHandler:^{
            dispatch_async(dispatch_get_main_queue(), ^{
                //这里是输出视频之后的操作,做你想做的
                [self exportDidFinish:exporter];
            });
        }];
    - (void)exportDidFinish:(AVAssetExportSession*)session {
        if (session.status == AVAssetExportSessionStatusCompleted) {
            NSURL *outputURL = session.outputURL;
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                __block PHObjectPlaceholder *placeholder;
                if (UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(outputURL.path)) {
                    NSError *error;
                    [[PHPhotoLibrary sharedPhotoLibrary] performChangesAndWait:^{
                        PHAssetChangeRequest* createAssetRequest = [PHAssetChangeRequest creationRequestForAssetFromVideoAtFileURL:outputURL];
                        placeholder = [createAssetRequest placeholderForCreatedAsset];
                    } error:&error];
                    if (error) {
                        [SVProgressHUD showErrorWithStatus:[NSString stringWithFormat:@"%@",error]];
                    }
                    else{
                        [SVProgressHUD showSuccessWithStatus:@"视频已经保存到相册"];
                    }
                }else {
                    [SVProgressHUD showErrorWithStatus:NSLocalizedString(@"视频保存相册失败,请设置软件读取相册权限", nil)];
                }
            });
        }
    }
    

    这是视频跟图片并排的放置最后合成视频的一些基本方法和注意事项。下一篇,视频并排放置,这才是真正的画中画吧。

    demo地址在这里,觉得有帮助给个star

    相关文章

      网友评论

        本文标题:『ios』视频编辑入门【画中画实现】改变视频尺寸与图片并排放置

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