有没有人知道当当前 AVPlayerItem 即将完成播放时,AVQueuePlayer
是否开始缓冲下一个 AVPlayerItem
?
我知道文档中没有任何提示,主要是想问是否有人观察到这种行为或者没有。
有没有人知道当当前 AVPlayerItem 即将完成播放时,AVQueuePlayer
是否开始缓冲下一个 AVPlayerItem
?
我知道文档中没有任何提示,主要是想问是否有人观察到这种行为或者没有。
好的,我再次查看了这个问题,并编写了一些代码来检查AVQueuePlayer
。
jollyCocoa的答案指出了正确的方向,建议观察AVPlayerItem
上的状态属性。然而,文档似乎没有指出此属性(特别是其AVPlayerItemStatusReadyToPlay
值)可能与缓冲有关。
然而,AVPlayerItem的
loadedTimeRanges
属性似乎更与缓冲有关。
对该数组进行KVO有点棘手 - 数组对象本身不会改变,只有它的项会改变 - 所以我决定每秒打印一次它的内容。
我发现在队列的第一个项目中几秒钟后,第二个项目的loadedTimeRanges
出现了一个新的CMTimeRange
,其开始时间为0,持续时间很短。在前一个项目继续播放的同时,持续时间可以增加到60秒左右。
简短回答:AVQueuePlayer
将在播放当前项目时缓冲下一个AVPlayerItem
。
从iOS 5开始,AVQueuePlayer似乎不再预缓冲了。在iOS 4中,它会预先缓冲下一曲。
我不确定为什么会有变化,或者这是否是苹果故意的,还是一个错误。无论如何,这很麻烦,我将向他们提交一个错误报告。
找到了一个解决方法!!! 经过多个小时的搜索和失败的测试,我找到了一个适用于iOS 5及以上版本的解决方案。
这将在文件之间没有任何延迟的情况下播放。如果您想在文件之间切换,这不是很方便,但肯定会为您整合所有内容。在添加AVURLAsset时仍然可以跟踪每个文件的起点,保留CMTime变量,类似于:
NSValue *toJumpTo = [NSValue valueWithCMTime:composition.duration];
[array addObject:toJumpTo];
然后只需遍历数组,检查当前时间值并使用CMTimeCompare进行比较。
编辑:在此页面上找到(该链接目前指向垃圾邮件)。
[playerItem addObserver: self forKeyPath:@"status" options: 0 context: NULL];
我还将控制器设置为AVPlayerItem通知AVPlayerItemDidPlayToEndTimeNotification的观察者:
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(playerItemDidReachEnd:)
name: AVPlayerItemDidPlayToEndTimeNotification
object: nil];
你可以使用三个AVPlayer。第一个是上一个,第二个是当前的,最后一个是下一个。当当前播放时,将上一个播放器作为预加载并观察其状态。当预加载播放器的状态变为AVPlayerStatusReadyToPlay时,停止它并调用下面的代码:
[player.playerItem cancelPendingSeeks]; [player.playerItem.asset cancelLoading];
这些代码将防止预加载视频的所有数据。
我为您和其他想要减少每个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];
}
请按照以下步骤操作。如果有任何疑问,请在评论区留言。