1 /******************************************************************************
2 Copyright (C) 2013-2014 by Hugh Bailey <obs.jim@gmail.com>
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
16 ******************************************************************************/
17
18 #include <inttypes.h>
19
20 #include "graphics/matrix4.h"
21 #include "callback/calldata.h"
22
23 #include "obs.h"
24 #include "obs-internal.h"
25
26 struct obs_core *obs = NULL;
27
28 extern void add_default_module_paths(void);
29 extern char *find_libobs_data_file(const char *file);
30
make_video_info(struct video_output_info * vi,struct obs_video_info * ovi)31 static inline void make_video_info(struct video_output_info *vi,
32 struct obs_video_info *ovi)
33 {
34 vi->name = "video";
35 vi->format = ovi->output_format;
36 vi->fps_num = ovi->fps_num;
37 vi->fps_den = ovi->fps_den;
38 vi->width = ovi->output_width;
39 vi->height = ovi->output_height;
40 vi->range = ovi->range;
41 vi->colorspace = ovi->colorspace;
42 vi->cache_size = 6;
43 }
44
calc_gpu_conversion_sizes(const struct obs_video_info * ovi)45 static inline void calc_gpu_conversion_sizes(const struct obs_video_info *ovi)
46 {
47 struct obs_core_video *video = &obs->video;
48
49 video->conversion_needed = false;
50 video->conversion_techs[0] = NULL;
51 video->conversion_techs[1] = NULL;
52 video->conversion_techs[2] = NULL;
53 video->conversion_width_i = 0.f;
54
55 switch ((uint32_t)ovi->output_format) {
56 case VIDEO_FORMAT_I420:
57 video->conversion_needed = true;
58 video->conversion_techs[0] = "Planar_Y";
59 video->conversion_techs[1] = "Planar_U_Left";
60 video->conversion_techs[2] = "Planar_V_Left";
61 video->conversion_width_i = 1.f / (float)ovi->output_width;
62 break;
63 case VIDEO_FORMAT_NV12:
64 video->conversion_needed = true;
65 video->conversion_techs[0] = "NV12_Y";
66 video->conversion_techs[1] = "NV12_UV";
67 video->conversion_width_i = 1.f / (float)ovi->output_width;
68 break;
69 case VIDEO_FORMAT_I444:
70 video->conversion_needed = true;
71 video->conversion_techs[0] = "Planar_Y";
72 video->conversion_techs[1] = "Planar_U";
73 video->conversion_techs[2] = "Planar_V";
74 break;
75 }
76 }
77
obs_init_gpu_conversion(struct obs_video_info * ovi)78 static bool obs_init_gpu_conversion(struct obs_video_info *ovi)
79 {
80 struct obs_core_video *video = &obs->video;
81
82 calc_gpu_conversion_sizes(ovi);
83
84 video->using_nv12_tex = ovi->output_format == VIDEO_FORMAT_NV12
85 ? gs_nv12_available()
86 : false;
87
88 if (!video->conversion_needed) {
89 blog(LOG_INFO, "GPU conversion not available for format: %u",
90 (unsigned int)ovi->output_format);
91 video->gpu_conversion = false;
92 video->using_nv12_tex = false;
93 blog(LOG_INFO, "NV12 texture support not available");
94 return true;
95 }
96
97 if (video->using_nv12_tex)
98 blog(LOG_INFO, "NV12 texture support enabled");
99 else
100 blog(LOG_INFO, "NV12 texture support not available");
101
102 #ifdef _WIN32
103 if (video->using_nv12_tex) {
104 gs_texture_create_nv12(&video->convert_textures[0],
105 &video->convert_textures[1],
106 ovi->output_width, ovi->output_height,
107 GS_RENDER_TARGET | GS_SHARED_KM_TEX);
108 } else {
109 #endif
110 video->convert_textures[0] =
111 gs_texture_create(ovi->output_width, ovi->output_height,
112 GS_R8, 1, NULL, GS_RENDER_TARGET);
113
114 const struct video_output_info *info =
115 video_output_get_info(video->video);
116 switch (info->format) {
117 case VIDEO_FORMAT_I420:
118 video->convert_textures[1] = gs_texture_create(
119 ovi->output_width / 2, ovi->output_height / 2,
120 GS_R8, 1, NULL, GS_RENDER_TARGET);
121 video->convert_textures[2] = gs_texture_create(
122 ovi->output_width / 2, ovi->output_height / 2,
123 GS_R8, 1, NULL, GS_RENDER_TARGET);
124 if (!video->convert_textures[2])
125 return false;
126 break;
127 case VIDEO_FORMAT_NV12:
128 video->convert_textures[1] = gs_texture_create(
129 ovi->output_width / 2, ovi->output_height / 2,
130 GS_R8G8, 1, NULL, GS_RENDER_TARGET);
131 break;
132 case VIDEO_FORMAT_I444:
133 video->convert_textures[1] = gs_texture_create(
134 ovi->output_width, ovi->output_height, GS_R8, 1,
135 NULL, GS_RENDER_TARGET);
136 video->convert_textures[2] = gs_texture_create(
137 ovi->output_width, ovi->output_height, GS_R8, 1,
138 NULL, GS_RENDER_TARGET);
139 if (!video->convert_textures[2])
140 return false;
141 break;
142 default:
143 break;
144 }
145 #ifdef _WIN32
146 }
147 #endif
148
149 if (!video->convert_textures[0])
150 return false;
151 if (!video->convert_textures[1])
152 return false;
153
154 return true;
155 }
156
obs_init_gpu_copy_surfaces(struct obs_video_info * ovi,size_t i)157 static bool obs_init_gpu_copy_surfaces(struct obs_video_info *ovi, size_t i)
158 {
159 struct obs_core_video *video = &obs->video;
160
161 video->copy_surfaces[i][0] = gs_stagesurface_create(
162 ovi->output_width, ovi->output_height, GS_R8);
163 if (!video->copy_surfaces[i][0])
164 return false;
165
166 const struct video_output_info *info =
167 video_output_get_info(video->video);
168 switch (info->format) {
169 case VIDEO_FORMAT_I420:
170 video->copy_surfaces[i][1] = gs_stagesurface_create(
171 ovi->output_width / 2, ovi->output_height / 2, GS_R8);
172 if (!video->copy_surfaces[i][1])
173 return false;
174 video->copy_surfaces[i][2] = gs_stagesurface_create(
175 ovi->output_width / 2, ovi->output_height / 2, GS_R8);
176 if (!video->copy_surfaces[i][2])
177 return false;
178 break;
179 case VIDEO_FORMAT_NV12:
180 video->copy_surfaces[i][1] = gs_stagesurface_create(
181 ovi->output_width / 2, ovi->output_height / 2, GS_R8G8);
182 if (!video->copy_surfaces[i][1])
183 return false;
184 break;
185 case VIDEO_FORMAT_I444:
186 video->copy_surfaces[i][1] = gs_stagesurface_create(
187 ovi->output_width, ovi->output_height, GS_R8);
188 if (!video->copy_surfaces[i][1])
189 return false;
190 video->copy_surfaces[i][2] = gs_stagesurface_create(
191 ovi->output_width, ovi->output_height, GS_R8);
192 if (!video->copy_surfaces[i][2])
193 return false;
194 break;
195 default:
196 break;
197 }
198
199 return true;
200 }
201
obs_init_textures(struct obs_video_info * ovi)202 static bool obs_init_textures(struct obs_video_info *ovi)
203 {
204 struct obs_core_video *video = &obs->video;
205
206 for (size_t i = 0; i < NUM_TEXTURES; i++) {
207 #ifdef _WIN32
208 if (video->using_nv12_tex) {
209 video->copy_surfaces[i][0] =
210 gs_stagesurface_create_nv12(ovi->output_width,
211 ovi->output_height);
212 if (!video->copy_surfaces[i][0])
213 return false;
214
215 } else {
216 #endif
217 if (video->gpu_conversion) {
218 if (!obs_init_gpu_copy_surfaces(ovi, i))
219 return false;
220 } else {
221 video->copy_surfaces[i][0] =
222 gs_stagesurface_create(
223 ovi->output_width,
224 ovi->output_height, GS_RGBA);
225 if (!video->copy_surfaces[i][0])
226 return false;
227 }
228 #ifdef _WIN32
229 }
230 #endif
231 }
232
233 video->render_texture = gs_texture_create(ovi->base_width,
234 ovi->base_height, GS_RGBA, 1,
235 NULL, GS_RENDER_TARGET);
236
237 if (!video->render_texture)
238 return false;
239
240 video->output_texture = gs_texture_create(ovi->output_width,
241 ovi->output_height, GS_RGBA,
242 1, NULL, GS_RENDER_TARGET);
243
244 if (!video->output_texture)
245 return false;
246
247 return true;
248 }
249
obs_load_effect(gs_effect_t ** effect,const char * file)250 gs_effect_t *obs_load_effect(gs_effect_t **effect, const char *file)
251 {
252 if (!*effect) {
253 char *filename = obs_find_data_file(file);
254 *effect = gs_effect_create_from_file(filename, NULL);
255 bfree(filename);
256 }
257
258 return *effect;
259 }
260
obs_init_graphics(struct obs_video_info * ovi)261 static int obs_init_graphics(struct obs_video_info *ovi)
262 {
263 struct obs_core_video *video = &obs->video;
264 uint8_t transparent_tex_data[2 * 2 * 4] = {0};
265 const uint8_t *transparent_tex = transparent_tex_data;
266 struct gs_sampler_info point_sampler = {0};
267 bool success = true;
268 int errorcode;
269
270 errorcode =
271 gs_create(&video->graphics, ovi->graphics_module, ovi->adapter);
272 if (errorcode != GS_SUCCESS) {
273 switch (errorcode) {
274 case GS_ERROR_MODULE_NOT_FOUND:
275 return OBS_VIDEO_MODULE_NOT_FOUND;
276 case GS_ERROR_NOT_SUPPORTED:
277 return OBS_VIDEO_NOT_SUPPORTED;
278 default:
279 return OBS_VIDEO_FAIL;
280 }
281 }
282
283 gs_enter_context(video->graphics);
284
285 char *filename = obs_find_data_file("default.effect");
286 video->default_effect = gs_effect_create_from_file(filename, NULL);
287 bfree(filename);
288
289 if (gs_get_device_type() == GS_DEVICE_OPENGL) {
290 filename = obs_find_data_file("default_rect.effect");
291 video->default_rect_effect =
292 gs_effect_create_from_file(filename, NULL);
293 bfree(filename);
294 }
295
296 filename = obs_find_data_file("opaque.effect");
297 video->opaque_effect = gs_effect_create_from_file(filename, NULL);
298 bfree(filename);
299
300 filename = obs_find_data_file("solid.effect");
301 video->solid_effect = gs_effect_create_from_file(filename, NULL);
302 bfree(filename);
303
304 filename = obs_find_data_file("repeat.effect");
305 video->repeat_effect = gs_effect_create_from_file(filename, NULL);
306 bfree(filename);
307
308 filename = obs_find_data_file("format_conversion.effect");
309 video->conversion_effect = gs_effect_create_from_file(filename, NULL);
310 bfree(filename);
311
312 filename = obs_find_data_file("bicubic_scale.effect");
313 video->bicubic_effect = gs_effect_create_from_file(filename, NULL);
314 bfree(filename);
315
316 filename = obs_find_data_file("lanczos_scale.effect");
317 video->lanczos_effect = gs_effect_create_from_file(filename, NULL);
318 bfree(filename);
319
320 filename = obs_find_data_file("area.effect");
321 video->area_effect = gs_effect_create_from_file(filename, NULL);
322 bfree(filename);
323
324 filename = obs_find_data_file("bilinear_lowres_scale.effect");
325 video->bilinear_lowres_effect =
326 gs_effect_create_from_file(filename, NULL);
327 bfree(filename);
328
329 filename = obs_find_data_file("premultiplied_alpha.effect");
330 video->premultiplied_alpha_effect =
331 gs_effect_create_from_file(filename, NULL);
332 bfree(filename);
333
334 point_sampler.max_anisotropy = 1;
335 video->point_sampler = gs_samplerstate_create(&point_sampler);
336
337 obs->video.transparent_texture =
338 gs_texture_create(2, 2, GS_RGBA, 1, &transparent_tex, 0);
339
340 if (!video->default_effect)
341 success = false;
342 if (gs_get_device_type() == GS_DEVICE_OPENGL) {
343 if (!video->default_rect_effect)
344 success = false;
345 }
346 if (!video->opaque_effect)
347 success = false;
348 if (!video->solid_effect)
349 success = false;
350 if (!video->conversion_effect)
351 success = false;
352 if (!video->premultiplied_alpha_effect)
353 success = false;
354 if (!video->transparent_texture)
355 success = false;
356 if (!video->point_sampler)
357 success = false;
358
359 gs_leave_context();
360 return success ? OBS_VIDEO_SUCCESS : OBS_VIDEO_FAIL;
361 }
362
set_video_matrix(struct obs_core_video * video,struct obs_video_info * ovi)363 static inline void set_video_matrix(struct obs_core_video *video,
364 struct obs_video_info *ovi)
365 {
366 struct matrix4 mat;
367 struct vec4 r_row;
368
369 if (format_is_yuv(ovi->output_format)) {
370 video_format_get_parameters(ovi->colorspace, ovi->range,
371 (float *)&mat, NULL, NULL);
372 matrix4_inv(&mat, &mat);
373
374 /* swap R and G */
375 r_row = mat.x;
376 mat.x = mat.y;
377 mat.y = r_row;
378 } else {
379 matrix4_identity(&mat);
380 }
381
382 memcpy(video->color_matrix, &mat, sizeof(float) * 16);
383 }
384
obs_init_video(struct obs_video_info * ovi)385 static int obs_init_video(struct obs_video_info *ovi)
386 {
387 struct obs_core_video *video = &obs->video;
388 struct video_output_info vi;
389 int errorcode;
390
391 make_video_info(&vi, ovi);
392 video->base_width = ovi->base_width;
393 video->base_height = ovi->base_height;
394 video->output_width = ovi->output_width;
395 video->output_height = ovi->output_height;
396 video->gpu_conversion = ovi->gpu_conversion;
397 video->scale_type = ovi->scale_type;
398
399 set_video_matrix(video, ovi);
400
401 errorcode = video_output_open(&video->video, &vi);
402
403 if (errorcode != VIDEO_OUTPUT_SUCCESS) {
404 if (errorcode == VIDEO_OUTPUT_INVALIDPARAM) {
405 blog(LOG_ERROR, "Invalid video parameters specified");
406 return OBS_VIDEO_INVALID_PARAM;
407 } else {
408 blog(LOG_ERROR, "Could not open video output");
409 }
410 return OBS_VIDEO_FAIL;
411 }
412
413 gs_enter_context(video->graphics);
414
415 if (ovi->gpu_conversion && !obs_init_gpu_conversion(ovi))
416 return OBS_VIDEO_FAIL;
417 if (!obs_init_textures(ovi))
418 return OBS_VIDEO_FAIL;
419
420 gs_leave_context();
421
422 if (pthread_mutex_init(&video->gpu_encoder_mutex, NULL) < 0)
423 return OBS_VIDEO_FAIL;
424 if (pthread_mutex_init(&video->task_mutex, NULL) < 0)
425 return OBS_VIDEO_FAIL;
426
427 #ifdef __APPLE__
428 errorcode = pthread_create(&video->video_thread, NULL,
429 obs_graphics_thread_autorelease, obs);
430 #else
431 errorcode = pthread_create(&video->video_thread, NULL,
432 obs_graphics_thread, obs);
433 #endif
434 if (errorcode != 0)
435 return OBS_VIDEO_FAIL;
436
437 video->thread_initialized = true;
438 video->ovi = *ovi;
439 return OBS_VIDEO_SUCCESS;
440 }
441
stop_video(void)442 static void stop_video(void)
443 {
444 struct obs_core_video *video = &obs->video;
445 void *thread_retval;
446
447 if (video->video) {
448 video_output_stop(video->video);
449 if (video->thread_initialized) {
450 pthread_join(video->video_thread, &thread_retval);
451 video->thread_initialized = false;
452 }
453 }
454 }
455
obs_free_video(void)456 static void obs_free_video(void)
457 {
458 struct obs_core_video *video = &obs->video;
459
460 if (video->video) {
461 video_output_close(video->video);
462 video->video = NULL;
463
464 if (!video->graphics)
465 return;
466
467 gs_enter_context(video->graphics);
468
469 for (size_t c = 0; c < NUM_CHANNELS; c++) {
470 if (video->mapped_surfaces[c]) {
471 gs_stagesurface_unmap(
472 video->mapped_surfaces[c]);
473 video->mapped_surfaces[c] = NULL;
474 }
475 }
476
477 for (size_t i = 0; i < NUM_TEXTURES; i++) {
478 for (size_t c = 0; c < NUM_CHANNELS; c++) {
479 if (video->copy_surfaces[i][c]) {
480 gs_stagesurface_destroy(
481 video->copy_surfaces[i][c]);
482 video->copy_surfaces[i][c] = NULL;
483 }
484 }
485 }
486
487 gs_texture_destroy(video->render_texture);
488
489 for (size_t c = 0; c < NUM_CHANNELS; c++) {
490 if (video->convert_textures[c]) {
491 gs_texture_destroy(video->convert_textures[c]);
492 video->convert_textures[c] = NULL;
493 }
494 }
495
496 for (size_t i = 0; i < NUM_TEXTURES; i++) {
497 for (size_t c = 0; c < NUM_CHANNELS; c++) {
498 if (video->copy_surfaces[i][c]) {
499 gs_stagesurface_destroy(
500 video->copy_surfaces[i][c]);
501 video->copy_surfaces[i][c] = NULL;
502 }
503 }
504 }
505
506 gs_texture_destroy(video->output_texture);
507 video->render_texture = NULL;
508 video->output_texture = NULL;
509
510 gs_leave_context();
511
512 circlebuf_free(&video->vframe_info_buffer);
513 circlebuf_free(&video->vframe_info_buffer_gpu);
514
515 video->texture_rendered = false;
516 memset(video->textures_copied, 0,
517 sizeof(video->textures_copied));
518 video->texture_converted = false;
519
520 pthread_mutex_destroy(&video->gpu_encoder_mutex);
521 pthread_mutex_init_value(&video->gpu_encoder_mutex);
522 da_free(video->gpu_encoders);
523
524 pthread_mutex_destroy(&video->task_mutex);
525 pthread_mutex_init_value(&video->task_mutex);
526 circlebuf_free(&video->tasks);
527
528 video->gpu_encoder_active = 0;
529 video->cur_texture = 0;
530 }
531 }
532
obs_free_graphics(void)533 static void obs_free_graphics(void)
534 {
535 struct obs_core_video *video = &obs->video;
536
537 if (video->graphics) {
538 gs_enter_context(video->graphics);
539
540 gs_texture_destroy(video->transparent_texture);
541
542 gs_samplerstate_destroy(video->point_sampler);
543
544 gs_effect_destroy(video->default_effect);
545 gs_effect_destroy(video->default_rect_effect);
546 gs_effect_destroy(video->opaque_effect);
547 gs_effect_destroy(video->solid_effect);
548 gs_effect_destroy(video->conversion_effect);
549 gs_effect_destroy(video->bicubic_effect);
550 gs_effect_destroy(video->repeat_effect);
551 gs_effect_destroy(video->lanczos_effect);
552 gs_effect_destroy(video->area_effect);
553 gs_effect_destroy(video->bilinear_lowres_effect);
554 video->default_effect = NULL;
555
556 gs_leave_context();
557
558 gs_destroy(video->graphics);
559 video->graphics = NULL;
560 }
561 }
562
obs_init_audio(struct audio_output_info * ai)563 static bool obs_init_audio(struct audio_output_info *ai)
564 {
565 struct obs_core_audio *audio = &obs->audio;
566 int errorcode;
567
568 pthread_mutex_init_value(&audio->monitoring_mutex);
569
570 if (pthread_mutex_init_recursive(&audio->monitoring_mutex) != 0)
571 return false;
572
573 audio->user_volume = 1.0f;
574
575 audio->monitoring_device_name = bstrdup("Default");
576 audio->monitoring_device_id = bstrdup("default");
577
578 errorcode = audio_output_open(&audio->audio, ai);
579 if (errorcode == AUDIO_OUTPUT_SUCCESS)
580 return true;
581 else if (errorcode == AUDIO_OUTPUT_INVALIDPARAM)
582 blog(LOG_ERROR, "Invalid audio parameters specified");
583 else
584 blog(LOG_ERROR, "Could not open audio output");
585
586 return false;
587 }
588
stop_audio(void)589 static void stop_audio(void)
590 {
591 struct obs_core_audio *audio = &obs->audio;
592
593 if (audio->audio) {
594 audio_output_close(audio->audio);
595 audio->audio = NULL;
596 }
597 }
598
obs_free_audio(void)599 static void obs_free_audio(void)
600 {
601 struct obs_core_audio *audio = &obs->audio;
602 if (audio->audio)
603 audio_output_close(audio->audio);
604
605 circlebuf_free(&audio->buffered_timestamps);
606 da_free(audio->render_order);
607 da_free(audio->root_nodes);
608
609 da_free(audio->monitors);
610 bfree(audio->monitoring_device_name);
611 bfree(audio->monitoring_device_id);
612 pthread_mutex_destroy(&audio->monitoring_mutex);
613
614 memset(audio, 0, sizeof(struct obs_core_audio));
615 }
616
obs_init_data(void)617 static bool obs_init_data(void)
618 {
619 struct obs_core_data *data = &obs->data;
620
621 assert(data != NULL);
622
623 pthread_mutex_init_value(&obs->data.displays_mutex);
624 pthread_mutex_init_value(&obs->data.draw_callbacks_mutex);
625
626 if (pthread_mutex_init_recursive(&data->sources_mutex) != 0)
627 goto fail;
628 if (pthread_mutex_init_recursive(&data->audio_sources_mutex) != 0)
629 goto fail;
630 if (pthread_mutex_init_recursive(&data->displays_mutex) != 0)
631 goto fail;
632 if (pthread_mutex_init_recursive(&data->outputs_mutex) != 0)
633 goto fail;
634 if (pthread_mutex_init_recursive(&data->encoders_mutex) != 0)
635 goto fail;
636 if (pthread_mutex_init_recursive(&data->services_mutex) != 0)
637 goto fail;
638 if (pthread_mutex_init_recursive(&obs->data.draw_callbacks_mutex) != 0)
639 goto fail;
640 if (!obs_view_init(&data->main_view))
641 goto fail;
642
643 data->private_data = obs_data_create();
644 data->valid = true;
645
646 fail:
647 return data->valid;
648 }
649
obs_main_view_free(struct obs_view * view)650 void obs_main_view_free(struct obs_view *view)
651 {
652 if (!view)
653 return;
654
655 for (size_t i = 0; i < MAX_CHANNELS; i++)
656 obs_source_release(view->channels[i]);
657
658 memset(view->channels, 0, sizeof(view->channels));
659 pthread_mutex_destroy(&view->channels_mutex);
660 }
661
662 #define FREE_OBS_LINKED_LIST(type) \
663 do { \
664 int unfreed = 0; \
665 while (data->first_##type) { \
666 obs_##type##_destroy(data->first_##type); \
667 unfreed++; \
668 } \
669 if (unfreed) \
670 blog(LOG_INFO, "\t%d " #type "(s) were remaining", \
671 unfreed); \
672 } while (false)
673
obs_free_data(void)674 static void obs_free_data(void)
675 {
676 struct obs_core_data *data = &obs->data;
677
678 data->valid = false;
679
680 obs_main_view_free(&data->main_view);
681
682 blog(LOG_INFO, "Freeing OBS context data");
683
684 FREE_OBS_LINKED_LIST(source);
685 FREE_OBS_LINKED_LIST(output);
686 FREE_OBS_LINKED_LIST(encoder);
687 FREE_OBS_LINKED_LIST(display);
688 FREE_OBS_LINKED_LIST(service);
689
690 pthread_mutex_destroy(&data->sources_mutex);
691 pthread_mutex_destroy(&data->audio_sources_mutex);
692 pthread_mutex_destroy(&data->displays_mutex);
693 pthread_mutex_destroy(&data->outputs_mutex);
694 pthread_mutex_destroy(&data->encoders_mutex);
695 pthread_mutex_destroy(&data->services_mutex);
696 pthread_mutex_destroy(&data->draw_callbacks_mutex);
697 da_free(data->draw_callbacks);
698 da_free(data->tick_callbacks);
699 obs_data_release(data->private_data);
700 }
701
702 static const char *obs_signals[] = {
703 "void source_create(ptr source)",
704 "void source_destroy(ptr source)",
705 "void source_remove(ptr source)",
706 "void source_save(ptr source)",
707 "void source_load(ptr source)",
708 "void source_activate(ptr source)",
709 "void source_deactivate(ptr source)",
710 "void source_show(ptr source)",
711 "void source_hide(ptr source)",
712 "void source_audio_activate(ptr source)",
713 "void source_audio_deactivate(ptr source)",
714 "void source_rename(ptr source, string new_name, string prev_name)",
715 "void source_volume(ptr source, in out float volume)",
716 "void source_volume_level(ptr source, float level, float magnitude, "
717 "float peak)",
718 "void source_transition_start(ptr source)",
719 "void source_transition_video_stop(ptr source)",
720 "void source_transition_stop(ptr source)",
721
722 "void channel_change(int channel, in out ptr source, ptr prev_source)",
723 "void master_volume(in out float volume)",
724
725 "void hotkey_layout_change()",
726 "void hotkey_register(ptr hotkey)",
727 "void hotkey_unregister(ptr hotkey)",
728 "void hotkey_bindings_changed(ptr hotkey)",
729
730 NULL,
731 };
732
obs_init_handlers(void)733 static inline bool obs_init_handlers(void)
734 {
735 obs->signals = signal_handler_create();
736 if (!obs->signals)
737 return false;
738
739 obs->procs = proc_handler_create();
740 if (!obs->procs)
741 return false;
742
743 return signal_handler_add_array(obs->signals, obs_signals);
744 }
745
746 static pthread_once_t obs_pthread_once_init_token = PTHREAD_ONCE_INIT;
obs_init_hotkeys(void)747 static inline bool obs_init_hotkeys(void)
748 {
749 struct obs_core_hotkeys *hotkeys = &obs->hotkeys;
750 bool success = false;
751
752 assert(hotkeys != NULL);
753
754 da_init(hotkeys->hotkeys);
755 hotkeys->signals = obs->signals;
756 hotkeys->name_map_init_token = obs_pthread_once_init_token;
757 hotkeys->mute = bstrdup("Mute");
758 hotkeys->unmute = bstrdup("Unmute");
759 hotkeys->push_to_mute = bstrdup("Push-to-mute");
760 hotkeys->push_to_talk = bstrdup("Push-to-talk");
761 hotkeys->sceneitem_show = bstrdup("Show '%1'");
762 hotkeys->sceneitem_hide = bstrdup("Hide '%1'");
763
764 if (!obs_hotkeys_platform_init(hotkeys))
765 return false;
766
767 if (pthread_mutex_init_recursive(&hotkeys->mutex) != 0)
768 goto fail;
769
770 if (os_event_init(&hotkeys->stop_event, OS_EVENT_TYPE_MANUAL) != 0)
771 goto fail;
772 if (pthread_create(&hotkeys->hotkey_thread, NULL, obs_hotkey_thread,
773 NULL))
774 goto fail;
775
776 hotkeys->hotkey_thread_initialized = true;
777
778 success = true;
779
780 fail:
781 return success;
782 }
783
stop_hotkeys(void)784 static inline void stop_hotkeys(void)
785 {
786 struct obs_core_hotkeys *hotkeys = &obs->hotkeys;
787 void *thread_ret;
788
789 if (hotkeys->hotkey_thread_initialized) {
790 os_event_signal(hotkeys->stop_event);
791 pthread_join(hotkeys->hotkey_thread, &thread_ret);
792 hotkeys->hotkey_thread_initialized = false;
793 }
794
795 os_event_destroy(hotkeys->stop_event);
796 obs_hotkeys_free();
797 }
798
obs_free_hotkeys(void)799 static inline void obs_free_hotkeys(void)
800 {
801 struct obs_core_hotkeys *hotkeys = &obs->hotkeys;
802
803 bfree(hotkeys->mute);
804 bfree(hotkeys->unmute);
805 bfree(hotkeys->push_to_mute);
806 bfree(hotkeys->push_to_talk);
807 bfree(hotkeys->sceneitem_show);
808 bfree(hotkeys->sceneitem_hide);
809
810 obs_hotkey_name_map_free();
811
812 obs_hotkeys_platform_free(hotkeys);
813 pthread_mutex_destroy(&hotkeys->mutex);
814 }
815
816 extern const struct obs_source_info scene_info;
817 extern const struct obs_source_info group_info;
818
submix_name(void * unused)819 static const char *submix_name(void *unused)
820 {
821 UNUSED_PARAMETER(unused);
822 return "Audio line (internal use only)";
823 }
824
825 const struct obs_source_info audio_line_info = {
826 .id = "audio_line",
827 .type = OBS_SOURCE_TYPE_INPUT,
828 .output_flags = OBS_SOURCE_AUDIO | OBS_SOURCE_CAP_DISABLED |
829 OBS_SOURCE_SUBMIX,
830 .get_name = submix_name,
831 };
832
833 extern void log_system_info(void);
834
obs_init(const char * locale,const char * module_config_path,profiler_name_store_t * store)835 static bool obs_init(const char *locale, const char *module_config_path,
836 profiler_name_store_t *store)
837 {
838 obs = bzalloc(sizeof(struct obs_core));
839
840 pthread_mutex_init_value(&obs->audio.monitoring_mutex);
841 pthread_mutex_init_value(&obs->video.gpu_encoder_mutex);
842 pthread_mutex_init_value(&obs->video.task_mutex);
843
844 obs->name_store_owned = !store;
845 obs->name_store = store ? store : profiler_name_store_create();
846 if (!obs->name_store) {
847 blog(LOG_ERROR, "Couldn't create profiler name store");
848 return false;
849 }
850
851 log_system_info();
852
853 if (!obs_init_data())
854 return false;
855 if (!obs_init_handlers())
856 return false;
857 if (!obs_init_hotkeys())
858 return false;
859
860 if (module_config_path)
861 obs->module_config_path = bstrdup(module_config_path);
862 obs->locale = bstrdup(locale);
863 obs_register_source(&scene_info);
864 obs_register_source(&group_info);
865 obs_register_source(&audio_line_info);
866 add_default_module_paths();
867 return true;
868 }
869
870 #ifdef _WIN32
871 extern bool initialize_com(void);
872 extern void uninitialize_com(void);
873 static bool com_initialized = false;
874 #endif
875
876 /* Separate from actual context initialization
877 * since this can be set before startup and persist
878 * after shutdown. */
879 static DARRAY(struct dstr) core_module_paths = {0};
880
obs_find_data_file(const char * file)881 char *obs_find_data_file(const char *file)
882 {
883 struct dstr path = {0};
884
885 char *result = find_libobs_data_file(file);
886 if (result)
887 return result;
888
889 for (size_t i = 0; i < core_module_paths.num; ++i) {
890 if (check_path(file, core_module_paths.array[i].array, &path))
891 return path.array;
892 }
893
894 dstr_free(&path);
895 return NULL;
896 }
897
obs_add_data_path(const char * path)898 void obs_add_data_path(const char *path)
899 {
900 struct dstr *new_path = da_push_back_new(core_module_paths);
901 dstr_init_copy(new_path, path);
902 }
903
obs_remove_data_path(const char * path)904 bool obs_remove_data_path(const char *path)
905 {
906 for (size_t i = 0; i < core_module_paths.num; ++i) {
907 int result = dstr_cmp(&core_module_paths.array[i], path);
908
909 if (result == 0) {
910 dstr_free(&core_module_paths.array[i]);
911 da_erase(core_module_paths, i);
912 return true;
913 }
914 }
915
916 return false;
917 }
918
919 static const char *obs_startup_name = "obs_startup";
obs_startup(const char * locale,const char * module_config_path,profiler_name_store_t * store)920 bool obs_startup(const char *locale, const char *module_config_path,
921 profiler_name_store_t *store)
922 {
923 bool success;
924
925 profile_start(obs_startup_name);
926
927 if (obs) {
928 blog(LOG_WARNING, "Tried to call obs_startup more than once");
929 return false;
930 }
931
932 #ifdef _WIN32
933 com_initialized = initialize_com();
934 #endif
935
936 success = obs_init(locale, module_config_path, store);
937 profile_end(obs_startup_name);
938 if (!success)
939 obs_shutdown();
940
941 return success;
942 }
943
944 static struct obs_cmdline_args cmdline_args = {0, NULL};
obs_set_cmdline_args(int argc,const char * const * argv)945 void obs_set_cmdline_args(int argc, const char *const *argv)
946 {
947 char *data;
948 size_t len;
949 int i;
950
951 /* Once argc is set (non-zero) we shouldn't call again */
952 if (cmdline_args.argc)
953 return;
954
955 cmdline_args.argc = argc;
956
957 /* Safely copy over argv */
958 len = 0;
959 for (i = 0; i < argc; i++)
960 len += strlen(argv[i]) + 1;
961
962 cmdline_args.argv = bmalloc(sizeof(char *) * (argc + 1) + len);
963 data = (char *)cmdline_args.argv + sizeof(char *) * (argc + 1);
964
965 for (i = 0; i < argc; i++) {
966 cmdline_args.argv[i] = data;
967 len = strlen(argv[i]) + 1;
968 memcpy(data, argv[i], len);
969 data += len;
970 }
971
972 cmdline_args.argv[argc] = NULL;
973 }
974
obs_get_cmdline_args(void)975 struct obs_cmdline_args obs_get_cmdline_args(void)
976 {
977 return cmdline_args;
978 }
979
obs_shutdown(void)980 void obs_shutdown(void)
981 {
982 struct obs_module *module;
983
984 for (size_t i = 0; i < obs->source_types.num; i++) {
985 struct obs_source_info *item = &obs->source_types.array[i];
986 if (item->type_data && item->free_type_data)
987 item->free_type_data(item->type_data);
988 if (item->id)
989 bfree((void *)item->id);
990 }
991 da_free(obs->source_types);
992
993 #define FREE_REGISTERED_TYPES(structure, list) \
994 do { \
995 for (size_t i = 0; i < list.num; i++) { \
996 struct structure *item = &list.array[i]; \
997 if (item->type_data && item->free_type_data) \
998 item->free_type_data(item->type_data); \
999 } \
1000 da_free(list); \
1001 } while (false)
1002
1003 FREE_REGISTERED_TYPES(obs_output_info, obs->output_types);
1004 FREE_REGISTERED_TYPES(obs_encoder_info, obs->encoder_types);
1005 FREE_REGISTERED_TYPES(obs_service_info, obs->service_types);
1006 FREE_REGISTERED_TYPES(obs_modal_ui, obs->modal_ui_callbacks);
1007 FREE_REGISTERED_TYPES(obs_modeless_ui, obs->modeless_ui_callbacks);
1008
1009 #undef FREE_REGISTERED_TYPES
1010
1011 da_free(obs->input_types);
1012 da_free(obs->filter_types);
1013 da_free(obs->transition_types);
1014
1015 stop_video();
1016 stop_audio();
1017 stop_hotkeys();
1018
1019 module = obs->first_module;
1020 while (module) {
1021 struct obs_module *next = module->next;
1022 free_module(module);
1023 module = next;
1024 }
1025 obs->first_module = NULL;
1026
1027 obs_free_data();
1028 obs_free_audio();
1029 obs_free_video();
1030 obs_free_hotkeys();
1031 obs_free_graphics();
1032 proc_handler_destroy(obs->procs);
1033 signal_handler_destroy(obs->signals);
1034 obs->procs = NULL;
1035 obs->signals = NULL;
1036
1037 for (size_t i = 0; i < obs->module_paths.num; i++)
1038 free_module_path(obs->module_paths.array + i);
1039 da_free(obs->module_paths);
1040
1041 if (obs->name_store_owned)
1042 profiler_name_store_free(obs->name_store);
1043
1044 bfree(obs->module_config_path);
1045 bfree(obs->locale);
1046 bfree(obs);
1047 obs = NULL;
1048 bfree(cmdline_args.argv);
1049
1050 #ifdef _WIN32
1051 if (com_initialized)
1052 uninitialize_com();
1053 #endif
1054 }
1055
obs_initialized(void)1056 bool obs_initialized(void)
1057 {
1058 return obs != NULL;
1059 }
1060
obs_get_version(void)1061 uint32_t obs_get_version(void)
1062 {
1063 return LIBOBS_API_VER;
1064 }
1065
obs_get_version_string(void)1066 const char *obs_get_version_string(void)
1067 {
1068 return OBS_VERSION;
1069 }
1070
obs_set_locale(const char * locale)1071 void obs_set_locale(const char *locale)
1072 {
1073 struct obs_module *module;
1074
1075 if (obs->locale)
1076 bfree(obs->locale);
1077 obs->locale = bstrdup(locale);
1078
1079 module = obs->first_module;
1080 while (module) {
1081 if (module->set_locale)
1082 module->set_locale(locale);
1083
1084 module = module->next;
1085 }
1086 }
1087
obs_get_locale(void)1088 const char *obs_get_locale(void)
1089 {
1090 return obs->locale;
1091 }
1092
1093 #define OBS_SIZE_MIN 2
1094 #define OBS_SIZE_MAX (32 * 1024)
1095
size_valid(uint32_t width,uint32_t height)1096 static inline bool size_valid(uint32_t width, uint32_t height)
1097 {
1098 return (width >= OBS_SIZE_MIN && height >= OBS_SIZE_MIN &&
1099 width <= OBS_SIZE_MAX && height <= OBS_SIZE_MAX);
1100 }
1101
obs_reset_video(struct obs_video_info * ovi)1102 int obs_reset_video(struct obs_video_info *ovi)
1103 {
1104 if (!obs)
1105 return OBS_VIDEO_FAIL;
1106
1107 /* don't allow changing of video settings if active. */
1108 if (obs->video.video && obs_video_active())
1109 return OBS_VIDEO_CURRENTLY_ACTIVE;
1110
1111 if (!size_valid(ovi->output_width, ovi->output_height) ||
1112 !size_valid(ovi->base_width, ovi->base_height))
1113 return OBS_VIDEO_INVALID_PARAM;
1114
1115 struct obs_core_video *video = &obs->video;
1116
1117 stop_video();
1118 obs_free_video();
1119
1120 /* align to multiple-of-two and SSE alignment sizes */
1121 ovi->output_width &= 0xFFFFFFFC;
1122 ovi->output_height &= 0xFFFFFFFE;
1123
1124 if (!video->graphics) {
1125 int errorcode = obs_init_graphics(ovi);
1126 if (errorcode != OBS_VIDEO_SUCCESS) {
1127 obs_free_graphics();
1128 return errorcode;
1129 }
1130 }
1131
1132 const char *scale_type_name = "";
1133 switch (ovi->scale_type) {
1134 case OBS_SCALE_DISABLE:
1135 scale_type_name = "Disabled";
1136 break;
1137 case OBS_SCALE_POINT:
1138 scale_type_name = "Point";
1139 break;
1140 case OBS_SCALE_BICUBIC:
1141 scale_type_name = "Bicubic";
1142 break;
1143 case OBS_SCALE_BILINEAR:
1144 scale_type_name = "Bilinear";
1145 break;
1146 case OBS_SCALE_LANCZOS:
1147 scale_type_name = "Lanczos";
1148 break;
1149 case OBS_SCALE_AREA:
1150 scale_type_name = "Area";
1151 break;
1152 }
1153
1154 bool yuv = format_is_yuv(ovi->output_format);
1155 const char *yuv_format = get_video_colorspace_name(ovi->colorspace);
1156 const char *yuv_range =
1157 get_video_range_name(ovi->output_format, ovi->range);
1158
1159 blog(LOG_INFO, "---------------------------------");
1160 blog(LOG_INFO,
1161 "video settings reset:\n"
1162 "\tbase resolution: %dx%d\n"
1163 "\toutput resolution: %dx%d\n"
1164 "\tdownscale filter: %s\n"
1165 "\tfps: %d/%d\n"
1166 "\tformat: %s\n"
1167 "\tYUV mode: %s%s%s",
1168 ovi->base_width, ovi->base_height, ovi->output_width,
1169 ovi->output_height, scale_type_name, ovi->fps_num, ovi->fps_den,
1170 get_video_format_name(ovi->output_format),
1171 yuv ? yuv_format : "None", yuv ? "/" : "", yuv ? yuv_range : "");
1172
1173 return obs_init_video(ovi);
1174 }
1175
obs_reset_audio(const struct obs_audio_info * oai)1176 bool obs_reset_audio(const struct obs_audio_info *oai)
1177 {
1178 struct audio_output_info ai;
1179
1180 /* don't allow changing of audio settings if active. */
1181 if (obs->audio.audio && audio_output_active(obs->audio.audio))
1182 return false;
1183
1184 obs_free_audio();
1185 if (!oai)
1186 return true;
1187
1188 ai.name = "Audio";
1189 ai.samples_per_sec = oai->samples_per_sec;
1190 ai.format = AUDIO_FORMAT_FLOAT_PLANAR;
1191 ai.speakers = oai->speakers;
1192 ai.input_callback = audio_callback;
1193
1194 blog(LOG_INFO, "---------------------------------");
1195 blog(LOG_INFO,
1196 "audio settings reset:\n"
1197 "\tsamples per sec: %d\n"
1198 "\tspeakers: %d",
1199 (int)ai.samples_per_sec, (int)ai.speakers);
1200
1201 return obs_init_audio(&ai);
1202 }
1203
obs_get_video_info(struct obs_video_info * ovi)1204 bool obs_get_video_info(struct obs_video_info *ovi)
1205 {
1206 struct obs_core_video *video = &obs->video;
1207
1208 if (!video->graphics)
1209 return false;
1210
1211 *ovi = video->ovi;
1212 return true;
1213 }
1214
obs_get_audio_info(struct obs_audio_info * oai)1215 bool obs_get_audio_info(struct obs_audio_info *oai)
1216 {
1217 struct obs_core_audio *audio = &obs->audio;
1218 const struct audio_output_info *info;
1219
1220 if (!oai || !audio->audio)
1221 return false;
1222
1223 info = audio_output_get_info(audio->audio);
1224
1225 oai->samples_per_sec = info->samples_per_sec;
1226 oai->speakers = info->speakers;
1227 return true;
1228 }
1229
obs_enum_source_types(size_t idx,const char ** id)1230 bool obs_enum_source_types(size_t idx, const char **id)
1231 {
1232 if (idx >= obs->source_types.num)
1233 return false;
1234 *id = obs->source_types.array[idx].id;
1235 return true;
1236 }
1237
obs_enum_input_types(size_t idx,const char ** id)1238 bool obs_enum_input_types(size_t idx, const char **id)
1239 {
1240 if (idx >= obs->input_types.num)
1241 return false;
1242 *id = obs->input_types.array[idx].id;
1243 return true;
1244 }
1245
obs_enum_input_types2(size_t idx,const char ** id,const char ** unversioned_id)1246 bool obs_enum_input_types2(size_t idx, const char **id,
1247 const char **unversioned_id)
1248 {
1249 if (idx >= obs->input_types.num)
1250 return false;
1251 if (id)
1252 *id = obs->input_types.array[idx].id;
1253 if (unversioned_id)
1254 *unversioned_id = obs->input_types.array[idx].unversioned_id;
1255 return true;
1256 }
1257
obs_get_latest_input_type_id(const char * unversioned_id)1258 const char *obs_get_latest_input_type_id(const char *unversioned_id)
1259 {
1260 struct obs_source_info *latest = NULL;
1261 int version = -1;
1262
1263 if (!unversioned_id)
1264 return NULL;
1265
1266 for (size_t i = 0; i < obs->source_types.num; i++) {
1267 struct obs_source_info *info = &obs->source_types.array[i];
1268 if (strcmp(info->unversioned_id, unversioned_id) == 0 &&
1269 (int)info->version > version) {
1270 latest = info;
1271 version = info->version;
1272 }
1273 }
1274
1275 assert(!!latest);
1276 if (!latest)
1277 return NULL;
1278
1279 return latest->id;
1280 }
1281
obs_enum_filter_types(size_t idx,const char ** id)1282 bool obs_enum_filter_types(size_t idx, const char **id)
1283 {
1284 if (idx >= obs->filter_types.num)
1285 return false;
1286 *id = obs->filter_types.array[idx].id;
1287 return true;
1288 }
1289
obs_enum_transition_types(size_t idx,const char ** id)1290 bool obs_enum_transition_types(size_t idx, const char **id)
1291 {
1292 if (idx >= obs->transition_types.num)
1293 return false;
1294 *id = obs->transition_types.array[idx].id;
1295 return true;
1296 }
1297
obs_enum_output_types(size_t idx,const char ** id)1298 bool obs_enum_output_types(size_t idx, const char **id)
1299 {
1300 if (idx >= obs->output_types.num)
1301 return false;
1302 *id = obs->output_types.array[idx].id;
1303 return true;
1304 }
1305
obs_enum_encoder_types(size_t idx,const char ** id)1306 bool obs_enum_encoder_types(size_t idx, const char **id)
1307 {
1308 if (idx >= obs->encoder_types.num)
1309 return false;
1310 *id = obs->encoder_types.array[idx].id;
1311 return true;
1312 }
1313
obs_enum_service_types(size_t idx,const char ** id)1314 bool obs_enum_service_types(size_t idx, const char **id)
1315 {
1316 if (idx >= obs->service_types.num)
1317 return false;
1318 *id = obs->service_types.array[idx].id;
1319 return true;
1320 }
1321
obs_enter_graphics(void)1322 void obs_enter_graphics(void)
1323 {
1324 if (obs->video.graphics)
1325 gs_enter_context(obs->video.graphics);
1326 }
1327
obs_leave_graphics(void)1328 void obs_leave_graphics(void)
1329 {
1330 if (obs->video.graphics)
1331 gs_leave_context();
1332 }
1333
obs_get_audio(void)1334 audio_t *obs_get_audio(void)
1335 {
1336 return obs->audio.audio;
1337 }
1338
obs_get_video(void)1339 video_t *obs_get_video(void)
1340 {
1341 return obs->video.video;
1342 }
1343
1344 /* TODO: optimize this later so it's not just O(N) string lookups */
1345 static inline struct obs_modal_ui *
get_modal_ui_callback(const char * id,const char * task,const char * target)1346 get_modal_ui_callback(const char *id, const char *task, const char *target)
1347 {
1348 for (size_t i = 0; i < obs->modal_ui_callbacks.num; i++) {
1349 struct obs_modal_ui *callback =
1350 obs->modal_ui_callbacks.array + i;
1351
1352 if (strcmp(callback->id, id) == 0 &&
1353 strcmp(callback->task, task) == 0 &&
1354 strcmp(callback->target, target) == 0)
1355 return callback;
1356 }
1357
1358 return NULL;
1359 }
1360
1361 static inline struct obs_modeless_ui *
get_modeless_ui_callback(const char * id,const char * task,const char * target)1362 get_modeless_ui_callback(const char *id, const char *task, const char *target)
1363 {
1364 for (size_t i = 0; i < obs->modeless_ui_callbacks.num; i++) {
1365 struct obs_modeless_ui *callback;
1366 callback = obs->modeless_ui_callbacks.array + i;
1367
1368 if (strcmp(callback->id, id) == 0 &&
1369 strcmp(callback->task, task) == 0 &&
1370 strcmp(callback->target, target) == 0)
1371 return callback;
1372 }
1373
1374 return NULL;
1375 }
1376
obs_exec_ui(const char * name,const char * task,const char * target,void * data,void * ui_data)1377 int obs_exec_ui(const char *name, const char *task, const char *target,
1378 void *data, void *ui_data)
1379 {
1380 struct obs_modal_ui *callback;
1381 int errorcode = OBS_UI_NOTFOUND;
1382
1383 if (!obs)
1384 return errorcode;
1385
1386 callback = get_modal_ui_callback(name, task, target);
1387 if (callback) {
1388 bool success = callback->exec(data, ui_data);
1389 errorcode = success ? OBS_UI_SUCCESS : OBS_UI_CANCEL;
1390 }
1391
1392 return errorcode;
1393 }
1394
obs_create_ui(const char * name,const char * task,const char * target,void * data,void * ui_data)1395 void *obs_create_ui(const char *name, const char *task, const char *target,
1396 void *data, void *ui_data)
1397 {
1398 struct obs_modeless_ui *callback;
1399
1400 callback = get_modeless_ui_callback(name, task, target);
1401 return callback ? callback->create(data, ui_data) : NULL;
1402 }
1403
obs_get_output_source(uint32_t channel)1404 obs_source_t *obs_get_output_source(uint32_t channel)
1405 {
1406 return obs_view_get_source(&obs->data.main_view, channel);
1407 }
1408
obs_set_output_source(uint32_t channel,obs_source_t * source)1409 void obs_set_output_source(uint32_t channel, obs_source_t *source)
1410 {
1411 assert(channel < MAX_CHANNELS);
1412
1413 if (channel >= MAX_CHANNELS)
1414 return;
1415
1416 struct obs_source *prev_source;
1417 struct obs_view *view = &obs->data.main_view;
1418 struct calldata params = {0};
1419
1420 pthread_mutex_lock(&view->channels_mutex);
1421
1422 obs_source_addref(source);
1423
1424 prev_source = view->channels[channel];
1425
1426 calldata_set_int(¶ms, "channel", channel);
1427 calldata_set_ptr(¶ms, "prev_source", prev_source);
1428 calldata_set_ptr(¶ms, "source", source);
1429 signal_handler_signal(obs->signals, "channel_change", ¶ms);
1430 calldata_get_ptr(¶ms, "source", &source);
1431 calldata_free(¶ms);
1432
1433 view->channels[channel] = source;
1434
1435 pthread_mutex_unlock(&view->channels_mutex);
1436
1437 if (source)
1438 obs_source_activate(source, MAIN_VIEW);
1439
1440 if (prev_source) {
1441 obs_source_deactivate(prev_source, MAIN_VIEW);
1442 obs_source_release(prev_source);
1443 }
1444 }
1445
obs_enum_sources(bool (* enum_proc)(void *,obs_source_t *),void * param)1446 void obs_enum_sources(bool (*enum_proc)(void *, obs_source_t *), void *param)
1447 {
1448 obs_source_t *source;
1449
1450 pthread_mutex_lock(&obs->data.sources_mutex);
1451 source = obs->data.first_source;
1452
1453 while (source) {
1454 obs_source_t *next_source =
1455 (obs_source_t *)source->context.next;
1456
1457 if (strcmp(source->info.id, group_info.id) == 0 &&
1458 !enum_proc(param, source)) {
1459 break;
1460 } else if (source->info.type == OBS_SOURCE_TYPE_INPUT &&
1461 !source->context.private &&
1462 !enum_proc(param, source)) {
1463 break;
1464 }
1465
1466 source = next_source;
1467 }
1468
1469 pthread_mutex_unlock(&obs->data.sources_mutex);
1470 }
1471
obs_enum_scenes(bool (* enum_proc)(void *,obs_source_t *),void * param)1472 void obs_enum_scenes(bool (*enum_proc)(void *, obs_source_t *), void *param)
1473 {
1474 obs_source_t *source;
1475
1476 pthread_mutex_lock(&obs->data.sources_mutex);
1477 source = obs->data.first_source;
1478
1479 while (source) {
1480 obs_source_t *next_source =
1481 (obs_source_t *)source->context.next;
1482
1483 if (source->info.type == OBS_SOURCE_TYPE_SCENE &&
1484 !source->context.private && !enum_proc(param, source)) {
1485 break;
1486 }
1487
1488 source = next_source;
1489 }
1490
1491 pthread_mutex_unlock(&obs->data.sources_mutex);
1492 }
1493
obs_enum(void * pstart,pthread_mutex_t * mutex,void * proc,void * param)1494 static inline void obs_enum(void *pstart, pthread_mutex_t *mutex, void *proc,
1495 void *param)
1496 {
1497 struct obs_context_data **start = pstart, *context;
1498 bool (*enum_proc)(void *, void *) = proc;
1499
1500 assert(start);
1501 assert(mutex);
1502 assert(enum_proc);
1503
1504 pthread_mutex_lock(mutex);
1505
1506 context = *start;
1507 while (context) {
1508 if (!enum_proc(param, context))
1509 break;
1510
1511 context = context->next;
1512 }
1513
1514 pthread_mutex_unlock(mutex);
1515 }
1516
obs_enum_all_sources(bool (* enum_proc)(void *,obs_source_t *),void * param)1517 void obs_enum_all_sources(bool (*enum_proc)(void *, obs_source_t *),
1518 void *param)
1519 {
1520 obs_enum(&obs->data.first_source, &obs->data.sources_mutex, enum_proc,
1521 param);
1522 }
1523
obs_enum_outputs(bool (* enum_proc)(void *,obs_output_t *),void * param)1524 void obs_enum_outputs(bool (*enum_proc)(void *, obs_output_t *), void *param)
1525 {
1526 obs_enum(&obs->data.first_output, &obs->data.outputs_mutex, enum_proc,
1527 param);
1528 }
1529
obs_enum_encoders(bool (* enum_proc)(void *,obs_encoder_t *),void * param)1530 void obs_enum_encoders(bool (*enum_proc)(void *, obs_encoder_t *), void *param)
1531 {
1532 obs_enum(&obs->data.first_encoder, &obs->data.encoders_mutex, enum_proc,
1533 param);
1534 }
1535
obs_enum_services(bool (* enum_proc)(void *,obs_service_t *),void * param)1536 void obs_enum_services(bool (*enum_proc)(void *, obs_service_t *), void *param)
1537 {
1538 obs_enum(&obs->data.first_service, &obs->data.services_mutex, enum_proc,
1539 param);
1540 }
1541
get_context_by_name(void * vfirst,const char * name,pthread_mutex_t * mutex,void * (* addref)(void *))1542 static inline void *get_context_by_name(void *vfirst, const char *name,
1543 pthread_mutex_t *mutex,
1544 void *(*addref)(void *))
1545 {
1546 struct obs_context_data **first = vfirst;
1547 struct obs_context_data *context;
1548
1549 pthread_mutex_lock(mutex);
1550
1551 context = *first;
1552 while (context) {
1553 if (!context->private && strcmp(context->name, name) == 0) {
1554 context = addref(context);
1555 break;
1556 }
1557 context = context->next;
1558 }
1559
1560 pthread_mutex_unlock(mutex);
1561 return context;
1562 }
1563
obs_source_addref_safe_(void * ref)1564 static inline void *obs_source_addref_safe_(void *ref)
1565 {
1566 return obs_source_get_ref(ref);
1567 }
1568
obs_output_addref_safe_(void * ref)1569 static inline void *obs_output_addref_safe_(void *ref)
1570 {
1571 return obs_output_get_ref(ref);
1572 }
1573
obs_encoder_addref_safe_(void * ref)1574 static inline void *obs_encoder_addref_safe_(void *ref)
1575 {
1576 return obs_encoder_get_ref(ref);
1577 }
1578
obs_service_addref_safe_(void * ref)1579 static inline void *obs_service_addref_safe_(void *ref)
1580 {
1581 return obs_service_get_ref(ref);
1582 }
1583
obs_id_(void * data)1584 static inline void *obs_id_(void *data)
1585 {
1586 return data;
1587 }
1588
obs_get_source_by_name(const char * name)1589 obs_source_t *obs_get_source_by_name(const char *name)
1590 {
1591 return get_context_by_name(&obs->data.first_source, name,
1592 &obs->data.sources_mutex,
1593 obs_source_addref_safe_);
1594 }
1595
obs_get_output_by_name(const char * name)1596 obs_output_t *obs_get_output_by_name(const char *name)
1597 {
1598 return get_context_by_name(&obs->data.first_output, name,
1599 &obs->data.outputs_mutex,
1600 obs_output_addref_safe_);
1601 }
1602
obs_get_encoder_by_name(const char * name)1603 obs_encoder_t *obs_get_encoder_by_name(const char *name)
1604 {
1605 return get_context_by_name(&obs->data.first_encoder, name,
1606 &obs->data.encoders_mutex,
1607 obs_encoder_addref_safe_);
1608 }
1609
obs_get_service_by_name(const char * name)1610 obs_service_t *obs_get_service_by_name(const char *name)
1611 {
1612 return get_context_by_name(&obs->data.first_service, name,
1613 &obs->data.services_mutex,
1614 obs_service_addref_safe_);
1615 }
1616
obs_get_base_effect(enum obs_base_effect effect)1617 gs_effect_t *obs_get_base_effect(enum obs_base_effect effect)
1618 {
1619 switch (effect) {
1620 case OBS_EFFECT_DEFAULT:
1621 return obs->video.default_effect;
1622 case OBS_EFFECT_DEFAULT_RECT:
1623 return obs->video.default_rect_effect;
1624 case OBS_EFFECT_OPAQUE:
1625 return obs->video.opaque_effect;
1626 case OBS_EFFECT_SOLID:
1627 return obs->video.solid_effect;
1628 case OBS_EFFECT_REPEAT:
1629 return obs->video.repeat_effect;
1630 case OBS_EFFECT_BICUBIC:
1631 return obs->video.bicubic_effect;
1632 case OBS_EFFECT_LANCZOS:
1633 return obs->video.lanczos_effect;
1634 case OBS_EFFECT_AREA:
1635 return obs->video.area_effect;
1636 case OBS_EFFECT_BILINEAR_LOWRES:
1637 return obs->video.bilinear_lowres_effect;
1638 case OBS_EFFECT_PREMULTIPLIED_ALPHA:
1639 return obs->video.premultiplied_alpha_effect;
1640 }
1641
1642 return NULL;
1643 }
1644
1645 /* OBS_DEPRECATED */
obs_get_default_rect_effect(void)1646 gs_effect_t *obs_get_default_rect_effect(void)
1647 {
1648 return obs->video.default_rect_effect;
1649 }
1650
obs_get_signal_handler(void)1651 signal_handler_t *obs_get_signal_handler(void)
1652 {
1653 return obs->signals;
1654 }
1655
obs_get_proc_handler(void)1656 proc_handler_t *obs_get_proc_handler(void)
1657 {
1658 return obs->procs;
1659 }
1660
1661 /* OBS_DEPRECATED */
obs_render_main_view(void)1662 void obs_render_main_view(void)
1663 {
1664 obs_view_render(&obs->data.main_view);
1665 }
1666
obs_render_main_texture_internal(enum gs_blend_type src_c,enum gs_blend_type dest_c,enum gs_blend_type src_a,enum gs_blend_type dest_a)1667 static void obs_render_main_texture_internal(enum gs_blend_type src_c,
1668 enum gs_blend_type dest_c,
1669 enum gs_blend_type src_a,
1670 enum gs_blend_type dest_a)
1671 {
1672 struct obs_core_video *video;
1673 gs_texture_t *tex;
1674 gs_effect_t *effect;
1675 gs_eparam_t *param;
1676
1677 video = &obs->video;
1678 if (!video->texture_rendered)
1679 return;
1680
1681 tex = video->render_texture;
1682 effect = obs_get_base_effect(OBS_EFFECT_DEFAULT);
1683 param = gs_effect_get_param_by_name(effect, "image");
1684 gs_effect_set_texture(param, tex);
1685
1686 gs_blend_state_push();
1687 gs_blend_function_separate(src_c, dest_c, src_a, dest_a);
1688
1689 while (gs_effect_loop(effect, "Draw"))
1690 gs_draw_sprite(tex, 0, 0, 0);
1691
1692 gs_blend_state_pop();
1693 }
1694
obs_render_main_texture(void)1695 void obs_render_main_texture(void)
1696 {
1697 obs_render_main_texture_internal(GS_BLEND_ONE, GS_BLEND_INVSRCALPHA,
1698 GS_BLEND_ONE, GS_BLEND_INVSRCALPHA);
1699 }
1700
obs_render_main_texture_src_color_only(void)1701 void obs_render_main_texture_src_color_only(void)
1702 {
1703 obs_render_main_texture_internal(GS_BLEND_ONE, GS_BLEND_ZERO,
1704 GS_BLEND_ONE, GS_BLEND_INVSRCALPHA);
1705 }
1706
obs_get_main_texture(void)1707 gs_texture_t *obs_get_main_texture(void)
1708 {
1709 struct obs_core_video *video;
1710
1711 video = &obs->video;
1712 if (!video->texture_rendered)
1713 return NULL;
1714
1715 return video->render_texture;
1716 }
1717
obs_set_master_volume(float volume)1718 void obs_set_master_volume(float volume)
1719 {
1720 struct calldata data = {0};
1721
1722 calldata_set_float(&data, "volume", volume);
1723 signal_handler_signal(obs->signals, "master_volume", &data);
1724 volume = (float)calldata_float(&data, "volume");
1725 calldata_free(&data);
1726
1727 obs->audio.user_volume = volume;
1728 }
1729
obs_get_master_volume(void)1730 float obs_get_master_volume(void)
1731 {
1732 return obs->audio.user_volume;
1733 }
1734
obs_load_source_type(obs_data_t * source_data)1735 static obs_source_t *obs_load_source_type(obs_data_t *source_data)
1736 {
1737 obs_data_array_t *filters = obs_data_get_array(source_data, "filters");
1738 obs_source_t *source;
1739 const char *name = obs_data_get_string(source_data, "name");
1740 const char *id = obs_data_get_string(source_data, "id");
1741 const char *v_id = obs_data_get_string(source_data, "versioned_id");
1742 obs_data_t *settings = obs_data_get_obj(source_data, "settings");
1743 obs_data_t *hotkeys = obs_data_get_obj(source_data, "hotkeys");
1744 double volume;
1745 double balance;
1746 int64_t sync;
1747 uint32_t prev_ver;
1748 uint32_t caps;
1749 uint32_t flags;
1750 uint32_t mixers;
1751 int di_order;
1752 int di_mode;
1753 int monitoring_type;
1754
1755 prev_ver = (uint32_t)obs_data_get_int(source_data, "prev_ver");
1756
1757 if (!*v_id)
1758 v_id = id;
1759
1760 source = obs_source_create_set_last_ver(v_id, name, settings, hotkeys,
1761 prev_ver);
1762 if (source->owns_info_id) {
1763 bfree((void *)source->info.unversioned_id);
1764 source->info.unversioned_id = bstrdup(id);
1765 }
1766
1767 obs_data_release(hotkeys);
1768
1769 caps = obs_source_get_output_flags(source);
1770
1771 obs_data_set_default_double(source_data, "volume", 1.0);
1772 volume = obs_data_get_double(source_data, "volume");
1773 obs_source_set_volume(source, (float)volume);
1774
1775 obs_data_set_default_double(source_data, "balance", 0.5);
1776 balance = obs_data_get_double(source_data, "balance");
1777 obs_source_set_balance_value(source, (float)balance);
1778
1779 sync = obs_data_get_int(source_data, "sync");
1780 obs_source_set_sync_offset(source, sync);
1781
1782 obs_data_set_default_int(source_data, "mixers", 0x3F);
1783 mixers = (uint32_t)obs_data_get_int(source_data, "mixers");
1784 obs_source_set_audio_mixers(source, mixers);
1785
1786 obs_data_set_default_int(source_data, "flags", source->default_flags);
1787 flags = (uint32_t)obs_data_get_int(source_data, "flags");
1788 obs_source_set_flags(source, flags);
1789
1790 obs_data_set_default_bool(source_data, "enabled", true);
1791 obs_source_set_enabled(source,
1792 obs_data_get_bool(source_data, "enabled"));
1793
1794 obs_data_set_default_bool(source_data, "muted", false);
1795 obs_source_set_muted(source, obs_data_get_bool(source_data, "muted"));
1796
1797 obs_data_set_default_bool(source_data, "push-to-mute", false);
1798 obs_source_enable_push_to_mute(
1799 source, obs_data_get_bool(source_data, "push-to-mute"));
1800
1801 obs_data_set_default_int(source_data, "push-to-mute-delay", 0);
1802 obs_source_set_push_to_mute_delay(
1803 source, obs_data_get_int(source_data, "push-to-mute-delay"));
1804
1805 obs_data_set_default_bool(source_data, "push-to-talk", false);
1806 obs_source_enable_push_to_talk(
1807 source, obs_data_get_bool(source_data, "push-to-talk"));
1808
1809 obs_data_set_default_int(source_data, "push-to-talk-delay", 0);
1810 obs_source_set_push_to_talk_delay(
1811 source, obs_data_get_int(source_data, "push-to-talk-delay"));
1812
1813 di_mode = (int)obs_data_get_int(source_data, "deinterlace_mode");
1814 obs_source_set_deinterlace_mode(source,
1815 (enum obs_deinterlace_mode)di_mode);
1816
1817 di_order =
1818 (int)obs_data_get_int(source_data, "deinterlace_field_order");
1819 obs_source_set_deinterlace_field_order(
1820 source, (enum obs_deinterlace_field_order)di_order);
1821
1822 monitoring_type = (int)obs_data_get_int(source_data, "monitoring_type");
1823 if (prev_ver < MAKE_SEMANTIC_VERSION(23, 2, 2)) {
1824 if ((caps & OBS_SOURCE_MONITOR_BY_DEFAULT) != 0) {
1825 /* updates older sources to enable monitoring
1826 * automatically if they added monitoring by default in
1827 * version 24 */
1828 monitoring_type = OBS_MONITORING_TYPE_MONITOR_ONLY;
1829 obs_source_set_audio_mixers(source, 0x3F);
1830 }
1831 }
1832 obs_source_set_monitoring_type(
1833 source, (enum obs_monitoring_type)monitoring_type);
1834
1835 obs_data_release(source->private_settings);
1836 source->private_settings =
1837 obs_data_get_obj(source_data, "private_settings");
1838 if (!source->private_settings)
1839 source->private_settings = obs_data_create();
1840
1841 if (filters) {
1842 size_t count = obs_data_array_count(filters);
1843
1844 for (size_t i = 0; i < count; i++) {
1845 obs_data_t *filter_data =
1846 obs_data_array_item(filters, i);
1847
1848 obs_source_t *filter =
1849 obs_load_source_type(filter_data);
1850 if (filter) {
1851 obs_source_filter_add(source, filter);
1852 obs_source_release(filter);
1853 }
1854
1855 obs_data_release(filter_data);
1856 }
1857
1858 obs_data_array_release(filters);
1859 }
1860
1861 obs_data_release(settings);
1862
1863 return source;
1864 }
1865
obs_load_source(obs_data_t * source_data)1866 obs_source_t *obs_load_source(obs_data_t *source_data)
1867 {
1868 return obs_load_source_type(source_data);
1869 }
1870
obs_load_sources(obs_data_array_t * array,obs_load_source_cb cb,void * private_data)1871 void obs_load_sources(obs_data_array_t *array, obs_load_source_cb cb,
1872 void *private_data)
1873 {
1874 struct obs_core_data *data = &obs->data;
1875 DARRAY(obs_source_t *) sources;
1876 size_t count;
1877 size_t i;
1878
1879 da_init(sources);
1880
1881 count = obs_data_array_count(array);
1882 da_reserve(sources, count);
1883
1884 pthread_mutex_lock(&data->sources_mutex);
1885
1886 for (i = 0; i < count; i++) {
1887 obs_data_t *source_data = obs_data_array_item(array, i);
1888 obs_source_t *source = obs_load_source(source_data);
1889
1890 da_push_back(sources, &source);
1891
1892 obs_data_release(source_data);
1893 }
1894
1895 /* tell sources that we want to load */
1896 for (i = 0; i < sources.num; i++) {
1897 obs_source_t *source = sources.array[i];
1898 obs_data_t *source_data = obs_data_array_item(array, i);
1899 if (source) {
1900 if (source->info.type == OBS_SOURCE_TYPE_TRANSITION)
1901 obs_transition_load(source, source_data);
1902 obs_source_load2(source);
1903 if (cb)
1904 cb(private_data, source);
1905 }
1906 obs_data_release(source_data);
1907 }
1908
1909 for (i = 0; i < sources.num; i++)
1910 obs_source_release(sources.array[i]);
1911
1912 pthread_mutex_unlock(&data->sources_mutex);
1913
1914 da_free(sources);
1915 }
1916
obs_save_source(obs_source_t * source)1917 obs_data_t *obs_save_source(obs_source_t *source)
1918 {
1919 obs_data_array_t *filters = obs_data_array_create();
1920 obs_data_t *source_data = obs_data_create();
1921 obs_data_t *settings = obs_source_get_settings(source);
1922 obs_data_t *hotkey_data = source->context.hotkey_data;
1923 obs_data_t *hotkeys;
1924 float volume = obs_source_get_volume(source);
1925 float balance = obs_source_get_balance_value(source);
1926 uint32_t mixers = obs_source_get_audio_mixers(source);
1927 int64_t sync = obs_source_get_sync_offset(source);
1928 uint32_t flags = obs_source_get_flags(source);
1929 const char *name = obs_source_get_name(source);
1930 const char *id = source->info.unversioned_id;
1931 const char *v_id = source->info.id;
1932 bool enabled = obs_source_enabled(source);
1933 bool muted = obs_source_muted(source);
1934 bool push_to_mute = obs_source_push_to_mute_enabled(source);
1935 uint64_t ptm_delay = obs_source_get_push_to_mute_delay(source);
1936 bool push_to_talk = obs_source_push_to_talk_enabled(source);
1937 uint64_t ptt_delay = obs_source_get_push_to_talk_delay(source);
1938 int m_type = (int)obs_source_get_monitoring_type(source);
1939 int di_mode = (int)obs_source_get_deinterlace_mode(source);
1940 int di_order = (int)obs_source_get_deinterlace_field_order(source);
1941
1942 obs_source_save(source);
1943 hotkeys = obs_hotkeys_save_source(source);
1944
1945 if (hotkeys) {
1946 obs_data_release(hotkey_data);
1947 source->context.hotkey_data = hotkeys;
1948 hotkey_data = hotkeys;
1949 }
1950
1951 obs_data_set_int(source_data, "prev_ver", LIBOBS_API_VER);
1952
1953 obs_data_set_string(source_data, "name", name);
1954 obs_data_set_string(source_data, "id", id);
1955 obs_data_set_string(source_data, "versioned_id", v_id);
1956 obs_data_set_obj(source_data, "settings", settings);
1957 obs_data_set_int(source_data, "mixers", mixers);
1958 obs_data_set_int(source_data, "sync", sync);
1959 obs_data_set_int(source_data, "flags", flags);
1960 obs_data_set_double(source_data, "volume", volume);
1961 obs_data_set_double(source_data, "balance", balance);
1962 obs_data_set_bool(source_data, "enabled", enabled);
1963 obs_data_set_bool(source_data, "muted", muted);
1964 obs_data_set_bool(source_data, "push-to-mute", push_to_mute);
1965 obs_data_set_int(source_data, "push-to-mute-delay", ptm_delay);
1966 obs_data_set_bool(source_data, "push-to-talk", push_to_talk);
1967 obs_data_set_int(source_data, "push-to-talk-delay", ptt_delay);
1968 obs_data_set_obj(source_data, "hotkeys", hotkey_data);
1969 obs_data_set_int(source_data, "deinterlace_mode", di_mode);
1970 obs_data_set_int(source_data, "deinterlace_field_order", di_order);
1971 obs_data_set_int(source_data, "monitoring_type", m_type);
1972
1973 obs_data_set_obj(source_data, "private_settings",
1974 source->private_settings);
1975
1976 if (source->info.type == OBS_SOURCE_TYPE_TRANSITION)
1977 obs_transition_save(source, source_data);
1978
1979 pthread_mutex_lock(&source->filter_mutex);
1980
1981 if (source->filters.num) {
1982 for (size_t i = source->filters.num; i > 0; i--) {
1983 obs_source_t *filter = source->filters.array[i - 1];
1984 obs_data_t *filter_data = obs_save_source(filter);
1985 obs_data_array_push_back(filters, filter_data);
1986 obs_data_release(filter_data);
1987 }
1988
1989 obs_data_set_array(source_data, "filters", filters);
1990 }
1991
1992 pthread_mutex_unlock(&source->filter_mutex);
1993
1994 obs_data_release(settings);
1995 obs_data_array_release(filters);
1996
1997 return source_data;
1998 }
1999
obs_save_sources_filtered(obs_save_source_filter_cb cb,void * data_)2000 obs_data_array_t *obs_save_sources_filtered(obs_save_source_filter_cb cb,
2001 void *data_)
2002 {
2003 struct obs_core_data *data = &obs->data;
2004 obs_data_array_t *array;
2005 obs_source_t *source;
2006
2007 array = obs_data_array_create();
2008
2009 pthread_mutex_lock(&data->sources_mutex);
2010
2011 source = data->first_source;
2012
2013 while (source) {
2014 if ((source->info.type != OBS_SOURCE_TYPE_FILTER) != 0 &&
2015 !source->context.private && !source->removed &&
2016 !source->temp_removed && cb(data_, source)) {
2017 obs_data_t *source_data = obs_save_source(source);
2018
2019 obs_data_array_push_back(array, source_data);
2020 obs_data_release(source_data);
2021 }
2022
2023 source = (obs_source_t *)source->context.next;
2024 }
2025
2026 pthread_mutex_unlock(&data->sources_mutex);
2027
2028 return array;
2029 }
2030
save_source_filter(void * data,obs_source_t * source)2031 static bool save_source_filter(void *data, obs_source_t *source)
2032 {
2033 UNUSED_PARAMETER(data);
2034 UNUSED_PARAMETER(source);
2035 return true;
2036 }
2037
obs_save_sources(void)2038 obs_data_array_t *obs_save_sources(void)
2039 {
2040 return obs_save_sources_filtered(save_source_filter, NULL);
2041 }
2042
2043 /* ensures that names are never blank */
dup_name(const char * name,bool private)2044 static inline char *dup_name(const char *name, bool private)
2045 {
2046 if (private && !name)
2047 return NULL;
2048
2049 if (!name || !*name) {
2050 struct dstr unnamed = {0};
2051 dstr_printf(&unnamed, "__unnamed%04lld",
2052 obs->data.unnamed_index++);
2053
2054 return unnamed.array;
2055 } else {
2056 return bstrdup(name);
2057 }
2058 }
2059
obs_context_data_init_wrap(struct obs_context_data * context,enum obs_obj_type type,obs_data_t * settings,const char * name,obs_data_t * hotkey_data,bool private)2060 static inline bool obs_context_data_init_wrap(struct obs_context_data *context,
2061 enum obs_obj_type type,
2062 obs_data_t *settings,
2063 const char *name,
2064 obs_data_t *hotkey_data,
2065 bool private)
2066 {
2067 assert(context);
2068 memset(context, 0, sizeof(*context));
2069 context->private = private;
2070 context->type = type;
2071
2072 pthread_mutex_init_value(&context->rename_cache_mutex);
2073 if (pthread_mutex_init(&context->rename_cache_mutex, NULL) < 0)
2074 return false;
2075
2076 context->signals = signal_handler_create();
2077 if (!context->signals)
2078 return false;
2079
2080 context->procs = proc_handler_create();
2081 if (!context->procs)
2082 return false;
2083
2084 context->name = dup_name(name, private);
2085 context->settings = obs_data_newref(settings);
2086 context->hotkey_data = obs_data_newref(hotkey_data);
2087 return true;
2088 }
2089
obs_context_data_init(struct obs_context_data * context,enum obs_obj_type type,obs_data_t * settings,const char * name,obs_data_t * hotkey_data,bool private)2090 bool obs_context_data_init(struct obs_context_data *context,
2091 enum obs_obj_type type, obs_data_t *settings,
2092 const char *name, obs_data_t *hotkey_data,
2093 bool private)
2094 {
2095 if (obs_context_data_init_wrap(context, type, settings, name,
2096 hotkey_data, private)) {
2097 return true;
2098 } else {
2099 obs_context_data_free(context);
2100 return false;
2101 }
2102 }
2103
obs_context_data_free(struct obs_context_data * context)2104 void obs_context_data_free(struct obs_context_data *context)
2105 {
2106 obs_hotkeys_context_release(context);
2107 signal_handler_destroy(context->signals);
2108 proc_handler_destroy(context->procs);
2109 obs_data_release(context->settings);
2110 obs_context_data_remove(context);
2111 pthread_mutex_destroy(&context->rename_cache_mutex);
2112 bfree(context->name);
2113
2114 for (size_t i = 0; i < context->rename_cache.num; i++)
2115 bfree(context->rename_cache.array[i]);
2116 da_free(context->rename_cache);
2117
2118 memset(context, 0, sizeof(*context));
2119 }
2120
obs_context_data_insert(struct obs_context_data * context,pthread_mutex_t * mutex,void * pfirst)2121 void obs_context_data_insert(struct obs_context_data *context,
2122 pthread_mutex_t *mutex, void *pfirst)
2123 {
2124 struct obs_context_data **first = pfirst;
2125
2126 assert(context);
2127 assert(mutex);
2128 assert(first);
2129
2130 context->mutex = mutex;
2131
2132 pthread_mutex_lock(mutex);
2133 context->prev_next = first;
2134 context->next = *first;
2135 *first = context;
2136 if (context->next)
2137 context->next->prev_next = &context->next;
2138 pthread_mutex_unlock(mutex);
2139 }
2140
obs_context_data_remove(struct obs_context_data * context)2141 void obs_context_data_remove(struct obs_context_data *context)
2142 {
2143 if (context && context->mutex) {
2144 pthread_mutex_lock(context->mutex);
2145 if (context->prev_next)
2146 *context->prev_next = context->next;
2147 if (context->next)
2148 context->next->prev_next = context->prev_next;
2149 pthread_mutex_unlock(context->mutex);
2150
2151 context->mutex = NULL;
2152 }
2153 }
2154
obs_context_data_setname(struct obs_context_data * context,const char * name)2155 void obs_context_data_setname(struct obs_context_data *context,
2156 const char *name)
2157 {
2158 pthread_mutex_lock(&context->rename_cache_mutex);
2159
2160 if (context->name)
2161 da_push_back(context->rename_cache, &context->name);
2162 context->name = dup_name(name, context->private);
2163
2164 pthread_mutex_unlock(&context->rename_cache_mutex);
2165 }
2166
obs_get_profiler_name_store(void)2167 profiler_name_store_t *obs_get_profiler_name_store(void)
2168 {
2169 return obs->name_store;
2170 }
2171
obs_get_video_frame_time(void)2172 uint64_t obs_get_video_frame_time(void)
2173 {
2174 return obs->video.video_time;
2175 }
2176
obs_get_active_fps(void)2177 double obs_get_active_fps(void)
2178 {
2179 return obs->video.video_fps;
2180 }
2181
obs_get_average_frame_time_ns(void)2182 uint64_t obs_get_average_frame_time_ns(void)
2183 {
2184 return obs->video.video_avg_frame_time_ns;
2185 }
2186
obs_get_frame_interval_ns(void)2187 uint64_t obs_get_frame_interval_ns(void)
2188 {
2189 return obs->video.video_frame_interval_ns;
2190 }
2191
obs_obj_get_type(void * obj)2192 enum obs_obj_type obs_obj_get_type(void *obj)
2193 {
2194 struct obs_context_data *context = obj;
2195 return context ? context->type : OBS_OBJ_TYPE_INVALID;
2196 }
2197
obs_obj_get_id(void * obj)2198 const char *obs_obj_get_id(void *obj)
2199 {
2200 struct obs_context_data *context = obj;
2201 if (!context)
2202 return NULL;
2203
2204 switch (context->type) {
2205 case OBS_OBJ_TYPE_SOURCE:
2206 return ((obs_source_t *)obj)->info.id;
2207 case OBS_OBJ_TYPE_OUTPUT:
2208 return ((obs_output_t *)obj)->info.id;
2209 case OBS_OBJ_TYPE_ENCODER:
2210 return ((obs_encoder_t *)obj)->info.id;
2211 case OBS_OBJ_TYPE_SERVICE:
2212 return ((obs_service_t *)obj)->info.id;
2213 default:;
2214 }
2215
2216 return NULL;
2217 }
2218
obs_obj_invalid(void * obj)2219 bool obs_obj_invalid(void *obj)
2220 {
2221 struct obs_context_data *context = obj;
2222 if (!context)
2223 return true;
2224
2225 return !context->data;
2226 }
2227
obs_obj_get_data(void * obj)2228 void *obs_obj_get_data(void *obj)
2229 {
2230 struct obs_context_data *context = obj;
2231 if (!context)
2232 return NULL;
2233
2234 return context->data;
2235 }
2236
obs_obj_is_private(void * obj)2237 bool obs_obj_is_private(void *obj)
2238 {
2239 struct obs_context_data *context = obj;
2240 if (!context)
2241 return false;
2242
2243 return context->private;
2244 }
2245
obs_set_audio_monitoring_device(const char * name,const char * id)2246 bool obs_set_audio_monitoring_device(const char *name, const char *id)
2247 {
2248 if (!name || !id || !*name || !*id)
2249 return false;
2250
2251 #if defined(_WIN32) || HAVE_PULSEAUDIO || defined(__APPLE__)
2252 pthread_mutex_lock(&obs->audio.monitoring_mutex);
2253
2254 if (strcmp(id, obs->audio.monitoring_device_id) == 0) {
2255 pthread_mutex_unlock(&obs->audio.monitoring_mutex);
2256 return true;
2257 }
2258
2259 bfree(obs->audio.monitoring_device_name);
2260 bfree(obs->audio.monitoring_device_id);
2261
2262 obs->audio.monitoring_device_name = bstrdup(name);
2263 obs->audio.monitoring_device_id = bstrdup(id);
2264
2265 for (size_t i = 0; i < obs->audio.monitors.num; i++) {
2266 struct audio_monitor *monitor = obs->audio.monitors.array[i];
2267 audio_monitor_reset(monitor);
2268 }
2269
2270 pthread_mutex_unlock(&obs->audio.monitoring_mutex);
2271 return true;
2272 #else
2273 return false;
2274 #endif
2275 }
2276
obs_get_audio_monitoring_device(const char ** name,const char ** id)2277 void obs_get_audio_monitoring_device(const char **name, const char **id)
2278 {
2279 if (name)
2280 *name = obs->audio.monitoring_device_name;
2281 if (id)
2282 *id = obs->audio.monitoring_device_id;
2283 }
2284
obs_add_tick_callback(void (* tick)(void * param,float seconds),void * param)2285 void obs_add_tick_callback(void (*tick)(void *param, float seconds),
2286 void *param)
2287 {
2288 struct tick_callback data = {tick, param};
2289
2290 pthread_mutex_lock(&obs->data.draw_callbacks_mutex);
2291 da_insert(obs->data.tick_callbacks, 0, &data);
2292 pthread_mutex_unlock(&obs->data.draw_callbacks_mutex);
2293 }
2294
obs_remove_tick_callback(void (* tick)(void * param,float seconds),void * param)2295 void obs_remove_tick_callback(void (*tick)(void *param, float seconds),
2296 void *param)
2297 {
2298 struct tick_callback data = {tick, param};
2299
2300 pthread_mutex_lock(&obs->data.draw_callbacks_mutex);
2301 da_erase_item(obs->data.tick_callbacks, &data);
2302 pthread_mutex_unlock(&obs->data.draw_callbacks_mutex);
2303 }
2304
obs_add_main_render_callback(void (* draw)(void * param,uint32_t cx,uint32_t cy),void * param)2305 void obs_add_main_render_callback(void (*draw)(void *param, uint32_t cx,
2306 uint32_t cy),
2307 void *param)
2308 {
2309 struct draw_callback data = {draw, param};
2310
2311 pthread_mutex_lock(&obs->data.draw_callbacks_mutex);
2312 da_insert(obs->data.draw_callbacks, 0, &data);
2313 pthread_mutex_unlock(&obs->data.draw_callbacks_mutex);
2314 }
2315
obs_remove_main_render_callback(void (* draw)(void * param,uint32_t cx,uint32_t cy),void * param)2316 void obs_remove_main_render_callback(void (*draw)(void *param, uint32_t cx,
2317 uint32_t cy),
2318 void *param)
2319 {
2320 struct draw_callback data = {draw, param};
2321
2322 pthread_mutex_lock(&obs->data.draw_callbacks_mutex);
2323 da_erase_item(obs->data.draw_callbacks, &data);
2324 pthread_mutex_unlock(&obs->data.draw_callbacks_mutex);
2325 }
2326
obs_get_total_frames(void)2327 uint32_t obs_get_total_frames(void)
2328 {
2329 return obs->video.total_frames;
2330 }
2331
obs_get_lagged_frames(void)2332 uint32_t obs_get_lagged_frames(void)
2333 {
2334 return obs->video.lagged_frames;
2335 }
2336
start_raw_video(video_t * v,const struct video_scale_info * conversion,void (* callback)(void * param,struct video_data * frame),void * param)2337 void start_raw_video(video_t *v, const struct video_scale_info *conversion,
2338 void (*callback)(void *param, struct video_data *frame),
2339 void *param)
2340 {
2341 struct obs_core_video *video = &obs->video;
2342 os_atomic_inc_long(&video->raw_active);
2343 video_output_connect(v, conversion, callback, param);
2344 }
2345
stop_raw_video(video_t * v,void (* callback)(void * param,struct video_data * frame),void * param)2346 void stop_raw_video(video_t *v,
2347 void (*callback)(void *param, struct video_data *frame),
2348 void *param)
2349 {
2350 struct obs_core_video *video = &obs->video;
2351 os_atomic_dec_long(&video->raw_active);
2352 video_output_disconnect(v, callback, param);
2353 }
2354
obs_add_raw_video_callback(const struct video_scale_info * conversion,void (* callback)(void * param,struct video_data * frame),void * param)2355 void obs_add_raw_video_callback(const struct video_scale_info *conversion,
2356 void (*callback)(void *param,
2357 struct video_data *frame),
2358 void *param)
2359 {
2360 struct obs_core_video *video = &obs->video;
2361 start_raw_video(video->video, conversion, callback, param);
2362 }
2363
obs_remove_raw_video_callback(void (* callback)(void * param,struct video_data * frame),void * param)2364 void obs_remove_raw_video_callback(void (*callback)(void *param,
2365 struct video_data *frame),
2366 void *param)
2367 {
2368 struct obs_core_video *video = &obs->video;
2369 stop_raw_video(video->video, callback, param);
2370 }
2371
obs_apply_private_data(obs_data_t * settings)2372 void obs_apply_private_data(obs_data_t *settings)
2373 {
2374 if (!settings)
2375 return;
2376
2377 obs_data_apply(obs->data.private_data, settings);
2378 }
2379
obs_set_private_data(obs_data_t * settings)2380 void obs_set_private_data(obs_data_t *settings)
2381 {
2382 obs_data_clear(obs->data.private_data);
2383 if (settings)
2384 obs_data_apply(obs->data.private_data, settings);
2385 }
2386
obs_get_private_data(void)2387 obs_data_t *obs_get_private_data(void)
2388 {
2389 obs_data_t *private_data = obs->data.private_data;
2390 obs_data_addref(private_data);
2391 return private_data;
2392 }
2393
2394 extern bool init_gpu_encoding(struct obs_core_video *video);
2395 extern void stop_gpu_encoding_thread(struct obs_core_video *video);
2396 extern void free_gpu_encoding(struct obs_core_video *video);
2397
start_gpu_encode(obs_encoder_t * encoder)2398 bool start_gpu_encode(obs_encoder_t *encoder)
2399 {
2400 struct obs_core_video *video = &obs->video;
2401 bool success = true;
2402
2403 obs_enter_graphics();
2404 pthread_mutex_lock(&video->gpu_encoder_mutex);
2405
2406 if (!video->gpu_encoders.num)
2407 success = init_gpu_encoding(video);
2408 if (success)
2409 da_push_back(video->gpu_encoders, &encoder);
2410 else
2411 free_gpu_encoding(video);
2412
2413 pthread_mutex_unlock(&video->gpu_encoder_mutex);
2414 obs_leave_graphics();
2415
2416 if (success) {
2417 os_atomic_inc_long(&video->gpu_encoder_active);
2418 video_output_inc_texture_encoders(video->video);
2419 }
2420
2421 return success;
2422 }
2423
stop_gpu_encode(obs_encoder_t * encoder)2424 void stop_gpu_encode(obs_encoder_t *encoder)
2425 {
2426 struct obs_core_video *video = &obs->video;
2427 bool call_free = false;
2428
2429 os_atomic_dec_long(&video->gpu_encoder_active);
2430 video_output_dec_texture_encoders(video->video);
2431
2432 pthread_mutex_lock(&video->gpu_encoder_mutex);
2433 da_erase_item(video->gpu_encoders, &encoder);
2434 if (!video->gpu_encoders.num)
2435 call_free = true;
2436 pthread_mutex_unlock(&video->gpu_encoder_mutex);
2437
2438 os_event_wait(video->gpu_encode_inactive);
2439
2440 if (call_free) {
2441 stop_gpu_encoding_thread(video);
2442
2443 obs_enter_graphics();
2444 pthread_mutex_lock(&video->gpu_encoder_mutex);
2445 free_gpu_encoding(video);
2446 pthread_mutex_unlock(&video->gpu_encoder_mutex);
2447 obs_leave_graphics();
2448 }
2449 }
2450
obs_video_active(void)2451 bool obs_video_active(void)
2452 {
2453 struct obs_core_video *video = &obs->video;
2454
2455 return os_atomic_load_long(&video->raw_active) > 0 ||
2456 os_atomic_load_long(&video->gpu_encoder_active) > 0;
2457 }
2458
obs_nv12_tex_active(void)2459 bool obs_nv12_tex_active(void)
2460 {
2461 struct obs_core_video *video = &obs->video;
2462 return video->using_nv12_tex;
2463 }
2464
2465 /* ------------------------------------------------------------------------- */
2466 /* task stuff */
2467
2468 struct task_wait_info {
2469 obs_task_t task;
2470 void *param;
2471 os_event_t *event;
2472 };
2473
task_wait_callback(void * param)2474 static void task_wait_callback(void *param)
2475 {
2476 struct task_wait_info *info = param;
2477 info->task(info->param);
2478 os_event_signal(info->event);
2479 }
2480
2481 THREAD_LOCAL bool is_graphics_thread = false;
2482
in_task_thread(enum obs_task_type type)2483 static bool in_task_thread(enum obs_task_type type)
2484 {
2485 /* NOTE: OBS_TASK_UI is handled independently */
2486
2487 if (type == OBS_TASK_GRAPHICS)
2488 return is_graphics_thread;
2489
2490 assert(false);
2491 return false;
2492 }
2493
obs_queue_task(enum obs_task_type type,obs_task_t task,void * param,bool wait)2494 void obs_queue_task(enum obs_task_type type, obs_task_t task, void *param,
2495 bool wait)
2496 {
2497 if (type == OBS_TASK_UI) {
2498 if (obs->ui_task_handler) {
2499 obs->ui_task_handler(task, param, wait);
2500 } else {
2501 blog(LOG_ERROR, "UI task could not be queued, "
2502 "there's no UI task handler!");
2503 }
2504 } else {
2505 if (in_task_thread(type)) {
2506 task(param);
2507 } else if (wait) {
2508 struct task_wait_info info = {
2509 .task = task,
2510 .param = param,
2511 };
2512
2513 os_event_init(&info.event, OS_EVENT_TYPE_MANUAL);
2514 obs_queue_task(type, task_wait_callback, &info, false);
2515 os_event_wait(info.event);
2516 os_event_destroy(info.event);
2517 } else {
2518 struct obs_core_video *video = &obs->video;
2519 struct obs_task_info info = {task, param};
2520
2521 pthread_mutex_lock(&video->task_mutex);
2522 circlebuf_push_back(&video->tasks, &info, sizeof(info));
2523 pthread_mutex_unlock(&video->task_mutex);
2524 }
2525 }
2526 }
2527
obs_set_ui_task_handler(obs_task_handler_t handler)2528 void obs_set_ui_task_handler(obs_task_handler_t handler)
2529 {
2530 obs->ui_task_handler = handler;
2531 }
2532