avsubtitleWriter demo解析(四):writeSubtitles下篇

来源:互联网 发布:软件硬件英语 编辑:程序博客网 时间:2024/05/02 01:04

接着上篇。

首先,保存原视频的track group,AVAssetTrackGroup是一组track,同时只能播放其中一条track,但是不同的AVAssetTrackGroup中的track可以同时播放。通过asset.trackGroups可以获取某个asset的所有trackGroup。通过检查某个trackGroup的trackIDs,你可以知道哪些track是一个group的。前面我们已经把每条track和对应的trackID放到字典assetWriterInputsCorrespondingToOriginalTrackIDs中,这里就可以派上用场了。每个group要设置一个默认的track,通过trackID获取asset的某条track,然后检查其enabled属性,yes就是默认track,no则不是。

接着检查有没有legible group,可以通过TrackID来获取对应的track,然后使用hasMediaCharacteristic:AVMediaCharacteristicLegible检查track是否是legible类型。如果有,就把subtitlesInput加入iputs数组。接着创建AVAssetWriterInputGroup,把inputs数组和defaultInput指定给AVAssetWriterInputGroup便利构造器。我们回过头来捋一下思路,iputs数组里有某个track group的track对应的AVAssetWriterInput,如果该group是音视频的,很简单,我们单纯从assetWriterInputsCorrespondingToOriginalTrackIDs取出来AVAssetWriterInput加入iputs数组。如果是legible的,那么我们把我们新建的字幕(组,可能有多个字幕)AVAssetWriterInput(s,表明可能是个数组)加入该数组。然后将group加到assetWriter。

// Preserve track groups from the original assetBOOL groupedSubtitles = NO;for (AVAssetTrackGroup *trackGroup in asset.trackGroups){// Collect the inputs that correspond to the group's track IDs in an arrayNSMutableArray *inputs = [NSMutableArray array];AVAssetWriterInput *defaultInput;for (NSNumber *trackID in trackGroup.trackIDs){AVAssetWriterInput *input = assetWriterInputsCorrespondingToOriginalTrackIDs[trackID];if (input)[inputs addObject:input];// Determine which of the inputs is the default according to the enabled state of the corresponding tracksif (!defaultInput && [asset trackWithTrackID:(CMPersistentTrackID)[trackID intValue]].enabled)defaultInput = input;}// See if this is a legible (all of the tracks have characteristic AVMediaCharacteristicLegible), and group the new subtitle tracks with it if soBOOL isLegibleGroup = NO;for (NSNumber *trackID in trackGroup.trackIDs){if ([[asset trackWithTrackID:(CMPersistentTrackID)[trackID intValue]] hasMediaCharacteristic:AVMediaCharacteristicLegible]){isLegibleGroup = YES;}else if (isLegibleGroup){isLegibleGroup = NO;break;}}// If it is a legible group, add the new subtitles to this groupif (!groupedSubtitles && isLegibleGroup){[inputs addObjectsFromArray:newSubtitlesInputs];groupedSubtitles = YES;}AVAssetWriterInputGroup *inputGroup = [AVAssetWriterInputGroup assetWriterInputGroupWithInputs:inputs defaultInput:defaultInput];if ([assetWriter canAddInputGroup:inputGroup]){[assetWriter addInputGroup:inputGroup];}else{NSLog(@"cannot add asset writer group");}}

如果原视频没有legible group,就使用我们的AVAssetWriterInput(s)创建一个。

// If no legible group was found to add the new subtitles to, create a group for them (if there are any)if (!groupedSubtitles && (newSubtitlesInputs.count > 0)){AVAssetWriterInputGroup *inputGroup = [AVAssetWriterInputGroup assetWriterInputGroupWithInputs:newSubtitlesInputs defaultInput:nil];if ([assetWriter canAddInputGroup:inputGroup]){[assetWriter addInputGroup:inputGroup];}else{NSLog(@"cannot add asset writer group");}}

保存原视频的track引用,这里是为track添加相关联的track。譬如说我切换音轨,对应的字幕也发生切换,那么这里就使用到了关联track。

先使用tracks属性遍历asset的track,然后使用availableTrackAssociationTypes属性遍历某条track的关联track的类型。这边需要注意的是,在av中一般都是以类型来区分的,虽然也有ID类型,因为很多都是数组的,所以使用类型会比较有意义。因此我们接着遍历关联track类型数组,根据类型,使用associatedTracksOfType方法从该track中取到关联的track(s)。然后我们建立一个associatedTrackIDs数组来存放这些tracks对应的id,然后将这个数组和对应的类型存入trackReferencesForTrack字典中。这样下来,我们就获得了某条track对应的关联tracks的IDs。一个asset有很多条track,因此最后我们把某条track的ID和其关联track的ID数组存入trackReferencesCorrespondingToOriginalTrackIDs字典。当然这样我们只完成了ID的对应关系。接着我们要把ID转化为AVAssetWriterInput。我们最终的目的是要把referencingInput加上关联的referencedInput,使用addTrackAssociationWithTrackOfInput:type:方法。感觉不会再爱了~~~~~

// Preserve track references from original assetNSMutableDictionary *trackReferencesCorrespondingToOriginalTrackIDs = [NSMutableDictionary dictionary];for (AVAssetTrack *track in asset.tracks){NSMutableDictionary *trackReferencesForTrack = [NSMutableDictionary dictionary];NSMutableSet *availableTrackAssociatonTypes = [NSMutableSet setWithArray:track.availableTrackAssociationTypes];for (NSString *trackAssociationType in availableTrackAssociatonTypes){NSArray *associatedTracks = [track associatedTracksOfType:trackAssociationType];if (associatedTracks.count > 0){NSMutableArray *associatedTrackIDs = [NSMutableArray arrayWithCapacity:associatedTracks.count];for (AVAssetTrack *associatedTrack in associatedTracks){[associatedTrackIDs addObject:@(associatedTrack.trackID)];}trackReferencesForTrack[trackAssociationType] = associatedTrackIDs;}}trackReferencesCorrespondingToOriginalTrackIDs[@(track.trackID)] = trackReferencesForTrack;}for (NSNumber *referencingTrackIDKey in trackReferencesCorrespondingToOriginalTrackIDs){AVAssetWriterInput *referencingInput = assetWriterInputsCorrespondingToOriginalTrackIDs[referencingTrackIDKey];NSDictionary *trackReferences = trackReferencesCorrespondingToOriginalTrackIDs[referencingTrackIDKey];for (NSString *trackReferenceTypeKey in trackReferences){NSArray *referencedTrackIDs = trackReferences[trackReferenceTypeKey];for (NSNumber *thisReferencedTrackID in referencedTrackIDs){AVAssetWriterInput *referencedInput = assetWriterInputsCorrespondingToOriginalTrackIDs[thisReferencedTrackID];if (referencingInput && referencedInput && [referencingInput canAddTrackAssociationWithTrackOfInput:referencedInput type:trackReferenceTypeKey])[referencingInput addTrackAssociationWithTrackOfInput:referencedInput type:trackReferenceTypeKey];}}}

写入原视频的内容。这是启动assetWriter,告诉它可以写入output了。startSessionAtSourceTime有待了解。接着叫assetReader可以开始读取。现在我们要处理的资源都在字典数组inputsOutputs中。我们分别取出:

AVAssetWriterInput *input = inputOutput[@"input"];
AVAssetReaderTrackOutput *assetReaderTrackOutput = inputOutput[@"output"];

现在让AVAssetWriterInput去请求media Data,当请求成功就处理。这时候我们检查AVAssetWriterInput的状态是不是isReadyForMoreMediaData。如果是,我们就从assetReaderTrackOutput 调用copyNextSampleBuffer获取一个sampleBuffer。获取成功,就让AVAssetWriterInput完成拼接操作。如果获取不到,我们就标记AVAssetWriterInput为完成。检查assetReader的状态看看是完成还是失败还是等待什么。

// Write the movieif ([assetWriter startWriting]){[assetWriter startSessionAtSourceTime:kCMTimeZero];dispatch_group_t dispatchGroup = dispatch_group_create();[assetReader startReading];// Write samples from AVAssetReaderTrackOutputsfor (NSDictionary *inputOutput in inputsOutputs){dispatch_group_enter(dispatchGroup);dispatch_queue_t requestMediaDataQueue = dispatch_queue_create("request media data", DISPATCH_QUEUE_SERIAL);AVAssetWriterInput *input = inputOutput[@"input"];AVAssetReaderTrackOutput *assetReaderTrackOutput = inputOutput[@"output"];[input requestMediaDataWhenReadyOnQueue:requestMediaDataQueue usingBlock:^{while ([input isReadyForMoreMediaData]){CMSampleBufferRef nextSampleBuffer = [assetReaderTrackOutput copyNextSampleBuffer];if (nextSampleBuffer){[input appendSampleBuffer:nextSampleBuffer];CFRelease(nextSampleBuffer);}else{[input markAsFinished];dispatch_group_leave(dispatchGroup);if (assetReader.status == AVAssetReaderStatusFailed)NSLog(@"the reader failed: %@", assetReader.error);break;}}}];}

写入字幕的buffer

// Write samples from SubtitlesTextReadersfor (NSDictionary *subtitlesInputOutput in subtitlesInputsOutputs){dispatch_group_enter(dispatchGroup);dispatch_queue_t requestMediaDataQueue = dispatch_queue_create("request media data", DISPATCH_QUEUE_SERIAL);AVAssetWriterInput *input = subtitlesInputOutput[@"input"];SubtitlesTextReader *subtitlesTextReader = subtitlesInputOutput[@"output"];[input requestMediaDataWhenReadyOnQueue:requestMediaDataQueue usingBlock:^{while ([input isReadyForMoreMediaData]){CMSampleBufferRef nextSampleBuffer = [subtitlesTextReader copyNextSampleBuffer];if (nextSampleBuffer){[input appendSampleBuffer:nextSampleBuffer];CFRelease(nextSampleBuffer);}else{[input markAsFinished];dispatch_group_leave(dispatchGroup);break;}}}];}

完成一些后续操作,这边我们完成了读取操作,因此我们取消assetReader的读取。接着assetWriter进行真正写入的操作,并调用完成处理,我们检查assetWriter的状态,看是成功了还是失败了。这边读写是繁重的任务,采用了dispatch_group_enter、dispatch_group_wait对来使用GCD,提高性能。

dispatch_group_wait(dispatchGroup, DISPATCH_TIME_FOREVER);[assetReader cancelReading];dispatch_group_enter(dispatchGroup);[assetWriter finishWritingWithCompletionHandler:^(void) {if (AVAssetWriterStatusCompleted == assetWriter.status)NSLog(@"writing success to %@", assetWriter.outputURL);else if (AVAssetWriterStatusFailed == assetWriter.status)NSLog (@"writer failed with error: %@", assetWriter.error);dispatch_group_leave(dispatchGroup);}];dispatch_group_wait(dispatchGroup, DISPATCH_TIME_FOREVER);

整个系列完成。第四篇还只是个毛坯,需要添加润色。

0 0
原创粉丝点击