mirror of
https://github.com/niellun/FastCarPlay.git
synced 2026-06-07 09:38:25 +02:00
Callback based audio player
This commit is contained in:
+6
-1
@@ -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
|
||||
|
||||
+4
-5
@@ -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<Message> segment = _data->wait(_active);
|
||||
|
||||
if (!_active)
|
||||
continue;
|
||||
|
||||
std::unique_ptr<Message> 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);
|
||||
}
|
||||
}
|
||||
|
||||
+100
-70
@@ -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<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)
|
||||
{
|
||||
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<Message> 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<int16_t *>(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<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
|
||||
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<std::mutex> lock(_mtx);
|
||||
_cv.wait(lock, [&]
|
||||
{ return _reconfig.load() || !_active.load(); });
|
||||
}
|
||||
|
||||
if (device)
|
||||
{
|
||||
SDL_PauseAudioDevice(device, 1);
|
||||
SDL_CloseAudioDevice(device);
|
||||
}
|
||||
}
|
||||
|
||||
+13
-2
@@ -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<Message> *_data = nullptr;
|
||||
static void callback(void *userdata, Uint8 *stream, int len);
|
||||
static ChannelConfig _configTable[];
|
||||
static ChannelConfig getConfig(int type);
|
||||
|
||||
float _volume = 1.0f;
|
||||
|
||||
AtomicQueue<Message> *_data;
|
||||
ChannelConfig _config;
|
||||
int _offset;
|
||||
int _nodata;
|
||||
|
||||
std::thread _thread;
|
||||
std::mutex _mtx;
|
||||
std::condition_variable _cv;
|
||||
std::atomic<bool> _reconfig{false};
|
||||
std::atomic<bool> _active{false};
|
||||
};
|
||||
|
||||
|
||||
+4
-4
@@ -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),
|
||||
|
||||
+3
-1
@@ -19,7 +19,9 @@ public:
|
||||
static inline Setting<int> sourceFps{"source-fps", 30};
|
||||
static inline Setting<bool> logging{"logging", false};
|
||||
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<bool> encryption{"encryption", false};
|
||||
static inline Setting<bool> autoconnect{"autoconnect", true};
|
||||
|
||||
+14
-13
@@ -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<T> obj)
|
||||
bool pushReplace(unique_ptr<T> 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<T> pop()
|
||||
{
|
||||
if (_count == 0)
|
||||
@@ -61,20 +68,14 @@ public:
|
||||
return item;
|
||||
}
|
||||
|
||||
unique_ptr<T> wait(atomic<bool> &waitFlag)
|
||||
bool wait(atomic<bool> &waitFlag, int count = 0)
|
||||
{
|
||||
unique_lock<std::mutex> 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()
|
||||
|
||||
Reference in New Issue
Block a user