Merging two m4v Movie Files Using AVMutableComposition - Videos Will Not Merge - iphone

I am using the below code to try and merge two m4v files stored in the documents folder :
CMTime insertionPoint = kCMTimeZero;
NSError * error = nil;
AVMutableComposition *composition = [AVMutableComposition composition];
AVURLAsset* asset = [AVURLAsset URLAssetWithURL: [assetURLArray objectForKey:kIntroVideo] options:nil];
if (![composition insertTimeRange:CMTimeRangeMake(kCMTimeZero, asset.duration)
ofAsset:asset
atTime:insertionPoint
error:&error])
{
NSLog(#"error: %#",error);
}
insertionPoint = CMTimeAdd(insertionPoint, asset.duration);
AVURLAsset* asset2 = [AVURLAsset URLAssetWithURL: [assetURLArray objectForKey:kMainVideo] options:nil];
if (![composition insertTimeRange:CMTimeRangeMake(kCMTimeZero, asset2.duration)
ofAsset:asset2
atTime:insertionPoint
error:&error])
{
NSLog(#"error: %#",error);
}
AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:composition presetName:AVAssetExportPresetHighestQuality];
NSString *exportVideoPath = [NSHomeDirectory() stringByAppendingPathComponent:#"Documents/FinishedVideo.m4v"];
NSURL *exportURL = [NSURL fileURLWithPath:exportVideoPath];
exportSession.outputURL = exportURL;
exportSession.outputFileType = AVFileTypeQuickTimeMovie;
[exportSession exportAsynchronouslyWithCompletionHandler:^{
switch (exportSession.status) {
case AVAssetExportSessionStatusFailed:{
NSLog (#"FAIL");
break;
}
case AVAssetExportSessionStatusCompleted: {
NSLog (#"SUCCESS");
}
};
}];
}
The problem is that the two videos will not merge properly. The total merged movie duration is correct, however the video never transitions to the second movie and continues to display the last frame of the first movie for its duration. Oddly I can hear the audio for the second video playing in the background.
Does anyone have any ideas what is wrong ?
EDIT - The odd thing is is that if I merge two clips of exactly the same length it works.
EDIT - Have tried changing file extension to .mov with same problem.

You havent set the composition to the exportSession.
After the line:
AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:composition presetName:AVAssetExportPresetHighestQuality];
Add this line
exportSession.videoComposition = composition;
This should solve your problem.

Ok - so I eventually got this working by using individual AVMutableComposition tracks and then setting a mutablecomposition for audio and one for video.

Related

How to record an audio by mixing multiple audio in iphone?

I have to record an audio by mixing multiple audio file. For example, if three audio file are being played, so I have to mix the sound of all those playing audio and record it into a single audio file.
Please help me in this in this, if any working code is there to achieve it.
Thanks in Advance !!
I think I understand what you're asking.
You want to create a single track using three tracks available to you. Now, you cannot have audio files playing when you try to use them, they're locked.
You'll want to use AVMutableComposition, load all the tracks as AVURLAssets and then combine them. Now I've only written code to append one file to another file, so my example won't be complete, but it should point you in the right direction.
// Generate a composition of the two audio assets that will be combined into
// a single track
AVMutableComposition* composition = [AVMutableComposition composition];
AVMutableCompositionTrack* audioTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio
preferredTrackID:kCMPersistentTrackID_Invalid];
// grab the two audio assets as AVURLAssets according to the file paths
AVURLAsset* masterAsset = [[AVURLAsset alloc] initWithURL:[NSURL fileURLWithPath:self.masterFile] options:nil];
AVURLAsset* activeAsset = [[AVURLAsset alloc] initWithURL:[NSURL fileURLWithPath:self.newRecording] options:nil];
NSError* error = nil;
// grab the portion of interest from the master asset
[audioTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, masterAsset.duration)
ofTrack:[[masterAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0]
atTime:kCMTimeZero
error:&error];
if (error)
{
// report the error
return;
}
// append the entirety of the active recording
[audioTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, activeAsset.duration)
ofTrack:[[activeAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0]
atTime:masterAsset.duration
error:&error];
if (error)
{
// report the error
return;
}
// now export the two files
// create the export session
// no need for a retain here, the session will be retained by the
// completion handler since it is referenced there
AVAssetExportSession* exportSession = [AVAssetExportSession
exportSessionWithAsset:composition
presetName:AVAssetExportPresetAppleM4A];
if (nil == exportSession)
{
// report the error
return;
}
NSString* combined = #"combined file path";// create a new file for the combined file
// configure export session output with all our parameters
exportSession.outputURL = [NSURL fileURLWithPath:combined]; // output path
exportSession.outputFileType = AVFileTypeAppleM4A; // output file type
[exportSession exportAsynchronouslyWithCompletionHandler:^{
// export status changed, check to see if it's done, errored, waiting, etc
switch (exportSession.status)
{
case AVAssetExportSessionStatusFailed:
break;
case AVAssetExportSessionStatusCompleted:
break;
case AVAssetExportSessionStatusWaiting:
break;
default:
break;
}
NSError* error = nil;
// your code for dealing with the now combined file
}];

How can i share a sound file, to facebook or Twitter in iphone

Hi i am working on application in which , i have to upload a sound file to Facebook.
Please ,provide me a better solution, whether it is possible to share a sound file on Facebook or not.
Thanks in advance
Facebook does not have sound uploading. You could always upload the sound file elsewhere and use Facebook to share the link to it.
if you check the webApps for twitter/facebook, they does not provide any means to UPLOAD an audio file.
Twittier allows only text post and on the other hand, Facebook allow Image/Video to be uploaded.
In the light of these facts, I do not think it is possible without a url share.
It is not possible to upload audio files to Facebook, only photos and videos are allowed. However, another solution would be to upload the audio file somewhere else and then use the Facebook API to post a link using that reference. One place you may wish to look to upload audio is http://developers.soundcloud.com/
Use AVAssetExportSession, create a movie with the sound file and then upload it to Facebook.
This is possible to do but it is a bit of a pain. To do this you must convert the audio file into a video file and then post it to Facebook as a video.
First we need to have access to our audioFile, you should already have this, if not then there are a lot of Stackoverflow questions devoted to this, I won't complicate matters by going off track. We then create a NSURL to a video in our documents. In this case we have an video named video_base.mp4 which has been designed to be a nice background for our audio track. Finally we merge the files before sharing the returned file to Facebook.
- (IBAction)shareToFacebook:(id)sender {
// You should already have your audio file saved
NSString * songFileName = [self getSongFileName];
NSArray * searchPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString * documentPath = [searchPaths objectAtIndex:0];
NSString * file = [documentPath stringByAppendingPathComponent:songFileName];
NSURL * audioFileURL = [NSURL fileURLWithPath: audioFile];
NSURL * videoFileURL = [NSURL fileURLWithPath:[NSFileManager getFilePath:#"video_base.mp4" withFolder:#""]];
[self mergeAudio:audioFileURL andVideo:videoFileURL withSuccess:^(NSURL * url) {
// Now we have the URL of the video file
[self shareVideoToFacebook:url];
}];
}
Credit to #dineshprasanna for this part of the code which can be found here. We want to merge our audio and video and then save them to a path. We then return the exportURL in the completion block.
- (void)mergeAudio: (NSURL *)audioURL andVideo: (NSURL *)videoURL withSuccess:(void (^)(NSURL * url))successBlock {
AVURLAsset* audioAsset = [[AVURLAsset alloc]initWithURL:audioURL options:nil];
AVURLAsset* videoAsset = [[AVURLAsset alloc]initWithURL:videoURL options:nil];
AVMutableComposition * mixComposition = [AVMutableComposition composition];
AVMutableCompositionTrack * compositionCommentaryTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio
preferredTrackID:kCMPersistentTrackID_Invalid];
[compositionCommentaryTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, audioAsset.duration)
ofTrack:[[audioAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0]
atTime:kCMTimeZero error:nil];
AVMutableCompositionTrack *compositionVideoTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo
preferredTrackID:kCMPersistentTrackID_Invalid];
[compositionVideoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, videoAsset.duration)
ofTrack:[[videoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0]
atTime:kCMTimeZero error:nil];
AVAssetExportSession* _assetExport = [[AVAssetExportSession alloc] initWithAsset:mixComposition
presetName:AVAssetExportPresetHighestQuality];
NSString * videoName = #"export.mov";
NSString * exportPath = [NSTemporaryDirectory() stringByAppendingPathComponent:videoName];
NSURL * exportUrl = [NSURL fileURLWithPath:exportPath];
if ([[NSFileManager defaultManager] fileExistsAtPath:exportPath]) {
[[NSFileManager defaultManager] removeItemAtPath:exportPath error:nil];
}
_assetExport.outputFileType = #"com.apple.quicktime-movie";
_assetExport.outputURL = exportUrl;
_assetExport.shouldOptimizeForNetworkUse = YES;
[_assetExport exportAsynchronouslyWithCompletionHandler: ^(void ) {
if(successBlock) successBlock(exportUrl);
}];
}
Finally we want to save our return videoURL to Facebook. It is worth noting that we need a few libraries to be added for this functionality to work:
#import <AssetsLibrary/AssetsLibrary.h>
#import <FBSDKCoreKit/FBSDKCoreKit.h>
#import <FBSDKShareKit/FBSDKShareKit.h>
We then share the merged file to Facebook:
- (void)shareVideoToFacebook: (NSURL *)videoURL {
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
ALAssetsLibraryWriteVideoCompletionBlock videoWriteCompletionBlock = ^(NSURL *newURL, NSError *error) {
if(error) {
NSLog( #"Error writing image with metadata to Photo Library: %#", error );
} else {
NSLog( #"Wrote image with metadata to Photo Library %#", newURL.absoluteString);
FBSDKShareDialog *shareDialog = [[FBSDKShareDialog alloc]init];
NSURL *videoURL = newURL;
FBSDKShareVideo *video = [[FBSDKShareVideo alloc] init];
video.videoURL = videoURL;
FBSDKShareVideoContent *content = [[FBSDKShareVideoContent alloc] init];
content.video = video;
[FBSDKShareDialog showFromViewController:self
withContent:content
delegate:nil];
}
};
if([library videoAtPathIsCompatibleWithSavedPhotosAlbum:videoURL]) {
[library writeVideoAtPathToSavedPhotosAlbum:videoURL
completionBlock:videoWriteCompletionBlock];
}
}
This should open up the Facebook app and then allow the user to share their audio file on their wall with a background of the video stored in your app.
Obviously everyone's project is different, this means you might not be able to copy paste this code exactly into your project. I have tried to split up the process meaning it should be easy to extrapolate to get audio messages uploading successfully.

Bad Access error on AVAssetExportSession on exportAsynchronouslyWithCompletionHandler call

I am attempting to trim a video using the following code:
AVURLAsset *videoAsset = [AVURLAsset URLAssetWithURL:[NSURL URLWithString:[NSString stringWithFormat:#"%#/%#.mp4",documentsDirectory,name]] options:nil];
AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:videoAsset presetName:AVAssetExportPresetHighestQuality];
NSURL *url = [[NSURL alloc] initWithString:[NSString stringWithFormat:#"%#/finalOutput.mp4",documentsDirectory]];
exportSession.outputURL = url;
NSLog(#"outputting to: %#", [NSString stringWithFormat:#"%#/finalOutput.mp4",documentsDirectory,name]);
exportSession.outputFileType = AVFileTypeQuickTimeMovie;
CMTimeRange timeRange = CMTimeRangeMake(flashbackStart, CMTimeSubtract(flashbackEnd, flashbackStart));
exportSession.timeRange = timeRange;
[exportSession exportAsynchronouslyWithCompletionHandler:^{
switch (exportSession.status) {
case AVAssetExportSessionStatusCompleted:
// Custom method to import the Exported Video
//[self loadAssetFromFile:exportSession.outputURL];
NSLog(#"completed!!!");
break;
case AVAssetExportSessionStatusFailed:
//
NSLog(#"Failed:%#",exportSession.error);
break;
case AVAssetExportSessionStatusCancelled:
//
NSLog(#"Canceled:%#",exportSession.error);
break;
default:
break;
}
}];
However, I'm getting a bad access error for this line:
[exportSession exportAsynchronouslyWithCompletionHandler:^{
Even with NSZombie enabled, I'm not getting any details about the error. Can anyone explain what's going on here? The input video file does exist, and the output video file does not exist before I attempt to write to it.
Thanks,
James
It turns out the problem was with the NSURLs I used. All I had to do was use initFileURLWithPath and the problem was solved!
You might be running into a problem with the storage type. Try adding __block to your exportSession.
__block AVAssetExportSession *exportSession...
You can read more here:
http://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Blocks/Articles/bxGettingStarted.html#//apple_ref/doc/uid/TP40007502-CH7-SW5

AVAssetExportSession with and without break points

I have simple video compression code in low quality conversion.I am testing my code in iphone 4 with IOS-4.2.1.The problem is when I test my code on device without break points the code failed to create video(it just a zero kb file or empty file created) but when I use breakpoint checking line by line this code slowly it will make a perfect compressed video which also runs on quicktime player in mac.After compression I make zip of this video file.
NSURL *videoURL=[[self.videourlarray objectAtIndex:i] valueForKey:UIImagePickerControllerReferenceURL];
NSURL *outputURL = [NSURL fileURLWithPath:videoFile];
[[NSFileManager defaultManager] removeItemAtURL:outputURL error:nil];
AVURLAsset *asset = [AVURLAsset URLAssetWithURL:videoURL options:nil];
AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:asset presetName:AVAssetExportPresetLowQuality];
exportSession.outputURL = outputURL;
exportSession.shouldOptimizeForNetworkUse = YES;
exportSession.outputFileType = AVFileTypeQuickTimeMovie;
[exportSession exportAsynchronouslyWithCompletionHandler:^(void)
{
NSLog(#"Export Complete %d %#", exportSession.status, exportSession.error);
[exportSession release];
}];
thanx for any help...
I think you need to make sure you're not messing with the threads.. (AVFoundation guide says that the exporter is not guaranteed to run on any particular thread).
Use a block like this.
[exportSession exportAsynchronouslyWithCompletionHandler:^(void)
{
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"Export Complete %d %#", exportSession.status, exportSession.error);
});
}];
I would personally call a delegate from the block, but I presume your simple log statement is just for this example and you already know that :)
I had the same problem. After hours of debuging, I found out that my extension of the audio file was .M4A all uppercase. Lowercasing the letters will fix the problem

iPhone SdK: AVPlayer will not play a composition after adding a audio track

After adding an audio track (recorded with AVAudioRecorder) to a composition AVPlayer will not play it. AVPlayer.status = 1 and AVPlayer.error = null.
I have looked at the WWDC10 AVEdit demo code and it's pretty much similar. Could it be an audio format incompatibility?
the original composition allready contains a video and audio track from a video asset.
after removing the newly added audio track the player still does not play
after removing the original audio track the player will play.
I'm also finding it hard to get debug information that would point me to the problem. If anyone has a suggestion it would be greatly appreciated.
Here is my code for adding the audio track
any help is most welcome
Jean-Pierre
-(void)editAudioViewController:(EditAudioViewController *)editAudioViewController didEditAudio:(NSURL *)url
{
[self dismissModalViewControllerAnimated:TRUE];
BOOL result;
NSError * error = nil;
if (url)
{
AVURLAsset * asset = [AVURLAsset URLAssetWithURL:url options:nil] ;
CMTimeRange audioTimeRange = CMTimeRangeMake(kCMTimeZero , asset.duration);
if (CMTIME_COMPARE_INLINE(CMTimeRangeGetEnd(audioTimeRange), >, [composition duration]))
audioTimeRange.duration = CMTimeSubtract([composition duration], audioTimeRange.start);
AVMutableCompositionTrack *compositionAudioTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio
preferredTrackID:kCMPersistentTrackID_Invalid];
AVAssetTrack *audioTrack = [asset compatibleTrackForCompositionTrack:compositionAudioTrack];
result = [compositionAudioTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, audioTimeRange.duration) ofTrack:audioTrack atTime:audioTimeRange.start error:&error];
if (!result)
{
NSLog(#"%#", [error description]);
}
[mp replaceCurrentItemWithPlayerItem:[AVPlayerItem playerItemWithAsset:composition]];
[mp seekToTime:kCMTimeZero];
}
}
I had two separate sessions in my audio recorder code one for recording and one for playback.
I switched to a single record/play session and this somehow fixed the problem:
audioSession = [AVAudioSession sharedInstance];
if (!audioSession.inputIsAvailable)
{
NSLog(#"No input available");
}
[audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];
NSError * error = nil;
[audioSession setActive:TRUE error:&error];
if (error)
{
NSLog(#"%#", [error description]);
}