obs-source.c 28 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066
  1. /******************************************************************************
  2. Copyright (C) 2013 by Hugh Bailey <[email protected]>
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation, either version 2 of the License, or
  6. (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. ******************************************************************************/
  14. #include "media-io/format-conversion.h"
  15. #include "util/platform.h"
  16. #include "callback/calldata.h"
  17. #include "graphics/matrix3.h"
  18. #include "graphics/vec3.h"
  19. #include "obs.h"
  20. #include "obs-data.h"
  21. static void obs_source_destroy(obs_source_t source);
  22. bool load_source_info(void *module, const char *module_name,
  23. const char *source_id, struct source_info *info)
  24. {
  25. info->getname = load_module_subfunc(module, module_name,
  26. source_id, "getname", true);
  27. info->create = load_module_subfunc(module, module_name,
  28. source_id,"create", true);
  29. info->destroy = load_module_subfunc(module, module_name,
  30. source_id, "destroy", true);
  31. info->get_output_flags = load_module_subfunc(module, module_name,
  32. source_id, "get_output_flags", true);
  33. if (!info->getname || !info->create || !info->destroy ||
  34. !info->get_output_flags)
  35. return false;
  36. info->config = load_module_subfunc(module, module_name,
  37. source_id, "config", false);
  38. info->activate = load_module_subfunc(module, module_name,
  39. source_id, "activate", false);
  40. info->deactivate = load_module_subfunc(module, module_name,
  41. source_id, "deactivate", false);
  42. info->video_tick = load_module_subfunc(module, module_name,
  43. source_id, "video_tick", false);
  44. info->video_render = load_module_subfunc(module, module_name,
  45. source_id, "video_render", false);
  46. info->getwidth = load_module_subfunc(module, module_name,
  47. source_id, "getwidth", false);
  48. info->getheight = load_module_subfunc(module, module_name,
  49. source_id, "getheight", false);
  50. info->getparam = load_module_subfunc(module, module_name,
  51. source_id, "getparam", false);
  52. info->setparam = load_module_subfunc(module, module_name,
  53. source_id, "setparam", false);
  54. info->filter_video = load_module_subfunc(module, module_name,
  55. source_id, "filter_video", false);
  56. info->filter_audio = load_module_subfunc(module, module_name,
  57. source_id, "filter_audio", false);
  58. info->id = source_id;
  59. return true;
  60. }
  61. static inline const struct source_info *find_source(struct darray *list,
  62. const char *id)
  63. {
  64. size_t i;
  65. struct source_info *array = list->array;
  66. for (i = 0; i < list->num; i++) {
  67. struct source_info *info = array+i;
  68. if (strcmp(info->id, id) == 0)
  69. return info;
  70. }
  71. return NULL;
  72. }
  73. static const struct source_info *get_source_info(enum obs_source_type type,
  74. const char *id)
  75. {
  76. struct darray *list = NULL;
  77. switch (type) {
  78. case SOURCE_INPUT: list = &obs->input_types.da; break;
  79. case SOURCE_FILTER: list = &obs->filter_types.da; break;
  80. case SOURCE_TRANSITION: list = &obs->transition_types.da; break;
  81. case SOURCE_SCENE:
  82. default:
  83. blog(LOG_WARNING, "get_source_info: invalid source type");
  84. return NULL;
  85. }
  86. return find_source(list, id);
  87. }
  88. bool obs_source_init_handlers(struct obs_source *source)
  89. {
  90. source->signals = signal_handler_create();
  91. if (!source->signals)
  92. return false;
  93. source->procs = proc_handler_create();
  94. return (source->procs != NULL);
  95. }
  96. const char *obs_source_getdisplayname(enum obs_source_type type,
  97. const char *id, const char *locale)
  98. {
  99. const struct source_info *info = get_source_info(type, id);
  100. return (info != NULL) ? info->getname(locale) : NULL;
  101. }
  102. /* internal initialization */
  103. bool obs_source_init(struct obs_source *source, const char *settings,
  104. const struct source_info *info)
  105. {
  106. uint32_t flags = info->get_output_flags(source->data);
  107. source->refs = 1;
  108. source->volume = 1.0f;
  109. pthread_mutex_init_value(&source->filter_mutex);
  110. pthread_mutex_init_value(&source->video_mutex);
  111. pthread_mutex_init_value(&source->audio_mutex);
  112. dstr_copy(&source->settings, settings);
  113. memcpy(&source->callbacks, info, sizeof(struct source_info));
  114. if (pthread_mutex_init(&source->filter_mutex, NULL) != 0)
  115. return false;
  116. if (pthread_mutex_init(&source->audio_mutex, NULL) != 0)
  117. return false;
  118. if (pthread_mutex_init(&source->video_mutex, NULL) != 0)
  119. return false;
  120. if (flags & SOURCE_AUDIO) {
  121. source->audio_line = audio_output_createline(obs->audio.audio,
  122. source->name);
  123. if (!source->audio_line) {
  124. blog(LOG_ERROR, "Failed to create audio line for "
  125. "source '%s'", source->name);
  126. return false;
  127. }
  128. }
  129. return true;
  130. }
  131. static inline void obs_source_dosignal(struct obs_source *source,
  132. const char *signal)
  133. {
  134. struct calldata data;
  135. calldata_init(&data);
  136. calldata_setptr(&data, "source", source);
  137. signal_handler_signal(obs->signals, signal, &data);
  138. calldata_free(&data);
  139. }
  140. obs_source_t obs_source_create(enum obs_source_type type, const char *id,
  141. const char *name, const char *settings)
  142. {
  143. struct obs_source *source;
  144. const struct source_info *info = get_source_info(type, id);
  145. if (!info) {
  146. blog(LOG_WARNING, "Source '%s' not found", id);
  147. return NULL;
  148. }
  149. source = bmalloc(sizeof(struct obs_source));
  150. memset(source, 0, sizeof(struct obs_source));
  151. if (!obs_source_init_handlers(source))
  152. goto fail;
  153. source->name = bstrdup(name);
  154. source->type = type;
  155. source->data = info->create(settings, source);
  156. if (!source->data)
  157. goto fail;
  158. if (!obs_source_init(source, settings, info))
  159. goto fail;
  160. obs_source_dosignal(source, "source-create");
  161. return source;
  162. fail:
  163. blog(LOG_ERROR, "obs_source_create failed");
  164. obs_source_destroy(source);
  165. return NULL;
  166. }
  167. static void obs_source_destroy(obs_source_t source)
  168. {
  169. size_t i;
  170. obs_source_dosignal(source, "source-destroy");
  171. if (source->filter_parent)
  172. obs_source_filter_remove(source->filter_parent, source);
  173. for (i = 0; i < source->filters.num; i++)
  174. obs_source_release(source->filters.array[i]);
  175. for (i = 0; i < source->audio_wait_buffer.num; i++)
  176. audiobuf_free(source->audio_wait_buffer.array+i);
  177. for (i = 0; i < source->video_frames.num; i++)
  178. source_frame_destroy(source->video_frames.array[i]);
  179. gs_entercontext(obs->video.graphics);
  180. texture_destroy(source->output_texture);
  181. gs_leavecontext();
  182. if (source->data)
  183. source->callbacks.destroy(source->data);
  184. bfree(source->audio_data.data);
  185. audio_line_destroy(source->audio_line);
  186. audio_resampler_destroy(source->resampler);
  187. proc_handler_destroy(source->procs);
  188. signal_handler_destroy(source->signals);
  189. da_free(source->video_frames);
  190. da_free(source->audio_wait_buffer);
  191. da_free(source->filters);
  192. pthread_mutex_destroy(&source->filter_mutex);
  193. pthread_mutex_destroy(&source->audio_mutex);
  194. pthread_mutex_destroy(&source->video_mutex);
  195. dstr_free(&source->settings);
  196. bfree(source->name);
  197. bfree(source);
  198. }
  199. int obs_source_addref(obs_source_t source)
  200. {
  201. assert(source != NULL);
  202. if (!source)
  203. return 0;
  204. return ++source->refs;
  205. }
  206. int obs_source_release(obs_source_t source)
  207. {
  208. int refs;
  209. assert(source != NULL);
  210. if (!source)
  211. return 0;
  212. refs = --source->refs;
  213. if (refs == 0)
  214. obs_source_destroy(source);
  215. return refs;
  216. }
  217. void obs_source_remove(obs_source_t source)
  218. {
  219. struct obs_data *data = &obs->data;
  220. size_t id;
  221. source->removed = true;
  222. pthread_mutex_lock(&data->sources_mutex);
  223. id = da_find(data->sources, &source, 0);
  224. if (id != DARRAY_INVALID) {
  225. da_erase_item(data->sources, &source);
  226. obs_source_release(source);
  227. }
  228. pthread_mutex_unlock(&data->sources_mutex);
  229. }
  230. bool obs_source_removed(obs_source_t source)
  231. {
  232. return source->removed;
  233. }
  234. uint32_t obs_source_get_output_flags(obs_source_t source)
  235. {
  236. return source->callbacks.get_output_flags(source->data);
  237. }
  238. bool obs_source_hasconfig(obs_source_t source)
  239. {
  240. return source->callbacks.config != NULL;
  241. }
  242. void obs_source_config(obs_source_t source, void *parent)
  243. {
  244. if (source->callbacks.config)
  245. source->callbacks.config(source->data, parent);
  246. }
  247. void obs_source_activate(obs_source_t source)
  248. {
  249. if (source->callbacks.activate)
  250. source->callbacks.activate(source->data);
  251. }
  252. void obs_source_deactivate(obs_source_t source)
  253. {
  254. if (source->callbacks.deactivate)
  255. source->callbacks.deactivate(source->data);
  256. }
  257. void obs_source_video_tick(obs_source_t source, float seconds)
  258. {
  259. if (source->callbacks.video_tick)
  260. source->callbacks.video_tick(source->data, seconds);
  261. }
  262. /* maximum "direct" timestamp variance in nanoseconds */
  263. #define MAX_VARIANCE 2000000000ULL
  264. static void source_output_audio_line(obs_source_t source,
  265. const struct audio_data *data)
  266. {
  267. struct audio_data in = *data;
  268. if (!source->timing_set) {
  269. source->timing_set = true;
  270. source->timing_adjust = os_gettime_ns() - in.timestamp;
  271. /* detects 'directly' set timestamps as long as they're within
  272. * a certain threshold */
  273. if ((source->timing_adjust+MAX_VARIANCE) < MAX_VARIANCE*2)
  274. source->timing_adjust = 0;
  275. }
  276. in.timestamp += source->timing_adjust;
  277. in.volume = source->volume;
  278. audio_line_output(source->audio_line, &in);
  279. }
  280. static void obs_source_flush_audio_wait_buffer(obs_source_t source)
  281. {
  282. size_t i;
  283. pthread_mutex_lock(&source->audio_mutex);
  284. source->timing_set = true;
  285. for (i = 0; i < source->audio_wait_buffer.num; i++) {
  286. struct audiobuf *buf = source->audio_wait_buffer.array+i;
  287. struct audio_data data;
  288. data.data = buf->data;
  289. data.frames = buf->frames;
  290. data.timestamp = buf->timestamp + source->timing_adjust;
  291. data.volume = source->volume;
  292. audio_line_output(source->audio_line, &data);
  293. audiobuf_free(buf);
  294. }
  295. da_free(source->audio_wait_buffer);
  296. pthread_mutex_unlock(&source->audio_mutex);
  297. }
  298. static bool set_texture_size(obs_source_t source, struct source_frame *frame)
  299. {
  300. if (source->output_texture) {
  301. uint32_t width = texture_getwidth(source->output_texture);
  302. uint32_t height = texture_getheight(source->output_texture);
  303. if (width == frame->width && height == frame->height)
  304. return true;
  305. }
  306. texture_destroy(source->output_texture);
  307. source->output_texture = gs_create_texture(frame->width, frame->height,
  308. GS_RGBA, 1, NULL, GS_DYNAMIC);
  309. return source->output_texture != NULL;
  310. }
  311. enum convert_type {
  312. CONVERT_NONE,
  313. CONVERT_NV12,
  314. CONVERT_420,
  315. CONVERT_422_U,
  316. CONVERT_422_Y,
  317. };
  318. static inline enum convert_type get_convert_type(enum video_format format)
  319. {
  320. switch (format) {
  321. case VIDEO_FORMAT_I420:
  322. return CONVERT_420;
  323. case VIDEO_FORMAT_NV12:
  324. return CONVERT_NV12;
  325. case VIDEO_FORMAT_YVYU:
  326. case VIDEO_FORMAT_YUY2:
  327. return CONVERT_422_Y;
  328. case VIDEO_FORMAT_UYVY:
  329. return CONVERT_422_U;
  330. case VIDEO_FORMAT_UNKNOWN:
  331. case VIDEO_FORMAT_YUVX:
  332. case VIDEO_FORMAT_UYVX:
  333. case VIDEO_FORMAT_RGBA:
  334. case VIDEO_FORMAT_BGRA:
  335. case VIDEO_FORMAT_BGRX:
  336. return CONVERT_NONE;
  337. }
  338. return CONVERT_NONE;
  339. }
  340. static inline bool is_yuv(enum video_format format)
  341. {
  342. switch (format) {
  343. case VIDEO_FORMAT_I420:
  344. case VIDEO_FORMAT_NV12:
  345. case VIDEO_FORMAT_YVYU:
  346. case VIDEO_FORMAT_YUY2:
  347. case VIDEO_FORMAT_UYVY:
  348. case VIDEO_FORMAT_YUVX:
  349. case VIDEO_FORMAT_UYVX:
  350. return true;
  351. case VIDEO_FORMAT_UNKNOWN:
  352. case VIDEO_FORMAT_RGBA:
  353. case VIDEO_FORMAT_BGRA:
  354. case VIDEO_FORMAT_BGRX:
  355. return false;
  356. }
  357. return false;
  358. }
  359. static bool upload_frame(texture_t tex, const struct source_frame *frame)
  360. {
  361. void *ptr;
  362. uint32_t row_bytes;
  363. enum convert_type type = get_convert_type(frame->format);
  364. if (type == CONVERT_NONE) {
  365. texture_setimage(tex, frame->data, frame->row_bytes, false);
  366. return true;
  367. }
  368. if (!texture_map(tex, &ptr, &row_bytes))
  369. return false;
  370. if (type == CONVERT_420)
  371. decompress_420(frame->data, frame->width, frame->height,
  372. frame->row_bytes, 0, frame->height, ptr);
  373. else if (type == CONVERT_NV12)
  374. decompress_nv12(frame->data, frame->width, frame->height,
  375. frame->row_bytes, 0, frame->height, ptr);
  376. else if (type == CONVERT_422_Y)
  377. decompress_422(frame->data, frame->width, frame->height,
  378. frame->row_bytes, 0, frame->height, ptr, true);
  379. else if (type == CONVERT_422_U)
  380. decompress_422(frame->data, frame->width, frame->height,
  381. frame->row_bytes, 0, frame->height, ptr, false);
  382. texture_unmap(tex);
  383. return true;
  384. }
  385. static void obs_source_draw_texture(texture_t tex, struct source_frame *frame)
  386. {
  387. effect_t effect = obs->video.default_effect;
  388. bool yuv = is_yuv(frame->format);
  389. const char *type = yuv ? "DrawYUV" : "DrawRGB";
  390. technique_t tech;
  391. eparam_t param;
  392. if (!upload_frame(tex, frame))
  393. return;
  394. tech = effect_gettechnique(effect, type);
  395. technique_begin(tech);
  396. technique_beginpass(tech, 0);
  397. if (yuv) {
  398. param = effect_getparambyname(effect, "yuv_matrix");
  399. effect_setval(effect, param, frame->yuv_matrix,
  400. sizeof(float) * 16);
  401. }
  402. param = effect_getparambyname(effect, "diffuse");
  403. effect_settexture(effect, param, tex);
  404. gs_draw_sprite(tex, frame->flip ? GS_FLIP_V : 0, 0, 0);
  405. technique_endpass(tech);
  406. technique_end(tech);
  407. }
  408. static void obs_source_render_async_video(obs_source_t source)
  409. {
  410. struct source_frame *frame = obs_source_getframe(source);
  411. if (!frame)
  412. return;
  413. source->timing_adjust = frame->timestamp - os_gettime_ns();
  414. if (!source->timing_set && source->audio_wait_buffer.num)
  415. obs_source_flush_audio_wait_buffer(source);
  416. if (set_texture_size(source, frame))
  417. obs_source_draw_texture(source->output_texture, frame);
  418. obs_source_releaseframe(source, frame);
  419. }
  420. static inline void obs_source_render_filters(obs_source_t source)
  421. {
  422. source->rendering_filter = true;
  423. obs_source_video_render(source->filters.array[0]);
  424. source->rendering_filter = false;
  425. }
  426. static inline void obs_source_default_render(obs_source_t source, bool yuv)
  427. {
  428. effect_t effect = obs->video.default_effect;
  429. const char *tech_name = yuv ? "DrawYUV" : "DrawRGB";
  430. technique_t tech = effect_gettechnique(effect, tech_name);
  431. size_t passes, i;
  432. passes = technique_begin(tech);
  433. for (i = 0; i < passes; i++) {
  434. technique_beginpass(tech, i);
  435. source->callbacks.video_render(source->data);
  436. technique_endpass(tech);
  437. }
  438. technique_end(tech);
  439. }
  440. static inline void obs_source_main_render(obs_source_t source)
  441. {
  442. uint32_t flags = source->callbacks.get_output_flags(source->data);
  443. bool default_effect = !source->filter_parent &&
  444. source->filters.num == 0 &&
  445. (flags & SOURCE_DEFAULT_EFFECT) != 0;
  446. if (default_effect)
  447. obs_source_default_render(source, (flags & SOURCE_YUV) != 0);
  448. else
  449. source->callbacks.video_render(source->data);
  450. }
  451. void obs_source_video_render(obs_source_t source)
  452. {
  453. if (source->callbacks.video_render) {
  454. if (source->filters.num && !source->rendering_filter)
  455. obs_source_render_filters(source);
  456. else
  457. obs_source_main_render(source);
  458. } else if (source->filter_target) {
  459. obs_source_video_render(source->filter_target);
  460. } else {
  461. obs_source_render_async_video(source);
  462. }
  463. }
  464. uint32_t obs_source_getwidth(obs_source_t source)
  465. {
  466. if (source->callbacks.getwidth)
  467. return source->callbacks.getwidth(source->data);
  468. return 0;
  469. }
  470. uint32_t obs_source_getheight(obs_source_t source)
  471. {
  472. if (source->callbacks.getheight)
  473. return source->callbacks.getheight(source->data);
  474. return 0;
  475. }
  476. size_t obs_source_getparam(obs_source_t source, const char *param, void *buf,
  477. size_t buf_size)
  478. {
  479. if (source->callbacks.getparam)
  480. return source->callbacks.getparam(source->data, param, buf,
  481. buf_size);
  482. return 0;
  483. }
  484. void obs_source_setparam(obs_source_t source, const char *param,
  485. const void *data, size_t size)
  486. {
  487. if (source->callbacks.setparam)
  488. source->callbacks.setparam(source->data, param, data, size);
  489. }
  490. obs_source_t obs_filter_getparent(obs_source_t filter)
  491. {
  492. return filter->filter_parent;
  493. }
  494. obs_source_t obs_filter_gettarget(obs_source_t filter)
  495. {
  496. return filter->filter_target;
  497. }
  498. void obs_source_filter_add(obs_source_t source, obs_source_t filter)
  499. {
  500. pthread_mutex_lock(&source->filter_mutex);
  501. if (da_find(source->filters, &filter, 0) != DARRAY_INVALID) {
  502. blog(LOG_WARNING, "Tried to add a filter that was already "
  503. "present on the source");
  504. return;
  505. }
  506. if (source->filters.num) {
  507. obs_source_t *back = da_end(source->filters);
  508. (*back)->filter_target = filter;
  509. }
  510. da_push_back(source->filters, &filter);
  511. pthread_mutex_unlock(&source->filter_mutex);
  512. filter->filter_parent = source;
  513. filter->filter_target = source;
  514. }
  515. void obs_source_filter_remove(obs_source_t source, obs_source_t filter)
  516. {
  517. size_t idx;
  518. pthread_mutex_lock(&source->filter_mutex);
  519. idx = da_find(source->filters, &filter, 0);
  520. if (idx == DARRAY_INVALID)
  521. return;
  522. if (idx > 0) {
  523. obs_source_t prev = source->filters.array[idx-1];
  524. prev->filter_target = filter->filter_target;
  525. }
  526. da_erase(source->filters, idx);
  527. pthread_mutex_unlock(&source->filter_mutex);
  528. filter->filter_parent = NULL;
  529. filter->filter_target = NULL;
  530. }
  531. void obs_source_filter_setorder(obs_source_t source, obs_source_t filter,
  532. enum order_movement movement)
  533. {
  534. size_t idx = da_find(source->filters, &filter, 0);
  535. size_t i;
  536. if (idx == DARRAY_INVALID)
  537. return;
  538. if (movement == ORDER_MOVE_UP) {
  539. if (idx == source->filters.num-1)
  540. return;
  541. da_move_item(source->filters, idx, idx+1);
  542. } else if (movement == ORDER_MOVE_DOWN) {
  543. if (idx == 0)
  544. return;
  545. da_move_item(source->filters, idx, idx-1);
  546. } else if (movement == ORDER_MOVE_TOP) {
  547. if (idx == source->filters.num-1)
  548. return;
  549. da_move_item(source->filters, idx, source->filters.num-1);
  550. } else if (movement == ORDER_MOVE_BOTTOM) {
  551. if (idx == 0)
  552. return;
  553. da_move_item(source->filters, idx, 0);
  554. }
  555. /* reorder filter targets, not the nicest way of dealing with things */
  556. for (i = 0; i < source->filters.num; i++) {
  557. obs_source_t next_filter = (i == source->filters.num-1) ?
  558. source : source->filters.array[idx+1];
  559. source->filters.array[i]->filter_target = next_filter;
  560. }
  561. }
  562. const char *obs_source_getsettings(obs_source_t source)
  563. {
  564. return source->settings.array;
  565. }
  566. void obs_source_savesettings(obs_source_t source, const char *settings)
  567. {
  568. dstr_copy(&source->settings, settings);
  569. }
  570. static inline struct source_frame *filter_async_video(obs_source_t source,
  571. struct source_frame *in)
  572. {
  573. size_t i;
  574. for (i = source->filters.num; i > 0; i--) {
  575. struct obs_source *filter = source->filters.array[i-1];
  576. if (filter->callbacks.filter_video) {
  577. in = filter->callbacks.filter_video(filter->data, in);
  578. if (!in)
  579. return NULL;
  580. }
  581. }
  582. return in;
  583. }
  584. static inline struct source_frame *cache_video(obs_source_t source,
  585. const struct source_frame *frame)
  586. {
  587. /* TODO: use an actual cache */
  588. struct source_frame *new_frame = bmalloc(sizeof(struct source_frame));
  589. memcpy(new_frame, frame, sizeof(struct source_frame));
  590. new_frame->data = bmalloc(frame->row_bytes * frame->height);
  591. return new_frame;
  592. }
  593. void obs_source_output_video(obs_source_t source,
  594. const struct source_frame *frame)
  595. {
  596. struct source_frame *output = cache_video(source, frame);
  597. pthread_mutex_lock(&source->filter_mutex);
  598. output = filter_async_video(source, output);
  599. pthread_mutex_unlock(&source->filter_mutex);
  600. if (output) {
  601. pthread_mutex_lock(&source->video_mutex);
  602. da_push_back(source->video_frames, &output);
  603. pthread_mutex_unlock(&source->video_mutex);
  604. }
  605. }
  606. static inline struct filtered_audio *filter_async_audio(obs_source_t source,
  607. struct filtered_audio *in)
  608. {
  609. size_t i;
  610. for (i = source->filters.num; i > 0; i--) {
  611. struct obs_source *filter = source->filters.array[i-1];
  612. if (filter->callbacks.filter_audio) {
  613. in = filter->callbacks.filter_audio(filter->data, in);
  614. if (!in)
  615. return NULL;
  616. }
  617. }
  618. return in;
  619. }
  620. static inline void reset_resampler(obs_source_t source,
  621. const struct source_audio *audio)
  622. {
  623. const struct audio_info *obs_info;
  624. struct resample_info output_info;
  625. obs_info = audio_output_getinfo(obs->audio.audio);
  626. output_info.format = obs_info->format;
  627. output_info.samples_per_sec = obs_info->samples_per_sec;
  628. output_info.speakers = obs_info->speakers;
  629. source->sample_info.format = audio->format;
  630. source->sample_info.samples_per_sec = audio->samples_per_sec;
  631. source->sample_info.speakers = audio->speakers;
  632. if (source->sample_info.samples_per_sec == obs_info->samples_per_sec &&
  633. source->sample_info.format == obs_info->format &&
  634. source->sample_info.speakers == obs_info->speakers) {
  635. source->audio_failed = false;
  636. return;
  637. }
  638. audio_resampler_destroy(source->resampler);
  639. source->resampler = audio_resampler_create(&output_info,
  640. &source->sample_info);
  641. source->audio_failed = source->resampler == NULL;
  642. if (source->resampler == NULL)
  643. blog(LOG_ERROR, "creation of resampler failed");
  644. }
  645. static inline void copy_audio_data(obs_source_t source,
  646. const void *data, uint32_t frames, uint64_t timestamp)
  647. {
  648. size_t blocksize = audio_output_blocksize(obs->audio.audio);
  649. size_t size = (size_t)frames * blocksize;
  650. /* ensure audio storage capacity */
  651. if (source->audio_storage_size < size) {
  652. bfree(source->audio_data.data);
  653. source->audio_data.data = bmalloc(size);
  654. source->audio_storage_size = size;
  655. }
  656. source->audio_data.frames = frames;
  657. source->audio_data.timestamp = timestamp;
  658. memcpy(source->audio_data.data, data, size);
  659. }
  660. /* resamples/remixes new audio to the designated main audio output format */
  661. static void process_audio(obs_source_t source, const struct source_audio *audio)
  662. {
  663. if (source->sample_info.samples_per_sec != audio->samples_per_sec ||
  664. source->sample_info.format != audio->format ||
  665. source->sample_info.speakers != audio->speakers)
  666. reset_resampler(source, audio);
  667. if (source->audio_failed)
  668. return;
  669. if (source->resampler) {
  670. void *output;
  671. uint32_t frames;
  672. uint64_t offset;
  673. audio_resampler_resample(source->resampler, &output, &frames,
  674. audio->data, audio->frames, &offset);
  675. copy_audio_data(source, output, frames,
  676. audio->timestamp - offset);
  677. } else {
  678. copy_audio_data(source, audio->data, audio->frames,
  679. audio->timestamp);
  680. }
  681. }
  682. void obs_source_output_audio(obs_source_t source,
  683. const struct source_audio *audio)
  684. {
  685. uint32_t flags = obs_source_get_output_flags(source);
  686. size_t blocksize = audio_output_blocksize(obs->audio.audio);
  687. struct filtered_audio *output;
  688. process_audio(source, audio);
  689. pthread_mutex_lock(&source->filter_mutex);
  690. output = filter_async_audio(source, &source->audio_data);
  691. if (output) {
  692. pthread_mutex_lock(&source->audio_mutex);
  693. /* wait for video to start before outputting any audio so we
  694. * have a base for sync */
  695. if (!source->timing_set && (flags & SOURCE_ASYNC_VIDEO) != 0) {
  696. struct audiobuf newbuf;
  697. size_t audio_size = blocksize * output->frames;
  698. newbuf.data = bmalloc(audio_size);
  699. newbuf.frames = output->frames;
  700. newbuf.timestamp = output->timestamp;
  701. memcpy(newbuf.data, output->data, audio_size);
  702. da_push_back(source->audio_wait_buffer, &newbuf);
  703. } else {
  704. struct audio_data data;
  705. data.data = output->data;
  706. data.frames = output->frames;
  707. data.timestamp = output->timestamp;
  708. source_output_audio_line(source, &data);
  709. }
  710. pthread_mutex_unlock(&source->audio_mutex);
  711. }
  712. pthread_mutex_unlock(&source->filter_mutex);
  713. }
  714. /*
  715. * Ensures that cached frames are displayed on time. If multiple frames
  716. * were cached between renders, then releases the unnecessary frames and uses
  717. * the frame with the closest timing to ensure sync.
  718. */
  719. struct source_frame *obs_source_getframe(obs_source_t source)
  720. {
  721. uint64_t last_frame_time = source->last_frame_timestamp;
  722. struct source_frame *frame = NULL;
  723. struct source_frame *next_frame;
  724. uint64_t sys_time, frame_time;
  725. pthread_mutex_lock(&source->video_mutex);
  726. if (!source->video_frames.num)
  727. goto unlock;
  728. next_frame = source->video_frames.array[0];
  729. sys_time = os_gettime_ns();
  730. frame_time = next_frame->timestamp;
  731. if (!source->last_frame_timestamp) {
  732. frame = next_frame;
  733. da_erase(source->video_frames, 0);
  734. source->last_frame_timestamp = frame_time;
  735. } else {
  736. uint64_t sys_offset, frame_offset;
  737. sys_offset = sys_time - source->last_sys_timestamp;
  738. frame_offset = frame_time - last_frame_time;
  739. source->last_frame_timestamp += sys_offset;
  740. while (frame_offset <= sys_offset) {
  741. if (frame)
  742. source_frame_destroy(frame);
  743. frame = next_frame;
  744. da_erase(source->video_frames, 0);
  745. if (!source->video_frames.num)
  746. break;
  747. next_frame = source->video_frames.array[0];
  748. frame_time = next_frame->timestamp;
  749. frame_offset = frame_time - last_frame_time;
  750. }
  751. }
  752. source->last_sys_timestamp = sys_time;
  753. unlock:
  754. pthread_mutex_unlock(&source->video_mutex);
  755. if (frame != NULL)
  756. obs_source_addref(source);
  757. return frame;
  758. }
  759. void obs_source_releaseframe(obs_source_t source, struct source_frame *frame)
  760. {
  761. if (frame) {
  762. source_frame_destroy(frame);
  763. obs_source_release(source);
  764. }
  765. }
  766. const char *obs_source_getname(obs_source_t source)
  767. {
  768. return source->name;
  769. }
  770. void obs_source_setname(obs_source_t source, const char *name)
  771. {
  772. bfree(source->name);
  773. source->name = bstrdup(name);
  774. }
  775. void obs_source_gettype(obs_source_t source, enum obs_source_type *type,
  776. const char **id)
  777. {
  778. if (type) *type = source->type;
  779. if (id) *id = source->callbacks.id;
  780. }
  781. static inline void render_filter_bypass(obs_source_t target, effect_t effect,
  782. uint32_t width, uint32_t height, bool yuv)
  783. {
  784. const char *tech_name = yuv ? "DrawYUV" : "DrawRGB";
  785. technique_t tech = effect_gettechnique(effect, tech_name);
  786. eparam_t diffuse = effect_getparambyname(effect, "diffuse");
  787. size_t passes, i;
  788. passes = technique_begin(tech);
  789. for (i = 0; i < passes; i++) {
  790. technique_beginpass(tech, i);
  791. obs_source_video_render(target);
  792. technique_endpass(tech);
  793. }
  794. technique_end(tech);
  795. }
  796. static inline void render_filter_tex(texture_t tex, effect_t effect,
  797. uint32_t width, uint32_t height, bool yuv)
  798. {
  799. const char *tech_name = yuv ? "DrawYUV" : "DrawRGB";
  800. technique_t tech = effect_gettechnique(effect, tech_name);
  801. eparam_t diffuse = effect_getparambyname(effect, "diffuse");
  802. size_t passes, i;
  803. effect_settexture(effect, diffuse, tex);
  804. passes = technique_begin(tech);
  805. for (i = 0; i < passes; i++) {
  806. technique_beginpass(tech, i);
  807. gs_draw_sprite(tex, width, height, 0);
  808. technique_endpass(tech);
  809. }
  810. technique_end(tech);
  811. }
  812. void obs_source_process_filter(obs_source_t filter, texrender_t texrender,
  813. effect_t effect, uint32_t width, uint32_t height,
  814. enum allow_direct_render allow_direct)
  815. {
  816. obs_source_t target = obs_filter_gettarget(filter);
  817. obs_source_t parent = obs_filter_getparent(filter);
  818. uint32_t target_flags = obs_source_get_output_flags(target);
  819. uint32_t parent_flags = obs_source_get_output_flags(parent);
  820. int cx = obs_source_getwidth(target);
  821. int cy = obs_source_getheight(target);
  822. bool yuv = (target_flags & SOURCE_YUV) != 0;
  823. bool expects_def = (parent_flags & SOURCE_DEFAULT_EFFECT) != 0;
  824. bool can_directly = allow_direct == ALLOW_DIRECT_RENDERING;
  825. /* if the parent does not use any custom effects, and this is the last
  826. * filter in the chain for the parent, then render the parent directly
  827. * using the filter effect instead of rendering to texture to reduce
  828. * the total number of passes */
  829. if (can_directly && expects_def && target == parent) {
  830. render_filter_bypass(target, effect, width, height, yuv);
  831. return;
  832. }
  833. if (texrender_begin(texrender, cx, cy)) {
  834. gs_ortho(0.0f, (float)cx, 0.0f, (float)cy, -100.0f, 100.0f);
  835. if (expects_def && parent == target)
  836. obs_source_default_render(parent, yuv);
  837. else
  838. obs_source_video_render(target);
  839. texrender_end(texrender);
  840. }
  841. /* --------------------------- */
  842. render_filter_tex(texrender_gettexture(texrender), effect,
  843. width, height, yuv);
  844. }
  845. signal_handler_t obs_source_signalhandler(obs_source_t source)
  846. {
  847. return source->signals;
  848. }
  849. proc_handler_t obs_source_prochandler(obs_source_t source)
  850. {
  851. return source->procs;
  852. }
  853. void obs_source_setvolume(obs_source_t source, float volume)
  854. {
  855. source->volume = volume;
  856. }
  857. float obs_source_getvolume(obs_source_t source)
  858. {
  859. return source->volume;
  860. }