Switching Videos in AVPlayer Creates Flash When Changing

kdbdallas picture kdbdallas · Aug 30, 2011 · Viewed 10.2k times · Source

I am using AVFoundation's AVPlayer to play 2 video clips made from 1 longer video (so the end of the first matches the beginning of the second)

When the first video ends and the user taps, I create a new AVPlayer and assign it to my PlayerView, and start playing the second clip.

This all works, however, there is a prominent screen "flicker".

My assumption is that this is caused by the player view removing the first clip and then showing the second clip.

What I need is for this flicker to no appear, so that going between the two clips is seamless.

Do anyone know if there is a way to stop this flickr, either via the AVPlayer* classes, or a way to "fake" it by doing something to make it so this isn't visible.

Thanks

Below is the code of my load and play method:

- (void)loadAssetFromFile
{
    NSURL *fileURL = nil;

    switch (playingClip)
    {
        case 1:
            fileURL = [[NSBundle mainBundle] URLForResource:@"wh_3a" withExtension:@"mp4"];
        break;

        case 2:
            fileURL = [[NSBundle mainBundle] URLForResource:@"wh_3b" withExtension:@"mp4"];
        break;

        case 3:
            fileURL = [[NSBundle mainBundle] URLForResource:@"wh_3c" withExtension:@"mp4"];
        break;

        case 4:
            fileURL = [[NSBundle mainBundle] URLForResource:@"wh_3d" withExtension:@"mp4"];
        break;

        default:
            return;
        break;
    }

    AVURLAsset *asset = [AVURLAsset URLAssetWithURL:fileURL options:nil];
    NSString *tracksKey = @"tracks";

    [asset loadValuesAsynchronouslyForKeys:[NSArray arrayWithObject:tracksKey] completionHandler:
 ^{
     // The completion block goes here.
     NSError *error = nil;
     AVKeyValueStatus status = [asset statusOfValueForKey:tracksKey error:&error];

     if (status == AVKeyValueStatusLoaded)
     {
         self.playerItem = [AVPlayerItem playerItemWithAsset:asset];

         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playerItemDidReachEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:playerItem];

         self.player = [AVPlayer playerWithPlayerItem:playerItem];
         [playerView setPlayer:player];

         [self.player seekToTime:kCMTimeZero];

         [self play];
     }
     else {
         // Deal with the error appropriately.
         NSLog(@"The asset's tracks were not loaded:\n%@", [error localizedDescription]);
     }
 }];
}

Answer

Alex Kennberg picture Alex Kennberg · Sep 3, 2011

You do not need to re-create AVPlayer for this task. You can just have multiple AVPlayerItems and then switch which one is current via [AVPlayer replaceCurrentItemWithPlayerItem:item].

Also, you can observe for when current item has changed with the code below.

 static void* CurrentItemObservationContext = &CurrentItemObservationContext;

... After creating a player, register the observer:

 [player1 addObserver:self 
                   forKeyPath:kCurrentItemKey 
                      options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
                      context:CurrentItemObservationContext];

...

 - (void)observeValueForKeyPath:(NSString*) path 
                  ofObject:(id)object 
                    change:(NSDictionary*)change 
                   context:(void*)context {
     if (context == CurrentItemObservationContext) {
         AVPlayerItem *item = [change objectForKey:NSKeyValueChangeNewKey];
         if (item != (id)[NSNull null]) {
             [player1 play];
         }
     }
 }