123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891 |
- /* Copyright (C) 2012 IGN Entertainment, Inc. */
- #import "VideoPlayerKit.h"
- #import "FullScreenViewController.h"
- #import "WFCUImage.h"
- // #import "ShareThis.h"
- NSString * const kVideoPlayerVideoChangedNotification = @"VideoPlayerVideoChangedNotification";
- NSString * const kVideoPlayerWillHideControlsNotification = @"VideoPlayerWillHideControlsNotitication";
- NSString * const kVideoPlayerWillShowControlsNotification = @"VideoPlayerWillShowControlsNotification";
- NSString * const kTrackEventVideoStart = @"Video Start";
- NSString * const kTrackEventVideoLiveStart = @"Video Live Start";
- NSString * const kTrackEventVideoComplete = @"Video Complete";
- // Match the controls animation duration with status bar duration
- static const NSTimeInterval controlsAnimationDuration = 0.4;
- @interface VideoPlayerKit () <UIGestureRecognizerDelegate>
- @property (readwrite, strong) NSDictionary *currentVideoInfo;
- @property (readwrite, strong) VideoPlayerView *videoPlayerView;
- @property (readwrite) BOOL restoreVideoPlayStateAfterScrubbing;
- @property (readwrite, strong) id scrubberTimeObserver;
- @property (readwrite, strong) id playClockTimeObserver;
- @property (readwrite) BOOL seekToZeroBeforePlay;
- @property (readwrite) BOOL rotationIsLocked;
- @property (readwrite) BOOL playerIsBuffering;
- @property (nonatomic, weak) UIView *containingView;
- @property (nonatomic, weak) UIView *topView;
- @property (readwrite) BOOL fullScreenModeToggled;
- @property (nonatomic) BOOL isAlwaysFullscreen;
- @property (nonatomic, readwrite) BOOL isPlaying;
- @property (nonatomic, strong) FullScreenViewController *fullscreenViewController;
- @property (nonatomic) CGRect previousBounds;
- @property (nonatomic) BOOL hideTopViewWithControls;
- @property (nonatomic) UIStatusBarStyle previousStatusBarStyle;
- @end
- @implementation VideoPlayerKit {
- BOOL playWhenReady;
- BOOL scrubBuffering;
- BOOL showShareOptions;
- }
- - (void)setTopView:(UIView *)topView
- {
- _topView = topView;
- if (self.hideTopViewWithControls) {
- __weak UIView *weakTopView = _topView;
- [[NSNotificationCenter defaultCenter] removeObserver:self];
-
- [[NSNotificationCenter defaultCenter] addObserverForName:kVideoPlayerWillHideControlsNotification
- object:self
- queue:[NSOperationQueue mainQueue]
- usingBlock:^(NSNotification *note) {
- [UIView animateWithDuration:controlsAnimationDuration
- animations:^{
- [weakTopView setAlpha:0.0f];
- }];
- }];
-
- [[NSNotificationCenter defaultCenter] addObserverForName:kVideoPlayerWillShowControlsNotification
- object:self
- queue:[NSOperationQueue mainQueue]
- usingBlock:^(NSNotification *note) {
- [UIView animateWithDuration:controlsAnimationDuration
- animations:^{
- [weakTopView setAlpha:1.0f];
- }];
- }];
- }
- }
- - (id)initWithContainingView:(UIView *)containingView optionalTopView:(UIView *)topView hideTopViewWithControls:(BOOL)hideTopViewWithControls
- {
- if ((self = [super init])) {
- self.containingView = containingView;
- self.hideTopViewWithControls = hideTopViewWithControls;
- self.topView = topView;
- self.previousStatusBarStyle = [[UIApplication sharedApplication] statusBarStyle];
- self.view.backgroundColor = [UIColor blackColor];
- }
-
- return self;
- }
- + (VideoPlayerKit *)videoPlayerWithContainingViewController:(UIViewController *)containingViewController
- optionalTopView:(UIView *)topView
- hideTopViewWithControls:(BOOL)hideTopViewWithControls
- {
- VideoPlayerKit *videoPlayer = [[VideoPlayerKit alloc] initWithContainingView:containingViewController.view
- optionalTopView:topView
- hideTopViewWithControls:hideTopViewWithControls];
-
- return videoPlayer;
- }
- + (VideoPlayerKit *)videoPlayerWithContainingView:(UIView *)containingView
- optionalTopView:(UIView *)topView
- hideTopViewWithControls:(BOOL)hideTopViewWithControls
- {
- VideoPlayerKit *videoPlayer = [[VideoPlayerKit alloc] initWithContainingView:containingView
- optionalTopView:topView
- hideTopViewWithControls:hideTopViewWithControls];
-
- return videoPlayer;
- }
- - (void)setControlsEdgeInsets:(UIEdgeInsets)controlsEdgeInsets
- {
- if (!self.videoPlayerView) {
- self.videoPlayerView = [[VideoPlayerView alloc] initWithFrame:self.containingView.bounds];
- }
- _controlsEdgeInsets = controlsEdgeInsets;
- self.videoPlayerView.controlsEdgeInsets = _controlsEdgeInsets;
- [self.view setNeedsLayout];
- }
- - (void)dealloc
- {
- [[NSNotificationCenter defaultCenter] removeObserver:self];
-
- [self removeObserversFromVideoPlayerItem];
- [self removePlayerTimeObservers];
- }
- - (void)removeObserversFromVideoPlayerItem
- {
- [self.videoPlayer.currentItem removeObserver:self forKeyPath:@"status"];
- [self.videoPlayer.currentItem removeObserver:self forKeyPath:@"playbackBufferEmpty"];
- [self.videoPlayer.currentItem removeObserver:self forKeyPath:@"playbackLikelyToKeepUp"];
- [self.videoPlayer.currentItem removeObserver:self forKeyPath:@"loadedTimeRanges"];
- [_videoPlayer removeObserver:self forKeyPath:@"externalPlaybackActive"];
- [_videoPlayer removeObserver:self forKeyPath:@"airPlayVideoActive"];
- }
- - (void)loadView
- {
- if (!self.videoPlayerView) {
- self.videoPlayerView = [[VideoPlayerView alloc] initWithFrame:self.containingView.bounds];
- }
-
- if (self.topView) {
- self.topView.frame = CGRectMake(0, 0, self.videoPlayerView.frame.size.width, self.topView.frame.size.height);
- [self.videoPlayerView addSubview:self.topView];
- }
-
- self.view = self.videoPlayerView;
- }
- - (void)viewDidLoad
- {
- [super viewDidLoad];
- _currentVideoInfo = [[NSDictionary alloc] init];
-
- [_videoPlayerView.playPauseButton addTarget:self action:@selector(playPauseHandler) forControlEvents:UIControlEventTouchUpInside];
-
- [_videoPlayerView.fullScreenButton addTarget:self action:@selector(fullScreenButtonHandler) forControlEvents:UIControlEventTouchUpInside];
-
- [self.videoPlayerView.shareButton addTarget:self action:@selector(shareButtonHandler) forControlEvents:UIControlEventTouchUpInside];
-
- [_videoPlayerView.videoScrubber addTarget:self action:@selector(scrubbingDidBegin) forControlEvents:UIControlEventTouchDown];
- [_videoPlayerView.videoScrubber addTarget:self action:@selector(scrubberIsScrolling) forControlEvents:UIControlEventValueChanged];
- [_videoPlayerView.videoScrubber addTarget:self action:@selector(scrubbingDidEnd) forControlEvents:(UIControlEventTouchUpInside | UIControlEventTouchCancel)];
-
- UITapGestureRecognizer *playerTouchedGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(videoTapHandler)];
- playerTouchedGesture.delegate = self;
- [_videoPlayerView addGestureRecognizer:playerTouchedGesture];
-
- UIPinchGestureRecognizer *pinchRecognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(pinchGesture:)];
- [pinchRecognizer setDelegate:self];
- [self.view addGestureRecognizer:pinchRecognizer];
- }
- - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
- {
- if ([touch.view isDescendantOfView:self.videoPlayerView.playerControlBar] || [touch.view isDescendantOfView:self.videoPlayerView.shareButton]) {
- return NO;
- }
- return YES;
- }
- - (void)viewWillAppear:(BOOL)animated
- {
- [super viewWillAppear:animated];
-
- if (self.fullScreenModeToggled) {
- BOOL isHidingPlayerControls = self.videoPlayerView.playerControlBar.alpha == 0;
- [[UIApplication sharedApplication] setStatusBarHidden:isHidingPlayerControls withAnimation:UIStatusBarAnimationNone];
- } else {
- [[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationNone];
- }
- }
- - (void)presentShareOptions
- {
- showShareOptions = NO;
- // [ShareThis showShareOptionsToShareUrl:[_currentVideoInfo objectForKey:@"shareURL"] title:[_currentVideoInfo objectForKey:@"title"] image:nil onViewController:[[[UIApplication sharedApplication] keyWindow] rootViewController] forTypeOfContent:STContentTypeVideo];
- }
- - (void)shareButtonHandler
- {
- // Minimize the video if fullscreen so that ShareThis can work
- if (self.fullScreenModeToggled) {
- showShareOptions = YES;
- [self minimizeVideo];
- } else {
- [self presentShareOptions];
- }
- }
- - (void)playVideoWithTitle:(NSString *)title URL:(NSURL *)url videoID:(NSString *)videoID shareURL:(NSURL *)shareURL isStreaming:(BOOL)streaming playInFullScreen:(BOOL)playInFullScreen
- {
- [self.videoPlayer pause];
-
- [[_videoPlayerView activityIndicator] startAnimating];
- // Reset the buffer bar back to 0
- [self.videoPlayerView.progressView setProgress:0 animated:NO];
- [self showControls];
-
- NSString *vidID = videoID ?: @"";
- _currentVideoInfo = @{ @"title": title ?: @"", @"videoID": vidID, @"isStreaming": @(streaming), @"shareURL": shareURL ? @"" : url};
-
- [[NSNotificationCenter defaultCenter] postNotificationName:kVideoPlayerVideoChangedNotification
- object:self
- userInfo:_currentVideoInfo];
- if ([self.delegate respondsToSelector:@selector(trackEvent:videoID:title:)]) {
- if (streaming) {
- [self.delegate trackEvent:kTrackEventVideoLiveStart videoID:vidID title:title];
- } else {
- [self.delegate trackEvent:kTrackEventVideoStart videoID:vidID title:title];
- }
- }
-
- [_videoPlayerView.currentPositionLabel setText:@""];
- [_videoPlayerView.timeLeftLabel setText:@""];
- _videoPlayerView.videoScrubber.value = 0;
-
- [_videoPlayerView setTitle:title];
-
- [[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:@{
- MPMediaItemPropertyTitle: title,
- }];
-
- [self setURL:url];
-
- [self syncPlayPauseButtons];
-
- if (playInFullScreen) {
- self.isAlwaysFullscreen = YES;
- [self launchFullScreen];
- } else {
- [self.containingView addSubview:self.videoPlayerView];
- }
- }
- - (void)showCannotFetchStreamError
- {
- UIAlertView *alertView = [[UIAlertView alloc]
- initWithTitle:@"Sad Panda says..."
- message:@"I can't seem to fetch that stream. Please try again later."
- delegate:nil
- cancelButtonTitle:@"Bummer!"
- otherButtonTitles:nil];
- [alertView show];
- }
- - (void)playPauseHandler
- {
- if (_seekToZeroBeforePlay) {
- _seekToZeroBeforePlay = NO;
- [_videoPlayer seekToTime:kCMTimeZero];
- }
-
- if ([self isPlaying]) {
- [_videoPlayer pause];
- } else {
- [self playVideo];
- [[_videoPlayerView activityIndicator] stopAnimating];
- }
-
- [self syncPlayPauseButtons];
- [self showControls];
- }
- - (void)launchFullScreen
- {
- if (!self.fullScreenModeToggled) {
- self.fullScreenModeToggled = YES;
-
- if (!self.isAlwaysFullscreen) {
- [self hideControlsAnimated:YES];
- }
-
- [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];
- [self syncFullScreenButton:[[UIApplication sharedApplication] statusBarOrientation]];
-
- if (!self.fullscreenViewController) {
- self.fullscreenViewController = [[FullScreenViewController alloc] init];
- self.fullscreenViewController.allowPortraitFullscreen = self.allowPortraitFullscreen;
- }
-
- [self.videoPlayerView setFullscreen:YES];
- [self.fullscreenViewController.view addSubview:self.videoPlayerView];
-
-
- if (self.topView) {
- [self.topView removeFromSuperview];
- [self.fullscreenViewController.view addSubview:self.topView];
- }
-
- if (self.isAlwaysFullscreen) {
- self.videoPlayerView.alpha = 0.0;
- if (self.topView) {
- self.topView.alpha = 0.0;
- }
- } else {
- self.previousBounds = self.videoPlayerView.frame;
- [UIView animateWithDuration:0.45f
- delay:0.0f
- options:UIViewAnimationCurveLinear
- animations:^{
- [self.videoPlayerView setCenter:CGPointMake( self.videoPlayerView.superview.bounds.size.width / 2, ( self.videoPlayerView.superview.bounds.size.height / 2))];
- self.videoPlayerView.bounds = self.videoPlayerView.superview.bounds;
- }
- completion:nil];
- }
- self.fullscreenViewController.modalPresentationStyle = UIModalPresentationFullScreen;
- [[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:self.fullscreenViewController animated:YES completion:^{
- if (self.isAlwaysFullscreen) {
- self.videoPlayerView.frame = CGRectMake(self.videoPlayerView.superview.bounds.size.width / 2, self.videoPlayerView.superview.bounds.size.height / 2, 0, 0);
- self.previousBounds = CGRectMake(self.videoPlayerView.superview.bounds.size.width / 2, self.videoPlayerView.superview.bounds.size.height / 2, 0, 0);
- [self.videoPlayerView setCenter:CGPointMake( self.videoPlayerView.superview.bounds.size.width / 2, self.videoPlayerView.superview.bounds.size.height / 2)];
- [UIView animateWithDuration:0.25f
- delay:0.0f
- options:UIViewAnimationCurveLinear
- animations:^{
- self.videoPlayerView.alpha = 1.0;
- self.topView.alpha = 1.0;
- }
- completion:nil];
-
- self.videoPlayerView.frame = self.videoPlayerView.superview.bounds;
- }
-
- if (self.topView) {
- self.topView.frame = CGRectMake(0, 0, self.videoPlayerView.frame.size.width, self.topView.frame.size.height);
- }
-
- if ([self.delegate respondsToSelector:@selector(setFullScreenToggled:)]) {
- [self.delegate setFullScreenToggled:self.fullScreenModeToggled];
- }
- }];
- }
- }
- - (void)minimizeVideo
- {
- if (self.fullScreenModeToggled) {
- self.fullScreenModeToggled = NO;
- [self.videoPlayerView setFullscreen:NO];
- [self hideControlsAnimated:NO];
- [self syncFullScreenButton:self.interfaceOrientation];
-
- if (self.topView) {
- [self.topView removeFromSuperview];
- [self.videoPlayerView addSubview:self.topView];
- }
-
- if (self.isAlwaysFullscreen) {
- [self.videoPlayer pause];
- [UIView animateWithDuration:0.45f
- delay:0.0f
- options:UIViewAnimationCurveLinear
- animations:^{
- self.videoPlayerView.frame = self.previousBounds;
- }
- completion:^(BOOL success){
-
- if (showShareOptions) {
- [self presentShareOptions];
- }
-
- [self.videoPlayerView removeFromSuperview];
- [[NSNotificationCenter defaultCenter] postNotificationName:@"kVideoPlayerKitPlayCanceled" object:nil];
- }];
- } else {
- [UIView animateWithDuration:0.45f
- delay:0.0f
- options:UIViewAnimationCurveLinear
- animations:^{
- self.videoPlayerView.frame = self.previousBounds;
- }
- completion:^(BOOL success){
- if (showShareOptions) {
- [self presentShareOptions];
- }
- }];
-
- [self.videoPlayerView removeFromSuperview];
- [self.containingView addSubview:self.videoPlayerView];
- }
-
- [[UIApplication sharedApplication] setStatusBarStyle:self.previousStatusBarStyle];
-
- [[UIApplication sharedApplication].keyWindow.rootViewController dismissViewControllerAnimated:self.isAlwaysFullscreen completion:^{
- if (!self.isAlwaysFullscreen) {
- [self showControls];
- }
- [[UIApplication sharedApplication] setStatusBarHidden:NO
- withAnimation:UIStatusBarAnimationFade];
-
- if ([self.delegate respondsToSelector:@selector(setFullScreenToggled:)]) {
- [self.delegate setFullScreenToggled:self.fullScreenModeToggled];
- }
- }];
- }
- }
- - (void)fullScreenButtonHandler
- {
- [self showControls];
-
- if (self.fullScreenModeToggled) {
- [self minimizeVideo];
- } else {
- [self launchFullScreen];
- }
- }
- - (void)pinchGesture:(id)sender
- {
- if([(UIPinchGestureRecognizer *)sender state] == UIGestureRecognizerStateEnded) {
- [self fullScreenButtonHandler];
- }
- }
- - (void)forceOrientationChange
- {
- _rotationIsLocked = YES;
- [self performSelector:@selector(unlockRotationLock) withObject:nil afterDelay:0.5];
-
- if ([[[UIDevice currentDevice] systemVersion] floatValue] < 6.0) {
- UIWindow *window = [[UIApplication sharedApplication] keyWindow];
- UIView *view = [window.subviews objectAtIndex:0];
- [view removeFromSuperview];
- [window addSubview:view];
-
- [_videoPlayerView.superview layoutSubviews];
- } else {
- // Have the VideoPlayerVC's parent VC implement rotation trigger
- if ([self.delegate respondsToSelector:@selector(setFullScreenToggled:)]) {
- [self.delegate setFullScreenToggled:self.fullScreenModeToggled];
- }
- }
- }
- - (void)unlockRotationLock
- {
- _rotationIsLocked = NO;
- }
- - (void)videoTapHandler
- {
- if (_videoPlayerView.playerControlBar.alpha) {
- [self hideControlsAnimated:YES];
- } else {
- [self showControls];
- }
- }
- - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
- {
- return YES;
- }
- - (void)setURL:(NSURL *)url
- {
- AVPlayerItem *playerItem = [AVPlayerItem playerItemWithURL:url];
-
- [playerItem addObserver:self
- forKeyPath:@"status"
- options:NSKeyValueObservingOptionNew
- context:nil];
-
- [playerItem addObserver:self
- forKeyPath:@"playbackBufferEmpty"
- options:NSKeyValueObservingOptionNew
- context:nil];
-
- [playerItem addObserver:self
- forKeyPath:@"playbackLikelyToKeepUp"
- options:NSKeyValueObservingOptionNew
- context:nil];
-
- [playerItem addObserver:self
- forKeyPath:@"loadedTimeRanges"
- options:NSKeyValueObservingOptionNew
- context:nil];
-
- if (!self.videoPlayer) {
- _videoPlayer = [AVPlayer playerWithPlayerItem:playerItem];
- [_videoPlayer setAllowsAirPlayVideo:YES];
- [_videoPlayer setUsesAirPlayVideoWhileAirPlayScreenIsActive:YES];
-
- if ([_videoPlayer respondsToSelector:@selector(setAllowsExternalPlayback:)]) { // iOS 6 API
- [_videoPlayer setAllowsExternalPlayback:YES];
- }
-
- [_videoPlayerView setPlayer:_videoPlayer];
- } else {
- [self removeObserversFromVideoPlayerItem];
- [self.videoPlayer replaceCurrentItemWithPlayerItem:playerItem];
- }
-
- // iOS 5
- [_videoPlayer addObserver:self forKeyPath:@"airPlayVideoActive" options:NSKeyValueObservingOptionNew context:nil];
-
- // iOS 6
- [_videoPlayer addObserver:self
- forKeyPath:@"externalPlaybackActive"
- options:NSKeyValueObservingOptionNew
- context:nil];
-
- [[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(playerItemDidReachEnd:)
- name:AVPlayerItemDidPlayToEndTimeNotification
- object:self.videoPlayer.currentItem];
-
- [[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(playerItemDidReachEnd:)
- name:AVPlayerItemFailedToPlayToEndTimeNotification
- object:self.videoPlayer.currentItem];
-
- }
- // Wait for the video player status to change to ready before initializing video player controls
- - (void)observeValueForKeyPath:(NSString *)keyPath
- ofObject:(id)object
- change:(NSDictionary *)change
- context:(void *)context
- {
- if (object == _videoPlayer
- && ([keyPath isEqualToString:@"externalPlaybackActive"] || [keyPath isEqualToString:@"airPlayVideoActive"])) {
- BOOL externalPlaybackActive = [[change objectForKey:NSKeyValueChangeNewKey] boolValue];
- [[_videoPlayerView airplayIsActiveView] setHidden:!externalPlaybackActive];
- return;
- }
-
- if (object != [_videoPlayer currentItem]) {
- return;
- }
-
- if ([keyPath isEqualToString:@"status"]) {
- AVPlayerStatus status = [[change objectForKey:NSKeyValueChangeNewKey] integerValue];
- switch (status) {
- case AVPlayerStatusReadyToPlay:
- playWhenReady = YES;
- if (![self isPlaying]) {
- [self playVideo];
- }
- break;
- case AVPlayerStatusFailed:
- // TODO:
- [self removeObserversFromVideoPlayerItem];
- [self removePlayerTimeObservers];
- self.videoPlayer = nil;
- [self minimizeVideo];
- break;
- }
- } else if ([keyPath isEqualToString:@"playbackBufferEmpty"] && _videoPlayer.currentItem.playbackBufferEmpty) {
- self.playerIsBuffering = YES;
- [[_videoPlayerView activityIndicator] startAnimating];
- [self syncPlayPauseButtons];
- } else if ([keyPath isEqualToString:@"playbackLikelyToKeepUp"] && _videoPlayer.currentItem.playbackLikelyToKeepUp) {
- if (![self isPlaying] && playWhenReady)
- {
- if (self.playerIsBuffering || scrubBuffering) {
- if (self.restoreVideoPlayStateAfterScrubbing) {
- self.restoreVideoPlayStateAfterScrubbing = NO;
- [self playVideo];
- }
- } else {
- [self playVideo];
- }
- }
- [[_videoPlayerView activityIndicator] stopAnimating];
-
- } else if ([keyPath isEqualToString:@"loadedTimeRanges"]) {
- float durationTime = CMTimeGetSeconds([[self.videoPlayer currentItem] duration]);
- float bufferTime = [self availableDuration];
- [self.videoPlayerView.progressView setProgress:bufferTime/durationTime animated:YES];
- }
-
- return;
- }
- - (float)availableDuration
- {
- NSArray *loadedTimeRanges = [[self.videoPlayer currentItem] loadedTimeRanges];
-
- // Check to see if the timerange is not an empty array, fix for when video goes on airplay
- // and video doesn't include any time ranges
- if ([loadedTimeRanges count] > 0) {
- CMTimeRange timeRange = [[loadedTimeRanges objectAtIndex:0] CMTimeRangeValue];
- float startSeconds = CMTimeGetSeconds(timeRange.start);
- float durationSeconds = CMTimeGetSeconds(timeRange.duration);
- return (startSeconds + durationSeconds);
- } else {
- return 0.0f;
- }
- }
- - (void)playVideo
- {
- if (self.view.superview) {
- self.playerIsBuffering = NO;
- scrubBuffering = NO;
- playWhenReady = NO;
- // Configuration is done, ready to start.
- [self.videoPlayer play];
- [self updatePlaybackProgress];
- }
- }
- - (void)showControls
- {
- [[NSNotificationCenter defaultCenter] postNotificationName:kVideoPlayerWillShowControlsNotification
- object:self
- userInfo:nil];
- [UIView animateWithDuration:controlsAnimationDuration animations:^{
- self.videoPlayerView.playerControlBar.alpha = 1.0;
- self.videoPlayerView.titleLabel.alpha = 1.0;
- self.videoPlayerView.shareButton.alpha = 1.0;
- } completion:nil];
-
- if (self.fullScreenModeToggled) {
- [[UIApplication sharedApplication] setStatusBarHidden:NO
- withAnimation:UIStatusBarAnimationFade];
- }
- [NSObject cancelPreviousPerformRequestsWithTarget:self
- selector:@selector(hideControlsAnimated:)
- object:[NSString stringWithFormat:@"YES"]];
-
- if ([self isPlaying]) {
- [self performSelector:@selector(hideControlsAnimated:)
- withObject:[NSString stringWithFormat:@"YES"]
- afterDelay:4.0];
- }
- }
- - (void)hideControlsAnimated:(BOOL)animated
- {
- [[NSNotificationCenter defaultCenter] postNotificationName:kVideoPlayerWillHideControlsNotification
- object:self
- userInfo:nil];
- if (animated) {
- [UIView animateWithDuration:controlsAnimationDuration animations:^{
- self.videoPlayerView.playerControlBar.alpha = 0;
- self.videoPlayerView.titleLabel.alpha = 0;
- _videoPlayerView.shareButton.alpha = 0;
- } completion:nil];
-
- if (self.fullScreenModeToggled) {
- [[UIApplication sharedApplication] setStatusBarHidden:YES
- withAnimation:UIStatusBarAnimationFade];
- }
-
- } else {
- self.videoPlayerView.playerControlBar.alpha = 0;
- self.videoPlayerView.titleLabel.alpha = 0;
- _videoPlayerView.shareButton.alpha = 0;
- if (self.fullScreenModeToggled) {
- [[UIApplication sharedApplication] setStatusBarHidden:YES
- withAnimation:UIStatusBarAnimationNone];
- }
- }
- }
- - (void)updatePlaybackProgress
- {
- [self syncPlayPauseButtons];
- [self showControls];
-
- double interval = .1f;
-
- CMTime playerDuration = [self playerItemDuration];
- if (CMTIME_IS_INVALID(playerDuration)) {
- return;
- }
-
- double duration = CMTimeGetSeconds(playerDuration);
- if (CMTIME_IS_INDEFINITE(playerDuration) || duration <= 0) {
- [_videoPlayerView.videoScrubber setHidden:YES];
- [_videoPlayerView.progressView setHidden:YES];
- [self syncPlayClock];
- return;
- }
-
- [_videoPlayerView.videoScrubber setHidden:NO];
- [_videoPlayerView.progressView setHidden:NO];
- CGFloat width = CGRectGetWidth([_videoPlayerView.videoScrubber bounds]);
- interval = 0.5f * duration / width;
- __weak VideoPlayerKit *vpvc = self;
- _scrubberTimeObserver = [_videoPlayer addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(interval, NSEC_PER_SEC)
- queue:NULL
- usingBlock:^(CMTime time) {
- [vpvc syncScrubber];
- }];
-
- // Update the play clock every second
- _playClockTimeObserver = [_videoPlayer addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(1, NSEC_PER_SEC)
- queue:NULL
- usingBlock:^(CMTime time) {
- [vpvc syncPlayClock];
- }];
-
- }
- -(void)removePlayerTimeObservers
- {
- if (_scrubberTimeObserver) {
- [_videoPlayer removeTimeObserver:_scrubberTimeObserver];
- _scrubberTimeObserver = nil;
- }
-
- if (_playClockTimeObserver) {
- [_videoPlayer removeTimeObserver:_playClockTimeObserver];
- _playClockTimeObserver = nil;
- }
- }
- - (void)playerItemDidReachEnd:(NSNotification *)notification
- {
- [self syncPlayPauseButtons];
- _seekToZeroBeforePlay = YES;
- if ([self.delegate respondsToSelector:@selector(trackEvent:videoID:title:)]) {
- [self.delegate trackEvent:kTrackEventVideoComplete videoID:[_currentVideoInfo objectForKey:@"videoID"] title:[_currentVideoInfo objectForKey:@"title"]];
- }
-
- [self minimizeVideo];
- }
- - (void)syncScrubber
- {
- CMTime playerDuration = [self playerItemDuration];
- if (CMTIME_IS_INVALID(playerDuration)) {
- _videoPlayerView.videoScrubber.minimumValue = 0.0;
- return;
- }
-
- double duration = CMTimeGetSeconds(playerDuration);
- if (isfinite(duration)) {
- float minValue = [_videoPlayerView.videoScrubber minimumValue];
- float maxValue = [_videoPlayerView.videoScrubber maximumValue];
- double time = CMTimeGetSeconds([_videoPlayer currentTime]);
-
- [_videoPlayerView.videoScrubber setValue:(maxValue - minValue) * time / duration + minValue];
- }
- }
- - (void)syncPlayClock
- {
- CMTime playerDuration = [self playerItemDuration];
- if (CMTIME_IS_INVALID(playerDuration)) {
- return;
- }
-
- if (CMTIME_IS_INDEFINITE(playerDuration)) {
- [_videoPlayerView.currentPositionLabel setText:@"LIVE"];
- [_videoPlayerView.timeLeftLabel setText:@""];
- return;
- }
-
- double duration = CMTimeGetSeconds(playerDuration);
- if (isfinite(duration)) {
- double currentTime = floor(CMTimeGetSeconds([_videoPlayer currentTime]));
- double timeLeft = floor(duration - currentTime);
-
- if (currentTime <= 0) {
- currentTime = 0;
- timeLeft = floor(duration);
- }
-
- [_videoPlayerView.currentPositionLabel setText:[NSString stringWithFormat:@"%@ ", [self stringFormattedTimeFromSeconds:¤tTime]]];
- if (!self.showStaticEndTime) {
- [_videoPlayerView.timeLeftLabel setText:[NSString stringWithFormat:@"-%@", [self stringFormattedTimeFromSeconds:&timeLeft]]];
- } else {
- [_videoPlayerView.timeLeftLabel setText:[NSString stringWithFormat:@"%@", [self stringFormattedTimeFromSeconds:&duration]]];
- }
- }
- }
- - (CMTime)playerItemDuration
- {
- if (_videoPlayer.status == AVPlayerItemStatusReadyToPlay) {
- return([_videoPlayer.currentItem duration]);
- }
-
- return(kCMTimeInvalid);
- }
- - (BOOL)isPlaying
- {
- return [_videoPlayer rate] != 0.0;
- }
- - (void)syncPlayPauseButtons
- {
- if ([self isPlaying]) {
- [_videoPlayerView.playPauseButton setImage:[WFCUImage imageNamed:@"pause-button"] forState:UIControlStateNormal];
- } else {
- [_videoPlayerView.playPauseButton setImage:[WFCUImage imageNamed:@"play-button"] forState:UIControlStateNormal];
- }
- }
- - (void)syncFullScreenButton:(UIInterfaceOrientation)toInterfaceOrientation
- {
- if (_fullScreenModeToggled) {
- [_videoPlayerView.fullScreenButton setImage:[WFCUImage imageNamed:@"minimize-button"] forState:UIControlStateNormal];
- } else {
- [_videoPlayerView.fullScreenButton setImage:[WFCUImage imageNamed:@"fullscreen-button"] forState:UIControlStateNormal];
- }
- }
- -(void)scrubbingDidBegin
- {
- if ([self isPlaying]) {
- [_videoPlayer pause];
- [self syncPlayPauseButtons];
- self.restoreVideoPlayStateAfterScrubbing = YES;
- [self showControls];
- }
- }
- -(void)scrubberIsScrolling
- {
- CMTime playerDuration = [self playerItemDuration];
- double duration = CMTimeGetSeconds(playerDuration);
- if (isfinite(duration)) {
- double currentTime = floor(duration * _videoPlayerView.videoScrubber.value);
- double timeLeft = floor(duration - currentTime);
-
- if (currentTime <= 0) {
- currentTime = 0;
- timeLeft = floor(duration);
- }
-
- [_videoPlayerView.currentPositionLabel setText:[NSString stringWithFormat:@"%@ ", [self stringFormattedTimeFromSeconds:¤tTime]]];
-
- if (!self.showStaticEndTime) {
- [_videoPlayerView.timeLeftLabel setText:[NSString stringWithFormat:@"-%@", [self stringFormattedTimeFromSeconds:&timeLeft]]];
- } else {
- [_videoPlayerView.timeLeftLabel setText:[NSString stringWithFormat:@"%@", [self stringFormattedTimeFromSeconds:&duration]]];
- }
- [_videoPlayer seekToTime:CMTimeMakeWithSeconds((float) currentTime, NSEC_PER_SEC)];
- }
- }
- -(void)scrubbingDidEnd
- {
- if (self.restoreVideoPlayStateAfterScrubbing) {
- scrubBuffering = YES;
- }
- [[_videoPlayerView activityIndicator] startAnimating];
-
- [self showControls];
- }
- - (NSString *)stringFormattedTimeFromSeconds:(double *)seconds
- {
- NSDate *date = [NSDate dateWithTimeIntervalSince1970:*seconds];
- NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
- [formatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
- if (*seconds >= 3600) {
- [formatter setDateFormat:@"HH:mm:ss"];
- } else {
- [formatter setDateFormat:@"mm:ss"];
- }
-
- return [formatter stringFromDate:date];
- }
- @end
|