/* |
|
File: MyMovieViewController.m |
Abstract: A UIViewController controller subclass that implements a movie playback view. |
Uses a MyMovieController object to control playback of a movie. |
Adds and removes an overlay view to the view hierarchy. Handles button presses to the |
'Close Movie' button in the overlay view. |
Adds and removes a background view to hide any underlying user interface controls when playing a movie. |
Gets user movie settings preferences by calling the MoviePlayerUserPref methods. Apply these settings to the movie with the MyMovieController singleton. |
|
Version: 1.5 |
|
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple |
Inc. ("Apple") in consideration of your agreement to the following |
terms, and your use, installation, modification or redistribution of |
this Apple software constitutes acceptance of these terms. If you do |
not agree with these terms, please do not use, install, modify or |
redistribute this Apple software. |
|
In consideration of your agreement to abide by the following terms, and |
subject to these terms, Apple grants you a personal, non-exclusive |
license, under Apple's copyrights in this original Apple software (the |
"Apple Software"), to use, reproduce, modify and redistribute the Apple |
Software, with or without modifications, in source and/or binary forms; |
provided that if you redistribute the Apple Software in its entirety and |
without modifications, you must retain this notice and the following |
text and disclaimers in all such redistributions of the Apple Software. |
Neither the name, trademarks, service marks or logos of Apple Inc. may |
be used to endorse or promote products derived from the Apple Software |
without specific prior written permission from Apple. Except as |
expressly stated in this notice, no other rights or licenses, express or |
implied, are granted by Apple herein, including but not limited to any |
patent rights that may be infringed by your derivative works or by other |
works in which the Apple Software may be incorporated. |
|
The Apple Software is provided by Apple on an "AS IS" basis. APPLE |
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION |
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS |
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND |
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. |
|
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL |
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, |
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED |
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), |
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE |
POSSIBILITY OF SUCH DAMAGE. |
|
Copyright (C) 2014 Apple Inc. All Rights Reserved. |
|
|
*/ |
|
#import "MyMovieViewController.h" |
#import "MoviePlayerUserPrefs.h" |
|
CGFloat kMovieViewOffsetX = 20.0; |
CGFloat kMovieViewOffsetY = 20.0; |
|
@interface MyMovieViewController (OverlayView) |
|
-(void)addOverlayView; |
-(void)removeOverlayView; |
-(void)resizeOverlayWindow; |
|
@end |
|
@interface MyMovieViewController(MovieControllerInternal) |
-(void)createAndPlayMovieForURL:(NSURL *)movieURL sourceType:(MPMovieSourceType)sourceType; |
-(void)applyUserSettingsToMoviePlayer; |
-(void)moviePlayBackDidFinish:(NSNotification*)notification; |
-(void)loadStateDidChange:(NSNotification *)notification; |
-(void)moviePlayBackStateDidChange:(NSNotification*)notification; |
-(void)mediaIsPreparedToPlayDidChange:(NSNotification*)notification; |
-(void)installMovieNotificationObservers; |
-(void)removeMovieNotificationHandlers; |
-(void)deletePlayerAndNotificationObservers; |
@end |
|
@interface MyMovieViewController (ViewController) |
-(void)removeMovieViewFromViewHierarchy; |
@end |
|
@implementation MyMovieViewController(ViewController) |
|
#pragma mark View Controller |
|
/* Sent to the view controller after the user interface rotates. */ |
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation |
{ |
/* Size movie view to fit parent view. */ |
CGRect viewInsetRect = CGRectInset ([self.view bounds], |
kMovieViewOffsetX, |
kMovieViewOffsetY ); |
[[[self moviePlayerController] view] setFrame:viewInsetRect]; |
|
/* Size the overlay view for the current orientation. */ |
[self resizeOverlayWindow]; |
} |
|
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation |
{ |
/* Return YES for supported orientations. */ |
return YES; |
} |
|
- (BOOL)canBecomeFirstResponder |
{ |
return YES; |
} |
|
- (void)viewDidUnload |
{ |
[self deletePlayerAndNotificationObservers]; |
|
[super viewDidUnload]; |
} |
|
/* Notifies the view controller that its view is about to be become visible. */ |
- (void)viewWillAppear:(BOOL)animated |
{ |
[super viewWillAppear:animated]; |
|
/* Size the overlay view for the current orientation. */ |
[self resizeOverlayWindow]; |
/* Update user settings for the movie (in case they changed). */ |
[self applyUserSettingsToMoviePlayer]; |
} |
|
/* Notifies the view controller that its view is about to be dismissed, |
covered, or otherwise hidden from view. */ |
- (void)viewWillDisappear:(BOOL)animated |
{ |
[super viewWillDisappear:animated]; |
/* Remove the movie view from the current view hierarchy. */ |
[self removeMovieViewFromViewHierarchy]; |
/* Removie the overlay view. */ |
[self removeOverlayView]; |
/* Remove the background view. */ |
[self.backgroundView removeFromSuperview]; |
|
/* Delete the movie player object and remove the notification observers. */ |
[self deletePlayerAndNotificationObservers]; |
} |
|
- (void)dealloc |
{ |
[self setMoviePlayerController:nil]; |
self.imageView = nil; |
self.movieBackgroundImageView = nil; |
self.backgroundView = nil; |
self.overlayController = nil; |
|
} |
|
/* Remove the movie view from the view hierarchy. */ |
-(void)removeMovieViewFromViewHierarchy |
{ |
MPMoviePlayerController *player = [self moviePlayerController]; |
|
[player.view removeFromSuperview]; |
} |
|
#pragma mark Error Reporting |
|
-(void)displayError:(NSError *)theError |
{ |
if (theError) |
{ |
UIAlertView *alert = [[UIAlertView alloc] |
initWithTitle: @"Error" |
message: [theError localizedDescription] |
delegate: nil |
cancelButtonTitle:@"OK" |
otherButtonTitles:nil]; |
[alert show]; |
} |
} |
|
@end |
|
#pragma mark - |
@implementation MyMovieViewController (OverlayView) |
|
|
/* Add an overlay view on top of the movie. This view will display movie |
play states and includes a 'Close Movie' button. */ |
-(void)addOverlayView |
{ |
MPMoviePlayerController *player = [self moviePlayerController]; |
|
if (!([self.overlayController.view isDescendantOfView:self.view]) |
&& ([player.view isDescendantOfView:self.view])) |
{ |
// add an overlay view to the window view hierarchy |
[self.view addSubview:self.overlayController.view]; |
} |
} |
|
/* Remove overlay view from the view hierarchy. */ |
-(void)removeOverlayView |
{ |
[self.overlayController.view removeFromSuperview]; |
} |
|
-(void)resizeOverlayWindow |
{ |
CGRect frame = self.overlayController.view.frame; |
frame.origin.x = round((self.view.frame.size.width - frame.size.width) / 2.0); |
frame.origin.y = round((self.view.frame.size.height - frame.size.height) / 2.0); |
self.overlayController.view.frame = frame; |
} |
|
@end |
|
#pragma mark - |
@implementation MyMovieViewController |
|
@synthesize moviePlayerController; |
|
@synthesize imageView; |
@synthesize movieBackgroundImageView; |
@synthesize backgroundView; |
@synthesize overlayController; |
|
@synthesize appDelegate; |
|
/* Action method for the overlay view 'Close Movie' button. |
Remove the movie view and overlay view from the window, |
dispose the movie object and remove the notification |
handlers. */ |
-(IBAction)overlayViewCloseButtonPress:(id)sender |
{ |
[[self moviePlayerController] stop]; |
|
[self removeMovieViewFromViewHierarchy]; |
|
[self removeOverlayView]; |
[self.backgroundView removeFromSuperview]; |
|
[self deletePlayerAndNotificationObservers]; |
} |
|
/* |
Called by the MoviePlayerAppDelegate (UIApplicationDelegate protocol) |
applicationWillEnterForeground when the app is about to enter |
the foreground. |
*/ |
- (void)viewWillEnterForeground |
{ |
/* Set the movie object settings (control mode, background color, and so on) |
in case these changed. */ |
[self applyUserSettingsToMoviePlayer]; |
} |
|
#pragma mark Play Movie Actions |
|
/* Called soon after the Play Movie button is pressed to play the local movie. */ |
-(void)playMovieFile:(NSURL *)movieFileURL |
{ |
[self createAndPlayMovieForURL:movieFileURL sourceType:MPMovieSourceTypeFile]; |
} |
|
/* Called soon after the Play Movie button is pressed to play the streaming movie. */ |
-(void)playMovieStream:(NSURL *)movieFileURL |
{ |
MPMovieSourceType movieSourceType = MPMovieSourceTypeUnknown; |
/* If we have a streaming url then specify the movie source type. */ |
if ([[movieFileURL pathExtension] compare:@"m3u8" options:NSCaseInsensitiveSearch] == NSOrderedSame) |
{ |
movieSourceType = MPMovieSourceTypeStreaming; |
} |
[self createAndPlayMovieForURL:movieFileURL sourceType:movieSourceType]; |
} |
|
@end |
|
#pragma mark - |
#pragma mark Movie Player Controller Methods |
#pragma mark - |
|
@implementation MyMovieViewController (MovieControllerInternal) |
|
#pragma mark Create and Play Movie URL |
|
/* |
Create a MPMoviePlayerController movie object for the specified URL and add movie notification |
observers. Configure the movie object for the source type, scaling mode, control style, background |
color, background image, repeat mode and AirPlay mode. Add the view containing the movie content and |
controls to the existing view hierarchy. |
*/ |
-(void)createAndConfigurePlayerWithURL:(NSURL *)movieURL sourceType:(MPMovieSourceType)sourceType |
{ |
/* Create a new movie player object. */ |
MPMoviePlayerController *player = [[MPMoviePlayerController alloc] initWithContentURL:movieURL]; |
|
if (player) |
{ |
/* Save the movie object. */ |
[self setMoviePlayerController:player]; |
|
/* Register the current object as an observer for the movie |
notifications. */ |
[self installMovieNotificationObservers]; |
|
/* Specify the URL that points to the movie file. */ |
[player setContentURL:movieURL]; |
|
/* If you specify the movie type before playing the movie it can result |
in faster load times. */ |
[player setMovieSourceType:sourceType]; |
|
/* Apply the user movie preference settings to the movie player object. */ |
[self applyUserSettingsToMoviePlayer]; |
|
/* Add a background view as a subview to hide our other view controls |
underneath during movie playback. */ |
[self.view addSubview:self.backgroundView]; |
|
CGRect viewInsetRect = CGRectInset ([self.view bounds], |
kMovieViewOffsetX, |
kMovieViewOffsetY ); |
/* Inset the movie frame in the parent view frame. */ |
[[player view] setFrame:viewInsetRect]; |
|
[player view].backgroundColor = [UIColor lightGrayColor]; |
|
/* To present a movie in your application, incorporate the view contained |
in a movie player’s view property into your application’s view hierarchy. |
Be sure to size the frame correctly. */ |
[self.view addSubview: [player view]]; |
} |
} |
|
/* Load and play the specified movie url with the given file type. */ |
-(void)createAndPlayMovieForURL:(NSURL *)movieURL sourceType:(MPMovieSourceType)sourceType |
{ |
[self createAndConfigurePlayerWithURL:movieURL sourceType:sourceType]; |
|
/* Play the movie! */ |
[[self moviePlayerController] play]; |
} |
|
#pragma mark Movie Notification Handlers |
|
/* Notification called when the movie finished playing. */ |
- (void) moviePlayBackDidFinish:(NSNotification*)notification |
{ |
NSNumber *reason = [notification userInfo][MPMoviePlayerPlaybackDidFinishReasonUserInfoKey]; |
switch ([reason integerValue]) |
{ |
/* The end of the movie was reached. */ |
case MPMovieFinishReasonPlaybackEnded: |
/* |
Add your code here to handle MPMovieFinishReasonPlaybackEnded. |
*/ |
break; |
|
/* An error was encountered during playback. */ |
case MPMovieFinishReasonPlaybackError: |
NSLog(@"An error was encountered during playback"); |
[self performSelectorOnMainThread:@selector(displayError:) withObject:[notification userInfo][@"error"] |
waitUntilDone:NO]; |
[self removeMovieViewFromViewHierarchy]; |
[self removeOverlayView]; |
[self.backgroundView removeFromSuperview]; |
break; |
|
/* The user stopped playback. */ |
case MPMovieFinishReasonUserExited: |
[self removeMovieViewFromViewHierarchy]; |
[self removeOverlayView]; |
[self.backgroundView removeFromSuperview]; |
break; |
|
default: |
break; |
} |
} |
|
/* Handle movie load state changes. */ |
- (void)loadStateDidChange:(NSNotification *)notification |
{ |
MPMoviePlayerController *player = notification.object; |
MPMovieLoadState loadState = player.loadState; |
|
/* The load state is not known at this time. */ |
if (loadState & MPMovieLoadStateUnknown) |
{ |
[self.overlayController setLoadStateDisplayString:@"n/a"]; |
|
[overlayController setLoadStateDisplayString:@"unknown"]; |
} |
|
/* The buffer has enough data that playback can begin, but it |
may run out of data before playback finishes. */ |
if (loadState & MPMovieLoadStatePlayable) |
{ |
[overlayController setLoadStateDisplayString:@"playable"]; |
} |
|
/* Enough data has been buffered for playback to continue uninterrupted. */ |
if (loadState & MPMovieLoadStatePlaythroughOK) |
{ |
// Add an overlay view on top of the movie view |
[self addOverlayView]; |
|
[overlayController setLoadStateDisplayString:@"playthrough ok"]; |
} |
|
/* The buffering of data has stalled. */ |
if (loadState & MPMovieLoadStateStalled) |
{ |
[overlayController setLoadStateDisplayString:@"stalled"]; |
} |
} |
|
/* Called when the movie playback state has changed. */ |
- (void) moviePlayBackStateDidChange:(NSNotification*)notification |
{ |
MPMoviePlayerController *player = notification.object; |
|
/* Playback is currently stopped. */ |
if (player.playbackState == MPMoviePlaybackStateStopped) |
{ |
[overlayController setPlaybackStateDisplayString:@"stopped"]; |
} |
/* Playback is currently under way. */ |
else if (player.playbackState == MPMoviePlaybackStatePlaying) |
{ |
[overlayController setPlaybackStateDisplayString:@"playing"]; |
} |
/* Playback is currently paused. */ |
else if (player.playbackState == MPMoviePlaybackStatePaused) |
{ |
[overlayController setPlaybackStateDisplayString:@"paused"]; |
} |
/* Playback is temporarily interrupted, perhaps because the buffer |
ran out of content. */ |
else if (player.playbackState == MPMoviePlaybackStateInterrupted) |
{ |
[overlayController setPlaybackStateDisplayString:@"interrupted"]; |
} |
} |
|
/* Notifies observers of a change in the prepared-to-play state of an object |
conforming to the MPMediaPlayback protocol. */ |
- (void) mediaIsPreparedToPlayDidChange:(NSNotification*)notification |
{ |
// Add an overlay view on top of the movie view |
[self addOverlayView]; |
} |
|
#pragma mark Install Movie Notifications |
|
/* Register observers for the various movie object notifications. */ |
-(void)installMovieNotificationObservers |
{ |
MPMoviePlayerController *player = [self moviePlayerController]; |
|
[[NSNotificationCenter defaultCenter] addObserver:self |
selector:@selector(loadStateDidChange:) |
name:MPMoviePlayerLoadStateDidChangeNotification |
object:player]; |
|
[[NSNotificationCenter defaultCenter] addObserver:self |
selector:@selector(moviePlayBackDidFinish:) |
name:MPMoviePlayerPlaybackDidFinishNotification |
object:player]; |
|
[[NSNotificationCenter defaultCenter] addObserver:self |
selector:@selector(mediaIsPreparedToPlayDidChange:) |
name:MPMediaPlaybackIsPreparedToPlayDidChangeNotification |
object:player]; |
|
[[NSNotificationCenter defaultCenter] addObserver:self |
selector:@selector(moviePlayBackStateDidChange:) |
name:MPMoviePlayerPlaybackStateDidChangeNotification |
object:player]; |
} |
|
#pragma mark Remove Movie Notification Handlers |
|
/* Remove the movie notification observers from the movie object. */ |
-(void)removeMovieNotificationHandlers |
{ |
MPMoviePlayerController *player = [self moviePlayerController]; |
|
[[NSNotificationCenter defaultCenter]removeObserver:self name:MPMoviePlayerLoadStateDidChangeNotification object:player]; |
[[NSNotificationCenter defaultCenter]removeObserver:self name:MPMoviePlayerPlaybackDidFinishNotification object:player]; |
[[NSNotificationCenter defaultCenter]removeObserver:self name:MPMediaPlaybackIsPreparedToPlayDidChangeNotification object:player]; |
[[NSNotificationCenter defaultCenter]removeObserver:self name:MPMoviePlayerPlaybackStateDidChangeNotification object:player]; |
} |
|
/* Delete the movie player object, and remove the movie notification observers. */ |
-(void)deletePlayerAndNotificationObservers |
{ |
[self removeMovieNotificationHandlers]; |
[self setMoviePlayerController:nil]; |
} |
|
#pragma mark Movie Settings |
|
/* Apply user movie preference settings (these are set from the Settings: iPhone Settings->Movie Player) |
for scaling mode, control style, background color, repeat mode, application audio session, background |
image and AirPlay mode. |
*/ |
-(void)applyUserSettingsToMoviePlayer |
{ |
MPMoviePlayerController *player = [self moviePlayerController]; |
if (player) |
{ |
player.scalingMode = [MoviePlayerUserPrefs scalingModeUserSetting]; |
player.controlStyle =[MoviePlayerUserPrefs controlStyleUserSetting]; |
player.backgroundView.backgroundColor = [MoviePlayerUserPrefs backgroundColorUserSetting]; |
player.repeatMode = [MoviePlayerUserPrefs repeatModeUserSetting]; |
if ([MoviePlayerUserPrefs backgroundImageUserSetting] == YES) |
{ |
[self.movieBackgroundImageView setFrame:[self.view bounds]]; |
[player.backgroundView addSubview:self.movieBackgroundImageView]; |
} |
else |
{ |
[self.movieBackgroundImageView removeFromSuperview]; |
} |
|
/* Indicate the movie player allows AirPlay movie playback. */ |
player.allowsAirPlay = YES; |
} |
} |
|
|
|
@end |