Пример #1
0
static void image_source_update(void *data, obs_data_t *settings)
{
    struct image_source *context = data;
    const char *file = obs_data_get_string(settings, "file");
    const bool unload = obs_data_get_bool(settings, "unload");

    if (context->file)
        bfree(context->file);
    context->file = bstrdup(file);
    context->persistent = !unload;

    /* Load the image if the source is persistent or showing */
    if (context->persistent || obs_source_showing(context->source))
        image_source_load(data);
    else
        image_source_unload(data);
}
Пример #2
0
static void scene_load_item(struct obs_scene *scene, obs_data_t *item_data)
{
	const char            *name = obs_data_get_string(item_data, "name");
	obs_source_t          *source = obs_get_source_by_name(name);
	struct obs_scene_item *item;
	bool visible;

	if (!source) {
		blog(LOG_WARNING, "[scene_load_item] Source %s not found!",
				name);
		return;
	}

	item = obs_scene_add(scene, source);
	if (!item) {
		blog(LOG_WARNING, "[scene_load_item] Could not add source '%s' "
		                  "to scene '%s'!",
		                  name, obs_source_get_name(scene->source));
		
		obs_source_release(source);
		return;
	}

	obs_data_set_default_int(item_data, "align",
			OBS_ALIGN_TOP | OBS_ALIGN_LEFT);

	item->rot     = (float)obs_data_get_double(item_data, "rot");
	item->align   = (uint32_t)obs_data_get_int(item_data, "align");
	visible = obs_data_get_bool(item_data, "visible");
	obs_data_get_vec2(item_data, "pos",    &item->pos);
	obs_data_get_vec2(item_data, "scale",  &item->scale);

	set_visibility(item, visible);

	item->bounds_type =
		(enum obs_bounds_type)obs_data_get_int(item_data,
				"bounds_type");
	item->bounds_align =
		(uint32_t)obs_data_get_int(item_data, "bounds_align");
	obs_data_get_vec2(item_data, "bounds", &item->bounds);

	obs_source_release(source);

	update_item_transform(item);
}
Пример #3
0
static bool is_local_file_modified(obs_properties_t *props,
		obs_property_t *prop, obs_data_t *settings)
{
	UNUSED_PARAMETER(prop);

	bool enabled = obs_data_get_bool(settings, "is_local_file");
	obs_property_t *input = obs_properties_get(props, "input");
	obs_property_t *input_format =obs_properties_get(props,
			"input_format");
	obs_property_t *local_file = obs_properties_get(props, "local_file");
	obs_property_t *looping = obs_properties_get(props, "looping");
	obs_property_set_visible(input, !enabled);
	obs_property_set_visible(input_format, !enabled);
	obs_property_set_visible(local_file, enabled);
	obs_property_set_visible(looping, enabled);

	return true;
}
Пример #4
0
static bool relative_clicked(obs_properties_t *props, obs_property_t *p,
		obs_data_t *settings)
{
	bool relative = obs_data_get_bool(settings, "relative");

	obs_property_set_description(obs_properties_get(props, "left"),
			relative ? obs_module_text("Crop.Left") : "X");
	obs_property_set_description(obs_properties_get(props, "top"),
			relative ? obs_module_text("Crop.Top") : "Y");

	obs_property_set_visible(obs_properties_get(props, "right"), relative);
	obs_property_set_visible(obs_properties_get(props, "bottom"), relative);
	obs_property_set_visible(obs_properties_get(props, "cx"), !relative);
	obs_property_set_visible(obs_properties_get(props, "cy"), !relative);

	UNUSED_PARAMETER(p);
	return true;
}
Пример #5
0
static bool is_advanced_modified(obs_properties_t *props,
		obs_property_t *prop, obs_data_t *settings)
{
	UNUSED_PARAMETER(prop);

	bool enabled = obs_data_get_bool(settings, "advanced");
	obs_property_t *fscale = obs_properties_get(props, "force_scale");
	obs_property_t *abuf = obs_properties_get(props, "audio_buffer_size");
	obs_property_t *vbuf = obs_properties_get(props, "video_buffer_size");
	obs_property_t *frame_drop = obs_properties_get(props, "frame_drop");
	obs_property_t *color_range = obs_properties_get(props, "color_range");
	obs_property_set_visible(fscale, enabled);
	obs_property_set_visible(abuf, enabled);
	obs_property_set_visible(vbuf, enabled);
	obs_property_set_visible(frame_drop, enabled);
	obs_property_set_visible(color_range, enabled);

	return true;
}
static inline void update_settings(struct duplicator_capture *capture,
		obs_data_t *settings)
{
	capture->monitor        = (int)obs_data_get_int(settings, "monitor");
	capture->capture_cursor = obs_data_get_bool(settings, "capture_cursor");

	obs_enter_graphics();

	gs_duplicator_destroy(capture->duplicator);
	capture->duplicator = gs_duplicator_create(capture->monitor);
	capture->width = 0;
	capture->height = 0;
	capture->x = 0;
	capture->y = 0;
	capture->rot = 0;
	capture->reset_timeout = 0.0f;

	obs_leave_graphics();
}
Пример #7
0
static void decklink_update(void *data, obs_data_t *settings)
{
	DeckLink *decklink = (DeckLink *)data;
	const char *hash = obs_data_get_string(settings, DEVICE_HASH);
	long long id = obs_data_get_int(settings, MODE_ID);
	BMDPixelFormat pixelFormat = (BMDPixelFormat)obs_data_get_int(settings,
			PIXEL_FORMAT);
	speaker_layout channelFormat = (speaker_layout)obs_data_get_int(settings,
			CHANNEL_FORMAT);

	decklink_enable_buffering(decklink,
			obs_data_get_bool(settings, BUFFERING));

	ComPtr<DeckLinkDevice> device;
	device.Set(deviceEnum->FindByHash(hash));

	decklink->SetPixelFormat(pixelFormat);
	decklink->SetChannelFormat(channelFormat);
	decklink->Activate(device, id);
}
Пример #8
0
/**
 * Update the settings for the v4l2 source
 *
 * Since there are very few settings that can be changed without restarting the
 * stream we don't bother to even try. Whenever this is called the currently
 * active stream (if exists) is stopped, the settings are updated and finally
 * the new stream is started.
 */
static void v4l2_update(void *vptr, obs_data_t *settings)
{
	V4L2_DATA(vptr);

	v4l2_terminate(data);

	if (data->device_id)
		bfree(data->device_id);

	data->device_id  = bstrdup(obs_data_get_string(settings, "device_id"));
	data->input      = obs_data_get_int(settings, "input");
	data->pixfmt     = obs_data_get_int(settings, "pixelformat");
	data->standard   = obs_data_get_int(settings, "standard");
	data->dv_timing  = obs_data_get_int(settings, "dv_timing");
	data->resolution = obs_data_get_int(settings, "resolution");
	data->framerate  = obs_data_get_int(settings, "framerate");
	data->sys_timing = obs_data_get_bool(settings, "system_timing");

	v4l2_init(data);
}
Пример #9
0
/**
 * Update the capture with changed settings
 */
static void xshm_update(void *vptr, obs_data_t settings)
{
	XSHM_DATA(vptr);

	data->show_cursor = obs_data_get_bool(settings, "show_cursor");

	if (data->xshm)
		xshm_detach(data->xshm);

	if (xshm_update_geometry(data, settings) < 0) {
		blog(LOG_ERROR, "xshm-input: failed to update geometry !");
		return;
	}

	xshm_resize_texture(data);
	xcursor_offset(data->cursor, data->x_org, data->y_org);

	data->xshm = xshm_attach(data->dpy, data->screen,
		data->width, data->height);
	if (!data->xshm) {
		blog(LOG_ERROR, "xshm-input: failed to attach shm !");
		return;
	}
}
Пример #10
0
static void ffmpeg_source_update(void *data, obs_data_t *settings)
{
	struct ffmpeg_source *s = data;

	bool is_local_file = obs_data_get_bool(settings, "is_local_file");
	bool is_advanced = obs_data_get_bool(settings, "advanced");

	char *input;
	char *input_format;

	bfree(s->input);
	bfree(s->input_format);

	if (is_local_file) {
		input = (char *)obs_data_get_string(settings, "local_file");
		input_format = NULL;
		s->is_looping = obs_data_get_bool(settings, "looping");
	} else {
		input = (char *)obs_data_get_string(settings, "input");
		input_format = (char *)obs_data_get_string(settings,
				"input_format");
		s->is_looping = false;
	}

	s->input = input ? bstrdup(input) : NULL;
	s->input_format = input_format ? bstrdup(input_format) : NULL;
	s->is_advanced = is_advanced;
	s->is_hw_decoding = obs_data_get_bool(settings, "hw_decode");
	s->is_clear_on_media_end = obs_data_get_bool(settings,
			"clear_on_media_end");
	s->restart_on_activate = obs_data_get_bool(settings,
			"restart_on_activate");
	s->is_forcing_scale = true;

	if (is_advanced) {
		s->audio_buffer_size = (int)obs_data_get_int(settings,
				"audio_buffer_size");
		s->video_buffer_size = (int)obs_data_get_int(settings,
				"video_buffer_size");
		s->frame_drop = (enum AVDiscard)obs_data_get_int(settings,
					"frame_drop");
		s->is_forcing_scale = obs_data_get_bool(settings,
				"force_scale");

		if (s->audio_buffer_size < 1) {
			s->audio_buffer_size = 1;
			FF_BLOG(LOG_WARNING, "invalid audio_buffer_size %d",
					s->audio_buffer_size);
		}
		if (s->video_buffer_size < 1) {
			s->video_buffer_size = 1;
			FF_BLOG(LOG_WARNING, "invalid audio_buffer_size %d",
					s->audio_buffer_size);
		}

		if (s->frame_drop < AVDISCARD_NONE ||
		    s->frame_drop > AVDISCARD_ALL) {
			s->frame_drop = AVDISCARD_DEFAULT;
			FF_BLOG(LOG_WARNING, "invalid frame_drop %d",
					s->frame_drop);
		}
	}

	dump_source_info(s, input, input_format, is_advanced);
	if (!s->restart_on_activate || obs_source_active(s->source))
		ffmpeg_source_start(s);
}
Пример #11
0
static void update_params(struct obs_qsv *obsqsv, obs_data_t *settings)
{
	video_t *video = obs_encoder_video(obsqsv->encoder);
	const struct video_output_info *voi = video_output_get_info(video);

	const char *target_usage = obs_data_get_string(settings, "target_usage");
	const char *profile = obs_data_get_string(settings, "profile");
	const char *rate_control = obs_data_get_string(settings, "rate_control");
	int async_depth = (int)obs_data_get_int(settings, "async_depth");
	int target_bitrate = (int)obs_data_get_int(settings, "bitrate");
	int max_bitrate = (int)obs_data_get_int(settings, "max_bitrate");
	int accuracy = (int)obs_data_get_int(settings, "accuracy");
	int convergence = (int)obs_data_get_int(settings, "convergence");
	int qpi = (int)obs_data_get_int(settings, "qpi");
	int qpp = (int)obs_data_get_int(settings, "qpp");
	int qpb = (int)obs_data_get_int(settings, "qpb");
	int icq_quality = (int)obs_data_get_int(settings, "icq_quality");
	int la_depth = (int)obs_data_get_int(settings, "la_depth");
	int keyint_sec = (int)obs_data_get_int(settings, "keyint_sec");
	bool cbr_override = obs_data_get_bool(settings, "cbr");
	int bFrames = 7;

	int width = (int)obs_encoder_get_width(obsqsv->encoder);
	int height = (int)obs_encoder_get_height(obsqsv->encoder);
	if (astrcmpi(target_usage, "quality") == 0)
		obsqsv->params.nTargetUsage = MFX_TARGETUSAGE_BEST_QUALITY;
	else if (astrcmpi(target_usage, "balanced") == 0)
		obsqsv->params.nTargetUsage = MFX_TARGETUSAGE_BALANCED;
	else if (astrcmpi(target_usage, "speed") == 0)
		obsqsv->params.nTargetUsage = MFX_TARGETUSAGE_BEST_SPEED;

	if (astrcmpi(profile, "baseline") == 0)
		obsqsv->params.nCodecProfile = MFX_PROFILE_AVC_BASELINE;
	else if (astrcmpi(profile, "main") == 0)
		obsqsv->params.nCodecProfile = MFX_PROFILE_AVC_MAIN;
	else if (astrcmpi(profile, "high") == 0)
		obsqsv->params.nCodecProfile = MFX_PROFILE_AVC_HIGH;

	/* internal convenience parameter, overrides rate control param
	 * XXX: Deprecated */
	if (cbr_override) {
		warn("\"cbr\" setting has been deprecated for all encoders!  "
		     "Please set \"rate_control\" to \"CBR\" instead.  "
		     "Forcing CBR mode.  "
		     "(Note to all: this is why you shouldn't use strings for "
		     "common settings)");
		rate_control = "CBR";
	}

	if (astrcmpi(rate_control, "CBR") == 0)
		obsqsv->params.nRateControl = MFX_RATECONTROL_CBR;
	else if (astrcmpi(rate_control, "VBR") == 0)
		obsqsv->params.nRateControl = MFX_RATECONTROL_VBR;
	else if (astrcmpi(rate_control, "VCM") == 0)
		obsqsv->params.nRateControl = MFX_RATECONTROL_VCM;
	else if (astrcmpi(rate_control, "CQP") == 0)
		obsqsv->params.nRateControl = MFX_RATECONTROL_CQP;
	else if (astrcmpi(rate_control, "AVBR") == 0)
		obsqsv->params.nRateControl = MFX_RATECONTROL_AVBR;
	else if (astrcmpi(rate_control, "ICQ") == 0)
		obsqsv->params.nRateControl = MFX_RATECONTROL_ICQ;
	else if (astrcmpi(rate_control, "LA_ICQ") == 0)
		obsqsv->params.nRateControl = MFX_RATECONTROL_LA_ICQ;
	else if (astrcmpi(rate_control, "LA") == 0)
		obsqsv->params.nRateControl = MFX_RATECONTROL_LA;

	obsqsv->params.nAsyncDepth = (mfxU16)async_depth;
	obsqsv->params.nAccuracy = (mfxU16)accuracy;
	obsqsv->params.nConvergence = (mfxU16)convergence;
	obsqsv->params.nQPI = (mfxU16)qpi;
	obsqsv->params.nQPP = (mfxU16)qpp;
	obsqsv->params.nQPB = (mfxU16)qpb;
	obsqsv->params.nLADEPTH = (mfxU16)la_depth;
	obsqsv->params.nTargetBitRate = (mfxU16)target_bitrate;
	obsqsv->params.nMaxBitRate = (mfxU16)max_bitrate;
	obsqsv->params.nWidth = (mfxU16)width;
	obsqsv->params.nHeight = (mfxU16)height;
	obsqsv->params.nFpsNum = (mfxU16)voi->fps_num;
	obsqsv->params.nFpsDen = (mfxU16)voi->fps_den;
	obsqsv->params.nbFrames = (mfxU16)bFrames;
	obsqsv->params.nKeyIntSec = (mfxU16)keyint_sec;
	obsqsv->params.nICQQuality = (mfxU16)icq_quality;

	info("settings:\n\trate_control:   %s", rate_control);

	if (obsqsv->params.nRateControl != MFX_RATECONTROL_LA_ICQ &&
	    obsqsv->params.nRateControl != MFX_RATECONTROL_ICQ    &&
	    obsqsv->params.nRateControl != MFX_RATECONTROL_CQP)
		blog(LOG_INFO,
			"\ttarget_bitrate: %d",
			(int)obsqsv->params.nTargetBitRate);

	if (obsqsv->params.nRateControl == MFX_RATECONTROL_VBR ||
	    obsqsv->params.nRateControl == MFX_RATECONTROL_VCM)
		blog(LOG_INFO,
			"\tmax_bitrate:    %d",
			(int)obsqsv->params.nMaxBitRate);

	if (obsqsv->params.nRateControl == MFX_RATECONTROL_LA_ICQ ||
	    obsqsv->params.nRateControl == MFX_RATECONTROL_ICQ)
		blog(LOG_INFO,
			"\tICQ Quality:    %d",
			(int)obsqsv->params.nICQQuality);

	if (obsqsv->params.nRateControl == MFX_RATECONTROL_LA_ICQ ||
	    obsqsv->params.nRateControl == MFX_RATECONTROL_LA)
		blog(LOG_INFO,
			"\tLookahead Depth:%d",
			(int)obsqsv->params.nLADEPTH);

	if (obsqsv->params.nRateControl == MFX_RATECONTROL_CQP)
		blog(LOG_INFO,
			"\tqpi:            %d\n"
			"\tqpb:            %d\n"
			"\tqpp:            %d",
			qpi, qpb, qpp);

	blog(LOG_INFO,
		"\tfps_num:        %d\n"
		"\tfps_den:        %d\n"
		"\twidth:          %d\n"
		"\theight:         %d",
		voi->fps_num, voi->fps_den,
		width, height);

	info("debug info:");
}
Пример #12
0
void XCompcapMain::updateSettings(obs_data_t *settings)
{
	PLock lock(&p->lock);
	XErrorLock xlock;
	ObsGsContextHolder obsctx;

	blog(LOG_DEBUG, "Settings updating");

	Window prevWin = p->win;

	xcc_cleanup(p);

	if (settings) {
		const char *windowName = obs_data_get_string(settings,
				"capture_window");

		p->windowName = windowName;
		p->win = getWindowFromString(windowName);

		p->cut_top = obs_data_get_int(settings, "cut_top");
		p->cut_left = obs_data_get_int(settings, "cut_left");
		p->cut_right = obs_data_get_int(settings, "cut_right");
		p->cut_bot = obs_data_get_int(settings, "cut_bot");
		p->lockX = obs_data_get_bool(settings, "lock_x");
		p->swapRedBlue = obs_data_get_bool(settings, "swap_redblue");
		p->show_cursor = obs_data_get_bool(settings, "show_cursor");
		p->include_border = obs_data_get_bool(settings, "include_border");
		p->exclude_alpha = obs_data_get_bool(settings, "exclude_alpha");
	} else {
		p->win = prevWin;
	}

	xlock.resetError();

	if (p->win)
		XCompositeRedirectWindow(xdisp, p->win,
				CompositeRedirectAutomatic);

	if (xlock.gotError()) {
		blog(LOG_ERROR, "XCompositeRedirectWindow failed: %s",
				xlock.getErrorText().c_str());
		return;
	}

	if (p->win)
		XSelectInput(xdisp, p->win, StructureNotifyMask | ExposureMask);
	XSync(xdisp, 0);

	XWindowAttributes attr;
	if (!p->win || !XGetWindowAttributes(xdisp, p->win, &attr)) {
		p->win = 0;
		p->width = 0;
		p->height = 0;
		return;
	}

	if (p->win && p->cursor && p->show_cursor) {
		Window child;
		int x, y;

		XTranslateCoordinates(xdisp, p->win, attr.root, 0, 0, &x, &y,
				&child);
		xcursor_offset(p->cursor, x, y);
	}

	gs_color_format cf = GS_RGBA;

	if (p->exclude_alpha) {
		cf = GS_BGRX;
	}

	p->border = attr.border_width;

	if (p->include_border) {
		p->width = attr.width + p->border * 2;
		p->height = attr.height + p->border * 2;
	} else {
		p->width = attr.width;
		p->height = attr.height;
	}

	if (p->cut_top + p->cut_bot < (int)p->height) {
		p->cur_cut_top = p->cut_top;
		p->cur_cut_bot = p->cut_bot;
	} else {
		p->cur_cut_top = 0;
		p->cur_cut_bot = 0;
	}

	if (p->cut_left + p->cut_right < (int)p->width) {
		p->cur_cut_left = p->cut_left;
		p->cur_cut_right = p->cut_right;
	} else {
		p->cur_cut_left = 0;
		p->cur_cut_right = 0;
	}

	if (p->tex)
		gs_texture_destroy(p->tex);

	uint8_t *texData = new uint8_t[width() * height() * 4];

	memset(texData, 0, width() * height() * 4);

	const uint8_t* texDataArr[] = { texData, 0 };

	p->tex = gs_texture_create(width(), height(), cf, 1,
			texDataArr, 0);

	delete[] texData;

	if (p->swapRedBlue) {
		GLuint tex = *(GLuint*)gs_texture_get_obj(p->tex);
		glBindTexture(GL_TEXTURE_2D, tex);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE);
		glBindTexture(GL_TEXTURE_2D, 0);
	}

	const int attrs[] =
	{
		GLX_BIND_TO_TEXTURE_RGBA_EXT, GL_TRUE,
		GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT,
		GLX_BIND_TO_TEXTURE_TARGETS_EXT, GLX_TEXTURE_2D_BIT_EXT,
		GLX_DOUBLEBUFFER, GL_FALSE,
		None
	};

	int nelem = 0;
	GLXFBConfig* configs = glXChooseFBConfig(xdisp,
			XCompcap::getRootWindowScreen(attr.root),
			attrs, &nelem);

	if (nelem <= 0) {
		blog(LOG_ERROR, "no matching fb config found");
		p->win = 0;
		p->height = 0;
		p->width = 0;
		return;
	}

	glXGetFBConfigAttrib(xdisp, configs[0], GLX_Y_INVERTED_EXT, &nelem);
	p->inverted = nelem != 0;

	xlock.resetError();

	p->pixmap = XCompositeNameWindowPixmap(xdisp, p->win);

	if (xlock.gotError()) {
		blog(LOG_ERROR, "XCompositeNameWindowPixmap failed: %s",
				xlock.getErrorText().c_str());
		p->pixmap = 0;
		XFree(configs);
		return;
	}

	const int attribs[] =
	{
		GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT,
		GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGBA_EXT,
		None
	};

	p->glxpixmap = glXCreatePixmap(xdisp, configs[0], p->pixmap, attribs);

	if (xlock.gotError()) {
		blog(LOG_ERROR, "glXCreatePixmap failed: %s",
				xlock.getErrorText().c_str());
		XFreePixmap(xdisp, p->pixmap);
		XFree(configs);
		p->pixmap = 0;
		p->glxpixmap = 0;
		return;
	}

	XFree(configs);

	p->gltex = gs_texture_create(p->width, p->height, cf, 1, 0,
			GS_GL_DUMMYTEX);

	GLuint gltex = *(GLuint*)gs_texture_get_obj(p->gltex);
	glBindTexture(GL_TEXTURE_2D, gltex);
	glXBindTexImageEXT(xdisp, p->glxpixmap, GLX_FRONT_LEFT_EXT, NULL);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}
Пример #13
0
/** Update source flags depending on the settings */
static void v4l2_update_source_flags(struct v4l2_data *data,
		obs_data_t *settings)
{
	obs_source_set_async_unbuffered(data->source,
			!obs_data_get_bool(settings, "buffering"));
}
Пример #14
0
/**
 * The x server was changed
 */
static bool xshm_server_changed(obs_properties_t *props,
		obs_property_t *p, obs_data_t *settings)
{
	UNUSED_PARAMETER(p);

	bool advanced           = obs_data_get_bool(settings, "advanced");
	int_fast32_t old_screen = obs_data_get_int(settings, "screen");
	const char *server      = obs_data_get_string(settings, "server");
	obs_property_t *screens = obs_properties_get(props, "screen");

	/* we want a real NULL here in case there is no string here */
	server = (advanced && *server) ? server : NULL;

	obs_property_list_clear(screens);

	xcb_connection_t *xcb = xcb_connect(server, NULL);
	if (!xcb || xcb_connection_has_error(xcb)) {
		obs_property_set_enabled(screens, false);
		return true;
	}

	struct dstr screen_info;
	dstr_init(&screen_info);
	bool xinerama = xinerama_is_active(xcb);
	int_fast32_t count = (xinerama) ?
			xinerama_screen_count(xcb) :
			xcb_setup_roots_length(xcb_get_setup(xcb));

	for (int_fast32_t i = 0; i < count; ++i) {
		int_fast32_t x, y, w, h;
		x = y = w = h = 0;

		if (xinerama)
			xinerama_screen_geo(xcb, i, &x, &y, &w, &h);
		else
			x11_screen_geo(xcb, i, &w, &h);

		dstr_printf(&screen_info, "Screen %"PRIuFAST32" (%"PRIuFAST32
				"x%"PRIuFAST32" @ %"PRIuFAST32
				",%"PRIuFAST32")", i, w, h, x, y);

		obs_property_list_add_int(screens, screen_info.array, i);
	}

	/* handle missing screen */
	if (old_screen + 1 > count) {
		dstr_printf(&screen_info, "Screen %"PRIuFAST32" (not found)",
				old_screen);
		size_t index = obs_property_list_add_int(screens,
				screen_info.array, old_screen);
		obs_property_list_item_disable(screens, index, true);

	}

	dstr_free(&screen_info);

	xcb_disconnect(xcb);
	obs_property_set_enabled(screens, true);

	return true;
}
Пример #15
0
static void scene_load_item(struct obs_scene *scene, obs_data_t *item_data)
{
	const char            *name = obs_data_get_string(item_data, "name");
	obs_source_t          *source = obs_get_source_by_name(name);
	const char            *scale_filter_str;
	struct obs_scene_item *item;
	bool visible;

	if (!source) {
		blog(LOG_WARNING, "[scene_load_item] Source %s not found!",
				name);
		return;
	}

	item = obs_scene_add(scene, source);
	if (!item) {
		blog(LOG_WARNING, "[scene_load_item] Could not add source '%s' "
		                  "to scene '%s'!",
		                  name, obs_source_get_name(scene->source));
		
		obs_source_release(source);
		return;
	}

	obs_data_set_default_int(item_data, "align",
			OBS_ALIGN_TOP | OBS_ALIGN_LEFT);

	item->rot     = (float)obs_data_get_double(item_data, "rot");
	item->align   = (uint32_t)obs_data_get_int(item_data, "align");
	visible = obs_data_get_bool(item_data, "visible");
	obs_data_get_vec2(item_data, "pos",    &item->pos);
	obs_data_get_vec2(item_data, "scale",  &item->scale);

	set_visibility(item, visible);

	item->bounds_type =
		(enum obs_bounds_type)obs_data_get_int(item_data,
				"bounds_type");
	item->bounds_align =
		(uint32_t)obs_data_get_int(item_data, "bounds_align");
	obs_data_get_vec2(item_data, "bounds", &item->bounds);

	item->crop.left   = (uint32_t)obs_data_get_int(item_data, "crop_left");
	item->crop.top    = (uint32_t)obs_data_get_int(item_data, "crop_top");
	item->crop.right  = (uint32_t)obs_data_get_int(item_data, "crop_right");
	item->crop.bottom = (uint32_t)obs_data_get_int(item_data, "crop_bottom");

	scale_filter_str = obs_data_get_string(item_data, "scale_filter");
	item->scale_filter = OBS_SCALE_DISABLE;

	if (scale_filter_str) {
		if (astrcmpi(scale_filter_str, "point") == 0)
			item->scale_filter = OBS_SCALE_POINT;
		else if (astrcmpi(scale_filter_str, "bilinear") == 0)
			item->scale_filter = OBS_SCALE_BILINEAR;
		else if (astrcmpi(scale_filter_str, "bicubic") == 0)
			item->scale_filter = OBS_SCALE_BICUBIC;
		else if (astrcmpi(scale_filter_str, "lanczos") == 0)
			item->scale_filter = OBS_SCALE_LANCZOS;
	}

	if (item->item_render && !item_texture_enabled(item)) {
		obs_enter_graphics();
		gs_texrender_destroy(item->item_render);
		item->item_render = NULL;
		obs_leave_graphics();

	} else if (!item->item_render && item_texture_enabled(item)) {
		obs_enter_graphics();
		item->item_render = gs_texrender_create(GS_RGBA, GS_ZS_NONE);
		obs_leave_graphics();
	}

	obs_source_release(source);

	update_item_transform(item);
}
Пример #16
0
static void *libfdk_create(obs_data_t settings, obs_encoder_t encoder)
{
	bool hasFdkHandle = false;
	libfdk_encoder_t *enc = 0;
	int bitrate = (int)obs_data_get_int(settings, "bitrate") * 1000;
	int afterburner = obs_data_get_bool(settings, "afterburner") ? 1 : 0;
	audio_t audio = obs_encoder_audio(encoder);
	int mode = 0;
	AACENC_ERROR err;

	if (!bitrate) {
		blog(LOG_ERROR, "Invalid bitrate");
		return NULL;
	}

	enc = bzalloc(sizeof(libfdk_encoder_t));
	enc->encoder = encoder;

	enc->channels = (int)audio_output_get_channels(audio);
	enc->sample_rate = audio_output_get_sample_rate(audio);

	switch(enc->channels) {
	case 1:
		mode = MODE_1;
		break;
	case 2:
		mode = MODE_2;
		break;
	case 3:
		mode = MODE_1_2;
		break;
	case 4:
		mode = MODE_1_2_1;
		break;
	case 5:
		mode = MODE_1_2_2;
		break;
	case 6:
		mode = MODE_1_2_2_1;
		break;
	default:
		blog(LOG_ERROR, "Invalid channel count");
		goto fail;
	}

	CHECK_LIBFDK(aacEncOpen(&enc->fdkhandle, 0, enc->channels));
	hasFdkHandle = true;

	CHECK_LIBFDK(aacEncoder_SetParam(enc->fdkhandle, AACENC_AOT,
	                                 2)); // MPEG-4 AAC-LC
	CHECK_LIBFDK(aacEncoder_SetParam(enc->fdkhandle, AACENC_SAMPLERATE,
	                                 enc->sample_rate));
	CHECK_LIBFDK(aacEncoder_SetParam(enc->fdkhandle, AACENC_CHANNELMODE, mode));
	CHECK_LIBFDK(aacEncoder_SetParam(enc->fdkhandle, AACENC_CHANNELORDER, 1));
	CHECK_LIBFDK(aacEncoder_SetParam(enc->fdkhandle, AACENC_BITRATEMODE, 0));
	CHECK_LIBFDK(aacEncoder_SetParam(enc->fdkhandle, AACENC_BITRATE, bitrate));
	CHECK_LIBFDK(aacEncoder_SetParam(enc->fdkhandle, AACENC_TRANSMUX, 0));
	CHECK_LIBFDK(aacEncoder_SetParam(enc->fdkhandle, AACENC_AFTERBURNER,
	                                 afterburner));

	CHECK_LIBFDK(aacEncEncode(enc->fdkhandle, NULL, NULL, NULL, NULL));

	CHECK_LIBFDK(aacEncInfo(enc->fdkhandle, &enc->info));

	enc->frame_size_bytes = enc->info.frameLength * 2 * enc->channels;

	enc->packet_buffer_size = enc->channels * 768;
	if(enc->packet_buffer_size < 8192)
		enc->packet_buffer_size = 8192;

	enc->packet_buffer = bmalloc(enc->packet_buffer_size);

	blog(LOG_INFO, "libfdk_aac encoder created");

	blog(LOG_INFO, "libfdk_aac bitrate: %d, channels: %d",
			bitrate / 1000, enc->channels);

	return enc;

fail:

	if(hasFdkHandle)
		aacEncClose(&enc->fdkhandle);

	if(enc->packet_buffer)
		bfree(enc->packet_buffer);

	if(enc)
		bfree(enc);

	blog(LOG_WARNING, "libfdk_aac encoder creation failed");

	return 0;
}
Пример #17
0
static inline void load_modifier(uint32_t *modifiers, obs_data_t *data,
		const char *name, uint32_t flag)
{
	if (obs_data_get_bool(data, name))
		*modifiers |= flag;
}
Пример #18
0
static void ffmpeg_source_update(void *data, obs_data_t *settings)
{
	struct ffmpeg_source *s = data;

	bool is_local_file = obs_data_get_bool(settings, "is_local_file");
	bool is_advanced = obs_data_get_bool(settings, "advanced");

	bool is_looping;
	char *input;
	char *input_format;

	if (is_local_file) {
		input = (char *)obs_data_get_string(settings, "local_file");
		input_format = NULL;
		is_looping = obs_data_get_bool(settings, "looping");
	} else {
		input = (char *)obs_data_get_string(settings, "input");
		input_format = (char *)obs_data_get_string(settings,
				"input_format");
		is_looping = false;
	}

	s->is_forcing_scale = obs_data_get_bool(settings, "force_scale");
	s->is_hw_decoding = obs_data_get_bool(settings, "hw_decode");
	s->is_clear_on_media_end = obs_data_get_bool(settings,
			"clear_on_media_end");

	if (s->demuxer != NULL)
		ff_demuxer_free(s->demuxer);

	s->demuxer = ff_demuxer_init();
	s->demuxer->options.is_hw_decoding = s->is_hw_decoding;
	s->demuxer->options.is_looping = is_looping;

	if (is_advanced) {
		int audio_buffer_size = (int)obs_data_get_int(settings,
				"audio_buffer_size");
		int video_buffer_size = (int)obs_data_get_int(settings,
				"video_buffer_size");
		enum AVDiscard frame_drop =
				(enum AVDiscard)obs_data_get_int(settings,
					"frame_drop");

		if (audio_buffer_size < 1) {
			audio_buffer_size = 1;
			FF_BLOG(LOG_WARNING, "invalid audio_buffer_size %d",
					audio_buffer_size);
		}
		if (video_buffer_size < 1) {
			video_buffer_size = 1;
			FF_BLOG(LOG_WARNING, "invalid audio_buffer_size %d",
					audio_buffer_size);
		}
		s->demuxer->options.audio_frame_queue_size = audio_buffer_size;
		s->demuxer->options.video_frame_queue_size = video_buffer_size;

		if (frame_drop < AVDISCARD_NONE || frame_drop > AVDISCARD_ALL) {
			frame_drop = AVDISCARD_NONE;
			FF_BLOG(LOG_WARNING, "invalid frame_drop %d",
					frame_drop);
		}
		s->demuxer->options.frame_drop = frame_drop;
	}

	ff_demuxer_set_callbacks(&s->demuxer->video_callbacks,
			video_frame, NULL,
			NULL, NULL, NULL, s);

	ff_demuxer_set_callbacks(&s->demuxer->audio_callbacks,
			audio_frame, NULL,
			NULL, NULL, NULL, s);

	dump_source_info(s, input, input_format, is_advanced);

	ff_demuxer_open(s->demuxer, input, input_format);
}
Пример #19
0
void OBSProjector::UpdateMultiview()
{
	multiviewScenes.clear();
	multiviewLabels.clear();

	struct obs_video_info ovi;
	obs_get_video_info(&ovi);

	uint32_t w  = ovi.base_width;
	uint32_t h  = ovi.base_height;
	fw        = float(w);
	fh        = float(h);
	ratio     = fw / fh;

	struct obs_frontend_source_list scenes = {};
	obs_frontend_get_scenes(&scenes);

	multiviewLabels.emplace_back(CreateLabel(Str("StudioMode.Preview"),
			h / 2));
	multiviewLabels.emplace_back(CreateLabel(Str("StudioMode.Program"),
			h / 2));

	multiviewLayout = static_cast<MultiviewLayout>(config_get_int(
			GetGlobalConfig(), "BasicWindow", "MultiviewLayout"));

	drawLabel = config_get_bool(GetGlobalConfig(),
			"BasicWindow", "MultiviewDrawNames");

	drawSafeArea = config_get_bool(GetGlobalConfig(), "BasicWindow",
			"MultiviewDrawAreas");

	mouseSwitching = config_get_bool(GetGlobalConfig(), "BasicWindow",
			"MultiviewMouseSwitch");

	transitionOnDoubleClick = config_get_bool(GetGlobalConfig(),
			"BasicWindow", "TransitionOnDoubleClick");

	switch(multiviewLayout) {
	case MultiviewLayout::HORIZONTAL_TOP_24_SCENES:
		pvwprgCX   = fw / 3;
		pvwprgCY   = fh / 3;

		maxSrcs = 24;
		break;
	default:
		pvwprgCX   = fw / 2;
		pvwprgCY   = fh / 2;

		maxSrcs = 8;
	}

	ppiCX     = pvwprgCX - thicknessx2;
	ppiCY     = pvwprgCY - thicknessx2;
	ppiScaleX = (pvwprgCX - thicknessx2) / fw;
	ppiScaleY = (pvwprgCY - thicknessx2) / fh;

	scenesCX = pvwprgCX / 2;
	scenesCY = pvwprgCY / 2;
	siCX      = scenesCX - thicknessx2;
	siCY      = scenesCY - thicknessx2;
	siScaleX  = (scenesCX - thicknessx2) / fw;
	siScaleY  = (scenesCY - thicknessx2) / fh;

	numSrcs = 0;
	size_t i = 0;
	while (i < scenes.sources.num && numSrcs < maxSrcs) {
		obs_source_t *src = scenes.sources.array[i++];
		OBSData data = obs_source_get_private_settings(src);
		obs_data_release(data);

		obs_data_set_default_bool(data, "show_in_multiview", true);
		if (!obs_data_get_bool(data, "show_in_multiview"))
			continue;

		// We have a displayable source.
		numSrcs++;

		multiviewScenes.emplace_back(OBSGetWeakRef(src));
		obs_source_inc_showing(src);

		std::string name = std::to_string(numSrcs) + " - " +
				obs_source_get_name(src);
		multiviewLabels.emplace_back(CreateLabel(name.c_str(), h / 3));
	}

	obs_frontend_source_list_free(&scenes);
}
Пример #20
0
static void ft2_source_update(void *data, obs_data_t settings)
{
	struct ft2_source *srcdata = data;
	obs_data_t font_obj = obs_data_get_obj(settings, "font");
	bool vbuf_needs_update = false;
	bool word_wrap = false;
	uint32_t color[2];
	uint32_t custom_width = 0;

	const char *font_name  = obs_data_get_string(font_obj, "face");
	const char *font_style = obs_data_get_string(font_obj, "style");
	uint16_t   font_size   = (uint16_t)obs_data_get_int(font_obj, "size");
	uint32_t   font_flags  = (uint32_t)obs_data_get_int(font_obj, "flags");

	if (!font_obj)
		return;

	srcdata->drop_shadow = obs_data_get_bool(settings, "drop_shadow");
	srcdata->outline_text = obs_data_get_bool(settings, "outline");
	word_wrap = obs_data_get_bool(settings, "word_wrap");

	color[0] = (uint32_t)obs_data_get_int(settings, "color1");
	color[1] = (uint32_t)obs_data_get_int(settings, "color2");

	custom_width = (uint32_t)obs_data_get_int(settings, "custom_width");
	if (custom_width >= 100) {
		if (custom_width != srcdata->custom_width) {
			srcdata->custom_width = custom_width;
			vbuf_needs_update = true;
		}
	}
	else {
		if (srcdata->custom_width >= 100)
			vbuf_needs_update = true;
		srcdata->custom_width = 0;
	}

	if (word_wrap != srcdata->word_wrap) {
		srcdata->word_wrap = word_wrap;
		vbuf_needs_update = true;
	}

	if (color[0] != srcdata->color[0] || color[1] != srcdata->color[1]) {
		srcdata->color[0] = color[0];
		srcdata->color[1] = color[1];
		vbuf_needs_update = true;
	}

	bool from_file = obs_data_get_bool(settings, "from_file");
	bool chat_log_mode = obs_data_get_bool(settings, "log_mode");

	srcdata->log_mode = chat_log_mode;

	if (ft2_lib == NULL) goto error;

	if (srcdata->draw_effect == NULL) {
		char *effect_file = NULL;
		char *error_string = NULL;

		effect_file =
			obs_module_file("text_default.effect");

		if (effect_file) {
			obs_enter_graphics();
			srcdata->draw_effect = gs_effect_create_from_file(
				effect_file, &error_string);
			obs_leave_graphics();

			bfree(effect_file);
			if (error_string != NULL)
				bfree(error_string);
		}
	}

	if (srcdata->font_size != font_size ||
	    srcdata->from_file != from_file)
		vbuf_needs_update = true;

	srcdata->file_load_failed = false;
	srcdata->from_file = from_file;

	if (srcdata->font_name != NULL) {
		if (strcmp(font_name,  srcdata->font_name)  == 0 &&
		    strcmp(font_style, srcdata->font_style) == 0 &&
		    font_flags == srcdata->font_flags &&
		    font_size  == srcdata->font_size)
			goto skip_font_load;

		bfree(srcdata->font_name);
		bfree(srcdata->font_style);
		srcdata->font_name = NULL;
		srcdata->font_style = NULL;
		srcdata->max_h = 0;
	}

	srcdata->font_name  = bstrdup(font_name);
	srcdata->font_style = bstrdup(font_style);
	srcdata->font_size  = font_size;
	srcdata->font_flags = font_flags;

	if (!init_font(srcdata) || srcdata->font_face == NULL) {
		blog(LOG_WARNING, "FT2-text: Failed to load font %s",
			srcdata->font_name);
		goto error;
	}
	else {
		FT_Set_Pixel_Sizes(srcdata->font_face, 0, srcdata->font_size); 
		FT_Select_Charmap(srcdata->font_face, FT_ENCODING_UNICODE);
	}

	if (srcdata->texbuf != NULL) {
		bfree(srcdata->texbuf);
		srcdata->texbuf = NULL;
	}
	srcdata->texbuf = bzalloc(texbuf_w * texbuf_h * 4);

	if (srcdata->font_face)
		cache_standard_glyphs(srcdata);
skip_font_load:;
	if (from_file) {
		const char *tmp = obs_data_get_string(settings, "text_file");
		if (!tmp || !*tmp) {
			blog(LOG_WARNING,
				"FT2-text: Failed to open %s for reading", tmp);
			goto error;
		}

		if (srcdata->text_file != NULL &&
		    strcmp(srcdata->text_file, tmp) == 0 &&
		    !vbuf_needs_update)
			goto error;

		bfree(srcdata->text_file);

		srcdata->text_file = bstrdup(tmp);
		if (chat_log_mode)
			read_from_end(srcdata, tmp);
		else
			load_text_from_file(srcdata, tmp);
		srcdata->last_checked = os_gettime_ns();
	}
	else {
		const char *tmp = obs_data_get_string(settings, "text");
		if (!tmp || !*tmp) goto error;

		if (srcdata->text != NULL) {
			bfree(srcdata->text);
			srcdata->text = NULL;
		}

		os_utf8_to_wcs_ptr(tmp, strlen(tmp), &srcdata->text);
	}

	if (srcdata->font_face) {
		cache_glyphs(srcdata, srcdata->text);
		set_up_vertex_buffer(srcdata);
	}

error:
	obs_data_release(font_obj);
}
Пример #21
0
static void update_params(struct obs_x264 *obsx264, obs_data_t *settings,
		char **params)
{
	video_t *video = obs_encoder_video(obsx264->encoder);
	const struct video_output_info *voi = video_output_get_info(video);
	struct video_scale_info info;

	info.format = voi->format;
	info.colorspace = voi->colorspace;
	info.range = voi->range;

	obs_x264_video_info(obsx264, &info);

	int bitrate      = (int)obs_data_get_int(settings, "bitrate");
	int buffer_size  = (int)obs_data_get_int(settings, "buffer_size");
	int keyint_sec   = (int)obs_data_get_int(settings, "keyint_sec");
	int crf          = (int)obs_data_get_int(settings, "crf");
	int width        = (int)obs_encoder_get_width(obsx264->encoder);
	int height       = (int)obs_encoder_get_height(obsx264->encoder);
	bool use_bufsize = obs_data_get_bool(settings, "use_bufsize");
	bool vfr         = obs_data_get_bool(settings, "vfr");
	bool cbr         = obs_data_get_bool(settings, "cbr");

	if (keyint_sec)
		obsx264->params.i_keyint_max =
			keyint_sec * voi->fps_num / voi->fps_den;

	if (!use_bufsize)
		buffer_size = bitrate;

	obsx264->params.b_vfr_input          = vfr;
	obsx264->params.rc.i_vbv_max_bitrate = bitrate;
	obsx264->params.rc.i_vbv_buffer_size = buffer_size;
	obsx264->params.rc.i_bitrate         = bitrate;
	obsx264->params.i_width              = width;
	obsx264->params.i_height             = height;
	obsx264->params.i_fps_num            = voi->fps_num;
	obsx264->params.i_fps_den            = voi->fps_den;
	obsx264->params.pf_log               = log_x264;
	obsx264->params.p_log_private        = obsx264;
	obsx264->params.i_log_level          = X264_LOG_WARNING;

	obsx264->params.vui.i_transfer =
		get_x264_cs_val(info.colorspace, x264_transfer_names);
	obsx264->params.vui.i_colmatrix =
		get_x264_cs_val(info.colorspace, x264_colmatrix_names);
	obsx264->params.vui.i_colorprim =
		get_x264_cs_val(info.colorspace, x264_colorprim_names);
	obsx264->params.vui.b_fullrange =
		info.range == VIDEO_RANGE_FULL;

	/* use the new filler method for CBR to allow real-time adjusting of
	 * the bitrate */
	if (cbr) {
		obsx264->params.rc.f_rf_constant = 0.0f;
		obsx264->params.rc.i_rc_method   = X264_RC_ABR;

#if X264_BUILD >= 139
		obsx264->params.rc.b_filler      = true;
#else
		obsx264->params.i_nal_hrd        = X264_NAL_HRD_CBR;
#endif
	} else {
		obsx264->params.rc.i_rc_method   = X264_RC_CRF;
		obsx264->params.rc.f_rf_constant = (float)crf;
	}

	if (info.format == VIDEO_FORMAT_NV12)
		obsx264->params.i_csp = X264_CSP_NV12;
	else if (info.format == VIDEO_FORMAT_I420)
		obsx264->params.i_csp = X264_CSP_I420;
	else if (info.format == VIDEO_FORMAT_I444)
		obsx264->params.i_csp = X264_CSP_I444;
	else
		obsx264->params.i_csp = X264_CSP_NV12;

	while (*params)
		set_param(obsx264, *(params++));

	info("settings:\n"
	     "\tbitrate:     %d\n"
	     "\tbuffer size: %d\n"
	     "\tcrf:         %d%s\n"
	     "\tfps_num:     %d\n"
	     "\tfps_den:     %d\n"
	     "\twidth:       %d\n"
	     "\theight:      %d\n"
	     "\tkeyint:      %d\n"
	     "\tvfr:         %s\n"
	     "\tcbr:         %s",
	     obsx264->params.rc.i_vbv_max_bitrate,
	     obsx264->params.rc.i_vbv_buffer_size,
	     (int)obsx264->params.rc.f_rf_constant,
	     cbr ? " (0 when CBR is enabled)" : "",
	     voi->fps_num, voi->fps_den,
	     width, height,
	     obsx264->params.i_keyint_max,
	     vfr ? "on" : "off",
	     cbr ? "on" : "off");
}
Пример #22
0
/**
 * Initialize the v4l2 device
 *
 * This function:
 * - tries to open the device
 * - sets pixelformat and requested resolution
 * - sets the requested framerate
 * - maps the buffers
 * - starts the capture thread
 */
static void v4l2_init(struct v4l2_data *data)
{
	uint32_t input_caps;
	int fps_num, fps_denom;

	blog(LOG_INFO, "Start capture from %s", data->device_id);
	data->dev = v4l2_open(data->device_id, O_RDWR | O_NONBLOCK);
	if (data->dev == -1) {
		blog(LOG_ERROR, "Unable to open device");
		goto fail;
	}

	/* set input */
	if (v4l2_set_input(data->dev, &data->input) < 0) {
		blog(LOG_ERROR, "Unable to set input %d", data->input);
		goto fail;
	}
	blog(LOG_INFO, "Input: %d", data->input);
	if (v4l2_get_input_caps(data->dev, -1, &input_caps) < 0) {
		blog(LOG_ERROR, "Unable to get input capabilities");
		goto fail;
	}

	/* set video standard if supported */
	if (input_caps & V4L2_IN_CAP_STD) {
		if (v4l2_set_standard(data->dev, &data->standard) < 0) {
			blog(LOG_ERROR, "Unable to set video standard");
			goto fail;
		}
		data->resolution = -1;
		data->framerate  = -1;
	}
	/* set dv timing if supported */
#ifdef __FreeBSD__
	if (input_caps & V4L2_IN_CAP_CUSTOM_TIMINGS) {
#else
	if (input_caps & V4L2_IN_CAP_DV_TIMINGS) {
#endif
		if (v4l2_set_dv_timing(data->dev, &data->dv_timing) < 0) {
			blog(LOG_ERROR, "Unable to set dv timing");
			goto fail;
		}
		data->resolution = -1;
		data->framerate  = -1;
	}

	/* set pixel format and resolution */
	if (v4l2_set_format(data->dev, &data->resolution, &data->pixfmt,
			&data->linesize) < 0) {
		blog(LOG_ERROR, "Unable to set format");
		goto fail;
	}
	if (v4l2_to_obs_video_format(data->pixfmt) == VIDEO_FORMAT_NONE) {
		blog(LOG_ERROR, "Selected video format not supported");
		goto fail;
	}
	v4l2_unpack_tuple(&data->width, &data->height, data->resolution);
	blog(LOG_INFO, "Resolution: %dx%d", data->width, data->height);
	blog(LOG_INFO, "Pixelformat: %s", V4L2_FOURCC_STR(data->pixfmt));
	blog(LOG_INFO, "Linesize: %d Bytes", data->linesize);

	/* set framerate */
	if (v4l2_set_framerate(data->dev, &data->framerate) < 0) {
		blog(LOG_ERROR, "Unable to set framerate");
		goto fail;
	}
	v4l2_unpack_tuple(&fps_num, &fps_denom, data->framerate);
	blog(LOG_INFO, "Framerate: %.2f fps", (float) fps_denom / fps_num);

	/* map buffers */
	if (v4l2_create_mmap(data->dev, &data->buffers) < 0) {
		blog(LOG_ERROR, "Failed to map buffers");
		goto fail;
	}

	/* start the capture thread */
	if (os_event_init(&data->event, OS_EVENT_TYPE_MANUAL) != 0)
		goto fail;
	if (pthread_create(&data->thread, NULL, v4l2_thread, data) != 0)
		goto fail;
	return;
fail:
	blog(LOG_ERROR, "Initialization failed");
	v4l2_terminate(data);
}

/** Update source flags depending on the settings */
static void v4l2_update_source_flags(struct v4l2_data *data,
		obs_data_t *settings)
{
	uint32_t flags = obs_source_get_flags(data->source);
	flags = (obs_data_get_bool(settings, "buffering"))
			? flags & ~OBS_SOURCE_FLAG_UNBUFFERED
			: flags | OBS_SOURCE_FLAG_UNBUFFERED;
	obs_source_set_flags(data->source, flags);
}