HandBrake/macosx/HBQueueWorker.m
2024-10-30 07:14:51 +01:00

282 lines
10 KiB
Objective-C

/* HBQueueWorker.h
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 "HBQueueWorker.h"
#import "HBRemoteCore.h"
#import "HBJobOutputFileWriter.h"
#import "HBPreferencesKeys.h"
static void *HBQueueWorkerContext = &HBQueueWorkerContext;
static void *HBQueueWorkerLogLevelContext = &HBQueueWorkerLogLevelContext;
NSString * const HBQueueWorkerDidChangeStateNotification = @"HBQueueWorkerDidChangeStateNotification";
NSString * const HBQueueWorkerProgressNotification = @"HBQueueWorkerProgressNotification";
NSString * const HBQueueWorkerProgressNotificationPercentKey = @"HBQueueWorkerProgressNotificationPercentKey";
NSString * const HBQueueWorkerProgressNotificationHoursKey = @"HBQueueWorkerProgressNotificationHoursKey";
NSString * const HBQueueWorkerProgressNotificationMinutesKey = @"HBQueueWorkerProgressNotificationMinutesKey";
NSString * const HBQueueWorkerProgressNotificationSecondsKey = @"HBQueueWorkerProgressNotificationSecondsKey";
NSString * const HBQueueWorkerProgressNotificationInfoKey = @"HBQueueWorkerProgressNotificationInfoKey";
NSString * const HBQueueWorkerDidStartItemNotification = @"HBQueueWorkerDidStartItemNotification";
NSString * const HBQueueWorkerDidCompleteItemNotification = @"HBQueueWorkerDidCompleteItemNotification";
NSString * const HBQueueWorkerItemNotificationItemKey = @"HBQueueWorkerItemNotificationItemKey";
@interface HBQueueWorker ()
@property (nonatomic, readonly) HBRemoteCore *core;
@property (nonatomic, nullable) HBQueueJobItem *item;
@property (nonatomic, nullable) HBJobOutputFileWriter *currentLog;
@end
@implementation HBQueueWorker
- (instancetype)initWithXPCServiceName:(NSString *)serviceName
{
self = [super init];
if (self)
{
NSInteger loggingLevel = [NSUserDefaults.standardUserDefaults integerForKey:HBLoggingLevel];
_core = [[HBRemoteCore alloc] initWithLogLevel:loggingLevel name:serviceName serviceName:serviceName];
// Set up observers
[self.core addObserver:self forKeyPath:@"state"
options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial
context:HBQueueWorkerContext];
[NSUserDefaultsController.sharedUserDefaultsController addObserver:self forKeyPath:@"values.LoggingLevel"
options:0 context:HBQueueWorkerLogLevelContext];
}
return self;
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if (context == HBQueueWorkerContext)
{
[NSNotificationCenter.defaultCenter postNotificationName:HBQueueWorkerDidChangeStateNotification object:self];
}
else if (context == HBQueueWorkerLogLevelContext)
{
self.core.logLevel = [NSUserDefaults.standardUserDefaults integerForKey:HBLoggingLevel];
}
else
{
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
- (void)dealloc
{
[self.core removeObserver:self forKeyPath:@"state" context:HBQueueWorkerContext];
[NSUserDefaultsController.sharedUserDefaultsController removeObserver:self forKeyPath:@"values.LoggingLevel" context:HBQueueWorkerLogLevelContext];
[self.core invalidate];
}
- (void)invalidate
{
[self.core invalidate];
}
- (BOOL)canEncode
{
return self.item == nil;
}
- (BOOL)isEncoding
{
return self.item != nil;
}
- (BOOL)canPause
{
HBState s = self.core.state;
return (s == HBStateWorking || s == HBStateMuxing);
}
- (void)pause
{
[self.item pausedAtDate:[NSDate date]];
[self.core pause];
}
- (BOOL)canResume
{
return self.core.state == HBStatePaused;
}
- (void)resume
{
[self.item resumedAtDate:[NSDate date]];
[self.core resume];
}
- (void)completedItem:(HBQueueJobItem *)item result:(HBCoreResult)result
{
NSParameterAssert(item);
item.endedDate = [NSDate date];
// Since we are done with this encode, tell output to stop writing to the
// individual encode log.
[self.core.stderrRedirect removeListener:self.currentLog];
[self.core.stdoutRedirect removeListener:self.currentLog];
self.currentLog = nil;
// Mark the encode just finished
[self.item setDoneWithResult:result];
self.item = nil;
[NSNotificationCenter.defaultCenter postNotificationName:HBQueueWorkerProgressNotification
object:self
userInfo:@{HBQueueWorkerProgressNotificationPercentKey: @1.0,
HBQueueWorkerProgressNotificationInfoKey: @""}];
[NSNotificationCenter.defaultCenter postNotificationName:HBQueueWorkerDidCompleteItemNotification
object:self
userInfo:@{HBQueueWorkerItemNotificationItemKey: item}];
}
/**
* Here we actually tell hb_scan to perform the source scan, using the path to source and title number
*/
- (void)encodeItem:(HBQueueJobItem *)item
{
NSParameterAssert(item);
// now we mark the queue item as working so another instance can not come along and try to scan it while we are scanning
item.startedDate = [NSDate date];
item.state = HBQueueItemStateWorking;
self.item = item;
[NSNotificationCenter.defaultCenter postNotificationName:HBQueueWorkerDidStartItemNotification
object:self
userInfo:@{HBQueueWorkerItemNotificationItemKey: item}];
// Tell HB to output a new activity log file for this encode
self.currentLog = [[HBJobOutputFileWriter alloc] initWithJob:item.job];
if (self.currentLog)
{
item.activityLogURL = self.currentLog.url;
dispatch_queue_t mainQueue = dispatch_get_main_queue();
[self.core.stderrRedirect addListener:self.currentLog queue:mainQueue];
[self.core.stdoutRedirect addListener:self.currentLog queue:mainQueue];
}
// Progress handler
void (^progressHandler)(HBState state, HBProgress progress, NSString *info) = ^(HBState state, HBProgress progress, NSString *info)
{
[NSNotificationCenter.defaultCenter postNotificationName:HBQueueWorkerProgressNotification
object:self
userInfo:@{HBQueueWorkerProgressNotificationPercentKey: @0,
HBQueueWorkerProgressNotificationInfoKey: info}];
};
// Completion handler
void (^completionHandler)(HBCoreResult result) = ^(HBCoreResult result)
{
if (result.code == HBCoreResultCodeDone)
{
[self realEncodeItem:item];
}
else
{
[self completedItem:item result:result];
}
};
[item.job refreshSecurityScopedResources];
// Only scan 10 previews before an encode - additional previews are
// only useful for autocrop and static previews, which are already taken care of at this point
[self.core scanURL:item.fileURL
titleIndex:item.job.titleIdx
previews:10
minDuration:0
maxDuration:0
keepPreviews:NO
hardwareDecoder:[NSUserDefaults.standardUserDefaults boolForKey:HBUseHardwareDecoder]
keepDuplicateTitles:item.job.keepDuplicateTitles
progressHandler:progressHandler
completionHandler:completionHandler];
}
/**
* This assumes that we have re-scanned and loaded up a new queue item to send to libhb
*/
- (void)realEncodeItem:(HBQueueJobItem *)item
{
HBJob *job = item.job;
if ([NSUserDefaults.standardUserDefaults boolForKey:HBUseHardwareDecoder])
{
job.hwDecodeUsage = HBJobHardwareDecoderUsageFullPathOnly;
if ([NSUserDefaults.standardUserDefaults boolForKey:HBAlwaysUseHardwareDecoder])
{
job.hwDecodeUsage = HBJobHardwareDecoderUsageAlways;
}
}
else
{
job.hwDecodeUsage = HBJobHardwareDecoderUsageNone;
}
// Progress handler
void (^progressHandler)(HBState state, HBProgress progress, NSString *info) = ^(HBState state, HBProgress progress, NSString *info)
{
if (state == HBStateMuxing)
{
[NSNotificationCenter.defaultCenter postNotificationName:HBQueueWorkerProgressNotification
object:self
userInfo:@{HBQueueWorkerProgressNotificationPercentKey: @1,
HBQueueWorkerProgressNotificationInfoKey: info}];
}
else
{
[NSNotificationCenter.defaultCenter postNotificationName:HBQueueWorkerProgressNotification
object:self
userInfo:@{HBQueueWorkerProgressNotificationPercentKey: @(progress.percent),
HBQueueWorkerProgressNotificationHoursKey: @(progress.hours),
HBQueueWorkerProgressNotificationMinutesKey: @(progress.minutes),
HBQueueWorkerProgressNotificationSecondsKey: @(progress.seconds),
HBQueueWorkerProgressNotificationInfoKey: info}];
}
};
// Completion handler
void (^completionHandler)(HBCoreResult result) = ^(HBCoreResult result)
{
[self completedItem:item result:result];
};
// We should be all setup so let 'er rip
[self.core encodeJob:job progressHandler:progressHandler completionHandler:completionHandler];
}
/**
* Cancels the current job
*/
- (void)cancel
{
if (self.core.state == HBStateScanning)
{
[self.core cancelScan];
}
else
{
[self.core cancelEncode];
}
}
@end