1 /******************************************************************************
2 Copyright (C) 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 "obs-internal.h"
19
find_service(const char * id)20 const struct obs_service_info *find_service(const char *id)
21 {
22 size_t i;
23 for (i = 0; i < obs->service_types.num; i++)
24 if (strcmp(obs->service_types.array[i].id, id) == 0)
25 return obs->service_types.array + i;
26
27 return NULL;
28 }
29
obs_service_get_display_name(const char * id)30 const char *obs_service_get_display_name(const char *id)
31 {
32 const struct obs_service_info *info = find_service(id);
33 return (info != NULL) ? info->get_name(info->type_data) : NULL;
34 }
35
obs_service_create_internal(const char * id,const char * name,obs_data_t * settings,obs_data_t * hotkey_data,bool private)36 static obs_service_t *obs_service_create_internal(const char *id,
37 const char *name,
38 obs_data_t *settings,
39 obs_data_t *hotkey_data,
40 bool private)
41 {
42 const struct obs_service_info *info = find_service(id);
43 struct obs_service *service;
44
45 if (!info) {
46 blog(LOG_ERROR, "Service '%s' not found", id);
47 return NULL;
48 }
49
50 service = bzalloc(sizeof(struct obs_service));
51
52 if (!obs_context_data_init(&service->context, OBS_OBJ_TYPE_SERVICE,
53 settings, name, hotkey_data, private)) {
54 bfree(service);
55 return NULL;
56 }
57
58 service->info = *info;
59 service->context.data =
60 service->info.create(service->context.settings, service);
61 if (!service->context.data)
62 blog(LOG_ERROR, "Failed to create service '%s'!", name);
63
64 service->control = bzalloc(sizeof(obs_weak_service_t));
65 service->control->service = service;
66
67 obs_context_data_insert(&service->context, &obs->data.services_mutex,
68 &obs->data.first_service);
69
70 blog(LOG_DEBUG, "service '%s' (%s) created", name, id);
71 return service;
72 }
73
obs_service_create(const char * id,const char * name,obs_data_t * settings,obs_data_t * hotkey_data)74 obs_service_t *obs_service_create(const char *id, const char *name,
75 obs_data_t *settings, obs_data_t *hotkey_data)
76 {
77 return obs_service_create_internal(id, name, settings, hotkey_data,
78 false);
79 }
80
obs_service_create_private(const char * id,const char * name,obs_data_t * settings)81 obs_service_t *obs_service_create_private(const char *id, const char *name,
82 obs_data_t *settings)
83 {
84 return obs_service_create_internal(id, name, settings, NULL, true);
85 }
86
actually_destroy_service(struct obs_service * service)87 static void actually_destroy_service(struct obs_service *service)
88 {
89 if (service->context.data)
90 service->info.destroy(service->context.data);
91
92 if (service->output)
93 service->output->service = NULL;
94
95 blog(LOG_DEBUG, "service '%s' destroyed", service->context.name);
96
97 obs_context_data_free(&service->context);
98 if (service->owns_info_id)
99 bfree((void *)service->info.id);
100 bfree(service);
101 }
102
obs_service_destroy(obs_service_t * service)103 void obs_service_destroy(obs_service_t *service)
104 {
105 if (service) {
106 obs_context_data_remove(&service->context);
107
108 service->destroy = true;
109
110 /* do NOT destroy the service until the service is no
111 * longer in use */
112 if (!service->active)
113 actually_destroy_service(service);
114 }
115 }
116
obs_service_get_name(const obs_service_t * service)117 const char *obs_service_get_name(const obs_service_t *service)
118 {
119 return obs_service_valid(service, "obs_service_get_name")
120 ? service->context.name
121 : NULL;
122 }
123
get_defaults(const struct obs_service_info * info)124 static inline obs_data_t *get_defaults(const struct obs_service_info *info)
125 {
126 obs_data_t *settings = obs_data_create();
127 if (info->get_defaults)
128 info->get_defaults(settings);
129 return settings;
130 }
131
obs_service_defaults(const char * id)132 obs_data_t *obs_service_defaults(const char *id)
133 {
134 const struct obs_service_info *info = find_service(id);
135 return (info) ? get_defaults(info) : NULL;
136 }
137
obs_get_service_properties(const char * id)138 obs_properties_t *obs_get_service_properties(const char *id)
139 {
140 const struct obs_service_info *info = find_service(id);
141 if (info && info->get_properties) {
142 obs_data_t *defaults = get_defaults(info);
143 obs_properties_t *properties;
144
145 properties = info->get_properties(NULL);
146 obs_properties_apply_settings(properties, defaults);
147 obs_data_release(defaults);
148 return properties;
149 }
150 return NULL;
151 }
152
obs_service_properties(const obs_service_t * service)153 obs_properties_t *obs_service_properties(const obs_service_t *service)
154 {
155 if (!obs_service_valid(service, "obs_service_properties"))
156 return NULL;
157
158 if (service->info.get_properties) {
159 obs_properties_t *props;
160 props = service->info.get_properties(service->context.data);
161 obs_properties_apply_settings(props, service->context.settings);
162 return props;
163 }
164
165 return NULL;
166 }
167
obs_service_get_type(const obs_service_t * service)168 const char *obs_service_get_type(const obs_service_t *service)
169 {
170 return obs_service_valid(service, "obs_service_get_type")
171 ? service->info.id
172 : NULL;
173 }
174
obs_service_update(obs_service_t * service,obs_data_t * settings)175 void obs_service_update(obs_service_t *service, obs_data_t *settings)
176 {
177 if (!obs_service_valid(service, "obs_service_update"))
178 return;
179
180 obs_data_apply(service->context.settings, settings);
181
182 if (service->info.update)
183 service->info.update(service->context.data,
184 service->context.settings);
185 }
186
obs_service_get_settings(const obs_service_t * service)187 obs_data_t *obs_service_get_settings(const obs_service_t *service)
188 {
189 if (!obs_service_valid(service, "obs_service_get_settings"))
190 return NULL;
191
192 obs_data_addref(service->context.settings);
193 return service->context.settings;
194 }
195
obs_service_get_signal_handler(const obs_service_t * service)196 signal_handler_t *obs_service_get_signal_handler(const obs_service_t *service)
197 {
198 return obs_service_valid(service, "obs_service_get_signal_handler")
199 ? service->context.signals
200 : NULL;
201 }
202
obs_service_get_proc_handler(const obs_service_t * service)203 proc_handler_t *obs_service_get_proc_handler(const obs_service_t *service)
204 {
205 return obs_service_valid(service, "obs_service_get_proc_handler")
206 ? service->context.procs
207 : NULL;
208 }
209
obs_service_get_url(const obs_service_t * service)210 const char *obs_service_get_url(const obs_service_t *service)
211 {
212 if (!obs_service_valid(service, "obs_service_get_url"))
213 return NULL;
214
215 if (!service->info.get_url)
216 return NULL;
217 return service->info.get_url(service->context.data);
218 }
219
obs_service_get_key(const obs_service_t * service)220 const char *obs_service_get_key(const obs_service_t *service)
221 {
222 if (!obs_service_valid(service, "obs_service_get_key"))
223 return NULL;
224
225 if (!service->info.get_key)
226 return NULL;
227 return service->info.get_key(service->context.data);
228 }
229
obs_service_get_username(const obs_service_t * service)230 const char *obs_service_get_username(const obs_service_t *service)
231 {
232 if (!obs_service_valid(service, "obs_service_get_username"))
233 return NULL;
234
235 if (!service->info.get_username)
236 return NULL;
237 return service->info.get_username(service->context.data);
238 }
239
obs_service_get_password(const obs_service_t * service)240 const char *obs_service_get_password(const obs_service_t *service)
241 {
242 if (!obs_service_valid(service, "obs_service_get_password"))
243 return NULL;
244
245 if (!service->info.get_password)
246 return NULL;
247 return service->info.get_password(service->context.data);
248 }
249
obs_service_activate(struct obs_service * service)250 void obs_service_activate(struct obs_service *service)
251 {
252 if (!obs_service_valid(service, "obs_service_activate"))
253 return;
254 if (!service->output) {
255 blog(LOG_WARNING,
256 "obs_service_deactivate: service '%s' "
257 "is not assigned to an output",
258 obs_service_get_name(service));
259 return;
260 }
261 if (service->active)
262 return;
263
264 if (service->info.activate)
265 service->info.activate(service->context.data,
266 service->context.settings);
267 service->active = true;
268 }
269
obs_service_deactivate(struct obs_service * service,bool remove)270 void obs_service_deactivate(struct obs_service *service, bool remove)
271 {
272 if (!obs_service_valid(service, "obs_service_deactivate"))
273 return;
274 if (!service->output) {
275 blog(LOG_WARNING,
276 "obs_service_deactivate: service '%s' "
277 "is not assigned to an output",
278 obs_service_get_name(service));
279 return;
280 }
281
282 if (!service->active)
283 return;
284
285 if (service->info.deactivate)
286 service->info.deactivate(service->context.data);
287 service->active = false;
288
289 if (service->destroy)
290 actually_destroy_service(service);
291 else if (remove)
292 service->output = NULL;
293 }
294
obs_service_initialize(struct obs_service * service,struct obs_output * output)295 bool obs_service_initialize(struct obs_service *service,
296 struct obs_output *output)
297 {
298 if (!obs_service_valid(service, "obs_service_initialize"))
299 return false;
300 if (!obs_output_valid(output, "obs_service_initialize"))
301 return false;
302
303 if (service->info.initialize)
304 return service->info.initialize(service->context.data, output);
305 return true;
306 }
307
obs_service_apply_encoder_settings(obs_service_t * service,obs_data_t * video_encoder_settings,obs_data_t * audio_encoder_settings)308 void obs_service_apply_encoder_settings(obs_service_t *service,
309 obs_data_t *video_encoder_settings,
310 obs_data_t *audio_encoder_settings)
311 {
312 if (!obs_service_valid(service, "obs_service_apply_encoder_settings"))
313 return;
314 if (!service->info.apply_encoder_settings)
315 return;
316
317 if (video_encoder_settings || audio_encoder_settings)
318 service->info.apply_encoder_settings(service->context.data,
319 video_encoder_settings,
320 audio_encoder_settings);
321 }
322
obs_service_addref(obs_service_t * service)323 void obs_service_addref(obs_service_t *service)
324 {
325 if (!service)
326 return;
327
328 obs_ref_addref(&service->control->ref);
329 }
330
obs_service_release(obs_service_t * service)331 void obs_service_release(obs_service_t *service)
332 {
333 if (!service)
334 return;
335
336 obs_weak_service_t *control = service->control;
337 if (obs_ref_release(&control->ref)) {
338 // The order of operations is important here since
339 // get_context_by_name in obs.c relies on weak refs
340 // being alive while the context is listed
341 obs_service_destroy(service);
342 obs_weak_service_release(control);
343 }
344 }
345
obs_weak_service_addref(obs_weak_service_t * weak)346 void obs_weak_service_addref(obs_weak_service_t *weak)
347 {
348 if (!weak)
349 return;
350
351 obs_weak_ref_addref(&weak->ref);
352 }
353
obs_weak_service_release(obs_weak_service_t * weak)354 void obs_weak_service_release(obs_weak_service_t *weak)
355 {
356 if (!weak)
357 return;
358
359 if (obs_weak_ref_release(&weak->ref))
360 bfree(weak);
361 }
362
obs_service_get_ref(obs_service_t * service)363 obs_service_t *obs_service_get_ref(obs_service_t *service)
364 {
365 if (!service)
366 return NULL;
367
368 return obs_weak_service_get_service(service->control);
369 }
370
obs_service_get_weak_service(obs_service_t * service)371 obs_weak_service_t *obs_service_get_weak_service(obs_service_t *service)
372 {
373 if (!service)
374 return NULL;
375
376 obs_weak_service_t *weak = service->control;
377 obs_weak_service_addref(weak);
378 return weak;
379 }
380
obs_weak_service_get_service(obs_weak_service_t * weak)381 obs_service_t *obs_weak_service_get_service(obs_weak_service_t *weak)
382 {
383 if (!weak)
384 return NULL;
385
386 if (obs_weak_ref_get_ref(&weak->ref))
387 return weak->service;
388
389 return NULL;
390 }
391
obs_weak_service_references_service(obs_weak_service_t * weak,obs_service_t * service)392 bool obs_weak_service_references_service(obs_weak_service_t *weak,
393 obs_service_t *service)
394 {
395 return weak && service && weak->service == service;
396 }
397
obs_service_get_type_data(obs_service_t * service)398 void *obs_service_get_type_data(obs_service_t *service)
399 {
400 return obs_service_valid(service, "obs_service_get_type_data")
401 ? service->info.type_data
402 : NULL;
403 }
404
obs_service_get_id(const obs_service_t * service)405 const char *obs_service_get_id(const obs_service_t *service)
406 {
407 return obs_service_valid(service, "obs_service_get_id")
408 ? service->info.id
409 : NULL;
410 }
411
obs_service_get_output_type(const obs_service_t * service)412 const char *obs_service_get_output_type(const obs_service_t *service)
413 {
414 if (!obs_service_valid(service, "obs_service_get_output_type"))
415 return NULL;
416
417 if (service->info.get_output_type)
418 return service->info.get_output_type(service->context.data);
419 return NULL;
420 }
421
obs_service_get_supported_resolutions(const obs_service_t * service,struct obs_service_resolution ** resolutions,size_t * count)422 void obs_service_get_supported_resolutions(
423 const obs_service_t *service,
424 struct obs_service_resolution **resolutions, size_t *count)
425 {
426 if (!obs_service_valid(service, "obs_service_supported_resolutions"))
427 return;
428 if (!obs_ptr_valid(resolutions, "obs_service_supported_resolutions"))
429 return;
430 if (!obs_ptr_valid(count, "obs_service_supported_resolutions"))
431 return;
432
433 *resolutions = NULL;
434 *count = 0;
435
436 if (service->info.get_supported_resolutions)
437 service->info.get_supported_resolutions(service->context.data,
438 resolutions, count);
439 }
440
obs_service_get_max_fps(const obs_service_t * service,int * fps)441 void obs_service_get_max_fps(const obs_service_t *service, int *fps)
442 {
443 if (!obs_service_valid(service, "obs_service_get_max_fps"))
444 return;
445 if (!obs_ptr_valid(fps, "obs_service_get_max_fps"))
446 return;
447
448 *fps = 0;
449
450 if (service->info.get_max_fps)
451 service->info.get_max_fps(service->context.data, fps);
452 }
453
obs_service_get_max_bitrate(const obs_service_t * service,int * video_bitrate,int * audio_bitrate)454 void obs_service_get_max_bitrate(const obs_service_t *service,
455 int *video_bitrate, int *audio_bitrate)
456 {
457 if (video_bitrate)
458 *video_bitrate = 0;
459 if (audio_bitrate)
460 *audio_bitrate = 0;
461
462 if (!obs_service_valid(service, "obs_service_get_max_bitrate"))
463 return;
464
465 if (service->info.get_max_bitrate)
466 service->info.get_max_bitrate(service->context.data,
467 video_bitrate, audio_bitrate);
468 }
469