Move Opus encoding off-thread because of SDL
SDL gives its audio threads a very small stack size and it was making Opus fuck up
This commit is contained in:
parent
52ba6fa7f7
commit
73c5a17746
@ -140,10 +140,22 @@ Voice::Voice(ApplicationControllerPtr appController) : m_encoder(nullptr, opus_e
|
|||||||
m_channelMode = VoiceChannelMode::Mono;
|
m_channelMode = VoiceChannelMode::Mono;
|
||||||
m_applicationController = appController;
|
m_applicationController = appController;
|
||||||
|
|
||||||
|
m_stopThread = false;
|
||||||
|
m_thread = Thread::invoke("Voice::thread", mem_fn(&Voice::thread), this);
|
||||||
|
|
||||||
s_singleton = this;
|
s_singleton = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Voice::~Voice() {
|
Voice::~Voice() {
|
||||||
|
m_stopThread = true;
|
||||||
|
|
||||||
|
{
|
||||||
|
MutexLocker locker(m_threadMutex);
|
||||||
|
m_threadCond.broadcast();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_thread.finish();
|
||||||
|
|
||||||
save();
|
save();
|
||||||
closeDevice();
|
closeDevice();
|
||||||
|
|
||||||
@ -232,14 +244,18 @@ void Voice::readAudioData(uint8_t* stream, int len) {
|
|||||||
if (m_inputMode == VoiceInputMode::VoiceActivity) {
|
if (m_inputMode == VoiceInputMode::VoiceActivity) {
|
||||||
if (decibels > m_threshold)
|
if (decibels > m_threshold)
|
||||||
m_lastThresholdTime = now;
|
m_lastThresholdTime = now;
|
||||||
active = now - m_lastThresholdTime < 50;
|
active = now - m_lastThresholdTime < 50;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool added = false;
|
||||||
|
|
||||||
|
MutexLocker captureLock(m_captureMutex);
|
||||||
if (active) {
|
if (active) {
|
||||||
m_capturedChunksFrames += sampleCount / m_deviceChannels;
|
m_capturedChunksFrames += sampleCount / m_deviceChannels;
|
||||||
auto data = (opus_int16*)malloc(len);
|
auto data = (opus_int16*)malloc(len);
|
||||||
memcpy(data, stream, len);
|
memcpy(data, stream, len);
|
||||||
m_capturedChunks.emplace(data, sampleCount); // takes ownership
|
m_capturedChunks.emplace(data, sampleCount); // takes ownership
|
||||||
|
added = true;
|
||||||
}
|
}
|
||||||
else { // Clear out any residual data so they don't manifest at the start of the next encode, whenever that is
|
else { // Clear out any residual data so they don't manifest at the start of the next encode, whenever that is
|
||||||
while (!m_capturedChunks.empty())
|
while (!m_capturedChunks.empty())
|
||||||
@ -248,46 +264,8 @@ void Voice::readAudioData(uint8_t* stream, int len) {
|
|||||||
m_capturedChunksFrames = 0;
|
m_capturedChunksFrames = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
ByteArray encoded(VOICE_MAX_PACKET_SIZE, 0);
|
if (added)
|
||||||
size_t frameSamples = VOICE_FRAME_SIZE * (size_t)m_deviceChannels;
|
m_threadCond.signal();
|
||||||
while (m_capturedChunksFrames >= VOICE_FRAME_SIZE) {
|
|
||||||
std::vector<opus_int16> samples;
|
|
||||||
samples.reserve(frameSamples);
|
|
||||||
size_t samplesLeft = frameSamples;
|
|
||||||
while (samplesLeft && !m_capturedChunks.empty()) {
|
|
||||||
auto& front = m_capturedChunks.front();
|
|
||||||
if (front.exhausted())
|
|
||||||
m_capturedChunks.pop();
|
|
||||||
else
|
|
||||||
samplesLeft -= front.takeSamples(samples, samplesLeft);
|
|
||||||
}
|
|
||||||
m_capturedChunksFrames -= VOICE_FRAME_SIZE;
|
|
||||||
|
|
||||||
if (m_inputVolume != 1.0f) {
|
|
||||||
for (size_t i = 0; i != samples.size(); ++i)
|
|
||||||
samples[i] *= m_inputVolume;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (int encodedSize = opus_encode(m_encoder.get(), samples.data(), VOICE_FRAME_SIZE, (unsigned char*)encoded.ptr(), encoded.size())) {
|
|
||||||
if (encodedSize == 1)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
encoded.resize(encodedSize);
|
|
||||||
|
|
||||||
{
|
|
||||||
MutexLocker lock(m_captureMutex);
|
|
||||||
m_encodedChunks.emplace_back(move(encoded)); // reset takes ownership of data buffer
|
|
||||||
m_encodedChunksLength += encodedSize;
|
|
||||||
|
|
||||||
encoded = ByteArray(VOICE_MAX_PACKET_SIZE, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger::info("Voice: encoded Opus chunk {} bytes big", encodedSize);
|
|
||||||
}
|
|
||||||
else if (encodedSize < 0)
|
|
||||||
Logger::error("Voice: Opus encode error {}", opus_strerror(encodedSize));
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Voice::mix(int16_t* buffer, size_t frameCount, unsigned channels) {
|
void Voice::mix(int16_t* buffer, size_t frameCount, unsigned channels) {
|
||||||
@ -368,15 +346,17 @@ void Voice::setDeviceName(Maybe<String> deviceName) {
|
|||||||
int Voice::send(DataStreamBuffer& out, size_t budget) {
|
int Voice::send(DataStreamBuffer& out, size_t budget) {
|
||||||
out.setByteOrder(ByteOrder::LittleEndian);
|
out.setByteOrder(ByteOrder::LittleEndian);
|
||||||
out.write<uint16_t>(VOICE_VERSION);
|
out.write<uint16_t>(VOICE_VERSION);
|
||||||
MutexLocker captureLock(m_captureMutex);
|
|
||||||
|
|
||||||
if (m_capturedChunks.empty())
|
MutexLocker encodeLock(m_encodeMutex);
|
||||||
|
|
||||||
|
if (m_encodedChunks.empty())
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
std::vector<ByteArray> encodedChunks = move(m_encodedChunks);
|
std::vector<ByteArray> encodedChunks = move(m_encodedChunks);
|
||||||
size_t encodedChunksLength = m_encodedChunksLength;
|
size_t encodedChunksLength = m_encodedChunksLength;
|
||||||
m_encodedChunksLength = 0;
|
m_encodedChunksLength = 0;
|
||||||
captureLock.unlock();
|
|
||||||
|
encodeLock.unlock();
|
||||||
|
|
||||||
for (auto& chunk : encodedChunks) {
|
for (auto& chunk : encodedChunks) {
|
||||||
out.write<uint32_t>(chunk.size());
|
out.write<uint32_t>(chunk.size());
|
||||||
@ -518,4 +498,63 @@ bool Voice::playSpeaker(SpeakerPtr const& speaker, int channels) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Voice::thread() {
|
||||||
|
while (true) {
|
||||||
|
MutexLocker locker(m_threadMutex);
|
||||||
|
|
||||||
|
m_threadCond.wait(m_threadMutex);
|
||||||
|
if (m_stopThread)
|
||||||
|
return;
|
||||||
|
|
||||||
|
{
|
||||||
|
MutexLocker locker(m_captureMutex);
|
||||||
|
ByteArray encoded(VOICE_MAX_PACKET_SIZE, 0);
|
||||||
|
size_t frameSamples = VOICE_FRAME_SIZE * (size_t)m_deviceChannels;
|
||||||
|
while (m_capturedChunksFrames >= VOICE_FRAME_SIZE) {
|
||||||
|
std::vector<opus_int16> samples;
|
||||||
|
samples.reserve(frameSamples);
|
||||||
|
size_t samplesLeft = frameSamples;
|
||||||
|
while (samplesLeft && !m_capturedChunks.empty()) {
|
||||||
|
auto& front = m_capturedChunks.front();
|
||||||
|
if (front.exhausted())
|
||||||
|
m_capturedChunks.pop();
|
||||||
|
else
|
||||||
|
samplesLeft -= front.takeSamples(samples, samplesLeft);
|
||||||
|
}
|
||||||
|
m_capturedChunksFrames -= VOICE_FRAME_SIZE;
|
||||||
|
|
||||||
|
if (m_inputVolume != 1.0f) {
|
||||||
|
for (size_t i = 0; i != samples.size(); ++i)
|
||||||
|
samples[i] *= m_inputVolume;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (int encodedSize = opus_encode(m_encoder.get(), samples.data(), VOICE_FRAME_SIZE, (unsigned char*)encoded.ptr(), encoded.size())) {
|
||||||
|
if (encodedSize == 1)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
encoded.resize(encodedSize);
|
||||||
|
|
||||||
|
{
|
||||||
|
MutexLocker lock(m_encodeMutex);
|
||||||
|
m_encodedChunks.emplace_back(move(encoded)); // reset takes ownership of data buffer
|
||||||
|
m_encodedChunksLength += encodedSize;
|
||||||
|
|
||||||
|
encoded = ByteArray(VOICE_MAX_PACKET_SIZE, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger::info("Voice: encoded Opus chunk {} bytes big", encodedSize);
|
||||||
|
}
|
||||||
|
else if (encodedSize < 0)
|
||||||
|
Logger::error("Voice: Opus encode error {}", opus_strerror(encodedSize));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
|
||||||
|
locker.unlock();
|
||||||
|
Thread::yield();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -134,17 +134,19 @@ public:
|
|||||||
void setInput(bool input = true);
|
void setInput(bool input = true);
|
||||||
|
|
||||||
inline int encoderChannels() const { return (int)m_channelMode; }
|
inline int encoderChannels() const { return (int)m_channelMode; }
|
||||||
private:
|
|
||||||
static Voice* s_singleton;
|
|
||||||
|
|
||||||
static OpusDecoder* createDecoder(int channels);
|
static OpusDecoder* createDecoder(int channels);
|
||||||
static OpusEncoder* createEncoder(int channels);
|
static OpusEncoder* createEncoder(int channels);
|
||||||
|
private:
|
||||||
|
static Voice* s_singleton;
|
||||||
void resetEncoder();
|
void resetEncoder();
|
||||||
void openDevice();
|
void openDevice();
|
||||||
void closeDevice();
|
void closeDevice();
|
||||||
|
|
||||||
bool playSpeaker(SpeakerPtr const& speaker, int channels);
|
bool playSpeaker(SpeakerPtr const& speaker, int channels);
|
||||||
|
|
||||||
|
void thread();
|
||||||
|
|
||||||
SpeakerId m_speakerId = 0;
|
SpeakerId m_speakerId = 0;
|
||||||
SpeakerPtr m_clientSpeaker;
|
SpeakerPtr m_clientSpeaker;
|
||||||
HashMap<SpeakerId, SpeakerPtr> m_speakers;
|
HashMap<SpeakerId, SpeakerPtr> m_speakers;
|
||||||
@ -152,6 +154,8 @@ private:
|
|||||||
Mutex m_activeSpeakersMutex;
|
Mutex m_activeSpeakersMutex;
|
||||||
HashSet<SpeakerPtr> m_activeSpeakers;
|
HashSet<SpeakerPtr> m_activeSpeakers;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
OpusEncoderPtr m_encoder;
|
OpusEncoderPtr m_encoder;
|
||||||
|
|
||||||
float m_outputVolume = 1.0f;
|
float m_outputVolume = 1.0f;
|
||||||
@ -171,6 +175,11 @@ private:
|
|||||||
VoiceInputMode m_inputMode;
|
VoiceInputMode m_inputMode;
|
||||||
VoiceChannelMode m_channelMode;
|
VoiceChannelMode m_channelMode;
|
||||||
|
|
||||||
|
ThreadFunction<void> m_thread;
|
||||||
|
Mutex m_threadMutex;
|
||||||
|
ConditionVariable m_threadCond;
|
||||||
|
atomic<bool> m_stopThread;
|
||||||
|
|
||||||
ApplicationControllerPtr m_applicationController;
|
ApplicationControllerPtr m_applicationController;
|
||||||
|
|
||||||
struct EncodedChunk {
|
struct EncodedChunk {
|
||||||
@ -183,13 +192,13 @@ private:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Mutex m_encodeMutex;
|
||||||
std::vector<ByteArray> m_encodedChunks;
|
std::vector<ByteArray> m_encodedChunks;
|
||||||
size_t m_encodedChunksLength = 0;
|
size_t m_encodedChunksLength = 0;
|
||||||
|
|
||||||
|
Mutex m_captureMutex;
|
||||||
std::queue<VoiceAudioChunk> m_capturedChunks;
|
std::queue<VoiceAudioChunk> m_capturedChunks;
|
||||||
size_t m_capturedChunksFrames = 0;
|
size_t m_capturedChunksFrames = 0;
|
||||||
|
|
||||||
Mutex m_captureMutex;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user