我有一个UITableView
包含滚动时要播放的视频。由于tableView中的单元格正在被重用,我只会为每一行实例化一个AVPlayer
。重新使用单元格时,我只需调用PlayerItem
即可更改单元格播放器的[self.player replaceCurrentItemWithPlayerItem:newItem];
。目前这是在tableView:cellForRowAtIndexPath
内部间接调用的。向下滚动时,重新使用时会出现明显的延迟。通过消除过程,我得出结论,滞后是replaceCurrentItemWithPlayerItem
引起的,甚至在它开始播放之前。当删除这一行代码(阻止播放器获取新视频)时,滞后消失。
我试图修复它:
我有一个用于播放这些视频的自定义UITableViewCell
,我在这些视频中创建了一个方法,用来自对象的新信息进行初始化。 I.E,在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;
}
}/*...*/
}
运行时,我得到的结果与完全删除更换项目的调用相同。显然,这个功能不能被线程化。
我不完全确定我对此的期望。我想我需要一个干净的copy
AVPlayer
来实现这一点,但在搜索了一下后,我发现了一些评论,指出replaceCurrentItemWithPlayerItem:
可以不在一个单独的线程中调用,这对我没有意义。我知道UI元素永远不应该在除main / UI-thread之外的其他线程中处理,但我绝不会想象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:
导致此明显延迟。该文档声明它正在更改另一个线程中的项目,但该方法中的某些正在阻止我的UI。
我被告知在仪器中使用Time Profiler来查明它是什么,但我不知道该怎么做。通过运行探查器并偶尔滚动一次, this is the result(图像)。每个图组的峰值都是我所说的滞后。当我滚动到底部并点击状态栏滚动到顶部时,单个极端峰值(我认为)。我不知道如何解释结果。我在堆栈中搜索replace
,找到了这个混蛋。它是在Main Thread
内(在顶部)调用的,这是有道理的,运行时间为" 50毫秒,我不知道。图的相关比例是约1分半的时间跨度。
相比之下,当评论该单行并再次运行Time Profiler时,图形的峰值显着降低(最高峰值为13%,与图像上的最高值相比,可能在60-70左右) %)。
我不知道该找什么......
答案 0 :(得分:1)
如果你做一些基本的分析,我认为你可以缩小问题的范围。对我来说,我有一个类似的问题,其中replaceCurrentItemWithPlayerItem
阻止了UI线程。我通过检查我的代码来解决它,找出哪条线需要时间。对我来说,AVAsset加载需要时间。所以我使用了AVAsset的loadValuesAsynchronouslyForKeys
方法来解决我的问题。
因此,您可以尝试以下方法:
-(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];
[newAsset loadValuesAsynchronouslyForKeys:@[@"duration"] completionHandler:^{
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;
}
答案 1 :(得分:0)
您应该使用时间分析器来分析您的应用,以查看滞后实际发生的位置。
replaceCurrentItemWithPlayerItem:
的文档明确指出它是异步执行的,所以它不应该是主队列跳跃的来源。
项目替换是异步发生的;观察currentItem 找出何时更换将会/确实发生的财产。
如果您仍然无法弄明白,请发布更多代码,特别是至少使用 cellForRowAtIndexPath
方法。
<强>更新强>
根据@ joey在下面的评论,此答案的先前内容不再有效。 documentation不再声明该方法是异步的,因此可能不是。
答案 2 :(得分:0)
在CustomCell.m中,您应该使用缩略图显示特定单元格视频的图像,您可以将缩略图添加到单元格内的按钮,然后在CustomCell.h中构建一个自定义委托,当您点击时将调用该委托在新创建的按钮上,例如:
@class CustomCell;
@protocol CustomCellDelegate <NSObject>
- (void)userTappedPlayButtonOnCell:(CustomCell *)cell;
@end
也在你的.h中添加按钮的动作:
@interface CustomCell : UITableViewCell
@property (strong, nonatomic) IBOutlet UIButton *playVideoButton;
- (IBAction)didTappedPlayVideo;
- (void)settupVideoWithURL:(NSString *)stringURL;
- (void)removeVideoAndShowPlaceholderImage:(UIImage *)placeholder;
@end
也可以在“didTappedPlayVideo”中执行下一步操作:
- (void)didTappedPlayVideo{
[self.delegate userTappedPlayButtonOnCell:self];
}
方法:settupVideoWithURL
用于初始化你的玩家
和方法:removeVideoAndShowPlaceholderImage
将停止视频并使AVPlayerItem和AVPlayer为零。
现在,当用户点击一个单元格时,您将调用发送回您拥有tableView的UIViewController,在委托方法中,您应该执行下一个操作:
- (void)userTappedPlayButtonOnCell:(CustomCell *)cell{
//check if this is the first time when the user plays a video
if(self.previousPlayedVideoIndexPath){
CustomCell *previousCell = (CustomCell *)[tableView cellForRowAtIndexPath:self.previousPlayedVideoIndexPath];
[previousCell removeVideoAndShowPlaceholderImage: [UIImage imageNamed:@"img.png"]];
}
[cell settupVideoWithURL:@"YOURvideoURL"];
self.previousPlayedVideoIndexPath = cell.indexPath;
}
在较旧的设备上(几乎所有设备,直到新的iPad Pro)都没有显示有AVPlayer
的多个实例。
也希望这些代码可以指导或帮助您完成任务:)