| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445 | 
							- /******************************************************************************
 
-     Copyright (C) 2016 by Hugh Bailey <[email protected]>
 
-     This program is free software: you can redistribute it and/or modify
 
-     it under the terms of the GNU General Public License as published by
 
-     the Free Software Foundation, either version 2 of the License, or
 
-     (at your option) any later version.
 
-     This program is distributed in the hope that it will be useful,
 
-     but WITHOUT ANY WARRANTY; without even the implied warranty of
 
-     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
-     GNU General Public License for more details.
 
-     You should have received a copy of the GNU General Public License
 
-     along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
- ******************************************************************************/
 
- #include "obs-internal.h"
 
- static bool ready_deinterlace_frames(obs_source_t *source, uint64_t sys_time)
 
- {
 
- 	struct obs_source_frame *next_frame = source->async_frames.array[0];
 
- 	struct obs_source_frame *prev_frame = NULL;
 
- 	struct obs_source_frame *frame      = NULL;
 
- 	uint64_t sys_offset = sys_time - source->last_sys_timestamp;
 
- 	uint64_t frame_time = next_frame->timestamp;
 
- 	uint64_t frame_offset = 0;
 
- 	size_t idx = 1;
 
- 	if (source->async_unbuffered) {
 
- 		while (source->async_frames.num > 2) {
 
- 			da_erase(source->async_frames, 0);
 
- 			remove_async_frame(source, next_frame);
 
- 			next_frame = source->async_frames.array[0];
 
- 		}
 
- 		if (source->async_frames.num == 2)
 
- 			source->async_frames.array[0]->prev_frame = true;
 
- 		source->deinterlace_offset = 0;
 
- 		source->last_frame_ts = next_frame->timestamp;
 
- 		return true;
 
- 	}
 
- 	/* account for timestamp invalidation */
 
- 	if (frame_out_of_bounds(source, frame_time)) {
 
- 		source->last_frame_ts = next_frame->timestamp;
 
- 		source->deinterlace_offset = 0;
 
- 		return true;
 
- 	} else {
 
- 		frame_offset = frame_time - source->last_frame_ts;
 
- 		source->last_frame_ts += sys_offset;
 
- 	}
 
- 	while (source->last_frame_ts > next_frame->timestamp) {
 
- 		/* this tries to reduce the needless frame duplication, also
 
- 		 * helps smooth out async rendering to frame boundaries.  In
 
- 		 * other words, tries to keep the framerate as smooth as
 
- 		 * possible */
 
- 		if ((source->last_frame_ts - next_frame->timestamp) < 2000000)
 
- 			break;
 
- 		if (prev_frame) {
 
- 			da_erase(source->async_frames, 0);
 
- 			remove_async_frame(source, prev_frame);
 
- 		}
 
- 		if (source->async_frames.num <= 2) {
 
- 			bool exit = true;
 
- 			if (prev_frame) {
 
- 				prev_frame->prev_frame = true;
 
- 			} else if (!frame && source->async_frames.num == 2) {
 
- 				exit = false;
 
- 			}
 
- 			if (exit) {
 
- 				source->deinterlace_offset = 0;
 
- 				return true;
 
- 			}
 
- 		}
 
- 		if (frame)
 
- 			idx = 2;
 
- 		else
 
- 			idx = 1;
 
- 		prev_frame = frame;
 
- 		frame = next_frame;
 
- 		next_frame = source->async_frames.array[idx];
 
- 		/* more timestamp checking and compensating */
 
- 		if ((next_frame->timestamp - frame_time) > MAX_TS_VAR) {
 
- 			source->last_frame_ts =
 
- 				next_frame->timestamp - frame_offset;
 
- 			source->deinterlace_offset = 0;
 
- 		}
 
- 		frame_time   = next_frame->timestamp;
 
- 		frame_offset = frame_time - source->last_frame_ts;
 
- 	}
 
- 	if (prev_frame)
 
- 		prev_frame->prev_frame = true;
 
- 	return frame != NULL;
 
- }
 
- static inline bool first_frame(obs_source_t *s)
 
- {
 
- 	if (s->last_frame_ts)
 
- 		return false;
 
- 	if (s->async_frames.num >= 2)
 
- 		s->async_frames.array[0]->prev_frame = true;
 
- 	return true;
 
- }
 
- static inline uint64_t uint64_diff(uint64_t ts1, uint64_t ts2)
 
- {
 
- 	return (ts1 < ts2) ?  (ts2 - ts1) : (ts1 - ts2);
 
- }
 
- static inline void deinterlace_get_closest_frames(obs_source_t *s,
 
- 		uint64_t sys_time)
 
- {
 
- 	const struct video_output_info *info;
 
- 	uint64_t half_interval;
 
- 	if (!s->async_frames.num)
 
- 		return;
 
- 	info = video_output_get_info(obs->video.video);
 
- 	half_interval = (uint64_t)info->fps_den * 500000000ULL /
 
- 		(uint64_t)info->fps_num;
 
- 	if (first_frame(s) || ready_deinterlace_frames(s, sys_time)) {
 
- 		uint64_t offset;
 
- 		s->prev_async_frame = NULL;
 
- 		s->cur_async_frame = s->async_frames.array[0];
 
- 		da_erase(s->async_frames, 0);
 
- 		if (s->cur_async_frame->prev_frame) {
 
- 			s->prev_async_frame = s->cur_async_frame;
 
- 			s->cur_async_frame = s->async_frames.array[0];
 
- 			da_erase(s->async_frames, 0);
 
- 			s->deinterlace_half_duration = (uint32_t)
 
- 				((s->cur_async_frame->timestamp -
 
- 				  s->prev_async_frame->timestamp) / 2);
 
- 		} else {
 
- 			s->deinterlace_half_duration = (uint32_t)
 
- 				((s->cur_async_frame->timestamp -
 
- 				  s->deinterlace_frame_ts) / 2);
 
- 		}
 
- 		if (!s->last_frame_ts)
 
- 			s->last_frame_ts = s->cur_async_frame->timestamp;
 
- 		s->deinterlace_frame_ts = s->cur_async_frame->timestamp;
 
- 		offset = obs->video.video_time - s->deinterlace_frame_ts;
 
- 		if (!s->deinterlace_offset) {
 
- 			s->deinterlace_offset = offset;
 
- 		} else {
 
- 			uint64_t offset_diff = uint64_diff(
 
- 					s->deinterlace_offset, offset);
 
- 			if (offset_diff > half_interval)
 
- 				s->deinterlace_offset = offset;
 
- 		}
 
- 	}
 
- }
 
- void deinterlace_process_last_frame(obs_source_t *s, uint64_t sys_time)
 
- {
 
- 	if (s->prev_async_frame) {
 
- 		remove_async_frame(s, s->prev_async_frame);
 
- 		s->prev_async_frame = NULL;
 
- 	}
 
- 	if (s->cur_async_frame) {
 
- 		remove_async_frame(s, s->cur_async_frame);
 
- 		s->cur_async_frame = NULL;
 
- 	}
 
- 	deinterlace_get_closest_frames(s, sys_time);
 
- }
 
- void set_deinterlace_texture_size(obs_source_t *source)
 
- {
 
- 	if (source->async_gpu_conversion) {
 
- 		source->async_prev_texrender =
 
- 			gs_texrender_create(GS_BGRX, GS_ZS_NONE);
 
- 		source->async_prev_texture = gs_texture_create(
 
- 				source->async_convert_width,
 
- 				source->async_convert_height,
 
- 				source->async_texture_format,
 
- 				1, NULL, GS_DYNAMIC);
 
- 	} else {
 
- 		enum gs_color_format format = convert_video_format(
 
- 				source->async_format);
 
- 		source->async_prev_texture = gs_texture_create(
 
- 				source->async_width, source->async_height,
 
- 				format, 1, NULL, GS_DYNAMIC);
 
- 	}
 
- }
 
- static inline struct obs_source_frame *get_prev_frame(obs_source_t *source,
 
- 		bool *updated)
 
- {
 
- 	struct obs_source_frame *frame = NULL;
 
- 	pthread_mutex_lock(&source->async_mutex);
 
- 	*updated = source->cur_async_frame != NULL;
 
- 	frame = source->prev_async_frame;
 
- 	source->prev_async_frame = NULL;
 
- 	if (frame)
 
- 		os_atomic_inc_long(&frame->refs);
 
- 	pthread_mutex_unlock(&source->async_mutex);
 
- 	return frame;
 
- }
 
- void deinterlace_update_async_video(obs_source_t *source)
 
- {
 
- 	struct obs_source_frame *frame;
 
- 	bool updated;
 
- 	if (source->deinterlace_rendered)
 
- 		return;
 
- 	frame = get_prev_frame(source, &updated);
 
- 	source->deinterlace_rendered = true;
 
- 	if (frame)
 
- 		frame = filter_async_video(source, frame);
 
- 	if (frame) {
 
- 		if (set_async_texture_size(source, frame)) {
 
- 			update_async_texture(source, frame,
 
- 					source->async_prev_texture,
 
- 					source->async_prev_texrender);
 
- 		}
 
- 		obs_source_release_frame(source, frame);
 
- 	} else if (updated) { /* swap cur/prev if no previous texture */
 
- 		gs_texture_t *prev_tex = source->async_prev_texture;
 
- 		source->async_prev_texture = source->async_texture;
 
- 		source->async_texture = prev_tex;
 
- 		if (source->async_texrender) {
 
- 			gs_texrender_t *prev = source->async_prev_texrender;
 
- 			source->async_prev_texrender = source->async_texrender;
 
- 			source->async_texrender = prev;
 
- 		}
 
- 	}
 
- }
 
- static inline gs_effect_t *get_effect(enum obs_deinterlace_mode mode)
 
- {
 
- 	switch (mode) {
 
- 	case OBS_DEINTERLACE_MODE_DISABLE: return NULL;
 
- 	case OBS_DEINTERLACE_MODE_DISCARD:
 
- 		return obs_load_effect(&obs->video.deinterlace_discard_effect,
 
- 				"deinterlace_discard.effect");
 
- 	case OBS_DEINTERLACE_MODE_RETRO:
 
- 		return obs_load_effect(&obs->video.deinterlace_discard_2x_effect,
 
- 				"deinterlace_discard_2x.effect");
 
- 	case OBS_DEINTERLACE_MODE_BLEND:
 
- 		return obs_load_effect(&obs->video.deinterlace_blend_effect,
 
- 				"deinterlace_blend.effect");
 
- 	case OBS_DEINTERLACE_MODE_BLEND_2X:
 
- 		return obs_load_effect(&obs->video.deinterlace_blend_2x_effect,
 
- 				"deinterlace_blend_2x.effect");
 
- 	case OBS_DEINTERLACE_MODE_LINEAR:
 
- 		return obs_load_effect(&obs->video.deinterlace_linear_effect,
 
- 				"deinterlace_linear.effect");
 
- 	case OBS_DEINTERLACE_MODE_LINEAR_2X:
 
- 		return obs_load_effect(&obs->video.deinterlace_linear_2x_effect,
 
- 				"deinterlace_linear_2x.effect");
 
- 	case OBS_DEINTERLACE_MODE_YADIF:
 
- 		return obs_load_effect(&obs->video.deinterlace_yadif_effect,
 
- 				"deinterlace_yadif.effect");
 
- 	case OBS_DEINTERLACE_MODE_YADIF_2X:
 
- 		return obs_load_effect(&obs->video.deinterlace_yadif_2x_effect,
 
- 				"deinterlace_yadif_2x.effect");
 
- 	}
 
- 	return NULL;
 
- }
 
- #define TWOX_TOLERANCE 1000000
 
- void deinterlace_render(obs_source_t *s)
 
- {
 
- 	gs_effect_t *effect = s->deinterlace_effect;
 
- 	uint64_t frame2_ts;
 
- 	gs_eparam_t *image = gs_effect_get_param_by_name(effect, "image");
 
- 	gs_eparam_t *prev = gs_effect_get_param_by_name(effect,
 
- 			"previous_image");
 
- 	gs_eparam_t *field = gs_effect_get_param_by_name(effect, "field_order");
 
- 	gs_eparam_t *frame2 = gs_effect_get_param_by_name(effect, "frame2");
 
- 	gs_eparam_t *dimensions = gs_effect_get_param_by_name(effect,
 
- 			"dimensions");
 
- 	struct vec2 size = {(float)s->async_width, (float)s->async_height};
 
- 	bool yuv = format_is_yuv(s->async_format);
 
- 	bool limited_range = yuv && !s->async_full_range;
 
- 	const char *tech = yuv ? "DrawMatrix" : "Draw";
 
- 	gs_texture_t *cur_tex = s->async_texrender ?
 
- 		gs_texrender_get_texture(s->async_texrender) :
 
- 		s->async_texture;
 
- 	gs_texture_t *prev_tex = s->async_prev_texrender ?
 
- 		gs_texrender_get_texture(s->async_prev_texrender) :
 
- 		s->async_prev_texture;
 
- 	if (!cur_tex || !prev_tex || !s->async_width || !s->async_height)
 
- 		return;
 
- 	gs_effect_set_texture(image, cur_tex);
 
- 	gs_effect_set_texture(prev, prev_tex);
 
- 	gs_effect_set_int(field, s->deinterlace_top_first);
 
- 	gs_effect_set_vec2(dimensions, &size);
 
- 	if (yuv) {
 
- 		gs_eparam_t *color_matrix = gs_effect_get_param_by_name(
 
- 				effect, "color_matrix");
 
- 		gs_effect_set_val(color_matrix, s->async_color_matrix,
 
- 				sizeof(float) * 16);
 
- 	}
 
- 	if (limited_range) {
 
- 		const size_t size = sizeof(float) * 3;
 
- 		gs_eparam_t *color_range_min = gs_effect_get_param_by_name(
 
- 				effect, "color_range_min");
 
- 		gs_eparam_t *color_range_max = gs_effect_get_param_by_name(
 
- 				effect, "color_range_max");
 
- 		gs_effect_set_val(color_range_min, s->async_color_range_min,
 
- 				size);
 
- 		gs_effect_set_val(color_range_max, s->async_color_range_max,
 
- 				size);
 
- 	}
 
- 	frame2_ts = s->deinterlace_frame_ts + s->deinterlace_offset +
 
- 		s->deinterlace_half_duration - TWOX_TOLERANCE;
 
- 	gs_effect_set_bool(frame2, obs->video.video_time >= frame2_ts);
 
- 	while (gs_effect_loop(effect, tech))
 
- 		gs_draw_sprite(NULL, s->async_flip ? GS_FLIP_V : 0,
 
- 				s->async_width, s->async_height);
 
- }
 
- static void enable_deinterlacing(obs_source_t *source,
 
- 		enum obs_deinterlace_mode mode)
 
- {
 
- 	obs_enter_graphics();
 
- 	if (source->async_format != VIDEO_FORMAT_NONE &&
 
- 	    source->async_width != 0 &&
 
- 	    source->async_height != 0)
 
- 		set_deinterlace_texture_size(source);
 
- 	source->deinterlace_mode = mode;
 
- 	source->deinterlace_effect = get_effect(mode);
 
- 	pthread_mutex_lock(&source->async_mutex);
 
- 	if (source->prev_async_frame) {
 
- 		remove_async_frame(source, source->prev_async_frame);
 
- 		source->prev_async_frame = NULL;
 
- 	}
 
- 	pthread_mutex_unlock(&source->async_mutex);
 
- 	obs_leave_graphics();
 
- }
 
- static void disable_deinterlacing(obs_source_t *source)
 
- {
 
- 	obs_enter_graphics();
 
- 	gs_texture_destroy(source->async_prev_texture);
 
- 	gs_texrender_destroy(source->async_prev_texrender);
 
- 	source->deinterlace_mode = OBS_DEINTERLACE_MODE_DISABLE;
 
- 	source->async_prev_texture = NULL;
 
- 	source->async_prev_texrender = NULL;
 
- 	obs_leave_graphics();
 
- }
 
- void obs_source_set_deinterlace_mode(obs_source_t *source,
 
- 		enum obs_deinterlace_mode mode)
 
- {
 
- 	if (!obs_source_valid(source, "obs_source_set_deinterlace_mode"))
 
- 		return;
 
- 	if (source->deinterlace_mode == mode)
 
- 		return;
 
- 	if (source->deinterlace_mode == OBS_DEINTERLACE_MODE_DISABLE) {
 
- 		enable_deinterlacing(source, mode);
 
- 	} else if (mode == OBS_DEINTERLACE_MODE_DISABLE) {
 
- 		disable_deinterlacing(source);
 
- 	} else {
 
- 		obs_enter_graphics();
 
- 		source->deinterlace_mode = mode;
 
- 		source->deinterlace_effect = get_effect(mode);
 
- 		obs_leave_graphics();
 
- 	}
 
- }
 
- enum obs_deinterlace_mode obs_source_get_deinterlace_mode(
 
- 		const obs_source_t *source)
 
- {
 
- 	return obs_source_valid(source, "obs_source_set_deinterlace_mode") ?
 
- 		source->deinterlace_mode : OBS_DEINTERLACE_MODE_DISABLE;
 
- }
 
- void obs_source_set_deinterlace_field_order(obs_source_t *source,
 
- 		enum obs_deinterlace_field_order field_order)
 
- {
 
- 	if (!obs_source_valid(source, "obs_source_set_deinterlace_field_order"))
 
- 		return;
 
- 	source->deinterlace_top_first =
 
- 		field_order == OBS_DEINTERLACE_FIELD_ORDER_TOP;
 
- }
 
- enum obs_deinterlace_field_order obs_source_get_deinterlace_field_order(
 
- 		const obs_source_t *source)
 
- {
 
- 	if (!obs_source_valid(source, "obs_source_set_deinterlace_field_order"))
 
- 		return OBS_DEINTERLACE_FIELD_ORDER_TOP;
 
- 	return source->deinterlace_top_first
 
- 		? OBS_DEINTERLACE_FIELD_ORDER_TOP
 
- 		: OBS_DEINTERLACE_FIELD_ORDER_BOTTOM;
 
- }
 
 
  |