obs-source.c 26 KB

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