Cordova 外部应用 + 本地视频

Cordova external app + local video

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

作为应用程序功能的一部分,我们必须播放一些视频。因为该应用程序设计为也可以离线工作,所以我们希望在本地缓存这些视频。因此,我们使用 FileTransfer 插件将它们与其他资源(例如图像或 PDF)一起下载到设备。下载文件后,我们将使用 file:// 协议获得 URLs。我们还可以选择使用 cdvfile:// 协议。当我们使用 cdvfile:// URLs 显示图像时,图像正确显示。但是,视频根本无法播放。

为了播放视频,我们使用标准 HTML5 个视频标签:

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

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

到目前为止我尝试过的:

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

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

无论如何,我主要关心的是如何让这些视频发挥作用。我愿意尝试最骇人听闻的解决方法!如果当前的开发决策确实不可能,那么也许您可以给我一些提示,说明我应该如何构建应用程序以使其运行并仍然满足我的要求。

非常感谢!

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

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

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

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

其中 cdvfile://localhost/www/ 是您在调用 fileTransfer.download() referenced here.

时为 target 参数设置的路径

一旦 successCallback 触发,可能需要在 javascript 中创建视频元素或设置视频源。同样,您可以将 src 设置为相对 URL.

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

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.

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

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

您可以使用像 video:// 这样的自定义 URL 方案来绕过它,但是您需要编写一些本机代码来拦截此请求并将实际视频通过管道传输回 URL 加载系统.

最终结果:

这是我如何使用 Cordova 4.3.0 和一些 ObjectiveC 实现的

  1. 创建一个新的 Objective C class 命名的 VideoURLProtocol 扩展 NSURLProtocol:

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. 下面是使用这个新 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 的应用程序。这是我的应用程序被拒绝后发现的困难方法。 您可以在以下位置查看您的应用所获取的 space:设置 > iCloud > 存储 > 管理存储 > {{您的 iphone 名称}} > 显示所有应用

您可以通过将以下行添加到 config.xml

来自动播放视频
<preference name="MediaPlaybackRequiresUserAction" value="false" />    

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

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

<video controls="controls" autoplay="true" webkit-playsinline="true" preload="auto">
</video>

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