|
|
|
|
@ -250,7 +250,7 @@ bool StreamPublishEngine::start(uint64_t group, const StreamPublishConfig& confi
|
|
|
|
|
try { |
|
|
|
|
m->videoThread = std::thread(&StreamPublishEngine::videoLoop, this); |
|
|
|
|
if (config.audio) m->audioThread = std::thread(&StreamPublishEngine::audioLoop, this); |
|
|
|
|
} catch (const std::system_error& error) { sourceFailed(m->generation, QString::fromUtf8(error.what())); return false; } |
|
|
|
|
} catch (const std::system_error& error) { publicationFailed(m->id, QString::fromUtf8(error.what())); return false; } |
|
|
|
|
if (!openSource(config) || (config.audio && !openMicrophone())) { |
|
|
|
|
sourceFailed(m->generation, text("Could not open the selected capture device")); return false; |
|
|
|
|
} |
|
|
|
|
@ -288,6 +288,13 @@ void StreamPublishEngine::sourceFailed(uint64_t generation, const QString& error
|
|
|
|
|
if (rollback) reconfigure(restore); else stop(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Ошибку общего audio worker нельзя исправить заменой только video encoder. */ |
|
|
|
|
void StreamPublishEngine::publicationFailed(uint64_t id, const QString& error) { |
|
|
|
|
if (id != m->id || !m->running) return; |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "desktop publication failed id=%llu reason=%s", (unsigned long long)id, qPrintable(error)); |
|
|
|
|
emit errorOccurred(error); stop(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Сначала callbacks, затем workers; FIN не обгоняет уже принятые access units. */ |
|
|
|
|
void StreamPublishEngine::stop() { |
|
|
|
|
if (!m->running.exchange(false)) return; |
|
|
|
|
@ -303,9 +310,11 @@ bool StreamPublishEngine::openMicrophone() {
|
|
|
|
|
m_capture.close(); |
|
|
|
|
m->audioAnchor = -1; m->audioFrames = 0; |
|
|
|
|
auto config = this->config(); auto* context = SoundManager::instance()->context(); |
|
|
|
|
if (!context) { DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "desktop stream audio context unavailable"); return false; } |
|
|
|
|
if (!context) { DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "desktop stream audio context unavailable"); m_capture.retryLater(); return false; } |
|
|
|
|
ma_device_id device; bool selected = SoundManager::deviceId(config.microphone, &device); |
|
|
|
|
if (!config.microphone.isEmpty() && !selected) { DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "desktop selected microphone ID is unavailable"); return false; } |
|
|
|
|
if (!config.microphone.isEmpty() && !selected) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "desktop selected microphone ID is unavailable"); m_capture.retryLater(); return false; |
|
|
|
|
} |
|
|
|
|
for (int channels : {2, 1}) { |
|
|
|
|
ma_device_config capture = ma_device_config_init(ma_device_type_capture); |
|
|
|
|
capture.capture.format = ma_format_s16; capture.capture.channels = channels; capture.sampleRate = 48000; |
|
|
|
|
@ -347,12 +356,13 @@ void StreamPublishEngine::captureAudio(const void* input, unsigned frames) {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void StreamPublishEngine::audioLoop() { |
|
|
|
|
const uint64_t id = m->id; |
|
|
|
|
auto* encoder = opus_codec_encoder_create(48000, 2); auto* compressor = audio_compressor_create(); |
|
|
|
|
int result = encoder && compressor ? opus_codec_encoder_application_set(encoder, OPUS_CODEC_APP_AUDIO) : -1; |
|
|
|
|
if (result >= 0) result = opus_codec_encoder_bitrate_set(encoder, 128000); |
|
|
|
|
if (result < 0) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "desktop stream audio encoder initialization failed"); |
|
|
|
|
QMetaObject::invokeMethod(this, [this] { sourceFailed(m->generation, text("Audio encoder initialization failed")); }); |
|
|
|
|
QMetaObject::invokeMethod(this, [this, id] { publicationFailed(id, text("Audio encoder initialization failed")); }); |
|
|
|
|
} else { |
|
|
|
|
int16_t pcm[1920]{}; unsigned used = 0; uint64_t sequence = 0; |
|
|
|
|
int64_t pts = 0, expected = -1, lastSent = -1; int settings[3] = {-1, -1, -1}; |
|
|
|
|
@ -396,7 +406,7 @@ void StreamPublishEngine::audioLoop() {
|
|
|
|
|
used = 0; |
|
|
|
|
} |
|
|
|
|
if (result < 0) { |
|
|
|
|
QMetaObject::invokeMethod(this, [this] { sourceFailed(m->generation, text("Audio encoding failed")); }); break; |
|
|
|
|
QMetaObject::invokeMethod(this, [this, id] { publicationFailed(id, text("Audio encoding failed")); }); break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|