为什么AVPlayer的初始加载这么慢?

时间:2020-02-03 10:16:22

标签: ios objective-c avfoundation avplayer

我已经在iOS中开发了一个播放视频列表的应用程序。数据结构是,我创建自己的viewController的视图后,向我自己的服务器发送请求,并立即获取所有视频的URL。

之后,我开始依次播放URL的第一项。 问题是,对于第一个视频,实际加载需要10秒钟以上的时间;但是对于其他有时比第一个视频更长且更大的视频,其加载时间要少得多(可能需要1或2秒)。

通常,我的第一个视频非常短且非常小(平均200 KB),但是加载时间仍然比我的第二个视频(1 mb)(长5倍,但小5倍)要长得多。

过去3天,我一直在研究该问题,并且尝试了很多下面将提及的不同方法,但是我的问题是“为什么会这样?”不是解决它的方法。我想知道为什么会发生这种情况,因此我可以使用AVFoundation的知识来解决它,或者编写自己的播放器来最终为我解决该问题。

这是我的初始化代码:

self.player = [AVPlayer new];
self.playerView = [[NZPlayerView alloc] initWithPlayer:self.player];
self.playerView.videoGravity = AVLayerVideoGravityResizeAspect;
self.playerView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:self.playerView];
// Constraints

请注意,您在上面看到的NZPlayerView是我的,它只是一个视图,其中包含AVPlayerLayer并为我处理了一些额外的事情,例如应用程序进入后台并返回等。 我认为这种观点不会引起任何问题,因为对于其他使用其他初始化播放器方法的开发人员来说,该问题似乎仍然存在。

创建视图并将请求发送到自己的服务器后,我将获得一个URL列表,然后使用下面的代码逐一播放

AVPlayerItem *newCurrentItem = [[AVPlayerItem alloc] initWithURL:[NSURL URLWithString:[[self.selectedMovementModel videoURL] url]]];
[self.player replaceCurrentItemWithPlayerItem:newCurrentItem];

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playerDidFinishPlaying:) name:AVPlayerItemDidPlayToEndTimeNotification object:self.player.currentItem];

然后,我使用方法[self.player playImmediatelyAtRate:1];播放视频播放器,这是我实际尝试最小化初始停顿的方法之一,我曾经使用方法[self.player play];播放视频播放器

该视频播放完毕后,我决定是否循环播放该视频,否则,请返回该功能并播放下一个selectedMovementModel

我还使用self.player.status观察self.player.rateself.player.timeControlStatusself.player.reasonForWaitingToPlayRACObserve,其工作方式与KVO相同,除了我解雇了viewController之后不必摆脱它,所以那里也没有问题。

我尝试使用的方法是:创建新的loadValuesAsynchronouslyForKeys:completionHandler:之后并在其资产上使用currentItem。对于新的preferredForwardBufferDuration,我将currentItem设置为一个很小的数字。

我还尝试将播放器的automaticallyWaitsToMinimizeStalling属性设置为false,但全部无效。 我还使用xCode工具对我的网络进行了配置,看来我的第一个视频与其他视频没有什么不同。 iOS开始按块下载视频,然后在可用时立即播放这些块,即使我以在开始播放前下载整个视频的方式配置播放器,我的第一个视频也应该比第二个要早得多因为它要小得多。 我的猜测之一是可能需要更长的时间,因为玩家第一次建立与我的HOST的连接,然后玩家将会话保持未知的时间。如果来自两个不同网站的两个视频的加载时间相对相似,则很有意义,但事实并非如此。第一个仍然需要更长的时间。

我不惜一切代价避免的另一种解决方案是,我将在服务器响应甚至之前的页面之前创建播放器的实例,并加载一个非常短的视频,以便它能够完成在用户必须看到长负荷之前必须要做的事情。

但是我宁愿知道采取什么绝望措施之前是什么原因造成的。

编辑1 **:我在应用程序中创建了一个单例,该单例中有我在所有视图控制器中使用的视频播放器的实例。我使用URL对其进行了初始化并加载了它。但是对于列表的第一个视频,我的视图控制器中仍然存在相同的问题。我还认为也许我的视频播放器没有加载但缺少界面,但结果没有变化。 我将尝试阅读Telegram的代码,因为我知道他们使用的是AVPlayer,即使在大多数情况下他们是在播放视频之前先下载视频,也可能有一些关于如何初始化视频播放器的线索。

谢谢你们。

1 个答案:

答案 0 :(得分:2)

我要回答我自己的问题。

AVPlayer最初延迟的原因是非常罕见的情况,可能永远不会发生,但是考虑到自己找到答案可能很有趣并且很奇怪,我将发布它。

>

经过进一步调查,我意识到xCode调试器中有日志,其中包含视频URL的某些任务被“取消”,错误代码为“ -999”。确实很有趣,因为我还没有告诉视频播放器开始播放。

我还意识到我的应用程序大约在调用viewController的viewDidLoad方法时使用了超过180 MB的内存。

考虑到我没有任何关于哪个任务被取消的权限(我自己的请求没有被取消)。我决定解决NSURLSessionDataTask类的cancel方法,但发现了问题。初始化模型列表时,我正在向每个模型添加一个函数,该函数将确定视频是否应使用以下代码循环播放。

    _defineWeakSelf;
    AVAsset *asset = [AVAsset assetWithURL:[NSURL URLWithString:[self.videoURL url]]];
    [asset loadValuesAsynchronouslyForKeys:@[@"duration"] completionHandler:^{
        AVKeyValueStatus status = [asset statusOfValueForKey:@"duration" error:nil];

        weakSelf._shouldLoop = YES;
        if (status == AVKeyValueStatusLoaded)
        {
            CGFloat fullDurationSeconds = CMTimeGetSeconds([asset duration]);
            if (weakSelf._movementType == MovementTypeTime)
                weakSelf._shouldLoop = (fullDurationSeconds < weakSelf.duration);
            else
                weakSelf._shouldLoop = YES;
        }
    }];

请注意,_defineWeakSelf就像@strongify和@weakify一样,但它更易于使用。

这就是问题所在。每次在创建模型时使用此功能时,都会创建一个新的后台线程,并发送一个新的请求以实际同时检索所有视频。如果最多有30-40个型号,那么在我的笔记本电脑上这不是什么大问题,但是有90多个型号,可以想象这在iOS手机上会发生。自然,没有设备能够同时发送这么多请求,并且其中一些请求被一遍又一遍地取消(例如我的第一个视频),当它们完成后,延迟就消失了。

现在,此问题已解决。所有其他建议(包括社区其他成员提供的解决方案)更加有效,并且实际上将初始实例化延迟降低到最低限度。所以,我感谢大家。

*我还检查了Telegram对AVPlayer的使用,发现我的初始化与它非常相似,因此您也可以在其Github上对其进行检查。

相关问题