From 9da4e6b4c3b86d69a48263adbc7d2c25846fd0de Mon Sep 17 00:00:00 2001 From: Brian Jordan Date: Mon, 16 Apr 2012 21:51:44 -0400 Subject: add scrubber control. Closes issue #17 --- Video Tuneup/ViewController.h | 14 +++ Video Tuneup/ViewController.m | 168 +++++++++++++++++++++++++- Video Tuneup/en.lproj/ViewController_iPad.xib | 58 ++++++++- 3 files changed, 230 insertions(+), 10 deletions(-) diff --git a/Video Tuneup/ViewController.h b/Video Tuneup/ViewController.h index 99a7a1b..8c84b4f 100644 --- a/Video Tuneup/ViewController.h +++ b/Video Tuneup/ViewController.h @@ -16,6 +16,11 @@ @interface ViewController : UIViewController { AVURLAsset *asset; AVURLAsset *songAsset; + + // Related to scrubbing + float mRestoreAfterScrubbingRate; + BOOL seekToZeroBeforePlay; + id mTimeObserver; } @property (nonatomic, retain) AVPlayer *player; @@ -27,6 +32,8 @@ @property (nonatomic, retain) IBOutlet UIButton *rewindButton; @property (nonatomic, retain) IBOutlet UIToolbar *videoNavBar; @property (nonatomic, retain) IBOutlet UILabel *exportStatus; +@property (nonatomic, retain) IBOutlet UISlider* mScrubber; + - (void)hideCameraRollText; - (IBAction)loadAssetFromFile:sender; @@ -36,6 +43,13 @@ - (IBAction)rewind:sender; - (IBAction)exportToCameraRoll:sender; - (void)syncUI; +- (void)syncScrubber; +- (void)beginScrubbing:(id)sender; +- (void)scrub:(id)sender; +- (void)endScrubbing:(id)sender; +- (BOOL)isScrubbing; +- (void)initScrubberTimer; +- (CMTime)playerItemDuration; - (void)exportDidFinish:(AVAssetExportSession*)session; @end \ No newline at end of file diff --git a/Video Tuneup/ViewController.m b/Video Tuneup/ViewController.m index 4fc5a99..83a82a9 100644 --- a/Video Tuneup/ViewController.m +++ b/Video Tuneup/ViewController.m @@ -15,7 +15,8 @@ static const NSString *ItemStatusContext; @implementation ViewController -@synthesize player, playerItem, playerView, playButton, pauseButton, rewindButton, editor, videoNavBar, exportStatus; +@synthesize player, playerItem, playerView, playButton, pauseButton, rewindButton, editor, videoNavBar, exportStatus, +mScrubber; #pragma mark - Video playback @@ -26,6 +27,7 @@ static const NSString *ItemStatusContext; ([player.currentItem status] == AVPlayerItemStatusReadyToPlay && CMTimeCompare([player.currentItem duration], kCMTimeZero) != 0)) { playButton.enabled = YES; + NSLog(@"Enabling play button"); } else { @@ -35,13 +37,18 @@ static const NSString *ItemStatusContext; } - (void)refreshEditor { - // Update assets + NSLog(@"Refreshing editor"); + + // Update editor assets if (asset) self.editor.video = asset; if (songAsset) self.editor.song = songAsset; - // Begin export + // Remove old player + [self.player pause]; + + // Build composition for playback [self.editor buildNewCompositionForPlayback:YES]; // Initialize editor's player @@ -127,18 +134,20 @@ static const NSString *ItemStatusContext; CMTimeCompare([player.currentItem duration], kCMTimeZero) != 0)) { // Paused NSLog(@"Playing item"); [player play]; + [self initScrubberTimer]; [self.videoNavBar setItems:[NSArray arrayWithObjects:[self.videoNavBar.items objectAtIndex:0], [self.videoNavBar.items objectAtIndex:1], [self.videoNavBar.items objectAtIndex:2], - [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemPause target:self action:@selector(play:)],nil] animated:NO]; + [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemPause target:self action:@selector(play:)],[self.videoNavBar.items objectAtIndex:4],[self.videoNavBar.items objectAtIndex:5],nil] animated:NO]; } else { [player pause]; [self.videoNavBar setItems:[NSArray arrayWithObjects:[self.videoNavBar.items objectAtIndex:0], [self.videoNavBar.items objectAtIndex:1], [self.videoNavBar.items objectAtIndex:2], - [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemPlay target:self action:@selector(play:)],nil] animated:NO]; + [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemPlay target:self action:@selector(play:)],[self.videoNavBar.items objectAtIndex:4], + [self.videoNavBar.items objectAtIndex:5], nil] animated:NO]; } } @@ -152,6 +161,154 @@ static const NSString *ItemStatusContext; [player seekToTime:kCMTimeZero]; } + +// Handle scrubbing +// Based on sample code from http://developer.apple.com/library/ios/#samplecode/AVPlayerDemo/Listings/Classes_AVPlayerDemoPlaybackViewController_m.html#//apple_ref/doc/uid/DTS40010101-Classes_AVPlayerDemoPlaybackViewController_m-DontLinkElementID_8 + +#pragma mark - +#pragma mark Movie scrubber control + +/* --------------------------------------------------------- + ** Methods to handle manipulation of the movie scrubber control + ** ------------------------------------------------------- */ + +- (CMTime)playerItemDuration +{ + return [playerItem duration]; +} + +/* Requests invocation of a given block during media playback to update the movie scrubber control. */ +-(void)initScrubberTimer +{ + double interval = .1f; + + CMTime playerDuration = [self playerItemDuration]; + if (CMTIME_IS_INVALID(playerDuration)) + { + return; + } + double duration = CMTimeGetSeconds(playerDuration); + if (isfinite(duration)) + { + CGFloat width = CGRectGetWidth([mScrubber bounds]); + interval = 0.5f * duration / width; + } + + /* Update the scrubber during normal playback. */ + mTimeObserver = [player addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(interval, NSEC_PER_SEC) + queue:NULL /* If you pass NULL, the main queue is used. */ + usingBlock:^(CMTime time) + { + [self syncScrubber]; + }]; + +} + +/* Set the scrubber based on the player current time. */ +- (void)syncScrubber +{ + CMTime playerDuration = [self playerItemDuration]; + if (CMTIME_IS_INVALID(playerDuration)) + { + mScrubber.minimumValue = 0.0; + return; + } + + double duration = CMTimeGetSeconds(playerDuration); + if (isfinite(duration)) + { + float minValue = [mScrubber minimumValue]; + float maxValue = [mScrubber maximumValue]; + double time = CMTimeGetSeconds([player currentTime]); + + [mScrubber setValue:(maxValue - minValue) * time / duration + minValue]; + } +} + +/* The user is dragging the movie controller thumb to scrub through the movie. */ +- (IBAction)beginScrubbing:(id)sender +{ + mRestoreAfterScrubbingRate = [player rate]; + [player setRate:0.f]; + + /* Remove previous timer. */ +// [self removePlayerTimeObserver]; +} + +/* Set the player current time to match the scrubber position. */ +- (IBAction)scrub:(id)sender +{ + if ([sender isKindOfClass:[UISlider class]]) + { + UISlider* slider = sender; + + CMTime playerDuration = [self playerItemDuration]; + if (CMTIME_IS_INVALID(playerDuration)) { + return; + } + + double duration = CMTimeGetSeconds(playerDuration); + if (isfinite(duration)) + { + float minValue = [slider minimumValue]; + float maxValue = [slider maximumValue]; + float value = [slider value]; + + double time = duration * (value - minValue) / (maxValue - minValue); + + [player seekToTime:CMTimeMakeWithSeconds(time, NSEC_PER_SEC)]; + } + } +} + +/* The user has released the movie thumb control to stop scrubbing through the movie. */ +- (IBAction)endScrubbing:(id)sender +{ + if (!mTimeObserver) + { + CMTime playerDuration = [self playerItemDuration]; + if (CMTIME_IS_INVALID(playerDuration)) + { + return; + } + + double duration = CMTimeGetSeconds(playerDuration); + if (isfinite(duration)) + { + CGFloat width = CGRectGetWidth([mScrubber bounds]); + double tolerance = 0.5f * duration / width; + + mTimeObserver = [player addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(tolerance, NSEC_PER_SEC) queue:NULL usingBlock: + ^(CMTime time) + { + [self syncScrubber]; + }]; + } + } + + if (mRestoreAfterScrubbingRate) + { + [player setRate:mRestoreAfterScrubbingRate]; + mRestoreAfterScrubbingRate = 0.f; + } +} + +- (BOOL)isScrubbing +{ + return mRestoreAfterScrubbingRate != 0.f; +} + +-(void)enableScrubber +{ + self.mScrubber.enabled = YES; +} + +-(void)disableScrubber +{ + self.mScrubber.enabled = NO; +} + + - (IBAction)exportToCameraRoll:(id)sender { NSLog(@"Editing..."); @@ -238,6 +395,7 @@ static const NSString *ItemStatusContext; - (void)playerItemDidReachEnd:(NSNotification *)notification { [player seekToTime:kCMTimeZero]; + [player play]; // loop player. If not doing this, set button to pause } diff --git a/Video Tuneup/en.lproj/ViewController_iPad.xib b/Video Tuneup/en.lproj/ViewController_iPad.xib index bcd19c2..d4cece4 100644 --- a/Video Tuneup/en.lproj/ViewController_iPad.xib +++ b/Video Tuneup/en.lproj/ViewController_iPad.xib @@ -2,10 +2,10 @@ 1280 - 11C74 + 11D50d 1938 - 1138.23 - 567.00 + 1138.32 + 568.00 com.apple.InterfaceBuilder.IBCocoaTouchPlugin 933 @@ -117,7 +117,6 @@ IBIPadFramework 0 0 - 0.5 {{0, 587}, {671, 44}} @@ -317,6 +316,14 @@ 44 + + + mScrubber + + + + 51 + loadAssetFromFile: @@ -343,6 +350,42 @@ 48 + + + beginScrubbing: + + + 1 + + 55 + + + + endScrubbing: + + + 7 + + 56 + + + + endScrubbing: + + + 8 + + 57 + + + + scrub: + + + 13 + + 58 + exportToCameraRoll: @@ -504,7 +547,7 @@ - 49 + 58 @@ -555,6 +598,7 @@ UILabel + UISlider UIButton UIButton PlayerView @@ -566,6 +610,10 @@ exportStatus UILabel + + mScrubber + UISlider + pauseButton UIButton -- cgit v1.2.3