I have an application that allows you to add multiple video attributes and add one or more audio tracks to the composition. Everything seems to work, I can reproduce the resulting composition with AVPlayer(although the sound level seems low). After exporting the song to a file, there is no sound track.
My code is largely based on the sample AVEditDemo code from WWDC10 sessions. I double checked my code for AVEditDemo code and cannot find what might be the problem. I also checked the forums, but not many posts / solutions related to AVFoundation.
Any help is appreciated. Cheers
Jean pierre
A method of constructing a composition with additional audio tracks
Notes:
compositionArray: contains the assets to create the composition.
AssetView: an object containing AVURLAsset.
- (AVMutableComposition *)buildCompositionObjects
{
if ([compositionArray count] < 1)
{
return nil;
}
AssetView * view = [compositionArray objectAtIndex:0];
AVURLAsset * asset = view.asset;
CGSize videoSize = [asset naturalSize];
AVMutableComposition * cmp = [AVMutableComposition composition];
cmp.naturalSize = videoSize;
[self buildComposition:cmp];
[self addAudioTrackToComposition:cmp];
return cmp;
}
The way to build a basic composition
- (void) buildComposition:(AVMutableComposition *)cmp
{
CMTime nextClipStartTime = kCMTimeZero;
[cmp removeTimeRange:CMTimeRangeMake(CMTimeMake(0, 600), cmp.duration)];
AVMutableCompositionTrack *compositionVideoTrack = [cmp addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
AVMutableCompositionTrack *compositionAudioTrack = [cmp addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
for (AssetView * view in compositionArray)
{
AVURLAsset *asset = view.asset;
CMTimeRange timeRangeInAsset;
timeRangeInAsset = CMTimeRangeMake(kCMTimeZero, [asset duration]);
AVAssetTrack *clipVideoTrack = [[asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
[compositionVideoTrack insertTimeRange:timeRangeInAsset ofTrack:clipVideoTrack atTime:nextClipStartTime error:nil];
if ([[asset tracksWithMediaType:AVMediaTypeAudio] count] > 0)
{
AVAssetTrack *clipAudioTrack = [[asset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0];
[compositionAudioTrack insertTimeRange:timeRangeInAsset ofTrack:clipAudioTrack atTime:nextClipStartTime error:nil];
}
nextClipStartTime = CMTimeAdd(nextClipStartTime, timeRangeInAsset.duration);
}
}
Method for adding additional audio tracks
- (void)addAudioTrackToComposition:(AVMutableComposition *)cmp
{
if ([audioTracks count] < 1)
{
return;
}
long baseTrackID = 100;
for (AVURLAsset * audioAsset in audioTracks)
{
CMTimeRange commentaryTimeRange = CMTimeRangeMake(kCMTimeZero, audioAsset.duration);
if (CMTIME_COMPARE_INLINE(CMTimeRangeGetEnd(commentaryTimeRange), >, [cmp duration]))
{
commentaryTimeRange.duration = CMTimeSubtract([cmp duration], commentaryTimeRange.start);
}
AVMutableCompositionTrack *compositionCommentaryTrack = [cmp addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:baseTrackID++];
[compositionCommentaryTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, commentaryTimeRange.duration) ofTrack:[[audioAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0] atTime:commentaryTimeRange.start error:nil];
}
}
Composition export method
- (void) save
{
NSString * eventFolder = [NSString stringWithFormat:@"%@/%@-%@",
DOCUMENTS_FOLDER,
event.title,
[StringUtils stringForDate:event.timeStamp]];
NSString * exportVideoPath = [NSString stringWithFormat:@"%@/Edits/%@.MOV", eventFolder, [StringUtils stringForDate:[NSDate date]]];
video.path = exportVideoPath;
AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:composition presetName:AVAssetExportPresetHighestQuality];
NSURL *exportURL = [NSURL fileURLWithPath:exportVideoPath];
exportSession.outputURL = exportURL;
exportSession.outputFileType = AVFileTypeQuickTimeMovie;
[exportSession exportAsynchronouslyWithCompletionHandler:^
{
switch (exportSession.status)
{
case AVAssetExportSessionStatusFailed:
{
NSLog (@"FAIL");
[self performSelectorOnMainThread:@selector (doPostExportFailed:)
withObject:nil
waitUntilDone:NO];
break;
}
case AVAssetExportSessionStatusCompleted:
{
NSLog (@"SUCCESS");
[self performSelectorOnMainThread:@selector (doPostExportSuccess:)
withObject:nil
waitUntilDone:NO];
break;
}
case AVAssetExportSessionStatusCancelled:
{
NSLog (@"CANCELED");
[self performSelectorOnMainThread:@selector (doPostExportCancelled:)
withObject:nil
waitUntilDone:NO];
break;
}
};
}];
}