From 29b7d3621ce84d1244f1f99b9f12d22a27e413d8 Mon Sep 17 00:00:00 2001
From: jp9000 <obs.jim@gmail.com>
Date: Thu, 16 Jan 2014 22:34:51 -0700
Subject: [PATCH] Add preliminary output/encoder interface

- First, I redid the output interface for libobs.  I feel like it's
  going in a pretty good direction in terms of design.

  Right now, the design is so that outputs and encoders are separate.
  One or more outputs can connect to a specific encoder to receive its
  data, or the output can connect directly to raw data from libobs
  output itself, if the output doesn't want to use a designated encoder.
  Data is received via callbacks set when you connect to the encoder or
  raw output.  Multiple outputs can receive the data from a single
  encoder context if need be (such as for streaming to multiple channels
  at once, and/or recording with the same data).

  When an encoder is first connected to, it will connect to raw output,
  and start encoding.  Additional connections will receive that same
  data being encoded as well after that.  When the last encoder has
  disconnected, it will stop encoding.  If for some reason the encoder
  needs to stop, it will use the callback with NULL to signal that
  encoding has stopped.  Some of these things may be subject to change
  in the future, though it feels pretty good with this design so far.
  Will have to see how well it works out in practice versus theory.

- Second, Started adding preliminary RTMP/x264 output plugin code.

  To speed things up, I might just make a direct raw->FFmpeg output to
  create a quick output plugin that we can start using for testing all
  the subsystems.
---
 libobs/CMakeLists.txt                         |   1 +
 libobs/makefile.am                            |   1 +
 libobs/obs-data.h                             |   6 +
 libobs/obs-defs.h                             |   3 +
 libobs/obs-encoder.c                          | 164 ++++++++++++++++++
 libobs/obs-encoder.h                          | 147 ++++++++++++++++
 libobs/obs-module.c                           |   2 +
 libobs/obs-output.c                           |  37 ++--
 libobs/obs-output.h                           |  46 ++++-
 libobs/obs-source.h                           |   4 +-
 libobs/obs.c                                  |  47 ++++-
 libobs/obs.h                                  |  90 +++++++++-
 plugins/obs-outputs/obs-outputs.c             |   1 +
 plugins/obs-outputs/obs-outputs.h             |   2 +-
 plugins/obs-outputs/obs-stream.c              |  34 +++-
 plugins/obs-outputs/obs-stream.h              |  40 ++++-
 plugins/obs-outputs/obs-x264.c                |  58 +++++++
 plugins/obs-outputs/obs-x264.h                |  49 ++++++
 vs/2013/libobs/libobs.vcxproj                 |   2 +
 vs/2013/libobs/libobs.vcxproj.filters         |   6 +
 vs/2013/obs-outputs/obs-outputs.vcxproj       | 162 +++++++++++++++++
 .../obs-outputs/obs-outputs.vcxproj.filters   |  39 +++++
 22 files changed, 894 insertions(+), 47 deletions(-)
 create mode 100644 libobs/obs-encoder.c
 create mode 100644 libobs/obs-encoder.h
 create mode 100644 plugins/obs-outputs/obs-x264.c
 create mode 100644 plugins/obs-outputs/obs-x264.h
 create mode 100644 vs/2013/obs-outputs/obs-outputs.vcxproj
 create mode 100644 vs/2013/obs-outputs/obs-outputs.vcxproj.filters

diff --git a/libobs/CMakeLists.txt b/libobs/CMakeLists.txt
index 94d729414..0edccb393 100644
--- a/libobs/CMakeLists.txt
+++ b/libobs/CMakeLists.txt
@@ -40,6 +40,7 @@ add_library(libobs SHARED
 		obs-display.c
 		obs-module.c
 		obs-output.c
+		obs-encoder.c
 		obs-scene.c
 		obs-source.c
 		obs-video.c
diff --git a/libobs/makefile.am b/libobs/makefile.am
index 074b18f26..9ab59a131 100644
--- a/libobs/makefile.am
+++ b/libobs/makefile.am
@@ -23,6 +23,7 @@ libobs_la_SOURCES = obs.c \
 		    obs-output.c \
 		    obs-scene.c \
 		    obs-source.c \
+		    obs-encoder.c \
 		    obs-video.c \
 		    util/base.c \
 		    util/bmem.c \
diff --git a/libobs/obs-data.h b/libobs/obs-data.h
index f734d1495..bbf3599c8 100644
--- a/libobs/obs-data.h
+++ b/libobs/obs-data.h
@@ -30,6 +30,7 @@
 #include "obs-source.h"
 #include "obs-output.h"
 #include "obs-service.h"
+#include "obs-encoder.h"
 
 #define NUM_TEXTURES 2
 
@@ -71,10 +72,14 @@ struct obs_data {
 	 * linked lists. */
 	DARRAY(struct obs_display*) displays;
 	DARRAY(struct obs_source*)  sources;
+	DARRAY(struct obs_output*)  outputs;
+	DARRAY(struct obs_encoder*) encoders;
 
 	obs_source_t                channels[MAX_CHANNELS];
 	pthread_mutex_t             sources_mutex;
 	pthread_mutex_t             displays_mutex;
+	pthread_mutex_t             outputs_mutex;
+	pthread_mutex_t             encoders_mutex;
 };
 
 struct obs_subsystem {
@@ -83,6 +88,7 @@ struct obs_subsystem {
 	DARRAY(struct source_info)  filter_types;
 	DARRAY(struct source_info)  transition_types;
 	DARRAY(struct output_info)  output_types;
+	DARRAY(struct encoder_info) encoder_types;
 	DARRAY(struct service_info) service_types;
 
 	signal_handler_t            signals;
diff --git a/libobs/obs-defs.h b/libobs/obs-defs.h
index c15b36c0b..8e9725713 100644
--- a/libobs/obs-defs.h
+++ b/libobs/obs-defs.h
@@ -31,3 +31,6 @@
 #define SOURCE_ASYNC_VIDEO    (1<<2) /* Async video (use with SOURCE_VIDEO) */
 #define SOURCE_DEFAULT_EFFECT (1<<3) /* Source uses default/filter effect */
 #define SOURCE_YUV            (1<<4) /* Source is in YUV color space */
+
+#define OUTPUT_VIDEO_ENCODER (1<<0) /* Output requires a video encoder */
+#define OUTPUT_AUDIO_ENCODER (1<<1) /* Ouptut requires an audio encoder */
diff --git a/libobs/obs-encoder.c b/libobs/obs-encoder.c
new file mode 100644
index 000000000..44275c548
--- /dev/null
+++ b/libobs/obs-encoder.c
@@ -0,0 +1,164 @@
+/******************************************************************************
+    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.h"
+#include "obs-data.h"
+
+bool load_encoder_info(void *module, const char *module_name,
+		const char *encoder_id, struct encoder_info *info)
+{
+	info->getname = load_module_subfunc(module, module_name,
+			encoder_id, "getname", true);
+	info->create = load_module_subfunc(module, module_name,
+			encoder_id, "create", true);
+	info->destroy = load_module_subfunc(module, module_name,
+			encoder_id, "destroy", true);
+	info->update = load_module_subfunc(module, module_name,
+			encoder_id, "update", true);
+	info->reset = load_module_subfunc(module, module_name,
+			encoder_id, "reset", true);
+	info->encode = load_module_subfunc(module, module_name,
+			encoder_id, "encode", true);
+	info->getheader = load_module_subfunc(module, module_name,
+			encoder_id, "getheader", true);
+
+	if (!info->getname || !info->create || !info->destroy ||
+	    !info->reset || !info->encode || !info->getheader)
+		return false;
+
+	info->setbitrate = load_module_subfunc(module, module_name,
+			encoder_id, "setbitrate", false);
+	info->request_keyframe = load_module_subfunc(module, module_name,
+			encoder_id, "request_keyframe", false);
+
+	info->id = encoder_id;
+	return true;
+}
+
+static inline struct encoder_info *get_encoder_info(const char *id)
+{
+	for (size_t i = 0; i < obs->encoder_types.num; i++) {
+		struct encoder_info *info = obs->encoder_types.array+i;
+
+		if (strcmp(info->id, id) == 0)
+			return info;
+	}
+
+	return NULL;
+}
+
+const char *obs_encoder_getdisplayname(const char *id, const char *locale)
+{
+	struct encoder_info *ei = get_encoder_info(id);
+	if (!ei)
+		return NULL;
+
+	return ei->getname(locale);
+}
+
+obs_encoder_t obs_encoder_create(const char *id, const char *name,
+		const char *settings)
+{
+	struct obs_encoder *encoder;
+	struct encoder_info *ei = get_encoder_info(id);
+
+	if (!ei)
+		return NULL;
+
+	encoder = bmalloc(sizeof(struct obs_encoder));
+	memset(encoder, 0, sizeof(struct obs_encoder));
+	encoder->callbacks = *ei;
+
+	if (pthread_mutex_init(&encoder->data_callbacks_mutex, NULL) != 0) {
+		bfree(encoder);
+		return NULL;
+	}
+
+	encoder->data = ei->create(settings, encoder);
+	if (!encoder->data) {
+		pthread_mutex_destroy(&encoder->data_callbacks_mutex);
+		bfree(encoder);
+		return NULL;
+	}
+
+	dstr_copy(&encoder->settings, settings);
+
+	pthread_mutex_lock(&obs->data.encoders_mutex);
+	da_push_back(obs->data.encoders, &encoder);
+	pthread_mutex_unlock(&obs->data.encoders_mutex);
+	return encoder;
+}
+
+void obs_encoder_destroy(obs_encoder_t encoder)
+{
+	if (encoder) {
+		pthread_mutex_lock(&obs->data.encoders_mutex);
+		da_erase_item(obs->data.encoders, &encoder);
+		pthread_mutex_unlock(&obs->data.encoders_mutex);
+
+		encoder->callbacks.destroy(encoder->data);
+		dstr_free(&encoder->settings);
+		bfree(encoder);
+	}
+}
+
+void obs_encoder_update(obs_encoder_t encoder, const char *settings)
+{
+	encoder->callbacks.update(encoder->data, settings);
+}
+
+bool obs_encoder_reset(obs_encoder_t encoder)
+{
+	return encoder->callbacks.reset(encoder->data);
+}
+
+bool obs_encoder_encode(obs_encoder_t encoder, void *frames, size_t size)
+{
+	/* TODO */
+	//encoder->callbacks.encode(encoder->data, frames, size, packets);
+	return false;
+}
+
+int obs_encoder_getheader(obs_encoder_t encoder,
+		struct encoder_packet **packets)
+{
+	return encoder->callbacks.getheader(encoder, packets);
+}
+
+void obs_encoder_setbitrate(obs_encoder_t encoder, uint32_t bitrate,
+		uint32_t buffersize)
+{
+	if (encoder->callbacks.setbitrate)
+		encoder->callbacks.setbitrate(encoder->data, bitrate,
+				buffersize);
+}
+
+void obs_encoder_request_keyframe(obs_encoder_t encoder)
+{
+	if (encoder->callbacks.request_keyframe)
+		encoder->callbacks.request_keyframe(encoder->data);
+}
+
+const char *obs_encoder_get_settings(obs_encoder_t encoder)
+{
+	return encoder->settings.array;
+}
+
+void obs_encoder_save_settings(obs_encoder_t encoder, const char *settings)
+{
+	dstr_copy(&encoder->settings, settings);
+}
diff --git a/libobs/obs-encoder.h b/libobs/obs-encoder.h
new file mode 100644
index 000000000..d534676fc
--- /dev/null
+++ b/libobs/obs-encoder.h
@@ -0,0 +1,147 @@
+/******************************************************************************
+    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 "util/dstr.h"
+
+/*
+ * ===========================================
+ *  Outputs 
+ * ===========================================
+ *
+ *   An output takes raw audio and/or video and processes and/or outputs it
+ * to a destination, whether that destination be a file, network, or other.
+ *
+ *   A module with outputs needs to export these functions:
+ *       + enum_outputs
+ *
+ *   Each individual output is then exported by it's name.  For example, an
+ * output named "myoutput" would have the following exports:
+ *       + myoutput_getname
+ *       + myoutput_create
+ *       + myoutput_destroy
+ *       + myoutput_start
+ *       + myoutput_stop
+ *       + myoutput_encoders
+ *
+ *       [and optionally]
+ *       + myoutput_setencoder
+ *       + myoutput_getencoder
+ *       + myoutput_config
+ *       + myoutput_pause
+ *
+ * ===========================================
+ *   Primary Exports
+ * ===========================================
+ *   const char *enum_outputs(size_t idx);
+ *       idx: index of the output.
+ *       Return value: Output identifier name.  NULL when no more available.
+ *
+ * ===========================================
+ *   Output Exports
+ * ===========================================
+ *   const char *[name]_getname(const char *locale);
+ *       Returns the full translated name of the output type (seen by the user).
+ *
+ * ---------------------------------------------------------
+ *   void *[name]_create(const char *settings, obs_output_t output);
+ *       Creates an output.
+ *
+ *       settings: Settings of the output.
+ *       output: pointer to main output
+ *       Return value: Internal output pointer, or NULL if failed.
+ *
+ * ---------------------------------------------------------
+ *   void [name]_destroy(void *data);
+ *       Destroys the output.
+ *
+ * ---------------------------------------------------------
+ *   void [name]_update(void *data, const char *settings)
+ *       Updates the output's settings
+ *
+ *       settings: New settings of the output
+ *
+ * ---------------------------------------------------------
+ *   bool [name]_reset(void *data)
+ *       Starts output
+ *
+ *       Return value: true if successful
+ *
+ * ---------------------------------------------------------
+ *   int [name]_encode(void *data, void *frames, size_t size,
+ *                      struct encoder_packet **packets)
+ *
+ *       frames: frame data
+ *       size: size of data pointed to by the frame parameter
+ *       packets: returned packets, or NULL if none
+ *       Return value: number of output frames
+ *
+ * ---------------------------------------------------------
+ *   bool [name]_reset(void *data)
+ *       Resets encoder data
+ *
+ *       Return value: true if successful
+ *
+ * ===========================================
+ *   Optional Output Exports
+ * ===========================================
+ *   void [name]_setbitrate(void *data, uint32_t bitrate, uint32_t buffersize);
+ *       Sets the bitrate of the encoder
+ */
+
+struct obs_encoder;
+
+struct encoder_info {
+	const char *id;
+
+	const char *(*getname)(const char *locale);
+
+	void *(*create)(const char *settings, struct obs_encoder *encoder);
+	void (*destroy)(void *data);
+
+	void (*update)(void *data, const char *settings);
+
+	bool (*reset)(void *data);
+
+	int (*encode)(void *data, void *frames, size_t size,
+			struct encoder_packet **packets);
+	int (*getheader)(void *data, struct encoder_packet **packets);
+
+	/* optional */
+	void (*setbitrate)(void *data, uint32_t bitrate, uint32_t buffersize);
+	void (*request_keyframe)(void *data);
+};
+
+struct obs_encoder_callback {
+	void (*new_packet)(void *param, struct encoder_packet *packet);
+	void *param;
+};
+
+struct obs_encoder {
+	char                                *name;
+	void                                *data;
+	struct encoder_info                 callbacks;
+	struct dstr                         settings;
+
+	pthread_mutex_t                     data_callbacks_mutex;
+	DARRAY(struct obs_encoder_callback) data_callbacks;
+};
+
+extern bool load_encoder_info(void *module, const char *module_name,
+		const char *encoder_name, struct encoder_info *info);
diff --git a/libobs/obs-module.c b/libobs/obs-module.c
index 236d2b106..a9cdf6b4c 100644
--- a/libobs/obs-module.c
+++ b/libobs/obs-module.c
@@ -154,6 +154,8 @@ int obs_load_module(const char *path)
 			sizeof(struct source_info), load_source_info);
 	module_load_exports(&mod, &obs->output_types.da, "outputs",
 			sizeof(struct output_info), load_output_info);
+	module_load_exports(&mod, &obs->encoder_types.da, "encoders",
+			sizeof(struct encoder_info), load_encoder_info);
 
 	da_push_back(obs->modules, &mod);
 	return MODULE_SUCCESS;
diff --git a/libobs/obs-output.c b/libobs/obs-output.c
index f6520d158..2ca3b3455 100644
--- a/libobs/obs-output.c
+++ b/libobs/obs-output.c
@@ -31,13 +31,15 @@ bool load_output_info(void *module, const char *module_name,
 			output_id, "start", true);
 	info->stop = load_module_subfunc(module, module_name,
 			output_id, "stop", true);
+	info->active = load_module_subfunc(module, module_name,
+			output_id, "active", true);
 
 	if (!info->getname || !info->create || !info->destroy ||
-	    !info->start || !info->stop)
+	    !info->start || !info->stop || !info->active)
 		return false;
 
-	info->config = load_module_subfunc(module, module_name,
-			output_id, "config", false);
+	info->update = load_module_subfunc(module, module_name,
+			output_id, "update", false);
 	info->pause = load_module_subfunc(module, module_name,
 			output_id, "pause", false);
 
@@ -55,7 +57,8 @@ static inline const struct output_info *find_output(const char *id)
 	return NULL;
 }
 
-obs_output_t obs_output_create(const char *id, const char *settings)
+obs_output_t obs_output_create(const char *id, const char *name,
+		const char *settings)
 {
 	const struct output_info *info = find_output(id);
 	struct obs_output *output;
@@ -66,29 +69,39 @@ obs_output_t obs_output_create(const char *id, const char *settings)
 	}
 
 	output = bmalloc(sizeof(struct obs_output));
+	output->callbacks = *info;
 	output->data = info->create(settings, output);
 	if (!output->data) {
 		bfree(output);
 		return NULL;
 	}
 
+	output->name = bstrdup(name);
 	dstr_init_copy(&output->settings, settings);
-	memcpy(&output->callbacks, info, sizeof(struct output_info));
+
+	pthread_mutex_lock(&obs->data.outputs_mutex);
+	da_push_back(obs->data.outputs, &output);
+	pthread_mutex_unlock(&obs->data.outputs_mutex);
 	return output;
 }
 
 void obs_output_destroy(obs_output_t output)
 {
 	if (output) {
+		pthread_mutex_lock(&obs->data.outputs_mutex);
+		da_erase_item(obs->data.outputs, &output);
+		pthread_mutex_unlock(&obs->data.outputs_mutex);
+
 		output->callbacks.destroy(output->data);
 		dstr_free(&output->settings);
+		bfree(output->name);
 		bfree(output);
 	}
 }
 
-void obs_output_start(obs_output_t output)
+bool obs_output_start(obs_output_t output)
 {
-	output->callbacks.start(output->data);
+	return output->callbacks.start(output->data);
 }
 
 void obs_output_stop(obs_output_t output)
@@ -96,15 +109,15 @@ void obs_output_stop(obs_output_t output)
 	output->callbacks.stop(output->data);
 }
 
-bool obs_output_canconfig(obs_output_t output)
+bool obs_output_active(obs_output_t output)
 {
-	return output->callbacks.config != NULL;
+	return output->callbacks.active(output);
 }
 
-void obs_output_config(obs_output_t output, void *parent)
+void obs_output_update(obs_output_t output, const char *settings)
 {
-	if (output->callbacks.config)
-		output->callbacks.config(output->data, parent);
+	if (output->callbacks.update)
+		output->callbacks.update(output->data, settings);
 }
 
 bool obs_output_canpause(obs_output_t output)
diff --git a/libobs/obs-output.h b/libobs/obs-output.h
index a4054ca83..986af1a34 100644
--- a/libobs/obs-output.h
+++ b/libobs/obs-output.h
@@ -29,7 +29,7 @@
  * to a destination, whether that destination be a file, network, or other.
  *
  *   A module with outputs needs to export these functions:
- *       + enum_outputss
+ *       + enum_outputs
  *
  *   Each individual output is then exported by it's name.  For example, an
  * output named "myoutput" would have the following exports:
@@ -38,6 +38,7 @@
  *       + myoutput_destroy
  *       + myoutput_start
  *       + myoutput_stop
+ *       + myoutput_encoders
  *
  *       [and optionally]
  *       + myoutput_config
@@ -75,16 +76,46 @@
  *       settings: New settings of the output
  *
  * ---------------------------------------------------------
- *   void [name]_start(void *data)
+ *   bool [name]_start(void *data)
  *       Starts output
  *
+ *       Return value: true if successful
+ *
  * ---------------------------------------------------------
  *   void [name]_stop(void *data)
  *       Stops output
  *
+ * ---------------------------------------------------------
+ *   bool [name]_active(void *data)
+ *       Returns whether currently active or not
+ *
+ * ---------------------------------------------------------
+ *   uint32_t [name]_encoders(void *data)
+ *       Returns 0 or a combination of the following values:
+ *           + OUTPUT_VIDEO_ENCODER: requires a video encoder
+ *           + OUTPUT_AUDIO_ENCODER: requires an audio encoder
+ *
  * ===========================================
  *   Optional Output Exports
  * ===========================================
+ *   bool [name]_setencoder(void *data, obs_encoder_t encoder,
+ *                          enum obs_encoder_type type)
+ *       Sets the encoder for this output.
+ *
+ *       encoder: Encoder context
+ *       type: Type of encoder (ENCODER_VIDEO or ENCODER_AUDIO)
+ *
+ *       Returns true if successful and compatible
+ *
+ * ---------------------------------------------------------
+ *   obs_encoder_t [name]_getencoder(void *data, enum obs_encoder_type type)
+ *       Gets the encoder for this output
+ *
+ *       type: Type of encoder
+ *
+ *       Returns the encoder, or NULL if none.
+ *
+ * ---------------------------------------------------------
  *   void [name]_config(void *data, void *parent);
  *       Called to configure the output.
  *
@@ -105,18 +136,21 @@ struct output_info {
 	void *(*create)(const char *settings, struct obs_output *output);
 	void (*destroy)(void *data);
 
-	void (*start)(void *data);
+	bool (*start)(void *data);
 	void (*stop)(void *data);
 
+	bool (*active)(void *data);
+
 	/* optional */
-	void (*config)(void *data, void *parent);
+	void (*update)(void *data, const char *settings);
 	void (*pause)(void *data);
 };
 
 struct obs_output {
-	void *data;
+	char               *name;
+	void               *data;
 	struct output_info callbacks;
-	struct dstr settings;
+	struct dstr        settings;
 };
 
 extern bool load_output_info(void *module, const char *module_name,
diff --git a/libobs/obs-source.h b/libobs/obs-source.h
index 9409a40fc..3ac074677 100644
--- a/libobs/obs-source.h
+++ b/libobs/obs-source.h
@@ -81,7 +81,9 @@
  *       Returns a combination of one of the following values:
  *           + SOURCE_VIDEO: source has video
  *           + SOURCE_AUDIO: source has audio
- *           + SOURCE_ASYNC: video is sent asynchronously via RAM
+ *           + SOURCE_ASYNC_VIDEO: video is sent asynchronously via RAM
+ *           + SOURCE_DEFAULT_EFFECT: source uses default effect
+ *           + SOURCE_YUV: source is in YUV color space
  *
  * ---------------------------------------------------------
  *   uint32_t [name]_getwidth(void *data);
diff --git a/libobs/obs.c b/libobs/obs.c
index 99dd36ebf..67da3170c 100644
--- a/libobs/obs.c
+++ b/libobs/obs.c
@@ -238,6 +238,10 @@ static bool obs_init_data(void)
 		goto fail;
 	if (pthread_mutex_init(&data->displays_mutex, &attr) != 0)
 		goto fail;
+	if (pthread_mutex_init(&data->outputs_mutex, &attr) != 0)
+		goto fail;
+	if (pthread_mutex_init(&data->encoders_mutex, &attr) != 0)
+		goto fail;
 
 	success = true;
 
@@ -254,6 +258,10 @@ static void obs_free_data(void)
 	for (i = 0; i < MAX_CHANNELS; i++)
 		obs_set_output_source(i, NULL);
 
+	while (data->outputs.num)
+		obs_output_destroy(data->outputs.array[0]);
+	while (data->encoders.num)
+		obs_encoder_destroy(data->encoders.array[0]);
 	while (data->displays.num)
 		obs_display_destroy(data->displays.array[0]);
 
@@ -262,6 +270,11 @@ static void obs_free_data(void)
 		obs_source_release(data->sources.array[i]);
 	da_free(data->sources);
 	pthread_mutex_unlock(&obs->data.sources_mutex);
+
+	pthread_mutex_destroy(&data->sources_mutex);
+	pthread_mutex_destroy(&data->displays_mutex);
+	pthread_mutex_destroy(&data->outputs_mutex);
+	pthread_mutex_destroy(&data->encoders_mutex);
 }
 
 static inline bool obs_init_handlers(void)
@@ -487,17 +500,41 @@ void obs_set_output_source(uint32_t channel, obs_source_t source)
 	}
 }
 
-void obs_enum_sources(bool (*enum_proc)(obs_source_t, void*), void *param)
+void obs_enum_outputs(bool (*enum_proc)(void*, obs_output_t), void *param)
+{
+	struct obs_data *data = &obs->data;
+
+	pthread_mutex_lock(&data->outputs_mutex);
+
+	for (size_t i = 0; i < data->outputs.num; i++)
+		if (!enum_proc(param, data->outputs.array[i]))
+			break;
+
+	pthread_mutex_unlock(&data->outputs_mutex);
+}
+
+void obs_enum_encoders(bool (*enum_proc)(void*, obs_encoder_t), void *param)
+{
+	struct obs_data *data = &obs->data;
+
+	pthread_mutex_lock(&data->encoders_mutex);
+
+	for (size_t i = 0; i < data->encoders.num; i++)
+		if (!enum_proc(param, data->encoders.array[i]))
+			break;
+
+	pthread_mutex_unlock(&data->encoders_mutex);
+}
+
+void obs_enum_sources(bool (*enum_proc)(void*, obs_source_t), void *param)
 {
 	struct obs_data *data = &obs->data;
-	size_t i;
 
 	pthread_mutex_lock(&data->sources_mutex);
 
-	for (i = 0; i < data->sources.num; i++) {
-		if (!enum_proc(data->sources.array[i], param))
+	for (size_t i = 0; i < data->sources.num; i++)
+		if (!enum_proc(param, data->sources.array[i]))
 			break;
-	}
 
 	pthread_mutex_unlock(&data->sources_mutex);
 }
diff --git a/libobs/obs.h b/libobs/obs.h
index 78b9683df..d3c7adfb2 100644
--- a/libobs/obs.h
+++ b/libobs/obs.h
@@ -50,6 +50,11 @@ enum obs_source_type {
 	SOURCE_SCENE
 };
 
+enum obs_encoder_type {
+	ENCODER_VIDEO,
+	ENCODER_AUDIO
+};
+
 enum obs_video_type {
 	OBS_VIDEO_YUV,
 	OBS_VIDEO_RGB
@@ -136,12 +141,29 @@ static inline void source_frame_destroy(struct source_frame *frame)
 	}
 }
 
+enum packet_priority {
+	PACKET_PRIORITY_DISPOSABLE,
+	PACKET_PRIORITY_LOW,
+	PACKET_PRIORITY_PFRAME,
+	PACKET_PRIORITY_IFRAME,
+	PACKET_PRIORITY_OTHER /* audio usually */
+};
+
+struct encoder_packet {
+	int64_t              dts;
+	int64_t              pts;
+	void                 *data;
+	size_t               size;
+	enum packet_priority priority;
+};
+
 /* opaque types */
 struct obs_display;
 struct obs_source;
 struct obs_scene;
 struct obs_scene_item;
 struct obs_output;
+struct obs_encoder;
 struct obs_service;
 
 typedef struct obs_display    *obs_display_t;
@@ -149,6 +171,7 @@ typedef struct obs_source     *obs_source_t;
 typedef struct obs_scene      *obs_scene_t;
 typedef struct obs_scene_item *obs_sceneitem_t;
 typedef struct obs_output     *obs_output_t;
+typedef struct obs_encoder    *obs_encoder_t;
 typedef struct obs_service    *obs_service_t;
 
 /* ------------------------------------------------------------------------- */
@@ -254,7 +277,15 @@ EXPORT obs_source_t obs_get_output_source(uint32_t channel);
  *   Callback function returns true to continue enumeration, or false to end
  * enumeration.
  */
-EXPORT void obs_enum_sources(bool (*enum_proc)(obs_source_t, void*),
+EXPORT void obs_enum_sources(bool (*enum_proc)(void*, obs_source_t),
+		void *param);
+
+/** Enumerates outputs */
+EXPORT void obs_enum_outputs(bool (*enum_proc)(void*, obs_output_t),
+		void *param);
+
+/** Enumerates encoders */
+EXPORT void obs_enum_encoders(bool (*enum_proc)(void*, obs_encoder_t),
 		void *param);
 
 /**
@@ -504,26 +535,30 @@ EXPORT void  obs_sceneitem_getscale(obs_sceneitem_t item, struct vec2 *scale);
 /* ------------------------------------------------------------------------- */
 /* Outputs */
 
+EXPORT const char *obs_output_getdisplayname(const char *id,
+		const char *locale);
+
 /**
  * Creates an output.
  *
  *   Outputs allow outputting to file, outputting to network, outputting to
  * directshow, or other custom outputs.
  */
-EXPORT obs_output_t obs_output_create(const char *name, const char *settings);
+EXPORT obs_output_t obs_output_create(const char *id, const char *name,
+		const char *settings);
 EXPORT void obs_output_destroy(obs_output_t output);
 
 /** Starts the output. */
-EXPORT void obs_output_start(obs_output_t output);
+EXPORT bool obs_output_start(obs_output_t output);
 
 /** Stops the output. */
 EXPORT void obs_output_stop(obs_output_t output);
 
-/** Specifies whether the output can be configured */
-EXPORT bool obs_output_canconfig(obs_output_t output);
+/** Returns whether the output is active */
+EXPORT bool obs_output_active(obs_output_t output);
 
-/** Opens a configuration panel with the specified parent window */
-EXPORT void obs_output_config(obs_output_t output, void *parent);
+/** Updates the settings for this output context */
+EXPORT void obs_output_update(obs_output_t output, const char *settings);
 
 /** Specifies whether the output can be paused */
 EXPORT bool obs_output_canpause(obs_output_t output);
@@ -539,13 +574,52 @@ EXPORT void obs_output_save_settings(obs_output_t output,
 		const char *settings);
 
 
+/* ------------------------------------------------------------------------- */
+/* Stream 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,
+		const char *settings);
+EXPORT void obs_encoder_destroy(obs_encoder_t encoder);
+
+EXPORT void obs_encoder_update(obs_encoder_t encoder, const char *settings);
+
+EXPORT bool obs_encoder_reset(obs_encoder_t encoder);
+
+EXPORT bool obs_encoder_encode(obs_encoder_t encoder, void *frames,
+		size_t size);
+EXPORT int obs_encoder_getheader(obs_encoder_t encoder,
+		struct encoder_packet **packets);
+
+EXPORT bool obs_encoder_start(obs_encoder_t encoder,
+		void (*new_packet)(void *param, struct encoder_packet *packet),
+		void *param);
+EXPORT bool obs_encoder_stop(obs_encoder_t encoder,
+		void (*new_packet)(void *param, struct encoder_packet *packet),
+		void *param);
+
+EXPORT void obs_encoder_setbitrate(obs_encoder_t encoder, uint32_t bitrate,
+		uint32_t buffersize);
+
+EXPORT void obs_encoder_request_keyframe(obs_encoder_t encoder);
+
+EXPORT const char *obs_encoder_get_settings(obs_encoder_t encoder);
+
+EXPORT void obs_encoder_save_settings(obs_encoder_t encoder,
+		const char *settings);
+
+
 /* ------------------------------------------------------------------------- */
 /* Stream Services */
+EXPORT const char *obs_service_getdisplayname(const char *id,
+		const char *locale);
+
 EXPORT obs_service_t obs_service_create(const char *service,
 		const char *settings);
 EXPORT void obs_service_destroy(obs_service_t service);
 
-EXPORT void obs_service_setdata(obs_service_t service,const char *attribute,
+EXPORT void obs_service_setdata(obs_service_t service, const char *attribute,
 		const char *data);
 EXPORT const char *obs_service_getdata(obs_service_t service,
 		const char *attribute);
diff --git a/plugins/obs-outputs/obs-outputs.c b/plugins/obs-outputs/obs-outputs.c
index 6ce9ace17..a9dd9b999 100644
--- a/plugins/obs-outputs/obs-outputs.c
+++ b/plugins/obs-outputs/obs-outputs.c
@@ -1,3 +1,4 @@
+#include <string.h>
 #include "obs-outputs.h"
 
 static const char *outputs[] = {"rtmp_stream"};
diff --git a/plugins/obs-outputs/obs-outputs.h b/plugins/obs-outputs/obs-outputs.h
index 4e24817cf..0601083d2 100644
--- a/plugins/obs-outputs/obs-outputs.h
+++ b/plugins/obs-outputs/obs-outputs.h
@@ -1,5 +1,5 @@
 #pragma once
 
-#include "util/c99defs.h"
+#include <util/c99defs.h>
 
 EXPORT const char *enum_outputs(size_t idx);
diff --git a/plugins/obs-outputs/obs-stream.c b/plugins/obs-outputs/obs-stream.c
index cc0cf383d..a7405c044 100644
--- a/plugins/obs-outputs/obs-stream.c
+++ b/plugins/obs-outputs/obs-stream.c
@@ -1,6 +1,23 @@
+/******************************************************************************
+    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-stream.h"
 
-void *rtmp_stream_getname(const char *locale)
+const char *rtmp_stream_getname(const char *locale)
 {
 	/* TODO: locale stuff */
 	return "RTMP Stream";
@@ -8,21 +25,26 @@ void *rtmp_stream_getname(const char *locale)
 
 void *rtmp_stream_create(const char *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)
+{
 }
 
-void *rtmp_stream_destroy(void *data)
+void rtmp_stream_update(struct rtmp_stream *stream, const char *settings)
 {
 }
 
-void *rtmp_stream_update(void *data, const char *settings)
+bool rtmp_stream_start(struct rtmp_stream *stream)
 {
 }
 
-void rtmp_stream_start(void *data)
+void rtmp_stream_stop(struct rtmp_stream *stream)
 {
 }
 
-void rtmp_stream_stop(void *data)
+bool rtmp_stream_active(struct rtmp_stream *stream)
 {
 }
diff --git a/plugins/obs-outputs/obs-stream.h b/plugins/obs-outputs/obs-stream.h
index f6f151eba..02edeb73a 100644
--- a/plugins/obs-outputs/obs-stream.h
+++ b/plugins/obs-outputs/obs-stream.h
@@ -1,15 +1,39 @@
+/******************************************************************************
+    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 <util/c99defs.h>
+#include <obs.h>
 
 struct rtmp_stream {
-	obs_output_t handler;
+	obs_output_t  output;
+	obs_encoder_t video_encoder;
+	obs_encoder_t audio_encoder;
+	obs_service_t service;
+
+	bool active;
 };
 
-EXPORT void *rtmp_stream_getname(const char *locale);
+EXPORT const char *rtmp_stream_getname(const char *locale);
 EXPORT void *rtmp_stream_create(const char *settings, obs_output_t output);
-EXPORT void *rtmp_stream_destroy(void *data);
-EXPORT void *rtmp_stream_update(void *data, const char *settings);
-EXPORT void rtmp_stream_start(void *data);
-EXPORT void rtmp_stream_stop(void *data);
+EXPORT void rtmp_stream_destroy(struct rtmp_stream *stream);
+EXPORT void rtmp_stream_update(struct rtmp_stream *stream,
+		const char *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-outputs/obs-x264.c b/plugins/obs-outputs/obs-x264.c
new file mode 100644
index 000000000..d65d7e27c
--- /dev/null
+++ b/plugins/obs-outputs/obs-x264.c
@@ -0,0 +1,58 @@
+/******************************************************************************
+    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(const char *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, const char *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
new file mode 100644
index 000000000..8190ce28b
--- /dev/null
+++ b/plugins/obs-outputs/obs-x264.h
@@ -0,0 +1,49 @@
+/******************************************************************************
+    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(const char *settings,
+		obs_encoder_t encoder);
+EXPORT void obs_x264_destroy(struct obs_x264 *data);
+
+EXPORT void obs_x264_update(struct obs_x264 *data, const char *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/vs/2013/libobs/libobs.vcxproj b/vs/2013/libobs/libobs.vcxproj
index 481988a8e..899fcec4c 100644
--- a/vs/2013/libobs/libobs.vcxproj
+++ b/vs/2013/libobs/libobs.vcxproj
@@ -45,6 +45,7 @@
     <ClInclude Include="..\..\..\libobs\media-io\video-io.h" />
     <ClInclude Include="..\..\..\libobs\obs-data.h" />
     <ClInclude Include="..\..\..\libobs\obs-defs.h" />
+    <ClInclude Include="..\..\..\libobs\obs-encoder.h" />
     <ClInclude Include="..\..\..\libobs\obs-module.h" />
     <ClInclude Include="..\..\..\libobs\obs-output.h" />
     <ClInclude Include="..\..\..\libobs\obs-scene.h" />
@@ -91,6 +92,7 @@
     <ClCompile Include="..\..\..\libobs\media-io\format-conversion.c" />
     <ClCompile Include="..\..\..\libobs\media-io\video-io.c" />
     <ClCompile Include="..\..\..\libobs\obs-display.c" />
+    <ClCompile Include="..\..\..\libobs\obs-encoder.c" />
     <ClCompile Include="..\..\..\libobs\obs-module.c" />
     <ClCompile Include="..\..\..\libobs\obs-output.c" />
     <ClCompile Include="..\..\..\libobs\obs-scene.c" />
diff --git a/vs/2013/libobs/libobs.vcxproj.filters b/vs/2013/libobs/libobs.vcxproj.filters
index e79d369ce..ce920f27a 100644
--- a/vs/2013/libobs/libobs.vcxproj.filters
+++ b/vs/2013/libobs/libobs.vcxproj.filters
@@ -198,6 +198,9 @@
     <ClInclude Include="..\..\..\libobs\callback\signal.h">
       <Filter>callback\Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\..\libobs\obs-encoder.h">
+      <Filter>libobs\Header Files</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="..\..\..\libobs\obs-output.c">
@@ -326,5 +329,8 @@
     <ClCompile Include="..\..\..\libobs\callback\proc.c">
       <Filter>callback\Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\..\libobs\obs-encoder.c">
+      <Filter>libobs\Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>
\ No newline at end of file
diff --git a/vs/2013/obs-outputs/obs-outputs.vcxproj b/vs/2013/obs-outputs/obs-outputs.vcxproj
new file mode 100644
index 000000000..12e19fcc4
--- /dev/null
+++ b/vs/2013/obs-outputs/obs-outputs.vcxproj
@@ -0,0 +1,162 @@
+<?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>{CDC3B5C9-2BEA-4509-A2A8-6477506010ED}</ProjectGuid>
+    <Keyword>Win32Proj</Keyword>
+    <RootNamespace>obsoutputs</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>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <LinkIncremental>true</LinkIncremental>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <LinkIncremental>false</LinkIncremental>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <LinkIncremental>false</LinkIncremental>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;OBSOUTPUTS_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>../../../libobs</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalDependencies>libobs.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;OBSOUTPUTS_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>../../../libobs</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalDependencies>libobs.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+  </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;OBSOUTPUTS_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>../../../libobs</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <AdditionalDependencies>libobs.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+  </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;OBSOUTPUTS_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>../../../libobs</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <AdditionalDependencies>libobs.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\..\plugins\obs-outputs\obs-outputs.h" />
+    <ClInclude Include="..\..\..\plugins\obs-outputs\obs-stream.h" />
+    <ClInclude Include="..\..\..\plugins\obs-outputs\obs-x264.h" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\..\plugins\obs-outputs\obs-outputs.c" />
+    <ClCompile Include="..\..\..\plugins\obs-outputs\obs-stream.c" />
+    <ClCompile Include="..\..\..\plugins\obs-outputs\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-outputs/obs-outputs.vcxproj.filters b/vs/2013/obs-outputs/obs-outputs.vcxproj.filters
new file mode 100644
index 000000000..386f7c70a
--- /dev/null
+++ b/vs/2013/obs-outputs/obs-outputs.vcxproj.filters
@@ -0,0 +1,39 @@
+<?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>
+    <ClInclude Include="..\..\..\plugins\obs-outputs\obs-stream.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\..\plugins\obs-outputs\obs-outputs.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\..\plugins\obs-outputs\obs-x264.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\..\plugins\obs-outputs\obs-stream.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\..\plugins\obs-outputs\obs-outputs.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\..\plugins\obs-outputs\obs-x264.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+</Project>
\ No newline at end of file
-- 
GitLab