Chapter 18
Multimedia

Key Skills & Concepts

• Playing system sounds

• Playing songs

• Using the Media Player to interact with a device’s multimedia

• Playing video

Up until the release of the iOS 3.0, the iPhone was a difficult platform for developing multimedia applications. The capabilities were there, but you had to resort to using low-level C APIs to program audio and video. And as for the multimedia on a device that was placed there by iTunes? Forget it, off limits. Any media you wished playing in your application had to either be packaged as part of your application or be streamed from a server. That restriction changed with iOS 3.0; now you can access and play a user’s audio iTunes multimedia, making the iPhone and iPod touch the most programmable portable music players ever released. The vast majority of iOS devices currently in use are running iOS 3.0 or later, so except for a very unusual application, you can assume at least iOS 3.0 in your build settings and feel free to write your application to take advantage of the new APIs.

In this chapter, you explore the basic multimedia capabilities of the iPhone and iPod touch. You first learn how to play system sounds and longer sounds. You then move to the Media Player framework, where you use the framework to select and play a user’s iTunes audio multimedia. After learning to play iTunes media, you then learn how to play a video using the Media Player framework’s video player.

Playing Sounds

Playing short sounds on an iPhone or iPod touch is easy. Simply load the song as a system sound, obtain the sound’s id, and use the AudioServicesPlaySystemSound method to play the sound. Playing a longer sound using the AVAudioPlayer is not difficult, but a little more involved. However, there is one important limitation you must realize when using sound on your device using the AudioServicesPlaySystemSound function or AVAudioPlayer: Any media you play must be packaged as part of your application or must be streamed from a server. So, although these two classes are good for adding sound to your application or for developing an interface to a server that streams multimedia, they are not good classes for developing a music player. Instead, you should use the Media Player Framework, covered later in this chapter.

AudioServicesPlaySystemSound

The AudioServicesPlaySystemSound function plays a short system sound. Although security restrictions prevent your application from playing a device’s OS system sounds, you can load and play your own short (30 seconds or less) sounds and play them using this function.

The AudioServicesPlaySystemSound function can only play a sound with the following format: .caf, .aif, or .wav. The sound plays at whatever audio level the device is set to, and the sound plays immediately upon its id being passed to the function. There is no pausing, rewinding, fast-forwarding, or other sound manipulation functionality. You load a sound, and the function plays it.

void AudioServicesPlaySystemSound (SystemSoundID inSystemSoundID);

The function takes a SystemSoundID as a parameter. A SystemSoundID is an unsigned integer that uniquely identifies the sound. You obtain a SystemSoundID by loading a sound with the AudioServicesCreateSystemSoundID function.

OSStatus AudioServicesCreateSystemSoundID (CFURLRef inFileURL, SystemSoundID * outSystemSoundID);

The AudioServicesCreateSystemSoundID function takes a reference to the file’s URL and the SystemSoundID to assign the value to. A CFURLRef is simply a lower-level pointer to a URL. You can ignore creating a CFURL (what the CFURLRef points to) and instead cast an NSURL as a CFURLRef. After obtaining a sound’s URL, you pass it to the create system sound function. It assigns the value to the system sound ID variable you defined; you pass that ID to the system sound player function; and it plays the sound.

TIP
You can use the AudioServicesPlaySystemSound to vibrate a user’s iPhone. Pass the kSystemSoundID_Vibrate identifier constant to the function. All versions of the iPhone can vibrate, but only the latest 4th-generation iPod touch is capable of vibrating; this code does not do anything on an earlier iPod touch.

AVAudioPlayer and AVAudioPlayerDelegate

The AVAudioPlayer plays sounds. The audio player does not have the limitations of the AudioServicesPlaySystemSound function. It can play any length sound, loop a sound, and play multiple sounds at the same time; it also allows control over a sound’s volume. Methods you might use include prepareToPlay, play, pause, and stop. Each method’s functionality should be intuitive. Notice that prepareToPlay and play return a BOOL, so you can evaluate if the call was successful.

-(BOOL)prepareToPlay
-(BOOL)play
-(void)pause
-(void)stop

You can initialize an AVAudioPlayer with data or a URL. The initWithData:error: function initializes an audio player using data encapsulated in an NSData object. The initWithContentsOfURL:error: initializes an audio player using the sound file referenced by the URL. That sound file can be in your application’s bundle, or it can be a resource on a server and streamed. If it is streamed, note that the prepareToPlay method discussed previously takes more importance, as it buffers the content and helps lessen a user’s wait when playing an external resource.

-(id)initWithData:(NSData *) data error:(NSError **) outError
-(id)initWithContentsOfURL:(NSURL *) url error:(NSError **) outError

Properties you might use include the currentTime, data, delegate, duration, playing, volume, and numberOfLoops. The currentTime property returns the playback in seconds as an NSTimeInterval. The duration property returns a sound’s duration in seconds as an NSTimeInterval. The volume returns the player’s playback gain as a float between 0.0 and 1.0. The playing property returns a BOOL, while the numberOfLoops property returns an unsigned int. There are also more advanced properties, such as numberOfChannels and peakPowerForChannel. For a more complete listing of AVAudioPlayer’s properties and methods, refer to the AVAudioPlayer Class Reference.

An AVAudioPlayer’s delegate property refers to an audio player’s AVAudioPlayerDelegate protocol. As with all protocols, you implement a custom class that adopts the protocol. Protocol methods you might implement are listed in Table 18-1.

Image

Table 18-1 AVAudioPlayerDelegate Methods

Try This
Playing a Sound and an MP3

1. Create a new View-based Application and name it AVPlayer.

2. From the sample code Resources folder, add the mp3, charleston1925_64kb.mp3, to the application’s Resources folder. Also add the burp_2.aif file.

3. Add the AudioToolbox.framework to the application’s frameworks. Also add the AVFoundation.framework. Remember, you can easily add a framework by control-clicking one of the frameworks already in your project (e.g., UIKit) and selecting Show In Finder. Then drag and drop the new frameworks from the Finder into the Frameworks folder in the project window. Be sure to uncheck the Copy Items Into Destination Group Folder option.

4. Open AVPlayerViewController.h and import the AudioToolbox and AVFoundation header files (Listing 18-1). Have the class adopt the AVAudioPlayerDelegate protocol.

Listing 18-1 AVPlayerViewController.h

#import <UIKit/UIKit.h>
#import <AudioToolbox/AudioToolbox.h>
#import <AVFoundation/AVFoundation.h>
@interface AVPlayerViewController : UIViewController
<AVAudioPlayerDelegate> {
SystemSoundID burpSoundID;
AVAudioPlayer * player;
}
- (IBAction) playSound: (id) sender;
- (IBAction) playSong: (id) sender;
@end

5. Add a SystemSoundID as a variable to AVPlayerViewController; name it burpSoundID. Also add an AVAudioPlayer as a variable and name it player.

6. Add two IBActions to AVPlayerViewController named playSound and playSong. Do not forget to add the actions to AVPlayerViewController’s header and implementation (Listing 18-2). Don’t implement the actions yet; you do that in Step 9. Save the application.

Listing 18-2 AvplayerViewController.m

#import "AVPlayerViewController.h"
@implementation AVPlayerViewController
-(void) viewDidLoad {
  AudioServicesCreateSystemSoundID((CFURLRef)
         [NSURL fileURLWithPath:
           [[NSBundle mainBundle] pathForResource:@"burp_2"
           ofType:@"aif"]], &burpSoundID);
}
-(IBAction) playSound: (id) sender {
  AudioServicesPlaySystemSound (burpSoundID);
}
-(IBAction) playSong: (id) sender {
  NSError *error = nil;
  player = [[AVAudioPlayer alloc] initWithContentsOfURL:
        NSURL fileURLWithPath:[[NSBundle mainBundle]
        pathForResource: @"charleston1925_64kb"
        ofType:@"mp3"]] error:&error];
  player.delegate = self;
  if(error != NULL) {
     NSLog([error description]);
    [error release];
  }
  [player play];
}
-(void) audioPlayerDidFinishPlaying:
        (AVAudioPlayer *) theplayer successfully:(BOOL)flag {
  [theplayer release];
}
-(void)dealloc {
  [player release];
  [super dealloc];
}
@end

7. Open AVPlayerViewController.xib in Interface Builder and add two buttons. Connect Touch Up Inside for one button to playSound and for the other button to playSong. Label both buttons appropriately.

Image

Figure 18-1 The finished application in the iPhone Simulator

8. Save and exit Interface Builder.

9. Implement playSound, playSong, and viewDidLoad. Also implement the audioPlayerDidFinishPlaying:successfully: method from the AVAudioPlayerDelegate protocol.

10. Click Run. Tap the button connected to playSong to begin playing the song. After the song starts playing, tap the button connected to playSound, and the iPhone Simulator belches simultaneously (Figure 18-1).

This task is straightforward; first, you loaded the sound and obtained its id. As system sounds are 30 seconds or less, loading it into memory and keeping it there should not tax your device’s memory. Notice you do not load the longer song into memory until you actually play it in the playSong method, as it takes more memory.

You initialize the system sound in the viewDidLoad method. Don’t let the one line of code be intimidating; it’s actually doing something quite simple. It gets the path to the file, creates an NSURL using the path, casts it as a CFURLRef, and creates a system sound from the resource.

The playSong method creates a new AVAudioPlayer instance every time the application calls it. If an error occurs, the method logs the error; otherwise, it plays the song. When the song is finished playing, it calls the audioPlayerDidFinishPlaying:successfully: method and releases the player.

NOTE
The mp3 song is entitled The Charleston, and is a digital copy of the 1925 recording. It was obtained from the Internet Archive’s 78RPMS & Cylinder Recordings Collection. The mp3 is licensed under the Creative Commons Commercial license. The burp_2.aif sound is also public domain.

Media Player Framework

Before iOS 3.0, a device’s multimedia loaded by iTunes was off limits for application developers. The Media Player framework released with OS 3.0 removes that restriction by providing several classes that work with a device’s iTunes-loaded multimedia.

NOTE
Running the Media Player audio application in this chapter requires installing the application on an iPod touch or iPhone running iOS 3.0 or later. The sample application won’t run correctly in the iPhone simulator.

Media Data Classes

An MPMediaLibrary class represents a device’s multimedia library loaded from iTunes. An MPMediaItem object represents every multimedia item in the media library. The MPMediaItem class contains metadata such as title, artist, and length about a media item.

When working with the MPMediaLibrary, you usually execute an MPMediaQuery that returns an MPMediaItemCollection. Although the Media Player framework offers several methods for accessing a device’s media programmatically, another way is by using an MPMediaPickerController. An MPMediaPickerController is a class that presents a view much like the current iPod application. A user can then select one or more media items, and the picker returns an MPMediaItemCollection.

After selecting the media items to play, you pass them to an MPMusicController to play. The MPMusicController class is responsible for playing music, rewinding, forwarding, and other playback functionality.

MPMediaItem and MPMediaItemCollection

An MPMediaItem encapsulates a single audio multimedia element in a device’s iTunes multimedia collection. The MPMediaItem contains one method for obtaining a media item’s properties, the valueForProperty: method.

-(id)valueForProperty:(NSString *)property

The valueForProperty: method takes a constant representing the property for which to obtain a value. Notice that the method returns an id; this means the function’s return value is tied to the property constant passed to the method. Listing 18-3 lists the properties you might pass to the valueForProperty: method.

Listing 18-3 Media Item properties

NSString *const MPMediaItemPropertyPersistentID;
NSString *const MPMediaItemPropertyAlbumTrackNumber;
NSString *const MPMediaItemPropertyAlbumTrackCount;
NSString *const MPMediaItemPropertyDiscNumber;
NSString *const MPMediaItemPropertyDiscCount;
NSString *const MPMediaItemPropertyArtwork;
NSString *const MPMediaItemPropertyLyrics;
NSString *const MPMediaItemPropertyPodcastTitle;

You can also use user-defined properties. These properties have a variable value, depending upon a user’s multimedia collection use. Listing 18-4 lists the user-defined properties.

Listing 18-4 User-defined properties

NSString *const MPMediaItemPropertyPlayCount;
NSString *const MPMediaItemPropertySkipCount;
NSString *const MPMediaItemPropertyRating;
NSString *const MPMediaItemPropertyLastPlayedDate;

The MPMediaItemCollection class is a collection of media items. You obtain a collection’s media items through its items property. This property returns an NSArray of MPMediaItem objects. Other properties include count, mediaTypes, and the representativeItem properties. The count property returns a count of the collection’s multimedia items as an NSUInteger. You use the mediaTypes property and the representativeItem property to obtain a collection’s media types.

Selecting Multimedia

Before playing multimedia, a user must select the media items. The easiest way to allow a user to do this is through the MPMediaPickerController. Similar to the UIImagePickerController, it controls a view that is hidden from developers. Also, like the image picker, it enables you to define a delegate for the media picker. That delegate, the MPMediaPickerControllerDelegate, responds to a user’s interaction with the media picker.

MPMediaPickerController

The MPMediaPickerController class manages the media picker view. As with the UIImagePickerController, you present this controller’s view as a modal view that overlays the currently displaying view.

[self presentModalViewController:mediaController animated:YES];

You can initialize a media picker to only display certain media types using the initWithMediaTypes method. This method takes an MPMediaType; valid types are MPMediaTypeMusic, MPMediaTypePodcast, MPMediaTypeAudioBook, and MPMediaTypeAnyAudio. Notice there is no MPMediaTypeVideo; you cannot select or play an iTunes-loaded video on devices using MPMediaPickerController. (Note that iPhone 3gs and later devices are capable of selecting video using the UIImagePickerController.)

You can also initialize a media picker to allow a user to select multiple items by setting the allowsPickingMultipletems property to YES. If you don’t set this property, its default value is NO.

MPMediaPickerControllerDelegate

A media picker requires an MPMediaPickerControllerDelegate if it is to do anything interesting. An MPMediaPickerControllerDelegate has two methods you implement when adopting this protocol: the mediaPicker:didPickMediaItems: and mediaPickerDidCancel: methods.

(void) mediaPicker: (MPMediaPickerController *)mediaPicker
didPickMediaItems: (MPMediaItemCollection *) mediaItemCollection
(void) mediaPickerDidCancel: (MPMediaPickerController *) mediaPicker

The mediaPickerDidCancel: method responds to a user canceling the media picker, while the mediaPicker:didPickMediaItems: method responds to a user clicking the Done button after selecting media in the media picker.

Playing Multimedia: MPMusicPlayerController

A user will probably want to play a multimedia object after selecting it. You play an MPMediaItemsCollection using the MPMusicPlayerController. This class is responsible for playing audio media items. There are two player types to choose from: an iPodMusicPlayer and an applicationMusicPlayer. An iPodMusicPlayer replaces an iPod’s state, while an applicationMusicPlayer is independent of a user’s iPod. For instance, if you are using iPodMusicPlayer and set it to shuffle mode, the user’s iPod application will be in shuffle mode the next time he or she uses it. When using the applicationMusicPlayer, this does not happen because it does not modify the iPod’s state.

You initialize a music player’s media collection using the setQueueWithQuery:, setQueueWithItems:, or setQueueWithItemCollection: method. The set with query method takes an MPMediaQuery, the set with items method takes an NSArray of MPMediaItems, and the set with collection method takes an MPMediaItemCollection.

You can initialize a media player’s state using the repeatMode and shuffleMode properties. If you do not set these properties, your player’s state is the user’s iPod application state. Other properties you can use to obtain information about a media player are the nowPlayingItem, currentPlaybackTime, and playbackState properties. The nowPlayingItem property is an MPMediaItem that represents the currently playing media item. The currentPlaybackTime is an NSInterval containing the now-playing item’s playback location in seconds. The playbackState property returns the music player’s playback state as an MPMusicPlaybackState. Valid values for these states include MPMusicPlaybackStateStopped, MPMusicPlaybackStatePlaying, MPMusicPlaybackStatePaused, MPMusicPlaybackStateInterrupted, MPMusicPlaybackState SeekingForward, and MPMusicPlaybackStateSeekingBackward.

Methods you can use to control a media player are play, pause, stop, beginSeekingForward, beginSeekingBackward, endSeeking, skipToNextItem, skipToBeginning, and skipToPreviousItem. The play, pause, and stop methods should be self-explanatory. The beginSeekingForward and beginSeekingBackward methods are for fast-forwarding and rewinding, respectively, while the endSeeking method stops fast-forwarding or rewinding. The skipToNextItem, skipToPreviousItem, and skipToBeginning methods should also be self-explanatory.

The MPMusicPlayerController has no delegate protocol for responding to its events. Instead it provides two notifications: the MPMusicPlayerControllerPlaybackStateDidChange and MPMusicPlayerControllerNowPlayingItemDidChange notifications. The first notification posts when an MPMusicPlayerController’s playback state changes. The second notification posts when an MPMusicPlayerController’s now-playing media item changes. By using these two notifications, your application can respond to the media player’s state, as the next example task illustrates.

Try This
Using the Media Picker and Media Player

1. Create a new View-based Application; name it iPodSongs.

2. Add the Media Player framework (MediaPlayer.framework) to the project’s frameworks.

3. Add the player_stop.png, player_pause.png, player_play.png, player_rew.png and player_ fwd.png images from the sample code Resources folder to the project’s Resources folder.

4. Open iPodSongsViewController and add four IBOutlets: three for UILabels and one for a UIView. Name the outlets currentTitle, currentArtist, currentLengthInSeconds, and volumeView.

5. Add an IBAction named selectSongs. Don’t implement selectSongs yet. Add an IBAction named changeState (Listings 18-5 and 18-6).

Listing 18-5 iPodSongsViewController.h

#import <UIKit/UIKit.h>
#import <MediaPlayer/MediaPlayer.h>
@class MyMediaPickerDelegate;
@interface iPodSongsViewController : UIViewController {
  MyMediaPickerDelegate * mediaControllerDelegate;
  MPMediaLibrary * mediaLib;
  UILabel * currentTitle;
  UILabel * currentLengthInSeconds;
  MPMusicPlayerController * player;
  UIView * volumeView;
  MPVolumeView * mpVolumeView;
}
@property (nonatomic, retain) MyMediaPickerDelegate
*mediaControllerDelegate;
@property (nonatomic, retain) MPMediaLibrary * mediaLib;
@property (nonatomic, retain) IBOutlet UILabel * currentTitle;
@property (nonatomic, retain) IBOutlet UILabel * currentArtist;
@property (nonatomic, retain) IBOutlet UILabel * currentLengthInSeconds;
@property (nonatomic, retain) MPMusicPlayerController * player;
@property (nonatomic, retain) IBOutlet UIView * volumeView;
- (IBAction) selectSongs : (id) sender;
- (IBAction) changeState: (id) sender;
@end

Listing 18-6 iPodSongsViewController.m

#import "iPodSongsViewController.h"
#import "MyMediaPickerDelegate.h"
@implementation iPodSongsViewController
@synthesize mediaControllerDelegate;
@synthesize mediaLib;
@synthesize currentTitle;
@synthesize currentArtist;
@synthesize currentLengthInSeconds;
@synthesize player;
@synthesize volumeView; int currentItem = 0;
-(void) viewDidLoad {
  [MPMediaLibrary defaultMediaLibrary];
  [[NSNotificationCenter defaultCenter] addObserver:self
        selector: @selector(songsPicked:)
        name:@"SongsPicked" object:nil];
  [[NSNotificationCenter defaultCenter] addObserver:self
        selector: @selector(songChanged)
        name: @"MPMusicPlayerControllerNowPlayingItemDidChangeNotification"
        object:nil];
  mpVolumeView = [[MPVolumeView alloc] init];
  [mpVolumeView setFrame:[self.volumeView bounds]];
  [self.volumeView addSubview:mpVolumeView];
}
-(void) songsPicked: (NSNotification *) notification {
  player = [MPMusicPlayerController applicationMusicPlayer];
  player.repeatMode = MPMusicRepeatModeNone;
  player.shuffleMode = MPMusicShuffleModeOff;
  [player setQueueWithItemCollection:(MPMediaItemCollection *)
  [notification object]];
  [player beginGeneratingPlaybackNotifications];
  [player play];
}
-(void) songChanged {
  MPMediaItem * tempMediaItem = (MPMediaItem *)player.nowPlayingItem;
  [self.currentTitle setText:[tempMediaItem valueForProperty:
        MPMediaItemPropertyTitle]];
  [self.currentArtist setText: [tempMediaItem valueForProperty:
        MPMediaItemPropertyArtist]];
  [self.currentLengthInSeconds setText: [NSString stringWithFormat:@"%i",
  [tempMediaItem valueForProperty:MPMediaItemPropertyPlaybackDuration]]];
}
-(IBAction) changeState: (id) sender {
  NSInteger num = ((UIControl*)sender).tag;
  switch (num) {
    case 1: [player pause]; break;
     case 2: [player play]; break;
     case 3: [player stop]; break;
     case 4: [player skipToPreviousItem]; break;
    case 5: [player skipToNextItem]; break;
  }
}
-(IBAction) selectSongs: (id) sender {
  MPMediaPickerController * mediaController =
         [[MPMediaPickerController alloc] init];
  mediaController.allowsPickingMultipleItems = YES;
  mediaController.delegate = [[MyMediaPickerDelegate alloc] init];
  [self presentModalViewController:mediaController animated:YES];
}
-(void)dealloc {
  [[NSNotificationCenter defaultCenter] removeObserver:self];
  [mediaControllerDelegate release];
  [player stop];
  [mpVolumeView release];
  [super dealloc];
}
@end

Image

Figure 18-2 The application’s canvas in Interface Builder

6. Save your changes and open iPodSongsViewController.xib. Create a button and connect its Touch Up Inside event to the selectSongs method.

7. Create five buttons and add an image to each button (Figure 18-2). Assign each button a unique tag in the Inspector. Connect the five buttons to the changeState action.

8. Add three labels to the canvas. Also add a UIView to the view’s canvas and connect it to the volumeView outlet.

9. Connect the three labels to the currentTitle, currentArtist, and currentLengthInSeconds outlets.

10. Exit Interface Builder and return to iPodSongsViewController.

11. Create a new Objective-C class that is a subclass of NSObject named MyMediaPickerDelegate and have it adopt the MPMediaPickerControllerDelegate protocol (Listings 18-7 and 18-8). Don’t forget to import the MediaPlayer header file.

Listing 18-7 MyMediaPickerDelegate.h

#import <Foundation/Foundation.h>
#import <MediaPlayer/MediaPlayer.h>
@interface MyMediaPickerDelegate : NSObject
<MPMediaPickerControllerDelegate> {
}
@end

Listing 18-8 MyMediaPickerDelegate.m

#import "MyMediaPickerDelegate.h"
@implementation MyMediaPickerDelegate
-(void) mediaPicker: (MPMediaPickerController *) mediaPicker
         didPickMediaItems: (MPMediaItemCollection *)
mediaItemCollection {
  NSArray * mediaItems = [mediaItemCollection items];
  NSEnumerator * enumer = [mediaItems objectEnumerator];
  id myObject;
  while (myObject = [enumer nextObject]) {
    MPMediaItem * tempMediaItem = (MPMediaItem *) myObject;
    NSLog(@"Title: %@", [tempMediaItem valueForProperty:
            MPMediaItemPropertyTitle]);
    NSLog(@"id: %@", [tempMediaItem
            valueForProperty:MPMediaItemPropertyArtist]);
    NSLog(@"id: %i", [tempMediaItem valueForProperty:
            MPMediaItemPropertyPersistentID]);
    NSLog(@"-----------------------");
  }
  [mediaPicker.parentViewController dismissModalViewControllerAnimated
:YES];
  [mediaPicker release];
  [[NSNotificationCenter defaultCenter] postNotificationName:
            @"SongsPicked" object: mediaItemCollection];
}
-(void) dealloc {
  [[NSNotificationCenter defaultCenter] removeObserver: self];
  [super dealloc];
}
@end

12. Open iPodSongsViewController and add properties for the MyMediaPickerDelegate, MPMediaLibrary, and MPMusicPlayerController. Name the properties mediaPickerDelegate, mediaLib, and player.

13. Add a variable for MPVolumeView named mpVolumeView.

14. Be certain that iPodSongsViewController imports MyMediaPickerDelegate, and do not forget to synthesize the three properties.

15. Return to MyMediaPickerDelegate and implement the didPickMediaItems delegate methods.

16. Return to iPodSongsViewController and implement the selectSongs and changeState methods.

17. Implement the viewDidLoad method and the songsPicked method, paying particular attention to the NSNotification.

18. The application runs, displays the controls, and allows selecting multimedia. Click Select Songs, choose a few songs from your iPod and then tap Done. The music player starts playing and displays the currently playing item in the labels (Figure 18-3).

Image

Figure 18-3 The application running on an iPod touch

When you think about how long it took us to write this application, it is perhaps the coolest application in the entire book. The user interface isn’t pretty, but the application works well as a simple music player. In fact, with a little polish you might even like it better than the iPod touch’s Music application because you can quickly select all the songs you’re interested in and then play them.

In the viewDidLoad method, notice the code for initializing a volume control. Although the MPVolumeView class provides a sizeThatFits method, it is better to do it the way presented here. Simply add a UIView as a subview, size it to the desired size, and then add the volume view to the subview. Easy, and it is guaranteed to size correctly.

mpVolumeView = [[MPVolumeView alloc] init];
[mpVolumeView setFrame:[self.volumeView bounds]];
[self.volumeView addSubview:mpVolumeView];

As the MPMediaPlayerController does not have a delegate, you made the iPodSongsViewController a listener for the MPMusicPlayerControllerNowPlayingItemDid ChangeNotification event. When a player’s now-playing item changes, the player fires this event. You set the songChanged method as the method called when the event fires. After being called, the songChanged method obtains the now-playing item and updates the view’s labels.

The changeState method handles any one of the five control buttons being tapped. Because each button has a different tag, the method can distinguish which button fires the Touch Up Inside event and handle the message appropriately. In a real-world application, you would disable the buttons as appropriate. For instance, when paused, the Pause button should be disabled and only the Play button enabled. Here, however, the extra code would have detracted from the example’s intended purpose.

In addition to the player’s notification, you created your own notification in MyMediaPickerDelegate. Upon selecting an MPMediaItemCollection using the media picker, this notification is fired. The songsPicked method in iPodSongsViewController responds to this notification. The method takes the NSNotification as a parameter; remember notifications can contain an object. Here, that object is the selected MPMediaItemCollection. After initializing the media player with the collection, the songsPicked method tells the media player to begin generating playback notifications and then starts playing.

MPMoviePlayerController

The MPMoviePlayerController plays video bundled as part of your application. It can also stream video from a server. However, despite its name, it cannot use video loaded on a user’s device by iTunes.

In versions of iOS prior to 3.2, MPMoviePlayerController always presents a modal, full-screen video when it plays and you can only minimally modify its appearance using properties. With iOS 3.2 and later, MPMoviePlayerController can also play video in a portion of the screen or even in portrait orientation. This is particularly helpful on the iPad, where the display is large enough to display video in a portion of the screen alongside other information. If you want to remain compatible with devices running iOS 3.1 and earlier, then the player runs full screen and you can change the movie player’s background color, a movie’s scaling, and the controls presented to the user when playing a movie. You change a movie’s background color using the backgroundColor property. You change a movie’s scaling using the scalingMode property, and you change the controls presented using the movieControlMode property.

The backgroundColor property changes the movie player’s background color when playing a movie. For instance, when you watch a video using the Video application on an iPod touch, the background is black. If you wish, you can change that color using this property.

The scalingMode property changes a movie’s scaling. Valid values for scalingMode are MPMovieScalingModeNone, MPMovieScalingModeAspectFit, MPMovieScalingModeAspect Fill, and MPMovieScalingModeFill.

The movieControlMode property determines the visible controls when a movie plays. For instance, when playing a movie using the Video application, if you tap the screen, it presents a control showing the volume, a control showing the location in the video, a scaling control, and a Done button. You can modify which control a player presents using this property. Valid values are MPMovieControlModeDefault, MPMovieControlModeVolumeOnly, and MPMovie ControlModeHidden.

You initialize a movie player using the initWithContentURL: method. This method takes an NSURL to the movie’s location. This location must be within your application’s sandbox or available via an Internet server (it’s a URL). After initializing the player, you call the prepareToPlay and play methods. If you wish to be notified that the player has stopped playing, you register as a notification listener, listening for the player’s MPMoviePlayerPlay backDidFinishNotification notification.

Try This
Play a Video

1. Create a new View-based Application called MoviePlayer. Add the Media Player framework to the application.

2. Add the movie short.3gp from the sample code Resources folder to the project’s Resources folder.

3. Open MoviePlayerController and add an MPMoviePlayerController as a property; name it moviePlayer (Listings 18-9 and 18-10). Do not forget to import the MediaPlayer.

Listing 18-9 MovieplayerViewController.h

#import <UIKit/UIKit.h>
#import <MediaPlayer/MediaPlayer.h>
@interface MovieplayerViewController : UIViewController {
  MPMoviePlayerController * movieplayer;
}
@property (nonatomic, retain) MPMoviePlayerController * movieplayer;
-(IBAction) playMovie: (id) sender;
-(void) playingDone;
@end

Listing 18-10 MovieplayerViewController.m

#import "MovieplayerViewController.h"
@implementation MovieplayerViewController
@synthesize movieplayer;
-(void) viewDidLoad {
  [[NSNotificationCenter defaultCenter] addObserver:self selector:
          @selector (playingDone)
          name:MPMoviePlayerPlaybackDidFinishNotification object:nil];
}
-(IBAction) playMovie: (id) sender {
  movieplayer = [[MPMoviePlayerController alloc] initWithContentURL:
           [NSURL fileURLWithPath:[[NSBundle mainBundle]
           pathForResource:@"short" ofType:@"3gp"]]];
  // Only iOS 3.2 and above respond to the loadState selector
  if ([movieplayer respondsToSelector:@selector(loadState)]) {
     // Our application runs in portrait orientation,
    // so on iOS 3.2 and later, the movie player will also
    // display in portrait mode by default.
    // The next few lines change the 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
    [[movieplayer view] setFrame:CGRectMake(0, 0, 480, 320)];
    // Add movie player as subview
    [[self view] addSubview:[movieplayer view]];
    // Play the movie
     [movieplayer play];
  } else {
    // Prior to iOS 3.2, this was enough to play the movie correctly
     [movieplayer play];
  }
}
-(void) playingDone {
  [movieplayer release];
  movieplayer = nil;
}
-(void)dealloc {
  [[NSNotificationCenter defaultCenter] removeObserver:self];
  [movieplayer release]; [super dealloc];
}
@end

4. Add a method called playingDone and an IBAction called playMovie to MoviePlayerView Controller. Implement both methods the same as in Listing 18-10. Also implement the viewDidLoad method.

5. Save your changes and open MovieplayerViewController.xib.

6. Add a button to the canvas and connect it to the playMovie action.

7. Save your changes and click Run. Click the play movie button and an old B&W movie will play in landscape orientation.

Like the music player, you register to listen to the finished playing event in the viewDidLoad method. Notice that unlike the previous task, you didn’t place the event’s name in quotations. This omission was intentional. The event and the event’s NSString name are the same, and you can use either. The viewDidLoad method also initializes the movie player with the short.3gp movie’s location. In a real-world application, loading a movie when a view loads is probably not a good idea, as movies are usually quite large.

When a user taps the button, the movie player begins playing the movie in landscape mode (Figure 18-4). Upon tapping the Done button, the user returns to the application’s view in portrait mode. The controller also receives the notification that the player stopped playing and releases the player.

Image

Figure 18-4 The application running in iPhone Simulator

Summary

In this chapter, you learned how to play system sounds, sounds, a device’s audio multimedia loaded from iTunes, and video. You first played a system sound. System sounds are 30 seconds or less and are designed as audible alerts. You also played an MP3 using the AVAudioPlayer. The AVAudioPlayer is for playing longer sounds, including your application’s multimedia, such as MP3s. However, the audio player is limited to sounds bundled with your application or streamed from a server. It cannot play iTunes-loaded multimedia.

The newer media player can play iTunes multimedia, provided it is audio. You learned how to use the media player and how to use a controller to select music and a player to play it. After learning about the Media Player framework’s newer features, you then learned about its movie player. Despite being part of the Media Player framework, you can only play video bundled as part of your application or video streamed from a server.

In the next chapter we will return to the MoviePlayer example and look at how to play video in a subview on the iPad’s larger display.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset