summaryrefslogtreecommitdiffstats
path: root/macosx/HBPreviewController.m
diff options
context:
space:
mode:
Diffstat (limited to 'macosx/HBPreviewController.m')
-rw-r--r--macosx/HBPreviewController.m1822
1 files changed, 726 insertions, 1096 deletions
diff --git a/macosx/HBPreviewController.m b/macosx/HBPreviewController.m
index 49e01fd87..8cca74037 100644
--- a/macosx/HBPreviewController.m
+++ b/macosx/HBPreviewController.m
@@ -1,38 +1,135 @@
/* $Id: HBPreviewController.mm,v 1.11 2005/08/01 15:10:44 titer Exp $
- This file is part of the HandBrake source code.
- Homepage: <http://handbrake.fr/>.
- It may be used under the terms of the GNU General Public License. */
+ This file is part of the HandBrake source code.
+ Homepage: <http://handbrake.fr/>.
+ It may be used under the terms of the GNU General Public License. */
#import "HBPreviewController.h"
+#import "HBPreviewGenerator.h"
#import "Controller.h"
+#import <QTKit/QTKit.h>
-#define BORDER_SIZE 2.0
+@implementation QTMovieView (HBQTMovieViewExtensions)
-@implementation QTMovieView (HBExtensions)
-- (void) mouseMoved:(NSEvent *)theEvent
+- (void) mouseMoved: (NSEvent *) theEvent
{
[super mouseMoved:theEvent];
}
+
+@end
+
+@implementation QTMovie (HBQTMovieExtensions)
+
+- (BOOL) isPlaying
+{
+ if ([self rate])
+ return YES;
+ else
+ return NO;
+}
+
+- (NSString *) timecode
+{
+ QTTime time = [self currentTime];
+ double timeInSeconds = (double)time.timeValue / time.timeScale;
+ UInt16 seconds = fmod(timeInSeconds, 60.0);
+ UInt16 minutes = fmod(timeInSeconds / 60.0, 60.0);
+ UInt16 hours = timeInSeconds / (60.0 * 60.0);
+ UInt16 milliseconds = (timeInSeconds - (int) timeInSeconds) * 1000;
+ return [NSString stringWithFormat:@"%02d:%02d:%02d.%03d", hours, minutes, seconds, milliseconds];
+}
+
+- (void) setCurrentTimeDouble: (double) value
+{
+ long timeScale = [[self attributeForKey:QTMovieTimeScaleAttribute] longValue];
+ [self setCurrentTime:QTMakeTime(value * timeScale, timeScale)];
+}
+
@end
#if MAC_OS_X_VERSION_MAX_ALLOWED < 1070
@interface NSWindow(HBExtensions)
-
@property (readonly) CGFloat backingScaleFactor;
-
@end
#endif
-@interface PreviewController (Private)
+#define BORDER_SIZE 2.0
+#define MIN_WIDTH 480.0
+#define MIN_HEIGHT 360.0
+
+#define ANIMATION_DUR 0.2
+
+typedef enum ViewMode : NSUInteger {
+ ViewModePicturePreview,
+ ViewModeEncoding,
+ ViewModeMoviePreview
+} ViewMode;
+
+@interface HBPreviewController () <HBPreviewGeneratorDelegate>
+{
+ /* HUD boxes */
+ IBOutlet NSBox * fPictureControlBox;
+ IBOutlet NSBox * fEncodingControlBox;
+ IBOutlet NSBox * fMoviePlaybackControlBox;
+
+ IBOutlet NSSlider * fPictureSlider;
+ IBOutlet NSTextField * fInfoField;
+ IBOutlet NSTextField * fscaleInfoField;
+
+ /* Full Screen Mode Toggle */
+ IBOutlet NSButton * fScaleToScreenToggleButton;
+
+ /* Movie Previews */
+ IBOutlet QTMovieView * fMovieView;
+ /* Playback Panel Controls */
+ IBOutlet NSButton * fPlayPauseButton;
+ IBOutlet NSSlider * fMovieScrubberSlider;
+ IBOutlet NSTextField * fMovieInfoField;
+
+ IBOutlet NSProgressIndicator * fMovieCreationProgressIndicator;
+ IBOutlet NSTextField * fPreviewMovieStatusField;
+
+ /* Popup of choices for length of preview in seconds */
+ IBOutlet NSPopUpButton * fPreviewMovieLengthPopUp;
+}
+
+@property (nonatomic) CALayer *backLayer;
+@property (nonatomic) CALayer *pictureLayer;
+
+@property (nonatomic) CGFloat backingScaleFactor;
+
+@property (nonatomic) ViewMode currentViewMode;
+@property (nonatomic) BOOL scaleToScreen;
+
+@property (nonatomic, retain) NSTimer *hudTimer;
+
+@property (nonatomic, retain) HBPreviewGenerator *generator;
+@property (nonatomic) NSUInteger pictureIndex;
+
+@property (nonatomic, retain) QTMovie *movie;
+@property (nonatomic, retain) NSTimer *movieTimer;
+
+/* Pictures HUD actions */
+- (IBAction) previewDurationPopUpChanged: (id) sender;
+- (IBAction) pictureSliderChanged: (id) sender;
+- (IBAction) showPictureSettings:(id)sender;
+- (IBAction) toggleScaleToScreen:(id)sender;
+
+- (IBAction) cancelCreateMoviePreview: (id) sender;
+- (IBAction) createMoviePreview: (id) sender;
+
+/* Movie HUD actions */
+- (IBAction) showPicturesPreview: (id) sender;
+- (IBAction) toggleMoviePreviewPlayPause: (id) sender;
+- (IBAction) moviePlaybackGoToBeginning: (id) sender;
+- (IBAction) moviePlaybackGoToEnd: (id) sender;
+- (IBAction) previewScrubberChanged: (id) sender;
-- (NSSize)optimalViewSizeForImageSize: (NSSize)imageSize;
-- (void)resizeWindowForViewSize: (NSSize)viewSize;
@end
-@implementation PreviewController
+@implementation HBPreviewController
-- (id)init
+- (id) init
{
if (self = [super initWithWindowNibName:@"PicturePreview"])
{
@@ -45,21 +142,12 @@
// If/when we switch a lot of this stuff to bindings, this can probably
// go away.
[self window];
-
- fPicturePreviews = [[NSMutableDictionary alloc] init];
- /* Init libhb with check for updates libhb style set to "0" so its ignored and lets sparkle take care of it */
- int loggingLevel = [[[NSUserDefaults standardUserDefaults] objectForKey:@"LoggingLevel"] intValue];
- fPreviewLibhb = hb_init(loggingLevel, 0);
- }
- return self;
-}
-- (void)setHBController: (HBController *)controller
-{
- fHBController = controller;
+ }
+ return self;
}
-- (void)awakeFromNib
+- (void) awakeFromNib
{
[[self window] setDelegate:self];
@@ -68,274 +156,563 @@
[self setWindowFrameAutosaveName:@"Preview"];
[[self window] setExcludedFromWindowsMenu:YES];
-
+
/* lets set the preview window to accept mouse moved events */
[[self window] setAcceptsMouseMovedEvents:YES];
- [self startReceivingLibhbNotifications];
-
- hudTimerSeconds = 0;
+
/* we set the progress indicator to not use threaded animation
* as it causes a conflict with the qtmovieview's controllerbar
- */
+ */
[fMovieCreationProgressIndicator setUsesThreadedAnimation:NO];
-
- /* we set the preview length popup in seconds */
- [fPreviewMovieLengthPopUp removeAllItems];
- [fPreviewMovieLengthPopUp addItemWithTitle: @"15"];
- [fPreviewMovieLengthPopUp addItemWithTitle: @"30"];
- [fPreviewMovieLengthPopUp addItemWithTitle: @"45"];
- [fPreviewMovieLengthPopUp addItemWithTitle: @"60"];
- [fPreviewMovieLengthPopUp addItemWithTitle: @"90"];
- [fPreviewMovieLengthPopUp addItemWithTitle: @"105"];
- [fPreviewMovieLengthPopUp addItemWithTitle: @"120"];
- [fPreviewMovieLengthPopUp addItemWithTitle: @"135"];
- [fPreviewMovieLengthPopUp addItemWithTitle: @"150"];
- [fPreviewMovieLengthPopUp addItemWithTitle: @"165"];
- [fPreviewMovieLengthPopUp addItemWithTitle: @"180"];
- [fPreviewMovieLengthPopUp addItemWithTitle: @"195"];
- [fPreviewMovieLengthPopUp addItemWithTitle: @"210"];
- [fPreviewMovieLengthPopUp addItemWithTitle: @"225"];
- [fPreviewMovieLengthPopUp addItemWithTitle: @"240"];
[fMovieView setHidden:YES];
[fMovieView setDelegate:self];
+ [fMovieView setControllerVisible:NO];
+
+ /* we set the preview length popup in seconds */
+ [fPreviewMovieLengthPopUp removeAllItems];
+ [fPreviewMovieLengthPopUp addItemsWithTitles:@[@"15", @"30", @"45", @"60", @"90",
+ @"120", @"150", @"180", @"210", @"240"]];
+
+ if ([[NSUserDefaults standardUserDefaults] objectForKey:@"PreviewLength"])
+ [fPreviewMovieLengthPopUp selectItemWithTitle:[[NSUserDefaults standardUserDefaults]
+ objectForKey:@"PreviewLength"]];
+ if (![fPreviewMovieLengthPopUp selectedItem])
+ /* currently hard set default to 15 seconds */
+ [fPreviewMovieLengthPopUp selectItemAtIndex: 0];
/* Setup our layers for core animation */
[[[self window] contentView] setWantsLayer:YES];
[fPictureControlBox setWantsLayer:YES];
[fEncodingControlBox setWantsLayer:YES];
[fMoviePlaybackControlBox setWantsLayer:YES];
-
- fWhiteBackground = [CALayer layer];
- [fWhiteBackground setBounds:CGRectMake(0.0, 0.0, 480.0, 360.0)];
- [fWhiteBackground setPosition:CGPointMake([[[self window] contentView] frame].size.width /2,
- [[[self window] contentView] frame].size.height /2)];
-
- [fWhiteBackground setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable];
+
+ self.backLayer = [CALayer layer];
+ [self.backLayer setBounds:CGRectMake(0.0, 0.0, MIN_WIDTH, MIN_HEIGHT)];
+ [self.backLayer setPosition:CGPointMake([[[self window] contentView] frame].size.width /2,
+ [[[self window] contentView] frame].size.height /2)];
+
+ [self.backLayer setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable];
CGColorRef white = CGColorCreateGenericRGB(1.0, 1.0, 1.0, 1.0);
- [fWhiteBackground setBackgroundColor: white];
+ [self.backLayer setBackgroundColor: white];
CFRelease(white);
- [fWhiteBackground setShadowOpacity:0.5f];
- [fWhiteBackground setShadowOffset:CGSizeMake(0, 0)];
+ [self.backLayer setShadowOpacity:0.5f];
+ [self.backLayer setShadowOffset:CGSizeMake(0, 0)];
- fPictureLayer = [CALayer layer];
- [fPictureLayer setBounds:CGRectMake(0.0, 0.0, 476.0, 356.0)];
- [fPictureLayer setPosition:CGPointMake([[[self window] contentView] frame].size.width /2,
- [[[self window] contentView] frame].size.height /2)];
-
- [fPictureLayer setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable];
+ self.pictureLayer = [CALayer layer];
+ [self.pictureLayer setBounds:CGRectMake(0.0, 0.0, MIN_WIDTH - (BORDER_SIZE * 2), MIN_HEIGHT - (BORDER_SIZE * 2))];
+ [self.pictureLayer setPosition:CGPointMake([[[self window] contentView] frame].size.width /2,
+ [[[self window] contentView] frame].size.height /2)];
- NSMutableDictionary *actions = [NSMutableDictionary
- dictionaryWithDictionary:[fPictureLayer actions]];
+ [self.pictureLayer setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable];
// Disable fade on contents change
+ NSMutableDictionary *actions = [NSMutableDictionary
+ dictionaryWithDictionary:[self.pictureLayer actions]];
+
[actions setObject:[NSNull null] forKey:@"contents"];
- [fPictureLayer setActions:actions];
+ [self.pictureLayer setActions:actions];
- [[[[self window] contentView] layer] insertSublayer:fWhiteBackground below: [fMovieView layer]];
- [[[[self window] contentView] layer] insertSublayer:fPictureLayer below: [fMovieView layer]];
+ [[[[self window] contentView] layer] insertSublayer:self.backLayer below: [fMovieView layer]];
+ [[[[self window] contentView] layer] insertSublayer:self.pictureLayer below: [fMovieView layer]];
/* relocate our hud origins */
NSPoint hudControlBoxOrigin = [fMoviePlaybackControlBox frame].origin;
[fPictureControlBox setFrameOrigin:hudControlBoxOrigin];
[fEncodingControlBox setFrameOrigin:hudControlBoxOrigin];
[fMoviePlaybackControlBox setFrameOrigin:hudControlBoxOrigin];
-
+
+ /* set the current scale factor */
if( [[self window] respondsToSelector:@selector( backingScaleFactor )] )
- backingScaleFactor = [[self window] backingScaleFactor];
+ self.backingScaleFactor = [[self window] backingScaleFactor];
else
- backingScaleFactor = 1.0;
-
- [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(titleChanged:) name: HBTitleChangedNotification object:nil];
+ self.backingScaleFactor = 1.0;
}
-- (void) titleChanged: (NSNotification *) aNotification
+- (void) setTitle: (hb_title_t *) title
{
- /* Notification from HBController, only used to stop
- * an encoding while the HBController is scanning a new title
- */
- [self cancelCreateMoviePreview:self];
+ _title = title;
+
+ [self.generator cancel];
+ self.generator = nil;
+
+ if (_title)
+ {
+ /* alloc and init a generator for the current title */
+ self.generator = [[[HBPreviewGenerator alloc] initWithHandle:self.handle andTitle:self.title] autorelease];
+
+ /* adjust the preview slider length */
+ [fPictureSlider setMaxValue: self.generator.imagesCount - 1.0];
+ [fPictureSlider setNumberOfTickMarks: self.generator.imagesCount];
+
+ [self displayPreview];
+ }
}
-- (void)windowWillClose:(NSNotification *)aNotification
+- (void) reload
{
- /* Upon closing the preview window, we make sure we clean up any
- * preview movie that might be playing or encoding. However, first
- * make sure we have a preview picture before calling pictureSliderChanged
- * to go back to still previews .. just in case nothing is loaded up like in
- * a Launch, cancel new scan then quit type scenario.
- */
+ if (self.title)
+ {
+ // Purge the existing picture previews so they get recreated the next time
+ // they are needed.
+ [self.generator purgeImageCache];
+ [self switchViewToMode:ViewModePicturePreview];
+ [self displayPreview];
+ }
+}
- if (fEncodeState || [self isPlaying])
+- (void) showWindow: (id) sender
+{
+ if (self.currentViewMode == ViewModeMoviePreview)
+ {
+ [self startMovieTimer];
+ }
+
+ [super showWindow:sender];
+}
+
+- (void) windowWillClose: (NSNotification *) aNotification
+{
+ if (self.currentViewMode == ViewModeEncoding)
{
[self cancelCreateMoviePreview:self];
+ }
+ else if (self.currentViewMode == ViewModeMoviePreview)
+ {
[fMovieView pause:self];
[self stopMovieTimer];
}
- hudTimerSeconds = 0;
[[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"PreviewWindowIsOpen"];
}
-- (void) dealloc
+- (void) windowDidChangeBackingProperties: (NSNotification *) notification
{
- hb_stop(fPreviewLibhb);
- if (fPreviewMoviePath)
+ NSWindow *theWindow = (NSWindow *)[notification object];
+
+ CGFloat newBackingScaleFactor = [theWindow backingScaleFactor];
+ CGFloat oldBackingScaleFactor = [[[notification userInfo]
+ objectForKey:@"NSBackingPropertyOldScaleFactorKey"]
+ doubleValue];
+
+ if (newBackingScaleFactor != oldBackingScaleFactor)
{
- [[NSFileManager defaultManager] removeItemAtPath:fPreviewMoviePath error:nil];
- [fPreviewMoviePath release];
+ // Scale factor changed, update the preview window
+ // to the new situation
+ self.backingScaleFactor = newBackingScaleFactor;
+ if (self.title)
+ [self reload];
}
-
- [fLibhbTimer invalidate];
- [fLibhbTimer release];
-
- [fHudTimer invalidate];
- [fHudTimer release];
-
- [fMovieTimer invalidate];
- [fMovieTimer release];
-
- [fPicturePreviews release];
-
- hb_close(&fPreviewLibhb);
-
- [self removeMovieCallbacks];
-
- [super dealloc];
}
-- (void) SetHandle: (hb_handle_t *) handle
+/**
+ * Given the size of the preview image to be shown, returns the best possible
+ * size for the view.
+ */
+- (NSSize) optimalViewSizeForImageSize: (NSSize) imageSize
{
- fHandle = handle;
-
- /* adjust the preview slider length */
- /* We use our advance pref to determine how many previews we scanned */
- int hb_num_previews = [[[NSUserDefaults standardUserDefaults] objectForKey:@"PreviewsNumber"] intValue];
- [fPictureSlider setMaxValue: hb_num_previews - 1.0];
- [fPictureSlider setNumberOfTickMarks: hb_num_previews];
-
- if ([[NSUserDefaults standardUserDefaults] objectForKey:@"PreviewLength"])
+ CGFloat minWidth = MIN_WIDTH;
+ CGFloat minHeight = MIN_HEIGHT;
+
+ NSSize screenSize = [[[self window] screen] visibleFrame].size;
+ CGFloat maxWidth = screenSize.width;
+ CGFloat maxHeight = screenSize.height;
+
+ NSSize resultSize = imageSize;
+ CGFloat resultPar = resultSize.width / resultSize.height;
+
+ //note, a mbp 15" at 1440 x 900 is a 1.6 ar
+ CGFloat screenAspect = screenSize.width / screenSize.height;
+
+ if ( resultSize.width > maxWidth || resultSize.height > maxHeight )
{
- [fPreviewMovieLengthPopUp selectItemWithTitle:[[NSUserDefaults standardUserDefaults] objectForKey:@"PreviewLength"]];
+ // Source is larger than screen in one or more dimensions
+ if ( resultPar > screenAspect )
+ {
+ // Source aspect wider than screen aspect, snap to max width and vary height
+ resultSize.width = maxWidth;
+ resultSize.height = (maxWidth / resultPar);
+ }
+ else
+ {
+ // Source aspect narrower than screen aspect, snap to max height vary width
+ resultSize.height = maxHeight;
+ resultSize.width = (maxHeight * resultPar);
+ }
}
- else
+
+ // If necessary, grow to minimum dimensions to ensure controls overlay is not obstructed
+ if ( resultSize.width < minWidth )
+ resultSize.width = minWidth;
+ if ( resultSize.height < minHeight )
+ resultSize.height = minHeight;
+
+ return resultSize;
+}
+
+/**
+ * Resizes the entire window to accomodate a view of a particular size.
+ */
+- (void) resizeWindowForViewSize: (NSSize) viewSize
+{
+ // Figure out the deltas for the new frame area
+ NSSize currentSize = [[[self window] contentView] frame].size;
+ CGFloat deltaX = viewSize.width - currentSize.width;
+ CGFloat deltaY = viewSize.height - currentSize.height;
+
+ // Now resize the whole panel by those same deltas, but don't exceed the min
+ NSRect frame = [[self window] frame];
+ NSSize maxSize = [[[self window] screen] visibleFrame].size;
+
+ /* if we are not Scale To Screen, put an 10% of visible screen on the window */
+ if (self.scaleToScreen == NO)
{
- /* currently hard set default to 15 seconds */
- [fPreviewMovieLengthPopUp selectItemAtIndex: 0];
+ maxSize.width = maxSize.width * 0.90;
+ maxSize.height = maxSize.height * 0.90;
+ }
+
+ /* Set our min size to the storage size */
+ NSSize minSize;
+ minSize.width = self.title->width / self.backingScaleFactor;
+ minSize.height = self.title->height / self.backingScaleFactor;
+
+ frame.size.width += deltaX;
+ frame.size.height += deltaY;
+ if( frame.size.width < minSize.width )
+ {
+ frame.size.width = minSize.width;
+ deltaX = frame.size.width - currentSize.width;
+ }
+ if( frame.size.height < minSize.height )
+ {
+ frame.size.height = minSize.height;
+ //deltaY = frame.size.height - currentSize.height;
+ }
+ /* compare frame to max size of screen */
+
+ if( frame.size.width > maxSize.width )
+ {
+ frame.size.width = maxSize.width;
+ }
+
+ if( frame.size.height > maxSize.height )
+ {
+ frame.size.height = maxSize.height;
+ }
+
+ // But now the sheet is off-center, so also shift the origin to center it and
+ // keep the top aligned.
+ if (frame.size.width != [[self window] frame].size.width)
+ frame.origin.x -= (deltaX / 2.0);
+
+ /* Since upon launch we can open up the preview window if it was open
+ * the last time we quit (and at the size it was) we want to make
+ * sure that upon resize we do not have the window off the screen
+ * So check the origin against the screen origin and adjust if
+ * necessary.
+ */
+ NSSize screenSize = [[[self window] screen] visibleFrame].size;
+ NSPoint screenOrigin = [[[self window] screen] visibleFrame].origin;
+ if (screenSize.height < frame.size.height)
+ {
+ frame.size.height = screenSize.height;
}
+ if (screenSize.width < frame.size.width)
+ {
+ frame.size.width = screenSize.width;
+ }
+
+ /* our origin is off the screen to the left*/
+ if (frame.origin.x < screenOrigin.x)
+ {
+ /* so shift our origin to the right */
+ frame.origin.x = screenOrigin.x;
+ }
+ else if ((frame.origin.x + frame.size.width) > (screenOrigin.x + screenSize.width))
+ {
+ /* the right side of the preview is off the screen, so shift to the left */
+ frame.origin.x = (screenOrigin.x + screenSize.width) - frame.size.width;
+ }
+
+ if (self.scaleToScreen == YES)
+ {
+ /* our origin is off the screen to the top*/
+ if (frame.origin.y < screenOrigin.y)
+ {
+ /* so shift our origin to the bottom */
+ frame.origin.y = screenOrigin.y;
+ }
+ else if ((frame.origin.y + frame.size.height) > (screenOrigin.y + screenSize.height))
+ {
+ /* the top side of the preview is off the screen, so shift to the bottom */
+ frame.origin.y = (screenOrigin.y + screenSize.height) - frame.size.height;
+ }
+ }
+
+ [[self window] setFrame:frame display:YES animate:YES];
+}
+
+
+/**
+ * Enable/Disable an arbitrary number of UI elements.
+ * @param boxes an array of UI elements
+ * @param indexes a set of indexes of the elements in boxes to be enabled
+ */
+- (void) toggleBoxes: (NSArray *) boxes usingIndexes: (NSIndexSet *) indexes
+{
+ [NSAnimationContext beginGrouping];
+ [[NSAnimationContext currentContext] setDuration:ANIMATION_DUR];
+
+ [boxes enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
+ [[obj animator] setHidden:([indexes containsIndex:idx]) ? NO : YES];
+ }];
+
+ [NSAnimationContext endGrouping];
}
-- (void) SetTitle: (hb_title_t *) title
+/**
+ * Switch the preview controller to one of his view mode:
+ * ViewModePicturePreview, ViewModeEncoding, ViewModeMoviePreview
+ * This methods is the only way to change the mode, do not try otherwise.
+ * @param mode ViewMode mode
+ */
+- (void) switchViewToMode: (ViewMode) mode
{
- fTitle = title;
- fPicture = 0;
+ switch (mode) {
+ case ViewModePicturePreview:
+ {
+ if (self.currentViewMode == ViewModeEncoding)
+ {
+ [self.generator cancel];
+ [self toggleBoxes:@[fPictureControlBox, fEncodingControlBox]
+ usingIndexes:[NSIndexSet indexSetWithIndex:0]];
+ [fMovieCreationProgressIndicator stopAnimation:self];
+ }
+ else if (self.currentViewMode == ViewModeMoviePreview)
+ {
+ /* Stop playback and remove the observers */
+ [fMovieView pause:self];
+ [self stopMovieTimer];
+ [self removeMovieObservers];
+
+ [self toggleBoxes:@[fPictureControlBox, fMoviePlaybackControlBox, fMovieView]
+ usingIndexes:[NSIndexSet indexSetWithIndex:0]];
+
+ /* Release the movie */
+ [fMovieView setMovie:nil];
+ self.movie = nil;
+ }
+
+ break;
+ }
+
+ case ViewModeEncoding:
+ {
+ [fMovieCreationProgressIndicator setDoubleValue:0];
+ [fMovieCreationProgressIndicator startAnimation:self];
+ [self toggleBoxes:@[fEncodingControlBox, fPictureControlBox, fMoviePlaybackControlBox]
+ usingIndexes:[NSIndexSet indexSetWithIndex:0]];
+
+ break;
+ }
- [self settingsChanged:nil];
+ case ViewModeMoviePreview:
+ {
+ [self toggleBoxes:@[fMovieView, fMoviePlaybackControlBox, fEncodingControlBox, fPictureControlBox]
+ usingIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, 2)]];
+
+ [fMovieCreationProgressIndicator stopAnimation:self];
+ [self initPreviewScrubberForMovie];
+ [self startMovieTimer];
+
+ /* Install movie notifications */
+ [self addMovieObservers];
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ self.currentViewMode = mode;
}
-- (void)windowDidChangeBackingProperties:(NSNotification *)notification {
-
- NSWindow *theWindow = (NSWindow *)[notification object];
-
- CGFloat newBackingScaleFactor = [theWindow backingScaleFactor];
- CGFloat oldBackingScaleFactor = [[[notification userInfo]
- objectForKey:@"NSBackingPropertyOldScaleFactorKey"]
- doubleValue];
- if( newBackingScaleFactor != oldBackingScaleFactor )
+- (void) dealloc
+{
+ [_hudTimer invalidate];
+ [_hudTimer release];
+
+ [_movieTimer invalidate];
+ [_movieTimer release];
+
+ [_generator cancel];
+ [_generator release];
+
+ [self removeMovieObservers];
+
+ [super dealloc];
+}
+
+#pragma mark -
+#pragma mark Hud Control Overlay
+
+- (void) mouseMoved: (NSEvent *) theEvent
+{
+ [super mouseMoved:theEvent];
+ NSPoint mouseLoc = [theEvent locationInWindow];
+
+ /* Test for mouse location to show/hide hud controls */
+ if (self.currentViewMode != ViewModeEncoding && self.title)
{
- // Scale factor changed, update the preview window
- // to the new situation
- backingScaleFactor = newBackingScaleFactor;
- if (fTitle)
- [self pictureSliderChanged:self];
+ /* Since we are not encoding, verify which control hud to show
+ * or hide based on aMovie ( aMovie indicates we need movie controls )
+ */
+ NSBox *hudBoxToShow;
+ if (self.currentViewMode == !ViewModeMoviePreview) // No movie loaded up
+ {
+ hudBoxToShow = fPictureControlBox;
+ }
+ else // We have a movie
+ {
+ hudBoxToShow = fMoviePlaybackControlBox;
+ }
+
+ if (NSPointInRect(mouseLoc, [hudBoxToShow frame]))
+ {
+ [[hudBoxToShow animator] setHidden: NO];
+ [self stopHudTimer];
+ }
+ else if (NSPointInRect(mouseLoc, [[[self window] contentView] frame]))
+ {
+ [[hudBoxToShow animator] setHidden: NO];
+ [self startHudTimer];
+ }
+ else
+ {
+ [[hudBoxToShow animator] setHidden: YES];
+ }
+ }
+}
+
+- (void) startHudTimer
+{
+ if (self.hudTimer)
+ {
+ [self.hudTimer setFireDate:[NSDate dateWithTimeIntervalSinceNow:8.0]];
+ }
+ else
+ {
+ self.hudTimer = [NSTimer scheduledTimerWithTimeInterval:8.0 target:self selector:@selector(hudTimerFired:)
+ userInfo:nil repeats:YES];
}
}
-// Adjusts the window to draw the current picture (fPicture) adjusting its size as
-// necessary to display as much of the picture as possible.
+- (void) stopHudTimer
+{
+ [self.hudTimer invalidate];
+ self.hudTimer = nil;
+}
+
+- (void) hudTimerFired: (NSTimer *)theTimer
+{
+ /* Regardless which control box is active, after the timer
+ * period we want either one to fade to hidden.
+ */
+ [[fPictureControlBox animator] setHidden: YES];
+ [[fMoviePlaybackControlBox animator] setHidden: YES];
+ [self stopHudTimer];
+}
+
+#pragma mark -
+#pragma mark Still previews mode
+
+/**
+ * Adjusts the window to draw the current picture (fPicture) adjusting its size as
+ * necessary to display as much of the picture as possible.
+ */
- (void) displayPreview
{
- hb_job_t * job = fTitle->job;
+ hb_title_t *title = self.title;
- NSImage *fPreviewImage = [self imageForPicture: fPicture];
+ NSImage *fPreviewImage = [self.generator imageAtIndex:self.pictureIndex];
NSSize imageScaledSize = [fPreviewImage size];
- [fPictureLayer setContents:fPreviewImage];
+ [self.pictureLayer setContents:fPreviewImage];
- NSSize displaySize = NSMakeSize( ( CGFloat )fTitle->width, ( CGFloat )fTitle->height );
+ NSSize displaySize = NSMakeSize( ( CGFloat )title->width, ( CGFloat )title->height );
NSString *sizeInfoString;
/* Set the picture size display fields below the Preview Picture*/
int output_width, output_height, output_par_width, output_par_height;
int display_width;
- if( fTitle->job->anamorphic.mode == 1 ) // Original PAR Implementation
+ if( title->job->anamorphic.mode == 1 ) // Original PAR Implementation
{
- output_width = fTitle->width-fTitle->job->crop[2]-fTitle->job->crop[3];
- output_height = fTitle->height-fTitle->job->crop[0]-fTitle->job->crop[1];
- display_width = output_width * fTitle->job->anamorphic.par_width / fTitle->job->anamorphic.par_height;
+ output_width = title->width-title->job->crop[2]-title->job->crop[3];
+ output_height = title->height-title->job->crop[0]-title->job->crop[1];
+ display_width = output_width * title->job->anamorphic.par_width / title->job->anamorphic.par_height;
sizeInfoString = [NSString stringWithFormat:
@"Source: %dx%d, Output: %dx%d, Anamorphic: %dx%d Strict",
- fTitle->width, fTitle->height, output_width, output_height, display_width, output_height];
-
+ title->width, title->height, output_width, output_height, display_width, output_height];
+
displaySize.width = display_width;
- displaySize.height = fTitle->height;
+ displaySize.height = title->height;
imageScaledSize.width = display_width;
imageScaledSize.height = output_height;
}
- else if (fTitle->job->anamorphic.mode == 2) // Loose Anamorphic
+ else if (title->job->anamorphic.mode == 2) // Loose Anamorphic
{
- hb_set_anamorphic_size(job, &output_width, &output_height, &output_par_width, &output_par_height);
+ hb_set_anamorphic_size(title->job, &output_width, &output_height, &output_par_width, &output_par_height);
display_width = output_width * output_par_width / output_par_height;
sizeInfoString = [NSString stringWithFormat:
@"Source: %dx%d, Output: %dx%d, Anamorphic: %dx%d Loose",
- fTitle->width, fTitle->height, output_width, output_height, display_width, output_height];
-
+ title->width, title->height, output_width, output_height, display_width, output_height];
+
displaySize.width = display_width;
- displaySize.height = fTitle->height;
+ displaySize.height = title->height;
imageScaledSize.width = display_width;
imageScaledSize.height = output_height;
}
- else if (fTitle->job->anamorphic.mode == 3) // Custom Anamorphic
+ else if (title->job->anamorphic.mode == 3) // Custom Anamorphic
{
- hb_set_anamorphic_size(job, &output_width, &output_height, &output_par_width, &output_par_height);
+ hb_set_anamorphic_size(title->job, &output_width, &output_height, &output_par_width, &output_par_height);
sizeInfoString = [NSString stringWithFormat:
@"Source: %dx%d, Output: %dx%d, Anamorphic: %dx%d Custom",
- fTitle->width, fTitle->height, output_width, output_height, fTitle->job->anamorphic.dar_width, fTitle->job->anamorphic.dar_height];
-
- displaySize.width = fTitle->job->anamorphic.dar_width + fTitle->job->crop[2] + fTitle->job->crop[3] ;
- displaySize.height = fTitle->job->anamorphic.dar_height + fTitle->job->crop[0] + fTitle->job->crop[1];
- imageScaledSize.width = (int)fTitle->job->anamorphic.dar_width;
- imageScaledSize.height = (int)fTitle->job->height;
+ title->width, title->height, output_width, output_height, title->job->anamorphic.dar_width, title->job->anamorphic.dar_height];
+
+ displaySize.width = title->job->anamorphic.dar_width + title->job->crop[2] + title->job->crop[3] ;
+ displaySize.height = title->job->anamorphic.dar_height + title->job->crop[0] + title->job->crop[1];
+ imageScaledSize.width = (int)title->job->anamorphic.dar_width;
+ imageScaledSize.height = (int)title->job->height;
}
else // No Anamorphic
{
sizeInfoString = [NSString stringWithFormat:
- @"Source: %dx%d, Output: %dx%d", fTitle->width, fTitle->height,
- fTitle->job->width, fTitle->job->height];
-
- displaySize.width = fTitle->width;
- displaySize.height = fTitle->height;
- imageScaledSize.width = fTitle->job->width;
- imageScaledSize.height = fTitle->job->height;
+ @"Source: %dx%d, Output: %dx%d", title->width, title->height,
+ title->job->width, title->job->height];
+
+ displaySize.width = title->width;
+ displaySize.height = title->height;
+ imageScaledSize.width = title->job->width;
+ imageScaledSize.height = title->job->height;
}
- if( backingScaleFactor != 1.0 )
+ if (self.backingScaleFactor != 1.0)
{
// HiDPI mode usually display everything
// with douple pixel count, but we don't
// want to double the size of the video
- displaySize.height /= backingScaleFactor;
- displaySize.width /= backingScaleFactor;
- imageScaledSize.height /= backingScaleFactor;
- imageScaledSize.width /= backingScaleFactor;
+ displaySize.height /= self.backingScaleFactor;
+ displaySize.width /= self.backingScaleFactor;
+ imageScaledSize.height /= self.backingScaleFactor;
+ imageScaledSize.width /= self.backingScaleFactor;
}
// Get the optimal view size for the image
NSSize viewSize = [self optimalViewSizeForImageSize:displaySize];
viewSize.width += BORDER_SIZE * 2;
viewSize.height += BORDER_SIZE * 2;
-
+
NSSize windowSize;
- if (scaleToScreen == YES)
+ if (self.scaleToScreen == YES)
// Scale the window to the max possible size
windowSize = [[[self window] screen] visibleFrame].size;
else
@@ -347,14 +724,14 @@
areaSize.width -= BORDER_SIZE * 2;
areaSize.height -= BORDER_SIZE * 2;
- if (scaleToScreen == YES)
+ if (self.scaleToScreen == YES)
{
/* We are in Scale To Screen mode so, we have to get the ratio for height and width against the window
*size so we can scale from there.
*/
CGFloat pictureAspectRatio = imageScaledSize.width / imageScaledSize.height;
CGFloat areaAspectRatio = areaSize.width / areaSize.height;
-
+
if (pictureAspectRatio > areaAspectRatio)
{
viewSize.width = areaSize.width;
@@ -375,7 +752,7 @@
{
CGFloat pictureAspectRatio = imageScaledSize.width / imageScaledSize.height;
CGFloat areaAspectRatio = areaSize.width / areaSize.height;
-
+
if (pictureAspectRatio > areaAspectRatio)
{
viewSize.width = areaSize.width;
@@ -390,27 +767,26 @@
}
// Resize the CALayers
- [fWhiteBackground setBounds:CGRectMake(0, 0, viewSize.width + (BORDER_SIZE * 2), viewSize.height + (BORDER_SIZE * 2))];
- [fPictureLayer setBounds:CGRectMake(0, 0, viewSize.width, viewSize.height)];
+ [self.backLayer setBounds:CGRectMake(0, 0, viewSize.width + (BORDER_SIZE * 2), viewSize.height + (BORDER_SIZE * 2))];
+ [self.pictureLayer setBounds:CGRectMake(0, 0, viewSize.width, viewSize.height)];
NSString *scaleString;
- CGFloat scale = ( ( CGFloat )[fPictureLayer frame].size.width) / ( ( CGFloat )imageScaledSize.width);
+ CGFloat scale = ( ( CGFloat )[self.pictureLayer frame].size.width) / ( ( CGFloat )imageScaledSize.width);
if (scale * 100.0 != 100)
- scaleString = [NSString stringWithFormat:
- NSLocalizedString( @" (%.0f%% actual size)",
- @"String shown when a preview is scaled" ), scale * 100.0];
+ scaleString = [NSString stringWithFormat:@" (%.0f%% actual size)", scale * 100.0];
else
scaleString = @"(Actual size)";
-
- if (scaleToScreen == YES)
+
+ if (_scaleToScreen == YES)
scaleString = [scaleString stringByAppendingString:@" Scaled To Screen"];
/* Set the info fields in the hud controller */
[fInfoField setStringValue: [NSString stringWithFormat:
@"%@", sizeInfoString]];
-
+
[fscaleInfoField setStringValue: [NSString stringWithFormat:
@"%@", scaleString]];
+
/* Set the info field in the window title bar */
[[self window] setTitle:[NSString stringWithFormat: @"Preview - %@ %@",sizeInfoString, scaleString]];
}
@@ -419,519 +795,149 @@
{
[[NSUserDefaults standardUserDefaults] setObject:[fPreviewMovieLengthPopUp titleOfSelectedItem] forKey:@"PreviewLength"];
}
-
-- (IBAction) settingsChanged: (id) sender
-{
- // Purge the existing picture previews so they get recreated the next time
- // they are needed.
- [self purgeImageCache];
- [self pictureSliderChanged:nil];
-}
-- (IBAction) pictureSliderChanged: (id) sender
+- (void) setDeinterlacePreview: (BOOL) deinterlacePreview
{
- /* Run cancelCreateMoviePreview in case a preview is being encoded and then cancel if so */
- [self cancelCreateMoviePreview:nil];
-
- // Show the picture view
- if (aMovie)
- {
- [fMoviePlaybackControlBox setHidden:YES];
- [fMovieView pause:nil];
- [fMovieView setHidden:YES];
- [fMovieView setMovie:nil];
- aMovie = nil;
- }
-
- int newPicture = [fPictureSlider intValue];
- if (newPicture != fPicture)
- {
- fPicture = newPicture;
- }
- [self displayPreview];
-
+ _deinterlacePreview = deinterlacePreview;
+ self.generator.deinterlace = deinterlacePreview;
}
-- (IBAction)showWindow:(id)sender
-{
- if (aMovie)
- [self startMovieTimer];
-
- [super showWindow:sender];
-}
-- (NSString*) pictureSizeInfoString
-{
- return [fInfoField stringValue];
-}
-
-- (IBAction)showPictureSettings:(id)sender
-{
- [fHBController showPicturePanel:self];
-}
-
-#pragma mark Hud Control Overlay
-/* enableHudControls and disableHudControls are used to sync enableUI
- * in HBController so that during a scan we do not attempt to access source
- * images, etc. which can cause a crash. In general this ui behavior will mirror
- * the main window ui's enableUI method and in fact is called from there */
-- (void) enableHudControls
-{
- [fPictureSlider setEnabled:YES];
- [fScaleToScreenToggleButton setEnabled:YES];
- [fCreatePreviewMovieButton setEnabled:YES];
- [fGoToStillPreviewButton setEnabled:YES];
-}
-
-- (void) disableHudControls
-{
- [fPictureSlider setEnabled:NO];
- [fScaleToScreenToggleButton setEnabled:NO];
- [fCreatePreviewMovieButton setEnabled:NO];
- [fGoToStillPreviewButton setEnabled:NO];
-}
-
-- (void) mouseMoved:(NSEvent *)theEvent
-{
- [super mouseMoved:theEvent];
- NSPoint mouseLoc = [theEvent locationInWindow];
-
- /* Test for mouse location to show/hide hud controls */
- if( fEncodeState != 1 )
- {
- /* Since we are not encoding, verify which control hud to show
- * or hide based on aMovie ( aMovie indicates we need movie controls )
- */
- NSBox *hudBoxToShow;
- if ( aMovie == nil ) // No movie loaded up
- {
- hudBoxToShow = fPictureControlBox;
- }
- else // We have a movie
- {
- hudBoxToShow = fMoviePlaybackControlBox;
- }
-
- if( NSPointInRect( mouseLoc, [fPictureControlBox frame] ) )
- {
- [[hudBoxToShow animator] setHidden: NO];
- [self stopHudTimer];
- }
- else if( NSPointInRect( mouseLoc, [[[self window] contentView] frame] ) )
- {
- [[hudBoxToShow animator] setHidden: NO];
- [self startHudTimer];
- }
- else
- {
- [[hudBoxToShow animator] setHidden: YES];
- }
- }
-}
-
-- (void) startHudTimer
-{
- if( fHudTimer ) {
- [fHudTimer invalidate];
- [fHudTimer release];
- }
- fHudTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(hudTimerFired:) userInfo:nil repeats:YES];
- [fHudTimer retain];
-}
-
-- (void) stopHudTimer
-{
- if( fHudTimer )
- {
- [fHudTimer invalidate];
- [fHudTimer release];
- fHudTimer = nil;
- hudTimerSeconds = 0;
- }
-}
-
-- (void) hudTimerFired: (NSTimer*)theTimer
+- (IBAction) pictureSliderChanged: (id) sender
{
- hudTimerSeconds++;
- if( hudTimerSeconds >= 10 )
- {
- /* Regardless which control box is active, after the timer
- * period we want either one to fade to hidden.
- */
- [[fPictureControlBox animator] setHidden: YES];
- [[fMoviePlaybackControlBox animator] setHidden: YES];
- [self stopHudTimer];
+ if ((self.pictureIndex != [fPictureSlider intValue] || !sender) && self.title) {
+ self.pictureIndex = [fPictureSlider intValue];
+ [self displayPreview];
}
}
-- (IBAction)toggleScaleToScreen:(id)sender
+- (IBAction) toggleScaleToScreen: (id) sender
{
- if (scaleToScreen == YES)
+ if (self.scaleToScreen == YES)
{
- scaleToScreen = NO;
+ self.scaleToScreen = NO;
/* make sure we are set to a still preview */
- [self pictureSliderChanged:nil];
+ [self displayPreview];
[fScaleToScreenToggleButton setTitle:@"Scale To Screen"];
}
else
{
- scaleToScreen = YES;
+ self.scaleToScreen = YES;
/* make sure we are set to a still preview */
- [self pictureSliderChanged:nil];
+ [self displayPreview];
[fScaleToScreenToggleButton setTitle:@"Actual Scale"];
}
-
-}
-
-#pragma mark Still Preview Image Processing
-
-// This function converts an image created by libhb (specified via pictureIndex) into
-// an NSImage suitable for the GUI code to use. If removeBorders is YES,
-// makeImageForPicture crops the image generated by libhb stripping off the gray
-// border around the content. This is the low-level method that generates the image.
-// -imageForPicture calls this function whenever it can't find an image in its cache.
-- (NSImage *) makeImageForPicture: (NSInteger)pictureIndex
- libhb:(hb_handle_t*)handle
- title:(hb_title_t*)title
-{
- static uint8_t * buffer;
- static int bufferSize;
-
- // Make sure we have a big enough buffer to receive the image from libhb. libhb
- int dstWidth = title->job->width;
- int dstHeight = title->job->height;
-
- int newSize;
- newSize = dstWidth * dstHeight * 4;
- if( bufferSize < newSize )
- {
- bufferSize = newSize;
- buffer = (uint8_t *) realloc( buffer, bufferSize );
- }
-
- // Enable and the disable deinterlace just for preview if deinterlace
- // or decomb filters are enabled
- int deinterlaceStatus = title->job->deinterlace;
-
- if (self.deinterlacePreview)
- title->job->deinterlace = 1;
-
- hb_get_preview( handle, title->job, (int)pictureIndex, buffer );
-
- title->job->deinterlace = deinterlaceStatus;
-
- // Create an NSBitmapImageRep and copy the libhb image into it, converting it from
- // libhb's format to one suitable for NSImage. Along the way, we'll strip off the
- // border around libhb's image.
-
- // The image data returned by hb_get_preview is 4 bytes per pixel, BGRA format.
- // Alpha is ignored.
-
- NSBitmapFormat bitmapFormat = (NSBitmapFormat)NSAlphaFirstBitmapFormat;
- NSBitmapImageRep * imgrep = [[[NSBitmapImageRep alloc]
- initWithBitmapDataPlanes:nil
- pixelsWide:dstWidth
- pixelsHigh:dstHeight
- bitsPerSample:8
- samplesPerPixel:3 // ignore alpha
- hasAlpha:NO
- isPlanar:NO
- colorSpaceName:NSCalibratedRGBColorSpace
- bitmapFormat:bitmapFormat
- bytesPerRow:dstWidth * 4
- bitsPerPixel:32] autorelease];
-
- UInt32 * src = (UInt32 *)buffer;
- UInt32 * dst = (UInt32 *)[imgrep bitmapData];
- int r, c;
- for (r = 0; r < dstHeight; r++)
- {
- for (c = 0; c < dstWidth; c++)
-#if TARGET_RT_LITTLE_ENDIAN
- *dst++ = Endian32_Swap(*src++);
-#else
- *dst++ = *src++;
-#endif
- }
-
- NSImage * img = [[[NSImage alloc] initWithSize: NSMakeSize(dstWidth, dstHeight)] autorelease];
- [img addRepresentation:imgrep];
-
- return img;
}
-// Returns the preview image for the specified index, retrieving it from its internal
-// cache or by calling makeImageForPicture if it is not cached. Generally, you should
-// use imageForPicture so that images are cached. Calling makeImageForPicture will
-// always generate a new copy of the image.
-- (NSImage *) imageForPicture: (NSInteger) pictureIndex
+- (NSString *) pictureSizeInfoString
{
- // The preview for the specified index may not currently exist, so this method
- // generates it if necessary.
- NSNumber * key = [NSNumber numberWithInteger:pictureIndex];
- NSImage * theImage = [fPicturePreviews objectForKey:key];
- if (!theImage)
- {
- theImage = [self makeImageForPicture:pictureIndex libhb:fHandle title:fTitle];
- [fPicturePreviews setObject:theImage forKey:key];
- }
- return theImage;
+ return [fInfoField stringValue];
}
-// Purges all images from the cache. The next call to imageForPicture will cause a new
-// image to be generated.
-- (void) purgeImageCache
+- (IBAction) showPictureSettings: (id) sender
{
- [fPicturePreviews removeAllObjects];
+ [self.delegate showPicturePanel:self];
}
-#pragma mark Movie Preview
+#pragma mark -
+#pragma mark Movie preview mode
-- (IBAction) cancelCreateMoviePreview: (id) sender
-{
- hb_state_t s;
- hb_get_state2(fPreviewLibhb, &s);
-
- if (fEncodeState && (s.state == HB_STATE_WORKING ||
- s.state == HB_STATE_PAUSED))
- {
- fEncodeState = 2;
- hb_stop(fPreviewLibhb);
- hb_system_sleep_allow(fPreviewLibhb);
- [NSAnimationContext beginGrouping];
- [[NSAnimationContext currentContext] setDuration:0.2];
- [[fEncodingControlBox animator] setHidden:YES];
- [[fPictureControlBox animator] setHidden:NO];
- [NSAnimationContext endGrouping];
+- (void) updateProgress: (double) progress info: (NSString *) progressInfo {
+ [fPreviewMovieStatusField setStringValue: progressInfo];
- return;
- }
+ [fMovieCreationProgressIndicator setIndeterminate: NO];
+ [fMovieCreationProgressIndicator setDoubleValue: progress];
}
-- (IBAction) createMoviePreview: (id) sender
-{
- /* Rip or Cancel ? */
- hb_state_t s;
- hb_get_state2( fPreviewLibhb, &s );
-
- /* we use controller.mm's prepareJobForPreview to go ahead and set all of our settings
- * however, we want to use a temporary destination field of course
- * so that we do not put our temp preview in the users chosen
- * directory */
-
- hb_job_t * job = fTitle->job;
-
- /* We run our current setting through prepeareJob in Controller.mm
- * just as if it were a regular encode */
-
- [fHBController prepareJobForPreview];
-
- /* Make sure we have a Preview sub directory with our pidnum attached */
- NSString *PreviewDirectory = [NSString stringWithFormat:@"~/Library/Application Support/HandBrake/Previews/%d", [fHBController getPidnum]];
- PreviewDirectory = [PreviewDirectory stringByExpandingTildeInPath];
- if( ![[NSFileManager defaultManager] fileExistsAtPath:PreviewDirectory] )
- {
- [[NSFileManager defaultManager] createDirectoryAtPath:PreviewDirectory
- withIntermediateDirectories:NO
- attributes:nil
- error:nil];
- }
- /* Destination file. We set this to our preview directory
- * changing the extension appropriately.*/
- if (fTitle->job->mux & HB_MUX_MASK_MP4) // MP4 file
- {
- /* we use .m4v for our mp4 files so that ac3 and chapters in mp4 will play properly */
- fPreviewMoviePath = [PreviewDirectory stringByAppendingString:@"/preview_temp.m4v"];
- }
- else if (fTitle->job->mux & HB_MUX_MASK_MKV) // MKV file
- {
- fPreviewMoviePath = [PreviewDirectory stringByAppendingString:@"/preview_temp.mkv"];
- }
-
- fPreviewMoviePath = [[fPreviewMoviePath stringByExpandingTildeInPath]retain];
-
- /* See if there is an existing preview file, if so, delete it */
- if( ![[NSFileManager defaultManager] fileExistsAtPath:fPreviewMoviePath] )
- {
- [[NSFileManager defaultManager] removeItemAtPath:fPreviewMoviePath error:nil];
- }
-
- /* We now direct our preview encode to fPreviewMoviePath */
- hb_job_set_file(fTitle->job, [fPreviewMoviePath UTF8String]);
-
- /* We use our advance pref to determine how many previews to scan */
- int hb_num_previews = [[[NSUserDefaults standardUserDefaults] objectForKey:@"PreviewsNumber"] intValue];
- job->start_at_preview = fPicture + 1;
- job->seek_points = hb_num_previews;
-
- /* we use the preview duration popup to get the specified
- * number of seconds for the preview encode.
- */
-
- job->pts_to_stop = [[fPreviewMovieLengthPopUp titleOfSelectedItem] intValue] * 90000LL;
-
- /* lets go ahead and send it off to libhb
- * Note: unlike a full encode, we only send 1 pass regardless if the final encode calls for 2 passes.
- * this should suffice for a fairly accurate short preview and cuts our preview generation time in half.
- * However we also need to take into account the indepth scan for subtitles.
- */
- /*
- * If scanning we need to do some extra setup of the job.
- */
- if( job->indepth_scan == 1 )
+- (void) didCreateMovieAtURL: (NSURL *) fileURL
+{
+ /* Load the new movie into fMovieView */
+ if (fileURL)
{
- char *x264opts_tmp;
-
- /*
- * When subtitle scan is enabled do a fast pre-scan job
- * which will determine which subtitles to enable, if any.
- */
- job->pass = -1;
- x264opts_tmp = job->advanced_opts;
-
- job->advanced_opts = NULL;
- job->indepth_scan = 1;
- /*
- * Add the pre-scan job
- */
- hb_add( fPreviewLibhb, job );
- job->advanced_opts = x264opts_tmp;
- }
- /* Go ahead and perform the actual encoding preview scan */
- job->indepth_scan = 0;
- job->pass = 0;
- hb_add( fPreviewLibhb, job );
+ NSError *outError;
+ NSDictionary *movieAttributes = @{QTMovieURLAttribute: fileURL,
+ QTMovieAskUnresolvedDataRefsAttribute: @(NO),
+ @"QTMovieOpenForPlaybackAttribute": @(YES),
+ @"QTMovieOpenAsyncRequiredAttribute": @(NO),
+ @"QTMovieOpenAsyncOKAttribute": @(NO),
+ @"QTMovieIsSteppableAttribute": @(YES),
+ QTMovieApertureModeAttribute: QTMovieApertureModeClean};
+
+ QTMovie *movie = [[[QTMovie alloc] initWithAttributes:movieAttributes error:&outError] autorelease];
+
+ if (!movie)
+ {
+ [self.delegate writeToActivityLog: "showMoviePreview: Unable to open movie"];
+ [self switchViewToMode:ViewModePicturePreview];
+ }
+ else
+ {
+ /* Scale the fMovieView to the picture player size */
+ [fMovieView setFrameSize:[self.pictureLayer frame].size];
+ [fMovieView setFrameOrigin:[self.pictureLayer frame].origin];
- /* we need to clean up the various lists after the job(s) have been set */
- hb_job_reset( job );
+ [fMovieView setMovie:movie];
+ [movie setDelegate:self];
- [fMovieCreationProgressIndicator setDoubleValue:0];
+ // get and enable subtitles
+ NSArray *subtitlesArray = [movie tracksOfMediaType: @"sbtl"];
+ if (subtitlesArray && [subtitlesArray count])
+ {
+ // enable the first tx3g subtitle track
+ [[subtitlesArray objectAtIndex: 0] setEnabled: YES];
+ }
+ else
+ {
+ // Perian subtitles
+ subtitlesArray = [movie tracksOfMediaType: QTMediaTypeVideo];
+ if (subtitlesArray && ([subtitlesArray count] >= 2))
+ {
+ // track 0 should be video, other video tracks should
+ // be subtitles; force-enable the first subs track
+ [[subtitlesArray objectAtIndex: 1] setEnabled: YES];
+ }
+ }
- [NSAnimationContext beginGrouping];
- [[NSAnimationContext currentContext] setDuration:0.2];
- [[fEncodingControlBox animator] setHidden: NO];
- [[fPictureControlBox animator] setHidden: YES];
- [NSAnimationContext endGrouping];
+ // to actually play the movie
+ self.movie = movie;
- /* Let fPreviewLibhb do the job */
- fEncodeState = 1;
- hb_system_sleep_prevent(fPreviewLibhb);
- hb_start(fPreviewLibhb);
-
-}
+ [self switchViewToMode:ViewModeMoviePreview];
-- (void) startReceivingLibhbNotifications
-{
- if (!fLibhbTimer)
- {
- fLibhbTimer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(libhbTimerFired:) userInfo:nil repeats:YES];
- [fLibhbTimer retain];
+ [fMovieView play:movie];
+ }
}
}
-- (void) stopReceivingLibhbNotifications
-{
- if (fLibhbTimer)
- {
- [fLibhbTimer invalidate];
- [fLibhbTimer release];
- fLibhbTimer = nil;
- }
-}
-- (void) libhbTimerFired: (NSTimer*)theTimer
+- (IBAction) cancelCreateMoviePreview: (id) sender
{
- hb_state_t s;
- hb_get_state( fPreviewLibhb, &s );
- [self libhbStateChanged: s];
+ [self switchViewToMode:ViewModePicturePreview];
}
-- (void) libhbStateChanged: (hb_state_t)state
+- (IBAction) createMoviePreview: (id) sender
{
- switch( state.state )
- {
- case HB_STATE_IDLE:
- case HB_STATE_SCANNING:
- case HB_STATE_SCANDONE:
- break;
-
- case HB_STATE_WORKING:
- {
-#define p state.param.working
- NSMutableString * string;
- /* Update text field */
- string = [NSMutableString stringWithFormat: NSLocalizedString( @"Encoding preview: %.2f %%", @"" ), 100.0 * p.progress];
-
- if( p.seconds > -1 )
- {
- [string appendFormat:
- NSLocalizedString( @" (%.2f fps, avg %.2f fps, ETA %02dh%02dm%02ds)", @"" ),
- p.rate_cur, p.rate_avg, p.hours, p.minutes, p.seconds];
- }
- [fPreviewMovieStatusField setStringValue: string];
-
- [fMovieCreationProgressIndicator setIndeterminate: NO];
- /* Update slider */
- [fMovieCreationProgressIndicator setDoubleValue: 100.0 * p.progress];
-
- break;
-
- }
-#undef p
-
-#define p state.param.muxing
- case HB_STATE_MUXING:
- {
- // Update fMovieCreationProgressIndicator
- [fMovieCreationProgressIndicator setIndeterminate: YES];
- [fMovieCreationProgressIndicator startAnimation: nil];
- [fPreviewMovieStatusField setStringValue: NSLocalizedString( @"Muxing Preview ...", @"" )];
- break;
- }
-#undef p
- case HB_STATE_PAUSED:
- [fMovieCreationProgressIndicator stopAnimation: nil];
- break;
-
- case HB_STATE_WORKDONE:
- {
- // Delete all remaining jobs since libhb doesn't do this on its own.
- hb_job_t * job;
- while( ( job = hb_job(fPreviewLibhb, 0) ) )
- hb_rem( fHandle, job );
-
- [fPreviewMovieStatusField setStringValue: @""];
- [fMovieCreationProgressIndicator stopAnimation: nil];
-
- if (fEncodeState != 2)
- {
- // Show the movie view
- [self showMoviePreview:fPreviewMoviePath];
- }
-
- fEncodeState = 0;
- /* Done encoding, allow system sleep for the preview handle */
- hb_system_sleep_allow(fPreviewLibhb);
- break;
- }
- }
+ if (!self.generator)
+ return;
+
+ self.generator.delegate = self;
+ [self.delegate prepareJobForPreview];
+ [self.generator createMovieAsyncWithImageIndex:self.pictureIndex
+ andDuration:[[fPreviewMovieLengthPopUp titleOfSelectedItem] intValue]];
+
+ [self switchViewToMode:ViewModeEncoding];
}
- (IBAction) toggleMoviePreviewPlayPause: (id) sender
{
/* make sure a movie is even loaded up */
- if (aMovie != nil)
+ if (self.movie)
{
- /* For some stupid reason there is no "isPlaying" method for a QTMovie
- * object, given that, we detect the rate to determine whether the movie
- * is playing or not.
- */
- if ([self isPlaying]) // we are playing
+ if ([self.movie isPlaying]) // we are playing
{
- [fMovieView pause:aMovie];
+ [fMovieView pause:self.movie];
[fPlayPauseButton setState: NSOnState];
}
else // we are paused or stopped
{
- [fMovieView play:aMovie];
+ [fMovieView play:self.movie];
[fPlayPauseButton setState: NSOffState];
}
}
@@ -939,322 +945,134 @@
- (IBAction) moviePlaybackGoToBeginning: (id) sender
{
- /* make sure a movie is even loaded up */
- if (aMovie != nil)
- {
- [fMovieView gotoBeginning:aMovie];
- }
+ [fMovieView gotoBeginning:self.movie];
}
- (IBAction) moviePlaybackGoToEnd: (id) sender
{
- /* make sure a movie is even loaded up */
- if (aMovie != nil)
- {
- [fMovieView gotoEnd:aMovie];
- }
-}
-
-- (IBAction) moviePlaybackGoBackwardOneFrame: (id) sender
-{
- /* make sure a movie is even loaded up */
- if (aMovie != nil)
- {
- [fMovieView pause:aMovie]; // Pause the movie
- [fMovieView stepBackward:aMovie];
- }
-}
-
-- (IBAction) moviePlaybackGoForwardOneFrame: (id) sender
-{
- /* make sure a movie is even loaded up */
- if (aMovie != nil)
- {
- [fMovieView pause:aMovie]; // Pause the movie
- [fMovieView stepForward:aMovie];
- }
+ [fMovieView gotoEnd:self.movie];
}
- (void) startMovieTimer
{
- if( fMovieTimer ) {
- [fMovieTimer invalidate];
- [fMovieTimer release];
- }
- fMovieTimer = [NSTimer scheduledTimerWithTimeInterval:0.10 target:self selector:@selector(movieTimerFired:) userInfo:nil repeats:YES];
- [fMovieTimer retain];
+ if (!self.movieTimer)
+ {
+ self.movieTimer = [NSTimer scheduledTimerWithTimeInterval:0.09 target:self
+ selector:@selector(movieTimerFired:)
+ userInfo:nil repeats:YES];
+ }
}
- (void) stopMovieTimer
{
- if( fMovieTimer )
- {
- [fMovieTimer invalidate];
- [fMovieTimer release];
- fMovieTimer = nil;
- }
+ [self.movieTimer invalidate];
+ self.movieTimer = nil;
}
-- (void) movieTimerFired: (NSTimer*)theTimer
+- (void) movieTimerFired: (NSTimer *)theTimer
{
- if (aMovie != nil)
+ if (self.movie != nil)
{
[self adjustPreviewScrubberForCurrentMovieTime];
- [fMovieInfoField setStringValue: [self SMTPETimecode:[aMovie currentTime]]];
+ [fMovieInfoField setStringValue: [self.movie timecode]];
}
}
- (IBAction) showPicturesPreview: (id) sender
{
- [fMovieView pause:self];
- [self stopMovieTimer];
-
- [NSAnimationContext beginGrouping];
- [[NSAnimationContext currentContext] setDuration:0.2];
- [[fMoviePlaybackControlBox animator] setHidden:YES];
- [[fMovieView animator] setHidden:YES];
- [[fPictureControlBox animator] setHidden:NO];
- [NSAnimationContext endGrouping];
-
- [fMovieView setMovie:nil];
- aMovie = nil;
+ [self switchViewToMode:ViewModePicturePreview];
}
+#pragma mark -
+#pragma mark Movie Playback Scrubber
-- (IBAction) showMoviePreview: (NSString *) path
+// Initialize the preview scrubber min/max to appropriate values for the current movie
+- (void) initPreviewScrubberForMovie
{
- /* Since the gray background for the still images is part of
- * fPictureView, lets leave the picture view visible and postion
- * the fMovieView over the image portion of fPictureView so
- * we retain the gray cropping border we have already established
- * with the still previews
- */
-
- /* Load the new movie into fMovieView */
- if (path)
- {
- NSError *outError;
- NSURL *movieUrl = [NSURL fileURLWithPath:path];
- NSDictionary *movieAttributes = [NSDictionary dictionaryWithObjectsAndKeys:
- movieUrl, QTMovieURLAttribute,
- [NSNumber numberWithBool:NO], QTMovieAskUnresolvedDataRefsAttribute,
- [NSNumber numberWithBool:YES], @"QTMovieOpenForPlaybackAttribute",
- [NSNumber numberWithBool:NO], @"QTMovieOpenAsyncRequiredAttribute",
- [NSNumber numberWithBool:NO], @"QTMovieOpenAsyncOKAttribute",
- [NSNumber numberWithBool:YES], @"QTMovieIsSteppableAttribute",
- QTMovieApertureModeClean, QTMovieApertureModeAttribute,
- nil];
-
- aMovie = [[[QTMovie alloc] initWithAttributes:movieAttributes error:&outError] autorelease];
-
- if (!aMovie)
- {
- [fHBController writeToActivityLog: "showMoviePreview: Unable to open movie"];
- }
- else
- {
- NSRect movieBounds;
-
- [fMovieView setControllerVisible:NO];
-
- /* we get some size information from the preview movie */
- NSSize movieSize = [[aMovie attributeForKey:QTMovieNaturalSizeAttribute] sizeValue];
- movieBounds = [fMovieView movieBounds];
- movieBounds.size.height = movieSize.height;
- /* We also get our view size to use for scaling fMovieView's size */
- NSSize scaledMovieViewSize = [fPictureLayer frame].size;
- movieBounds.size.width = movieSize.width;
-
- /* we need to account for an issue where the scaledMovieViewSize > the window size */
- if (scaledMovieViewSize.height > [[self window] frame].size.height)
- {
- [fHBController writeToActivityLog: "showMoviePreview: Our window is not tall enough to show the controller bar ..."];
- }
-
- /* Scale the fMovieView to scaledMovieViewSize */
- [fMovieView setFrameSize:scaledMovieViewSize];
-
- /*set our origin try using fPictureViewArea or fPictureView */
- NSPoint origin = [fPictureLayer frame].origin;
- origin.x += trunc( ( [fPictureLayer frame].size.width -
- [fMovieView frame].size.width ) / 2.0 );
- origin.y += trunc( ( ( [fPictureLayer frame].size.height -
- [fMovieView frame].size.height ) / 2.0 ) );
-
- [fMovieView setFrameOrigin:origin];
- [fMovieView setMovie:aMovie];
-
- // get and enable subtitles
- NSArray *subtitlesArray;
- subtitlesArray = [aMovie tracksOfMediaType: @"sbtl"];
- if( subtitlesArray && [subtitlesArray count] )
- {
- // enable the first TX3G subtitle track
- [[subtitlesArray objectAtIndex: 0] setEnabled: YES];
- }
- else
- {
- // Perian subtitles
- subtitlesArray = [aMovie tracksOfMediaType: QTMediaTypeVideo];
- if( subtitlesArray && ( [subtitlesArray count] >= 2 ) )
- {
- // track 0 should be video, other video tracks should
- // be subtitles; force-enable the first subs track
- [[subtitlesArray objectAtIndex: 1] setEnabled: YES];
- }
- }
-
- // to actually play the movie
- [NSAnimationContext beginGrouping];
- [[NSAnimationContext currentContext] setDuration:0.2];
- [[fEncodingControlBox animator] setHidden: YES];
- [[fMovieView animator] setHidden:NO];
- [[fMoviePlaybackControlBox animator] setHidden: NO];
- [NSAnimationContext endGrouping];
+ QTTime duration = [self.movie duration];
+ CGFloat result = duration.timeValue / duration.timeScale;
- [self initPreviewScrubberForMovie];
- [self startMovieTimer];
- /* Install amovie notifications */
- [aMovie setDelegate:self];
- [self installMovieCallbacks];
- [fMovieView play:aMovie];
- }
- }
+ [fMovieScrubberSlider setMinValue:0.0];
+ [fMovieScrubberSlider setMaxValue: result];
+ [fMovieScrubberSlider setDoubleValue: 0.0];
}
-#pragma mark *** Movie Playback Scrubber and time code methods ***
-
-// Initialize the preview scrubber min/max to appropriate values for the current movie
--(void) initPreviewScrubberForMovie
+- (void) adjustPreviewScrubberForCurrentMovieTime
{
- if (aMovie)
- {
- QTTime duration = [aMovie duration];
- CGFloat result = duration.timeValue / duration.timeScale;
-
- [fMovieScrubberSlider setMinValue:0.0];
- [fMovieScrubberSlider setMaxValue: result];
- [fMovieScrubberSlider setDoubleValue: 0.0];
- }
-}
+ QTTime time = [self.movie currentTime];
--(void) adjustPreviewScrubberForCurrentMovieTime
-{
- if (aMovie)
- {
- QTTime time = [aMovie currentTime];
-
- CGFloat result = (CGFloat)time.timeValue / (CGFloat)time.timeScale;;
- [fMovieScrubberSlider setDoubleValue:result];
- }
+ CGFloat result = (CGFloat)time.timeValue / (CGFloat)time.timeScale;;
+ [fMovieScrubberSlider setDoubleValue:result];
}
- (IBAction) previewScrubberChanged: (id) sender
{
- if (aMovie)
- {
- [fMovieView pause:aMovie];
- QTTime time = [self SliderToQTTime:[fMovieScrubberSlider doubleValue]];
- [aMovie setCurrentTime:time];
- [fMovieInfoField setStringValue: [self SMTPETimecode:time]];
- }
+ [fMovieView pause:self.movie];
+ [self.movie setCurrentTimeDouble:[fMovieScrubberSlider doubleValue]];
+ [fMovieInfoField setStringValue: [self.movie timecode]];
}
-- (BOOL) isPlaying
-{
- if (aMovie != nil)
- {
- /* For some stupid reason there is no "isPlaying" method for a QTMovie
- * object, given that, we detect the rate to determine whether the movie
- * is playing or not.
- */
- if ([aMovie rate] != 0.0f) // we are playing
- return YES;
- else // we are paused or stopped
- return NO;
- }
- return NO;
-}
-
-#pragma mark *** Movie Notifications ***
+#pragma mark -
+#pragma mark Movie Notifications
-- (void) installMovieCallbacks
+- (void) addMovieObservers
{
- /*Notification for any time the movie rate changes */
+ /* Notification for any time the movie rate changes */
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(movieRateDidChange:)
name:@"QTMovieRateDidChangeNotification"
- object:aMovie];
- /*Notification for when the movie ends */
- [[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(movieDidEnd:)
- name:@"QTMovieDidEndNotification"
- object:aMovie];
+ object:self.movie];
}
-- (void)removeMovieCallbacks
+- (void) removeMovieObservers
{
- if (aMovie)
- {
- /*Notification for any time the movie rate changes */
- [[NSNotificationCenter defaultCenter] removeObserver:self
- name:@"QTMovieRateDidChangeNotification"
- object:aMovie];
- /*Notification for when the movie ends */
- [[NSNotificationCenter defaultCenter] removeObserver:self
- name:@"QTMovieDidEndNotification"
- object:aMovie];
- }
+ /*Notification for any time the movie rate changes */
+ [[NSNotificationCenter defaultCenter] removeObserver:self
+ name:@"QTMovieRateDidChangeNotification"
+ object:self.movie];
}
-- (void)movieRateDidChange:(NSNotification *)notification
+- (void) movieRateDidChange: (NSNotification *) notification
{
- if (aMovie != nil)
- {
- if ([self isPlaying])
- [fPlayPauseButton setState: NSOnState];
- else
- [fPlayPauseButton setState: NSOffState];
- }
+ if ([self.movie isPlaying])
+ [fPlayPauseButton setState: NSOnState];
+ else
+ [fPlayPauseButton setState: NSOffState];
}
-/* This notification is not currently used. However we should keep it "just in case" as
- * live preview playback is enhanced.
- */
-- (void)movieDidEnd:(NSNotification *)notification
-{
- //[fHBController writeToActivityLog: "Movie DidEnd Notification Received"];
-}
+#pragma mark -
+#pragma mark Keyboard and mouse wheel control
/* fMovieView Keyboard controls */
-- (void)keyDown:(NSEvent *)event
+- (void) keyDown: (NSEvent *) event
{
unichar key = [[event charactersIgnoringModifiers] characterAtIndex:0];
+ QTMovie *movie = self.movie;
- if (aMovie)
+ if (movie)
{
if (key == 32)
{
- if ([self isPlaying])
- [fMovieView pause:aMovie];
+ if ([movie isPlaying])
+ [fMovieView pause:movie];
else
- [fMovieView play:aMovie];
+ [fMovieView play:movie];
}
else if (key == 'k')
- [fMovieView pause:aMovie];
+ [fMovieView pause:movie];
else if (key == 'l')
{
- float rate = [aMovie rate];
+ float rate = [movie rate];
rate += 1.0f;
- [fMovieView play:aMovie];
- [aMovie setRate:rate];
+ [fMovieView play:movie];
+ [movie setRate:rate];
}
else if (key == 'j')
{
- float rate = [aMovie rate];
+ float rate = [movie rate];
rate -= 1.0f;
- [fMovieView play:aMovie];
- [aMovie setRate:rate];
+ [fMovieView play:movie];
+ [movie setRate:rate];
}
else if ([event modifierFlags] & NSAlternateKeyMask && key == NSLeftArrowFunctionKey)
[fMovieView gotoBeginning:self];
@@ -1267,16 +1085,16 @@
else
[super keyDown:event];
}
- else if (!fEncodeState)
+ else if (self.currentViewMode != ViewModeEncoding)
{
if (key == NSLeftArrowFunctionKey)
{
- [fPictureSlider setIntegerValue:fPicture > [fPictureSlider minValue] ? fPicture - 1 : fPicture];
+ [fPictureSlider setIntegerValue:self.pictureIndex > [fPictureSlider minValue] ? self.pictureIndex - 1 : self.pictureIndex];
[self pictureSliderChanged:self];
}
else if (key == NSRightArrowFunctionKey)
{
- [fPictureSlider setIntegerValue:fPicture < [fPictureSlider maxValue] ? fPicture + 1 : fPicture];
+ [fPictureSlider setIntegerValue:self.pictureIndex < [fPictureSlider maxValue] ? self.pictureIndex + 1 : self.pictureIndex];
[self pictureSliderChanged:self];
}
else
@@ -1286,209 +1104,21 @@
[super keyDown:event];
}
-- (void)scrollWheel:(NSEvent *)theEvent
+- (void) scrollWheel: (NSEvent *) theEvent
{
- if (!fEncodeState)
+ if (self.currentViewMode != ViewModeEncoding)
{
if ([theEvent deltaY] < 0)
{
- [fPictureSlider setIntegerValue:fPicture < [fPictureSlider maxValue] ? fPicture + 1 : fPicture];
+ [fPictureSlider setIntegerValue:self.pictureIndex < [fPictureSlider maxValue] ? self.pictureIndex + 1 : self.pictureIndex];
[self pictureSliderChanged:self];
}
else if ([theEvent deltaY] > 0)
{
- [fPictureSlider setIntegerValue:fPicture > [fPictureSlider minValue] ? fPicture - 1 : fPicture];
+ [fPictureSlider setIntegerValue:self.pictureIndex > [fPictureSlider minValue] ? self.pictureIndex - 1 : self.pictureIndex];
[self pictureSliderChanged:self];
}
}
}
-#pragma mark *** QTTime Utilities ***
-
- // convert a time value (long) to a QTTime structure
--(QTTime)SliderToQTTime:(double)value
-{
- long timeScale = [[aMovie attributeForKey:QTMovieTimeScaleAttribute] longValue];
- return QTMakeTime(value * timeScale, timeScale);
-}
-
-/* Since MacOSX Leopard QTKit has taken over some responsibility for assessing movie playback
- * information from the old QuickTime carbon api ( time code information as well as fps, etc.).
- * However, the QTKit devs at apple were not really big on documentation and further ...
- * QuickTimes ability to playback HB's largely variable framerate output makes perfectly frame
- * accurate information at best convoluted. Still, for the purpose of a custom hud based custom
- * playback scrubber slider this has so far proven to be as accurate as I have found. To say it
- * could use some better accuracy is not understating it enough probably.
- * Most of this was gleaned from this obscure Apple Mail list thread:
- * http://www.mailinglistarchive.com/[email protected]/msg05642.html
- * Now as we currently do not show a QTKit control bar with scrubber for display sizes > container
- * size, this seems to facilitate playback control from the HB custom HUD controller fairly close
- * to the built in controller bar.
- * Further work needs to be done to try to get accurate frame by frame playback display if we want it.
- * Note that the keyboard commands for frame by frame step through etc. work as always.
- */
-
-// Returns a human readable string from the currentTime of movie playback
-- (NSString*) SMTPETimecode:(QTTime)time
-{
- NSString *smtpeTimeCodeString;
- int days, hour, minute, second, frame;
- long long result;
-
- result = time.timeValue / time.timeScale; // second
- frame = (int)(time.timeValue % time.timeScale) / 100;
-
- second = result % 60;
-
- result = result / 60; // minute
- minute = result % 60;
-
- result = result / 60; // hour
- hour = result % 24;
- days = (int)result;
-
- smtpeTimeCodeString = [NSString stringWithFormat:@"%02d:%02d:%02d", hour, minute, second]; // hh:mm:ss
- return smtpeTimeCodeString;
-}
-
-@end
-
-@implementation PreviewController (Private)
-
-//
-// -[PictureController(Private) optimalViewSizeForImageSize:]
-//
-// Given the size of the preview image to be shown, returns the best possible
-// size for the view.
-//
-- (NSSize)optimalViewSizeForImageSize: (NSSize)imageSize
-{
- // The min size is 480x360
- CGFloat minWidth = 480.0;
- CGFloat minHeight = 360.0;
-
- NSSize screenSize = [[[self window] screen] visibleFrame].size;
- CGFloat maxWidth = screenSize.width;
- CGFloat maxHeight = screenSize.height;
-
- NSSize resultSize = imageSize;
- CGFloat resultPar = resultSize.width / resultSize.height;
-
- //note, a mbp 15" at 1440 x 900 is a 1.6 ar
- CGFloat screenAspect = screenSize.width / screenSize.height;
-
- if ( resultSize.width > maxWidth || resultSize.height > maxHeight )
- {
- // Source is larger than screen in one or more dimensions
- if ( resultPar > screenAspect )
- {
- // Source aspect wider than screen aspect, snap to max width and vary height
- resultSize.width = maxWidth;
- resultSize.height = (maxWidth / resultPar);
- }
- else
- {
- // Source aspect narrower than screen aspect, snap to max height vary width
- resultSize.height = maxHeight;
- resultSize.width = (maxHeight * resultPar);
- }
- }
-
- // If necessary, grow to minimum dimensions to ensure controls overlay is not obstructed
- if ( resultSize.width < minWidth )
- resultSize.width = minWidth;
- if ( resultSize.height < minHeight )
- resultSize.height = minHeight;
-
- return resultSize;
-}
-
-//
-// -[PictureController(Private) resizeWindowForViewSize:]
-//
-// Resizes the entire window to accomodate a view of a particular size.
-//
-- (void)resizeWindowForViewSize: (NSSize)viewSize
-{
- // Figure out the deltas for the new frame area
- NSSize currentSize = [[[self window] contentView] frame].size;
- CGFloat deltaX = viewSize.width - currentSize.width;
- CGFloat deltaY = viewSize.height - currentSize.height;
-
- // Now resize the whole panel by those same deltas, but don't exceed the min
- NSRect frame = [[self window] frame];
- NSSize maxSize = [[[self window] screen] visibleFrame].size;
- /* if we are not Scale To Screen, put an 10% of visible screen on the window */
- if (scaleToScreen == NO )
- {
- maxSize.width = maxSize.width * 0.90;
- maxSize.height = maxSize.height * 0.90;
- }
-
- /* Set our min size to the storage size */
- NSSize minSize;
- minSize.width = fTitle->width / backingScaleFactor;
- minSize.height = fTitle->height / backingScaleFactor;
-
- frame.size.width += deltaX;
- frame.size.height += deltaY;
- if( frame.size.width < minSize.width )
- {
- frame.size.width = minSize.width;
- deltaX = frame.size.width - currentSize.width;
- }
- if( frame.size.height < minSize.height )
- {
- frame.size.height = minSize.height;
- deltaY = frame.size.height - currentSize.height;
- }
- /* compare frame to max size of screen */
-
- if( frame.size.width > maxSize.width )
- {
- frame.size.width = maxSize.width;
- }
-
- if( frame.size.height > maxSize.height )
- {
- frame.size.height = maxSize.height;
- }
-
- // But now the sheet is off-center, so also shift the origin to center it and
- // keep the top aligned.
- if( frame.size.width != [[self window] frame].size.width )
- frame.origin.x -= (deltaX / 2.0);
-
- /* Since upon launch we can open up the preview window if it was open
- * the last time we quit (and at the size it was) we want to make
- * sure that upon resize we do not have the window off the screen
- * So check the origin against the screen origin and adjust if
- * necessary.
- */
- NSSize screenSize = [[[self window] screen] visibleFrame].size;
- NSPoint screenOrigin = [[[self window] screen] frame].origin;
- if (screenSize.height < frame.size.height)
- {
- frame.size.height = screenSize.height;
- }
- if (screenSize.width < frame.size.width)
- {
- frame.size.width = screenSize.width;
- }
-
- /* our origin is off the screen to the left*/
- if (frame.origin.x < screenOrigin.x)
- {
- /* so shift our origin to the right */
- frame.origin.x = screenOrigin.x;
- }
- else if ((frame.origin.x + frame.size.width) > (screenOrigin.x + screenSize.width))
- {
- /* the right side of the preview is off the screen, so shift to the left */
- frame.origin.x = (screenOrigin.x + screenSize.width) - frame.size.width;
- }
-
- [[self window] setFrame:frame display:YES animate:YES];
-}
-
@end