【问题标题】:Pre-buffering for AVQueuePlayerAVQueuePlayer 的预缓冲
【发布时间】:2011-05-12 05:04:27
【问题描述】:

有谁知道AVQueuePlayer 在当前项目即将结束播放时是否开始缓冲下一个AVPlayerItem

我知道文档中没有任何建议,我主要是在询问是否有人观察到这种行为。

【问题讨论】:

    标签: iphone objective-c avfoundation avplayer


    【解决方案1】:

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

    jollyCocoa 的回答通过建议观察AVPlayerItem 上的状态属性为我指明了正确的方向。但是文档似乎没有指出该属性(尤其是 AVPlayerItemStatusReadyToPlay 值)可能与缓冲有关。

    但是AVPlayerItem's loadedTimeRanges 属性似乎与缓冲更相关。

    在该数组上执行 KVO 有点棘手 - 数组对象本身不会改变,只有项目会改变 - 所以我每秒都打印出它的内容。

    我发现在队列的第一项中几秒钟,第二项的loadedTimeRanges 显示了一个新的CMTimeRange,开始时间为 0,持续时间较短。在前一个项目继续播放的同时,持续时间最多可以增加到 60 秒左右。

    简答:AVQueuePlayer 将在播放当前AVPlayerItem 时缓冲下一个AVPlayerItem

    【讨论】:

    • 加载 AVPlayerItem 不是导致玩家延迟的原因;如果您不同意,请向我展示证明它的代码。我有一个工作应用程序,可以在一个集合视图单元格中为 iPhone 上的每个视频资产加载一个 AVPlayerItem。您可以随心所欲地滚动,每个视频的第一帧都会显示,就好像您只从资源中请求单个图像一样。但是,播放延迟似乎只有在滚动时才开始显现。那是因为你在一个线程和一个队列中放了太多东西。在单独的线程和队列上启动播放,延迟就消失了。
    • @JamesBush 有趣的评论,你能详细说明一下吗,也许有一些代码?
    【解决方案2】:

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

    我不确定为什么会发生变化,或者它是否是 Apple 故意的,或者仅仅是一个错误。无论如何,这很痛苦,我会向他们提交一个关于它的错误。

    【讨论】:

    • 如果你提交了错误报告,你能打开雷达以便我们欺骗它吗?
    • 此错误报告是否有任何进展?我的应用程序受到相同错误的影响,当 AVQueuePlayer 移动到下一个文件时,将两个本地存储的 mp4 文件排队会导致播放停止。
    • @matt-gallagher,你有没有收到苹果公司关于 AVQueuePlayer 缺少预缓冲的回复?
    【解决方案3】:

    找到了解决办法!!!经过数小时的搜索和失败的测试,我找到了适用于 iOS 5 及更高版本的解决方案。

    这将播放文件,中间没有任何延迟。如果您想在文件之间切换,则不那么方便,但肯定会为您将所有内容放在一起。仍然可以跟踪每个文件的开始位置,添加 AVURLAsset 时,保留 CMTime 变量,例如:

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

    然后只需遍历数组,检查当前时间值并使用 CMTimeCompare 进行比较。

    编辑:在网络存档中可见的this page 上找到(该链接当前指向垃圾邮件)。

    【讨论】:

    • 哇哦,那个 AVComposition 技巧正是我所需要的。干杯! :)
    • 您可以将链接替换为archived one
    【解决方案4】:

    我一直在使用服务器提供的电影试验 AVQueuePlayer。在这个特定的测试中,我设置了一个 queuePlayer,其中 AVPlayerItems 使用 URL 初始化到两个不同类型的不同视频资产。一个是 .mp4 文件(渐进式下载),另一个是 .m3u8 http-streaming 文件。我必须说,它确实有点时髦。

    根据我对文件进行排队的顺序,我会得到完全不同的行为。如果我从 .mp4 文件开始,当 nextItem 启动播放器(.m3u8 文件)时,AVPlayerLayer 会变为空白和静音。如果我更改顺序并从 m3u8 文件开始,AVPlayerLayer 会变为空白,但第二个文件中的音频播放正常。

    到目前为止,我的印象是 AVQueuePlayer 确实 NOT 在当前 AVPlayerItem 完成播放之前开始下载下一个 AVPlayerItem。但我可能错了。

    我猜还有待续……

    编辑:好的,所以我做了更多的测试,似乎下一个项目在最后一个项目完成播放之前开始下载。请参阅下面的帖子。划“NOT”...

    Edit2:在这里添加我的解释,因为评论让我没有格式化选项。 (如果这是处理答案更新的不好方法,欢迎使用最佳实践)

    无论如何,我通过我的 itemsArray 使用 KVO 添加了对 status 属性的观察...

    [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 文档,我觉得这些资源是异步下载的,但我仍然不确定这些下载何时由 IAPlayerItem 启动。当我将 .m3u8 文件添加到队列中时,我仍然有一些奇怪的行为,这让我想知道这些资产是否在创建时开始下载(不过对我来说似乎有点奇怪)。

    【讨论】:

    • 好的,所以我遍历 items 数组中的项目,通过 KVO 添加观察...此注释不允许格式化。改为编辑我的主要帖子!
    【解决方案5】:

    我为您和其他希望减少来自 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];
    }
    

    按照以下步骤操作。如有任何疑问,请发表评论。

    【讨论】:

    • 请你提供一个上面的avqueueplayer代码示例
    • 哪段代码减少了缓冲时间(也就是预加载视频)?
    【解决方案6】:

    您可以使用三个 AVPlayer。第一个是上一个,第二个是当前,最后一个是下一个。当前播放时,将最后一个播放器作为预加载播放并观察其状态。当预加载播放器的状态变为 AVPlayerStatusReadyToPlay 时,停止它并调用下一行: [player.playerItem cancelPendingSeeks]; [player.playerItem.asset cancelLoading];

    这些代码将阻止预加载视频的所有数据。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-09-26
      • 2012-08-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-05-23
      相关资源
      最近更新 更多