4

I have an application in which the user can select from local video files. When one of those thumbnails gets pushed, the user is presented a new view which have a custom video player I've made that presents the video.

This works flawlessly, but only sometimes. The funny thing is that if the user selects a new video (thus getting presented a new view, initializing a new custom video player object) exactly 5 times, the underlying AVPlayerLayer that is used to present the visuals from the player renders black, even though it seems like the underlying asset still loads correctly (the player interface still holds the correct duration for the video and so forth).

When a new custom media player object gets initialized (which happens when the view controller for the media players containing view gets loaded), this is the part of the initializer method which sets up the AVPlayer and its associated item:

    // Start to load the specified asset
    mediaAsset = [[AVURLAsset alloc] initWithURL:contentURL options:nil];

    if (mediaAsset == nil)
        NSLog(@"The media asset is zero!!!");

    // Now we need to asynchronously load in the tracks of the specified asset (like audio and video tracks). We load them asynchronously to avoid having the entire app UI freeze while loading occours
    NSString* keyValueToLoad = @"tracks";

    // When loading the tracks asynchronously we also specify a completionHandler, which is the block of code that should be executed once the loading is either or for some reason failed
    [mediaAsset loadValuesAsynchronouslyForKeys:[NSArray arrayWithObject:keyValueToLoad
                                                 ] completionHandler:^
     {
         // When this block gets executed we check for potential errors or see if the asset loaded successfully
         NSError* error = nil;

         AVKeyValueStatus trackStatus = [mediaAsset statusOfValueForKey:keyValueToLoad error:&error];

         if (error != nil)
         {
             NSLog(@"Error: %@", error.description);
         }

         //switch (trackStatus) {
             //case AVKeyValueStatusLoaded:
         if (trackStatus == AVKeyValueStatusLoaded)
         {
                 NSLog(@"Did load properly!");
                 mediaItem = [AVPlayerItem playerItemWithAsset:mediaAsset];

                if (mediaItem.error == nil)
                    NSLog(@"Everything went fine!");

                if (mediaItem == nil)
                    NSLog(@"THE MEDIA ITEM WAS NIL");

                 [mediaItem addObserver:self forKeyPath:@"status" options:0 context:&itemStatusContext];

                     mediaContentPlayer = [[AVPlayer alloc] initWithPlayerItem:mediaItem];

                         [mediaContentView setPlayer:mediaContentPlayer];

             //mediaContentView = [AVPlayerLayer playerLayerWithPlayer:mediaContentPlayer];

                [activeModeViewBlocked configurePlaybackSliderWithDuration:mediaItem.duration];

                 originalDuration = mediaItem.duration.value / mediaItem.duration.timescale;

                 // We will subscribe to a timeObserver on the player to check for the current playback time of the movie within a specified interval. Doing so will allow us to frequently update the user interface with correct information
                 playbackTimeObserver = [mediaContentPlayer addPeriodicTimeObserverForInterval:CMTimeMake(1, 50) queue:dispatch_get_main_queue() usingBlock:^(CMTime time)
                                         {
                                             NSLog(@"TIME UPDATED!");
                                             [activeModeViewBlocked updatePlaybackSlider:time];
                                         }];

                 [self syncUI];
         }

         if (trackStatus == AVKeyValueStatusFailed)
         {
             NSLog(@"Something failed!");
         }

         if (trackStatus == AVKeyValueStatusCancelled)
         {
             NSLog(@"Something was cancelled!");
         }
     }];

Now if I initialize this custom media player object 5 times exactly, it always starts to render black screens.

Does anyone have any idea of why this could be happening?

CodingBeagle
  • 1,888
  • 2
  • 24
  • 52

1 Answers1

5

This bit me too. There is a limit on the number of concurrent video players that AVFoundation will allow. That number is four(for iOS 4.x, more recently the number seems to have increased. For example, on iOS 7 I've had up to eight on one screen with no issue). That is why it is going black on the fifth one. You can't even assume you'll get four, as other apps may need a 'render pipeline'.

This API causes a render pipeline:

+[AVPlayer playerWithPlayerItem:]
maz
  • 8,056
  • 4
  • 26
  • 25
  • What solution did you used? Are you keeping only one AVPlayer instance all the time? – Piotr Oct 04 '13 at 13:24
  • I stopped all players and deallocated them before changing to another page where I then created four more players on the fly. – maz Oct 04 '13 at 18:23
  • My issue is that I want to display AVPlayers in UITableViewCell. Since there are limited number of instances, should I create new AVPlayer every time I want to play it (while scrolling)? That causes very choppy and bad looking scrolling. Do you have any idea how to solve that? – Piotr Oct 05 '13 at 08:56
  • Do you have a cite on the limit of four? I've successfully had up to 9 AVPlayers running at once. – Stan James Aug 13 '14 at 13:05
  • This has changed on more recent iOS releases for sure. On iOS 7 I can get up to at least eight on one screen at a time. – maz Aug 18 '14 at 10:49