iOS 7 を使用しており、アプリにダウンロードする必要がある .mp4 ビデオがあります。ビデオはサイズが大きい (~ 1 GB) ため、アプリの一部として含まれていません。ダウンロードが開始されるとすぐに、ユーザーがビデオの視聴を開始できるようにしたいと考えています。また、ユーザーが後で再度ダウンロードする必要がないように、ビデオを iOS デバイスにキャッシュできるようにしたいと考えています。ビデオを再生する通常の方法 (プログレッシブ ダウンロードとライブ ストリーミング) の両方で、ビデオをキャッシュできないようです。そのため、ビデオ ファイルをチャンクアップしてバイトをクライアントにストリーミングする独自の Web サービスを作成しました。NSURLConnection を使用してストリーミング HTTP 呼び出しを開始します。
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 を使用することに縛られていないので、この状況で別のビデオ再生方法が機能する場合は、それで十分です。
関連する未解決の質問:
AVURLAssets を使用した AVPlayer およびプログレッシブ ビデオ ダウンロード
更新 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' */
}
}
}
これはうまく機能しますが、トランケートによってディスク上に ~1GB のファイルが作成される間、アプリが約 10 秒間ハングすることを除きます (シミュレーターでは瞬時に発生し、実際のデバイスでのみこの問題が発生します)。これは私が今行き詰まっている場所です-ファイルをより効率的に割り当てる方法、または実際に割り当てる必要なしにビデオプレーヤーにファイルのサイズを認識させる別の方法を知っている人はいますか? 一部のファイルシステムは「ファイルサイズ」と「ディスク上のサイズ」を2つの異なるプロパティとしてサポートしていることを知っています... iOSにそのようなものがあるかどうかわかりませんか?