Cordova外部应用+本地视频

Gra*_*mpa 13 javascript html5-video ios cordova phonegap-build

我们有一个使用PhoneGap/Cordova 4.3.0构建的iOS应用程序.此应用程序通过<content src="http://example.com/foo" />config.xml文件中使用直接加载外部网站.所有功能都包含在本网站中,因此我们实际上并没有使用任何本地HTML或JS文件.

作为应用功能的一部分,我们必须播放一些视频.由于该应用程序也设计为脱机工作,因此我们希望在本地缓存这些视频.因此,我们使用FileTransfer插件将其下载到设备,以及其他资源,如图像或PDF.下载文件后,我们将获得带有file://协议的URL .我们也可以选择使用该cdvfile://协议.当我们使用cdvfile://URL显示图像时,图像会正确显示.但是,视频根本不播放.

要播放视频,我们使用标准HTML5视频标记:

<video width="auto" height="100%" controls="controls" autoplay="true">
    <source src="..." type="video/mp4" />
</video>
Run Code Online (Sandbox Code Playgroud)

视频本身正在工作,它们将从外部源正确播放(如果我们从服务器而不是本地文件系统访问它们,它们将播放).我意识到问题与Web相关的概念有关,例如同源策略和访问本地文件系统的限制.然而,与此同时,我必须想知道为什么图像在这些相同的约束下工作正常.

到目前为止我尝试了什么:

  1. 使用file://cdvfile://URL作为src视频.这不会产生任何视觉效果.屏幕简直是黑色.
  2. 使用iframe带有src设置的视频URL.使用时file://,屏幕仍为黑色.但是,在使用时cdvfile://,会出现iOS视频播放器界面,带有播放按钮和全屏按钮,但视频无法播放,也没有时间线.
  3. 将一个本地文件添加到cordova中video.html,该文件将URL作为参数并video使用该URL 呈现标记src.计划是将此文件包含在内iframe,但显然我无法创建iframe本地文件.我尝试过各种可能指向该特定video.html文件的URL (虽然实际上我不确定这是否可行).在我试过的人分别为:cordova.file.applicationDirectory + 'www/video.html',http://localhost/www/video.html,cdvfile://localhost/www/video.html.
  4. 我找了一些可以播放视频的cordova插件,但是我找不到适合iOS的插件.大多数插件似乎都针对Android.

现在,我可能会以错误的方式解决这个问题.正如我所看到的,cordova的"标准用例"是您在本地存储HTML/JS/CSS文件.像我正在使用的外部内容可能有点不寻常.我将解释这个使我使用此功能的应用程序的要求.

  • 该应用程序应该是为多个平台构建的(虽然我们从iOS开始).因此我们正在使用PhoneGap.
  • 虽然所有内容都来自服务器(没有内容在本地生成),但它应该可以在线和离线访问.这就是我们下载内容并在本地保存的原因.
  • 它还应该动态自动更新自身的任何部分,而无需从App Store进行更新.这就是我们使用外部页面的原因 - 因为它cache.manifest允许我们控制Web应用程序代码的更新,同时允许它在本地缓存.这可能是最重要的考虑因素,因为如果我们想在Cordova中本地保留一些文件,我们将不得不在Javascript中重新实现这个缓存功能(尽可能使用尽可能薄的层).

无论如何,我主要关注的是如何让这些视频正常运行.我愿意尝试最骇客的解决方法!如果当前的开发决策真的不可能,那么也许你可以给我一些关于如何构建应用程序以使其工作并仍然满足我的要求的提示.

非常感谢你!

Fur*_*far 4

大约一年前我有一个类似的项目。但我不记得遇到过这个问题,因为我们将 html/js/css 资源与应用程序捆绑在一起。

问题是您正在尝试从http:///协议提供的 html 文件加载 file:/// 协议 url,这不是本机 UIWebView 所熟悉的。

您可以通过使用自定义 URL 方案(如 video://)来绕过此问题,但您需要编写一些本机代码来拦截此请求并将实际视频传输回 URL 加载系统。

最终结果:

最终结果

这是我使用 Cordova 4.3.0 和一些 ObjectiveC 的方法

  1. 创建一个名为 VideoURLProtocol 的新 Objective C 类,它扩展了 NSURLProtocol:

VideoURLProtocol.h:

#import <Foundation/Foundation.h>

@interface VideoURLProtocol : NSURLProtocol <NSURLConnectionDelegate>

@property (strong, nonatomic) NSURLConnection *connection;

@end
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)
  1. 将以下行添加到 AppDelegate.m 的 didFinishLaunchingWithOptions 方法中

    .
    .
    // These lines should already be there
    self.window.rootViewController = self.viewController;
    [self.window makeKeyAndVisible];
    
    // This line
    [NSURLProtocol registerClass:[VideoURLProtocol class]];
    .
    .    
    
    Run Code Online (Sandbox Code Playgroud)
  2. 这是利用这个新 URL 方案的 javascript 代码

    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);
    
    Run Code Online (Sandbox Code Playgroud)

一些值得一提的额外要点:

请注意,在我的 javascript 代码中,我将文件下载到“/Library/Caches”而不是“/Documents”目录(默认位置),这是因为“/Documents”目录被备份到 iCloud,并且 Apple 拒绝尝试尝试的应用程序备份超过 ~100 MB。这是我的应用程序被拒绝后我发现的困难。您可以在以下位置查看您的应用占用的空间:设置 > iCloud > 存储 > 管理存储 > {{您的 iphone 名称}} > 显示所有应用

您可以通过将以下行添加到 config.xml 来自动播放视频

<preference name="MediaPlaybackRequiresUserAction" value="false" />    
Run Code Online (Sandbox Code Playgroud)

您还可以通过将以下行添加到 config.xml 来内嵌播放视频,此外您还需要向视频添加 webkit-playsinline="true" 属性:

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

<video controls="controls" autoplay="true" webkit-playsinline="true" preload="auto">
</video>
Run Code Online (Sandbox Code Playgroud)

这是 Ray 的一个非常有趣的教程,详细解释了 NSURLProtocol:http://www.raywenderlich.com/59982/nsurlprotocol-tutorial