ionic-team / cordova-plugin-ionic-webview

Web View plugin for Cordova, specialized for Ionic apps.
Apache License 2.0
484 stars 391 forks source link

Very slow to load large local video #673

Open richpixel opened 1 year ago

richpixel commented 1 year ago

On iOS - I'm attempting to use the HTML5 video player to play a 'large' video (~200mb). When the video is played from a remote URL - my server - it begins playing quickly and quality is good. When the video is played from an app folder on the local device or simulator, it takes about 20 seconds to begin playing and is somewhat choppy at the beginning. The file is stored in the 'dataDirectory' retrieved from window.cordova.file.dataDirectory

I suspect this has something to do with trying to buffer in the entire file before playing, but I have no idea - any ideas would be appreciated.

jbgtmartin commented 1 year ago

Maybe it's the same issue as this one? It seems to be fixed since iOS 15.6 at least.

The only fix I've found so far is to use version 2 of this plugin, which handles the files differently, and to wait 1 or 2 years for our users to update their iOS before switching to the last version...

richpixel commented 1 year ago

Thanks for your reply - I'm actually using iOS 16 on my device so I don't think this is it. I do notice there's some code in IONAssetHandler that's responsible for 'serving' the video - it appears to load the entire contents of the media file (into virtual memory which is good) and then somehow serve it through the response. This gets called repeatedly as the video is buffering/before play starts. I think this must have something to do with the issue. I suspect there must be some way to stream the data here, but I'm not sure how:

- (void)webView:(WKWebView *)webView startURLSchemeTask:(id <WKURLSchemeTask>)urlSchemeTask
{
    NSString * startPath = @"";
    NSURL * url = urlSchemeTask.request.URL;
    NSString * stringToLoad = url.path;
    NSString * scheme = url.scheme;

    if ([scheme isEqualToString:self.scheme]) {
        if ([stringToLoad hasPrefix:@"/_app_file_"]) {
            startPath = [stringToLoad stringByReplacingOccurrencesOfString:@"/_app_file_" withString:@""];
        } else {
            startPath = self.basePath ? self.basePath : @"";
            if ([stringToLoad isEqualToString:@""] || [url.pathExtension isEqualToString:@""]) {
                startPath = [startPath stringByAppendingString:@"/index.html"];
            } else {
                startPath = [startPath stringByAppendingString:stringToLoad];
            }
        }
    }
    NSError * fileError = nil;
    NSData * data = nil;
    if ([self isMediaExtension:url.pathExtension]) {
        // ENTIRE CONTENTS OF MEDIA FILE IS LOADED:
        data = [NSData dataWithContentsOfFile:startPath options:NSDataReadingMappedIfSafe error:&fileError];
    }
    if (!data || fileError) {
        data =  [[NSData alloc] initWithContentsOfFile:startPath];
    }
    NSInteger statusCode = 200;
    if (!data) {
        statusCode = 404;
    }
    NSURL * localUrl = [NSURL URLWithString:url.absoluteString];
    NSString * mimeType = [self getMimeType:url.pathExtension];
    id response = nil;
    if (data && [self isMediaExtension:url.pathExtension]) {
        response = [[NSURLResponse alloc] initWithURL:localUrl MIMEType:mimeType expectedContentLength:data.length textEncodingName:nil];
    } else {
        NSDictionary * headers = @{ @"Content-Type" : mimeType, @"Cache-Control": @"no-cache"};
        response = [[NSHTTPURLResponse alloc] initWithURL:localUrl statusCode:statusCode HTTPVersion:nil headerFields:headers];
    }

    [urlSchemeTask didReceiveResponse:response];
    [urlSchemeTask didReceiveData:data]; // DATA IS "SERVED" , BUT THIS GETS CALLED REPEATEDLY
    [urlSchemeTask didFinish];

}

A potential hint at a solution: https://stackoverflow.com/questions/58217566/how-to-serve-big-files-using-wkurlschemehandler

ghenry22 commented 1 year ago

could be missing range headers when fetching from local file system, without range headers the entire file will be loaded.

richpixel commented 1 year ago

Thanks for the reply, that sounds promising. Do you have an idea for how to have the ionic webview accept range headers? Or supply them in the request somehow? It might actually need to happen somewhere in the code I posted.