为AVQueuePlayer预缓冲

有谁知道AVQueuePlayer是否开始caching下一个AVPlayerItem当前项目即将完成播放?

我知道在文档中没有什么可以提出这个build议,我主要问的是,如果有人观察到这种行为或不。

好的,我再次查看这个问题,并写了一些代码来检查出AVQueuePlayer

jollyCocoa的答案通过build议观察AVPlayerItem上的状态属性来指向正确的方向。 但是文档似乎并没有指出这个属性(特别是AVPlayerItemStatusReadyToPlay值)可能与缓冲有关。

然而, AVPlayerItem's loadedTimeRanges属性似乎更多地与缓冲相关。

在这个数组上做KVO有点麻烦 – 数组对象本身没有改变,只有它的项目做 – 所以我采取打印出它的内容每一秒。

我发现队列的第一个项目中有几秒钟,第二个项目的loadedTimeRanges显示了一个新的CMTimeRange ,开始时间为0,持续时间很短。 持续时间可以增加60秒左右,而前一个项目继续玩。

简短的回答: AVQueuePlayer将缓冲下一个AVPlayerItem播放当前的播放器。

从iOS 5开始,似乎AVQueuePlayer不再预缓冲。 它预先缓冲了iOS 4中的下一首曲目。

我不确定为什么会发生变化,或者甚至是苹果的故意,或者只是一个错误。 无论如何,这是一个痛苦,我会提交一个错误给他们。

find了一个工作! 经过许多小时的search和testing失败后,我发现了一个适用于iOS 5及更高版本的解决scheme。

这将播放文件之间没有任何延迟。 如果你想在文件之间进行切换,这样做不是很方便,但一定会把所有的东西放在一起。 当添加AVURLAsset时,仍然可以跟踪每个文件的起始位置,保持CMTimevariables,如下所示:

 NSValue *toJumpTo = [NSValue valueWithCMTime:composition.duration]; [array addObject:toJumpTo]; 

然后通过数组,检查当前时间值并使用CMTimeCompare进行比较。

编辑:被发现在http://www.developers-life.com/avplayer-looping-video-without-hiccupdelays.html但作为斯特凡·肯德尔在评论中提到,现在是一个垃圾邮件网站。 以为我会留下链接的归属目的,但不要访问它!

我一直在使用服务器提供的电影来试验AVQueuePlayer。 在这个特定的testing中,我设置了一个带有AVPlayerItems的URLPlayerItems的初始化URL到两个不同types的video资产。 一个是.mp4文件(渐进式下载),另一个是.m3u8 httpstream式文件。 它确实有点时髦,我必须说。

根据我排队文件的顺序,我会得到完全不同的行为。 如果我从.mp4文件开始,当nextPlayer启动播放器(.m3u8文件)时,AVPlayerLayer将变为空白且无声。 如果我更改顺序并从m3u8文件开始,AVPlayerLayer将变为空白,但来自第二个文件的audio播放正常。

从我目前看到的情况来看,我的印象是AVQueuePlayer在当前AVPlayerItem播放完毕之前不会开始下载下一个AVPlayerItem。 但我可能是错的。

待续,我猜…

编辑:好的,所以我做了一些更多的testing,似乎下一个项目开始下载之前,最后一个项目完成播放。 见下面的post。 描边“不”…

编辑2:添加我的解释,而不是,因为评论留给我没有格式选项。 (最佳做法是欢迎,如果这是一个不好的方式来处理答案更新)

无论如何,我迭代我的itemsArray添加使用KVO状态属性的观察…

 [playerItem addObserver: self forKeyPath:@"status" options: 0 context: NULL]; 

我也将我的控制器设置为AVPlayerItem通知AVPlayerItemDidPlayToEndTimeNotification的观察者:

 [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(playerItemDidReachEnd:) name: AVPlayerItemDidPlayToEndTimeNotification object: nil]; 

现在,我可以loggingAVPlayerItem的状态变化,并且事实certificate,在当前项目结束回放之前,队列中的下一个AVPlayerItem被logging为状态改变AVPlayerItemStatusReadyToPlay

我的结论是,在当前项目结束之前,下一个项目开始下载。

然而! 我用AVPlayerItems创build一个数组,用于创build我的播放器。 阅读AVAssets的AVFoundation文档,我得到的印象是这些下载他们的资产asynchronous,但我还不能确定何时这些下载是由IAPlayerItem启动。 当我将.m3u8文件添加到队列中时,我仍然有一些奇怪的行为,这让我怀疑这些资产是否在创build时开始下载(虽然对我来说似乎有点奇怪)。

我为你和其他人减less了每个video缓冲时间的简单演示。

注意:不知道这是正确的方式,但它没有任何问题对我来说是完美的。 如果有人知道更多或想改善代码,以获得更好的结果,然后欢迎改变我的答案。

在下面的代码中,第一个video采取正常的缓冲时间。 下一个video将在当前video结束时自动启动,您可以左右滑动来移动下一个video和上一个video。

按照以下步骤。

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文件中

在这里你可以通过点击button开始播放video。 下面是button的操作方法。

 -(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 } } 

通知观察者指示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. } 

播放下一个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]; } 

播放前一个video的方法。

 -(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]; } 

按着这些次序。 如果有任何疑问,请评论。