iOS-音視頻合成(混音)、多個(gè)視頻合成為一個(gè)視頻

本文內(nèi)容

1、多張圖片合成視頻

2、音視頻合成

  • 音視頻合成主要分兩種
    1.音頻+無音頻的視頻
    2.音頻+有音頻的視頻

3、多個(gè)視頻合稱為一個(gè)視頻

1、多張圖片合成視頻

推薦使用:https://github.com/HarrisonJackson/HJImagesToVideo

warning:我使用此方法時(shí)出現(xiàn)了一些問題(最后一張圖片總是一閃而過)

不過里面的方法不支持外部傳入視頻保存的地址,我就加上了一個(gè)

/**
 圖片生成視頻

 @param images 圖片數(shù)組
 @param path 視頻保存地址
 @param size 視頻尺寸
 @param fps 1秒鐘顯示圖片個(gè)數(shù)
 @param animate animate
 @param callbackBlock 成功回調(diào)
 */
+ (void)saveVideoToPhotosWithImages:(NSArray *)images
                          videoPath:(NSString *)path
                           withSize:(CGSize)size
                            withFPS:(int)fps
                 animateTransitions:(BOOL)animate
                  withCallbackBlock:(SuccessBlock)callbackBlock
{
    
    [[NSFileManager defaultManager] removeItemAtPath:path error:NULL];
    
    [HJImagesToVideo videoFromImages:images
                              toPath:path
                            withSize:size
                             withFPS:fps
                  animateTransitions:animate
                   withCallbackBlock:^(BOOL success) {
                       
                       if (success) {
                           UISaveVideoAtPathToSavedPhotosAlbum(path, self, nil, nil);
                       }
                       
                       if (callbackBlock) {
                           callbackBlock(success);
                       }
                   }];
}

使用

 NSString *path = [NSHomeDirectory() stringByAppendingPathComponent:
                      [NSString stringWithFormat:@"Documents/movie.mp4"]];
    
    NSArray * testImageArray = @[ [UIImage imageNamed:@"12.jpeg"],
                                  [UIImage imageNamed:@"1.jpeg"],
                                  [UIImage imageNamed:@"3.jpeg"],
                                  [UIImage imageNamed:@"4.jpeg"],
                                  [UIImage imageNamed:@"5.jpeg"],
                                  [UIImage imageNamed:@"6.jpeg"],
                                  [UIImage imageNamed:@"7.jpeg"],
                                  [UIImage imageNamed:@"8.jpeg"],
                                  [UIImage imageNamed:@"9.jpeg"],
                                  [UIImage imageNamed:@"10.jpeg"],
                                  [UIImage imageNamed:@"11.jpeg"],
                                  [UIImage imageNamed:@"2.jpeg"]];
    
    
    
    CGSize currentSize = CGSizeMake(320, 480);
    
    
    NSMutableArray *imageArray=[NSMutableArray array];
    
    for (int i = 0; i<testImageArray.count; i++) {
        UIImage *imageNew = testImageArray[i];
        //設(shè)置image的尺寸
        CGSize imagesize = imageNew.size;
        imagesize.height =currentSize.height;
        imagesize.width =currentSize.width;
        
        //對(duì)圖片大小進(jìn)行壓縮--確保圖片大小一樣
        imageNew = [imageNew imageByScalingAndCroppingForSize:currentSize];
        [imageArray addObject:imageNew];
    }
    
    
    //每次生成視頻前,先移除重復(fù)名的,
    [[NSFileManager defaultManager] removeItemAtPath:path error:NULL];
    
    
    NSLog(@"path:%@",path);
    
    //開始合成
    
    __weak typeof(self)weakSelf=self;
    
    [HJImagesToVideo saveVideoToPhotosWithImages:imageArray videoPath:path withSize:currentSize withFPS:1 animateTransitions:YES withCallbackBlock:^(BOOL success) {
        
        dispatch_async(dispatch_get_main_queue(), ^{
            
            if (success) {
                NSLog(@"success");
               }
            
        });
        
    }];

附另一種方法

 // 初始化壓縮引擎  path視頻保存地址
    AVAssetWriter *videoWriter = [[AVAssetWriter alloc] initWithURL:[NSURL fileURLWithPath:path] fileType:AVFileTypeQuickTimeMovie error:nil];
    
    // 斷言,就是希望程序在相應(yīng)位置設(shè)定的條件不滿足的時(shí)候拋出來,用NSParameterAssert讓程序crash到相應(yīng)位置,用來作安全檢查
    NSParameterAssert(videoWriter);
    
 
    
    // 對(duì)生成的視頻的設(shè)置  size 視頻小
    NSDictionary *videoSetting = [NSDictionary dictionaryWithObjectsAndKeys:AVVideoCodecH264,AVVideoCodecKey,[NSNumber numberWithInt:size.width],AVVideoWidthKey,[NSNumber numberWithInt:size.height],AVVideoHeightKey, nil];
    
    AVAssetWriterInput *writerInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:videoSetting];
    NSDictionary *sourcePixelBufferAttributesDictionary = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:kCVPixelFormatType_32ABGR],kCVPixelBufferPixelFormatTypeKey, nil];
    
    AVAssetWriterInputPixelBufferAdaptor *adaptor = [AVAssetWriterInputPixelBufferAdaptor assetWriterInputPixelBufferAdaptorWithAssetWriterInput:writerInput  sourcePixelBufferAttributes:sourcePixelBufferAttributesDictionary];
    NSParameterAssert(writerInput);
    NSParameterAssert([videoWriter canAddInput:writerInput]);
    
    if([videoWriter canAddInput:writerInput]){
        NSLog(@"1111");
    }
    else{
        NSLog(@"2222");
    }
    
    [videoWriter addInput:writerInput];
    
    [videoWriter startWriting];
    
    [videoWriter startSessionAtSourceTime:kCMTimeZero];
    
    
    // 將多張圖片合成為一個(gè)視頻文件
    dispatch_queue_t queue = dispatch_queue_create("mediaInputQueue", NULL);
    int __block frame = 0;
    
    [writerInput requestMediaDataWhenReadyOnQueue:queue usingBlock:^{
        
        while ([writerInput isReadyForMoreMediaData]) {
            if(++frame >= [array count] * 5){// 5  為控制每張圖片顯示時(shí)間,具體看著調(diào)
                [writerInput markAsFinished];
                [videoWriter finishWritingWithCompletionHandler:^{
                    
                    NSLog(@"結(jié)束了");
                    callbackBlock(YES);

                }];
                break;
            }
            
            CVPixelBufferRef buffer = NULL;
   

            int idx = frame / 5;// 5  為控制每張圖片顯示時(shí)間,與上面對(duì)應(yīng)
            
            NSLog(@"idx == %d",idx);
            
            buffer = [self pixelBufferFromCGImage:[[array objectAtIndex:idx] CGImage] size:size];
            
            if(buffer){
                // 設(shè)置視頻的時(shí)間
                if([adaptor appendPixelBuffer:buffer withPresentationTime:CMTimeMake(frame, 5)]){
                    NSLog(@"寫入成功");
                }
                else{
                    NSLog(@"寫入失敗");
                    CFRelease(buffer);
                }
            }
        }
    }];
    
    

2、音視頻合成

2.1、音頻+無音頻的視頻

/**
 沒有背景音樂的視頻添加背景音樂
 
 @param musicPath 背景音樂地址
 @param videoPath 視頻地址
 @param savePath 保存視頻地址
 @param successBlock 合成成功
 */
+ (void)mergeVideoWithMusic:(NSString *)musicPath noBgMusicVideo:(NSString *)videoPath saveVideoPath:(NSString *)savePath success:(mergeVideoSuccessBlock)successBlock{
    
    // 聲音來源
    NSURL *audioInputUrl = [NSURL fileURLWithPath:musicPath];
    // 視頻來源
    NSURL *videoInputUrl = [NSURL fileURLWithPath:videoPath];
    
    
    // 添加合成路徑
    NSURL *outputFileUrl = [NSURL fileURLWithPath:savePath];
    // 時(shí)間起點(diǎn)
    CMTime nextClistartTime = kCMTimeZero;
    // 創(chuàng)建可變的音視頻組合
    AVMutableComposition *comosition = [AVMutableComposition composition];
    
    
    // 視頻采集
    AVURLAsset *videoAsset = [[AVURLAsset alloc] initWithURL:videoInputUrl options:nil];
    // 視頻時(shí)間范圍
    CMTimeRange videoTimeRange = CMTimeRangeMake(kCMTimeZero, videoAsset.duration);
    // 視頻通道 枚舉 kCMPersistentTrackID_Invalid = 0
    AVMutableCompositionTrack *videoTrack = [comosition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
    // 視頻采集通道
    AVAssetTrack *videoAssetTrack = [[videoAsset tracksWithMediaType:AVMediaTypeVideo] firstObject];
    //  把采集軌道數(shù)據(jù)加入到可變軌道之中
    [videoTrack insertTimeRange:videoTimeRange ofTrack:videoAssetTrack atTime:nextClistartTime error:nil];
    
    
    
    // 聲音采集
    AVURLAsset *audioAsset = [[AVURLAsset alloc] initWithURL:audioInputUrl options:nil];
    // 因?yàn)橐曨l短這里就直接用視頻長度了,如果自動(dòng)化需要自己寫判斷
    CMTimeRange audioTimeRange = videoTimeRange;
    // 音頻通道
    AVMutableCompositionTrack *audioTrack = [comosition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
    // 音頻采集通道
    AVAssetTrack *audioAssetTrack = [[audioAsset tracksWithMediaType:AVMediaTypeAudio] firstObject];
    // 加入合成軌道之中
    [audioTrack insertTimeRange:audioTimeRange ofTrack:audioAssetTrack atTime:nextClistartTime error:nil];
    
    // 創(chuàng)建一個(gè)輸出
    AVAssetExportSession *assetExport = [[AVAssetExportSession alloc] initWithAsset:comosition presetName:AVAssetExportPresetMediumQuality];
    // 輸出類型
    assetExport.outputFileType = AVFileTypeQuickTimeMovie;
    // 輸出地址
    assetExport.outputURL = outputFileUrl;
    // 優(yōu)化
    assetExport.shouldOptimizeForNetworkUse = YES;
    // 合成完畢
    [assetExport exportAsynchronouslyWithCompletionHandler:^{
        // 回到主線程
        dispatch_async(dispatch_get_main_queue(), ^{
            
            successBlock();
            
        });
    }];
}

附另外一種方法

- (IBAction)addBackgroundMusicAction:(UIButton *)sender {
    if (self.asset == nil) {
        return;
    }
    
    //合成之后的輸出路徑
    NSString *outPutPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"mergeVideo-%d.mov",arc4random() % 1000]];
    //混合后的視頻輸出路徑
    NSURL *outPutUrl = [NSURL fileURLWithPath:outPutPath];
    if ([[NSFileManager defaultManager] fileExistsAtPath:outPutPath])
    {
        [[NSFileManager defaultManager] removeItemAtPath:outPutPath error:nil];
    }
    //可變音視頻組合
    AVMutableComposition *composition = [AVMutableComposition composition];
    //視頻時(shí)間
    CMTimeRange videoTimeRange = CMTimeRangeMake(kCMTimeZero, self.asset.duration);
    
    //可變視頻軌
    AVMutableCompositionTrack *videoTrack = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
    
    AVAssetTrack *videoAssetTrack = [[self.asset tracksWithMediaType:AVMediaTypeVideo] firstObject];
    //把素材軌道添加到可變的視頻軌道中去
    [videoTrack insertTimeRange:videoTimeRange ofTrack:videoAssetTrack atTime:kCMTimeZero error:nil];
    
    
    //聲音采集
    AVURLAsset *audioAsset = [[AVURLAsset alloc] initWithURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"一個(gè)人的冬天" ofType:@"mp3"]] options:nil];
    //音頻軌道
    AVMutableCompositionTrack *audioTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
    //音頻采集通道
    AVAssetTrack *audioAssetTrack = [[audioAsset tracksWithMediaType:AVMediaTypeAudio] firstObject];
    [audioTrack insertTimeRange:videoTimeRange ofTrack:audioAssetTrack atTime:kCMTimeZero error:nil];
    
    
    // 3.1 - Create AVMutableVideoCompositionInstruction
    AVMutableVideoCompositionInstruction *mainInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
    mainInstruction.timeRange = CMTimeRangeMake(kCMTimeZero, self.asset.duration);
    
    // 3.2 - Create an AVMutableVideoCompositionLayerInstruction for the video track and fix the orientation.
    AVMutableVideoCompositionLayerInstruction *videolayerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:videoTrack];
    UIImageOrientation videoAssetOrientation_  = UIImageOrientationUp;
    BOOL isVideoAssetPortrait_  = NO;
    CGAffineTransform videoTransform = videoAssetTrack.preferredTransform;
    if (videoTransform.a == 0 && videoTransform.b == 1.0 && videoTransform.c == -1.0 && videoTransform.d == 0) {
        videoAssetOrientation_ = UIImageOrientationRight;
        isVideoAssetPortrait_ = YES;
    }
    if (videoTransform.a == 0 && videoTransform.b == -1.0 && videoTransform.c == 1.0 && videoTransform.d == 0) {
        videoAssetOrientation_ =  UIImageOrientationLeft;
        isVideoAssetPortrait_ = YES;
    }
    if (videoTransform.a == 1.0 && videoTransform.b == 0 && videoTransform.c == 0 && videoTransform.d == 1.0) {
        videoAssetOrientation_ =  UIImageOrientationUp;
    }
    if (videoTransform.a == -1.0 && videoTransform.b == 0 && videoTransform.c == 0 && videoTransform.d == -1.0) {
        videoAssetOrientation_ = UIImageOrientationDown;
    }
    [videolayerInstruction setTransform:videoAssetTrack.preferredTransform atTime:kCMTimeZero];
    [videolayerInstruction setOpacity:0.0 atTime:self.asset.duration];
    
    // 3.3 - Add instructions
    mainInstruction.layerInstructions = [NSArray arrayWithObjects:videolayerInstruction,nil];
    
    AVMutableVideoComposition *mainCompositionInst = [AVMutableVideoComposition videoComposition];
    
    CGSize naturalSize;
    if(isVideoAssetPortrait_){
        naturalSize = CGSizeMake(videoAssetTrack.naturalSize.height, videoAssetTrack.naturalSize.width);
    } else {
        naturalSize = videoAssetTrack.naturalSize;
    }
    //防止合成后的視頻旋轉(zhuǎn)90度
    float renderWidth, renderHeight;
    renderWidth = naturalSize.width;
    renderHeight = naturalSize.height;
    mainCompositionInst.renderSize = CGSizeMake(renderWidth, renderHeight);
    mainCompositionInst.instructions = [NSArray arrayWithObject:mainInstruction];
    mainCompositionInst.frameDuration = CMTimeMake(1, 30);
    
    //創(chuàng)建輸出
    AVAssetExportSession * assetExport = [[AVAssetExportSession alloc] initWithAsset:composition presetName:AVAssetExportPresetMediumQuality];
    assetExport.outputURL = outPutUrl;//輸出路徑
    assetExport.outputFileType = AVFileTypeQuickTimeMovie;//輸出類型
    assetExport.shouldOptimizeForNetworkUse = YES;
    assetExport.videoComposition = mainCompositionInst;
    [assetExport exportAsynchronouslyWithCompletionHandler:^{
        dispatch_async(dispatch_get_main_queue(), ^{
            [self exportDidFinish:assetExport];
        });
    }];
}
- (void)exportDidFinish:(AVAssetExportSession*)session {
    if (session.status == AVAssetExportSessionStatusCompleted) {
        NSURL *outputURL = session.outputURL;
        ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
        if ([library videoAtPathIsCompatibleWithSavedPhotosAlbum:outputURL]) {
            [library writeVideoAtPathToSavedPhotosAlbum:outputURL completionBlock:^(NSURL *assetURL, NSError *error){
                dispatch_async(dispatch_get_main_queue(), ^{
                    if (error) {
                        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error" message:@"Video Saving Failed"
                                                                       delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
                        [alert show];
                    } else {
                        AVPlayerItem * playeritem = [AVPlayerItem playerItemWithURL:outputURL];
                        [_player replaceCurrentItemWithPlayerItem:playeritem];
                        [_player play];
                        
                    }
                });
            }];
        }
    }
}


2.2音頻+有音頻的視頻
處理方法
1、先獲取視頻的音頻,與需要添加的音頻進(jìn)行混音,得到混音文件。
2、將混音文件和視頻進(jìn)行合成,得到新的視頻文件

/**
 音頻視頻合成

 @param musicPath 音頻
 @param videoPath 視頻
 @param savePath 保存地址
 @param successBlock 合成成功
 */
+ (void)mergeVideoWithMusic:(NSString *)musicPath video:(NSString *)videoPath saveVideoPath:(NSString *)savePath success:(mergeVideoSuccessBlock)successBlock {
    
    //第一步:音頻和視頻中的音頻混音,生成混音文件
    
    
    AVMutableComposition *composition =[AVMutableComposition composition];
    NSMutableArray *audioMixParams =[NSMutableArray array];
    
    //錄制的視頻
    NSURL *video_inputFileUrl =[NSURL fileURLWithPath:videoPath];
    AVURLAsset *songAsset =[AVURLAsset URLAssetWithURL:video_inputFileUrl options:nil];
    CMTime startTime =CMTimeMakeWithSeconds(0,songAsset.duration.timescale);
    CMTime trackDuration =songAsset.duration;
    
    //獲取視頻中的音頻素材
    [self setUpAndAddAudioAtPath:video_inputFileUrl toComposition:composition start:startTime dura:trackDuration offset:CMTimeMake(14*44100,44100) addAudioParams:audioMixParams];
    
    
    //本地要插入的音樂
    NSURL *assetURL2 =[NSURL fileURLWithPath:musicPath];
    //獲取設(shè)置完的本地音樂素材
    [self setUpAndAddAudioAtPath:assetURL2 toComposition:composition start:startTime dura:trackDuration offset:CMTimeMake(0,44100) addAudioParams:audioMixParams];
    
    //創(chuàng)建一個(gè)可變的音頻混合
    AVMutableAudioMix *audioMix =[AVMutableAudioMix audioMix];
    audioMix.inputParameters =[NSArray arrayWithArray:audioMixParams];//從數(shù)組里取出處理后的音頻軌道參數(shù)
    
    //創(chuàng)建一個(gè)輸出
    AVAssetExportSession *exporter =[[AVAssetExportSession alloc]
                                     initWithAsset:composition
                                     presetName:AVAssetExportPresetAppleM4A];
    exporter.audioMix = audioMix;
    exporter.outputFileType=@"com.apple.m4a-audio";//輸出的類型也是 m4a文件
    
    
    //混音后的輸出地址
    NSString *exportFile = [NSHomeDirectory() stringByAppendingPathComponent:
                      [NSString stringWithFormat:@"Documents/music.m4a"]];//和上面的類型保持一致
    
    if([[NSFileManager defaultManager]fileExistsAtPath:exportFile]) {
        [[NSFileManager defaultManager]removeItemAtPath:exportFile error:nil];
    }
    NSLog(@"輸出混音路徑===%@",exportFile);
    
    NSURL *exportURL =[NSURL fileURLWithPath:exportFile];
    exporter.outputURL = exportURL;

    
    [exporter exportAsynchronouslyWithCompletionHandler:^{
        
        NSLog(@"音頻混音完畢,開始合成音頻、視頻");
        
        if ([[NSFileManager defaultManager] fileExistsAtPath:exportFile]) {
            //第二步:混音和視頻合成
            
            [self theVideoWithMixMusic:exportFile videoPath:videoPath savePath:savePath success:successBlock];


        }
        
    }];
    
}


/**
 音頻和視頻混合

 @param mixURLPath 混音
 @param videoPath 視頻
 @param savePath 保存視頻
 @param successBlock 成功
 */
+ (void)theVideoWithMixMusic:(NSString *)mixURLPath videoPath:(NSString *)videoPath savePath:(NSString *)savePath success:(mergeVideoSuccessBlock)successBlock
{
    //聲音來源路徑(最終混合的音頻)
    NSURL   *audio_inputFileUrl =[NSURL fileURLWithPath:mixURLPath];
    
    //視頻來源路徑
    NSURL   *video_inputFileUrl = [NSURL fileURLWithPath:videoPath];
    
    //最終合成輸出路徑
    NSURL   *outputFileUrl = [NSURL fileURLWithPath:savePath];
  
    CMTime nextClipStartTime =kCMTimeZero;
    
    //創(chuàng)建可變的音頻視頻組合
    AVMutableComposition* mixComposition =[AVMutableComposition composition];
    
    //視頻采集
    AVURLAsset* videoAsset =[[AVURLAsset alloc]initWithURL:video_inputFileUrl options:nil];
    CMTimeRange video_timeRange =CMTimeRangeMake(kCMTimeZero,videoAsset.duration);
    AVMutableCompositionTrack*a_compositionVideoTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
    [a_compositionVideoTrack insertTimeRange:video_timeRange ofTrack:[[videoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0]atTime:nextClipStartTime error:nil];
    
    //聲音采集
    AVURLAsset* audioAsset =[[AVURLAsset alloc]initWithURL:audio_inputFileUrl options:nil];
    CMTimeRange audio_timeRange =CMTimeRangeMake(kCMTimeZero,videoAsset.duration);//聲音長度截取范圍==視頻長度
    AVMutableCompositionTrack*b_compositionAudioTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
    [b_compositionAudioTrack insertTimeRange:audio_timeRange ofTrack:[[audioAsset tracksWithMediaType:AVMediaTypeAudio]objectAtIndex:0]atTime:nextClipStartTime error:nil];
    
    //創(chuàng)建一個(gè)輸出
    AVAssetExportSession* _assetExport =[[AVAssetExportSession alloc]initWithAsset:mixComposition presetName:AVAssetExportPresetMediumQuality];
    _assetExport.outputFileType =AVFileTypeQuickTimeMovie;
    _assetExport.outputURL =outputFileUrl;
    _assetExport.shouldOptimizeForNetworkUse=YES;
    
    
    [_assetExport exportAsynchronouslyWithCompletionHandler:
     ^(void ) {
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"完成!輸出路徑==%@",savePath);
            
            if([[NSFileManager defaultManager]fileExistsAtPath:mixURLPath]) {
                [[NSFileManager defaultManager]removeItemAtPath:mixURLPath error:nil];
            }
            successBlock();
 
        });
     }];
}

//通過文件路徑建立和添加音頻素材
+ (void)setUpAndAddAudioAtPath:(NSURL*)assetURL toComposition:(AVMutableComposition*)composition start:(CMTime)start dura:(CMTime)dura offset:(CMTime)offset addAudioParams:(NSMutableArray *)audioMixParams {
    
    AVURLAsset *songAsset =[AVURLAsset URLAssetWithURL:assetURL options:nil];
    
    AVMutableCompositionTrack *track =[composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
    AVAssetTrack *sourceAudioTrack =[[songAsset tracksWithMediaType:AVMediaTypeAudio]objectAtIndex:0];
    
    NSError *error =nil;
    BOOL ok =NO;
    
    CMTime startTime = start;
    CMTime trackDuration = dura;
    CMTimeRange tRange =CMTimeRangeMake(startTime,trackDuration);
    
    //設(shè)置音量
    //AVMutableAudioMixInputParameters(輸入?yún)?shù)可變的音頻混合)
    //audioMixInputParametersWithTrack(音頻混音輸入?yún)?shù)與軌道)
    AVMutableAudioMixInputParameters *trackMix =[AVMutableAudioMixInputParameters audioMixInputParametersWithTrack:track];
    [trackMix setVolume:0.8f atTime:startTime];
    
    //素材加入數(shù)組
    [audioMixParams addObject:trackMix];
    
    //Insert audio into track  //offsetCMTimeMake(0, 44100)
    ok = [track insertTimeRange:tRange ofTrack:sourceAudioTrack atTime:kCMTimeInvalid error:&error];
}

3、多個(gè)視頻合稱為一個(gè)視頻

// self.firstAsset、self.secondAsset 為視頻的地址, AVAsset 對(duì)象
// 沙河地址可轉(zhuǎn)為  AVURLAsset 對(duì)象
- (IBAction)mergeAction:(UIButton *)sender {
    
    if (self.firstAsset == nil || self.secondAsset == nil) {
        return;
    }
    
    AVMutableComposition *composition = [[AVMutableComposition alloc] init];
    AVMutableCompositionTrack *firstCompositionTrack = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
    
    [firstCompositionTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, self.firstAsset.duration) ofTrack:[[self.firstAsset tracksWithMediaType:AVMediaTypeVideo] firstObject] atTime:kCMTimeZero error:nil];
    [firstCompositionTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, self.secondAsset.duration) ofTrack:[[self.secondAsset tracksWithMediaType:AVMediaTypeVideo] firstObject] atTime:kCMTimeZero error:nil];
    // ...可繼續(xù)添加視頻    


    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *ducumentDirectory = [paths objectAtIndex:0];
    NSString *myPathDocs = [ducumentDirectory stringByAppendingPathComponent:[NSString stringWithFormat:@"mergeVideo-%d.mov",arc4random() % 1000]];
    NSURL *url = [NSURL fileURLWithPath:myPathDocs];
    
    AVAssetExportSession *export = [[AVAssetExportSession alloc] initWithAsset:composition presetName:AVAssetExportPresetHighestQuality];
    export.outputURL = url;
    export.outputFileType = AVFileTypeQuickTimeMovie;
    export.shouldOptimizeForNetworkUse = YES;
    [export exportAsynchronouslyWithCompletionHandler:^{
        dispatch_async(dispatch_get_main_queue(), ^{
            [self exportDidFinish:export];
        });
    }];
    
}

- (void)exportDidFinish:(AVAssetExportSession*)session {
    if (session.status == AVAssetExportSessionStatusCompleted) {
        NSURL *outputURL = session.outputURL;
        ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
        if ([library videoAtPathIsCompatibleWithSavedPhotosAlbum:outputURL]) {
            [library writeVideoAtPathToSavedPhotosAlbum:outputURL completionBlock:^(NSURL *assetURL, NSError *error){
                dispatch_async(dispatch_get_main_queue(), ^{
                    if (error) {
                        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error" message:@"Video Saving Failed"
                                                                       delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
                        [alert show];
                    } else {
                        AVPlayerItem * playeritem = [AVPlayerItem playerItemWithURL:outputURL];
                        [_player replaceCurrentItemWithPlayerItem:playeritem];
                        [_player play];
                        
                        //                        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Video Saved" message:@"Saved To Photo Album"
                        //                                                                       delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
                        //                        [alert show];
                    }
                });
            }];
        }
    }
}


下載地址:https://github.com/chjwrr/SyntheticVideoWithAudio

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容