Playing video in iOS

This week I had the interesting challenge of having to play a video in an iPad app. I basically needed to explore having a full-screen video play in the background while still allowing the user to interact with other controls rendered on top of the video.

You have 2 options for playing video in iOS – MPMoviePlayerController and AVPlayer. MPMoviePlayerController uses the AVPlayer behind the scenes so it’s an easier way of showing a video to the user and letting them interact with it while with AVPlayer you get more custom control.

Playing video with MPMoviePlayerController

For testing the MPMoviePlayerController I simply added a video file to my XCode project, just as you would do with a regular image file. According to the documentation the supported video formats are .mov, .mp4, .mpv, and .3gp – I used an .mp4 file.

To get started, I created the player inside my viewDidAppear and point it to my video file. (As far as I can tell it doesn’t always work if you set the frame inside your viewDidLoad method – I’m guessing it’s because the frame isn’t properly loaded) Make sure you set the player as a property inside your implementation.

NSString *filepath = [[NSBundle mainBundle] pathForResource:@"vid" ofType:@"mp4"];
NSURL *fileURL = [NSURL fileURLWithPath:filepath];
self.moviePlayerController = [[MPMoviePlayerController alloc] initWithContentURL:fileURL];

Now we need to give the player a frame and add it as a subview.

[self.moviePlayerController.view setFrame: CGRectMake(0, 0, 1024, 768)];
[self.view addSubview:self.moviePlayerController.view];

And now you simply play the video.

[self.moviePlayerController play];

To minimize playback delay, you can call the prepareToPlay method which will tell the player to start loading the file from disk.

I also needed to know when the video was finished. The MPMoviePlayerController uses the NSNotificationCenter to send notifications about events such as the video being ready to play or the video ending.

[[NSNotificationCenter defaultCenter] addObserver:self
                                          selector:@selector(introMovieFinished:)                                             name:MPMoviePlayerPlaybackDidFinishNotification
                                            object:self.moviePlayerController];

Here is the full code example:

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    NSString *filepath = [[NSBundle mainBundle] pathForResource:@"vid" ofType:@"mp4"];
    NSURL *fileURL = [NSURL fileURLWithPath:filepath];
    self.moviePlayerController = [[MPMoviePlayerController alloc] initWithContentURL:fileURL];
    [[NSNotificationCenter defaultCenter] addObserver:self 
                                             selector:@selector(introMovieFinished:)
                                                 name:MPMoviePlayerPlaybackDidFinishNotification
                                               object:self.moviePlayerController];
    
    // Hide the video controls from the user
    [self.moviePlayerController setControlStyle:MPMovieControlStyleNone];
    
    [self.moviePlayerController prepareToPlay];
    [self.moviePlayerController.view setFrame: CGRectMake(0, 0, 1024, 768)];
    [self.view addSubview:self.moviePlayerController.view];
    
    [self.moviePlayerController play];
}

- (void)introMovieFinished:(NSNotification *)notification
{
    NSLog(@"Video ended!");
}

Playing video with AVPlayer

If you need more fine-grained control over your video or you need to play multiple videos at once, you can use the AVPlayer.

Once again, I create the player inside my viewDidLoad and point it to my video file. Again, make sure you set the AVPlayer as a property.

NSString *filepath = [[NSBundle mainBundle] pathForResource:@"vid" ofType:@"mp4"];
NSURL *fileURL = [NSURL fileURLWithPath:filepath];
self.avPlayer = [AVPlayer playerWithURL:fileURL];

Now you need to create an AVPlayerLayer with your player and then add the layer to your current view.

AVPlayerLayer *layer = [AVPlayerLayer playerLayerWithPlayer:self.avPlayer];
self.avPlayer.actionAtItemEnd = AVPlayerActionAtItemEndNone;
        
layer.frame = CGRectMake(0, 0, 1024, 768);
[self.view.layer addSublayer: layer];

And now you simply play the video.

[self.avPlayer play];

The AVPlayer also uses NSNotificationCenter to send notifications about changes in the video’s state, so subscribing to those events follow the same pattern as with the MPMoviePlayerController.

Here is the full code example:

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    NSString *filepath = [[NSBundle mainBundle] pathForResource:@"vid" ofType:@"mp4"];
    NSURL *fileURL = [NSURL fileURLWithPath:filepath];
    self.avPlayer = [AVPlayer playerWithURL:fileURL];
    
    AVPlayerLayer *layer = [AVPlayerLayer playerLayerWithPlayer:self.avPlayer];
    self.avPlayer.actionAtItemEnd = AVPlayerActionAtItemEndNone;
    layer.frame = CGRectMake(0, 0, 1024, 768);
    [self.view.layer addSublayer: layer];
    
    [self.avPlayer play];
}

The AVPlayer also supports very fine-grained control in terms of synchronizing playback with external sources and jumping to a specified time within the video.

As far as I can tell the MPMoviePlayerController is supposed to be the easier version to implement while the AVPlayer offers more fine-grained control. Happy coding.