obs-source-deinterlace.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547
  1. /******************************************************************************
  2. Copyright (C) 2016 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 "obs-internal.h"
  15. static bool ready_deinterlace_frames(obs_source_t *source, uint64_t sys_time)
  16. {
  17. struct obs_source_frame *next_frame = source->async_frames.array[0];
  18. struct obs_source_frame *prev_frame = NULL;
  19. struct obs_source_frame *frame = NULL;
  20. uint64_t sys_offset = sys_time - source->last_sys_timestamp;
  21. uint64_t frame_time = next_frame->timestamp;
  22. uint64_t frame_offset = 0;
  23. size_t idx = 1;
  24. if (source->async_unbuffered) {
  25. while (source->async_frames.num > 2) {
  26. da_erase(source->async_frames, 0);
  27. remove_async_frame(source, next_frame);
  28. next_frame = source->async_frames.array[0];
  29. }
  30. if (source->async_frames.num == 2) {
  31. bool prev_frame = true;
  32. if (source->async_unbuffered &&
  33. source->deinterlace_offset) {
  34. const uint64_t timestamp =
  35. source->async_frames.array[0]->timestamp;
  36. const uint64_t after_timestamp =
  37. source->async_frames.array[1]->timestamp;
  38. const uint64_t duration =
  39. after_timestamp - timestamp;
  40. const uint64_t frame_end =
  41. timestamp + source->deinterlace_offset +
  42. duration;
  43. if (sys_time < frame_end) {
  44. // Don't skip ahead prematurely.
  45. prev_frame = false;
  46. source->deinterlace_frame_ts =
  47. timestamp - duration;
  48. }
  49. }
  50. source->async_frames.array[0]->prev_frame = prev_frame;
  51. }
  52. source->deinterlace_offset = 0;
  53. source->last_frame_ts = next_frame->timestamp;
  54. return true;
  55. }
  56. /* account for timestamp invalidation */
  57. if (frame_out_of_bounds(source, frame_time)) {
  58. source->last_frame_ts = next_frame->timestamp;
  59. source->deinterlace_offset = 0;
  60. return true;
  61. } else {
  62. frame_offset = frame_time - source->last_frame_ts;
  63. source->last_frame_ts += sys_offset;
  64. }
  65. while (source->last_frame_ts > next_frame->timestamp) {
  66. /* this tries to reduce the needless frame duplication, also
  67. * helps smooth out async rendering to frame boundaries. In
  68. * other words, tries to keep the framerate as smooth as
  69. * possible */
  70. if ((source->last_frame_ts - next_frame->timestamp) < 2000000)
  71. break;
  72. if (prev_frame) {
  73. da_erase(source->async_frames, 0);
  74. remove_async_frame(source, prev_frame);
  75. }
  76. if (source->async_frames.num <= 2) {
  77. bool exit = true;
  78. if (prev_frame) {
  79. prev_frame->prev_frame = true;
  80. } else if (!frame && source->async_frames.num == 2) {
  81. exit = false;
  82. }
  83. if (exit) {
  84. source->deinterlace_offset = 0;
  85. return true;
  86. }
  87. }
  88. if (frame)
  89. idx = 2;
  90. else
  91. idx = 1;
  92. prev_frame = frame;
  93. frame = next_frame;
  94. next_frame = source->async_frames.array[idx];
  95. /* more timestamp checking and compensating */
  96. if ((next_frame->timestamp - frame_time) > MAX_TS_VAR) {
  97. source->last_frame_ts =
  98. next_frame->timestamp - frame_offset;
  99. source->deinterlace_offset = 0;
  100. }
  101. frame_time = next_frame->timestamp;
  102. frame_offset = frame_time - source->last_frame_ts;
  103. }
  104. if (prev_frame)
  105. prev_frame->prev_frame = true;
  106. return frame != NULL;
  107. }
  108. static inline bool first_frame(obs_source_t *s)
  109. {
  110. if (s->last_frame_ts)
  111. return false;
  112. if (s->async_frames.num >= 2)
  113. s->async_frames.array[0]->prev_frame = true;
  114. return true;
  115. }
  116. static inline uint64_t uint64_diff(uint64_t ts1, uint64_t ts2)
  117. {
  118. return (ts1 < ts2) ? (ts2 - ts1) : (ts1 - ts2);
  119. }
  120. #define TWOX_TOLERANCE 1000000
  121. #define TS_JUMP_THRESHOLD 70000000ULL
  122. static inline void deinterlace_get_closest_frames(obs_source_t *s,
  123. uint64_t sys_time)
  124. {
  125. const struct video_output_info *info;
  126. uint64_t half_interval;
  127. if (s->async_unbuffered && s->deinterlace_offset) {
  128. // Want to keep frame if it has not elapsed.
  129. const uint64_t frame_end =
  130. s->deinterlace_frame_ts + s->deinterlace_offset +
  131. ((uint64_t)s->deinterlace_half_duration * 2) -
  132. TWOX_TOLERANCE;
  133. if (sys_time < frame_end) {
  134. // Process new frames if we think time jumped.
  135. const uint64_t diff = frame_end - sys_time;
  136. if (diff < TS_JUMP_THRESHOLD) {
  137. return;
  138. }
  139. }
  140. }
  141. if (!s->async_frames.num)
  142. return;
  143. info = video_output_get_info(obs->video.video);
  144. half_interval = (uint64_t)info->fps_den * 500000000ULL /
  145. (uint64_t)info->fps_num;
  146. if (first_frame(s) || ready_deinterlace_frames(s, sys_time)) {
  147. uint64_t offset;
  148. s->prev_async_frame = NULL;
  149. s->cur_async_frame = s->async_frames.array[0];
  150. da_erase(s->async_frames, 0);
  151. if ((s->async_frames.num > 0) &&
  152. s->cur_async_frame->prev_frame) {
  153. s->prev_async_frame = s->cur_async_frame;
  154. s->cur_async_frame = s->async_frames.array[0];
  155. da_erase(s->async_frames, 0);
  156. s->deinterlace_half_duration =
  157. (uint32_t)((s->cur_async_frame->timestamp -
  158. s->prev_async_frame->timestamp) /
  159. 2);
  160. } else {
  161. s->deinterlace_half_duration =
  162. (uint32_t)((s->cur_async_frame->timestamp -
  163. s->deinterlace_frame_ts) /
  164. 2);
  165. }
  166. if (!s->last_frame_ts)
  167. s->last_frame_ts = s->cur_async_frame->timestamp;
  168. s->deinterlace_frame_ts = s->cur_async_frame->timestamp;
  169. offset = obs->video.video_time - s->deinterlace_frame_ts;
  170. if (!s->deinterlace_offset) {
  171. s->deinterlace_offset = offset;
  172. } else {
  173. uint64_t offset_diff =
  174. uint64_diff(s->deinterlace_offset, offset);
  175. if (offset_diff > half_interval)
  176. s->deinterlace_offset = offset;
  177. }
  178. }
  179. }
  180. void deinterlace_process_last_frame(obs_source_t *s, uint64_t sys_time)
  181. {
  182. if (s->prev_async_frame) {
  183. remove_async_frame(s, s->prev_async_frame);
  184. s->prev_async_frame = NULL;
  185. }
  186. if (s->cur_async_frame) {
  187. remove_async_frame(s, s->cur_async_frame);
  188. s->cur_async_frame = NULL;
  189. }
  190. deinterlace_get_closest_frames(s, sys_time);
  191. }
  192. void set_deinterlace_texture_size(obs_source_t *source)
  193. {
  194. if (source->async_gpu_conversion) {
  195. source->async_prev_texrender = gs_texrender_create(
  196. source->async_color_format, GS_ZS_NONE);
  197. for (int c = 0; c < source->async_channel_count; c++)
  198. source->async_prev_textures[c] = gs_texture_create(
  199. source->async_convert_width[c],
  200. source->async_convert_height[c],
  201. source->async_texture_formats[c], 1, NULL,
  202. GS_DYNAMIC);
  203. } else {
  204. enum gs_color_format format =
  205. convert_video_format(source->async_format);
  206. source->async_prev_textures[0] = gs_texture_create(
  207. source->async_width, source->async_height, format, 1,
  208. NULL, GS_DYNAMIC);
  209. }
  210. }
  211. static inline struct obs_source_frame *get_prev_frame(obs_source_t *source,
  212. bool *updated)
  213. {
  214. struct obs_source_frame *frame = NULL;
  215. pthread_mutex_lock(&source->async_mutex);
  216. *updated = source->cur_async_frame != NULL;
  217. frame = source->prev_async_frame;
  218. source->prev_async_frame = NULL;
  219. if (frame)
  220. os_atomic_inc_long(&frame->refs);
  221. pthread_mutex_unlock(&source->async_mutex);
  222. return frame;
  223. }
  224. void deinterlace_update_async_video(obs_source_t *source)
  225. {
  226. struct obs_source_frame *frame;
  227. bool updated;
  228. if (source->deinterlace_rendered)
  229. return;
  230. frame = get_prev_frame(source, &updated);
  231. source->deinterlace_rendered = true;
  232. if (frame)
  233. frame = filter_async_video(source, frame);
  234. if (frame) {
  235. if (set_async_texture_size(source, frame)) {
  236. update_async_textures(source, frame,
  237. source->async_prev_textures,
  238. source->async_prev_texrender);
  239. }
  240. obs_source_release_frame(source, frame);
  241. } else if (updated) { /* swap cur/prev if no previous texture */
  242. for (size_t c = 0; c < MAX_AV_PLANES; c++) {
  243. gs_texture_t *prev_tex = source->async_prev_textures[c];
  244. source->async_prev_textures[c] =
  245. source->async_textures[c];
  246. source->async_textures[c] = prev_tex;
  247. }
  248. if (source->async_texrender) {
  249. gs_texrender_t *prev = source->async_prev_texrender;
  250. source->async_prev_texrender = source->async_texrender;
  251. source->async_texrender = prev;
  252. }
  253. }
  254. }
  255. static inline gs_effect_t *get_effect(enum obs_deinterlace_mode mode)
  256. {
  257. switch (mode) {
  258. case OBS_DEINTERLACE_MODE_DISABLE:
  259. return NULL;
  260. case OBS_DEINTERLACE_MODE_DISCARD:
  261. return obs_load_effect(&obs->video.deinterlace_discard_effect,
  262. "deinterlace_discard.effect");
  263. case OBS_DEINTERLACE_MODE_RETRO:
  264. return obs_load_effect(
  265. &obs->video.deinterlace_discard_2x_effect,
  266. "deinterlace_discard_2x.effect");
  267. case OBS_DEINTERLACE_MODE_BLEND:
  268. return obs_load_effect(&obs->video.deinterlace_blend_effect,
  269. "deinterlace_blend.effect");
  270. case OBS_DEINTERLACE_MODE_BLEND_2X:
  271. return obs_load_effect(&obs->video.deinterlace_blend_2x_effect,
  272. "deinterlace_blend_2x.effect");
  273. case OBS_DEINTERLACE_MODE_LINEAR:
  274. return obs_load_effect(&obs->video.deinterlace_linear_effect,
  275. "deinterlace_linear.effect");
  276. case OBS_DEINTERLACE_MODE_LINEAR_2X:
  277. return obs_load_effect(&obs->video.deinterlace_linear_2x_effect,
  278. "deinterlace_linear_2x.effect");
  279. case OBS_DEINTERLACE_MODE_YADIF:
  280. return obs_load_effect(&obs->video.deinterlace_yadif_effect,
  281. "deinterlace_yadif.effect");
  282. case OBS_DEINTERLACE_MODE_YADIF_2X:
  283. return obs_load_effect(&obs->video.deinterlace_yadif_2x_effect,
  284. "deinterlace_yadif_2x.effect");
  285. }
  286. return NULL;
  287. }
  288. static bool deinterlace_linear_required(enum obs_deinterlace_mode mode)
  289. {
  290. switch (mode) {
  291. case OBS_DEINTERLACE_MODE_DISABLE:
  292. case OBS_DEINTERLACE_MODE_DISCARD:
  293. case OBS_DEINTERLACE_MODE_RETRO:
  294. return false;
  295. case OBS_DEINTERLACE_MODE_BLEND:
  296. case OBS_DEINTERLACE_MODE_BLEND_2X:
  297. case OBS_DEINTERLACE_MODE_LINEAR:
  298. case OBS_DEINTERLACE_MODE_LINEAR_2X:
  299. case OBS_DEINTERLACE_MODE_YADIF:
  300. case OBS_DEINTERLACE_MODE_YADIF_2X:
  301. return true;
  302. }
  303. return false;
  304. }
  305. void deinterlace_render(obs_source_t *s)
  306. {
  307. gs_effect_t *effect = s->deinterlace_effect;
  308. gs_eparam_t *image = gs_effect_get_param_by_name(effect, "image");
  309. gs_eparam_t *prev =
  310. gs_effect_get_param_by_name(effect, "previous_image");
  311. gs_eparam_t *multiplier_param =
  312. gs_effect_get_param_by_name(effect, "multiplier");
  313. gs_eparam_t *field = gs_effect_get_param_by_name(effect, "field_order");
  314. gs_eparam_t *frame2 = gs_effect_get_param_by_name(effect, "frame2");
  315. gs_eparam_t *dimensions =
  316. gs_effect_get_param_by_name(effect, "dimensions");
  317. struct vec2 size = {(float)s->async_width, (float)s->async_height};
  318. gs_texture_t *cur_tex =
  319. s->async_texrender
  320. ? gs_texrender_get_texture(s->async_texrender)
  321. : s->async_textures[0];
  322. gs_texture_t *prev_tex =
  323. s->async_prev_texrender
  324. ? gs_texrender_get_texture(s->async_prev_texrender)
  325. : s->async_prev_textures[0];
  326. if (!cur_tex || !prev_tex || !s->async_width || !s->async_height)
  327. return;
  328. const enum gs_color_space source_space =
  329. (s->async_color_format == GS_RGBA16F) ? GS_CS_709_EXTENDED
  330. : GS_CS_SRGB;
  331. const bool linear_srgb =
  332. (source_space != GS_CS_SRGB) || gs_get_linear_srgb() ||
  333. deinterlace_linear_required(s->deinterlace_mode);
  334. const enum gs_color_space current_space = gs_get_color_space();
  335. const char *tech_name = "Draw";
  336. float multiplier = 1.0;
  337. switch (source_space) {
  338. case GS_CS_SRGB:
  339. switch (current_space) {
  340. case GS_CS_709_SCRGB:
  341. tech_name = "DrawMultiply";
  342. multiplier = obs_get_video_sdr_white_level() / 80.0f;
  343. }
  344. break;
  345. case GS_CS_709_EXTENDED:
  346. switch (current_space) {
  347. case GS_CS_SRGB:
  348. tech_name = "DrawTonemap";
  349. break;
  350. case GS_CS_709_SCRGB:
  351. tech_name = "DrawMultiply";
  352. multiplier = obs_get_video_sdr_white_level() / 80.0f;
  353. }
  354. break;
  355. case GS_CS_709_SCRGB:
  356. switch (current_space) {
  357. case GS_CS_SRGB:
  358. tech_name = "DrawMultiplyTonemap";
  359. multiplier = 80.0f / obs_get_video_sdr_white_level();
  360. break;
  361. case GS_CS_709_EXTENDED:
  362. tech_name = "DrawMultiply";
  363. multiplier = 80.0f / obs_get_video_sdr_white_level();
  364. }
  365. }
  366. const bool previous = gs_framebuffer_srgb_enabled();
  367. gs_enable_framebuffer_srgb(linear_srgb);
  368. if (linear_srgb) {
  369. gs_effect_set_texture_srgb(image, cur_tex);
  370. gs_effect_set_texture_srgb(prev, prev_tex);
  371. } else {
  372. gs_effect_set_texture(image, cur_tex);
  373. gs_effect_set_texture(prev, prev_tex);
  374. }
  375. gs_effect_set_float(multiplier_param, multiplier);
  376. gs_effect_set_int(field, s->deinterlace_top_first);
  377. gs_effect_set_vec2(dimensions, &size);
  378. const uint64_t frame2_ts =
  379. s->deinterlace_frame_ts + s->deinterlace_offset +
  380. s->deinterlace_half_duration - TWOX_TOLERANCE;
  381. gs_effect_set_bool(frame2, obs->video.video_time >= frame2_ts);
  382. while (gs_effect_loop(effect, tech_name))
  383. gs_draw_sprite(NULL, s->async_flip ? GS_FLIP_V : 0,
  384. s->async_width, s->async_height);
  385. gs_enable_framebuffer_srgb(previous);
  386. }
  387. static void enable_deinterlacing(obs_source_t *source,
  388. enum obs_deinterlace_mode mode)
  389. {
  390. obs_enter_graphics();
  391. if (source->async_format != VIDEO_FORMAT_NONE &&
  392. source->async_width != 0 && source->async_height != 0)
  393. set_deinterlace_texture_size(source);
  394. source->deinterlace_mode = mode;
  395. source->deinterlace_effect = get_effect(mode);
  396. pthread_mutex_lock(&source->async_mutex);
  397. if (source->prev_async_frame) {
  398. remove_async_frame(source, source->prev_async_frame);
  399. source->prev_async_frame = NULL;
  400. }
  401. pthread_mutex_unlock(&source->async_mutex);
  402. obs_leave_graphics();
  403. }
  404. static void disable_deinterlacing(obs_source_t *source)
  405. {
  406. obs_enter_graphics();
  407. gs_texture_destroy(source->async_prev_textures[0]);
  408. gs_texture_destroy(source->async_prev_textures[1]);
  409. gs_texture_destroy(source->async_prev_textures[2]);
  410. gs_texrender_destroy(source->async_prev_texrender);
  411. source->deinterlace_mode = OBS_DEINTERLACE_MODE_DISABLE;
  412. source->async_prev_textures[0] = NULL;
  413. source->async_prev_textures[1] = NULL;
  414. source->async_prev_textures[2] = NULL;
  415. source->async_prev_texrender = NULL;
  416. obs_leave_graphics();
  417. }
  418. void obs_source_set_deinterlace_mode(obs_source_t *source,
  419. enum obs_deinterlace_mode mode)
  420. {
  421. if (!obs_source_valid(source, "obs_source_set_deinterlace_mode"))
  422. return;
  423. if (source->deinterlace_mode == mode)
  424. return;
  425. if (source->deinterlace_mode == OBS_DEINTERLACE_MODE_DISABLE) {
  426. enable_deinterlacing(source, mode);
  427. } else if (mode == OBS_DEINTERLACE_MODE_DISABLE) {
  428. disable_deinterlacing(source);
  429. } else {
  430. obs_enter_graphics();
  431. source->deinterlace_mode = mode;
  432. source->deinterlace_effect = get_effect(mode);
  433. obs_leave_graphics();
  434. }
  435. }
  436. enum obs_deinterlace_mode
  437. obs_source_get_deinterlace_mode(const obs_source_t *source)
  438. {
  439. return obs_source_valid(source, "obs_source_set_deinterlace_mode")
  440. ? source->deinterlace_mode
  441. : OBS_DEINTERLACE_MODE_DISABLE;
  442. }
  443. void obs_source_set_deinterlace_field_order(
  444. obs_source_t *source, enum obs_deinterlace_field_order field_order)
  445. {
  446. if (!obs_source_valid(source, "obs_source_set_deinterlace_field_order"))
  447. return;
  448. source->deinterlace_top_first = field_order ==
  449. OBS_DEINTERLACE_FIELD_ORDER_TOP;
  450. }
  451. enum obs_deinterlace_field_order
  452. obs_source_get_deinterlace_field_order(const obs_source_t *source)
  453. {
  454. if (!obs_source_valid(source, "obs_source_set_deinterlace_field_order"))
  455. return OBS_DEINTERLACE_FIELD_ORDER_TOP;
  456. return source->deinterlace_top_first
  457. ? OBS_DEINTERLACE_FIELD_ORDER_TOP
  458. : OBS_DEINTERLACE_FIELD_ORDER_BOTTOM;
  459. }