summaryrefslogtreecommitdiffstats
path: root/macosx/HBSubtitles.m
diff options
context:
space:
mode:
authorritsuka <[email protected]>2015-01-13 08:08:04 +0000
committerritsuka <[email protected]>2015-01-13 08:08:04 +0000
commit677231f07d765e79afdc76e7741bb9e03bde1142 (patch)
treeb98775bb09649710c9b80b6370c8d261ce778cf9 /macosx/HBSubtitles.m
parent40edf3c9bce9f91fa3b5d8f422ad5df1e8ad3eb6 (diff)
MacGui: Move the audio/subtitles selection logic out of the view controllers. Now it's possible to create a HBJob and apply a preset to without the UI classes help.
git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@6741 b64f7644-9d1e-0410-96f1-a4d463321fa5
Diffstat (limited to 'macosx/HBSubtitles.m')
-rw-r--r--macosx/HBSubtitles.m497
1 files changed, 497 insertions, 0 deletions
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