У меня есть 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%).
Я не знаю, что искать..