I am working on an app where feed have videos looping in it (inside table view cells). There can be multiple videos present at the same time, but only one of them may be playing. I'm creating AVPlayer
s in background, and putting their playerLayer
s into my view's layer as a subview, and play the video with the most area on screen when user stops scrolling the feed.
However, recently I noticed that the video is not displayed in the feed when the user stops scrolling. The sound starts immediately, but the video is not displayed. I've checked the player layer's bounds are it's perfectly fine. I've seen AVPlayerLayer shows black screen but sound is working but that question applies only to an older version of iOS. I've also seen AVPlayer Video Blank but Hear Sound but that question is resolved as the OP realized that the bounds of the layer was zero rect. I've added a timer to print my video player's bounds repeatedly, and the bounds is correct even when the video is not displayed.
After a few seconds of sound-only playback, the video just kicks in playing perfectly. I've double checked that the "kick in" doesn't coincide of the video seeking back to 0:00. It just kicks in at a random part of the video. I've also iterated all the superlayers until I've reached UIWindow
's layer. The layer hierarchy correctly reaches the window's layer, and none of the intermediate layers have zero rect. Also, when the video randomly kicks in, none of the above data (superlayers, their rects) change at all. Just to be sure, I've added the following code and I don't get any assertion failures (I've already had NSLog
s in it, so yes, it's called):
timer = [NSTimer scheduledTimerWithTimeInterval:0.1 repeats:YES block:^(NSTimer * _Nonnull timer) {
NSAssert(_playerLayer.bounds.size.width, @"bounds");
NSAssert(_playerLayer.bounds.size.height, @"bounds");
NSAssert(!_playerLayer.hidden, @"hidden");
BOOL inWindow = NO;
CALayer *layer = _playerLayer.superlayer;
while(layer){
layer = layer.superlayer;
NSAssert(layer.bounds.size.width, @"bounds");
NSAssert(layer.bounds.size.height, @"bounds");
NSAssert(!layer.hidden, @"hidden");
if(layer == [UIApplication sharedApplication].keyWindow.layer){
inWindow = YES;
break;
}
}
NSAssert(inWindow, @"not in window");
}];
Some observations and summary:
AVPlayer
/AVPlayerLayer
combo and I don't create new players or layers for the same video again, I have a dictionary of URL-to-player/layer mapping and I use them again.What might be the problem?
Okay, I've missed out the fact that setting player's bounds must be called on the main queue. I was calling it on a background queue to offload some work from the main queue (which apparently failed), and it was resulting in undefined behavior. I've dispatched it to the main queue and it started working correctly.