Combining two .caf files on iPhone - iphone

I've looked and looked for an answer, but can't seem to find one. Lots have asked, but none have gotten answers. I have an app that records audio using AVAudioRecorder. Now I just want to merge two or more recordings into one file that can be sent out via email. Does anyone have any clue as to how this can be done?
(This answer suggests using something called Audio Service Queues, but I don't know anything about that)

It's not quite as easy as you would think. I used the AVFoundation framework to do exactly what you're asking about to create iAmRingtones. It required creating AVAssets from the audio files and setting up an AVExportSession. The end result was great, but it certainly took a bit of work. Here's more or less how we created the export functionality in our app:
- (void) setUpAndAddAudioAtPath:(NSURL*)assetURL toComposition:(AVMutableComposition *)composition {
AVURLAsset *songAsset = [AVURLAsset URLAssetWithURL:assetURL options:nil];
AVMutableCompositionTrack *track = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
AVAssetTrack *sourceAudioTrack = [[songAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0];
NSError *error = nil;
BOOL ok = NO;
CMTime startTime = CMTimeMakeWithSeconds(0, 1);
CMTime trackDuration = songAsset.duration;
CMTime longestTime = CMTimeMake(848896, 44100); //(19.24 seconds)
CMTimeRange tRange = CMTimeRangeMake(startTime, trackDuration);
//Set Volume
AVMutableAudioMixInputParameters *trackMix = [AVMutableAudioMixInputParameters audioMixInputParametersWithTrack:track];
[trackMix setVolume:0.8f atTime:startTime];
[audioMixParams addObject:trackMix];
//Insert audio into track
ok = [track insertTimeRange:tRange ofTrack:sourceAudioTrack atTime:CMTimeMake(0, 44100) error:&error];
}
The above method gets called twice (once for each audio track) from the following method:
- (void) exportAudio {
AVMutableComposition *composition = [AVMutableComposition composition];
audioMixParams = [[NSMutableArray alloc] initWithObjects:nil];
//Add Audio Tracks to Composition
NSString *URLPath1 = pathToYourAudioFile1;
NSURL *assetURL1 = [NSURL fileURLWithPath:URLPath1];
[self setUpAndAddAudioAtPath:assetURL1 toComposition:composition];
NSString *URLPath2 = pathToYourAudioFile2;
NSURL *assetURL2 = [NSURL fileURLWithPath:URLPath2];
[self setUpAndAddAudioAtPath:assetURL2 toComposition:composition];
AVMutableAudioMix *audioMix = [AVMutableAudioMix audioMix];
audioMix.inputParameters = [NSArray arrayWithArray:audioMixParams];
//If you need to query what formats you can export to, here's a way to find out
NSLog (#"compatible presets for songAsset: %#",
[AVAssetExportSession exportPresetsCompatibleWithAsset:composition]);
AVAssetExportSession *exporter = [[AVAssetExportSession alloc]
initWithAsset: composition
presetName: AVAssetExportPresetAppleM4A];
exporter.audioMix = audioMix;
exporter.outputFileType = #"com.apple.m4a-audio";
NSString *fileName = #"someFilename";
NSString *exportFile = [[util getDocumentsDirectory] stringByAppendingFormat: #"/%#.m4a", fileName];
// set up export
myDeleteFile(exportFile);
NSURL *exportURL = [NSURL fileURLWithPath:exportFile];
exporter.outputURL = exportURL;
// do the export
[exporter exportAsynchronouslyWithCompletionHandler:^{
int exportStatus = exporter.status;
switch (exportStatus) {
case AVAssetExportSessionStatusFailed:
NSError *exportError = exporter.error;
NSLog (#"AVAssetExportSessionStatusFailed: %#", exportError);
break;
case AVAssetExportSessionStatusCompleted: NSLog (#"AVAssetExportSessionStatusCompleted"); break;
case AVAssetExportSessionStatusUnknown: NSLog (#"AVAssetExportSessionStatusUnknown"); break;
case AVAssetExportSessionStatusExporting: NSLog (#"AVAssetExportSessionStatusExporting"); break;
case AVAssetExportSessionStatusCancelled: NSLog (#"AVAssetExportSessionStatusCancelled"); break;
case AVAssetExportSessionStatusWaiting: NSLog (#"AVAssetExportSessionStatusWaiting"); break;
default: NSLog (#"didn't get export status"); break;
}
}];
// start up the export progress bar
progressView.hidden = NO;
progressView.progress = 0.0;
[NSTimer scheduledTimerWithTimeInterval:0.1
target:self
selector:#selector (updateExportProgress:)
userInfo:exporter
repeats:YES];
}

How to merge any number of Audio Files sequentially whose path is contained by an array called as recordingsArray
# pragma mark mergeRecording
- (void) mergeRecording
{
AVMutableComposition *composition = [AVMutableComposition composition];
[self buildSequenceComposition:composition]; //given Below
NSLog (#"compatible presets for songAsset: %#",[AVAssetExportSession exportPresetsCompatibleWithAsset:composition]);
AVAssetExportSession *exporter = [[AVAssetExportSession alloc] initWithAsset: composition presetName: AVAssetExportPresetAppleM4A];
exporter.outputFileType = #"com.apple.m4a-audio";
//File Name
NSString *recordingFileName = [self setRecordingFileName];
self.recordingTimeLbl.text = #"00:00:00";
NSString *exportFile = [NSTemporaryDirectory() stringByAppendingFormat: #"/%#.m4a", recordingFileName];
// set up export
BOOL yes = [[NSFileManager defaultManager] removeItemAtPath:exportFile error:NULL];
NSURL *exportURL = [NSURL fileURLWithPath:exportFile];
exporter.outputURL = exportURL;
NSData *sound1Data = [[NSData alloc] initWithContentsOfURL: exportURL];
NSLog(#"Length %i",sound1Data.length);
[exporter exportAsynchronouslyWithCompletionHandler:^{
int exportStatus = exporter.status;
switch (exportStatus) {
case AVAssetExportSessionStatusFailed:
NSLog (#"AVAssetExportSessionStatusFailed:");
break;
case AVAssetExportSessionStatusCompleted: NSLog (#"AVAssetExportSessionStatusCompleted"); break;
case AVAssetExportSessionStatusUnknown: NSLog (#"AVAssetExportSessionStatusUnknown"); break;
case AVAssetExportSessionStatusExporting: NSLog (#"AVAssetExportSessionStatusExporting"); break;
case AVAssetExportSessionStatusCancelled: NSLog (#"AVAssetExportSessionStatusCancelled"); break;
case AVAssetExportSessionStatusWaiting: NSLog (#"AVAssetExportSessionStatusWaiting"); break;
default: NSLog (#"didn't get export status"); break;
}
}];
// start up the export progress bar
[NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector (updateProgress:) userInfo:exporter repeats:NO];
}
- (NSString *) setRecordingFileName
{
NSDate *todaysDate = [NSDate date];
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"dd-MM-yyyy"];
NSString *dateString11 = [dateFormat stringFromDate:todaysDate];
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *dateComponents = [gregorian components:(NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit) fromDate:todaysDate];
NSInteger hour = [dateComponents hour];
NSInteger minute = [dateComponents minute];
NSInteger second = [dateComponents second];
[gregorian release];
NSLog(#"Date: %# \n Time : %#-%#-%#",dateString11,[NSString stringWithFormat:#"%i",hour],[NSString stringWithFormat:#"%i",minute],[NSString stringWithFormat:#"%i",second]);
NSString *recordingFileName = #"Any Name";
if(recordingFileName.length > 0)
{
recordingFileName = [NSString stringWithFormat:#"%#AND%#AND%#-%#-%#", recordingFileName, dateString11, [NSString stringWithFormat:#"%i",hour], [NSString stringWithFormat:#"%i",minute], [NSString stringWithFormat:#"%i",second]];
}
else
{
recordingFileName = [NSString stringWithFormat:#"%#AND%#-%#-%#",dateString11,[NSString stringWithFormat:#"%i",hour],[NSString stringWithFormat:#"%i",minute],[NSString stringWithFormat:#"%i",second]];
}
return recordingFileName;
}
- (void)updateProgress:(id)timer
{
AVAssetExportSession *session;
if([timer isKindOfClass:[NSTimer class]])
session = (AVAssetExportSession *)[timer userInfo];
else if([timer isKindOfClass:[AVAssetExportSession class]])
session = timer;
if (session.status == AVAssetExportSessionStatusExporting)
{
NSArray *modes = [[[NSArray alloc] initWithObjects:NSDefaultRunLoopMode, UITrackingRunLoopMode, nil] autorelease];
[self performSelector:#selector(updateProgress:) withObject:session afterDelay:0.5 inModes:modes];
}
else if(session.status == AVAssetExportSessionStatusCompleted)
{
NSLog(#"Exporting Ended");
NSURL *exportURL = session.outputURL;
NSData *sound1Data = [[NSData alloc] initWithContentsOfURL: exportURL];
NSLog(#"Length %i \n Path %#",sound1Data.length,exportURL);
[self.activityIndicator stopAnimating];
self.activityIndicator.hidden = YES;
NSLog(#"Merging Complete");
for(int x = 0 ; x < [recordingsArray count] ; x++)
{
NSURL *recordingPathUrl = [recordingsArray objectAtIndex:x];
BOOL yes = [[NSFileManager defaultManager] removeItemAtPath:recordingPathUrl.relativePath error:NULL];
if (yes)
{
NSLog(#"File Removed at Path %#",recordingPathUrl.relativePath);
}
else
{
NSLog(#"Problem During Removal of Recording At Path %#",recordingPathUrl.relativePath);
}
}
NSString *exportFile = [NSString stringWithFormat:#"%#",exportURL];
NSString *recordingFileName = [self setRecordingFileName];
BOOL isInserted = [[DbFile sharedDatabase] insertRecordingDataIntoTable:recordingFileName recordingPath:exportFile];
if(isInserted)
{
NSLog(#"Recording Inserted In Database");
}
else
{
NSLog(#"Recording Inserted In Database");
}
if([timer isKindOfClass:[NSTimer class]])
[timer invalidate];
}
else if(session.status == AVAssetExportSessionStatusFailed)
{
[self.activityIndicator stopAnimating];
NSLog(#"Recording Export Failed");
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Error" message:#"Recording Export Failed" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles: nil];
[alertView show];
[alertView release];
if([timer isKindOfClass:[NSTimer class]])
[timer invalidate];
}
else if(session.status == AVAssetExportSessionStatusCancelled)
{
[self.activityIndicator stopAnimating];
NSLog(#"Recording Export Cancelled");
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Error" message:#"Recording Export Cancelled" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles: nil];
[alertView show];
[alertView release];
if([timer isKindOfClass:[NSTimer class]])
[timer invalidate];
}
}
- (void) buildSequenceComposition:(AVMutableComposition *)composition
{
AVMutableCompositionTrack *audioTrack1 = [composition addMutableTrackWithMediaType:AVMediaTypeAudio
preferredTrackID:kCMPersistentTrackID_Invalid];
CMTime nextClipStartTime = kCMTimeZero;
for(NSURL * view in recordingsArray)
{
AVURLAsset* audioAsset = [[AVURLAsset alloc]initWithURL:view options:nil];
CMTimeRange timeRangeInAsset;
timeRangeInAsset = CMTimeRangeMake(kCMTimeZero, [audioAsset duration]);
AVAssetTrack *clipVideoTrack = [[audioAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0];
[audioTrack1 insertTimeRange:timeRangeInAsset ofTrack:clipVideoTrack atTime:nextClipStartTime error:nil];
nextClipStartTime = CMTimeAdd(nextClipStartTime, timeRangeInAsset.duration);
}
}

Related

Pause & resume video capture using AVCaptureMovieFileOutput and AVCaptureVideoDataOutput in iOS

I have to implement functionality to repeatedly pause and resume video capture in a single session, but have each new segment (the captured segments after each pause) added to the same video file, with AVFoundation. Currently, every time I press "stop" then "record" again, it just saves a new video file to my iPhone's Document directory and starts capturing to a new file. I need to be able to press the "record/stop" button over, only capture video & audio when record is active... then when the "done" button is pressed, have a single AV file with all the segments together. And all this needs to happen in the same capture session / preview session.
I am not using AVAssetWriterInput.
The only way I can think of to try this is when the "done" button is pressed, taking each individual output file and combining them together into a single file.
This code is working for iOS 5 but not for iOS 6. Actually for iOS 6, the first time when I pause recording (stop recording) AVCaptureFileOutputRecordingDelegate method (captureOutput: didFinishRecordingToOutputFileAtURL: fromConnections: error:) is called but after that when I start the recording the delegate method (captureOutput: didFinishRecordingToOutputFileAtURL: fromConnections: error:) is called again but it is not called at the time of stop recording.
I need a solution for that issue. Please help me.
//View LifeCycle
- (void)viewDidLoad
{
[super viewDidLoad];
self.finalRecordedVideoName = [self stringWithNewUUID];
arrVideoName = [[NSMutableArray alloc]initWithCapacity:0];
arrOutputUrl = [[NSMutableArray alloc] initWithCapacity:0];
CaptureSession = [[AVCaptureSession alloc] init];
captureDevices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
if ([captureDevices count] > 0)
{
NSError *error;
VideoInputDevice = [[AVCaptureDeviceInput alloc] initWithDevice:[self backFacingCamera] error:&error];
if (!error)
{
if ([CaptureSession canAddInput:VideoInputDevice])
[CaptureSession addInput:VideoInputDevice];
else
NSLog(#"Couldn't add video input");
}
else
{
NSLog(#"Couldn't create video input");
}
}
else
{
NSLog(#"Couldn't create video capture device");
}
//ADD VIDEO PREVIEW LAYER
NSLog(#"Adding video preview layer");
AVCaptureVideoPreviewLayer *layer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:CaptureSession];
[self setPreviewLayer:layer];
UIDeviceOrientation currentOrientation = [UIDevice currentDevice].orientation;
NSLog(#"%d",currentOrientation);
if (currentOrientation == UIDeviceOrientationPortrait)
{
PreviewLayer.orientation = AVCaptureVideoOrientationPortrait;
}
else if (currentOrientation == UIDeviceOrientationPortraitUpsideDown)
{
PreviewLayer.orientation = AVCaptureVideoOrientationPortraitUpsideDown;
}
else if (currentOrientation == UIDeviceOrientationLandscapeRight)
{
PreviewLayer.orientation = AVCaptureVideoOrientationLandscapeRight;
}
else if (currentOrientation == UIDeviceOrientationLandscapeLeft)
{
PreviewLayer.orientation = AVCaptureVideoOrientationLandscapeLeft;
}
[[self PreviewLayer] setVideoGravity:AVLayerVideoGravityResizeAspectFill];
//ADD MOVIE FILE OUTPUT
NSLog(#"Adding movie file output");
MovieFileOutput = [[AVCaptureMovieFileOutput alloc] init];
VideoDataOutput = [[AVCaptureVideoDataOutput alloc] init];
[VideoDataOutput setSampleBufferDelegate:self queue:dispatch_get_main_queue()];
NSString* key = (NSString*)kCVPixelBufferBytesPerRowAlignmentKey;
NSNumber* value = [NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA];
NSDictionary* videoSettings = [NSDictionary dictionaryWithObject:value forKey:key];
[VideoDataOutput setVideoSettings:videoSettings];
Float64 TotalSeconds = 60; //Total seconds
int32_t preferredTimeScale = 30; //Frames per second
CMTime maxDuration = CMTimeMakeWithSeconds(TotalSeconds, preferredTimeScale);//<<SET MAX DURATION
MovieFileOutput.maxRecordedDuration = maxDuration;
MovieFileOutput.minFreeDiskSpaceLimit = 1024 * 1024; //<<SET MIN FREE SPACE IN BYTES FOR RECORDING TO CONTINUE ON A VOLUME
//SET THE CONNECTION PROPERTIES (output properties)
[self CameraSetOutputProperties]; //(We call a method as it also has to be done after changing camera)
AVCaptureConnection *videoConnection = nil;
for ( AVCaptureConnection *connection in [MovieFileOutput connections] )
{
NSLog(#"%#", connection);
for ( AVCaptureInputPort *port in [connection inputPorts] )
{
NSLog(#"%#", port);
if ( [[port mediaType] isEqual:AVMediaTypeVideo] )
{
videoConnection = connection;
}
}
}
if([videoConnection isVideoOrientationSupported]) // **Here it is, its always false**
{
[videoConnection setVideoOrientation:[[UIDevice currentDevice] orientation]];
} NSLog(#"Setting image quality");
[CaptureSession setSessionPreset:AVCaptureSessionPresetLow];
//----- DISPLAY THE PREVIEW LAYER -----
CGRect layerRect = CGRectMake(5, 5, 299, ([[UIScreen mainScreen] bounds].size.height == 568)?438:348);
[self.PreviewLayer setBounds:layerRect];
[self.PreviewLayer setPosition:CGPointMake(CGRectGetMidX(layerRect),CGRectGetMidY(layerRect))];
if ([CaptureSession canAddOutput:MovieFileOutput])
[CaptureSession addOutput:MovieFileOutput];
[CaptureSession addOutput:VideoDataOutput];
//We use this instead so it goes on a layer behind our UI controls (avoids us having to manually bring each control to the front):
CameraView = [[UIView alloc] init];
[videoPreviewLayer addSubview:CameraView];
[videoPreviewLayer sendSubviewToBack:CameraView];
[[CameraView layer] addSublayer:PreviewLayer];
//----- START THE CAPTURE SESSION RUNNING -----
[CaptureSession startRunning];
}
#pragma mark - IBACtion Methods
-(IBAction)btnStartAndStopPressed:(id)sender
{
UIButton *StartAndStopButton = (UIButton*)sender;
if ([StartAndStopButton isSelected] == NO)
{
[StartAndStopButton setSelected:YES];
[btnPauseAndResume setEnabled:YES];
[btnBack setEnabled:NO];
[btnSwitchCameraInput setHidden:YES];
NSDate *date = [NSDate date];
NSLog(#" date %#",date);
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *recordedFileName = nil;
recordedFileName = [NSString stringWithFormat:#"output%#.mov",date];
NSString *documentsDirectory = [paths objectAtIndex:0];
self.outputPath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:#"%#",recordedFileName]];
NSLog(#"%#",self.outputPath);
[arrVideoName addObject:recordedFileName];
NSURL *outputURL = [[NSURL alloc] initFileURLWithPath:self.outputPath];
if ([[NSFileManager defaultManager] fileExistsAtPath:self.outputPath])
{
NSError *error;
if ([[NSFileManager defaultManager] removeItemAtPath:self.outputPath error:&error] == NO)
{
//Error - handle if requried
}
}
//Start recording
[MovieFileOutput startRecordingToOutputFileURL:outputURL recordingDelegate:self];
recordingTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(VideoRecording) userInfo:nil repeats:YES];
}
else
{
[StartAndStopButton setSelected:NO];
[btnPauseAndResume setEnabled:NO];
[btnBack setEnabled:YES];
[btnSwitchCameraInput setHidden:NO];
NSLog(#"STOP RECORDING");
WeAreRecording = NO;
[MovieFileOutput stopRecording];
[((ActOutAppDelegate *)ActOut_AppDelegate) showLoadingViewOnView:self.view withLabel:#"Please wait...."];
if ([recordingTimer isValid])
{
[recordingTimer invalidate];
recordingTimer = nil;
recordingTime = 30;
}
stopRecording = YES;
}
}
- (IBAction)btnPauseAndResumePressed:(id)sender
{
UIButton *PauseAndResumeButton = (UIButton*)sender;
if (PauseAndResumeButton.selected == NO)
{
PauseAndResumeButton.selected = YES;
NSLog(#"recording paused");
WeAreRecording = NO;
[MovieFileOutput stopRecording];
[self pauseTimer:recordingTimer];
[btnStartAndStop setEnabled:NO];
[btnBack setEnabled:YES];
[btnSwitchCameraInput setHidden:NO];
}
else
{
PauseAndResumeButton.selected = NO;
NSLog(#"recording resumed");
[btnStartAndStop setEnabled:YES];
[btnBack setEnabled:NO];
[btnSwitchCameraInput setHidden:YES];
WeAreRecording = YES;
NSDate *date = [NSDate date];
NSLog(#" date %#",date);
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
NSString *recordedFileName = nil;
recordedFileName = [NSString stringWithFormat:#"output%#.mov",date];
NSString *documentsDirectory = [paths objectAtIndex:0];
self.outputPath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:#"%#",recordedFileName]];
NSLog(#"%#",self.outputPath);
[arrVideoName addObject:recordedFileName];
NSURL *outputURL = [[NSURL alloc] initFileURLWithPath:self.outputPath];
if ([[NSFileManager defaultManager] fileExistsAtPath:self.outputPath])
{
NSError *error;
if ([[NSFileManager defaultManager] removeItemAtPath:self.outputPath error:&error] == NO)
{
//Error - handle if requried
}
}
[self resumeTimer:recordingTimer];
//Start recording
[MovieFileOutput startRecordingToOutputFileURL:outputURL recordingDelegate:self];
}
}
- (void) CameraSetOutputProperties
{
//SET THE CONNECTION PROPERTIES (output properties)
AVCaptureConnection *CaptureConnection = [MovieFileOutput connectionWithMediaType:AVMediaTypeVideo];
[CaptureConnection setVideoOrientation:AVCaptureVideoOrientationPortrait];
//Set frame rate (if requried)
CMTimeShow(CaptureConnection.videoMinFrameDuration);
CMTimeShow(CaptureConnection.videoMaxFrameDuration);
if (CaptureConnection.supportsVideoMinFrameDuration)
CaptureConnection.videoMinFrameDuration = CMTimeMake(1, CAPTURE_FRAMES_PER_SECOND);
if (CaptureConnection.supportsVideoMaxFrameDuration)
CaptureConnection.videoMaxFrameDuration = CMTimeMake(1, CAPTURE_FRAMES_PER_SECOND);
CMTimeShow(CaptureConnection.videoMinFrameDuration);
CMTimeShow(CaptureConnection.videoMaxFrameDuration);
}
- (AVCaptureDevice *) CameraWithPosition:(AVCaptureDevicePosition) Position
{
NSArray *Devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
for (AVCaptureDevice *Device in Devices)
{
if ([Device position] == Position)
{
NSLog(#"%d",Position);
return Device;
}
}
return nil;
}
#pragma mark - AVCaptureFileOutputRecordingDelegate Method
-(void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection
{
if(videoWriterInput.readyForMoreMediaData && WeAreRecording) [videoWriterInput appendSampleBuffer:sampleBuffer];
for(AVCaptureConnection *captureConnection in [captureOutput connections])
{
if ([captureConnection isVideoOrientationSupported])
{
AVCaptureVideoOrientation orientation = AVCaptureVideoOrientationLandscapeLeft;
[captureConnection setVideoOrientation:orientation];
}
}
UIDeviceOrientation curOr = [[UIDevice currentDevice] orientation];
CGAffineTransform t;
if (curOr == UIDeviceOrientationPortrait)
{
t = CGAffineTransformMakeRotation(-M_PI / 2);
}
else if (curOr == UIDeviceOrientationPortraitUpsideDown)
{
t = CGAffineTransformMakeRotation(M_PI / 2);
}
else if (curOr == UIDeviceOrientationLandscapeRight)
{
t = CGAffineTransformMakeRotation(M_PI);
}
else
{
t = CGAffineTransformMakeRotation(0);
}
}
- (void)captureOutput:(AVCaptureFileOutput *)captureOutput didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL fromConnections:(NSArray *)connections error:(NSError *)error
{
NSLog(#"didFinishRecordingToOutputFileAtURL - enter");
NSLog(#"output file url : %#", [outputFileURL absoluteString]);
BOOL RecordedSuccessfully = YES;
if ([error code] != noErr)
{
// A problem occurred: Find out if the recording was successful.
id value = [[error userInfo] objectForKey:AVErrorRecordingSuccessfullyFinishedKey];
if (value)
{
RecordedSuccessfully = [value boolValue];
}
}
AVCaptureConnection *videoConnection=nil;
for ( AVCaptureConnection *connection in [MovieFileOutput connections] )
{
NSLog(#"%#", connection);
for ( AVCaptureInputPort *port in [connection inputPorts] )
{
NSLog(#"%#", port);
if ( [[port mediaType] isEqual:AVMediaTypeVideo] )
{
videoConnection = connection;
}
}
}
if([videoConnection isVideoOrientationSupported]) // **Here it is, its always false**
{
[videoConnection setVideoOrientation:[[UIDevice currentDevice] orientation]];
} NSLog(#"Setting image quality");
NSData *videoData = [NSData dataWithContentsOfURL:outputFileURL];
[videoData writeToFile:self.outputPath atomically:NO];
[arrOutputUrl addObject:outputFileURL];
if (stopRecording)
{
[self mergeMultipleVideo];
}
}
//Method to merge multiple audios
-(void)mergeMultipleVideo
{
mixComposition = [AVMutableComposition composition];
AVMutableCompositionTrack *compositionVideoTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
CMTime nextClipStartTime = kCMTimeZero;
NSLog(#"Array of output file url : %#", arrOutputUrl);
if (arrOutputUrl.count > 0)
{
for(int i = 0 ;i < [arrOutputUrl count];i++)
{
AVURLAsset* VideoAsset = [[AVURLAsset alloc]initWithURL:[arrOutputUrl objectAtIndex:i] options:nil];
CMTimeRange timeRangeInAsset;
timeRangeInAsset = CMTimeRangeMake(kCMTimeZero, [VideoAsset duration]);
[compositionVideoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, VideoAsset.duration) ofTrack:[[VideoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0] atTime:nextClipStartTime error:nil];
nextClipStartTime = CMTimeAdd(nextClipStartTime, timeRangeInAsset.duration);
}
}
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *myPathDocs = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:#"%#.mov",self.finalRecordedVideoName]];
NSURL *url = [NSURL fileURLWithPath:myPathDocs];
AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:mixComposition presetName:AVAssetExportPresetHighestQuality];
exportSession.outputURL=url;
exportSession.outputFileType = AVFileTypeQuickTimeMovie;
exportSession.shouldOptimizeForNetworkUse = YES;
[exportSession exportAsynchronouslyWithCompletionHandler:^{
dispatch_async(dispatch_get_main_queue(), ^{
[self exportDidFinish:exportSession path:myPathDocs];
});
}];
}
-(void)exportDidFinish:(AVAssetExportSession*)session path:(NSString*)outputVideoPath
{
NSLog(#"session.status : %d",session.status);
if (session.status == AVAssetExportSessionStatusCompleted)
{
NSURL *outputURL = session.outputURL;
NSData *videoData = [NSData dataWithContentsOfURL:outputURL];
[videoData writeToFile:outputVideoPath atomically:NO];
if ([arrVideoName count] > 0)
{
for (int i = 0; i < [arrVideoName count]; i++)
{
NSArray* documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* fullFilePath = [[documentPaths objectAtIndex:0] stringByAppendingPathComponent: [NSString stringWithFormat:#"%#",[arrVideoName objectAtIndex:i]]];
NSLog(#"Full path of file to be deleted: %#",fullFilePath);
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
if ([fileManager fileExistsAtPath:fullFilePath])
{
[fileManager removeItemAtPath:fullFilePath error:&error];
}
}
[arrVideoName removeAllObjects];
}
if (arrOutputUrl.count > 0)
{
[arrOutputUrl removeAllObjects];
}
[((ActOutAppDelegate *)ActOut_AppDelegate) removeLoadingViewfromView:self.view];
[self.view addSubview:afterRecordingPopupView];
}
}
Look at the AVCaptureConnection's enabled property. For your output connection, set enabled to NO instead of stopping the session.

Concatenating sound files on the iPhone (m4a) fails

I am trying to make one long sound file by concatenating several smaller sound files - all are in the m4a format, and the result should also be in the m4a format.
Here is the code (the audFiles array holds the names of the sound files to join).
Note I only print the file sizes for peace of mind...
CMTime nextClipStartTime = kCMTimeZero;
AVMutableComposition *combinedSounds = [AVMutableComposition composition];
NSString *tempDir = NSTemporaryDirectory();
NSArray *audFiles;
for (int i = 0; i < [audFiles count]; i++)
{
NSString *addSound = [tempDir stringByAppendingString:audFiles[i]];
if ([[NSFileManager defaultManager] fileExistsAtPath:addSound] == YES)
{
NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:addSound error:nil];
if (fileAttributes != nil)
{
NSString *fileSize = [fileAttributes objectForKey:NSFileSize];
NSLog(#"file %# %#", addSound, fileSize);
NSURL *assetURL = [[NSURL alloc] initFileURLWithPath:addSound];
AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:assetURL options:nil];
if (asset != nil)
{
CMTimeRange timeRange = CMTimeRangeMake(kCMTimeZero, asset.duration);
NSLog(#"asset %d length %lld", i, asset.duration.value);
if (asset.duration.value > 0)
{
AVAssetTrack *audTrack = [asset tracksWithMediaType:AVMediaTypeAudio][0];
AVMutableCompositionTrack *audioTrack = [combinedSounds addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
if ([audioTrack insertTimeRange:timeRange ofTrack:audTrack atTime:nextClipStartTime error:nil] == NO)
{
NSLog(#"insertTimeRange %d FAILED", i);
}
nextClipStartTime = CMTimeAdd(nextClipStartTime, asset.duration);
nextClipStartTime = CMTimeAdd(nextClipStartTime, CMTimeMake(0.1, 1));
NSLog(#"nextClipStartTime %lld", nextClipStartTime.value);
}
}
}
}
}
NSString *finalSound = [tempDir stringByAppendingString:#"result.m4a"];
AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:combinedSounds presetName:AVAssetExportPresetPassthrough];
NSString *exported = [tempDir stringByAppendingString:finalSound];
[[NSFileManager defaultManager] removeItemAtPath:exported error:nil];
NSURL *exportedURL = [[NSURL alloc] initFileURLWithPath:exported];
exportSession.outputURL = exportedURL;
exportSession.shouldOptimizeForNetworkUse = YES;
exportSession.outputFileType = AVFileTypeAppleM4A;
[exportSession exportAsynchronouslyWithCompletionHandler:^{
switch (exportSession.status)
{
case AVAssetExportSessionStatusFailed:
{
NSLog(#"exportSession FAIL");
break;
}
case AVAssetExportSessionStatusCompleted:
{
NSLog(#"exportSession SUCCESS");
}
}
}];
"exportSession SUCCESS" is reported, and the exported file exists and can be played, but it only contains the first of the constituent files.
Any ideas what I am doing wrong ?
Thanks.
Step 1 : Add AVFoundation.Framework and CoreMedia.Framework in your Project
Step 2 : #import <AVFoundation/AVFoundation.h> in your .h file
Step 3 : Write following method in your .m file
Step 4 : Call this function from ViewDidAppear to create Merged Audio file.
// Get Asset Detail
- (AVAsset *)getAvAssetForFile:(NSString *)fileName andType:(NSString *)fileType
{
NSString *URLPath = [[NSBundle mainBundle] pathForResource:fileName ofType:fileType];
NSURL *assetURL = [NSURL fileURLWithPath:URLPath];
AVAsset *asset = [AVAsset assetWithURL:assetURL];
return asset;
}
- (void)mergeAudioFiles
{
// `An Array with List of Audio Clips to be Merged, they could be of different type also`
NSMutableArray *listOfAudio = [[NSMutableArray alloc] initWithCapacity:0];
// `Create AVAsset Object from your audio files and add to array`
[listOfAudio addObject:[self getAvAssetForFile:#"audioFile1" andType:#"caf"]];
[listOfAudio addObject:[self getAvAssetForFile:#"audioFile2" andType:#"caf"]];
[listOfAudio addObject:[self getAvAssetForFile:#"audioFile3" andType:#"aiff"]];
AVMutableComposition *composition = [AVMutableComposition composition];
CMTime current = kCMTimeZero;
NSError *compositionError = nil;
for(AVAsset *asset in listOfAudio) {
BOOL result = [composition insertTimeRange:CMTimeRangeMake(kCMTimeZero, [asset duration])
ofAsset:asset
atTime:current
error:&compositionError];
if(!result) {
if(compositionError) {
// manage the composition error case
NSLog(#"Error in Merging : %#",[compositionError debugDescription]);
}
} else {
current = CMTimeAdd(current, [asset duration]);
}
}
AVAssetExportSession *exporter = [[AVAssetExportSession alloc]
initWithAsset: composition
presetName: AVAssetExportPresetAppleM4A];
exporter.outputFileType = #"com.apple.m4a-audio";
// Set Output FileName
NSString *fileName = #"myAudioTrack";
NSString *exportFile = [DOC_DIR_PATH stringByAppendingFormat: #"/%#.m4a", fileName];
// set up export
NSURL *exportURL = [NSURL fileURLWithPath:exportFile];
exporter.outputURL = exportURL;
// do the export
[exporter exportAsynchronouslyWithCompletionHandler:^{
int exportStatus = exporter.status;
switch (exportStatus) {
case AVAssetExportSessionStatusFailed: NSLog (#"AVAssetExportSessionStatusFailed: %#", exporter.debugDescription); break;
case AVAssetExportSessionStatusCompleted: NSLog (#"AVAssetExportSessionStatusCompleted"); break;
case AVAssetExportSessionStatusUnknown: NSLog (#"AVAssetExportSessionStatusUnknown"); break;
case AVAssetExportSessionStatusExporting: NSLog (#"AVAssetExportSessionStatusExporting"); break;
case AVAssetExportSessionStatusCancelled: NSLog (#"AVAssetExportSessionStatusCancelled"); break;
case AVAssetExportSessionStatusWaiting: NSLog (#"AVAssetExportSessionStatusWaiting"); break;
default: NSLog (#"didn't get export status"); break;
}
}];
}

How to change video metadata using AVAssetWriter?

How to change a video(.mp4) meta-info using a AVAssetWriter API?
I want to not re-encode. only wanted to modify the video meta-info.
how to write a next code?
AVAssetWriter *writer = [AVAssetWriter assetWriterWithURL:[NSURL URLWithString:myPath] fileType:AVFileTypeQuickTimeMovie error:nil];
if I mistaken, give me some hint.
Thanks!!
refer a following code.
AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:[NSURL fileURLWithPath:"your path"] options:nil];
NSMutableArray *metadata = [NSMutableArray array];
AVMutableMetadataItem *metaItem = [AVMutableMetadataItem metadataItem];
metaItem.key = AVMetadataCommonKeyPublisher;
metaItem.keySpace = AVMetadataKeySpaceCommon;
metaItem.value = #"your_value";
[metadata addObject:metaItem];
AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:asset presetName:AVAssetExportPresetPassthrough];
exportSession.outputURL = [NSURL fileURLWithPath:"your output path"];
CMTime start = CMTimeMakeWithSeconds(0.0, BASIC_TIMESCALE);
CMTimeRange range = CMTimeRangeMake(start, [asset duration]);
exportSession.timeRange = range;
exportSession.outputFileType = AVFileTypeAppleM4V // AVFileTypeMPEG4 or AVFileTypeQuickTimeMovie (video format);
exportSession.metadata = metadata;
exportSession.shouldOptimizeForNetworkUse = YES;
[exportSession exportAsynchronouslyWithCompletionHandler:^{
switch ([exportSession status])
{
case AVAssetExportSessionStatusCompleted:
NSLog(#"Export sucess");
case AVAssetExportSessionStatusFailed:
NSLog(#"Export failed: %#", [[exportSession error] localizedDescription]);
case AVAssetExportSessionStatusCancelled:
NSLog(#"Export canceled");
default:
break;
}
}];

Is there a way to save iPod music files to iPhone App using iPhone SDK?

Is there a way to save iPod music files to iPhone App using iPhone SDK?
i show audio list on table view when i click on audio tableview then that song i hold in nsdata for upload
-(void)startVideoStart:(NSNumber*)number
{
MPMediaItem *song = [miPodSongsArray objectAtIndex:[number intValue]];
songTitle = [song valueForProperty: MPMediaItemPropertyTitle];
NSURL *url = [song valueForProperty:MPMediaItemPropertyAssetURL];
[NSThread detachNewThreadSelector:#selector(loadAudioInBackground:) toTarget:self withObject:url];
[self performSelector:#selector(UploadSong:) withObject:songDict afterDelay:10];
}
-(void)loadAudioInBackground:(NSURL*)url
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
NSLog(#"%#",url);
AVURLAsset *songAsset = [AVURLAsset URLAssetWithURL: url options:nil];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSLog (#"compatible presets for songAsset: %#",[AVAssetExportSession exportPresetsCompatibleWithAsset:songAsset]);
AVAssetExportSession *exporter = [[AVAssetExportSession alloc]
initWithAsset: songAsset
presetName: AVAssetExportPresetPassthrough];
NSLog (#"created exporter. supportedFileTypes: %#", exporter.supportedFileTypes);
exporter.outputFileType = #"com.apple.m4a-audio";
NSString *exportFile = [documentsDirectory stringByAppendingPathComponent: #"item.m4a"];
NSError *error1;
if([[NSFileManager defaultManager] fileExistsAtPath:exportFile])
{
[[NSFileManager defaultManager] removeItemAtPath:exportFile error:&error1];
}
NSURL* exportURL = [[NSURL fileURLWithPath:exportFile] retain];
exporter.outputURL = exportURL;
// do the export
[exporter exportAsynchronouslyWithCompletionHandler:^{
NSData *data1 = [NSData dataWithContentsOfFile: [documentsDirectory stringByAppendingPathComponent: #"item.m4a"]];
//NSLog(#"%#",data1);
if (songDict) {
[songDict release];
songDict=nil;
}
songDict= [[NSMutableDictionary alloc]init];
[songDict setValue:data1 forKey:#"AudioData"];
[songDict setValue:songTitle forKey:#"AudioName"];
[songDict setValue:[[mAppDelegate.userInfoArray objectAtIndex:1]valueForKey:#"user_id"] forKey:#"user_id"];
//NSLog(#"%#",infoDict);
mAppDelegate.uploadType = #"Audio";
int exportStatus = exporter.status;
switch (exportStatus) {
case AVAssetExportSessionStatusFailed: {
// log error to text view
NSError *exportError = exporter.error;
NSLog (#"AVAssetExportSessionStatusFailed: %#", exportError);
// errorView.text = exportError ? [exportError description] : #"Unknown failure";
//errorView.hidden = NO;
break;
}
case AVAssetExportSessionStatusCompleted: {
NSLog (#"AVAssetExportSessionStatusCompleted");
break;
}
case AVAssetExportSessionStatusUnknown: { NSLog (#"AVAssetExportSessionStatusUnknown");
break; } case AVAssetExportSessionStatusExporting: { NSLog (#"AVAssetExportSessionStatusExporting"); break; }
case AVAssetExportSessionStatusCancelled: { NSLog (#"AVAssetExportSessionStatusCancelled");
break; }
case AVAssetExportSessionStatusWaiting: { NSLog (#"AVAssetExportSessionStatusWaiting"); break; }
default:
{ NSLog (#"didn't get export status"); break; } }
}];
[pool release];
}

copy ipod music library audio file to Iphone App folder?

Can we store ipod music library file (song) to iPhone App folder?
if Yes Please suggest How can I do that?
Please Help
Thanks
i show audio list on table view when i click on audio tableview then that song i hold in nsdata for upload
-(void)startVideoStart:(NSNumber*)number
{
MPMediaItem *song = [miPodSongsArray objectAtIndex:[number intValue]];
songTitle = [song valueForProperty: MPMediaItemPropertyTitle];
NSURL *url = [song valueForProperty:MPMediaItemPropertyAssetURL];
[NSThread detachNewThreadSelector:#selector(loadAudioInBackground:) toTarget:self withObject:url];
[self performSelector:#selector(UploadSong:) withObject:songDict afterDelay:10];
}
-(void)loadAudioInBackground:(NSURL*)url
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
NSLog(#"%#",url);
AVURLAsset *songAsset = [AVURLAsset URLAssetWithURL: url options:nil];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSLog (#"compatible presets for songAsset: %#",[AVAssetExportSession exportPresetsCompatibleWithAsset:songAsset]);
AVAssetExportSession *exporter = [[AVAssetExportSession alloc]
initWithAsset: songAsset
presetName: AVAssetExportPresetPassthrough];
NSLog (#"created exporter. supportedFileTypes: %#", exporter.supportedFileTypes);
exporter.outputFileType = #"com.apple.m4a-audio";
NSString *exportFile = [documentsDirectory stringByAppendingPathComponent: #"item.m4a"];
NSError *error1;
if([[NSFileManager defaultManager] fileExistsAtPath:exportFile])
{
[[NSFileManager defaultManager] removeItemAtPath:exportFile error:&error1];
}
NSURL* exportURL = [[NSURL fileURLWithPath:exportFile] retain];
exporter.outputURL = exportURL;
// do the export
[exporter exportAsynchronouslyWithCompletionHandler:^{
NSData *data1 = [NSData dataWithContentsOfFile: [documentsDirectory stringByAppendingPathComponent: #"item.m4a"]];
//NSLog(#"%#",data1);
if (songDict) {
[songDict release];
songDict=nil;
}
songDict= [[NSMutableDictionary alloc]init];
[songDict setValue:data1 forKey:#"AudioData"];
[songDict setValue:songTitle forKey:#"AudioName"];
[songDict setValue:[[mAppDelegate.userInfoArray objectAtIndex:1]valueForKey:#"user_id"] forKey:#"user_id"];
//NSLog(#"%#",infoDict);
mAppDelegate.uploadType = #"Audio";
int exportStatus = exporter.status;
switch (exportStatus) {
case AVAssetExportSessionStatusFailed: {
// log error to text view
NSError *exportError = exporter.error;
NSLog (#"AVAssetExportSessionStatusFailed: %#", exportError);
// errorView.text = exportError ? [exportError description] : #"Unknown failure";
//errorView.hidden = NO;
break;
}
case AVAssetExportSessionStatusCompleted: {
NSLog (#"AVAssetExportSessionStatusCompleted");
break;
}
case AVAssetExportSessionStatusUnknown: { NSLog (#"AVAssetExportSessionStatusUnknown");
break;
}
case AVAssetExportSessionStatusExporting: { NSLog (#"AVAssetExportSessionStatusExporting");
break;
}
case AVAssetExportSessionStatusCancelled: { NSLog (#"AVAssetExportSessionStatusCancelled");
break;
}
case AVAssetExportSessionStatusWaiting: {
NSLog (#"AVAssetExportSessionStatusWaiting");
break;
}
default:
{ NSLog (#"didn't get export status");
break;
}
}
}];
[pool release];
}
Please have a look at TSLibraryImport also.It handles AVAssetExportSession code for you.