Hi,
I am currently working on streaming mp3 audio through the Internet. I am using AudioFileStream to parse the mp3 steam
comes through a CFReadStreamRef, decode the mp3 using AudioConverterFillComplexBuffer and copy the converted PCM
data into a ring buffer and finally play the PCM using RemoteIO.
The problem I am currently facing is the AudioConverterFillComplexBuffer always returns 0 (no error) but the conversion
result seems incorrect. In details, I can notice,
A. The UInt32 *ioOutputDataPacketSize keeps the same value I sent in,
B. The convertedData.mBuffers[0].mDataByteSize always been set to the size of the outputbuffer (doesn't matter how big the buffer is).
C. I can only hear clicking noise with the output data.
Below are my procedures for rendering the audio.
The same procedure works for my Audio queue implementation so I believe
I did something wrong in either the place I invoking the AudioConverterFillComplexBuffer or
the callback of AudioConverterFillComplexBuffer.
I have been stuck on this issue for a long time. Any help will be highly appreciated.
1. Open a AudioFileStream.
// create an audio file stream parser
AudioFileTypeID fileTypeHint = kAudioFileMP3Type;
AudioFileStreamOpen(self, MyPropertyListenerProc, MyPacketsProc, fileTypeHint, &audioFileStream);
2. Handle the parsed data in the callback function ("MyPacketsProc").
void MyPacketsProc(void * inClientData,
UInt32 inNumberBytes,
UInt32 inNumberPackets,
const void * inInputData,
AudioStreamPacketDescription *inPacketDescriptions)
{
@synchronized(self)
{
// Init the audio converter.
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;
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;
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. Feed the audio converter from its callback function ("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;
}
4. Playback using the RemoteIO component.
5. The input and output AudioStreamBasicDescription.
Input:
2012-11-21 12:12:32.197 BAM_IOS[48999:4b03] Sample Rate: 16000
2012-11-21 12:12:32.197 BAM_IOS[48999:4b03] Format ID: .mp3
2012-11-21 12:12:32.198 BAM_IOS[48999:4b03] Format Flags: 0
2012-11-21 12:12:32.198 BAM_IOS[48999:4b03] Bytes per Packet: 0
2012-11-21 12:12:32.199 BAM_IOS[48999:4b03] Frames per Packet: 576
2012-11-21 12:12:32.199 BAM_IOS[48999:4b03] Bytes per Frame: 0
2012-11-21 12:12:32.199 BAM_IOS[48999:4b03] Channels per Frame: 1
2012-11-21 12:12:32.200 BAM_IOS[48999:4b03] Bits per Channel: 0
output:
2012-11-21 12:12:32.200 BAM_IOS[48999:4b03] Sample Rate: 44100
2012-11-21 12:12:32.200 BAM_IOS[48999:4b03] Format ID: lpcm
2012-11-21 12:12:32.201 BAM_IOS[48999:4b03] Format Flags: 3116
2012-11-21 12:12:32.201 BAM_IOS[48999:4b03] Bytes per Packet: 4
2012-11-21 12:12:32.202 BAM_IOS[48999:4b03] Frames per Packet: 1
2012-11-21 12:12:32.202 BAM_IOS[48999:4b03] Bytes per Frame: 4
2012-11-21 12:12:32.205 BAM_IOS[48999:4b03] Channels per Frame: 1
2012-11-21 12:12:32.205 BAM_IOS[48999:4b03] Bits per Channel: 32