缓冲区较小时,音频队列的播放速度过快 [英] Audio Queue is playing too fast when the buffer size is small

查看:111
本文介绍了缓冲区较小时,音频队列的播放速度过快的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我能够使用音频文件服务+音频队列服务流式传输和播放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];
                                }

但是,我接受并奖励100点以下的DAVE答案,很快我意识到我的问题有所不同.....

解决方案

为可变比特率分配队列时,您需要计算数据包大小,而不是使用XMAQDefaultBufSize来可变比特率.我从

#######################################

EDIT:
For anyone who is visiting this in the future, turns out my exact problem was AudioStreamPacketDescription packetDescs[XMAQMaxPacketDescs]; so XMAQMaxPacketDescs here is 512 when I choose bigger buffer sizes I was enqueueing closer numbers to 512 packets for each buffer so it was playing at normal speed

However for small buffer sizes like 1024 this is only 2-3 packets total so rest of the 508 packets were 0, and player was trying to play all the packetdescriptions 512 of them an that's why it was too fast.

I solved the problem by counting the number of total number of packets that I put the buffers then I created a dynamic AudioStreamPacketDescription description array..

  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];
                                }

However I accepted and rewarded 100 points to DAVE answer's below, soon I realized my problem was different.....

解决方案

When you allocate your queue for variable bit rate, instead of using XMAQDefaultBufSize, for variable bit rate, you need to calculate the packet size. I pulled a method from this tutorial from this book that shows how it's done.

void DeriveBufferSize (AudioQueueRef audioQueue, AudioStreamBasicDescription ASBDescription, Float64 seconds, UInt32 *outBufferSize)
{
    static const int maxBufferSize = 0x50000; // punting with 50k
    int maxPacketSize = ASBDescription.mBytesPerPacket; 
    if (maxPacketSize == 0) 
    {                           
        UInt32 maxVBRPacketSize = sizeof(maxPacketSize);
        AudioQueueGetProperty(audioQueue, kAudioConverterPropertyMaximumOutputPacketSize, &maxPacketSize, &maxVBRPacketSize);
    }

    Float64 numBytesForTime = ASBDescription.mSampleRate * maxPacketSize * seconds;
    *outBufferSize =  (UInt32)((numBytesForTime < maxBufferSize) ? numBytesForTime : maxBufferSize);
}

You would use it like this.

Float64 bufferDurSeconds = 0.54321;  
AudioStreamBasicDescription myAsbd = self.format; // or something

UInt32 bufferByteSize;   
DeriveBufferSize(recordState.queue, myAsbd, bufferDurSeconds, &bufferByteSize);

AudioQueueAllocateBuffer(queue, bufferByteSize, &buffers[i]);

Using kAudioConverterPropertyMaximumOutputPacketSize, you calculate the smallest buffer size you can safely use for the unpredictable variable bit rate file. If your file is too small, you just need to identify which samples are padding for the codec.

这篇关于缓冲区较小时,音频队列的播放速度过快的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆