длительная задержка при использовании супвебсокет-соединения на некоторых платформах
Я хочу получить программу двусторонней голосовой связи, используя SDL_Audio и libsoup. Я создаю сервер и клиент для его сборки.
Сервер создает SoupServer и добавляет обработчик веб-сокетов. Клиент создает SoupSession и подключается к серверу.
тогда и сервер, и клиент могут получить SoupWebsocketConnection. Отсюда они делают почти одно и то же: запускают SDL_Audio и открывают устройство воспроизведения и захвата для выполнения захвата и воспроизведения, отправляют сэмплы на соединение и получают сообщение от соединения. Как сказано в документе, код воспроизведения и захвата будет выполняться в другом потоке.
Программа хорошо работает на MacOS и Linux на базе Intel. Но когда я запускаю его на Linux-боксе на базе Arm (rockpi4a), голос, который я слышу на Macos/Linux на базе Intel, задерживается примерно на 1,8 секунды.
Я думаю, что где-то были кэшированы/поставлены в очередь, но я не знаю, как их изменить. Возможно, производительность коробки на основе руки была низкой, а SDL_Audio или SoupWebsocketConnection автоматически увеличивали размер очереди?
основной код был здесь:
#include <stdio.h>
#include <libsoup/soup.h>
#include <opus.h>
#include <SDL2/SDL.h>
GAsyncQueue *queue = NULL;
OpusEncoder *encoder = NULL;
OpusDecoder *decoder = NULL;
SDL_AudioSpec spec;
SDL_AudioDeviceID playback_id;
SDL_AudioDeviceID capture_id;
void WsMessage(SoupWebsocketConnection *connection,
gint type,
GBytes *message,
gpointer user_data)
{
g_async_queue_lock (queue);
if (g_async_queue_length_unlocked (queue) > 1000)
g_bytes_unref (g_async_queue_pop_unlocked (queue));
g_async_queue_push_unlocked (queue,
message);
g_bytes_ref (message); g_async_queue_unlock (queue);
}
void WsClose (SoupWebsocketConnection *connection,
gpointer user_data)
{
SDL_CloseAudioDevice(playback_id);
puts ("关闭放音设备");
SDL_CloseAudioDevice(capture_id);
puts ("关闭采音设备");
opus_encoder_destroy (encoder);
opus_decoder_destroy (decoder);
while (g_async_queue_length(queue))
g_bytes_unref (g_async_queue_pop (queue));
g_async_queue_unref (queue);
g_object_unref (connection);
SDL_Quit();
}
void PlayAudio (void *userdata,
Uint8 *stream,
int len)
{
SDL_memset (stream, '\0', len);
GBytes *buffer = NULL;
g_async_queue_lock(queue);
if (g_async_queue_length_unlocked (queue))
buffer = g_async_queue_pop_unlocked (queue);
g_async_queue_unlock(queue);
if (buffer)
{
float *pcm = (float *)malloc(len);
int size = opus_decode_float (decoder,
g_bytes_get_data (buffer,
NULL),
g_bytes_get_size (buffer),
pcm,
spec.samples,
0);
if (size > 0)
SDL_memcpy (stream, pcm,
len);
else
{
fprintf (stderr,
"解码结果[%d]。\n",
size);
fprintf (stderr,
" %d: Ok\n",
OPUS_OK);
fprintf (stderr,
"%d: Bad arg\n",
OPUS_BAD_ARG);
fprintf (stderr,
"%d: Buffer too small\n",
OPUS_BUFFER_TOO_SMALL);
fprintf (stderr,
"%d: Internal error\n",
OPUS_INTERNAL_ERROR);
fprintf (stderr,
"%d: Unimplemented\n",
OPUS_UNIMPLEMENTED);
fprintf (stderr,
"%d: Invalid state\n",
OPUS_INVALID_STATE);
fprintf (stderr,
"%d: Alloc fail\n",
OPUS_ALLOC_FAIL);
}
free (pcm);
g_bytes_unref (buffer);
}
}
void CaptAudio (void *userdata,
Uint8 *stream,
int len)
{
SoupWebsocketConnection *connection = (SoupWebsocketConnection *)userdata;
unsigned char *encoded = malloc (len);
gsize size = opus_encode_float (encoder,
(float *)stream,
spec.samples,
encoded,
len);
if (size > 0)
{
if (SOUP_WEBSOCKET_STATE_OPEN == soup_websocket_connection_get_state (connection))
{
soup_websocket_connection_send_binary (connection,
encoded,
size);
}
}
else
{
fprintf (stderr,
"解码结果[%d]。\n",
(int)size); fprintf (stderr,
" %d: Ok\n",
OPUS_OK);
fprintf (stderr,
"%d: Bad arg\n",
OPUS_BAD_ARG);
fprintf (stderr,
"%d: Buffer too small\n",
OPUS_BUFFER_TOO_SMALL);
fprintf (stderr,
"%d: Internal error\n",
OPUS_INTERNAL_ERROR);
fprintf (stderr,
"%d: Unimplemented\n",
OPUS_UNIMPLEMENTED);
fprintf (stderr,
"%d: Invalid state\n",
OPUS_INVALID_STATE);
fprintf (stderr,
"%d: Alloc fail\n",
OPUS_ALLOC_FAIL);
}
free (encoded);
}
void ConnectionInit (SoupWebsocketConnection *connection,
const char *playback_device,
const char *capture_device)
{
SDL_Init (SDL_INIT_AUDIO);
SDL_zero(spec);
spec.freq = 16000;
spec.format = AUDIO_F32SYS;
spec.channels = 1;
spec.samples = spec.freq / 1000 * 20;
spec.callback = PlayAudio;
playback_id = SDL_OpenAudioDevice(playback_device,
FALSE,
&spec,
NULL,
0);
if (!playback_id)
{
fprintf (stderr,
"无法打开放音设备[%s]:%s\n",
playback_device,
SDL_GetError());
goto err_open_playback;
}
spec.callback = CaptAudio;
spec.userdata = connection;
capture_id = SDL_OpenAudioDevice (capture_device,
SDL_TRUE,
&spec,
NULL,
0);
if (!capture_id)
{
fprintf (stderr,
"无法打开采音设备[%s]:%s\n",
capture_device,
SDL_GetError());
goto err_open_capture;
}
encoder = opus_encoder_create(spec.freq,
spec.channels,
OPUS_APPLICATION_VOIP,
NULL);
decoder = opus_decoder_create(spec.freq,
spec.channels,
NULL);
g_signal_connect (connection,
"message",
G_CALLBACK (WsMessage),
connection);
g_signal_connect (connection,
"closed",
G_CALLBACK (WsClose),
NULL);
queue = g_async_queue_new();
SDL_PauseAudioDevice (playback_id,
SDL_FALSE);
puts("打开放音设备。");
SDL_PauseAudioDevice(capture_id,
SDL_FALSE);
puts("打开采音设备。");
g_object_ref (connection);
return;
err_open_capture:
SDL_CloseAudioDevice(playback_id);
err_open_playback:
err_connection:
return;
}