Browse Source

Add preliminary ffmpeg plugin (still testing)

- Added some code for FFmpeg output that I'm still playing around with.
  Right now I'm just trying to get it to output to file and try to
  understand the FFmpeg/libav APIs.  Hopefully in the future this plugin
  can be used for any sort of output to FFmpeg.

- Fixed a cast warning in audio-io.c with size_t -> uint32_t

- Renamed the 'video_info' and 'audio_info' structures to
  'video_conver_info' and 'audio_convert_info' to better represent their
  actual purpose, and to avoid confusion with 'audio_output_info' and
  'video_output_info' structures.

- Removed a few macros from obs-def.h that were at one point going to be
  used but no longer going to be used (at least for now)
jp9000 11 years ago
parent
commit
fc8851e9f4

+ 12 - 11
libobs/media-io/audio-io.c

@@ -23,7 +23,7 @@
 #include "audio-io.h"
 #include "audio-io.h"
 
 
 struct audio_input {
 struct audio_input {
-	struct audio_info format;
+	struct audio_convert_info conversion;
 	void (*callback)(void *param, const struct audio_data *data);
 	void (*callback)(void *param, const struct audio_data *data);
 	void *param;
 	void *param;
 };
 };
@@ -88,13 +88,13 @@ static inline void audio_output_removeline(struct audio_output *audio,
 	audio_line_destroy_data(line);
 	audio_line_destroy_data(line);
 }
 }
 
 
-static inline size_t time_to_frames(audio_t audio, uint64_t offset)
+static inline uint32_t time_to_frames(audio_t audio, uint64_t offset)
 {
 {
 	double audio_offset_d = (double)offset;
 	double audio_offset_d = (double)offset;
 	audio_offset_d /= 1000000000.0;
 	audio_offset_d /= 1000000000.0;
 	audio_offset_d *= (double)audio->info.samples_per_sec;
 	audio_offset_d *= (double)audio->info.samples_per_sec;
 
 
-	return (size_t)audio_offset_d;
+	return (uint32_t)audio_offset_d;
 }
 }
 
 
 static inline size_t time_to_bytes(audio_t audio, uint64_t offset)
 static inline size_t time_to_bytes(audio_t audio, uint64_t offset)
@@ -146,7 +146,7 @@ static inline void mix_audio_line(struct audio_output *audio,
 }
 }
 
 
 static inline void do_audio_output(struct audio_output *audio,
 static inline void do_audio_output(struct audio_output *audio,
-		uint64_t timestamp, size_t frames)
+		uint64_t timestamp, uint32_t frames)
 {
 {
 	struct audio_data data;
 	struct audio_data data;
 	data.data = audio->mix_buffer.array;
 	data.data = audio->mix_buffer.array;
@@ -168,7 +168,7 @@ static void mix_and_output(struct audio_output *audio, uint64_t audio_time,
 {
 {
 	struct audio_line *line = audio->first_line;
 	struct audio_line *line = audio->first_line;
 	uint64_t time_offset = audio_time - prev_time;
 	uint64_t time_offset = audio_time - prev_time;
-	size_t frames = time_to_frames(audio, time_offset);
+	uint32_t frames = time_to_frames(audio, time_offset);
 	size_t bytes = frames * audio->block_size;
 	size_t bytes = frames * audio->block_size;
 
 
 	da_resize(audio->mix_buffer, bytes);
 	da_resize(audio->mix_buffer, bytes);
@@ -231,7 +231,8 @@ static size_t audio_get_input_idx(audio_t video,
 	return DARRAY_INVALID;
 	return DARRAY_INVALID;
 }
 }
 
 
-void audio_output_connect(audio_t audio, struct audio_info *format,
+void audio_output_connect(audio_t audio,
+		struct audio_convert_info *conversion,
 		void (*callback)(void *param, const struct audio_data *data),
 		void (*callback)(void *param, const struct audio_data *data),
 		void *param)
 		void *param)
 {
 {
@@ -243,12 +244,12 @@ void audio_output_connect(audio_t audio, struct audio_info *format,
 		input.param    = param;
 		input.param    = param;
 
 
 		/* TODO: conversion */
 		/* TODO: conversion */
-		if (format) {
-			input.format = *format;
+		if (conversion) {
+			input.conversion = *conversion;
 		} else {
 		} else {
-			input.format.format = audio->info.format;
-			input.format.speakers = audio->info.speakers;
-			input.format.samples_per_sec =
+			input.conversion.format = audio->info.format;
+			input.conversion.speakers = audio->info.speakers;
+			input.conversion.samples_per_sec =
 				audio->info.samples_per_sec;
 				audio->info.samples_per_sec;
 		}
 		}
 
 

+ 3 - 2
libobs/media-io/audio-io.h

@@ -71,7 +71,7 @@ struct audio_output_info {
 	uint64_t            buffer_ms;
 	uint64_t            buffer_ms;
 };
 };
 
 
-struct audio_info {
+struct audio_convert_info {
 	uint32_t            samples_per_sec;
 	uint32_t            samples_per_sec;
 	enum audio_format   format;
 	enum audio_format   format;
 	enum speaker_layout speakers;
 	enum speaker_layout speakers;
@@ -124,7 +124,8 @@ static inline size_t get_audio_size(enum audio_format type,
 EXPORT int audio_output_open(audio_t *audio, struct audio_output_info *info);
 EXPORT int audio_output_open(audio_t *audio, struct audio_output_info *info);
 EXPORT void audio_output_close(audio_t audio);
 EXPORT void audio_output_close(audio_t audio);
 
 
-EXPORT void audio_output_connect(audio_t video, struct audio_info *format,
+EXPORT void audio_output_connect(audio_t video,
+		struct audio_convert_info *conversion,
 		void (*callback)(void *param, const struct audio_data *data),
 		void (*callback)(void *param, const struct audio_data *data),
 		void *param);
 		void *param);
 EXPORT void audio_output_disconnect(audio_t video,
 EXPORT void audio_output_disconnect(audio_t video,

+ 2 - 2
libobs/media-io/audio-resampler-ffmpeg.c

@@ -17,9 +17,9 @@
 
 
 #include "../util/bmem.h"
 #include "../util/bmem.h"
 #include "audio-resampler.h"
 #include "audio-resampler.h"
-#include <libswresample/swresample.h>
-#include <libavutil/channel_layout.h>
 #include <libavutil/opt.h>
 #include <libavutil/opt.h>
+#include <libavutil/channel_layout.h>
+#include <libswresample/swresample.h>
 
 
 struct audio_resampler {
 struct audio_resampler {
 	struct SwrContext   *context;
 	struct SwrContext   *context;

+ 19 - 12
libobs/media-io/video-io.c

@@ -24,8 +24,8 @@
 #include "video-io.h"
 #include "video-io.h"
 
 
 struct video_input {
 struct video_input {
-	struct video_info format;
-	void (*callback)(void *param, struct video_frame *frame);
+	struct video_convert_info conversion;
+	void (*callback)(void *param, const struct video_frame *frame);
 	void *param;
 	void *param;
 };
 };
 
 
@@ -116,7 +116,7 @@ int video_output_open(video_t *video, struct video_output_info *info)
 	out = bmalloc(sizeof(struct video_output));
 	out = bmalloc(sizeof(struct video_output));
 	memset(out, 0, sizeof(struct video_output));
 	memset(out, 0, sizeof(struct video_output));
 
 
-	memcpy(&out->info, info, sizeof(struct video_info));
+	memcpy(&out->info, info, sizeof(struct video_output_info));
 	out->frame_time = (uint64_t)(1000000000.0 * (double)info->fps_den /
 	out->frame_time = (uint64_t)(1000000000.0 * (double)info->fps_den /
 		(double)info->fps_num);
 		(double)info->fps_num);
 	out->initialized = false;
 	out->initialized = false;
@@ -157,7 +157,7 @@ void video_output_close(video_t video)
 }
 }
 
 
 static size_t video_get_input_idx(video_t video,
 static size_t video_get_input_idx(video_t video,
-		void (*callback)(void *param, struct video_frame *frame),
+		void (*callback)(void *param, const struct video_frame *frame),
 		void *param)
 		void *param)
 {
 {
 	for (size_t i = 0; i < video->inputs.num; i++) {
 	for (size_t i = 0; i < video->inputs.num; i++) {
@@ -169,8 +169,9 @@ static size_t video_get_input_idx(video_t video,
 	return DARRAY_INVALID;
 	return DARRAY_INVALID;
 }
 }
 
 
-void video_output_connect(video_t video, struct video_info *format,
-		void (*callback)(void *param, struct video_frame *frame),
+void video_output_connect(video_t video,
+		struct video_convert_info *conversion,
+		void (*callback)(void *param, const struct video_frame *frame),
 		void *param)
 		void *param)
 {
 {
 	pthread_mutex_lock(&video->input_mutex);
 	pthread_mutex_lock(&video->input_mutex);
@@ -181,12 +182,18 @@ void video_output_connect(video_t video, struct video_info *format,
 		input.param    = param;
 		input.param    = param;
 
 
 		/* TODO: conversion */
 		/* TODO: conversion */
-		if (format) {
-			input.format = *format;
+		if (conversion) {
+			input.conversion = *conversion;
+
+			if (input.conversion.width == 0)
+				input.conversion.width = video->info.width;
+			if (input.conversion.height == 0)
+				input.conversion.height = video->info.height;
 		} else {
 		} else {
-			input.format.type   = video->info.type;
-			input.format.height = video->info.height;
-			input.format.width  = video->info.width;
+			input.conversion.format    = video->info.format;
+			input.conversion.width     = video->info.width;
+			input.conversion.height    = video->info.height;
+			input.conversion.row_align = 1;
 		}
 		}
 
 
 		da_push_back(video->inputs, &input);
 		da_push_back(video->inputs, &input);
@@ -196,7 +203,7 @@ void video_output_connect(video_t video, struct video_info *format,
 }
 }
 
 
 void video_output_disconnect(video_t video,
 void video_output_disconnect(video_t video,
-		void (*callback)(void *param, struct video_frame *frame),
+		void (*callback)(void *param, const struct video_frame *frame),
 		void *param)
 		void *param)
 {
 {
 	pthread_mutex_lock(&video->input_mutex);
 	pthread_mutex_lock(&video->input_mutex);

+ 9 - 8
libobs/media-io/video-io.h

@@ -29,7 +29,7 @@ struct video_output;
 typedef struct video_output *video_t;
 typedef struct video_output *video_t;
 
 
 enum video_format {
 enum video_format {
-	VIDEO_FORMAT_UNKNOWN,
+	VIDEO_FORMAT_NONE,
 
 
 	/* planar 420 format */
 	/* planar 420 format */
 	VIDEO_FORMAT_I420, /* three-plane */
 	VIDEO_FORMAT_I420, /* three-plane */
@@ -57,18 +57,18 @@ struct video_frame {
 struct video_output_info {
 struct video_output_info {
 	const char        *name;
 	const char        *name;
 
 
-	enum video_format type;
+	enum video_format format;
 	uint32_t          fps_num;
 	uint32_t          fps_num;
 	uint32_t          fps_den;
 	uint32_t          fps_den;
 	uint32_t          width;
 	uint32_t          width;
 	uint32_t          height;
 	uint32_t          height;
 };
 };
 
 
-struct video_info {
-	enum video_format type;
+struct video_convert_info {
+	enum video_format format;
 	uint32_t          width;
 	uint32_t          width;
 	uint32_t          height;
 	uint32_t          height;
-	uint32_t          row_size; /* if any */
+	uint32_t          row_align;
 };
 };
 
 
 #define VIDEO_OUTPUT_SUCCESS       0
 #define VIDEO_OUTPUT_SUCCESS       0
@@ -78,11 +78,12 @@ struct video_info {
 EXPORT int video_output_open(video_t *video, struct video_output_info *info);
 EXPORT int video_output_open(video_t *video, struct video_output_info *info);
 EXPORT void video_output_close(video_t video);
 EXPORT void video_output_close(video_t video);
 
 
-EXPORT void video_output_connect(video_t video, struct video_info *format,
-		void (*callback)(void *param, struct video_frame *frame),
+EXPORT void video_output_connect(video_t video,
+		struct video_convert_info *conversion,
+		void (*callback)(void *param, const struct video_frame *frame),
 		void *param);
 		void *param);
 EXPORT void video_output_disconnect(video_t video,
 EXPORT void video_output_disconnect(video_t video,
-		void (*callback)(void *param, struct video_frame *frame),
+		void (*callback)(void *param, const struct video_frame *frame),
 		void *param);
 		void *param);
 
 
 EXPORT const struct video_output_info *video_output_getinfo(video_t video);
 EXPORT const struct video_output_info *video_output_getinfo(video_t video);

+ 0 - 3
libobs/obs-defs.h

@@ -31,6 +31,3 @@
 #define SOURCE_ASYNC_VIDEO    (1<<2) /* Async video (use with SOURCE_VIDEO) */
 #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_DEFAULT_EFFECT (1<<3) /* Source uses default/filter effect */
 #define SOURCE_YUV            (1<<4) /* Source is in YUV color space */
 #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 */

+ 1 - 0
libobs/obs-output.h

@@ -39,6 +39,7 @@
  *       + myoutput_update
  *       + myoutput_update
  *       + myoutput_start
  *       + myoutput_start
  *       + myoutput_stop
  *       + myoutput_stop
+ *       + myoutput_active
  *
  *
  *       [and optionally]
  *       [and optionally]
  *       + myoutput_pause
  *       + myoutput_pause

+ 2 - 2
libobs/obs-source.c

@@ -404,7 +404,7 @@ static inline enum convert_type get_convert_type(enum video_format format)
 	case VIDEO_FORMAT_UYVY:
 	case VIDEO_FORMAT_UYVY:
 		return CONVERT_422_U;
 		return CONVERT_422_U;
 
 
-	case VIDEO_FORMAT_UNKNOWN:
+	case VIDEO_FORMAT_NONE:
 	case VIDEO_FORMAT_YUVX:
 	case VIDEO_FORMAT_YUVX:
 	case VIDEO_FORMAT_UYVX:
 	case VIDEO_FORMAT_UYVX:
 	case VIDEO_FORMAT_RGBA:
 	case VIDEO_FORMAT_RGBA:
@@ -427,7 +427,7 @@ static inline bool is_yuv(enum video_format format)
 	case VIDEO_FORMAT_YUVX:
 	case VIDEO_FORMAT_YUVX:
 	case VIDEO_FORMAT_UYVX:
 	case VIDEO_FORMAT_UYVX:
 		return true;
 		return true;
-	case VIDEO_FORMAT_UNKNOWN:
+	case VIDEO_FORMAT_NONE:
 	case VIDEO_FORMAT_RGBA:
 	case VIDEO_FORMAT_RGBA:
 	case VIDEO_FORMAT_BGRA:
 	case VIDEO_FORMAT_BGRA:
 	case VIDEO_FORMAT_BGRX:
 	case VIDEO_FORMAT_BGRX:

+ 2 - 2
libobs/obs.c

@@ -41,7 +41,7 @@ static inline void make_video_info(struct video_output_info *vi,
 		struct obs_video_info *ovi)
 		struct obs_video_info *ovi)
 {
 {
 	vi->name    = "video";
 	vi->name    = "video";
-	vi->type    = ovi->output_format;
+	vi->format  = ovi->output_format;
 	vi->fps_num = ovi->fps_num;
 	vi->fps_num = ovi->fps_num;
 	vi->fps_den = ovi->fps_den;
 	vi->fps_den = ovi->fps_den;
 	vi->width   = ovi->output_width;
 	vi->width   = ovi->output_width;
@@ -381,7 +381,7 @@ bool obs_get_video_info(struct obs_video_info *ovi)
 	ovi->base_height   = video->base_height;
 	ovi->base_height   = video->base_height;
 	ovi->output_width  = info->width;
 	ovi->output_width  = info->width;
 	ovi->output_height = info->height;
 	ovi->output_height = info->height;
-	ovi->output_format = info->type;
+	ovi->output_format = info->format;
 	ovi->fps_num       = info->fps_num;
 	ovi->fps_num       = info->fps_num;
 	ovi->fps_den       = info->fps_den;
 	ovi->fps_den       = info->fps_den;
 
 

+ 0 - 3
libobs/obs.h

@@ -374,9 +374,6 @@ EXPORT bool obs_source_removed(obs_source_t source);
  */
  */
 EXPORT uint32_t obs_source_get_output_flags(obs_source_t source);
 EXPORT uint32_t obs_source_get_output_flags(obs_source_t source);
 
 
-/** Specifies whether the source can be configured */
-EXPORT bool obs_source_hasconfig(obs_source_t source);
-
 /** Updates settings for this source */
 /** Updates settings for this source */
 EXPORT void obs_source_update(obs_source_t source, const char *settings);
 EXPORT void obs_source_update(obs_source_t source, const char *settings);
 
 

+ 369 - 0
plugins/obs-ffmpeg/obs-ffmpeg-output.c

@@ -0,0 +1,369 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#include <obs.h>
+#include "obs-ffmpeg-output.h"
+
+#define FILENAME_TODO "D:\\test.mp4"
+
+static inline enum AVPixelFormat obs_to_ffmpeg_video_format(
+		enum video_format format)
+{
+	switch (format) {
+	case VIDEO_FORMAT_NONE: return AV_PIX_FMT_NONE;
+	case VIDEO_FORMAT_I420: return AV_PIX_FMT_YUV420P;
+	case VIDEO_FORMAT_NV12: return AV_PIX_FMT_NV12;
+	case VIDEO_FORMAT_YVYU: return AV_PIX_FMT_NONE;
+	case VIDEO_FORMAT_YUY2: return AV_PIX_FMT_YUYV422;
+	case VIDEO_FORMAT_UYVY: return AV_PIX_FMT_UYVY422;
+	case VIDEO_FORMAT_YUVX: return AV_PIX_FMT_NONE;
+	case VIDEO_FORMAT_UYVX: return AV_PIX_FMT_NONE;
+	case VIDEO_FORMAT_RGBA: return AV_PIX_FMT_RGBA;
+	case VIDEO_FORMAT_BGRA: return AV_PIX_FMT_BGRA;
+	case VIDEO_FORMAT_BGRX: return AV_PIX_FMT_BGRA;
+	}
+
+	return AV_PIX_FMT_NONE;
+}
+
+static bool new_stream(struct ffmpeg_data *data, AVStream **stream,
+		AVCodec **codec, enum AVCoecID id)
+{
+	*codec = avcodec_find_encoder(id);
+	if (!*codec) {
+		blog(LOG_ERROR, "Couldn't find encoder '%s'",
+				avcodec_get_name(id));
+		return false;
+	}
+
+	*stream = avformat_new_stream(data->output, *codec);
+	if (!*stream) {
+		blog(LOG_ERROR, "Couldn't create stream for encoder '%s'",
+				avcodec_get_name(id));
+		return false;
+	}
+
+	(*stream)->id = data->output->nb_streams-1;
+	return true;
+}
+
+static bool open_video_codec(struct ffmpeg_data *data,
+		struct obs_video_info *ovi)
+{
+	AVCodecContext *context = data->video->codec;
+	int ret;
+
+	ret = avcodec_open2(context, data->vcodec, NULL);
+	if (ret < 0) {
+		blog(LOG_ERROR, "Failed to open video codec: %s",
+				av_err2str(ret));
+		return false;
+	}
+
+	data->vframe = av_frame_alloc();
+	if (!data->vframe) {
+		blog(LOG_ERROR, "Failed to allocate video frame");
+		return false;
+	}
+
+	data->vframe->format = context->pix_fmt;
+	data->vframe->width  = context->width;
+	data->vframe->height = context->height;
+
+	ret = avpicture_alloc(&data->dst_picture, context->pix_fmt,
+			context->width, context->height);
+	if (ret < 0) {
+		blog(LOG_ERROR, "Failed to allocate dst_picture: %s",
+				av_err2str(ret));
+		return false;
+	}
+
+	if (context->pix_fmt != AV_PIX_FMT_YUV420P) {
+		ret = avpicture_alloc(&data->src_picture, AV_PIX_FMT_YUV420P,
+				context->width, context->height);
+		if (ret < 0) {
+			blog(LOG_ERROR, "Failed to allocate src_picture: %s",
+					av_err2str(ret));
+			return false;
+		}
+	}
+
+	*((AVPicture*)data->vframe) = data->dst_picture;
+	return true;
+}
+
+static bool create_video_stream(struct ffmpeg_data *data)
+{
+	AVCodecContext *context;
+	struct obs_video_info ovi;
+
+	if (!obs_get_video_info(&ovi)) {
+		blog(LOG_ERROR, "No active video");
+		return false;
+	}
+	if (!new_stream(data, &data->video, &data->vcodec,
+				data->output->oformat->video_codec))
+		return false;
+
+	context                = data->video->codec;
+	context->codec_id      = data->output->oformat->video_codec;
+	context->bit_rate      = 6000000;
+	context->width         = ovi.output_width;
+	context->height        = ovi.output_height;
+	context->time_base.num = ovi.fps_den;
+	context->time_base.den = ovi.fps_num;
+	context->gop_size      = 12;
+	context->pix_fmt       = AV_PIX_FMT_YUV420P;
+
+	if (data->output->oformat->flags & AVFMT_GLOBALHEADER)
+		context->flags |= CODEC_FLAG_GLOBAL_HEADER;
+
+	return open_video_codec(data, &ovi);
+}
+
+static bool open_audio_codec(struct ffmpeg_data *data,
+		struct audio_output_info *aoi)
+{
+	AVCodecContext *context = data->audio->codec;
+	int ret;
+
+	data->aframe = av_frame_alloc();
+	if (!data->aframe) {
+		blog(LOG_ERROR, "Failed to allocate audio frame");
+		return false;
+	}
+
+	ret = avcodec_open2(context, data->acodec, NULL);
+	if (ret < 0) {
+		blog(LOG_ERROR, "Failed to open audio codec: %s",
+				av_err2str(ret));
+		return false;
+	}
+
+	return true;
+}
+
+static bool create_audio_stream(struct ffmpeg_data *data)
+{
+	AVCodecContext *context;
+	struct audio_output_info aoi;
+
+	if (!obs_get_audio_info(&aoi)) {
+		blog(LOG_ERROR, "No active audio");
+		return false;
+	}
+	if (!new_stream(data, &data->audio, &data->acodec,
+				data->output->oformat->audio_codec))
+		return false;
+
+	context = data->audio->codec;
+	context->bit_rate    = 128000;
+	context->channels    = get_audio_channels(aoi.speakers);
+	context->sample_rate = aoi.samples_per_sec;
+
+	if (data->output->oformat->flags & AVFMT_GLOBALHEADER)
+		context->flags |= CODEC_FLAG_GLOBAL_HEADER;
+
+	return open_audio_codec(data, &aoi);
+}
+
+static inline bool init_streams(struct ffmpeg_data *data)
+{
+	AVOutputFormat *format = data->output->oformat;
+
+	if (format->video_codec != AV_CODEC_ID_NONE) {
+		if (!create_video_stream(data))
+			return false;
+	}
+
+	if (format->audio_codec != AV_CODEC_ID_NONE) {
+		if (!create_audio_stream(data))
+			return false;
+	}
+
+	return true;
+}
+
+static inline bool open_output_file(struct ffmpeg_data *data)
+{
+	AVOutputFormat *format = data->output->oformat;
+	int ret;
+
+	if ((format->flags & AVFMT_NOFILE) == 0) {
+		ret = avio_open(&data->output->pb, FILENAME_TODO,
+				AVIO_FLAG_WRITE);
+		if (ret < 0) {
+			blog(LOG_ERROR, "Couldn't open file '%s', %s",
+					FILENAME_TODO, av_err2str(ret));
+			return false;
+		}
+	}
+
+	ret = avformat_write_header(data->output, NULL);
+	if (ret < 0) {
+		blog(LOG_ERROR, "Error opening file '%s': %s",
+				FILENAME_TODO, av_err2str(ret));
+	}
+
+	return true;
+}
+
+static void close_video(struct ffmpeg_data *data)
+{
+	avcodec_close(data->video->codec);
+	av_free(data->src_picture.data[0]);
+	av_free(data->dst_picture.data[0]);
+	av_frame_free(&data->vframe);
+}
+
+static void close_audio(struct ffmpeg_data *data)
+{
+	avcodec_close(data->audio->codec);
+
+	av_free(data->samples[0]);
+	av_free(data->samples);
+	av_frame_free(&data->aframe);
+}
+
+static void ffmpeg_data_free(struct ffmpeg_data *data)
+{
+	if (data->initialized)
+		av_write_trailer(data->output);
+
+	if (data->video)
+		close_video(data);
+	if (data->audio)
+		close_audio(data);
+	if ((data->output->oformat->flags & AVFMT_NOFILE) == 0)
+		avio_close(data->output->pb);
+
+	avformat_free_context(data->output);
+}
+
+static bool ffmpeg_data_init(struct ffmpeg_data *data)
+{
+	memset(data, 0, sizeof(struct ffmpeg_data));
+
+	av_register_all();
+
+	/* TODO: settings */
+	avformat_alloc_output_context2(&data->output, NULL, NULL,
+			"D:\\test.mp4");
+	if (!data->output) {
+		blog(LOG_ERROR, "Couldn't create avformat context");
+		goto fail;
+	}
+
+	if (!init_streams(data))
+		goto fail;
+	if (!open_output_file(data))
+		goto fail;
+
+	data->initialized = true;
+	return true;
+
+fail:
+	blog(LOG_ERROR, "ffmpeg_data_init failed");
+	ffmpeg_data_free(data);
+	return false;
+}
+
+/* ------------------------------------------------------------------------- */
+
+const char *ffmpeg_output_getname(const char *locale)
+{
+	/* TODO: translation */
+	return "FFmpeg file output";
+}
+
+struct ffmpeg_output *ffmpeg_output_create(const char *settings,
+		obs_output_t output)
+{
+	struct ffmpeg_output *data = bmalloc(sizeof(struct ffmpeg_output));
+	memset(data, 0, sizeof(struct ffmpeg_output));
+
+	data->output = output;
+
+	return data;
+}
+
+void ffmpeg_output_destroy(struct ffmpeg_output *data)
+{
+	if (data) {
+		ffmpeg_data_free(&data->ff_data);
+		bfree(data);
+	}
+}
+
+void ffmpeg_output_update(struct ffmpeg_output *data, const char *settings)
+{
+}
+
+static void receive_video(void *param, const struct video_frame *frame)
+{
+}
+
+static void receive_audio(void *param, const struct audio_data *frame)
+{
+}
+
+bool ffmpeg_output_start(struct ffmpeg_output *data)
+{
+	video_t video = obs_video();
+	audio_t audio = obs_audio();
+
+	if (!video || !audio) {
+		blog(LOG_ERROR, "ffmpeg_output_start: audio and video must "
+		                "both be active (at least as of this writing)");
+		return false;
+	}
+
+	if (!ffmpeg_data_init(&data->ff_data))
+		return false;
+
+	struct audio_convert_info aci;
+	aci.samples_per_sec = 44100;
+	aci.format          = AUDIO_FORMAT_16BIT;
+	aci.speakers        = SPEAKERS_STEREO;
+
+	struct video_convert_info vci;
+	vci.format          = VIDEO_FORMAT_I420;
+	vci.width           = 0;
+	vci.height          = 0;
+	vci.row_align       = 1;
+
+	video_output_connect(video, &vci, receive_video, data);
+	audio_output_connect(audio, &aci, receive_audio, data);
+	data->active = true;
+
+	return true;
+}
+
+void ffmpeg_output_stop(struct ffmpeg_output *data)
+{
+	if (data->active) {
+		data->active = false;
+		video_output_disconnect(obs_video(), receive_video, data);
+		audio_output_disconnect(obs_audio(), receive_audio, data);
+		ffmpeg_data_free(&data->ff_data);
+	}
+}
+
+bool ffmpeg_output_active(struct ffmpeg_output *data)
+{
+	return data->active;
+}

+ 62 - 0
plugins/obs-ffmpeg/obs-ffmpeg-output.h

@@ -0,0 +1,62 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#pragma once
+
+#include <util/c99defs.h>
+#include <media-io/video-io.h>
+
+#include <libavformat/avformat.h>
+#include <libswscale/swscale.h>
+
+struct ffmpeg_data {
+	AVStream           *video;
+	AVStream           *audio;
+	AVCodec            *acodec;
+	AVCodec            *vcodec;
+	AVFormatContext    *output;
+	struct SwsContext  *swscale;
+
+	AVFrame            *vframe;
+	AVPicture          src_picture;
+	AVPicture          dst_picture;
+
+	AVFrame            *aframe;
+	uint8_t            **samples;
+
+	bool               initialized;
+};
+
+struct ffmpeg_output {
+	obs_output_t       output;
+	volatile bool      active;
+	struct ffmpeg_data ff_data;
+};
+
+EXPORT const char *ffmpeg_output_getname(const char *locale);
+
+EXPORT struct ffmpeg_output *ffmpeg_output_create(const char *settings,
+		obs_output_t output);
+EXPORT void ffmpeg_output_destroy(struct ffmpeg_output *data);
+
+EXPORT void ffmpeg_output_update(struct ffmpeg_output *data,
+		const char *settings);
+
+EXPORT bool ffmpeg_output_start(struct ffmpeg_output *data);
+EXPORT void ffmpeg_output_stop(struct ffmpeg_output *data);
+
+EXPORT bool ffmpeg_output_active(struct ffmpeg_output *data);

+ 14 - 0
plugins/obs-ffmpeg/obs-ffmpeg.c

@@ -0,0 +1,14 @@
+#include <string.h>
+#include <util/c99defs.h>
+
+EXPORT const char *enum_outputs(size_t idx);
+
+static const char *outputs[] = {"obs_ffmpeg"};
+
+const char *enum_outputs(size_t idx)
+{
+	if (idx >= sizeof(outputs)/sizeof(const char*))
+		return NULL;
+
+	return outputs[idx];
+}

+ 1 - 1
plugins/obs-outputs/obs-stream.c → plugins/obs-outputs/rtmp-stream.c

@@ -15,7 +15,7 @@
     along with this program.  If not, see <http://www.gnu.org/licenses/>.
     along with this program.  If not, see <http://www.gnu.org/licenses/>.
 ******************************************************************************/
 ******************************************************************************/
 
 
-#include "obs-stream.h"
+#include "rtmp-stream.h"
 
 
 const char *rtmp_stream_getname(const char *locale)
 const char *rtmp_stream_getname(const char *locale)
 {
 {

+ 0 - 0
plugins/obs-outputs/obs-stream.h → plugins/obs-outputs/rtmp-stream.h


+ 13 - 0
vs/2013/OBS.sln

@@ -40,6 +40,11 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "OBS", "OBS\OBS.vcxproj", "{
 EndProject
 EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "jansson", "..\..\deps\jansson\win32\vs2010\jansson.vcxproj", "{76226D20-1972-4789-A595-EDACC7A76DC3}"
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "jansson", "..\..\deps\jansson\win32\vs2010\jansson.vcxproj", "{76226D20-1972-4789-A595-EDACC7A76DC3}"
 EndProject
 EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "obs-ffmpeg", "obs-ffmpeg\obs-ffmpeg.vcxproj", "{36970254-B1E5-4BE6-A561-301B6E7CDCE3}"
+	ProjectSection(ProjectDependencies) = postProject
+		{6F1AC2AE-6424-401A-AF9F-A771E6BEE026} = {6F1AC2AE-6424-401A-AF9F-A771E6BEE026}
+	EndProjectSection
+EndProject
 Global
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Win32 = Debug|Win32
 		Debug|Win32 = Debug|Win32
@@ -120,6 +125,14 @@ Global
 		{76226D20-1972-4789-A595-EDACC7A76DC3}.Release|Win32.Build.0 = Release|Win32
 		{76226D20-1972-4789-A595-EDACC7A76DC3}.Release|Win32.Build.0 = Release|Win32
 		{76226D20-1972-4789-A595-EDACC7A76DC3}.Release|x64.ActiveCfg = Release|x64
 		{76226D20-1972-4789-A595-EDACC7A76DC3}.Release|x64.ActiveCfg = Release|x64
 		{76226D20-1972-4789-A595-EDACC7A76DC3}.Release|x64.Build.0 = Release|x64
 		{76226D20-1972-4789-A595-EDACC7A76DC3}.Release|x64.Build.0 = Release|x64
+		{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|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
 	EndGlobalSection
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
 		HideSolutionNode = FALSE

+ 183 - 0
vs/2013/obs-ffmpeg/obs-ffmpeg.vcxproj

@@ -0,0 +1,183 @@
+<?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>{36970254-B1E5-4BE6-A561-301B6E7CDCE3}</ProjectGuid>
+    <Keyword>Win32Proj</Keyword>
+    <RootNamespace>obsffmpeg</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>$(FFmpegPath);$(IncludePath)</IncludePath>
+    <LibraryPath>$(FFmpegPath)\lib32;$(FFmpegPath)\libavcodec;$(FFmpegPath)\libavutil;$(FFmpegPath)\libavformat;$(FFmpegPath)\libswresample;$(FFmpegPath)\libswscale;$(LibraryPath)</LibraryPath>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <LinkIncremental>true</LinkIncremental>
+    <IncludePath>$(FFmpegPath);$(IncludePath)</IncludePath>
+    <LibraryPath>$(FFmpegPath)\lib64;$(FFmpegPath)\libavcodec;$(FFmpegPath)\libavutil;$(FFmpegPath)\libavformat;$(FFmpegPath)\libswresample;$(FFmpegPath)\libswscale;$(LibraryPath)</LibraryPath>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <LinkIncremental>false</LinkIncremental>
+    <IncludePath>$(FFmpegPath);$(IncludePath)</IncludePath>
+    <LibraryPath>$(FFmpegPath)\lib32;$(FFmpegPath)\libavcodec;$(FFmpegPath)\libavutil;$(FFmpegPath)\libavformat;$(FFmpegPath)\libswresample;$(FFmpegPath)\libswscale;$(LibraryPath)</LibraryPath>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <LinkIncremental>false</LinkIncremental>
+    <IncludePath>$(FFmpegPath);$(IncludePath)</IncludePath>
+    <LibraryPath>$(FFmpegPath)\lib64;$(FFmpegPath)\libavcodec;$(FFmpegPath)\libavutil;$(FFmpegPath)\libavformat;$(FFmpegPath)\libswresample;$(FFmpegPath)\libswscale;$(LibraryPath)</LibraryPath>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;OBSFFMPEG_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>../../../libobs</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalDependencies>avcodec.lib;avformat.lib;swscale.lib;avutil.lib;libobs.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+    <PostBuildEvent>
+      <Command>copy "$(OutDir)$(TargetName)$(TargetExt)" "../../../build/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;OBSFFMPEG_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>../../../libobs</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalDependencies>avcodec.lib;avformat.lib;swscale.lib;avutil.lib;libobs.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+    <PostBuildEvent>
+      <Command>copy "$(OutDir)$(TargetName)$(TargetExt)" "../../../build/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;OBSFFMPEG_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>avcodec.lib;avformat.lib;swscale.lib;avutil.lib;libobs.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+    <PostBuildEvent>
+      <Command>copy "$(OutDir)$(TargetName)$(TargetExt)" "../../../build/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;OBSFFMPEG_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>avcodec.lib;avformat.lib;swscale.lib;avutil.lib;libobs.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+    <PostBuildEvent>
+      <Command>copy "$(OutDir)$(TargetName)$(TargetExt)" "../../../build/plugins/64bit/$(TargetName)$(TargetExt)"</Command>
+    </PostBuildEvent>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\..\plugins\obs-ffmpeg\obs-ffmpeg-output.c" />
+    <ClCompile Include="..\..\..\plugins\obs-ffmpeg\obs-ffmpeg.c" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\..\plugins\obs-ffmpeg\obs-ffmpeg-output.h" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>

+ 30 - 0
vs/2013/obs-ffmpeg/obs-ffmpeg.vcxproj.filters

@@ -0,0 +1,30 @@
+<?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-ffmpeg\obs-ffmpeg.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\..\plugins\obs-ffmpeg\obs-ffmpeg-output.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\..\plugins\obs-ffmpeg\obs-ffmpeg-output.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+  </ItemGroup>
+</Project>

+ 16 - 0
vs/2013/obs-outputs/obs-outputs.vcxproj

@@ -91,7 +91,11 @@
       <SubSystem>Windows</SubSystem>
       <SubSystem>Windows</SubSystem>
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <AdditionalDependencies>libobs.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <AdditionalDependencies>libobs.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
     </Link>
     </Link>
+    <PostBuildEvent>
+      <Command>copy "$(OutDir)$(TargetName)$(TargetExt)" "../../../build/plugins/32bit/$(TargetName)$(TargetExt)"</Command>
+    </PostBuildEvent>
   </ItemDefinitionGroup>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
     <ClCompile>
     <ClCompile>
@@ -106,7 +110,11 @@
       <SubSystem>Windows</SubSystem>
       <SubSystem>Windows</SubSystem>
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <AdditionalDependencies>libobs.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <AdditionalDependencies>libobs.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
     </Link>
     </Link>
+    <PostBuildEvent>
+      <Command>copy "$(OutDir)$(TargetName)$(TargetExt)" "../../../build/plugins/64bit/$(TargetName)$(TargetExt)"</Command>
+    </PostBuildEvent>
   </ItemDefinitionGroup>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
     <ClCompile>
     <ClCompile>
@@ -125,7 +133,11 @@
       <EnableCOMDATFolding>true</EnableCOMDATFolding>
       <EnableCOMDATFolding>true</EnableCOMDATFolding>
       <OptimizeReferences>true</OptimizeReferences>
       <OptimizeReferences>true</OptimizeReferences>
       <AdditionalDependencies>libobs.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <AdditionalDependencies>libobs.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
     </Link>
     </Link>
+    <PostBuildEvent>
+      <Command>copy "$(OutDir)$(TargetName)$(TargetExt)" "../../../build/plugins/32bit/$(TargetName)$(TargetExt)"</Command>
+    </PostBuildEvent>
   </ItemDefinitionGroup>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
     <ClCompile>
     <ClCompile>
@@ -144,7 +156,11 @@
       <EnableCOMDATFolding>true</EnableCOMDATFolding>
       <EnableCOMDATFolding>true</EnableCOMDATFolding>
       <OptimizeReferences>true</OptimizeReferences>
       <OptimizeReferences>true</OptimizeReferences>
       <AdditionalDependencies>libobs.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <AdditionalDependencies>libobs.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
     </Link>
     </Link>
+    <PostBuildEvent>
+      <Command>copy "$(OutDir)$(TargetName)$(TargetExt)" "../../../build/plugins/64bit/$(TargetName)$(TargetExt)"</Command>
+    </PostBuildEvent>
   </ItemDefinitionGroup>
   </ItemDefinitionGroup>
   <ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\..\..\plugins\obs-outputs\obs-outputs.h" />
     <ClInclude Include="..\..\..\plugins\obs-outputs\obs-outputs.h" />

+ 5 - 5
vs/2013/obs-outputs/obs-outputs.vcxproj.filters

@@ -15,21 +15,21 @@
     </Filter>
     </Filter>
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
-    <ClInclude Include="..\..\..\plugins\obs-outputs\obs-stream.h">
-      <Filter>Header Files</Filter>
-    </ClInclude>
     <ClInclude Include="..\..\..\plugins\obs-outputs\obs-outputs.h">
     <ClInclude Include="..\..\..\plugins\obs-outputs\obs-outputs.h">
       <Filter>Header Files</Filter>
       <Filter>Header Files</Filter>
     </ClInclude>
     </ClInclude>
     <ClInclude Include="..\..\..\plugins\obs-outputs\obs-x264.h">
     <ClInclude Include="..\..\..\plugins\obs-outputs\obs-x264.h">
       <Filter>Header Files</Filter>
       <Filter>Header Files</Filter>
     </ClInclude>
     </ClInclude>
+    <ClInclude Include="..\..\..\plugins\obs-outputs\obs-stream.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
-    <ClCompile Include="..\..\..\plugins\obs-outputs\obs-stream.c">
+    <ClCompile Include="..\..\..\plugins\obs-outputs\obs-outputs.c">
       <Filter>Source Files</Filter>
       <Filter>Source Files</Filter>
     </ClCompile>
     </ClCompile>
-    <ClCompile Include="..\..\..\plugins\obs-outputs\obs-outputs.c">
+    <ClCompile Include="..\..\..\plugins\obs-outputs\obs-stream.c">
       <Filter>Source Files</Filter>
       <Filter>Source Files</Filter>
     </ClCompile>
     </ClCompile>
     <ClCompile Include="..\..\..\plugins\obs-outputs\obs-x264.c">
     <ClCompile Include="..\..\..\plugins\obs-outputs\obs-x264.c">