AVFoundation视频添加背景音乐(六)

2020-10-11  本文已影响0人  仙人掌__

前言

从本文开始逐渐学习iOS自带的多媒体处理框架,例如AVFoundation,VideoToolbox,CoreMedia,CoreVideo实现多媒体的处理,并且将实现方式以及效果和ffmpeg的方式做对比

给一个视频添加音乐,将多段音视频文件合并为一个文件是很常见的需求,AVFoundation就提供了这样的接口。

本文的目的:
为一段视频添加背景音乐

音视频合并相关流程

image.png

上图介绍了AVFoundation框架中关于合并音视频文件的相关的对象关系图,可以看到使用AVFoundation合并音视频还是相对比较简单的。

相关对象及函数介绍

实现代码

#import <Foundation/Foundation.h>

@interface AVMYComposition : NSObject

/** 实现音视频合并功能
 *  1、要合并的视频时长大于任何一个音频的时长,有可能小于两段音频的时长
 *  2、以视频的时长为基准,如果两段音频的时长之和大于视频时长,则截取掉第二个音频的部分时间
 */
- (void)startMerge:(NSURL*)audioUrl audio2:(NSURL*)audioUrl2 videoUrl:(NSURL*)videoUrl dst:(NSURL*)dsturl;

@end
import "AVMYComposition.h"
#import <AVFoundation/AVFoundation.h>

@implementation AVMYComposition
{
    dispatch_semaphore_t processSemaphore;
}
- (void)startMerge:(NSURL*)audioUrl1 audio2:(NSURL*)audioUrl2 videoUrl:(NSURL*)videoUrl dst:(NSURL*)dsturl
{
    processSemaphore = dispatch_semaphore_create(0);
    
    /** AVMutableComposition对象
     * 组合对象,它是AVAsset的子类,通过它来实现音视频的合并。它就相当于一个编辑容器,每一个要被合并的
     * 音频或者视频轨道被组装为AVMutableCompositionTrack然后进行合并
     *
     *  AVMutableCompositionTrack组合对象轨道,他是AVAssetTrack的子类。代表了每一个要被合并的音频或者视频轨道
     */
    AVMutableComposition *mixComposition = [AVMutableComposition composition];
    
    // 添加一个组合对象轨道,用于添加视频轨道
    AVMutableCompositionTrack *videoCompostioTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
    AVURLAsset *videoAsset = [[AVURLAsset alloc] initWithURL:videoUrl options:nil];
    CMTime videoDuration = videoAsset.duration;
    AVAssetTrack *vdeotrack = [[videoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
    CMTimeRange videoTiemRange = CMTimeRangeMake(kCMTimeZero, videoDuration);
    NSError *error = nil;
    [videoCompostioTrack insertTimeRange:videoTiemRange ofTrack:vdeotrack atTime:kCMTimeZero error:&error];
    if (error) {
        NSLog(@"video insert error %@",error);
        return;
    }
    
    // 添加一个组合对象轨道,第二个参数为kCMPersistentTrackID_Invalid代表由系统自动生成ID
    AVMutableCompositionTrack *audioComTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
    AVURLAsset *audioAsset1 = [AVURLAsset assetWithURL:audioUrl1];
    // 将同步解析,会阻塞当前线程
    CMTime duration1 = audioAsset1.duration;
    AVAssetTrack *audioTrack1 = [[audioAsset1 tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0];
    CMTimeRange firstTimeRange = CMTimeRangeMake(kCMTimeZero,duration1);
    // 往组合对象轨道中添加轨道对象
    [audioComTrack insertTimeRange:firstTimeRange ofTrack:audioTrack1 atTime:kCMTimeZero error:&error];
    if (error) {
        NSLog(@"audio track %@",error);
        return;
    }
   
    AVURLAsset *audioAsset2 = [AVURLAsset assetWithURL:audioUrl2];
    // 阻塞当前线程
    CMTime duration2 = audioAsset2.duration;
    CMTime newDuration2 = duration2;
    if (CMTimeGetSeconds(duration1)+CMTimeGetSeconds(duration2) > CMTimeGetSeconds(videoDuration) && CMTimeGetSeconds(duration1) < CMTimeGetSeconds(duration2)) {
        newDuration2 = CMTimeSubtract(videoDuration, duration1);
    }
    CMTimeRange secondTimeRange = CMTimeRangeMake(kCMTimeZero, newDuration2);
    NSLog(@" tt %f tt %f",CMTimeGetSeconds(duration1),CMTimeGetSeconds(newDuration2));
    AVAssetTrack *audioTrack2 = [[audioAsset2 tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0];
    /** 参数解释:
     *  timeRange:代表截取track的时间范围内容然后插入这个组合对象的轨道中
     *  startTime:代表将这段内容按组对象轨道时间轴的指定位置插入
     */
    [audioComTrack insertTimeRange:secondTimeRange ofTrack:audioTrack2 atTime:duration1 error:&error];
    
    // 执行合并
    if ([[NSFileManager defaultManager] fileExistsAtPath:dsturl.path]) {
        [[NSFileManager defaultManager] removeItemAtURL:dsturl error:nil];
    }
    
    // 合并导出会话
    AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:mixComposition presetName:AVAssetExportPresetHighestQuality];
    exportSession.outputURL = dsturl;
    exportSession.outputFileType = AVFileTypeQuickTimeMovie;
    
    [exportSession exportAsynchronouslyWithCompletionHandler:^{
        NSLog(@"over");
        dispatch_semaphore_signal(self->processSemaphore);
    }];
    
    dispatch_semaphore_wait(processSemaphore, DISPATCH_TIME_FOREVER);
    
    NSLog(@"结束了");
}
@end

遇到问题

项目地址

https://github.com/nldzsz/ffmpeg-demo

位于AVFoundation目录下文件AVMYComposition.h/AVMYComposition.m中

上一篇下一篇

猜你喜欢

热点阅读