使用AudioQueue播放AAC,回调函数不起作用

Use AudioQueue to play AAC, but the callback function doesn't work

最近我的项目想实现AAC的音频通信,所以我使用了AudioQueue,但是有一个问题是我的播放器的回调函数不起作用,我的项目中回调函数已经起作用了3次。即 for 函数工作了 3 次。当激活时 AudioQueueStart(mQueue, NULL),永远不会调用回调函数。

我用了两个iPhone到运行。它们通过 udp 套接字连接,我相信这部分没问题。我可以得到正确的音频数据。

并且我修改了一个播放文件数据而不是内存的演示。所以我不知道这是不是一个问题?

这是我的代码:AQPlayer.h

#include <AudioToolbox/AudioToolbox.h>

#include "CAStreamBasicDescription.h"
#include "CAXException.h"

#define kNumberBuffers 3
#define kBufferDurationSeconds 0.5

class AQPlayer
{
public:
    AQPlayer();
    ~AQPlayer();

    OSStatus                        StartQueue(BOOL inResume);
    OSStatus                        StopQueue();
    OSStatus                        PauseQueue();

    AudioQueueRef                   Queue()                 { return mQueue; }
    CAStreamBasicDescription        DataFormat() const      { return mDataFormat; }
    Boolean                         IsRunning() const       { return (mIsRunning) ? true : false; }
    Boolean                         IsInitialized() const   { return mIsInitialized; }
    CFStringRef                     GetFilePath() const     { return (mFilePath) ? mFilePath : CFSTR(""); }
    Boolean                         IsLooping() const       { return mIsLooping; }

    void SetLooping(Boolean inIsLooping)    { mIsLooping = inIsLooping; }
    void CreateQueueForFile(CFStringRef inFilePath);
    void DisposeQueue(Boolean inDisposeFile);
    void prepareAudioQueue();
    void start();
    void stop();

private:
    UInt32                          GetNumPacketsToRead()               { return mNumPacketsToRead; }
    SInt64                          GetCurrentPacket()                  { return mCurrentPacket; }
    AudioFileID                     GetAudioFileID()                    { return mAudioFile; }
    void                            SetCurrentPacket(SInt64 inPacket)   { mCurrentPacket = inPacket; }

    void                            SetupNewQueue();

    AudioQueueRef                   mQueue;
    AudioQueueBufferRef             mBuffers[kNumberBuffers];
    AudioFileID                     mAudioFile;
    CFStringRef                     mFilePath;
    CAStreamBasicDescription        mDataFormat;

    Boolean                         mIsInitialized;
    UInt32                          mNumPacketsToRead;
    SInt64                          mCurrentPacket;
    UInt32                          mIsRunning;
    Boolean                         mIsDone;
    Boolean                         mIsLooping;

    static void isRunningProc(      void *              inUserData,
                              AudioQueueRef           inAQ,
                              AudioQueuePropertyID    inID);

    static void AQBufferCallback(   void *                  inUserData,
                                 AudioQueueRef          inAQ,
                                 AudioQueueBufferRef        inCompleteAQBuffer);

    void CalculateBytesForTime(     CAStreamBasicDescription & inDesc,
                               UInt32 inMaxPacketSize, 
                               Float64 inSeconds, 
                               UInt32 *outBufferSize, 
                               UInt32 *outNumPackets);

};

这里是AQPlayer.mm

#include "package.h"
#include "udpsocket.h"
#define MAXPACKETSIZE 1000
#define BUFFER_SIZE 4000

#include "AQPlayer.h"


extern udpsocket *udp;

void AQPlayer::AQBufferCallback(void *                  inUserData,
                                AudioQueueRef           inAQ,
                                AudioQueueBufferRef     inCompleteAQBuffer) 
{

    AQPlayer *THIS = (AQPlayer *)inUserData;

    dispatch_semaphore_wait(udp->sempahore,DISPATCH_TIME_FOREVER);
    NSLog(@"read begin");

    while ([udp->AudioQueue count]>0 &&
        ![[udp->AudioQueue objectAtIndex:0] isKindOfClass:[AUDIO_CACHE_OBJECT class]])
    {
        [udp->AudioQueue removeObjectAtIndex:0];
    }
    if([udp->AudioQueue count]<1){
        [NSThread sleepForTimeInterval:0.05];
        AQBufferCallback(inUserData, inAQ, inCompleteAQBuffer);
        return;
    }
    int packets = 0;
    int dataLen = 0;
    AUDIO_CACHE_OBJECT *pack;

    char *data = (char*)malloc(sizeof(char)*BUFFER_SIZE);;
    memset(data, 0, BUFFER_SIZE);
    pack = [udp->AudioQueue firstObject];

    while (dataLen+pack.datalen<BUFFER_SIZE && [udp->AudioQueue count]>0 /*&& packets<21*/) {
        memcpy(data+dataLen, [pack GetData], [pack datalen]);
        dataLen+=[pack datalen];
        [udp->AudioQueue removeObjectAtIndex:0];
        //            [pack memset];
        packets ++;

        while ([udp->AudioQueue count]>1 &&
               ![[udp->AudioQueue objectAtIndex:0] isKindOfClass:[AUDIO_CACHE_OBJECT class]])
        {
            [udp->AudioQueue removeObjectAtIndex:0];
        }
        if([udp->AudioQueue count]<1){
            break;
        }
        pack = [udp->AudioQueue firstObject];

    }

    memcpy(inCompleteAQBuffer->mAudioData, data, dataLen);

    inCompleteAQBuffer->mAudioDataByteSize = dataLen;
    inCompleteAQBuffer->mPacketDescriptionCount = packets;
    AudioQueueEnqueueBuffer(inAQ, inCompleteAQBuffer, 0,NULL);
    THIS->mCurrentPacket += packets;
    free(data);
    NSLog(@"read end --- %lld",THIS->mCurrentPacket);

}

void AQPlayer::isRunningProc (  void *              inUserData,
                              AudioQueueRef           inAQ,
                              AudioQueuePropertyID    inID)
{
    AQPlayer *THIS = (AQPlayer *)inUserData;
    UInt32 size = sizeof(THIS->mIsRunning);
    OSStatus result = AudioQueueGetProperty (inAQ, kAudioQueueProperty_IsRunning, &THIS->mIsRunning, &size);

    if ((result == noErr) && (!THIS->mIsRunning))
        [[NSNotificationCenter defaultCenter] postNotificationName: @"playbackQueueStopped" object: nil];
}

void AQPlayer::CalculateBytesForTime (CAStreamBasicDescription & inDesc, UInt32 inMaxPacketSize, Float64 inSeconds, UInt32 *outBufferSize, UInt32 *outNumPackets)
{
    // we only use time here as a guideline
    // we're really trying to get somewhere between 16K and 64K buffers, but not allocate too much if we don't need it
    static const int maxBufferSize = 0x10000; // limit size to 64K
    static const int minBufferSize = 0x4000; // limit size to 16K

    if (inDesc.mFramesPerPacket) {
        Float64 numPacketsForTime = inDesc.mSampleRate / inDesc.mFramesPerPacket * inSeconds;
        *outBufferSize = numPacketsForTime * inMaxPacketSize;
    } else {
        // if frames per packet is zero, then the codec has no predictable packet == time
        // so we can't tailor this (we don't know how many Packets represent a time period
        // we'll just return a default buffer size
        *outBufferSize = maxBufferSize > inMaxPacketSize ? maxBufferSize : inMaxPacketSize;
    }

    // we're going to limit our size to our default
    if (*outBufferSize > maxBufferSize && *outBufferSize > inMaxPacketSize)
        *outBufferSize = maxBufferSize;
    else {
        // also make sure we're not too small - we don't want to go the disk for too small chunks
        if (*outBufferSize < minBufferSize)
            *outBufferSize = minBufferSize;
    }
    *outNumPackets = *outBufferSize / inMaxPacketSize;
}

AQPlayer::AQPlayer() :
mQueue(0),
mAudioFile(0),
mFilePath(NULL),
mIsRunning(false),
mIsInitialized(false),
mNumPacketsToRead(0),
mCurrentPacket(0),
mIsDone(false),
mIsLooping(false) { }

AQPlayer::~AQPlayer()
{
    DisposeQueue(true);
}





OSStatus AQPlayer::StartQueue(BOOL inResume)
{
    if (mQueue == NULL)
        CreateQueueForFile(mFilePath);

    mIsDone = false;
    if (!inResume)
        mCurrentPacket = 0;

    for (int i = 0; i < kNumberBuffers; ++i) {
        AQBufferCallback (this, mQueue, mBuffers[i]);
    }
    NSLog(@"audioqueuestart");
    UInt32 i =0;
    AudioQueuePrime(mQueue, 0, &i);
    NSLog(@"%d",(unsigned int)i);
    return AudioQueueStart(mQueue, NULL);
}

OSStatus AQPlayer::StopQueue()
{
    OSStatus result = AudioQueueStop(mQueue, true);
    if (result) printf("ERROR STOPPING QUEUE!\n");

    return result;
}

OSStatus AQPlayer::PauseQueue()
{
    OSStatus result = AudioQueuePause(mQueue);

    return result;
}

void AQPlayer::CreateQueueForFile(CFStringRef inFilePath)
{

    try {

            UInt32 size = sizeof(mDataFormat);

            mDataFormat.mSampleRate         =  44100;
            mDataFormat.mFormatID           =  kAudioFormatMPEG4AAC;
            mDataFormat.mFormatFlags        =  0;
            mDataFormat.mFramesPerPacket    =  1024;
            mDataFormat.mChannelsPerFrame   =  2;
            mDataFormat.mBitsPerChannel     =  0;//表示这是一个压缩格式
            mDataFormat.mBytesPerPacket     =  0;//表示这是一个变比特率压缩
            mDataFormat.mBytesPerFrame      =  0;
            mDataFormat.mReserved           =  0;
            //aqc.bufferByteSize                  =  2000;

        SetupNewQueue();
    }
    catch (NSException *e) {

        fprintf(stderr, "Error: %@ (%@)\n", [e debugDescription], [e description]);
    }
}

void AQPlayer::SetupNewQueue()
{
    AudioQueueNewOutput(&mDataFormat, AQPlayer::AQBufferCallback, this,//NULL,NULL,0,&mQueue);
                                      CFRunLoopGetCurrent(), kCFRunLoopCommonModes, 0, &mQueue);
    UInt32 bufferByteSize;
    UInt32 maxPacketSize = MAXPACKETSIZE;
    UInt32 size = sizeof(maxPacketSize);
    CalculateBytesForTime (mDataFormat, maxPacketSize, kBufferDurationSeconds, &bufferByteSize, &mNumPacketsToRead);

    size = sizeof(UInt32);


   AudioQueueAddPropertyListener(mQueue, kAudioQueueProperty_IsRunning, isRunningProc, this);

    bool isFormatVBR = (mDataFormat.mBytesPerPacket == 0 || mDataFormat.mFramesPerPacket == 0);
    AudioQueueSetParameter(mQueue, kAudioQueueParam_Volume, 1.0);


    for (int i = 0; i < kNumberBuffers; ++i) {
        AudioQueueAllocateBuffer(mQueue, bufferByteSize, &mBuffers[i]);
    }

    // set the volume of the queue
    mIsInitialized = true;
}
void AQPlayer::DisposeQueue(Boolean inDisposeFile)
{
    if (mQueue)
    {
        AudioQueueDispose(mQueue, true);
        mQueue = NULL;
    }
    if (inDisposeFile)
    {
        if (mAudioFile)
        {
            AudioFileClose(mAudioFile);
            mAudioFile = 0;
        }
        if (mFilePath)
        {
            CFRelease(mFilePath);
            mFilePath = NULL;
        }
    }
    mIsInitialized = false;
}

感谢您的宝贵时间。

您的输入是 VBR,因此您的缓冲区需要一个随附的数据包描述数组。最初创建缓冲区时,请使用 AudioQueueAllocateBufferWithPacketDescriptions: 而不是 AudioQueueAllocateBuffer:。然后在回调过程中为 while 循环内的每个数据包设置 mStartOffsetmDataByteSizemVariableFramesInPacket(始终为零)。