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
Related
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.
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
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.
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];
I'm having trouble playing some files with AVAudioPlayer. When I try to play a certain m4a, it works fine. It also works with an mp3 that I try. However it fails on one particular mp3 every time (15 Step, by Radiohead), regardless of the order in which I try to play them. The audio just does not play, though the view loading and everything that happens concurrently happens correctly. The code is below. I get the "Player loaded." log output on the other two songs, but not on 15 Step. I know the file path is correct (I have it log outputted earlier in the app, and it is correct). Any ideas?
NSData *musicData = [NSData dataWithContentsOfURL:[[NSURL alloc] initFileURLWithPath:[[NSBundle mainBundle] pathForResource:[song filename] ofType:nil]]];
NSLog([[NSBundle mainBundle] pathForResource:[song filename] ofType:nil]);
if(musicData)
{
NSLog(#"File found.");
}
self.songView.player = [[AVAudioPlayer alloc] initWithData:musicData error:nil];
if(self.songView.player)
{
NSLog(#"Player loaded.");
}
[self.songView.player play];
NSLog(#"You should be hearing something now.");
Sorry for entering the party so late. Just want to post a tip so that It will be useful for any person referring this in the future.
A small correction to shaggy frog's answer. The correct usage would be:
NSError *anError = nil;
anAVAudioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:someURL] error:&anError];
if (!anAVAudioPlayer) {
NSLog(#"localizedDescription : %#", [anError localizedDescription]);
}
NSData *musicData = [NSData dataWithContentsOfURL:[[NSURL alloc] initFileURLWithPath:[[NSBundle mainBundle] pathForResource:[song filename] ofType:nil]]];
Here, is musicData nil for the problem case?
Also:
self.songView.player = [[AVAudioPlayer alloc] initWithData:musicData error:nil];
You shouldn't pass nil for methods that take an NSError object. Likely it will shed more light on your problem if you use it correctly:
NSError* error = nil;
self.songView.player = [[AVAudioPlayer alloc] initWithData:musicData error:&error];
if (error)
{
NSLog(#"Error with initWithData: %#", [error localizedDescription]);
}