4

最近、iPhone で AudioQueue Service を使用してネットワーク オーディオ ストリーム (MP3 パケット形式) を再生する方法を見つけるのに苦労していました。

この目標を連続して達成するために、まず AudioQueue を使用して再生する一般的なローカル MP3 ファイルを取得しました。次に、AudioFileReadPackets を stdio 関数 fread に置き換え、ネットワークからのオーディオ ストリームをシミュレートするために、AudioFileReadPackets と同じ量の mp3 パケットを fread するたびに行いました。ただし、今回は次のエラーが発生しました。

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

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

差し替え作業で何をしたかを説明するために、コードの主な変更部分を示したいと思います。「差し替え前」と「差し替え後」の 2 つの部分があります。

  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,
                         &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 data buffer get by 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) オーディオ ファイルを開きます (AudioFileOpenURL の代わりに fopen を使用します)。

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);    
}
4

1 に答える 1

3

彼ら:

根本原因がわかりました!

この問題は、関数 HandleOutputBuffer (変更されたもの) から発生しました!

毎回、関数 fread は mp3 データの 338 パケット (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;
    }
    ...
}

最後に、問題が修正されました!

于 2011-10-01T02:05:27.220 に答える