AVFoundation – AVAssetExportSession 裁剪/转码

零基础 Object-C 学习路线推荐 : Object-C 学习目录 >> Object-C 基础

零基础 Object-C 学习路线推荐 : Object-C 学习目录 >> Object-C 线程

零基础 Object-C 学习路线推荐 : Object-C 学习目录 >> OpenGL ES

零基础 Object-C 学习路线推荐 : Object-C 学习目录 >> GPUImage

零基础 Object-C 学习路线推荐 : Object-C 学习目录 >> AVFoundation

零基础 Object-C 学习路线推荐 : Object-C 学习目录 >> CocoaPods


一.前言

1.AVAsset

Assets 可以来自一个文件或用户的相册,可以理解为多媒体资源,通过 URL 作为一个 asset 对象的标识. 这个 URL 可以是本地文件路径或网络流;


2.AVAssetTrack

AVAsset 包含很多轨道 AVAssetTrack 的结合,如 audio, video, text, closed captions, subtitles…


3.AVComposition / AVMutableComposition

使用 AVMutableComposition 类可以增删 AVAsset 来将单个或者多个 AVAsset 集合到一起,用来合成新视频。除此之外,若想将集合到一起的视听资源以自定义的方式进行播放,需要使用 AVMutableAudioMix 和 AVMutableVideoComposition 类对其中的资源进行协调管理;


4.AVMutableVideoComposition

AVFoundation 类 API 中最核心的类是 AVVideoComposition / AVMutableVideoComposition 。

AVVideoComposition / AVMutableVideoComposition 对两个或多个视频轨道组合在一起的方法给出了一个总体描述。它由一组时间范围和描述组合行为的介绍内容组成。这些信息出现在组合资源内的任意时间点。

AVVideoComposition / AVMutableVideoComposition 管理所有视频轨道,可以决定最终视频的尺寸,裁剪需要在这里进行;


5.AVMutableCompositionTrack

多个 AVAsset 集合到一起合成新视频中轨道信息,有音频轨、视频轨等,里面可以插入各种对应的素材(画中画,水印等);


6.AVMutableVideoCompositionLayerInstruction

AVMutableVideoCompositionLayerInstruction 主要用于对视频轨道中的一个视频处理缩放、模糊、裁剪、旋转等;


7.AVMutableVideoCompositionInstruction

表示一个指令,决定一个 timeRange 内每个轨道的状态,每一个指令包含多个 AVMutableVideoCompositionLayerInstruction ;而 AVVideoComposition 由多个 AVVideoCompositionInstruction 构成;

AVVideoCompositionInstruction 所提供的最关键的一段数据是组合对象时间轴内的时间范围信息。这一时间范围是在某一组合形式出现时的时间范围。要执行的组全特质是通过其 AVMutableVideoCompositionLayerInstruction 集合定义的。


8.AVAssetExportSession

AVAssetExportSession 主要用于导出视频;


二.AVAssetExportSession简介

1.初始化会话

/*
参数:
    asset :要导出的会话
    presetName :字符串常量
*/
- (instancetype)initWithAsset:(AVAsset *)asset presetName:(NSString *)presetName;
+ (instancetype)exportSessionWithAsset:(AVAsset *)asset presetName:(NSString *)presetName;

2.presetName 字符串常量

// -- Export Preset Names --


/* These export options can be used to produce movie files with video size appropriate to the device.
   The export will not scale the video up from a smaller size. The video will be compressed using
   H.264 and the audio will be compressed using AAC.  */
AVF_EXPORT NSString *const AVAssetExportPresetLowQuality         API_AVAILABLE(macos(10.11), ios(4.0), tvos(9.0)) API_UNAVAILABLE(watchos);
AVF_EXPORT NSString *const AVAssetExportPresetMediumQuality      API_AVAILABLE(macos(10.11), ios(4.0), tvos(9.0)) API_UNAVAILABLE(watchos);
AVF_EXPORT NSString *const AVAssetExportPresetHighestQuality     API_AVAILABLE(macos(10.11), ios(4.0), tvos(9.0)) API_UNAVAILABLE(watchos);

/* These export options can be used to produce movie files with video size appropriate to the device.
   The export will not scale the video up from a smaller size. The video will be compressed using
   HEVC and the audio will be compressed using AAC.  */
AVF_EXPORT NSString *const AVAssetExportPresetHEVCHighestQuality 			API_AVAILABLE(macos(10.13), ios(11.0), tvos(11.0)) API_UNAVAILABLE(watchos);
AVF_EXPORT NSString *const AVAssetExportPresetHEVCHighestQualityWithAlpha	API_AVAILABLE(macos(10.15), ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);

/* These export options can be used to produce movie files with the specified video size.
   The export will not scale the video up from a smaller size. The video will be compressed using
   H.264 and the audio will be compressed using AAC.  Some devices cannot support some sizes. */
AVF_EXPORT NSString *const AVAssetExportPreset640x480           API_AVAILABLE(macos(10.7), ios(4.0), tvos(9.0)) API_UNAVAILABLE(watchos);
AVF_EXPORT NSString *const AVAssetExportPreset960x540           API_AVAILABLE(macos(10.7), ios(4.0), tvos(9.0)) API_UNAVAILABLE(watchos);
AVF_EXPORT NSString *const AVAssetExportPreset1280x720          API_AVAILABLE(macos(10.7), ios(4.0), tvos(9.0)) API_UNAVAILABLE(watchos);
AVF_EXPORT NSString *const AVAssetExportPreset1920x1080         API_AVAILABLE(macos(10.7), ios(5.0), tvos(9.0)) API_UNAVAILABLE(watchos);
AVF_EXPORT NSString *const AVAssetExportPreset3840x2160         API_AVAILABLE(macos(10.10), ios(9.0), tvos(9.0)) API_UNAVAILABLE(watchos);

/* These export options can be used to produce movie files with the specified video size.
   The export will not scale the video up from a smaller size. The video will be compressed using
   HEVC and the audio will be compressed using AAC.  Some devices cannot support some sizes. */
AVF_EXPORT NSString *const AVAssetExportPresetHEVC1920x1080				API_AVAILABLE(macos(10.13), ios(11.0), tvos(11.0)) API_UNAVAILABLE(watchos);
AVF_EXPORT NSString *const AVAssetExportPresetHEVC1920x1080WithAlpha	API_AVAILABLE(macos(10.15), ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);
AVF_EXPORT NSString *const AVAssetExportPresetHEVC3840x2160				API_AVAILABLE(macos(10.13), ios(11.0), tvos(11.0)) API_UNAVAILABLE(watchos);
AVF_EXPORT NSString *const AVAssetExportPresetHEVC3840x2160WithAlpha	API_AVAILABLE(macos(10.15), ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);

/*  This export option will produce an audio-only .m4a file with appropriate iTunes gapless playback data */
AVF_EXPORT NSString *const AVAssetExportPresetAppleM4A			API_AVAILABLE(macos(10.7), ios(4.0), tvos(9.0)) API_UNAVAILABLE(watchos);

/* This export option will cause the media of all tracks to be passed through to the output exactly as stored in the source asset, except for
   tracks for which passthrough is not possible, usually because of constraints of the container format as indicated by the specified outputFileType.
   This option is not included in the arrays returned by -allExportPresets and -exportPresetsCompatibleWithAsset. */
AVF_EXPORT NSString *const AVAssetExportPresetPassthrough		API_AVAILABLE(macos(10.7), ios(4.0), tvos(9.0)) API_UNAVAILABLE(watchos);

3.获取所有可用的presetName

//获取所有可用的presetName
+ (NSArray<NSString *> *)allExportPresets;

注意:并非所有 presetName都与 assets 兼容。


4.返回与指定asset兼容的presetName

//返回与指定 asset 兼容的标识符
+ (NSArray<NSString *> *)exportPresetsCompatibleWithAsset:(AVAsset *)asset;

5.检查兼容性

不是所有 presetName 都与 asset 兼容。例如,只支持视频的 asset 与只支持音频的 presetName 不兼容。 为了确保使用指定 presetName 的导出操作的设置和运行成功,不应在检索兼容标识符和执行导出操作之间对 asset 进行重大更改:例如添加或删除 AVAssetTrack 轨道

+ (void)determineCompatibilityOfExportPreset:(NSString *)presetName
    				withAsset:(AVAsset *)asset
    			outputFileType:(AVFileType)outputFileType
    			completionHandler:(void (^)(BOOL compatible))handler;

6.导出媒体

/******************************************************************************************/
//@Author:猿说编程
//@Blog(个人博客地址): www.codersrc.com
//@File:AVFoundation - AVAssetExportSession 裁剪/转码
//@Time:2021/07/29 07:30
//@Motto:不积跬步无以至千里,不积小流无以成江海,程序人生的精彩需要坚持不懈地积累!
/******************************************************************************************/

- (void)exportAsynchronouslyWithCompletionHandler:(void (^)(void))handler;


//例如:
[avAssetExportSession exportAsynchronouslyWithCompletionHandler:^(void){
        switch (avAssetExportSession.status) {
            case AVAssetExportSessionStatusFailed:
                NSLog(@"exporting failed %@",[avAssetExportSession error]);
                break;
            case AVAssetExportSessionStatusCompleted:
                NSLog(@"exporting completed");
                // 想做什么事情在这个做
                [self cutImageByVideoPath:v_strSavePath];
                break;
            case AVAssetExportSessionStatusCancelled:
                NSLog(@"export cancelled");
                break;
        }
    }];

7.取消导出

//可以在导出期间调用此方法。
- (void)cancelExport;

导出操作可能因为以下原因失败

  • 有来电显示
  • 有别的应用程序开始播放音频当程序进入后台

8.导出状态枚举

//枚举 AVAssetExportSessionStatus

AVAssetExportSessionStatusUnknown 未知状态
AVAssetExportSessionStatusWaiting 正在等待导出更多数据
AVAssetExportSessionStatusExporting 正在导出中
AVAssetExportSessionStatusCompleted 已成功导出
AVAssetExportSessionStatusFailed 导出失败
AVAssetExportSessionStatusCancelled 已取消

三.AVAssetExportSession使用

NSURL* url = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"123.mp4" ofType:nil]];
AVURLAsset* asset = [[AVURLAsset alloc] initWithURL:url options:nil];

NSLog(@"%@",[AVAssetExportSession allExportPresets]);
NSLog(@"%@",[AVAssetExportSession exportPresetsCompatibleWithAsset:asset]);
/*
2021-07-25 12:07:08.306772+0800 LearnAVFoundation[2104:114694] (
    AVAssetExportPreset640x480,
    AVAssetExportPreset960x540,
    AVAssetExportPreset1280x720,
    AVAssetExportPreset1920x1080,
    AVAssetExportPreset3840x2160,
    AVAssetExportPresetAppleM4A,
    AVAssetExportPresetHEVC1920x1080,
    AVAssetExportPresetHEVC1920x1080WithAlpha,
    AVAssetExportPresetHEVC3840x2160,
    AVAssetExportPresetHEVC3840x2160WithAlpha,
    AVAssetExportPresetHEVCHighestQuality,
    AVAssetExportPresetHEVCHighestQualityWithAlpha,
    AVAssetExportPresetHighestQuality,
    AVAssetExportPresetLowQuality,
    AVAssetExportPresetMediumQuality
)
2021-07-25 12:07:08.307319+0800 LearnAVFoundation[2104:114694] (
    AVAssetExportPreset640x480,
    AVAssetExportPreset960x540,
    AVAssetExportPreset1280x720,
    AVAssetExportPreset1920x1080,
    AVAssetExportPreset3840x2160,
    AVAssetExportPresetAppleM4A,
    AVAssetExportPresetHEVC1920x1080,
    AVAssetExportPresetHEVC3840x2160,
    AVAssetExportPresetHEVCHighestQuality,
    AVAssetExportPresetHighestQuality,
    AVAssetExportPresetLowQuality,
    AVAssetExportPresetMediumQuality
)

*/

四.AVAssetExportSession导出视频


1.导出案例1

/******************************************************************************************/
//@Author:猿说编程
//@Blog(个人博客地址): www.codersrc.com
//@File:AVFoundation - AVAssetExportSession 裁剪/转码
//@Time:2021/07/29 07:30
//@Motto:不积跬步无以至千里,不积小流无以成江海,程序人生的精彩需要坚持不懈地积累!
/******************************************************************************************/

- (void)compressVideoWithVideoURL:(NSURL *)videoURL
                        savedName:(NSString *)savedName
                       completion:(void (^)(NSString *savedPath))completion {
  // Accessing video by URL
  AVURLAsset *videoAsset = [[AVURLAsset alloc] initWithURL:videoURL options:nil];
  
  // Find compatible presets by video asset.
  NSArray *presets = [AVAssetExportSession exportPresetsCompatibleWithAsset:videoAsset];
  
  // Begin to compress video
  // Now we just compress to low resolution if it supports
  // If you need to upload to the server, but server does't support to upload by streaming,
  // You can compress the resolution to lower. Or you can support more higher resolution.
  if ([presets containsObject:AVAssetExportPreset640x480]) {
    AVAssetExportSession *session = [[AVAssetExportSession alloc] initWithAsset:videoAsset  presetName:AVAssetExportPreset640x480];
    
    NSString *doc = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents"];
    NSString *folder = [doc stringByAppendingPathComponent:@"HYBVideos"];
    BOOL isDir = NO;
    BOOL isExist = [[NSFileManager defaultManager] fileExistsAtPath:folder isDirectory:&isDir];
    if (!isExist || (isExist && !isDir)) {
      NSError *error = nil;
      [[NSFileManager defaultManager] createDirectoryAtPath:folder
                                withIntermediateDirectories:YES
                                                 attributes:nil
                                                      error:&error];
      if (error == nil) {
        NSLog(@"目录创建成功");
      } else {
        NSLog(@"目录创建失败");
      }
    }
    
    NSString *outPutPath = [folder stringByAppendingPathComponent:savedName];
    session.outputURL = [NSURL fileURLWithPath:outPutPath];
    
    // Optimize for network use.
    session.shouldOptimizeForNetworkUse = true;
    
    NSArray *supportedTypeArray = session.supportedFileTypes;
    if ([supportedTypeArray containsObject:AVFileTypeMPEG4]) {
      session.outputFileType = AVFileTypeMPEG4;
    } else if (supportedTypeArray.count == 0) {
      NSLog(@"No supported file types");
      return;
    } else {
      session.outputFileType = [supportedTypeArray objectAtIndex:0];
    }
    
    // Begin to export video to the output path asynchronously.
    [session exportAsynchronouslyWithCompletionHandler:^{
      if ([session status] == AVAssetExportSessionStatusCompleted) {
        dispatch_async(dispatch_get_main_queue(), ^{
          if (completion) {
            completion([session.outputURL path]);
          }
        });
      } else {
        dispatch_async(dispatch_get_main_queue(), ^{
          if (completion) {
            completion(nil);
          }
        });
      }
    }];
  }
}

2.导出案例2


/******************************************************************************************/
//@Author:猿说编程
//@Blog(个人博客地址): www.codersrc.com
//@File:AVFoundation - AVAssetExportSession 裁剪/转码
//@Time:2021/07/29 07:30
//@Motto:不积跬步无以至千里,不积小流无以成江海,程序人生的精彩需要坚持不懈地积累!
/******************************************************************************************/

AVAsset *anAsset = <#Get an asset#>;
NSArray *compatiblePresets = [AVAssetExportSession exportPresetsCompatibleWithAsset:anAsset];
if ([compatiblePresets containsObject:AVAssetExportPresetLowQuality]) {
    AVAssetExportSession *exportSession = [[AVAssetExportSession alloc]
        initWithAsset:anAsset presetName:AVAssetExportPresetLowQuality];
    // Implementation continues.
}

exportSession.outputURL = <#A file URL#>;
exportSession.outputFileType = AVFileTypeQuickTimeMovie;
 
CMTime start = CMTimeMakeWithSeconds(1.0, 600);
CMTime duration = CMTimeMakeWithSeconds(3.0, 600);
CMTimeRange range = CMTimeRangeMake(start, duration);
exportSession.timeRange = range;

//导出
[exportSession exportAsynchronouslyWithCompletionHandler:^{
 
        switch ([exportSession status]) {
            case AVAssetExportSessionStatusFailed:
                NSLog(@"Export failed: %@", [[exportSession error] localizedDescription]);
                break;
            case AVAssetExportSessionStatusCancelled:
                NSLog(@"Export canceled");
                break;
            default:
                break;
        }
    }];

五.iOS AVAssetExportSession 视频剪切、合并、压缩

/******************************************************************************************/
//@Author:猿说编程
//@Blog(个人博客地址): www.codersrc.com
//@File:AVFoundation - AVAssetExportSession 裁剪/转码
//@Time:2021/07/29 07:30
//@Motto:不积跬步无以至千里,不积小流无以成江海,程序人生的精彩需要坚持不懈地积累!
/******************************************************************************************/


- (void) loadVideoByPath:(NSString*) v_strVideoPath andSavePath:(NSString*) v_strSavePath {

  NSLog(@"\nv_strVideoPath = %@ \nv_strSavePath = %@\n ",v_strVideoPath,v_strSavePath);
    AVAsset *avAsset = [AVAsset assetWithURL:[NSURL fileURLWithPath:v_strVideoPath]];
    CMTime assetTime = [avAsset duration];
    Float64 duration = CMTimeGetSeconds(assetTime);
    NSLog(@"视频时长 %f\n",duration);

    AVMutableComposition *avMutableComposition = [AVMutableComposition composition];

    AVMutableCompositionTrack *avMutableCompositionTrack = [avMutableComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];

    AVAssetTrack *avAssetTrack = [[avAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];

    NSError *error = nil;
    // 这块是裁剪,rangtime .前面的是开始时间,后面是裁剪多长 (我这裁剪的是从第二秒开始裁剪,裁剪2.55秒时长.)
    [avMutableCompositionTrack insertTimeRange:CMTimeRangeMake(CMTimeMakeWithSeconds(2.0f, 30), CMTimeMakeWithSeconds(2.55f, 30))
                                       ofTrack:avAssetTrack
                                        atTime:kCMTimeZero
                                         error:&error];

    AVMutableVideoComposition *avMutableVideoComposition = [[AVMutableVideoComposition videoComposition] retain];
// 这个视频大小可以由你自己设置。比如源视频640*480.而你这320*480.最后出来的是320*480这么大的,640多出来的部分就没有了。并非是把图片压缩成那么大了。
    avMutableVideoComposition.renderSize = CGSizeMake(320.0f, 480.0f);
    avMutableVideoComposition.frameDuration = CMTimeMake(1, 30);
    
    AVMutableVideoCompositionInstruction *avMutableVideoCompositionInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];

    [avMutableVideoCompositionInstruction setTimeRange:CMTimeRangeMake(kCMTimeZero, [avMutableComposition duration])];

    AVMutableVideoCompositionLayerInstruction *avMutableVideoCompositionLayerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:avAssetTrack];
    [avMutableVideoCompositionLayerInstruction setTransform:avAssetTrack.preferredTransform atTime:kCMTimeZero];

    avMutableVideoCompositionInstruction.layerInstructions = [NSArray arrayWithObject:avMutableVideoCompositionLayerInstruction];


    avMutableVideoComposition.instructions = [NSArray arrayWithObject:avMutableVideoCompositionInstruction];


    NSFileManager *fm = [[NSFileManager alloc] init];
    if ([fm fileExistsAtPath:v_strSavePath]) {
        NSLog(@"video is have. then delete that");
        if ([fm removeItemAtPath:v_strSavePath error:&error]) {
            NSLog(@"delete is ok");
        }else {
            NSLog(@"delete is no error = %@",error.description);
        }
    }
    [fm release];

    AVAssetExportSession *avAssetExportSession = [[AVAssetExportSession alloc] initWithAsset:avMutableComposition presetName:AVAssetExportPreset640x480];
    [avAssetExportSession setVideoComposition:avMutableVideoComposition];
    [avAssetExportSession setOutputURL:[NSURL fileURLWithPath:v_strSavePath]];
    [avAssetExportSession setOutputFileType:AVFileTypeQuickTimeMovie];
    [avAssetExportSession setShouldOptimizeForNetworkUse:YES];
    [avAssetExportSession exportAsynchronouslyWithCompletionHandler:^(void){
        switch (avAssetExportSession.status) {
            case AVAssetExportSessionStatusFailed:
                NSLog(@"exporting failed %@",[avAssetExportSession error]);
                break;
            case AVAssetExportSessionStatusCompleted:
                NSLog(@"exporting completed");
                // 想做什么事情在这个做
                [self cutImageByVideoPath:v_strSavePath];
                break;
            case AVAssetExportSessionStatusCancelled:
                NSLog(@"export cancelled");
                break;
        }
    }];
    if (avAssetExportSession.status != AVAssetExportSessionStatusCompleted){
        NSLog(@"Retry export");
    }
    [avMutableVideoComposition release];
    [avAssetExportSession release];
}


六.猜你喜欢


未经允许不得转载:猿说编程 » AVFoundation – AVAssetExportSession 裁剪/转码
喜欢(2) 打赏

评论抢沙发

评论前必须登录!

文章付费之后,如果下载源码失败,请直接留言,博主看到消息会及时处理的,感谢配合!!!共勉!!

开始学习

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏