Movie Playback on the iPhone/iPod touch
For a project I’ve been working on over the past few months, I needed to be able to playback video on the iPhone. What seemed like an easy task ended up taking much longer than I cared for because of lack of documentation about this on the web. So, this post will hopefully help others out in the same situation.
Now, most of the problems in this area boil down to two things — one is that Apple hasn’t released an official SDK yet. They promised one for February, and in Apple terms, expect it to show up around 6:00PM on the 29th at the earliest. The other is that there doesn’t seem to be as much interest in movie playback in the iPhone hacking community. Part of this might be the lack of info on the topic, the other part might be that not too many apps want to play video, since MobileMusicPlayer.app handles that functionality just fine from the user’s perspective.
My project, however, needed to be able to play video — and like the preverbal elephant in the room, it was getting harder to ignore the task at hand and get it working. So, I dug in last night to everything the web has to offer on the subject…. which is pretty close to nothing. Until now, that is 🙂
Let me back up a bit — on MacOS X, Apple provides QuickTime as the API to use for playing video. Apple also provides a framework called QTKit for the Cocoa programmers. QuickTime has been around for quite a while now and is a very robust way to play video. But, on the iPhone and iPod touch, Apple didn’t bring QuickTime over directly. Instead, they created a new library called Fig that is roughly analogous to QuickTime and another framework called Celestial that some have compared to QTKit. But, what isn’t usually mentioned is a third framework — MobileMoviePlayerUI. This, IMO, is the real counterpart to QTKit.
But I’m jumping ahead again — why was I looking for another framework anyway if I had Celestial? Well, earlier in the process, I had come across this blog post with a cornucopia of information on audio for the iPhone. In fact, I wish I would’ve ran across it earlier when I was implementing audio support for the iPhone into SDL — but that’s another subject all together. Anyway, this page had little sniplet of code for playing back any media file that Fig/Celestial supported:
AVController * av = [[AVController alloc] init];
AVItem * item = [[AVItem alloc] initWithPath:[t path] error:&error];
[av setCurrentItem:item preservingRate:NO];
BOOL ok = [av play:nil];
Was it really that easy? Four lines of code, and *boom* video playback on the iPhone? Well, not quite. See, if you look closely, that code doesn’t have anything tying it into UIKit to actually get it on the screen. But it will play the audio quite happily for you. If you are just wanting to play sound files on the iPhone, those are basically the four lines of code you need to play back pretty much anything. But video is a different beast.
I spent a while trying to get Celestial and UIKit to play nice with each other, but the lack of documentation got in the way again. And, to top it off, when I ran nm on MobileMusicPlayer.app (which is the app that plays videos as well as music on the iPhone), none of the interesting symbols that I thought would be used to get it to work with UIKit were even referenced in that app. So, whatever secret method Apple had for connecting Celestial to UIKit was someplace else.
About this time, I noticed a new class in the output of nm — UIMoviePlayerController. Hmm… what’s this? This wasn’t anywhere in the headers I had for Celestial. In fact, it wasn’t anywhere in my include directory for my iPhone toolchain. So, I looked around to see if I could find the framework where UIMoviePlayerController lived, and I found MobileMoviePlayerUI in /System/Library/Frameworks. A quick run of nm confirmed that this was the framework I was looking for. But, I still had that lack of headers problem.
At first, I wasn’t sure if headers had even been generated at all for this framework. But, a quick look over at Erica’s wonderful online API reference told me that there were headers somewhere. Thankfully, someone on #iphone-uikit pointed me to a page with a more complete set of headers. Yay!
One little bit of advice — don’t go and blow away your existing include directory and replace it with that. It will not work. Instead, copy over the headers you need, and expect to have to do some minor edits to get them working. For example, you can’t
#import "NSObject.h" — you need to
#import <Foundation/Foundation.h> instead. If you don’t understand what you need to do, then I would recommend updating your toolchain, and if that doesn’t give you the new headers, you’ll probably have to wait until someone updates the toolchain with the latest headers. Or you can do what I did and fix the handful of errors you get. Your call.
Anyway, at this point, I could start playing with UIMoviePlayerController and friends, and thankfully, the API is pretty straightforward. But, some sample code is always beneficial, especially when you have no documentation to reference. So I went back to my sometimes good friend Google, and it gave me seven links when I searched for UIMoviePlayerController. And lucky for me, one of those links were to actual code that successfully used it. Yay!
So, to boil it down to a code sniplet, here is my rough distilled version suitable to use as a starting point in your own code:
(void) applicationDidFinishLaunching: (id) unused
// hide the status bar
[self setStatusBarMode:2 orientation:0 duration:0.0f fenceID:0];
UIWindow *window = [[UIWindow alloc] initWithContentRect: [UIHardware fullScreenApplicationContentRect]];
[window orderFront: self];
[window makeKey: self];
struct CGRect rect = [UIHardware fullScreenApplicationContentRect];
UIMoviePlayerController *player = [[UIMoviePlayerController alloc] initWithPlayerSize:rect.size isFullScreen:YES];
[player setUIOrientation:1 animated:NO forced:YES];
[window setContentView:[player playerView]];
[[player movieView] setMovieWithPath:@"/var/root/movie.mp4"];
[[player movieView] setScaleMode:0 isHint:NO animate:NO];
[[player movieView] play];
Drop that into an iPhone app, and you should be able to play the movie fullscreen, with the screen rotated with the button on the right. When the video is done, the app will just sit there until you press the home button. There are obviously a lot of problems with this code (error checking? We don’t need no stinking error checking…), but it’s a good starting point. The
setScaleMode:0 is something that I had to use for my videos — for some reason, it wanted to scale down my video that was sized to the same size as the iPhone’s display. But,
setScaleMode:0 forced it to take up the entire screen. Also, if you double-tap on the screen, the scaling will change. You won’t be able to tap it back to the fullscreen. If anyone figures out how to disable that, please post it below.
Hope this helps anyone out there that’s trying to get video to work on the iPhone. And thank you to everyone else who has blazed a trail with this as well — otherwise, I would still be looking at that elephant and wondering what I was going to do about it.
Edit: There is an update for this post — I figured out how to work with AVController directly now.