Skip to content Skip to sidebar Skip to footer

Cordova External App + Local Video

We have an iOS app built with PhoneGap / Cordova 4.3.0. This app directly loads an external website by using in the config.xml file.

Solution 1:

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>@interfaceVideoURLProtocol : NSURLProtocol <NSURLConnectionDelegate>@property (strong, nonatomic) NSURLConnection *connection;

@end

VideoURLProtocol.m:

#import "VideoURLProtocol.h"@implementationVideoURLProtocol@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 thereself.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";
            newFileTransfer().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

<preferencename="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:

<preferencename="AllowInlineMediaPlayback"value="true" /><videocontrols="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

Solution 2:

In my experience using the file:// protocol has been problematic on iOS because the protocol starts at the root of the device filesystem.

I don't believe that any Cross-Origin problems are being faced here because Cordova doesn't implement Cross-Origin requests, but rather treats all requests as coming from the origin to which they are requesting. See this answer.

My theoretical solution is using a relative URL instead of trying to use any protocol at all. However, the way you implement this might depend on when the file is successfully downloaded.

<video width="auto" height="100%" controls="controls" autoplay="true">
    <source src="/localhost/www/video.mp4"type="video/mp4" />
</video>

Where cdvfile://localhost/www/ was the path you set for the target argument when you called fileTransfer.download()referenced here.

It may be necessary to either create the video element or set the video src in javascript once the successCallback has fired. Again you would set the src as a relative URL.

Please note videos will not autoplay on mobile

From the Safari Developer Library

In Safari on iOS (for all devices, including iPad), where the user may be on a cellular network and be charged per data unit, preload and autoplay are disabled.

Post a Comment for "Cordova External App + Local Video"