下载iOS时流视频 [英] Stream video while downloading iOS

查看:137
本文介绍了下载iOS时流视频的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用iOS 7,我有一个.mp4视频,我需要在我的应用程序中下载。该视频很大(约1 GB),这就是为什么它不包含在应用程序中的原因。我希望用户能够在开始下载后立即开始观看视频。我还希望视频能够在iOS设备上缓存,以便用户以后不需要再次下载。播放视频的正常方法(渐进式下载和实时流式传输)似乎都不允许您缓存视频,因此我创建了自己的Web服务,将视频文件分块并将字节流式传输到客户端。我使用NSURLConnection启动流式HTTP调用:

I am using iOS 7 and I have a .mp4 video that I need to download in my app. The video is large (~ 1 GB) which is why it is not included as part of the app. I want the user to be able to start watching the video as soon as is starts downloading. I also want the video to be able to be cached on the iOS device so the user doesn't need to download it again later. Both the normal methods of playing videos (progressive download and live streaming) don't seem to let you cache the video, so I have made my own web service that chunks up my video file and streams the bytes down to the client. I start the streaming HTTP call using 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];

当我收到数据块时,我将它附加到文件本地副本的末尾:

When I receive a data chunk, I append it to the end of the local copy of the file:

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
     NSFileHandle *handle = [NSFileHandle fileHandleForWritingAtPath:[self videoFilePath]];
     [handle truncateFileAtOffset:[handle seekToEndOfFile]];
     [handle writeData:data];
}

如果我让设备运行,文件下载成功,我可以玩它使用MPMoviePlayerViewController:

If I let the device run, the file is downloaded successfully and I can play it using MPMoviePlayerViewController:

NSURL *url=[NSURL fileURLWithPath:self.videoFilePath];
MPMoviePlayerViewController *controller = [[MPMoviePlayerViewController alloc] initWithContentURL:url];
controller.moviePlayer.scalingMode = MPMovieScalingModeAspectFit;
[self presentMoviePlayerViewControllerAnimated:controller];

但是,如果我在文件完全下载之前启动播放器,则视频开始播放就好了。它甚至可以在顶部擦洗条上显示正确的视频长度。但是当用户到达视频中我在视频开始之前完成下载的位置时,视频就会挂起。如果我关闭并重新打开MPMoviePlayerViewController,那么视频会播放,直到它到达我再次启动MPMoviePlayerViewController时的任何位置。如果我等到整个视频下载完毕,那么视频播放没有问题。

However, if I start the player before the file is completely downloaded, the video starts playing just fine. It even has the correct video length displayed at the top scrubber bar. But when the user gets to the position in the video that I had completed downloading before the video started, the video just hangs. If I close and reopen the MPMoviePlayerViewController, then the video plays until it gets to whatever location I was then at when I launched the MPMoviePlayerViewController again. If I wait until the entire video is downloaded, then the video plays without a problem.

我没有收到任何事件,或者在此时打印到控制台的错误消息发生(视频开始后,MPMoviePlayerPlaybackStateDidChangeNotification和MPMoviePlayerPlaybackDidFinishNotification永远不会发送)。看起来还有其他东西告诉控制器视频的长度是什么,而不是擦洗器使用的...

I am not getting any events fired, or error messages printed to the console when this happens (MPMoviePlayerPlaybackStateDidChangeNotification and MPMoviePlayerPlaybackDidFinishNotification are never sent after the video starts). It seems like there is something else that is telling the controller what the length of the video is other than what the scrubber is using...

有谁知道什么可能是造成这个问题?我不一定要使用MPMoviePlayerViewController,所以如果在这种情况下使用不同的视频播放方法,我就可以了。

Does anyone know what could be causing this issue? I am not bound to using MPMoviePlayerViewController, so if a different video playback method would work in this situation I am all for it.

相关未解决的问题:

AVPlayer和AVURLAssets的渐进式视频下载

iOS上的累积视频下载

中播放下载进度视频文件?rq = 1>

How to play an in downloading progress video file in IOS

UPDATE 1
我发现视频停止确实是因为视频开始播放时的文件大小。我可以通过在开始下载之前创建一个归零文件并在我去的时候覆盖它来解决这个问题。由于我可以控制视频流服务器,因此我添加了自定义标头,因此我知道正在流式传输的文件的大小(流式文件的默认文件大小标头为-1)。我在didReceiveResponse方法中创建文件如下:

UPDATE 1 I have found that the video stall is indeed because of the file size when the video starts playing. I can get around this issue by creating a zero-ed out file before I start the download and over overwrite it as I go. Since I have control over the video streaming server, I added a custom header so I know the size of the file being streamed (default file size header for a streaming file is -1). I am creating the file in my didReceiveResponse method as follows:

- (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秒,同时它在磁盘上创建~1GB文件(在模拟器上它是即时的,只有真正的设备有这个问题)。这就是我现在被困住的地方 - 有没有人知道如何更有效地分配文件,或者以不同的方式让视频播放器识别文件的大小而无需实际分配它?我知道有些文件系统支持文件大小和磁盘大小作为两个不同的属性...不确定iOS是否有这样的东西?

This works great, except that truncate causes the app to hang for about 10 seconds while it creates the ~1GB file on disk (on the simulator it is instant, only a real device has this problem). This is where I am stuck now - does anyone know of a way to allocate a file more efficiently, or a different way to get the video player to recognize the size of the file without needing to actually allocate it? I know some filesystems support "file size" and "size on disk" as two different properties... not sure if iOS has something like that?

推荐答案

我想出了如何做到这一点,它比我原来的想法简单得多。

I figured out how to do this, and it is much simpler than my original idea.

首先,由于我的视频是在.mp4, MPMoviePlayerViewController或AVPlayer类可以直接从Web服务器播放 - 我不需要实现任何特殊的东西,他们仍然可以寻找视频中的任何一点。这必须是.mp4编码如何与电影播放器​​配合使用的一部分。所以,我只是在服务器上提供原始文件 - 不需要特殊标题。

First, since my video is in .mp4, the MPMoviePlayerViewController or AVPlayer class can play it directly from a web server - I don't need to implement anything special and they can still seek to any point in the video. This must be part of how the .mp4 encoding works with the movie players. So, I just have the raw file available on the server - no special headers required.

接下来,当用户决定播放视频时,我立即开始播放视频服务器URL:

Next, when the user decides to play the video I immediately start playing the video from the server URL:

NSURL *url=[NSURL fileURLWithPath:serverVidelFileURLString];
controller = [[MPMoviePlayerViewController alloc] initWithContentURL:url];
controller.moviePlayer.scalingMode = MPMovieScalingModeAspectFit;
[self presentMoviePlayerViewControllerAnimated:controller];

这使得用户可以观看视频并寻找他们想要的任何位置。然后,我开始使用NSURLConnection手动下载文件,就像我上面一直在做的那样,除了现在我没有流式传输文件,我只是直接下载它。这样我就不需要自定义标头,因为文件大小包含在HTTP响应中。

This makes it so the user can watch the video and seek to any location they want. Then, I start downloading the file manually using NSURLConnection like I had been doing above, except now I am not streaming the file, I just download it directly. This way I don't need the custom header since the file size is included in the HTTP response.

当我的后台下载完成时,我从服务器切换播放项目本地文件的URL。这对于网络性能非常重要,因为电影播放器​​只能在用户观看之前几秒钟下载。能够尽快切换到本地文件是避免下载过多重复数据的关键:

When my background download completes, I switch the playing item from the server URL to the local file. This is important for network performance because the movie players only download a few seconds ahead of what the user is watching. Being able to switch to the local file as soon as possible is key to avoid downloading too much duplicate data:

NSTimeInterval currentPlaybackTime = videoController.moviePlayer.currentPlaybackTime;

[controller.moviePlayer setContentURL:url];
[controller.moviePlayer setCurrentPlaybackTime:currentPlaybackTime];
[controller.moviePlayer play];

这种方法确实让用户最初同时下载两个视频文件,但是初步测试了网络速度我的用户将使用显示它只会增加下载时间几秒钟。适合我!

This method does have the user downloading two video files at the same time initially, but initial testing on the network speeds my users will be using shows it only increases the download time by a few seconds. Works for me!

这篇关于下载iOS时流视频的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆