Воспроизведение аудио из непрерывного потока данных (iOS) - PullRequest
8 голосов
/ 07 июля 2011

Все утро бился головой об эту проблему.

Я установил соединение с источником данных, который возвращает аудиоданные (это записывающее устройство, поэтому в данных нет заданной длины. Данные просто передаются. Например, если вы откроете поток для радио)

и мне удалось получить все пакеты данных в моем коде. Теперь мне просто нужно в нее поиграть. Я хочу воспроизвести поступающие данные, поэтому я не хочу ставить в очередь несколько минут или что-то еще, я хочу использовать данные, которые я получаю в тот момент, и воспроизводить их.

Теперь я искал все утро, чтобы найти разные примеры, но ни один из них не был действительно выложен.

в

  • (недействительное) соединение: (NSURLConnection ) соединение didReceiveData: (NSData ) data {

функция, пакет «data» - это аудио пакет. Я пытался транслировать его с помощью AVPlayer, MFVideoPlayer, но пока у меня ничего не получалось. Также попытался взглянуть на Audiostreamer Мэттгаллагера, но все еще не смог его достичь.

Кто-нибудь здесь может помочь, есть некоторые (желательно) рабочие примеры?

1 Ответ

2 голосов
/ 07 июля 2011

Осторожно: приведенный ниже ответ действителен, только если вы получаете данные PCM с сервера.Это, конечно, никогда не происходит.Вот почему между рендерингом аудио и получением данных вам необходим еще один шаг: преобразование данных.

В зависимости от формата, это может быть более или менее сложно, но в целом вы должны использовать Audio Converter Services для этого шага.

Вы должны использовать -(void)connection:(NSURLConnection )connection didReceiveData:(NSData)data только для заполнения буфера с помощьюданные, поступающие с сервера и воспроизводящие его, не должны иметь ничего общего с этим методом.

Теперь, чтобы воспроизвести данные, которые вы «сохранили» в памяти, используя буфер, вам необходимо использовать RemoteIO и аудиоустройства. Вот хороший, всеобъемлющий учебник .Вы можете удалить часть «запись» из руководства, поскольку она вам на самом деле не нужна.

Как вы видите, они определяют обратный вызов для воспроизведения:

callbackStruct.inputProc = playbackCallback;
callbackStruct.inputProcRefCon = self;
status = AudioUnitSetProperty(audioUnit, 
                              kAudioUnitProperty_SetRenderCallback, 
                              kAudioUnitScope_Global, 
                              kOutputBus,
                              &callbackStruct, 
                              sizeof(callbackStruct));

и playbackCallback функция выглядит следующим образом:

static OSStatus playbackCallback(void *inRefCon, 
                          AudioUnitRenderActionFlags *ioActionFlags, 
                          const AudioTimeStamp *inTimeStamp, 
                          UInt32 inBusNumber, 
                          UInt32 inNumberFrames, 
                          AudioBufferList *ioData) {

    for (int i = 0 ; i < ioData->mNumberBuffers; i++){      
        AudioBuffer buffer = ioData->mBuffers[i];
        unsigned char *frameBuffer = buffer.mData;
        for (int j = 0; j < inNumberFrames*2; j++){
            frameBuffer[j] = getNextPacket();//this here is a function you have to make to get the next chunk of bytes available in the stream buffer
        }
    }

    return noErr;
}

В основном этозаполнить буфер ioData следующим фрагментом байтов, которые необходимо воспроизвести.Обязательно обнуляйте (отключайте) буфер ioData, если нет новых данных для воспроизведения (проигрыватель отключается, если в буфере потока недостаточно данных).

Кроме того, вы можете добиться того жечто-то с OpenAL с использованием alSourceQueueBuffers и alSourceUnqueueBuffers для постановки в очередь буферов один за другим.

Вот и все.Удачной кодировки!

...