I have recorded one video and save it into document directory.
Now i want to edit video to slow,fast or normal speed.
I can able to do fast and normal speed of the video.
But i can't able to do slow speed of the video.It's alway converting to normal speed.
here is my code
AVMutableComposition *composition = [AVMutableComposition composition];
AVURLAsset * sourceAsset = [[AVURLAsset alloc] initWithURL:[NSURL fileURLWithPath:inputVideoPath] options:nil];
AVMutableCompositionTrack *compositionVideoTrack = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
BOOL ok = NO;
AVAssetTrack * sourceVideoTrack = [[sourceAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
CMTimeRange x = CMTimeRangeMake(kCMTimeZero, [sourceAsset duration]);
ok = [compositionVideoTrack insertTimeRange:x ofTrack:sourceVideoTrack atTime:kCMTimeZero error:nil];
CMTime fiveSecondsIn;
if([Cell.text isEqualToString:#"Fast"]){
fiveSecondsIn = CMTimeMake(10,1);
}
else if([Cell.text isEqualToString:#"Normal"]){
fiveSecondsIn = CMTimeMake(1,1);
}
else if([Cell.textLabel.text isEqualToString:#"Slow"]){
fiveSecondsIn = CMTimeMake(1,.5);
}
CMTimeRange video_timeRange = CMTimeRangeMake(kCMTimeZero,sourceAsset.duration);
[compositionVideoTrack scaleTimeRange:video_timeRange toDuration:fiveSecondsIn];
if([[NSFileManager defaultManager] fileExistsAtPath:filePath]){
[[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
}
NSURL *exportUrl = [NSURL fileURLWithPath:filePath];
AVAssetExportSession *exporter = [[AVAssetExportSession alloc]initWithAsset:composition presetName:AVAssetExportPresetHighestQuality];
exporter.outputURL=exportUrl;
exporter.outputFileType = #"com.apple.quicktime-movie";
[exporter exportAsynchronouslyWithCompletionHandler:^(void){
[self performSelectorOnMainThread:#selector(playnewVideo) withObject:nil waitUntilDone:NO];
}];
Related
I am mixing an audio and video file using AVMutableComposition. Below is my code for that:
enter code here
AVMutableComposition* mixComposition = [AVMutableComposition composition];
NSString *bundleDirectory = [[NSBundle mainBundle] bundlePath];
NSString *audio_inputFilePath = [bundleDirectory stringByAppendingPathComponent:#"xyz.mp3"];//audio of 35 seconds
NSURL *audio_inputFileUrl = [NSURL fileURLWithPath:audio_inputFilePath];
NSURL *video_inputFileUrl = [NSURL fileURLWithPath:videoOutputPath];
NSString *outputFilePath = [documentsDirectory stringByAppendingPathComponent:#"video.mp4"];//video of 60 seconds
NSURL *outputFileUrl = [NSURL fileURLWithPath:outputFilePath];
if ([[NSFileManager defaultManager] fileExistsAtPath:outputFilePath])
[[NSFileManager defaultManager] removeItemAtPath:outputFilePath error:nil];
CMTime nextClipStartTime = kCMTimeZero;
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];
enter code here
CMTimeRange audio_timeRange = CMTimeRangeMake(kCMTimeZero, audioAsset.duration);
enter code here
AVMutableCompositionTrack *b_compositionAudioTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
[b_compositionAudioTrack insertTimeRange:audio_timeRange ofTrack:[[audioAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0] atTime:nextClipStartTime error:nil];
enter code here
AVAssetExportSession* _assetExport = [[AVAssetExportSession alloc] initWithAsset:mixComposition presetName:AVAssetExportPreset640x480];
Issue i am facing is my audio file duration is shorter than the video duration. So what i want to do is loop the audio file till the video ends. Like my video is is 60 seconds and audio is 35 seconds, so the audio should repeat for 25 seconds.
Can anyone help me how this can be done.
Solution 1: Create a new CMTimeRange and insertTimeRange in AVMutableCompositionTrack
if (videoAsset.duration>audioAsset.duration)
{
//new time range
CMTime duration = CMTimeMakeWithSeconds(CMTimeGetSeconds(videoAsset.duration)-CMTimeGetSeconds(audioAsset.duration), audioAsset.duration.timescale);
if (CMTIME_IS_VALID(duration))
{
CMTimeRange video_timeRange2 = CMTimeRangeMake(audioAsset.duration,duration);
//start from where left
CMTime nextClipStartTime2 = audioAsset.duration;
//add in AVMutableCompositionTrack
[b_compositionVideoTrack insertTimeRange:video_timeRange2 ofTrack:[[videoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0] atTime:nextClipStartTime2 error:nil];
}
else
NSLog(#"time is invalid");
}
NOTE: not tested but it should work.
EDIT:
Solution 2: Try this. Don't use my code and replace this line below with yours
[b_compositionAudioTrack insertTimeRange:audio_timeRange ofTrack:[[audioAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0] atTime:kCMTimeInvalid error:nil];
I wanna merge a .caf file and a .mp3 file to a .mp3 file on iPhone,or I can convert them to .aac and then merge them.How can I do this ?
(Just Like Kala OK,I wanna merge my voice and the music together)
You need to decode both files to LPCM (plain old ints), add/mix them together then re-encode. The iPhone SDK does not support MP3 encoding, so you should re-encode to AAC.
The Apple sample code iPhoneExtAudioFileConvertTest would be a good place to start.
You can use this method
- (BOOL) combineVoices1
{
NSError *error = nil;
BOOL ok = NO;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
CMTime nextClipStartTime = kCMTimeZero;
//Create AVMutableComposition Object.This object will hold our multiple AVMutableCompositionTrack.
AVMutableComposition *composition = [[AVMutableComposition alloc] init];
AVMutableCompositionTrack *compositionAudioTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
[compositionAudioTrack setPreferredVolume:0.8];
NSString *soundOne =[[NSBundle mainBundle]pathForResource:#"test1" ofType:#"caf"];
NSURL *url = [NSURL fileURLWithPath:soundOne];
AVAsset *avAsset = [AVURLAsset URLAssetWithURL:url options:nil];
NSArray *tracks = [avAsset tracksWithMediaType:AVMediaTypeAudio];
AVAssetTrack *clipAudioTrack = [[avAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0];
[compositionAudioTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, avAsset.duration) ofTrack:clipAudioTrack atTime:kCMTimeZero error:nil];
AVMutableCompositionTrack *compositionAudioTrack1 = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
[compositionAudioTrack setPreferredVolume:0.3];
NSString *soundOne1 =[[NSBundle mainBundle]pathForResource:#"test" ofType:#"caf"];
NSURL *url1 = [NSURL fileURLWithPath:soundOne1];
AVAsset *avAsset1 = [AVURLAsset URLAssetWithURL:url1 options:nil];
NSArray *tracks1 = [avAsset1 tracksWithMediaType:AVMediaTypeAudio];
AVAssetTrack *clipAudioTrack1 = [[avAsset1 tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0];
[compositionAudioTrack1 insertTimeRange:CMTimeRangeMake(kCMTimeZero, avAsset.duration) ofTrack:clipAudioTrack1 atTime:kCMTimeZero error:nil];
AVMutableCompositionTrack *compositionAudioTrack2 = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
[compositionAudioTrack2 setPreferredVolume:1.0];
NSString *soundOne2 =[[NSBundle mainBundle]pathForResource:#"song" ofType:#"caf"];
NSURL *url2 = [NSURL fileURLWithPath:soundOne2];
AVAsset *avAsset2 = [AVURLAsset URLAssetWithURL:url2 options:nil];
NSArray *tracks2 = [avAsset2 tracksWithMediaType:AVMediaTypeAudio];
AVAssetTrack *clipAudioTrack2 = [[avAsset2 tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0];
[compositionAudioTrack1 insertTimeRange:CMTimeRangeMake(kCMTimeZero, avAsset2.duration) ofTrack:clipAudioTrack2 atTime:kCMTimeZero error:nil];
AVAssetExportSession *exportSession = [AVAssetExportSession
exportSessionWithAsset:composition
presetName:AVAssetExportPresetAppleM4A];
if (nil == exportSession) return NO;
NSString *soundOneNew = [documentsDirectory stringByAppendingPathComponent:#"combined10.m4a"];
//NSLog(#"Output file path - %#",soundOneNew);
// configure export session output with all our parameters
exportSession.outputURL = [NSURL fileURLWithPath:soundOneNew]; // output path
exportSession.outputFileType = AVFileTypeAppleM4A; // output file type
// perform the export
[exportSession exportAsynchronouslyWithCompletionHandler:^{
if (AVAssetExportSessionStatusCompleted == exportSession.status) {
NSLog(#"AVAssetExportSessionStatusCompleted");
} else if (AVAssetExportSessionStatusFailed == exportSession.status) {
// a failure may happen because of an event out of your control
// for example, an interruption like a phone call comming in
// make sure and handle this case appropriately
NSLog(#"AVAssetExportSessionStatusFailed");
} else {
NSLog(#"Export Session Status: %d", exportSession.status);
}
}];
return YES;
}
you can use the this SDAVAssetExportSession with the following code to export many files to AAC:
-(void)mergeAudioFiles
{
NSFileManager * fm = [[NSFileManager alloc] init];
NSError * error;
NSArray * filesNames = **NSArray of File Names;
NSString * filePath = #"Dest File Name";
NSString * pathToSave =[NSString stringWithFormat:#"%#%#",filePath,#".m4a"];
CMTime startTime = kCMTimeZero;
AVMutableComposition *composition = [AVMutableComposition composition];
AVMutableCompositionTrack *compositionAudioTrack =[AVMutableCompositionTrack alloc];
compositionAudioTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
float audioEndTime=0;
for (NSString *fileName in filesNames) {
NSURL *audioUrl = [NSURL fileURLWithPath:fileName];
AVURLAsset *audioasset = [[AVURLAsset alloc]initWithURL:audioUrl options:nil];
CMTimeRange timeRange = CMTimeRangeMake(kCMTimeZero, audioasset.duration);
AVAssetTrack *audioAssetTrack= [[audioasset tracksWithMediaType:AVMediaTypeAudio] lastObject];
[compositionAudioTrack insertTimeRange:timeRange ofTrack:audioAssetTrack atTime:startTime error:&error];
startTime = CMTimeAdd(startTime, timeRange.duration);
CMTime assetTime2 = [audioasset duration];
Float64 duration2 = CMTimeGetSeconds(assetTime2);
audioEndTime+=duration2;
}
NSURL *exportUrl = [NSURL fileURLWithPath:pathToSave];
float audioStartTime=0;
CMTime startTime1 = CMTimeMake((int)(floor(audioStartTime * 100)), 100);
CMTime stopTime = CMTimeMake((int)(ceil(audioEndTime * 100)), 100);
CMTimeRange exportTimeRange = CMTimeRangeFromTimeToTime(startTime1, stopTime);
SDAVAssetExportSession *encoder = [SDAVAssetExportSession.alloc initWithAsset:composition];
encoder.outputFileType = AVFileTypeAppleM4A;
encoder.outputURL = exportUrl;
encoder.audioSettings = #
{
AVFormatIDKey: #(kAudioFormatMPEG4AAC),
AVNumberOfChannelsKey: #2,
AVSampleRateKey: #44100,
AVEncoderBitRateKey: #128000,
};
encoder.timeRange = exportTimeRange;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
NSLog(#"Starting Audio Marge");
[encoder exportAsynchronouslyWithCompletionHandler:^
{
if (encoder.status == AVAssetExportSessionStatusCompleted)
{
NSLog(#"Audio Marge succeeded");
NSError * err = NULL;
BOOL result = [fm moveItemAtPath:pathToSave toPath:filePath error:&err];
if(!result) {
NSLog(#"Error: %#", err);
}
NSLog(#"Audio Copied");
} else if (encoder.status == AVAssetExportSessionStatusCancelled) {
NSLog(#"Audio export cancelled");
} else {
NSLog(#"Audio export failed with error: %# (%ld)", encoder.error.localizedDescription, encoder.error.code);
}
dispatch_semaphore_signal(semaphore);
}];
NSLog(#"Audio Wait to Finish");
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
//cleanup
for (NSString *fileName in filesNames) {
[fm removeItemAtPath:fileName error:&error];
}
NSLog(#"Audio Marge Finished");
}
merging two audio files code
-(void)mergeTwoAudioFile
{
NSError * error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [[paths objectAtIndex:0] stringByAppendingPathComponent:#"Music_Directory"];
//first audio path
NSString *firstPath= [documentsDirectory stringByAppendingPathComponent:#"audio01.m4a"];
NSURL *audioUrl1 = [NSURL fileURLWithPath:firstPath];
AVMutableComposition *mixComposition = [AVMutableComposition composition];
AVMutableCompositionTrack *audioCompositionTrack1 =[[AVMutableCompositionTrack alloc]init];
AVURLAsset *audioasset1 = [[AVURLAsset alloc]initWithURL:audioUrl1 options:nil];
AVAssetTrack *audioAssetTrack1= [[audioasset1 tracksWithMediaType:AVMediaTypeAudio] lastObject];
audioCompositionTrack1 = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
CMTime tempTime1= mixComposition.duration;
[audioCompositionTrack1 insertTimeRange:CMTimeRangeMake(kCMTimeZero, audioasset1.duration) ofTrack:audioAssetTrack1 atTime:tempTime1 error:&error];
//========second audio
NSString *secondPath= [documentsDirectory stringByAppendingPathComponent:#"audio02.m4a"];
NSURL *audioUrl2 = [NSURL fileURLWithPath:secondPath];
AVMutableCompositionTrack *audioCompositionTrack2 =[[AVMutableCompositionTrack alloc]init];
AVURLAsset *audioasset2 = [[AVURLAsset alloc]initWithURL:audioUrl2 options:nil];
AVAssetTrack *audioAssetTrack2= [[audioasset2 tracksWithMediaType:AVMediaTypeAudio] lastObject];
audioCompositionTrack2 = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
CMTime tempTime2 = mixComposition.duration;
[audioCompositionTrack2 insertTimeRange:CMTimeRangeMake(kCMTimeZero, audioasset2.duration) ofTrack:audioAssetTrack2 atTime:tempTime2 error:&error];
NSString * pathToSave = [NSString stringWithFormat:#"finalTest.m4a"];
pathToSave =[documentsDirectory stringByAppendingPathComponent:pathToSave];
NSURL *movieUrl = [NSURL fileURLWithPath:pathToSave];
AVAssetExportSession *exporter =[[AVAssetExportSession alloc] initWithAsset:mixComposition presetName:AVAssetExportPresetAppleM4A];
exporter.shouldOptimizeForNetworkUse = YES;
exporter.outputURL = movieUrl;
exporter.outputFileType = AVFileTypeAppleM4A;
//====================================================================
float audioStartTime=0;
CMTime assetTime1 = [audioasset1 duration];
Float64 duration1 = CMTimeGetSeconds(assetTime1);
CMTime assetTime2 = [audioasset2 duration];
Float64 duration2 = CMTimeGetSeconds(assetTime2);
float audioEndTime=duration1+duration2;
CMTime startTime = CMTimeMake((int)(floor(audioStartTime * 100)), 100);
CMTime stopTime = CMTimeMake((int)(ceil(audioEndTime * 100)), 100);
CMTimeRange exportTimeRange = CMTimeRangeFromTimeToTime(startTime, stopTime);
exporter.timeRange =exportTimeRange;
//====================================================================
[exporter exportAsynchronouslyWithCompletionHandler:^(void) {
NSString* message;
switch (exporter.status) {
case AVAssetExportSessionStatusFailed:
message = [NSString stringWithFormat:#"Export failed. Error: %#", exporter.error.description];
NSLog(#"%#", message);
break;
case AVAssetExportSessionStatusCompleted:
message = [NSString stringWithFormat:#"Export completed"];
NSLog(#"%#", message);
break;
case AVAssetExportSessionStatusCancelled:
message = [NSString stringWithFormat:#"Export cancelled!"];
NSLog(#"%#", message);
break;
default:
NSLog(#"Export unhandled status: %ld", (long)exporter.status);
break;
}
}];
}
My question is: How to merge video and audio files that has almost the same duration?
I searched and got some answers to this question. However when I try the code they gave, it just does not produce a "non-zero byte" movie.
Could you take a look at it and see where it went wrong?
-(void)putTogether
{
NSLog(#"Starting to put together all the files!");
AVMutableComposition *mixComposition = [AVMutableComposition composition];
NSString *audioPath = #"/Users/admin/Documents/Sound.caf";
NSURL *audioUrl = [NSURL fileURLWithPath:audioPath];
//AVURLAsset *audioasset = [AVURLAsset URLAssetWithURL:audioUrl options:nil];
AVURLAsset *audioasset = [[AVURLAsset alloc]initWithURL:audioUrl options:nil];
NSString *videoPath = #"/Users/admin/Documents/video.mp4";
NSURL *videoUrl = [NSURL fileURLWithPath:videoPath];
//AVURLAsset *videoasset = [AVURLAsset URLAssetWithURL:videoUrl options:nil];
AVURLAsset *videoasset = [[AVURLAsset alloc]initWithURL:videoUrl options:nil];
NSString *moviepath = #"/Users/admin/Documents/fmovie.mov";
NSURL *movieUrl = [NSURL fileURLWithPath:moviepath];
if([[NSFileManager defaultManager] fileExistsAtPath:moviepath])
{
[[NSFileManager defaultManager] removeItemAtPath:moviepath error:nil];
}
AVMutableCompositionTrack *compositionTrackB = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
AVAssetTrack *clipVideoTrackB = [[videoasset tracksWithMediaType:AVMediaTypeVideo] lastObject];
[compositionTrackB insertTimeRange:CMTimeRangeMake( kCMTimeZero, videoasset.duration) ofTrack:clipVideoTrackB atTime:kCMTimeZero error:nil];
AVMutableCompositionTrack *compositionTrackA = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
AVAssetTrack *clipAudioTrackA = [[audioasset tracksWithMediaType:AVMediaTypeAudio] lastObject];
[compositionTrackA insertTimeRange:CMTimeRangeMake(kCMTimeZero, audioasset.duration) ofTrack:clipAudioTrackA atTime:kCMTimeZero error:nil];
AVAssetExportSession *exporter =[[AVAssetExportSession alloc] initWithAsset:mixComposition presetName:AVAssetExportPresetHighestQuality];
//AVAssetExportSession *exporter =[AVAssetExportSession exportSessionWithAsset:mixComposition presetName:AVAssetExportPresetLowQuality];
NSParameterAssert(exporter!=nil);
exporter.outputFileType=AVFileTypeQuickTimeMovie;
exporter.outputURL=movieUrl;
CMTime start=CMTimeMake(0, 600);
CMTime duration=CMTimeMake(600, 600);
CMTimeRange range=CMTimeRangeMake(start, duration);
exporter.timeRange=range;
[exporter exportAsynchronouslyWithCompletionHandler:nil];
}
(Answered by the OP by an edit in the question. Converted to a community wiki answer. See Question with no answers, but issue solved in the comments (or extended in chat) )
The OP wrote:
I solved my problem.
For the ones who have same problem as I did, here is the simple solution:
a.Delete the following code:
CMTime start=CMTimeMake(0, 600);
CMTime duration=CMTimeMake(600, 600);
CMTimeRange range=CMTimeRangeMake(start, duration);
exporter.timeRange=range;
b.(optional)Rewrite completion handler:
[exporter exportAsynchronouslyWithCompletionHandler:^{
switch ([exporter status]) {
case AVAssetExportSessionStatusFailed:
NSLog(#"Export failed: %#", [[exporter error] localizedDescription]);
break;
case AVAssetExportSessionStatusCancelled:
NSLog(#"Export canceled");
break;
default:
break;
}
}];
c.Then wait long enough for the program to complete writing.
Im trying to create a video that shows two videos one after the other using avcomposition on the iphone. This code works, however i can only see one of the videos for the entire duration of the newly created video
- (void) startEdit{
AVMutableComposition* mixComposition = [AVMutableComposition composition];
NSString* a_inputFileName = #"export.mov";
NSString* a_inputFilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:a_inputFileName];
NSURL* a_inputFileUrl = [NSURL fileURLWithPath:a_inputFilePath];
NSString* b_inputFileName = #"output.mov";
NSString* b_inputFilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:b_inputFileName];
NSURL* b_inputFileUrl = [NSURL fileURLWithPath:b_inputFilePath];
NSString* outputFileName = #"outputFile.mov";
NSString* outputFilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:outputFileName];
NSURL* outputFileUrl = [NSURL fileURLWithPath:outputFilePath];
if ([[NSFileManager defaultManager] fileExistsAtPath:outputFilePath])
[[NSFileManager defaultManager] removeItemAtPath:outputFilePath error:nil];
CMTime nextClipStartTime = kCMTimeZero;
AVURLAsset* a_videoAsset = [[AVURLAsset alloc]initWithURL:a_inputFileUrl options:nil];
CMTimeRange a_timeRange = CMTimeRangeMake(kCMTimeZero,a_videoAsset.duration);
AVMutableCompositionTrack *a_compositionVideoTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
[a_compositionVideoTrack insertTimeRange:a_timeRange ofTrack:[[a_videoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0] atTime:nextClipStartTime error:nil];
nextClipStartTime = CMTimeAdd(nextClipStartTime, a_timeRange.duration);
AVURLAsset* b_videoAsset = [[AVURLAsset alloc]initWithURL:b_inputFileUrl options:nil];
CMTimeRange b_timeRange = CMTimeRangeMake(kCMTimeZero, b_videoAsset.duration);
AVMutableCompositionTrack *b_compositionVideoTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
[b_compositionVideoTrack insertTimeRange:b_timeRange ofTrack:[[b_videoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0] atTime:nextClipStartTime error:nil];
AVAssetExportSession* _assetExport = [[AVAssetExportSession alloc] initWithAsset:mixComposition presetName:AVAssetExportPresetLowQuality];
_assetExport.outputFileType = #"com.apple.quicktime-movie";
_assetExport.outputURL = outputFileUrl;
[_assetExport exportAsynchronouslyWithCompletionHandler:
^(void ) {
[self saveVideoToAlbum:outputFilePath];
}
];
}
- (void) saveVideoToAlbum:(NSString*)path{
if(UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(path)){
UISaveVideoAtPathToSavedPhotosAlbum (path, self, #selector(video:didFinishSavingWithError: contextInfo:), nil);
}
}
- (void) video: (NSString *) videoPath didFinishSavingWithError: (NSError *) error contextInfo: (void *) contextInfo {
NSLog(#"Finished saving video with error: %#", error);
}
I've posted the whole code as it may help someone else.
Shouldn't
nextClipStartTime = CMTimeAdd(nextClipStartTime, a_timeRange.duration);
[b_compositionVideoTrack insertTimeRange:b_timeRange ofTrack:[[b_videoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0] atTime:nextClipStartTime error:nil];
add the second video to the end of the first
Cheers
Figured it out. Should have only had one AVMutableCompositionTrack.
Like so:
CMTime nextClipStartTime = kCMTimeZero;
AVURLAsset* a_videoAsset = [[AVURLAsset alloc]initWithURL:a_inputFileUrl options:nil];
CMTimeRange a_timeRange = CMTimeRangeMake(kCMTimeZero,a_videoAsset.duration);
AVMutableCompositionTrack *a_compositionVideoTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
[a_compositionVideoTrack insertTimeRange:a_timeRange ofTrack:[[a_videoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0] atTime:nextClipStartTime error:nil];
nextClipStartTime = CMTimeAdd(nextClipStartTime, a_timeRange.duration);
AVURLAsset* b_videoAsset = [[AVURLAsset alloc]initWithURL:b_inputFileUrl options:nil];
CMTimeRange b_timeRange = CMTimeRangeMake(kCMTimeZero, b_videoAsset.duration);
[a_compositionVideoTrack insertTimeRange:b_timeRange ofTrack:[[b_videoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0] atTime:nextClipStartTime error:nil];
I haven't spotted the flaw yet, but I do have a couple of suggestions:
First, capture the error in insertTimeRange and every other opportunity and inspect it.
Second, for the simple case of appending videos, you can use AVMutableComposition without so much track mucking. Use "insertTimeRange:ofAsset:atTime:error:" with AVAssets initialized from the files and you will simplify your code greatly. If you need to do something more complicated such as crossfades, you'll need to use a video composition and audio mix as well, and at that point you can deal with the complexity of tracks.
I wanna merge a .caf file and a .mp3 file to a .mp3 file on iPhone,or I can convert them to .aac and then merge them.How can I do this ?
(Just Like Kala OK,I wanna merge my voice and the music together)
You need to decode both files to LPCM (plain old ints), add/mix them together then re-encode. The iPhone SDK does not support MP3 encoding, so you should re-encode to AAC.
The Apple sample code iPhoneExtAudioFileConvertTest would be a good place to start.
You can use this method
- (BOOL) combineVoices1
{
NSError *error = nil;
BOOL ok = NO;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
CMTime nextClipStartTime = kCMTimeZero;
//Create AVMutableComposition Object.This object will hold our multiple AVMutableCompositionTrack.
AVMutableComposition *composition = [[AVMutableComposition alloc] init];
AVMutableCompositionTrack *compositionAudioTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
[compositionAudioTrack setPreferredVolume:0.8];
NSString *soundOne =[[NSBundle mainBundle]pathForResource:#"test1" ofType:#"caf"];
NSURL *url = [NSURL fileURLWithPath:soundOne];
AVAsset *avAsset = [AVURLAsset URLAssetWithURL:url options:nil];
NSArray *tracks = [avAsset tracksWithMediaType:AVMediaTypeAudio];
AVAssetTrack *clipAudioTrack = [[avAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0];
[compositionAudioTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, avAsset.duration) ofTrack:clipAudioTrack atTime:kCMTimeZero error:nil];
AVMutableCompositionTrack *compositionAudioTrack1 = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
[compositionAudioTrack setPreferredVolume:0.3];
NSString *soundOne1 =[[NSBundle mainBundle]pathForResource:#"test" ofType:#"caf"];
NSURL *url1 = [NSURL fileURLWithPath:soundOne1];
AVAsset *avAsset1 = [AVURLAsset URLAssetWithURL:url1 options:nil];
NSArray *tracks1 = [avAsset1 tracksWithMediaType:AVMediaTypeAudio];
AVAssetTrack *clipAudioTrack1 = [[avAsset1 tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0];
[compositionAudioTrack1 insertTimeRange:CMTimeRangeMake(kCMTimeZero, avAsset.duration) ofTrack:clipAudioTrack1 atTime:kCMTimeZero error:nil];
AVMutableCompositionTrack *compositionAudioTrack2 = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
[compositionAudioTrack2 setPreferredVolume:1.0];
NSString *soundOne2 =[[NSBundle mainBundle]pathForResource:#"song" ofType:#"caf"];
NSURL *url2 = [NSURL fileURLWithPath:soundOne2];
AVAsset *avAsset2 = [AVURLAsset URLAssetWithURL:url2 options:nil];
NSArray *tracks2 = [avAsset2 tracksWithMediaType:AVMediaTypeAudio];
AVAssetTrack *clipAudioTrack2 = [[avAsset2 tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0];
[compositionAudioTrack1 insertTimeRange:CMTimeRangeMake(kCMTimeZero, avAsset2.duration) ofTrack:clipAudioTrack2 atTime:kCMTimeZero error:nil];
AVAssetExportSession *exportSession = [AVAssetExportSession
exportSessionWithAsset:composition
presetName:AVAssetExportPresetAppleM4A];
if (nil == exportSession) return NO;
NSString *soundOneNew = [documentsDirectory stringByAppendingPathComponent:#"combined10.m4a"];
//NSLog(#"Output file path - %#",soundOneNew);
// configure export session output with all our parameters
exportSession.outputURL = [NSURL fileURLWithPath:soundOneNew]; // output path
exportSession.outputFileType = AVFileTypeAppleM4A; // output file type
// perform the export
[exportSession exportAsynchronouslyWithCompletionHandler:^{
if (AVAssetExportSessionStatusCompleted == exportSession.status) {
NSLog(#"AVAssetExportSessionStatusCompleted");
} else if (AVAssetExportSessionStatusFailed == exportSession.status) {
// a failure may happen because of an event out of your control
// for example, an interruption like a phone call comming in
// make sure and handle this case appropriately
NSLog(#"AVAssetExportSessionStatusFailed");
} else {
NSLog(#"Export Session Status: %d", exportSession.status);
}
}];
return YES;
}
you can use the this SDAVAssetExportSession with the following code to export many files to AAC:
-(void)mergeAudioFiles
{
NSFileManager * fm = [[NSFileManager alloc] init];
NSError * error;
NSArray * filesNames = **NSArray of File Names;
NSString * filePath = #"Dest File Name";
NSString * pathToSave =[NSString stringWithFormat:#"%#%#",filePath,#".m4a"];
CMTime startTime = kCMTimeZero;
AVMutableComposition *composition = [AVMutableComposition composition];
AVMutableCompositionTrack *compositionAudioTrack =[AVMutableCompositionTrack alloc];
compositionAudioTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
float audioEndTime=0;
for (NSString *fileName in filesNames) {
NSURL *audioUrl = [NSURL fileURLWithPath:fileName];
AVURLAsset *audioasset = [[AVURLAsset alloc]initWithURL:audioUrl options:nil];
CMTimeRange timeRange = CMTimeRangeMake(kCMTimeZero, audioasset.duration);
AVAssetTrack *audioAssetTrack= [[audioasset tracksWithMediaType:AVMediaTypeAudio] lastObject];
[compositionAudioTrack insertTimeRange:timeRange ofTrack:audioAssetTrack atTime:startTime error:&error];
startTime = CMTimeAdd(startTime, timeRange.duration);
CMTime assetTime2 = [audioasset duration];
Float64 duration2 = CMTimeGetSeconds(assetTime2);
audioEndTime+=duration2;
}
NSURL *exportUrl = [NSURL fileURLWithPath:pathToSave];
float audioStartTime=0;
CMTime startTime1 = CMTimeMake((int)(floor(audioStartTime * 100)), 100);
CMTime stopTime = CMTimeMake((int)(ceil(audioEndTime * 100)), 100);
CMTimeRange exportTimeRange = CMTimeRangeFromTimeToTime(startTime1, stopTime);
SDAVAssetExportSession *encoder = [SDAVAssetExportSession.alloc initWithAsset:composition];
encoder.outputFileType = AVFileTypeAppleM4A;
encoder.outputURL = exportUrl;
encoder.audioSettings = #
{
AVFormatIDKey: #(kAudioFormatMPEG4AAC),
AVNumberOfChannelsKey: #2,
AVSampleRateKey: #44100,
AVEncoderBitRateKey: #128000,
};
encoder.timeRange = exportTimeRange;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
NSLog(#"Starting Audio Marge");
[encoder exportAsynchronouslyWithCompletionHandler:^
{
if (encoder.status == AVAssetExportSessionStatusCompleted)
{
NSLog(#"Audio Marge succeeded");
NSError * err = NULL;
BOOL result = [fm moveItemAtPath:pathToSave toPath:filePath error:&err];
if(!result) {
NSLog(#"Error: %#", err);
}
NSLog(#"Audio Copied");
} else if (encoder.status == AVAssetExportSessionStatusCancelled) {
NSLog(#"Audio export cancelled");
} else {
NSLog(#"Audio export failed with error: %# (%ld)", encoder.error.localizedDescription, encoder.error.code);
}
dispatch_semaphore_signal(semaphore);
}];
NSLog(#"Audio Wait to Finish");
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
//cleanup
for (NSString *fileName in filesNames) {
[fm removeItemAtPath:fileName error:&error];
}
NSLog(#"Audio Marge Finished");
}
merging two audio files code
-(void)mergeTwoAudioFile
{
NSError * error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [[paths objectAtIndex:0] stringByAppendingPathComponent:#"Music_Directory"];
//first audio path
NSString *firstPath= [documentsDirectory stringByAppendingPathComponent:#"audio01.m4a"];
NSURL *audioUrl1 = [NSURL fileURLWithPath:firstPath];
AVMutableComposition *mixComposition = [AVMutableComposition composition];
AVMutableCompositionTrack *audioCompositionTrack1 =[[AVMutableCompositionTrack alloc]init];
AVURLAsset *audioasset1 = [[AVURLAsset alloc]initWithURL:audioUrl1 options:nil];
AVAssetTrack *audioAssetTrack1= [[audioasset1 tracksWithMediaType:AVMediaTypeAudio] lastObject];
audioCompositionTrack1 = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
CMTime tempTime1= mixComposition.duration;
[audioCompositionTrack1 insertTimeRange:CMTimeRangeMake(kCMTimeZero, audioasset1.duration) ofTrack:audioAssetTrack1 atTime:tempTime1 error:&error];
//========second audio
NSString *secondPath= [documentsDirectory stringByAppendingPathComponent:#"audio02.m4a"];
NSURL *audioUrl2 = [NSURL fileURLWithPath:secondPath];
AVMutableCompositionTrack *audioCompositionTrack2 =[[AVMutableCompositionTrack alloc]init];
AVURLAsset *audioasset2 = [[AVURLAsset alloc]initWithURL:audioUrl2 options:nil];
AVAssetTrack *audioAssetTrack2= [[audioasset2 tracksWithMediaType:AVMediaTypeAudio] lastObject];
audioCompositionTrack2 = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
CMTime tempTime2 = mixComposition.duration;
[audioCompositionTrack2 insertTimeRange:CMTimeRangeMake(kCMTimeZero, audioasset2.duration) ofTrack:audioAssetTrack2 atTime:tempTime2 error:&error];
NSString * pathToSave = [NSString stringWithFormat:#"finalTest.m4a"];
pathToSave =[documentsDirectory stringByAppendingPathComponent:pathToSave];
NSURL *movieUrl = [NSURL fileURLWithPath:pathToSave];
AVAssetExportSession *exporter =[[AVAssetExportSession alloc] initWithAsset:mixComposition presetName:AVAssetExportPresetAppleM4A];
exporter.shouldOptimizeForNetworkUse = YES;
exporter.outputURL = movieUrl;
exporter.outputFileType = AVFileTypeAppleM4A;
//====================================================================
float audioStartTime=0;
CMTime assetTime1 = [audioasset1 duration];
Float64 duration1 = CMTimeGetSeconds(assetTime1);
CMTime assetTime2 = [audioasset2 duration];
Float64 duration2 = CMTimeGetSeconds(assetTime2);
float audioEndTime=duration1+duration2;
CMTime startTime = CMTimeMake((int)(floor(audioStartTime * 100)), 100);
CMTime stopTime = CMTimeMake((int)(ceil(audioEndTime * 100)), 100);
CMTimeRange exportTimeRange = CMTimeRangeFromTimeToTime(startTime, stopTime);
exporter.timeRange =exportTimeRange;
//====================================================================
[exporter exportAsynchronouslyWithCompletionHandler:^(void) {
NSString* message;
switch (exporter.status) {
case AVAssetExportSessionStatusFailed:
message = [NSString stringWithFormat:#"Export failed. Error: %#", exporter.error.description];
NSLog(#"%#", message);
break;
case AVAssetExportSessionStatusCompleted:
message = [NSString stringWithFormat:#"Export completed"];
NSLog(#"%#", message);
break;
case AVAssetExportSessionStatusCancelled:
message = [NSString stringWithFormat:#"Export cancelled!"];
NSLog(#"%#", message);
break;
default:
NSLog(#"Export unhandled status: %ld", (long)exporter.status);
break;
}
}];
}