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