Cordova外部应用+本地视频

时间:2015-04-05 09:22:48

标签: javascript ios cordova html5-video phonegap-build

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

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

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

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

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

到目前为止我尝试过:

  1. 使用file://cdvfile://个网址作为视频的src。这不会产生任何视觉效果。屏幕简直是黑色。
  2. 使用iframe src设置为视频网址。使用file://时,屏幕仍为黑色。但是,使用cdvfile://时,会出现iOS视频播放器界面,其中包含播放按钮和全屏按钮,但视频无法播放,也没有时间轴。
  3. 将一个本地文件添加到名为video.html的cordova中,该文件将URL作为参数,并将video标记与该网址src一起呈现。计划是将此文件包含为iframe,但显然我无法对本地文件进行iframe。我尝试过各种可能指向特定video.html文件的URL(虽然实际上我不确定这是否可行)。我尝试过的是:cordova.file.applicationDirectory + 'www/video.html'http://localhost/www/video.htmlcdvfile://localhost/www/video.html
  4. 我找了一些可以播放视频的cordova插件,但是我找不到适合iOS的插件。大多数插件似乎都是针对Android。
  5. 现在,我可能会以错误的方式解决这个问题。正如我所看到的,cordova的“标准用例”是您在本地存储HTML / JS / CSS文件。像我正在使用的外部内容可能有点不寻常。我将解释这个使我使用此功能的应用程序的要求。

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

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

    非常感谢!

2 个答案:

答案 0 :(得分:4)

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

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

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

最终结果:

The end result

以下是我使用Cordova 4.3.0&amp;一点ObjectiveC

  1. 创建一个名为VideoURLProtocol的新Objective C类,它扩展了NSURLProtocol:
  2. 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. 将以下行添加到AppDelegate.m的didFinishLaunchingWithOptions方法

      .
      .
      // These lines should already be there
      self.window.rootViewController = self.viewController;
      [self.window makeKeyAndVisible];
      
      // This line
      [NSURLProtocol registerClass:[VideoURLProtocol class]];
      .
      .    
      
    2. 以下是使用此新网址方案的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);
      
    3. 值得一提的其他一些要点:

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

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

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

      您还可以通过在config.xml中添加以下行来内联播放视频,此外还需要添加webkit-playsinline =&#34; true&#34;属于您的视频:

      <preference name="AllowInlineMediaPlayback" value="true" />
      
      <video controls="controls" autoplay="true" webkit-playsinline="true" preload="auto">
      </video>
      

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

答案 1 :(得分:0)

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

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

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

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

cdvfile://localhost/www/是您在调用target referenced here时为fileTransfer.download()参数设置的路径。

successCallback触发后,可能需要创建视频元素或在javascript中设置视频src。您再次将src设置为相对URL。

请注意视频不会在移动设备上自动播放

From the Safari Developer Library

  

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