AVAssetReader介绍
可以通过AVAssetReader获取视频文件里媒体样本,可以直接从存储器中读取未解码的原始媒体样本,获得解码成可渲染形式的样本。 文档里说明AVAssetrader管道内部是多线程的。初始化之后,读取器在使用前加载并处理合理数量的样本数据,以copyNextSampleBuffer(AVAssetReaderOutput)等检索操作的延迟非常低。但AVAssetReader还是不适用于实时源,并且它的性能也不能保证用于实时操作。 由于使用前需要加载并处理一些样本数据,导致占用的内存可能会比较大,需要注意同一时间使用的reader个数不要过多,视频像素越高,占用的内存也会越大。
AVAssetReader初始化
使用AVAsset对AVAssetReader进行初始化,前面也说了初始化之后就会加载样本数据,所以这一步就已经会对内存产生印象,如果内存紧张就不要预先初始化。
NSError *createReaderError;
_reader = [[AVAssetReader alloc]initWithAsset:_asset error:&createReaderError];
AVAssetReader设置Output
在开始读取之前,需要添加output来控制读取初始化使用的asset中哪些track,以及配置如何读取。 AVAssetReaderOutput还有其他的子类实现,如AVAssetReaderVideoCompositionOutput,AVAssetReaderAudioMixOutput和AVAssetReaderSampleReferenceOutput。 这里使用AVAssetReaderTrackOutput演示。需要一个track来初始化,track从asset中获取。
NSArray tracks = [_asset tracksWithMediaType:AVMediaTypeAudio];
if (tracks.count > 0) {
AVAssetTrack audioTrack = [tracks objectAtIndex:0];
}
或者
NSArray tracks = [_asset tracksWithMediaType:AVMediaTypeVideo];
if (tracks.count > 0) {
AVAssetTrack videoTrack = [tracks objectAtIndex:0];
}
还可以对输出的格式进行配置,更多配置可以查阅文档。
NSDictionary * const VideoAssetTrackReaderOutputOptions = @{(id) kCVPixelBufferOpenGLESCompatibilityKey : @(YES),
(id) kCVPixelBufferIOSurfacePropertiesKey : [NSDictionary dictionary],
(NSString*)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA)};
_readerOutput = [AVAssetReaderTrackOutput assetReaderTrackOutputWithTrack:_track
outputSettings:VideoAssetTrackReaderOutputOptions];
if ([_reader canAddOutput:_readerOutput]) {
[_reader addOutput:_readerOutput];
}
seek操作
AVAssetReader并不适合频繁随机读取的操作,如果需要频繁seek可能需要别的方式实现。 在开始读取之前,可以对读取的范围进行设置,当开始读取后不可以修改,只能顺序向后读。 有两种方案来调整读取范围:
- output中可以设置supportsRandomAccess,当为true时,可以重置读取范围,但需要调用方调用copyNextSampleBuffer,直到该方法返回NULL。
- 或者重新初始化一个AVAssetReader来设置读取时间。 如果尝试第一种方案,需要使用seek,可以尝试每次设置一个不太长的区间,以保证读取完整个区间不会耗时太多,且时间间隔最好以关键帧划分。
读取数据
_reader.timeRange = range;
[_reader startReading];
_sampleBuffer = [_readerOutput copyNextSampleBuffer];
CMSampleBuffer中提供了方法获取解码数据,比如获取图像信息可以使用
CVImageBufferRef pixelBuffer =
CMSampleBufferGetImageBuffer(_sampleBuffer);
需要注意,当CMSampleBuffer使用完毕,需要调用release来释放
CFRelease(_sampleBuffer);
代码示例
NSDictionary * const AssetOptions = @{AVURLAssetPreferPreciseDurationAndTimingKey:@YES};
NSDictionary * const VideoAssetTrackReaderOutputOptions = @{(id) kCVPixelBufferOpenGLESCompatibilityKey : @(YES),
(id) kCVPixelBufferIOSurfacePropertiesKey : [NSDictionary dictionary],
(NSString*)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA)};
_videoAsset = [[AVURLAsset alloc] initWithURL:[NSURL fileURLWithPath: filePath]
options:AssetOptions];
_videoTrack = [[mPrivate->mVideoAsset tracksWithMediaType:AVMediaTypeVideo] firstObject];
if (_videoTrack) {
NSError *createReaderError;
_reader = [[AVAssetReader alloc] initWithAsset:_videoAsset error:&createReaderError];
if (!createReaderError) {
_readerOutput = [AVAssetReaderTrackOutput assetReaderTrackOutputWithTrack:mPrivate->_videoTrack
outputSettings:VideoAssetTrackReaderOutputOptions];
_readerOutput.supportsRandomAccess = YES;
if ([_reader canAddOutput:_readerOutput])
{
[_reader addOutput:_readerOutput];
}
[_reader startReading];
if (_reader.status == AVAssetReaderStatusReading || _reader.status == AVAssetReaderStatusCompleted) {
CMSampleBufferRef samplebuffer = [_readerOutput copyNextSampleBuffer];
if (samplebuffer) {
//绘制samepleBuffer中的画面
CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(samplebuffer);
CVPixelBufferLockBaseAddress(imageBuffer, 0);
uint8_t *baseAddress = (uint8_t *)CVPixelBufferGetBaseAddress(imageBuffer);
size_t width = CVPixelBufferGetWidth(imageBuffer);
size_t height = CVPixelBufferGetHeight(imageBuffer);
size_t bytesPerRow = CVPixelBufferGetBytesPerRowOfPlane(imageBuffer, 0);
size_t bufferSize = CVPixelBufferGetDataSize(imageBuffer);
CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, baseAddress, bufferSize, NULL);
CGImageRef cgImage = CGImageCreate(width, height, 8, 32, bytesPerRow, rgbColorSpace, kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrderDefault, provider, NULL, true, kCGRenderingIntentDefault);
CGImageRelease(cgImage);
CVPixelBufferUnlockBaseAddress(imageBuffer, 0);
CFRelease(samplebuffer);
}
}
}
}
来源:oschina
链接:https://my.oschina.net/u/4050557/blog/4911224