summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--libhb/dvdnav.c9
-rw-r--r--macosx/Controller.m16
-rw-r--r--macosx/English.lproj/Audio.xib20
-rw-r--r--macosx/English.lproj/Subtitles.xib16
-rw-r--r--macosx/HBAudio.h43
-rw-r--r--macosx/HBAudio.m430
-rw-r--r--macosx/HBAudioController.h36
-rw-r--r--macosx/HBAudioController.m434
-rw-r--r--macosx/HBAudioTrack.h4
-rw-r--r--macosx/HBCore.m1
-rw-r--r--macosx/HBJob+HBJobConversion.h18
-rw-r--r--macosx/HBJob+HBJobConversion.m522
-rw-r--r--macosx/HBJob.h35
-rw-r--r--macosx/HBJob.m565
-rw-r--r--macosx/HBPicture.m2
-rw-r--r--macosx/HBPreviewGenerator.m1
-rw-r--r--macosx/HBQueueController.mm55
-rw-r--r--macosx/HBSubtitles.h65
-rw-r--r--macosx/HBSubtitles.m497
-rw-r--r--macosx/HBSubtitlesController.h21
-rw-r--r--macosx/HBSubtitlesController.m504
-rw-r--r--macosx/HandBrake.xcodeproj/project.pbxproj52
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 */,