0

In my iOS application, I'm trying to play list of videos downloaded to applications' Documents directory. To achieve that target, I used AVQueuePlayer. Following is my code which leads to app crash after 6/7 times looping.

@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];
    }
}

After getting memory issue, I tried to make some changes to above code.

I tried to set avQueuePlayer variable public and set it as strong variable

 @property (strong, nonatomic) AVQueuePlayer *avQueuePlayer;

By doing that I expected avQueuePlayer variable remain in the memory till we manually set to nil. But that didn't solve the problem.

Then I tried to set player, related arrays and layers to nil and created again for new loop session.

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];

But that also didn't help to solve the issue.

Next I try to remove the observer before it re-initialized in a new loop

if (avPlayerItemsArray != nil)
{
    avPlayerItemsArray = nil;

    [[NSNotificationCenter defaultCenter] removeObserver:self
                                                    name:AVPlayerItemDidPlayToEndTimeNotification
                                                  object:nil];
}

But that also didn't help.

Next I used Instrument to find out memory usages and leaks. Application is not exceeding 18 MB when it is crashing and also there were more than 200 MB remaining as free. Instruments is little more complicated but still I didn't find any memory leaks related to this code.

AnujAroshA
  • 4,623
  • 8
  • 56
  • 99

1 Answers1

0

Actually the error was not with the AVQueuePlayer. In my application I'm listing all the videos inside a table below the video playing view. In that table, each row consists with video thumbnail that I taken from below code.

+ (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;
}

Every time a video clip ends playing and also playlist begins a new loop, I update the table view. So each time I call above method and that's the reason for memory issue.

As the solution I call this method only once for a single video clip and store the returning UIImage in a mutable array. That solved the issue.

Heading of the question and the tags may not adequate with the answer, but I thought this is worth existing as a Q & A rather than deleting the post.

AnujAroshA
  • 4,623
  • 8
  • 56
  • 99