obs-source-deinterlace.c 13 KB

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