ref:e40d355f9607507545ac8f9b475cb62a12f4ff51

fix(video): fix video stream freezing on capture re-init (e.g. pipewire display switch) (#5249)

Co-authored-by: psyke83 <psyke83@users.noreply.github.com>
SHA: e40d355f9607507545ac8f9b475cb62a12f4ff51
Author: Kishi <41839133+Kishi85@users.noreply.github.com>
Date: 2026-06-26 14:40
Parents: 1cde8fe
3 files changed +29 -14
Type
src/platform/linux/pipewire.cpp +12 −11
@@ -136,16 +136,12 @@
~pipewire_t() {
BOOST_LOG(debug) << "[pipewire] Destroying pipewire_t"sv;
if (loop) {
BOOST_LOG(debug) << "[pipewire] Stop PW thread loop"sv;
pw_thread_loop_stop(loop);
}
try {
cleanup_stream();
} catch (const std::exception &e) {
BOOST_LOG(error) << "[pipewire] Standard exception caught in ~pipewire_t cleanup_stream: "sv << e.what();
BOOST_LOG(error) << "[pipewire] Standard exception caught in ~pipewire_t: "sv << e.what();
} catch (...) {
BOOST_LOG(error) << "[pipewire] Unknown exception caught in ~pipewire_t"sv;
BOOST_LOG(error) << "[pipewire] Unknown exception caught in ~pipewire_t cleanup_stream"sv;
}
pw_thread_loop_lock(loop);
@@ -533,7 +529,12 @@
};
static void on_stream_state_changed(void *user_data, enum pw_stream_state old, enum pw_stream_state state, const char *err_msg) {
if (err_msg != nullptr) {
BOOST_LOG(info) << "[pipewire] PipeWire stream error '" << err_msg << "' on state: " << pw_stream_state_as_string(old)
<< " -> " << pw_stream_state_as_string(state);
} else {
BOOST_LOG(debug) << "[pipewire] PipeWire stream state: " << pw_stream_state_as_string(old)
<< " -> " << pw_stream_state_as_string(state);
BOOST_LOG(info) << "[pipewire] PipeWire stream state: " << pw_stream_state_as_string(old)
<< " -> " << pw_stream_state_as_string(state);
}
auto *d = static_cast<stream_data_t *>(user_data);
@@ -1003,18 +1004,18 @@
case platf::capture_e::timeout:
if (!pull_free_image_cb(img_out)) {
// Detect if shutdown is pending
BOOST_LOG(debug) << "[pipewire] PipeWire: timeout -> shutdown pending -> interrupt nudge";
BOOST_LOG(debug) << "[pipewire] PipeWire: timeout -> interrupt nudge";
pipewire.frame_cv().notify_all();
return platf::capture_e::interrupted;
}
if (!push_captured_image_cb(std::move(img_out), false)) {
BOOST_LOG(debug) << "[pipewire] PipeWire: timeout -> !push_captured_image_cb -> ok";
BOOST_LOG(debug) << "[pipewire] PipeWire: !push_captured_image_cb -> ok";
return platf::capture_e::ok;
}
break;
case platf::capture_e::ok:
if (!push_captured_image_cb(std::move(img_out), true)) {
BOOST_LOG(debug) << "[pipewire] PipeWire: !push_captured_image_cb -> ok";
BOOST_LOG(debug) << "[pipewire] PipeWire: ok -> !push_captured_image_cb -> ok";
return platf::capture_e::ok;
}
break;
src/thread_safe.h +16 −1
@@ -49,6 +49,21 @@
_cv.notify_all();
}
/**
* @brief Try to remove and return the next queued item without any further wait
*
* @return Removed queue item, or empty result when the queue is stopped or empty.
*/
status_t try_pop() {
std::lock_guard lg {_lock};
if (!_status) {
return util::false_v<status_t>;
}
auto val = std::move(_status);
_status = util::false_v<status_t>;
return val;
}
// pop and view should not be used interchangeably
/**
* @brief Remove and return the next queued item, waiting when requested.
@@ -94,6 +109,6 @@
}
auto val = std::move(_status);
_status.reset();
_status = util::false_v<status_t>;
return val;
}
src/video.cpp +1 −2
@@ -1697,8 +1697,7 @@
continue;
}
while (capture_ctx->images->peek()) {
capture_ctx->images->pop();
while (capture_ctx->images->try_pop()) {
}
++capture_ctx;