31

I want to play a movie in iOS 4.3 on the iPad. I've successfully used MPMoviePlayerController and AVPlayer to load files from a remote URL when the filename has a file extension. However, when I use a CDN that doesn't return the filename (just an un-guessable random name), neither MPMoviePlayerController or AVPlayer seem to be able to cope.

Is there a way to tell either player that it really is a movie of type x and it should just get on playing it?

MPMoviePlayerController will return the following error from it's changed state notification:

{
    MPMoviePlayerPlaybackDidFinishReasonUserInfoKey = 1;
    error = "Error Domain=MediaPlayerErrorDomain Code=-12847 \"This movie format is not supported.\" UserInfo=0x5b60030 {NSLocalizedDescription=This movie format is not supported.}";
}

I know that file is a valid m4v file, as when I rename it all is fine.

Srikar Appalaraju
  • 71,928
  • 54
  • 216
  • 264
Michael
  • 1,355
  • 2
  • 12
  • 15
  • +1 for good question - hope someone comes up with a way to accomplish this.... – Till Apr 03 '11 at 11:25
  • 1
    No a ideal solution, but I've found something out that might help some people - if you make sure the content type header is correct then the video will play if there's no file extension. So to get a video to play in MPMoviePlayerController you need **either** the proper file extension **or** the proper mime type. I tested it with a valid file extension and an incorrect content type and it would still play. – Michael Apr 19 '11 at 12:18
  • 1
    Hi @Michael, Did you get worked with this question? I am trying with same now, may I know how it worked? – Santo Aug 24 '15 at 07:09
  • @Santo If you are comfortable with Private API, check my answer below – naituw Feb 02 '18 at 09:17
  • Does this answer your question? [Is it possible to make AVURLAsset work without a file extension?](https://stackoverflow.com/questions/9290972/is-it-possible-to-make-avurlasset-work-without-a-file-extension) – Mohsen Khosravinia Feb 21 '22 at 14:25

8 Answers8

10

File at tmp

NSString* _filePath

Create symlink

NSFileManager *filemgr = [NSFileManager defaultManager];
NSString *slink = [_filePath stringByAppendingPathExtension:@"m4v"];
if (![filemgr fileExistsAtPath:slink]) {
    NSError *error = nil;
    [filemgr createSymbolicLinkAtPath:[_filePath stringByAppendingPathExtension:@"m4v"] withDestinationPath: _filePath error: &error];
    if (error) {
        ...
    }
}
...

play video by slink

Andrii Tishchenko
  • 1,842
  • 2
  • 13
  • 15
5

If the player can't guess the file format you need to check that the CDN sends the right mime type back. My guess is that your CDN can't guess the mimetype correctly nor can the player.

In most cases this is due to how the CDN presents the HTTP header. Check that the "Content-Type" header is set to a video format matching your content.

Magnus
  • 2,016
  • 24
  • 32
  • 4
    My server is returning content type video/mp4, my file is also a valid mp4 file with H.264 encoding, yet the AVPlayer does not play it without the file extension. And this is five years later with Swift 3. Any ideas what else could be wrong? – shelll May 03 '17 at 10:50
4

WebKit handle this by a Private AVURLAsset option: AVURLAssetOutOfBandMIMETypeKey, this option is used when you specify a MIME type in the HTML's video tag,

You can use this option like:

NSString * mimeType = @"video/mp4";

// or even with codecs
mimeType = @"video/mp4; codecs=\"avc1.42E01E, mp4a.40.2\"";

// create asset
AVURLAsset * asset = [[AVURLAsset alloc] initWithURL:url options:@{@"AVURLAssetOutOfBandMIMETypeKey": mimeType}];

// create AVPlayer with AVURLAsset
AVPlayer * player = [AVPlayer playerWithPlayerItem:[AVPlayerItem playerItemWithAsset:asset]];

Since it is a private key, you may want to obfuscate it if you plan to submit it to AppStore.

The WebKit source can be found here: https://opensource.apple.com/source/WebCore/WebCore-7604.1.38.1.6/platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm.auto.html

naituw
  • 1,087
  • 10
  • 14
2

Finally, I found the answer.

You should use AVURLAsset (the subclass of AVAsset) and set the MIMEType in the options input :

let mimeType = "video/mp4; codecs=\"avc1.42E01E, mp4a.40.2\""
let urlAsset = AVURLAsset(url: url, options: ["AVURLAssetOutOfBandMIMETypeKey": mimeType])

Source -> https://stackoverflow.com/a/54087143/6736184

user2878850
  • 2,446
  • 1
  • 18
  • 22
  • In such scenarios, this question qualifies as a possible duplicate. You can flag the OPs question if you feel this solution already exists on the website – shuberman Jul 01 '20 at 06:46
  • Thanks - this helped my situation where I was uploading iOS videos to Firebase Storage, but then Firebase insists on giving it a URL with no extension. With the above adjustment, I can use an AVPlayer to play the AVURLAsset when loaded into an AVPlayerItem. Thanks! – Tom Wilson Jul 01 '22 at 23:25
1

If you have problems to get the ContentType of your connection you could cycle through the playable MIME types and create symbolic links to the actual file with the extension and check if they are playable. Like so:

NSLog(@"linked path: %@",[videoURL absoluteString]);
NSString* linkedPath;
AVURLAsset* asset;
NSFileManager *filemgr = [NSFileManager defaultManager];

for (NSString* string in [AVURLAsset audiovisualMIMETypes]) {

    if ([string containsString:@"video/"]) {

        NSLog(@"Trying: %@",string);

        linkedPath = [[videoURL absoluteString] stringByAppendingPathExtension:[string stringByReplacingOccurrencesOfString:@"video/" withString:@""]];
        NSLog(@"linked path: %@",linkedPath);

        if (![filemgr fileExistsAtPath:linkedPath]) {
            NSError *error = nil;
            [filemgr createSymbolicLinkAtURL:[NSURL URLWithString:linkedPath] withDestinationURL:videoURL error:&error];
            if (error) {
               NSLog(@"error %@",error.localizedDescription);
            }
        }

       asset = [AVURLAsset assetWithURL:[NSURL URLWithString:linkedPath]];
        if ([asset isPlayable]) {
            NSLog(@"Playable");
            break;
        }else{
            NSLog(@"Not Playable");
            asset = nil;
        }
    }

}
ben
  • 900
  • 9
  • 17
  • I included this line of code along with above code, solves my problem, `NSString *slink = [_filePath stringByAppendingPathExtension:@"mp4"];` – Nasir Mar 13 '17 at 17:47
  • Basically, above code converts the without extension "myfilename" to something like that, "myfilename.mp4" and "myfilename.m4v" and "myfilename.3gpp" and "myfilename.3gpp2" and so on. All these symlink created can be in same directory or diffrent directory. – Nasir Mar 13 '17 at 17:56
1

iPhone support video H.264, MPEG-4 in .mp4, .m4v, .mov formats and audio files in AAC, MP3, M4a, Apple lossless and Audible. You can use NSFileManager's -contentsOfDirectoryAtPath:error: method to get an array with the contents of a directory (as strings).Then you just do strings operations .

imdhresh
  • 31
  • 3
1

Dylan is correct.

Both MPMoviePlayer and AVPlayer needs a file extension in order to play the file from URL otherwise an error message will be shown. Better to use some kind of tricks.

Tarun
  • 766
  • 1
  • 5
  • 12
0

It's sort of a hack, but what you could do is run each name through a method that checks for a period with three characters after it. If not, just append .m4v automatically. Or get the MIME type and append an extension automatically based on the returned type. If available. Look up documentation with NSString for more info. Good luck! Let me know if that helped.

Dylan Gattey
  • 1,713
  • 13
  • 34