AVQueuePlayer のプリバッファリング
-
26-09-2019 - |
質問
誰か知っていますか? AVQueuePlayer
次のバッファリングを開始します AVPlayerItem
現在のアイテムの再生が終了するのはいつですか?
ドキュメントにはこれを示唆するものが何もないことはわかっていますが、私が主に尋ねているのは、誰かがこの種の動作を観察したかどうかです。
解決
OK、この問題をもう一度見直して、チェックアウトするコードをいくつか書きました AVQueuePlayer
.
jollyCocoa の答えは、status プロパティを観察することを提案することで、私に正しい方向を示しました。 AVPlayerItem
. 。ただし、ドキュメントではこのプロパティが指摘されていないようです(そしてそれは AVPlayerItemStatusReadyToPlay
特に値) はバッファリングに関連している可能性があります。
しかし AVPlayerItem's
loadedTimeRanges
プロパティはバッファリングに関連しているようです。
その配列に対して KVO を実行するのは少し複雑でした。配列オブジェクト自体は変更されず、その項目のみが変更されました。そこで、その内容を毎秒出力することにしました。
私が発見したのは、キューの最初の項目の数秒間、 loadedTimeRanges
2 番目の項目には新しい項目が表示されます CMTimeRange
開始時間は 0 で、持続時間は短いです。前のアイテムが再生され続ける間、持続時間は最大 60 秒ほど増加する可能性があります。
短い答え: AVQueuePlayer
次をバッファリングします AVPlayerItem
現在のものをプレイしているとき。
他のヒント
iOSの5のとおり、AVQueuePlayerはもはや-バッファを事前ことを表示されません。それは、iOS 4でのプリバッファ次の曲でした。
私は変更があっただ理由はわからないか、それもAppleの一部または単にバグを審議います。いずれにせよ、それは苦痛だと私はそれについて彼らにバグを提出することがあります。
回避策を見つけました!!!何時間も検索し、テストに失敗した後、iOS 5 以降で動作する解決策を見つけました。
これにより、ファイル間の遅延なしでファイルが再生されます。ファイルを切り替えたい場合にはそれほど便利ではありませんが、すべてをまとめてくれるのは間違いありません。AVURLAsset を追加するときに、次のように CMTime 変数を保持することで、各ファイルの開始位置を追跡することは可能です。
NSValue *toJumpTo = [NSValue valueWithCMTime:composition.duration];
[array addObject:toJumpTo];
次に、配列を調べて現在の時刻値を確認し、CMTimeCompare を使用して比較します。
編集:で見つかりました このページ Web アーカイブから見ることができます (リンクは現在スパムを指しています)。
サーバーから提供されたムービーを使用して AVQueuePlayer を実験してきました。この特定のテストでは、タイプの異なる 2 つの異なるビデオ アセットへの URL で初期化された AVPlayerItems を使用して queuePlayer をセットアップしました。1 つは .mp4 ファイル (プログレッシブ ダウンロード)、もう 1 つは .m3u8 http ストリーミング ファイルです。ちょっとファンキーな動作をしていると言わざるを得ません。
ファイルをキューに入れる順序に応じて、動作はまったく異なります。.mp4 ファイルから開始すると、nextItem がプレーヤー (.m3u8 ファイル) を開始すると、AVPlayerLayer は空白になり、沈黙します。順序を変更して m3u8 ファイルから開始すると、AVPlayerLayer は空白になりますが、2 番目のファイルのオーディオは正常に再生されます。
これまで見てきたことからの私の印象は、AVQueuePlayer は次のことを行うということです。 ない 現在の AVPlayerItem の再生が終了する前に、次の AVPlayerItem のダウンロードを開始します。しかし、私は間違っているかもしれません。
続きそうですが…
編集:さて、さらにテストを行ったところ、最後のアイテムの再生が終了する前に次のアイテムのダウンロードが開始されるようです。以下の投稿を参照してください。「NO」を押してください...
編集2:コメントでは書式設定のオプションがなかったため、代わりにここに説明を追加します。(これが回答の更新を処理する悪い方法である場合は、これに関するベスト プラクティスを歓迎します)
とにかく、KVO を使用して status プロパティの監視を追加して itemsArray を反復処理しました。
[playerItem addObserver: self forKeyPath:@"status" options: 0 context: NULL];
また、コントローラーを AVPlayerItem 通知のオブザーバーとして設定しました AVPlayerItemDidPlayToEndTimeNotification:
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(playerItemDidReachEnd:)
name: AVPlayerItemDidPlayToEndTimeNotification
object: nil];
これで、AVPlayerItem のステータス変更をログに記録できるようになり、キュー内の次の AVPlayerItem がステータス変更とともにログに記録されていることがわかりました。 AVPlayerItemStatusReadyToPlay 現在のアイテムの再生が終了する前に。
したがって、私の結論は、現在のアイテムが終了する前に、次のアイテムのダウンロードが開始されるということです。
しかし! プレーヤーの作成に使用する AVPlayerItems を含む配列を作成します。AVAssets に関する AVFoundation ドキュメントを読むと、AVAssets はアセットを非同期でダウンロードするという印象を受けますが、これらのダウンロードが 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];
}
次の手順を実行します。疑問がある場合はコメントしてください。