現在のアイテムの再生が終了しようとしているときにAVQueuePlayer
次のバッファリングを開始するかどうかは誰にもわかりませんか?AVPlayerItem
ドキュメントにはこれを示唆するものは何もないことは知っています。主に、誰かがこの種の動作を観察したかどうかを尋ねています。
現在のアイテムの再生が終了しようとしているときにAVQueuePlayer
次のバッファリングを開始するかどうかは誰にもわかりませんか?AVPlayerItem
ドキュメントにはこれを示唆するものは何もないことは知っています。主に、誰かがこの種の動作を観察したかどうかを尋ねています。
わかりました、この問題をもう一度調べて、チェックアウトするコードをいくつか書きましたAVQueuePlayer
。
jollyCocoaの答えは、ステータスプロパティを観察することを提案することで、私を正しい方向に向けましたAVPlayerItem
。AVPlayerItemStatusReadyToPlay
ただし、ドキュメントでは、このプロパティ (および特にその値) がバッファリングに関連している可能性があることを指摘していないようです。
ただし、AVPlayerItem's
loadedTimeRanges
プロパティはバッファリングに関連しているようです。
その配列でKVOを実行するのは少しトリッキーでした-配列オブジェクト自体は変更されず、アイテムのみが変更されます-そのため、その内容を毎秒印刷することにしました。
私が見つけたのは、キューの最初のアイテムの数秒で、2 番目のアイテムが開始時間 0 と短い期間でloadedTimeRanges
新しいアイテムを表示することです。CMTimeRange
前のアイテムが再生され続けている間、持続時間は最大 60 秒程度まで増加する可能性があります。
簡単な答え: 現在のものを再生している間AVQueuePlayer
に次のものをバッファリングします。AVPlayerItem
iOS 5 の時点で、AVQueuePlayer はプリバッファリングを行わなくなったようです。iOS 4 では次のトラックをプリバッファリングしました。
なぜ変更があったのか、それが Apple 側の意図的なものなのか、それとも単なるバグなのかはわかりません。いずれにせよ、それは苦痛であり、私はそれについて彼らにバグを提出します.
回避策を見つけました!!! 何時間も検索してテストに失敗した後、iOS5以降で動作するソリューションを見つけました。
これにより、間に遅延なくファイルが再生されます。ファイルを切り替えたい場合はそれほど便利ではありませんが、すべてをまとめることはできます。AVURLAssetを追加するときに、次のようなCMTime変数を保持して、各ファイルの開始位置を追跡することは引き続き可能です。
NSValue *toJumpTo = [NSValue valueWithCMTime:composition.duration];
[array addObject:toJumpTo];
次に、配列を調べて、現在の時刻の値を確認し、CMTimeCompareを使用して比較します。
編集:ウェブアーカイブから見えるこのページで見つかりました(リンクは現在スパムを指しています)。
サーバーが提供するムービーを使用して AVQueuePlayer を試しています。この特定のテストでは、異なるタイプの 2 つの異なるビデオ アセットへの URL で初期化された AVPlayerItems を使用して queuePlayer をセットアップしました。1 つは .mp4 ファイル (プログレッシブ ダウンロード)、もう 1 つは .m3u8 http ストリーミング ファイルです。それは少しファンキーに振る舞います、私は言わなければなりません.
ファイルをキューに入れる順序によって、まったく異なる動作が得られます。.mp4 ファイルから開始すると、nextItem がプレーヤー (.m3u8 ファイル) を開始すると、AVPlayerLayer は空白になり、サイレントになります。順序を変更して m3u8 ファイルから開始すると、AVPlayerLayer は空白になりますが、2 番目のファイルのオーディオは正常に再生されます。
私の印象は、これまで見てきたことから、現在の AVPlayerItem の再生が終了する前に AVQueuePlayer が次の AVPlayerItem のダウンロードを開始しないということです。しかし、私は間違っているかもしれません。
つづきはきっと…
編集: OK、さらにテストを行ったところ、最後のアイテムの再生が終了する前に次のアイテムのダウンロードが開始されるようです。以下の投稿を参照してください。「NOT」をなでる...
Edit2: コメントで書式設定オプションがなくなったため、代わりにここに説明を追加します。(これが回答の更新を処理する悪い方法である場合、これのベストプラクティスは大歓迎です)
とにかく、KVOを使用してステータスプロパティの観察を追加して、itemsArrayを繰り返し処理しました...
[playerItem addObserver: self forKeyPath:@"status" options: 0 context: NULL];
また、コントローラーを AVPlayerItem Notification AVPlayerItemDidPlayToEndTimeNotificationのオブザーバーとして設定しました。
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(playerItemDidReachEnd:)
name: AVPlayerItemDidPlayToEndTimeNotification
object: nil];
これで、AVPlayerItem のステータス変更をログに記録できました。現在のアイテムの再生が終了する前に、キュー内の次の AVPlayerItem がステータス変更AVPlayerItemStatusReadyToPlayでログに記録されていることがわかりました。
したがって、現在のアイテムが終了する前に、次のアイテムのダウンロードが開始されるというのが私の結論です。
でも! プレーヤーの作成に使用する AVPlayerItems で配列を作成します。AVAssets の AVFoundation ドキュメントを読むと、これらがアセットを非同期的にダウンロードする印象を受けますが、これらのダウンロードが IAPlayerItem によっていつ開始されるのかはまだわかりません。.m3u8 ファイルをキューに追加すると、まだファンキーな動作が発生するため、これらのアセットが作成時にダウンロードを開始するかどうか疑問に思います (ただし、少し奇妙に思えます)。
URL からの各ビデオのバッファリング時間を短縮したいあなたや他の人のために、簡単なデモを作成しました。
注:これが正しい方法かどうかはわかりませんが、問題なく私にとっては完璧に機能しています。誰かがもっと知っているか、より良い結果を得るためにコードを改善したい場合は、私の答えを変更してください。
以下のコードでは、最初のビデオに通常のバッファリング時間がかかります。現在のビデオが終了すると、次のビデオが自動的に開始され、左右にスワイプして次のビデオと前のビデオに移動できます。
以下の手順に従ってください。
1) videoPlayerVC.hファイル内。
#import <AVFoundation/AVFoundation.h>
#import <AVKit/AVKit.h>
@interface videoPlayerVC : UIViewController
{
AVPlayerViewController *avPlyrViewController;
AVPlayerItem *currentAVPlyrItem;
int currentVideoNO; // For current video track.
NSMutableArray *arrOfAVPItems;
NSMutableArray *arrOfPlyer;
}
2) videoPlayerVC.mファイル内
ここで、ボタンをクリックしてビデオの再生を開始できます。以下はボタンのアクションメソッドです。
-(void)clickOnPlayVideosImage:(UIButton *)sender
{
// In my App. all video URLs is up to 15 second.
currentVideoNO = 0;
NSMutableArray *arrForVideoURL = [[NSMutableArray alloc]initWithObjects:
[NSURL URLWithString:@"http://videos/url1.mov"],
[NSURL URLWithString:@"http://videos/url2.mov"],
[NSURL URLWithString:@"http://videos/url3.mov"],
[NSURL URLWithString:@"http://videos/url3.mov"],
[NSURL URLWithString:@"http://videos/url4.mov"],
[NSURL URLWithString:@"http://videos/url5.mov"], nil];
AVPlayerItem *thePlayerItemA = [[AVPlayerItem alloc] initWithURL:[arrForVideoURL objectAtIndex:0]];
AVPlayerItem *thePlayerItemB = [[AVPlayerItem alloc] initWithURL:[arrForVideoURL objectAtIndex:1]];
AVPlayerItem *thePlayerItemC = [[AVPlayerItem alloc] initWithURL:[arrForVideoURL objectAtIndex:2]];
AVPlayerItem *thePlayerItemD = [[AVPlayerItem alloc] initWithURL:[arrForVideoURL objectAtIndex:3]];
AVPlayerItem *thePlayerItemE = [[AVPlayerItem alloc] initWithURL:[arrForVideoURL objectAtIndex:4]];
AVPlayerItem *thePlayerItemF = [[AVPlayerItem alloc] initWithURL:[arrForVideoURL objectAtIndex:5]];
if(arrOfAVPItems.count > 0)
[arrOfAVPItems removeAllObjects];
arrOfAVPItems = nil;
if(arrOfPlyer.count > 0)
[arrOfPlyer removeAllObjects];
arrOfPlyer = nil;
arrOfPlyer = [[NSMutableArray alloc] init];
arrOfAVPItems = [NSMutableArray arrayWithObjects:thePlayerItemA, thePlayerItemB, thePlayerItemC, thePlayerItemD, thePlayerItemE, thePlayerItemF, nil]; // Add All items in the Array
for(AVPlayerItem *myPlyrItem in arrOfAVPItems)
{
AVPlayer *videoPlayerNext = [AVPlayer playerWithPlayerItem:myPlyrItem]; /// Add item in the player
[videoPlayerNext play];
[videoPlayerNext pause];
[arrOfPlyer addObject:videoPlayerNext]; /// Make Array of "AVPlayer" just reduce buffering of each video.
}
avPlyrViewController = [AVPlayerViewController new];
avPlyrViewController.delegate = self;
avPlyrViewController.player = (AVPlayer *)arrOfPlyer[0]; // Add first player from the Array.
avPlyrViewController.showsPlaybackControls = YES;
avPlyrViewController.allowsPictureInPicturePlayback = YES;
avPlyrViewController.videoGravity = AVLayerVideoGravityResizeAspect;
[self presentViewController:avPlyrViewController animated:YES completion:^{
[self performSelector:@selector(playVideos) withObject:nil afterDelay:1];/// call method after one second for play video.
UISwipeGestureRecognizer * swipeleft=[[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(swipeleftToNextVideo:)];
swipeleft.direction=UISwipeGestureRecognizerDirectionLeft;
[avPlyrViewController.view addGestureRecognizer:swipeleft]; // Add left swipe for move on next video
UISwipeGestureRecognizer * swiperight=[[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(swipeleftToPerviousVideo:)];
swiperight.direction=UISwipeGestureRecognizerDirectionRight;
[avPlyrViewController.view addGestureRecognizer:swiperight]; // Add right swipe for move on previous video
}];
}
次に、playVideos
メソッド コードを記述します。
#pragma mark - AVPlayer Methods -
-(void)playVideos
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:currentAVPlyrItem]; // remove current "AVPlayerItem" from the notification observe.
if(arrOfAVPItems.count > 0)
{
currentAVPlyrItem = (AVPlayerItem *)arrOfAVPItems[currentVideoNO]; // Add "AVPlayerItem" from the array.
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playerItemDidReachEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:currentAVPlyrItem]; // Add notification observer to indication current "AVPlayerItem" is finish.
// pause and nil previous player if available.
[avPlyrViewController.player pause];
avPlyrViewController.player = nil;
avPlyrViewController.player = (AVPlayer *)arrOfPlyer[currentVideoNO]; // add new player from the array
[avPlyrViewController.player.currentItem seekToTime:kCMTimeZero]; // set for start video on initial position.
[avPlyrViewController.player play]; // Play video
}
}
ビデオが終了したことを示す通知オブザーバー。
- (void)playerItemDidReachEnd:(NSNotification *)notification
{
NSLog(@"IT REACHED THE END");
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:currentAVPlyrItem]; // remove current "AVPlayerItem" from the notification observe.
[self swipeleftToNextVideo:nil]; // Call method for next video.
}
次の動画を再生する方法
-(void)swipeleftToNextVideo:(UISwipeGestureRecognizer*)gestureRecognizer
{
currentVideoNO++;
if(currentVideoNO > (arrOfAVPItems.count -1))
currentVideoNO = 0;
NSLog(@"current - %d and last - %d", currentVideoNO, (int)(arrOfAVPItems.count -1));
[self playVideos];
}
前の動画を再生する方法。
-(void)swipeleftToPerviousVideo:(UISwipeGestureRecognizer*)gestureRecognizer
{
currentVideoNO--;
if(currentVideoNO < 0)
currentVideoNO = (int)(arrOfAVPItems.count -1);
NSLog(@"current - %d and last - %d", currentVideoNO, (int)(arrOfAVPItems.count -1));
[self playVideos];
}
次の手順を実行します。疑問がある場合は、コメントしてください。