Я использую iOS 7, и у меня есть видео .mp4, которое мне нужно скачать в моем приложении. Видеоролик большой (~ 1 ГБ), поэтому он не входит в состав приложения. Я хочу, чтобы пользователь мог начать просмотр видео, как только начнется загрузка. Я также хочу, чтобы видео можно было кэшировать на устройстве iOS, поэтому пользователю не нужно будет загружать его позже. Как обычные способы воспроизведения видео (прогрессивная загрузка, так и потоковая передача в реальном времени), похоже, не позволяют вам кэшировать видео, поэтому я сделал свой собственный веб-сервис, который разбивает мой видеофайл и передает потоки байтам клиенту. Я запускаю потоковый HTTP-вызов с помощью NSURLConnection:
self.request = [[NSMutableURLRequest alloc] initWithURL:self.url];
[self.request setTimeoutInterval:10]; // Expect data at least every 10 seconds
[self.request setHTTPMethod:@"GET"];
self.connection = [[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:YES];
Когда я получаю кусок данных, я добавляю его в конец локальной копии файла:
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
NSFileHandle *handle = [NSFileHandle fileHandleForWritingAtPath:[self videoFilePath]];
[handle truncateFileAtOffset:[handle seekToEndOfFile]];
[handle writeData:data];
}
Если я разрешаю устройству работать, файл загружается успешно, и я могу воспроизвести его с помощью MPMoviePlayerViewController:
NSURL *url=[NSURL fileURLWithPath:self.videoFilePath];
MPMoviePlayerViewController *controller = [[MPMoviePlayerViewController alloc] initWithContentURL:url];
controller.moviePlayer.scalingMode = MPMovieScalingModeAspectFit;
[self presentMoviePlayerViewControllerAnimated:controller];
Однако, если я запустил плеер до того, как файл будет полностью загружен, видео начнет играть очень хорошо. Он даже имеет правильную длину видео, отображаемую на верхней панели скруббера. Но когда пользователь добирается до позиции в видео, которое я завершил загрузку до начала видео, видео просто зависает. Если я закрываю и снова открываю MPMoviePlayerViewController, тогда видео воспроизводится до тех пор, пока оно не попадет в то место, где я был тогда, когда я снова запустил MPMoviePlayerViewController. Если я подожду, пока не будет загружено все видео, видео воспроизводится без проблем.
Я не запускаю какие-либо события или сообщения об ошибках, напечатанные на консоли, когда это происходит (MPMoviePlayerPlaybackStateDidChangeNotification и MPMoviePlayerPlaybackDidFinishNotification никогда не отправляются после запуска видео). Кажется, есть что-то еще, что говорит контроллеру, какая длина видео отличается от того, что использует скруббер...
Кто-нибудь знает, что может вызвать эту проблему? Я не обязан использовать MPMoviePlayerViewController, поэтому, если в этой ситуации будет работать другой способ воспроизведения видео, я все для него.
Связанные неразрешенные вопросы:
Загрузка AVPlayer и прогрессивного видео с помощью AVURLAssets
Прогрессивная загрузка видео на iOS
Как играть в загрузку видеоизображения в IOS
ОБНОВЛЕНИЕ 1 Я обнаружил, что видеозахват действительно зависит от размера файла при воспроизведении видео. Я могу обойти эту проблему, создав нулевой файл, прежде чем запускать загрузку и перезаписать его по мере поступления. Поскольку у меня есть контроль над сервером потоковой передачи видео, я добавил пользовательский заголовок, поэтому я знаю, что размер потока данных (размер файла по умолчанию для потокового файла равен -1). Я создаю файл в моем методе didReceiveResponse следующим образом:
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
// Retrieve the size of the file being streamed.
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
NSDictionary *headers = httpResponse.allHeaderFields;
NSNumberFormatter * formatter = [[NSNumberFormatter alloc] init];
[formatter setNumberStyle:NSNumberFormatterDecimalStyle];
self.streamingFileSize = [formatter numberFromString:[headers objectForKey:@"StreamingFileSize"]];
// Check if we need to initialize the download file
if (![[NSFileManager defaultManager] fileExistsAtPath:self.path])
{
// Create the file being downloaded
[[NSData data] writeToFile:self.path atomically:YES];
// Allocate the size of the file we are going to download.
const char *cString = [self.path cStringUsingEncoding:NSASCIIStringEncoding];
int success = truncate(cString, self.streamingFileSize.longLongValue);
if (success != 0)
{
/* TODO: handle errors here. Probably not enough space... See 'man truncate' */
}
}
}
Это отлично работает, за исключением того, что truncate заставляет приложение зависать в течение примерно 10 секунд, пока он создает файл размером ~ 1 ГБ на диске (на симуляторе он работает мгновенно, только у этого реального устройства есть эта проблема). Вот где я сейчас застрял - кто-нибудь знает способ более эффективного распределения файла или другой способ заставить видеоплеер распознавать размер файла без необходимости его фактически распределять? Я знаю, что некоторые файловые системы поддерживают "размер файла" и "размер на диске" как два разных свойства... не уверен, что у iOS есть что-то подобное?