4

(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)"}}

Swinny89
  • 7,273
  • 3
  • 32
  • 52
  • So is your video local or remote? If it's local, how are you generating your `inputURL`? These things are actually pretty sensitive to differences between `+[NSURL URLWithString:]` and `+[NSURL fileURLWithPath:]`. That `Cannot Open` error could be codec or wrong URLs I believe. – Alejandro Iván Jun 26 '18 at 16:03
  • The URLs are correct, it's a local file and i'm using the NSBundle to return a URL to the file. This is to test the function, once this is done then i'll replace it for an actual URL – Swinny89 Jun 26 '18 at 16:20
  • Extract audio and video tracks and create a new movie with AVMutableVideoComposition. – El Tomato Jun 26 '18 at 20:16
  • The issue is that i can't extract the video using the export session – Swinny89 Jun 27 '18 at 07:46
  • Have you try to put extantion mp4 in output URL – guru Jul 02 '18 at 13:00
  • Try running `determineCompatibility(ofExportPreset:…)` before exporting – Ashley Mills Jul 02 '18 at 13:01
  • @guru yes i'm using @"MyVideo.mp4" – Swinny89 Jul 03 '18 at 08:21

3 Answers3

0

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: enter image description here

Finally, you can just inspect the definition of AVFileType from within XCode to see this list for yourself.

enter image description here

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.

CodeBender
  • 35,668
  • 12
  • 125
  • 132
0

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).

arturdev
  • 10,884
  • 2
  • 39
  • 67
  • I've done a spike with the FFmeg library, it takes a long time to convert these videos. Plus In regards to kxmovie, I wouldn't be interested in using a library that hasn't been updated in 4 years and has 117 issues – Swinny89 Jul 04 '18 at 08:09
0

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"];

Shakir Zareen
  • 232
  • 3
  • 15