obs-source-deinterlace.c 15 KB

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