From a8fd77c69da573ca06982548fd09813c1174551c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81dric=20Luthi?= Date: Mon, 20 May 2013 00:30:23 +0200 Subject: [PATCH 01/14] Restore status bar state when movie player exits full-screen Fixes #3 --- .../XCDYouTubeVideoPlayerViewController.h | 2 +- .../XCDYouTubeVideoPlayerViewController.m | 26 +++++++++++++++++-- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/XCDYouTubeVideoPlayerViewController/XCDYouTubeVideoPlayerViewController.h b/XCDYouTubeVideoPlayerViewController/XCDYouTubeVideoPlayerViewController.h index fcabfe63..ee21a2aa 100644 --- a/XCDYouTubeVideoPlayerViewController/XCDYouTubeVideoPlayerViewController.h +++ b/XCDYouTubeVideoPlayerViewController/XCDYouTubeVideoPlayerViewController.h @@ -29,7 +29,7 @@ MP_EXTERN NSString *const XCDMoviePlayerPlaybackDidFinishErrorUserInfoKey; // NS // If you really know what you are doing, you can use the `itag` values as described on http://en.wikipedia.org/wiki/YouTube#Quality_and_codecs @property (nonatomic, copy) NSArray *preferredVideoQualities; -// Ownership of the `moviePlayer` property is transferred to the view. +// Ownership of the XCDYouTubeVideoPlayerViewController instance is transferred to the view. - (void) presentInView:(UIView *)view; @end diff --git a/XCDYouTubeVideoPlayerViewController/XCDYouTubeVideoPlayerViewController.m b/XCDYouTubeVideoPlayerViewController/XCDYouTubeVideoPlayerViewController.m index 23beb15c..e5423e50 100644 --- a/XCDYouTubeVideoPlayerViewController/XCDYouTubeVideoPlayerViewController.m +++ b/XCDYouTubeVideoPlayerViewController/XCDYouTubeVideoPlayerViewController.m @@ -37,11 +37,13 @@ @interface XCDYouTubeVideoPlayerViewController () @property (nonatomic, strong) NSMutableData *connectionData; @property (nonatomic, strong) NSMutableArray *elFields; @property (nonatomic, assign, getter = isEmbedded) BOOL embedded; +@property (nonatomic, assign) BOOL statusBarHidden; +@property (nonatomic, assign) UIStatusBarStyle statusBarStyle; @end @implementation XCDYouTubeVideoPlayerViewController -static void *MoviePlayerKey = &MoviePlayerKey; +static void *XCDYouTubeVideoPlayerViewControllerKey = &XCDYouTubeVideoPlayerViewControllerKey; - (id) init { @@ -66,9 +68,16 @@ - (id) initWithVideoIdentifier:(NSString *)videoIdentifier if (videoIdentifier) self.videoIdentifier = videoIdentifier; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(moviePlayerWillExitFullscreen:) name:MPMoviePlayerWillExitFullscreenNotification object:self.moviePlayer]; + return self; } +- (void) dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + - (void) setVideoIdentifier:(NSString *)videoIdentifier { if (![NSThread isMainThread]) @@ -91,11 +100,15 @@ - (void) presentInView:(UIView *)view { self.embedded = YES; + UIApplication *application = [UIApplication sharedApplication]; + self.statusBarHidden = application.statusBarHidden; + self.statusBarStyle = application.statusBarStyle; + self.moviePlayer.controlStyle = MPMovieControlStyleEmbedded; self.moviePlayer.view.frame = CGRectMake(0.f, 0.f, view.bounds.size.width, view.bounds.size.height); self.moviePlayer.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; [view addSubview:self.moviePlayer.view]; - objc_setAssociatedObject(view, MoviePlayerKey, self.moviePlayer, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + objc_setAssociatedObject(view, XCDYouTubeVideoPlayerViewControllerKey, self, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (void) startVideoInfoRequest @@ -178,6 +191,15 @@ - (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)er [self finishWithError:error]; } +#pragma mark - Notifications + +- (void) moviePlayerWillExitFullscreen:(NSNotification *)notification +{ + UIApplication *application = [UIApplication sharedApplication]; + [application setStatusBarHidden:self.statusBarHidden withAnimation:UIStatusBarAnimationFade]; + [application setStatusBarStyle:self.statusBarStyle animated:YES]; +} + #pragma mark - URL Parsing - (NSURL *) videoURLWithData:(NSData *)data error:(NSError **)error From 1b9c8b76b8e9c2f6efbe29f94d2bf12e43fd732e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81dric=20Luthi?= Date: Mon, 20 May 2013 01:23:09 +0200 Subject: [PATCH 02/14] Save status bar state when movie player enters full-screen --- .../XCDYouTubeVideoPlayerViewController.m | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/XCDYouTubeVideoPlayerViewController/XCDYouTubeVideoPlayerViewController.m b/XCDYouTubeVideoPlayerViewController/XCDYouTubeVideoPlayerViewController.m index e5423e50..945c1684 100644 --- a/XCDYouTubeVideoPlayerViewController/XCDYouTubeVideoPlayerViewController.m +++ b/XCDYouTubeVideoPlayerViewController/XCDYouTubeVideoPlayerViewController.m @@ -68,6 +68,7 @@ - (id) initWithVideoIdentifier:(NSString *)videoIdentifier if (videoIdentifier) self.videoIdentifier = videoIdentifier; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(moviePlayerWillEnterFullscreen:) name:MPMoviePlayerWillEnterFullscreenNotification object:self.moviePlayer]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(moviePlayerWillExitFullscreen:) name:MPMoviePlayerWillExitFullscreenNotification object:self.moviePlayer]; return self; @@ -100,10 +101,6 @@ - (void) presentInView:(UIView *)view { self.embedded = YES; - UIApplication *application = [UIApplication sharedApplication]; - self.statusBarHidden = application.statusBarHidden; - self.statusBarStyle = application.statusBarStyle; - self.moviePlayer.controlStyle = MPMovieControlStyleEmbedded; self.moviePlayer.view.frame = CGRectMake(0.f, 0.f, view.bounds.size.width, view.bounds.size.height); self.moviePlayer.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; @@ -193,6 +190,13 @@ - (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)er #pragma mark - Notifications +- (void) moviePlayerWillEnterFullscreen:(NSNotification *)notification +{ + UIApplication *application = [UIApplication sharedApplication]; + self.statusBarHidden = application.statusBarHidden; + self.statusBarStyle = application.statusBarStyle; +} + - (void) moviePlayerWillExitFullscreen:(NSNotification *)notification { UIApplication *application = [UIApplication sharedApplication]; From 2240dfe40250d88cc20272ffcd0c0c5bbf3f6fcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81dric=20Luthi?= Date: Tue, 21 May 2013 13:33:09 +0200 Subject: [PATCH 03/14] Ensure the stream has a URL --- .../XCDYouTubeVideoPlayerViewController.m | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/XCDYouTubeVideoPlayerViewController/XCDYouTubeVideoPlayerViewController.m b/XCDYouTubeVideoPlayerViewController/XCDYouTubeVideoPlayerViewController.m index 945c1684..9a42330b 100644 --- a/XCDYouTubeVideoPlayerViewController/XCDYouTubeVideoPlayerViewController.m +++ b/XCDYouTubeVideoPlayerViewController/XCDYouTubeVideoPlayerViewController.m @@ -218,9 +218,10 @@ - (NSURL *) videoURLWithData:(NSData *)data error:(NSError **)error { NSDictionary *stream = DictionaryWithQueryString(streamQuery, queryEncoding); NSString *type = stream[@"type"]; - if ([AVURLAsset isPlayableExtendedMIMEType:type]) + NSString *urlString = stream[@"url"]; + if (urlString && [AVURLAsset isPlayableExtendedMIMEType:type]) { - NSURL *streamURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@&signature=%@", stream[@"url"], stream[@"sig"]]]; + NSURL *streamURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@&signature=%@", urlString, stream[@"sig"]]]; streamURLs[@([stream[@"itag"] integerValue])] = streamURL; } } From 7a40c61ea576d888fa3ae9a76842dc10f5a87861 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81dric=20Luthi?= Date: Wed, 22 May 2013 14:52:28 +0200 Subject: [PATCH 04/14] Post metadata notification --- .../XCDYouTubeVideoPlayerViewController.h | 6 +++++ .../XCDYouTubeVideoPlayerViewController.m | 23 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/XCDYouTubeVideoPlayerViewController/XCDYouTubeVideoPlayerViewController.h b/XCDYouTubeVideoPlayerViewController/XCDYouTubeVideoPlayerViewController.h index ee21a2aa..5b9373b2 100644 --- a/XCDYouTubeVideoPlayerViewController/XCDYouTubeVideoPlayerViewController.h +++ b/XCDYouTubeVideoPlayerViewController/XCDYouTubeVideoPlayerViewController.h @@ -18,6 +18,12 @@ typedef NS_ENUM(NSUInteger, XCDYouTubeVideoQuality) { MP_EXTERN NSString *const XCDYouTubeVideoErrorDomain; MP_EXTERN NSString *const XCDMoviePlayerPlaybackDidFinishErrorUserInfoKey; // NSError +MP_EXTERN NSString *const XCDYouTubeVideoPlayerViewControllerDidReceiveMetadataNotification; +MP_EXTERN NSString *const XCDMetadataKeyTitle; +MP_EXTERN NSString *const XCDMetadataKeySmallThumbnailURL; +MP_EXTERN NSString *const XCDMetadataKeyMediumThumbnailURL; +MP_EXTERN NSString *const XCDMetadataKeyLargeThumbnailURL; + @interface XCDYouTubeVideoPlayerViewController : MPMoviePlayerViewController - (id) initWithVideoIdentifier:(NSString *)videoIdentifier; diff --git a/XCDYouTubeVideoPlayerViewController/XCDYouTubeVideoPlayerViewController.m b/XCDYouTubeVideoPlayerViewController/XCDYouTubeVideoPlayerViewController.m index 9a42330b..8bc8ad65 100644 --- a/XCDYouTubeVideoPlayerViewController/XCDYouTubeVideoPlayerViewController.m +++ b/XCDYouTubeVideoPlayerViewController/XCDYouTubeVideoPlayerViewController.m @@ -14,6 +14,12 @@ NSString *const XCDYouTubeVideoErrorDomain = @"XCDYouTubeVideoErrorDomain"; NSString *const XCDMoviePlayerPlaybackDidFinishErrorUserInfoKey = @"XCDMoviePlayerPlaybackDidFinishErrorUserInfoKey"; +NSString *const XCDYouTubeVideoPlayerViewControllerDidReceiveMetadataNotification = @"XCDYouTubeVideoPlayerViewControllerDidReceiveMetadataNotification"; +NSString *const XCDMetadataKeyTitle = @"Title"; +NSString *const XCDMetadataKeySmallThumbnailURL = @"SmallThumbnailURL"; +NSString *const XCDMetadataKeyMediumThumbnailURL = @"MediumThumbnailURL"; +NSString *const XCDMetadataKeyLargeThumbnailURL = @"LargeThumbnailURL"; + static NSDictionary *DictionaryWithQueryString(NSString *string, NSStringEncoding encoding) { NSMutableDictionary *dictionary = [NSMutableDictionary new]; @@ -230,7 +236,24 @@ - (NSURL *) videoURLWithData:(NSData *)data error:(NSError **)error { NSURL *streamURL = streamURLs[videoQuality]; if (streamURL) + { + NSString *title = video[@"title"]; + NSString *thumbnailSmall = video[@"thumbnail_url"]; + NSString *thumbnailMedium = video[@"iurlsd"]; + NSString *thumbnailLarge = video[@"iurlmaxres"]; + NSMutableDictionary *userInfo = [NSMutableDictionary new]; + if (title) + userInfo[XCDMetadataKeyTitle] = title; + if (thumbnailSmall) + userInfo[XCDMetadataKeySmallThumbnailURL] = [NSURL URLWithString:thumbnailSmall]; + if (thumbnailMedium) + userInfo[XCDMetadataKeyMediumThumbnailURL] = [NSURL URLWithString:thumbnailMedium]; + if (thumbnailLarge) + userInfo[XCDMetadataKeyLargeThumbnailURL] = [NSURL URLWithString:thumbnailLarge]; + + [[NSNotificationCenter defaultCenter] postNotificationName:XCDYouTubeVideoPlayerViewControllerDidReceiveMetadataNotification object:self userInfo:userInfo]; return streamURL; + } } if (error) From 2cdaf98e5ae0e12df77eb83d24f206ca47da8d9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81dric=20Luthi?= Date: Wed, 22 May 2013 14:52:56 +0200 Subject: [PATCH 05/14] Log metadata notifications --- YouTube Video Player Demo/DemoViewController.m | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/YouTube Video Player Demo/DemoViewController.m b/YouTube Video Player Demo/DemoViewController.m index deef9a8e..744be1bc 100644 --- a/YouTube Video Player Demo/DemoViewController.m +++ b/YouTube Video Player Demo/DemoViewController.m @@ -26,6 +26,7 @@ - (id) initWithNibName:(NSString *)nibName bundle:(NSBundle *)nibBundle if (!(self = [super initWithNibName:nibName bundle:nibBundle])) return nil; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(videoPlayerViewControllerDidReceiveMetadata:) name:XCDYouTubeVideoPlayerViewControllerDidReceiveMetadataNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(moviePlayerPlaybackDidFinish:) name:MPMoviePlayerPlaybackDidFinishNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(moviePlayerPlaybackStateDidChange:) name:MPMoviePlayerPlaybackStateDidChangeNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(moviePlayerLoadStateDidChange:) name:MPMoviePlayerLoadStateDidChangeNotification object:nil]; @@ -163,4 +164,9 @@ - (void) moviePlayerLoadStateDidChange:(NSNotification *)notification NSLog(@"Load State: %@", loadState.length > 0 ? [loadState substringFromIndex:3] : @"N/A"); } +- (void) videoPlayerViewControllerDidReceiveMetadata:(NSNotification *)notification +{ + NSLog(@"Metadata: %@", notification.userInfo); +} + @end From 2766bad9b5915715e802554b6b83b2cacd47b8de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81dric=20Luthi?= Date: Wed, 22 May 2013 15:13:26 +0200 Subject: [PATCH 06/14] Always play the video when it is presented in full-screen --- .../XCDYouTubeVideoPlayerViewController.m | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/XCDYouTubeVideoPlayerViewController/XCDYouTubeVideoPlayerViewController.m b/XCDYouTubeVideoPlayerViewController/XCDYouTubeVideoPlayerViewController.m index 8bc8ad65..30235755 100644 --- a/XCDYouTubeVideoPlayerViewController/XCDYouTubeVideoPlayerViewController.m +++ b/XCDYouTubeVideoPlayerViewController/XCDYouTubeVideoPlayerViewController.m @@ -145,16 +145,21 @@ - (void) viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; - if ([self isBeingPresented]) - self.moviePlayer.controlStyle = MPMovieControlStyleFullscreen; + if (![self isBeingPresented]) + return; + + self.moviePlayer.controlStyle = MPMovieControlStyleFullscreen; + [self.moviePlayer play]; } - (void) viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; - if ([self isBeingDismissed]) - [self.connection cancel]; + if (![self isBeingDismissed]) + return; + + [self.connection cancel]; } #pragma mark - NSURLConnectionDataDelegate / NSURLConnectionDelegate From c88ca207ed3e6826b508ed39819829d8335410ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81dric=20Luthi?= Date: Wed, 29 May 2013 10:31:44 +0200 Subject: [PATCH 07/14] Do not automatically call prepareToPlay --- .../XCDYouTubeVideoPlayerViewController.m | 7 ------- 1 file changed, 7 deletions(-) diff --git a/XCDYouTubeVideoPlayerViewController/XCDYouTubeVideoPlayerViewController.m b/XCDYouTubeVideoPlayerViewController/XCDYouTubeVideoPlayerViewController.m index 30235755..54e8f8f1 100644 --- a/XCDYouTubeVideoPlayerViewController/XCDYouTubeVideoPlayerViewController.m +++ b/XCDYouTubeVideoPlayerViewController/XCDYouTubeVideoPlayerViewController.m @@ -180,18 +180,11 @@ - (void) connectionDidFinishLoading:(NSURLConnection *)connection NSError *error = nil; NSURL *videoURL = [self videoURLWithData:self.connectionData error:&error]; if (videoURL) - { self.moviePlayer.contentURL = videoURL; - [self.moviePlayer prepareToPlay]; - } else if (self.elFields.count > 0) - { [self startVideoInfoRequest]; - } else - { [self finishWithError:error]; - } } - (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error From e5fcf4712dea6daceadc4a8ade60fc52b1c40c03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81dric=20Luthi?= Date: Thu, 30 May 2013 01:24:36 +0200 Subject: [PATCH 08/14] Do not load the video but display a thumbnail instead --- .../DemoViewController.m | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/YouTube Video Player Demo/DemoViewController.m b/YouTube Video Player Demo/DemoViewController.m index 744be1bc..f28add0d 100644 --- a/YouTube Video Player Demo/DemoViewController.m +++ b/YouTube Video Player Demo/DemoViewController.m @@ -81,6 +81,7 @@ - (IBAction) playTrendingVideo:(id)sender { [self.videoContainerView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)]; [videoPlayerViewController presentInView:self.videoContainerView]; + [videoPlayerViewController.moviePlayer prepareToPlay]; } // https://developers.google.com/youtube/2.0/developers_guide_protocol_video_feeds#Standard_feeds @@ -167,6 +168,43 @@ - (void) moviePlayerLoadStateDidChange:(NSNotification *)notification - (void) videoPlayerViewControllerDidReceiveMetadata:(NSNotification *)notification { NSLog(@"Metadata: %@", notification.userInfo); + + if (notification.object != self.videoPlayerViewController) + return; + + NSURL *thumbnailURL = notification.userInfo[XCDMetadataKeyMediumThumbnailURL] ?: notification.userInfo[XCDMetadataKeySmallThumbnailURL]; + [NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:thumbnailURL] queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) { + UIImageView *thumbnailImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, self.videoContainerView.bounds.size.width, self.videoContainerView.bounds.size.height)]; + thumbnailImageView.image = [UIImage imageWithData:data]; + thumbnailImageView.backgroundColor = [UIColor blackColor]; + thumbnailImageView.contentMode = UIViewContentModeScaleAspectFit; + thumbnailImageView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + thumbnailImageView.userInteractionEnabled = YES; + [self.videoContainerView addSubview:thumbnailImageView]; + + // Do not get the `Play` image like this in production code + NSString *simulatorRoot = [[[NSProcessInfo processInfo] environment] objectForKey:@"IPHONE_SIMULATOR_ROOT"] ?: @""; + NSBundle *quickTimePlugin = [NSBundle bundleWithPath:[simulatorRoot stringByAppendingPathComponent:@"/System/Library/Internet Plug-Ins/QuickTime Plugin.webplugin"]]; + NSURL *playURL = [quickTimePlugin URLForResource:@"Play" withExtension:@"png"]; + UIImage *playImage = [UIImage imageWithContentsOfFile:playURL.path]; + UIButton *playButton = [UIButton buttonWithType:UIButtonTypeCustom]; + playButton.frame = thumbnailImageView.frame; + playButton.autoresizingMask = thumbnailImageView.autoresizingMask; + [playButton setImage:playImage forState:UIControlStateNormal]; + [playButton addTarget:self action:@selector(play:) forControlEvents:UIControlEventTouchUpInside]; + [thumbnailImageView addSubview:playButton]; + }]; +} + +- (void) play:(UIButton *)sender +{ + [UIView animateWithDuration:0.3f animations:^{ + sender.superview.alpha = 0.f; + } completion:^(BOOL finished) { + [sender.superview removeFromSuperview]; + }]; + + [self.videoPlayerViewController.moviePlayer play]; } @end From ef38b91bce9dcf1059f62cb6866247f609d2e808 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81dric=20Luthi?= Date: Thu, 30 May 2013 01:32:39 +0200 Subject: [PATCH 09/14] Move logging from DemoViewController into AppDelegate --- YouTube Video Player Demo/AppDelegate.m | 77 +++++++++++++++++++ .../DemoViewController.m | 69 ----------------- 2 files changed, 77 insertions(+), 69 deletions(-) diff --git a/YouTube Video Player Demo/AppDelegate.m b/YouTube Video Player Demo/AppDelegate.m index 33bfefcc..f9362dd8 100644 --- a/YouTube Video Player Demo/AppDelegate.m +++ b/YouTube Video Player Demo/AppDelegate.m @@ -8,6 +8,7 @@ #import "AppDelegate.h" +#import "XCDYouTubeVideoPlayerViewController.h" #import "DemoViewController.h" @implementation AppDelegate @@ -16,10 +17,86 @@ @implementation AppDelegate - (BOOL) application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(videoPlayerViewControllerDidReceiveMetadata:) name:XCDYouTubeVideoPlayerViewControllerDidReceiveMetadataNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(moviePlayerPlaybackDidFinish:) name:MPMoviePlayerPlaybackDidFinishNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(moviePlayerPlaybackStateDidChange:) name:MPMoviePlayerPlaybackStateDidChangeNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(moviePlayerLoadStateDidChange:) name:MPMoviePlayerLoadStateDidChangeNotification object:nil]; + self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; self.window.rootViewController = [[UINavigationController alloc] initWithRootViewController:[DemoViewController new]]; [self.window makeKeyAndVisible]; return YES; } +#pragma mark - Notifications + +- (void) moviePlayerPlaybackDidFinish:(NSNotification *)notification +{ + MPMovieFinishReason finishReason = [notification.userInfo[MPMoviePlayerPlaybackDidFinishReasonUserInfoKey] integerValue]; + NSError *error = notification.userInfo[XCDMoviePlayerPlaybackDidFinishErrorUserInfoKey]; + NSString *reason = @"Unknown"; + switch (finishReason) + { + case MPMovieFinishReasonPlaybackEnded: + reason = @"Playback Ended"; + break; + case MPMovieFinishReasonPlaybackError: + reason = @"Playback Error"; + break; + case MPMovieFinishReasonUserExited: + reason = @"User Exited"; + break; + } + NSLog(@"Finish Reason: %@%@", reason, error ? [@"\n" stringByAppendingString:[error description]] : @""); +} + +- (void) moviePlayerPlaybackStateDidChange:(NSNotification *)notification +{ + MPMoviePlayerController *moviePlayerController = notification.object; + NSString *playbackState = @"Unknown"; + switch (moviePlayerController.playbackState) + { + case MPMoviePlaybackStateStopped: + playbackState = @"Stopped"; + break; + case MPMoviePlaybackStatePlaying: + playbackState = @"Playing"; + break; + case MPMoviePlaybackStatePaused: + playbackState = @"Paused"; + break; + case MPMoviePlaybackStateInterrupted: + playbackState = @"Interrupted"; + break; + case MPMoviePlaybackStateSeekingForward: + playbackState = @"Seeking Forward"; + break; + case MPMoviePlaybackStateSeekingBackward: + playbackState = @"Seeking Backward"; + break; + } + NSLog(@"Playback State: %@", playbackState); +} + +- (void) moviePlayerLoadStateDidChange:(NSNotification *)notification +{ + MPMoviePlayerController *moviePlayerController = notification.object; + + NSMutableString *loadState = [NSMutableString new]; + MPMovieLoadState state = moviePlayerController.loadState; + if (state & MPMovieLoadStatePlayable) + [loadState appendString:@" | Playable"]; + if (state & MPMovieLoadStatePlaythroughOK) + [loadState appendString:@" | Playthrough OK"]; + if (state & MPMovieLoadStateStalled) + [loadState appendString:@" | Stalled"]; + + NSLog(@"Load State: %@", loadState.length > 0 ? [loadState substringFromIndex:3] : @"N/A"); +} + +- (void) videoPlayerViewControllerDidReceiveMetadata:(NSNotification *)notification +{ + NSLog(@"Metadata: %@", notification.userInfo); +} + @end diff --git a/YouTube Video Player Demo/DemoViewController.m b/YouTube Video Player Demo/DemoViewController.m index f28add0d..4f6fb43e 100644 --- a/YouTube Video Player Demo/DemoViewController.m +++ b/YouTube Video Player Demo/DemoViewController.m @@ -27,9 +27,6 @@ - (id) initWithNibName:(NSString *)nibName bundle:(NSBundle *)nibBundle return nil; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(videoPlayerViewControllerDidReceiveMetadata:) name:XCDYouTubeVideoPlayerViewControllerDidReceiveMetadataNotification object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(moviePlayerPlaybackDidFinish:) name:MPMoviePlayerPlaybackDidFinishNotification object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(moviePlayerPlaybackStateDidChange:) name:MPMoviePlayerPlaybackStateDidChangeNotification object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(moviePlayerLoadStateDidChange:) name:MPMoviePlayerLoadStateDidChangeNotification object:nil]; return self; } @@ -101,74 +98,8 @@ - (BOOL) textFieldShouldReturn:(UITextField *)textField #pragma mark - Notifications -- (void) moviePlayerPlaybackDidFinish:(NSNotification *)notification -{ - MPMovieFinishReason finishReason = [notification.userInfo[MPMoviePlayerPlaybackDidFinishReasonUserInfoKey] integerValue]; - NSError *error = notification.userInfo[XCDMoviePlayerPlaybackDidFinishErrorUserInfoKey]; - NSString *reason = @"Unknown"; - switch (finishReason) - { - case MPMovieFinishReasonPlaybackEnded: - reason = @"Playback Ended"; - break; - case MPMovieFinishReasonPlaybackError: - reason = @"Playback Error"; - break; - case MPMovieFinishReasonUserExited: - reason = @"User Exited"; - break; - } - NSLog(@"Finish Reason: %@%@", reason, error ? [@"\n" stringByAppendingString:[error description]] : @""); -} - -- (void) moviePlayerPlaybackStateDidChange:(NSNotification *)notification -{ - MPMoviePlayerController *moviePlayerController = notification.object; - NSString *playbackState = @"Unknown"; - switch (moviePlayerController.playbackState) - { - case MPMoviePlaybackStateStopped: - playbackState = @"Stopped"; - break; - case MPMoviePlaybackStatePlaying: - playbackState = @"Playing"; - break; - case MPMoviePlaybackStatePaused: - playbackState = @"Paused"; - break; - case MPMoviePlaybackStateInterrupted: - playbackState = @"Interrupted"; - break; - case MPMoviePlaybackStateSeekingForward: - playbackState = @"Seeking Forward"; - break; - case MPMoviePlaybackStateSeekingBackward: - playbackState = @"Seeking Backward"; - break; - } - NSLog(@"Playback State: %@", playbackState); -} - -- (void) moviePlayerLoadStateDidChange:(NSNotification *)notification -{ - MPMoviePlayerController *moviePlayerController = notification.object; - - NSMutableString *loadState = [NSMutableString new]; - MPMovieLoadState state = moviePlayerController.loadState; - if (state & MPMovieLoadStatePlayable) - [loadState appendString:@" | Playable"]; - if (state & MPMovieLoadStatePlaythroughOK) - [loadState appendString:@" | Playthrough OK"]; - if (state & MPMovieLoadStateStalled) - [loadState appendString:@" | Stalled"]; - - NSLog(@"Load State: %@", loadState.length > 0 ? [loadState substringFromIndex:3] : @"N/A"); -} - - (void) videoPlayerViewControllerDidReceiveMetadata:(NSNotification *)notification { - NSLog(@"Metadata: %@", notification.userInfo); - if (notification.object != self.videoPlayerViewController) return; From 4d5ad7316a0ef153e25eb3dbcb919d9fddd0ae53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81dric=20Luthi?= Date: Thu, 30 May 2013 01:35:42 +0200 Subject: [PATCH 10/14] Add documentation about metadata notification userInfo keys --- .../XCDYouTubeVideoPlayerViewController.h | 1 + 1 file changed, 1 insertion(+) diff --git a/XCDYouTubeVideoPlayerViewController/XCDYouTubeVideoPlayerViewController.h b/XCDYouTubeVideoPlayerViewController/XCDYouTubeVideoPlayerViewController.h index 5b9373b2..54a94ca0 100644 --- a/XCDYouTubeVideoPlayerViewController/XCDYouTubeVideoPlayerViewController.h +++ b/XCDYouTubeVideoPlayerViewController/XCDYouTubeVideoPlayerViewController.h @@ -19,6 +19,7 @@ MP_EXTERN NSString *const XCDYouTubeVideoErrorDomain; MP_EXTERN NSString *const XCDMoviePlayerPlaybackDidFinishErrorUserInfoKey; // NSError MP_EXTERN NSString *const XCDYouTubeVideoPlayerViewControllerDidReceiveMetadataNotification; +// Metadata notification userInfo keys, they are all optional MP_EXTERN NSString *const XCDMetadataKeyTitle; MP_EXTERN NSString *const XCDMetadataKeySmallThumbnailURL; MP_EXTERN NSString *const XCDMetadataKeyMediumThumbnailURL; From 613de8a5ff6eb91ec9881c2e922fd1efd698b426 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81dric=20Luthi?= Date: Thu, 30 May 2013 01:49:53 +0200 Subject: [PATCH 11/14] Make sure the movie player view is not added as a subview twice --- .../XCDYouTubeVideoPlayerViewController.m | 3 ++- YouTube Video Player Demo/DemoViewController.m | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/XCDYouTubeVideoPlayerViewController/XCDYouTubeVideoPlayerViewController.m b/XCDYouTubeVideoPlayerViewController/XCDYouTubeVideoPlayerViewController.m index 54e8f8f1..1e65af0d 100644 --- a/XCDYouTubeVideoPlayerViewController/XCDYouTubeVideoPlayerViewController.m +++ b/XCDYouTubeVideoPlayerViewController/XCDYouTubeVideoPlayerViewController.m @@ -110,7 +110,8 @@ - (void) presentInView:(UIView *)view self.moviePlayer.controlStyle = MPMovieControlStyleEmbedded; self.moviePlayer.view.frame = CGRectMake(0.f, 0.f, view.bounds.size.width, view.bounds.size.height); self.moviePlayer.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; - [view addSubview:self.moviePlayer.view]; + if (![view.subviews containsObject:self.moviePlayer.view]) + [view addSubview:self.moviePlayer.view]; objc_setAssociatedObject(view, XCDYouTubeVideoPlayerViewControllerKey, self, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } diff --git a/YouTube Video Player Demo/DemoViewController.m b/YouTube Video Player Demo/DemoViewController.m index 4f6fb43e..50b9f09e 100644 --- a/YouTube Video Player Demo/DemoViewController.m +++ b/YouTube Video Player Demo/DemoViewController.m @@ -63,7 +63,7 @@ - (IBAction) playYouTubeVideo:(id)sender if (self.fullScreenSwitch.on) [self presentMoviePlayerViewControllerAnimated:self.videoPlayerViewController]; - else if (![self.videoContainerView.subviews containsObject:self.videoPlayerViewController.moviePlayer.view]) + else [self.videoPlayerViewController presentInView:self.videoContainerView]; } From ba452f46ccf6d6a8b1d700ae2f660d2158401b00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81dric=20Luthi?= Date: Fri, 9 Aug 2013 17:23:37 +0200 Subject: [PATCH 12/14] Add constants for NSError codes --- .../XCDYouTubeVideoPlayerViewController.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/XCDYouTubeVideoPlayerViewController/XCDYouTubeVideoPlayerViewController.h b/XCDYouTubeVideoPlayerViewController/XCDYouTubeVideoPlayerViewController.h index 54a94ca0..76d93708 100644 --- a/XCDYouTubeVideoPlayerViewController/XCDYouTubeVideoPlayerViewController.h +++ b/XCDYouTubeVideoPlayerViewController/XCDYouTubeVideoPlayerViewController.h @@ -18,6 +18,12 @@ typedef NS_ENUM(NSUInteger, XCDYouTubeVideoQuality) { MP_EXTERN NSString *const XCDYouTubeVideoErrorDomain; MP_EXTERN NSString *const XCDMoviePlayerPlaybackDidFinishErrorUserInfoKey; // NSError +enum { + XCDYouTubeErrorInvalidVideoIdentifier = 2, + XCDYouTubeErrorRemovedVideo = 100, + XCDYouTubeErrorRestrictedPlayback = 150 +}; + MP_EXTERN NSString *const XCDYouTubeVideoPlayerViewControllerDidReceiveMetadataNotification; // Metadata notification userInfo keys, they are all optional MP_EXTERN NSString *const XCDMetadataKeyTitle; From 1f0559ea151980663ce4d3c96af2af36919c9d37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81dric=20Luthi?= Date: Fri, 9 Aug 2013 17:26:54 +0200 Subject: [PATCH 13/14] Make sure a signature is present to ensure accurate error reporting Fixes #6 --- .../XCDYouTubeVideoPlayerViewController.m | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/XCDYouTubeVideoPlayerViewController/XCDYouTubeVideoPlayerViewController.m b/XCDYouTubeVideoPlayerViewController/XCDYouTubeVideoPlayerViewController.m index 1e65af0d..36dfc239 100644 --- a/XCDYouTubeVideoPlayerViewController/XCDYouTubeVideoPlayerViewController.m +++ b/XCDYouTubeVideoPlayerViewController/XCDYouTubeVideoPlayerViewController.m @@ -224,9 +224,10 @@ - (NSURL *) videoURLWithData:(NSData *)data error:(NSError **)error NSDictionary *stream = DictionaryWithQueryString(streamQuery, queryEncoding); NSString *type = stream[@"type"]; NSString *urlString = stream[@"url"]; - if (urlString && [AVURLAsset isPlayableExtendedMIMEType:type]) + NSString *signature = stream[@"sig"]; + if (urlString && signature && [AVURLAsset isPlayableExtendedMIMEType:type]) { - NSURL *streamURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@&signature=%@", urlString, stream[@"sig"]]]; + NSURL *streamURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@&signature=%@", urlString, signature]]; streamURLs[@([stream[@"itag"] integerValue])] = streamURL; } } From 78f2837a466d975876c6c6cd90df077fec304968 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81dric=20Luthi?= Date: Fri, 9 Aug 2013 17:37:57 +0200 Subject: [PATCH 14/14] Version 1.1.0 --- XCDYouTubeVideoPlayerViewController.podspec | 4 ++-- YouTube Video Player Demo.xcodeproj/project.pbxproj | 4 ++-- .../YouTube Video Player Demo-Info.plist | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/XCDYouTubeVideoPlayerViewController.podspec b/XCDYouTubeVideoPlayerViewController.podspec index 599c4ef0..d50de642 100644 --- a/XCDYouTubeVideoPlayerViewController.podspec +++ b/XCDYouTubeVideoPlayerViewController.podspec @@ -1,11 +1,11 @@ Pod::Spec.new do |s| s.name = "XCDYouTubeVideoPlayerViewController" - s.version = "1.0.0" + s.version = "1.1.0" s.summary = "YouTube video player for iPhone and iPad." s.homepage = "https://github.com/0xced/XCDYouTubeVideoPlayerViewController" s.license = 'MIT' s.author = { "CeĢdric Luthi" => "cedric.luthi@gmail.com" } - s.source = { :git => "https://github.com/0xced/XCDYouTubeVideoPlayerViewController.git", :tag => "1.0.0" } + s.source = { :git => "https://github.com/0xced/XCDYouTubeVideoPlayerViewController.git", :tag => "1.1.0" } s.platform = :ios, '5.0' s.source_files = 'XCDYouTubeVideoPlayerViewController' s.frameworks = 'AVFoundation', 'MediaPlayer' diff --git a/YouTube Video Player Demo.xcodeproj/project.pbxproj b/YouTube Video Player Demo.xcodeproj/project.pbxproj index 1af531d3..6eddd6fe 100644 --- a/YouTube Video Player Demo.xcodeproj/project.pbxproj +++ b/YouTube Video Player Demo.xcodeproj/project.pbxproj @@ -213,7 +213,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_OPTIMIZATION_LEVEL = 0; @@ -247,7 +247,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = YES; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_PREPROCESSOR_DEFINITIONS = "NS_BLOCK_ASSERTIONS=1"; GCC_WARN_ABOUT_RETURN_TYPE = YES; diff --git a/YouTube Video Player Demo/YouTube Video Player Demo-Info.plist b/YouTube Video Player Demo/YouTube Video Player Demo-Info.plist index 12528ffb..c60cca64 100644 --- a/YouTube Video Player Demo/YouTube Video Player Demo-Info.plist +++ b/YouTube Video Player Demo/YouTube Video Player Demo-Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.0.0 + 1.1.0 CFBundleVersion ${CURRENT_PROJECT_VERSION} LSRequiresIPhoneOS