Jul 09

Apple made significant changes to the MPMoviePlayerController API:s in iOS 3.2. Before, all you had to do was to initialize the MPMoviePlayerController and call play. Now things are a bit more complicated because videos can be played on a portion of the screen (good for the iPad) and not just in full screen mode.

But if all you want is to play a video in full-screen mode on all versions of the OS, then here’s some code:

NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:introVideoFileName ofType:@""]];
MPMoviePlayerController *moviePlayer = [[MPMoviePlayerController alloc] initWithContentURL:url];

// Register to receive a notification when the movie has finished playing.
[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(moviePlayBackDidFinish:)
                                             name:MPMoviePlayerPlaybackDidFinishNotification
                                           object:moviePlayer];

if ([moviePlayer respondsToSelector:@selector(setFullscreen:animated:)]) {
  // Use the new 3.2 style API
  moviePlayer.controlStyle = MPMovieControlStyleNone;
  moviePlayer.shouldAutoplay = YES;
  [self.view addSubview:moviePlayer.view];
  [moviePlayer setFullscreen:YES animated:YES];
} else {
  // Use the old 2.0 style API
  moviePlayer.movieControlMode = MPMovieControlModeHidden;
  [moviePlayer play];
}
- (void) moviePlayBackDidFinish:(NSNotification*)notification {
  MPMoviePlayerController *moviePlayer = [notification object];
  [[NSNotificationCenter defaultCenter] removeObserver:self
                                                  name:MPMoviePlayerPlaybackDidFinishNotification
                                                object:moviePlayer];

  // If the moviePlayer.view was added to the view, it needs to be removed
  if ([moviePlayer respondsToSelector:@selector(setFullscreen:animated:)]) {
    [moviePlayer.view removeFromSuperview];
  }

  [moviePlayer release];
}

 

Recently I’ve been doing some development using cocos2d and I had to make some small changes to the code. (If your app is in portrait mode, then you may not have to do the rotation transform shown below.)

NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:introVideoFileName ofType:@""]];
MPMoviePlayerController *moviePlayer = [[MPMoviePlayerController alloc] initWithContentURL:url];

// Register to receive a notification when the movie has finished playing.
[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(moviePlayBackDidFinish:)
                                             name:MPMoviePlayerPlaybackDidFinishNotification
                                           object:moviePlayer];

if ([moviePlayer respondsToSelector:@selector(setFullscreen:animated:)]) {
  // Use the new 3.2 style API
  moviePlayer.controlStyle = MPMovieControlStyleNone;
  moviePlayer.shouldAutoplay = YES;
  // This does blows up in cocos2d, so we'll resize manually
  // [moviePlayer setFullscreen:YES animated:YES];
  [moviePlayer.view setTransform:CGAffineTransformMakeRotation((float)M_PI_2)];
  CGSize winSize = [[CCDirector sharedDirector] winSize];
  moviePlayer.view.frame = CGRectMake(0, 0, winSize.height, winSize.width);// width and height are swapped after rotation
  [[[CCDirector sharedDirector] openGLView] addSubview:moviePlayer.view];
} else {
  // Use the old 2.0 style API
  moviePlayer.movieControlMode = MPMovieControlModeHidden;
  [moviePlayer play];
}

written by Nick \\ tags: ,

Mar 03

Happy Square Root Day!

Playing a video (of a supported file format) on the iPhone is very easy using the MPMoviePlayerController class. You just create an instance of the class and initialize it with the URL of the video. The controller plays the video in full screen mode and returns back to your application when it’s done.

However, if the URL of the video is recognized by the iPhone as a YouTube URL then the Apple URL Scheme mechanism kicks in and launches the YouTube app. In this scenario control will not return to your app after the video has played. (The behavior is equivalent of calling [UIApplication openURL:] with the video URL.)

One workaround is to use a UIWebView and load it with the video URL. The drawback with this approach is that the user will see the rather ugly YouTube mobile web site and has to find and tap on the link of the video to play it.

YouTube Mobile

 

Another, visually more appealing, option is to create a small UIWebView on your screen and load it with the YouTube embed code. The result is a small button like image that shows the YouTube play button above a screen image from the video. When the user taps the image, the iPhone video player opens in full screen mode as usual, and when the video is done control is returned back to this screen.

YouTube Video Embedded in iPhone App

 

Here’s the code:

- (void)embedYouTube:(NSString*)url frame:(CGRect)frame {
 NSString* embedHTML = @"\
    <html><head>\
 <style type=\"text/css\">\
 body {\
 background-color: transparent;\
 color: white;\
 }\
 </style>\
 </head><body style=\"margin:0\">\
    <embed id=\"yt\" src=\"%@\" type=\"application/x-shockwave-flash\" \
 width=\"%0.0f\" height=\"%0.0f\"></embed>\
    </body></html>";
 NSString* html = [NSString stringWithFormat:embedHTML, url, frame.size.width, frame.size.height];
 if(videoView == nil) {
 	videoView = [[UIWebView alloc] initWithFrame:frame];
 	[self.view addSubview:videoView];
 }
 [videoView loadHTMLString:html baseURL:nil];
}

Tip of the hat to joehewitt.

Update: A complete working Xcode project has been posted here.

written by Nick \\ tags:

Feb 19

The iPhone media player is very easy to use, but it only works in landscape mode and it seems designed more for video than audio.  If you wish to play an onboard MP3 or an MP3 file as it’s downloading (not really streaming, but progressive download) you have another option!  Use the audio player embedded with Safari running in a UIWebView.

Here’s how to do it without having to create a special window for it.  Instantiate an UIWebView object using a 1×1 pixel sized frame (here self.playerView is an instance variable on my controller and is NOT added as a subview)

UIWebView *webView = [[UIWebView alloc] initWithFrame: CGRectMake(0.0, 0.0, 1.0, 1.0)];
webView.delegate = self;
self.playerView = webView;
[webView release];

Then load your NSURLRequest.

NSURLRequest *request = [[NSURLRequest alloc] initWithURL: [NSURL URLWithString: myMP3URL] cachePolicy: NSURLRequestUseProtocolCachePolicy timeoutInterval: myTimeoutValue];
[self.playerView loadRequest: request];
[request release];

That’s it!  When the request starts downloading the player with full controls will launch and your screen will look the the picture below.

Audio player launched from a UIWebView

Check out the web view delegate methods to learn more about controlling the behavior of the web view to suite your specific needs.

written by Jess \\ tags: ,

Mar 10

Playing audio with the iPhone Software Development Kit (SDK) should be easy, right? After all this is an iPod – the music jukebox.

After some fruitless experiments I decided to take a shortcut and use the audio support classes in the JigSaw sample application. Copy the entire AudioSupport directory from the JigSaw project. It should contain the following files:

  • Audio_Internal.h
  • AudioFX.h
  • AudioFX.m

 

You can copy and add the files to your project in Xcode in one fell swoop:

  1. Command-click on the Classes folder.
  2. Select Add > Existing Files
  3. Navigate to the JigSaw sample project and select the AudioSupport directory. Click Add.
  4. Check the “Copy items” checkbox. Click Add.
  5. Now you should have a new folder called AudioSupport under Classes in your project.

 

You also need to add the AudioToolbox framework

  1. Command-click on the Linked Frameworks folder inside the Frameworks folder in your Xcode project.
  2. Select Add > Existing Frameworks
  3. Navigate to the AudioToolbox.framework folder. Click Add. If you can’t immediately find this file, try this location: /Developer/Platforms/AspenSimulator.platform/Developer/SDKs/AspenSimulator1.2.sdk/System/Library/Frameworks
  4. Click Add again.

 

Your project should now build without any errors.

 

Of course you need an audio file to play. The iPhone is capable of playing a large number of audio file formats [link to dev center]. But to start with I recommend that you select a file that is known to work. Let’s go back to the JigSaw sample application again.

  1. Command-click on the Resources folder in your Xcode project.
  2. Select Add > Existing Files
  3. Navigate to the Sounds directory in the JigSaw project. Select one of the sound files. (They have the .caf filename extension.)
  4. Click Add.
  5. Check the “Copy items” checkbox. Click Add.

 

Now we need some code to play the audio file.

 

Add this at the top of your file:

#import "AudioFX.h"

 

Then use this code where you want to play the audio file:

AudioFX *audio = [[AudioFX alloc] initWithPath:@"Completed.caf"];
[audio play];

 

“Completed.caf” is the name of the audio file that you added as a resource to your project.

The above code is simple and to the point, but it has a few problems. One obvious one is that an AudioFX object is allocated but never released. Adding [audio release]; just after [audio play]; will blow up because the play method is asynchronous: it returns immediately. And releasing the AudioFX object while it’s playing is not very nice.

Another problem is that the code allocates a new AudioFX object each time that audio file is played. If you are going to play the same audio more than once, you should create the AudioFX object once in the init method and use it throughout the life of the app. You can then safely release the object in the dealloc method. This also solves problem number one.

 

Here’s the final code, used in a UIController:

- (id)init
{
  if (self = [super init]) {
    // Initialize your view controller.
    self.title = @"Play Audio"; 

audio = [[AudioFX alloc] initWithPath:@"Completed.caf"];
  }
  return self;
}

- (void)dealloc
{
  [audio dealloc];
  [super dealloc];
}

- (void)viewDidAppear:(BOOL)animated
{
  [audio play];
}

 

UPDATE: This code example is outdated. Please see the SoundEffect class used in the BubbleLevel, GLPaint and Metronome projects instead. The interface is very similar, but instead of initWithPath use initWithContentsOfFile.

written by Nick \\ tags: