Callback based audio player

This commit is contained in:
Niellun
2025-05-26 12:16:29 +03:00
parent 787aac6b99
commit 1e59784e06
7 changed files with 144 additions and 96 deletions
+6 -1
View File
@@ -41,7 +41,12 @@
#logging = false #logging = false
# Size of video and audio buffers. Increase if you see artifacts # 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 for messgaes on screen. Set to 0 is you do not want any
#font-size = 30 #font-size = 30
+4 -5
View File
@@ -135,14 +135,11 @@ void Decoder::loop(AVCodecContext *context, AVCodecParserContext *parser, AVPack
uint32_t counter = 0; uint32_t counter = 0;
// Main decoding loop; runs until global_quit flag is set // Main decoding loop; runs until global_quit flag is set
_data->wait(_active);
while (_active) while (_active)
{ {
// Get raw data segment from queue // Get raw data segment from queue
std::unique_ptr<Message> segment = _data->wait(_active); std::unique_ptr<Message> segment = _data->pop();
if (!_active)
continue;
uint8_t *data_ptr = segment->data(); uint8_t *data_ptr = segment->data();
int data_size = segment->length(); int data_size = segment->length();
@@ -193,5 +190,7 @@ void Decoder::loop(AVCodecContext *context, AVCodecParserContext *parser, AVPack
_vb->commit(); _vb->commit();
} }
} }
_data->wait(_active);
} }
} }
+100 -70
View File
@@ -1,5 +1,14 @@
#include "pcm_audio.h" #include "pcm_audio.h"
#include "helper/functions.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 // Implementation
PcmAudio::PcmAudio() {} PcmAudio::PcmAudio() {}
@@ -9,6 +18,60 @@ PcmAudio::~PcmAudio()
stop(); stop();
} }
void PcmAudio::callback(void *userdata, Uint8 *stream, int len)
{
PcmAudio *self = static_cast<PcmAudio *>(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) void PcmAudio::setVolume(float vol)
{ {
if (vol < 0) if (vol < 0)
@@ -39,23 +102,7 @@ void PcmAudio::stop()
return; return;
_active = false; _active = false;
_data->notify(); _data->notify();
if (_thread.joinable()) _cv.notify_all();
_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};
} }
void PcmAudio::runner() void PcmAudio::runner()
@@ -64,72 +111,55 @@ void PcmAudio::runner()
SDL_AudioDeviceID device = 0; SDL_AudioDeviceID device = 0;
SDL_AudioSpec spec; SDL_AudioSpec spec;
size_t bufferedBytes = 0;
bool unpaused = false;
size_t targetBytes = 0;
ChannelConfig config = {0, 0};
while (_active) while (_active)
{ {
unique_ptr<Message> segment = _data->wait(_active); printf("AUDIO - WAIT BUFFER\n");
if (!_active) _data->wait(_active, Settings::audioDelay);
break; const Message *segment = _data->peek();
if (!segment)
continue;
ChannelConfig segmentConfig = getConfig(segment->getInt(OFFSET_AUDIO_FORMAT)); printf("AUDIO - START\n");
_config = getConfig(segment->getInt(OFFSET_AUDIO_FORMAT));
// If settings changed, (re)open audio device if (device != 0)
if (device == 0 || config != segmentConfig)
{ {
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_PauseAudioDevice(device, 1);
SDL_CloseAudioDevice(device);
device = 0;
} }
// Apply volume in-place // Configure new spec
int16_t *samples = reinterpret_cast<int16_t *>(segment->data()); SDL_zero(spec);
size_t count = segment->length() / sizeof(int16_t); spec.freq = _config.rate;
for (size_t i = 0; i < count; ++i) 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<int16_t>(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 printf("AUDIO - SLEED %b\n", _reconfig.load() || !_active.load());
SDL_QueueAudio(device, segment->data(), segment->length()); std::unique_lock<std::mutex> lock(_mtx);
bufferedBytes += segment->length(); _cv.wait(lock, [&]
{ return _reconfig.load() || !_active.load(); });
// Unpause when enough buffered
if (!unpaused && bufferedBytes >= targetBytes)
{
SDL_PauseAudioDevice(device, 0);
unpaused = true;
}
} }
if (device) if (device)
{
SDL_PauseAudioDevice(device, 1);
SDL_CloseAudioDevice(device); SDL_CloseAudioDevice(device);
}
} }
+13 -2
View File
@@ -11,6 +11,8 @@
#include "struct/message.h" #include "struct/message.h"
#include "helper/error.h" #include "helper/error.h"
#define NO_DATA_FRAMES 20
struct ChannelConfig struct ChannelConfig
{ {
int rate; int rate;
@@ -40,15 +42,24 @@ public:
void setVolume(float vol); void setVolume(float vol);
private: private:
ChannelConfig getConfig(int type) const;
void runner(); void runner();
void loop(SDL_AudioDeviceID device); void loop(SDL_AudioDeviceID device);
AtomicQueue<Message> *_data = nullptr; static void callback(void *userdata, Uint8 *stream, int len);
static ChannelConfig _configTable[];
static ChannelConfig getConfig(int type);
float _volume = 1.0f; float _volume = 1.0f;
AtomicQueue<Message> *_data;
ChannelConfig _config;
int _offset;
int _nodata;
std::thread _thread; std::thread _thread;
std::mutex _mtx;
std::condition_variable _cv;
std::atomic<bool> _reconfig{false};
std::atomic<bool> _active{false}; std::atomic<bool> _active{false};
}; };
+4 -4
View File
@@ -8,10 +8,10 @@
Protocol::Protocol(uint16_t width, uint16_t height, uint16_t fps, uint16_t padding) Protocol::Protocol(uint16_t width, uint16_t height, uint16_t fps, uint16_t padding)
: connector(padding), : connector(padding),
videoData(Settings::queue), videoData(Settings::videoQueue),
audioStream0(Settings::queue), audioStream0(Settings::audioQueue),
audioStream1(Settings::queue), audioStream1(Settings::audioQueue),
audioStream2(Settings::queue), audioStream2(Settings::audioQueue),
phoneConnected(false), phoneConnected(false),
_width(width), _width(width),
_height(height), _height(height),
+3 -1
View File
@@ -19,7 +19,9 @@ public:
static inline Setting<int> sourceFps{"source-fps", 30}; static inline Setting<int> sourceFps{"source-fps", 30};
static inline Setting<bool> logging{"logging", false}; static inline Setting<bool> logging{"logging", false};
static inline Setting<int> scaler{"scaler", 2}; static inline Setting<int> scaler{"scaler", 2};
static inline Setting<int> queue{"queue-size", 32}; static inline Setting<int> videoQueue{"video-buffer-size", 32};
static inline Setting<int> audioQueue{"audio-buffer-size", 16};
static inline Setting<int> audioDelay{"audio-buffer-wait", 2};
static inline Setting<int> fontSize{"font-size", 30}; static inline Setting<int> fontSize{"font-size", 30};
static inline Setting<bool> encryption{"encryption", false}; static inline Setting<bool> encryption{"encryption", false};
static inline Setting<bool> autoconnect{"autoconnect", true}; static inline Setting<bool> autoconnect{"autoconnect", true};
+12 -11
View File
@@ -35,7 +35,7 @@ public:
return true; return true;
} }
bool pushReplace(unique_ptr<T> obj) bool pushReplace(unique_ptr<T> obj)
{ {
if (_count == _size) if (_count == _size)
{ {
@@ -50,6 +50,13 @@ public:
return true; return true;
} }
const T *peek()
{
if (_count == 0)
return nullptr;
return _data[(_last + 1) % _size].get();
}
unique_ptr<T> pop() unique_ptr<T> pop()
{ {
if (_count == 0) if (_count == 0)
@@ -61,20 +68,14 @@ public:
return item; return item;
} }
unique_ptr<T> wait(atomic<bool> &waitFlag) bool wait(atomic<bool> &waitFlag, int count = 0)
{ {
unique_lock<std::mutex> lock(_mtx); unique_lock<std::mutex> lock(_mtx);
bool exit = _count > count;
_lock.wait(lock, [&] _lock.wait(lock, [&]
{ return _count > 0 || !waitFlag; }); { return _count > count || !waitFlag; });
return !exit;
if (!waitFlag)
return nullptr;
_last = (_last + 1) % _size;
auto item = std::move(_data[_last]);
--_count;
return item;
} }
void clear() void clear()