Cordova 外部应用程序 + 本地视频

Posted

技术标签:

【中文标题】Cordova 外部应用程序 + 本地视频【英文标题】:Cordova external app + local video 【发布时间】:2015-06-09 22:34:44 【问题描述】:

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

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

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

<video   controls="controls" autoplay="true">
    <source src="..." type="video/mp4" />
</video>

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

到目前为止我所尝试的:

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

现在,我可能以错误的方式处理这件事。在我看来,cordova 的“标准用例”是您将 HTML/JS/CSS 文件存储在本地。像我正在使用的外部内容可能有点不寻常。我将解释使我使用此功能的此应用程序的要求。

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

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

非常感谢!

【问题讨论】:

我建议使用远程调试器并查看网络中发生的情况。也许 mime 文件类型不正确。 您是否在任何地方使用.toURL()?如果是这样,请尝试替换为.toNativeURL() 我已经尝试了这些建议,我的发现是:@SergeySnegirev 我已经调试了传入的文件。哑剧类型还可以。文件已成功下载到设备。但是,我无法调试通过file://cdvfile:// 协议发出的请求,因为它们实际上并不通过网络。 @FlyingLemon 我试过.toURL().toNativeURL().toInternalURL()。前两个给我一个file:// URL,而最后一个给我一个cdvfile:// URL。 cdvfile:// 网址适用于图片,但不适用于视频。 @Grampa 我发现其他人也有类似的问题,尝试使用文件插件dev分支文件传输插件媒体插件 @FlyingLemon 谢谢你的建议。我刚刚尝试使用文件和文件传输插件的 dev 分支(我根本没有使用媒体插件)。但是,如果我使用此分支,则构建会失败,因为依赖于 CDVFilesystemURL 接口,无法找到该接口。我研究了一下插件代码,确实,这个接口只存在于文件插件的发布版本中。开发分支最后一次更新是在 11 个月前,所以可能代码太旧而无法使用。 【参考方案1】:

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

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

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

最终结果:

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

    创建一个名为 VideoURLProtocol 的新 Objective C 类,它扩展了 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

    将以下行添加到 AppDelegate.m 的 didFinishLaunchingWithOptions 方法中

    .
    .
    // These lines should already be there
    self.window.rootViewController = self.viewController;
    [self.window makeKeyAndVisible];
    
    // This line
    [NSURLProtocol registerClass:[VideoURLProtocol class]];
    .
    .    
    

    这里是使用这个新 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 来自动播放视频

<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 的一个非常有趣的教程,它非常详细地解释了 NSURLProtocol:http://www.raywenderlich.com/59982/nsurlprotocol-tutorial

【讨论】:

这被证明是可行的解决方案!完全按照这些说明,将从toURL() 获得的file:/// URL 重写为video:// 链接很好地解决了这个问题。对于尝试将此解决方案用于自己的项目的任何人,请特别注意搜索和替换:带有 3 个斜杠的 file:/// 替换为带有 2 个斜杠的 video:// 在尝试了这个解决方案一段时间后,我想指出一个问题(不是海报的解释,而是 iOS 本身)。如果您使用上述方法插入 &lt;video&gt; 标记并将源设置为指示的 URL,则应用程序将立即将整个视频加载到内存中,而无需缓冲。在我们的例子中,我们首先加载了一个 10MB 的视频,这很好。然后我们加载了一个 300MB 的视频,应用程序立即崩溃。我们最终使用的解决方案是免费的第三方视频播放器,它可以流式传输而不是加载视频。【参考方案2】:

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

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

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

<video   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

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

【讨论】:

我刚刚尝试过这种方法,不幸的是它不起作用。我已经尝试过使用cdvfile://localhost/ ... 之类的 URL,将它们转换为 /localhost/ ... 以及将 file:///var/ ... 转换为 /var/ ...。很公平,最后一个有点牵强,但我在这里很绝望 :) 将 URL 注入源代码并不是什么大问题,因为无论如何我的视频都是由 Javascript 添加的。

以上是关于Cordova 外部应用程序 + 本地视频的主要内容,如果未能解决你的问题,请参考以下文章

Cordova iOS 视频标签本地文件源

304 在 iOS Cordova 应用上播放 html5 视频时未修改

在带有外部 URL 的 iOS 上使用 PhoneGap/Cordova

Apache Cordova 本地通知插件图标

使用插件访问从 phonegap Cordova 中的外部 URL 加载 Webste

如何在 Cordova iOS 应用程序中播放 html 5 视频?