2

我目前正在通过 Internet 传输 mp3 音频。我正在使用 AudioFileStream 解析来自 CFReadStreamRef 的 mp3 流,使用 AudioConverterFillComplexBuffer 解码 mp3 并将转换后的 PCM 数据复制到环形缓冲区中,最后使用 RemoteIO 播放 PCM。

我目前面临的问题是 AudioConverterFillComplexBuffer 总是返回 0(没有错误),但转换结果似乎不正确。在细节上,我可以注意到,

A. UInt32 *ioOutputDataPacketSize 保持与我发送的相同值。

B. convertData.mBuffers[0].mDataByteSize 总是设置为输出缓冲区的大小(不管缓冲区有多大)。

C. 我只能听到输出数据的咔嗒声。

以下是我渲染音频的程序。同样的过程适用于我的音频队列实现,所以我相信我在调用 AudioConverterFillComplexBuffer 或 AudioConverterFillComplexBuffer 回调的地方都没有错。

我在这个问题上被困了很长时间。任何帮助将不胜感激。

  1. 打开一个音频文件流。

    // 创建音频文件流解析器 AudioFileTypeID fileTypeHint = kAudioFileMP3Type;
    AudioFileStreamOpen(self, MyPropertyListenerProc, MyPacketsProc, fileTypeHint, &audioFileStream);

  2. 在回调函数(“MyPacketsProc”)中处理解析的数据。

    void MyPacketsProc(void * inClientData, UInt32 inNumberBytes, UInt32 inNumberPackets, const void * inInputData, AudioStreamPacketDescription *inPacketDescriptions) { @synchronized(self) { // 初始化音频转换器。if (!audioConverter) AudioConverterNew(&asbd, &asbd_out, &audioConverter);

        struct mp3Data mSettings;
        memset(&mSettings, 0, sizeof(mSettings));
    
    
        UInt32 packetsPerBuffer = 0;
        UInt32 outputBufferSize = 1024 * 32; // 32 KB is a good starting point.
        UInt32 sizePerPacket = asbd.mBytesPerPacket;
    
    
        // Calculate the size per buffer.
        // Variable Bit Rate Data.
        if (sizePerPacket == 0)
        {
            UInt32 size = sizeof(sizePerPacket);
            AudioConverterGetProperty(audioConverter, kAudioConverterPropertyMaximumOutputPacketSize, &size, &sizePerPacket);
    
            if (sizePerPacket > outputBufferSize)
                outputBufferSize = sizePerPacket;
    
            packetsPerBuffer = outputBufferSize / sizePerPacket;
        }
        //CBR
        else
            packetsPerBuffer = outputBufferSize / sizePerPacket;
    
    
    
        // Prepare the input data for the callback.
        mSettings.inputBuffer.mDataByteSize = inNumberBytes;
        mSettings.inputBuffer.mData = (void *)inInputData;
        mSettings.inputBuffer.mNumberChannels = 1;
        mSettings.numberPackets = inNumberPackets;
        mSettings.packetDescription = inPacketDescriptions;
    
    
        // Set up our output buffers
        UInt8 * outputBuffer = (UInt8*)malloc(sizeof(UInt8) * outputBufferSize);
        memset(outputBuffer, 0, outputBufferSize);
    
    
        // describe output data buffers into which we can receive data.
        AudioBufferList convertedData;
        convertedData.mNumberBuffers = 1;
        convertedData.mBuffers[0].mNumberChannels = 1;
        convertedData.mBuffers[0].mDataByteSize = outputBufferSize;
        convertedData.mBuffers[0].mData = outputBuffer;
    
        // Convert.
        UInt32 ioOutputDataPackets = packetsPerBuffer;
        OSStatus result = AudioConverterFillComplexBuffer(audioConverter,                
                                                          converterComplexInputDataProc, 
                                                          &mSettings,                          
                                                          &ioOutputDataPackets,          
                                                          &convertedData,            
                                                          NULL                 
                                                          );
    
        // Enqueue the ouput pcm data.
        TPCircularBufferProduceBytes(&m_pcmBuffer, convertedData.mBuffers[0].mData, convertedData.mBuffers[0].mDataByteSize);
        free(outputBuffer);
    }
    

    }

  3. 从其回调函数(“converterComplexInputDataProc”)向音频转换器提供数据。

OSStatus converterComplexInputDataProc(AudioConverterRef inAudioConverter, UInt32* ioNumberDataPackets, AudioBufferList* ioData, AudioStreamPacketDescription** ioDataPacketDescription, void* inUserData) { struct mp3Data THIS = (struct mp3Data ) inUserData;

if (THIS->inputBuffer.mDataByteSize > 0)
{
    *ioNumberDataPackets = THIS->numberPackets;
    ioData->mNumberBuffers = 1;
    ioData->mBuffers[0].mDataByteSize = THIS->inputBuffer.mDataByteSize;
    ioData->mBuffers[0].mData = THIS->inputBuffer.mData;
    ioData->mBuffers[0].mNumberChannels = 1;

    if (ioDataPacketDescription)
        *ioDataPacketDescription = THIS->packetDescription;
}
else
    *ioDataPacketDescription = 0;

return 0;

}

  1. 使用 RemoteIO 组件播放。

  2. 输入和输出 AudioStreamBasicDescription。

输入:

采样率:16000 格式 ID:.mp3

格式标志:0

每个数据包的字节数:0

每包帧数:576

每帧字节数:0

每帧通道数:1

每通道位数:0

输出:

采样率:44100

格式 ID:lpcm

格式标志:3116

每个数据包的字节数:4

每包帧数:1

每帧字节数:4

每帧通道数:1

每通道位数:32

4

0 回答 0