replaceCurrentItemWithPlayerItem

replaceCurrentItemWithPlayerItem

我有一个UITableView,其中包含许多在滚动时播放的视频。由于tableView中的单元格正在重复使用,因此我只为每一行实例化一个AVPlayer。当一个单元被重用时,我只需通过调用PlayerItem来更改该单元播放器的[self.player replaceCurrentItemWithPlayerItem:newItem];。目前,这是在tableView:cellForRowAtIndexPath内部间接调用的。向下滚动时,重新使用时会有明显的滞后。通过消除过程,我得出结论,延迟是由replaceCurrentItemWithPlayerItem引起的,甚至还没有开始播放。删除单行代码(防止播放器获得新视频)时,滞后现象消失了。

我已尝试解决的问题:

我有一个自定义的UITableViewCell来播放这些视频,并且在其中创建了一个方法,以使用来自对象的新信息进行初始化。即,在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;
        }
    }/*...*/
}

运行此命令时,得到的结果与完全删除更换项的调用相同。显然,该函数无法进行线程化。
我不太确定我对此有什么期望。我以为我需要干净的copyAVPlayer才能起作用,但是经过一番搜索,我发现了几条评论,指出replaceCurrentItemWithPlayerItem:可以在单独的线程中调用而不是,这对我来说没有意义。我知道,UI元素绝不应在除主线程/ UI线程之外的其他线程中处理,但我永远也无法想象replaceCurrentItemWithPlayerItem属于此类。

我现在正在寻找一种方法来更改AVPlayer的项目而不会出现滞后,但是找不到任何内容。我希望我理解此函数的线程错误,并且有人会纠正我。

编辑:
现在,我被告知该调用已被线程化,并且这实际上不应该发生。但是,我没有其他解释。下面是我的cellForRowAtIndexPath:。它位于自定义UITableView中,其委托(delegate)设置为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。

有人告诉我要使用Instruments中的Time Profiler找出它是什么,但是我不知道该怎么做。通过运行探查器并不时滚动一次, this is the result(图像)。每个图形组的峰值就是我所说的滞后。当我滚动到底部并轻按状态栏滚动到顶部时,(我认为)是一个单一的最高峰。我不知道如何解释结果。我在堆栈中搜索replace,发现了 SCSS 。它是从Main Thread(在顶部)中调用的,这很有意义,“运行时间”为50毫秒,我不知道要考虑什么。图表的相关比例为大约1分钟半的时间跨度。
相比之下,在注释掉该单行并再次运行Time Profiler时,图形的峰值显着降低(最高峰值为13%,而图像上的峰值大约为60-70%)。

我不知道要寻找什么。

最佳答案

如果您进行基本的性能分析,我认为您可以缩小问题的范围。对我来说,我遇到了类似的问题,其中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;
}

07-26 09:31