Skip to content

Commit f460a38

Browse files
committed
frontend: fix leaving FE canvases in bad state
The `obs_frontend_remove_canvas` is called, the destructor of OBS::Canvas calls obs_canvas_release() on an object which is both allocated on the heap and used as reference. This causes the `canvases` collection on the main window to become corrupt, and crash the next time you try to add another frontend canvas. This PR modifies the canvases vector to be represented as a map of obs_canvas_t* to a shared pointer to OBS::Canvas. This will preserve both the OBS::Canvas manager instance, and pointer to obs_canvas_t* for reference by obs_frontend_canvas_remove(), ensuring that the right objects will be used when called upon.
1 parent 0b12296 commit f460a38

File tree

7 files changed

+35
-30
lines changed

7 files changed

+35
-30
lines changed

frontend/OBSStudioAPI.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -664,8 +664,8 @@ void OBSStudioAPI::obs_frontend_add_undo_redo_action(const char *name, const und
664664

665665
void OBSStudioAPI::obs_frontend_get_canvases(obs_frontend_canvas_list *canvas_list)
666666
{
667-
for (const auto &canvas : main->canvases) {
668-
obs_canvas_t *ref = obs_canvas_get_ref(canvas);
667+
for (const auto &kv : main->canvases) {
668+
obs_canvas_t *ref = obs_canvas_get_ref(kv.first);
669669
if (ref)
670670
da_push_back(canvas_list->canvases, &ref);
671671
}
@@ -674,7 +674,7 @@ void OBSStudioAPI::obs_frontend_get_canvases(obs_frontend_canvas_list *canvas_li
674674
obs_canvas_t *OBSStudioAPI::obs_frontend_add_canvas(const char *name, obs_video_info *ovi, int flags)
675675
{
676676
auto &canvas = main->AddCanvas(std::string(name), ovi, flags);
677-
return obs_canvas_get_ref(canvas);
677+
return obs_canvas_get_ref(canvas->canvas);
678678
}
679679

680680
bool OBSStudioAPI::obs_frontend_remove_canvas(obs_canvas_t *canvas)

frontend/settings/OBSBasicSettings_Stream.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,10 +173,11 @@ void OBSBasicSettings::LoadStream1Settings()
173173
ui->multitrackVideoAdditionalCanvas->clear();
174174
ui->multitrackVideoAdditionalCanvas->addItem(QTStr("None"));
175175
for (const auto &canvas : main->GetCanvases()) {
176-
if (obs_canvas_get_flags(canvas) & EPHEMERAL)
176+
if (obs_canvas_get_flags(canvas.first) & EPHEMERAL)
177177
continue;
178178

179-
ui->multitrackVideoAdditionalCanvas->addItem(obs_canvas_get_name(canvas), obs_canvas_get_uuid(canvas));
179+
ui->multitrackVideoAdditionalCanvas->addItem(obs_canvas_get_name(canvas.first),
180+
obs_canvas_get_uuid(canvas.first));
180181
}
181182

182183
if (config_has_user_value(main->Config(), "Stream1", "MultitrackExtraCanvas")) {

frontend/utility/OBSCanvas.cpp

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,14 @@ Canvas::~Canvas() noexcept
3838
canvas = nullptr;
3939
}
4040

41+
/*
4142
Canvas &Canvas::operator=(Canvas &&other) noexcept
4243
{
4344
canvas = std::exchange(other.canvas, canvas);
4445
4546
return *this;
4647
}
48+
*/
4749

4850
std::optional<OBSDataAutoRelease> Canvas::Save() const
4951
{
@@ -55,37 +57,37 @@ std::optional<OBSDataAutoRelease> Canvas::Save() const
5557
return std::nullopt;
5658
}
5759

58-
std::unique_ptr<Canvas> Canvas::Load(obs_data_t *data)
60+
std::shared_ptr<Canvas> Canvas::Load(obs_data_t *data)
5961
{
6062
if (OBSDataAutoRelease canvas_data = obs_data_get_obj(data, "info")) {
6163
if (obs_canvas_t *canvas = obs_load_canvas(canvas_data)) {
62-
return std::make_unique<Canvas>(canvas);
64+
return std::make_shared<Canvas>(canvas);
6365
}
6466
}
6567

6668
return nullptr;
6769
}
6870

69-
std::vector<Canvas> Canvas::LoadCanvases(obs_data_array_t *canvases)
71+
std::map<obs_canvas_t *, std::shared_ptr<Canvas>> Canvas::LoadCanvases(obs_data_array_t *canvases)
7072
{
7173
auto cb = [](obs_data_t *data, void *param) -> void {
72-
auto vec = static_cast<std::vector<Canvas> *>(param);
74+
auto vec = static_cast<std::map<obs_canvas_t *, std::shared_ptr<Canvas>> *>(param);
7375
if (auto canvas = Canvas::Load(data))
74-
vec->emplace_back(std::move(*canvas));
76+
(*vec)[canvas->canvas] = canvas;
7577
};
7678

77-
std::vector<Canvas> ret;
79+
std::map<obs_canvas_t *, std::shared_ptr<Canvas>> ret;
7880
obs_data_array_enum(canvases, cb, &ret);
7981

8082
return ret;
8183
}
8284

83-
OBSDataArrayAutoRelease Canvas::SaveCanvases(const std::vector<Canvas> &canvases)
85+
OBSDataArrayAutoRelease Canvas::SaveCanvases(const std::map<obs_canvas_t *, std::shared_ptr<Canvas>> &canvases)
8486
{
8587
OBSDataArrayAutoRelease savedCanvases = obs_data_array_create();
8688

87-
for (auto &canvas : canvases) {
88-
auto canvas_data = canvas.Save();
89+
for (auto kv : canvases) {
90+
auto canvas_data = kv.second->Save();
8991
if (!canvas_data)
9092
continue;
9193

frontend/utility/OBSCanvas.hpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <memory>
2121
#include <optional>
2222
#include <vector>
23+
#include <map>
2324

2425
#include "obs.h"
2526
#include "obs.hpp"
@@ -37,16 +38,16 @@ class Canvas {
3738
Canvas() = delete;
3839
Canvas(Canvas &other) = delete;
3940

40-
Canvas &operator=(Canvas &&other) noexcept;
41+
Canvas &operator=(Canvas &&other) = delete;
4142

4243
operator obs_canvas_t *() const { return canvas; }
4344

4445
[[nodiscard]] std::optional<OBSDataAutoRelease> Save() const;
45-
static std::unique_ptr<Canvas> Load(obs_data_t *data);
46-
static std::vector<Canvas> LoadCanvases(obs_data_array_t *canvases);
47-
static OBSDataArrayAutoRelease SaveCanvases(const std::vector<Canvas> &canvases);
46+
static std::shared_ptr<Canvas> Load(obs_data_t *data);
47+
static std::map<obs_canvas_t *, std::shared_ptr<Canvas>> LoadCanvases(obs_data_array_t *canvases);
48+
static OBSDataArrayAutoRelease SaveCanvases(const std::map<obs_canvas_t *, std::shared_ptr<Canvas>> &canvases);
4849

49-
private:
50+
public:
5051
obs_canvas_t *canvas = nullptr;
5152
};
5253
} // namespace OBS

frontend/widgets/OBSBasic.hpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1120,14 +1120,15 @@ private slots:
11201120
* -------------------------------------
11211121
*/
11221122
private:
1123-
std::vector<OBS::Canvas> canvases;
1123+
std::map<obs_canvas_t *, std::shared_ptr<OBS::Canvas>> canvases;
11241124

11251125
static void CanvasRemoved(void *data, calldata_t *params);
11261126

11271127
public:
1128-
const std::vector<OBS::Canvas> &GetCanvases() const noexcept { return canvases; }
1128+
const std::map<obs_canvas_t *, std::shared_ptr<OBS::Canvas>> &GetCanvases() const noexcept { return canvases; }
11291129

1130-
const OBS::Canvas &AddCanvas(const std::string &name, obs_video_info *ovi = nullptr, int flags = 0);
1130+
const std::shared_ptr<OBS::Canvas> AddCanvas(const std::string &name, obs_video_info *ovi = nullptr,
1131+
int flags = 0);
11311132

11321133
public slots:
11331134
bool RemoveCanvas(OBSCanvas canvas);

frontend/widgets/OBSBasic_Canvases.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,22 +23,22 @@ void OBSBasic::CanvasRemoved(void *data, calldata_t *params)
2323
QMetaObject::invokeMethod(static_cast<OBSBasic *>(data), "RemoveCanvas", Q_ARG(OBSCanvas, OBSCanvas(canvas)));
2424
}
2525

26-
const OBS::Canvas &OBSBasic::AddCanvas(const std::string &name, obs_video_info *ovi, int flags)
26+
const std::shared_ptr<OBS::Canvas> OBSBasic::AddCanvas(const std::string &name, obs_video_info *ovi, int flags)
2727
{
2828
OBSCanvas canvas = obs_canvas_create(name.c_str(), ovi, flags);
29-
auto &it = canvases.emplace_back(canvas);
29+
canvases[canvas] = std::make_shared<OBS::Canvas>(canvas);
30+
3031
OnEvent(OBS_FRONTEND_EVENT_CANVAS_ADDED);
31-
return it;
32+
return canvases[canvas];
3233
}
3334

3435
bool OBSBasic::RemoveCanvas(OBSCanvas canvas)
3536
{
3637
if (!canvas)
3738
return false;
3839

39-
auto canvas_it = std::find(std::begin(canvases), std::end(canvases), canvas);
40-
if (canvas_it != std::end(canvases)) {
41-
canvases.erase(canvas_it);
40+
if (canvases.count(canvas)) {
41+
canvases.erase(canvas);
4242
OnEvent(OBS_FRONTEND_EVENT_CANVAS_REMOVED);
4343
return true;
4444
}

frontend/widgets/OBSBasic_SceneCollections.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1535,8 +1535,8 @@ void OBSBasic::ClearSceneData()
15351535
obs_enum_scenes(cb, nullptr);
15361536
obs_enum_sources(cb, nullptr);
15371537

1538-
for (const auto &canvas : canvases) {
1539-
obs_canvas_enum_scenes(canvas, cb, nullptr);
1538+
for (const auto &kv : canvases) {
1539+
obs_canvas_enum_scenes(kv.first, cb, nullptr);
15401540
}
15411541

15421542
canvases.clear();

0 commit comments

Comments
 (0)