Update on Movie Playback on the iPhone/iPod touch
The other day, I posted up some code for playing back a movie on the iPhone and iPod touch using the MoviePlayerUI class. In that post, I mentioned that I couldn’t get Celestial and UIKit to play nice with each other, and that was the reason that I ended up using the MoviePlayerUI framework instead. Well, I did some more sleuthing tonight, and I was successfully able to get Celestial and UIKit to be friends again.
Before I get to that, let me tell you why I didn’t find MoviePlayerUI to be an acceptable alternative. The big problem that I ran into had to do with the scaling. If you double-tapped on the display while a movie was playing back, the movie would scale down to a smaller size. Double-tap again, and it would scale larger — in my case, the larger size wasn’t even the full screen size, so you wouldn’t even be able to watch the movie in full size unless another movie was started by the app. Ugh.
So tonight I took a look at AVController again, and found the magic to get it to work with UIKit — but, doing so meant using a private method in UIView. Basically, AVController provides a function to take over an existing LayerKit (aka Core Animation) layer. What I did was create a UIWindow with a UIView in it and then pass the layer for the UIView over to AVController to handle the updating. Of course, this isn’t a very nice thing to do to UIView, since I just basically stole it’s layer for my own (evil?) purposes.
The rest of the code looks like the code example that I first posted — i.e, use a AVController and AVItem to playback an item. I added a few things like registering for a notification when the movie is done playing, but it’s fairly straightforward:
- (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];
UIView *view = [[UIView alloc] init];
[controller setLayer:[view _layer]];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(movieFinishedPlaying:) name:@"AVController_ItemPlaybackDidEnd" object:controller];
AVItem *item = [[AVItem alloc] initWithPath:@"/var/root/movie.mp4" error:&error];
[controller setCurrentItem:item preservingRate:NO];
Just note that there isn’t any error checking in this — for example, if the movie you try to play isn’t valid, the code will just hang waiting for a item finished notification that will never come since it never started in the first place. Also, both this code and the previous code have a rather noticeable delay when starting up and they both pause if you disconnect the headphones.