我已经预见到这部分会比较长,因此切分为上下两篇。
现在我们回到main函数调用void writeSubtitles(NSString *inputPath, NSString *outputPath, NSArray *subtitlesTextPaths)
首先从视频地址创建AVAsset,再创建对其的读取器AVAssetReader。这部分是为读取原视频信息做准备的。
NSError *error;
// Setup the asset reader and writer
AVAsset *asset = [AVAsset assetWithURL:[NSURL fileURLWithPath:[inputPath stringByExpandingTildeInPath]]];
AVAssetReader *assetReader = [AVAssetReader assetReaderWithAsset:asset error:&error];
if (error)
{
NSLog(@"error creating asset reader. exiting: %@", error);
return;
}
NSURL *outputURL = [NSURL fileURLWithPath:[outputPath stringByExpandingTildeInPath]];
AVAssetWriter *assetWriter = [AVAssetWriter assetWriterWithURL:outputURL fileType:AVFileTypeQuickTimeMovie error:&error];
if (error)
{
NSLog(@"error creating asset writer. exiting: %@", error);
return;
}
// Copy metadata from the asset to the asset writer
NSMutableArray *assetMetadata = [NSMutableArray array];
for (NSString *metadataFormat in asset.availableMetadataFormats)
[assetMetadata addObjectsFromArray:[asset metadataForFormat:metadataFormat]];
assetWriter.metadata = assetMetadata;
assetWriter.shouldOptimizeForNetworkUse = YES;
接着将原视频里的track读出作为写入的源。
// Build up inputs and outputs for the reader and writer to carry over the tracks from the input movie into the new movie
NSMutableDictionary *assetWriterInputsCorrespondingToOriginalTrackIDs = [NSMutableDictionary dictionary];
NSMutableArray *inputsOutputs = [NSMutableArray array];
for (AVAssetTrack *track in asset.tracks)
{
NSString *mediaType = track.mediaType;
// Make the reader
AVAssetReaderTrackOutput *trackOutput = [AVAssetReaderTrackOutput assetReaderTrackOutputWithTrack:track outputSettings:nil];
[assetReader addOutput:trackOutput];
// Make the writer input, using a source format hint if a format description is available
AVAssetWriterInput *input;
CMFormatDescriptionRef formatDescription = CFBridgingRetain([track.formatDescriptions firstObject]);
if (formatDescription)
{
input = [AVAssetWriterInput assetWriterInputWithMediaType:mediaType outputSettings:nil sourceFormatHint:formatDescription];
CFRelease(formatDescription);
}
else
{
NSLog(@"skipping track on the assumption that there is no media data to carry over");
continue;
}
// Carry over language code
input.languageCode = track.languageCode;
input.extendedLanguageTag = track.extendedLanguageTag;
// Copy metadata from the asset track to the asset writer input
NSMutableArray *trackMetadata = [NSMutableArray array];
for (NSString *metadataFormat in track.availableMetadataFormats)
[trackMetadata addObjectsFromArray:[track metadataForFormat:metadataFormat]];
input.metadata = trackMetadata;
// Add the input, if that's okay to do
if ([assetWriter canAddInput:input])
{
[assetWriter addInput:input];
// Store the input and output to be used later when actually writing out the new movie
[inputsOutputs addObject:@{@"input" : input, @"output" : trackOutput}];
// Track inputs corresponsing to track IDs for later preservation of track groups
assetWriterInputsCorrespondingToOriginalTrackIDs[@(track.trackID)] = input;
}
else
{
NSLog(@"skipping input because it cannot be added to the asset writer");
}
}
写入有点麻烦,要将track的MediaType,sourceFormatHint也就是track的格式描述传给AVAssetWriterInput。这里我们只是构造了一下,还没add给写入器,因为我们的准备工作还没做完。
// Carry over language code
input.languageCode = track.languageCode;
input.extendedLanguageTag = track.extendedLanguageTag;
拷贝language code和extendedLanguageTag
// Copy metadata from the asset track to the asset writer input
NSMutableArray *trackMetadata = [NSMutableArray array];
for (NSString *metadataFormat in track.availableMetadataFormats)
[trackMetadata addObjectsFromArray:[track metadataForFormat:metadataFormat]];
input.metadata = trackMetadata;
可以将input add到写入器了
// Add the input, if that's okay to do
if ([assetWriter canAddInput:input])
{
[assetWriter addInput:input];
// Store the input and output to be used later when actually writing out the new movie
[inputsOutputs addObject:@{@"input" : input, @"output" : trackOutput}];
// Track inputs corresponsing to track IDs for later preservation of track groups
assetWriterInputsCorrespondingToOriginalTrackIDs[@(track.trackID)] = input;
}
else
{
NSLog(@"skipping input because it cannot be added to the asset writer");
}
// Setup the inputs and outputs for new subtitle tracks
NSMutableArray *newSubtitlesInputs = [NSMutableArray array];
NSMutableArray *subtitlesInputsOutputs = [NSMutableArray array];
for (NSString *subtitlesPath in subtitlesTextPaths)
{
// Read the contents of the subtitles file
NSString *text = [NSString stringWithContentsOfURL:[NSURL fileURLWithPath:subtitlesPath] encoding:NSUTF8StringEncoding error:&error];
if (!text)
{
NSLog(@"there was a problem reading a subtitles file: %@", error);
continue;
}
// Make the subtitles reader
SubtitlesTextReader *subtitlesTextReader = [SubtitlesTextReader subtitlesTextReaderWithText:text];
// Make the writer input, using a source format hint if a format description is available
AVAssetWriterInput *subtitlesInput;
CMFormatDescriptionRef formatDescription = [subtitlesTextReader copyFormatDescription];
if (formatDescription)
{
subtitlesInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeSubtitle outputSettings:nil sourceFormatHint:formatDescription];
CFRelease(formatDescription);
}
else
{
NSLog(@"skipping subtitles reader on the assumption that there is no media data to carry over");
continue;
}
subtitlesInput.languageCode = subtitlesTextReader.languageCode;
subtitlesInput.extendedLanguageTag = subtitlesTextReader.extendedLanguageTag;
subtitlesInput.metadata = subtitlesTextReader.metadata;
if ([assetWriter canAddInput:subtitlesInput])
{
[assetWriter addInput:subtitlesInput];
// Store the input and output to be used later when actually writing out the new movie
[subtitlesInputsOutputs addObject:@{@"input" : subtitlesInput, @"output" : subtitlesTextReader}];
[newSubtitlesInputs addObject:subtitlesInput];
}
else
{
NSLog(@"skipping subtitles input because it cannot be added to the asset writer");
}
}