diff --git a/buildspec.json b/buildspec.json index 57e3ee1..2653476 100644 --- a/buildspec.json +++ b/buildspec.json @@ -1,45 +1,45 @@ { - "dependencies": { - "obs-studio": { - "version": "31.1.1", - "baseUrl": "https://github.com/obsproject/obs-studio/archive/refs/tags", - "label": "OBS sources", - "hashes": { - "macos": "39751f067bacc13d44b116c5138491b5f1391f91516d3d590d874edd21292291", - "windows-x64": "2c8427c10b55ac6d68008df2e9a3e82f4647aaad18f105e30d4713c2de678ccf" - } - }, - "prebuilt": { - "version": "2025-07-11", - "baseUrl": "https://github.com/obsproject/obs-deps/releases/download", - "label": "Pre-Built obs-deps", - "hashes": { - "macos": "495687e63383d1a287684b6e2e9bfe246bb8f156fe265926afb1a325af1edd2a", - "windows-x64": "c8c642c1070dc31ce9a0f1e4cef5bb992f4bff4882255788b5da12129e85caa7" - } - }, - "qt6": { - "version": "2025-07-11", - "baseUrl": "https://github.com/obsproject/obs-deps/releases/download", - "label": "Pre-Built Qt6", - "hashes": { - "macos": "d3f5f04b6ea486e032530bdf0187cbda9a54e0a49621a4c8ba984c5023998867", - "windows-x64": "0e76bf0555dd5382838850b748d3dcfab44a1e1058441309ab54e1a65b156d0a" - }, - "debugSymbols": { - "windows-x64": "11b7be92cf66a273299b8f3515c07a5cfb61614b59a4e67f7fc5ecba5e2bdf21" - } - } + "dependencies": { + "obs-studio": { + "version": "31.1.1", + "baseUrl": "https://github.com/obsproject/obs-studio/archive/refs/tags", + "label": "OBS sources", + "hashes": { + "macos": "39751f067bacc13d44b116c5138491b5f1391f91516d3d590d874edd21292291", + "windows-x64": "2c8427c10b55ac6d68008df2e9a3e82f4647aaad18f105e30d4713c2de678ccf" + } }, - "platformConfig": { - "macos": { - "bundleId": "com.example.audio-wave" - } + "prebuilt": { + "version": "2025-07-11", + "baseUrl": "https://github.com/obsproject/obs-deps/releases/download", + "label": "Pre-Built obs-deps", + "hashes": { + "macos": "495687e63383d1a287684b6e2e9bfe246bb8f156fe265926afb1a325af1edd2a", + "windows-x64": "c8c642c1070dc31ce9a0f1e4cef5bb992f4bff4882255788b5da12129e85caa7" + } }, - "name": "audio-wave", - "displayName": "Create audio waves out of any audio source", - "version": "1.2.0", - "author": "MMLTech", - "website": "https://ko-fi.com/mmltech", - "email": "contact@obscountdown.com" + "qt6": { + "version": "2025-07-11", + "baseUrl": "https://github.com/obsproject/obs-deps/releases/download", + "label": "Pre-Built Qt6", + "hashes": { + "macos": "d3f5f04b6ea486e032530bdf0187cbda9a54e0a49621a4c8ba984c5023998867", + "windows-x64": "0e76bf0555dd5382838850b748d3dcfab44a1e1058441309ab54e1a65b156d0a" + }, + "debugSymbols": { + "windows-x64": "11b7be92cf66a273299b8f3515c07a5cfb61614b59a4e67f7fc5ecba5e2bdf21" + } + } + }, + "platformConfig": { + "macos": { + "bundleId": "com.example.audio-wave" + } + }, + "name": "audio-wave", + "displayName": "Create audio waves out of any audio source", + "version": "1.3.0", + "author": "MMLTech", + "website": "https://ko-fi.com/mmltech", + "email": "contact@obscountdown.com" } diff --git a/src/audio-wave.cpp b/src/audio-wave.cpp index e4316c6..81c04da 100644 --- a/src/audio-wave.cpp +++ b/src/audio-wave.cpp @@ -8,6 +8,8 @@ #include #include +#include + #define BLOG(log_level, format, ...) \ blog(log_level, "[audio-wave] " format, ##__VA_ARGS__) @@ -166,18 +168,33 @@ static bool enum_audio_sources(void *data, obs_source_t *source) static void audio_capture_cb(void *param, obs_source_t *, const struct audio_data *audio, bool muted) { - auto *s = static_cast(param); - if (!s || !audio) - return; +auto *s = static_cast(param); +if (!s || !audio) + return; - if (muted || audio->frames == 0 || !audio->data[0]) - return; +// Lifetime guard: the callback may fire while the source is being destroyed. +if (!s->alive.load(std::memory_order_acquire)) + return; + +s->audio_cb_inflight.fetch_add(1, std::memory_order_acq_rel); + +// Re-check after increment in case destroy flipped alive concurrently. +if (!s->alive.load(std::memory_order_acquire)) { + s->audio_cb_inflight.fetch_sub(1, std::memory_order_acq_rel); + return; +} + +if (muted || audio->frames == 0 || !audio->data[0]) { + s->audio_cb_inflight.fetch_sub(1, std::memory_order_acq_rel); + return; +} - const size_t frames = audio->frames; +const size_t frames = audio->frames; - const float *left = reinterpret_cast(audio->data[0]); - const float *right = audio->data[1] ? reinterpret_cast(audio->data[1]) : nullptr; +const float *left = reinterpret_cast(audio->data[0]); +const float *right = audio->data[1] ? reinterpret_cast(audio->data[1]) : nullptr; +{ std::lock_guard lock(s->audio_mutex); if (s->samples_left.size() != frames) @@ -194,6 +211,9 @@ static void audio_capture_cb(void *param, obs_source_t *, const struct audio_dat s->num_samples = frames; } +s->audio_cb_inflight.fetch_sub(1, std::memory_order_acq_rel); +} + static void attach_to_audio_source(audio_wave_source *s) { if (!s) @@ -431,26 +451,37 @@ static void *audio_wave_create(obs_data_t *settings, obs_source_t *source) static void audio_wave_destroy(void *data) { - auto *s = static_cast(data); - if (!s) - return; +auto *s = static_cast(data); +if (!s) + return; - detach_from_audio_source(s); +// Stop the audio callback from touching this instance. +s->alive.store(false, std::memory_order_release); - if (s->theme && s->theme->destroy_data) { - s->theme->destroy_data(s); - s->theme_data = nullptr; - } +// Detach callback first. +detach_from_audio_source(s); - { - std::lock_guard lock(s->audio_mutex); - s->samples_left.clear(); - s->samples_right.clear(); - s->wave.clear(); - s->num_samples = 0; - } +// Wait briefly for an in-flight callback to finish (prevents use-after-free). +for (int i = 0; i < 2000; ++i) { + if (s->audio_cb_inflight.load(std::memory_order_acquire) == 0) + break; + os_sleep_ms(1); +} + +if (s->theme && s->theme->destroy_data) { + s->theme->destroy_data(s); + s->theme_data = nullptr; +} + +{ + std::lock_guard lock(s->audio_mutex); + s->samples_left.clear(); + s->samples_right.clear(); + s->wave.clear(); + s->num_samples = 0; +} - delete s; +delete s; } static void audio_wave_show(void *data) diff --git a/src/includes/audio-wave.hpp b/src/includes/audio-wave.hpp index d1f8c77..e4445fe 100644 --- a/src/includes/audio-wave.hpp +++ b/src/includes/audio-wave.hpp @@ -7,6 +7,7 @@ #include #include #include +#include // ───────────────────────────────────────────── // Core setting keys @@ -39,6 +40,10 @@ struct audio_wave_source { std::string audio_source_name; obs_weak_source_t *audio_weak = nullptr; + // Lifetime guards for audio callback (prevents use-after-free during destroy) + std::atomic alive{true}; + std::atomic audio_cb_inflight{0}; + std::mutex audio_mutex; std::vector samples_left; std::vector samples_right;