【发布时间】:2014-08-17 18:11:42
【问题描述】:
我有一个UITableView,其中包含许多滚动时要播放的视频。由于 tableView 中的单元格被重复使用,我只为每一行实例化一个AVPlayer。当一个单元被重新使用时,我只需通过调用[self.player replaceCurrentItemWithPlayerItem:newItem]; 来更改单元播放器的PlayerItem。这目前在tableView:cellForRowAtIndexPath 内部被间接调用。向下滚动时,重用发生时有明显的滞后。通过消除过程,我得出结论,延迟是由replaceCurrentItemWithPlayerItem 引起的,甚至还没有开始播放。删除这一行代码(阻止播放器获取新视频)时,滞后消失了。
我尝试解决的问题:
我有一个自定义UITableViewCell 用于播放这些视频,并且我在其中创建了一个方法来使用来自对象的新信息进行初始化。 IE,在cellForRowAtIndexPath: 我调用[cell initializeNewObject:newObject]; 执行以下方法:
//In CustomCell.m
-(void)initializeNewObject:(CustomObject*)newObject
{ /*...*/
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
AVPlayerItem *xPlayerItem = [[AVPlayerItem alloc] initWithURL:[NSURL URLWithString:newObject.url]];
AVPlayer *dummy = self.player;
[dummy replaceCurrentItemWithPlayerItem:xPlayerItem];
dispatch_async(dispatch_get_main_queue(), ^{
self.player = dummy;
playerItem = xPlayerItem;
}
}/*...*/
}
运行此程序时,我得到的结果与完全删除替换项目的调用相同。显然,这个函数不能被线程化。
我不完全确定我对此的期望。我想我需要AVPlayer 中的一个干净的copy 才能工作,但经过一番搜索,我发现几个 cmets 声明 replaceCurrentItemWithPlayerItem: 可以 not 在单独的线程中调用,这对我来说毫无意义。我知道 UI 元素不应该在主/UI 线程之外的其他线程中处理,但我永远不会想象 replaceCurrentItemWithPlayerItem 属于这一类。
我现在正在寻找一种方法来更改 AVPlayer 的项目而不会出现延迟,但找不到任何项目。我希望我理解这个函数的线程是错误的,并且有人会纠正我..
编辑:
我现在被告知这个调用已经线程化了,这不应该真的发生。但是,我没有看到其他解释。下面是我的cellForRowAtIndexPath:。它在一个自定义的UITableView 中,代表设置为self(所以self == tableView)
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
CustomCell *cell = [self dequeueReusableCellWithIdentifier:kCellIdentifier];
if(!cell)
cell = [[[NSBundle mainBundle] loadNibNamed:@"CustomCell" owner:self options:nil] objectAtIndex:0];
//Array 'data' contains all objects to be shown. The objects each have titles, url's etc.
CustomVideoObject *currentObject = [data objectAtIndex:indexPath.row];
//When a cell later starts playing, I store a pointer to the cell in this tableView named 'playing'
//After a quick scroll, the dequeueing cell might be the cell currently playing - resetting
if(playing == cell)
playing = nil;
//This call will insert the correct URL, title, etc for the new video, in the custom cell
[cell initializeNewObject:currentObject];
return cell;
}
当前“工作”的initializeNewObject,但滞后(这是在CustomCell.m 内部:
-(void)initializeNewObject:(CustomObject*)o
{
//If this cell is being dequeued/re-used, its player might still be playing the old file
[self.player pause];
self.currentObject = o;
/*
//Setting text-variables for title etc. Removed from this post, but by commenting them out in the code, nothing improves.
*/
//Replace the old playerItem in the cell's player
NSURL *url = [NSURL URLWithString:self.currentObject.url];
AVAsset *newAsset = [AVAsset assetWithURL:url];
AVPlayerItem *newItem = [AVPlayerItem playerItemWithAsset:newAsset];
[self.player replaceCurrentItemWithPlayerItem:newItem];
//The last line above, replaceCurrentItemWithPlayerItem:, is the 'bad guy'.
//When commenting that one out, all lag is gone (of course, no videos will be playing either)
//The lag still occurs even if I never call [self.player play], which leads me
//to believe that nothing after this function can cause the lag
self.isPlaying = NO;
}
每次延迟都发生在完全相同的位置。当快速向下滚动时,我们可以看到当最底部单元格的顶部到达屏幕中心时会发生短暂的延迟。我想这对您来说没有任何意义,但很明显,每次延迟都发生在完全相同的地方,即当一个新的单元格出列时。更改 AVPlayer 的 playerItem 后,我什么都不做。我直到稍后才开始播放视频。 replaceCurrentItemWithPlayerItem: 是造成了这种明显的滞后。文档声明它正在更改另一个线程中的项目,但该方法中的 something 阻碍了我的 UI。
有人告诉我在 Instruments 中使用 Time Profiler 来找出它是什么,但我不知道该怎么做。通过运行分析器并偶尔滚动一次,this is the result(image)。每个图组的峰值是我正在谈论的滞后。一个极端的峰值是(我认为)当我滚动到底部并点击状态栏滚动到顶部时。我不知道如何解释结果。我在堆栈中搜索replace,找到了那个混蛋。它是从Main Thread(在顶部)中调用的,这是有道理的,“运行时间”为 50 毫秒,我不知道怎么想。该图的相关比例是大约1分半钟的时间跨度。
相比之下,当注释掉该单行并再次运行 Time Profiler 时,图表的峰值显着降低(最高峰值为 13%,而图像上的峰值可能约为 60-70%)。
我不知道要找什么..
【问题讨论】:
-
我也有同样的问题。你修好了没有?
-
@user1561346 No.. :/ 我决定只在滚动停止时才开始播放。这样,我可以滚动浏览许多视频(带有正确的缩略图)而无需加载视频,并且仅在滚动停止时加载。不是我想要的,但比滞后更好。如果您发现了什么,请告诉我!
-
@Sti - 你如何获取和设置视频缩略图?
-
不确定 replaceCurrentItemWithPlayerItem 本身就是问题所在。我尝试保留所有 AVPlayer(每个单元 1 个)并简单地为每个单元交换 AVPlayerLayer 的播放器。因此,永远不要调用 replaceCurrentItemWithPlayerItem,因为每个 AVPlayer 只被赋予一个 AVPlayerItem。即使将 AVPlayerLayer 的播放器替换为另一个现有的 AVPlayer,延迟仍然存在并发生。
-
@Sti 你找到解决方案了吗?我也遇到了同样的问题。你能帮忙吗?
标签: ios objective-c multithreading cocoa-touch avplayer