Skip to content

Commit 31d9b4b

Browse files
fix avoid per-frame heap allocations of support buffers #24
- Add StreamBuffers struct to hold persistent buffers for audio processing - Fix to allocate once per session instead of ~50 malloc/free per second per call - Fix formatting in STREAM_API_SYNTAX help text Signed-off-by: Dario Pellegrino <dario.pellegrino@voismart.it>
1 parent 2fdc118 commit 31d9b4b

File tree

4 files changed

+35
-17
lines changed

4 files changed

+35
-17
lines changed

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.18)
22
project(mod_openai_audio_stream
33
VERSION 1.0.0
44
DESCRIPTION "Audio streaming module for FreeSWITCH."
5-
HOMEPAGE_URL "https://github.com/VoiSmart/mod_openai_audio_stream")
5+
HOMEPAGE_URL "https://github.com/VoiSmart/mod_openai_realtime")
66

77
include(GNUInstallDirs)
88

mod_openai_audio_stream.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -170,9 +170,9 @@ static switch_status_t send_json(switch_core_session_t *session, char* json) {
170170
#define STREAM_API_SYNTAX \
171171
"USAGE:\n" \
172172
"--------------------------------------------------------------------------------\n" \
173-
"uuid_openai_audio_stream <uuid> start <wss-url> <mono | mixed | stereo> \n" \
174-
" [8k | 16k | 24k | <other rate>] [mute_user]\n" \
175-
" where <rate> = 8k|16k|24k or any multiple of 8000 (default: 8k)\n" \
173+
"uuid_openai_audio_stream <uuid> start <wss-url> <mono | mixed | stereo>\n" \
174+
" [8k | 16k | 24k | <other rate>] [mute_user]\n" \
175+
" where <rate> = 8k|16k|24k or any multiple of 8000 (default: 8k)\n" \
176176
"uuid_openai_audio_stream <uuid> [stop | pause | resume]\n" \
177177
"uuid_openai_audio_stream <uuid> [mute | unmute] [user | openai | all]\n" \
178178
"uuid_openai_audio_stream <uuid> send_json <base64json>\n" \

mod_openai_audio_stream.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ struct private_data {
3535
switch_buffer_t *sbuffer;
3636
int rtp_packets;
3737
switch_buffer_t *playback_buffer;
38+
void *stream_buffers;
3839
};
3940

4041
typedef struct private_data private_t;

openai_audio_streamer_glue.cpp

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,19 @@
1515

1616
#define FRAME_SIZE_8000 320 /* 1000x0.02 (20ms)= 160 x(16bit= 2 bytes) 320 frame size*/
1717

18+
// Persistent buffers for stream_frame to avoid per-frame heap allocations
19+
struct StreamBuffers {
20+
std::vector<uint8_t> flush_buffer;
21+
std::vector<spx_int16_t> resample_buffer;
22+
std::vector<uint8_t> data_buf;
23+
24+
StreamBuffers() {
25+
flush_buffer.reserve(SWITCH_RECOMMENDED_BUFFER_SIZE);
26+
resample_buffer.reserve(SWITCH_RECOMMENDED_BUFFER_SIZE / sizeof(spx_int16_t));
27+
data_buf.resize(SWITCH_RECOMMENDED_BUFFER_SIZE);
28+
}
29+
};
30+
1831
class AudioStreamer {
1932
public:
2033

@@ -526,6 +539,7 @@ namespace {
526539
tls_cafile, tls_keyfile, tls_certfile, tls_disable_hostname_validation, sampling, disable_audiofiles);
527540

528541
tech_pvt->pAudioStreamer = static_cast<void *>(as);
542+
tech_pvt->stream_buffers = static_cast<void *>(new StreamBuffers());
529543

530544
switch_mutex_init(&tech_pvt->mutex, SWITCH_MUTEX_NESTED, pool);
531545

@@ -567,6 +581,11 @@ namespace {
567581
delete as;
568582
tech_pvt->pAudioStreamer = nullptr;
569583
}
584+
if (tech_pvt->stream_buffers) {
585+
auto* sb = static_cast<StreamBuffers*>(tech_pvt->stream_buffers);
586+
delete sb;
587+
tech_pvt->stream_buffers = nullptr;
588+
}
570589
}
571590

572591
void finish(private_t* tech_pvt) {
@@ -936,23 +955,21 @@ extern "C" {
936955
return SWITCH_TRUE;
937956
}
938957

939-
// Pre-allocate reusable buffers to avoid repeated allocations
940-
std::vector<uint8_t> flush_buffer;
941-
std::vector<spx_int16_t> resample_buffer;
958+
// Get persistent buffers (allocated once per session, reused across all frames)
959+
auto *bufs = static_cast<StreamBuffers*>(tech_pvt->stream_buffers);
942960

943961
auto flush_sbuffer = [&]() {
944962
switch_size_t inuse = switch_buffer_inuse(tech_pvt->sbuffer);
945963
if (inuse > 0) {
946-
flush_buffer.resize(inuse);
947-
switch_buffer_read(tech_pvt->sbuffer, flush_buffer.data(), inuse);
964+
bufs->flush_buffer.resize(inuse);
965+
switch_buffer_read(tech_pvt->sbuffer, bufs->flush_buffer.data(), inuse);
948966
switch_buffer_zero(tech_pvt->sbuffer);
949-
pAudioStreamer->writeAudioDelta(flush_buffer.data(), inuse);
967+
pAudioStreamer->writeAudioDelta(bufs->flush_buffer.data(), inuse);
950968
}
951969
};
952970

953-
std::vector<uint8_t> data_buf(SWITCH_RECOMMENDED_BUFFER_SIZE);
954971
switch_frame_t frame{};
955-
frame.data = data_buf.data();
972+
frame.data = bufs->data_buf.data();
956973
frame.buflen = SWITCH_RECOMMENDED_BUFFER_SIZE;
957974

958975
while (switch_core_media_bug_read(bug, &frame, SWITCH_TRUE) == SWITCH_STATUS_SUCCESS) {
@@ -1003,23 +1020,23 @@ extern "C" {
10031020
}
10041021
}
10051022

1006-
resample_buffer.resize(out_len * tech_pvt->channels);
1023+
bufs->resample_buffer.resize(out_len * tech_pvt->channels);
10071024

10081025
if (tech_pvt->channels == 1) {
10091026
speex_resampler_process_int(
10101027
tech_pvt->resampler,
10111028
0,
10121029
static_cast<const spx_int16_t *>(frame.data),
10131030
&in_len,
1014-
resample_buffer.data(),
1031+
bufs->resample_buffer.data(),
10151032
&out_len
10161033
);
10171034
} else {
10181035
speex_resampler_process_interleaved_int(
10191036
tech_pvt->resampler,
10201037
static_cast<const spx_int16_t *>(frame.data),
10211038
&in_len,
1022-
resample_buffer.data(),
1039+
bufs->resample_buffer.data(),
10231040
&out_len
10241041
);
10251042
}
@@ -1028,7 +1045,7 @@ extern "C" {
10281045
if (bytes_written > 0) {
10291046
// For 20ms packets, send immediately without buffering
10301047
if (tech_pvt->rtp_packets == 1) {
1031-
pAudioStreamer->writeAudioDelta(reinterpret_cast<uint8_t *>(resample_buffer.data()), bytes_written);
1048+
pAudioStreamer->writeAudioDelta(reinterpret_cast<uint8_t *>(bufs->resample_buffer.data()), bytes_written);
10321049
} else {
10331050
// Check if buffer has enough space before writing
10341051
switch_size_t free_space = switch_buffer_freespace(tech_pvt->sbuffer);
@@ -1039,7 +1056,7 @@ extern "C" {
10391056
if (bytes_written <= free_space) {
10401057
switch_buffer_write(
10411058
tech_pvt->sbuffer,
1042-
reinterpret_cast<const uint8_t *>(resample_buffer.data()),
1059+
reinterpret_cast<const uint8_t *>(bufs->resample_buffer.data()),
10431060
bytes_written
10441061
);
10451062
if (switch_buffer_freespace(tech_pvt->sbuffer) == 0) {

0 commit comments

Comments
 (0)