Утечка памяти из-за зацикливания SKVideoNode (только на реальном устройстве)

У меня есть утечка памяти, которую я не могу диагностировать. Я пробовал несколько подходов к созданию бесшовного зацикленного видео. Помимо AVPlayerLooper, все подходы, с которыми я сталкивался и пробовал, включают создание наблюдателя для просмотра AVPlayerItemDidPlayToEndTimeNotification, а затем либо поиск начала видео (в случае AVPlayer), либо вставка видео для зацикливания в очередь видео (в случае AVQueuePlayer). Оба имеют одинаковую производительность, но оба также имеют постоянное сохранение памяти, связанное с методом seekToTime (в случае AVPlayer) и методом insertItem (в случае AVQueuePlayer). Моя конечная цель — создать подкласс SKVideoNode, который зацикливается по умолчанию. Ниже мой код для подкласса:

#import "SDLoopingVideoNode.h"
#import <AVFoundation/AVFoundation.h>


@interface SDLoopingVideoNode()
@property AVQueuePlayer *avQueuePlayer;
@property AVPlayerLooper *playerLooper;
@end

@implementation SDLoopingVideoNode

-(instancetype)initWithPathToResource:(NSString *)path withFiletype:(NSString *)filetype
{
if(self == [super init])
{
    NSString *resourcePath = [[NSBundle mainBundle] pathForResource:path ofType:filetype];
    NSURL *videoURL = [NSURL fileURLWithPath:resourcePath];
    AVAsset *videoAsset = [AVAsset assetWithURL:videoURL];
    AVPlayerItem * videoItem  = [AVPlayerItem playerItemWithAsset:videoAsset];


    self.avQueuePlayer = [[AVQueuePlayer alloc] initWithItems:@[videoItem]];

    NSNotificationCenter *noteCenter = [NSNotificationCenter defaultCenter];
    [noteCenter addObserverForName:AVPlayerItemDidPlayToEndTimeNotification
                            object:nil
                             queue:nil
                        usingBlock:^(NSNotification *note) {
                            AVPlayerItem *video = [[AVPlayerItem alloc] initWithURL:videoURL];
                            [self.avQueuePlayer insertItem:video afterItem:nil];
                            NSLog(@"Video changed");
                        }];

    self = (SDLoopingVideoNode*)[[SKVideoNode alloc] initWithAVPlayer: self.avQueuePlayer];
    return self;
}
return nil;
}


@end

А вот как инициализируется подкласс в didMoveToView:

SDLoopingVideoNode *videoNode = [[SDLoopingVideoNode alloc]initWithPathToResource:@"147406" withFiletype:@"mp4"];
[videoNode setSize:CGSizeMake(self.size.width, self.size.height)];
[videoNode setAnchorPoint:CGPointMake(0.5, 0.5)];
[videoNode setPosition:CGPointMake(0, 0)];
[self addChild:videoNode];
[videoNode play];

person Solsma Dev    schedule 16.11.2016    source источник
comment
Еще одна странная вещь, которую я заметил - эта утечка памяти проявляется только на устройстве - при запуске симулятора утечки памяти нет.   -  person Solsma Dev    schedule 17.11.2016


Ответы (1)


Короткий ответ: вы не сможете заставить это работать с AVPlayer. Поверьте, я пытался. Вместо этого можно выполнять плавный цикл, используя аппаратное обеспечение H264 для декодирования, а затем повторно кодировать каждый видеокадр как ключевой, ссылка на github здесь. Я также создал бесшовный зацикленный слой, который поддерживает полный альфа-канал. Производительность даже для полноэкранного видео 1x1 на iPad или iPad pro великолепна. Кроме того, с этим кодом не происходит утечек памяти.

person MoDJ    schedule 16.11.2016
comment
Спасибо за ответ! Это твой проект? Я скачал и просмотрел этот проект ранее сегодня — зацикливание — это здорово, но знаете ли вы способ встроить это зацикленное видео в SKVideoNode? Желаемый эффект, который я собираюсь получить, - это видеофон в Sprite Kit. - person Solsma Dev; 16.11.2016
comment
Цикл в приведенном выше коде работает хорошо, но инициализация SKVideoNode с помощью AVQueuePlayer и добавление видео в очередь вызывает связь с памятью в каждом цикле видео. - person Solsma Dev; 16.11.2016
comment
Нет, вы не можете встроить это напрямую в SKVideoNode. Но вы можете декодировать и затем визуализировать видеокадр в пиксельный буфер, а затем установить этот пиксельный буфер в качестве текстуры для обычного узла SKSpriteNode. Для минимального использования памяти можно использовать SKMutableTexture и дождаться, пока асинхронный обратный вызов скопирует содержимое буфера пикселей. - person MoDJ; 17.11.2016
comment
Отлично, спасибо! Это область, в которой у меня нет большого опыта, знаете ли вы какие-нибудь хорошие рекомендации для начала? - person Solsma Dev; 17.11.2016
comment
Единственные документы - это документы Apple, но они весьма ограничены. Если вы хотите увидеть рабочий пример быстрого цикла FPS, выполненного с подходом загрузки текстуры (это не совсем та же реализация, но близкая), см. связанный пример SpriteKitFireAnimation. stackoverflow.com/questions/22470907/< /а> - person MoDJ; 17.11.2016
comment
Я запустил демо SpriteKitFireAnimation на iPhone 6 под управлением iOS 10 и получил менее 3 кадров в секунду. - person Solsma Dev; 17.11.2016
comment
Вы запускаете его в режиме DEBUG? На самом деле я еще не обновил свои устройства до iOS 10, но я бы начал с того, что включил оптимизацию. В любом случае, демонстрация не имеет прямого отношения к вашей проблеме, поскольку реализация отличается от декодирования видео и отправки декодированного кадра с использованием SKMutableTexture. Я просто привел пример такого подхода в SpriteKit, который вы действительно могли запустить. Вам все равно нужно будет создать свою собственную реализацию. - person MoDJ; 17.11.2016
comment
Я только что протестировал демо SpriteKitFireAnimation на iOS 10, и оно работает со скоростью 60 кадров в секунду в оптимизированном режиме. Вам нужно подождать, пока текстуры начнут загружаться, и происходит что-то странное, когда отображение FPS в правом нижнем углу, кажется, не обновляется, пока вы не повернете экран. - person MoDJ; 24.11.2016