Search code examples
iosobjective-cxcodeavplayeravplayerviewcontroller

Stop Playback of AVPlayer item and replace with new


My app has a tableview that when selected, opens a new view controller and uses AVPlayer to start playing the URL passed to it. However, when they go back and select a new item for playback, I can't get it to stop the first, and begin playback of the second. Any suggestions?

- (void)viewWillAppear:(BOOL)animated {
        [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];

          
           [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
           [self becomeFirstResponder];
           NSURL *newURL = [NSURL URLWithString:_entry.articleUrl];
          
           AVPlayer *player = [AVPlayer playerWithURL:newURL];
    
            _controller = [[AVPlayerViewController alloc]init];
            _controller.player = player;
        
            
           
            // show the view controller
            [self addChildViewController:_controller];
            [self.view addSubview:_controller.view];
            _controller.view.frame = self.view.frame;
    [player play];
   
    self.title = _entry.articleTitle;
   
    

    [super viewWillAppear:YES];
}

Solution

  • EDIT I've changed this to return the player when you message startPlaying - that way it is real easy to update your controller as discussed in the comments.

    Here is an outline that may work - did not test it but hopefully it will solve your problem.

    Header file should contain

    @interface MyPlayer : NSObject
    
    // Start playing some URL and return the player
    + ( AVPlayer * ) startPlaying:( NSURL * ) url;
    
    @end
    

    And in m file, very similar to before,

    #import <AVKit/AVKit.h>
    #import "MyPlayer.h"
    
    @interface MyPlayer ()
    
    @property (nonatomic,strong) NSObject * playerLock;
    @property (nonatomic,strong) AVPlayer * player;
    
    @end
    
    @implementation MyPlayer
    
    // Initialise
    - ( id ) init
    {
        self = super.init;
    
        if ( self )
        {
            self.playerLock = NSObject.new;
        }
    
        return self;
    }
    
    // Return singleton
    + ( MyPlayer * ) myPlayer
    {
        static MyPlayer * mp;
        static dispatch_once_t t;
    
        dispatch_once ( & t, ^ {
    
            mp = [[MyPlayer alloc] init];
    
        } );
    
        return mp;
    }
    
    // Start playing some URL and return the player
    + ( AVPlayer * ) startPlaying:( NSURL * ) url
    {
        MyPlayer * mp = MyPlayer.myPlayer;
        AVPlayer * player;
    
        @synchronized ( mp.playerLock )
        {
            if ( mp.player )
            {
                // Replace
                [mp.player replaceCurrentItemWithPlayerItem:[AVPlayerItem playerItemWithURL:url]];
            }
            else
            {
                // Create a new player
                mp.player = [AVPlayer playerWithURL:url];
            }
            
            player = mp.player;
        }
    
        // Start playing on a background thread
        dispatch_async ( dispatch_get_global_queue ( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^ {
    
            @synchronized ( mp.playerLock )
            {
                // Start playing
                mp.player.play;
            }
    
        } );
    
        return player;
    }
    
    @end
    

    Just message [MyPlayer startPlaying:url]; to play a URL as before, but this time, it will return the player.

    So in your code you can do something like

    _controller.player = [MyPlayer startPlaying:url];