diff --git a/libobs-d3d11/d3d11-exports.h b/libobs-d3d11/d3d11-exports.h
index 750b29ad0b54ab4cd52559d1f437b323ae917852..c456b0e7162d5c26425a93f31733c8738f84719c 100644
--- a/libobs-d3d11/d3d11-exports.h
+++ b/libobs-d3d11/d3d11-exports.h
@@ -150,7 +150,7 @@ EXPORT void     stagesurface_destroy(stagesurf_t stagesurf);
 EXPORT uint32_t stagesurface_getwidth(stagesurf_t stagesurf);
 EXPORT uint32_t stagesurface_getheight(stagesurf_t stagesurf);
 EXPORT enum gs_color_format stagesurface_getcolorformat(stagesurf_t stagesurf);
-EXPORT bool     stagesurface_map(stagesurf_t stagesurf, const uint8_t **data,
+EXPORT bool     stagesurface_map(stagesurf_t stagesurf, uint8_t **data,
 		uint32_t *linesize);
 EXPORT void     stagesurface_unmap(stagesurf_t stagesurf);
 
diff --git a/libobs-d3d11/d3d11-subsystem.cpp b/libobs-d3d11/d3d11-subsystem.cpp
index 19478d288d4b36951a8ce056788aa16c7937258f..433a174213935519c9d9c0e597216bdcbcf27967 100644
--- a/libobs-d3d11/d3d11-subsystem.cpp
+++ b/libobs-d3d11/d3d11-subsystem.cpp
@@ -1552,8 +1552,7 @@ enum gs_color_format stagesurface_getcolorformat(stagesurf_t stagesurf)
 	return stagesurf->format;
 }
 
-bool stagesurface_map(stagesurf_t stagesurf, const uint8_t **data,
-		uint32_t *linesize)
+bool stagesurface_map(stagesurf_t stagesurf, uint8_t **data, uint32_t *linesize)
 {
 	D3D11_MAPPED_SUBRESOURCE map;
 	if (FAILED(stagesurf->device->context->Map(stagesurf->texture, 0,
diff --git a/libobs-opengl/gl-exports.h b/libobs-opengl/gl-exports.h
index 06070bcc31131c9c03d39bfbf8aafd9e3f0473be..1d2430a3e123a68539a3ecf0fd94c47116efbb92 100644
--- a/libobs-opengl/gl-exports.h
+++ b/libobs-opengl/gl-exports.h
@@ -145,7 +145,7 @@ EXPORT void     stagesurface_destroy(stagesurf_t stagesurf);
 EXPORT uint32_t stagesurface_getwidth(stagesurf_t stagesurf);
 EXPORT uint32_t stagesurface_getheight(stagesurf_t stagesurf);
 EXPORT enum gs_color_format stagesurface_getcolorformat(stagesurf_t stagesurf);
-EXPORT bool     stagesurface_map(stagesurf_t stagesurf, const uint8_t **data,
+EXPORT bool     stagesurface_map(stagesurf_t stagesurf, uint8_t **data,
 		uint32_t *linesize);
 EXPORT void     stagesurface_unmap(stagesurf_t stagesurf);
 
diff --git a/libobs-opengl/gl-stagesurf.c b/libobs-opengl/gl-stagesurf.c
index 924c54c91abfe5a9e53c6d420116a08b990adec1..73d9cacc787dd3cad2649bf0404d55b9f67ef7ad 100644
--- a/libobs-opengl/gl-stagesurf.c
+++ b/libobs-opengl/gl-stagesurf.c
@@ -199,8 +199,7 @@ enum gs_color_format stagesurface_getcolorformat(stagesurf_t stagesurf)
 	return stagesurf->format;
 }
 
-bool stagesurface_map(stagesurf_t stagesurf, const uint8_t **data,
-		uint32_t *linesize)
+bool stagesurface_map(stagesurf_t stagesurf, uint8_t **data, uint32_t *linesize)
 {
 	if (!gl_bind_buffer(GL_PIXEL_PACK_BUFFER, stagesurf->pack_buffer))
 		goto fail;
diff --git a/libobs/graphics/graphics-internal.h b/libobs/graphics/graphics-internal.h
index 5990b78fcbec52a77501ec079c5cd1f3e2f0622e..35ee061681bfe1b332286985ec763e74982f1eeb 100644
--- a/libobs/graphics/graphics-internal.h
+++ b/libobs/graphics/graphics-internal.h
@@ -158,7 +158,7 @@ struct gs_exports {
 	enum gs_color_format (*stagesurface_getcolorformat)(
 			stagesurf_t stagesurf);
 	bool     (*stagesurface_map)(stagesurf_t stagesurf,
-			const uint8_t **data, uint32_t *linesize);
+			uint8_t **data, uint32_t *linesize);
 	void     (*stagesurface_unmap)(stagesurf_t stagesurf);
 
 	void (*zstencil_destroy)(zstencil_t zstencil);
diff --git a/libobs/graphics/graphics.c b/libobs/graphics/graphics.c
index 693afac2422a46b7eeec03e73e24bb7048901886..5b5de8969b1a6cf5fed29d8f96cb137256e09461 100644
--- a/libobs/graphics/graphics.c
+++ b/libobs/graphics/graphics.c
@@ -1805,8 +1805,7 @@ enum gs_color_format stagesurface_getcolorformat(stagesurf_t stagesurf)
 	return graphics->exports.stagesurface_getcolorformat(stagesurf);
 }
 
-bool stagesurface_map(stagesurf_t stagesurf, const uint8_t **data,
-		uint32_t *linesize)
+bool stagesurface_map(stagesurf_t stagesurf, uint8_t **data, uint32_t *linesize)
 {
 	graphics_t graphics = thread_graphics;
 	if (!graphics || !stagesurf) return false;
diff --git a/libobs/graphics/graphics.h b/libobs/graphics/graphics.h
index 3f77db6550a45ca78382efa42c329f5b2d3e0d9c..84cf2767f4aa8c39924b1f158c08b65bae50cb52 100644
--- a/libobs/graphics/graphics.h
+++ b/libobs/graphics/graphics.h
@@ -659,7 +659,7 @@ EXPORT void     stagesurface_destroy(stagesurf_t stagesurf);
 EXPORT uint32_t stagesurface_getwidth(stagesurf_t stagesurf);
 EXPORT uint32_t stagesurface_getheight(stagesurf_t stagesurf);
 EXPORT enum gs_color_format stagesurface_getcolorformat(stagesurf_t stagesurf);
-EXPORT bool     stagesurface_map(stagesurf_t stagesurf, const uint8_t **data,
+EXPORT bool     stagesurface_map(stagesurf_t stagesurf, uint8_t **data,
 		uint32_t *linesize);
 EXPORT void     stagesurface_unmap(stagesurf_t stagesurf);
 
diff --git a/libobs/media-io/audio-io.c b/libobs/media-io/audio-io.c
index 69c4b3a7755c6621ce7b29fb31467fb8f7f5c384..30be6cd642107a115777d4c63f676a15ad4b0853 100644
--- a/libobs/media-io/audio-io.c
+++ b/libobs/media-io/audio-io.c
@@ -34,7 +34,7 @@ struct audio_input {
 	struct audio_convert_info conversion;
 	audio_resampler_t         resampler;
 
-	void (*callback)(void *param, const struct audio_data *data);
+	void (*callback)(void *param, struct audio_data *data);
 	void *param;
 };
 
@@ -337,7 +337,8 @@ static bool resample_audio_output(struct audio_input *input,
 
 		success = audio_resampler_resample(input->resampler,
 				output, &frames, &offset,
-				data->data, data->frames);
+				(const uint8_t *const *)data->data,
+				data->frames);
 
 		for (size_t i = 0; i < MAX_AV_PLANES; i++)
 			data->data[i] = output[i];
@@ -455,7 +456,7 @@ static void *audio_thread(void *param)
 /* ------------------------------------------------------------------------- */
 
 static size_t audio_get_input_idx(audio_t video,
-		void (*callback)(void *param, const struct audio_data *data),
+		void (*callback)(void *param, struct audio_data *data),
 		void *param)
 {
 	for (size_t i = 0; i < video->inputs.num; i++) {
@@ -500,7 +501,7 @@ static inline bool audio_input_init(struct audio_input *input,
 
 bool audio_output_connect(audio_t audio,
 		const struct audio_convert_info *conversion,
-		void (*callback)(void *param, const struct audio_data *data),
+		void (*callback)(void *param, struct audio_data *data),
 		void *param)
 {
 	bool success = false;
@@ -542,7 +543,7 @@ bool audio_output_connect(audio_t audio,
 }
 
 void audio_output_disconnect(audio_t audio,
-		void (*callback)(void *param, const struct audio_data *data),
+		void (*callback)(void *param, struct audio_data *data),
 		void *param)
 {
 	if (!audio) return;
diff --git a/libobs/media-io/audio-io.h b/libobs/media-io/audio-io.h
index a54393c6582e391838adda98df99fd7554d974da..41be4fb00ebf41a1cfa93b940a8f2f1508c7e05a 100644
--- a/libobs/media-io/audio-io.h
+++ b/libobs/media-io/audio-io.h
@@ -63,7 +63,7 @@ enum speaker_layout {
 };
 
 struct audio_data {
-	const uint8_t       *data[MAX_AV_PLANES];
+	uint8_t             *data[MAX_AV_PLANES];
 	uint32_t            frames;
 	uint64_t            timestamp;
 	float               volume;
@@ -174,10 +174,10 @@ EXPORT void audio_output_close(audio_t audio);
 
 EXPORT bool audio_output_connect(audio_t video,
 		const struct audio_convert_info *conversion,
-		void (*callback)(void *param, const struct audio_data *data),
+		void (*callback)(void *param, struct audio_data *data),
 		void *param);
 EXPORT void audio_output_disconnect(audio_t video,
-		void (*callback)(void *param, const struct audio_data *data),
+		void (*callback)(void *param, struct audio_data *data),
 		void *param);
 
 EXPORT bool audio_output_active(audio_t audio);
diff --git a/libobs/media-io/video-io.c b/libobs/media-io/video-io.c
index e71ce71fd8d7c22d2a45cf36e31421eacf5ced23..da226d6eeeda1d5ee34792eab5ac1e37e29f55fa 100644
--- a/libobs/media-io/video-io.c
+++ b/libobs/media-io/video-io.c
@@ -34,7 +34,7 @@ struct video_input {
 	struct video_frame        frame[MAX_CONVERT_BUFFERS];
 	int                       cur_frame;
 
-	void (*callback)(void *param, const struct video_data *frame);
+	void (*callback)(void *param, struct video_data *frame);
 	void *param;
 };
 
@@ -91,7 +91,8 @@ static inline bool scale_video_output(struct video_input *input,
 
 		success = video_scaler_scale(input->scaler,
 				frame->data, frame->linesize,
-				data->data, data->linesize);
+				(const uint8_t * const*)data->data,
+				data->linesize);
 
 		if (success) {
 			for (size_t i = 0; i < MAX_AV_PLANES; i++) {
@@ -209,7 +210,7 @@ void video_output_close(video_t video)
 }
 
 static size_t video_get_input_idx(video_t video,
-		void (*callback)(void *param, const struct video_data *frame),
+		void (*callback)(void *param, struct video_data *frame),
 		void *param)
 {
 	for (size_t i = 0; i < video->inputs.num; i++) {
@@ -259,7 +260,7 @@ static inline bool video_input_init(struct video_input *input,
 
 bool video_output_connect(video_t video,
 		const struct video_scale_info *conversion,
-		void (*callback)(void *param, const struct video_data *frame),
+		void (*callback)(void *param, struct video_data *frame),
 		void *param)
 {
 	bool success = false;
@@ -300,7 +301,7 @@ bool video_output_connect(video_t video,
 }
 
 void video_output_disconnect(video_t video,
-		void (*callback)(void *param, const struct video_data *frame),
+		void (*callback)(void *param, struct video_data *frame),
 		void *param)
 {
 	if (!video || !callback)
diff --git a/libobs/media-io/video-io.h b/libobs/media-io/video-io.h
index 4f0b1624a0e9e8e8c9379cdf902568287b9c6c66..ab57fb7bb310f91d727253d6ebacdb9430cf20a4 100644
--- a/libobs/media-io/video-io.h
+++ b/libobs/media-io/video-io.h
@@ -47,7 +47,7 @@ enum video_format {
 };
 
 struct video_data {
-	const uint8_t     *data[MAX_AV_PLANES];
+	uint8_t           *data[MAX_AV_PLANES];
 	uint32_t          linesize[MAX_AV_PLANES];
 	uint64_t          timestamp;
 };
@@ -82,24 +82,30 @@ static inline bool format_is_yuv(enum video_format format)
 }
 
 enum video_scale_type {
-	VIDEO_SCALE_POINT         = 0,
-	VIDEO_SCALE_FAST_BILINEAR = 1,
-	VIDEO_SCALE_DEFAULT       = VIDEO_SCALE_FAST_BILINEAR,
-	VIDEO_SCALE_BILINEAR      = 2,
-	VIDEO_SCALE_BICUBIC       = 3,
+	VIDEO_SCALE_DEFAULT,
+	VIDEO_SCALE_POINT,
+	VIDEO_SCALE_FAST_BILINEAR,
+	VIDEO_SCALE_BILINEAR,
+	VIDEO_SCALE_BICUBIC,
 };
 
 enum video_colorspace {
-	VIDEO_CS_601              = 0,
-	VIDEO_CS_DEFAULT          = VIDEO_CS_601,
-	VIDEO_CS_709              = 1,
+	VIDEO_CS_DEFAULT,
+	VIDEO_CS_601,
+	VIDEO_CS_709,
+};
+
+enum video_range_type {
+	VIDEO_RANGE_DEFAULT,
+	VIDEO_RANGE_PARTIAL,
+	VIDEO_RANGE_FULL
 };
 
 struct video_scale_info {
 	enum video_format     format;
 	uint32_t              width;
 	uint32_t              height;
-	bool                  full_range;
+	enum video_range_type range;
 	enum video_colorspace colorspace;
 };
 
@@ -112,10 +118,10 @@ EXPORT void video_output_close(video_t video);
 
 EXPORT bool video_output_connect(video_t video,
 		const struct video_scale_info *conversion,
-		void (*callback)(void *param, const struct video_data *frame),
+		void (*callback)(void *param, struct video_data *frame),
 		void *param);
 EXPORT void video_output_disconnect(video_t video,
-		void (*callback)(void *param, const struct video_data *frame),
+		void (*callback)(void *param, struct video_data *frame),
 		void *param);
 
 EXPORT bool video_output_active(video_t video);
diff --git a/libobs/media-io/video-scaler-ffmpeg.c b/libobs/media-io/video-scaler-ffmpeg.c
index c527645fdb66f34a971b12f62237d1ba11ab805e..1e3d2b62d3a25461a765fecad7e567b589508022 100644
--- a/libobs/media-io/video-scaler-ffmpeg.c
+++ b/libobs/media-io/video-scaler-ffmpeg.c
@@ -46,6 +46,7 @@ static inline enum AVPixelFormat get_ffmpeg_video_format(
 static inline int get_ffmpeg_scale_type(enum video_scale_type type)
 {
 	switch (type) {
+	case VIDEO_SCALE_DEFAULT:       return SWS_FAST_BILINEAR;
 	case VIDEO_SCALE_POINT:         return SWS_POINT;
 	case VIDEO_SCALE_FAST_BILINEAR: return SWS_FAST_BILINEAR;
 	case VIDEO_SCALE_BILINEAR:      return SWS_BILINEAR | SWS_AREA;
@@ -58,13 +59,25 @@ static inline int get_ffmpeg_scale_type(enum video_scale_type type)
 static inline const int *get_ffmpeg_coeffs(enum video_colorspace cs)
 {
 	switch (cs) {
-	case VIDEO_CS_601: return sws_getCoefficients(SWS_CS_ITU601);
-	case VIDEO_CS_709: return sws_getCoefficients(SWS_CS_ITU709);
+	case VIDEO_CS_DEFAULT: return sws_getCoefficients(SWS_CS_ITU601);
+	case VIDEO_CS_601:     return sws_getCoefficients(SWS_CS_ITU601);
+	case VIDEO_CS_709:     return sws_getCoefficients(SWS_CS_ITU709);
 	}
 
 	return sws_getCoefficients(SWS_CS_ITU601);
 }
 
+static inline int get_ffmpeg_range_type(enum video_range_type type)
+{
+	switch (type) {
+	case VIDEO_RANGE_DEFAULT: return 0;
+	case VIDEO_RANGE_PARTIAL: return 0;
+	case VIDEO_RANGE_FULL:    return 1;
+	}
+
+	return 0;
+}
+
 #define FIXED_1_0 (1<<16)
 
 int video_scaler_create(video_scaler_t *scaler_out,
@@ -77,6 +90,8 @@ int video_scaler_create(video_scaler_t *scaler_out,
 	int                scale_type = get_ffmpeg_scale_type(type);
 	const int          *coeff_src = get_ffmpeg_coeffs(src->colorspace);
 	const int          *coeff_dst = get_ffmpeg_coeffs(dst->colorspace);
+	int                range_src  = get_ffmpeg_range_type(src->range);
+	int                range_dst  = get_ffmpeg_range_type(dst->range);
 	struct video_scaler *scaler;
 	int ret;
 
@@ -101,8 +116,8 @@ int video_scaler_create(video_scaler_t *scaler_out,
 	}
 
 	ret = sws_setColorspaceDetails(scaler->swscale,
-			coeff_src, src->full_range,
-			coeff_dst, dst->full_range,
+			coeff_src, range_src,
+			coeff_dst, range_dst,
 			0, FIXED_1_0, FIXED_1_0);
 	if (ret < 0) {
 		blog(LOG_DEBUG, "video_scaler_create: "
diff --git a/libobs/obs-encoder.c b/libobs/obs-encoder.c
index cd25fa4825d74445bc87dbbdd95aac525f9cde7e..e3f08af6f7d169abd25d7fe126aa424c00365557 100644
--- a/libobs/obs-encoder.c
+++ b/libobs/obs-encoder.c
@@ -33,56 +33,160 @@ static inline struct obs_encoder_info *get_encoder_info(const char *id)
 const char *obs_encoder_getdisplayname(const char *id, const char *locale)
 {
 	struct obs_encoder_info *ei = get_encoder_info(id);
-	if (!ei)
-		return NULL;
-
-	return ei->getname(locale);
+	return ei ? ei->getname(locale) : NULL;
 }
 
-obs_encoder_t obs_encoder_create(const char *id, const char *name,
+static bool init_encoder(struct obs_encoder *encoder, const char *name,
 		obs_data_t settings)
+{
+	if (pthread_mutex_init(&encoder->callbacks_mutex, NULL) != 0)
+		return false;
+
+	encoder->settings = obs_data_newref(settings);
+	encoder->data     = encoder->info.create(encoder->settings, encoder);
+
+	if (!encoder->data) {
+		pthread_mutex_destroy(&encoder->callbacks_mutex);
+		obs_data_release(encoder->settings);
+		return false;
+	}
+
+	pthread_mutex_lock(&obs->data.encoders_mutex);
+	da_push_back(obs->data.encoders, &encoder);
+	pthread_mutex_unlock(&obs->data.encoders_mutex);
+
+	encoder->name = bstrdup(name);
+	return true;
+}
+
+static struct obs_encoder *create_encoder(const char *id,
+		enum obs_encoder_type type, const char *name,
+		obs_data_t settings, void *output,
+		uint32_t timebase_num, uint32_t timebase_den)
 {
 	struct obs_encoder *encoder;
 	struct obs_encoder_info *ei = get_encoder_info(id);
+	bool success;
 
-	if (!ei)
+	if (!ei || ei->type != type)
 		return NULL;
 
 	encoder = bzalloc(sizeof(struct obs_encoder));
-	encoder->info = *ei;
+	encoder->info         = *ei;
+	encoder->output       = output;
+	encoder->timebase_num = timebase_num;
+	encoder->timebase_den = timebase_den;
 
-	if (pthread_mutex_init(&encoder->data_callbacks_mutex, NULL) != 0) {
+	success = init_encoder(encoder, name, settings);
+	if (!success) {
 		bfree(encoder);
-		return NULL;
+		encoder = NULL;
 	}
 
-	encoder->settings = obs_data_newref(settings);
-	encoder->data     = ei->create(encoder->settings, encoder);
+	return encoder;
+}
 
-	if (!encoder->data) {
-		pthread_mutex_destroy(&encoder->data_callbacks_mutex);
-		obs_data_release(encoder->settings);
-		bfree(encoder);
+obs_encoder_t obs_encoder_create_video(const char *id, const char *name,
+		obs_data_t settings, video_t video)
+{
+	const struct video_output_info *voi;
+
+	if (!name || !id || !video)
 		return NULL;
+
+	voi = video_output_getinfo(video);
+	return create_encoder(id, OBS_ENCODER_VIDEO, name, settings, video,
+			voi->fps_den, voi->fps_num);
+}
+
+obs_encoder_t obs_encoder_create_audio(const char *id, const char *name,
+		obs_data_t settings, audio_t audio)
+{
+	const struct audio_output_info *aoi;
+
+	if (!name || !id || !audio)
+		return NULL;
+
+	aoi = audio_output_getinfo(audio);
+	return create_encoder(id, OBS_ENCODER_AUDIO, name, settings, audio,
+			1, aoi->samples_per_sec);
+}
+
+static void receive_video(void *param, struct video_data *frame);
+static void receive_audio(void *param, struct audio_data *data);
+
+static inline struct audio_convert_info *get_audio_info(
+		struct obs_encoder *encoder, struct audio_convert_info *info)
+{
+	if (encoder->info.audio_info)
+		if (encoder->info.audio_info(encoder->data, info))
+			return info;
+
+	return false;
+}
+
+static inline struct video_scale_info *get_video_info(
+		struct obs_encoder *encoder, struct video_scale_info *info)
+{
+	if (encoder->info.video_info)
+		if (encoder->info.video_info(encoder->data, info))
+			return info;
+
+	return NULL;
+}
+
+static void add_connection(struct obs_encoder *encoder)
+{
+	struct audio_convert_info audio_info = {0};
+	struct video_scale_info   video_info = {0};
+
+	if (encoder->info.type == OBS_ENCODER_AUDIO) {
+		struct audio_convert_info *info = NULL;
+
+		info = get_audio_info(encoder, &audio_info);
+		audio_output_connect(encoder->output, info, receive_audio,
+				encoder);
+	} else {
+		struct video_scale_info *info = NULL;
+
+		info = get_video_info(encoder, &video_info);
+		video_output_connect(encoder->output, info, receive_video,
+				encoder);
 	}
+}
 
-	pthread_mutex_lock(&obs->data.encoders_mutex);
-	da_push_back(obs->data.encoders, &encoder);
-	pthread_mutex_unlock(&obs->data.encoders_mutex);
+static void remove_connection(struct obs_encoder *encoder)
+{
+	if (encoder->info.type == OBS_ENCODER_AUDIO)
+		audio_output_disconnect(encoder->output, receive_audio,
+				encoder);
+	else
+		video_output_disconnect(encoder->output, receive_video,
+				encoder);
+}
 
-	encoder->name = bstrdup(name);
-	return encoder;
+static void full_stop(struct obs_encoder *encoder)
+{
+	if (encoder) {
+		pthread_mutex_lock(&encoder->callbacks_mutex);
+		da_free(encoder->callbacks);
+		remove_connection(encoder);
+		pthread_mutex_unlock(&encoder->callbacks_mutex);
+	}
 }
 
 void obs_encoder_destroy(obs_encoder_t encoder)
 {
 	if (encoder) {
+		full_stop(encoder);
+
 		pthread_mutex_lock(&obs->data.encoders_mutex);
 		da_erase_item(obs->data.encoders, &encoder);
 		pthread_mutex_unlock(&obs->data.encoders_mutex);
 
 		encoder->info.destroy(encoder->data);
 		obs_data_release(encoder->settings);
+		pthread_mutex_destroy(&encoder->callbacks_mutex);
 		bfree(encoder->name);
 		bfree(encoder);
 	}
@@ -114,33 +218,15 @@ void obs_encoder_update(obs_encoder_t encoder, obs_data_t settings)
 	if (!encoder) return;
 
 	obs_data_apply(encoder->settings, settings);
-	encoder->info.update(encoder->data, encoder->settings);
-}
-
-bool obs_encoder_reset(obs_encoder_t encoder, obs_data_t settings)
-{
-	if (!encoder) return false;
-
-	return encoder->info.reset(encoder->data, settings);
-}
-
-bool obs_encoder_encode(obs_encoder_t encoder,
-		const struct encoder_frame *frame,
-		struct encoder_packet *packet, bool *received_packet)
-{
-	if (!encoder) return false;
-
-	return encoder->info.encode(encoder->data, frame, packet,
-			received_packet);
+	if (encoder->info.update)
+		encoder->info.update(encoder->data, encoder->settings);
 }
 
 bool obs_encoder_get_extra_data(obs_encoder_t encoder, uint8_t **extra_data,
 		size_t *size)
 {
-	if (!encoder) return false;
-
-	if (encoder->info.get_extra_data)
-		return encoder->info.get_extra_data(encoder, extra_data, size);
+	if (encoder && encoder->info.extra_data)
+		return encoder->info.extra_data(encoder, extra_data, size);
 
 	return false;
 }
@@ -153,24 +239,216 @@ obs_data_t obs_encoder_get_settings(obs_encoder_t encoder)
 	return encoder->settings;
 }
 
+static inline size_t get_callback_idx(
+		struct obs_encoder *encoder,
+		void (*new_packet)(void *param, struct encoder_packet *packet),
+		void *param)
+{
+	for (size_t i = 0; i < encoder->callbacks.num; i++) {
+		struct encoder_callback *cb = encoder->callbacks.array+i;
+
+		if (cb->new_packet == new_packet && cb->param == param)
+			return i;
+	}
+
+	return DARRAY_INVALID;
+}
+
 bool obs_encoder_start(obs_encoder_t encoder,
 		void (*new_packet)(void *param, struct encoder_packet *packet),
 		void *param)
 {
-	/* TODO: implement */
-	UNUSED_PARAMETER(encoder);
-	UNUSED_PARAMETER(new_packet);
-	UNUSED_PARAMETER(param);
-	return false;
+	struct encoder_callback cb = {false, new_packet, param};
+	bool success = true;
+	bool first   = false;
+
+	if (!encoder || !new_packet) return false;
+
+	pthread_mutex_lock(&encoder->callbacks_mutex);
+
+	first = (encoder->callbacks.num == 0);
+	if (first)
+		success = encoder->info.start(encoder->data, encoder->settings);
+
+	if (success) {
+		size_t idx = get_callback_idx(encoder, new_packet, param);
+		if (idx == DARRAY_INVALID)
+			da_push_back(encoder->callbacks, &cb);
+		else
+			success = false;
+	}
+
+	pthread_mutex_unlock(&encoder->callbacks_mutex);
+
+	if (first) {
+		encoder->cur_pts = 0;
+		add_connection(encoder);
+	}
+
+	return success;
 }
 
 void obs_encoder_stop(obs_encoder_t encoder,
 		void (*new_packet)(void *param, struct encoder_packet *packet),
 		void *param)
 {
-	/* TODO: implement */
-	UNUSED_PARAMETER(encoder);
-	UNUSED_PARAMETER(new_packet);
-	UNUSED_PARAMETER(param);
-	return;
+	bool   last = false;
+	size_t idx;
+
+	if (!encoder) return;
+
+	pthread_mutex_lock(&encoder->callbacks_mutex);
+
+	idx = get_callback_idx(encoder, new_packet, param);
+	if (idx != DARRAY_INVALID) {
+		da_erase(encoder->callbacks, idx);
+		last = (encoder->callbacks.num == 0);
+
+		if (last)
+			encoder->info.stop(encoder->data);
+	}
+
+	pthread_mutex_unlock(&encoder->callbacks_mutex);
+
+	if (last)
+		remove_connection(encoder);
+}
+
+video_t obs_encoder_video(obs_encoder_t encoder)
+{
+	return (encoder && encoder->info.type == OBS_ENCODER_VIDEO) ?
+		encoder->output : NULL;
+}
+
+audio_t obs_encoder_audio(obs_encoder_t encoder)
+{
+	return (encoder && encoder->info.type == OBS_ENCODER_AUDIO) ?
+		encoder->output : NULL;
+}
+
+static inline bool get_sei(struct obs_encoder *encoder,
+		uint8_t **sei, size_t *size)
+{
+	if (encoder->info.sei_data)
+		return encoder->info.sei_data(encoder->data, sei, size);
+	return false;
+}
+
+static void send_first_video_packet(struct obs_encoder *encoder,
+		struct encoder_callback *cb, struct encoder_packet *packet)
+{
+	struct encoder_packet first_packet;
+	DARRAY(uint8_t)       data;
+	uint8_t               *sei;
+	size_t                size;
+
+	/* always wait for first keyframe */
+	if (!packet->keyframe)
+		return;
+
+	da_init(data);
+
+	if (!get_sei(encoder, &sei, &size)) {
+		cb->new_packet(cb->param, packet);
+		return;
+	}
+
+	da_push_back_array(data, sei, size);
+	da_push_back_array(data, packet->data, packet->size);
+
+	first_packet      = *packet;
+	first_packet.data = data.array;
+	first_packet.size = data.num;
+
+	cb->new_packet(cb->param, &first_packet);
+	cb->sent_first_packet = true;
+
+	da_free(data);
+}
+
+static inline void send_packet(struct obs_encoder *encoder,
+		struct encoder_callback *cb, struct encoder_packet *packet)
+{
+	/* include SEI in first video packet */
+	if (encoder->info.type == OBS_ENCODER_VIDEO && !cb->sent_first_packet)
+		send_first_video_packet(encoder, cb, packet);
+	else
+		cb->new_packet(cb->param, packet);
+}
+
+static inline void do_encode(struct obs_encoder *encoder,
+		struct encoder_frame *frame, struct encoder_packet *packet)
+{
+	bool received = false;
+	bool success;
+
+	packet->timebase_num = encoder->timebase_num;
+	packet->timebase_den = encoder->timebase_den;
+
+	success = encoder->info.encode(encoder->data, frame, packet, &received);
+	if (!success) {
+		full_stop(encoder);
+		blog(LOG_ERROR, "Error encoding with encoder '%s'",
+				encoder->name);
+		return;
+	}
+
+	if (received) {
+		pthread_mutex_lock(&encoder->callbacks_mutex);
+
+		for (size_t i = 0; i < encoder->callbacks.num; i++) {
+			struct encoder_callback *cb;
+
+			cb = encoder->callbacks.array+i;
+		}
+
+		pthread_mutex_unlock(&encoder->callbacks_mutex);
+	}
+}
+
+static void receive_video(void *param, struct video_data *frame)
+{
+	struct obs_encoder    *encoder  = param;
+	struct encoder_packet packet    = {0};
+	struct encoder_frame  enc_frame;
+
+	memset(&enc_frame, 0, sizeof(struct encoder_frame));
+
+	for (size_t i = 0; i < MAX_AV_PLANES; i++) {
+		enc_frame.data[i]     = frame->data[i];
+		enc_frame.linesize[i] = frame->linesize[i];
+	}
+
+	enc_frame.frames = 1;
+	enc_frame.pts    = encoder->cur_pts;
+
+	do_encode(encoder, &enc_frame, &packet);
+
+	encoder->cur_pts += encoder->timebase_num;
+}
+
+static void receive_audio(void *param, struct audio_data *data)
+{
+	struct obs_encoder    *encoder  = param;
+	struct encoder_packet packet    = {0};
+	struct encoder_frame  enc_frame;
+	size_t                data_size;
+
+	memset(&enc_frame, 0, sizeof(struct encoder_frame));
+
+	data_size = audio_output_blocksize(encoder->output) * data->frames;
+
+	for (size_t i = 0; i < MAX_AV_PLANES; i++) {
+		if (data->data[i]) {
+			enc_frame.data[i]     = data->data[i];
+			enc_frame.linesize[i] = (uint32_t)data_size;
+		}
+	}
+
+	enc_frame.frames = data->frames;
+	enc_frame.pts    = encoder->cur_pts;
+
+	do_encode(encoder, &enc_frame, &packet);
+
+	encoder->cur_pts += data->frames;
 }
diff --git a/libobs/obs-encoder.h b/libobs/obs-encoder.h
index f5747a94581b54c828779deae4bedef6c53f72ed..b61b3e7570cb0869916101e362c8a7f6f4017f4d 100644
--- a/libobs/obs-encoder.h
+++ b/libobs/obs-encoder.h
@@ -19,34 +19,38 @@
 
 /** Specifies the encoder type */
 enum obs_encoder_type {
-	OBS_PACKET_AUDIO,
-	OBS_PACKET_VIDEO
+	OBS_ENCODER_AUDIO,
+	OBS_ENCODER_VIDEO
 };
 
 /** Encoder output packet */
 struct encoder_packet {
-	uint8_t               *data;      /**< Packet data */
-	size_t                size;       /**< Packet size */
+	uint8_t               *data;        /**< Packet data */
+	size_t                size;         /**< Packet size */
 
-	int64_t               pts;        /**< Presentation timestamp */
-	int64_t               dts;        /**< Decode timestamp */
+	int64_t               pts;          /**< Presentation timestamp */
+	int64_t               dts;          /**< Decode timestamp */
 
-	enum obs_encoder_type type;       /**< Encoder type */
+	int32_t               timebase_num; /**< Timebase numerator */
+	int32_t               timebase_den; /**< Timebase denominator */
+
+	enum obs_encoder_type type;         /**< Encoder type */
+
+	bool                  keyframe;     /**< Is a keyframe */
 
 	/**
 	 * Packet priority
 	 *
 	 * This is generally use by video encoders to specify the priority
-	 * of the packet.  If this frame is dropped, it will have to wait for
-	 * another packet of drop_priority.
+	 * of the packet.
 	 */
 	int                   priority;
 
 	/**
 	 * Dropped packet priority
 	 *
-	 * If this packet is dropped, the next packet must be of this priority
-	 * or higher to continue transmission.
+	 * If this packet needs to be dropped, the next packet must be of this
+	 * priority or higher to continue transmission.
 	 */
 	int                   drop_priority;
 };
@@ -83,6 +87,12 @@ struct obs_encoder_info {
 	/** Specifies the named identifier of this encoder */
 	const char *id;
 
+	/** Specifies the encoder type (video or audio) */
+	enum obs_encoder_type type;
+
+	/** Specifies the codec */
+	const char *codec;
+
 	/**
 	 * Gets the full translated name of this encoder
 	 *
@@ -108,13 +118,13 @@ struct obs_encoder_info {
 	void (*destroy)(void *data);
 
 	/**
-	 * Resets the encoder with the specified settings
+	 * Starts the encoder
 	 *
 	 * @param  data      Data associated with this encoder context
-	 * @param  settings  New settings for the encoder
+	 * @param  settings  Settings for the encoder
 	 * @return           true if successful, false otherwise
 	 */
-	bool (*reset)(void *data, obs_data_t settings);
+	bool (*start)(void *data, obs_data_t settings);
 
 	/**
 	 * Encodes frame(s), and outputs encoded packets as they become
@@ -128,7 +138,7 @@ struct obs_encoder_info {
 	 *                               false otherwise
 	 * @return                       true if successful, false otherwise.
 	 */
-	int (*encode)(void *data, const struct encoder_frame *frame,
+	bool (*encode)(void *data, struct encoder_frame *frame,
 			struct encoder_packet *packet, bool *received_packet);
 
 	/* ----------------------------------------------------------------- */
@@ -141,6 +151,13 @@ struct obs_encoder_info {
 	 */
 	void (*defaults)(obs_data_t settings);
 
+	/**
+	 * Stops the encoder
+	 *
+	 * @param  data  Data associated with this encoder context
+	 */
+	void (*stop)(void *data);
+
 	/** 
 	 * Gets the property information of this encoder
 	 *
@@ -150,21 +167,56 @@ struct obs_encoder_info {
 	obs_properties_t (*properties)(const char *locale);
 
 	/**
-	 * Updates the settings for this encoder
+	 * Updates the settings for this encoder (usually used for things like
+	 * changeing birate while active)
 	 *
 	 * @param  data      Data associated with this encoder context
 	 * @param  settings  New settings for this encoder
+	 * @return           true if successful, false otherwise
 	 */
-	void (*update)(void *data, obs_data_t settings);
+	bool (*update)(void *data, obs_data_t settings);
 
 	/**
 	 * Returns extra data associated with this encoder (usually header)
 	 *
-	 * @param  data        Data associated with this encoder context
-	 * @param  extra_data  Pointer to receive the extra data
-	 * @param  size        Pointer to receive the size of the extra data
+	 * @param  data             Data associated with this encoder context
+	 * @param[out]  extra_data  Pointer to receive the extra data
+	 * @param[out]  size        Pointer to receive the size of the extra
+	 *                          data
+	 * @return                  true if extra data available, false
+	 *                          otherwise
+	 */
+	bool (*extra_data)(void *data, uint8_t **extra_data, size_t *size);
+
+	/**
+	 * Gets the SEI data, if any
+	 *
+	 * @param       data      Data associated with this encoder context
+	 * @param[out]  sei_data  Pointer to receive the SEI data
+	 * @param[out]  size      Pointer to receive the SEI data size
+	 * @return                true if SEI data available, false otherwise
+	 */
+	bool (*sei_data)(void *data, uint8_t **sei_data, size_t *size);
+
+	/**
+	 * Returns desired audio format and sample information
+	 *
+	 * @param       data  Data associated with this encoder context
+	 * @param[out]  info  Audio format information
+	 * @return            true if specific format is desired, false
+	 *                    otherwise
+	 */
+	bool (*audio_info)(void *data, struct audio_convert_info *info);
+
+	/**
+	 * Returns desired video format information
+	 *
+	 * @param       data  Data associated with this encoder context
+	 * @param[out]  info  Video format information
+	 * @return            true if specific format is desired, false
+	 *                    otherwise
 	 */
-	bool (*get_extra_data)(void *data, uint8_t **extra_data, size_t *size);
+	bool (*video_info)(void *data, struct video_scale_info *info);
 };
 
 /**
diff --git a/libobs/obs-internal.h b/libobs/obs-internal.h
index c3f2414ba096e413e45f9e581d6454b7e3aeca88..b0f87fafad6f167ce9a289caded322f33b6b596f 100644
--- a/libobs/obs-internal.h
+++ b/libobs/obs-internal.h
@@ -276,6 +276,7 @@ struct obs_output {
 /* encoders  */
 
 struct encoder_callback {
+	bool sent_first_packet;
 	void (*new_packet)(void *param, struct encoder_packet *packet);
 	void *param;
 };
@@ -286,6 +287,13 @@ struct obs_encoder {
 	struct obs_encoder_info         info;
 	obs_data_t                      settings;
 
-	pthread_mutex_t                 data_callbacks_mutex;
-	DARRAY(struct encoder_callback) data_callbacks;
+	uint32_t                        timebase_num;
+	uint32_t                        timebase_den;
+
+	int64_t                         cur_pts;
+
+	void                            *output;
+
+	pthread_mutex_t                 callbacks_mutex;
+	DARRAY(struct encoder_callback) callbacks;
 };
diff --git a/libobs/obs-module.c b/libobs/obs-module.c
index ee7190398754f298aba44e7ba5f4c9d005af2610..0df14a72ae0988899bc3e7b7c17ad244551fc706 100644
--- a/libobs/obs-module.c
+++ b/libobs/obs-module.c
@@ -204,7 +204,7 @@ void obs_register_encoder(const struct obs_encoder_info *info)
 	CHECK_REQUIRED_VAL(info, getname,   obs_register_encoder);
 	CHECK_REQUIRED_VAL(info, create,    obs_register_encoder);
 	CHECK_REQUIRED_VAL(info, destroy,   obs_register_encoder);
-	CHECK_REQUIRED_VAL(info, reset,     obs_register_encoder);
+	CHECK_REQUIRED_VAL(info, start,     obs_register_encoder);
 	CHECK_REQUIRED_VAL(info, encode,    obs_register_encoder);
 
 	REGISTER_OBS_DEF(cur_encoder_info_size, obs_encoder_info,
diff --git a/libobs/obs-output.c b/libobs/obs-output.c
index 12150007599e00f3c58dc1f72788737f249f5237..c6b847beb0bb047dd4af7fa43cebfe63bd7fce41 100644
--- a/libobs/obs-output.c
+++ b/libobs/obs-output.c
@@ -154,7 +154,7 @@ obs_data_t obs_output_get_settings(obs_output_t output)
 
 bool obs_output_canpause(obs_output_t output)
 {
-	return (output != NULL) ? output->info.pause != NULL : false;
+	return output ? (output->info.pause != NULL) : false;
 }
 
 void obs_output_pause(obs_output_t output)
@@ -165,10 +165,10 @@ void obs_output_pause(obs_output_t output)
 
 signal_handler_t obs_output_signalhandler(obs_output_t output)
 {
-	return output->signals;
+	return output ? output->signals : NULL;
 }
 
 proc_handler_t obs_output_prochandler(obs_output_t output)
 {
-	return output->procs;
+	return output ? output->procs : NULL;
 }
diff --git a/libobs/obs.h b/libobs/obs.h
index 5636c752b06c8fea064da053ef18993c19769b3b..67c2dbaf189605c0d2ce66a34c65074d9c2066de 100644
--- a/libobs/obs.h
+++ b/libobs/obs.h
@@ -686,23 +686,54 @@ EXPORT proc_handler_t obs_output_prochandler(obs_output_t output);
 
 /* ------------------------------------------------------------------------- */
 /* Encoders */
+
 EXPORT const char *obs_encoder_getdisplayname(const char *id,
 		const char *locale);
 
-EXPORT obs_encoder_t obs_encoder_create(const char *id, const char *name,
-		obs_data_t settings);
-EXPORT void obs_encoder_destroy(obs_encoder_t encoder);
+/**
+ * Creates a video encoder context
+ *
+ * @param  id        Video encoder ID
+ * @param  name      Name to assign to this context
+ * @param  settings  Settings
+ * @param  video     Video output context to encode data from
+ * @return           The video encoder context, or NULL if failed or not found.
+ */
+EXPORT obs_encoder_t obs_encoder_create_video(const char *id, const char *name,
+		obs_data_t settings, video_t video);
 
-EXPORT bool obs_encoder_reset(obs_encoder_t encoder, obs_data_t settings);
+/**
+ * Creates an audio encoder context
+ *
+ * @param  id        Audio Encoder ID
+ * @param  name      Name to assign to this context
+ * @param  settings  Settings
+ * @param  audio     Audio output context to encode data from
+ * @return           The video encoder context, or NULL if failed or not found.
+ */
+EXPORT obs_encoder_t obs_encoder_create_audio(const char *id, const char *name,
+		obs_data_t settings, audio_t audio);
 
-EXPORT bool obs_encoder_encode(obs_encoder_t encoder,
-		const struct encoder_frame *frame,
-		struct encoder_packet *packet,
-		bool *received_packet);
+/** Destroys an encoder context */
+EXPORT void obs_encoder_destroy(obs_encoder_t encoder);
 
+/**
+ * Starts encoding.  This function can be called more than once, and each
+ * callback will receive the same encoder data.
+ *
+ * @param  encoder     Encoder context
+ * @param  new_packet  Callback that receives encoded packets
+ * @param  param       Callback parameter
+ */
 EXPORT bool obs_encoder_start(obs_encoder_t encoder,
 		void (*new_packet)(void *param, struct encoder_packet *packet),
 		void *param);
+
+/**
+ * Stops encoding.  You must use the same callback/parameter combination that
+ * was used for obs_encoder_start.  Only when the last callback has been
+ * removed will all encoding stop.
+ */
 EXPORT void obs_encoder_stop(obs_encoder_t encoder,
 		void (*new_packet)(void *param, struct encoder_packet *packet),
 		void *param);
@@ -714,13 +745,31 @@ EXPORT obs_data_t obs_encoder_defaults(const char *id);
 EXPORT obs_properties_t obs_encoder_properties(const char *id,
 		const char *locale);
 
+/**
+ * Updates the settings of the encoder context.  Usually used for changing
+ * bitrate while active
+ */
 EXPORT void obs_encoder_update(obs_encoder_t encoder, obs_data_t settings);
 
+/** Gets extra data (headers) associated with this context */
 EXPORT bool obs_encoder_get_extra_data(obs_encoder_t encoder,
 		uint8_t **extra_data, size_t *size);
 
+/** Returns the current settings for this encoder */
 EXPORT obs_data_t obs_encoder_get_settings(obs_encoder_t encoder);
 
+/**
+ * Returns the video output context used with this encoder, or NULL if not
+ * a video context
+ */
+EXPORT video_t obs_encoder_video(obs_encoder_t encoder);
+
+/**
+ * Returns the audio output context used with this encoder, or NULL if not
+ * a audio context
+ */
+EXPORT audio_t obs_encoder_audio(obs_encoder_t encoder);
+
 
 /* ------------------------------------------------------------------------- */
 /* Stream Services */
diff --git a/libobs/util/bmem.h b/libobs/util/bmem.h
index 2ba4233ddd07f862fa09c3d8d1e792bd3ee96554..7b8f0cafb20d14a062e432f27804bffbf0ee7ca4 100644
--- a/libobs/util/bmem.h
+++ b/libobs/util/bmem.h
@@ -66,7 +66,7 @@ static inline char *bstrdup_n(const char *str, size_t n)
 static inline wchar_t *bwstrdup_n(const wchar_t *str, size_t n)
 {
 	wchar_t *dup;
-	if (!str || !*str)
+	if (!str || (!*str && n > 0))
 		return NULL;
 
 	dup = (wchar_t*)bmemdup(str, (n+1) * sizeof(wchar_t));
diff --git a/libobs/util/dstr.c b/libobs/util/dstr.c
index d3c1f35710ae1a6371be50e98f4b456643414fd1..aaceb83342b7e476e9f025af703228bb5a66b656 100644
--- a/libobs/util/dstr.c
+++ b/libobs/util/dstr.c
@@ -22,8 +22,10 @@
 #include <ctype.h>
 #include <wchar.h>
 #include <wctype.h>
+
 #include "c99defs.h"
 #include "dstr.h"
+#include "darray.h"
 #include "bmem.h"
 #include "utf8.h"
 #include "lexer.h"
@@ -235,6 +237,53 @@ wchar_t *wcsdepad(wchar_t *str)
 	return str;
 }
 
+char **strlist_split(const char *str, char split_ch, bool include_empty)
+{
+	const char    *cur_str = str;
+	const char    *next_str;
+	const char    *new_str;
+	DARRAY(char*) list;
+
+	da_init(list);
+
+	if (str) {
+		next_str = strchr(str, split_ch);
+
+		while (next_str) {
+			size_t size = next_str - cur_str;
+
+			if (size || include_empty) {
+				new_str = bstrdup_n(cur_str, size);
+				da_push_back(list, &new_str);
+			}
+
+			cur_str = next_str;
+			next_str = strchr(cur_str, split_ch);
+		}
+
+		if (*cur_str || include_empty) {
+			new_str = bstrdup(cur_str);
+			da_push_back(list, &new_str);
+		}
+	}
+
+	new_str = NULL;
+	da_push_back(list, &new_str);
+
+	return list.array;
+}
+
+void strlist_free(char **strlist)
+{
+	if (strlist) {
+		char **temp = strlist;
+		while (*temp)
+			bfree(*(temp++));
+
+		bfree(strlist);
+	}
+}
+
 void dstr_init_strref(struct dstr *dst, const struct strref *src)
 {
 	dstr_init(dst);
diff --git a/libobs/util/dstr.h b/libobs/util/dstr.h
index 11f5d033610213ad4fbcfc84a525da2981274781..866ea22682035d31e1a7b5389dd42147365a6f2f 100644
--- a/libobs/util/dstr.h
+++ b/libobs/util/dstr.h
@@ -51,6 +51,9 @@ EXPORT char *astrstri(char *str, const char *find);
 EXPORT char *strdepad(char *str);
 EXPORT wchar_t *wcsdepad(wchar_t *str);
 
+EXPORT char **strlist_split(const char *str, char split_ch, bool include_empty);
+EXPORT void strlist_free(char **strlist);
+
 static inline void dstr_init(struct dstr *dst);
 static inline void dstr_init_move(struct dstr *dst, struct dstr *src);
 static inline void dstr_init_move_array(struct dstr *dst, char *str);
diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt
index 241e4ebfa1571c2b6ae626adf979b146533038af..93df39bbda2e0de39e6438276990f1aebc392898 100644
--- a/plugins/CMakeLists.txt
+++ b/plugins/CMakeLists.txt
@@ -11,5 +11,6 @@ elseif("${CMAKE_SYSTEM_NAME}" MATCHES "Linux")
 	add_subdirectory(linux-pulseaudio)
 endif()
 
+add_subdirectory(obs-x264)
 add_subdirectory(obs-ffmpeg)
-add_subdirectory(obs-outputs)
+# add_subdirectory(obs-outputs)
diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-output.c b/plugins/obs-ffmpeg/obs-ffmpeg-output.c
index 7d93721e6be94cf74528b18384739a80c2364daa..2dc9192fe3c0996c4a202fc4841760953c166a43 100644
--- a/plugins/obs-ffmpeg/obs-ffmpeg-output.c
+++ b/plugins/obs-ffmpeg/obs-ffmpeg-output.c
@@ -513,7 +513,7 @@ static inline void copy_data(AVPicture *pic, const struct video_data *frame,
 	}
 }
 
-static void receive_video(void *param, const struct video_data *frame)
+static void receive_video(void *param, struct video_data *frame)
 {
 	struct ffmpeg_output *output = param;
 	struct ffmpeg_data   *data   = &output->ff_data;
@@ -527,7 +527,7 @@ static void receive_video(void *param, const struct video_data *frame)
 		data->start_timestamp = frame->timestamp;
 
 	if (context->pix_fmt != AV_PIX_FMT_YUV420P)
-		sws_scale(data->swscale, frame->data,
+		sws_scale(data->swscale, (const uint8_t *const *)frame->data,
 				(const int*)frame->linesize,
 				0, context->height, data->dst_picture.data,
 				data->dst_picture.linesize);
@@ -599,7 +599,7 @@ static inline void encode_audio(struct ffmpeg_output *output,
 			context->sample_fmt, data->samples[0],
 			(int)total_size, 1);
 	if (ret < 0) {
-		blog(LOG_WARNING, "receive_audio: avcodec_fill_audio_frame "
+		blog(LOG_WARNING, "encode_audio: avcodec_fill_audio_frame "
 		                  "failed: %s", av_err2str(ret));
 		return;
 	}
@@ -609,7 +609,7 @@ static inline void encode_audio(struct ffmpeg_output *output,
 	ret = avcodec_encode_audio2(context, &packet, data->aframe,
 			&got_packet);
 	if (ret < 0) {
-		blog(LOG_WARNING, "receive_audio: Error encoding audio: %s",
+		blog(LOG_WARNING, "encode_audio: Error encoding audio: %s",
 				av_err2str(ret));
 		return;
 	}
@@ -655,7 +655,7 @@ static bool prepare_audio(struct ffmpeg_data *data,
 	return true;
 }
 
-static void receive_audio(void *param, const struct audio_data *frame)
+static void receive_audio(void *param, struct audio_data *frame)
 {
 	struct ffmpeg_output *output = param;
 	struct ffmpeg_data   *data   = &output->ff_data;
diff --git a/plugins/obs-outputs/CMakeLists.txt b/plugins/obs-outputs/CMakeLists.txt
index 4e2fc447c3b3d4275cbc06bec32ae3039ce2ad55..23dcf9dce72b958d82b9222365bd088a6cac6c5b 100644
--- a/plugins/obs-outputs/CMakeLists.txt
+++ b/plugins/obs-outputs/CMakeLists.txt
@@ -5,17 +5,10 @@ include_directories(${Libx264_INCLUDE_DIR})
 
 set(obs-outputs_SOURCES
 	obs-outputs.c
-	obs-x264.c
 	rtmp-stream.c)
-
-set(obs-outputs_HEADERS
-	obs-outputs.h
-	obs-x264.h
-	rtmp-stream.h)
 	
 add_library(obs-outputs MODULE
-	${obs-outputs_SOURCES}
-	${obs-outputs_HEADERS})
+	${obs-outputs_SOURCES})
 target_link_libraries(obs-outputs
 	libobs
 	${Libx264_LIBRARIES})
diff --git a/plugins/obs-outputs/obs-outputs.c b/plugins/obs-outputs/obs-outputs.c
index a9dd9b99965f921bd5298a19f7f921f0cbd3576a..bbd183d27b64dfa780cde9631e1f98332d0d6fa1 100644
--- a/plugins/obs-outputs/obs-outputs.c
+++ b/plugins/obs-outputs/obs-outputs.c
@@ -1,12 +1,10 @@
-#include <string.h>
-#include "obs-outputs.h"
+#include <obs-module.h>
 
-static const char *outputs[] = {"rtmp_stream"};
+OBS_DECLARE_MODULE()
 
-const char *enum_outputs(size_t idx)
-{
-	if (idx >= sizeof(outputs)/sizeof(const char*))
-		return NULL;
 
-	return outputs[idx];
+bool obs_module_load(uint32_t libobs_ver)
+{
+	UNUSED_PARAMETER(libobs_ver);
+	return true;
 }
diff --git a/plugins/obs-outputs/obs-outputs.h b/plugins/obs-outputs/obs-outputs.h
deleted file mode 100644
index 0601083d23a9d3305cad15c32550f538bfc548dd..0000000000000000000000000000000000000000
--- a/plugins/obs-outputs/obs-outputs.h
+++ /dev/null
@@ -1,5 +0,0 @@
-#pragma once
-
-#include <util/c99defs.h>
-
-EXPORT const char *enum_outputs(size_t idx);
diff --git a/plugins/obs-outputs/obs-x264.c b/plugins/obs-outputs/obs-x264.c
deleted file mode 100644
index c762d50b4110dfc71b9c806db4fdb6c6b597f884..0000000000000000000000000000000000000000
--- a/plugins/obs-outputs/obs-x264.c
+++ /dev/null
@@ -1,58 +0,0 @@
-/******************************************************************************
-    Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
-
-    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-x264.h"
-
-const char *obs_x264_getname(const char *locale)
-{
-	/* TODO locale lookup */
-	return "x264 (Software)";
-}
-
-struct obs_x264 *obs_x264_create(obs_data_t settings, obs_encoder_t encoder)
-{
-	struct obs_x264 *data = bmalloc(sizeof(struct obs_x264));
-}
-
-void obs_x264_destroy(struct obs_x264 *data)
-{
-}
-
-void obs_x264_update(struct obs_x264 *data, obs_data_t settings)
-{
-}
-
-void obs_x264_reset(struct obs_x264 *data)
-{
-}
-
-int obs_x264_encode(struct obs_x264 *data, struct encoder_packet **packets)
-{
-}
-
-int obs_x264_getheader(struct obs_x264 *data, struct encoder_packet **packets)
-{
-}
-
-void obs_x264_setbitrate(struct obs_x264 *data, uint32_t bitrate,
-		uint32_t buffersize)
-{
-}
-
-void obs_x264_request_keyframe(struct obs_x264 *data)
-{
-}
diff --git a/plugins/obs-outputs/obs-x264.h b/plugins/obs-outputs/obs-x264.h
deleted file mode 100644
index df527d8735d1bc5ab3a9ccf28b8de2c43470e5c8..0000000000000000000000000000000000000000
--- a/plugins/obs-outputs/obs-x264.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/******************************************************************************
-    Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
-
-    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/>.
-******************************************************************************/
-
-#pragma once
-
-#include <util/c99defs.h>
-#include <obs.h>
-#include <x264.h>
-
-struct obs_x264 {
-	obs_encoder_t  encoder;
-
-	x264_param_t   params;
-	x264_t         *context;
-	x264_picture_t pic_out;
-};
-
-EXPORT const char *obs_x264_getname(const char *locale);
-
-EXPORT struct obs_x264 *obs_x264_create(obs_data_t settings,
-		obs_encoder_t encoder);
-EXPORT void obs_x264_destroy(struct obs_x264 *data);
-
-EXPORT void obs_x264_update(struct obs_x264 *data, obs_data_t settings);
-
-EXPORT void obs_x264_reset(struct obs_x264 *data);
-
-EXPORT int obs_x264_encode(struct obs_x264 *data,
-		struct encoder_packet **packets);
-EXPORT int obs_x264_getheader(struct obs_x264 *data,
-		struct encoder_packet **packets);
-
-EXPORT void obs_x264_setbitrate(struct obs_x264 *data, uint32_t bitrate,
-		uint32_t buffersize);
-EXPORT void obs_x264_request_keyframe(struct obs_x264 *data);
diff --git a/plugins/obs-outputs/rtmp-stream.c b/plugins/obs-outputs/rtmp-stream.c
index 4e5ada48287e51ab9fdc7f4e9b80e93b0d879f01..ad7481d3dc29439d85cdb80a589adf5559df6427 100644
--- a/plugins/obs-outputs/rtmp-stream.c
+++ b/plugins/obs-outputs/rtmp-stream.c
@@ -15,36 +15,45 @@
     along with this program.  If not, see <http://www.gnu.org/licenses/>.
 ******************************************************************************/
 
-#include "rtmp-stream.h"
+#include <obs.h>
 
-const char *rtmp_stream_getname(const char *locale)
+struct rtmp_stream {
+	obs_output_t  output;
+	obs_encoder_t video_encoder;
+	obs_encoder_t audio_encoder;
+	obs_service_t service;
+
+	bool active;
+};
+
+static const char *rtmp_stream_getname(const char *locale)
 {
 	/* TODO: locale stuff */
 	return "RTMP Stream";
 }
 
-void *rtmp_stream_create(obs_data_t settings, obs_output_t output)
+static void *rtmp_stream_create(obs_data_t settings, obs_output_t output)
 {
 	struct rtmp_stream *stream = bmalloc(sizeof(struct rtmp_stream));
 	memset(stream, 0, sizeof(struct rtmp_stream));
 }
 
-void rtmp_stream_destroy(struct rtmp_stream *stream)
+static void rtmp_stream_destroy(struct rtmp_stream *stream)
 {
 }
 
-void rtmp_stream_update(struct rtmp_stream *stream, obs_data_t settings)
+static void rtmp_stream_update(struct rtmp_stream *stream, obs_data_t settings)
 {
 }
 
-bool rtmp_stream_start(struct rtmp_stream *stream)
+static bool rtmp_stream_start(struct rtmp_stream *stream)
 {
 }
 
-void rtmp_stream_stop(struct rtmp_stream *stream)
+static void rtmp_stream_stop(struct rtmp_stream *stream)
 {
 }
 
-bool rtmp_stream_active(struct rtmp_stream *stream)
+static bool rtmp_stream_active(struct rtmp_stream *stream)
 {
 }
diff --git a/plugins/obs-outputs/rtmp-stream.h b/plugins/obs-outputs/rtmp-stream.h
deleted file mode 100644
index 79f7546a5c402bd540465a394d5c2095cdf8f912..0000000000000000000000000000000000000000
--- a/plugins/obs-outputs/rtmp-stream.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/******************************************************************************
-    Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
-
-    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/>.
-******************************************************************************/
-
-#pragma once
-
-#include <util/c99defs.h>
-#include <obs.h>
-
-struct rtmp_stream {
-	obs_output_t  output;
-	obs_encoder_t video_encoder;
-	obs_encoder_t audio_encoder;
-	obs_service_t service;
-
-	bool active;
-};
-
-EXPORT const char *rtmp_stream_getname(const char *locale);
-EXPORT void *rtmp_stream_create(obs_data_t settings, obs_output_t output);
-EXPORT void rtmp_stream_destroy(struct rtmp_stream *stream);
-EXPORT void rtmp_stream_update(struct rtmp_stream *stream, obs_data_t settings);
-EXPORT bool rtmp_stream_start(struct rtmp_stream *stream);
-EXPORT void rtmp_stream_stop(struct rtmp_stream *stream);
-EXPORT bool rtmp_stream_active(struct rtmp_stream *stream);
diff --git a/plugins/obs-x264/CMakeLists.txt b/plugins/obs-x264/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..c4d655a80238705f3e744bbbaef607328830a733
--- /dev/null
+++ b/plugins/obs-x264/CMakeLists.txt
@@ -0,0 +1,19 @@
+project(obs-x264)
+
+find_package(Libx264 REQUIRED)
+include_directories(${Libx264_INCLUDE_DIR})
+add_definitions(${Libx264_DEFINITIONS})
+
+set(obs-x264_SOURCES
+	obs-x264.c
+	obs-x264-plugin-main.c)
+
+add_library(obs-x264 MODULE
+	${obs-x264_SOURCES})
+target_link_libraries(obs-x264
+	libobs
+	${Libx264_LIBRARIES})
+
+install_obs_plugin(obs-x264)
+
+obs_fixup_install_target(obs-x264 PATH ${Libx264_LIBRARIES})
diff --git a/plugins/obs-x264/obs-x264-plugin-main.c b/plugins/obs-x264/obs-x264-plugin-main.c
new file mode 100644
index 0000000000000000000000000000000000000000..4086afa327f6d04b9637b04e4d079ceb2b87298d
--- /dev/null
+++ b/plugins/obs-x264/obs-x264-plugin-main.c
@@ -0,0 +1,13 @@
+#include <obs-module.h>
+
+OBS_DECLARE_MODULE()
+
+extern struct obs_encoder_info obs_x264_encoder;
+
+bool obs_module_load(uint32_t libobs_ver)
+{
+	obs_register_encoder(&obs_x264_encoder);
+
+	UNUSED_PARAMETER(libobs_ver);
+	return true;
+}
diff --git a/plugins/obs-x264/obs-x264.c b/plugins/obs-x264/obs-x264.c
new file mode 100644
index 0000000000000000000000000000000000000000..fb284e3b77af9f8c7c96ae9e89f4cad17c95713d
--- /dev/null
+++ b/plugins/obs-x264/obs-x264.c
@@ -0,0 +1,506 @@
+/******************************************************************************
+    Copyright (C) 2014 by Hugh Bailey <obs.jim@gmail.com>
+
+    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 <util/dstr.h>
+#include <util/darray.h>
+#include <obs.h>
+#include <x264.h>
+
+struct obs_x264 {
+	obs_encoder_t   encoder;
+
+	x264_param_t    params;
+	x264_t          *context;
+
+	DARRAY(uint8_t) packet_data;
+
+	uint8_t         *extra_data;
+	uint8_t         *sei;
+
+	size_t          extra_data_size;
+	size_t          sei_size;
+};
+
+
+/* ------------------------------------------------------------------------- */
+
+static const char *obs_x264_getname(const char *locale)
+{
+	/* TODO locale lookup */
+	UNUSED_PARAMETER(locale);
+	return "x264";
+}
+
+static void obs_x264_stop(void *data);
+
+static void obs_x264_destroy(void *data)
+{
+	struct obs_x264 *obsx264 = data;
+
+	if (obsx264) {
+		obs_x264_stop(obsx264);
+		da_free(obsx264->packet_data);
+		bfree(obsx264);
+	}
+}
+
+static void obs_x264_defaults(obs_data_t settings)
+{
+	obs_data_set_default_int   (settings, "bitrate",     1000);
+	obs_data_set_default_int   (settings, "buffer_size", 1000);
+	obs_data_set_default_int   (settings, "keyint_sec",  0);
+
+	obs_data_set_default_string(settings, "preset",      "veryfast");
+	obs_data_set_default_string(settings, "profile",     "");
+	obs_data_set_default_string(settings, "tune",        "");
+	obs_data_set_default_string(settings, "x264opts",    "");
+}
+
+static inline void add_strings(obs_property_t list, const char *const *strings)
+{
+	while (*strings) {
+		obs_property_list_add_item(list, *strings, *strings);
+		strings++;
+	}
+}
+
+static obs_properties_t obs_x264_props(const char *locale)
+{
+	UNUSED_PARAMETER(locale);
+	/* TODO: locale */
+
+	obs_properties_t props = obs_properties_create();
+	obs_property_t list;
+
+	obs_properties_add_int(props, "bitrate", "Bitrate", 50, 100000, 1);
+	obs_properties_add_int(props, "buffer_size", "Buffer Size", 50, 100000,
+			1);
+	obs_properties_add_int(props,
+			"keyint_sec", "Keyframe interval (seconds, 0=auto)",
+			0, 20, 1);
+
+	list = obs_properties_add_list(props,
+			"preset", "CPU Usage Preset (encoder speed)",
+			OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING);
+	add_strings(list, x264_preset_names);
+
+	list = obs_properties_add_list(props, "profile", "Profile",
+			OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING);
+	obs_property_list_add_item(list, "baseline", "baseline");
+	obs_property_list_add_item(list, "main", "main");
+	obs_property_list_add_item(list, "high", "high");
+
+	list = obs_properties_add_list(props, "tune", "Tune",
+			OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING);
+	add_strings(list, x264_tune_names);
+
+	obs_properties_add_text(props, "x264opts",
+			"x264 encoder options (separated by ':')");
+
+	return props;
+}
+
+static bool getparam(const char *param, char **name, const char **value)
+{
+	const char *assign;
+
+	if (!param || !*param || (*param == '='))
+		return false;
+
+	assign = strchr(param, '=');
+	if (!assign || !*assign || !*(assign+1))
+		return false;
+
+	*name  = bstrdup_n(param, assign-param);
+	*value = assign+1;
+	return true;
+}
+
+static void override_base_param(const char *param,
+		char **preset, char **profile, char **tune)
+{
+	char       *name;
+	const char *val;
+
+	if (getparam(param, &name, &val)) {
+		if (astrcmpi(name, "preset") == 0) {
+			bfree(*preset);
+			*preset = bstrdup(val);
+
+		} else if (astrcmpi(name, "profile") == 0) {
+			bfree(*profile);
+			*profile = bstrdup(val);
+
+		} else if (astrcmpi(name, "tune") == 0) {
+			bfree(*tune);
+			*tune = bstrdup(val);
+		}
+
+		bfree(name);
+	}
+}
+
+static inline void override_base_params(char **params,
+		char **preset, char **profile, char **tune)
+{
+	while (*params)
+		override_base_param(*(params++), preset, profile, tune);
+}
+
+static inline void set_param(struct obs_x264 *obsx264, const char *param)
+{
+	char       *name;
+	const char *val;
+
+	if (getparam(param, &name, &val)) {
+		if (x264_param_parse(&obsx264->params, name, val) != 0)
+			blog(LOG_WARNING, "x264 param: %s failed", param);
+
+		bfree(name);
+	}
+}
+
+static inline void apply_x264_profile(struct obs_x264 *obsx264,
+		const char *profile)
+{
+	if (!*profile) profile = NULL;
+
+	if (!obsx264->context && profile) {
+		int ret = x264_param_apply_profile(&obsx264->params, profile);
+		if (ret != 0)
+			blog(LOG_WARNING, "Failed to set x264 "
+					"profile '%s'", profile);
+	}
+}
+
+static bool reset_x264_params(struct obs_x264 *obsx264,
+		const char *preset, const char *tune)
+{
+	if (!*preset)  preset  = NULL;
+	if (!*tune)    tune    = NULL;
+
+	return x264_param_default_preset(&obsx264->params, preset, tune) == 0;
+}
+
+static void log_x264(void *param, int level, const char *format, va_list args)
+{
+	blogva(LOG_INFO, format, args);
+
+	UNUSED_PARAMETER(param);
+	UNUSED_PARAMETER(level);
+}
+
+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_getinfo(video);
+
+	int bitrate      = (int)obs_data_getint(settings, "bitrate");
+	int buffer_size  = (int)obs_data_getint(settings, "buffer_size");
+	int keyint_sec   = (int)obs_data_getint(settings, "keyint_sec");
+
+	if (keyint_sec)
+		obsx264->params.i_keyint_max =
+			keyint_sec * voi->fps_num / voi->fps_den;
+
+	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              = voi->width;
+	obsx264->params.i_height             = voi->height;
+	obsx264->params.i_fps_num            = voi->fps_num;
+	obsx264->params.i_fps_den            = voi->fps_den;
+	obsx264->params.i_timebase_num       = voi->fps_den;
+	obsx264->params.i_timebase_den       = voi->fps_num;
+	obsx264->params.pf_log               = log_x264;
+	obsx264->params.i_log_level          = X264_LOG_WARNING;
+
+	if (voi->format == VIDEO_FORMAT_NV12)
+		obsx264->params.i_csp = X264_CSP_NV12;
+	else if (voi->format == VIDEO_FORMAT_I420)
+		obsx264->params.i_csp = X264_CSP_I420;
+	else
+		obsx264->params.i_csp = X264_CSP_NV12;
+
+	while (*params)
+		set_param(obsx264, *(params++));
+}
+
+static bool update_settings(struct obs_x264 *obsx264, obs_data_t settings)
+{
+	char *preset     = bstrdup(obs_data_getstring(settings, "preset"));
+	char *profile    = bstrdup(obs_data_getstring(settings, "profile"));
+	char *tune       = bstrdup(obs_data_getstring(settings, "tune"));
+	const char *opts = obs_data_getstring(settings, "x264opts");
+
+	char **paramlist;
+	bool success = true;
+
+	paramlist = strlist_split(opts, ':', false);
+
+	if (!obsx264->context) {
+		override_base_params(paramlist, &preset, &tune, &profile);
+		success = reset_x264_params(obsx264, preset, tune);
+	}
+
+	if (success) {
+		update_params(obsx264, settings, paramlist);
+
+		if (!obsx264->context)
+			apply_x264_profile(obsx264, profile);
+	}
+
+	strlist_free(paramlist);
+	bfree(preset);
+	bfree(profile);
+	bfree(tune);
+
+	return success;
+}
+
+static bool obs_x264_update(void *data, obs_data_t settings)
+{
+	struct obs_x264 *obsx264 = data;
+	bool success = update_settings(obsx264, settings);
+	int ret;
+
+	if (success) {
+		ret = x264_encoder_reconfig(obsx264->context, &obsx264->params);
+		if (ret != 0)
+			blog(LOG_WARNING, "Failed to reconfigure x264: %d",
+					ret);
+		return ret == 0;
+	}
+
+	return false;
+}
+
+static void load_headers(struct obs_x264 *obsx264)
+{
+	x264_nal_t      *nals;
+	int             nal_count;
+	DARRAY(uint8_t) header;
+	DARRAY(uint8_t) sei;
+
+	da_init(header);
+	da_init(sei);
+
+	x264_encoder_headers(obsx264->context, &nals, &nal_count);
+
+	for (int i = 0; i < nal_count; i++) {
+		x264_nal_t *nal = nals+i;
+
+		if (nal->i_type == NAL_SEI)
+			da_push_back_array(sei, nal->p_payload, nal->i_payload);
+		else
+			da_push_back_array(header, nal->p_payload,
+					nal->i_payload);
+	}
+
+	obsx264->extra_data      = header.array;
+	obsx264->extra_data_size = header.num;
+	obsx264->sei             = sei.array;
+	obsx264->sei_size        = sei.num;
+}
+
+static bool obs_x264_start(void *data, obs_data_t settings)
+{
+	struct obs_x264 *obsx264 = data;
+
+	assert(obsx264->context == NULL);
+	obs_x264_stop(data);
+
+	if (update_settings(obsx264, settings)) {
+		obsx264->context = x264_encoder_open(&obsx264->params);
+
+		if (obsx264->context == NULL)
+			blog(LOG_WARNING, "x264 failed to load");
+		else
+			load_headers(obsx264);
+	} else {
+		blog(LOG_WARNING, "bad settings specified for x264");
+	}
+
+	return obsx264->context != NULL;
+}
+
+static void obs_x264_stop(void *data)
+{
+	struct obs_x264 *obsx264 = data;
+
+	if (obsx264->context) {
+		x264_encoder_close(obsx264->context);
+		bfree(obsx264->sei);
+		bfree(obsx264->extra_data);
+
+		obsx264->context    = NULL;
+		obsx264->sei        = NULL;
+		obsx264->extra_data = NULL;
+	}
+}
+
+static void *obs_x264_create(obs_data_t settings, obs_encoder_t encoder)
+{
+	struct obs_x264 *data = bzalloc(sizeof(struct obs_x264));
+	data->encoder = encoder;
+
+	obs_x264_defaults(settings);
+	return data;
+}
+
+static inline int drop_priority(int priority)
+{
+	switch (priority) {
+	case NAL_PRIORITY_DISPOSABLE: return NAL_PRIORITY_DISPOSABLE;
+	case NAL_PRIORITY_LOW:        return NAL_PRIORITY_LOW;
+	case NAL_PRIORITY_HIGH:       return NAL_PRIORITY_HIGHEST;
+	case NAL_PRIORITY_HIGHEST:    return NAL_PRIORITY_HIGHEST;
+	}
+
+	return NAL_PRIORITY_HIGHEST;
+}
+
+static void parse_packet(struct obs_x264 *obsx264,
+		struct encoder_packet *packet, x264_nal_t *nals,
+		int nal_count, x264_picture_t *pic_out)
+{
+	if (!nal_count) return;
+
+	da_resize(obsx264->packet_data, 0);
+
+	for (int i = 0; i < nal_count; i++) {
+		x264_nal_t *nal = nals+i;
+		da_push_back_array(obsx264->packet_data, nal->p_payload,
+				nal->i_payload);
+	}
+
+	packet->data          = obsx264->packet_data.array;
+	packet->size          = obsx264->packet_data.num;
+	packet->type          = OBS_ENCODER_VIDEO;
+	packet->pts           = pic_out->i_pts;
+	packet->dts           = pic_out->i_dts;
+	packet->keyframe      = nals[0].i_type == NAL_SLICE_IDR;
+	packet->priority      = nals[0].i_ref_idc;
+	packet->drop_priority = drop_priority(nals[0].i_ref_idc);
+}
+
+static inline void init_pic_data(struct obs_x264 *obsx264, x264_picture_t *pic,
+		struct encoder_frame *frame)
+{
+	x264_picture_init(pic);
+
+	pic->i_pts = frame->pts;
+	pic->img.i_csp = obsx264->params.i_csp;
+
+	if (obsx264->params.i_csp == X264_CSP_NV12)
+		pic->img.i_plane = 2;
+	else if (obsx264->params.i_csp == X264_CSP_I420)
+		pic->img.i_plane = 3;
+
+	for (int i = 0; i < pic->img.i_plane; i++) {
+		pic->img.i_stride[i] = (int)frame->linesize[i];
+		pic->img.plane[i]    = frame->data[i];
+	}
+}
+
+static bool obs_x264_encode(void *data, struct encoder_frame *frame,
+		struct encoder_packet *packet, bool *received_packet)
+{
+	struct obs_x264 *obsx264 = data;
+	x264_nal_t      *nals;
+	int             nal_count;
+	int             ret;
+	x264_picture_t  pic, pic_out;
+
+	if (!frame || !packet || !received_packet)
+		return false;
+
+	init_pic_data(obsx264, &pic, frame);
+
+	ret = x264_encoder_encode(obsx264->context, &nals, &nal_count, &pic,
+			&pic_out);
+	if (ret < 0) {
+		blog(LOG_WARNING, "x264 encode failed");
+		return false;
+	}
+
+	*received_packet = (nal_count != 0);
+	parse_packet(obsx264, packet, nals, nal_count, &pic_out);
+
+	return true;
+}
+
+static bool obs_x264_extra_data(void *data, uint8_t **extra_data, size_t *size)
+{
+	struct obs_x264 *obsx264 = data;
+
+	if (!obsx264->context)
+		return false;
+
+	*extra_data = obsx264->extra_data;
+	*size       = obsx264->extra_data_size;
+	return true;
+}
+
+static bool obs_x264_sei(void *data, uint8_t **sei, size_t *size)
+{
+	struct obs_x264 *obsx264 = data;
+
+	if (!obsx264->context)
+		return false;
+
+	*sei  = obsx264->sei;
+	*size = obsx264->sei_size;
+	return true;
+}
+
+static bool obs_x264_video_info(void *data, struct video_scale_info *info)
+{
+	struct obs_x264 *obsx264 = data;
+	video_t video = obs_encoder_video(obsx264->encoder);
+	const struct video_output_info *vid_info = video_output_getinfo(video);
+
+	if (vid_info->format == VIDEO_FORMAT_I420 ||
+	    vid_info->format == VIDEO_FORMAT_NV12)
+		return false;
+
+	info->format     = VIDEO_FORMAT_NV12;
+	info->width      = vid_info->width;
+	info->height     = vid_info->height;
+	info->range      = VIDEO_RANGE_DEFAULT;
+	info->colorspace = VIDEO_CS_DEFAULT;
+	return true;
+}
+
+struct obs_encoder_info obs_x264_encoder = {
+	.id         = "obs_x264",
+	.type       = OBS_ENCODER_VIDEO,
+	.codec      = "h264",
+	.getname    = obs_x264_getname,
+	.create     = obs_x264_create,
+	.destroy    = obs_x264_destroy,
+	.start      = obs_x264_start,
+	.stop       = obs_x264_stop,
+	.encode     = obs_x264_encode,
+	.properties = obs_x264_props,
+	.defaults   = obs_x264_defaults,
+	.update     = obs_x264_update,
+	.extra_data = obs_x264_extra_data,
+	.sei_data   = obs_x264_sei,
+	.video_info = obs_x264_video_info
+};
diff --git a/vs/2013/OBS.sln b/vs/2013/OBS.sln
index 1fa95e3f1be44c2d01f7383dee3c92b0f627c305..f5ab29186ef7046a548a25150e1231cca1758962 100644
--- a/vs/2013/OBS.sln
+++ b/vs/2013/OBS.sln
@@ -43,102 +43,162 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "win-wasapi", "win-wasapi\wi
 EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "win-capture", "win-capture\win-capture.vcxproj", "{AB83E5F0-D76E-45AB-A4C9-4711B1BE6916}"
 EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "obs-x264", "obs-x264\obs-x264.vcxproj", "{1798CAF2-531A-4E0E-A722-29F4B1B5D49F}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Mixed Platforms = Debug|Mixed Platforms
 		Debug|Win32 = Debug|Win32
 		Debug|x64 = Debug|x64
+		Release|Mixed Platforms = Release|Mixed Platforms
 		Release|Win32 = Release|Win32
 		Release|x64 = Release|x64
 	EndGlobalSection
 	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{6F1AC2AE-6424-401A-AF9F-A771E6BEE026}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
+		{6F1AC2AE-6424-401A-AF9F-A771E6BEE026}.Debug|Mixed Platforms.Build.0 = Debug|Win32
 		{6F1AC2AE-6424-401A-AF9F-A771E6BEE026}.Debug|Win32.ActiveCfg = Debug|Win32
 		{6F1AC2AE-6424-401A-AF9F-A771E6BEE026}.Debug|Win32.Build.0 = Debug|Win32
 		{6F1AC2AE-6424-401A-AF9F-A771E6BEE026}.Debug|x64.ActiveCfg = Debug|x64
 		{6F1AC2AE-6424-401A-AF9F-A771E6BEE026}.Debug|x64.Build.0 = Debug|x64
+		{6F1AC2AE-6424-401A-AF9F-A771E6BEE026}.Release|Mixed Platforms.ActiveCfg = Release|Win32
+		{6F1AC2AE-6424-401A-AF9F-A771E6BEE026}.Release|Mixed Platforms.Build.0 = Release|Win32
 		{6F1AC2AE-6424-401A-AF9F-A771E6BEE026}.Release|Win32.ActiveCfg = Release|Win32
 		{6F1AC2AE-6424-401A-AF9F-A771E6BEE026}.Release|Win32.Build.0 = Release|Win32
 		{6F1AC2AE-6424-401A-AF9F-A771E6BEE026}.Release|x64.ActiveCfg = Release|x64
 		{6F1AC2AE-6424-401A-AF9F-A771E6BEE026}.Release|x64.Build.0 = Release|x64
+		{68A84F9A-5B89-4E7D-8183-87FEA5DC65F6}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
+		{68A84F9A-5B89-4E7D-8183-87FEA5DC65F6}.Debug|Mixed Platforms.Build.0 = Debug|Win32
 		{68A84F9A-5B89-4E7D-8183-87FEA5DC65F6}.Debug|Win32.ActiveCfg = Debug|Win32
 		{68A84F9A-5B89-4E7D-8183-87FEA5DC65F6}.Debug|Win32.Build.0 = Debug|Win32
 		{68A84F9A-5B89-4E7D-8183-87FEA5DC65F6}.Debug|x64.ActiveCfg = Debug|x64
 		{68A84F9A-5B89-4E7D-8183-87FEA5DC65F6}.Debug|x64.Build.0 = Debug|x64
+		{68A84F9A-5B89-4E7D-8183-87FEA5DC65F6}.Release|Mixed Platforms.ActiveCfg = Release|Win32
+		{68A84F9A-5B89-4E7D-8183-87FEA5DC65F6}.Release|Mixed Platforms.Build.0 = Release|Win32
 		{68A84F9A-5B89-4E7D-8183-87FEA5DC65F6}.Release|Win32.ActiveCfg = Release|Win32
 		{68A84F9A-5B89-4E7D-8183-87FEA5DC65F6}.Release|Win32.Build.0 = Release|Win32
 		{68A84F9A-5B89-4E7D-8183-87FEA5DC65F6}.Release|x64.ActiveCfg = Release|x64
 		{68A84F9A-5B89-4E7D-8183-87FEA5DC65F6}.Release|x64.Build.0 = Release|x64
+		{E11367B7-20CC-4741-B8E0-C20E85302A40}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
+		{E11367B7-20CC-4741-B8E0-C20E85302A40}.Debug|Mixed Platforms.Build.0 = Debug|Win32
 		{E11367B7-20CC-4741-B8E0-C20E85302A40}.Debug|Win32.ActiveCfg = Debug|Win32
 		{E11367B7-20CC-4741-B8E0-C20E85302A40}.Debug|Win32.Build.0 = Debug|Win32
 		{E11367B7-20CC-4741-B8E0-C20E85302A40}.Debug|x64.ActiveCfg = Debug|x64
 		{E11367B7-20CC-4741-B8E0-C20E85302A40}.Debug|x64.Build.0 = Debug|x64
+		{E11367B7-20CC-4741-B8E0-C20E85302A40}.Release|Mixed Platforms.ActiveCfg = Release|Win32
+		{E11367B7-20CC-4741-B8E0-C20E85302A40}.Release|Mixed Platforms.Build.0 = Release|Win32
 		{E11367B7-20CC-4741-B8E0-C20E85302A40}.Release|Win32.ActiveCfg = Release|Win32
 		{E11367B7-20CC-4741-B8E0-C20E85302A40}.Release|Win32.Build.0 = Release|Win32
 		{E11367B7-20CC-4741-B8E0-C20E85302A40}.Release|x64.ActiveCfg = Release|x64
 		{E11367B7-20CC-4741-B8E0-C20E85302A40}.Release|x64.Build.0 = Release|x64
+		{82C863C3-74C8-43BE-90CF-755EE698F2E8}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
+		{82C863C3-74C8-43BE-90CF-755EE698F2E8}.Debug|Mixed Platforms.Build.0 = Debug|Win32
 		{82C863C3-74C8-43BE-90CF-755EE698F2E8}.Debug|Win32.ActiveCfg = Debug|Win32
 		{82C863C3-74C8-43BE-90CF-755EE698F2E8}.Debug|Win32.Build.0 = Debug|Win32
 		{82C863C3-74C8-43BE-90CF-755EE698F2E8}.Debug|x64.ActiveCfg = Debug|x64
 		{82C863C3-74C8-43BE-90CF-755EE698F2E8}.Debug|x64.Build.0 = Debug|x64
+		{82C863C3-74C8-43BE-90CF-755EE698F2E8}.Release|Mixed Platforms.ActiveCfg = Release|Win32
+		{82C863C3-74C8-43BE-90CF-755EE698F2E8}.Release|Mixed Platforms.Build.0 = Release|Win32
 		{82C863C3-74C8-43BE-90CF-755EE698F2E8}.Release|Win32.ActiveCfg = Release|Win32
 		{82C863C3-74C8-43BE-90CF-755EE698F2E8}.Release|Win32.Build.0 = Release|Win32
 		{82C863C3-74C8-43BE-90CF-755EE698F2E8}.Release|x64.ActiveCfg = Release|x64
 		{82C863C3-74C8-43BE-90CF-755EE698F2E8}.Release|x64.Build.0 = Release|x64
+		{760ECBBC-EA7C-464A-B60E-945A0BB1B100}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
+		{760ECBBC-EA7C-464A-B60E-945A0BB1B100}.Debug|Mixed Platforms.Build.0 = Debug|Win32
 		{760ECBBC-EA7C-464A-B60E-945A0BB1B100}.Debug|Win32.ActiveCfg = Debug|Win32
 		{760ECBBC-EA7C-464A-B60E-945A0BB1B100}.Debug|Win32.Build.0 = Debug|Win32
 		{760ECBBC-EA7C-464A-B60E-945A0BB1B100}.Debug|x64.ActiveCfg = Debug|x64
 		{760ECBBC-EA7C-464A-B60E-945A0BB1B100}.Debug|x64.Build.0 = Debug|x64
+		{760ECBBC-EA7C-464A-B60E-945A0BB1B100}.Release|Mixed Platforms.ActiveCfg = Release|Win32
+		{760ECBBC-EA7C-464A-B60E-945A0BB1B100}.Release|Mixed Platforms.Build.0 = Release|Win32
 		{760ECBBC-EA7C-464A-B60E-945A0BB1B100}.Release|Win32.ActiveCfg = Release|Win32
 		{760ECBBC-EA7C-464A-B60E-945A0BB1B100}.Release|Win32.Build.0 = Release|Win32
 		{760ECBBC-EA7C-464A-B60E-945A0BB1B100}.Release|x64.ActiveCfg = Release|x64
 		{760ECBBC-EA7C-464A-B60E-945A0BB1B100}.Release|x64.Build.0 = Release|x64
+		{B6EAE19B-79BF-4F7C-9E66-976D14B9DC6C}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
+		{B6EAE19B-79BF-4F7C-9E66-976D14B9DC6C}.Debug|Mixed Platforms.Build.0 = Debug|Win32
 		{B6EAE19B-79BF-4F7C-9E66-976D14B9DC6C}.Debug|Win32.ActiveCfg = Debug|Win32
 		{B6EAE19B-79BF-4F7C-9E66-976D14B9DC6C}.Debug|Win32.Build.0 = Debug|Win32
 		{B6EAE19B-79BF-4F7C-9E66-976D14B9DC6C}.Debug|x64.ActiveCfg = Debug|x64
 		{B6EAE19B-79BF-4F7C-9E66-976D14B9DC6C}.Debug|x64.Build.0 = Debug|x64
+		{B6EAE19B-79BF-4F7C-9E66-976D14B9DC6C}.Release|Mixed Platforms.ActiveCfg = Release|Win32
+		{B6EAE19B-79BF-4F7C-9E66-976D14B9DC6C}.Release|Mixed Platforms.Build.0 = Release|Win32
 		{B6EAE19B-79BF-4F7C-9E66-976D14B9DC6C}.Release|Win32.ActiveCfg = Release|Win32
 		{B6EAE19B-79BF-4F7C-9E66-976D14B9DC6C}.Release|Win32.Build.0 = Release|Win32
 		{B6EAE19B-79BF-4F7C-9E66-976D14B9DC6C}.Release|x64.ActiveCfg = Release|x64
 		{B6EAE19B-79BF-4F7C-9E66-976D14B9DC6C}.Release|x64.Build.0 = Release|x64
+		{76226D20-1972-4789-A595-EDACC7A76DC3}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
+		{76226D20-1972-4789-A595-EDACC7A76DC3}.Debug|Mixed Platforms.Build.0 = Debug|Win32
 		{76226D20-1972-4789-A595-EDACC7A76DC3}.Debug|Win32.ActiveCfg = Debug|Win32
 		{76226D20-1972-4789-A595-EDACC7A76DC3}.Debug|Win32.Build.0 = Debug|Win32
 		{76226D20-1972-4789-A595-EDACC7A76DC3}.Debug|x64.ActiveCfg = Debug|Win32
 		{76226D20-1972-4789-A595-EDACC7A76DC3}.Debug|x64.Build.0 = Debug|Win32
+		{76226D20-1972-4789-A595-EDACC7A76DC3}.Release|Mixed Platforms.ActiveCfg = Release|Win32
+		{76226D20-1972-4789-A595-EDACC7A76DC3}.Release|Mixed Platforms.Build.0 = Release|Win32
 		{76226D20-1972-4789-A595-EDACC7A76DC3}.Release|Win32.ActiveCfg = Release|Win32
 		{76226D20-1972-4789-A595-EDACC7A76DC3}.Release|Win32.Build.0 = Release|Win32
 		{76226D20-1972-4789-A595-EDACC7A76DC3}.Release|x64.ActiveCfg = Release|Win32
 		{76226D20-1972-4789-A595-EDACC7A76DC3}.Release|x64.Build.0 = Release|Win32
+		{36970254-B1E5-4BE6-A561-301B6E7CDCE3}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
+		{36970254-B1E5-4BE6-A561-301B6E7CDCE3}.Debug|Mixed Platforms.Build.0 = Debug|Win32
 		{36970254-B1E5-4BE6-A561-301B6E7CDCE3}.Debug|Win32.ActiveCfg = Debug|Win32
 		{36970254-B1E5-4BE6-A561-301B6E7CDCE3}.Debug|Win32.Build.0 = Debug|Win32
 		{36970254-B1E5-4BE6-A561-301B6E7CDCE3}.Debug|x64.ActiveCfg = Debug|x64
 		{36970254-B1E5-4BE6-A561-301B6E7CDCE3}.Debug|x64.Build.0 = Debug|x64
+		{36970254-B1E5-4BE6-A561-301B6E7CDCE3}.Release|Mixed Platforms.ActiveCfg = Release|Win32
+		{36970254-B1E5-4BE6-A561-301B6E7CDCE3}.Release|Mixed Platforms.Build.0 = Release|Win32
 		{36970254-B1E5-4BE6-A561-301B6E7CDCE3}.Release|Win32.ActiveCfg = Release|Win32
 		{36970254-B1E5-4BE6-A561-301B6E7CDCE3}.Release|Win32.Build.0 = Release|Win32
 		{36970254-B1E5-4BE6-A561-301B6E7CDCE3}.Release|x64.ActiveCfg = Release|x64
 		{36970254-B1E5-4BE6-A561-301B6E7CDCE3}.Release|x64.Build.0 = Release|x64
+		{B12702AD-ABFB-343A-A199-8E24837244A3}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
+		{B12702AD-ABFB-343A-A199-8E24837244A3}.Debug|Mixed Platforms.Build.0 = Debug|Win32
 		{B12702AD-ABFB-343A-A199-8E24837244A3}.Debug|Win32.ActiveCfg = Debug|Win32
 		{B12702AD-ABFB-343A-A199-8E24837244A3}.Debug|Win32.Build.0 = Debug|Win32
 		{B12702AD-ABFB-343A-A199-8E24837244A3}.Debug|x64.ActiveCfg = Debug|x64
 		{B12702AD-ABFB-343A-A199-8E24837244A3}.Debug|x64.Build.0 = Debug|x64
+		{B12702AD-ABFB-343A-A199-8E24837244A3}.Release|Mixed Platforms.ActiveCfg = Release|Win32
+		{B12702AD-ABFB-343A-A199-8E24837244A3}.Release|Mixed Platforms.Build.0 = Release|Win32
 		{B12702AD-ABFB-343A-A199-8E24837244A3}.Release|Win32.ActiveCfg = Release|Win32
 		{B12702AD-ABFB-343A-A199-8E24837244A3}.Release|Win32.Build.0 = Release|Win32
 		{B12702AD-ABFB-343A-A199-8E24837244A3}.Release|x64.ActiveCfg = Release|x64
 		{B12702AD-ABFB-343A-A199-8E24837244A3}.Release|x64.Build.0 = Release|x64
+		{A3D24C9D-669D-4DDF-91BA-152D7DDD7C04}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
+		{A3D24C9D-669D-4DDF-91BA-152D7DDD7C04}.Debug|Mixed Platforms.Build.0 = Debug|Win32
 		{A3D24C9D-669D-4DDF-91BA-152D7DDD7C04}.Debug|Win32.ActiveCfg = Debug|Win32
 		{A3D24C9D-669D-4DDF-91BA-152D7DDD7C04}.Debug|Win32.Build.0 = Debug|Win32
 		{A3D24C9D-669D-4DDF-91BA-152D7DDD7C04}.Debug|x64.ActiveCfg = Debug|x64
 		{A3D24C9D-669D-4DDF-91BA-152D7DDD7C04}.Debug|x64.Build.0 = Debug|x64
+		{A3D24C9D-669D-4DDF-91BA-152D7DDD7C04}.Release|Mixed Platforms.ActiveCfg = Release|Win32
+		{A3D24C9D-669D-4DDF-91BA-152D7DDD7C04}.Release|Mixed Platforms.Build.0 = Release|Win32
 		{A3D24C9D-669D-4DDF-91BA-152D7DDD7C04}.Release|Win32.ActiveCfg = Release|Win32
 		{A3D24C9D-669D-4DDF-91BA-152D7DDD7C04}.Release|Win32.Build.0 = Release|Win32
 		{A3D24C9D-669D-4DDF-91BA-152D7DDD7C04}.Release|x64.ActiveCfg = Release|x64
 		{A3D24C9D-669D-4DDF-91BA-152D7DDD7C04}.Release|x64.Build.0 = Release|x64
+		{AB83E5F0-D76E-45AB-A4C9-4711B1BE6916}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
+		{AB83E5F0-D76E-45AB-A4C9-4711B1BE6916}.Debug|Mixed Platforms.Build.0 = Debug|Win32
 		{AB83E5F0-D76E-45AB-A4C9-4711B1BE6916}.Debug|Win32.ActiveCfg = Debug|Win32
 		{AB83E5F0-D76E-45AB-A4C9-4711B1BE6916}.Debug|Win32.Build.0 = Debug|Win32
 		{AB83E5F0-D76E-45AB-A4C9-4711B1BE6916}.Debug|x64.ActiveCfg = Debug|x64
 		{AB83E5F0-D76E-45AB-A4C9-4711B1BE6916}.Debug|x64.Build.0 = Debug|x64
+		{AB83E5F0-D76E-45AB-A4C9-4711B1BE6916}.Release|Mixed Platforms.ActiveCfg = Release|Win32
+		{AB83E5F0-D76E-45AB-A4C9-4711B1BE6916}.Release|Mixed Platforms.Build.0 = Release|Win32
 		{AB83E5F0-D76E-45AB-A4C9-4711B1BE6916}.Release|Win32.ActiveCfg = Release|Win32
 		{AB83E5F0-D76E-45AB-A4C9-4711B1BE6916}.Release|Win32.Build.0 = Release|Win32
 		{AB83E5F0-D76E-45AB-A4C9-4711B1BE6916}.Release|x64.ActiveCfg = Release|x64
 		{AB83E5F0-D76E-45AB-A4C9-4711B1BE6916}.Release|x64.Build.0 = Release|x64
+		{1798CAF2-531A-4E0E-A722-29F4B1B5D49F}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
+		{1798CAF2-531A-4E0E-A722-29F4B1B5D49F}.Debug|Mixed Platforms.Build.0 = Debug|Win32
+		{1798CAF2-531A-4E0E-A722-29F4B1B5D49F}.Debug|Win32.ActiveCfg = Debug|Win32
+		{1798CAF2-531A-4E0E-A722-29F4B1B5D49F}.Debug|Win32.Build.0 = Debug|Win32
+		{1798CAF2-531A-4E0E-A722-29F4B1B5D49F}.Debug|x64.ActiveCfg = Debug|x64
+		{1798CAF2-531A-4E0E-A722-29F4B1B5D49F}.Debug|x64.Build.0 = Debug|x64
+		{1798CAF2-531A-4E0E-A722-29F4B1B5D49F}.Release|Mixed Platforms.ActiveCfg = Release|Win32
+		{1798CAF2-531A-4E0E-A722-29F4B1B5D49F}.Release|Mixed Platforms.Build.0 = Release|Win32
+		{1798CAF2-531A-4E0E-A722-29F4B1B5D49F}.Release|Win32.ActiveCfg = Release|Win32
+		{1798CAF2-531A-4E0E-A722-29F4B1B5D49F}.Release|Win32.Build.0 = Release|Win32
+		{1798CAF2-531A-4E0E-A722-29F4B1B5D49F}.Release|x64.ActiveCfg = Release|x64
+		{1798CAF2-531A-4E0E-A722-29F4B1B5D49F}.Release|x64.Build.0 = Release|x64
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
diff --git a/vs/2013/obs-x264/obs-x264.vcxproj b/vs/2013/obs-x264/obs-x264.vcxproj
new file mode 100644
index 0000000000000000000000000000000000000000..90740e381be8c70d0dd6453c1dd0cc8c38038865
--- /dev/null
+++ b/vs/2013/obs-x264/obs-x264.vcxproj
@@ -0,0 +1,180 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{1798CAF2-531A-4E0E-A722-29F4B1B5D49F}</ProjectGuid>
+    <Keyword>Win32Proj</Keyword>
+    <RootNamespace>obsx264</RootNamespace>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v120</PlatformToolset>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v120</PlatformToolset>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v120</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v120</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <LinkIncremental>true</LinkIncremental>
+    <IncludePath>$(x264Path);$(IncludePath)</IncludePath>
+    <LibraryPath>$(x264Path)\lib32;$(x264Path);$(LibraryPath)</LibraryPath>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <LinkIncremental>true</LinkIncremental>
+    <IncludePath>$(x264Path);$(IncludePath)</IncludePath>
+    <LibraryPath>$(x264Path)\lib64;$(x264Path);$(LibraryPath)</LibraryPath>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <LinkIncremental>false</LinkIncremental>
+    <IncludePath>$(x264Path);$(IncludePath)</IncludePath>
+    <LibraryPath>$(x264Path)\lib32;$(x264Path);$(LibraryPath)</LibraryPath>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <LinkIncremental>false</LinkIncremental>
+    <IncludePath>$(x264Path);$(IncludePath)</IncludePath>
+    <LibraryPath>$(x264Path)\lib64;$(x264Path);$(LibraryPath)</LibraryPath>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;OBSX264_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>../../../libobs</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalDependencies>x264.lib;libobs.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+    <PostBuildEvent>
+      <Command>copy "$(OutDir)$(TargetName)$(TargetExt)" "../../../build/obs-plugins/32bit/$(TargetName)$(TargetExt)"</Command>
+    </PostBuildEvent>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;OBSX264_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>../../../libobs</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalDependencies>x264.lib;libobs.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+    <PostBuildEvent>
+      <Command>copy "$(OutDir)$(TargetName)$(TargetExt)" "../../../build/obs-plugins/64bit/$(TargetName)$(TargetExt)"</Command>
+    </PostBuildEvent>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;OBSX264_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>../../../libobs</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalDependencies>x264.lib;libobs.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+    <PostBuildEvent>
+      <Command>copy "$(OutDir)$(TargetName)$(TargetExt)" "../../../build/obs-plugins/32bit/$(TargetName)$(TargetExt)"</Command>
+    </PostBuildEvent>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;OBSX264_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>../../../libobs</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalDependencies>x264.lib;libobs.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+    <PostBuildEvent>
+      <Command>copy "$(OutDir)$(TargetName)$(TargetExt)" "../../../build/obs-plugins/64bit/$(TargetName)$(TargetExt)"</Command>
+    </PostBuildEvent>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\..\plugins\obs-x264\obs-x264-plugin-main.c" />
+    <ClCompile Include="..\..\..\plugins\obs-x264\obs-x264.c" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file
diff --git a/vs/2013/obs-x264/obs-x264.vcxproj.filters b/vs/2013/obs-x264/obs-x264.vcxproj.filters
new file mode 100644
index 0000000000000000000000000000000000000000..16cdf1a1c91bec54d5daebc05dd3df19d40cd15f
--- /dev/null
+++ b/vs/2013/obs-x264/obs-x264.vcxproj.filters
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="Header Files">
+      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+      <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
+    </Filter>
+    <Filter Include="Resource Files">
+      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\..\plugins\obs-x264\obs-x264.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\..\plugins\obs-x264\obs-x264-plugin-main.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+</Project>
\ No newline at end of file