Report capture termination and preserve explicit microphone selection
This commit is contained in:
1 parent
ee54626de9
commit
2caa9504c9
3 files changed
+30
-14
No files matched your search
@@ -40,7 +40,7 @@ struct StreamPublishEngine::Impl {
|
||||
std::condition_variable wake;
|
||||
std::thread videoThread, audioThread, probeThread;
|
||||
std::atomic<bool> running{false}, muted{false};
|
||||
std::atomic<unsigned> audioDropped{0};
|
||||
std::atomic<unsigned> audioDropped{0}, audioMissing{0};
|
||||
AudioEventQueue<Audio, 32> audio;
|
||||
uint64_t id = 0, group = 0, generation = 0, sourceGeneration = 0, confirmedGeneration = 0, frameGeneration = 0, audioSequence = 0;
|
||||
int64_t origin = 0, started = 0, framePts = -1, backendOrigin = -1, backendAnchor = -1;
|
||||
@@ -175,9 +175,10 @@ bool StreamPublishEngine::openSource(const StreamPublishConfig& config) {
|
||||
}
|
||||
if (best.isNull()) { DEBUG_ERROR(DEBUG_CATEGORY_VIDEO, "desktop camera lacks requested fps=%d", config.video.fps); return false; }
|
||||
m->camera->setCameraFormat(best); m->session->setCamera(m->camera.get());
|
||||
connect(m->camera.get(), &QCamera::activeChanged, this, [generation](bool active) {
|
||||
connect(m->camera.get(), &QCamera::activeChanged, this, [this, generation](bool active) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_VIDEO, "desktop camera active=%d generation=%llu", active, (unsigned long long)generation);
|
||||
});
|
||||
if (!active && generation == m->sourceGeneration) sourceFailed(m->generation, text("Video capture stopped"));
|
||||
}, Qt::QueuedConnection);
|
||||
connect(m->camera.get(), &QCamera::errorOccurred, this, [failed](QCamera::Error, const QString& error) { failed(error); }, Qt::QueuedConnection);
|
||||
m->camera->start();
|
||||
} else if (config.source.kind == StreamVideoSource::Screen) {
|
||||
@@ -189,18 +190,20 @@ bool StreamPublishEngine::openSource(const StreamPublishConfig& config) {
|
||||
m->screen->setScreen(selected);
|
||||
}
|
||||
m->session->setScreenCapture(m->screen.get());
|
||||
connect(m->screen.get(), &QScreenCapture::activeChanged, this, [generation](bool active) {
|
||||
connect(m->screen.get(), &QScreenCapture::activeChanged, this, [this, generation](bool active) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_VIDEO, "desktop screen capture active=%d generation=%llu", active, (unsigned long long)generation);
|
||||
});
|
||||
if (!active && generation == m->sourceGeneration) sourceFailed(m->generation, text("Video capture stopped"));
|
||||
}, Qt::QueuedConnection);
|
||||
connect(m->screen.get(), &QScreenCapture::errorOccurred, this, [failed](QScreenCapture::Error, const QString& error) { failed(error); }, Qt::QueuedConnection);
|
||||
m->screen->start();
|
||||
} else {
|
||||
auto window = config.source.window.value<QCapturableWindow>();
|
||||
if (!window.isValid()) { DEBUG_ERROR(DEBUG_CATEGORY_VIDEO, "desktop window is unavailable"); return false; }
|
||||
m->window = std::make_unique<QWindowCapture>(); m->window->setWindow(window); m->session->setWindowCapture(m->window.get());
|
||||
connect(m->window.get(), &QWindowCapture::activeChanged, this, [generation](bool active) {
|
||||
connect(m->window.get(), &QWindowCapture::activeChanged, this, [this, generation](bool active) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_VIDEO, "desktop window capture active=%d generation=%llu", active, (unsigned long long)generation);
|
||||
});
|
||||
if (!active && generation == m->sourceGeneration) sourceFailed(m->generation, text("Video capture stopped"));
|
||||
}, Qt::QueuedConnection);
|
||||
connect(m->window.get(), &QWindowCapture::errorOccurred, this, [failed](QWindowCapture::Error, const QString& error) { failed(error); }, Qt::QueuedConnection);
|
||||
m->window->start();
|
||||
}
|
||||
@@ -224,8 +227,11 @@ void StreamPublishEngine::closeSource() {
|
||||
}
|
||||
|
||||
bool StreamPublishEngine::start(uint64_t group, const StreamPublishConfig& config) {
|
||||
if (active()) { emit errorOccurred(text("A stream is already running")); return false; }
|
||||
if (!supported() || !codecs().contains(config.video.codec)) { emit errorOccurred(text("Video capture or encoder is unavailable")); return false; }
|
||||
if (active()) { DEBUG_WARN(DEBUG_CATEGORY_STREAM, "desktop publication already active"); emit errorOccurred(text("A stream is already running")); return false; }
|
||||
if (!supported() || !codecs().contains(config.video.codec)) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_VIDEO, "desktop capture/encoder unavailable codec=%d", config.video.codec);
|
||||
emit errorOccurred(text("Video capture or encoder is unavailable")); return false;
|
||||
}
|
||||
if (config.audio && !m_microphone.acquire()) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_STREAM, "desktop publication blocked: microphone owned by another operation");
|
||||
emit errorOccurred(text("The microphone is used by another operation")); return false;
|
||||
@@ -248,7 +254,7 @@ bool StreamPublishEngine::start(uint64_t group, const StreamPublishConfig& confi
|
||||
if (!openSource(config) || (config.audio && !openMicrophone())) {
|
||||
sourceFailed(m->generation, text("Could not open the selected capture device")); return false;
|
||||
}
|
||||
emit stateChanged(config.source.id == "portal" ? text("Select a screen or window in the system dialog") : text("Starting stream…"));
|
||||
emit stateChanged(config.source.id == "portal" ? text("Select a screen in the system dialog") : text("Starting stream…"));
|
||||
emit configurationChanged(); return true;
|
||||
}
|
||||
|
||||
@@ -256,6 +262,7 @@ bool StreamPublishEngine::reconfigure(const StreamPublishConfig& config) {
|
||||
if (!m->running) return false;
|
||||
StreamPublishConfig previous = this->config();
|
||||
if (config.audio != previous.audio || config.microphone != previous.microphone) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_STREAM, "desktop microphone selection changed during live publication");
|
||||
emit errorOccurred(text("Change the microphone before starting the stream")); return false;
|
||||
}
|
||||
bool sourceChange = config.source.kind != previous.source.kind || config.source.id != previous.source.id
|
||||
@@ -298,6 +305,7 @@ bool StreamPublishEngine::openMicrophone() {
|
||||
auto config = this->config(); auto* context = SoundManager::instance()->context();
|
||||
if (!context) { DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "desktop stream audio context unavailable"); 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; }
|
||||
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;
|
||||
@@ -323,7 +331,7 @@ void StreamPublishEngine::captureAudio(const void* input, unsigned frames) {
|
||||
m->audioAnchor = first; m->audioFrames = 0;
|
||||
}
|
||||
first = m->audioAnchor + (int64_t)m->audioFrames * 1000000 / 48000; m->audioFrames += frames;
|
||||
if (!pcm) m->audioDropped++;
|
||||
if (!pcm) m->audioMissing++;
|
||||
for (unsigned offset = 0; offset < frames;) {
|
||||
Impl::Audio record; record.frames = std::min(960u, frames - offset);
|
||||
record.pts = std::max<int64_t>(0, first + (int64_t)offset * 1000000 / 48000); record.created = monotonicUs();
|
||||
@@ -424,7 +432,11 @@ void StreamPublishEngine::videoLoop() {
|
||||
if (frame.isValid()) {
|
||||
if (capture <= lastPts || monotonicUs() - m->origin - capture > 250000) { skipped++; continue; }
|
||||
lastImage = frame.toImage();
|
||||
if (lastImage.isNull()) { DEBUG_ERROR(DEBUG_CATEGORY_VIDEO, "desktop captured frame conversion failed"); continue; }
|
||||
if (lastImage.isNull()) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_VIDEO, "desktop captured frame conversion failed format=%d", frame.pixelFormat());
|
||||
QMetaObject::invokeMethod(this, [this, generation] { sourceFailed(generation, text("Video frame conversion failed")); });
|
||||
continue;
|
||||
}
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0)
|
||||
if (frame.rotation() != QtVideo::Rotation::None) lastImage = lastImage.transformed(QTransform().rotate(static_cast<int>(frame.rotation())));
|
||||
if (frame.mirrored()) lastImage = lastImage.transformed(QTransform().scale(-1, 1));
|
||||
@@ -508,4 +520,6 @@ void StreamPublishEngine::tick() {
|
||||
}
|
||||
unsigned dropped = m->audioDropped.exchange(0);
|
||||
if (dropped) DEBUG_WARN(DEBUG_CATEGORY_STREAM, "desktop audio capture discarded blocks=%u", dropped);
|
||||
unsigned missing = m->audioMissing.exchange(0);
|
||||
if (missing) DEBUG_WARN(DEBUG_CATEGORY_STREAM, "desktop microphone callbacks lack PCM count=%u; sent silence", missing);
|
||||
}
|
||||
Reference in new issue
Block a user