jimbo
jimbo

Reputation: 11042

Find the AVPlayer associated with an AVPlayerItem

An AVPlayerItem can only ever be assigned to one AVPlayer. Once an AVPlayerItem has been added to an AVPlayer, future attempts to add it to a different AVPlayer will SIGABRT the app.

So, given an AVPlayerItem, how can you determine:

  1. What AVPlayer it is currently associated with? and
  2. If it has ever been inserted into an AVPlayer at any point in the past?

The following code demonstrates the problem reliably:

AVPlayerItem *item = [AVPlayerItem playerItemWithURL:[NSURL URLWithString:@"http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8"]];
AVPlayer *firstPlayer = [[AVPlayer alloc] init];
[firstPlayer replaceCurrentItemWithPlayerItem:item];
AVPlayer *secondPlayer = [[AVPlayer alloc] init];
[secondPlayer replaceCurrentItemWithPlayerItem:item];

And here's the error message:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'An AVPlayerItem cannot be associated with more than one instance of AVPlayer'

Upvotes: 15

Views: 7135

Answers (5)

DarkDust
DarkDust

Reputation: 92345

There is a semi-legal way that might break in the future. The player item has a private method (and, it seems, variable) named _player. You can query it like this (which technically doesn't use private API, so I think it should be AppStore-safe):

id player = [item valueForKey:@"_player"];

Upvotes: 8

kelin
kelin

Reputation: 11995

Here is an elegant solution, without private keys and associated objects. Create a custom player item:

class PlayerAwareItem {
    weak var player: AVPlayer?
}

Setup a player:

let item = PlayerAwareItem(asset: asset)
let player = AVPlayer(playerItem: item)
item.player = player

Access the player whenever you have the item later (in this example we want to restart the player after stalling):

@objc func handlePlayerItemDidStall(_ notification: Notification) {
    guard let playerItem = notification.object as? PlayerAwareItem,
        let player = playerItem.player else { return }
    player.play()
}

Upvotes: 3

Johnny Rockex
Johnny Rockex

Reputation: 4196

Based on Anil's answer, for a looped playback on multiple videos:

for (NSObject * obj in cachedVideos){
    if ([obj isKindOfClass:[AVPlayerLayer class]]){
        AVPlayerLayer * layer = (AVPlayerLayer *)obj;
        if ([layer.player.currentItem isEqual:n.object]){
            [layer.player seekToTime:kCMTimeZero];
            [layer.player play];
            break;
        }
    }
}

Upvotes: 0

vaughan
vaughan

Reputation: 7465

You can associate an AVPlayerItem with an AVPlayer using:

static char playerKey;

objc_setAssociatedObject(item, &playerKey, player, OBJC_ASSOCIATION_RETAIN);

and get it using:

objc_getAssociatedObject(item, &playerKey);

Upvotes: 0

Anil
Anil

Reputation: 2438

Did you try the other way round? Try accessing the details inside the item that is being played by the AVPlayer

AVPlayer *firstPlayer = [[AVPlayer alloc]init];

After which you can access firstPlayer.currentItem .This will give you access the firstPlayer's AVPlayerItem.

Eg:

firstPlayer.currentItem.duration

will give you the duration of that track.

Upvotes: 3

Related Questions