The API and overall approach for working withMPMoviePlayerController has changed just enough in the 3.2 SDK (iPad) and iOS4 SDK to cause working applications in earlier releases to be problematic when running on later SDKs. In this post I’ll walk through a short example of a movie player application that will work with 3.x, 3.2 and 4.0 SDKs.
In the next post, Display iPhone Movies in Portrait Mode, I’ll show how to create a movie player that runs in a view that is not fullscreen as well as how to show a few lines of code to display a movie in portrait mode – the one caveat here is that both of the tips in the second post will apply only to OS versions 3.2 and up.
What’s Changed with MPMoviePlayerController in 3.2 and 4.0?
There are any number of changes in the MPMoviePlayerController, however, a few standout as potential roadblocks to getting a movie to display:
- In 3.1 and earlier versions, MPMoviePlayerController was full-screen only. Playing a movie was straight-forward, create a player, initialize with a file (path or URL) and call a method to start playback – the rest was taken care of for you.
- With 3.2 and later, movies can playback in fullscreen or a custom view, as well as portrait or landscape.
- The notification MPMoviePlayerContentPreloadDidFinishNotification has been deprecated. This notification was used in earlier versions to notify observers that a movie was loaded and ready to play.
The Movie Application
The application in this post is quite simple, it consists of a view controller with nothing more than a button to start playback and a second view controller to manage aMPMoviePlayerController and the NSURL of the movie. The two views are shown in the figures below:
Primary View Controller
The interface definition for the primary view is shown below:
#import <UIKit/UIKit.h> @class CustomMoviePlayerViewController; @interface TestViewController : UIViewController { CustomMoviePlayerViewController *moviePlayer; UIButton *playButton; } @end
CustomMoviePlayerViewController is the controller for managing the movie, we’ll look at that code in a moment.
In the code below we create the view, add a play button, create a method for processing a button press event and within loadMoviePlayer, we get a reference to the movie file and create an instance of the CustomMoviePlayerViewController, which will load and play the movie.
- (void)loadView { // Setup the view [self setView:[[[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]] autorelease]]; [[self view] setBackgroundColor:[UIColor grayColor]]; [[self view] setUserInteractionEnabled:YES]; // Add play button playButton = [[UIButton alloc] initWithFrame:CGRectMake(53, 212, 214, 36)]; [playButton setBackgroundImage:[UIImage imageNamed:@"playButton.png"] forState:UIControlStateNormal]; [playButton addTarget:self action:@selector(buttonPressed:) forControlEvents: UIControlEventTouchUpInside]; [[self view] addSubview:playButton]; } - (void)buttonPressed:(UIButton *)button { // If pressed, play movie if (button == playButton) [self loadMoviePlayer]; } - (void)loadMoviePlayer { // Play movie from the bundle NSString *path = [[NSBundle mainBundle] pathForResource:@"Movie-1" ofType:@"mp4" inDirectory:nil]; // Create custom movie player moviePlayer = [[[CustomMoviePlayerViewController alloc] initWithPath:path] autorelease]; // Show the movie player as modal [self presentModalViewController:moviePlayer animated:YES]; // Prep and play the movie [moviePlayer readyPlayer]; }
Notice that the movie for this example is loaded from the application bundle. Also, once the custom movie player is created, the view is shown as a modal view.
Custom Movie Player View Controller
The interface definition for the movie view controller is below:
@interface CustomMoviePlayerViewController : UIViewController { MPMoviePlayerController *mp; NSURL *movieURL; } - (id)initWithPath:(NSString *)moviePath; - (void)readyPlayer; @end
The initialization code is where the movie player and associated URL are created. The primary goal of the initialization is to create the NSURL needed by the movie player.
- (id)initWithPath:(NSString *)moviePath { // Initialize and create movie URL if (self = [super init]) { movieURL = [NSURL fileURLWithPath:moviePath]; [movieURL retain]; } return self; }
The code to create the player and setup the notifications is where we start to deal with the differences in how the movie player works on various OS versions. Notice below the call to respondsToSelector, this is the Apple recommended way to check for feature availability, versus looking for a specific OS version.
For devices running 3.2 and above, the movie player controller has a method namedloadstate, if this exists we can access a few additional methods to set the player to fullscreen as well as request the movie to begin preloading.
Equally important is the distinction of which notification to setup – see lines 16 and 24.
You can read more about managing different OS version in this post Developing iPhone Apps with iOS4 SDK, Deploying to 3.x Devices
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | - (void) readyPlayer { mp = [[MPMoviePlayerController alloc] initWithContentURL:movieURL]; // For 3.2 devices and above if ([mp respondsToSelector:@selector(loadState)]) { // Set movie player layout [mp setControlStyle:MPMovieControlStyleFullscreen]; [mp setFullscreen:YES]; // May help to reduce latency [mp prepareToPlay]; // Register that the load state changed (movie is ready) [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(moviePlayerLoadStateChanged:) name:MPMoviePlayerLoadStateDidChangeNotification object:nil]; } else { // Register to receive a notification when the movie is in memory and ready to play. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(moviePreloadDidFinish:) name:MPMoviePlayerContentPreloadDidFinishNotification object:nil]; } // Register to receive a notification when the movie has finished playing. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(moviePlayBackDidFinish:) name:MPMoviePlayerPlaybackDidFinishNotification object:nil]; } |
The next two chunks of code are for each of the selectors, one for each notification. The code below is for earlier OS versions – nothing more than removing the notification and asking the movie to play.
/*--------------------------------------------------------------------------- * For 3.1.x devices * For 3.2 and 4.x see moviePlayerLoadStateChanged: *--------------------------------------------------------------------------*/ - (void) moviePreloadDidFinish:(NSNotification*)notification { // Remove observer [[NSNotificationCenter defaultCenter] removeObserver:self name:MPMoviePlayerContentPreloadDidFinishNotification object:nil]; // Play the movie [mp play]; }
For the notification generated in 3.2 and above, there are a few more details to tend to:
/*--------------------------------------------------------------------------- * For 3.2 and 4.x devices * For 3.1.x devices see moviePreloadDidFinish: *--------------------------------------------------------------------------*/ - (void) moviePlayerLoadStateChanged:(NSNotification*)notification { // Unless state is unknown, start playback if ([mp loadState] != MPMovieLoadStateUnknown) { // Remove observer [[NSNotificationCenter defaultCenter] removeObserver:self name:MPMoviePlayerLoadStateDidChangeNotification object:nil]; // When tapping movie, status bar will appear, it shows up // in portrait mode by default. Set orientation to landscape [[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeRight animated:NO]; // Rotate the view for landscape playback [[self view] setBounds:CGRectMake(0, 0, 480, 320)]; [[self view] setCenter:CGPointMake(160, 240)]; [[self view] setTransform:CGAffineTransformMakeRotation(M_PI / 2)]; // Set frame of movie player [[mp view] setFrame:CGRectMake(0, 0, 480, 320)]; // Add movie player as subview [[self view] addSubview:[mp view]]; // Play the movie [mp play]; } }
Beyond removing the notification, we also adjust the status bar, rotate the view, set the frame of the movie player, add the movie player as a subview in the view controller and wrap it all up by asking the movie to play.
Note: MPMoviePlayerViewController is also an option over creating your own view controller as I’ve done here.
Source Code
The easiest way to see all this working is to download the source code and step through the code in the debugger.