问题
我正在尝试从视频文件中提取LPCM音频的样本精确范围。目前,我正在使用AVAssetReaderTrackOutput
读取AVURLAsset
并获得AVAssetTrack
来实现此目的。
尽管使用AVURLAssetPreferPreciseDurationAndTimingKey
设置为YES
对资源进行初始化和准备,但在资源中寻找样本精确位置似乎不太准确。
NSDictionary *options = @{ AVURLAssetPreferPreciseDurationAndTimingKey : @(YES) };
_asset = [[AVURLAsset alloc] initWithURL:fileURL options:options];
例如,变量比特率编码的AAC流会表现出这种情况。虽然我知道VBR音频流在准确寻址方面会产生性能开销,但只要我能得到准确的样本,我愿意承担这个代价。
当使用Extended Audio File Services和ExtAudioFileRef
API时,我可以实现样本精确的寻址和音频提取。同样,使用AVAudioFile
也可以实现,因为它是建立在ExtAudioFileRef
之上的。
然而,问题在于我还想从音频文件API拒绝但通过AVURLAsset
支持的媒体容器中提取音频。
方法
使用CMTime
和CMTimeRange
定义样本精确的时间范围,并将其设置在AVAssetReaderTrackOutput
上。然后迭代地提取样本。
-(NSData *)readFromFrame:(SInt64)startFrame
requestedFrameCount:(UInt32)frameCount
{
NSUInteger expectedByteCount = frameCount * _bytesPerFrame;
NSMutableData *data = [NSMutableData dataWithCapacity:expectedByteCount];
//
// Configure Output
//
NSDictionary *settings = @{ AVFormatIDKey : @( kAudioFormatLinearPCM ),
AVLinearPCMIsNonInterleaved : @( NO ),
AVLinearPCMIsBigEndianKey : @( NO ),
AVLinearPCMIsFloatKey : @( YES ),
AVLinearPCMBitDepthKey : @( 32 ),
AVNumberOfChannelsKey : @( 2 ) };
AVAssetReaderOutput *output = [[AVAssetReaderTrackOutput alloc] initWithTrack:_track outputSettings:settings];
CMTime startTime = CMTimeMake( startFrame, _sampleRate );
CMTime durationTime = CMTimeMake( frameCount, _sampleRate );
CMTimeRange range = CMTimeRangeMake( startTime, durationTime );
//
// Configure Reader
//
NSError *error = nil;
AVAssetReader *reader = [[AVAssetReader alloc] initWithAsset:_asset error:&error];
if( !reader )
{
fprintf( stderr, "avf : failed to initialize reader\n" );
fprintf( stderr, "avf : %s\n%s\n", error.localizedDescription.UTF8String, error.localizedFailureReason.UTF8String );
exit( EXIT_FAILURE );
}
[reader addOutput:output];
[reader setTimeRange:range];
BOOL startOK = [reader startReading];
NSAssert( startOK && reader.status == AVAssetReaderStatusReading, @"Ensure we've started reading." );
NSAssert( _asset.providesPreciseDurationAndTiming, @"We expect the asset to provide accurate timing." );
//
// Start reading samples
//
CMSampleBufferRef sample = NULL;
while(( sample = [output copyNextSampleBuffer] ))
{
CMTime presentationTime = CMSampleBufferGetPresentationTimeStamp( sample );
if( data.length == 0 )
{
// First read - we should be at the expected presentation time requested.
int32_t comparisonResult = CMTimeCompare( presentationTime, startTime );
NSAssert( comparisonResult == 0, @"We expect sample accurate seeking" );
}
CMBlockBufferRef buffer = CMSampleBufferGetDataBuffer( sample );
if( !buffer )
{
fprintf( stderr, "avf : failed to obtain buffer" );
exit( EXIT_FAILURE );
}
size_t lengthAtOffset = 0;
size_t totalLength = 0;
char *bufferData = NULL;
if( CMBlockBufferGetDataPointer( buffer, 0, &lengthAtOffset, &totalLength, &bufferData ) != kCMBlockBufferNoErr )
{
fprintf( stderr, "avf : failed to get sample\n" );
exit( EXIT_FAILURE );
}
if( bufferData && lengthAtOffset )
{
[data appendBytes:bufferData length:lengthAtOffset];
}
CFRelease( sample );
}
NSAssert( reader.status == AVAssetReaderStatusCompleted, @"Completed reading" );
[output release];
[reader release];
return [NSData dataWithData:data];
}
笔记
CMSampleBufferGetPresentationTimeStamp
给出的演示时间似乎与我所追求的相匹配,但由于它不准确,因此我没有机会校正和对齐检索到的样本。
有什么想法吗?
或者,是否有一种适应 AVAudioFile
或 ExtAudioFile
使用的方法来调整 AVAssetTrack
?
是否可以通过 AudioFileOpenWithCallbacks
访问音频轨道?
在 macOS 中是否有其他方式可以访问视频容器中的音频流?