fix(linux/pipewire): avoid memory leaks (#5360)
Co-authored-by: Psyke83 <psyke83@users.noreply.github.com>
This commit is contained in:
@@ -248,7 +248,7 @@ namespace kwin {
|
|||||||
* @brief KWin screencast output name and geometry.
|
* @brief KWin screencast output name and geometry.
|
||||||
*/
|
*/
|
||||||
struct output_parameter_t {
|
struct output_parameter_t {
|
||||||
std::string name = ""; ///< KWin output name.
|
std::string name; ///< KWin output name.
|
||||||
int width = 0; ///< Output width in pixels.
|
int width = 0; ///< Output width in pixels.
|
||||||
int height = 0; ///< Output height in pixels.
|
int height = 0; ///< Output height in pixels.
|
||||||
int pos_x = 0; ///< Output X position in the compositor layout.
|
int pos_x = 0; ///< Output X position in the compositor layout.
|
||||||
@@ -272,6 +272,7 @@ namespace kwin {
|
|||||||
screencast_t &operator=(screencast_t &&) = delete; // Do not allow to copying
|
screencast_t &operator=(screencast_t &&) = delete; // Do not allow to copying
|
||||||
|
|
||||||
~screencast_t() {
|
~screencast_t() {
|
||||||
|
// Release KDE screencast wayland extensions and reset pointers
|
||||||
if (kde_screencast_stream_v1_) {
|
if (kde_screencast_stream_v1_) {
|
||||||
zkde_screencast_stream_unstable_v1_close(kde_screencast_stream_v1_);
|
zkde_screencast_stream_unstable_v1_close(kde_screencast_stream_v1_);
|
||||||
kde_screencast_stream_v1_ = nullptr;
|
kde_screencast_stream_v1_ = nullptr;
|
||||||
@@ -280,23 +281,30 @@ namespace kwin {
|
|||||||
zkde_screencast_unstable_v1_destroy(kde_screencast_v1_);
|
zkde_screencast_unstable_v1_destroy(kde_screencast_v1_);
|
||||||
kde_screencast_v1_ = nullptr;
|
kde_screencast_v1_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (kde_output_order) {
|
if (kde_output_order) {
|
||||||
kde_output_order_v1_destroy(kde_output_order);
|
kde_output_order_v1_destroy(kde_output_order);
|
||||||
kde_output_order = nullptr;
|
kde_output_order = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clear output order list
|
||||||
|
output_order.clear();
|
||||||
|
// Clear current output parameters
|
||||||
|
out_params.reset();
|
||||||
|
out_params = nullptr;
|
||||||
|
|
||||||
// wl_output is owned by the registry, released on disconnect
|
// wl_output is owned by the registry, released on disconnect
|
||||||
for (const auto &out : outputs | std::views::keys) {
|
// also cleanup associated output parameters and clear output list when done
|
||||||
wl_output_destroy(out);
|
for (auto &[output, params] : outputs) {
|
||||||
|
wl_output_destroy(output);
|
||||||
|
params.reset();
|
||||||
}
|
}
|
||||||
outputs.clear();
|
outputs.clear();
|
||||||
|
|
||||||
|
// Release wayland registry, display and reset pointers
|
||||||
if (wl_registry) {
|
if (wl_registry) {
|
||||||
wl_registry_destroy(wl_registry);
|
wl_registry_destroy(wl_registry);
|
||||||
wl_registry = nullptr;
|
wl_registry = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wl_display) {
|
if (wl_display) {
|
||||||
wl_display_disconnect(wl_display);
|
wl_display_disconnect(wl_display);
|
||||||
wl_display = nullptr;
|
wl_display = nullptr;
|
||||||
|
|||||||
@@ -123,6 +123,18 @@ namespace pipewire {
|
|||||||
int n_modifiers; ///< Number of entries in `modifiers`.
|
int n_modifiers; ///< Number of entries in `modifiers`.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Pipewire image assembled for encoding.
|
||||||
|
*/
|
||||||
|
struct img_descriptor_t: public egl::img_descriptor_t {
|
||||||
|
~img_descriptor_t() override {
|
||||||
|
if (data) {
|
||||||
|
delete[] data;
|
||||||
|
data = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief PipeWire core, context, and stream setup used for screencast capture.
|
* @brief PipeWire core, context, and stream setup used for screencast capture.
|
||||||
*/
|
*/
|
||||||
@@ -136,34 +148,44 @@ namespace pipewire {
|
|||||||
|
|
||||||
~pipewire_t() {
|
~pipewire_t() {
|
||||||
BOOST_LOG(debug) << "[pipewire] Destroying pipewire_t"sv;
|
BOOST_LOG(debug) << "[pipewire] Destroying pipewire_t"sv;
|
||||||
try {
|
|
||||||
cleanup_stream();
|
|
||||||
} catch (const std::exception &e) {
|
|
||||||
BOOST_LOG(error) << "[pipewire] Standard exception caught in ~pipewire_t cleanup_stream: "sv << e.what();
|
|
||||||
} catch (...) {
|
|
||||||
BOOST_LOG(error) << "[pipewire] Unknown exception caught in ~pipewire_t cleanup_stream"sv;
|
|
||||||
}
|
|
||||||
|
|
||||||
pw_thread_loop_lock(loop);
|
pw_thread_loop_lock(loop);
|
||||||
|
|
||||||
|
// Lock the frame mutex to stop fill_img
|
||||||
|
BOOST_LOG(debug) << "[pipewire] Stop fill_img"sv;
|
||||||
|
{
|
||||||
|
std::scoped_lock lock(stream_data.frame_mutex);
|
||||||
|
stream_data.frame_ready = false;
|
||||||
|
stream_data.current_buffer = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Release pipewire stream
|
||||||
|
if (stream_data.stream) {
|
||||||
|
BOOST_LOG(debug) << "[pipewire] Disconnect stream"sv;
|
||||||
|
pw_stream_disconnect(stream_data.stream);
|
||||||
|
BOOST_LOG(debug) << "[pipewire] Destroy stream"sv;
|
||||||
|
pw_stream_destroy(stream_data.stream);
|
||||||
|
stream_data.stream = nullptr;
|
||||||
|
}
|
||||||
|
// Release pipewire core
|
||||||
if (core) {
|
if (core) {
|
||||||
BOOST_LOG(debug) << "[pipewire] Disconnect PW core"sv;
|
BOOST_LOG(debug) << "[pipewire] Disconnect PW core"sv;
|
||||||
pw_core_disconnect(core);
|
pw_core_disconnect(core);
|
||||||
core = nullptr;
|
core = nullptr;
|
||||||
}
|
}
|
||||||
|
// Release pipewire context
|
||||||
if (context) {
|
if (context) {
|
||||||
BOOST_LOG(debug) << "[pipewire] Destroy PW context"sv;
|
BOOST_LOG(debug) << "[pipewire] Destroy PW context"sv;
|
||||||
pw_context_destroy(context);
|
pw_context_destroy(context);
|
||||||
context = nullptr;
|
context = nullptr;
|
||||||
}
|
}
|
||||||
|
// Release pipewire file descriptor
|
||||||
pw_thread_loop_unlock(loop);
|
|
||||||
|
|
||||||
if (fd >= 0) {
|
if (fd >= 0) {
|
||||||
BOOST_LOG(debug) << "[pipewire] Close pipewire_fd"sv;
|
BOOST_LOG(debug) << "[pipewire] Close pipewire_fd"sv;
|
||||||
close(fd);
|
close(fd);
|
||||||
}
|
}
|
||||||
|
// Release pipewire thread loop
|
||||||
BOOST_LOG(debug) << "[pipewire] Stop PW thread loop"sv;
|
BOOST_LOG(debug) << "[pipewire] Stop PW thread loop"sv;
|
||||||
|
pw_thread_loop_unlock(loop);
|
||||||
pw_thread_loop_stop(loop);
|
pw_thread_loop_stop(loop);
|
||||||
BOOST_LOG(debug) << "[pipewire] Destroy PW thread loop"sv;
|
BOOST_LOG(debug) << "[pipewire] Destroy PW thread loop"sv;
|
||||||
pw_thread_loop_destroy(loop);
|
pw_thread_loop_destroy(loop);
|
||||||
@@ -245,34 +267,6 @@ namespace pipewire {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Release the active PipeWire stream and listener.
|
|
||||||
*/
|
|
||||||
void cleanup_stream() {
|
|
||||||
BOOST_LOG(debug) << "[pipewire] Cleaning up stream"sv;
|
|
||||||
if (loop && stream_data.stream) {
|
|
||||||
pw_thread_loop_lock(loop);
|
|
||||||
|
|
||||||
// 1. Lock the frame mutex to stop fill_img
|
|
||||||
BOOST_LOG(debug) << "[pipewire] Stop fill_img"sv;
|
|
||||||
{
|
|
||||||
std::scoped_lock lock(stream_data.frame_mutex);
|
|
||||||
stream_data.frame_ready = false;
|
|
||||||
stream_data.current_buffer = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stream_data.stream) {
|
|
||||||
BOOST_LOG(debug) << "[pipewire] Disconnect stream"sv;
|
|
||||||
pw_stream_disconnect(stream_data.stream);
|
|
||||||
BOOST_LOG(debug) << "[pipewire] Destroy stream"sv;
|
|
||||||
pw_stream_destroy(stream_data.stream);
|
|
||||||
stream_data.stream = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
pw_thread_loop_unlock(loop);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Create the PipeWire stream if it is not already active.
|
* @brief Create the PipeWire stream if it is not already active.
|
||||||
*
|
*
|
||||||
@@ -934,7 +928,7 @@ namespace pipewire {
|
|||||||
*/
|
*/
|
||||||
std::shared_ptr<platf::img_t> alloc_img() override {
|
std::shared_ptr<platf::img_t> alloc_img() override {
|
||||||
// Note: this img_t type is also used for memory buffers
|
// Note: this img_t type is also used for memory buffers
|
||||||
auto img = std::make_shared<egl::img_descriptor_t>();
|
auto img = std::make_shared<img_descriptor_t>();
|
||||||
|
|
||||||
img->width = width;
|
img->width = width;
|
||||||
img->height = height;
|
img->height = height;
|
||||||
@@ -1067,12 +1061,7 @@ namespace pipewire {
|
|||||||
* @return Capture status reported to the streaming pipeline.
|
* @return Capture status reported to the streaming pipeline.
|
||||||
*/
|
*/
|
||||||
int dummy_img(platf::img_t *img) override {
|
int dummy_img(platf::img_t *img) override {
|
||||||
if (!img) {
|
// Empty images are recognized as dummies by the zero sequence number
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
img->data = new std::uint8_t[img->height * img->row_pitch];
|
|
||||||
std::fill_n(img->data, img->height * img->row_pitch, 0);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user