Skip to content

Commit

Permalink
Merge branch 'release/1.1.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
0xced committed Aug 9, 2013
2 parents ba3acd9 + 78f2837 commit ae02d34
Show file tree
Hide file tree
Showing 7 changed files with 198 additions and 83 deletions.
4 changes: 2 additions & 2 deletions XCDYouTubeVideoPlayerViewController.podspec
Original file line number Diff line number Diff line change
@@ -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 = { "Cédric Luthi" => "[email protected]" }
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'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,19 @@ 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;
MP_EXTERN NSString *const XCDMetadataKeySmallThumbnailURL;
MP_EXTERN NSString *const XCDMetadataKeyMediumThumbnailURL;
MP_EXTERN NSString *const XCDMetadataKeyLargeThumbnailURL;

@interface XCDYouTubeVideoPlayerViewController : MPMoviePlayerViewController

- (id) initWithVideoIdentifier:(NSString *)videoIdentifier;
Expand All @@ -29,7 +42,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
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand All @@ -37,11 +43,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
{
Expand All @@ -66,9 +74,17 @@ - (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;
}

- (void) dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}

- (void) setVideoIdentifier:(NSString *)videoIdentifier
{
if (![NSThread isMainThread])
Expand All @@ -94,8 +110,9 @@ - (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];
objc_setAssociatedObject(view, MoviePlayerKey, self.moviePlayer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
if (![view.subviews containsObject:self.moviePlayer.view])
[view addSubview:self.moviePlayer.view];
objc_setAssociatedObject(view, XCDYouTubeVideoPlayerViewControllerKey, self, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (void) startVideoInfoRequest
Expand Down Expand Up @@ -129,16 +146,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
Expand All @@ -159,25 +181,34 @@ - (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
{
[self finishWithError:error];
}

#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];
[application setStatusBarHidden:self.statusBarHidden withAnimation:UIStatusBarAnimationFade];
[application setStatusBarStyle:self.statusBarStyle animated:YES];
}

#pragma mark - URL Parsing

- (NSURL *) videoURLWithData:(NSData *)data error:(NSError **)error
Expand All @@ -192,9 +223,11 @@ - (NSURL *) videoURLWithData:(NSData *)data error:(NSError **)error
{
NSDictionary *stream = DictionaryWithQueryString(streamQuery, queryEncoding);
NSString *type = stream[@"type"];
if ([AVURLAsset isPlayableExtendedMIMEType:type])
NSString *urlString = stream[@"url"];
NSString *signature = stream[@"sig"];
if (urlString && signature && [AVURLAsset isPlayableExtendedMIMEType:type])
{
NSURL *streamURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@&signature=%@", stream[@"url"], stream[@"sig"]]];
NSURL *streamURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@&signature=%@", urlString, signature]];
streamURLs[@([stream[@"itag"] integerValue])] = streamURL;
}
}
Expand All @@ -203,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)
Expand Down
4 changes: 2 additions & 2 deletions YouTube Video Player Demo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
77 changes: 77 additions & 0 deletions YouTube Video Player Demo/AppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#import "AppDelegate.h"

#import "XCDYouTubeVideoPlayerViewController.h"
#import "DemoViewController.h"

@implementation AppDelegate
Expand All @@ -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
Loading

0 comments on commit ae02d34

Please sign in to comment.