今回は動画に音を追加する。
音は短い効果音であっても良いし、長いBGMであっても良い。
ここでは動画よりも長い曲をBGMとして追加することにする。

元動画とBGM、出力動画のイメージはこのようになる。
movie_add_sound_ss1

ソースコードはこうだ。
const int kVideoFPS = 30;

- (void)addSoundToMovie
{
    // 1
    NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
    NSString *inputPath = [[path stringByAppendingPathComponent:@"portrait1"] stringByAppendingPathExtension:@"mov"];
    NSString *outputPath = [[path stringByAppendingPathComponent:@"result"] stringByAppendingPathExtension:@"mov"];
    
    AVMutableComposition *composition = [AVMutableComposition composition];
    AVMutableCompositionTrack *compositionVideoTrack = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
    AVMutableCompositionTrack *compositionAudioTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
    NSError *error;
    
    NSURL *inputURL = [NSURL fileURLWithPath:inputPath];
    AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:inputURL options:nil];
    
    // 2
    CMTimeRange range = CMTimeRangeMake(kCMTimeZero, asset.duration);
    
    AVAssetTrack *videoTrack = [asset tracksWithMediaType:AVMediaTypeVideo][0];
    AVAssetTrack *audioTrack = [asset tracksWithMediaType:AVMediaTypeAudio][0];
    
    [compositionVideoTrack insertTimeRange:range ofTrack:videoTrack atTime:kCMTimeZero error:&error];
    [compositionAudioTrack insertTimeRange:range ofTrack:audioTrack atTime:kCMTimeZero error:&error];
    
    AVMutableVideoCompositionInstruction *instruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
    instruction.timeRange = range;
    
    AVMutableVideoCompositionLayerInstruction *layerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:compositionVideoTrack];
    
    // 3
    NSString *soundPath = [[NSBundle mainBundle] pathForResource:@"sound" ofType:@"caf"];
    NSURL *soundURL = [NSURL fileURLWithPath:soundPath];
    AVURLAsset *soundAsset = [[AVURLAsset alloc] initWithURL:soundURL options:nil];
    AVAssetTrack *soundTrack = [soundAsset tracksWithMediaType:AVMediaTypeAudio][0];
    AVMutableCompositionTrack *compositionSoundTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
    [compositionSoundTrack insertTimeRange:range ofTrack:soundTrack atTime:kCMTimeZero error:&error];
    
    CGSize videoSize = videoTrack.naturalSize;
    CGAffineTransform transform = videoTrack.preferredTransform;;
    if (transform.a == 0 && transform.d == 0 && (transform.b == 1.0 || transform.b == -1.0) && (transform.c == 1.0 || transform.c == -1.0))
    {
        videoSize = CGSizeMake(videoSize.height, videoSize.width);
    }
    
    [layerInstruction setTransform:transform atTime:kCMTimeZero];
    instruction.layerInstructions = @[layerInstruction];
    
    AVMutableVideoComposition *videoComposition = [AVMutableVideoComposition videoComposition];
    videoComposition.renderSize = videoSize;
    videoComposition.instructions = @[instruction];
    videoComposition.frameDuration = CMTimeMake(1, kVideoFPS);
    
    NSFileManager *fm = [NSFileManager defaultManager];
    if ([fm fileExistsAtPath:outputPath])
    {
        [fm removeItemAtPath:outputPath error:&error];
    }
    
    AVAssetExportSession *session = [[AVAssetExportSession alloc] initWithAsset:composition presetName:AVAssetExportPresetHighestQuality];
    session.outputURL = [NSURL fileURLWithPath:outputPath];
    session.outputFileType = AVFileTypeQuickTimeMovie;
    session.videoComposition = videoComposition;
    
    [session exportAsynchronouslyWithCompletionHandler:^{
        if (session.status == AVAssetExportSessionStatusCompleted)
        {
            NSLog(@"output complete!");
        }
        else
        {
            NSLog(@"output error! : %@", session.error);
        }
    }];
}
基本的には以前の記事([iOS] 動画を加工・編集する(1) 指定した時間の範囲を切り出す)と大差ない。
異なっている箇所を中心に説明していく。

1.今回は元動画の長さはそのままで、そこにBGMを追加するので開始時間とデュレーションは必要無い。
不要な変数は混乱のもととなるので、削除してある。

2.出力動画の長さを変えないということは、元動画と出力動画の長さが同じということなので、CMTimeRange変数はrangeという一つに統一しておく。

3.やっていることは以前の記事と基本的には同じだ。
BGM用のアセットとトラックを準備して、追加しているだけである。

これで処理は完成だ。
実行してみよう。
元動画にBGMが追加されているはずである。

関連記事
[iOS] 動画を加工・編集する(1) 指定した時間の範囲を切り出す
[iOS] 動画を加工・編集する(2) エラーコード-11841に気をつける
[iOS] 動画を加工・編集する(4) 動画をクリッピングする
[iOS] 動画を加工・編集する(5) 動画を拡大縮小する
[iOS] 動画を加工・編集する(6) 複数の動画を連結する