AVAssetReader视频数据读取
AVAssetReader介绍
可以通过AVAssetReader获取视频文件里媒体样本,可以直接从存储器中读取未解码的原始媒体样本,获得解码成可渲染形式的样本。文档里说明AVAssetrader管道内部是多线程的。初始化之后,读取器在使用前加载并处理合理数量的样本数据,以copyNextSampleBuffer(AVAssetReaderOutput)等检索操作的延迟非常低。但AVAssetReader还是不适用于实时源,并且它的性能也不能保证用于实时操作。
由于使用前需要加载并处理一些样本数据,导致占用的内存可能会比较大,需要注意同一时间使用的reader个数不要过多,视频像素越高,占用的内存也会越大。
一、AVAsset的获取
这里通过NSURL的方式来创建对应的AVAsset
- (void)customInit { NSDictionary* options = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:NO] forKey:(id)AVURLAssetPreferPreciseDurationAndTimingKey]; // 将url对应的内容载入到url资产中 AVURLAsset* inputAsset = [[AVURLAsset alloc] initWithURL:videoUrl options:options]; __weak typeof(self) weakSelf = self; // 由于url对应的video可能比较大会比较慢,所以需要异步等待载入完毕 [inputAsset loadValuesAsynchronouslyForKeys:[NSArray arrayWithObject:@"tracker"] completionHandler:^{ // 异步载入完毕之后进行判断 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSError* error = nil; AVKeyValueStatus valueStatus = [inputAsset statusOfValueForKey:@"tracker" error:&error]; if (valueStatus != AVKeyValueStatusLoaded) { NSLog(@"load failed %@", error); return; } [weakSelf processWithAsset:inputAsset]; }); }]; }
因为AVURLAsset是继承自AVAsset,所以这里创建AVURLAsset是可以的
这里参数设置使用AVURLAssetPreferPreciseDurationAndTimingKey,该参数表示 asset 应该提供精确的时长并能根据时间准确地随机访问,提供这样的能力是需要开销更大的计算的。当只是想播放视频时可以不设置这个选项。这里需要每一帧的渲染,则需要精确的时长。
二、AVAssetReader的创建
- (void)processWithAsset:(AVAsset *)asset { [lock lock]; NSLog(@"processWithAsset"); NSError* error = nil; // 通过url资产创建资产reader assetReader = [AVAssetReader assetReaderWithAsset:asset error:&error]; // 由于是要每一帧的进行读取并且是全部读取,则需要设置为kCVPixelFormatType_420YpCbCr8BiPlanarFullRange NSDictionary* outputSettings = [NSDictionary dictionaryWithObject:@(kCVPixelFormatType_420YpCbCr8BiPlanarFullRange) forKey:(id)kCVPixelBufferPixelFormatTypeKey]; // 创建资产reader追踪 // [asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0] 返回对应的track readerVideoTrackOutput = [AVAssetReaderTrackOutput assetReaderTrackOutputWithTrack:[[asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0] outputSettings:outputSettings]; // 将追踪器加入到资产reader [assetReader addOutput:readerVideoTrackOutput]; // 开始读 if (![assetReader startReading]) { NSLog(@"error startreading %@", asset); } [lock unlock]; }
如上细节已经在注释中说明,有个注意的点是追踪器需要在startReading之前设置。
三、每一帧采样缓冲区的读取
- (CMSampleBufferRef)readBuffer { [lock lock]; CMSampleBufferRef sampleBufferRef = nil; if (readerVideoTrackOutput) { sampleBufferRef = [readerVideoTrackOutput copyNextSampleBuffer]; } if (assetReader && assetReader.status == AVAssetReaderStatusCompleted) { NSLog(@" re customInit"); assetReader = nil; readerVideoTrackOutput = nil; [self customInit]; } [lock unlock]; return sampleBufferRef; }
如上[readerVideoTrackOutput copyNextSampleBuffer] 是按顺序读取了每一帧的buffer
当本次视频读取结束之后,则重新再一次创建asset
参考:
https://www.jianshu.com/p/7114536d705a
https://www.cnblogs.com/samirchen/p/7073992.html