diff options
-rw-r--r-- | libhb/dvdnav.c | 9 | ||||
-rw-r--r-- | macosx/Controller.m | 16 | ||||
-rw-r--r-- | macosx/English.lproj/Audio.xib | 20 | ||||
-rw-r--r-- | macosx/English.lproj/Subtitles.xib | 16 | ||||
-rw-r--r-- | macosx/HBAudio.h | 43 | ||||
-rw-r--r-- | macosx/HBAudio.m | 430 | ||||
-rw-r--r-- | macosx/HBAudioController.h | 36 | ||||
-rw-r--r-- | macosx/HBAudioController.m | 434 | ||||
-rw-r--r-- | macosx/HBAudioTrack.h | 4 | ||||
-rw-r--r-- | macosx/HBCore.m | 1 | ||||
-rw-r--r-- | macosx/HBJob+HBJobConversion.h | 18 | ||||
-rw-r--r-- | macosx/HBJob+HBJobConversion.m | 522 | ||||
-rw-r--r-- | macosx/HBJob.h | 35 | ||||
-rw-r--r-- | macosx/HBJob.m | 565 | ||||
-rw-r--r-- | macosx/HBPicture.m | 2 | ||||
-rw-r--r-- | macosx/HBPreviewGenerator.m | 1 | ||||
-rw-r--r-- | macosx/HBQueueController.mm | 55 | ||||
-rw-r--r-- | macosx/HBSubtitles.h | 65 | ||||
-rw-r--r-- | macosx/HBSubtitles.m | 497 | ||||
-rw-r--r-- | macosx/HBSubtitlesController.h | 21 | ||||
-rw-r--r-- | macosx/HBSubtitlesController.m | 504 | ||||
-rw-r--r-- | macosx/HandBrake.xcodeproj/project.pbxproj | 52 |
22 files changed, 1815 insertions, 1531 deletions
diff --git a/libhb/dvdnav.c b/libhb/dvdnav.c index f7153177c..9dcf9e530 100644 --- a/libhb/dvdnav.c +++ b/libhb/dvdnav.c @@ -334,6 +334,7 @@ static hb_title_t * hb_dvdnav_title_scan( hb_dvd_t * e, int t, uint64_t min_dura uint64_t duration, longest; int longest_pgcn, longest_pgn, longest_pgcn_end; const char * name; + unsigned char unused[1024]; const char * codec_name; hb_log( "scan: scanning title %d", t ); @@ -344,8 +345,13 @@ static hb_title_t * hb_dvdnav_title_scan( hb_dvd_t * e, int t, uint64_t min_dura { strncpy( title->name, name, sizeof( title->name ) ); } - else + + if (strlen(title->name) == 0) { + if( DVDUDFVolumeInfo( d->reader, title->name, sizeof( title->name ), + unused, sizeof( unused ) ) ) + { + char * p_cur, * p_last = d->path; for( p_cur = d->path; *p_cur; p_cur++ ) { @@ -358,6 +364,7 @@ static hb_title_t * hb_dvdnav_title_scan( hb_dvd_t * e, int t, uint64_t min_dura char *dot_term = strrchr(title->name, '.'); if (dot_term) *dot_term = '\0'; + } } /* VTS which our title is in */ diff --git a/macosx/Controller.m b/macosx/Controller.m index 8306c2eda..ebbf968fc 100644 --- a/macosx/Controller.m +++ b/macosx/Controller.m @@ -326,8 +326,8 @@ fPreviewController.job = job; fVideoController.job = job; - fAudioController.job = job; - fSubtitlesViewController.job = job; + fAudioController.audio = job.audio; + fSubtitlesViewController.subtitles = job.subtitles; fChapterTitlesController.job = job; if (job) @@ -791,8 +791,8 @@ if (p.job_cur == 1 && p.job_count > 1) { HBJob *queueJob = QueueFileArray[currentQueueEncodeIndex]; - if (queueJob.subtitlesTracks.count && - [[queueJob.subtitlesTracks firstObject][keySubTrackIndex] intValue] == -1) + if (queueJob.subtitles.tracks.count && + [queueJob.subtitles.tracks.firstObject[keySubTrackIndex] intValue] == -1) { pass_desc = NSLocalizedString(@"(subtitle scan)", @""); } @@ -2521,7 +2521,7 @@ static void queueFSEventStreamCallback( NSString *extension = @"mp4"; - BOOL anyCodecAC3 = [fAudioController anyCodecMatches: HB_ACODEC_AC3] || [fAudioController anyCodecMatches: HB_ACODEC_AC3_PASS]; + BOOL anyCodecAC3 = [self.job.audio anyCodecMatches:HB_ACODEC_AC3] || [self.job.audio anyCodecMatches:HB_ACODEC_AC3_PASS]; // Chapter markers are enabled if the checkbox is ticked and we are doing p2p or we have > 1 chapter BOOL chapterMarkers = (self.job.chaptersEnabled) && (self.job.range.type != HBRangeTypeChapters || @@ -2668,12 +2668,6 @@ static void queueFSEventStreamCallback( // Apply the preset to the current job [self.job applyPreset:preset]; - // Have to apply the presets the audio and subtitles controllers too - // because they haven't been moved to HBJob yer. - NSDictionary *chosenPreset = preset.content; - [fAudioController applySettingsFromPreset: chosenPreset]; - [fSubtitlesViewController applySettingsFromPreset:chosenPreset]; - // If Auto Naming is on. We create an output filename of dvd name - title number if ([[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultAutoNaming"]) { diff --git a/macosx/English.lproj/Audio.xib b/macosx/English.lproj/Audio.xib index 6c3d5cb86..2419cde46 100644 --- a/macosx/English.lproj/Audio.xib +++ b/macosx/English.lproj/Audio.xib @@ -1,16 +1,12 @@ <?xml version="1.0" encoding="UTF-8" standalone="no"?> -<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6254" systemVersion="14C81f" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none"> +<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6724" systemVersion="14C94b" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none"> <dependencies> <deployment version="1060" identifier="macosx"/> - <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6254"/> + <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6724"/> </dependencies> <objects> <customObject id="-2" userLabel="File's Owner" customClass="HBAudioController"> <connections> - <outlet property="configureDefaults" destination="vFP-nq-IQg" id="EFe-IR-Cyn"/> - <outlet property="fTableView" destination="LlC-ua-mth" id="NvM-yh-c0L"/> - <outlet property="reloadDefaults" destination="wcL-rL-aYS" id="R78-R5-U0p"/> - <outlet property="trackPopup" destination="jrP-M5-2Rq" id="Avl-Cd-Th2"/> <outlet property="view" destination="LOv-5G-86T" id="qyT-Z6-lCU"/> </connections> </customObject> @@ -21,7 +17,7 @@ <string>keyAudioTrackName</string> </declaredKeys> <connections> - <binding destination="-2" name="contentArray" keyPath="masterTrackArray" id="vVy-cS-D4a"/> + <binding destination="-2" name="contentArray" keyPath="self.audio.masterTrackArray" id="nbW-pN-u9p"/> </connections> </arrayController> <arrayController preservesSelection="NO" avoidsEmptySelection="NO" id="rzb-Si-Kpf" userLabel="Audios"> @@ -51,7 +47,7 @@ <string>bitrateEnabled</string> </declaredKeys> <connections> - <binding destination="-2" name="contentArray" keyPath="audioArray" id="Rfp-6S-XXq"/> + <binding destination="-2" name="contentArray" keyPath="self.audio.tracks" id="W0u-41-zX4"/> </connections> </arrayController> <view id="LOv-5G-86T"> @@ -273,7 +269,7 @@ </tableColumn> </tableColumns> <connections> - <binding destination="-2" name="enabled" keyPath="self.job" id="FGZ-gP-H5Y"> + <binding destination="-2" name="enabled" keyPath="self.audio" id="V0I-xd-Lo6"> <dictionary key="options"> <string key="NSValueTransformerName">NSIsNotNil</string> </dictionary> @@ -306,7 +302,7 @@ </buttonCell> <connections> <action selector="showSettingsSheet:" target="-2" id="D9K-M3-zHd"/> - <binding destination="-2" name="enabled" keyPath="self.job" id="VMC-Xv-X3R"> + <binding destination="-2" name="enabled" keyPath="self.audio" id="cWq-t8-vFR"> <dictionary key="options"> <string key="NSValueTransformerName">NSIsNotNil</string> </dictionary> @@ -322,7 +318,7 @@ </buttonCell> <connections> <action selector="reloadDefaults:" target="-2" id="k9I-I9-T2U"/> - <binding destination="-2" name="enabled" keyPath="self.job" id="rta-fa-Ezd"> + <binding destination="-2" name="enabled" keyPath="self.audio" id="sJS-am-vnf"> <dictionary key="options"> <string key="NSValueTransformerName">NSIsNotNil</string> </dictionary> @@ -353,7 +349,7 @@ </menu> </popUpButtonCell> <connections> - <binding destination="-2" name="enabled" keyPath="self.job" id="FGP-3D-g2t"> + <binding destination="-2" name="enabled" keyPath="self.audio" id="vyM-YV-Yqi"> <dictionary key="options"> <string key="NSValueTransformerName">NSIsNotNil</string> </dictionary> diff --git a/macosx/English.lproj/Subtitles.xib b/macosx/English.lproj/Subtitles.xib index 9d971f5ba..d53bd9634 100644 --- a/macosx/English.lproj/Subtitles.xib +++ b/macosx/English.lproj/Subtitles.xib @@ -1,16 +1,13 @@ <?xml version="1.0" encoding="UTF-8" standalone="no"?> -<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6254" systemVersion="14C81f" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none"> +<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6724" systemVersion="14C94b" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none"> <dependencies> <deployment version="1060" identifier="macosx"/> - <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6254"/> + <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6724"/> </dependencies> <objects> <customObject id="-2" userLabel="File's Owner" customClass="HBSubtitlesController"> <connections> - <outlet property="configureDefaults" destination="QsM-28-Pya" id="AbY-A5-kzy"/> <outlet property="fTableView" destination="0yM-wE-D2x" id="0vq-y5-Ubi"/> - <outlet property="reloadDefaults" destination="Vxx-gk-9kY" id="9fr-kb-QiD"/> - <outlet property="trackPopUp" destination="2Tb-KC-Ugi" id="kwx-uG-p4q"/> <outlet property="view" destination="Hz6-mo-xeY" id="0bl-1N-x8E"/> </connections> </customObject> @@ -133,7 +130,7 @@ </tableColumn> </tableColumns> <connections> - <binding destination="-2" name="enabled" keyPath="self.job" id="PJP-qc-FJe"> + <binding destination="-2" name="enabled" keyPath="self.subtitles" id="pmZ-qP-DnG"> <dictionary key="options"> <string key="NSValueTransformerName">NSIsNotNil</string> </dictionary> @@ -187,7 +184,7 @@ </menu> </popUpButtonCell> <connections> - <binding destination="-2" name="enabled" keyPath="self.job" id="FLd-lw-rWb"> + <binding destination="-2" name="enabled" keyPath="self.subtitles" id="khN-Sn-dnI"> <dictionary key="options"> <string key="NSValueTransformerName">NSIsNotNil</string> </dictionary> @@ -203,7 +200,7 @@ </buttonCell> <connections> <action selector="showSettingsSheet:" target="-2" id="OAA-S8-tfS"/> - <binding destination="-2" name="enabled" keyPath="self.job" id="Nuv-KK-HdK"> + <binding destination="-2" name="enabled" keyPath="self.subtitles" id="dpe-kM-iMF"> <dictionary key="options"> <string key="NSValueTransformerName">NSIsNotNil</string> </dictionary> @@ -219,7 +216,7 @@ </buttonCell> <connections> <action selector="addTracksFromDefaults:" target="-2" id="GOz-FT-Atg"/> - <binding destination="-2" name="enabled" keyPath="self.job" id="Mxg-ln-sW9"> + <binding destination="-2" name="enabled" keyPath="self.subtitles" id="CSa-3s-GD6"> <dictionary key="options"> <string key="NSValueTransformerName">NSIsNotNil</string> </dictionary> @@ -227,6 +224,7 @@ </connections> </button> </subviews> + <point key="canvasLocation" x="231" y="438"/> </customView> <menu id="KgC-dn-Hq2"> <items> diff --git a/macosx/HBAudio.h b/macosx/HBAudio.h new file mode 100644 index 000000000..07ed536fc --- /dev/null +++ b/macosx/HBAudio.h @@ -0,0 +1,43 @@ +/* HBAudio.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 <Foundation/Foundation.h> +#import "HBPresetCoding.h" + +@class HBTitle; +@class HBAudioTrack; +@class HBAudioDefaults; + +@interface HBAudio : NSObject <NSCoding, NSCopying, HBPresetCoding> + +- (instancetype)initWithTitle:(HBTitle *)title; + +@property (nonatomic, readonly) NSDictionary *noneTrack; +@property (nonatomic, readonly) NSArray *masterTrackArray; // the master list of audio tracks from the title +@property (nonatomic, readonly) NSMutableArray *tracks; + +@property (nonatomic, readwrite, retain) HBAudioDefaults *defaults; + +@property (nonatomic, readwrite) int container; // initially is the default HB_MUX_MP4 + +- (void)addAllTracks; +- (void)removeAll; +- (void)reloadDefaults; + +- (BOOL)anyCodecMatches:(int)codec; +- (void)settingTrackToNone:(HBAudioTrack *)newNoneTrack; +- (void)switchingTrackFromNone:(HBAudioTrack *)noLongerNoneTrack; + +@end + +@interface HBAudio (KVC) + +- (NSUInteger)countOfTracks; +- (HBAudioTrack *)objectInTracksAtIndex:(NSUInteger)index; +- (void)insertObject:(HBAudioTrack *)track inTracksAtIndex:(NSUInteger)index; +- (void)removeObjectFromTracksAtIndex:(NSUInteger)index; + +@end diff --git a/macosx/HBAudio.m b/macosx/HBAudio.m new file mode 100644 index 000000000..0d5363e41 --- /dev/null +++ b/macosx/HBAudio.m @@ -0,0 +1,430 @@ +/* HBAudio.m $ + + 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 "HBAudio.h" + +#import "HBTitle.h" +#import "HBAudioTrack.h" +#import "HBAudioTrackPreset.h" +#import "HBAudioDefaults.h" + +#import "NSCodingMacro.h" + +#include "hb.h" + +@implementation HBAudio + +- (instancetype)initWithTitle:(HBTitle *)title +{ + self = [super init]; + if (self) + { + _container = HB_MUX_MP4; + + _tracks = [[NSMutableArray alloc] init]; + _defaults = [[HBAudioDefaults alloc] init]; + + _noneTrack = [@{keyAudioTrackIndex: @0, + keyAudioTrackName: NSLocalizedString(@"None", @"None"), + keyAudioInputCodec: @0} retain]; + + NSMutableArray *sourceTracks = [NSMutableArray array]; + [sourceTracks addObject:_noneTrack]; + [sourceTracks addObjectsFromArray:title.audioTracks]; + _masterTrackArray = [sourceTracks copy]; + + [self switchingTrackFromNone: nil]; // this ensures there is a None track at the end of the lis + } + return self; +} + +- (void)addAllTracks +{ + [self addTracksFromDefaults:YES]; +} + +- (void)removeAll +{ + [self _clearAudioArray]; + [self switchingTrackFromNone:nil]; +} + +- (void)reloadDefaults +{ + [self addTracksFromDefaults:NO]; +} + +- (void)_clearAudioArray +{ + while (0 < [self countOfTracks]) + { + [self removeObjectFromTracksAtIndex: 0]; + } +} + +/** + * Uses the templateAudioArray from the preset to create the audios for the specified trackIndex. + * + * @param templateAudioArray the track template. + * @param trackIndex the index of the source track. + * @param firstOnly use only the first track of the template or all. + */ +- (void)_processPresetAudioArray:(NSArray *)templateAudioArray forTrack:(NSUInteger)trackIndex firstOnly:(BOOL)firstOnly +{ + for (HBAudioTrackPreset *preset in templateAudioArray) + { + BOOL fallenBack = NO; + HBAudioTrack *newAudio = [[HBAudioTrack alloc] init]; + [newAudio setController: self]; + [self insertObject: newAudio inTracksAtIndex: [self countOfTracks]]; + [newAudio setVideoContainerTag:@(self.container)]; + [newAudio setTrackFromIndex: (int)trackIndex]; + + const char *name = hb_audio_encoder_get_name(preset.encoder); + NSString *audioEncoder = nil; + + // Check if we need to use a fallback + if (name) + { + audioEncoder = @(name); + if (preset.encoder & HB_ACODEC_PASS_FLAG && + ![newAudio setCodecFromName:audioEncoder]) + { + int passthru, fallback; + fallenBack = YES; + passthru = hb_audio_encoder_get_from_name([audioEncoder UTF8String]); + fallback = hb_audio_encoder_get_fallback_for_passthru(passthru); + name = hb_audio_encoder_get_name(fallback); + + // If we couldn't find an encoder for the passthru format + // fall back to the selected encoder fallback + if (name == NULL) + { + name = hb_audio_encoder_get_name(self.defaults.encoderFallback); + } + } + else + { + name = hb_audio_encoder_sanitize_name([audioEncoder UTF8String]); + } + audioEncoder = @(name); + } + + // If our preset wants us to support a codec that the track does not support, instead + // of changing the codec we remove the audio instead. + if ([newAudio setCodecFromName:audioEncoder]) + { + const char *mixdown = hb_mixdown_get_name(preset.mixdown); + if (mixdown) + { + [newAudio setMixdownFromName: @(mixdown)]; + } + + const char *sampleRateName = hb_audio_samplerate_get_name(preset.sampleRate); + if (!sampleRateName) + { + [newAudio setSampleRateFromName: @"Auto"]; + } + else + { + [newAudio setSampleRateFromName: @(sampleRateName)]; + } + if (!fallenBack) + { + [newAudio setBitRateFromName: [NSString stringWithFormat:@"%d", preset.bitRate]]; + } + [newAudio setDrc: @(preset.drc)]; + [newAudio setGain: @(preset.gain)]; + } + else + { + [self removeObjectFromTracksAtIndex: [self countOfTracks] - 1]; + } + [newAudio release]; + + if (firstOnly) + { + break; + } + } +} + +/** + * Matches the source audio tracks with the specific language iso code. + * + * @param isoCode the iso code to match. + * @param selectOnlyFirst whether to match only the first track for the iso code. + * + * @return a NSIndexSet with the index of the matched tracks. + */ +- (NSIndexSet *)_tracksWithISOCode:(NSString *)isoCode selectOnlyFirst:(BOOL)selectOnlyFirst +{ + NSMutableIndexSet *indexes = [NSMutableIndexSet indexSet]; + + // We search for the prefix noting that our titles have the format %d: %s where the %s is the prefix + [self.masterTrackArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + if (idx) // Note that we skip the "None" track + { + if ([isoCode isEqualToString:@"und"] || [obj[keyAudioTrackLanguageIsoCode] isEqualToString:isoCode]) + { + [indexes addIndex:idx]; + + if (selectOnlyFirst) + { + *stop = YES; + } + } + } + }]; + + return indexes; +} + +- (void)_processPresetAudioArray:(NSArray *)templateAudioArray forTracks:(NSIndexSet *)trackIndexes firstOnly:(BOOL)firstOnly +{ + __block BOOL firsTrack = firstOnly; + [trackIndexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) { + // Add the track + [self _processPresetAudioArray: self.defaults.tracksArray forTrack:idx firstOnly:firsTrack]; + firsTrack = self.defaults.secondaryEncoderMode ? YES : NO; + }]; +} + +- (void)addTracksFromDefaults:(BOOL)allTracks +{ + BOOL firstTrack = NO; + NSMutableIndexSet *tracksAdded = [NSMutableIndexSet indexSet]; + NSMutableIndexSet *tracksToAdd = [NSMutableIndexSet indexSet]; + + // Reinitialize the configured list of audio tracks + [self _clearAudioArray]; + + if (self.defaults.trackSelectionBehavior != HBAudioTrackSelectionBehaviorNone) + { + // Add tracks of Default and Alternate Language by name + for (NSString *languageISOCode in self.defaults.trackSelectionLanguages) + { + NSMutableIndexSet *tracksIndexes = [[self _tracksWithISOCode: languageISOCode + selectOnlyFirst: self.defaults.trackSelectionBehavior == HBAudioTrackSelectionBehaviorFirst] mutableCopy]; + [tracksIndexes removeIndexes:tracksAdded]; + if (tracksIndexes.count) + { + [self _processPresetAudioArray:self.defaults.tracksArray forTracks:tracksIndexes firstOnly:firstTrack]; + firstTrack = self.defaults.secondaryEncoderMode ? YES : NO; + [tracksAdded addIndexes:tracksIndexes]; + } + [tracksIndexes release]; + } + + // If no preferred Language was found, add standard track 1 + if (tracksAdded.count == 0 && self.masterTrackArray.count > 1) + { + [tracksToAdd addIndex:1]; + } + } + + // If all tracks should be added, add all track numbers that are not yet processed + if (allTracks) + { + [tracksToAdd addIndexesInRange:NSMakeRange(1, self.masterTrackArray.count - 1)]; + [tracksToAdd removeIndexes:tracksAdded]; + } + + if (tracksToAdd.count) + { + [self _processPresetAudioArray:self.defaults.tracksArray forTracks:tracksToAdd firstOnly:firstTrack]; + } + + // Add an None item + [self switchingTrackFromNone: nil]; +} + +- (BOOL)anyCodecMatches:(int)codec +{ + BOOL retval = NO; + NSUInteger audioArrayCount = [self countOfTracks]; + for (NSUInteger i = 0; i < audioArrayCount && !retval; i++) + { + HBAudioTrack *anAudio = [self objectInTracksAtIndex: i]; + if ([anAudio enabled] && codec == [[anAudio codec][keyAudioCodec] intValue]) + { + retval = YES; + } + } + return retval; +} + +- (void)addNewAudioTrack +{ + HBAudioTrack *newAudio = [[HBAudioTrack alloc] init]; + [newAudio setController: self]; + [self insertObject: newAudio inTracksAtIndex: [self countOfTracks]]; + [newAudio setVideoContainerTag:@(self.container)]; + [newAudio setTrack: self.noneTrack]; + [newAudio setDrc: @0.0f]; + [newAudio setGain: @0.0f]; + [newAudio release]; +} + +#pragma mark - +#pragma mark Notification Handling + +- (void)settingTrackToNone:(HBAudioTrack *)newNoneTrack +{ + // If this is not the last track in the array we need to remove it. We then need to see if a new + // one needs to be added (in the case when we were at maximum count and this switching makes it + // so we are no longer at maximum. + NSUInteger index = [self.tracks indexOfObject: newNoneTrack]; + + if (NSNotFound != index && index < [self countOfTracks] - 1) + { + [self removeObjectFromTracksAtIndex: index]; + } + [self switchingTrackFromNone: nil]; // see if we need to add one to the list +} + +- (void)switchingTrackFromNone:(HBAudioTrack *)noLongerNoneTrack +{ + NSUInteger count = [self countOfTracks]; + BOOL needToAdd = NO; + + // If there is no last track that is None we add one. + if (0 < count) + { + HBAudioTrack *lastAudio = [self objectInTracksAtIndex: count - 1]; + if ([lastAudio enabled]) + { + needToAdd = YES; + } + } + else + { + needToAdd = YES; + } + + if (needToAdd) + { + [self addNewAudioTrack]; + } +} + +// This gets called whenever the video container changes. +- (void)setContainer:(int)container +{ + _container = container; + + // Update each of the instances because this value influences possible settings. + for (HBAudioTrack *audioObject in self.tracks) + { + [audioObject setVideoContainerTag:@(container)]; + } + + // Update the Auto Passthru Fallback Codec Popup + // lets get the tag of the currently selected item first so we might reset it later + [self.defaults validateEncoderFallbackForVideoContainer:container]; +} + +#pragma mark - NSCopying + +- (instancetype)copyWithZone:(NSZone *)zone +{ + HBAudio *copy = [[[self class] alloc] init]; + + if (copy) + { + copy->_container = _container; + + copy->_noneTrack = [_noneTrack copy]; + copy->_masterTrackArray = [_masterTrackArray copy]; + + copy->_tracks = [[NSMutableArray alloc] init]; + [_tracks enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + if (idx < _tracks.count) + { + HBAudioTrack *trackCopy = [obj copy]; + trackCopy.controller = copy; + [copy->_tracks addObject:[trackCopy autorelease]]; + } + }]; + + copy->_defaults = [_defaults copy]; + } + + return copy; +} + +#pragma mark - NSCoding + +- (void)encodeWithCoder:(NSCoder *)coder +{ + [coder encodeInt:1 forKey:@"HBAudioVersion"]; + + encodeInt(_container); + + encodeObject(_noneTrack); + encodeObject(_masterTrackArray); + encodeObject(_tracks); + + encodeObject(_defaults); +} + +- (id)initWithCoder:(NSCoder *)decoder +{ + self = [super init]; + + decodeInt(_container); + + decodeObject(_noneTrack); + decodeObject(_masterTrackArray); + decodeObject(_tracks); + + for (HBAudioTrack *track in _tracks) + { + track.controller = self; + } + + decodeObject(_defaults); + + return self; +} + +#pragma mark - Presets + +- (void)writeToPreset:(NSMutableDictionary *)preset +{ + [self.defaults writeToPreset:preset]; +} + +- (void)applyPreset:(NSDictionary *)preset +{ + [self.defaults applyPreset:preset]; + [self addTracksFromDefaults:NO]; +} + +#pragma mark - +#pragma mark KVC + +- (NSUInteger) countOfTracks +{ + return self.tracks.count; +} + +- (HBAudioTrack *)objectInTracksAtIndex:(NSUInteger)index +{ + return self.tracks[index]; +} + +- (void)insertObject:(HBAudioTrack *)track inTracksAtIndex:(NSUInteger)index; +{ + [self.tracks insertObject:track atIndex:index]; +} + +- (void)removeObjectFromTracksAtIndex:(NSUInteger)index +{ + [self.tracks removeObjectAtIndex:index]; +} + +@end diff --git a/macosx/HBAudioController.h b/macosx/HBAudioController.h index 9088e5b92..2dcb94427 100644 --- a/macosx/HBAudioController.h +++ b/macosx/HBAudioController.h @@ -1,40 +1,18 @@ -// -// HBAudioController.h -// HandBrake -// -// Created on 2010-08-24. -// +/* HBAudioController.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 <Cocoa/Cocoa.h> -@class HBJob; -@class HBAudioTrack; +@class HBAudio; /** * HBAudioController - * - * Responds to HBContainerChangedNotification. */ @interface HBAudioController : NSViewController -@property (nonatomic, readonly, retain) NSArray *masterTrackArray; -@property (nonatomic, readonly) NSDictionary *noneTrack; - -@property (nonatomic, readwrite, assign) HBJob *job; - -- (void) applySettingsFromPreset:(NSDictionary *)preset; - -- (BOOL) anyCodecMatches: (int) aCodecValue; -- (void) settingTrackToNone: (HBAudioTrack *) newNoneTrack; -- (void) switchingTrackFromNone: (HBAudioTrack *) noLongerNoneTrack; - -@end - -@interface HBAudioController (KVC) - -- (NSUInteger) countOfAudioArray; -- (HBAudioTrack *) objectInAudioArrayAtIndex: (NSUInteger) index; -- (void) insertObject: (HBAudioTrack *) audioObject inAudioArrayAtIndex: (NSUInteger) index; -- (void) removeObjectFromAudioArrayAtIndex: (NSUInteger) index; +@property (nonatomic, readwrite, assign) HBAudio *audio; @end diff --git a/macosx/HBAudioController.m b/macosx/HBAudioController.m index 961e904a0..343bd7124 100644 --- a/macosx/HBAudioController.m +++ b/macosx/HBAudioController.m @@ -1,426 +1,48 @@ -// -// HBAudioController.m -// HandBrake -// -// Created on 2010-08-24. -// +/* HBAudioController.m -#import "HBAudioController.h" -#import "HBAudioTrack.h" -#import "HBAudioDefaultsController.h" - -#import "HBJob.h" - -#import "hb.h" -#include "lang.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. */ -@interface HBAudioController () { - IBOutlet NSTableView * fTableView; - - NSMutableArray * audioArray; // the configured audio information - NSArray * masterTrackArray; // the master list of audio tracks from the title - NSDictionary * noneTrack; // this represents no audio track selection -} +#import "HBAudioController.h" -@property (assign) IBOutlet NSPopUpButton *trackPopup; -@property (assign) IBOutlet NSButton *configureDefaults; -@property (assign) IBOutlet NSButton *reloadDefaults; +#import "HBAudio.h" +#import "HBAudioDefaultsController.h" -@property (nonatomic, readwrite, retain) NSArray *masterTrackArray; -@property (nonatomic, retain) NSNumber *videoContainerTag; // initially is the default HB_MUX_MP4 +@interface HBAudioController () -// Defaults @property (nonatomic, readwrite, retain) HBAudioDefaultsController *defaultsController; -@property (nonatomic, readwrite, retain) HBAudioDefaults *settings; @end @implementation HBAudioController -#pragma mark - -#pragma mark Accessors - -@synthesize masterTrackArray; -@synthesize noneTrack; -@synthesize videoContainerTag; - - (instancetype)init { self = [super initWithNibName:@"Audio" bundle:nil]; - if (self) - { - [self setVideoContainerTag: @HB_MUX_MP4]; - audioArray = [[NSMutableArray alloc] init]; - - /* register that we are interested in changes made to the video container */ - NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; - [center addObserver: self selector: @selector(containerChanged:) name: HBContainerChangedNotification object: nil]; - } return self; } -- (void) dealloc - +- (BOOL)validateUserInterfaceItem:(id < NSValidatedUserInterfaceItem >)anItem { - [[NSNotificationCenter defaultCenter] removeObserver: self]; - [masterTrackArray release]; - [noneTrack release]; - [audioArray release]; - [self setVideoContainerTag: nil]; - [super dealloc]; + return (self.audio != nil); } - (IBAction)addAllAudioTracks:(id)sender { - [self addTracksFromDefaults:YES]; + [self.audio addAllTracks]; } - (IBAction)removeAll:(id)sender { - [self _clearAudioArray]; - [self switchingTrackFromNone:nil]; -} - -- (BOOL)validateUserInterfaceItem:(id < NSValidatedUserInterfaceItem >)anItem -{ - return (self.job != nil); -} - -#pragma mark - -#pragma mark HBController Support - -- (void)applySettingsFromPreset:(NSDictionary *)preset -{ - [self.settings validateEncoderFallbackForVideoContainer:[self.videoContainerTag intValue]]; - [self addTracksFromDefaults:NO]; -} - -- (void) _clearAudioArray -{ - while (0 < [self countOfAudioArray]) - { - [self removeObjectFromAudioArrayAtIndex: 0]; - } -} - -/** - * Uses the templateAudioArray from the preset to create the audios for the specified trackIndex. - * - * @param templateAudioArray the track template. - * @param trackIndex the index of the source track. - * @param firstOnly use only the first track of the template or all. - */ -- (void) _processPresetAudioArray: (NSArray *) templateAudioArray forTrack: (NSUInteger) trackIndex firstOnly: (BOOL) firstOnly - -{ - for (HBAudioTrackPreset *preset in templateAudioArray) - { - BOOL fallenBack = NO; - HBAudioTrack *newAudio = [[HBAudioTrack alloc] init]; - [newAudio setController: self]; - [self insertObject: newAudio inAudioArrayAtIndex: [self countOfAudioArray]]; - [newAudio setVideoContainerTag: [self videoContainerTag]]; - [newAudio setTrackFromIndex: (int)trackIndex]; - - const char *name = hb_audio_encoder_get_name(preset.encoder); - NSString *audioEncoder = nil; - - // Check if we need to use a fallback - if (name) - { - audioEncoder = @(name); - if (preset.encoder & HB_ACODEC_PASS_FLAG && - ![newAudio setCodecFromName:audioEncoder]) - { - int passthru, fallback; - fallenBack = YES; - passthru = hb_audio_encoder_get_from_name([audioEncoder UTF8String]); - fallback = hb_audio_encoder_get_fallback_for_passthru(passthru); - name = hb_audio_encoder_get_name(fallback); - - // If we couldn't find an encoder for the passthru format - // fall back to the selected encoder fallback - if (name == NULL) - { - name = hb_audio_encoder_get_name(self.settings.encoderFallback); - } - } - else - { - name = hb_audio_encoder_sanitize_name([audioEncoder UTF8String]); - } - audioEncoder = @(name); - } - - // If our preset wants us to support a codec that the track does not support, instead - // of changing the codec we remove the audio instead. - if ([newAudio setCodecFromName:audioEncoder]) - { - const char *mixdown = hb_mixdown_get_name(preset.mixdown); - if (mixdown) - { - [newAudio setMixdownFromName: @(mixdown)]; - } - - const char *sampleRateName = hb_audio_samplerate_get_name(preset.sampleRate); - if (!sampleRateName) - { - [newAudio setSampleRateFromName: @"Auto"]; - } - else - { - [newAudio setSampleRateFromName: @(sampleRateName)]; - } - if (!fallenBack) - { - [newAudio setBitRateFromName: [NSString stringWithFormat:@"%d", preset.bitRate]]; - } - [newAudio setDrc: @(preset.drc)]; - [newAudio setGain: @(preset.gain)]; - } - else - { - [self removeObjectFromAudioArrayAtIndex: [self countOfAudioArray] - 1]; - } - [newAudio release]; - - if (firstOnly) - { - break; - } - } -} - -/** - * Matches the source audio tracks with the specific language iso code. - * - * @param isoCode the iso code to match. - * @param selectOnlyFirst whether to match only the first track for the iso code. - * - * @return a NSIndexSet with the index of the matched tracks. - */ -- (NSIndexSet *) _tracksWithISOCode: (NSString *) isoCode selectOnlyFirst: (BOOL) selectOnlyFirst -{ - NSMutableIndexSet *indexes = [NSMutableIndexSet indexSet]; - - // We search for the prefix noting that our titles have the format %d: %s where the %s is the prefix - [masterTrackArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { - if (idx) // Note that we skip the "None" track - { - if ([isoCode isEqualToString:@"und"] || [obj[keyAudioTrackLanguageIsoCode] isEqualToString:isoCode]) - { - [indexes addIndex:idx]; - - if (selectOnlyFirst) - { - *stop = YES; - } - } - } - }]; - - return indexes; -} - -- (void) _processPresetAudioArray: (NSArray *) templateAudioArray forTracks: (NSIndexSet *) trackIndexes firstOnly: (BOOL) firstOnly -{ - __block BOOL firsTrack = firstOnly; - [trackIndexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) { - // Add the track - [self _processPresetAudioArray: self.settings.tracksArray forTrack:idx firstOnly:firsTrack]; - firsTrack = self.settings.secondaryEncoderMode ? YES : NO; - }]; -} - -- (void) addTracksFromDefaults: (BOOL) allTracks - -{ - BOOL firstTrack = NO; - NSMutableIndexSet *tracksAdded = [NSMutableIndexSet indexSet]; - NSMutableIndexSet *tracksToAdd = [NSMutableIndexSet indexSet]; - - // Reinitialize the configured list of audio tracks - [self _clearAudioArray]; - - if (self.settings.trackSelectionBehavior != HBAudioTrackSelectionBehaviorNone) - { - // Add tracks of Default and Alternate Language by name - for (NSString *languageISOCode in self.settings.trackSelectionLanguages) - { - NSMutableIndexSet *tracksIndexes = [[self _tracksWithISOCode: languageISOCode - selectOnlyFirst: self.settings.trackSelectionBehavior == HBAudioTrackSelectionBehaviorFirst] mutableCopy]; - [tracksIndexes removeIndexes:tracksAdded]; - if (tracksIndexes.count) - { - [self _processPresetAudioArray:self.settings.tracksArray forTracks:tracksIndexes firstOnly:firstTrack]; - firstTrack = self.settings.secondaryEncoderMode ? YES : NO; - [tracksAdded addIndexes:tracksIndexes]; - } - [tracksIndexes release]; - } - - // If no preferred Language was found, add standard track 1 - if (tracksAdded.count == 0 && masterTrackArray.count > 1) - { - [tracksToAdd addIndex:1]; - } - } - - // If all tracks should be added, add all track numbers that are not yet processed - if (allTracks) - { - [tracksToAdd addIndexesInRange:NSMakeRange(1, masterTrackArray.count - 1)]; - [tracksToAdd removeIndexes:tracksAdded]; - } - - if (tracksToAdd.count) - { - [self _processPresetAudioArray:self.settings.tracksArray forTracks:tracksToAdd firstOnly:firstTrack]; - } - - // Add an None item - [self switchingTrackFromNone: nil]; -} - -- (BOOL) anyCodecMatches: (int) aCodecValue - -{ - BOOL retval = NO; - NSUInteger audioArrayCount = [self countOfAudioArray]; - for (NSUInteger i = 0; i < audioArrayCount && !retval; i++) - { - HBAudioTrack *anAudio = [self objectInAudioArrayAtIndex: i]; - if ([anAudio enabled] && aCodecValue == [[anAudio codec][keyAudioCodec] intValue]) - { - retval = YES; - } - } - return retval; -} - -- (void) addNewAudioTrack - -{ - HBAudioTrack *newAudio = [[HBAudioTrack alloc] init]; - [newAudio setController: self]; - [self insertObject: newAudio inAudioArrayAtIndex: [self countOfAudioArray]]; - [newAudio setVideoContainerTag: [self videoContainerTag]]; - [newAudio setTrack: noneTrack]; - [newAudio setDrc: @0.0f]; - [newAudio setGain: @0.0f]; - [newAudio release]; -} - -#pragma mark - -#pragma mark Notification Handling - -- (void) settingTrackToNone: (HBAudioTrack *) newNoneTrack - -{ - // If this is not the last track in the array we need to remove it. We then need to see if a new - // one needs to be added (in the case when we were at maximum count and this switching makes it - // so we are no longer at maximum. - NSUInteger index = [audioArray indexOfObject: newNoneTrack]; - - if (NSNotFound != index && index < [self countOfAudioArray] - 1) - { - [self removeObjectFromAudioArrayAtIndex: index]; - } - [self switchingTrackFromNone: nil]; // see if we need to add one to the list -} - -- (void) switchingTrackFromNone: (HBAudioTrack *) noLongerNoneTrack - -{ - NSUInteger count = [self countOfAudioArray]; - BOOL needToAdd = NO; - - // If there is no last track that is None we add one. - if (0 < count) - { - HBAudioTrack *lastAudio = [self objectInAudioArrayAtIndex: count - 1]; - if ([lastAudio enabled]) - { - needToAdd = YES; - } - } - else - { - needToAdd = YES; - } - - if (needToAdd) - { - [self addNewAudioTrack]; - } -} - -// This gets called whenever the video container changes. -- (void) containerChanged: (NSNotification *) aNotification - -{ - NSDictionary *notDict = [aNotification userInfo]; - - [self setVideoContainerTag: notDict[keyContainerTag]]; - - // Update each of the instances because this value influences possible settings. - for (HBAudioTrack *audioObject in audioArray) - { - [audioObject setVideoContainerTag: [self videoContainerTag]]; - } - - // Update the Auto Passthru Fallback Codec Popup - // lets get the tag of the currently selected item first so we might reset it later - [self.settings validateEncoderFallbackForVideoContainer:[self.videoContainerTag intValue]]; -} - -- (void)setJob:(HBJob *)job - -{ - // Reinitialize the configured list of audio tracks - [self _clearAudioArray]; - - if (job) - { - _job = job; - [audioArray release]; - audioArray = [job.audioTracks retain]; - self.settings = job.audioDefaults; - - // Reinitialize the master list of available audio tracks from this title - NSMutableArray *newTrackArray = [NSMutableArray array]; - [noneTrack release]; - noneTrack = [@{keyAudioTrackIndex: @0, - keyAudioTrackName: NSLocalizedString(@"None", @"None"), - keyAudioInputCodec: @0} retain]; - [newTrackArray addObject: noneTrack]; - [newTrackArray addObjectsFromArray:job.title.audioTracks]; - self.masterTrackArray = newTrackArray; - - // Readd the controller reference to the audio tracks. - for (HBAudioTrack *audioTrack in audioArray) - { - audioTrack.controller = self; - } - - [self switchingTrackFromNone: nil]; // this ensures there is a None track at the end of the list - } - else - { - _job = nil; - [audioArray release]; - audioArray = nil; - self.settings = nil; - self.masterTrackArray = nil; - } - + [self.audio removeAll]; } #pragma mark - Defaults - (IBAction)showSettingsSheet:(id)sender { - self.defaultsController = [[[HBAudioDefaultsController alloc] initWithSettings:self.settings] autorelease]; + self.defaultsController = [[[HBAudioDefaultsController alloc] initWithSettings:self.audio.defaults] autorelease]; [NSApp beginSheet:[self.defaultsController window] modalForWindow:[[self view] window] @@ -436,35 +58,7 @@ - (IBAction)reloadDefaults:(id)sender { - [self addTracksFromDefaults:NO]; -} - -#pragma mark - -#pragma mark KVC - -- (NSUInteger) countOfAudioArray - -{ - return [audioArray count]; -} - -- (HBAudioTrack *) objectInAudioArrayAtIndex: (NSUInteger) index - -{ - return audioArray[index]; -} - -- (void) insertObject: (HBAudioTrack *) audioObject inAudioArrayAtIndex: (NSUInteger) index; - -{ - [audioArray insertObject: audioObject atIndex: index]; -} - -- (void) removeObjectFromAudioArrayAtIndex: (NSUInteger) index - -{ - [audioArray removeObjectAtIndex: index]; + [self.audio reloadDefaults]; } @end - diff --git a/macosx/HBAudioTrack.h b/macosx/HBAudioTrack.h index ed9749fa7..f7dcc88fe 100644 --- a/macosx/HBAudioTrack.h +++ b/macosx/HBAudioTrack.h @@ -6,7 +6,7 @@ #import <Foundation/Foundation.h> -@class HBAudioController; +@class HBAudio; /** * Audio track dicts keys. @@ -39,7 +39,7 @@ extern NSString *keyAudioBitrate; @property (nonatomic, retain) NSNumber *drc; @property (nonatomic, retain) NSNumber *gain; @property (nonatomic, retain) NSNumber *videoContainerTag; -@property (nonatomic, assign) HBAudioController *controller; +@property (nonatomic, assign) HBAudio *controller; @property (nonatomic, retain) NSMutableArray *codecs; @property (nonatomic, retain) NSMutableArray *mixdowns; diff --git a/macosx/HBCore.m b/macosx/HBCore.m index a1b9c995c..111f5dc1e 100644 --- a/macosx/HBCore.m +++ b/macosx/HBCore.m @@ -6,6 +6,7 @@ #import "HBCore.h" #import "HBJob.h" +#import "HBJob+HBJobConversion.h" #import "HBDVDDetector.h" #import "HBUtilities.h" diff --git a/macosx/HBJob+HBJobConversion.h b/macosx/HBJob+HBJobConversion.h new file mode 100644 index 000000000..b46aabc18 --- /dev/null +++ b/macosx/HBJob+HBJobConversion.h @@ -0,0 +1,18 @@ +/* HBJob+HBJobConversion.m $ + + 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 <Foundation/Foundation.h> +#import "HBJob.h" +#include "hb.h" + +@interface HBJob (HBJobConversion) + +/** + * Converts the job to a hb_job_t struct. + */ +@property (nonatomic, readonly) hb_job_t *hb_job; + +@end diff --git a/macosx/HBJob+HBJobConversion.m b/macosx/HBJob+HBJobConversion.m new file mode 100644 index 000000000..e886cb0ef --- /dev/null +++ b/macosx/HBJob+HBJobConversion.m @@ -0,0 +1,522 @@ +/* HBJob+HBJobConversion.m $ + + 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 "HBJob+HBJobConversion.h" + +#import "HBAudioDefaults.h" +#import "HBAudioTrack.h" + +@implementation HBJob (HBJobConversion) + +/** + * Prepares a hb_job_t + */ +- (hb_job_t *)hb_job +{ + NSAssert(self.title, @"HBJob: calling hb_job without a valid title loaded"); + + hb_title_t *title = self.title.hb_title; + hb_job_t *job = hb_job_init(title); + + hb_job_set_file(job, self.destURL.path.fileSystemRepresentation); + + // Title Angle for dvdnav + job->angle = self.angle; + + if (self.range.type == HBRangeTypeChapters) + { + // Chapter selection + job->chapter_start = self.range.chapterStart + 1; + job->chapter_end = self.range.chapterStop + 1; + } + else if (self.range.type == HBRangeTypeSeconds) + { + // we are pts based start / stop + // Point A to Point B. Time to time in seconds. + // get the start seconds from the start seconds field + int start_seconds = self.range.secondsStart; + job->pts_to_start = start_seconds * 90000LL; + // Stop seconds is actually the duration of encode, so subtract the end seconds from the start seconds + int stop_seconds = self.range.secondsStop; + job->pts_to_stop = stop_seconds * 90000LL; + } + else if (self.range.type == HBRangeTypeFrames) + { + // we are frame based start / stop + //Point A to Point B. Frame to frame + // get the start frame from the start frame field + int start_frame = self.range.frameStart; + job->frame_to_start = start_frame; + // get the frame to stop on from the end frame field + int stop_frame = self.range.frameStop; + job->frame_to_stop = stop_frame; + } + + // Format (Muxer) and Video Encoder + job->mux = self.container; + job->vcodec = self.video.encoder; + + // We set http optimized mp4 here + job->mp4_optimize = self.mp4HttpOptimize; + + // We set the chapter marker extraction here based on the format being + // mpeg4 or mkv and the checkbox being checked. + if (self.chaptersEnabled) + { + job->chapter_markers = 1; + + // now lets get our saved chapter names out the array in the queue file + // and insert them back into the title chapter list. We have it here, + // because unless we are inserting chapter markers there is no need to + // spend the overhead of iterating through the chapter names array imo + // Also, note that if for some reason we don't apply chapter names, the + // chapters just come out 001, 002, etc. etc. + int i = 0; + for (NSString *name in self.chapterTitles) + { + hb_chapter_t *chapter = (hb_chapter_t *) hb_list_item(job->list_chapter, i); + if (chapter != NULL) + { + hb_chapter_set_title(chapter, name.UTF8String); + } + i++; + } + } + else + { + job->chapter_markers = 0; + } + + if (job->vcodec & HB_VCODEC_H264_MASK) + { + // iPod 5G atom + job->ipod_atom = self.mp4iPodCompatible; + } + + job->twopass = self.video.twoPass; + if (job->vcodec == HB_VCODEC_X264 || job->vcodec == HB_VCODEC_X265) + { + // set fastfirstpass if 2-pass and Turbo are enabled + if (self.video.twoPass) + { + job->fastfirstpass = self.video.turboTwoPass; + } + + // advanced x264/x265 options + NSString *tmpString; + // translate zero-length strings to NULL for libhb + const char *encoder_preset = NULL; + const char *encoder_tune = NULL; + const char *encoder_options = NULL; + const char *encoder_profile = NULL; + const char *encoder_level = NULL; + if (self.video.advancedOptions) + { + // we are using the advanced panel + if ([(tmpString = self.video.videoOptionExtra) length]) + { + encoder_options = tmpString.UTF8String; + } + } + else + { + // we are using the x264/x265 preset system + if ([(tmpString = self.video.completeTune) length]) + { + encoder_tune = [tmpString UTF8String]; + } + if ([(tmpString = self.video.videoOptionExtra) length]) + { + encoder_options = [tmpString UTF8String]; + } + if ([(tmpString = self.video.profile) length]) + { + encoder_profile = [tmpString UTF8String]; + } + if ([(tmpString = self.video.level) length]) + { + encoder_level = [tmpString UTF8String]; + } + encoder_preset = self.video.preset.UTF8String; + } + hb_job_set_encoder_preset (job, encoder_preset); + hb_job_set_encoder_tune (job, encoder_tune); + hb_job_set_encoder_options(job, encoder_options); + hb_job_set_encoder_profile(job, encoder_profile); + hb_job_set_encoder_level (job, encoder_level); + } + else if (job->vcodec & HB_VCODEC_FFMPEG_MASK) + { + hb_job_set_encoder_options(job, self.video.videoOptionExtra.UTF8String); + } + + // Picture Size Settings + job->par.num = self.picture.parWidth; + job->par.den = self.picture.parHeight; + + // Video settings + // Framerate + int fps_mode, fps_num, fps_den; + if (self.video.frameRate > 0) + { + // a specific framerate has been chosen + fps_num = 27000000; + fps_den = self.video.frameRate; + if (self.video.frameRateMode == 1) + { + // CFR + fps_mode = 1; + } + else + { + // PFR + fps_mode = 2; + } + } + else + { + // same as source + fps_num = title->vrate.num; + fps_den = title->vrate.den; + if (self.video.frameRateMode == 1) + { + // CFR + fps_mode = 1; + } + else + { + // VFR + fps_mode = 0; + } + } + + switch (self.video.qualityType) + { + case 0: + // ABR + job->vquality = -1.0; + job->vbitrate = self.video.avgBitrate; + break; + case 1: + // Constant Quality + job->vquality = self.video.quality; + job->vbitrate = 0; + break; + } + + job->grayscale = self.filters.grayscale; + + // Map the settings in the dictionaries for the SubtitleList array to match title->list_subtitle + BOOL one_burned = NO; + int i = 0; + + for (NSDictionary *subtitleDict in self.subtitles.tracks) + { + // Skip the "None" track. + if (i == self.subtitles.tracks.count - 1) + { + continue; + } + + int subtitle = [subtitleDict[keySubTrackIndex] intValue]; + int force = [subtitleDict[keySubTrackForced] intValue]; + int burned = [subtitleDict[keySubTrackBurned] intValue]; + int def = [subtitleDict[keySubTrackDefault] intValue]; + + // if i is 0, then we are in the first item of the subtitles which we need to + // check for the "Foreign Audio Search" which would be keySubTrackIndex of -1 + + // if we are on the first track and using "Foreign Audio Search" + if (i == 0 && subtitle == -1) + { + job->indepth_scan = 1; + + if (burned != 1) + { + job->select_subtitle_config.dest = PASSTHRUSUB; + } + else + { + job->select_subtitle_config.dest = RENDERSUB; + } + + job->select_subtitle_config.force = force; + job->select_subtitle_config.default_track = def; + } + else + { + // if we are getting the subtitles from an external srt file + if ([subtitleDict[keySubTrackType] intValue] == SRTSUB) + { + hb_subtitle_config_t sub_config; + + sub_config.offset = [subtitleDict[keySubTrackSrtOffset] intValue]; + + // we need to srncpy file name and codeset + strncpy(sub_config.src_filename, [subtitleDict[keySubTrackSrtFilePath] UTF8String], 255); + sub_config.src_filename[255] = 0; + strncpy(sub_config.src_codeset, [subtitleDict[keySubTrackSrtCharCode] UTF8String], 39); + sub_config.src_codeset[39] = 0; + + if (!burned && hb_subtitle_can_pass(SRTSUB, job->mux)) + { + sub_config.dest = PASSTHRUSUB; + } + else if (hb_subtitle_can_burn(SRTSUB)) + { + // Only allow one subtitle to be burned into the video + if (one_burned) + continue; + one_burned = TRUE; + sub_config.dest = RENDERSUB; + } + + sub_config.force = 0; + sub_config.default_track = def; + hb_srt_add( job, &sub_config, [subtitleDict[keySubTrackLanguageIsoCode] UTF8String]); + continue; + } + + // We are setting a source subtitle so access the source subtitle info + hb_subtitle_t * subt = (hb_subtitle_t *) hb_list_item(title->list_subtitle, subtitle); + + if (subt != NULL) + { + hb_subtitle_config_t sub_config = subt->config; + + if (!burned && hb_subtitle_can_pass(subt->source, job->mux)) + { + sub_config.dest = PASSTHRUSUB; + } + else if (hb_subtitle_can_burn(subt->source)) + { + // Only allow one subtitle to be burned into the video + if (one_burned) + continue; + one_burned = TRUE; + sub_config.dest = RENDERSUB; + } + + sub_config.force = force; + sub_config.default_track = def; + hb_subtitle_add(job, &sub_config, subtitle); + } + } + i++; + } + + if (one_burned) + { + hb_filter_object_t *filter = hb_filter_init( HB_FILTER_RENDER_SUB ); + hb_add_filter( job, filter, [[NSString stringWithFormat:@"%d:%d:%d:%d", + job->crop[0], job->crop[1], + job->crop[2], job->crop[3]] UTF8String] ); + } + + // Audio Defaults + job->acodec_copy_mask = 0; + + HBAudioDefaults *audioDefaults = self.audio.defaults; + + if (audioDefaults.allowAACPassthru) + { + job->acodec_copy_mask |= HB_ACODEC_FFAAC; + } + if (audioDefaults.allowAC3Passthru) + { + job->acodec_copy_mask |= HB_ACODEC_AC3; + } + if (audioDefaults.allowDTSHDPassthru) + { + job->acodec_copy_mask |= HB_ACODEC_DCA_HD; + } + if (audioDefaults.allowDTSPassthru) + { + job->acodec_copy_mask |= HB_ACODEC_DCA; + } + if (audioDefaults.allowMP3Passthru) + { + job->acodec_copy_mask |= HB_ACODEC_MP3; + } + job->acodec_fallback = audioDefaults.encoderFallback; + + // Audio tracks and mixdowns + // Now lets add our new tracks to the audio list here + for (HBAudioTrack *audioTrack in self.audio.tracks) + { + if (audioTrack.enabled) + { + hb_audio_config_t *audio = (hb_audio_config_t *)calloc(1, sizeof(*audio)); + hb_audio_config_init(audio); + + NSNumber *sampleRateToUse = ([audioTrack.sampleRate[keyAudioSamplerate] intValue] == 0 ? + audioTrack.track[keyAudioInputSampleRate] : + audioTrack.sampleRate[keyAudioSamplerate]); + + audio->in.track = [audioTrack.track[keyAudioTrackIndex] intValue] -1; + + // We go ahead and assign values to our audio->out.<properties> + audio->out.track = audio->in.track; + audio->out.codec = [audioTrack.codec[keyAudioCodec] intValue]; + audio->out.compression_level = hb_audio_compression_get_default(audio->out.codec); + audio->out.mixdown = [audioTrack.mixdown[keyAudioMixdown] intValue]; + audio->out.normalize_mix_level = 0; + audio->out.bitrate = [audioTrack.bitRate[keyAudioBitrate] intValue]; + audio->out.samplerate = [sampleRateToUse intValue]; + audio->out.dither_method = hb_audio_dither_get_default(); + + // output is not passthru so apply gain + if (!([[audioTrack codec][keyAudioCodec] intValue] & HB_ACODEC_PASS_FLAG)) + { + audio->out.gain = [audioTrack.gain doubleValue]; + } + else + { + // output is passthru - the Gain dial is disabled so don't apply its value + audio->out.gain = 0; + } + + if (hb_audio_can_apply_drc([audioTrack.track[keyAudioInputCodec] intValue], + [audioTrack.track[keyAudioInputCodecParam] intValue], + [audioTrack.codec[keyAudioCodec] intValue])) + { + audio->out.dynamic_range_compression = [audioTrack.drc doubleValue]; + } + else + { + // source isn't AC3 or output is passthru - the DRC dial is disabled so don't apply its value + audio->out.dynamic_range_compression = 0; + } + + hb_audio_add(job, audio); + free(audio); + } + } + + // Now lets call the filters if applicable. + // The order of the filters is critical + + // Detelecine + hb_filter_object_t *filter; + if (self.filters.detelecine == 1) + { + filter = hb_filter_init(HB_FILTER_DETELECINE); + // use a custom detelecine string + hb_add_filter(job, filter, self.filters.detelecineCustomString.UTF8String); + } + else if (self.filters.detelecine == 2) + { + filter = hb_filter_init(HB_FILTER_DETELECINE); + // Use libhb's default values + hb_add_filter(job, filter, NULL); + } + + if (self.filters.useDecomb && self.filters.decomb) + { + // Decomb + filter = hb_filter_init(HB_FILTER_DECOMB); + if (self.filters.decomb == 1) + { + // use a custom decomb string */ + hb_add_filter(job, filter, self.filters.decombCustomString.UTF8String); + } + else if (self.filters.decomb == 2) + { + // use libhb defaults + hb_add_filter(job, filter, NULL); + } + else if (self.filters.decomb == 3) + { + // use old defaults (decomb fast) + hb_add_filter(job, filter, "7:2:6:9:1:80"); + } + else if (self.filters.decomb == 4) + { + // decomb 3 with bobbing enabled + hb_add_filter(job, filter, "455"); + } + } + else if (!self.filters.useDecomb && self.filters.deinterlace) + { + // Deinterlace + filter = hb_filter_init(HB_FILTER_DEINTERLACE); + if (self.filters.deinterlace == 1) + { + // we add the custom string if present + hb_add_filter(job, filter, self.filters.deinterlaceCustomString.UTF8String); + } + else if (self.filters.deinterlace == 2) + { + // Run old deinterlacer fd by default + hb_add_filter(job, filter, "0"); + } + else if (self.filters.deinterlace == 3) + { + // Yadif mode 0 (without spatial deinterlacing) + hb_add_filter(job, filter, "1"); + } + else if (self.filters.deinterlace == 4) + { + // Yadif (with spatial deinterlacing) + hb_add_filter(job, filter, "3"); + } + else if (self.filters.deinterlace == 5) + { + // Yadif (with spatial deinterlacing and bobbing) + hb_add_filter(job, filter, "15"); + } + } + // Denoise + if (![self.filters.denoise isEqualToString:@"off"]) + { + int filter_id = HB_FILTER_HQDN3D; + if ([self.filters.denoise isEqualToString:@"nlmeans"]) + filter_id = HB_FILTER_NLMEANS; + + if ([self.filters.denoisePreset isEqualToString:@"none"]) + { + const char *filter_str; + filter_str = self.filters.denoiseCustomString.UTF8String; + filter = hb_filter_init(filter_id); + hb_add_filter(job, filter, filter_str); + } + else + { + const char *filter_str, *preset, *tune; + preset = self.filters.denoisePreset.UTF8String; + tune = self.filters.denoiseTune.UTF8String; + filter_str = hb_generate_filter_settings(filter_id, preset, tune); + filter = hb_filter_init(filter_id); + hb_add_filter(job, filter, filter_str); + } + } + + // Deblock (uses pp7 default) + // NOTE: even though there is a valid deblock setting of 0 for the filter, for + // the macgui's purposes a value of 0 actually means to not even use the filter + // current hb_filter_deblock.settings valid ranges are from 5 - 15 + if (self.filters.deblock != 0) + { + filter = hb_filter_init(HB_FILTER_DEBLOCK); + hb_add_filter(job, filter, [NSString stringWithFormat:@"%ld", (long)self.filters.deblock].UTF8String); + } + + // Add Crop/Scale filter + filter = hb_filter_init(HB_FILTER_CROP_SCALE); + hb_add_filter( job, filter, [NSString stringWithFormat:@"%d:%d:%d:%d:%d:%d", + self.picture.width, self.picture.height, + self.picture.cropTop, self.picture.cropBottom, + self.picture.cropLeft, self.picture.cropRight].UTF8String); + + // Add framerate shaping filter + filter = hb_filter_init(HB_FILTER_VFR); + hb_add_filter(job, filter, [[NSString stringWithFormat:@"%d:%d:%d", + fps_mode, fps_num, fps_den] UTF8String]); + + return job; +} + +@end diff --git a/macosx/HBJob.h b/macosx/HBJob.h index 721db9dfe..d7e4f14c7 100644 --- a/macosx/HBJob.h +++ b/macosx/HBJob.h @@ -14,19 +14,16 @@ #import "HBPicture.h" #import "HBFilters.h" -#import "HBAudioTrack.h" -#import "HBAudioTrackPreset.h" - -#import "HBAudioDefaults.h" -#import "HBSubtitlesDefaults.h" - -#include "hb.h" +#import "HBAudio.h" +#import "HBSubtitles.h" extern NSString *HBMixdownChangedNotification; extern NSString *HBContainerChangedNotification; -extern NSString *keyContainerTag; -typedef NS_ENUM(NSUInteger, HBJobState) { +/** + * A flag to indicate the job's state + */ +typedef NS_ENUM(NSUInteger, HBJobState){ HBJobStateReady, HBJobStateWorking, HBJobStateCompleted, @@ -47,18 +44,22 @@ typedef NS_ENUM(NSUInteger, HBJobState) { * Current state of the job. */ @property (nonatomic, readwrite) HBJobState state; + @property (nonatomic, readwrite, copy) NSString *presetName; @property (nonatomic, readwrite, assign) HBTitle *title; @property (nonatomic, readonly) int titleIdx; @property (nonatomic, readwrite) int pidId; -// Urls +/** + * The file URL of the source. + */ @property (nonatomic, readonly) NSURL *fileURL; -@property (nonatomic, readwrite, copy) NSURL *destURL; -// Libhb job -@property (nonatomic, readonly) hb_job_t *hb_job; +/** + * The file URL at which the new file will be created. + */ +@property (nonatomic, readwrite, copy) NSURL *destURL; // Job settings @property (nonatomic, readwrite) int container; @@ -72,14 +73,10 @@ typedef NS_ENUM(NSUInteger, HBJobState) { @property (nonatomic, readonly) HBPicture *picture; @property (nonatomic, readonly) HBFilters *filters; -@property (nonatomic, readonly) NSMutableArray *audioTracks; -@property (nonatomic, readonly) NSMutableArray *subtitlesTracks; +@property (nonatomic, readonly) HBAudio *audio; +@property (nonatomic, readonly) HBSubtitles *subtitles; @property (nonatomic, readwrite) BOOL chaptersEnabled; @property (nonatomic, readonly) NSMutableArray *chapterTitles; -// Defaults settings -@property (nonatomic, readonly) HBAudioDefaults *audioDefaults; -@property (nonatomic, readonly) HBSubtitlesDefaults *subtitlesDefaults; - @end diff --git a/macosx/HBJob.m b/macosx/HBJob.m index 6bb19b2c0..0222a0b33 100644 --- a/macosx/HBJob.m +++ b/macosx/HBJob.m @@ -7,15 +7,15 @@ #import "HBJob.h" #import "HBPreset.h" -#import "HBAudioTrack.h" -#import "HBAudioController.h" -#import "HBSubtitlesController.h" +#import "HBAudioDefaults.h" +#import "HBSubtitlesDefaults.h" #import "NSCodingMacro.h" +#include "hb.h" + NSString *HBMixdownChangedNotification = @"HBMixdownChangedNotification"; NSString *HBContainerChangedNotification = @"HBContainerChangedNotification"; -NSString *keyContainerTag = @"keyContainerTag"; @implementation HBJob @@ -34,16 +34,13 @@ NSString *keyContainerTag = @"keyContainerTag"; _container = HB_MUX_MP4; _angle = 1; - _audioDefaults = [[HBAudioDefaults alloc] init]; - _subtitlesDefaults = [[HBSubtitlesDefaults alloc] init]; - _range = [[HBRange alloc] initWithTitle:title]; _video = [[HBVideo alloc] initWithJob:self]; _picture = [[HBPicture alloc] initWithTitle:title]; _filters = [[HBFilters alloc] init]; - _audioTracks = [[NSMutableArray alloc] init]; - _subtitlesTracks = [[NSMutableArray alloc] init]; + _audio = [[HBAudio alloc] initWithTitle:title]; + _subtitles = [[HBSubtitles alloc] initWithTitle:title]; _chapterTitles = [title.chapters mutableCopy]; @@ -75,7 +72,7 @@ NSString *keyContainerTag = @"keyContainerTag"; // Chapter Markers self.chaptersEnabled = [content[@"ChapterMarkers"] boolValue]; - [@[self.audioDefaults, self.subtitlesDefaults, self.filters, self.picture, self.video, ] makeObjectsPerformSelector:@selector(applyPreset:) + [@[self.audio, self.subtitles, self.filters, self.picture, self.video] makeObjectsPerformSelector:@selector(applyPreset:) withObject:content]; } @@ -87,20 +84,22 @@ NSString *keyContainerTag = @"keyContainerTag"; dict[@"Mp4HttpOptimize"] = @(self.mp4HttpOptimize); dict[@"Mp4iPodCompatible"] = @(self.mp4iPodCompatible); - [@[self.video, self.filters, self.picture, self.audioDefaults, self.subtitlesDefaults] makeObjectsPerformSelector:@selector(writeToPreset:) + [@[self.video, self.filters, self.picture, self.audio, self.subtitles] makeObjectsPerformSelector:@selector(writeToPreset:) withObject:dict]; } - (void)setContainer:(int)container { _container = container; + + self.audio.container = container; [self.video containerChanged]; /* post a notification for any interested observers to indicate that our video container has changed */ [[NSNotificationCenter defaultCenter] postNotification: [NSNotification notificationWithName:HBContainerChangedNotification object:self - userInfo:@{keyContainerTag: @(self.container)}]]; + userInfo:nil]]; } - (void)setTitle:(HBTitle *)title @@ -129,8 +128,8 @@ NSString *keyContainerTag = @"keyContainerTag"; - (void)dealloc { - [_audioTracks release]; - [_subtitlesTracks release]; + [_audio release]; + [_subtitles release]; [_fileURL release]; [_destURL release]; @@ -140,514 +139,11 @@ NSString *keyContainerTag = @"keyContainerTag"; [_picture release]; [_filters release]; - [_audioDefaults release]; - [_subtitlesDefaults release]; - [_chapterTitles release]; [super dealloc]; } -/** - * Prepares a hb_job_t - */ -- (hb_job_t *)hb_job -{ - NSAssert(self.title, @"HBJob: calling hb_job without a valid title loaded"); - - hb_title_t *title = self.title.hb_title; - hb_job_t *job = hb_job_init(title); - - hb_job_set_file(job, self.destURL.path.fileSystemRepresentation); - - // Title Angle for dvdnav - job->angle = self.angle; - - if (self.range.type == HBRangeTypeChapters) - { - // Chapter selection - job->chapter_start = self.range.chapterStart + 1; - job->chapter_end = self.range.chapterStop + 1; - } - else if (self.range.type == HBRangeTypeSeconds) - { - // we are pts based start / stop - // Point A to Point B. Time to time in seconds. - // get the start seconds from the start seconds field - int start_seconds = self.range.secondsStart; - job->pts_to_start = start_seconds * 90000LL; - // Stop seconds is actually the duration of encode, so subtract the end seconds from the start seconds - int stop_seconds = self.range.secondsStop; - job->pts_to_stop = stop_seconds * 90000LL; - } - else if (self.range.type == HBRangeTypeFrames) - { - // we are frame based start / stop - //Point A to Point B. Frame to frame - // get the start frame from the start frame field - int start_frame = self.range.frameStart; - job->frame_to_start = start_frame; - // get the frame to stop on from the end frame field - int stop_frame = self.range.frameStop; - job->frame_to_stop = stop_frame; - } - - // Format (Muxer) and Video Encoder - job->mux = self.container; - job->vcodec = self.video.encoder; - - // We set http optimized mp4 here - job->mp4_optimize = self.mp4HttpOptimize; - - // We set the chapter marker extraction here based on the format being - // mpeg4 or mkv and the checkbox being checked. - if (self.chaptersEnabled) - { - job->chapter_markers = 1; - - // now lets get our saved chapter names out the array in the queue file - // and insert them back into the title chapter list. We have it here, - // because unless we are inserting chapter markers there is no need to - // spend the overhead of iterating through the chapter names array imo - // Also, note that if for some reason we don't apply chapter names, the - // chapters just come out 001, 002, etc. etc. - int i = 0; - for (NSString *name in self.chapterTitles) - { - hb_chapter_t *chapter = (hb_chapter_t *) hb_list_item(job->list_chapter, i); - if (chapter != NULL) - { - hb_chapter_set_title(chapter, name.UTF8String); - } - i++; - } - } - else - { - job->chapter_markers = 0; - } - - if (job->vcodec & HB_VCODEC_H264_MASK) - { - // iPod 5G atom - job->ipod_atom = self.mp4iPodCompatible; - } - - job->twopass = self.video.twoPass; - if (job->vcodec == HB_VCODEC_X264 || job->vcodec == HB_VCODEC_X265) - { - // set fastfirstpass if 2-pass and Turbo are enabled - if (self.video.twoPass) - { - job->fastfirstpass = self.video.turboTwoPass; - } - - // advanced x264/x265 options - NSString *tmpString; - // translate zero-length strings to NULL for libhb - const char *encoder_preset = NULL; - const char *encoder_tune = NULL; - const char *encoder_options = NULL; - const char *encoder_profile = NULL; - const char *encoder_level = NULL; - if (self.video.advancedOptions) - { - // we are using the advanced panel - if ([(tmpString = self.video.videoOptionExtra) length]) - { - encoder_options = tmpString.UTF8String; - } - } - else - { - // we are using the x264/x265 preset system - if ([(tmpString = self.video.completeTune) length]) - { - encoder_tune = [tmpString UTF8String]; - } - if ([(tmpString = self.video.videoOptionExtra) length]) - { - encoder_options = [tmpString UTF8String]; - } - if ([(tmpString = self.video.profile) length]) - { - encoder_profile = [tmpString UTF8String]; - } - if ([(tmpString = self.video.level) length]) - { - encoder_level = [tmpString UTF8String]; - } - encoder_preset = self.video.preset.UTF8String; - } - hb_job_set_encoder_preset (job, encoder_preset); - hb_job_set_encoder_tune (job, encoder_tune); - hb_job_set_encoder_options(job, encoder_options); - hb_job_set_encoder_profile(job, encoder_profile); - hb_job_set_encoder_level (job, encoder_level); - } - else if (job->vcodec & HB_VCODEC_FFMPEG_MASK) - { - hb_job_set_encoder_options(job, self.video.videoOptionExtra.UTF8String); - } - - // Picture Size Settings - job->par.num = self.picture.parWidth; - job->par.den = self.picture.parHeight; - - // Video settings - // Framerate - int fps_mode, fps_num, fps_den; - if (self.video.frameRate > 0) - { - // a specific framerate has been chosen - fps_num = 27000000; - fps_den = self.video.frameRate; - if (self.video.frameRateMode == 1) - { - // CFR - fps_mode = 1; - } - else - { - // PFR - fps_mode = 2; - } - } - else - { - // same as source - fps_num = title->vrate.num; - fps_den = title->vrate.den; - if (self.video.frameRateMode == 1) - { - // CFR - fps_mode = 1; - } - else - { - // VFR - fps_mode = 0; - } - } - - switch (self.video.qualityType) - { - case 0: - // ABR - job->vquality = -1.0; - job->vbitrate = self.video.avgBitrate; - break; - case 1: - // Constant Quality - job->vquality = self.video.quality; - job->vbitrate = 0; - break; - } - - job->grayscale = self.filters.grayscale; - - // Map the settings in the dictionaries for the SubtitleList array to match title->list_subtitle - BOOL one_burned = NO; - int i = 0; - - for (NSDictionary *subtitleDict in self.subtitlesTracks) - { - int subtitle = [subtitleDict[keySubTrackIndex] intValue]; - int force = [subtitleDict[keySubTrackForced] intValue]; - int burned = [subtitleDict[keySubTrackBurned] intValue]; - int def = [subtitleDict[keySubTrackDefault] intValue]; - - // if i is 0, then we are in the first item of the subtitles which we need to - // check for the "Foreign Audio Search" which would be keySubTrackIndex of -1 - - // if we are on the first track and using "Foreign Audio Search" - if (i == 0 && subtitle == -1) - { - job->indepth_scan = 1; - - if (burned != 1) - { - job->select_subtitle_config.dest = PASSTHRUSUB; - } - else - { - job->select_subtitle_config.dest = RENDERSUB; - } - - job->select_subtitle_config.force = force; - job->select_subtitle_config.default_track = def; - } - else - { - // if we are getting the subtitles from an external srt file - if ([subtitleDict[keySubTrackType] intValue] == SRTSUB) - { - hb_subtitle_config_t sub_config; - - sub_config.offset = [subtitleDict[keySubTrackSrtOffset] intValue]; - - // we need to srncpy file name and codeset - strncpy(sub_config.src_filename, [subtitleDict[keySubTrackSrtFilePath] UTF8String], 255); - sub_config.src_filename[255] = 0; - strncpy(sub_config.src_codeset, [subtitleDict[keySubTrackSrtCharCode] UTF8String], 39); - sub_config.src_codeset[39] = 0; - - if (!burned && hb_subtitle_can_pass(SRTSUB, job->mux)) - { - sub_config.dest = PASSTHRUSUB; - } - else if (hb_subtitle_can_burn(SRTSUB)) - { - // Only allow one subtitle to be burned into the video - if (one_burned) - continue; - one_burned = TRUE; - sub_config.dest = RENDERSUB; - } - - sub_config.force = 0; - sub_config.default_track = def; - hb_srt_add( job, &sub_config, [subtitleDict[keySubTrackLanguageIsoCode] UTF8String]); - continue; - } - - // We are setting a source subtitle so access the source subtitle info - hb_subtitle_t * subt = (hb_subtitle_t *) hb_list_item(title->list_subtitle, subtitle); - - if (subt != NULL) - { - hb_subtitle_config_t sub_config = subt->config; - - if (!burned && hb_subtitle_can_pass(subt->source, job->mux)) - { - sub_config.dest = PASSTHRUSUB; - } - else if (hb_subtitle_can_burn(subt->source)) - { - // Only allow one subtitle to be burned into the video - if (one_burned) - continue; - one_burned = TRUE; - sub_config.dest = RENDERSUB; - } - - sub_config.force = force; - sub_config.default_track = def; - hb_subtitle_add(job, &sub_config, subtitle); - } - } - i++; - } - - if (one_burned) - { - hb_filter_object_t *filter = hb_filter_init( HB_FILTER_RENDER_SUB ); - hb_add_filter( job, filter, [[NSString stringWithFormat:@"%d:%d:%d:%d", - job->crop[0], job->crop[1], - job->crop[2], job->crop[3]] UTF8String] ); - } - - // Audio Defaults - job->acodec_copy_mask = 0; - - if (self.audioDefaults.allowAACPassthru) - { - job->acodec_copy_mask |= HB_ACODEC_FFAAC; - } - if (self.audioDefaults.allowAC3Passthru) - { - job->acodec_copy_mask |= HB_ACODEC_AC3; - } - if (self.audioDefaults.allowDTSHDPassthru) - { - job->acodec_copy_mask |= HB_ACODEC_DCA_HD; - } - if (self.audioDefaults.allowDTSPassthru) - { - job->acodec_copy_mask |= HB_ACODEC_DCA; - } - if (self.audioDefaults.allowMP3Passthru) - { - job->acodec_copy_mask |= HB_ACODEC_MP3; - } - job->acodec_fallback = self.audioDefaults.encoderFallback; - - // Audio tracks and mixdowns - // Now lets add our new tracks to the audio list here - for (HBAudioTrack *audioTrack in self.audioTracks) - { - if (audioTrack.enabled) - { - hb_audio_config_t *audio = (hb_audio_config_t *)calloc(1, sizeof(*audio)); - hb_audio_config_init(audio); - - NSNumber *sampleRateToUse = ([audioTrack.sampleRate[keyAudioSamplerate] intValue] == 0 ? - audioTrack.track[keyAudioInputSampleRate] : - audioTrack.sampleRate[keyAudioSamplerate]); - - audio->in.track = [audioTrack.track[keyAudioTrackIndex] intValue] -1; - - // We go ahead and assign values to our audio->out.<properties> - audio->out.track = audio->in.track; - audio->out.codec = [audioTrack.codec[keyAudioCodec] intValue]; - audio->out.compression_level = hb_audio_compression_get_default(audio->out.codec); - audio->out.mixdown = [audioTrack.mixdown[keyAudioMixdown] intValue]; - audio->out.normalize_mix_level = 0; - audio->out.bitrate = [audioTrack.bitRate[keyAudioBitrate] intValue]; - audio->out.samplerate = [sampleRateToUse intValue]; - audio->out.dither_method = hb_audio_dither_get_default(); - - // output is not passthru so apply gain - if (!([[audioTrack codec][keyAudioCodec] intValue] & HB_ACODEC_PASS_FLAG)) - { - audio->out.gain = [audioTrack.gain doubleValue]; - } - else - { - // output is passthru - the Gain dial is disabled so don't apply its value - audio->out.gain = 0; - } - - if (hb_audio_can_apply_drc([audioTrack.track[keyAudioInputCodec] intValue], - [audioTrack.track[keyAudioInputCodecParam] intValue], - [audioTrack.codec[keyAudioCodec] intValue])) - { - audio->out.dynamic_range_compression = [audioTrack.drc doubleValue]; - } - else - { - // source isn't AC3 or output is passthru - the DRC dial is disabled so don't apply its value - audio->out.dynamic_range_compression = 0; - } - - hb_audio_add(job, audio); - free(audio); - } - } - - // Now lets call the filters if applicable. - // The order of the filters is critical - - // Detelecine - hb_filter_object_t *filter; - if (self.filters.detelecine == 1) - { - filter = hb_filter_init(HB_FILTER_DETELECINE); - // use a custom detelecine string - hb_add_filter(job, filter, self.filters.detelecineCustomString.UTF8String); - } - else if (self.filters.detelecine == 2) - { - filter = hb_filter_init(HB_FILTER_DETELECINE); - // Use libhb's default values - hb_add_filter(job, filter, NULL); - } - - if (self.filters.useDecomb && self.filters.decomb) - { - // Decomb - filter = hb_filter_init(HB_FILTER_DECOMB); - if (self.filters.decomb == 1) - { - // use a custom decomb string */ - hb_add_filter(job, filter, self.filters.decombCustomString.UTF8String); - } - else if (self.filters.decomb == 2) - { - // use libhb defaults - hb_add_filter(job, filter, NULL); - } - else if (self.filters.decomb == 3) - { - // use old defaults (decomb fast) - hb_add_filter(job, filter, "7:2:6:9:1:80"); - } - else if (self.filters.decomb == 4) - { - // decomb 3 with bobbing enabled - hb_add_filter(job, filter, "455"); - } - } - else if (!self.filters.useDecomb && self.filters.deinterlace) - { - // Deinterlace - filter = hb_filter_init(HB_FILTER_DEINTERLACE); - if (self.filters.deinterlace == 1) - { - // we add the custom string if present - hb_add_filter(job, filter, self.filters.deinterlaceCustomString.UTF8String); - } - else if (self.filters.deinterlace == 2) - { - // Run old deinterlacer fd by default - hb_add_filter(job, filter, "0"); - } - else if (self.filters.deinterlace == 3) - { - // Yadif mode 0 (without spatial deinterlacing) - hb_add_filter(job, filter, "1"); - } - else if (self.filters.deinterlace == 4) - { - // Yadif (with spatial deinterlacing) - hb_add_filter(job, filter, "3"); - } - else if (self.filters.deinterlace == 5) - { - // Yadif (with spatial deinterlacing and bobbing) - hb_add_filter(job, filter, "15"); - } - } - // Denoise - if (![self.filters.denoise isEqualToString:@"off"]) - { - int filter_id = HB_FILTER_HQDN3D; - if ([self.filters.denoise isEqualToString:@"nlmeans"]) - filter_id = HB_FILTER_NLMEANS; - - if ([self.filters.denoisePreset isEqualToString:@"none"]) - { - const char *filter_str; - filter_str = self.filters.denoiseCustomString.UTF8String; - filter = hb_filter_init(filter_id); - hb_add_filter(job, filter, filter_str); - } - else - { - const char *filter_str, *preset, *tune; - preset = self.filters.denoisePreset.UTF8String; - tune = self.filters.denoiseTune.UTF8String; - filter_str = hb_generate_filter_settings(filter_id, preset, tune); - filter = hb_filter_init(filter_id); - hb_add_filter(job, filter, filter_str); - } - } - - // Deblock (uses pp7 default) - // NOTE: even though there is a valid deblock setting of 0 for the filter, for - // the macgui's purposes a value of 0 actually means to not even use the filter - // current hb_filter_deblock.settings valid ranges are from 5 - 15 - if (self.filters.deblock != 0) - { - filter = hb_filter_init(HB_FILTER_DEBLOCK); - hb_add_filter(job, filter, [NSString stringWithFormat:@"%ld", (long)self.filters.deblock].UTF8String); - } - - // Add Crop/Scale filter - filter = hb_filter_init(HB_FILTER_CROP_SCALE); - hb_add_filter( job, filter, [NSString stringWithFormat:@"%d:%d:%d:%d:%d:%d", - self.picture.width, self.picture.height, - self.picture.cropTop, self.picture.cropBottom, - self.picture.cropLeft, self.picture.cropRight].UTF8String); - - // Add framerate shaping filter - filter = hb_filter_init(HB_FILTER_VFR); - hb_add_filter(job, filter, [[NSString stringWithFormat:@"%d:%d:%d", - fps_mode, fps_num, fps_den] UTF8String]); - - return job; -} - #pragma mark - NSCopying - (instancetype)copyWithZone:(NSZone *)zone @@ -676,26 +172,11 @@ NSString *keyContainerTag = @"keyContainerTag"; copy->_video.job = copy; - // Copy the tracks, but not the last one because it's empty. - copy->_audioTracks = [[NSMutableArray alloc] init]; - [_audioTracks enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { - if (idx < _audioTracks.count - 1) - { - [copy->_audioTracks addObject:[[obj copy] autorelease]]; - } - }]; - copy->_subtitlesTracks = [[NSMutableArray alloc] init]; - [_subtitlesTracks enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { - if (idx < _subtitlesTracks.count - 1) - { - [copy->_subtitlesTracks addObject:[[obj copy] autorelease]]; - } - }]; + copy->_audio = [_audio copy]; + copy->_subtitles = [_subtitles copy]; + copy->_chaptersEnabled = _chaptersEnabled; copy->_chapterTitles = [[NSMutableArray alloc] initWithArray:_chapterTitles copyItems:YES]; - - copy->_audioDefaults = [_audioDefaults copy]; - copy->_subtitlesDefaults = [_subtitlesDefaults copy]; } return copy; @@ -725,14 +206,11 @@ NSString *keyContainerTag = @"keyContainerTag"; encodeObject(_picture); encodeObject(_filters); - encodeObject(_audioTracks); - encodeObject(_subtitlesTracks); + encodeObject(_audio); + encodeObject(_subtitles); encodeBool(_chaptersEnabled); encodeObject(_chapterTitles); - - encodeObject(_audioDefaults); - encodeObject(_subtitlesDefaults); } - (id)initWithCoder:(NSCoder *)decoder @@ -759,15 +237,12 @@ NSString *keyContainerTag = @"keyContainerTag"; _video.job = self; - decodeObject(_audioTracks); - decodeObject(_subtitlesTracks); + decodeObject(_audio); + decodeObject(_subtitles); decodeBool(_chaptersEnabled); decodeObject(_chapterTitles); - decodeObject(_audioDefaults); - decodeObject(_subtitlesDefaults); - return self; } diff --git a/macosx/HBPicture.m b/macosx/HBPicture.m index 19c231c93..7f33d6c16 100644 --- a/macosx/HBPicture.m +++ b/macosx/HBPicture.m @@ -512,7 +512,7 @@ NSString * const HBPictureChangedNotification = @"HBPictureChangedNotification"; return self; } -#pragma mark - Presets/Queue +#pragma mark - Presets - (void)writeToPreset:(NSMutableDictionary *)preset { diff --git a/macosx/HBPreviewGenerator.m b/macosx/HBPreviewGenerator.m index ab2010221..c5cd36b40 100644 --- a/macosx/HBPreviewGenerator.m +++ b/macosx/HBPreviewGenerator.m @@ -11,6 +11,7 @@ #import "HBCore.h" #import "HBJob.h" +#import "HBJob+HBJobConversion.h" typedef enum EncodeState : NSUInteger { EncodeStateIdle, diff --git a/macosx/HBQueueController.mm b/macosx/HBQueueController.mm index 7c25ac7b4..542b2ba33 100644 --- a/macosx/HBQueueController.mm +++ b/macosx/HBQueueController.mm @@ -13,6 +13,8 @@ #import "HBUtilities.h" #import "HBJob.h" +#import "HBAudioDefaults.h" +#import "HBAudioTrack.h" #import "HBPicture+UIAdditions.h" #import "HBFilters+UIAdditions.h" @@ -744,7 +746,7 @@ } NSString *passesString = @""; // check to see if our first subtitle track is Foreign Language Search, in which case there is an in depth scan - if (job.subtitlesTracks.count && [job.subtitlesTracks[0][@"keySubTrackIndex"] intValue] == -1) + if (job.subtitles.tracks.count && [job.subtitles.tracks[0][@"keySubTrackIndex"] intValue] == -1) { passesString = [passesString stringByAppendingString:@"1 Foreign Language Search Pass - "]; } @@ -786,28 +788,31 @@ NSString *audioCodecSummary = @""; // This seems to be set by the last track we have available... // Lets also get our audio track detail since we are going through the logic for use later - NSMutableArray *audioDetails = [NSMutableArray arrayWithCapacity:job.audioTracks.count]; + NSMutableArray *audioDetails = [NSMutableArray array]; BOOL autoPassthruPresent = NO; - for (HBAudioTrack *audioTrack in job.audioTracks) + for (HBAudioTrack *audioTrack in job.audio.tracks) { - audioCodecSummary = [NSString stringWithFormat: @"%@", audioTrack.codec[keyAudioCodecName]]; - NSNumber *drc = audioTrack.drc; - NSNumber *gain = audioTrack.gain; - NSString *detailString = [NSString stringWithFormat: @"%@ Encoder: %@ Mixdown: %@ SampleRate: %@(khz) Bitrate: %@(kbps), DRC: %@, Gain: %@", - audioTrack.track[keyAudioTrackName], - audioTrack.codec[keyAudioCodecName], - audioTrack.mixdown[keyAudioMixdownName], - audioTrack.sampleRate[keyAudioSampleRateName], - audioTrack.bitRate[keyAudioBitrateName], - (0.0 < [drc floatValue]) ? (NSObject *)drc : (NSObject *)@"Off", - (0.0 != [gain floatValue]) ? (NSObject *)gain : (NSObject *)@"Off" - ]; - [audioDetails addObject: detailString]; - // check if we have an Auto Passthru output track - if ([audioTrack.codec[keyAudioCodecName] isEqualToString: @"Auto Passthru"]) + if (audioTrack.enabled) { - autoPassthruPresent = YES; + audioCodecSummary = [NSString stringWithFormat: @"%@", audioTrack.codec[keyAudioCodecName]]; + NSNumber *drc = audioTrack.drc; + NSNumber *gain = audioTrack.gain; + NSString *detailString = [NSString stringWithFormat: @"%@ Encoder: %@ Mixdown: %@ SampleRate: %@(khz) Bitrate: %@(kbps), DRC: %@, Gain: %@", + audioTrack.track[keyAudioTrackName], + audioTrack.codec[keyAudioCodecName], + audioTrack.mixdown[keyAudioMixdownName], + audioTrack.sampleRate[keyAudioSampleRateName], + audioTrack.bitRate[keyAudioBitrateName], + (0.0 < [drc floatValue]) ? (NSObject *)drc : (NSObject *)@"Off", + (0.0 != [gain floatValue]) ? (NSObject *)gain : (NSObject *)@"Off" + ]; + [audioDetails addObject: detailString]; + // check if we have an Auto Passthru output track + if ([audioTrack.codec[keyAudioCodecName] isEqualToString: @"Auto Passthru"]) + { + autoPassthruPresent = YES; + } } } @@ -988,7 +993,7 @@ if (autoPassthruPresent == YES) { NSString *autoPassthruFallback = @"", *autoPassthruCodecs = @""; - HBAudioDefaults *audioDefaults = job.audioDefaults; + HBAudioDefaults *audioDefaults = job.audio.defaults; autoPassthruFallback = [autoPassthruFallback stringByAppendingString:@(hb_audio_encoder_get_name(audioDefaults.encoderFallback))]; if (audioDefaults.allowAACPassthru) { @@ -1042,8 +1047,15 @@ } // Ninth Line Subtitle Details - for (NSDictionary *track in job.subtitlesTracks) + int i = 0; + for (NSDictionary *track in job.subtitles.tracks) { + // Ignore the none track. + if (i == job.subtitles.tracks.count - 1) + { + continue; + } + /* remember that index 0 of Subtitles can contain "Foreign Audio Search*/ [finalString appendString: @"Subtitle: " withAttributes:detailBoldAttr]; [finalString appendString: track[@"keySubTrackName"] withAttributes:detailAttr]; @@ -1060,6 +1072,7 @@ [finalString appendString: @" - Default" withAttributes:detailAttr]; } [finalString appendString:@"\n" withAttributes:detailAttr]; + i++; } [pool release]; diff --git a/macosx/HBSubtitles.h b/macosx/HBSubtitles.h new file mode 100644 index 000000000..ae7421b7b --- /dev/null +++ b/macosx/HBSubtitles.h @@ -0,0 +1,65 @@ +/* HBSubtitles.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 <Foundation/Foundation.h> +#import "HBPresetCoding.h" + +extern NSString *keySubTrackSelectionIndex; +extern NSString *keySubTrackName; +extern NSString *keySubTrackIndex; +extern NSString *keySubTrackLanguage; +extern NSString *keySubTrackLanguageIsoCode; +extern NSString *keySubTrackType; + +extern NSString *keySubTrackForced; +extern NSString *keySubTrackBurned; +extern NSString *keySubTrackDefault; + +extern NSString *keySubTrackSrtOffset; +extern NSString *keySubTrackSrtFilePath; +extern NSString *keySubTrackSrtCharCode; +extern NSString *keySubTrackSrtCharCodeIndex; +extern NSString *keySubTrackLanguageIndex; + +@class HBTitle; +@class HBSubtitlesDefaults; + +@interface HBSubtitles : NSObject <NSCoding, NSCopying, HBPresetCoding> + +- (instancetype)initWithTitle:(HBTitle *)title; + +- (void)addAllTracks; +- (void)removeAll; +- (void)reloadDefaults; + +- (void)validatePassthru; +- (NSMutableDictionary *)createSubtitleTrack; +- (NSMutableDictionary *)trackFromSourceTrackIndex:(NSInteger)index; + +@property (nonatomic, readonly) NSMutableArray *masterTrackArray; // the master list of audio tracks from the title +@property (nonatomic, readonly) NSMutableArray *tracks; + +@property (nonatomic, readwrite, retain) NSString *foreignAudioSearchTrackName; +@property (nonatomic, readonly) NSArray *charCodeArray; + +@property (nonatomic, readonly) NSArray *languagesArray; +@property (nonatomic, readonly) NSInteger languagesArrayDefIndex; + +@property (nonatomic, readwrite) int container; // initially is the default HB_MUX_MP4 + +@property (nonatomic, readwrite, retain) HBSubtitlesDefaults *defaults; + +@end + +@interface HBSubtitles (KVC) + +- (NSUInteger)countOfTracks; +- (id)objectInTracksAtIndex:(NSUInteger)index; +- (void)insertObject:(id)audioObject inTracksAtIndex:(NSUInteger)index; +- (void)removeObjectFromTracksAtIndex:(NSUInteger)index; + +@end + diff --git a/macosx/HBSubtitles.m b/macosx/HBSubtitles.m new file mode 100644 index 000000000..c8965169c --- /dev/null +++ b/macosx/HBSubtitles.m @@ -0,0 +1,497 @@ +// +// HBSubtitles.m +// HandBrake +// +// Created by Damiano Galassi on 12/01/15. +// +// + +#import "HBSubtitles.h" +#import "HBSubtitlesDefaults.h" + +#import "HBTitle.h" +#import "NSCodingMacro.h" +#include "lang.h" + +NSString *keySubTrackSelectionIndex = @"keySubTrackSelectionIndex"; +NSString *keySubTrackName = @"keySubTrackName"; +NSString *keySubTrackIndex = @"keySubTrackIndex"; +NSString *keySubTrackLanguage = @"keySubTrackLanguage"; +NSString *keySubTrackLanguageIsoCode = @"keySubTrackLanguageIsoCode"; +NSString *keySubTrackType = @"keySubTrackType"; + +NSString *keySubTrackForced = @"keySubTrackForced"; +NSString *keySubTrackBurned = @"keySubTrackBurned"; +NSString *keySubTrackDefault = @"keySubTrackDefault"; + +NSString *keySubTrackSrtOffset = @"keySubTrackSrtOffset"; +NSString *keySubTrackSrtFilePath = @"keySubTrackSrtFilePath"; +NSString *keySubTrackSrtCharCode = @"keySubTrackSrtCharCode"; +NSString *keySubTrackSrtCharCodeIndex = @"keySubTrackSrtCharCodeIndex"; +NSString *keySubTrackLanguageIndex = @"keySubTrackLanguageIndex"; + +#define CHAR_CODE_DEFAULT_INDEX 11 + +@implementation HBSubtitles + +- (instancetype)initWithTitle:(HBTitle *)title +{ + self = [super init]; + if (self) + { + _container = HB_MUX_MP4; + + _tracks = [[NSMutableArray alloc] init]; + _defaults = [[HBSubtitlesDefaults alloc] init]; + + _masterTrackArray = [title.subtitlesTracks mutableCopy]; + + NSMutableArray *forcedSourceNamesArray = [NSMutableArray array]; + for (NSDictionary *dict in _masterTrackArray) + { + enum subsource source = [dict[keySubTrackType] intValue]; + NSString *subSourceName = @(hb_subsource_name(source)); + // if the subtitle track can be forced, add its source name to the array + if (hb_subtitle_can_force(source) && [forcedSourceNamesArray containsObject:subSourceName] == NO) + { + [forcedSourceNamesArray addObject:subSourceName]; + } + } + + // now set the name of the Foreign Audio Search track + if (forcedSourceNamesArray.count) + { + [forcedSourceNamesArray sortUsingComparator:^(id obj1, id obj2) + { + return [((NSString *)obj1) compare:((NSString *)obj2)]; + }]; + + NSString *tempList = @""; + for (NSString *tempString in forcedSourceNamesArray) + { + if (tempList.length) + { + tempList = [tempList stringByAppendingString:@", "]; + } + tempList = [tempList stringByAppendingString:tempString]; + } + self.foreignAudioSearchTrackName = [NSString stringWithFormat:@"Foreign Audio Search (Bitmap) (%@)", tempList]; + } + else + { + self.foreignAudioSearchTrackName = @"Foreign Audio Search (Bitmap)"; + } + } + return self; +} + +- (void)addAllTracks +{ + [self.tracks removeAllObjects]; + + // Add the foreign audio search pass + [self addTrack:[self trackFromSourceTrackIndex:-1]]; + + // Add the remainings tracks + for (NSDictionary *track in self.masterTrackArray) + { + NSInteger sourceIndex = [track[keySubTrackIndex] integerValue]; + [self addTrack:[self trackFromSourceTrackIndex:sourceIndex]]; + } + + [self.tracks addObject:[self createSubtitleTrack]]; + [self validatePassthru]; +} + +- (void)removeAll +{ + [self.tracks removeAllObjects]; + [self.tracks addObject:[self createSubtitleTrack]]; +} + +- (void)reloadDefaults +{ + [self addTracksFromDefaults]; +} + +// This gets called whenever the video container changes. +- (void)setContainer:(int)container +{ + _container = container; + + [self validatePassthru]; +} + +/** + * Convenience method to add a track to subtitlesArray. + * It calculates the keySubTrackSelectionIndex. + * + * @param track the track to add. + */ +- (void)addTrack:(NSMutableDictionary *)newTrack +{ + newTrack[keySubTrackSelectionIndex] = @([newTrack[keySubTrackIndex] integerValue] + 1 + (self.tracks.count == 0)); + [self insertObject:newTrack inTracksAtIndex:[self countOfTracks]]; +} + +/** + * Creates a new subtitle track. + */ +- (NSMutableDictionary *)createSubtitleTrack +{ + NSMutableDictionary *newSubtitleTrack = [[NSMutableDictionary alloc] init]; + newSubtitleTrack[keySubTrackIndex] = @0; + newSubtitleTrack[keySubTrackSelectionIndex] = @0; + newSubtitleTrack[keySubTrackName] = @"None"; + newSubtitleTrack[keySubTrackForced] = @0; + newSubtitleTrack[keySubTrackBurned] = @0; + newSubtitleTrack[keySubTrackDefault] = @0; + + return [newSubtitleTrack autorelease]; +} + +/** + * Creates a new track dictionary from a source track. + * + * @param index the index of the source track in the subtitlesSourceArray, + * -1 means a Foreign Audio Search pass. + * + * @return a new mutable track dictionary. + */ +- (NSMutableDictionary *)trackFromSourceTrackIndex:(NSInteger)index +{ + NSMutableDictionary *track = [self createSubtitleTrack]; + + if (index == -1) + { + /* + * we are foreign lang search, which is inherently bitmap + * + * since it can be either VOBSUB or PGS and the latter can't be + * passed through to MP4, we need to know whether there are any + * PGS tracks in the source - otherwise we can just set the + * source track type to VOBSUB + */ + int subtitleTrackType = VOBSUB; + if ([self.foreignAudioSearchTrackName rangeOfString:@(hb_subsource_name(PGSSUB))].location != NSNotFound) + { + subtitleTrackType = PGSSUB; + } + // Use -1 to indicate the foreign lang search + track[keySubTrackIndex] = @(-1); + track[keySubTrackName] = self.foreignAudioSearchTrackName; + track[keySubTrackType] = @(subtitleTrackType); + // foreign lang search is most useful when combined w/Forced Only - make it default + track[keySubTrackForced] = @1; + } + else + { + NSDictionary *sourceTrack = self.masterTrackArray[index]; + + track[keySubTrackIndex] = @(index); + track[keySubTrackName] = sourceTrack[keySubTrackName]; + + /* check to see if we are an srt, in which case set our file path and source track type kvp's*/ + if ([self.masterTrackArray[index][keySubTrackType] intValue] == SRTSUB) + { + track[keySubTrackType] = @(SRTSUB); + track[keySubTrackSrtFilePath] = sourceTrack[keySubTrackSrtFilePath]; + + track[keySubTrackLanguageIndex] = @(self.languagesArrayDefIndex); + track[keySubTrackLanguageIsoCode] = self.languagesArray[self.languagesArrayDefIndex][1]; + + track[keySubTrackSrtCharCodeIndex] = @(CHAR_CODE_DEFAULT_INDEX); + track[keySubTrackSrtCharCode] = self.charCodeArray[CHAR_CODE_DEFAULT_INDEX]; + } + else + { + track[keySubTrackType] = sourceTrack[keySubTrackType]; + } + } + + if (!hb_subtitle_can_burn([track[keySubTrackType] intValue])) + { + /* the source track cannot be burned in, so uncheck the widget */ + track[keySubTrackBurned] = @0; + } + + if (!hb_subtitle_can_force([track[keySubTrackType] intValue])) + { + /* the source track does not support forced flags, so uncheck the widget */ + track[keySubTrackForced] = @0; + } + + return track; +} + +/** + * Remove all the subtitles tracks and + * add new ones based on the defaults settings + */ +- (IBAction)addTracksFromDefaults +{ + // Keeps a set of the indexes of the added track + // so we don't add the same track twice. + NSMutableIndexSet *tracksAdded = [NSMutableIndexSet indexSet]; + + [self.tracks removeAllObjects]; + + // Add the foreign audio search pass + if (self.defaults.addForeignAudioSearch) + { + [self addTrack:[self trackFromSourceTrackIndex:-1]]; + } + + // Add the tracks for the selected languages + if (self.defaults.trackSelectionBehavior != HBSubtitleTrackSelectionBehaviorNone) + { + for (NSString *lang in self.defaults.trackSelectionLanguages) + { + for (NSDictionary *track in self.masterTrackArray) + { + if ([lang isEqualToString:@"und"] || [track[keySubTrackLanguageIsoCode] isEqualToString:lang]) + { + NSInteger sourceIndex = [track[keySubTrackIndex] intValue]; + + if (![tracksAdded containsIndex:sourceIndex]) + { + [self addTrack:[self trackFromSourceTrackIndex:sourceIndex]]; + } + [tracksAdded addIndex:sourceIndex]; + + if (self.defaults.trackSelectionBehavior == HBSubtitleTrackSelectionBehaviorFirst) + { + break; + } + } + } + } + } + + // Add the closed captions track if there is one. + if (self.defaults.addCC) + { + for (NSDictionary *track in self.masterTrackArray) + { + if ([track[keySubTrackType] intValue] == CC608SUB) + { + NSInteger sourceIndex = [track[keySubTrackIndex] intValue]; + if (![tracksAdded containsIndex:sourceIndex]) + { + [self addTrack:[self trackFromSourceTrackIndex:sourceIndex]]; + } + + if (self.defaults.trackSelectionBehavior == HBSubtitleTrackSelectionBehaviorFirst) + { + break; + } + } + } + } + + // Add an empty track + [self insertObject:[self createSubtitleTrack] inTracksAtIndex:[self countOfTracks]]; + + [self validatePassthru]; +} + +/** + * Checks whether any subtitles in the list cannot be passed through. + * Set the first of any such subtitles to burned-in, remove the others. + */ +- (void)validatePassthru +{ + int subtitleTrackType; + BOOL convertToBurnInUsed = NO; + NSMutableArray *tracksToDelete = [[NSMutableArray alloc] init]; + + // convert any non-None incompatible tracks to burn-in or remove them + for (NSMutableDictionary *track in self.tracks) + { + if (track[keySubTrackType] == nil) + { + continue; + } + + subtitleTrackType = [track[keySubTrackType] intValue]; + if (!hb_subtitle_can_pass(subtitleTrackType, self.container)) + { + if (convertToBurnInUsed == NO) + { + //we haven't set any track to burned-in yet, so we can + track[keySubTrackBurned] = @1; + convertToBurnInUsed = YES; //remove any additional tracks + } + else + { + //we already have a burned-in track, we must remove others + [tracksToDelete addObject:track]; + } + } + } + //if we converted a track to burned-in, unset it for tracks that support passthru + if (convertToBurnInUsed == YES) + { + for (NSMutableDictionary *track in self.tracks) + { + if (track[keySubTrackType] == nil) + { + continue; + } + + subtitleTrackType = [track[keySubTrackType] intValue]; + if (hb_subtitle_can_pass(subtitleTrackType, self.container)) + { + track[keySubTrackBurned] = @0; + } + } + } + + [self willChangeValueForKey:@"tracks"]; + if (tracksToDelete.count) + { + [self.tracks removeObjectsInArray:tracksToDelete]; + } + [self didChangeValueForKey:@"tracks"]; + + [tracksToDelete release]; +} + +#pragma mark - Languages + +@synthesize languagesArray = _languagesArray; + +- (NSArray *)languagesArray +{ + if (!_languagesArray) + { + _languagesArray = [[self populateLanguageArray] retain]; + } + + return _languagesArray; +} + +- (NSArray *)populateLanguageArray +{ + NSMutableArray *languages = [[[NSMutableArray alloc] init] autorelease]; + + for (const iso639_lang_t * lang = lang_get_next(NULL); lang != NULL; lang = lang_get_next(lang)) + { + [languages addObject:@[@(lang->eng_name), + @(lang->iso639_2)]]; + if (!strcasecmp(lang->eng_name, "English")) + { + _languagesArrayDefIndex = [languages count] - 1; + } + } + return [[languages copy] autorelease]; +} + +@synthesize charCodeArray = _charCodeArray; + +- (NSArray *)charCodeArray +{ + if (!_charCodeArray) + { + // populate the charCodeArray. + _charCodeArray = [@[@"ANSI_X3.4-1968", @"ANSI_X3.4-1986", @"ANSI_X3.4", @"ANSI_X3.110-1983", @"ANSI_X3.110", @"ASCII", + @"ECMA-114", @"ECMA-118", @"ECMA-128", @"ECMA-CYRILLIC", @"IEC_P27-1", @"ISO-8859-1", @"ISO-8859-2", + @"ISO-8859-3", @"ISO-8859-4", @"ISO-8859-5", @"ISO-8859-6", @"ISO-8859-7", @"ISO-8859-8", @"ISO-8859-9", + @"ISO-8859-9E", @"ISO-8859-10", @"ISO-8859-11", @"ISO-8859-13", @"ISO-8859-14", @"ISO-8859-15", @"ISO-8859-16", + @"UTF-7", @"UTF-8", @"UTF-16", @"UTF-16LE", @"UTF-16BE", @"UTF-32", @"UTF-32LE", @"UTF-32BE"] retain]; + } + return _charCodeArray; +} + +#pragma mark - NSCopying + +- (instancetype)copyWithZone:(NSZone *)zone +{ + HBSubtitles *copy = [[[self class] alloc] init]; + + if (copy) + { + copy->_container = _container; + + copy->_masterTrackArray = [_masterTrackArray copy]; + + copy->_tracks = [[NSMutableArray alloc] init]; + [_tracks enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + if (idx < _tracks.count) + { + NSMutableDictionary *trackCopy = [obj copy]; + [copy->_tracks addObject:[trackCopy autorelease]]; + } + }]; + + copy->_defaults = [_defaults copy]; + } + + return copy; +} + +#pragma mark - NSCoding + +- (void)encodeWithCoder:(NSCoder *)coder +{ + [coder encodeInt:1 forKey:@"HBAudioVersion"]; + + encodeInt(_container); + + encodeObject(_masterTrackArray); + encodeObject(_tracks); + + encodeObject(_defaults); +} + +- (id)initWithCoder:(NSCoder *)decoder +{ + self = [super init]; + + decodeInt(_container); + + decodeObject(_masterTrackArray); + decodeObject(_tracks); + + decodeObject(_defaults); + + return self; +} + +#pragma mark - Presets + +- (void)writeToPreset:(NSMutableDictionary *)preset +{ + [self.defaults writeToPreset:preset]; +} + +- (void)applyPreset:(NSDictionary *)preset +{ + [self.defaults applyPreset:preset]; + [self addTracksFromDefaults]; +} + +#pragma mark - +#pragma mark KVC + +- (NSUInteger) countOfTracks +{ + return self.tracks.count; +} + +- (id)objectInTracksAtIndex:(NSUInteger)index +{ + return self.tracks[index]; +} + +- (void)insertObject:(id)track inTracksAtIndex:(NSUInteger)index; +{ + [self.tracks insertObject:track atIndex:index]; +} + +- (void)removeObjectFromTracksAtIndex:(NSUInteger)index +{ + [self.tracks removeObjectAtIndex:index]; +} + +@end diff --git a/macosx/HBSubtitlesController.h b/macosx/HBSubtitlesController.h index 1d681349e..b65cb35e2 100644 --- a/macosx/HBSubtitlesController.h +++ b/macosx/HBSubtitlesController.h @@ -6,30 +6,13 @@ #import <Cocoa/Cocoa.h> -extern NSString *keySubTrackName; -extern NSString *keySubTrackIndex; -extern NSString *keySubTrackLanguage; -extern NSString *keySubTrackLanguageIsoCode; -extern NSString *keySubTrackType; - -extern NSString *keySubTrackForced; -extern NSString *keySubTrackBurned; -extern NSString *keySubTrackDefault; - -extern NSString *keySubTrackSrtOffset; -extern NSString *keySubTrackSrtFilePath; -extern NSString *keySubTrackSrtCharCode; - -@class HBJob; +@class HBSubtitles; /** * HBSubtitlesController - * Responds to HBContainerChangedNotification. */ @interface HBSubtitlesController : NSViewController -- (void)applySettingsFromPreset:(NSDictionary *)preset; - -@property (nonatomic, readwrite, assign) HBJob *job; +@property (nonatomic, readwrite, assign) HBSubtitles *subtitles; @end diff --git a/macosx/HBSubtitlesController.m b/macosx/HBSubtitlesController.m index b6433ef8a..5f2c7ef5b 100644 --- a/macosx/HBSubtitlesController.m +++ b/macosx/HBSubtitlesController.m @@ -7,56 +7,21 @@ #import "HBSubtitlesController.h" #import "HBSubtitlesDefaultsController.h" -#import "HBJob.h" +#import "HBSubtitles.h" +#import "HBSubtitlesDefaults.h" #include "hb.h" #include "lang.h" -NSString *keySubTrackSelectionIndex = @"keySubTrackSelectionIndex"; -NSString *keySubTrackName = @"keySubTrackName"; -NSString *keySubTrackIndex = @"keySubTrackIndex"; -NSString *keySubTrackLanguage = @"keySubTrackLanguage"; -NSString *keySubTrackLanguageIsoCode = @"keySubTrackLanguageIsoCode"; -NSString *keySubTrackType = @"keySubTrackType"; - -NSString *keySubTrackForced = @"keySubTrackForced"; -NSString *keySubTrackBurned = @"keySubTrackBurned"; -NSString *keySubTrackDefault = @"keySubTrackDefault"; - -NSString *keySubTrackSrtOffset = @"keySubTrackSrtOffset"; -NSString *keySubTrackSrtFilePath = @"keySubTrackSrtFilePath"; -NSString *keySubTrackSrtCharCode = @"keySubTrackSrtCharCode"; -NSString *keySubTrackSrtCharCodeIndex = @"keySubTrackSrtCharCodeIndex"; -NSString *keySubTrackLanguageIndex = @"keySubTrackLanguageIndex"; - -#define CHAR_CODE_DEFAULT_INDEX 11 +static void *HBSubtitlesControllerContext = &HBSubtitlesControllerContext; @interface HBSubtitlesController () <NSTableViewDataSource, NSTableViewDelegate> // IBOutles @property (assign) IBOutlet NSTableView *fTableView; -@property (assign) IBOutlet NSPopUpButton *trackPopUp; -@property (assign) IBOutlet NSButton *configureDefaults; -@property (assign) IBOutlet NSButton *reloadDefaults; - -// Subtitles arrays -@property (nonatomic, readwrite, retain) NSMutableArray *subtitleArray; -@property (nonatomic, readwrite, retain) NSMutableArray *subtitleSourceArray; - -@property (nonatomic, readwrite, retain) NSString *foreignAudioSearchTrackName; -@property (nonatomic, readwrite) int container; - // Defaults @property (nonatomic, readwrite, retain) HBSubtitlesDefaultsController *defaultsController; -@property (nonatomic, readwrite, retain) HBSubtitlesDefaults *settings; - -// Table view cells models -@property (nonatomic, readonly) NSArray *charCodeArray; -@property (nonatomic, readwrite) BOOL foreignAudioSearchSelected; - -@property (nonatomic, readonly) NSArray *languagesArray; -@property (nonatomic, readonly) NSInteger languagesArrayDefIndex; // Cached table view's cells @property (nonatomic, readonly) NSPopUpButtonCell *languagesCell; @@ -69,125 +34,43 @@ NSString *keySubTrackLanguageIndex = @"keySubTrackLanguageIndex"; - (instancetype)init { self = [super initWithNibName:@"Subtitles" bundle:nil]; - if (self) - { - _subtitleSourceArray = [[NSMutableArray alloc] init]; - _subtitleArray = [[NSMutableArray alloc] init]; - _languagesArray = [[self populateLanguageArray] retain]; - - // populate the charCodeArray. - _charCodeArray = [@[@"ANSI_X3.4-1968", @"ANSI_X3.4-1986", @"ANSI_X3.4", @"ANSI_X3.110-1983", @"ANSI_X3.110", @"ASCII", - @"ECMA-114", @"ECMA-118", @"ECMA-128", @"ECMA-CYRILLIC", @"IEC_P27-1", @"ISO-8859-1", @"ISO-8859-2", - @"ISO-8859-3", @"ISO-8859-4", @"ISO-8859-5", @"ISO-8859-6", @"ISO-8859-7", @"ISO-8859-8", @"ISO-8859-9", - @"ISO-8859-9E", @"ISO-8859-10", @"ISO-8859-11", @"ISO-8859-13", @"ISO-8859-14", @"ISO-8859-15", @"ISO-8859-16", - @"UTF-7", @"UTF-8", @"UTF-16", @"UTF-16LE", @"UTF-16BE", @"UTF-32", @"UTF-32LE", @"UTF-32BE"] retain]; - - // Register as observer for the HBJob notifications. - [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(containerChanged:) name: HBContainerChangedNotification object: nil]; - } + + [self addObserver:self forKeyPath:@"self.subtitles.tracks" options:NSKeyValueObservingOptionInitial context:HBSubtitlesControllerContext]; return self; } -- (void)setJob:(HBJob *)job -{ - /* reset the subtitles arrays */ - [self.subtitleArray removeAllObjects]; - [self.subtitleSourceArray removeAllObjects]; - self.settings = nil; +#pragma mark - KVO - if (job) +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context +{ + if (context == HBSubtitlesControllerContext) { - _job = job; - self.subtitleArray = job.subtitlesTracks; - self.settings = job.subtitlesDefaults; - self.subtitleSourceArray = [[job.title.subtitlesTracks mutableCopy] autorelease]; - - NSMutableArray *forcedSourceNamesArray = [NSMutableArray array]; - for (NSDictionary *dict in self.subtitleSourceArray) - { - enum subsource source = [dict[keySubTrackType] intValue]; - NSString *subSourceName = @(hb_subsource_name(source)); - // if the subtitle track can be forced, add its source name to the array - if (hb_subtitle_can_force(source) && [forcedSourceNamesArray containsObject:subSourceName] == NO) - { - [forcedSourceNamesArray addObject:subSourceName]; - } - } - - // now set the name of the Foreign Audio Search track - if (forcedSourceNamesArray.count) - { - [forcedSourceNamesArray sortUsingComparator:^(id obj1, id obj2) - { - return [((NSString *)obj1) compare:((NSString *)obj2)]; - }]; - - NSString *tempList = @""; - for (NSString *tempString in forcedSourceNamesArray) - { - if (tempList.length) - { - tempList = [tempList stringByAppendingString:@", "]; - } - tempList = [tempList stringByAppendingString:tempString]; - } - self.foreignAudioSearchTrackName = [NSString stringWithFormat:@"Foreign Audio Search (Bitmap) (%@)", tempList]; - } - else + // We use KVO to update the table manually + // because this table isn't using bindings + if ([keyPath isEqualToString:@"self.subtitles.tracks"]) { - self.foreignAudioSearchTrackName = @"Foreign Audio Search (Bitmap)"; - } - - // Note: we need to look for external subtitles so it can be added to the source array track. - // Remember the source container subs are already loaded with resetTitle which is already called - // so any external sub sources need to be added to our source subs here - for (NSDictionary *dict in self.subtitleArray) - { - /* We have an srt track */ - if ([dict[keySubTrackType] intValue] == SRTSUB) - { - NSString *filePath = dict[keySubTrackSrtFilePath]; - /* create a dictionary of source subtitle information to store in our array */ - [self.subtitleSourceArray addObject:@{keySubTrackIndex: @(self.subtitleSourceArray.count + 1), - keySubTrackName: [filePath lastPathComponent], - keySubTrackType: @(SRTSUB), - keySubTrackSrtFilePath: filePath}]; - } + [self.fTableView reloadData]; } - - // Append an empty track at the end - // to display a "None" row in the table view - [self.subtitleArray addObject:[self createSubtitleTrack]]; } else { - _job = nil; + [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; } - - [self.fTableView reloadData]; } -- (void)containerChanged:(NSNotification *)aNotification +- (void)setSubtitles:(HBSubtitles *)subtitles { - NSDictionary *notDict = [aNotification userInfo]; - self.container = [notDict[keyContainerTag] intValue]; - - [self validatePassthru]; - - [self.fTableView reloadData]; -} + _subtitles = subtitles; -- (void)applySettingsFromPreset:(NSDictionary *)preset -{ - [self addTracksFromDefaults:self]; + [self.fTableView reloadData]; } #pragma mark - Actions - (BOOL)validateUserInterfaceItem:(id < NSValidatedUserInterfaceItem >)anItem { - return (self.job != nil); + return (self.subtitles != nil); } /** @@ -195,20 +78,7 @@ NSString *keySubTrackLanguageIndex = @"keySubTrackLanguageIndex"; */ - (IBAction)addAll:(id)sender { - [self.subtitleArray removeAllObjects]; - - // Add the foreign audio search pass - [self addTrack:[self trackFromSourceTrackIndex:-1]]; - - // Add the remainings tracks - for (NSDictionary *track in self.subtitleSourceArray) - { - NSInteger sourceIndex = [track[keySubTrackIndex] integerValue]; - [self addTrack:[self trackFromSourceTrackIndex:sourceIndex]]; - } - - [self.subtitleArray addObject:[self createSubtitleTrack]]; - [self validatePassthru]; + [self.subtitles addAllTracks]; [self.fTableView reloadData]; } @@ -217,8 +87,7 @@ NSString *keySubTrackLanguageIndex = @"keySubTrackLanguageIndex"; */ - (IBAction)removeAll:(id)sender { - [self.subtitleArray removeAllObjects]; - [self.subtitleArray addObject:[self createSubtitleTrack]]; + [self.subtitles removeAll]; [self.fTableView reloadData]; } @@ -228,75 +97,13 @@ NSString *keySubTrackLanguageIndex = @"keySubTrackLanguageIndex"; */ - (IBAction)addTracksFromDefaults:(id)sender { - // Keeps a set of the indexes of the added track - // so we don't add the same track twice. - NSMutableIndexSet *tracksAdded = [NSMutableIndexSet indexSet]; - - [self.subtitleArray removeAllObjects]; - - // Add the foreign audio search pass - if (self.settings.addForeignAudioSearch) - { - [self addTrack:[self trackFromSourceTrackIndex:-1]]; - } - - // Add the tracks for the selected languages - if (self.settings.trackSelectionBehavior != HBSubtitleTrackSelectionBehaviorNone) - { - for (NSString *lang in self.settings.trackSelectionLanguages) - { - for (NSDictionary *track in self.subtitleSourceArray) - { - if ([lang isEqualToString:@"und"] || [track[keySubTrackLanguageIsoCode] isEqualToString:lang]) - { - NSInteger sourceIndex = [track[keySubTrackIndex] intValue]; - - if (![tracksAdded containsIndex:sourceIndex]) - { - [self addTrack:[self trackFromSourceTrackIndex:sourceIndex]]; - } - [tracksAdded addIndex:sourceIndex]; - - if (self.settings.trackSelectionBehavior == HBSubtitleTrackSelectionBehaviorFirst) - { - break; - } - } - } - } - } - - // Add the closed captions track if there is one. - if (self.settings.addCC) - { - for (NSDictionary *track in self.subtitleSourceArray) - { - if ([track[keySubTrackType] intValue] == CC608SUB) - { - NSInteger sourceIndex = [track[keySubTrackIndex] intValue]; - if (![tracksAdded containsIndex:sourceIndex]) - { - [self addTrack:[self trackFromSourceTrackIndex:sourceIndex]]; - } - - if (self.settings.trackSelectionBehavior == HBSubtitleTrackSelectionBehaviorFirst) - { - break; - } - } - } - } - - // Add an empty track - [self.subtitleArray addObject:[self createSubtitleTrack]]; - - [self validatePassthru]; + [self.subtitles reloadDefaults]; [self.fTableView reloadData]; } - (IBAction)showSettingsSheet:(id)sender { - self.defaultsController = [[[HBSubtitlesDefaultsController alloc] initWithSettings:self.settings] autorelease]; + self.defaultsController = [[[HBSubtitlesDefaultsController alloc] initWithSettings:self.subtitles.defaults] autorelease]; [NSApp beginSheet:[self.defaultsController window] modalForWindow:[[self view] window] @@ -313,171 +120,18 @@ NSString *keySubTrackLanguageIndex = @"keySubTrackLanguageIndex"; #pragma mark - Subtitles tracks creation and validation /** - * Convenience method to add a track to subtitlesArray. - * It calculates the keySubTrackSelectionIndex. - * - * @param track the track to add. - */ -- (void)addTrack:(NSMutableDictionary *)newTrack -{ - newTrack[keySubTrackSelectionIndex] = @([newTrack[keySubTrackIndex] integerValue] + 1 + (self.subtitleArray.count == 0)); - [self.subtitleArray addObject:newTrack]; -} - -/** - * Creates a new subtitle track. - */ -- (NSMutableDictionary *)createSubtitleTrack -{ - NSMutableDictionary *newSubtitleTrack = [[NSMutableDictionary alloc] init]; - newSubtitleTrack[keySubTrackIndex] = @0; - newSubtitleTrack[keySubTrackSelectionIndex] = @0; - newSubtitleTrack[keySubTrackName] = @"None"; - newSubtitleTrack[keySubTrackForced] = @0; - newSubtitleTrack[keySubTrackBurned] = @0; - newSubtitleTrack[keySubTrackDefault] = @0; - - return [newSubtitleTrack autorelease]; -} - -/** - * Creates a new track dictionary from a source track. - * - * @param index the index of the source track in the subtitlesSourceArray, - * -1 means a Foreign Audio Search pass. - * - * @return a new mutable track dictionary. - */ -- (NSMutableDictionary *)trackFromSourceTrackIndex:(NSInteger)index -{ - NSMutableDictionary *track = [self createSubtitleTrack]; - - if (index == -1) - { - /* - * we are foreign lang search, which is inherently bitmap - * - * since it can be either VOBSUB or PGS and the latter can't be - * passed through to MP4, we need to know whether there are any - * PGS tracks in the source - otherwise we can just set the - * source track type to VOBSUB - */ - int subtitleTrackType = VOBSUB; - if ([self.foreignAudioSearchTrackName rangeOfString:@(hb_subsource_name(PGSSUB))].location != NSNotFound) - { - subtitleTrackType = PGSSUB; - } - // Use -1 to indicate the foreign lang search - track[keySubTrackIndex] = @(-1); - track[keySubTrackName] = self.foreignAudioSearchTrackName; - track[keySubTrackType] = @(subtitleTrackType); - // foreign lang search is most useful when combined w/Forced Only - make it default - track[keySubTrackForced] = @1; - } - else - { - NSDictionary *sourceTrack = self.subtitleSourceArray[index]; - - track[keySubTrackIndex] = @(index); - track[keySubTrackName] = sourceTrack[keySubTrackName]; - - /* check to see if we are an srt, in which case set our file path and source track type kvp's*/ - if ([self.subtitleSourceArray[index][keySubTrackType] intValue] == SRTSUB) - { - track[keySubTrackType] = @(SRTSUB); - track[keySubTrackSrtFilePath] = sourceTrack[keySubTrackSrtFilePath]; - - track[keySubTrackLanguageIndex] = @(self.languagesArrayDefIndex); - track[keySubTrackLanguageIsoCode] = self.languagesArray[self.languagesArrayDefIndex][1]; - - track[keySubTrackSrtCharCodeIndex] = @(CHAR_CODE_DEFAULT_INDEX); - track[keySubTrackSrtCharCode] = self.charCodeArray[CHAR_CODE_DEFAULT_INDEX]; - } - else - { - track[keySubTrackType] = sourceTrack[keySubTrackType]; - } - } - - if (!hb_subtitle_can_burn([track[keySubTrackType] intValue])) - { - /* the source track cannot be burned in, so uncheck the widget */ - track[keySubTrackBurned] = @0; - } - - if (!hb_subtitle_can_force([track[keySubTrackType] intValue])) - { - /* the source track does not support forced flags, so uncheck the widget */ - track[keySubTrackForced] = @0; - } - - return track; -} - -/** * Checks whether any subtitles in the list cannot be passed through. * Set the first of any such subtitles to burned-in, remove the others. */ - (void)validatePassthru { - int subtitleTrackType; - BOOL convertToBurnInUsed = NO; - NSMutableArray *tracksToDelete = [[NSMutableArray alloc] init]; - - // convert any non-None incompatible tracks to burn-in or remove them - for (id tempObject in self.subtitleArray) - { - if (tempObject[keySubTrackType] == nil) - { - continue; - } - - subtitleTrackType = [tempObject[keySubTrackType] intValue]; - if (!hb_subtitle_can_pass(subtitleTrackType, self.container)) - { - if (convertToBurnInUsed == NO) - { - //we haven't set any track to burned-in yet, so we can - tempObject[keySubTrackBurned] = @1; - convertToBurnInUsed = YES; //remove any additional tracks - } - else - { - //we already have a burned-in track, we must remove others - [tracksToDelete addObject:tempObject]; - } - } - } - //if we converted a track to burned-in, unset it for tracks that support passthru - if (convertToBurnInUsed == YES) - { - for (id tempObject in self.subtitleArray) - { - if (tempObject[keySubTrackType] == nil) - { - continue; - } - - subtitleTrackType = [tempObject[keySubTrackType] intValue]; - if (hb_subtitle_can_pass(subtitleTrackType, self.container)) - { - tempObject[keySubTrackBurned] = @0; - } - } - } - - if (tracksToDelete.count) - { - [self.subtitleArray removeObjectsInArray:tracksToDelete]; - [self.fTableView reloadData]; - } - - [tracksToDelete release]; + [self.subtitles validatePassthru]; + [self.fTableView reloadData]; } - (void)validateBurned:(NSInteger)index { - [self.subtitleArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) + [self.subtitles.tracks enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { if (idx != index) { @@ -489,7 +143,7 @@ NSString *keySubTrackLanguageIndex = @"keySubTrackLanguageIndex"; - (void)validateDefault:(NSInteger)index { - [self.subtitleArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) + [self.subtitles.tracks enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { if (idx != index) { @@ -503,12 +157,12 @@ NSString *keySubTrackLanguageIndex = @"keySubTrackLanguageIndex"; - (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView { - return self.subtitleArray.count; + return self.subtitles.tracks.count; } - (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex { - NSDictionary *track = self.subtitleArray[rowIndex]; + NSDictionary *track = self.subtitles.tracks[rowIndex]; if ([[aTableColumn identifier] isEqualToString:@"track"]) { @@ -579,23 +233,23 @@ NSString *keySubTrackLanguageIndex = @"keySubTrackLanguageIndex"; /* Set the array to track if we are vobsub (picture sub) */ if ([anObject intValue] > 0) { - NSMutableDictionary *newTrack = [self trackFromSourceTrackIndex:[anObject integerValue] - 1 - (rowIndex == 0)]; + NSMutableDictionary *newTrack = [self.subtitles trackFromSourceTrackIndex:[anObject integerValue] - 1 - (rowIndex == 0)]; // Selection index calculation newTrack[keySubTrackSelectionIndex] = @([anObject integerValue]); - self.subtitleArray[rowIndex] = newTrack; + self.subtitles.tracks[rowIndex] = newTrack; } } else if ([[aTableColumn identifier] isEqualToString:@"forced"]) { - self.subtitleArray[rowIndex][keySubTrackForced] = @([anObject intValue]); + self.subtitles.tracks[rowIndex][keySubTrackForced] = @([anObject intValue]); } else if ([[aTableColumn identifier] isEqualToString:@"burned"]) { - self.subtitleArray[rowIndex][keySubTrackBurned] = @([anObject intValue]); + self.subtitles.tracks[rowIndex][keySubTrackBurned] = @([anObject intValue]); if([anObject intValue] == 1) { /* Burned In and Default are mutually exclusive */ - self.subtitleArray[rowIndex][keySubTrackDefault] = @0; + self.subtitles.tracks[rowIndex][keySubTrackDefault] = @0; } /* now we need to make sure no other tracks are set to burned if we have set burned */ if ([anObject intValue] == 1) @@ -605,11 +259,11 @@ NSString *keySubTrackLanguageIndex = @"keySubTrackLanguageIndex"; } else if ([[aTableColumn identifier] isEqualToString:@"default"]) { - self.subtitleArray[rowIndex][keySubTrackDefault] = @([anObject intValue]); + self.subtitles.tracks[rowIndex][keySubTrackDefault] = @([anObject intValue]); if([anObject intValue] == 1) { /* Burned In and Default are mutually exclusive */ - self.subtitleArray[rowIndex][keySubTrackBurned] = @0; + self.subtitles.tracks[rowIndex][keySubTrackBurned] = @0; } /* now we need to make sure no other tracks are set to default */ if ([anObject intValue] == 1) @@ -620,18 +274,18 @@ NSString *keySubTrackLanguageIndex = @"keySubTrackLanguageIndex"; /* These next three columns only apply to srt's. they are disabled for source subs */ else if ([[aTableColumn identifier] isEqualToString:@"srt_lang"]) { - self.subtitleArray[rowIndex][keySubTrackLanguageIndex] = @([anObject intValue]); - self.subtitleArray[rowIndex][keySubTrackLanguageIsoCode] = self.languagesArray[[anObject intValue]][1]; + self.subtitles.tracks[rowIndex][keySubTrackLanguageIndex] = @([anObject intValue]); + self.subtitles.tracks[rowIndex][keySubTrackLanguageIsoCode] = self.subtitles.languagesArray[[anObject intValue]][1]; } else if ([[aTableColumn identifier] isEqualToString:@"srt_charcode"]) { /* charCodeArray */ - self.subtitleArray[rowIndex][keySubTrackSrtCharCodeIndex] = @([anObject intValue]); - self.subtitleArray[rowIndex][keySubTrackSrtCharCode] = self.charCodeArray[[anObject intValue]]; + self.subtitles.tracks[rowIndex][keySubTrackSrtCharCodeIndex] = @([anObject intValue]); + self.subtitles.tracks[rowIndex][keySubTrackSrtCharCode] = self.subtitles.charCodeArray[[anObject intValue]]; } else if ([[aTableColumn identifier] isEqualToString:@"srt_offset"]) { - self.subtitleArray[rowIndex][keySubTrackSrtOffset] = @([anObject integerValue]); + self.subtitles.tracks[rowIndex][keySubTrackSrtOffset] = @([anObject integerValue]); } /* now lets do a bit of logic to add / remove tracks as necessary via the "None" track (index 0) */ @@ -641,13 +295,13 @@ NSString *keySubTrackLanguageIndex = @"keySubTrackLanguageIndex"; * by default to avoid massive confusion and anarchy. However we also want to guard against multiple burned in subtitle tracks * as libhb would ignore all but the first one anyway. Plus it would probably be stupid. */ - if ((self.container & HB_MUX_MASK_MP4) && ([anObject intValue] != 0)) + if ((self.subtitles.container & HB_MUX_MASK_MP4) && ([anObject intValue] != 0)) { - if ([self.subtitleArray[rowIndex][keySubTrackType] intValue] == VOBSUB) + if ([self.subtitles.tracks[rowIndex][keySubTrackType] intValue] == VOBSUB) { /* lets see if there are currently any burned in subs specified */ BOOL subtrackBurnedInFound = NO; - for (id tempObject in self.subtitleArray) + for (id tempObject in self.subtitles.tracks) { if ([tempObject[keySubTrackBurned] intValue] == 1) { @@ -657,9 +311,9 @@ NSString *keySubTrackLanguageIndex = @"keySubTrackLanguageIndex"; /* if we have no current vobsub set to burn it in ... burn it in by default */ if (!subtrackBurnedInFound) { - self.subtitleArray[rowIndex][keySubTrackBurned] = @1; + self.subtitles.tracks[rowIndex][keySubTrackBurned] = @1; /* Burned In and Default are mutually exclusive */ - self.subtitleArray[rowIndex][keySubTrackDefault] = @0; + self.subtitles.tracks[rowIndex][keySubTrackDefault] = @0; } } } @@ -668,25 +322,25 @@ NSString *keySubTrackLanguageIndex = @"keySubTrackLanguageIndex"; * to determine whether to 1 modify an existing track, 2. add a new empty "None" track or 3. remove an existing track. */ - if ([anObject intValue] != 0 && rowIndex == [self.subtitleArray count] - 1) // if we have a last track which != "None" + if ([anObject intValue] != 0 && rowIndex == [self.subtitles.tracks count] - 1) // if we have a last track which != "None" { /* add a new empty None track */ - [self.subtitleArray addObject:[self createSubtitleTrack]]; + [self.subtitles.tracks addObject:[self.subtitles createSubtitleTrack]]; } - else if ([anObject intValue] == 0 && rowIndex != ([self.subtitleArray count] -1))// if this track is set to "None" and not the last track displayed + else if ([anObject intValue] == 0 && rowIndex != ([self.subtitles.tracks count] -1))// if this track is set to "None" and not the last track displayed { /* we know the user chose to remove this track by setting it to None, so remove it from the array */ /* However,if this is the first track we have to reset the selected index of the next track by + 1, since it will now become * the first track, which has to account for the extra "Foreign Language Search" index. */ - if (rowIndex == 0 && [self.subtitleArray[1][keySubTrackSelectionIndex] intValue] != 0) + if (rowIndex == 0 && [self.subtitles.tracks[1][keySubTrackSelectionIndex] intValue] != 0) { /* get the index of the selection in row one (which is track two) */ - int trackOneSelectedIndex = [self.subtitleArray[1][keySubTrackSelectionIndex] intValue]; + int trackOneSelectedIndex = [self.subtitles.tracks[1][keySubTrackSelectionIndex] intValue]; /* increment the index of the subtitle menu item by one, to account for Foreign Language Search which is unique to the first track */ - self.subtitleArray[1][keySubTrackSelectionIndex] = @(trackOneSelectedIndex + 1); + self.subtitles.tracks[1][keySubTrackSelectionIndex] = @(trackOneSelectedIndex + 1); } /* now that we have made the adjustment for track one (index 0) go ahead and delete the track */ - [self.subtitleArray removeObjectAtIndex: rowIndex]; + [self.subtitles.tracks removeObjectAtIndex: rowIndex]; } // Validate the current passthru tracks. @@ -715,10 +369,10 @@ NSString *keySubTrackLanguageIndex = @"keySubTrackLanguageIndex"; // Foreign Audio Search (index 1 in the popup) is only available for the first track if (rowIndex == 0) { - [[cellTrackPopup menu] addItemWithTitle:self.foreignAudioSearchTrackName action:NULL keyEquivalent:@""]; + [[cellTrackPopup menu] addItemWithTitle:self.subtitles.foreignAudioSearchTrackName action:NULL keyEquivalent:@""]; } - for (NSDictionary *track in self.subtitleSourceArray) + for (NSDictionary *track in self.subtitles.masterTrackArray) { [[cellTrackPopup menu] addItemWithTitle:track[keySubTrackName] action:NULL keyEquivalent:@""]; } @@ -748,7 +402,7 @@ NSString *keySubTrackLanguageIndex = @"keySubTrackLanguageIndex"; } // If the Track is None, we disable the other cells as None is an empty track - if ([self.subtitleArray[rowIndex][keySubTrackSelectionIndex] intValue] == 0) + if ([self.subtitles.tracks[rowIndex][keySubTrackSelectionIndex] intValue] == 0) { [aCell setEnabled:NO]; } @@ -761,8 +415,8 @@ NSString *keySubTrackLanguageIndex = @"keySubTrackLanguageIndex"; if ([[aTableColumn identifier] isEqualToString:@"forced"]) { // Disable the "Forced Only" checkbox if a) the track is "None" or b) the subtitle track doesn't support forced flags - if (![self.subtitleArray[rowIndex][keySubTrackSelectionIndex] intValue] || - !hb_subtitle_can_force([self.subtitleArray[rowIndex][keySubTrackType] intValue])) + if (![self.subtitles.tracks[rowIndex][keySubTrackSelectionIndex] intValue] || + !hb_subtitle_can_force([self.subtitles.tracks[rowIndex][keySubTrackType] intValue])) { [aCell setEnabled:NO]; } @@ -779,9 +433,9 @@ NSString *keySubTrackLanguageIndex = @"keySubTrackLanguageIndex"; * b) the subtitle track can't be burned in OR * c) the subtitle track can't be passed through (e.g. PGS w/MP4) */ - int subtitleTrackType = [self.subtitleArray[rowIndex][keySubTrackType] intValue]; - if (![self.subtitleArray[rowIndex][keySubTrackSelectionIndex] intValue] || - !hb_subtitle_can_burn(subtitleTrackType) || !hb_subtitle_can_pass(subtitleTrackType, self.container)) + int subtitleTrackType = [self.subtitles.tracks[rowIndex][keySubTrackType] intValue]; + if (![self.subtitles.tracks[rowIndex][keySubTrackSelectionIndex] intValue] || + !hb_subtitle_can_burn(subtitleTrackType) || !hb_subtitle_can_pass(subtitleTrackType, self.subtitles.container)) { [aCell setEnabled:NO]; } @@ -797,8 +451,8 @@ NSString *keySubTrackLanguageIndex = @"keySubTrackLanguageIndex"; * a) the track is "None" OR * b) the subtitle track can't be passed through (e.g. PGS w/MP4) */ - if (![self.subtitleArray[rowIndex][keySubTrackSelectionIndex] intValue] || - !hb_subtitle_can_pass([self.subtitleArray[rowIndex][keySubTrackType] intValue], self.container)) + if (![self.subtitles.tracks[rowIndex][keySubTrackSelectionIndex] intValue] || + !hb_subtitle_can_pass([self.subtitles.tracks[rowIndex][keySubTrackType] intValue], self.subtitles.container)) { [aCell setEnabled:NO]; } @@ -811,7 +465,7 @@ NSString *keySubTrackLanguageIndex = @"keySubTrackLanguageIndex"; else if ([[aTableColumn identifier] isEqualToString:@"srt_lang"]) { /* We have an srt file so set the track type (Source or SRT, and the srt file path ) kvp's*/ - if ([self.subtitleArray[rowIndex][keySubTrackType] intValue] == SRTSUB) + if ([self.subtitles.tracks[rowIndex][keySubTrackType] intValue] == SRTSUB) { [aCell setEnabled:YES]; } @@ -823,7 +477,7 @@ NSString *keySubTrackLanguageIndex = @"keySubTrackLanguageIndex"; else if ([[aTableColumn identifier] isEqualToString:@"srt_charcode"]) { /* We have an srt file so set the track type (Source or SRT, and the srt file path ) kvp's*/ - if ([self.subtitleArray[rowIndex][keySubTrackType] intValue] == SRTSUB) + if ([self.subtitles.tracks[rowIndex][keySubTrackType] intValue] == SRTSUB) { [aCell setEnabled:YES]; } @@ -834,7 +488,7 @@ NSString *keySubTrackLanguageIndex = @"keySubTrackLanguageIndex"; } else if ([[aTableColumn identifier] isEqualToString:@"srt_offset"]) { - if ([self.subtitleArray[rowIndex][keySubTrackType] intValue] == SRTSUB) + if ([self.subtitles.tracks[rowIndex][keySubTrackType] intValue] == SRTSUB) { [aCell setEnabled:YES]; } @@ -884,16 +538,16 @@ NSString *keySubTrackLanguageIndex = @"keySubTrackLanguageIndex"; NSString *displayname = [importSrtFileURL lastPathComponent];// grok an appropriate display name from the srt subtitle */ /* create a dictionary of source subtitle information to store in our array */ - [self.subtitleSourceArray addObject:@{keySubTrackIndex: @(self.subtitleSourceArray.count), + [self.subtitles.masterTrackArray addObject:@{keySubTrackIndex: @(self.subtitles.masterTrackArray.count), keySubTrackName: displayname, keySubTrackType: @(SRTSUB), keySubTrackSrtFilePath: importSrtFileURL.path}]; // Now create a new srt subtitle dictionary assuming the user wants to add it to their list - NSMutableDictionary *newSubtitleSrtTrack = [self trackFromSourceTrackIndex:self.subtitleSourceArray.count - 1]; + NSMutableDictionary *newSubtitleSrtTrack = [self.subtitles trackFromSourceTrackIndex:self.subtitles.masterTrackArray.count - 1]; // Calculate the pop up selection index - newSubtitleSrtTrack[keySubTrackSelectionIndex] = @(self.subtitleSourceArray.count + (self.subtitleArray.count == 1)); - [self.subtitleArray insertObject:newSubtitleSrtTrack atIndex:self.subtitleArray.count - 1]; + newSubtitleSrtTrack[keySubTrackSelectionIndex] = @(self.subtitles.masterTrackArray.count + (self.subtitles.tracks.count == 1)); + [self.subtitles.tracks insertObject:newSubtitleSrtTrack atIndex:self.subtitles.tracks.count - 1]; [self.fTableView reloadData]; } @@ -902,22 +556,6 @@ NSString *keySubTrackLanguageIndex = @"keySubTrackLanguageIndex"; #pragma mark - UI cells -- (NSArray *)populateLanguageArray -{ - NSMutableArray *languages = [[[NSMutableArray alloc] init] autorelease]; - - for (const iso639_lang_t * lang = lang_get_next(NULL); lang != NULL; lang = lang_get_next(lang)) - { - [languages addObject:@[@(lang->eng_name), - @(lang->iso639_2)]]; - if (!strcasecmp(lang->eng_name, "English")) - { - _languagesArrayDefIndex = [languages count] - 1; - } - } - return [[languages copy] autorelease]; -} - @synthesize languagesCell = _languagesCell; - (NSPopUpButtonCell *)languagesCell @@ -931,7 +569,7 @@ NSString *keySubTrackLanguageIndex = @"keySubTrackLanguageIndex"; [_languagesCell setFont:[NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]]]; // list our languages as per the languagesArray - for (NSArray *lang in self.languagesArray) + for (NSArray *lang in self.subtitles.languagesArray) { [[_languagesCell menu] addItemWithTitle:lang[0] action:NULL keyEquivalent:@""]; } @@ -951,7 +589,7 @@ NSString *keySubTrackLanguageIndex = @"keySubTrackLanguageIndex"; [_encodingsCell setFont:[NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]]]; // list our character codes, as per charCodeArray - for (NSString *charCode in self.charCodeArray) + for (NSString *charCode in self.subtitles.charCodeArray) { [[_encodingsCell menu] addItemWithTitle:charCode action: NULL keyEquivalent: @""]; } diff --git a/macosx/HandBrake.xcodeproj/project.pbxproj b/macosx/HandBrake.xcodeproj/project.pbxproj index 6668729b4..e840a408a 100644 --- a/macosx/HandBrake.xcodeproj/project.pbxproj +++ b/macosx/HandBrake.xcodeproj/project.pbxproj @@ -112,6 +112,7 @@ 3490BCB41614CF8D002A5AD7 /* HandBrake.icns in Resources */ = {isa = PBXBuildFile; fileRef = 3490BCB31614CF8D002A5AD7 /* HandBrake.icns */; }; 46AB433515F98A2B009C0961 /* DockTextField.m in Sources */ = {isa = PBXBuildFile; fileRef = 46AB433415F98A2B009C0961 /* DockTextField.m */; }; A90A0CAF1988D57200DA65CE /* HBAudioTrackPreset.m in Sources */ = {isa = PBXBuildFile; fileRef = A90A0CAE1988D57200DA65CE /* HBAudioTrackPreset.m */; }; + A91017B41A64440A00039BFB /* HBSubtitles.m in Sources */ = {isa = PBXBuildFile; fileRef = A91017B31A64440A00039BFB /* HBSubtitles.m */; }; A91726E7197291BC00D1AFEF /* HBChapterTitlesController.m in Sources */ = {isa = PBXBuildFile; fileRef = A91726E6197291BC00D1AFEF /* HBChapterTitlesController.m */; }; A91806711A4807B000FC9BED /* HBRange.m in Sources */ = {isa = PBXBuildFile; fileRef = A91806701A4807B000FC9BED /* HBRange.m */; }; A91C024D1A16516A00DEA6F3 /* [email protected] in Resources */ = {isa = PBXBuildFile; fileRef = A91C024C1A16516A00DEA6F3 /* [email protected] */; }; @@ -136,6 +137,7 @@ A932E273198834130047D13E /* HBAudioDefaults.m in Sources */ = {isa = PBXBuildFile; fileRef = A932E272198834130047D13E /* HBAudioDefaults.m */; }; A93E0ED31972957000FD67FB /* HBVideoController.m in Sources */ = {isa = PBXBuildFile; fileRef = A93E0ED11972957000FD67FB /* HBVideoController.m */; }; A93E0ED71972958C00FD67FB /* Video.xib in Resources */ = {isa = PBXBuildFile; fileRef = A93E0ED51972958C00FD67FB /* Video.xib */; }; + A93FD4751A62ABE800A6AC43 /* HBAudio.m in Sources */ = {isa = PBXBuildFile; fileRef = A93FD4741A62ABE800A6AC43 /* HBAudio.m */; }; A9523937199A6AAE00588AEF /* HBFilters.m in Sources */ = {isa = PBXBuildFile; fileRef = A9523936199A6AAE00588AEF /* HBFilters.m */; }; A9537BF01A48A85C00141102 /* HBJob+UIAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = A9537BEF1A48A85C00141102 /* HBJob+UIAdditions.m */; }; A9537BF31A48A99500141102 /* HBVideo+UIAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = A9537BF21A48A99500141102 /* HBVideo+UIAdditions.m */; }; @@ -147,6 +149,7 @@ A967E4BA1A16768200DF1DFC /* [email protected] in Resources */ = {isa = PBXBuildFile; fileRef = A967E4B91A16768200DF1DFC /* [email protected] */; }; A971281F1A2C75180088C076 /* HBTitle.m in Sources */ = {isa = PBXBuildFile; fileRef = A971281E1A2C75180088C076 /* HBTitle.m */; }; A98C29C41977B10600AF5DED /* HBLanguagesSelection.m in Sources */ = {isa = PBXBuildFile; fileRef = A98C29C31977B10600AF5DED /* HBLanguagesSelection.m */; }; + A990D9071A64562200139032 /* HBJob+HBJobConversion.m in Sources */ = {isa = PBXBuildFile; fileRef = A990D9061A64562200139032 /* HBJob+HBJobConversion.m */; }; A9935213196F38A70069C6B7 /* ChaptersTitles.xib in Resources */ = {isa = PBXBuildFile; fileRef = A9935211196F38A70069C6B7 /* ChaptersTitles.xib */; }; A9AA447A1970664A00D7DEFC /* HBUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = A9AA44791970664A00D7DEFC /* HBUtilities.m */; }; A9BB0F2719A0ECE40079F1C1 /* HBHUDButtonCell.m in Sources */ = {isa = PBXBuildFile; fileRef = A9BB0F2619A0ECE40079F1C1 /* HBHUDButtonCell.m */; }; @@ -352,6 +355,8 @@ 46AB433415F98A2B009C0961 /* DockTextField.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DockTextField.m; sourceTree = "<group>"; }; A90A0CAD1988D57200DA65CE /* HBAudioTrackPreset.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HBAudioTrackPreset.h; sourceTree = "<group>"; }; A90A0CAE1988D57200DA65CE /* HBAudioTrackPreset.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HBAudioTrackPreset.m; sourceTree = "<group>"; }; + A91017B21A64440A00039BFB /* HBSubtitles.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HBSubtitles.h; sourceTree = "<group>"; }; + A91017B31A64440A00039BFB /* HBSubtitles.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HBSubtitles.m; sourceTree = "<group>"; }; A91726E5197291BC00D1AFEF /* HBChapterTitlesController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HBChapterTitlesController.h; sourceTree = "<group>"; }; A91726E6197291BC00D1AFEF /* HBChapterTitlesController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HBChapterTitlesController.m; sourceTree = "<group>"; }; A918066F1A4807B000FC9BED /* HBRange.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HBRange.h; sourceTree = "<group>"; }; @@ -381,6 +386,8 @@ A93E0ED01972957000FD67FB /* HBVideoController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HBVideoController.h; sourceTree = "<group>"; }; A93E0ED11972957000FD67FB /* HBVideoController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HBVideoController.m; sourceTree = "<group>"; }; A93E0ED61972958C00FD67FB /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = Video.xib; sourceTree = "<group>"; }; + A93FD4731A62ABE800A6AC43 /* HBAudio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HBAudio.h; sourceTree = "<group>"; }; + A93FD4741A62ABE800A6AC43 /* HBAudio.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HBAudio.m; sourceTree = "<group>"; }; A9523935199A6AAE00588AEF /* HBFilters.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HBFilters.h; sourceTree = "<group>"; }; A9523936199A6AAE00588AEF /* HBFilters.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HBFilters.m; sourceTree = "<group>"; }; A9537BEE1A48A85C00141102 /* HBJob+UIAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "HBJob+UIAdditions.h"; sourceTree = "<group>"; }; @@ -399,6 +406,8 @@ A971281E1A2C75180088C076 /* HBTitle.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HBTitle.m; sourceTree = "<group>"; }; A98C29C21977B10600AF5DED /* HBLanguagesSelection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HBLanguagesSelection.h; sourceTree = "<group>"; }; A98C29C31977B10600AF5DED /* HBLanguagesSelection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HBLanguagesSelection.m; sourceTree = "<group>"; }; + A990D9051A64562200139032 /* HBJob+HBJobConversion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "HBJob+HBJobConversion.h"; sourceTree = "<group>"; }; + A990D9061A64562200139032 /* HBJob+HBJobConversion.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "HBJob+HBJobConversion.m"; sourceTree = "<group>"; }; A9935212196F38A70069C6B7 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = ChaptersTitles.xib; sourceTree = "<group>"; }; A997D8EB1A4ABB0900E19B6F /* HBPresetCoding.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HBPresetCoding.h; sourceTree = "<group>"; }; A9A2A77F1A4737DD006C219C /* NSCodingMacro.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NSCodingMacro.h; sourceTree = "<group>"; }; @@ -841,6 +850,17 @@ name = "Products (external)"; sourceTree = "<group>"; }; + A91017B51A64441700039BFB /* Subtitles */ = { + isa = PBXGroup; + children = ( + A91017B21A64440A00039BFB /* HBSubtitles.h */, + A91017B31A64440A00039BFB /* HBSubtitles.m */, + A9F4728B1976BAA70009EC65 /* HBSubtitlesDefaults.h */, + A9F4728C1976BAA70009EC65 /* HBSubtitlesDefaults.m */, + ); + name = Subtitles; + sourceTree = "<group>"; + }; A932E270198833960047D13E /* Audio Defaults */ = { isa = PBXGroup; children = ( @@ -889,6 +909,8 @@ A971281E1A2C75180088C076 /* HBTitle.m */, A9DEC87D1A23DF6F00C79B48 /* HBJob.h */, A9DEC87E1A23DF6F00C79B48 /* HBJob.m */, + A990D9051A64562200139032 /* HBJob+HBJobConversion.h */, + A990D9061A64562200139032 /* HBJob+HBJobConversion.m */, A918066F1A4807B000FC9BED /* HBRange.h */, A91806701A4807B000FC9BED /* HBRange.m */, A9DEC8751A23C88D00C79B48 /* HBVideo.h */, @@ -897,25 +919,34 @@ A9DEC8791A23C89E00C79B48 /* HBPicture.m */, A9523935199A6AAE00588AEF /* HBFilters.h */, A9523936199A6AAE00588AEF /* HBFilters.m */, - A932E271198834130047D13E /* HBAudioDefaults.h */, - A932E272198834130047D13E /* HBAudioDefaults.m */, - A9F4728B1976BAA70009EC65 /* HBSubtitlesDefaults.h */, - A9F4728C1976BAA70009EC65 /* HBSubtitlesDefaults.m */, - 273F209114ADBE670021BE6D /* HBAudioTrack.h */, - 273F209214ADBE670021BE6D /* HBAudioTrack.m */, - A90A0CAD1988D57200DA65CE /* HBAudioTrackPreset.h */, - A90A0CAE1988D57200DA65CE /* HBAudioTrackPreset.m */, + A996B0F81A62C51C00B64179 /* Audio */, + A91017B51A64441700039BFB /* Subtitles */, + A9537BED1A48A7F900141102 /* UI Bindings Additions */, A9AA447D1970729300D7DEFC /* HBPreviewGenerator.h */, A9D1E41618262364002F6424 /* HBPreviewGenerator.m */, 273F209714ADBE670021BE6D /* HBDVDDetector.h */, 273F209814ADBE670021BE6D /* HBDVDDetector.m */, - A9537BED1A48A7F900141102 /* UI Bindings Additions */, A9A2A77F1A4737DD006C219C /* NSCodingMacro.h */, A997D8EB1A4ABB0900E19B6F /* HBPresetCoding.h */, ); name = Core; sourceTree = "<group>"; }; + A996B0F81A62C51C00B64179 /* Audio */ = { + isa = PBXGroup; + children = ( + A93FD4731A62ABE800A6AC43 /* HBAudio.h */, + A93FD4741A62ABE800A6AC43 /* HBAudio.m */, + A932E271198834130047D13E /* HBAudioDefaults.h */, + A932E272198834130047D13E /* HBAudioDefaults.m */, + 273F209114ADBE670021BE6D /* HBAudioTrack.h */, + 273F209214ADBE670021BE6D /* HBAudioTrack.m */, + A90A0CAD1988D57200DA65CE /* HBAudioTrackPreset.h */, + A90A0CAE1988D57200DA65CE /* HBAudioTrackPreset.m */, + ); + name = Audio; + sourceTree = "<group>"; + }; A9B34D6F197683FE00871B7D /* Controllers */ = { isa = PBXGroup; children = ( @@ -1197,6 +1228,7 @@ A9537BF61A48AB6300141102 /* HBPicture+UIAdditions.m in Sources */, 273F20B314ADBE670021BE6D /* HBOutputPanelController.m in Sources */, 273F20B414ADBE670021BE6D /* HBOutputRedirect.m in Sources */, + A93FD4751A62ABE800A6AC43 /* HBAudio.m in Sources */, A971281F1A2C75180088C076 /* HBTitle.m in Sources */, 273F20B514ADBE670021BE6D /* HBPreferencesController.m in Sources */, A9DC6C52196F04F6002AE6B4 /* HBSubtitlesController.m in Sources */, @@ -1207,9 +1239,11 @@ A93E0ED31972957000FD67FB /* HBVideoController.m in Sources */, 273F20B614ADBE670021BE6D /* HBPresetsManager.m in Sources */, 273F20B714ADBE670021BE6D /* HBPreviewController.m in Sources */, + A990D9071A64562200139032 /* HBJob+HBJobConversion.m in Sources */, A9D1E41718262364002F6424 /* HBPreviewGenerator.m in Sources */, A90A0CAF1988D57200DA65CE /* HBAudioTrackPreset.m in Sources */, 273F20B814ADBE670021BE6D /* HBQueueController.mm in Sources */, + A91017B41A64440A00039BFB /* HBSubtitles.m in Sources */, 273F20BA14ADBE670021BE6D /* HBPictureController.m in Sources */, A9CF25F71990D6820023F727 /* HBPresetsViewController.m in Sources */, A9537BF91A48AC9000141102 /* HBFilters+UIAdditions.m in Sources */, |