luckyled
luckyled

Reputation: 11

OS X: Setting up a playback controls overlay for an AVFoundation video

I am trying to set up an NSView that will be overlaid on top of a video that is being played by AVFoundation. I really just want to create something that looks similar to the native quicktime player. I am trying to do this on an OS X application.

I have set up an NSView that has two subviews: a view to play the video and a view to encompass the controls. Whenever I start playing the video the controls view gets pushed behind the video playing view rendering the controls view useless.

I believe the problem has to do with overlaying two subviews that are on the same level in view hierarchy. This problem would lead to the solution of making the controls view a subview of the video playing view. The problem is that to play a video with AVFoundation the video playing view must become a layer hosting view, which prevents it from having subviews.

Things I have tried to solve the problem:

I might be using these solutions wrong so if you believe they should work let me know.

This is how I set up my video playing view.

self.playerItem = [AVPlayerItem playerItemWithAsset:self.asset];
[self.playerItem addObserver:self forKeyPath:@"status"
             options:0 context:&ItemStatusContext];
self.player = [AVPlayer playerWithPlayerItem:self.playerItem];

self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];

[self.playerView setLayer:self.playerLayer];
[self.playerView setWantsLayer:YES];

This seems like a common thing someone would want to do for video playback, so if anyone knows a solution please let me know. Thanks!

Upvotes: 1

Views: 1044

Answers (1)

Amber Haq Dixon
Amber Haq Dixon

Reputation: 614

Note that the docs don't say that a layer-hosted view can't contain any subviews, they just say that you can't ADD any subviews to a layer-hosted view. You could add all the subviews you need to the view first, and THEN convert it to a layer hosted view. However, even in that case, the ordering of the layer and the subviews is not deterministic.

I solved the problem by creating TWO subviews within my main view. One of the subviews is layer-hosted and has the video playing layer; the other subview is a layer-backed view containing the controls. Here's how I solved this problem:

   [self setWantsLayer:YES]; // Turn your main view into a layer backed view, so ordering of subviews is deterministic.

    _controlsView = [[NSView alloc]initWithFrame:frame];        
    _playButtonOverlay = [[NSImageView alloc] initWithFrame:frame];
    [ _playButtonOverlay setImage:[NSImage imageNamed:@"play_overlay"]];
    [_controlsView addSubview:_playOverlay];

    _videoView = [[NSView alloc]initWithFrame:frame];
    _playerLayer = [AVPlayerLayer playerLayerWithPlayer:_player];        
    [_videoView setLayer:_playerLayer]; // make this subview layer-hosted
    [_videoView setWantsLayer:YES];
    [self insertSubview:_videoView belowSubview:_controlsView];

If you haven't already, see the documentation for setWantsLayer in NSView. It says:

"A layer-backed view is a view that is backed by a Core Animation layer. Any drawing done by the view is cached in the backing layer. You configure a layer-backed view by invoking setWantsLayer: with a value of YES.

"A layer-hosting view is a view that contains a Core Animation layer that you intend to manipulate directly. You create a layer-hosting view by instantiating a Core Animation layer class and supplying that layer to the view’s setLayer: method. After doing so, you then invoke setWantsLayer: with a value of YES. This method order is crucial."

Upvotes: 2

Related Questions