У меня есть UITableView, содержащий несколько видеороликов для воспроизведения при прокрутке. По мере повторного использования ячеек в таблицеView я создаю экземпляр только 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 никогда не должны обрабатываться в других потоках, кроме основного/UI-потока, но я бы никогда не подумал, что replaceCurrentItemWithPlayerItem попадает под эту категорию.
Теперь я ищу способ изменить элемент AVPlayer без запаздывания, но не могу найти. Я надеюсь, что я неправильно понял эту функцию, и кто-то меня исправит..
EDIT:
Теперь мне сообщили, что этот вызов уже прошит, и этого не должно произойти. Однако я не вижу другого объяснения. Ниже мой cellForRowAtIndexPath:. Он находится внутри пользовательского UITableView с делегатами, установленными на self (so 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 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 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;
}
Задержка происходит в одном и том же месте каждый раз. При прокрутке вниз мы видим, что короткое отставание происходит, когда верхняя часть нижней части ячейки достигает центра экрана. Я думаю, это ничего не значит для вас, но ясно, что отставание происходит в одном и том же месте каждый раз, а именно, когда новая ячейка перегружается. После изменения playerItem AVPlayer, я делаю ничего. Я не начинаю играть в видео до конца. replaceCurrentItemWithPlayerItem: , вызывающий это заметное отставание. В документации указано, что она меняет элемент в другом потоке, но что-то в этом методе поддерживает мой пользовательский интерфейс.
Мне сказали использовать Time Profiler в инструментах, чтобы узнать, что это такое, но я понятия не имею, как это сделать. Запустив профайлер и прокручивая время от времени, это результат (изображение). Пики каждой графовой группы - это отставание, о котором я говорю. Один единственный экстремальный пик (я думаю), когда я прокрутил до конца и постучал по строке состояния, чтобы прокрутить вверх. Я не знаю, как интерпретировать результат. Я обыскал стек для replace и нашел ублюдка. Он называется здесь из Main Thread (вверху), что имеет смысл, с "временем работы" 50 мс, о котором я и не подозреваю. Соответствующая доля графика составляет от 1 минуты до половины.
Для сравнения, при комментировании этой единственной строки и запуске Time Profiler графики пиков значительно ниже (максимальный пик на 13% по сравнению с тем, что на изображении, вероятно, около 60-70%).
Я не знаю, что искать..