diff options
author | ritsuka <[email protected]> | 2015-01-26 08:30:23 +0000 |
---|---|---|
committer | ritsuka <[email protected]> | 2015-01-26 08:30:23 +0000 |
commit | de6e8f85686ad3f63be7fba7b4bf10aa1912a09a (patch) | |
tree | aa4a061b01b810e38a6d3af245a2380aceb58839 | |
parent | 9545f5c9b6c3160b0e6cba9344232cb9e1493aaf (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.m | 2 | ||||
-rw-r--r-- | macosx/HBCore.h | 35 | ||||
-rw-r--r-- | macosx/HBCore.m | 188 | ||||
-rw-r--r-- | macosx/HBPreviewController.m | 4 | ||||
-rw-r--r-- | macosx/HBPreviewGenerator.h | 4 | ||||
-rw-r--r-- | macosx/HBPreviewGenerator.m | 97 | ||||
-rw-r--r-- | macosx/HandBrake.xcodeproj/project.pbxproj | 4 |
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 */, |