Skip to content

Commit 17e896e

Browse files
Fix NDI 6 runtime loading on macOS (#747)
* libobs-winrt: Guard WGC capture against removed graphics device (#733) * Guard DirectShow filter activation (#740) * libobs: append message/get_messages to obs_source_info (#735) * libobs: bound volmeter plane index against MAX_AV_PLANES (#737) * libobs: mix scene-item audio when item canvas is unset (#738) * libobs: Fix use-after-free of canvas view in audio thread (#736) * Add structured module load failures (#741) * Fix NDI 6 runtime loading on macOS --------- Co-authored-by: Vladimir <summeroff@gmail.com>
1 parent 87ab5ba commit 17e896e

11 files changed

Lines changed: 120 additions & 26 deletions

File tree

.gitmodules

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
[submodule "plugins/obs-ndi"]
1414
path = plugins/obs-ndi
1515
url = https://github.com/streamlabs/obs-ndi.git
16-
branch = streamlabs-obs
16+
branch = streamlabs
1717
[submodule "plugins/slobs-virtual-cam"]
1818
path = plugins/slobs-virtual-cam
1919
url = https://github.com/streamlabs/slobs-virtual-cam.git

deps/libdshowcapture/src

libobs-winrt/winrt-capture.cpp

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -301,13 +301,22 @@ static void winrt_capture_device_loss_rebuild(void *device_void, void *data)
301301
return;
302302

303303
ID3D11Device *const d3d_device = (ID3D11Device *)device_void;
304+
if (!d3d_device || FAILED(d3d_device->GetDeviceRemovedReason())) {
305+
blog(LOG_WARNING, "Skipping WinRT capture rebuild; graphics device unavailable or removed");
306+
return;
307+
}
308+
304309
ComPtr<IDXGIDevice> dxgi_device;
305-
if (FAILED(d3d_device->QueryInterface(&dxgi_device)))
310+
if (FAILED(d3d_device->QueryInterface(&dxgi_device))) {
306311
blog(LOG_ERROR, "Failed to get DXGI device");
312+
return;
313+
}
307314

308315
winrt::com_ptr<IInspectable> inspectable;
309-
if (FAILED(CreateDirect3D11DeviceFromDXGIDevice(dxgi_device.Get(), inspectable.put())))
316+
if (FAILED(CreateDirect3D11DeviceFromDXGIDevice(dxgi_device.Get(), inspectable.put()))) {
310317
blog(LOG_ERROR, "Failed to get WinRT device");
318+
return;
319+
}
311320

312321
const winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice device =
313322
inspectable.as<winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice>();
@@ -349,6 +358,11 @@ static struct winrt_capture *winrt_capture_init_internal(BOOL cursor, HWND windo
349358
HMONITOR monitor)
350359
try {
351360
ID3D11Device *const d3d_device = (ID3D11Device *)gs_get_device_obj();
361+
if (!d3d_device || FAILED(d3d_device->GetDeviceRemovedReason())) {
362+
blog(LOG_WARNING, "Skipping WinRT capture init; graphics device unavailable or removed");
363+
return nullptr;
364+
}
365+
352366
ComPtr<IDXGIDevice> dxgi_device;
353367

354368
HRESULT hr = d3d_device->QueryInterface(&dxgi_device);

libobs/obs-audio-controls.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -418,7 +418,7 @@ static void volmeter_process_peak(obs_volmeter_t *volmeter, const struct audio_d
418418
{
419419
int nr_samples = data->frames;
420420
int channel_nr = 0;
421-
for (int plane_nr = 0; channel_nr < nr_channels; plane_nr++) {
421+
for (int plane_nr = 0; channel_nr < nr_channels && plane_nr < MAX_AV_PLANES; plane_nr++) {
422422
float *samples = (float *)data->data[plane_nr];
423423
if (!samples) {
424424
continue;
@@ -466,7 +466,7 @@ static void volmeter_process_magnitude(obs_volmeter_t *volmeter, const struct au
466466
size_t nr_samples = data->frames;
467467

468468
int channel_nr = 0;
469-
for (int plane_nr = 0; channel_nr < nr_channels; plane_nr++) {
469+
for (int plane_nr = 0; channel_nr < nr_channels && plane_nr < MAX_AV_PLANES; plane_nr++) {
470470
float *samples = (float *)data->data[plane_nr];
471471
if (!samples) {
472472
continue;

libobs/obs-canvas.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,9 @@ void obs_canvas_destroy(obs_canvas_t *canvas)
228228

229229
pthread_mutex_destroy(&canvas->sources_mutex);
230230
obs_context_data_free(&canvas->context);
231+
232+
/* Null mixes referencing this view before freeing it; audio_callback() reads it via mix->view. */
233+
obs_view_remove(&canvas->view);
231234
obs_view_free(&canvas->view);
232235
bfree(canvas);
233236
}

libobs/obs-internal.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,8 @@ struct obs_module {
113113
char *data_path;
114114
void *module;
115115
bool loaded;
116+
char *load_error_code;
117+
char *load_error_message;
116118

117119
bool (*load)(void);
118120
void (*unload)(void);

libobs/obs-module.c

Lines changed: 72 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,18 @@ int obs_open_module(obs_module_t **module, const char *path, const char *data_pa
156156
return MODULE_SUCCESS;
157157
}
158158

159+
static void clear_module_load_error(obs_module_t *module)
160+
{
161+
if (!module)
162+
return;
163+
164+
bfree(module->load_error_code);
165+
module->load_error_code = NULL;
166+
167+
bfree(module->load_error_message);
168+
module->load_error_message = NULL;
169+
}
170+
159171
bool obs_init_module(obs_module_t *module)
160172
{
161173
if (!module || !obs)
@@ -167,14 +179,34 @@ bool obs_init_module(obs_module_t *module)
167179
profile_store_name(obs_get_profiler_name_store(), "obs_init_module(%s)", module->file);
168180
profile_start(profile_name);
169181

182+
clear_module_load_error(module);
170183
module->loaded = module->load();
171-
if (!module->loaded)
172-
blog(LOG_WARNING, "Failed to initialize module '%s'", module->file);
184+
if (!module->loaded) {
185+
if (module->load_error_code && module->load_error_message) {
186+
blog(LOG_WARNING, "Failed to initialize module '%s': %s (%s)", module->file,
187+
module->load_error_message, module->load_error_code);
188+
} else if (module->load_error_code) {
189+
blog(LOG_WARNING, "Failed to initialize module '%s': %s", module->file,
190+
module->load_error_code);
191+
} else {
192+
blog(LOG_WARNING, "Failed to initialize module '%s'", module->file);
193+
}
194+
}
173195

174196
profile_end(profile_name);
175197
return module->loaded;
176198
}
177199

200+
void obs_module_set_load_error(obs_module_t *module, const char *code, const char *message)
201+
{
202+
if (!module)
203+
return;
204+
205+
clear_module_load_error(module);
206+
module->load_error_code = bstrdup(code ? code : "");
207+
module->load_error_message = bstrdup(message ? message : "");
208+
}
209+
178210
void obs_log_loaded_modules(void)
179211
{
180212
blog(LOG_INFO, " Loaded Modules:");
@@ -294,9 +326,25 @@ extern void get_plugin_info(const char *path, bool *is_obs_plugin, bool *can_loa
294326

295327
struct fail_info {
296328
struct dstr fail_modules;
297-
size_t fail_count;
329+
DARRAY(struct obs_module_load_failure) failures;
298330
};
299331

332+
static void add_module_failure(struct fail_info *fail_info, const char *module, const char *code, const char *message)
333+
{
334+
if (!fail_info)
335+
return;
336+
337+
struct obs_module_load_failure failure = {
338+
bstrdup(module ? module : ""),
339+
bstrdup((code && *code) ? code : "MODULE_LOAD_FAILED"),
340+
bstrdup((message && *message) ? message : "Module failed to load."),
341+
};
342+
343+
dstr_cat(&fail_info->fail_modules, failure.module);
344+
dstr_cat(&fail_info->fail_modules, ";");
345+
da_push_back(fail_info->failures, &failure);
346+
}
347+
300348
static bool is_safe_module(const char *name)
301349
{
302350
if (!obs->safe_modules.num)
@@ -356,18 +404,16 @@ static void load_all_callback(void *param, const struct obs_module_info2 *info)
356404
return;
357405
}
358406

359-
if (!obs_init_module(module))
407+
if (!obs_init_module(module)) {
408+
add_module_failure(fail_info, info->name, module->load_error_code, module->load_error_message);
360409
free_module(module);
410+
}
361411

362412
UNUSED_PARAMETER(param);
363413
return;
364414

365415
load_failure:
366-
if (fail_info) {
367-
dstr_cat(&fail_info->fail_modules, info->name);
368-
dstr_cat(&fail_info->fail_modules, ";");
369-
fail_info->fail_count++;
370-
}
416+
add_module_failure(fail_info, info->name, NULL, NULL);
371417
}
372418

373419
static const char *obs_load_all_modules_name = "obs_load_all_modules";
@@ -403,7 +449,8 @@ void obs_load_all_modules2(struct obs_module_failure_info *mfi)
403449
#endif
404450
profile_end(obs_load_all_modules2_name);
405451

406-
mfi->count = fail_info.fail_count;
452+
mfi->count = fail_info.failures.num;
453+
mfi->failures = fail_info.failures.array;
407454
mfi->failed_modules = strlist_split(fail_info.fail_modules.array, ';', false);
408455
dstr_free(&fail_info.fail_modules);
409456
}
@@ -414,6 +461,19 @@ void obs_module_failure_info_free(struct obs_module_failure_info *mfi)
414461
bfree(mfi->failed_modules);
415462
mfi->failed_modules = NULL;
416463
}
464+
465+
if (mfi->failures) {
466+
for (size_t i = 0; i < mfi->count; i++) {
467+
bfree(mfi->failures[i].module);
468+
bfree(mfi->failures[i].code);
469+
bfree(mfi->failures[i].message);
470+
}
471+
472+
bfree(mfi->failures);
473+
mfi->failures = NULL;
474+
}
475+
476+
mfi->count = 0;
417477
}
418478

419479
void obs_post_load_modules(void)
@@ -621,6 +681,8 @@ void free_module(struct obs_module *mod)
621681
bfree(mod->mod_name);
622682
bfree(mod->bin_path);
623683
bfree(mod->data_path);
684+
bfree(mod->load_error_code);
685+
bfree(mod->load_error_message);
624686
bfree(mod);
625687
}
626688

libobs/obs-scene.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2111,9 +2111,12 @@ static bool scene_audio_render_do(void *data, uint64_t *ts_out,
21112111
for (size_t canvas_idx = 0;
21122112
canvas_idx < audio_output->outputs.num;
21132113
canvas_idx++) {
2114-
if (item->canvas !=
2115-
obs->video.canvases
2116-
.array[canvas_idx]) {
2114+
/* NULL canvas = all canvases, as in
2115+
* scene_video_render */
2116+
if (item->canvas &&
2117+
item->canvas !=
2118+
obs->video.canvases
2119+
.array[canvas_idx]) {
21172120
continue;
21182121
}
21192122

libobs/obs-source.h

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -301,12 +301,6 @@ struct obs_source_info {
301301
/** Called when the source has been activated in the main view */
302302
void (*activate)(void *data);
303303

304-
/** Called to send message to the source */
305-
void (*message)(void *data, obs_data_t *settings);
306-
307-
/** Called to get messages from the source */
308-
obs_data_array_t *(*get_messages)(void *data);
309-
310304
/**
311305
* Called when the source has been deactivated from the main view
312306
* (no longer being played/displayed)
@@ -562,6 +556,14 @@ struct obs_source_info {
562556
* @param source Source that the filter is being added to
563557
*/
564558
void (*filter_add)(void *data, obs_source_t *source);
559+
560+
/* Append-only: mid-struct inserts shift later offsets and break module ABI. */
561+
562+
/** Called to send message to the source */
563+
void (*message)(void *data, obs_data_t *settings);
564+
565+
/** Called to get messages from the source */
566+
obs_data_array_t *(*get_messages)(void *data);
565567
};
566568

567569
EXPORT void obs_register_source_s(const struct obs_source_info *info, size_t size);

libobs/obs.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -589,13 +589,21 @@ EXPORT void obs_add_safe_module(const char *name);
589589
/** Automatically loads all modules from module paths (convenience function) */
590590
EXPORT void obs_load_all_modules(void);
591591

592+
struct obs_module_load_failure {
593+
char *module;
594+
char *code;
595+
char *message;
596+
};
597+
592598
struct obs_module_failure_info {
593599
char **failed_modules;
594600
size_t count;
601+
struct obs_module_load_failure *failures;
595602
};
596603

597604
EXPORT void obs_module_failure_info_free(struct obs_module_failure_info *mfi);
598605
EXPORT void obs_load_all_modules2(struct obs_module_failure_info *mfi);
606+
EXPORT void obs_module_set_load_error(obs_module_t *module, const char *code, const char *message);
599607

600608
/** Notifies modules that all modules have been loaded. This function should
601609
* be called after all modules have been loaded. */

0 commit comments

Comments
 (0)