Подтвердить что ты не робот

Кордовое внешнее приложение + местное видео

У нас есть приложение iOS, построенное с помощью PhoneGap/Cordova 4.3.0. Это приложение напрямую загружает внешний веб-сайт, используя <content src="http://example.com/foo" /> в файле config.xml. Все функциональные возможности содержатся на этом веб-сайте, поэтому мы фактически не используем какие-либо локальные файлы HTML или JS.

Как часть функциональности приложения, мы должны воспроизвести некоторые видео. Поскольку приложение предназначено для работы в автономном режиме, мы хотим кэшировать эти видеоролики локально. Поэтому мы загружаем их на устройство с помощью плагина FileTransfer вместе с другими ресурсами, такими как изображения или PDF файлы. После загрузки файлов мы получаем URL-адреса с протоколом file://. У нас также есть возможность использовать протокол cdvfile://. Когда мы используем URL cdvfile:// для показа изображений, изображения отображаются правильно. Однако видео не воспроизводится вообще.

Для воспроизведения видео мы используем стандартные теги HTML5:

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

Видео сами работают, и они будут играть правильно из внешнего источника (как в, они будут воспроизводиться, если мы обращаемся к ним с сервера, а не из локальной файловой системы). Я понимаю, что проблема связана с концепциями, связанными с веб-сайтами, такими как политика одного и того же происхождения и с ограничением доступа к локальной файловой системе. Тем не менее, в то же время я должен задаться вопросом, почему именно изображения отлично работают при этих же ограничениях.

То, что я пробовал до сих пор:

  • Использование file:// и cdvfile:// URL-адресов в качестве src видео. Это не дает никакого визуального эффекта. Экран просто черный.
  • Использование iframe с src, установленным для URL-адреса видео. При использовании file:// экран по-прежнему остается черным. Однако при использовании cdvfile:// появляется интерфейс проигрывателя iOS с кнопкой воспроизведения и полноэкранной кнопкой, но видео не воспроизводится, и также нет временной шкалы.
  • Добавление локального файла в кордову под названием video.html, который принимает URL-адрес в качестве параметра и отображает тег video с этим URL как src. План должен был включить этот файл как iframe, но, видимо, я не могу сделать iframe для локального файла. Я пробовал различные URL-адреса, которые могли бы указывать на этот конкретный файл video.html (хотя на самом деле я не уверен, что это возможно). Среди тех, которые я пробовал, были: cordova.file.applicationDirectory + 'www/video.html', http://localhost/www/video.html, cdvfile://localhost/www/video.html.
  • Я искал плагин cordova, который будет воспроизводить видео, но я не смог найти его для iOS. Большинство плагинов, похоже, нацелены на Android.

Теперь, возможно, я ошибаюсь в этом. Как я вижу, "стандартным вариантом использования" для кордовой является то, что вы храните свои файлы HTML/JS/CSS локально. Внешний контент, подобный тому, который я использую, вероятно, немного необычен. Я объясню требования к этому приложению, которые привели меня к использованию этой функции.

  • Предполагается, что приложение будет создано для нескольких платформ (хотя мы и начинаем с iOS). Поэтому мы используем PhoneGap.
  • Предполагается, что он доступен как онлайн, так и офлайн, хотя весь контент поступает с сервера (контент не создается локально). Вот почему мы загружаем контент и сохраняем его локально.
  • Также предполагается автоматическое обновление любой части себя на лету, не требуя обновления из App Store. Вот почему мы используем внешнюю страницу, потому что она имеет cache.manifest, которая позволяет нам контролировать обновления кода веб-приложения и в то же время разрешать его кэширование локально. Это, наверное, самое главное, потому что, если мы хотим сохранить некоторые файлы локально в Кордове, нам придется повторно реализовать эту функциональность кеша в Javascript (используя как можно более тонкий слой).

В любом случае, моя главная проблема заключается в том, как заставить эти видео работать. Я готов попробовать самые взломанные обходные пути! Если это действительно невозможно с текущими решениями по развитию, то, возможно, вы могли бы дать мне несколько советов о том, как я должен структурировать приложение, чтобы он работал и выполнял мои требования.

Большое спасибо!

4b9b3361

Ответ 1

У меня был аналогичный проект около года назад. Но я не помню, как я столкнулся с этой проблемой, потому что мы связали наши html/js/css с приложением.

Проблема в том, что вы пытаетесь загрузить файл:///url протокола из html файла, который был из http:///, который не является чем-то удобным для UIWebView.

Вы можете обойти это с помощью специальной схемы URL, например video://, но вам нужно написать собственный код, который перехватывает эти запросы и передает фактическое видео обратно в систему загрузки URL.

Конечный результат:

The end result

Вот как я это сделал с помощью Cordova 4.3.0 и немного ObjectiveC

  • Создайте новый класс Objective C с именем 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. Добавьте следующую строку в метод didFinishLaunchingWithOptions приложения AppDelegate.m

    .
    .
    // These lines should already be there
    self.window.rootViewController = self.viewController;
    [self.window makeKeyAndVisible];
    
    // This line
    [NSURLProtocol registerClass:[VideoURLProtocol class]];
    .
    .    
    
  2. И вот код javascript, который использует эту новую схему URL

    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 МБ. Это то, что я нашел трудным путем после отклонения моего приложения. Вы можете увидеть пространство, занятое вашим приложением: Настройки > iCloud > Хранение > Управление хранилищем > {{Ваше iphone name}} > Показать все приложения

Вы можете автозапускать видео, добавив следующую строку в свой config.xml

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

Вы также можете воспроизводить видеоролики, добавив следующую строку в свой файл config.xml, в дополнение к этому вам также нужно добавить атрибут webkit-playinginline = "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

Ответ 2

В моем опыте использование протокола file:// было проблематичным для iOS, потому что протокол начинается с корня файловой системы устройства.

Я не считаю, что здесь возникают проблемы Cross-Origin, потому что Cordova не выполняет запросы Cross-Origin, а рассматривает все запросы как исходящие от источника, к которому они обращаются. См. этот ответ.

Мое теоретическое решение использует URL relative вместо того, чтобы вообще пытаться использовать какой-либо протокол. Однако способ, которым вы это реализуете, может зависеть от того, когда файл будет успешно загружен.

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

Где cdvfile://localhost/www/ - это путь, который вы задали для аргумента target, когда вы вызывали fileTransfer.download() на который ссылается здесь.

Может потребоваться либо создать элемент видео, либо установить видео src в javascript после того, как был запущен successCallback. Снова вы должны установить src как относительный URL.

Обратите внимание, что видеоролики не будут автоматически запускаться на мобильных устройствах

Из библиотеки разработчиков Safari

В Safari на iOS (для всех устройств, включая iPad), где пользователь может находиться в сотовой сети и заряжаться на единицу данных, предварительная загрузка и автовоспроизведение отключены.