obs-video.c 15.0 KB
Newer Older
J
jp9000 已提交
1
/******************************************************************************
2
    Copyright (C) 2013-2014 by Hugh Bailey <obs.jim@gmail.com>
J
jp9000 已提交
3 4 5

    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
6
    the Free Software Foundation, either version 2 of the License, or
J
jp9000 已提交
7 8 9 10 11 12 13 14 15 16 17 18
    (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.h"
J
jp9000 已提交
19
#include "obs-internal.h"
J
jp9000 已提交
20
#include "graphics/vec4.h"
21
#include "media-io/format-conversion.h"
22
#include "media-io/video-frame.h"
J
jp9000 已提交
23

24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
static inline void calculate_base_volume(struct obs_core_data *data,
		struct obs_view *view, obs_source_t *target)
{
	if (!target->activate_refs) {
		target->base_volume = 0.0f;

	/* only walk the tree if there are transitions active */
	} else if (data->active_transitions) {
		float best_vol = 0.0f;

		for (size_t i = 0; i < MAX_CHANNELS; i++) {
			struct obs_source *source = view->channels[i];
			float vol = 0.0f;

			if (!source)
				continue;

			vol = obs_source_get_target_volume(source, target);
			if (best_vol < vol)
				best_vol = vol;
		}

		target->base_volume = best_vol;

	} else {
		target->base_volume = 1.0f;
	}
}

J
jp9000 已提交
53
static uint64_t tick_sources(uint64_t cur_time, uint64_t last_time)
J
jp9000 已提交
54
{
55
	struct obs_core_data *data = &obs->data;
56
	struct obs_view      *view = &data->main_view;
57 58 59
	struct obs_source    *source;
	uint64_t             delta_time;
	float                seconds;
J
jp9000 已提交
60 61

	if (!last_time)
62 63 64
		last_time = cur_time -
			video_output_get_frame_time(obs->video.video);

J
jp9000 已提交
65
	delta_time = cur_time - last_time;
J
jp9000 已提交
66 67
	seconds = (float)((double)delta_time / 1000000000.0);

68
	pthread_mutex_lock(&data->sources_mutex);
J
jp9000 已提交
69

70
	/* call the tick function of each source */
71 72 73 74 75 76 77
	source = data->first_source;
	while (source) {
		if (source->refs)
			obs_source_video_tick(source, seconds);
		source = (struct obs_source*)source->context.next;
	}

78 79 80 81 82 83 84 85 86 87 88 89
	/* calculate source volumes */
	pthread_mutex_lock(&view->channels_mutex);

	source = data->first_source;
	while (source) {
		if (source->refs)
			calculate_base_volume(data, view, source);
		source = (struct obs_source*)source->context.next;
	}

	pthread_mutex_unlock(&view->channels_mutex);

90 91 92
	pthread_mutex_unlock(&data->sources_mutex);

	return cur_time;
J
jp9000 已提交
93 94
}

95 96
/* in obs-display.c */
extern void render_display(struct obs_display *display);
J
jp9000 已提交
97

98 99
static inline void render_displays(void)
{
100 101
	struct obs_display *display;

102 103 104
	if (!obs->data.valid)
		return;

105
	gs_enter_context(obs->video.graphics);
106

107 108
	/* render extra displays/swaps */
	pthread_mutex_lock(&obs->data.displays_mutex);
109

110 111 112 113 114
	display = obs->data.first_display;
	while (display) {
		render_display(display);
		display = display->next;
	}
115

116
	pthread_mutex_unlock(&obs->data.displays_mutex);
117

118
	/* render main display */
119
	render_display(&obs->video.main_display);
120

121
	gs_leave_context();
122 123
}

124
static inline void set_render_size(uint32_t width, uint32_t height)
J
jp9000 已提交
125
{
126 127
	gs_enable_depth_test(false);
	gs_set_cull_mode(GS_NEITHER);
128

129
	gs_ortho(0.0f, (float)width, 0.0f, (float)height, -100.0f, 100.0f);
130
	gs_set_viewport(0, 0, width, height);
131 132
}

133
static inline void unmap_last_surface(struct obs_core_video *video)
134 135
{
	if (video->mapped_surface) {
136
		gs_stagesurface_unmap(video->mapped_surface);
137
		video->mapped_surface = NULL;
J
jp9000 已提交
138
	}
139
}
J
jp9000 已提交
140

141
static inline void render_main_texture(struct obs_core_video *video,
J
jp9000 已提交
142
		int cur_texture)
143 144
{
	struct vec4 clear_color;
J
jp9000 已提交
145
	vec4_set(&clear_color, 0.0f, 0.0f, 0.0f, 1.0f);
J
jp9000 已提交
146

147
	gs_set_render_target(video->render_textures[cur_texture], NULL);
148
	gs_clear(GS_CLEAR_COLOR, &clear_color, 1.0f, 0);
J
jp9000 已提交
149

150
	set_render_size(video->base_width, video->base_height);
J
jp9000 已提交
151
	obs_view_render(&obs->data.main_view);
J
jp9000 已提交
152

153 154 155
	video->textures_rendered[cur_texture] = true;
}

156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
static inline gs_effect_t *get_scale_effect_internal(
		struct obs_core_video *video)
{
	switch (video->scale_type) {
	case OBS_SCALE_BILINEAR: return video->default_effect;
	case OBS_SCALE_LANCZOS:  return video->lanczos_effect;
	case OBS_SCALE_BICUBIC:;
	}

	return video->bicubic_effect;
}

static inline bool resolution_close(struct obs_core_video *video,
		uint32_t width, uint32_t height)
{
	long width_cmp  = (long)video->base_width  - (long)width;
	long height_cmp = (long)video->base_height - (long)height;

	return labs(width_cmp) <= 16 && labs(height_cmp) <= 16;
}

static inline gs_effect_t *get_scale_effect(struct obs_core_video *video,
		uint32_t width, uint32_t height)
{
	if (resolution_close(video, width, height)) {
		return video->default_effect;
	} else {
		/* if the scale method couldn't be loaded, use either bicubic
		 * or bilinear by default */
		gs_effect_t *effect = get_scale_effect_internal(video);
		if (!effect)
			effect = !!video->bicubic_effect ?
				video->bicubic_effect :
				video->default_effect;
		return effect;
	}
}

194
static inline void render_output_texture(struct obs_core_video *video,
195 196
		int cur_texture, int prev_texture)
{
197 198
	gs_texture_t *texture = video->render_textures[prev_texture];
	gs_texture_t *target  = video->output_textures[cur_texture];
199 200
	uint32_t     width   = gs_texture_get_width(target);
	uint32_t     height  = gs_texture_get_height(target);
201 202 203 204 205
	struct vec2  base_i;

	vec2_set(&base_i,
		1.0f / (float)video->base_width,
		1.0f / (float)video->base_height);
206

207
	gs_effect_t    *effect  = get_scale_effect(video, width, height);
208 209 210
	gs_technique_t *tech    = gs_effect_get_technique(effect, "DrawMatrix");
	gs_eparam_t    *image   = gs_effect_get_param_by_name(effect, "image");
	gs_eparam_t    *matrix  = gs_effect_get_param_by_name(effect,
211
			"color_matrix");
212 213
	gs_eparam_t    *bres_i  = gs_effect_get_param_by_name(effect,
			"base_dimension_i");
214 215 216 217 218
	size_t      passes, i;

	if (!video->textures_rendered[prev_texture])
		return;

219
	gs_set_render_target(target, NULL);
220 221
	set_render_size(width, height);

222 223 224
	if (bres_i)
		gs_effect_set_vec2(bres_i, &base_i);

225
	gs_effect_set_val(matrix, video->color_matrix, sizeof(float) * 16);
226
	gs_effect_set_texture(image, texture);
227

228
	gs_enable_blending(false);
229
	passes = gs_technique_begin(tech);
230
	for (i = 0; i < passes; i++) {
231
		gs_technique_begin_pass(tech, i);
232
		gs_draw_sprite(texture, 0, width, height);
233
		gs_technique_end_pass(tech);
234
	}
235
	gs_technique_end(tech);
236
	gs_enable_blending(true);
237 238 239 240

	video->textures_output[cur_texture] = true;
}

241
static inline void set_eparam(gs_effect_t *effect, const char *name, float val)
J
jp9000 已提交
242
{
243
	gs_eparam_t *param = gs_effect_get_param_by_name(effect, name);
244
	gs_effect_set_float(param, val);
J
jp9000 已提交
245 246 247
}

static void render_convert_texture(struct obs_core_video *video,
248 249
		int cur_texture, int prev_texture)
{
250 251
	gs_texture_t *texture = video->output_textures[prev_texture];
	gs_texture_t *target  = video->convert_textures[cur_texture];
252 253 254 255
	float        fwidth  = (float)video->output_width;
	float        fheight = (float)video->output_height;
	size_t       passes, i;

256 257 258
	gs_effect_t    *effect  = video->conversion_effect;
	gs_eparam_t    *image   = gs_effect_get_param_by_name(effect, "image");
	gs_technique_t *tech    = gs_effect_get_technique(effect,
J
jp9000 已提交
259
			video->conversion_tech);
260 261 262 263

	if (!video->textures_output[prev_texture])
		return;

J
jp9000 已提交
264 265 266 267 268 269 270 271 272 273 274 275
	set_eparam(effect, "u_plane_offset", (float)video->plane_offsets[1]);
	set_eparam(effect, "v_plane_offset", (float)video->plane_offsets[2]);
	set_eparam(effect, "width",  fwidth);
	set_eparam(effect, "height", fheight);
	set_eparam(effect, "width_i",  1.0f / fwidth);
	set_eparam(effect, "height_i", 1.0f / fheight);
	set_eparam(effect, "width_d2",  fwidth  * 0.5f);
	set_eparam(effect, "height_d2", fheight * 0.5f);
	set_eparam(effect, "width_d2_i",  1.0f / (fwidth  * 0.5f));
	set_eparam(effect, "height_d2_i", 1.0f / (fheight * 0.5f));
	set_eparam(effect, "input_height", (float)video->conversion_height);

276
	gs_effect_set_texture(image, texture);
J
jp9000 已提交
277

278
	gs_set_render_target(target, NULL);
J
jp9000 已提交
279 280
	set_render_size(video->output_width, video->conversion_height);

281
	gs_enable_blending(false);
282
	passes = gs_technique_begin(tech);
J
jp9000 已提交
283
	for (i = 0; i < passes; i++) {
284
		gs_technique_begin_pass(tech, i);
J
jp9000 已提交
285 286
		gs_draw_sprite(texture, 0, video->output_width,
				video->conversion_height);
287
		gs_technique_end_pass(tech);
J
jp9000 已提交
288
	}
289
	gs_technique_end(tech);
290
	gs_enable_blending(true);
J
jp9000 已提交
291 292 293 294 295 296 297

	video->textures_converted[cur_texture] = true;
}

static inline void stage_output_texture(struct obs_core_video *video,
		int cur_texture, int prev_texture)
{
298
	gs_texture_t   *texture;
J
jp9000 已提交
299
	bool        texture_ready;
300
	gs_stagesurf_t *copy = video->copy_surfaces[cur_texture];
J
jp9000 已提交
301 302 303 304 305 306 307 308 309 310 311 312 313 314

	if (video->gpu_conversion) {
		texture = video->convert_textures[prev_texture];
		texture_ready = video->textures_converted[prev_texture];
	} else {
		texture = video->output_textures[prev_texture];
		texture_ready = video->output_textures[prev_texture];
	}

	unmap_last_surface(video);

	if (!texture_ready)
		return;

315 316 317 318 319
	gs_stage_texture(copy, texture);

	video->textures_copied[cur_texture] = true;
}

320
static inline void render_video(struct obs_core_video *video, int cur_texture,
321
		int prev_texture)
322
{
323
	gs_begin_scene();
324

325 326
	gs_enable_depth_test(false);
	gs_set_cull_mode(GS_NEITHER);
327

J
jp9000 已提交
328
	render_main_texture(video, cur_texture);
329
	render_output_texture(video, cur_texture, prev_texture);
J
jp9000 已提交
330 331 332
	if (video->gpu_conversion)
		render_convert_texture(video, cur_texture, prev_texture);

333 334
	stage_output_texture(video, cur_texture, prev_texture);

335
	gs_set_render_target(NULL, NULL);
J
jp9000 已提交
336
	gs_enable_blending(true);
337

338
	gs_end_scene();
339 340
}

J
jp9000 已提交
341
static inline bool download_frame(struct obs_core_video *video,
342
		int prev_texture, struct video_data *frame)
343
{
344
	gs_stagesurf_t *surface = video->copy_surfaces[prev_texture];
345 346

	if (!video->textures_copied[prev_texture])
347
		return false;
348

349
	if (!gs_stagesurface_map(surface, &frame->data[0], &frame->linesize[0]))
350 351 352 353 354
		return false;

	video->mapped_surface = surface;
	return true;
}
355

J
jp9000 已提交
356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393
static inline uint32_t calc_linesize(uint32_t pos, uint32_t linesize)
{
	uint32_t size = pos % linesize;
	return size ? size : linesize;
}

static void copy_dealign(
		uint8_t *dst, uint32_t dst_pos, uint32_t dst_linesize,
		const uint8_t *src, uint32_t src_pos, uint32_t src_linesize,
		uint32_t remaining)
{
	while (remaining) {
		uint32_t src_remainder = src_pos % src_linesize;
		uint32_t dst_offset = dst_linesize - src_remainder;
		uint32_t src_offset = src_linesize - src_remainder;

		if (remaining < dst_offset) {
			memcpy(dst + dst_pos, src + src_pos, remaining);
			src_pos += remaining;
			dst_pos += remaining;
			remaining = 0;
		} else {
			memcpy(dst + dst_pos, src + src_pos, dst_offset);
			src_pos += src_offset;
			dst_pos += dst_offset;
			remaining -= dst_offset;
		}
	}
}

static inline uint32_t make_aligned_linesize_offset(uint32_t offset,
		uint32_t dst_linesize, uint32_t src_linesize)
{
	uint32_t remainder = offset % dst_linesize;
	return (offset / dst_linesize) * src_linesize + remainder;
}

static void fix_gpu_converted_alignment(struct obs_core_video *video,
394
		struct video_frame *output, const struct video_data *input)
J
jp9000 已提交
395
{
396 397
	uint32_t src_linesize = input->linesize[0];
	uint32_t dst_linesize = output->linesize[0];
J
jp9000 已提交
398 399 400 401 402 403 404 405 406
	uint32_t src_pos      = 0;

	for (size_t i = 0; i < 3; i++) {
		if (video->plane_linewidth[i] == 0)
			break;

		src_pos = make_aligned_linesize_offset(video->plane_offsets[i],
				dst_linesize, src_linesize);

407 408
		copy_dealign(output->data[i], 0, dst_linesize,
				input->data[0], src_pos, src_linesize,
J
jp9000 已提交
409 410 411 412
				video->plane_sizes[i]);
	}
}

413 414 415
static void set_gpu_converted_data(struct obs_core_video *video,
		struct video_frame *output, const struct video_data *input,
		const struct video_output_info *info)
J
jp9000 已提交
416
{
417 418 419
	if (input->linesize[0] == video->output_width*4) {
		struct video_frame frame;

J
jp9000 已提交
420 421 422 423
		for (size_t i = 0; i < 3; i++) {
			if (video->plane_linewidth[i] == 0)
				break;

424 425 426
			frame.linesize[i] = video->plane_linewidth[i];
			frame.data[i] =
				input->data[0] + video->plane_offsets[i];
J
jp9000 已提交
427 428
		}

429 430
		video_frame_copy(output, &frame, info->format, info->height);

J
jp9000 已提交
431
	} else {
432
		fix_gpu_converted_alignment(video, output, input);
J
jp9000 已提交
433 434 435
	}
}

436 437 438
static void convert_frame(
		struct video_frame *output, const struct video_data *input,
		const struct video_output_info *info)
439 440 441
{
	if (info->format == VIDEO_FORMAT_I420) {
		compress_uyvx_to_i420(
442
				input->data[0], input->linesize[0],
443
				0, info->height,
444
				output->data, output->linesize);
445 446 447

	} else if (info->format == VIDEO_FORMAT_NV12) {
		compress_uyvx_to_nv12(
448
				input->data[0], input->linesize[0],
449
				0, info->height,
450
				output->data, output->linesize);
451 452

	} else {
J
jp9000 已提交
453
		blog(LOG_ERROR, "convert_frame: unsupported texture format");
454 455 456 457
	}
}

static inline void output_video_data(struct obs_core_video *video,
458
		struct video_data *input_frame, int count)
459 460
{
	const struct video_output_info *info;
461 462 463
	struct video_frame output_frame;
	bool locked;

464
	info = video_output_get_info(video->video);
465

466 467 468 469 470 471 472 473 474 475
	locked = video_output_lock_frame(video->video, &output_frame, count,
			input_frame->timestamp);
	if (locked) {
		if (video->gpu_conversion) {
			set_gpu_converted_data(video, &output_frame,
					input_frame, info);

		} else if (format_is_yuv(info->format)) {
			convert_frame(&output_frame, input_frame, info);
		}
J
jp9000 已提交
476

477
		video_output_unlock_frame(video->video);
J
jp9000 已提交
478
	}
479
}
480

481 482 483 484 485 486 487 488
static inline void video_sleep(struct obs_core_video *video,
		uint64_t *p_time, uint64_t interval_ns)
{
	struct obs_vframe_info vframe_info;
	uint64_t cur_time = *p_time;
	uint64_t t = cur_time + interval_ns;
	int count;

489
	if (os_sleepto_ns(t)) {
490 491 492 493 494 495 496 497 498 499 500
		*p_time = t;
		count = 1;
	} else {
		count = (int)((os_gettime_ns() - cur_time) / interval_ns);
		*p_time = cur_time + interval_ns * count;
	}

	vframe_info.timestamp = cur_time;
	vframe_info.count = count;
	circlebuf_push_back(&video->vframe_info_buffer, &vframe_info,
			sizeof(vframe_info));
501 502
}

503
static inline void output_frame(uint64_t *cur_time, uint64_t interval)
504
{
505
	struct obs_core_video *video = &obs->video;
506 507
	int cur_texture  = video->cur_texture;
	int prev_texture = cur_texture == 0 ? NUM_TEXTURES-1 : cur_texture-1;
508
	struct video_data frame;
509 510
	bool frame_ready;

511
	memset(&frame, 0, sizeof(struct video_data));
512

513
	gs_enter_context(video->graphics);
514
	render_video(video, cur_texture, prev_texture);
J
jp9000 已提交
515
	frame_ready = download_frame(video, prev_texture, &frame);
J
jp9000 已提交
516
	gs_flush();
517
	gs_leave_context();
518

519
	if (frame_ready) {
520 521 522
		struct obs_vframe_info vframe_info;
		circlebuf_pop_front(&video->vframe_info_buffer, &vframe_info,
				sizeof(vframe_info));
523

524 525
		frame.timestamp = vframe_info.timestamp;
		output_video_data(video, &frame, vframe_info.count);
526
	}
J
jp9000 已提交
527

528 529
	if (++video->cur_texture == NUM_TEXTURES)
		video->cur_texture = 0;
530 531

	video_sleep(video, cur_time, interval);
J
jp9000 已提交
532 533 534 535 536
}

void *obs_video_thread(void *param)
{
	uint64_t last_time = 0;
537 538
	uint64_t cur_time = os_gettime_ns();
	uint64_t interval = video_output_get_frame_time(obs->video.video);
J
jp9000 已提交
539

J
jp9000 已提交
540 541
	os_set_thread_name("libobs: graphics thread");

542
	while (!video_output_stopped(obs->video.video)) {
J
jp9000 已提交
543
		last_time = tick_sources(cur_time, last_time);
544

545
		render_displays();
546

547
		output_frame(&cur_time, interval);
J
jp9000 已提交
548 549
	}

J
jp9000 已提交
550
	UNUSED_PARAMETER(param);
J
jp9000 已提交
551 552
	return NULL;
}