Использование Audio Queue Services для воспроизведения данных PCM через сокет - PullRequest
11 голосов
/ 08 апреля 2010

Я пишу клиент удаленного рабочего стола для iPhone и пытаюсь реализовать перенаправление звука.
Клиент подключен к серверу через сокетное соединение, и сервер отправляет 32 КБ данных 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--;
}

Спасибо!

Ответы [ 4 ]

1 голос
/ 06 октября 2010

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

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

0 голосов
/ 20 июня 2014

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

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

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

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

РЕДАКТИРОВАТЬ: Пока вы публикуете новый буфер для каждого обратного вызова, вам никогда не потребуется перезапускать воспроизведение или что-либо еще,В моем проигрывателе, если я не закончил обработку буфера, когда следующий буфер «вызывается обратно», поток для этого буфера будет заблокирован до тех пор, пока первый буфер не будет заполнен.Это сделано с NSLock.Это главная причина, по которой AudioQueues сильно страдает от голода, когда я теряю синхронизацию, когда мой проигрыватель еще не понимает 24-битные FLAC.NSLock также предотвращает любые условия гонки, когда AudioQueue дает вам больше буферов для заполнения.Я использую 3 буфера с низкой задержкой.Слишком низкая задержка дает полное молчание, поэтому вам нужно найти «хороший размер» для вашей системы.

0 голосов
/ 26 апреля 2010

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

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

0 голосов
/ 12 апреля 2010

Вы можете найти ответ на этот вопрос полезным: AudioQueue съел мой буфер (первые 15 миллисекунд из него)

...