avsubtitleWriter demo解析(四):writeSubtitles上篇

我已经预见到这部分会比较长,因此切分为上下两篇。


现在我们回到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;
	}

同样地,根据输出地址创建AVAsset和对应的写入器AVAssetWriter。这部分是为写入新视频做准备的。

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;
	}

接着,拷贝原视频的metaData给写入器

// 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;

这边的思路是枚举asset的metadataFormats,这里返回NSString,再根据这个NSString获取AVMetadataItem。


接着将原视频里的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传给AVAssetReaderTrackOutput就行,这个便利构造器,产生一个AVAssetReaderTrackOutput。

写入有点麻烦,要将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;

还要拷贝track的metaData

可以将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");
		}

接着为字幕构造input

// 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");
		}
	}

上篇先到这儿,有些内容以后补充。

你可能感兴趣的:(Mac开发)