From 600f0f20bd4b972fc3cdd1c689b7671889c960c0 Mon Sep 17 00:00:00 2001 From: SoftFever Date: Sun, 30 Aug 2026 11:19:06 +0800 Subject: [PATCH] Copy the decoded frame instead of aliasing the decoder's buffer wxImage with static_data set stores the pointer and never copies it, so the frame handed to wxMediaCtrl3 aliased AVVideoDecoder::bits_. That buffer is rewritten by the next sws_scale with the mutex released, reallocated by bits_.resize() when the window grows, and freed outright when the decoder leaves PlayThread's loop body at end of stream, all while the GUI thread may be painting from it. Windows is unaffected either way, since toWxBitmap already copies the bits into GDI. --- src/slic3r/GUI/AVVideoDecoder.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/AVVideoDecoder.cpp b/src/slic3r/GUI/AVVideoDecoder.cpp index 4c951fdb61..d7b8432bd3 100644 --- a/src/slic3r/GUI/AVVideoDecoder.cpp +++ b/src/slic3r/GUI/AVVideoDecoder.cpp @@ -115,7 +115,10 @@ bool AVVideoDecoder::toWxImage(wxImage &image, wxSize const &size2) if (result_h != size.GetHeight()) { return false; } - image = wxImage(size.GetWidth(), size.GetHeight(), bits_.data(), true); + // Copy: the frame outlives this decoder and is painted by the GUI thread while the + // next sws_scale is already overwriting bits_, so it must own its pixels. The Windows + // path below needs no equivalent, wxBitmap copies the bits into GDI. + image = wxImage(size.GetWidth(), size.GetHeight(), bits_.data(), true).Copy(); if (!image.IsOk()) { fprintf(stderr, "AVVideoDecoder: image not ok %dx%d\n", size.GetWidth(), size.GetHeight()); return false;