So I've been trying to use AVAssetExportSession to trim a square video. But for some reason I just keep getting this error:
Error Domain=AVFoundationErrorDomain Code=-11800 "The operation could not be completed" UserInfo=0x1a03be70 {NSLocalizedDescription=The operation could not be completed, NSUnderlyingError=0x1a04c5e0 "The operation couldn’t be completed. (OSStatus error -12769.)", NSLocalizedFailureReason=An unknown error occurred (-12769)}
From Apple's website I found out that -11800 is an unknown error, but what about OSStatus error -12769? I searched everywhere on the internet and I had not seen any question/problem related to this error code. Please help. Thanks!
My code here:
AVAsset *asset = [[AVURLAsset alloc]initWithURL:self.originalVidUrl options:nil];
AVAssetTrack *clipVideoTrack = [[asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
if (clipVideoTrack.naturalSize.width==clipVideoTrack.naturalSize.height) {
NSLog(#"Square video");
NSArray *presets = [AVAssetExportSession exportPresetsCompatibleWithAsset:asset];
if ([presets containsObject:AVAssetExportPresetHighestQuality]) {
self.exportSession = [[AVAssetExportSession alloc]initWithAsset:asset presetName:AVAssetExportPresetHighestQuality];
self.exportSession.outputURL = [NSURL fileURLWithPath: self.tmpVidPath];
self.exportSession.outputFileType = AVFileTypeMPEG4;
CMTime start = ...
CMTime duration = ...
CMTimeRange range = CMTimeRangeMake(start, duration);
self.exportSession.timeRange = range;
[self.exportSession exportAsynchronouslyWithCompletionHandler:^{
switch ([self.exportSession status]) {
case AVAssetExportSessionStatusFailed:
NSLog(#"%#",self.exportSession.error);
break;
case AVAssetExportSessionStatusCancelled:
NSLog(#"Export canceled");
break;
default:
NSLog(#"Export Success, File Saved.");
break;
}
}];
}
}
P.S. this code works for videos taken with the native camera app (i.e. non-square/non-processed videos).
I think I found the solution, but I have no idea why or how it worked.
Simply change the preset name from AVAssetExportPresetHighestQuality to AVAssetExportPreset1280x720, and you're good to go!
Related
(Please note I've already looked at this other SO post.)
The Problem
I'm trying to convert an avi video to an mp4 so that I can play it natively on an iOS app using Objective-C
What I've Tried
I'm trying the following code to do that conversion:
- (void)convertVideoToLowQuailtyWithInputURL:(NSURL*)inputURL outputURL:(NSURL*)outputURL handler:(void (^)(AVAssetExportSession*))handler {
[[NSFileManager defaultManager] removeItemAtURL:outputURL error:nil];
AVURLAsset *asset = [AVURLAsset URLAssetWithURL:inputURL options:nil];
AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:asset presetName:AVAssetExportPresetHighestQuality];
exportSession.outputURL = outputURL;
exportSession.outputFileType = AVFileTypeMPEG4;
[exportSession exportAsynchronouslyWithCompletionHandler:^(void) {
handler(exportSession);
}];
}
The error that is returned from the exportSession is Cannot Open
Extra Information
When I run the video that I'm trying to convert through Mediainfo I get the following for the video:
7 332kb/s, 1920*1080 (16:9), at 25.000 FPS, AVC (Baseline#L3.1) (CABAC / 1 Ref Frames)
And this for the audio:
128 kb/s, 8 000 Hz, 16 bits, 1 channel, PCM (Little / Signed)
I also used the exportPresetsCompatibleWithAsset: method on AVAssetExportSession and got the following results:
AVAssetExportPreset1920x1080,
AVAssetExportPresetLowQuality,
AVAssetExportPresetAppleM4A,
AVAssetExportPresetHEVCHighestQuality,
AVAssetExportPreset640x480,
AVAssetExportPreset3840x2160,
AVAssetExportPresetHEVC3840x2160,
AVAssetExportPresetHighestQuality,
AVAssetExportPreset1280x720,
AVAssetExportPresetMediumQuality,
AVAssetExportPreset960x540,
AVAssetExportPresetHEVC1920x1080
Another thing to note is that when playing with the preset and the output I managed to get an audio only file that was basically white noise. This was using the preset AVAssetExportPresetAppleM4A.
I hope that I've jotted down enough information.
Update
Using the comment by Ashley, i've created a function to return the export settings compatible with the asset.
- (void)determineCompatibleExportForAsset:(AVURLAsset *)asset completion:(void(^)(NSArray<ExportSettings *> *exports))handler {
NSArray<NSString *> *presets = #[
AVAssetExportPresetLowQuality,
AVAssetExportPresetMediumQuality,
AVAssetExportPresetHighestQuality,
AVAssetExportPresetHEVCHighestQuality,
AVAssetExportPreset640x480,
AVAssetExportPreset960x540,
AVAssetExportPreset1280x720,
AVAssetExportPreset1920x1080,
AVAssetExportPreset3840x2160,
AVAssetExportPresetHEVC1920x1080,
AVAssetExportPresetHEVC3840x2160,
AVAssetExportPresetAppleM4A,
AVAssetExportPresetPassthrough
];
NSArray<NSString *> *outputs = #[
AVFileTypeQuickTimeMovie,
AVFileTypeMPEG4,
AVFileTypeAppleM4V,
AVFileTypeAppleM4A,
AVFileType3GPP,
AVFileType3GPP2,
AVFileTypeCoreAudioFormat,
AVFileTypeWAVE,
AVFileTypeAIFF,
AVFileTypeAIFC,
AVFileTypeAMR,
AVFileTypeMPEGLayer3,
AVFileTypeSunAU,
AVFileTypeAC3,
AVFileTypeEnhancedAC3,
AVFileTypeJPEG,
AVFileTypeDNG,
AVFileTypeHEIC,
AVFileTypeAVCI,
AVFileTypeHEIF,
AVFileTypeTIFF
];
__block int counter = 0;
int totalCount = (int)presets.count * (int)outputs.count;
NSMutableArray<ExportSettings *> *exportSettingsArray = [#[] mutableCopy];
for (NSString *preset in presets) {
for (NSString *output in outputs) {
[AVAssetExportSession determineCompatibilityOfExportPreset:preset withAsset:asset outputFileType:output completionHandler:^(BOOL compatible) {
if (compatible) {
ExportSettings *exportSettings = [[ExportSettings alloc] initWithPreset:preset outputType:output];
[exportSettingsArray addObject:exportSettings];
}
counter++;
if (counter == totalCount) {
if (handler) {
handler([exportSettingsArray copy]);
}
}
}];
}
}
}
The results of this are as follows:
"Preset: AVAssetExportPresetAppleM4A Output: com.apple.m4a-audio",
"Preset: AVAssetExportPresetPassthrough Output: com.microsoft.waveform-audio",
"Preset: AVAssetExportPresetPassthrough Output: public.aifc-audio",
"Preset: AVAssetExportPresetPassthrough Output: public.aiff-audio",
"Preset: AVAssetExportPresetPassthrough Output: com.apple.coreaudio-format",
"Preset: AVAssetExportPresetPassthrough Output: com.apple.quicktime-movie"
From this I deduced that using the preset AVAssetExportPresetPassthrough and output type AVFileTypeQuickTimeMovie would be compatible.
However when running the following code: (i've tried .mp4, .mov and .qt for the file type)
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:#"MyVideo.mov"];
NSURL *outputURL = [NSURL fileURLWithPath:filePath];
NSURL *localURL = [NSBundle URLForResource:#"20180626_145233-v" withExtension:#"avi" subdirectory:nil inBundleWithURL:[NSBundle mainBundle].bundleURL];
[self convertVideoToLowQuailtyWithInputURL:localURL outputURL:outputURL handler:^(AVAssetExportSession *exportSession) {
switch ([exportSession status]) {
case AVAssetExportSessionStatusFailed:
NSLog(#"Export failed: %#", [exportSession error]);
break;
case AVAssetExportSessionStatusCancelled:
NSLog(#"Export canceled");
break;
case AVAssetExportSessionStatusCompleted:
NSLog(#"Successfully");
NSLog(#"OutputURL: %#", outputURL.absoluteString);
break;
default:
break;
}
}];
Which calls:
- (void)convertVideoToLowQuailtyWithInputURL:(NSURL*)inputURL outputURL:(NSURL*)outputURL handler:(void (^)(AVAssetExportSession*))handler {
[[NSFileManager defaultManager] removeItemAtURL:outputURL error:nil];
AVURLAsset *asset = [AVURLAsset URLAssetWithURL:inputURL options:nil];
AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:asset presetName:AVAssetExportPresetPassthrough];
exportSession.outputURL = outputURL;
exportSession.outputFileType = AVFileTypeQuickTimeMovie;
[exportSession exportAsynchronouslyWithCompletionHandler:^(void) {
handler(exportSession);
}];
}
I get this error:
Export failed: Error Domain=AVFoundationErrorDomain Code=-11800 "The operation could not be completed" UserInfo={NSLocalizedFailureReason=An unknown error occurred (-12842), NSLocalizedDescription=The operation could not be completed, NSUnderlyingError=0x60400024def0 {Error Domain=NSOSStatusErrorDomain Code=-12842 "(null)"}}
The reason you cannot open the file is because iOS does not support AVI files.
Here is the link to the Apple documentation on supported file types within AVFoundation.
Or this image for posterity should the values change:
Finally, you can just inspect the definition of AVFileType from within XCode to see this list for yourself.
For what it is worth, a cursory inspection on AVI via Google indicates that there are limitations with the AVI container that have been rectified in newer formats that are supported by iOS, so there is likely little incentive to add support at a later date.
While also a potential issue being that Microsoft created AVI, I cannot locate any restrictive licensing that would prohibit someone from using it, but IANAL.
You should use FFmpeg library for that kind of purposes.
Here is an open-source video-player based on FFmpeg: https://github.com/kolyvan/kxmovie
I don't know if this player still works for the latest iOS but in any case in source codes, you can discover many interesting things that can help you with your problem (such as how to use FFmpeg library).
Very easy to do with MobileFFMpeg
https://stackoverflow.com/a/59325680/1466453
Once you have MobileFFMpeg then make a call as follows:
[MobileFFMpeg execute:#"-i infile.avi youroutput.mp4"];
It seems that AVFoundation cannot accept one of my videos. I really don't know why. It works with other videos, but not this one.
I'm not even modifying the video, I'm just doing a composition with the video track, and exporting it with the preset "AVAssetExportPresetHighestQuality".
I get this error :
Error Domain=AVFoundationErrorDomain Code=-11800 "The operation could not be completed" UserInfo={NSUnderlyingError=0x60000045a8e0 {Error Domain=NSOSStatusErrorDomain Code=-12769 "(null)"}, NSLocalizedFailureReason=An unknown error occurred (-12769), NSLocalizedDescription=The operation could not be completed}
Do you know if there is something wrong in my code, or if the video is just not supported by AVFoundation ?
Here's the project on Github (it just exports the video to the camera roll) :
https://github.com/moonshaped/ExportSessionCrash
Or if you don't want to use Github :
Here's the video :
Dropbox link : https://www.dropbox.com/s/twgah26gqgsv9y9/localStoreTempVideoPath.mp4?dl=0
Or WeTransfer link : https://wetransfer.com/downloads/8f8ab257068461a2c9a051542610725220170606122640/8d934c
And here's the code :
- (void)exportVideo:(AVAsset *)videoAsset
videoDuration:(Float64)videoAssetDuration
to:(NSString *)resultPath{
[Utilities deleteFileIfExists:resultPath];
AVMutableComposition *mainComposition = [[AVMutableComposition alloc] init];
AVMutableCompositionTrack *compositionVideoTrack = [mainComposition addMutableTrackWithMediaType:AVMediaTypeVideo
preferredTrackID:kCMPersistentTrackID_Invalid];
int timeScale = 100000;
int videoDurationI = (int) (videoAssetDuration * (float) timeScale);
CMTime videoDuration = CMTimeMake(videoDurationI, timeScale);
CMTimeRange videoTimeRange = CMTimeRangeMake(kCMTimeZero, videoDuration);
NSArray<AVAssetTrack *> *videoTracks = [videoAsset tracksWithMediaType:AVMediaTypeVideo];
AVAssetTrack *videoTrack = [videoTracks objectAtIndex:0];
[compositionVideoTrack insertTimeRange:videoTimeRange
ofTrack:videoTrack
atTime:kCMTimeZero
error:nil];
NSURL *outptVideoUrl = [NSURL fileURLWithPath:resultPath];
self.exporter = [[AVAssetExportSession alloc] initWithAsset:mainComposition
presetName:AVAssetExportPresetHighestQuality];
self.exporter.outputURL = outptVideoUrl;
self.exporter.outputFileType = AVFileTypeMPEG4;
self.exporter.shouldOptimizeForNetworkUse = YES;
[self.exporter exportAsynchronouslyWithCompletionHandler:^{
dispatch_async(dispatch_get_main_queue(), ^{
switch (self.exporter.status) {
case AVAssetExportSessionStatusFailed:{
#throw [NSException exceptionWithName:#"failed export"
reason:[self.exporter.error description]
userInfo:nil];
}
case AVAssetExportSessionStatusCancelled:
#throw [NSException exceptionWithName:#"cancelled export"
reason:#"Export cancelled"
userInfo:nil];
case AVAssetExportSessionStatusCompleted: {
NSLog(#"Export finished");
}
break;
default:
break;
}
});
}];
}
I did an experiment and come to this. If you reduce 1 or more millisecond from the videoTimeRange then it will work. Try replacing the below code block:
int timeScale = 100000;
Float64 seconds = CMTimeGetSeconds([videoAsset duration]) - 0.001;
NSUInteger videoDurationI = (NSUInteger) (seconds * (float) timeScale);
CMTime videoDuration = CMTimeMake(videoDurationI, timeScale);
CMTimeRange videoTimeRange = CMTimeRangeMake(kCMTimeZero, videoDuration);
The device you are trying to test is not able to decode it. Please try it on some newer devices e.g. iPhone 6. I tested your media on iPad simulator iOS10.3 and it worked fine there so it must be something to do with encoding.
I'm trying to merge a video and an audio together using AVAssetExportSession and AVMutableComposition. My code works for most of the cases(audio and video). But it fails for some videos. The failed videos are working fine with quicktime player and other players. The failed videos are failing when I simply exporting even without any audiomerging option(code given below).
[AVURLAsset assetWithURL:[NSURL fileURLWithPath:videoPath]];
AVAssetExportSession* _assetExport = [[AVAssetExportSession alloc] initWithAsset:videoAsset presetName:AVAssetExportPresetHighestQuality];
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 (AVAssetExportSessionStatusCompleted == _assetExport.status) {
} else if (AVAssetExportSessionStatusFailed == _assetExport.status) {
NSLog(#"AVAssetExportSessionStatusFailed with error--%#", _assetExport.error);
}
}
];
And the error I got is the following..
Error Domain=AVFoundationErrorDomain Code=-11800 "The operation could not be completed" UserInfo={NSUnderlyingError=0x600000053aa0 {Error Domain=NSOSStatusErrorDomain Code=-12769 "(null)"}, NSLocalizedFailureReason=An unknown error occurred (-12769), NSLocalizedDescription=The operation could not be completed}
I'm not posting the full code for merging here because the above basic code even fails with the mentioned videos.
Any tips or hints with the error codes will be really helpful. Thanks in advance.
I resolved this problem by using the AVAssetExportPresetPassthrough export preset…
let exportSession = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetPassthrough)
This should use the resolution of the imported video in the exported file.
Check what codec used to compress that video first.
It seems that setting the export preset to "AVAssetExportPreset1280x720" for example, fixes the problem. But in my case I really need to keep the original resolution so that's not an option..
I'm trying to trim and download online video using AVExportSession.
code:
FileMove *fileMove = (FileMove*)data;
NSString *url = #"http://download.wavetlan.com/SVV/Media/HTTP/H264/Talkinghead_Media/H264_test1_Talkinghead_mp4_480x360.mp4";
NSURL *videoURL = [NSURL URLWithString:[url stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSLog(#"VideoURL: %#",videoURL);
AVAsset *anAsset = [AVURLAsset URLAssetWithURL:videoURL options:nil];
NSArray *compatiblePresets = [AVAssetExportSession exportPresetsCompatibleWithAsset:anAsset];
if ([compatiblePresets containsObject:AVAssetExportPresetLowQuality]) {
AVAssetExportSession *exportSession = [[AVAssetExportSession alloc]
initWithAsset:anAsset presetName:AVAssetExportPresetLowQuality];
NSURL *outputURL = [NSURL fileURLWithPath:fileMove.dst];
NSLog(#"outputURL: %#",outputURL);
exportSession.outputURL = outputURL;
exportSession.outputFileType = AVFileTypeQuickTimeMovie;
CMTime start = CMTimeMakeWithSeconds(1.0, 600);
CMTime duration = CMTimeMakeWithSeconds(3.0, 600);
CMTimeRange range = CMTimeRangeMake(start, duration);
exportSession.timeRange = range;
if ([[NSFileManager defaultManager] fileExistsAtPath:fileMove.dst])
[[NSFileManager defaultManager] removeItemAtPath:fileMove.dst error:nil];
[exportSession exportAsynchronouslyWithCompletionHandler:^{
switch ([exportSession status]) {
case AVAssetExportSessionStatusFailed:
NSLog(#"Export failed: %#", [[exportSession error]description ]);
break;
case AVAssetExportSessionStatusCancelled:
NSLog(#"Export canceled");
break;
default:
break;
}
}];
}
Error:
Export failed: Error Domain=AVFoundationErrorDomain Code=-11800 "The
operation could not be completed" UserInfo=0x635a820
{NSLocalizedDescription=The operation could not be completed,
NSUnderlyingError=0x1ff4240 "The operation couldn’t be completed.
(OSStatus error -12780.)", NSLocalizedFailureReason=An unknown error
occurred (-12780)}
Do you guys see any issue in the code ? Is there any limitation in AVExportSession accessing online video ?
I've managed to trim remote video using AVFoundation. Here is the sample code which is written in Swift:
let range: CMTimeRange
let sourceURL: NSURL
let targetFileURL: NSURL
let requiredKeys = [ "exportable", "tracks" ]
let asset = AVAsset(URL: sourceURL)
asset.loadValuesAsynchronouslyForKeys(requiredKeys) {
// Error handling code here
precondition(asset.statusOfValueForKey("exportable", error: nil) == .Loaded)
precondition(asset.statusOfValueForKey("tracks", error: nil) == .Loaded)
precondition(asset.exportable)
let composition = AVMutableComposition()
do {
try composition.insertTimeRange(range, ofAsset: asset, atTime: kCMTimeZero)
} catch {
// Error handling code here
return
}
let finalComposition = composition.copy() as! AVComposition
guard let export = AVAssetExportSession(asset: finalComposition, presetName: AVAssetExportPresetPassthrough) else {
// Error handling code here
return
}
export.outputURL = targetFileURL
export.outputFileType = AVFileTypeMPEG4
export.exportAsynchronouslyWithCompletionHandler {
switch export.status {
case .Completed:
// Alright!
break
case .Cancelled, .Failed:
// Error handling code here
break
default:
fatalError("Shouldn't be called")
}
}
}
The URL used for creating an AVAsset needs to be a local file that you have access to. You will need to download it onto the device before creating the AVAsset like so
I think we are not able to export online videos
see here:
Unable to export AVPlayerItem
So I guess, you can download video first locally and then trim,save and use it.
I'm trying to merge (append) 3 videos using AVAssetExportSession, but I keep getting this error. Weirdly for 1 or 2 videos it worked.
Error Domain=AVFoundationErrorDomain Code=-11820 "Cannot Complete Export" UserInfo=0x458120 {NSLocalizedRecoverySuggestion=Try exporting again., NSLocalizedDescription=Cannot Complete Export}
I even tried to redo the function in case of error but what I got is only infinite error message. This is the snippet of my code.
AVMutableComposition *mixComposition = [AVMutableComposition composition];
AVMutableCompositionTrack *compositionTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
NSError * error = nil;
NSMutableArray * timeRanges = [NSMutableArray arrayWithCapacity:arrayMovieUrl.count];
NSMutableArray * tracks = [NSMutableArray arrayWithCapacity:arrayMovieUrl.count];
for (int i=0; i<[arrayMovieUrl count]; i++) {
AVURLAsset *assetClip = [arrayMovieUrl objectAtIndex:i];
AVAssetTrack *clipVideoTrackB = [[assetClip tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
[timeRanges addObject:[NSValue valueWithCMTimeRange:CMTimeRangeMake(kCMTimeZero, assetClip.duration)]];
[tracks addObject:clipVideoTrackB];
}
[compositionTrack insertTimeRanges:timeRanges ofTracks:tracks atTime:kCMTimeZero error:&error];
AVAssetExportSession *exporter = [[AVAssetExportSession alloc] initWithAsset:mixComposition presetName:AVAssetExportPreset1280x720];
NSParameterAssert(exporter != nil);
exporter.outputFileType = AVFileTypeQuickTimeMovie;
exporter.outputURL = outputUrl;
[exporter exportAsynchronouslyWithCompletionHandler:^{
switch ([exporter status]) {
case AVAssetExportSessionStatusFailed:
NSLog(#"Export failed: %#", [exporter error]);
break;
case AVAssetExportSessionStatusCancelled:
NSLog(#"Export canceled");
break;
case AVAssetExportSessionStatusCompleted:
NSLog(#"Export successfully");
break;
default:
break;
}
if (exporter.status != AVAssetExportSessionStatusCompleted){
NSLog(#"Retry export");
[self renderMovie];
}
}];
Is there something wrong with my code or iOS 5 has some bug?
I've found the problem. So the problem was actually because I use AVPlayerLayer to display each video in preview mode simultaneously. Referring to this question AVPlayerItem fails with AVStatusFailed and error code "Cannot Decode" , there's undocumented limit of maximum 4 simultaneous AVPlayer to work. And this limit somehow hinders AVAssetExportSession from working when there's 4 AVPlayer instance at that moment.
The solution is to release the AVPlayer before exporting, or not using AVPlayer altogether.