使用音频队列服务通过套接字连接播放 PCM 数据

发布于 2024-08-28 05:28:19 字数 1738 浏览 12 评论 0原文

我正在为 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--;
}

谢谢!

I'm writing a remote desktop client for the iPhone and I'm trying to implement audio redirection.
The client is connected to the server over a socket connection, and the server sends 32K chunks of PCM data at a time.

I'm trying to use AQS to play the data and it plays the first two seconds (1 buffer worth). However, since the next chunk of data hasn't come in over the socket yet, the next AudioQueueBuffer is empty. When the data comes in, I fill the next available buffer with the data and enqueue it with AudioQueueEnqueueBuffer. However, it never plays these buffers.

Does the queue stop playing if there are no buffers in the queue, even if you later add a buffer?

Here's the relevant part of the code:

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--;
}

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

黎歌 2024-09-04 05:28:19

通过在播放完成时触发的回调中执行此操作,确保您始终在播放完成后立即重新排队每个缓冲区,从而大大简化了 CoreAudio 音频排队服务的使用。从网络接收音频数据时,音频数据应位于单独的循环缓冲区中,以便网络和音频代码不会直接耦合。

为了确保不会丢失音频,请将固定数量的缓冲区与数据排队;这充当抖动缓冲器。在所有缓冲区排队之前不要开始播放。一旦每个缓冲区完成播放,就立即将其与下一个数据包重新排队。如果没有数据可用,就排队一个静默的缓冲区;由于到达的音频数据包最终会赶上,因此这只会减少音频丢失,但会增加额外的延迟。

Using CoreAudio's audio queuing services is vastly simplified by ensuring that you always re-queue every buffer as soon as it's finished playback by doing so in the callback that is fired when playback is finished. The audio data should sit in a separate circular buffer as it is received from the network so that the network and audio code aren't directly coupled.

To ensure that you don't drop audio, queue up a fixed number of buffers with data; this acts as a jitter buffer. Do not start playback until all the buffers are queued. As soon as each buffer is finished playback, re-queue it immediately with the next packet of data. If no data is available, just queue a buffer of silence; since the arriving audio packets will eventually catch up, this just has the effect of reducing dropped audio at the expense of extra latency.

羁绊已千年 2024-09-04 05:28:19

您可能会发现这个问题的答案很有用: AudioQueue 吃了我的缓冲区(前 15 毫秒)

You may find the answer to this question useful: AudioQueue ate my buffer (first 15 milliseconds of it)

装纯掩盖桑 2024-09-04 05:28:19

一般来说,当使用循环音频缓冲区时,您应该防止缓冲区运行不足。如果您缺少必要的数据(例如由于网络拥塞),请尝试用静音填充音频数据或暂停音频播放。

一旦您的缓冲链运行不足,您可能需要重新启动播放。我从来没有真正地运行过 AudioQueue 缓冲区,但我记得在 Win32 编程中确实是这样,所以如果我错了,请随时纠正我。

Generally, when using circular audio buffers, you should prevent the buffers from underrunning. If you lack the necessary data (due to e.g. network congestion), try to pad your audio data with silence or pause the audio playback.

It could be that once your buffer chain underruns, you need to restart playback. I've never actually underrunned the AudioQueue buffer, but I remember from Win32 programming that this was the case, so please feel free to correct me if I'm wrong.

青春有你 2024-09-04 05:28:19

我觉得很愚蠢的是,如果我没有足够的积分,我可以在这里发布答案,但不能发表评论。我只是想补充以下答案:

“可能是,一旦您的缓冲区链欠载,您需要重新启动播放。我实际上从未真正欠载 AudioQueue 缓冲区,但我记得 Win32 编程就是这种情况,所以如果我错了,请随时纠正我。”

我实际上在我最近制作的音频播放器中测试了这种情况。我从头开始制作了一个FLAC解码器,目前仅支持16位歌曲。如果我偶然发现一首 24 位的歌曲,我就会不断地与正在播放的歌曲失去同步 - 它根本不会播放 - 并且可能需要任何时间(例如 30 秒)才能恢复。这使音频队列严重饥饿,当我最终开始再次向音频队列发送缓冲区时,需要 30 秒的静音才能赶上下一首歌曲才能再次播放。

这只是我的观察,我还没有仔细思考为什么我会观察到这种行为。也许它会丢弃样本以匹配 AudioQueue 认为它现在应该播放的样本数 - 它在饥饿期间丢失了?我的音频播放器似乎快速播放歌曲,直到达到它想要再次播放的程度。

编辑:只要您为每个回调发布一个新的缓冲区,您就永远不需要重新启动播放或任何其他操作。在我的播放器中,如果在“回调”下一个缓冲区时我没有完成对缓冲区的处理,则该缓冲区的线程将被阻塞,直到第一个缓冲区完成填充。这是通过 NSLock 完成的。这是当我的播放器还不理解 24 位 FLAC 时失去同步时 AudioQueues 严重匮乏的主要原因。当 AudioQueue 为您提供更多缓冲区来填充时,NSLock 还可以防止任何竞争条件。我使用 3 个低延迟缓冲区。太低的延迟会导致完全安静,因此您需要为您的系统找到一个“合适的大小”。

I find it stupid that I can post answers but not comments here if I don't have enough points. I just wanted to add to the following answer:

"It could be that once your buffer chain underruns, you need to restart playback. I've never actually underrunned the AudioQueue buffer, but I remember from Win32 programming that this was the case, so please feel free to correct me if I'm wrong."

I have actually tested this scenario in my audio player that I have recently made. I made a FLAC decoder from scratch, and it supports 16 bits songs only at the moment. If I stumble upon a song that is 24 bits, I keep losing sync with the song I am playing - it won't play at all - and that can take any amount of time, say 30 seconds to make an example, to recover. This starves the Audio Queues really badly, and when I finally begin sending buffers again to audio queues, it takes 30 seconds of silence-catching-up into the next song to make it play again.

This is just my observation, and I have not yet put much thought into why I am observing this behaviour. Maybe it throws away samples to match the sample count AudioQueue thinks it should be playing at the moment - that it lost during starvation? My audio player seems to fast-play the song till is reaches a point it wants to play again.

EDIT: As long as you post a new buffer for every callback, you will never need to restart play or anything. In my player, if I am not done processing a buffer when the next buffer is 'called back', the thread for that buffer will be blocked till the first buffer is done filling. This is done with NSLock. This is the main reason AudioQueues is severely starved when I lose sync when my player don't yet understand 24 bit FLACs. NSLock also prevents any race conditions when AudioQueue gives you more buffers to fill. I use 3 buffers with low latency. Too low latency gives complete silence, so you need to find a 'good size' for your system.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文