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