summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorritsuka <[email protected]>2015-01-26 08:30:23 +0000
committerritsuka <[email protected]>2015-01-26 08:30:23 +0000
commitde6e8f85686ad3f63be7fba7b4bf10aa1912a09a (patch)
treeaa4a061b01b810e38a6d3af245a2380aceb58839
parent9545f5c9b6c3160b0e6cba9344232cb9e1493aaf (diff)
MacGui: add a method to return a CGImageRef for a preview in HBCore, and skip the alpha to use less memory. Use a dispatch_source as a timer in HBCore so we will be able to run the update loop on its own thread. Remove the pointer to hb_handle_t, no class outside HBCore uses it.
git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@6816 b64f7644-9d1e-0410-96f1-a4d463321fa5
-rw-r--r--macosx/HBController.m2
-rw-r--r--macosx/HBCore.h35
-rw-r--r--macosx/HBCore.m188
-rw-r--r--macosx/HBPreviewController.m4
-rw-r--r--macosx/HBPreviewGenerator.h4
-rw-r--r--macosx/HBPreviewGenerator.m97
-rw-r--r--macosx/HandBrake.xcodeproj/project.pbxproj4
7 files changed, 179 insertions, 155 deletions
diff --git a/macosx/HBController.m b/macosx/HBController.m
index 9de07ef23..75c3c5ef1 100644
--- a/macosx/HBController.m
+++ b/macosx/HBController.m
@@ -791,7 +791,7 @@
// Generate a new file name
NSString *fileName = [HBUtilities automaticNameForSource:title.name
- title:title.hb_title->index
+ title:title.index
chapters:NSMakeRange(self.job.range.chapterStart + 1, self.job.range.chapterStop + 1)
quality:self.job.video.qualityType ? self.job.video.quality : 0
bitrate:!self.job.video.qualityType ? self.job.video.avgBitrate : 0
diff --git a/macosx/HBCore.h b/macosx/HBCore.h
index 6eee94065..03d05ba9e 100644
--- a/macosx/HBCore.h
+++ b/macosx/HBCore.h
@@ -8,6 +8,8 @@
#include "hb.h"
@class HBJob;
+@class HBPicture;
+@class HBTitle;
// These constants specify the current state of HBCore.
typedef NS_ENUM(NSUInteger, HBState) {
@@ -70,11 +72,6 @@ typedef void (^HBCoreCompletionHandler)(BOOL success);
@property (nonatomic, readonly) HBState state;
/**
- * Pointer to a libhb handle used by this HBCore instance.
- */
-@property (nonatomic, readonly) hb_handle_t *hb_handle;
-
-/**
* The name of the core, used for debugging purpose.
*/
@property (nonatomic, copy) NSString *name;
@@ -90,7 +87,7 @@ typedef void (^HBCoreCompletionHandler)(BOOL success);
- (BOOL)canScan:(NSURL *)url error:(NSError **)error;
/**
- * Starts the asynchronous execution of a scan.
+ * Initiates an asynchronous scan operation and returns immediately.
*
* @param url the URL of the input file.
* @param index the index of the desired title. Use 0 to scan every title.
@@ -103,6 +100,7 @@ typedef void (^HBCoreCompletionHandler)(BOOL success);
/**
* Cancels the scan execution.
+ * Cancel can be invoked when the scan is running.
*/
- (void)cancelScan;
@@ -112,7 +110,23 @@ typedef void (^HBCoreCompletionHandler)(BOOL success);
@property (nonatomic, readonly) NSArray *titles;
/**
- * Starts an asynchronous encoding session with the passed job.
+ * This function converts an image created by libhb (specified via index)
+ * into an CGImage.
+ *
+ * @param index the index of the desired image.
+ * @param title Handle to hb_title_t of desired title
+ * @param frame a HBPicture instance that describe the image's frame.
+ * @param deinterlace whether the preview image must be deinterlaced or not.
+ *
+ * @return a CGImageRef of the wanted image, NULL if the index is out of bounds.
+ */
+- (CGImageRef)copyImageAtIndex:(NSUInteger)index
+ forTitle:(HBTitle *)title
+ pictureFrame:(HBPicture *)frame
+ deinterlace:(BOOL)deinterlace;
+
+/**
+ * Initiates an asynchronous encode operation and returns immediately.
*
* @param job the job to encode
* @param progressHandler a block called periodically with the progress information.
@@ -121,17 +135,20 @@ typedef void (^HBCoreCompletionHandler)(BOOL success);
- (void)encodeJob:(HBJob *)job progressHandler:(HBCoreProgressHandler)progressHandler completionHandler:(HBCoreCompletionHandler)completionHandler;
/**
- * Stops encoding session and releases resources.
+ * Stops encode operation and releases resources.
+ * Cancel can be invoked when the encode is running.
*/
- (void)cancelEncode;
/**
- * Pauses the encoding session.
+ * Pauses the encode operation.
+ * Pause can be invoked when the encode is running.
*/
- (void)pause;
/**
* Resumes a paused encoding session.
+ * Resume can be invoked when the encode is running.
*/
- (void)resume;
diff --git a/macosx/HBCore.m b/macosx/HBCore.m
index 61afbcdc4..8446a6a33 100644
--- a/macosx/HBCore.m
+++ b/macosx/HBCore.m
@@ -32,11 +32,15 @@ static void hb_error_handler(const char *errmsg)
/// Pointer to a hb_state_s struct containing the detailed state information of libhb.
@property (nonatomic, readonly) hb_state_t *hb_state;
+/// Pointer to a libhb handle used by this HBCore instance.
+@property (nonatomic, readonly) hb_handle_t *hb_handle;
+
/// Current state of HBCore.
@property (nonatomic, readwrite) HBState state;
/// Timer used to poll libhb for state changes.
-@property (nonatomic, readwrite, retain) NSTimer *updateTimer;
+@property (nonatomic, readwrite) dispatch_source_t updateTimer;
+@property (nonatomic, readonly) dispatch_queue_t updateTimerQueue;
/// Current scanned titles.
@property (nonatomic, readwrite, retain) NSArray *titles;
@@ -91,8 +95,6 @@ static void hb_error_handler(const char *errmsg)
* functions HBCore are used.
*
* @param debugMode If set to YES, libhb will print verbose debug output.
- *
- * @return YES if libhb was opened, NO if there was an error.
*/
- (instancetype)initWithLoggingLevel:(int)loggingLevel
{
@@ -101,6 +103,7 @@ static void hb_error_handler(const char *errmsg)
{
_name = @"HBCore";
_state = HBStateIdle;
+ _updateTimerQueue = dispatch_queue_create("fr.handbrake.coreQueue", DISPATCH_QUEUE_SERIAL);
_hb_state = malloc(sizeof(struct hb_state_s));
_hb_handle = hb_init(loggingLevel, 0);
@@ -120,10 +123,19 @@ static void hb_error_handler(const char *errmsg)
- (void)dealloc
{
[self stopUpdateTimer];
+
+ dispatch_release(_updateTimerQueue);
+
hb_close(&_hb_handle);
_hb_handle = NULL;
-
free(_hb_state);
+
+ [_name release];
+ _name = nil;
+
+ [_titles release];
+ _titles = nil;
+
[super dealloc];
}
@@ -177,11 +189,12 @@ static void hb_error_handler(const char *errmsg)
- (void)scanURL:(NSURL *)url titleIndex:(NSUInteger)index previews:(NSUInteger)previewsNum minDuration:(NSUInteger)seconds progressHandler:(HBCoreProgressHandler)progressHandler completionHandler:(HBCoreCompletionHandler)completionHandler
{
NSAssert(self.state == HBStateIdle, @"[HBCore scanURL:] called while another scan or encode already in progress");
+ NSAssert(url, @"[HBCore scanURL:] called with nil url.");
// Reset the titles array
self.titles = nil;
- // Copy the progress/completation blocks
+ // Copy the progress/completion blocks
self.progressHandler = progressHandler;
self.completionHandler = completionHandler;
@@ -260,13 +273,80 @@ static void hb_error_handler(const char *errmsg)
[HBUtilities writeToActivityLog:"%s scan cancelled", self.name.UTF8String];
}
+#pragma mark - Preview images
+
+- (CGImageRef)copyImageAtIndex:(NSUInteger)index
+ forTitle:(HBTitle *)title
+ pictureFrame:(HBPicture *)frame
+ deinterlace:(BOOL)deinterlace
+{
+ CGImageRef img = NULL;
+
+ hb_geometry_settings_t geo;
+ memset(&geo, 0, sizeof(geo));
+ geo.geometry.width = frame.width;
+ geo.geometry.height = frame.height;
+ // ignore the par.
+ geo.geometry.par.num = 1;
+ geo.geometry.par.den = 1;
+ int crop[4] = {frame.cropTop, frame.cropBottom, frame.cropLeft, frame.cropRight};
+ memcpy(geo.crop, crop, sizeof(int[4]));
+
+ hb_image_t *image = hb_get_preview2(_hb_handle, title.index, (int)index, &geo, deinterlace);
+
+ if (image)
+ {
+ // Create an CGImageRef and copy the libhb image into it.
+ // The image data returned by hb_get_preview2 is 4 bytes per pixel, BGRA format.
+ // Alpha is ignored.
+ CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault | kCGImageAlphaNone;
+ CFMutableDataRef imgData = CFDataCreateMutable(kCFAllocatorDefault, 3 * image->width * image->height);
+ CGDataProviderRef provider = CGDataProviderCreateWithCFData(imgData);
+ CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
+ img = CGImageCreate(image->width,
+ image->height,
+ 8,
+ 24,
+ image->width * 3,
+ colorSpace,
+ bitmapInfo,
+ provider,
+ NULL,
+ NO,
+ kCGRenderingIntentDefault);
+ CGColorSpaceRelease(colorSpace);
+ CGDataProviderRelease(provider);
+ CFRelease(imgData);
+
+ UInt8 *src_line = image->data;
+ UInt8 *dst = CFDataGetMutableBytePtr(imgData);
+ for (int r = 0; r < image->height; r++)
+ {
+ UInt8 *src = src_line;
+ for (int c = 0; c < image->width; c++)
+ {
+ *dst++ = src[2];
+ *dst++ = src[1];
+ *dst++ = src[0];
+ src += 4;
+ }
+ src_line += image->plane[0].stride;
+ }
+
+ hb_image_close(&image);
+ }
+
+ return img;
+}
+
#pragma mark - Encodes
- (void)encodeJob:(HBJob *)job progressHandler:(HBCoreProgressHandler)progressHandler completionHandler:(HBCoreCompletionHandler)completionHandler;
{
NSAssert(self.state == HBStateIdle, @"[HBCore encodeJob:] called while another scan or encode already in progress");
+ NSAssert(job, @"[HBCore encodeJob:] called with nil job");
- // Copy the progress/completation blocks
+ // Copy the progress/completion blocks
self.progressHandler = progressHandler;
self.completionHandler = completionHandler;
@@ -347,13 +427,16 @@ static void hb_error_handler(const char *errmsg)
{
if (!self.updateTimer)
{
- self.updateTimer = [NSTimer scheduledTimerWithTimeInterval:seconds
- target:self
- selector:@selector(stateUpdateTimer:)
- userInfo:NULL
- repeats:YES];
-
- [[NSRunLoop currentRunLoop] addTimer:self.updateTimer forMode:NSEventTrackingRunLoopMode];
+ dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
+ if (timer)
+ {
+ dispatch_source_set_timer(timer, dispatch_walltime(NULL, 0), (uint64_t)(seconds * NSEC_PER_SEC), (uint64_t)(seconds * NSEC_PER_SEC / 10));
+ dispatch_source_set_event_handler(timer, ^{
+ [self updateState];
+ });
+ dispatch_resume(timer);
+ }
+ self.updateTimer = timer;
}
}
@@ -362,30 +445,11 @@ static void hb_error_handler(const char *errmsg)
*/
- (void)stopUpdateTimer
{
- [self.updateTimer invalidate];
- self.updateTimer = nil;
-}
-
-/**
- * Transforms a libhb state constant to a matching HBCore selector.
- */
-- (const SEL)selectorForState:(HBState)stateValue
-{
- switch (stateValue)
+ if (self.updateTimer)
{
- case HB_STATE_WORKING:
- case HB_STATE_SCANNING:
- case HB_STATE_MUXING:
- case HB_STATE_PAUSED:
- case HB_STATE_SEARCHING:
- return @selector(handleProgress);
- case HB_STATE_SCANDONE:
- return @selector(handleScanCompletion);
- case HB_STATE_WORKDONE:
- return @selector(handleWorkCompletion);
- default:
- NSAssert1(NO, @"[HBCore selectorForState:] unknown state %lu", stateValue);
- return NULL;
+ dispatch_source_cancel(self.updateTimer);
+ dispatch_release(self.updateTimer);
+ self.updateTimer = NULL;
}
}
@@ -394,7 +458,7 @@ static void hb_error_handler(const char *errmsg)
* Additional processing for each state is performed in methods that start
* with 'handle'.
*/
-- (void)stateUpdateTimer:(NSTimer *)timer
+- (void)updateState
{
hb_get_state(_hb_handle, _hb_state);
@@ -407,25 +471,18 @@ static void hb_error_handler(const char *errmsg)
// Update HBCore state to reflect the current state of libhb
self.state = _hb_state->state;
- // Determine name of the method that does further processing for this state.
- SEL sel = [self selectorForState:self.state];
-
+ // Call the handler for the current state
if (_hb_state->state == HB_STATE_WORKDONE || _hb_state->state == HB_STATE_SCANDONE)
{
- // Libhb reported HB_STATE_WORKDONE or HB_STATE_SCANDONE,
- // so nothing interesting will happen after this point, stop the timer.
- [self stopUpdateTimer];
-
- // Set the state to idle, because the update timer won't fire again.
- self.state = HBStateIdle;
- hb_system_sleep_allow(_hb_handle);
+ [self handleCompletion];
+ }
+ else
+ {
+ [self handleProgress];
}
-
- // Call the determined selector.
- [self performSelector:sel];
}
-#pragma mark - Notifications
+#pragma mark - Blocks callbacks
/**
* Processes progress state information.
@@ -439,6 +496,32 @@ static void hb_error_handler(const char *errmsg)
}
/**
+ * Processes completion state information.
+ */
+- (void)handleCompletion
+{
+ // Libhb reported HB_STATE_WORKDONE or HB_STATE_SCANDONE,
+ // so nothing interesting will happen after this point, stop the timer.
+ [self stopUpdateTimer];
+
+ // Set the state to idle, because the update timer won't fire again.
+ self.state = HBStateIdle;
+ // Reallow system sleep.
+ hb_system_sleep_allow(_hb_handle);
+
+ // Call the completion block and clean ups the handlers
+ self.progressHandler = nil;
+ if (_hb_state->state == HB_STATE_WORKDONE)
+ {
+ [self handleWorkCompletion];
+ }
+ else
+ {
+ [self handleScanCompletion];
+ }
+}
+
+/**
* Runs the completion block and clean ups the internal blocks.
*
* @param result the result to pass to the completion block.
@@ -447,8 +530,9 @@ static void hb_error_handler(const char *errmsg)
{
if (self.completionHandler)
{
+ // Retain the completion block, because it could be replaced
+ // inside the same block.
HBCoreCompletionHandler completionHandler = [self.completionHandler retain];
- self.progressHandler = nil;
self.completionHandler = nil;
completionHandler(result);
[completionHandler release];
diff --git a/macosx/HBPreviewController.m b/macosx/HBPreviewController.m
index b139fa25d..31ac1c3f6 100644
--- a/macosx/HBPreviewController.m
+++ b/macosx/HBPreviewController.m
@@ -676,8 +676,8 @@ typedef enum ViewMode : NSUInteger {
{
if (self.window.isVisible)
{
- NSImage *fPreviewImage = [self.generator imageAtIndex:self.pictureIndex shouldCache:YES];
- [self.pictureLayer setContents:fPreviewImage];
+ CGImageRef fPreviewImage = [self.generator imageAtIndex:self.pictureIndex shouldCache:YES];
+ [self.pictureLayer setContents:(id)fPreviewImage];
}
HBPicture *pict = self.job.picture;
diff --git a/macosx/HBPreviewGenerator.h b/macosx/HBPreviewGenerator.h
index 577fe2d6e..42cf013a0 100644
--- a/macosx/HBPreviewGenerator.h
+++ b/macosx/HBPreviewGenerator.h
@@ -4,7 +4,7 @@
Homepage: <http://handbrake.fr/>.
It may be used under the terms of the GNU General Public License. */
-#import <Cocoa/Cocoa.h>
+#import <Foundation/Foundation.h>
@class HBCore;
@class HBJob;
@@ -25,7 +25,7 @@
- (instancetype)initWithCore:(HBCore *)core job:(HBJob *)job;
/* Still image generator */
-- (NSImage *) imageAtIndex: (NSUInteger) index shouldCache: (BOOL) cache;
+- (CGImageRef) imageAtIndex: (NSUInteger) index shouldCache: (BOOL) cache;
- (NSUInteger) imagesCount;
- (void) purgeImageCache;
diff --git a/macosx/HBPreviewGenerator.m b/macosx/HBPreviewGenerator.m
index c4a0d2aa7..61ed448bb 100644
--- a/macosx/HBPreviewGenerator.m
+++ b/macosx/HBPreviewGenerator.m
@@ -10,11 +10,10 @@
#import "HBCore.h"
#import "HBJob.h"
-#import "HBJob+HBJobConversion.h"
@interface HBPreviewGenerator ()
-@property (nonatomic, readonly, retain) NSMutableDictionary *picturePreviews;
+@property (nonatomic, readonly) NSMutableDictionary *picturePreviews;
@property (nonatomic, readonly) NSUInteger imagesCount;
@property (nonatomic, readonly) HBCore *scanCore;
@property (nonatomic, readonly) HBJob *job;
@@ -46,26 +45,28 @@
*
* @param index picture index in title.
*/
-- (NSImage *) imageAtIndex: (NSUInteger) index shouldCache: (BOOL) cache
+- (CGImageRef) imageAtIndex: (NSUInteger) index shouldCache: (BOOL) cache
{
if (index >= self.imagesCount)
return nil;
// The preview for the specified index may not currently exist, so this method
// generates it if necessary.
- NSImage *theImage = [self.picturePreviews objectForKey:@(index)];
+ CGImageRef theImage = (CGImageRef)[self.picturePreviews objectForKey:@(index)];
if (!theImage)
{
HBFilters *filters = self.job.filters;
BOOL deinterlace = (filters.deinterlace && !filters.useDecomb) || (filters.decomb && filters.useDecomb);
- theImage = [HBPreviewGenerator makeImageForPicture:index
- libhb:self.scanCore.hb_handle
- picture:self.job.picture
- deinterlace:deinterlace];
+ theImage = (CGImageRef)[(id)[self.scanCore copyImageAtIndex:index
+ forTitle:self.job.title
+ pictureFrame:self.job.picture
+ deinterlace:deinterlace] autorelease];
if (cache && theImage)
- [self.picturePreviews setObject:theImage forKey:@(index)];
+ {
+ [self.picturePreviews setObject:(id)theImage forKey:@(index)];
+ }
}
return theImage;
@@ -80,84 +81,6 @@
[self.picturePreviews removeAllObjects];
}
-/**
- * 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.
- *
- * @param pictureIndex Index in title.
- * @param handle Handle to hb_handle_t.
- * @param title Handle to hb_title_t of desired title.
- * @param deinterlace Whether the preview image must be deinterlaced or not.
- */
-+ (NSImage *) makeImageForPicture: (NSUInteger) pictureIndex
- libhb: (hb_handle_t *) handle
- picture: (HBPicture *) picture
- deinterlace: (BOOL) deinterlace
-{
- NSImage *img = nil;
-
- hb_geometry_settings_t geo;
- memset(&geo, 0, sizeof(geo));
- geo.geometry.width = picture.width;
- geo.geometry.height = picture.height;
- // HBPreviewController will scale the image later,
- // ignore the par.
- geo.geometry.par.num = 1;
- geo.geometry.par.den = 1;
- int crop[4] = {picture.cropTop, picture.cropBottom, picture.cropLeft, picture.cropRight};
- memcpy(geo.crop, crop, sizeof(int[4]));
-
- hb_image_t *image;
- image = hb_get_preview2(handle, picture.title.hb_title->index, (int)pictureIndex, &geo, deinterlace);
-
- if (image)
- {
- // Create an NSBitmapImageRep and copy the libhb image into it, converting it from
- // libhb's format to one suitable for NSImage.
-
- // The image data returned by hb_get_preview2 is 4 bytes per pixel, BGRA format.
- // Alpha is ignored.
- NSBitmapImageRep *imgrep = [[[NSBitmapImageRep alloc]
- initWithBitmapDataPlanes:nil
- pixelsWide:image->width
- pixelsHigh:image->height
- bitsPerSample:8
- samplesPerPixel:3 // ignore alpha
- hasAlpha:NO
- isPlanar:NO
- colorSpaceName:NSCalibratedRGBColorSpace
- bitmapFormat:NSAlphaFirstBitmapFormat
- bytesPerRow:image->width * 4
- bitsPerPixel:32] autorelease];
-
- UInt8 *src_line = image->data;
- UInt32 *dst = (UInt32 *)[imgrep bitmapData];
- for (int r = 0; r < image->height; r++)
- {
- UInt32 *src = (UInt32 *)src_line;
- for (int c = 0; c < image->width; c++)
- {
-#if TARGET_RT_LITTLE_ENDIAN
- *dst++ = Endian32_Swap(*src++);
-#else
- *dst++ = *src++;
-#endif
- }
- src_line += image->plane[0].stride;
- }
-
- img = [[[NSImage alloc] initWithSize: NSMakeSize(image->width, image->height)] autorelease];
- [img addRepresentation:imgrep];
- }
-
- hb_image_close(&image);
-
- return img;
-}
-
#pragma mark -
#pragma mark Preview movie
diff --git a/macosx/HandBrake.xcodeproj/project.pbxproj b/macosx/HandBrake.xcodeproj/project.pbxproj
index f052c27ae..bed13cd57 100644
--- a/macosx/HandBrake.xcodeproj/project.pbxproj
+++ b/macosx/HandBrake.xcodeproj/project.pbxproj
@@ -942,8 +942,6 @@
A996B0F81A62C51C00B64179 /* Audio */,
A91017B51A64441700039BFB /* Subtitles */,
A9537BED1A48A7F900141102 /* UI Bindings Additions */,
- A9AA447D1970729300D7DEFC /* HBPreviewGenerator.h */,
- A9D1E41618262364002F6424 /* HBPreviewGenerator.m */,
273F209714ADBE670021BE6D /* HBDVDDetector.h */,
273F209814ADBE670021BE6D /* HBDVDDetector.m */,
A9A2A77F1A4737DD006C219C /* NSCodingMacro.h */,
@@ -985,6 +983,8 @@
273F20AA14ADBE670021BE6D /* HBPictureController.m */,
273F20A314ADBE670021BE6D /* HBPreviewController.h */,
273F20A414ADBE670021BE6D /* HBPreviewController.m */,
+ A9AA447D1970729300D7DEFC /* HBPreviewGenerator.h */,
+ A9D1E41618262364002F6424 /* HBPreviewGenerator.m */,
273F209B14ADBE670021BE6D /* HBOutputPanelController.h */,
273F209C14ADBE670021BE6D /* HBOutputPanelController.m */,
273F209F14ADBE670021BE6D /* HBPreferencesController.h */,