Использование служб очереди звука для воспроизведения данных PCM через соединение сокета

Я пишу клиент удаленного рабочего стола для iPhone, и я пытаюсь реализовать перенаправление звука.
Клиент подключается к серверу через соединение сокета, и сервер одновременно отправляет 32K кусков данных PCM.

Я пытаюсь использовать AQS для воспроизведения данных, и он воспроизводит первые две секунды (1 буфер стоит). Однако, поскольку следующий фрагмент данных еще не попал в сокет, следующий AudioQueueBuffer пуст. Когда данные поступают, я заполняю следующий доступный буфер данными и вставляю его в AudioQueueEnqueueBuffer. Тем не менее, он никогда не воспроизводит эти буферы.

Остается ли очередь останавливаться, если в очереди нет буферов, даже если вы позже добавили буфер?

Здесь соответствующая часть кода:

void
wave_out_write(STREAM s, uint16 tick, uint8 index)
{

    if(items_in_queue == NUM_BUFFERS){
        return;
    }
    if(!playState.busy){
        OSStatus status;
        status = AudioQueueNewOutput(&playState.dataFormat, AudioOutputCallback, &playState, CFRunLoopGetCurrent(), NULL, 0, &playState.queue);

        if(status == 0){
            for(int i=0; i<NUM_BUFFERS; i++){
                AudioQueueAllocateBuffer(playState.queue, 40000, &playState.buffers[i]);

            }
            AudioQueueAddPropertyListener(playState.queue, kAudioQueueProperty_IsRunning, MyAudioQueuePropertyListenerProc, &playState);

            status = AudioQueueStart(playState.queue, NULL);
            if(status ==0){
                playState.busy = True;
            }
            else{
                return;
            }
        }
        else{
            return;
        }
    }
    playState.buffers[queue_hi]->mAudioDataByteSize = s->size;

    memcpy(playState.buffers[queue_hi]->mAudioData, s->data, s->size);

    AudioQueueEnqueueBuffer(playState.queue, playState.buffers[queue_hi], 0, 0);
    queue_hi++;
    queue_hi = queue_hi % NUM_BUFFERS;
    items_in_queue++;
}


void AudioOutputCallback(void* inUserData, AudioQueueRef outAQ, AudioQueueBufferRef outBuffer)
{
    PlayState *playState = (PlayState *)inUserData;
    items_in_queue--;
}

Спасибо!

Ответ 1

Использование служб очереди звукового сопровождения CoreAudio значительно упрощается, гарантируя, что вы всегда переупорядочиваете каждый буфер, как только закончите воспроизведение, делая это в обратном вызове, который запускается, когда воспроизведение завершено. Аудиоданные должны размещаться в отдельном кольцевом буфере, когда он принимается из сети, так что сетевой и звуковой коды не связаны напрямую.

Чтобы вы не потеряли звук, установите в очередь фиксированное количество буферов с данными; это действует как буфер дрожания. Не начинайте воспроизведение до тех пор, пока все буферы не будут поставлены в очередь. Как только каждый буфер завершит воспроизведение, немедленно переставьте его в очередь со следующим пакетом данных. Если данных нет, просто поставьте в очередь буфер молчания; поскольку входящие аудиопакеты в конечном итоге догонят, это просто приводит к уменьшению сброшенного звука за счет дополнительной задержки.

Ответ 3

Как правило, при использовании круговых звуковых буферов вы должны предотвращать недопущение буферов. Если вам не хватает необходимых данных (из-за, например, перегрузки сети), попробуйте отключить звуковые данные или отключите воспроизведение звука.

Возможно, после того, как ваша цепочка буферов перестанет работать, вам необходимо перезапустить воспроизведение. Я никогда не разбирал буфер AudioQueue, но я помню из программирования Win32, что так было, поэтому, пожалуйста, не стесняйтесь меня исправлять, если я ошибаюсь.

Ответ 4

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

"Возможно, после того, как ваша цепочка буферов прервана, вам нужно перезапустить воспроизведение. Я никогда не разбирал буфер AudioQueue, но я помню из программирования Win32, что это так, поэтому, пожалуйста, не стесняйтесь исправлять меня, если Я ошибаюсь."

Я действительно протестировал этот сценарий в своем аудиоплеере, который я недавно сделал. Я сделал декодер FLAC с нуля, и он поддерживает только 16-битные песни только на данный момент. Если я наткнулся на песню, которая составляет 24 бита, я продолжаю проигрывать синхронизацию с песней, которую я играю - она ​​вообще не будет воспроизводиться - и это может занять какое-то время, например, 30 секунд, чтобы сделать пример, чтобы восстановиться. Это очень плохо переносит звуковые очереди, и, когда я, наконец, снова отправляю буферы в очереди аудио, требуется 30 секунд молчания в следующей песне, чтобы она снова воспроизводилась.

Это просто мое наблюдение, и я еще не много думал о том, почему я наблюдаю это поведение. Может быть, он выбрасывает образцы в соответствии с количеством отсчетов. AudioQueue думает, что он должен играть на данный момент - что он потерял во время голода? Мой аудиоплеер, похоже, быстро воспроизводит песню до тех пор, пока не достигнет точки, в которой он хочет играть снова.

EDIT: до тех пор, пока вы отправляете новый буфер для каждого обратного вызова, вам никогда не придется перезапускать игру или что-то еще. В моем проигрывателе, если я не закончил обработку буфера, когда следующий буфер "обратился назад", поток для этого буфера будет заблокирован до завершения первого буфера. Это делается с NSLock. Это основная причина, по которой AudioQueues сильно голодает, когда я теряю синхронизацию, когда мой плеер еще не понимает 24-битные FLAC. NSLock также предотвращает любые условия гонки, когда AudioQueue дает вам больше буферов для заполнения. Я использую 3 буфера с низкой задержкой. Слишком низкая латентность дает полную тишину, поэтому вам нужно найти "хороший размер" для вашей системы.