Воспроизведение буфера данных MP3 с помощью службы AudioQueue: сбой Prime (-50)

#iphone #ios #audio-streaming #audioqueueservices #audiotoolbox

#iPhone #iOS #потоковое аудио #audioqueueservices #audiotoolbox

Вопрос:

В эти дни я изо всех сил пытался найти способ воспроизведения сетевого аудиопотока (в формате MP3 packet) с помощью службы AudioQueue на iPhone.

Чтобы последовательно достичь этой цели, я сначала получил общий локальный файл MP3 для воспроизведения с помощью AudioQueue, и он действительно работает. А затем я заменил пакеты AudioFileReadPackets на stdio function fread, и каждый раз я загружаю такое же количество пакетов mp3, что и пакеты AudioFileReadPackets, чтобы имитировать аудиопоток, поступающий из сети. Однако на этот раз произошла ошибка:

 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 (-50)»? Помогите zzzzzzz

*********************************************************************************************************************************************************************.

Чтобы объяснить, что я сделал при замене, я хотел бы показать вам основную измененную часть кода, состоящую из двух частей: «перед заменой» и «после замены»

  1. Перед заменой (воспроизведение локального файла MP3):

1) Обратный вызов цикла выполнения очереди аудио

 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,
                         amp;numBytesReadFromFile,
                         pAqData->mPacketDescs,
                         pAqData->mCurrentPacket,
                         amp;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 resu<
    UInt32 maxPacketSize;
    UInt32 propertySize = sizeof (maxPacketSize);

    audioFileURL = 
        CFURLCreateFromFileSystemRepresentation(NULL,
                                                (const UInt8 *) filePath,
                                                strlen (filePath),
                                                false);

    result = AudioFileOpenURL(audioFileURL,
                              kAudioFileReadPermission,
                              0,
                              amp;(pAqData->mAudioFile));
    CFRelease(audioFileURL);

    AudioFileGetProperty(pAqData->mAudioFile,
                         kAudioFilePropertyPacketSizeUpperBound,
                         amp;propertySize,
                         amp;maxPacketSize);

    DeriveBufferSize(pAqData->mDataFormat,
                     maxPacketSize,
                     0.5,
                     amp;(pAqData->bufferByteSize),
                     amp;(pAqData->mNumPacketsToRead));

    return resu<
}
  

3) Create audio queue

 OSStatus AqCreateAudioQueue(AQPlayerState *pAqData)
{
    UInt32 dataFormatSize = sizeof (AudioStreamBasicDescription);
    OSStatus resu<
    bool isFormatVBR;
    UInt32 cookieSize = sizeof (UInt32);
    bool couldNotGetProperty;

    AudioFileGetProperty(pAqData->mAudioFile,
                         kAudioFilePropertyDataFormat,
                         amp;dataFormatSize,
                         amp;(pAqData->mDataFormat));

    result = AudioQueueNewOutput(amp;(pAqData->mDataFormat),
                                 HandleOutputBuffer,
                                 pAqData,
                                 CFRunLoopGetCurrent(),
                                 kCFRunLoopCommonModes,
                                 0,
                                 amp;(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,
                                 amp;cookieSize,
                                 NULL);

    if (!couldNotGetProperty amp;amp; cookieSize)
    {
        char* magicCookie = (char *)malloc(cookieSize);
        AudioFileGetProperty(pAqData->mAudioFile,
                             kAudioFilePropertyMagicCookieData,
                             amp;cookieSize,
                             magicCookie);

        AudioQueueSetProperty(pAqData->mQueue,
                              kAudioQueueProperty_MagicCookie,
                              magicCookie,
                              cookieSize);
        free(magicCookie);
    }

    //Set the playback gain
    AudioQueueSetParameter(pAqData->mQueue,
                           kAudioQueueParam_Volume,
                           AQ_PLAYBACK_GAIN);    
}
  

2. After replacing (playbak mp3 data buffer get by fread):

In order to make the code porting smoothly, I copied the run-time value of critical variables, like pAqData->bufferByteSize, pAqData->mNumPacketsToRead, pAqData->mDataFormat…etc. And directly initialize these variables using the copied value in the replaced code. The intent of this behavior is to discard involking the interfaces of AudioToolbox framework like: AudioFileOpenURL, AudioFileGetProperty, AudioFileReadPackets… And then, we can use the stdio function fread to get the mp3 packet directly. The changed code is shown below:

1) The audio queue runloop callback (In previous code, the AudioFileReadPackets read 338 packets, and totally 129792 bytes, I copied these values directly into the replaced code)

 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 resu<
    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 resu<
}
  

3) Создайте очередь аудио (непосредственно инициализируйте pAqData-> mDataFormat значением, присвоенным в локальном режиме воспроизведения MP3)

 OSStatus AqCreateAudioQueue(AQPlayerState *pAqData)
{
    UInt32 dataFormatSize = sizeof (AudioStreamBasicDescription);
    OSStatus resu<
    bool isFormatVBR;
    UInt32 cookieSize = sizeof (UInt32);
    bool couldNotGetProperty;


    memset(amp;(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(amp;(pAqData->mDataFormat),
                                 HandleOutputBuffer,
                                 pAqData,
                                 CFRunLoopGetCurrent(),
                                 kCFRunLoopCommonModes,
                                 0,
                                 amp;(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:

Ребята:

Я нашел основную причину!

Проблема возникла из-за функции 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;
    }
    ...
}
  

Наконец, проблема устранена!

Комментарии:

1. У меня та же проблема. Но я не могу понять, где я должен изменить или переопределить HandleOutputBuffer?