Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Switching Videos in AVPlayer Creates Flash When Changing

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]);
     }
 }];
}
like image 237
kdbdallas Avatar asked Aug 30 '11 02:08

kdbdallas


3 Answers

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];
         }
     }
 }
like image 153
Alex Kennberg Avatar answered Nov 04 '22 20:11

Alex Kennberg


There are two workaround that I found. To me both approaches worked and I prefer the second one.

First, as @Alex Kennberg mentioned, create two set of AVPlayerLayer and AVPlayer. and switching them when you switch between videos. Be sure to set background color to clear color.

Second, use UIImageView as the owner view of AVPlayerLayer. Create thumbnail image of a video and set it to the imageview before switching the video. Be sure to set the view mode correctly.

like image 45
ccoroom Avatar answered Nov 04 '22 20:11

ccoroom


I ran into the same issue with the video "flashing" and solved it this way in Swift 5.

Set my player variable to this

var player = AVPlayer(playerItem: nil)

Then inside my playVideo function, I changed this

self.player.replaceCurrentItem(with: AVPlayerItem(url: fileURL))

to this

player = AVPlayer(url: fileURL)

"fileURL" is the path to video I want to play.

This removed the flash and played the next video seamlessly for me.

like image 1
TerrenceDev Avatar answered Nov 04 '22 20:11

TerrenceDev