美文网首页iOS高级iOS DevelopmentiOS学习开发
iOS-音频合并,音视频合并,剪切

iOS-音频合并,音视频合并,剪切

作者: FlyElephant | 来源:发表于2016-09-10 14:48 被阅读2618次

    iOS中音频合并是指将两个不同的声音文件合成一个声音文件进行输出,音视频合并适用于视频中没有声音,将声音合并到视频中,最好声音的时长和视频的时长是一致的,生成新的适配会更有效果一点,剪切是合并的逆向操作,对音视频可以进行更精细化的处理~

    CMTime简介

    音视频合并剪切需要更精确,所有中间时间的都是通过CMTime进行出来的,先来看一下结构:

    typedef struct
    {
     CMTimeValue value;  /*! @field value The value of the CMTime. value/timescale = seconds. */
     CMTimeScale timescale; /*! @field timescale The timescale of the CMTime. value/timescale = seconds.  */
     CMTimeFlags flags;  /*! @field flags The flags, eg. kCMTimeFlags_Valid, kCMTimeFlags_PositiveInfinity, etc. */
     CMTimeEpoch epoch;  /*! @field epoch Differentiates between equal timestamps that are actually different because
                 of looping, multi-item sequencing, etc.  
                 Will be used during comparison: greater epochs happen after lesser ones. 
                 Additions/subtraction is only possible within a single epoch,
                 however, since epoch length may be unknown/variable. */
    } CMTime;
    

    value是分子,timeScale是分母,下面是几种CMTime常用的方式,CMTimeShow进行输出,代码如下:

        CMTime startTime = CMTimeMake(13, 100);
        CMTimeShow(startTime);
        
        CMTime endTime = CMTimeMake(40, 100);
        CMTimeShow(endTime);
        
        CMTime addTime = CMTimeAdd(startTime, endTime);
        CMTimeShow(addTime);
        
        CMTime subTime = CMTimeSubtract(startTime,endTime);
        CMTimeShow(subTime);
        
        CMTimeRange timeRange = CMTimeRangeMake(startTime, endTime);
        CMTimeRangeShow(timeRange);
        
        CMTimeRange fromRange = CMTimeRangeFromTimeToTime(startTime, endTime);
        CMTimeRangeShow(fromRange);
        
        //交集
        CMTimeRange intersectionRange = CMTimeRangeGetIntersection(timeRange, fromRange);
        CMTimeRangeShow(intersectionRange);
        //合集
        CMTimeRange unionRange = CMTimeRangeGetUnion(timeRange, fromRange);
        CMTimeRangeShow(unionRange);
    

    音频合并

    音视频合并只需要AVFoundation头文件即可进行文件操作,不过有一个限制就是导出的文件都是m4a格式的:
    音频合并操作:

        NSString *wayPath = [[NSBundle mainBundle] pathForResource:@"MyWay" ofType:@"mp3"];
        NSString *easyPath = [[NSBundle mainBundle] pathForResource:@"Easy" ofType:@"mp3"];
        NSMutableArray *dataArr = [NSMutableArray array];
        [dataArr addObject:[NSURL fileURLWithPath:wayPath]];
        [dataArr addObject:[NSURL fileURLWithPath:easyPath]];
        NSString *destPath = [[self composeDir] stringByAppendingString:@"FlyElephant.m4a"];
        
        if ([[NSFileManager defaultManager] fileExistsAtPath:destPath]) {
            [[NSFileManager defaultManager] removeItemAtPath:destPath error:nil];
        }
        [self audioMerge:dataArr destUrl:[NSURL fileURLWithPath:destPath]];
    

    核心代码:

    - (void)audioMerge:(NSMutableArray *)dataSource destUrl:(NSURL *)destUrl{
        AVMutableComposition *mixComposition = [AVMutableComposition composition];
        
        // 开始时间
        CMTime beginTime = kCMTimeZero;
        // 设置音频合并音轨
        AVMutableCompositionTrack *compositionAudioTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
        
        NSError *error = nil;
        for (NSURL *sourceURL in dataSource) {
            //音频文件资源
            AVURLAsset  *audioAsset = [[AVURLAsset alloc] initWithURL:sourceURL options:nil];
            //需要合并的音频文件的区间
            CMTimeRange audio_timeRange = CMTimeRangeMake(kCMTimeZero, audioAsset.duration);
            // ofTrack 音频文件内容
            BOOL success = [compositionAudioTrack insertTimeRange:audio_timeRange ofTrack:[[audioAsset tracksWithMediaType:AVMediaTypeAudio] firstObject] atTime:beginTime error:&error];
            
            if (!success) {
                NSLog(@"Error: %@",error);
            }
            beginTime = CMTimeAdd(beginTime, audioAsset.duration);
        }
        // presetName 与 outputFileType 要对应  导出合并的音频
        AVAssetExportSession* assetExportSession = [[AVAssetExportSession alloc] initWithAsset:mixComposition presetName:AVAssetExportPresetAppleM4A];
        assetExportSession.outputURL = destUrl;
        assetExportSession.outputFileType = @"com.apple.m4a-audio";
        assetExportSession.shouldOptimizeForNetworkUse = YES;
        [assetExportSession exportAsynchronouslyWithCompletionHandler:^{
            dispatch_async(dispatch_get_main_queue(), ^{
                NSLog(@"%@",assetExportSession.error);
            });
        }];
    }
    

    音视频合并

    音视频和音频合并类似,合并操作:

        NSString *wayPath = [[NSBundle mainBundle] pathForResource:@"MyWay" ofType:@"mp3"];
        NSString *easyPath = [[NSBundle mainBundle] pathForResource:@"Way" ofType:@"mp4"];
        
        NSString *destPath = [[self composeDir] stringByAppendingString:@"FlyElephant.mp4"];
        
        if ([[NSFileManager defaultManager] fileExistsAtPath:destPath]) {
            [[NSFileManager defaultManager] removeItemAtPath:destPath error:nil];
        }
        [self audioVedioMerge:[NSURL fileURLWithPath:wayPath] vedioUrl:[NSURL fileURLWithPath:easyPath] destUrl:[NSURL fileURLWithPath:destPath]];
    

    核心代码:

    
    - (void)audioVedioMerge:(NSURL *)audioUrl vedioUrl:(NSURL *)vedioUrl destUrl:(NSURL *)destUrl {
        AVMutableComposition *mixComposition = [AVMutableComposition composition];
        NSError *error;
        
        AVMutableCompositionTrack *audioCompostionTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
        //音频文件资源
        AVURLAsset  *audioAsset = [[AVURLAsset alloc] initWithURL:audioUrl options:nil];
        CMTimeRange audio_timeRange = CMTimeRangeMake(kCMTimeZero, audioAsset.duration);
        [audioCompostionTrack insertTimeRange:audio_timeRange ofTrack:[[audioAsset tracksWithMediaType:AVMediaTypeAudio] firstObject] atTime:kCMTimeZero error:&error];
        
        //视频文件资源
        AVMutableCompositionTrack *vedioCompostionTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
        AVURLAsset *vedioAsset = [[AVURLAsset alloc] initWithURL:vedioUrl options:nil];
        CMTimeRange vedio_timeRange = CMTimeRangeMake(kCMTimeZero, vedioAsset.duration);
        [vedioCompostionTrack insertTimeRange:vedio_timeRange ofTrack:[[vedioAsset tracksWithMediaType:AVMediaTypeVideo] firstObject] atTime:kCMTimeZero error:&error];
        
        // presetName 与 outputFileType 要对应  导出合并的音频
        AVAssetExportSession* assetExportSession = [[AVAssetExportSession alloc] initWithAsset:mixComposition presetName:AVAssetExportPresetMediumQuality];
        assetExportSession.outputURL = destUrl;
        assetExportSession.outputFileType = @"com.apple.quicktime-movie";
        assetExportSession.shouldOptimizeForNetworkUse = YES;
        [assetExportSession exportAsynchronouslyWithCompletionHandler:^{
            dispatch_async(dispatch_get_main_queue(), ^{
                NSLog(@"%@",assetExportSession.error);
            });
        }];
    }
    

    音频剪切

    剪切比合并简单,只需要操作一个文件即可:

        NSString *inputPath = [[self composeDir] stringByAppendingString:@"FlyElephant.m4a"];
        
        [self audioCrop:[NSURL fileURLWithPath:inputPath] startTime:CMTimeMake(30, 1) endTime:CMTimeMake(48, 1)];
    

    核心代码:

    - (void)audioCrop:(NSURL *)url startTime:(CMTime)startTime endTime:(CMTime)endTime {
        
        NSString *outPutPath = [[self composeDir] stringByAppendingPathComponent:@"Crop.m4a"];
        NSURL *audioFileOutput = [NSURL fileURLWithPath:outPutPath];
        
        [[NSFileManager defaultManager] removeItemAtURL:audioFileOutput error:NULL];
        AVAsset *asset = [AVAsset assetWithURL:url];
        
        AVAssetExportSession *exportSession = [AVAssetExportSession exportSessionWithAsset:asset
                                                                                presetName:AVAssetExportPresetAppleM4A];
        CMTimeRange exportTimeRange = CMTimeRangeFromTimeToTime(startTime, endTime);
        
        exportSession.outputURL = audioFileOutput;
        exportSession.outputFileType = AVFileTypeAppleM4A;
        exportSession.timeRange = exportTimeRange;
        
        [exportSession exportAsynchronouslyWithCompletionHandler:^{
            if (AVAssetExportSessionStatusCompleted == exportSession.status) {
                NSLog(@" FlyElephant \n %@", outPutPath);
            } else if (AVAssetExportSessionStatusFailed == exportSession.status) {
                NSLog(@"FlyElephant error: %@", exportSession.error.localizedDescription);
            }
        }];
    }
    

    文件路径:

    - (NSString *)composeDir {
        NSString *cacheDir = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
        NSFileManager *fileManager  = [NSFileManager defaultManager];
        NSString *compseDir = [NSString stringWithFormat:@"%@/AudioCompose/", cacheDir];
        BOOL isDir = NO;
        BOOL existed = [fileManager fileExistsAtPath:compseDir isDirectory:&isDir];
        if ( !(isDir == YES && existed == YES) ) {
            [fileManager createDirectoryAtPath:compseDir withIntermediateDirectories:YES attributes:nil error:nil];
        }
        return compseDir;
    }
    

    以上就是音频合并,剪切的基本操作,如有疑问,欢迎评论区探讨~

    相关文章

      网友评论

      • 菜先生:Error Domain=AVFoundationErrorDomain Code=-11800 "The operation could not be completed" UserInfo={NSLocalizedFailureReason=An unknown error occurred (-12780), NSLocalizedDescription=The operation could not be completed, NSUnderlyingError=0x1c4644410 {Error Domain=NSOSStatusErrorDomain Code=-12780 "(null)"}}

        楼主这个怎么解
        329fd8af610c:请问楼主这个问题解决了吗?我现在也碰到这个问题了
        FlyElephant:很久之前写的,你们根据错误码搜一下,更新了一下代码格式,可以再比对一下代码
        筱贰笔:我也碰到了这个错误,你解决了么
      • Z了个Y:请问 m4a格式如何转为mp3格式啊?
      • Z了个Y:合成之后感觉不是很连贯
      • Treey_Ahao:合成的时候太慢怎么办
      • ganser:你好,我有个问题困扰我好久了。我想要的两个音频合并覆盖,而不是追加的方式,怎么处理呢?
        FlyElephant:@ganser 两个视频合并之后出现新的视频,把原有的删除,新的改名字,能满足需求吗?
      • sunlin1234:有demon么,想跟着做做

      本文标题:iOS-音频合并,音视频合并,剪切

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