AVF 9 - AVAssetReader & Writer
一、说明
参考:https://blog.csdn.net/u014084081/article/details/73738871?locationNum=3&fps=1
1、AVAssetReader
AVAssetReader用于从AVAssert实例中读取媒体样本。每个AVAssetReader对象在某个时刻只能和单个asset关联,但这个asset可包含多个tracks。
所以,在开始读取之前,必须给asset reader指定一个AVAssetReaderOutput 的具体子类,来配置media data怎样读取。
可通过copyNextSampleBuffer方法可以访问音频样本和视频帧。
AVAssetReaderOutput是一个抽象类,其有3个具体子类:
- AVAssetReaderTrackOutput从指定的AVAssetTrack中读取解码的媒体样本
- AVAssetReaderAudioMixOutput从多音频轨道中读取混合输出
- AVAssetReaderVideoCompositionOutput从多视频轨道中读取组合输出
AVAssetReader
只针对带有一个资源的媒体样本。如果需要同时从多个基于文件的资源中读取样本,可将它们组合到一个AVAsset
子类AVComposition
中
在创建asset reader之后,至少设置一个output来接收读取的媒体数据。在设置outputs时,要确保设置alwaysCopiesSampleData为NO,这样的话,有改进性能的好处。
如果你仅仅想从一个或多个tracks中读取媒体数据,并可能会把数据转换成另一种格式,就使用AVAssetReaderTrackOutput类,为你想要从asset中读取的每个AVAssetTrack对象,使用一个单独的track ouput对象。
使用asset reader将audio track解压缩为Linear PCM,按如下方式设置track output
2、AVAssetWriter
AVAssetWriter 类将媒体数据从 多个源写入指定文件格式的单个文件。
不需要将asset writer对象与特定的asset相关联,但必须为要创建的每个输出文件使用单独的asset writer。由于asset writer可以从多个源写入媒体数据,因此必须要为写入文件的每个track创建一个AVAssetWriterInput对象,每个AVAssetWriterInput期望以CMSampleBufferRef对象形式接收数据,但如果你想要将CVPixelBufferRef类型对象添加到asset writer input,就使用AVAssetWriterInputPixelBufferAdaptor类。
AVAssetWriter用于对媒体资源进行编码并将其写入到容器文件中,比如一个MPEG-4文件或QuickTime文件。它由一个或多个AVAssetWriterInput对象配置,用于附加将包含要写入容器的媒体样本的CMSampleBufferRef对象。AVAssetWriterInput被配置为可以处理指定的媒体类型,比如音频或视频,并且附加在其后的样本会在最终输出时生成一个独立的AVAssetTrack。当使用一个配置了处理视频样本AVAssetWriterInput时,开发者会经常用到一个专门的适配器对象AVAssetWriterInputPixelBufferAdaptor。这个类在附加被包装为CVPixelBufferRef对象的视频样本是提供最优性能。输入信息也可以通过使用AVAssetWriterInputGroup组成互斥的参数。这就让开发者能够创建特定资源,其中包含在播放时使用AVMediaSelectionGroup和AVMediaSelectionOption类选择的指定语言媒体轨道。
注意:与AVAssetExportSession相比,AVAssetWriter明显的优势就是它对输出进行编码时能够进行更加细致的压缩设置控制。可以让开发者指定诸如关键帧间隔、视频比特率、H.264配置文件、像素宽高比和纯净光圈等设置
二、使用
1、AVAssetReader 读数据
+ (void)read{
NSString *fileName = @"ElephantSeals.mov";
NSURL *fileURL = [[NSBundle mainBundle] URLForResource:fileName
withExtension:nil];
AVURLAsset *asset = [AVURLAsset assetWithURL:fileURL];
[self loadAudioSamplesFromAsset:asset];
}
+ (void)loadAudioSamplesFromAsset:(AVAsset *)asset{
NSString *tracks = @"tracks";
[asset loadValuesAsynchronouslyForKeys:@[tracks] completionHandler:^{ // 1
AVKeyValueStatus status = [asset statusOfValueForKey:tracks error:nil];
NSData *sampleData = nil;
if (status == AVKeyValueStatusLoaded) { // 2
sampleData = [self readAudioSamplesFromAsset:asset];
NSLog(@"sampleData : %ld",sampleData.length);
}
dispatch_async(dispatch_get_main_queue(), ^{ // 3
// completionBlock(sampleData);
});
}];
}
+ (NSData *)readAudioSamplesFromAsset:(AVAsset *)asset {
NSError *error = nil;
AVAssetReader *assetReader = // 1
[[AVAssetReader alloc] initWithAsset:asset error:&error];
if (!assetReader) {
NSLog(@"Error creating asset reader: %@", [error localizedDescription]);
return nil;
}
AVAssetTrack *track = // 2
[[asset tracksWithMediaType:AVMediaTypeAudio] firstObject];
NSDictionary *outputSettings = @{ // 3
AVFormatIDKey : @(kAudioFormatLinearPCM),
AVLinearPCMIsBigEndianKey : @NO,
AVLinearPCMIsFloatKey : @NO,
AVLinearPCMBitDepthKey : @(16)
};
AVAssetReaderTrackOutput *trackOutput = // 4
[[AVAssetReaderTrackOutput alloc] initWithTrack:track
outputSettings:outputSettings];
[assetReader addOutput:trackOutput];
[assetReader startReading];
NSMutableData *sampleData = [NSMutableData data];
while (assetReader.status == AVAssetReaderStatusReading) {
CMSampleBufferRef sampleBuffer = [trackOutput copyNextSampleBuffer];// 5
if (sampleBuffer) {
CMBlockBufferRef blockBufferRef = // 6
CMSampleBufferGetDataBuffer(sampleBuffer);
size_t length = CMBlockBufferGetDataLength(blockBufferRef);
SInt16 sampleBytes[length];
CMBlockBufferCopyDataBytes(blockBufferRef, // 7
0,
length,
sampleBytes);
[sampleData appendBytes:sampleBytes length:length];
NSLog(@"buffer : %zu",length);
CMSampleBufferInvalidate(sampleBuffer); // 8
CFRelease(sampleBuffer);
}
}
if (assetReader.status == AVAssetReaderStatusCompleted) { // 9
return sampleData;
} else {
NSLog(@"Failed to read audio samples from asset");
return nil;
}
}