From 1e59784e0630f0dbce598087e85b7812ff29529a Mon Sep 17 00:00:00 2001 From: Niellun Date: Mon, 26 May 2025 12:16:29 +0300 Subject: [PATCH] Callback based audio player --- settings.txt | 7 +- src/decoder.cpp | 9 +- src/pcm_audio.cpp | 170 ++++++++++++++++++++++---------------- src/pcm_audio.h | 15 +++- src/protocol.cpp | 8 +- src/settings.h | 4 +- src/struct/atomic_queue.h | 27 +++--- 7 files changed, 144 insertions(+), 96 deletions(-) diff --git a/settings.txt b/settings.txt index 14a3938..2ae2156 100644 --- a/settings.txt +++ b/settings.txt @@ -41,7 +41,12 @@ #logging = false # Size of video and audio buffers. Increase if you see artifacts -#queue-size = 32 +#video-buffer-size = 32 +#audio-buffer-size = 16 + +# Audio delay. Fill the buffer to this value before start playing. Increase if you hear audio artifacts. +# Should be less than audio-buffer-size +#audio-buffer-wait = 2 # Font size for messgaes on screen. Set to 0 is you do not want any #font-size = 30 diff --git a/src/decoder.cpp b/src/decoder.cpp index e6bcc6d..6454007 100644 --- a/src/decoder.cpp +++ b/src/decoder.cpp @@ -135,14 +135,11 @@ void Decoder::loop(AVCodecContext *context, AVCodecParserContext *parser, AVPack uint32_t counter = 0; // Main decoding loop; runs until global_quit flag is set + _data->wait(_active); while (_active) { // Get raw data segment from queue - std::unique_ptr segment = _data->wait(_active); - - if (!_active) - continue; - + std::unique_ptr segment = _data->pop(); uint8_t *data_ptr = segment->data(); int data_size = segment->length(); @@ -193,5 +190,7 @@ void Decoder::loop(AVCodecContext *context, AVCodecParserContext *parser, AVPack _vb->commit(); } } + + _data->wait(_active); } } diff --git a/src/pcm_audio.cpp b/src/pcm_audio.cpp index 1d1833b..6c454c2 100644 --- a/src/pcm_audio.cpp +++ b/src/pcm_audio.cpp @@ -1,5 +1,14 @@ #include "pcm_audio.h" #include "helper/functions.h" +#include "settings.h" + +ChannelConfig PcmAudio::_configTable[] = { + {8000, 1}, // type = 3 + {48000, 2}, // type = 4 + {16000, 1}, // type = 5 + {24000, 1}, // type = 6 + {16000, 2}, // type = 7 +}; // Implementation PcmAudio::PcmAudio() {} @@ -9,6 +18,60 @@ PcmAudio::~PcmAudio() stop(); } +void PcmAudio::callback(void *userdata, Uint8 *stream, int len) +{ + PcmAudio *self = static_cast(userdata); + const Message *segment = self->_data->peek(); + if (segment && self->_offset == 0 && getConfig(segment->getInt(OFFSET_AUDIO_FORMAT)) != self->_config) + { + self->_reconfig = true; + self->_cv.notify_one(); + } + while (len > 0) + { + if (segment == nullptr || self->_reconfig) + { + std::fill_n(stream, len, 0); + if (self->_nodata++ > NO_DATA_FRAMES) + { + self->_reconfig = true; + self->_cv.notify_one(); + } + return; + } + + self->_nodata = 0; + int remain = segment->length() - self->_offset; + uint8_t *data = segment->data() + self->_offset; + + if (remain > len) + { + std::memcpy(stream, data, len); + self->_offset = self->_offset + len; + return; + } + + std::memcpy(stream, data, remain); + len = len - remain; + stream = stream + remain; + self->_data->pop(); + self->_offset = 0; + segment = self->_data->peek(); + if (segment && getConfig(segment->getInt(OFFSET_AUDIO_FORMAT)) != self->_config) + { + self->_reconfig = true; + self->_cv.notify_one(); + } + } +} + +ChannelConfig PcmAudio::getConfig(int type) +{ + if (type >= 3 && type <= 7) + return _configTable[type - 3]; + return {44100, 2}; +} + void PcmAudio::setVolume(float vol) { if (vol < 0) @@ -39,23 +102,7 @@ void PcmAudio::stop() return; _active = false; _data->notify(); - if (_thread.joinable()) - _thread.join(); -} - -static ChannelConfig configTable[] = { - {8000, 1}, // type = 3 - {48000, 2}, // type = 4 - {16000, 1}, // type = 5 - {24000, 1}, // type = 6 - {16000, 2}, // type = 7 -}; - -ChannelConfig PcmAudio::getConfig(int type) const -{ - if (type >= 3 && type <= 7) - return configTable[type - 3]; - return {44100, 2}; + _cv.notify_all(); } void PcmAudio::runner() @@ -64,72 +111,55 @@ void PcmAudio::runner() SDL_AudioDeviceID device = 0; SDL_AudioSpec spec; - size_t bufferedBytes = 0; - bool unpaused = false; - size_t targetBytes = 0; - ChannelConfig config = {0, 0}; while (_active) { - unique_ptr segment = _data->wait(_active); - if (!_active) - break; + printf("AUDIO - WAIT BUFFER\n"); + _data->wait(_active, Settings::audioDelay); + const Message *segment = _data->peek(); + if (!segment) + continue; - ChannelConfig segmentConfig = getConfig(segment->getInt(OFFSET_AUDIO_FORMAT)); - - // If settings changed, (re)open audio device - if (device == 0 || config != segmentConfig) + printf("AUDIO - START\n"); + _config = getConfig(segment->getInt(OFFSET_AUDIO_FORMAT)); + if (device != 0) { - config = segmentConfig; - - // Close existing device - if (device != 0) - { - SDL_CloseAudioDevice(device); - device = 0; - } - // Configure new spec - SDL_zero(spec); - spec.freq = config.rate; - spec.format = AUDIO_S16SYS; - spec.channels = config.channels; - spec.samples = 4096; - spec.callback = nullptr; - - device = SDL_OpenAudioDevice(nullptr, 0, &spec, nullptr, 0); - if (device == 0) - { - std::cerr << "[Audio] Failed to open audio: " << SDL_GetError() << std::endl; - continue; - } - // Calculate new buffer target: 0.5s - targetBytes = config.rate * config.channels * sizeof(int16_t) / 2; - bufferedBytes = 0; - unpaused = false; - // Start paused SDL_PauseAudioDevice(device, 1); + SDL_CloseAudioDevice(device); + device = 0; } - // Apply volume in-place - int16_t *samples = reinterpret_cast(segment->data()); - size_t count = segment->length() / sizeof(int16_t); - for (size_t i = 0; i < count; ++i) + // Configure new spec + SDL_zero(spec); + spec.freq = _config.rate; + spec.format = AUDIO_S16SYS; + spec.channels = _config.channels; + spec.samples = 4096; + spec.callback = callback; + spec.userdata = this; + + _reconfig = false; + _offset = 0; + _nodata = 0; + + device = SDL_OpenAudioDevice(nullptr, 0, &spec, nullptr, 0); + if (device == 0) { - samples[i] = static_cast(samples[i] * _volume); + std::cerr << "[Audio] Failed to open audio: " << SDL_GetError() << std::endl; + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + continue; } + SDL_PauseAudioDevice(device, 0); - // Queue audio - SDL_QueueAudio(device, segment->data(), segment->length()); - bufferedBytes += segment->length(); - - // Unpause when enough buffered - if (!unpaused && bufferedBytes >= targetBytes) - { - SDL_PauseAudioDevice(device, 0); - unpaused = true; - } + printf("AUDIO - SLEED %b\n", _reconfig.load() || !_active.load()); + std::unique_lock lock(_mtx); + _cv.wait(lock, [&] + { return _reconfig.load() || !_active.load(); }); } if (device) + { + SDL_PauseAudioDevice(device, 1); SDL_CloseAudioDevice(device); + } } diff --git a/src/pcm_audio.h b/src/pcm_audio.h index 6289945..106413d 100644 --- a/src/pcm_audio.h +++ b/src/pcm_audio.h @@ -11,6 +11,8 @@ #include "struct/message.h" #include "helper/error.h" +#define NO_DATA_FRAMES 20 + struct ChannelConfig { int rate; @@ -40,15 +42,24 @@ public: void setVolume(float vol); private: - ChannelConfig getConfig(int type) const; void runner(); void loop(SDL_AudioDeviceID device); - AtomicQueue *_data = nullptr; + static void callback(void *userdata, Uint8 *stream, int len); + static ChannelConfig _configTable[]; + static ChannelConfig getConfig(int type); float _volume = 1.0f; + AtomicQueue *_data; + ChannelConfig _config; + int _offset; + int _nodata; + std::thread _thread; + std::mutex _mtx; + std::condition_variable _cv; + std::atomic _reconfig{false}; std::atomic _active{false}; }; diff --git a/src/protocol.cpp b/src/protocol.cpp index 6cf8491..6c4a630 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -8,10 +8,10 @@ Protocol::Protocol(uint16_t width, uint16_t height, uint16_t fps, uint16_t padding) : connector(padding), - videoData(Settings::queue), - audioStream0(Settings::queue), - audioStream1(Settings::queue), - audioStream2(Settings::queue), + videoData(Settings::videoQueue), + audioStream0(Settings::audioQueue), + audioStream1(Settings::audioQueue), + audioStream2(Settings::audioQueue), phoneConnected(false), _width(width), _height(height), diff --git a/src/settings.h b/src/settings.h index aaf84a1..fee7b39 100644 --- a/src/settings.h +++ b/src/settings.h @@ -19,7 +19,9 @@ public: static inline Setting sourceFps{"source-fps", 30}; static inline Setting logging{"logging", false}; static inline Setting scaler{"scaler", 2}; - static inline Setting queue{"queue-size", 32}; + static inline Setting videoQueue{"video-buffer-size", 32}; + static inline Setting audioQueue{"audio-buffer-size", 16}; + static inline Setting audioDelay{"audio-buffer-wait", 2}; static inline Setting fontSize{"font-size", 30}; static inline Setting encryption{"encryption", false}; static inline Setting autoconnect{"autoconnect", true}; diff --git a/src/struct/atomic_queue.h b/src/struct/atomic_queue.h index 225bd2f..ae58948 100644 --- a/src/struct/atomic_queue.h +++ b/src/struct/atomic_queue.h @@ -31,11 +31,11 @@ public: _first = (_first + 1) % _size; _data[_first] = std::move(obj); ++_count; - _lock.notify_one(); + _lock.notify_one(); return true; } - bool pushReplace(unique_ptr obj) + bool pushReplace(unique_ptr obj) { if (_count == _size) { @@ -46,10 +46,17 @@ public: _first = (_first + 1) % _size; _data[_first] = std::move(obj); ++_count; - _lock.notify_one(); + _lock.notify_one(); return true; } + const T *peek() + { + if (_count == 0) + return nullptr; + return _data[(_last + 1) % _size].get(); + } + unique_ptr pop() { if (_count == 0) @@ -61,20 +68,14 @@ public: return item; } - unique_ptr wait(atomic &waitFlag) + bool wait(atomic &waitFlag, int count = 0) { unique_lock lock(_mtx); + bool exit = _count > count; _lock.wait(lock, [&] - { return _count > 0 || !waitFlag; }); - - if (!waitFlag) - return nullptr; - - _last = (_last + 1) % _size; - auto item = std::move(_data[_last]); - --_count; - return item; + { return _count > count || !waitFlag; }); + return !exit; } void clear()