视频添加背景音乐遇到的“大坑”----Asset

/* 注意:

 新的音乐资源,一定要提前创建加载 , 否则在添加音乐轨迹时, audioAssetTrack.asset 有可能会为nil,会导致在插入音轨时,失败,引发错误:无法完成这项操作

 [audioMutableCompositionTrack insertTimeRange:timeRange ofTrack:audioAssetTrack atTime:startTime error:&error];

*/

+ (void)compressVideoQuality:(NSString *)presetName lengthLimit:(NSUInteger)maxLimit

                    videoUrl:(NSURL *)videoUrl musicAsset:(AVAsset *)musicAsset

                  completion:(void(^)(NSURL *outputUrl, NSError *error, CGSize videoSize, CGFloat videoDuration))completionBlock progressBlock:(void(^)(float progress))progressBlock

{

    AVAsset *asset = [AVAsset assetWithURL:videoUrl];

    if (!asset) {

        NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey:@"未找到视频资源"}];

        completionBlock(nil,error, CGSizeZero, 0.0f);

        return;

    }

    if (!presetName) {

        NSError *error = [NSError errorWithDomain:NSArgumentDomain code:-2 userInfo:@{NSLocalizedDescriptionKey:@"请选择压缩视频格式"}];

        completionBlock(nil,error, CGSizeZero, 0.0f);

        return;

    }

    //david

    CGSize newSize = CGSizeZero;

    if (presetName == KSAVAssetExportPreset320x240) {

        presetName = AVAssetExportPresetLowQuality;

        newSize = CGSizeMake(320, 240);

    }else{

        NSArray *compatiblePresets = [AVAssetExportSession exportPresetsCompatibleWithAsset:asset];

        if (![compatiblePresets containsObject:presetName]) {

            NSError *error = [NSError errorWithDomain:NSArgumentDomain code:-2 userInfo:@{NSLocalizedDescriptionKey:@"不兼容此压缩格式"}];

            completionBlock(nil,error, CGSizeZero, 0.0f);

            return;

        }

    }

    

    //源视频时长s

    CGFloat originSeconds = round(asset.duration.value / (CGFloat)asset.duration.timescale);

    CMTime startTime = CMTimeMakeWithSeconds(0, asset.duration.timescale);

    CMTimeRange timeRange = CMTimeRangeMake(kCMTimeZero, asset.duration);

    

    AVAssetTrack *assetTrack = [[asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];

    AVMutableComposition *mixComposition = [AVMutableComposition composition];

    

    // 3 - Video track

    AVMutableCompositionTrack *videoCompostionTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];

    [videoCompostionTrack insertTimeRange:timeRange

                                  ofTrack:assetTrack

                                   atTime:kCMTimeZero error:nil];

    

     //  设置音频

    NSArray *audioAssetTracks = nil;

    if (musicAsset) {

        //新的背景音乐

        audioAssetTracks = [musicAsset tracksWithMediaType:AVMediaTypeAudio];

    }else{

        //视频自带的音乐

        audioAssetTracks = [asset tracksWithMediaType:AVMediaTypeAudio];

    }

    if (audioAssetTracks.count > 0){

        NSError *error = nil;

        AVAssetTrack *audioAssetTrack = [audioAssetTracks firstObject];   

        

        AVMutableCompositionTrack *audioMutableCompositionTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio  preferredTrackID:kCMPersistentTrackID_Invalid];

       

        //asset.duration

        [audioMutableCompositionTrack insertTimeRange:timeRange ofTrack:audioAssetTrack atTime:startTime error:&error];

        if (error) {

            completionBlock(nil,error, CGSizeZero, 0.0f);

        }

        

        AVMutableAudioMixInputParameters *mixParameters = [AVMutableAudioMixInputParameters audioMixInputParametersWithTrack:audioMutableCompositionTrack];

        [mixParameters setVolumeRampFromStartVolume:1.f toEndVolume:0.f timeRange:timeRange];

        

        AVMutableAudioMix *mutableAudioMix = [AVMutableAudioMix audioMix];

        mutableAudioMix.inputParameters = @[mixParameters];

    }

    

    

    // 3.1 - Create AVMutableVideoCompositionInstruction

    AVMutableVideoCompositionInstruction *mainInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];

    mainInstruction.timeRange = timeRange;

    

    // 3.2 - Create an AVMutableVideoCompositionLayerInstruction for the video track and fix the orientation.

    AVMutableVideoCompositionLayerInstruction *videolayerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:videoCompostionTrack];

    

    BOOL isVideoAssetPortrait_  = NO;

    CGAffineTransform videoTransform = assetTrack.preferredTransform;

    // 左

    if (videoTransform.a == 0 && videoTransform.b == 1.0 && videoTransform.c == -1.0 && videoTransform.d == 0) {

        isVideoAssetPortrait_ = YES;

    }

    // 右

    if (videoTransform.a == 0 && videoTransform.b == -1.0 && videoTransform.c == 1.0 && videoTransform.d == 0) {

        isVideoAssetPortrait_ = YES;

    }

    

    CGSize naturalSize;

    if(isVideoAssetPortrait_){

        naturalSize = CGSizeMake(assetTrack.naturalSize.height, assetTrack.naturalSize.width);

    } else {

        naturalSize = assetTrack.naturalSize;

    }

    

    //david

    if (newSize.width > 0 && newSize.height > 0) {

        if (newSize.height > newSize.width) {

            newSize = CGSizeMake(newSize.height, newSize.width);

        }

        videoTransform = CGAffineTransformScale(videoTransform, newSize.width/naturalSize.width,

                                                newSize.height/naturalSize.height);

    }

    

    [videolayerInstruction setTransform:videoTransform atTime:kCMTimeZero];

    [videolayerInstruction setOpacity:0.0 atTime:asset.duration];

    // 3.3 - Add instructions

    mainInstruction.layerInstructions = @[videolayerInstruction];    

    AVMutableVideoComposition *mainCompositionInst = [AVMutableVideoComposition videoComposition];

    

    //david

    if (newSize.width > 0 && newSize.height > 0){

        if(isVideoAssetPortrait_){

            naturalSize = CGSizeMake(newSize.height, newSize.width);

        } else {

            naturalSize = newSize;

        }

    }

    

    int32_t frameRate = assetTrack.nominalFrameRate <= 0 ? 30 : assetTrack.nominalFrameRate;

    float renderWidth, renderHeight;

    renderWidth = naturalSize.width;

    renderHeight = naturalSize.height;

    mainCompositionInst.renderSize = CGSizeMake(renderWidth, renderHeight);

    mainCompositionInst.instructions = [NSArray arrayWithObject:mainInstruction];

    mainCompositionInst.frameDuration = CMTimeMake(1, frameRate);

    

    

    // 4 - Get path

    NSFileManager *fileManager = [[NSFileManager alloc] init];

    NSString *fileDirectory = [NSString getPathFromCache:@"KSVideoCaches"];

    if (![fileManager fileExistsAtPath:fileDirectory]) {

        [fileManager createDirectoryAtPath:fileDirectory withIntermediateDirectories:YES attributes:nil error:nil];

    }

    NSString *filePath = [[fileDirectory stringByAppendingPathComponent:@"CompressedVideo"] stringByAppendingPathExtension:@"mp4"];

    NSURL *o_url = [NSURL fileURLWithPath:filePath];

    if ([fileManager fileExistsAtPath:filePath]) {

        [fileManager removeItemAtPath:filePath error:nil];

    }

    

    // 5 - Create exporter

    AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:mixComposition presetName:presetName];

    exportSession.outputURL= o_url;

    exportSession.outputFileType = AVFileTypeMPEG4;

    exportSession.shouldOptimizeForNetworkUse = YES;

    exportSession.videoComposition = mainCompositionInst;

    exportSession.fileLengthLimit = maxLimit;//长度限制

    [exportSession exportAsynchronouslyWithCompletionHandler:^{

        dispatch_async(dispatch_get_main_queue(), ^{

            if (exportSession.status == AVAssetExportSessionStatusCompleted) {

                completionBlock(exportSession.outputURL, nil, CGSizeMake(renderWidth, renderHeight), originSeconds);

            }else if(exportSession.status == AVAssetExportSessionStatusFailed) {

                completionBlock(nil, exportSession.error, CGSizeZero, 0.0);

            }

        });

    }];

    

    //获取 压缩进度

    if (progressBlock) {

        NSTimeInterval period = 1.0; //设置时间间隔

        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

        _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);

        dispatch_source_set_timer(_timer, dispatch_walltime(NULL, 0), period * NSEC_PER_SEC, 0);

        dispatch_source_set_event_handler(_timer, ^{    //在这里执行事件

            if (exportSession.progress >= 1.0) {

                dispatch_source_cancel(_timer);

                _timer = nil;

            }else{

                dispatch_async(dispatch_get_main_queue(), ^{

                    progressBlock(exportSession.progress);

                });

            }

        });

        dispatch_resume(_timer);

    }   

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值