5

I'm using Sprite Kit and an SKVideoNode to play back a short video. When the video node is added to the scene, be that before of after the initial set up, the player flashes black for a brief second.

I have tried using an AVPlayer for the video instead of the video directly, also I've used KVO to only load the SKVideoNode when the video says it's ready to play.

Either way I get a black flash before my video starts to play.

Also there doesn't seem to be a way to add an AVPlayerLayer to the SKScene/SKView, although I don't know if that would help.

Any suggestions on what to try next would be great.

Here's the code I'm using

- (void)didMoveToView:(SKView *)view
{
    if (!self.contentCreated) {
        [self createSceneContents];
    }
}

- (void)createSceneContents
{
    NSString *resourcePath = [[NSBundle mainBundle] pathForResource:@"IntroMovie" ofType:@"mov"];
    NSURL *introVideoURL = [NSURL fileURLWithPath:resourcePath];
    self.playerItem = [AVPlayerItem playerItemWithURL:introVideoURL];
    self.player = [[AVPlayer alloc] initWithPlayerItem:self.playerItem];

    [self.playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];

    SKSpriteNode *playButton = [SKSpriteNode spriteNodeWithImageNamed:@"PlayButton"];
    [playButton setPosition:CGPointMake(CGRectGetMidX(self.view.frame), (CGRectGetMidY(self.view.frame) / 5) * 3)];
    [self addChild:playButton];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if ([keyPath isEqualToString:@"status"]) {
        if ([[change objectForKey:NSKeyValueChangeNewKey] integerValue] == AVPlayerItemStatusReadyToPlay) {
            NSLog(@"Change has the following %@", change);
                [self.player prerollAtRate:0 completionHandler:^(BOOL done){
                    SKVideoNode *introVideo = [SKVideoNode videoNodeWithAVPlayer:self.player];
                    [introVideo setSize:CGSizeMake(self.size.width, self.size.height)];
                    [introVideo setPosition:self.view.center];
                    [self addChild:introVideo];
                    [introVideo play];
                    [self.playerItem removeObserver:self forKeyPath:@"status"];
                }];
        }
    } else {
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
}

Here's an alternative piece of code which just plays the video and gives the same black flash between the video being loaded and it playing.

NSString *resourcePath = [[NSBundle mainBundle] pathForResource:@"IntroMovie" ofType:@"m4v"];
NSURL *introVideoURL = [NSURL fileURLWithPath:resourcePath];
self.playerItem = [AVPlayerItem playerItemWithURL:introVideoURL];
self.player = [[AVPlayer alloc] initWithPlayerItem:self.playerItem];

SKVideoNode *introVideo = [SKVideoNode videoNodeWithAVPlayer:self.player];
[introVideo setSize:CGSizeMake(self.size.width, self.size.height)];
[introVideo setPosition:self.view.center];
[self addChild:introVideo];
[introVideo play];
Mark Reid
  • 2,611
  • 3
  • 23
  • 45
  • where skvideonode is initialized? can you post code of skvideonode? – Ilario Mar 24 '14 at 17:45
  • Added the code where I'm using it. I tried the preroll code as you'll see and that didn't make any difference to things. – Mark Reid Mar 25 '14 at 10:22
  • where call [introVideo play] ? – Ilario Mar 25 '14 at 10:33
  • Sorry, I'd removed that in testing something else. Right after the node is added. I've added it back to the code. I also tried starting it before hand, but then still gives me the same result. – Mark Reid Mar 25 '14 at 11:05
  • why you use an observer? you can load video in initWithSize and after with global variable SKVideoNode *_introVideo you can play the video from everywhere – Ilario Mar 25 '14 at 11:09
  • That is there to make sure that the video is ready to play before I play it. I'll add an alternative version of my code with everything stripped out besides the video playback bit. – Mark Reid Mar 25 '14 at 11:12

3 Answers3

1

I had a similar problem and solved it like this:

I exported the first frame of the video as PNG. Then I added a SKSpriteNode with that PNG in front of the SKVideoNode. When I start the video, I set the hidden property of the SKSpriteNode to YES.


Here is an simplified example of my code with the relevant parts:

-(void)didMoveToView:(SKView *)view {    

    // add first frame on top of the SKVideoNode 
    firstFrame = [SKSpriteNode spriteNodeWithImageNamed:@"firstFrame"];
    firstFrame.zPosition = 1;
    [self addChild:firstFrame];

    // here I add the SKVideoNode with AVPlayer  
    ......

    // Add KVO  
    [playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];

}


- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    if (object == player.currentItem && [keyPath isEqualToString:@"status"]) {
        if (player.currentItem.status == AVPlayerItemStatusReadyToPlay) {

            [introVideo play]; 
            firstFrame.hidden = YES;             
        }
    }
}


This is the best solution I've found so far. I hope that helps you!

  • Thanks. I had thought about doing something along those lines, but was hopeful there would have been something simpler. Thank you for confirming this is, at least for now, is the way to go. – Mark Reid Mar 31 '14 at 13:47
1

Having the same issue. My workaround :

videoNode = SKVideoNode(AVPlayer: player)
videoNode.hidden = true

player.addBoundaryTimeObserverForTimes([1/30.0], queue: dispatch_get_main_queue()) { [unowned self] () -> Void in
    self.videoNode.hidden = false
}
Dominique PERETTI
  • 1,063
  • 12
  • 12
0

strange issue... can you try without using AVPlayerItem ? like this:

  NSString *resourcePath = [[NSBundle mainBundle] pathForResource:@"IntroMovie" 
                                                           ofType:@"m4v"];
  NSURL *introVideoURL = [NSURL fileURLWithPath:resourcePath];

  AVPlayer *player = [AVPlayer playerWithURL: introVideoURL];

  SKVideoNode *introVideo = [[SKVideoNode alloc] initWithAVPlayer: player];

  [introVideo setSize:CGSizeMake(self.size.width, self.size.height)];
  [introVideo setPosition:self.view.center];
  [self addChild:introVideo];
  [introVideo play];
Ilario
  • 5,979
  • 2
  • 32
  • 46
  • Same result. It seems unrelated to using an AVPlayer or AVPlayerItem and still happens when not using either and directly loading the SKVideoNode with the video. – Mark Reid Mar 25 '14 at 11:21
  • I have had issues in the past like this with MPMoviePlayer, but you were able to insert a background colour or image into that which took care of it. No options to do that here as the AVPlayer and SKVideoNode isn't backed by a view. – Mark Reid Mar 25 '14 at 11:23