iPhone: Problems encoding 32KHz PCM to 96Kbit AAC using AudioConverterFillComplexBuffer
Has anyone had success converting 32KHz PCM to 96Kbit AAC on iPhone/iOS?
I can not get this to work correctly on any hardware device. The code I wrote only works correctly in the simulator. When run on current-generation iPad/iPod/iPhone, my code 'skips' large chunks of audio.
The resulting encoded stream contains a repeating pattern of ~640ms of 'good' audio followed by ~640ms of 'bad' audio.
Encoding both 16bit linear and 8.24 fixed-point PCM yielded the same results.
Here is the code to setup an Audio Converter to encode MPEG4-AAC 96kbits @ 32KHz:
AudioStreamBasicDescription descPCMFormat;
descPCMFormat.mSampleRate = 32000;
descPCMFormat.mChannelsPerFrame = 1;
descPCMFormat.mBitsPerChannel = sizeof(AudioUnitSampleType) * 8;
descPCMFormat.mBytesPerPacket = sizeof(AudioUnitSampleType);
descPCMFormat.mFramesPerPacket = 1;
descPCMFormat.mBytesPerFrame = sizeof(AudioUnitSampleType);
descPCMFormat.mFormatID = kAudioFormatLinearPCM;
descPCMFormat.mFormatFlags = kAudioFormatFlagsAudioUnitCanonical;
AudioStreamBasicDescription descAACFormat;
descAACFormat.mSampleRate = 32000;
descAACFormat.mChannelsPerFrame = 1;
descAACFormat.mBitsPerChannel = 0;
descAACFormat.mBytesPerPacket = 0;
descAACFormat.mFramesPerPacket = 1024;
descAACFormat.mBy开发者_StackOverflow社区tesPerFrame = 0;
descAACFormat.mFormatID = kAudioFormatMPEG4AAC;
descAACFormat.mFormatFlags = 0;
AudioConverterNew(& descPCMFormat, & descAACFormat, &m_hCodec);
UInt32 ulBitRate = 96000;
UInt32 ulSize = sizeof(ulBitRate);
AudioConverterSetProperty(m_hCodec, kAudioConverterEncodeBitRate, ulSize, & ulBitRate);
Simple conversion routine. This routine is called every 32ms with a block of 1024 PCM samples, and expects 384 bytes of encoded AAC:
OSStatus CMyObj::Convert(
const AudioUnitSampleType * pSrc,
const size_t ulSrc,
uint8_t * pDst,
size_t & ulDst)
{
// error and sanity checking removed..
// assume caller is converting 1024 samples to at most 384 bytes
OSStatus osStatus;
m_pSrcPtr = (uint8_t*)pSrc;
m_ulSrcLen = ulSrc; // verified to be 1024*sizeof(AudioUnitSampleType);
AudioBufferList destBuffers;
destBuffers.mNumberBuffers = 1;
destBuffers.mBuffers[0].mNumberChannels = 1;
destBuffers.mBuffers[0].mDataByteSize = 384;
destBuffers.mBuffers[0].mData = pDst;
AudioStreamPacketDescription destDescription;
destDescription.mStartOffset = 0;
destDescription.mVariableFramesInPacket = 0;
destDescription.mDataByteSize = 384;
UInt32 ulDstPackets = 1;
osStatus = AudioConverterFillComplexBuffer(
m_hCodec,
InputDataProc,
this,
& ulDstPackets,
& destBuffers,
& destDescription);
ulDst = destBuffers.mBuffers[0].mDataByteSize;
return osStatus;
}
The input data proceedure simply provides the 1024 samples to the encoder:
static OSStatus CMyObj::InputDataProc(
AudioConverterRef hCodec,
UInt32 *pulSrcPackets,
AudioBufferList *pSrcBuffers,
AudioStreamPacketDescription **ppPacketDescription,
void *pUserData)
{
// error and sanity checking removed
CMyObj *pThis = (CMyObj*)pUserData;
const UInt32 ulMaxSrcPackets = pThis->m_ulSrcLen / sizeof(AudioUnitSampleType);
const UInt32 ulRetSrcPackets = min(ulMaxSrcPackets, *pulSrcPackets);
if( ulRetSrcPackets )
{
UInt32 ulRetSrcBytes = ulRetSrcPackets * sizeof(AudioUnitSampleType);
*pulSrcPackets = ulRetSrcPackets;
pSrcBuffers->mBuffers[0].mData = pThis->m_pSrcPtr;
pSrcBuffers->mBuffers[0].mDataByteSize = ulRetSrcBytes;
pSrcBuffers->mBuffers[0].mNumberChannels = 1;
pThis->m_pSrcPtr += ulRetSrcBytes;
pThis-> m_ulSrcLen -= ulRetSrcBytes;
return noErr;
}
*pulSrcPackets = 0;
pSrcBuffers->mBuffers[0].mData = NULL;
pSrcBuffers->mBuffers[0].mDataByteSize = 0;
pSrcBuffers->mBuffers[0].mNumberChannels = 1;
return 500; // local error code to signal end-of-packet
}
Everything works fine when run on the simulator.
When run on the device, however, InputDataProc is not called consistently. For up to 20 times in a row, calls to AudioConverterFillComplexBuffer provoke calls to InputDataProc, and everything looks fine. Then, for the next ~ 21 calls to AudioConverterFillComplexBuffer, InputDataProc will NOT be called. This pattern repeats forever:
-> Convert
-> AudioConverterFillComplexBuffer
-> InputDataProc
-> results in 384 bytes of 'good' AAC
-> Convert
-> AudioConverterFillComplexBuffer
-> InputDataProc
-> results in 384 bytes of 'good' AAC
.. repeats up to 18 more times
-> Convert
-> AudioConverterFillComplexBuffer
-> results in 384 bytes of 'bad' AAC
-> Convert
-> AudioConverterFillComplexBuffer
-> results in 384 bytes of 'bad' AAC
.. repeats up to 18 more times
Where is the converter getting the input data to create the 'bad' AAC, since it isn't calling InputDataProc?
Does anyone see anything glaringly wrong with this approach?
Are there any special settings that need to be made on the hardware codec (MagicCookies or ?) ?
Does the HW AAC codec support 32000 sample rate?
I find that: the default outputBitRate for 32KHz-input-PCM is 48000 bit, the default outputBitRate for 44.1KHz-input-PCM is 64000 bit. When use the the default outputBitRate, 32KHz input makes huge noise. Even use these codes from apple`s sample , 44.1KHz input have a little noise.
Then i fix the outputBitRate to 64kbs, 32KHz & 44.1KHz both works well。
UInt32 outputBitRate = 64000; // 64kbs
UInt32 propSize = sizeof(outputBitRate);
if (AudioConverterSetProperty(m_converter, kAudioConverterEncodeBitRate, propSize, &outputBitRate) != noErr) {
} else {
NSLog(@"upyun.com uplivesdk UPAACEncoder error 102");
}
精彩评论