Ошибка памяти AVQueuePlayer при зацикливании списка воспроизведения в iOS
В моем iOS-приложении я пытаюсь воспроизвести список видео, загруженных в каталог документов приложения. Для достижения этой цели я использовал AVQueuePlayer. Ниже приведен мой код, который приводит к сбою приложения после 6/7 циклов.
@interface PlayYTVideoViewController () <NSURLConnectionDataDelegate, UITableViewDataSource, UITableViewDelegate>
{
AVQueuePlayer *avQueuePlayer;
}
- (void)playlistLoop
{
NSLog(@"%s - %d", __PRETTY_FUNCTION__, __LINE__);
lastPlayedVideoNumber = 0;
_loadingVideoLabel.hidden = YES;
avPlayerItemsMutArray = [[NSMutableArray alloc] init];
for (NSString *videoPath in clipUrlsMutArr)
{
NSURL *vidPathUrl = [NSURL fileURLWithPath:videoPath];
AVPlayerItem *avpItem = [AVPlayerItem playerItemWithURL:vidPathUrl];
[avPlayerItemsMutArray addObject:avpItem];
}
avPlayerItemsArray = [avPlayerItemsMutArray copy];
for(AVPlayerItem *item in avPlayerItemsArray)
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(itemDidPlayToEndTime:) name:AVPlayerItemDidPlayToEndTimeNotification object:item];
}
avQueuePlayer = [AVQueuePlayer queuePlayerWithItems:avPlayerItemsArray];
avQueuePlayer.actionAtItemEnd = AVPlayerActionAtItemEndAdvance;
introVideoLayer = [AVPlayerLayer playerLayerWithPlayer:avQueuePlayer];
introVideoLayer.frame = _mpIntroVideoView.bounds;
[_mpContainerView.layer addSublayer:introVideoLayer];
[avQueuePlayer play];
}
- (void)itemDidPlayToEndTime:(NSNotification *)notification
{
NSLog(@"%s - %d", __PRETTY_FUNCTION__, __LINE__);
AVPlayerItem *endedAVPlayerItem = [notification object];
[endedAVPlayerItem seekToTime:kCMTimeZero];
for (AVPlayerItem *item in avPlayerItemsArray)
{
if (item == endedAVPlayerItem)
{
lastPlayedVideoNumber++;
break;
}
}
[self reloadVideoClipsTable];
if ([endedAVPlayerItem isEqual:[avPlayerItemsArray lastObject]])
{
[self playlistLoop];
}
}
После получения проблемы с памятью я попытался внести некоторые изменения в приведенный выше код.
Я попытался установить переменную avQueuePlayer public и установить ее как сильную переменную
@property (strong, nonatomic) AVQueuePlayer *avQueuePlayer;
Делая это, я ожидал, что переменная avQueuePlayer останется в памяти, пока мы вручную не установим значение nil. Но это не решило проблему.
Затем я попытался установить проигрыватель, связанные массивы и слои на ноль и создал заново для новой сессии цикла.
if (avPlayerItemsMutArray != nil)
{
avPlayerItemsMutArray = nil;
}
avPlayerItemsMutArray = [[NSMutableArray alloc] init];
if (avPlayerItemsArray != nil)
{
avPlayerItemsArray = nil;
}
avPlayerItemsArray = [avPlayerItemsMutArray copy];
if (avQueuePlayer != nil)
{
avQueuePlayer = nil;
}
avQueuePlayer = [AVQueuePlayer queuePlayerWithItems:avPlayerItemsArray];
if(introVideoLayer != nil)
{
[introVideoLayer removeFromSuperlayer];
introVideoLayer = nil;
}
introVideoLayer = [AVPlayerLayer playerLayerWithPlayer:avQueuePlayer];
Но это также не помогло решить проблему.
Затем я пытаюсь удалить наблюдателя, прежде чем он будет повторно инициализирован в новом цикле
if (avPlayerItemsArray != nil)
{
avPlayerItemsArray = nil;
[[NSNotificationCenter defaultCenter] removeObserver:self
name:AVPlayerItemDidPlayToEndTimeNotification
object:nil];
}
Но это также не помогло.
Затем я использовал Инструмент, чтобы выяснить использование памяти и утечки. Приложение не превышает 18 МБ в случае сбоя, а также более 200 МБ осталось свободным. Инструменты немного сложнее, но все же я не обнаружил утечек памяти, связанных с этим кодом.
1 ответ
На самом деле ошибка не была с AVQueuePlayer. В моем приложении я перечисляю все видео в таблице под просмотром видео. В этой таблице каждая строка содержит миниатюру видео, которую я взял из кода ниже.
+ (UIImage *)createThumbForVideo:(NSString *)vidFileName
{
NSString *videoFolder = [Video getVideoFolder];
NSString *videoFilePath = [videoFolder stringByAppendingFormat:@"/trickbook/videos/edited/%@",vidFileName];
NSURL *url = [NSURL fileURLWithPath:videoFilePath];
AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:url options:nil];
AVAssetImageGenerator *generateImg = [[AVAssetImageGenerator alloc] initWithAsset:asset];
generateImg.appliesPreferredTrackTransform = YES;
NSError *error = NULL;
CMTime time = CMTimeMake(1, 65);
CGImageRef refImg = [generateImg copyCGImageAtTime:time actualTime:NULL error:&error];
UIImage *frameImage = [[UIImage alloc] initWithCGImage:refImg];
return frameImage;
}
Каждый раз, когда заканчивается воспроизведение видеоклипа, а также в плейлисте начинается новый цикл, я обновляю представление таблицы. Поэтому каждый раз, когда я вызываю вышеуказанный метод, это и есть причина проблемы с памятью.
В качестве решения я вызываю этот метод только один раз для одного видеоклипа и сохраняю возвращаемый UIImage в изменяемом массиве. Это решило проблему.
Заголовок вопроса и теги могут не соответствовать ответу, но я подумал, что это стоит использовать как вопрос и ответ, а не удалять сообщение.