I'm writing an application that works with video using AVFoundation.
The behaviour of my application is simple: I take a video from the camera roll, then I create an AVMutableComposition with some audio tracks. With the mix composition i initialize an AVAssetExportSession that stores the video file in the documents directory of my app.
Until this point everything it's ok: my video is stored and I'm able to play it in another controller. If I take the video that i have just stored in my documents folder to make some editing (in the same way of the first time AVmutableComposition, AVAssetExportSession) it's ok again.
But the third time I do this process to editing a video the AVAssetExportSession status becomes "Fail" and with this error:
"Domain=AVFoundationErrorDomain Code=-11820 "Cannot Complete Export" UserInfo=0x1a9260 {NSLocalizedRecoverySuggestion=Try exporting again., NSLocalizedDescription=Cannot Complete Export}"
I have read that is a general error where the session couldn't be exported. What is the sense of this? Why only the third time that i made the editing process? Could it be a memory management mistake? A bug?. This is the code of my AVAssetExportSession:
_assetExport = [[AVAssetExportSession alloc] initWithAsset:mixComposition presetName:AVAssetExportPresetHighestQuality];
_assetExport.shouldOptimizeForNetworkUse = YES;
///data odierna
NSDateFormatter *format = [[NSDateFormatter alloc] init];
[format setDateFormat:#"ddMMyyyyHHmmss"];
NSDate *now = [[NSDate alloc] init];
NSString *dateString = [format stringFromDate:now];
[now release];
[format release];
NSString* ext = #".MOV";
NSString* videoName=[NSString stringWithFormat:#"%#%#", dateString, ext];
///data odierna
NSString *exportPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:videoName];
if ([[NSFileManager defaultManager] fileExistsAtPath:exportPath])
{
[[NSFileManager defaultManager] removeItemAtPath:exportPath error:nil];
}
_assetExport.outputFileType = AVFileTypeQuickTimeMovie;
[_assetExport setTimeRange:CMTimeRangeMake(kCMTimeZero, videoAsset.duration)];
NSURL *exportUrl = [NSURL fileURLWithPath:exportPath] ;
_assetExport.outputURL = exportUrl ;
[_assetExport exportAsynchronouslyWithCompletionHandler:^
{
switch (_assetExport.status)
{
case AVAssetExportSessionStatusFailed:
{
NSLog (#"FAIL %#",_assetExport.error);
if ([[NSFileManager defaultManager] fileExistsAtPath:[_assetExport.outputURL path]])
{
[[NSFileManager defaultManager] removeItemAtPath:[_assetExport.outputURL path] error:nil];
}
[self performSelectorOnMainThread:#selector (ritenta)
withObject:nil
waitUntilDone:NO];
break;
}
case AVAssetExportSessionStatusCompleted:
{
NSLog (#"SUCCESS");
[self performSelectorOnMainThread:#selector (saveVideoToAlbum:)
withObject:exportPath
waitUntilDone:NO];
break;
}
case AVAssetExportSessionStatusCancelled:
{
NSLog (#"CANCELED");
break;
}
};
}];
I have done many searches on the web, some people have had a problem in the outputURL of the session, but I have tried and seems all ok in my code. To assign a unique name to the file I use a NSDate. For debugging purposes I have tried to restore a standard string name but the problem remains. Any ideas? Can someone suggest to me an alternative method to export to the documents folder an asset with AssetWriter insted the AVassetExportSession?
The problem is _assetExport.outputFileType you have set the type AVFileTypeQuickTimeMovie. Which is not likely to be supported type.
Try to find out what output file types are supported by the _assetExport using the following code and use the suitable one.
NSLog (#"created exporter. supportedFileTypes: %#", exporter.supportedFileTypes);
OR
just change the
_assetExport.outputFileType = AVFileTypeQuickTimeMovie;
TO
exporter.outputFileType = #"com.apple.m4a-audio";
Also dont forget to change the extension from
NSString* ext = #".MOV"; to #".m4a"
This should work. It worked for me.
Related
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.
In my application I am combining two audio files using AVAssetExportSession and it works fine in earlier ios versions.But in ios5 device its not working. What i am getting is an error
AVAssetExportSessionStatusFailed: Error Domain=AVFoundationErrorDomain Code=-11820 "Cannot Complete Export" UserInfo=0x1df1c0 {NSLocalizedRecoverySuggestion=Try exporting again., NSLocalizedDescription=Cannot Complete Export}
The code that I use for exporting is given below
Did anyone experience the same issue? Please provide your valuable suggestions.
I am in an urgent need to fix this issue..
//Export function to export the combined audios as one.
-(void)exportAudioFile:(AVComposition*)combinedComposition
{
AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:combinedComposition
presetName:AVAssetExportPresetPassthrough];
NSArray *presets =[AVAssetExportSession exportPresetsCompatibleWithAsset:combinedComposition];
NSLog(#"presets======%#",presets);
NSLog (#"can export: %#", exportSession.supportedFileTypes);
NSArray *dirs = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectoryPath = [dirs objectAtIndex:0];
exportPath = [documentsDirectoryPath stringByAppendingPathComponent:#"CombinedNew.m4a"];
[[NSFileManager defaultManager] removeItemAtPath:exportPath error:nil];
exportURL = [NSURL fileURLWithPath:exportPath];
exportSession.outputURL = exportURL;
exportSession.outputFileType = #"com.apple.m4a-audio";
exportSession.shouldOptimizeForNetworkUse = YES;
[exportSession exportAsynchronouslyWithCompletionHandler:^{
NSLog (#"i is in your block, exportin. status is %d",
exportSession.status);
switch (exportSession.status)
{
case AVAssetExportSessionStatusFailed:
{
// log error to text view
NSError *exportError = exportSession.error;
DEBUG_LOG(#"AVAssetExportSessionStatusFailed: %#", exportError);
[self enableUI];
break;
}
case AVAssetExportSessionStatusCompleted:
{
DEBUG_LOG(#"AVAssetExportSessionStatusCompleted");
DEBUG_LOG(#"Completed export");
exportSuccess = YES;
if (recorderFilePath)
{
NSError *finalurlError;
[[NSFileManager defaultManager]removeItemAtPath:recorderFilePath error:&finalurlError];
finalurlError = nil;
[[NSFileManager defaultManager]copyItemAtPath:[exportURL path] toPath:recorderFilePath error:&finalurlError];
}
isExported = YES;
fileUrl = [NSURL fileURLWithPath:recorderFilePath];
[self performSelectorInBackground:#selector(updatePlayerForUrl:) withObject:fileUrl];
break;
}
case AVAssetExportSessionStatusUnknown:
{
DEBUG_LOG(#"AVAssetExportSessionStatusUnknown");
break;
}
case AVAssetExportSessionStatusExporting:
{
DEBUG_LOG(#"AVAssetExportSessionStatusExporting");
break;
}
case AVAssetExportSessionStatusCancelled:
{
DEBUG_LOG(#"AVAssetExportSessionStatusCancelled");
break;
}
case AVAssetExportSessionStatusWaiting:
{
DEBUG_LOG(#"AVAssetExportSessionStatusWaiting");
break;
}
default:
{
DEBUG_LOG(#"didn't get export status");
break;
}
};
}];
[exportSession release];
}
I sorted out the answer for myself and would like to share it with others who experience the same problem.
The problem is that for some reason the AVAssetExportPresetPassthrough is not working properly in ios5. Substituting it with AVAssetExportPresetAppleM4A solved the issue.
But it takes longer to export now.
Perhaps a way around it is to use AVAssetWriter directly and not use AVAssetExportSession. Please please please, file a bug at http://bugreport.apple.com so that maybe it gets fixed in the next rev of iOS5. (I filed one of my own, but the more the merrier.)
As a workaround, I found that using .mov as the file extension, then renaming it back to mp3 seems to work. I dont need to do this for m4a files.
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
As a practice, I am trying to write an app similar to the built-in notes app.
But I cannot figure out how to save the file and display it in a UITableView.
Right now, I have a UITextView that the user can type in. I also have a save button.
When the user taps the save button, I want to save it, and later have it displayed in a table view.
I am very lost so if you know of any relevant tutorials etc. it would be greatly appreciated.
As noted by the commenters in the real world, you're definitely going to want to look at Core Data or some other data persistence strategy. If you're dead set on pursuing this as a learning experience, something like this should solve your problem:
- (void)writeStringToFile:(NSString*)aString {
// Build the path, and create if needed.
NSString* filePath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString* fileName = #"myTextFile.txt";
NSString* fileAtPath = [filePath stringByAppendingPathComponent:fileName];
if (![[NSFileManager defaultManager] fileExistsAtPath:fileAtPath]) {
[[NSFileManager defaultManager] createFileAtPath:fileAtPath contents:nil attributes:nil];
}
// The main act...
[[aString dataUsingEncoding:NSUTF8StringEncoding] writeToFile:fileAtPath atomically:NO];
}
- (NSString*)readStringFromFile {
// Build the path...
NSString* filePath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString* fileName = #"myTextFile.txt";
NSString* fileAtPath = [filePath stringByAppendingPathComponent:fileName];
// The main act...
return [[[NSString alloc] initWithData:[NSData dataWithContentsOfFile:fileAtPath] encoding:NSUTF8StringEncoding] autorelease];
}
The easiest way to save text is using NSUserDefaults.
[[NSUserDefaults standardUserDefaults] setObject:theText forKey:#"SavedTextKey"];
or, if you want to have the user name each "file" or be able to have multiple files
NSMutableDictionary *saveTextDict = [[[[NSUserDefaults standardUserDefaults] objectForKey:#"SavedTextKey"] mutableCopy] autorelease];
if (saveTextDict == nil) {
saveTextDict = [NSMutableDictionary dictionary];
}
[saveTextDict setObject:theText forKey:fileName];
[[NSUserDefaults standardUserDefaults] setObject:saveTextDict forKey:#SavedTextKey"];
So supposedly in the iOS 4 SDK you can edit and write to the user's iTunes library. I can successfully load an AVAsset from my iPhone/iPod library, but as a quick test I'm trying to just overwrite the same file right away using AVAssetExportSession but it's always returning the status "4" which I THINK is AVAssetExportSessionStatusFailed... In the documentation it says:
enum {
AVAssetExportSessionStatusUnknown,
AVAssetExportSessionStatusExporting,
AVAssetExportSessionStatusCompleted,
AVAssetExportSessionStatusFailed,
AVAssetExportSessionStatusCancelled,
AVAssetExportSessionStatusWaiting
};
but in AVAssetExportSession.h it says:
enum {
AVAssetExportSessionStatusUnknown,
AVAssetExportSessionStatusWaiting,
AVAssetExportSessionStatusExporting,
AVAssetExportSessionStatusCompleted,
AVAssetExportSessionStatusFailed,
AVAssetExportSessionStatusCancelled
};
typedef NSInteger AVAssetExportSessionStatus;
Here's the code I'm using:
// before this, i'm using mpmediapicker to pick an m4a file i synched with my itunes library
NSURL *assetUrl = [[self.userMediaItemCollection.items objectAtIndex: 0] valueForProperty: MPMediaItemPropertyAssetURL];
AVURLAsset *asset = [AVURLAsset URLAssetWithURL: assetUrl options: nil];
AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset: asset presetName: AVAssetExportPresetAppleM4A];
exportSession.outputURL = asset.URL;
exportSession.outputFileType = AVFileTypeAppleM4A;
NSLog(#"output filetype: %#", exportSession.outputFileType);
// prints "com.apple.m4a-audio"
[exportSession exportAsynchronouslyWithCompletionHandler: ^(void) {
NSLog(#"status: %i for %#", exportSession.status, exportSession.outputURL);
// prints "status: 4 for ipod-library://item/item.m4a?id=3631988601206299774"
}];
[exportSession release];
So either way... I guess it's "failed" or "cancelled." Has anyone else successfully written to the media library before?
Thanks!
you cannot write to itunes library, only read from it now.
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
NSParameterAssert(library);
if ([library videoAtPathIsCompatibleWithSavedPhotosAlbum:[NSURL fileURLWithPath:movieFileName]]) {
[library writeVideoAtPathToSavedPhotosAlbum:[NSURL fileURLWithPath:movieFileName] completionBlock:^(NSURL *assetURL, NSError *error){}];
}
[library release];