使用AudioQueue服务播放MP3数据缓冲区:Prime失败(-50)

时间:2011-09-29 06:05:53

标签: iphone ios audio-streaming audioqueueservices audiotoolbox

这些天我一直在努力寻找使用iPhone上的AudioQueue Service播放网络音频流(MP3数据包格式)的方法。

为了连续实现这个目标,我首先使用AudioQueue将常用的本地MP3文件播放,它确实有效。然后,我用stdio函数fread替换了AudioFileReadPackets,并且每次我像AudioFileReadPackets那样使用相同数量的mp3数据包来模拟来自网络的音频流。然而,这一次,错误来了:

2011-09-28 14:21:28.245 SunFlower[1554:207] Prime: Exiting because mConverterError is -50 (0x11940 req, 0x0 primed)
2011-09-28 14:21:28.253 SunFlower[1554:207] Prime failed (-50); will stop (72000/0 frames)

有人知道这个“Prime failed(-50)”错误的原因是什么?帮助zzzzzzz

************* ************************************************** ************************************************** *********************

为了解释我在更换工作上所做的工作,我想向您展示代码的主要更改部分,有两部分:“替换前”和“替换后”

  1. 更换前(playbak本地MP3文件):
  2. 1)音频队列runloop回调

    static void HandleOutputBuffer(void                *aqData, 
                                   AudioQueueRef       inAQ, 
                                   AudioQueueBufferRef inBuffer) 
    {
        AQPlayerState *pAqData = (AQPlayerState *) aqData; 
        UInt32 numBytesReadFromFile;
        UInt32 numPackets = pAqData->mNumPacketsToRead;
        UInt32 i = 0;
    
        printf("HandleOutputBuffer::Start!\n");
        //If the audio queue is stopped, returns immediately
        if(pAqData->mIsRunning == 0) 
        {
            printf("HandleOutputBuffer::Error Return!\n");
            return;
        }
    
        //Read a packet of audio data from file stream
        AudioFileReadPackets(pAqData->mAudioFile,
                             false,
                             &numBytesReadFromFile,
                             pAqData->mPacketDescs,
                             pAqData->mCurrentPacket,
                             &numPackets,
                             inBuffer->mAudioData);
    
        //Enqueue the audio packet into the audio queue
        if(numPackets > 0) 
        {
            printf("HandleOutputBuffer::Step 1!\n");
            inBuffer->mAudioDataByteSize = numBytesReadFromFile;
            AudioQueueEnqueueBuffer(pAqData->mQueue,
                                    inBuffer,
                                    (pAqData->mPacketDescs ? numPackets : 0),
                                    pAqData->mPacketDescs);
    
            pAqData->mCurrentPacket += numPackets;
        }
        else
        {
            printf("HandleOutputBuffer::Step 2!\n");
            AudioQueueStop(pAqData->mQueue,
                           false);
            pAqData->mIsRunning = false;
        }
    }
    

    2)打开音频文件

    OSStatus AqOpenAudioFile(char *filePath, AQPlayerState *pAqData)
    {
        CFURLRef audioFileURL;
        OSStatus result;
        UInt32 maxPacketSize;
        UInt32 propertySize = sizeof (maxPacketSize);
    
        audioFileURL = 
            CFURLCreateFromFileSystemRepresentation(NULL,
                                                    (const UInt8 *) filePath,
                                                    strlen (filePath),
                                                    false);
    
        result = AudioFileOpenURL(audioFileURL,
                                  kAudioFileReadPermission,
                                  0,
                                  &(pAqData->mAudioFile));
        CFRelease(audioFileURL);
    
        AudioFileGetProperty(pAqData->mAudioFile,
                             kAudioFilePropertyPacketSizeUpperBound,
                             &propertySize,
                             &maxPacketSize);
    
        DeriveBufferSize(pAqData->mDataFormat,
                         maxPacketSize,
                         0.5,
                         &(pAqData->bufferByteSize),
                         &(pAqData->mNumPacketsToRead));
    
        return result;
    }
    

    3)创建音频队列

    OSStatus AqCreateAudioQueue(AQPlayerState *pAqData)
    {
        UInt32 dataFormatSize = sizeof (AudioStreamBasicDescription);
        OSStatus result;
        bool isFormatVBR;
        UInt32 cookieSize = sizeof (UInt32);
        bool couldNotGetProperty;
    
        AudioFileGetProperty(pAqData->mAudioFile,
                             kAudioFilePropertyDataFormat,
                             &dataFormatSize,
                             &(pAqData->mDataFormat));
    
        result = AudioQueueNewOutput(&(pAqData->mDataFormat),
                                     HandleOutputBuffer,
                                     pAqData,
                                     CFRunLoopGetCurrent(),
                                     kCFRunLoopCommonModes,
                                     0,
                                     &(pAqData->mQueue));
    
        //Configurate the VBR property if any
        isFormatVBR = (pAqData->mDataFormat.mBytesPerPacket == 0 || 
                       pAqData->mDataFormat.mFramesPerPacket == 0);
    
        if(isFormatVBR)
        {
            pAqData->mPacketDescs = 
                (AudioStreamPacketDescription*)malloc(pAqData->mNumPacketsToRead * sizeof(AudioStreamPacketDescription));
        }
        else
        {
            pAqData->mPacketDescs = NULL;
        }
    
        //Set Metadata for Audio Queue
        couldNotGetProperty = 
            AudioFileGetPropertyInfo(pAqData->mAudioFile,
                                     kAudioFilePropertyMagicCookieData,
                                     &cookieSize,
                                     NULL);
    
        if (!couldNotGetProperty && cookieSize)
        {
            char* magicCookie = (char *)malloc(cookieSize);
            AudioFileGetProperty(pAqData->mAudioFile,
                                 kAudioFilePropertyMagicCookieData,
                                 &cookieSize,
                                 magicCookie);
    
            AudioQueueSetProperty(pAqData->mQueue,
                                  kAudioQueueProperty_MagicCookie,
                                  magicCookie,
                                  cookieSize);
            free(magicCookie);
        }
    
        //Set the playback gain
        AudioQueueSetParameter(pAqData->mQueue,
                               kAudioQueueParam_Volume,
                               AQ_PLAYBACK_GAIN);    
    }
    

    2。更换后(playbak mp3数据缓冲区获取fread):

    为了使代码平滑移植,我复制了关键变量的运行时值,如pAqData-> bufferByteSize,pAqData-> mNumPacketsToRead,pAqData-> mDataFormat ...等。并使用替换代码中的复制值直接初始化这些变量。这种行为的意图是抛弃AudioToolbox框架的接口,如:AudioFileOpenURL,AudioFileGetProperty,AudioFileReadPackets ......然后,我们可以使用stdio函数fread直接获取mp3数据包。更改后的代码如下所示:

    1)音频队列runloop回调(在前面的代码中,AudioFileReadPackets读取338个数据包,总共129792个字节,我将这些值直接复制到替换代码中)

    static void HandleOutputBuffer(void                *aqData, 
                                   AudioQueueRef       inAQ, 
                                   AudioQueueBufferRef inBuffer) 
    {
        AQPlayerState *pAqData = (AQPlayerState *) aqData; 
        UInt32 numBytesReadFromFile;
        UInt32 numPackets = pAqData->mNumPacketsToRead;
        UInt32 i = 0;
    
        printf("HandleOutputBuffer::Start!\n");
        //If the audio queue is stopped, returns immediately
        if(pAqData->mIsRunning == 0) 
        {
            printf("HandleOutputBuffer::Error Return!\n");
            return;
        }
    
        //Read a packet of audio data using fread
        memset(audio_buffer, 0, 327680);
        memset(inBuffer->mAudioData, 0, 327680);
        pAqData->mPacketDescs->mStartOffset = 0;
        pAqData->mPacketDescs->mVariableFramesInPacket = 0;
        pAqData->mPacketDescs->mDataByteSize = 384;
    
        numBytesReadFromFile = fread(audio_buffer, sizeof(uint8_t), 129792, source_file);
        numPackets = 338;
        memcpy(inBuffer->mAudioData, audio_buffer, 327680);
    
        //Enqueue the audio packet into the audio queue
        if(numPackets > 0) 
        {
            printf("HandleOutputBuffer::Step 1!\n");
            inBuffer->mAudioDataByteSize = numBytesReadFromFile;
            AudioQueueEnqueueBuffer(pAqData->mQueue,
                                    inBuffer,
                                    (pAqData->mPacketDescs ? numPackets : 0),
                                    pAqData->mPacketDescs);
    
            pAqData->mCurrentPacket += numPackets;
        }
        else
        {
            printf("HandleOutputBuffer::Step 2!\n");
            AudioQueueStop(pAqData->mQueue,
                           false);
            pAqData->mIsRunning = false;
        }
    }
    

    2)打开音频文件(使用fopen替换AudioFileOpenURL)

    OSStatus AqOpenAudioFile(char *filePath, AQPlayerState *pAqData)
    {
        CFURLRef audioFileURL;
        OSStatus result;
        UInt32 maxPacketSize;
        UInt32 propertySize = sizeof (maxPacketSize);
    
        source_file = fopen(filePath, "r");
    
        memset(audio_buffer, 0, 327680);
        fread(audio_buffer, sizeof(uint8_t), 32, source_file);
    
        pAqData->bufferByteSize = 327680;
        pAqData->mNumPacketsToRead = 338;
    
        return result;
    }
    

    3)创建音频队列(使用本地MP3播放模式中指定的值直接初始化pAqData-> mDataFormat)

    OSStatus AqCreateAudioQueue(AQPlayerState *pAqData)
    {
        UInt32 dataFormatSize = sizeof (AudioStreamBasicDescription);
        OSStatus result;
        bool isFormatVBR;
        UInt32 cookieSize = sizeof (UInt32);
        bool couldNotGetProperty;
    
    
        memset(&(pAqData->mDataFormat), 0, sizeof(AudioStreamBasicDescription));
        pAqData->mDataFormat.mSampleRate = 48000;
        pAqData->mDataFormat.mFormatID = 778924083;//mp3 ID
        pAqData->mDataFormat.mFramesPerPacket = 1152;
        pAqData->mDataFormat.mChannelsPerFrame = 2;
    
    
        result = AudioQueueNewOutput(&(pAqData->mDataFormat),
                                     HandleOutputBuffer,
                                     pAqData,
                                     CFRunLoopGetCurrent(),
                                     kCFRunLoopCommonModes,
                                     0,
                                     &(pAqData->mQueue));
    
        //Configurate the VBR property if any
        isFormatVBR = (pAqData->mDataFormat.mBytesPerPacket == 0 || 
                       pAqData->mDataFormat.mFramesPerPacket == 0);
    
        if(isFormatVBR)
        {
            pAqData->mPacketDescs = 
                (AudioStreamPacketDescription*)malloc(pAqData->mNumPacketsToRead * 
                                                  sizeof(AudioStreamPacketDescription));
        }
        else
        {
            pAqData->mPacketDescs = NULL;
        }
    
        //Set the playback gain
        AudioQueueSetParameter(pAqData->mQueue,
                               kAudioQueueParam_Volume,
                               AQ_PLAYBACK_GAIN);    
    }
    

1 个答案:

答案 0 :(得分:3)

专家:

我找到了根本原因!

问题来自HandleOutputBuffer函数(已更改的函数)!

因为每次,该函数都会传输338个mp3数据包(不仅仅是1个数据包),因此,[pAqData-> mPacketDescs]不是单个变量,它实际上是一个大小为338个数组项的数组。因此,我们必须初始化所有338个数组项。

因此,我们需要更改代码:

static void HandleOutputBuffer(void                *aqData, 
                               AudioQueueRef       inAQ, 
                               AudioQueueBufferRef inBuffer) 
{
    ...
    pAqData->mPacketDescs->mStartOffset = 0;
    pAqData->mPacketDescs->mVariableFramesInPacket = 0;
    pAqData->mPacketDescs->mDataByteSize = 384;
    ...
}

static void HandleOutputBuffer(void                *aqData, 
                               AudioQueueRef       inAQ, 
                               AudioQueueBufferRef inBuffer) 
{
    ...
    for (i = 0; i < 338; i++) 
    {
        pAqData->mPacketDescs[i].mStartOffset = PACKET_SIZE * i;
        pAqData->mPacketDescs[i].mVariableFramesInPacket = 0;
        pAqData->mPacketDescs[i].mDataByteSize = PACKET_SIZE;
    }
    ...
}

最后,问题解决了!