#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
*********************************************************************************************************************************************************************.
Чтобы объяснить, что я сделал при замене, я хотел бы показать вам основную измененную часть кода, состоящую из двух частей: «перед заменой» и «после замены»
- Перед заменой (воспроизведение локального файла 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?