Cordova外部应用+本地video

我们有一个使用PhoneGap / Cordova 4.3.0构建的iOS应用程序。 此应用程序使用config.xml文件中的直接加载外部网站。 所有function都包含在本网站中,因此我们实际上并没有使用任何本地HTML或JS文件。

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

要播放video,我们使用标准HTML5video标记:

  

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

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

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

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

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

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

非常感谢你!

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

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

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

最终结果:

最终的结果

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

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

VideoURLProtocol.h:

 #import  @interface VideoURLProtocol : NSURLProtocol  @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. 将以下行添加到AppDelegate.m的didFinishLaunchingWithOptions方法中

     . . // These lines should already be there self.window.rootViewController = self.viewController; [self.window makeKeyAndVisible]; // This line [NSURLProtocol registerClass:[VideoURLProtocol class]]; . . 
  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); 

还有一些值得一提的点:

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

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

  

您还可以通过在config.xml中添加以下行来内联播放video,此外还需要在video中添加webkit-playsinline =“true”属性:

   

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

根据我使用file://协议的经验在iOS上存在问题,因为协议从设备文件系统的根开始。

我不相信这里遇到任何跨源问题,因为Cordova没有实现跨源请求,而是将所有请求视为来自他们请求的来源。 看到这个答案 。

我的理论解决方案是使用相对 URL而不是尝试使用任何协议。 但是,实现此方法可能取决于文件成功下载的时间。

  

其中cdvfile://localhost/www/是您在调用此处引用的 fileTransfer.download()时为target参数设置的路径。

一旦successCallback被触发,可能需要创建video元素或在javascript中设置videosrc。 您将再次将src设置为相对URL。

请注意,video不会在手机上自动播放

来自Safari开发者库

在iOS上的Safari(适用于所有设备,包括iPad)中,用户可能位于蜂窝网络上并按数据单元收费,因此禁用预加载和自动播放。