AudioQueue如何找出排队数据的播放长度
Posted
技术标签:
【中文标题】AudioQueue如何找出排队数据的播放长度【英文标题】:AudioQueue how to find out playback length of queued data 【发布时间】:2011-09-10 23:17:16 【问题描述】:我正在使用 AudioQueue 流式传输一些歌曲,我的问题是如何判断已排队缓冲区的播放长度?我想一次传输两秒的数据,我遇到的问题是我怎么知道有多少字节实际上对应于两秒的音乐(所以我总是可以领先两秒)。
谢谢
丹尼尔
【问题讨论】:
未压缩或未压缩的歌曲?如果压缩,是 CBR 还是 VBR? 我猜它会被 mp3 或 m4a 压缩 【参考方案1】:这是一个使用音频文件服务获取比特率/数据包/帧数据的类,以从音乐文件中获取对应于 x 秒的字节数,该示例已使用 mp3 和 m4a 文件进行测试
标题
#import <Foundation/Foundation.h>
#import <AudioToolbox/AudioToolbox.h>
@interface MusicChunker : NSObject
AudioFileID audioFile;
int _sampleRate;
int _totalFrames;
UInt64 _framesPerPacket;
UInt64 _totalPackets;
UInt64 fileDataSize;
AudioFilePacketTableInfo _packetInfo;
int _fileLength;
AudiostreamBasicDescription _fileDataFormat;
NSFileHandle * _fileHandle;
int _packetOffset;
int _totalReadBytes;
int _maxPacketSize;
BOOL firstTime;
BOOL _ism4a;
-(id)initWithURL:(NSURL*)url andFileType:(NSString*)ext;
//gets next chunk that corresponds to seconds of audio
-(NSData*)getNextDataChunk:(int)seconds;
@end
实施
#import "MusicChunker.h"
void ReportAudioError(OSStatus statusCode);
@implementation MusicChunker
- (id)init
self = [super init];
if (self)
// Initialization code here.
return self;
void ReportAudioError(OSStatus statusCode)
switch (statusCode)
case noErr:
break;
case kAudioFileUnspecifiedError:
[NSException raise:@"AudioFileUnspecifiedError" format:@"An unspecified error occured."];
break;
case kAudioFileUnsupportedDataFormatError:
[NSException raise:@"AudioFileUnsupportedDataFormatError" format:@"The data format is not supported by the output file type."];
break;
case kAudioFileUnsupportedFileTypeError:
[NSException raise:@"AudioFileUnsupportedFileTypeError" format:@"The file type is not supported."];
break;
case kAudioFileUnsupportedPropertyError:
[NSException raise:@"AudioFileUnsupportedPropertyError" format:@"A file property is not supported."];
break;
case kAudioFilePermissionsError:
[NSException raise:@"AudioFilePermissionsError" format:@"The operation violated the file permissions. For example, an attempt was made to write to a file opened with the kAudioFileReadPermission constant."];
break;
case kAudioFileNotOptimizedError:
[NSException raise:@"AudioFileNotOptimizedError" format:@"The chunks following the audio data chunk are preventing the extension of the audio data chunk. To write more data, you must optimize the file."];
break;
case kAudioFileInvalidChunkError:
[NSException raise:@"AudioFileInvalidChunkError" format:@"Either the chunk does not exist in the file or it is not supported by the file."];
break;
case kAudioFileDoesNotAllow64BitDataSizeError:
[NSException raise:@"AudioFileDoesNotAllow64BitDataSizeError" format:@"The file offset was too large for the file type. The AIFF and WAVE file format types have 32-bit file size limits."];
break;
case kAudioFileInvalidPacketOffsetError:
[NSException raise:@"AudioFileInvalidPacketOffsetError" format:@"A packet offset was past the end of the file, or not at the end of the file when a VBR format was written, or a corrupt packet size was read when the packet table was built."];
break;
case kAudioFileInvalidFileError:
[NSException raise:@"AudioFileInvalidFileError" format:@"The file is malformed, or otherwise not a valid instance of an audio file of its type."];
break;
case kAudioFileOperationNotSupportedError:
[NSException raise:@"AudioFileOperationNotSupportedError" format:@"The operation cannot be performed. For example, setting the kAudioFilePropertyAudioDataByteCount constant to increase the size of the audio data in a file is not a supported operation. Write the data instead."];
break;
case -50:
[NSException raise:@"AudioFileBadParameter" format:@"An invalid parameter was passed, possibly the current packet and/or the inNumberOfPackets."];
break;
default:
[NSException raise:@"AudioFileUknownError" format:@"An unknown error type %@ occured. [%s]", [NSNumber numberWithInteger:statusCode], (char*)&statusCode];
break;
+ (AudioFileTypeID)hintForFileExtension:(NSString *)fileExtension
AudioFileTypeID fileTypeHint = kAudioFileAAC_ADTSType;
if ([fileExtension isEqual:@"mp3"])
fileTypeHint = kAudioFileMP3Type;
else if ([fileExtension isEqual:@"wav"])
fileTypeHint = kAudioFileWAVEType;
else if ([fileExtension isEqual:@"aifc"])
fileTypeHint = kAudioFileAIFCType;
else if ([fileExtension isEqual:@"aiff"])
fileTypeHint = kAudioFileAIFFType;
else if ([fileExtension isEqual:@"m4a"])
fileTypeHint = kAudioFileM4AType;
else if ([fileExtension isEqual:@"mp4"])
fileTypeHint = kAudioFileMPEG4Type;
else if ([fileExtension isEqual:@"caf"])
fileTypeHint = kAudioFileCAFType;
else if ([fileExtension isEqual:@"aac"])
fileTypeHint = kAudioFileAAC_ADTSType;
return fileTypeHint;
-(id)initWithURL:(NSURL*)url andFileType:(NSString*)ext
self = [super init];
if (self)
// Initialization code here.
//OSStatus theErr = noErr;
if([ext isEqualToString:@"mp3"])
_ism4a=FALSE;
else
_ism4a=TRUE;
firstTime=TRUE;
_packetOffset=0;
AudioFileTypeID hint=[MusicChunker hintForFileExtension:ext];
OSStatus theErr = AudioFileOpenURL((CFURLRef)url, kAudioFileReadPermission, hint, &audioFile);
if(theErr)
ReportAudioError(theErr);
UInt32 thePropertySize;// = sizeof(theFileFormat);
thePropertySize = sizeof(fileDataSize);
theErr = AudioFileGetProperty(audioFile, kAudioFilePropertyAudioDataByteCount, &thePropertySize, &fileDataSize);
if(theErr)
ReportAudioError(theErr);
theErr = AudioFileGetProperty(audioFile, kAudioFilePropertyAudioDataPacketCount, &thePropertySize, &_totalPackets);
if(theErr)
ReportAudioError(theErr);
/*
UInt32 size;
size= sizeof(_packetInfo);
theErr= AudioFileGetProperty(audioFile, kAudioFilePropertyPacketTableInfo, &size, &_packetInfo);
g(@"Key %@", key );
if(theErr)
ReportAudioError(theErr);
*/
UInt32 size;
size=sizeof(_maxPacketSize);
theErr=AudioFileGetProperty(audioFile, kAudioFilePropertyMaximumPacketSize , &size, &_maxPacketSize);
size = sizeof( _fileDataFormat );
theErr=AudioFileGetProperty( audioFile, kAudioFilePropertyDataFormat, &size, &_fileDataFormat );
_framesPerPacket=_fileDataFormat.mFramesPerPacket;
_totalFrames=_fileDataFormat.mFramesPerPacket*_totalPackets;
_fileHandle=[[NSFileHandle fileHandleForReadingFromURL:url error:nil] retain];
_fileLength=[_fileHandle seekToEndOfFile];
_sampleRate=_fileDataFormat.mSampleRate;
_totalReadBytes=0;
/*
AudioFramePacketTranslation tran;//= .mFrame = 0, .mPacket = packetCount - 1, .mFrameOffsetInPacket = 0 ;
tran.mFrame=0;
tran.mFrameOffsetInPacket=0;
tran.mPacket=1;
UInt32 size=sizeof(tran);
theErr=AudioFileGetProperty(audioFile, kAudioFilePropertyPacketToFrame, &size, &tran);
*/
/*
AudioBytePacketTranslation bt;
bt.mPacket=4;
bt.mByteOffsetInPacket=0;
size=sizeof(bt);
theErr=AudioFileGetProperty(audioFile, kAudioFilePropertyPacketToByte, &size, &bt);
*/
return self;
//gets next chunk that corresponds to seconds of audio
-(NSData*)getNextDataChunk:(int)seconds
//NSLog(@"%d, total packets",_totalPackets);
if(_packetOffset>=_totalPackets)
return nil;
//sampleRate * seconds = number of wanted frames
int framesWanted= _sampleRate*seconds;
NSData *header=nil;
int wantedPackets= framesWanted/_framesPerPacket;
if(firstTime && _ism4a)
firstTime=false;
//when we have a header that was stripped off, we grab it from the original file
int totallen= [_fileHandle seekToEndOfFile];
int dif=totallen-fileDataSize;
[_fileHandle seekToFileOffset:0];
header= [_fileHandle readDataOfLength:dif];
int packetOffset=_packetOffset+wantedPackets;
//bound condition
if(packetOffset>_totalPackets)
packetOffset=_totalPackets;
UInt32 outBytes;
UInt32 packetCount = wantedPackets;
int x=packetCount * _maxPacketSize;
void *data = (void *)malloc(x);
OSStatus theErr=AudioFileReadPackets(audioFile, false, &outBytes, NULL, _packetOffset, &packetCount, data);
if(theErr)
ReportAudioError(theErr);
//calculate bytes to read
int bytesRead=outBytes;
//update read bytes
_totalReadBytes+=bytesRead;
// NSLog(@"total bytes read %d", _totalReadBytes);
_packetOffset=packetOffset;
NSData *subdata=[[NSData dataWithBytes:data length:outBytes] retain];
free(data);
if(header)
NSMutableData *data=[[NSMutableData alloc]init];
[data appendData:header];
[data appendData:subdata];
[subdata release];
return [data autorelease];
return [subdata autorelease];
@end
【讨论】:
【参考方案2】:如果歌曲采用任意压缩格式,并且您想要精确的 2 秒剪辑,您可能必须先将歌曲转换为原始 PCM 样本或 WAV 数据(AVAssetReader 等)。然后,您可以以已知的采样率对样本进行计数。例如44.1k 采样率下的 88200 帧相当于 2 秒。
【讨论】:
所以如果我知道采样率并且有帧,那么我应该能够得到时间吗?转换我的 porpouses 需要太长时间......我总是知道采样率 如果不转换音频,可能不知道缓冲区中有多少个 PCM 采样帧,具体取决于压缩格式。 试图找到一种不必转换的方法,必须有其他方法,AQ 可以做的一切...... @Daniel - 不要假设一些简单的方法是可能的,除非你可以在文档化的 API 中找到它。 我想我找到了一种通过音频文件服务的方法,仍在努力,但我得到它后会在这里发回以上是关于AudioQueue如何找出排队数据的播放长度的主要内容,如果未能解决你的问题,请参考以下文章