AVPlayer continues to play after ViewController is removed from NavigationController
Asked Answered
A

4

26

So I'm using ARC in my project and when I add an AVPlayerLayer it works just fine and dandy, but when I pop the UIViewController from my UINavigationItem the video continues to play in the background. Does anyone know how you would handle this? It seems like something easy I'm just overlooking. Here's the code I have for the initially instantiations.

self.currentItem = [[AVPlayerItem alloc] initWithURL:url];

self.player = [[AVPlayer alloc]initWithPlayerItem:self.currentItem];
self.avPlayerLayer = [AVPlayerLayer playerLayerWithPlayer:player];

self.avPlayerLayer.bounds = self.view.bounds;
self.avPlayerLayer.frame = CGRectMake(0,55, 1024, 670);

self.view.backgroundColor = [UIColor clearColor];

[self.view.layer addSublayer:avPlayerLayer];

Also this is how I have the properties definied.

@property (strong) AVPlayer *player;
@property (strong) AVPlayerLayer *avPlayerLayer;
@property (strong) AVPlayerItem *currentItem;

Maybe that's entirely wrong as well. I'm not exactly sure when to use (strong) vs (weak). Any case thank you ahead of time for any help.

Aletheaalethia answered 9/11, 2011 at 23:16 Comment(3)
Have you tried setting all its subviews to nil before you pop the view controller?Sinuous
self.view = nil; [nc popViewControllerAnimated:TRUE]; The video stops, and then reloads from the beginning and starts playing again in the background.Aletheaalethia
my AVPlayer is in tableviewcell..how do i stop it when i pop to previous viewcontroller ??Charlottcharlotta
T
18

If avPlayerLayer is the only class interacting with the avPlayer, you don't need to maintain a reference to it with a property in the class you're using to present it (unless this class uses it outside the code you've shared). In fact, this is probably why it isn't working the way you expect.

The reason the clip continues to play (I think) is because the player isn't being deallocated. You create it, own it with the strong property in your class, than it's owned again by the AVPlayerLayer class you hand it to. So when the AVPlayerLayer is deallocated, the AVPlayer looses one owner. But it still has an owner (your class), so it isn't deallocated, and it keeps playing. The solution here is to get rid of your owning property altogether for *avPlayer. You don't need it. Create the AVPlayer and pass it to AVPlayerLayer. That should be all that's needed.

Something else you could do which might fix the behavior but not the problem, call:

[avPlayer pause]

In your AVPlayerLayer's dealloc method.

Re: Strong vs. Weak references: A strong reference implies ownership. As ARC is managing memory, it'll be doing all the [object retain]ing and [object release]ing that you previously would have done in code OR that you would have done with properties, ie:

@property (retain) NSObject *iAmRetainedWhenProperyIsAssigned;

So now with ARC, we simple users don't use words like retain or release in code or when defining properties. But we still design software. ARC is smart, but not smart enough to infer the architectural implications of relationships that we're defining. It still needs to be told that a class "owns" a reference to the object that the property refers to. Which reduces us, in the most basic terms:

(strong) means to ARC what (retain) meant to properties pre-ARC (owned/retained)

(weak) means to ARC what (assign) meant to properites pre-ARC (not owned/not retained)

Thurstan answered 10/11, 2011 at 0:19 Comment(7)
Thanks for the suggestions. I'll try some of this out tomorrow and see what I come up with. I originally had the AVPlayer as just an iVar, but when I started having problems with it I switched it. Also there is no stop property on the AVPLayer class. That is only available on the AVAudioPlayer class from what I've read in the docs. In any case thanks again for taking the time to write up such a detailed post.Aletheaalethia
Edited [avPlayer stop] to [avPlayer pause]. I mistakenly thought that AVPLayerLayer was your own subclass, I didn't realize it was a CALayer subclass.Thurstan
From what I understand releasing and deallocating is all managed by ARC and you're not even allowed to call those functions. Is that wrong?Aletheaalethia
Ya, if I try call dealloc on a particular variable / property it gives me an error message saying ARC forbids explicit deallocating.Aletheaalethia
So I ended up just pausing the video. Haven't put it on a device to see how well it actually manages the memory, but it seems to reload fine when I come back to the page with a different video.Aletheaalethia
Yes, you don't explicitly release anything in dealloc. In many cases this means you don't need to use dealloc at all, but you're still allowed to, for example, to pause. But as I said in the answer, that's really not a great solution, because you're probably leaking the player. Does it not work at all if you omit your @property (strong) AVPlayer? You could also simply set that to weak instead of strong, because I really don't think you need an owning reference...Thurstan
my AVPlayer is in tableviewcell..how do i stop it when i pop to previous viewcontroller ??Charlottcharlotta
S
15

Maybe you did not pause. Do it and then remove layer and nullify player.

[player Pause];
[player removefromsuperlayer];
player = nil;
Straighten answered 9/2, 2012 at 10:36 Comment(2)
shouldn't that be (at least as of today) [avPlayerLayer removefromsuperlayer]. At least that's the equivalent of what I see from Swift. AVPlayer offers no removefromsuperlayer method.Sidoon
my AVPlayer is in tableviewcell..how do i stop it when i pop to previous viewcontroller ??Charlottcharlotta
C
1

When you want to stop an AVPlayerItem from loading, use AVQueuePlayer's removeAllItems and then re-initialize it.

  [self.avPlayer removeAllItems];
  self.avPlayer = [AVQueuePlayer playerWithURL:[NSURL URLWithString:@""]];
  self.avPlayer = nil;

This will stop the current item from loading -- it is the only way I found to accomplish this.

Creationism answered 26/2, 2014 at 17:33 Comment(0)
G
0

2023 answer ...

As there are only very old answers. These days in fact:

( 1 ) All you need to do is nil it in the playerLayer:

///Unplay and return to blankness.
public func unplay() {
    playerLayer.player = nil
}

However, as @isaac astutely pointed out, you almost always also have just a variable with the player in it (to make it easier for you to control it). Don't forget that you of course have to nil that as well.

Thus when you lunch the player, your code will look something like

var handyPlayer: AVPlayer?

...
handyPlayer = AVPlayer(url: u)
playerLayer.player = handyPlayer
handyPlayer?.play()
handyPlayer? ... seek, etc etc

( 2 ) Thus, almost always, the code is:

///Unplay and return to blankness.
public func unplay() {
    playerLayer.player = nil
    handyPlayer = nil
}

There is no need to pause, release anything, or anything else. That's all there is to it.

Gnaw answered 23/2, 2023 at 18:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.