【发布时间】:2015-05-29 15:51:09
【问题描述】:
我能够使用音频文件服务 + 音频队列服务流式传输和播放 m4a 文件。由于文件类型,文件的比特率信息不可用于音频队列。
下载所有音频包后,我将它们提供给播放器。
当我选择 32768 或 16384 左右的缓冲区大小时,因为回调被调用的频率较低并且每个缓冲区大小都很大,所以它似乎几乎以正常速度播放。问题是有时我也必须播放小文件,但是当我选择较小的缓冲区大小 -512 或 1024 或 2048 到 8192 时 - 音频播放速度非常快并且偶尔会出现故障。
我知道在 c 回调中调用 Objective-c 函数不是一个好主意,但为了可读性和易用性我这样做了。无论如何,我认为这不是问题。
// allocate the buffers and prime the queue with some data before starting
AudioQueueBufferRef buffers[XMNumberPlaybackBuffers];
int i;
for (i = 0; i < XMNumberPlaybackBuffers; ++i)
{
err=AudioQueueAllocateBuffer(queue, XMAQDefaultBufSize, &buffers[i]);
if (err) {
[self failWithErrorCode:err customError:AP_AUDIO_QUEUE_BUFFER_ALLOCATION_FAILED];
}
@synchronized(self)
{
state=AP_WAITING_FOR_QUEUE_TO_START;
}
// manually invoke callback to fill buffers with data
MyAQOutputCallBack((__bridge void *)(self), queue, buffers[i]);
}
我还从可变字典数组中获取音频包...
#define XMNumberPlaybackBuffers 4
#define XMAQDefaultBufSize 8192
#pragma mark playback callback function
static void MyAQOutputCallBack(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inCompleteAQBuffer)
{
// this is called by the audio queue when it has finished decoding our data.
// The buffer is now free to be reused.
NSLog(@"MyAQOutputCallBack..");
//printf("MyAQOutputCallBack...\n");
XMAudioPlayer* player = (__bridge XMAudioPlayer *)inUserData;
[player handleBufferCompleteForQueue:inAQ buffer:inCompleteAQBuffer];
//printf("##################\n");
}
- (void)handleBufferCompleteForQueue:(AudioQueueRef)inAQ
buffer:(AudioQueueBufferRef)inBuffer
{
//NSLog(@"######################\n");
AudioTimeStamp queueTime;
Boolean discontinuity;
err = AudioQueueGetCurrentTime(queue, NULL, &queueTime, &discontinuity);
printf("queueTime.mSampleTime %.2f\n",queueTime.mSampleTime/dataFormat.mSampleRate);
AudioStreamPacketDescription packetDescs[XMAQMaxPacketDescs]; // packet descriptions for enqueuing audio
BOOL isBufferFilled=NO;
size_t bytesFilled=0; // how many bytes have been filled
size_t packetsFilled=0; // how many packets have been filled
size_t bufSpaceRemaining;
while (isBufferFilled==NO && isEOF==NO) {
if (currentlyReadingBufferIndex<[sharedCache.audioCache count]) {
//loop thru untill buffer is enqued
if (sharedCache.audioCache) {
NSMutableDictionary *myDict= [[NSMutableDictionary alloc] init];
myDict=[sharedCache.audioCache objectAtIndex:currentlyReadingBufferIndex];
//why I cant use this info?
//UInt32 inNumberBytes =[[myDict objectForKey:@"inNumberBytes"] intValue];
UInt32 inNumberPackets =[[myDict objectForKey:@"inNumberPackets"] intValue];
NSData *convert=[myDict objectForKey:@"inInputData"];
const void *inInputData=(const char *)[convert bytes];
//AudioStreamPacketDescription *inPacketDescriptions;
AudioStreamPacketDescription *inPacketDescriptions= malloc(sizeof(AudioStreamPacketDescription));
NSNumber *mStartOffset = [myDict objectForKey:@"mStartOffset"];
NSNumber *mDataByteSize = [myDict objectForKey:@"mDataByteSize"];
NSNumber *mVariableFramesInPacket = [myDict objectForKey:@"mVariableFramesInPacket"];
inPacketDescriptions->mVariableFramesInPacket=[mVariableFramesInPacket intValue];
inPacketDescriptions->mStartOffset=[mStartOffset intValue];
inPacketDescriptions->mDataByteSize=[mDataByteSize intValue];
for (int i = 0; i < inNumberPackets; ++i)
{
SInt64 packetOffset = [mStartOffset intValue];
SInt64 packetSize = [mDataByteSize intValue];
//printf("packetOffset %lli\n",packetOffset);
//printf("packetSize %lli\n",packetSize);
currentlyReadingBufferIndex++;
if (packetSize > packetBufferSize)
{
//[self failWithErrorCode:AS_AUDIO_BUFFER_TOO_SMALL];
}
bufSpaceRemaining = packetBufferSize - bytesFilled;
//printf("bufSpaceRemaining %zu\n",bufSpaceRemaining);
// if the space remaining in the buffer is not enough for this packet, then enqueue the buffer.
if (bufSpaceRemaining < packetSize)
{
inBuffer->mAudioDataByteSize = (UInt32)bytesFilled;
err=AudioQueueEnqueueBuffer(inAQ,inBuffer,(UInt32)packetsFilled,packetDescs);
if (err) {
[self failWithErrorCode:err customError:AP_AUDIO_QUEUE_ENQUEUE_FAILED];
}
isBufferFilled=YES;
[self incrementBufferUsedCount];
return;
}
@synchronized(self)
{
//
// If there was some kind of issue with enqueueBuffer and we didn't
// make space for the new audio data then back out
//
if (bytesFilled + packetSize > packetBufferSize)
{
return;
}
// copy data to the audio queue buffer
//error -66686 refers to
//kAudioQueueErr_BufferEmpty = -66686
//memcpy((char*)inBuffer->mAudioData + bytesFilled, (const char*)inInputData + packetOffset, packetSize);
memcpy(inBuffer->mAudioData + bytesFilled, (const char*)inInputData + packetOffset, packetSize);
// fill out packet description
packetDescs[packetsFilled] = inPacketDescriptions[0];
packetDescs[packetsFilled].mStartOffset = bytesFilled;
bytesFilled += packetSize;
packetsFilled += 1;
free(inPacketDescriptions);
}
// if that was the last free packet description, then enqueue the buffer.
// size_t packetsDescsRemaining = kAQMaxPacketDescs - packetsFilled;
// if (packetsDescsRemaining == 0) {
//
// }
if (sharedCache.numberOfToTalPackets>0)
{
if (currentlyReadingBufferIndex==[sharedCache.audioCache count]-1) {
if (loop==NO) {
inBuffer->mAudioDataByteSize = (UInt32)bytesFilled;
lastEnqueudBufferSize=bytesFilled;
lastbufferPacketCount=(int)packetsFilled;
err=AudioQueueEnqueueBuffer(inAQ,inBuffer,(UInt32)packetsFilled,packetDescs);
if (err) {
[self failWithErrorCode:err customError:AP_AUDIO_QUEUE_ENQUEUE_FAILED];
}
printf("if that was the last free packet description, then enqueue the buffer\n");
//go to the next item on keepbuffer array
isBufferFilled=YES;
[self incrementBufferUsedCount];
return;
}
else
{
//if loop is yes return to first packet pointer and fill the rest of the buffer before enqueing it
//set the currently reading to zero
//check the space in buffer
//if space is avaialbele create a while loop till it is filled
//then enqueu the buffer
currentlyReadingBufferIndex=0;
}
}
}
}
}
}
}
}
#######################################
编辑:
对于将来访问此内容的任何人,原来我的确切问题是AudioStreamPacketDescription packetDescs[XMAQMaxPacketDescs]; 所以XMAQMaxPacketDescs 这里是 512 当我选择更大的缓冲区大小时,我为每个缓冲区排入更接近 512 个数据包的数字,因此它以正常速度播放
但是对于像 1024 这样的小缓冲区大小,总共只有 2-3 个数据包,因此 508 个数据包的其余部分为 0,并且播放器试图播放其中的所有数据包描述 512,这就是它太快的原因。
我通过计算放入缓冲区的数据包总数解决了这个问题,然后我创建了一个动态 AudioStreamPacketDescription 描述数组..
AudioStreamPacketDescription * tempDesc = (AudioStreamPacketDescription *)(malloc(packetsFilledDesc * sizeof(AudioStreamPacketDescription)));
memcpy(tempDesc,packetDescs, packetsFilledDesc*sizeof(AudioStreamPacketDescription));
err = AudioQueueEnqueueBuffer(inAQ,inBuffer,packetsFilledDesc,tempDesc);
if (err) {
[self failWithErrorCode:err customError:AP_AUDIO_QUEUE_ENQUEUE_FAILED];
}
但是我接受并奖励了下面 DAVE 答案的 100 分,很快我就意识到我的问题不一样.....
【问题讨论】:
标签: ios objective-c buffer audioqueue audioqueueservices