avsubtitleWriter demo解析(一):字幕解析

来源:互联网 发布:用qq群推广淘宝客 编辑:程序博客网 时间:2024/05/22 15:33

这是苹果官方的subtitleWriter demo的系列教程。目前关于这部分的材料还不是很多,特地分析源码了解下。

demo下载地址:

https://developer.apple.com/library/mac/samplecode/avsubtitleswriterOSX/Listings/avsubtitleswriter_SubtitlesTextReader_m.html


这是第一篇,字幕解析。

首先引入一个subtitle类,这个类用于从字幕文件中提取字幕信息,并存取。

@interface Subtitle : NSObject+ (instancetype)subtitleWithText:(NSString *)text timeRange:(CMTimeRange)timeRange forced:(BOOL)forced;- (CMFormatDescriptionRef)copyFormatDescription;- (CMSampleBufferRef)copySampleBuffer;@property NSString *text;@property CMTimeRange timeRange;@property BOOL forced;@property CMTextDisplayFlags displayFlags;@end

第一个方法是会在SubtitlesTextReader 调用初始化方法initWithText中被调用,从字幕文件中解析出各部分,然后据此构造字幕对象。

第二第三个方法先留着,暂时不用。


initWithText中解析必要的信息

NSMutableArray *mutableSubtitles = [NSMutableArray array];// Check for a languageNSRegularExpression *languageExpression = [NSRegularExpression regularExpressionWithPattern:@"language: (.*)" options:0 error:nil];NSTextCheckingResult *languageResult = [languageExpression firstMatchInString:text options:0 range:NSMakeRange(0, [text length])];_languageCode = [text substringWithRange:[languageResult rangeAtIndex:1]];// Check for an extended languageNSRegularExpression *extendedLanguageExpression = [NSRegularExpression regularExpressionWithPattern:@"extended language: (.*)" options:0 error:nil];NSTextCheckingResult *extendedLanguageResult = [extendedLanguageExpression firstMatchInString:text options:0 range:NSMakeRange(0, [text length])];_extendedLanguageTag = [text substringWithRange:[extendedLanguageResult rangeAtIndex:1]];// See if SDH has been requestedNSRegularExpression *characteristicsExpression = [NSRegularExpression regularExpressionWithPattern:@"characteristics:.*(SDH)" options:NSRegularExpressionCaseInsensitive error:nil];NSTextCheckingResult *characteristicsResult = [characteristicsExpression firstMatchInString:text options:0 range:NSMakeRange(0, [text length])];_wantsSDH = ([[text substringWithRange:[characteristicsResult rangeAtIndex:1]] caseInsensitiveCompare:@"SDH"] == NSOrderedSame) ? YES : NO;

这边的做法是有待商榷的,你不可能简单从文本去解析字幕的语言,可能需要识别,当然规范的情况下是可以这么做的。

这边解析出三个内容,一是语言代码,二是扩展语言标签,三是SDH标记。你可能不理解第二和第三,第二大概是辅助语言的意思,譬如一部电影,可能对白基本上是英语,但是偶尔出现点法语什么的,那么法语就算是extended language了。至于SDH标记,subtitle for deaf and hard of hearing 听力障碍提示字幕,具体不大清楚,可能就是不固定在下方的一些提示性的字幕吧。

当然这部分不是我们的重点,因为它可能并没多少实际的用处。

下面是解析字幕,是重点,也是有实际意义的。

// Find the subtitle time ranges and text__block int forcedCount = 0;NSRegularExpression *subtitlesExpression = [NSRegularExpression regularExpressionWithPattern:@"(..):(..):(..),(...) --> (..):(..):(..),(...)( !!!)?\n(.*)" options:0 error:nil];[subtitlesExpression enumerateMatchesInString:text options:0 range:NSMakeRange(0, [text length]) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {// Get the textNSString *subtitleText = [text substringWithRange:[result rangeAtIndex:10]];// Create the time rangedouble startTime = ([[text substringWithRange:[result rangeAtIndex:1]] doubleValue] * 60.0 * 60.0) + ([[text substringWithRange:[result rangeAtIndex:2]] doubleValue] * 60.0) + [[text substringWithRange:[result rangeAtIndex:3]] doubleValue] + ([[text substringWithRange:[result rangeAtIndex:4]] doubleValue] / 1000.0);double endTime = ([[text substringWithRange:[result rangeAtIndex:5]] doubleValue] * 60.0 * 60.0) + ([[text substringWithRange:[result rangeAtIndex:6]] doubleValue] * 60.0) + [[text substringWithRange:[result rangeAtIndex:7]] doubleValue] + ([[text substringWithRange:[result rangeAtIndex:8]] doubleValue] / 1000.0);CMTimeRange timeRange = CMTimeRangeMake(CMTimeMakeWithSeconds(startTime, 600), CMTimeMakeWithSeconds(endTime - startTime, 600));// Is it forced?BOOL forced = NO;if (([result rangeAtIndex:9].length > 0) && [[text substringWithRange:[result rangeAtIndex:9]] isEqualToString:@" !!!"]){forced = YES;forcedCount++;}// Stash a Subtitle object for later use by -copyNextSampleBuffer[mutableSubtitles addObject:[Subtitle subtitleWithText:subtitleText timeRange:timeRange forced:forced]];}];

这里使用了正则表达式,其中

NSRegularExpression *subtitlesExpression = [NSRegularExpression regularExpressionWithPattern:@"(..):(..):(..),(...) --> (..):(..):(..),(...)( !!!)?\n(.*)" options:0 error:nil];

这一句是我们要匹配的表达式,代表了一行时间轴以及对应的字幕。每个括号代表一个子表达式。

接着,我们处理子表达式

[subtitlesExpression enumerateMatchesInString:text options:0 range:NSMakeRange(0, [text length]) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {}];

这个方法是获取一条字幕以时间轴,再在block中进行子查询。所有子表达式的结果存放在result中,它其实可以认为是个NSRange数组。使用rangeatindex取出每个子表达式匹配的结果,结果是一个nsrange,反应了它在字符串中的位置和长度。值得注意的是,index 0代表的是整条正则的匹配结果,从1开始才是子表达式的匹配结果。

在本例中,前面四个括号分别对应index 1,2,3,4,表示时,分,秒,毫秒,其中秒和毫秒之间用逗号分隔。可以据此计算出开始的时间,以秒为单位。同理也能计算出结束时间。下标是9的是字符串“!!!”或者没有,前者表示该字幕是强制字幕,后者表示没有该属性。强制字幕就是即使你切换也会强制显示的字幕。下标10是我们的字幕。

最后调用下面的方法打包。

+ (instancetype)subtitleWithText:(NSString *)text timeRange:(CMTimeRange)timeRange forced:(BOOL)forced;

接下来是设置字幕显示标志,也就是是否强制,单独放出来,是因为要做整体性的判断。

// Set forced subtitles display flags as appropriate.if ([mutableSubtitles count] == forcedCount){for (Subtitle *subtitle in mutableSubtitles){subtitle.displayFlags = kCMTextDisplayFlag_forcedSubtitlesPresent | kCMTextDisplayFlag_allSubtitlesForced;}}else if (forcedCount > 0){for (Subtitle *subtitle in mutableSubtitles){subtitle.displayFlags = kCMTextDisplayFlag_forcedSubtitlesPresent;}}_subtitles = [mutableSubtitles copy];

这边,如果每条字幕都是强制的,那么我们设置显示标记为kCMTextDisplayFlag_forcedSubtitlesPresent | kCMTextDisplayFlag_allSubtitlesForced,意思是所有字母都强制或者根据每条字幕自身属性而定。如果不是所有字幕都是强制的,那么设置为kCMTextDisplayFlag_forcedSubtitlesPresent,表明依据每条字幕的实际情况选择是否强制。现在我们的SubtitlesTextReader 已经拥有了一个字幕数组。

1 0
原创粉丝点击