How to Play Movie with a Url Using a Custom Nsurlprotocol

How to play movie with a URL using a custom NSURLProtocol?

UPDATE: I spoke to Apple about this and it's not possible to use MPMoviePlayerController with a NSURLProtocol subclass at the moment!


Hej,

I am not sure but it could be possible. I am currently working on something similar but haven't got it fully working. What I have found out is that MPMoviePlayerController interacts with my custom NSURLProtocol subclass BUT it seems to be important to take the HTTPHeaders of the NSURLRequest into account because they define a range of bytes the MPMoviePlayerController needs.

If you dump them in your NSURLProtocol subclass you will get something like this twice for the start:

2011-01-16 17:00:47.287 iPhoneApp[1177:5f03] Start loading from request: {
Range = "bytes=0-1";

}

So my GUESS is that as long as you can provide the correct range and return a mp4 file that can be played by the MPMoviePlayerController it should be possible!

EDIT: Here is a interesting link: Protecting resources in iPhone and iPad apps

Using NSURLProtocol to implement play while downloading for AVPlayer on iOS

OK, finally I found this:
How to play movie with a URL using a custom NSURLProtocol?

It seems that custom NSURLProtocol can not work with AVPlayer. So I give up.

iOS - Use AFNetworking with custom NSURLProtocol class

So, for other looking for information about this:

Along with the fact that AFURLSessionManager doesn't use the standard NSURLProtocol registrations, it also processes the array First-In-First-Out, not Last-In-First-Out like NSURLProtocol.

Meaning, if you want to overwrite the behavior of the AFURLSessionManager (say for testing purposes), you can't just add your NSURLProtocol subclass to session.configuration.protocolClasses, you must instead add it to the beginning of the array (or at least in front of the behavior you're overwriting/modifying).

Playing a stream of bytes on the iPhone MPMoviePlayerController

You could try implementing a custom URL protocol (see NSURLProtocol). Basically you create the protocol and register it, then any in-app requests to load a url having this protocol will be routed to your protocol instance. You'd likely have to mimic the responses that a HTTP server would send for a progressive-download of the file.

This wont work if MPMoviePlayerController uses lower-level CFNetwork calls vs. NSURLConnection to make its requests. This question implies that MPMoviePlayerController DOES make use of NSURLConnectino: How to play movie with a URL using a custom NSURLProtocol?

Also read the URL Loading System docs.

Cordova external app + local video

I had a similar project about a year ago. But i don't remember running into this issue because we were bundling our html/js/css assets with the app.

The issue is that your are trying to load file:/// protocol url from an html file served from http:/// protocol, which is not something that the native UIWebView is comfortable with.

You can bypass this by using a custom URL scheme like video://, but you need to write some native code which intercepts this requests and pipes the actual video back to URL loading system.

The end result:

The end result

Here is how i did it using Cordova 4.3.0 & a bit of ObjectiveC

  1. Create a new Objective C class named VideoURLProtocol which extends NSURLProtocol:

VideoURLProtocol.h:

#import <Foundation/Foundation.h>

@interface VideoURLProtocol : NSURLProtocol <NSURLConnectionDelegate>

@property (strong, nonatomic) NSURLConnection *connection;

@end

VideoURLProtocol.m:

#import "VideoURLProtocol.h"

@implementation VideoURLProtocol

@synthesize connection;

+ (BOOL)canInitWithRequest:(NSURLRequest *)request {
return [[[request URL] absoluteString] rangeOfString:@"video://"].location != NSNotFound;
}

+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request {
return request;
}

+ (BOOL)requestIsCacheEquivalent:(NSURLRequest *)a toRequest:(NSURLRequest *)b {
return [super requestIsCacheEquivalent:a toRequest:b];
}

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
[self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[self.client URLProtocol:self didLoadData:data];
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[self.client URLProtocolDidFinishLoading:self];
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
[self.client URLProtocol:self didFailWithError:error];
}

- (void)startLoading {
NSString *currentURL = [[self.request URL] absoluteString];
NSString *newURL = [currentURL stringByReplacingOccurrencesOfString:@"video://" withString:@"file:///"];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[[NSURL alloc] initWithString:newURL]];
self.connection = [NSURLConnection connectionWithRequest:request delegate:self];
}

- (void)stopLoading {
[self.connection cancel];
self.connection = nil;
}

@end

  1. Add the following line to the didFinishLaunchingWithOptions method of AppDelegate.m

    .
    .
    // These lines should already be there
    self.window.rootViewController = self.viewController;
    [self.window makeKeyAndVisible];

    // This line
    [NSURLProtocol registerClass:[VideoURLProtocol class]];
    .
    .
  2. And here's the javascript code that utilises this new URL scheme

    document.addEventListener('deviceready', function(){    
    window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function(fileSystem){
    var caches = fileSystem.root.nativeURL.replace("Documents", "Library/Caches"),
    videoPath = caches + "video.mp4";
    new FileTransfer().download("http://clips.vorwaerts-gmbh.de/VfE_html5.mp4", videoPath, function(entry){
    document.getElementsByTagName("video")[0].src = caches.replace("file:///", "video://") + "video.mp4"
    }, function(error){
    alert("unable to download file: " + error);
    });
    });
    }, false);

Some additional points worth mentioning:

Notice in my javascript code i am downloading the file to "/Library/Caches" instead of the "/Documents" directory (the default location) this is because the "/Documents" directory gets backed up to iCloud & Apple rejects apps that try to backup more than ~100 MB. This is something I found the hard way after getting my App rejected.
You can see the space taken by your app under: Settings > iCloud > Storage > Manage Storage > {{Your iphone name}} > Show All Apps

You can autoplay videos by add the following line to your config.xml

<preference name="MediaPlaybackRequiresUserAction" value="false" />    

You can also play videos inline by adding the following line to your config.xml in addition to this you also need to add webkit-playsinline="true" attribute to your video:

<preference name="AllowInlineMediaPlayback" value="true" />

<video controls="controls" autoplay="true" webkit-playsinline="true" preload="auto">
</video>

Here's a really interesting tutorial by Ray that explains NSURLProtocol in great detail: http://www.raywenderlich.com/59982/nsurlprotocol-tutorial

Can I create an NSURL that refers to in-memory NSData?

NSURL supports the data:// URL-Scheme (RFC 2397).

This scheme allows you to build URLs in the form of

data://data:MIME-Type;base64,<data>

A working Cocoa example would be:

NSImage* img = [NSImage imageNamed:@"img"];
NSData* imgData = [img TIFFRepresentation];
NSString* dataFormatString = @"data:image/png;base64,%@";
NSString* dataString = [NSString stringWithFormat:dataFormatString, [imgData base64EncodedStringWithOptions:0]];
NSURL* dataURL = [NSURL URLWithString:dataString];

Passing around large binary blobs with data URLs might be a bit inefficient due to the nature of base64 encoding.

You could also implement a custom NSURLProtocol that specifically deals with your data.
Apple has some sample code that uses a custom protocol to pass around image objects: https://developer.apple.com/library/mac/samplecode/SpecialPictureProtocol/Introduction/Intro.html#//apple_ref/doc/uid/DTS10003816



Related Topics



Leave a reply



Submit