Przeglądaj źródła

Merge pull request #131 from jp9000/image-support

Implement image file support
Jim 11 lat temu
rodzic
commit
66de2869bf

+ 5 - 5
libobs-d3d11/d3d11-subsystem.cpp

@@ -508,7 +508,7 @@ uint32_t device_getheight(device_t device)
 
 texture_t device_create_texture(device_t device, uint32_t width,
 		uint32_t height, enum gs_color_format color_format,
-		uint32_t levels, const void **data, uint32_t flags)
+		uint32_t levels, const uint8_t **data, uint32_t flags)
 {
 	gs_texture *texture = NULL;
 	try {
@@ -527,7 +527,7 @@ texture_t device_create_texture(device_t device, uint32_t width,
 
 texture_t device_create_cubetexture(device_t device, uint32_t size,
 		enum gs_color_format color_format, uint32_t levels,
-		const void **data, uint32_t flags)
+		const uint8_t **data, uint32_t flags)
 {
 	gs_texture *texture = NULL;
 	try {
@@ -549,7 +549,7 @@ texture_t device_create_cubetexture(device_t device, uint32_t size,
 texture_t device_create_volumetexture(device_t device, uint32_t width,
 		uint32_t height, uint32_t depth,
 		enum gs_color_format color_format, uint32_t levels,
-		const void **data, uint32_t flags)
+		const uint8_t **data, uint32_t flags)
 {
 	/* TODO */
 	UNUSED_PARAMETER(device);
@@ -1502,7 +1502,7 @@ enum gs_color_format texture_getcolorformat(texture_t tex)
 	return static_cast<gs_texture_2d*>(tex)->format;
 }
 
-bool texture_map(texture_t tex, void **ptr, uint32_t *linesize)
+bool texture_map(texture_t tex, uint8_t **ptr, uint32_t *linesize)
 {
 	HRESULT hr;
 
@@ -1517,7 +1517,7 @@ bool texture_map(texture_t tex, void **ptr, uint32_t *linesize)
 	if (FAILED(hr))
 		return false;
 
-	*ptr = map.pData;
+	*ptr = (uint8_t*)map.pData;
 	*linesize = map.RowPitch;
 	return true;
 }

+ 4 - 4
libobs-d3d11/d3d11-subsystem.hpp

@@ -259,8 +259,8 @@ struct gs_texture_2d : gs_texture {
 	bool            genMipmaps;
 	HANDLE          sharedHandle;
 
-	void InitSRD(vector<D3D11_SUBRESOURCE_DATA> &srd, const void **data);
-	void InitTexture(const void **data);
+	void InitSRD(vector<D3D11_SUBRESOURCE_DATA> &srd, const uint8_t **data);
+	void InitTexture(const uint8_t **data);
 	void InitResourceView();
 	void InitRenderTargets();
 
@@ -280,8 +280,8 @@ struct gs_texture_2d : gs_texture {
 
 	gs_texture_2d(device_t device, uint32_t width, uint32_t height,
 			gs_color_format colorFormat, uint32_t levels,
-			const void **data, uint32_t flags, gs_texture_type type,
-			bool gdiCompatible, bool shared);
+			const uint8_t **data, uint32_t flags,
+			gs_texture_type type, bool gdiCompatible, bool shared);
 };
 
 struct gs_zstencil_buffer {

+ 5 - 5
libobs-d3d11/d3d11-texture2d.cpp

@@ -19,7 +19,7 @@
 #include "d3d11-subsystem.hpp"
 
 void gs_texture_2d::InitSRD(vector<D3D11_SUBRESOURCE_DATA> &srd,
-		const void **data)
+		const uint8_t **data)
 {
 	uint32_t rowSizeBytes  = width  * gs_get_format_bpp(format);
 	uint32_t texSizeBytes  = height * rowSizeBytes / 8;
@@ -49,7 +49,7 @@ void gs_texture_2d::InitSRD(vector<D3D11_SUBRESOURCE_DATA> &srd,
 	}
 }
 
-void gs_texture_2d::InitTexture(const void **data)
+void gs_texture_2d::InitTexture(const uint8_t **data)
 {
 	vector<D3D11_SUBRESOURCE_DATA> srd;
 	D3D11_TEXTURE2D_DESC td;
@@ -142,9 +142,9 @@ void gs_texture_2d::InitRenderTargets()
 }
 
 gs_texture_2d::gs_texture_2d(device_t device, uint32_t width, uint32_t height,
-		gs_color_format colorFormat, uint32_t levels, const void **data,
-		uint32_t flags, gs_texture_type type, bool gdiCompatible,
-		bool shared)
+		gs_color_format colorFormat, uint32_t levels,
+		const uint8_t **data, uint32_t flags, gs_texture_type type,
+		bool gdiCompatible, bool shared)
 	: gs_texture      (device, type, levels, colorFormat),
 	  width           (width),
 	  height          (height),

+ 2 - 2
libobs-opengl/gl-helpers.c

@@ -20,10 +20,10 @@
 bool gl_init_face(GLenum target, GLenum type, uint32_t num_levels,
 		GLenum format, GLint internal_format, bool compressed,
 		uint32_t width, uint32_t height, uint32_t size,
-		const void ***p_data)
+		const uint8_t ***p_data)
 {
 	bool success = true;
-	const void **data = p_data ? *p_data : NULL;
+	const uint8_t **data = p_data ? *p_data : NULL;
 	uint32_t i;
 
 	for (i = 0; i < num_levels; i++) {

+ 1 - 1
libobs-opengl/gl-helpers.h

@@ -146,7 +146,7 @@ static inline bool gl_get_integer_v(GLenum pname, GLint *params)
 extern bool gl_init_face(GLenum target, GLenum type, uint32_t num_levels,
 		GLenum format, GLint internal_format, bool compressed,
 		uint32_t width, uint32_t height, uint32_t size,
-		const void ***p_data);
+		const uint8_t ***p_data);
 
 extern bool gl_copy_texture(struct gs_device *device,
 		GLuint dst, GLenum dst_target, uint32_t dst_x, uint32_t dst_y,

+ 1 - 1
libobs-opengl/gl-subsystem.c

@@ -269,7 +269,7 @@ uint32_t device_getheight(device_t device)
 texture_t device_create_volumetexture(device_t device, uint32_t width,
 		uint32_t height, uint32_t depth,
 		enum gs_color_format color_format, uint32_t levels,
-		const void **data, uint32_t flags)
+		const uint8_t **data, uint32_t flags)
 {
 	/* TODO */
 	UNUSED_PARAMETER(device);

+ 3 - 3
libobs-opengl/gl-texture2d.c

@@ -17,7 +17,7 @@
 
 #include "gl-subsystem.h"
 
-static bool upload_texture_2d(struct gs_texture_2d *tex, const void **data)
+static bool upload_texture_2d(struct gs_texture_2d *tex, const uint8_t **data)
 {
 	uint32_t row_size   = tex->width  * gs_get_format_bpp(tex->base.format);
 	uint32_t tex_size   = tex->height * row_size / 8;
@@ -76,7 +76,7 @@ static bool create_pixel_unpack_buffer(struct gs_texture_2d *tex)
 
 texture_t device_create_texture(device_t device, uint32_t width,
 		uint32_t height, enum gs_color_format color_format,
-		uint32_t levels, const void **data, uint32_t flags)
+		uint32_t levels, const uint8_t **data, uint32_t flags)
 {
 	struct gs_texture_2d *tex = bzalloc(sizeof(struct gs_texture_2d));
 	tex->base.device             = device;
@@ -164,7 +164,7 @@ enum gs_color_format texture_getcolorformat(texture_t tex)
 	return tex->format;
 }
 
-bool texture_map(texture_t tex, void **ptr, uint32_t *linesize)
+bool texture_map(texture_t tex, uint8_t **ptr, uint32_t *linesize)
 {
 	struct gs_texture_2d *tex2d = (struct gs_texture_2d*)tex;
 

+ 2 - 2
libobs-opengl/gl-texturecube.c

@@ -18,7 +18,7 @@
 #include "gl-subsystem.h"
 
 static inline bool upload_texture_cube(struct gs_texture_cube *tex,
-		const void **data)
+		const uint8_t **data)
 {
 	uint32_t row_size   = tex->size * gs_get_format_bpp(tex->base.format);
 	uint32_t tex_size   = tex->size * row_size / 8;
@@ -60,7 +60,7 @@ static inline bool upload_texture_cube(struct gs_texture_cube *tex,
 
 texture_t device_create_cubetexture(device_t device, uint32_t size,
 		enum gs_color_format color_format, uint32_t levels,
-		const void **data, uint32_t flags)
+		const uint8_t **data, uint32_t flags)
 {
 	struct gs_texture_cube *tex = bzalloc(sizeof(struct gs_texture_cube));
 	tex->base.device             = device;

+ 7 - 1
libobs/CMakeLists.txt

@@ -16,6 +16,10 @@ find_package(Libavformat REQUIRED)
 include_directories(${LIBAVFORMAT_INCLUDE_DIRS})
 add_definitions(${LIBAVFORMAT_DEFINITIONS})
 
+find_package(Libavcodec REQUIRED)
+include_directories(${LIBAVCODEC_INCLUDE_DIRS})
+add_definitions(${LIBAVCODEC_DEFINITIONS})
+
 add_definitions(-DLIBOBS_EXPORTS)
 
 if(WIN32)
@@ -88,6 +92,7 @@ set(libobs_graphics_SOURCES
 	graphics/matrix4.c
 	graphics/vec3.c
 	graphics/graphics.c
+	graphics/graphics-ffmpeg.c
 	graphics/shader-parser.c
 	graphics/plane.c
 	graphics/effect.c
@@ -238,7 +243,8 @@ target_link_libraries(libobs
 		${LIBSWSCALE_LIBRARIES}
 		${LIBSWRESAMPLE_LIBRARIES}
 		${LIBAVFORMAT_LIBRARIES}
-		${LIBAVUTIL_LIBRARIES})
+		${LIBAVUTIL_LIBRARIES}
+		${LIBAVCODEC_LIBRARIES})
 
 install_obs_core(libobs EXPORT LibObs)
 install_obs_data(libobs ../build/data/libobs libobs)

+ 3 - 3
libobs/graphics/device-exports.h

@@ -36,14 +36,14 @@ EXPORT uint32_t device_getwidth(device_t device);
 EXPORT uint32_t device_getheight(device_t device);
 EXPORT texture_t device_create_texture(device_t device, uint32_t width,
 		uint32_t height, enum gs_color_format color_format,
-		uint32_t levels, const void **data, uint32_t flags);
+		uint32_t levels, const uint8_t **data, uint32_t flags);
 EXPORT texture_t device_create_cubetexture(device_t device, uint32_t size,
 		enum gs_color_format color_format, uint32_t levels,
-		const void **data, uint32_t flags);
+		const uint8_t **data, uint32_t flags);
 EXPORT texture_t device_create_volumetexture(device_t device, uint32_t width,
 		uint32_t height, uint32_t depth,
 		enum gs_color_format color_format, uint32_t levels,
-		const void **data, uint32_t flags);
+		const uint8_t **data, uint32_t flags);
 EXPORT zstencil_t device_create_zstencil(device_t device, uint32_t width,
 		uint32_t height, enum gs_zstencil_format format);
 EXPORT stagesurf_t device_create_stagesurface(device_t device, uint32_t width,

+ 183 - 0
libobs/graphics/graphics-ffmpeg.c

@@ -0,0 +1,183 @@
+#include "graphics.h"
+
+#include <libavcodec/avcodec.h>
+#include <libavformat/avformat.h>
+#include <libswscale/swscale.h>
+
+struct ffmpeg_image {
+	const char         *file;
+	AVFormatContext    *fmt_ctx;
+	AVCodecContext     *decoder_ctx;
+	AVCodec            *decoder;
+	AVStream           *stream;
+	int                stream_idx;
+
+	int                cx, cy;
+	enum AVPixelFormat format;
+};
+
+static bool ffmpeg_image_open_decoder_context(struct ffmpeg_image *info)
+{
+	int ret = av_find_best_stream(info->fmt_ctx, AVMEDIA_TYPE_VIDEO,
+			-1, 1, NULL, 0);
+	if (ret < 0) {
+		blog(LOG_WARNING, "Couldn't find video stream in file '%s': %s",
+				info->file, av_err2str(ret));
+		return false;
+	}
+
+	info->stream_idx  = ret;
+	info->stream      = info->fmt_ctx->streams[ret];
+	info->decoder_ctx = info->stream->codec;
+	info->decoder     = avcodec_find_decoder(info->decoder_ctx->codec_id);
+
+	if (!info->decoder) {
+		blog(LOG_WARNING, "Failed to find decoder for file '%s'",
+				info->file);
+		return false;
+	}
+
+	ret = avcodec_open2(info->decoder_ctx, info->decoder, NULL);
+	if (ret < 0) {
+		blog(LOG_WARNING, "Failed to open video codec for file '%s': "
+		                  "%s", info->file, av_err2str(ret));
+		return false;
+	}
+
+	return true;
+}
+
+static void ffmpeg_image_free(struct ffmpeg_image *info)
+{
+	avcodec_close(info->decoder_ctx);
+	avformat_close_input(&info->fmt_ctx);
+}
+
+static bool ffmpeg_image_init(struct ffmpeg_image *info, const char *file)
+{
+	int ret;
+
+	memset(info, 0, sizeof(struct ffmpeg_image));
+	info->file       = file;
+	info->stream_idx = -1;
+
+	ret = avformat_open_input(&info->fmt_ctx, file, NULL, NULL);
+	if (ret < 0) {
+		blog(LOG_WARNING, "Failed to open file '%s': %s",
+				info->file, av_err2str(ret));
+		return false;
+	}
+
+	ret = avformat_find_stream_info(info->fmt_ctx, NULL);
+	if (ret < 0) {
+		blog(LOG_WARNING, "Could not find stream info for file '%s':"
+		                  " %s", info->file, av_err2str(ret));
+		goto fail;
+	}
+
+	if (!ffmpeg_image_open_decoder_context(info))
+		goto fail;
+
+	info->cx     = info->decoder_ctx->width;
+	info->cy     = info->decoder_ctx->height;
+	info->format = info->decoder_ctx->pix_fmt;
+	return true;
+
+fail:
+	ffmpeg_image_free(info);
+	return false;
+}
+
+static bool ffmpeg_image_reformat_frame(struct ffmpeg_image *info,
+		AVFrame *frame, uint8_t *out, int linesize)
+{
+	struct SwsContext *sws_ctx = NULL;
+	int               ret      = 0;
+
+	sws_ctx = sws_getContext(info->cx, info->cy, info->format,
+			info->cx, info->cy, AV_PIX_FMT_BGRA,
+			SWS_POINT, NULL, NULL, NULL);
+	if (!sws_ctx) {
+		blog(LOG_WARNING, "Failed to create scale context for '%s'",
+				info->file);
+		return false;
+	}
+
+	ret = sws_scale(sws_ctx, (const uint8_t *const*)frame->data,
+			frame->linesize, 0, info->cy, &out, &linesize);
+	sws_freeContext(sws_ctx);
+
+	if (ret < 0) {
+		blog(LOG_WARNING, "sws_scale failed for '%s': %s",
+				info->file, av_err2str(ret));
+		return false;
+	}
+
+	return true;
+}
+
+static bool ffmpeg_image_decode(struct ffmpeg_image *info, uint8_t *out,
+		int linesize)
+{
+	AVPacket          packet    = {0};
+	bool              success   = false;
+	AVFrame           *frame    = av_frame_alloc();
+	int               got_frame = 0;
+	int               ret;
+
+	if (!frame) {
+		blog(LOG_WARNING, "Failed to create frame data for '%s'",
+				info->file);
+		return false;
+	}
+
+	ret = av_read_frame(info->fmt_ctx, &packet);
+	if (ret < 0) {
+		blog(LOG_WARNING, "Failed to read image frame from '%s': %s",
+				info->file, av_err2str(ret));
+		goto fail;
+	}
+
+	while (!got_frame) {
+		ret = avcodec_decode_video2(info->decoder_ctx, frame,
+				&got_frame, &packet);
+		if (ret < 0) {
+			blog(LOG_WARNING, "Failed to decode frame for '%s': %s",
+					info->file, av_err2str(ret));
+			goto fail;
+		}
+	}
+
+	success = ffmpeg_image_reformat_frame(info, frame, out, linesize);
+
+fail:
+	av_free_packet(&packet);
+	av_frame_free(&frame);
+	return success;
+}
+
+void gs_init_image_deps(void)
+{
+	av_register_all();
+}
+
+void gs_free_image_deps(void)
+{
+}
+
+texture_t gs_create_texture_from_file(const char *file)
+{
+	struct ffmpeg_image image;
+	texture_t           tex = NULL;
+
+	if (ffmpeg_image_init(&image, file)) {
+		uint8_t *data = malloc(image.cx * image.cy * 4);
+		if (ffmpeg_image_decode(&image, data, image.cx * 4))
+			tex = gs_create_texture(image.cx, image.cy, GS_BGRA, 1, 
+					(const uint8_t**)&data, 0);
+
+		ffmpeg_image_free(&image);
+		free(data);
+	}
+	return tex;
+}

+ 4 - 4
libobs/graphics/graphics-internal.h

@@ -37,14 +37,14 @@ struct gs_exports {
 	uint32_t (*device_getheight)(device_t device);
 	texture_t (*device_create_texture)(device_t device, uint32_t width,
 			uint32_t height, enum gs_color_format color_format,
-			uint32_t levels, const void **data, uint32_t flags);
+			uint32_t levels, const uint8_t **data, uint32_t flags);
 	texture_t (*device_create_cubetexture)(device_t device, uint32_t size,
 			enum gs_color_format color_format, uint32_t levels,
-			const void **data, uint32_t flags);
+			const uint8_t **data, uint32_t flags);
 	texture_t (*device_create_volumetexture)(device_t device,
 			uint32_t width, uint32_t height, uint32_t depth,
 			enum gs_color_format color_format, uint32_t levels,
-			const void **data, uint32_t flags);
+			const uint8_t **data, uint32_t flags);
 	zstencil_t (*device_create_zstencil)(device_t device,
 			uint32_t width, uint32_t height,
 			enum gs_zstencil_format format);
@@ -134,7 +134,7 @@ struct gs_exports {
 	uint32_t (*texture_getwidth)(texture_t tex);
 	uint32_t (*texture_getheight)(texture_t tex);
 	enum gs_color_format (*texture_getcolorformat)(texture_t tex);
-	bool     (*texture_map)(texture_t tex, void **ptr,
+	bool     (*texture_map)(texture_t tex, uint8_t **ptr,
 			uint32_t *linesize);
 	void     (*texture_unmap)(texture_t tex);
 	bool     (*texture_isrect)(texture_t tex);

+ 51 - 0
libobs/graphics/graphics-magick.c

@@ -0,0 +1,51 @@
+#include "graphics.h"
+
+#include <magick/MagickCore.h>
+
+void gs_init_image_deps()
+{
+	MagickCoreGenesis(NULL, MagickTrue);
+}
+
+void gs_free_image_deps()
+{
+	MagickCoreTerminus();
+}
+
+texture_t gs_create_texture_from_file(const char *file)
+{
+	texture_t     tex        = NULL;
+	ImageInfo     *info      = CloneImageInfo(NULL);
+	ExceptionInfo *exception = AcquireExceptionInfo();
+	Image         *image;
+
+	strcpy(info->filename, file);
+	image = ReadImage(info, exception);
+	if (image) {
+		size_t  cx    = image->magick_columns;
+		size_t  cy    = image->magick_rows;
+		uint8_t *data = malloc(cx * cy * 4);
+
+		ExportImagePixels(image, 0, 0, cx, cy, "BGRA", CharPixel,
+				data, exception);
+		if (exception->severity == UndefinedException)
+			tex = gs_create_texture(cx, cy, GS_BGRA, 1,
+					(const uint8**)&data, 0);
+		else
+			blog(LOG_WARNING, "magickcore warning/error getting "
+			                  "pixels from file '%s': %s", file,
+			                  exception->reason);
+
+		free(data);
+		DestroyImage(image);
+
+	} else if (exception->severity != UndefinedException) {
+		blog(LOG_WARNING, "magickcore warning/error reading file "
+		                  "'%s': %s", file, exception->reason);
+	}
+
+	DestroyImageInfo(info);
+	DestroyExceptionInfo(exception);
+
+	return tex;
+}

+ 22 - 21
libobs/graphics/graphics.c

@@ -36,6 +36,9 @@ static __thread graphics_t thread_graphics = NULL;
 
 #define IMMEDIATE_COUNT 512
 
+extern void gs_init_image_deps(void);
+extern void gs_free_image_deps(void);
+
 bool load_graphics_imports(struct gs_exports *exports, void *module,
 		const char *module_name);
 
@@ -103,6 +106,7 @@ static bool graphics_init(struct graphics_subsystem *graphics)
 
 	graphics->exports.device_leavecontext(graphics->device);
 
+	gs_init_image_deps();
 	return true;
 }
 
@@ -164,6 +168,8 @@ void gs_destroy(graphics_t graphics)
 	if (graphics->module)
 		os_dlclose(graphics->module);
 	bfree(graphics);
+
+	gs_free_image_deps();
 }
 
 void gs_entercontext(graphics_t graphics)
@@ -685,13 +691,6 @@ shader_t gs_create_pixelshader_from_file(const char *file, char **error_string)
 	return shader;
 }
 
-texture_t gs_create_texture_from_file(const char *file)
-{
-	/* TODO */
-	UNUSED_PARAMETER(file);
-	return NULL;
-}
-
 static inline void assign_sprite_rect(float *start, float *end, float size,
 		bool flip)
 {
@@ -845,18 +844,20 @@ void gs_viewport_pop(void)
 	da_pop_back(thread_graphics->viewport_stack);
 }
 
-void texture_setimage(texture_t tex, const void *data, uint32_t linesize,
+void texture_setimage(texture_t tex, const uint8_t *data, uint32_t linesize,
 		bool flip)
 {
-	if (!thread_graphics || !tex)
-		return;
-
-	void *ptr;
+	uint8_t *ptr;
 	uint32_t linesize_out;
 	uint32_t row_copy;
-	int32_t height = (int32_t)texture_getheight(tex);
+	int32_t height;
 	int32_t y;
 
+	if (!thread_graphics || !tex)
+		return;
+
+	height = (int32_t)texture_getheight(tex);
+
 	if (!texture_map(tex, &ptr, &linesize_out))
 		return;
 
@@ -864,8 +865,8 @@ void texture_setimage(texture_t tex, const void *data, uint32_t linesize,
 
 	if (flip) {
 		for (y = height-1; y >= 0; y--)
-			memcpy((uint8_t*)ptr  + (uint32_t)y * linesize_out,
-			       (uint8_t*)data + (uint32_t)y * linesize,
+			memcpy(ptr  + (uint32_t)y * linesize_out,
+			       data + (uint32_t)y * linesize,
 			       row_copy);
 
 	} else if (linesize == linesize_out) {
@@ -873,8 +874,8 @@ void texture_setimage(texture_t tex, const void *data, uint32_t linesize,
 
 	} else {
 		for (y = 0; y < height; y++)
-			memcpy((uint8_t*)ptr  + (uint32_t)y * linesize_out,
-			       (uint8_t*)data + (uint32_t)y * linesize,
+			memcpy(ptr  + (uint32_t)y * linesize_out,
+			       data + (uint32_t)y * linesize,
 			       row_copy);
 	}
 
@@ -967,7 +968,7 @@ static inline bool is_pow2(uint32_t size)
 
 texture_t gs_create_texture(uint32_t width, uint32_t height,
 		enum gs_color_format color_format, uint32_t levels,
-		const void **data, uint32_t flags)
+		const uint8_t **data, uint32_t flags)
 {
 	graphics_t graphics = thread_graphics;
 	bool pow2tex = is_pow2(width) && is_pow2(height);
@@ -999,7 +1000,7 @@ texture_t gs_create_texture(uint32_t width, uint32_t height,
 
 texture_t gs_create_cubetexture(uint32_t size,
 		enum gs_color_format color_format, uint32_t levels,
-		const void **data, uint32_t flags)
+		const uint8_t **data, uint32_t flags)
 {
 	graphics_t graphics = thread_graphics;
 	bool pow2tex = is_pow2(size);
@@ -1032,7 +1033,7 @@ texture_t gs_create_cubetexture(uint32_t size,
 
 texture_t gs_create_volumetexture(uint32_t width, uint32_t height,
 		uint32_t depth, enum gs_color_format color_format,
-		uint32_t levels, const void **data, uint32_t flags)
+		uint32_t levels, const uint8_t **data, uint32_t flags)
 {
 	graphics_t graphics = thread_graphics;
 	if (!graphics) return NULL;
@@ -1653,7 +1654,7 @@ enum gs_color_format texture_getcolorformat(texture_t tex)
 	return graphics->exports.texture_getcolorformat(tex);
 }
 
-bool texture_map(texture_t tex, void **ptr, uint32_t *linesize)
+bool texture_map(texture_t tex, uint8_t **ptr, uint32_t *linesize)
 {
 	graphics_t graphics = thread_graphics;
 	if (!graphics || !tex) return false;

+ 5 - 5
libobs/graphics/graphics.h

@@ -495,7 +495,7 @@ EXPORT void gs_set3dmode(double fovy, double znear, double zvar);
 EXPORT void gs_viewport_push(void);
 EXPORT void gs_viewport_pop(void);
 
-EXPORT void texture_setimage(texture_t tex, const void *data,
+EXPORT void texture_setimage(texture_t tex, const uint8_t *data,
 		uint32_t linesize, bool invert);
 EXPORT void cubetexture_setimage(texture_t cubetex, uint32_t side,
 		const void *data, uint32_t linesize, bool invert);
@@ -514,13 +514,13 @@ EXPORT uint32_t gs_getheight(void);
 
 EXPORT texture_t gs_create_texture(uint32_t width, uint32_t height,
 		enum gs_color_format color_format, uint32_t levels,
-		const void **data, uint32_t flags);
+		const uint8_t **data, uint32_t flags);
 EXPORT texture_t gs_create_cubetexture(uint32_t size,
 		enum gs_color_format color_format, uint32_t levels,
-		const void **data, uint32_t flags);
+		const uint8_t **data, uint32_t flags);
 EXPORT texture_t gs_create_volumetexture(uint32_t width, uint32_t height,
 		uint32_t depth, enum gs_color_format color_format,
-		uint32_t levels, const void **data, uint32_t flags);
+		uint32_t levels, const uint8_t **data, uint32_t flags);
 
 EXPORT zstencil_t gs_create_zstencil(uint32_t width, uint32_t height,
 		enum gs_zstencil_format format);
@@ -618,7 +618,7 @@ EXPORT void     texture_destroy(texture_t tex);
 EXPORT uint32_t texture_getwidth(texture_t tex);
 EXPORT uint32_t texture_getheight(texture_t tex);
 EXPORT enum gs_color_format texture_getcolorformat(texture_t tex);
-EXPORT bool     texture_map(texture_t tex, void **ptr, uint32_t *linesize);
+EXPORT bool     texture_map(texture_t tex, uint8_t **ptr, uint32_t *linesize);
 EXPORT void     texture_unmap(texture_t tex);
 /** special-case function (GL only) - specifies whether the texture is a
  * GL_TEXTURE_RECTANGLE type, which doesn't use normalized texture

+ 48 - 7
libobs/obs-properties.c

@@ -43,6 +43,12 @@ struct list_item {
 	};
 };
 
+struct path_data {
+	char               *filter;
+	char               *default_path;
+	enum obs_path_type type;
+};
+
 struct text_data {
 	enum obs_text_type type;
 };
@@ -57,6 +63,13 @@ struct button_data {
 	obs_property_clicked_t callback;
 };
 
+static inline void path_data_free(struct path_data *data)
+{
+	bfree(data->default_path);
+	if (data->type == OBS_PATH_FILE)
+		bfree(data->filter);
+}
+
 static inline void list_item_free(struct list_data *data,
 		struct list_item *item)
 {
@@ -133,10 +146,10 @@ obs_properties_t obs_properties_create_param(void *param,
 
 static void obs_property_destroy(struct obs_property *property)
 {
-	if (property->type == OBS_PROPERTY_LIST) {
-		struct list_data *data = get_property_data(property);
-		list_data_free(data);
-	}
+	if (property->type == OBS_PROPERTY_LIST)
+		list_data_free(get_property_data(property));
+	else if (property->type == OBS_PROPERTY_PATH)
+		path_data_free(get_property_data(property));
 
 	bfree(property);
 }
@@ -210,7 +223,7 @@ static inline size_t get_property_size(enum obs_property_type type)
 	case OBS_PROPERTY_INT:       return sizeof(struct int_data);
 	case OBS_PROPERTY_FLOAT:     return sizeof(struct float_data);
 	case OBS_PROPERTY_TEXT:      return sizeof(struct text_data);
-	case OBS_PROPERTY_PATH:      return 0;
+	case OBS_PROPERTY_PATH:      return sizeof(struct path_data);
 	case OBS_PROPERTY_LIST:      return sizeof(struct list_data);
 	case OBS_PROPERTY_COLOR:     return 0;
 	case OBS_PROPERTY_BUTTON:    return sizeof(struct button_data);
@@ -315,10 +328,20 @@ obs_property_t obs_properties_add_text(obs_properties_t props, const char *name,
 }
 
 obs_property_t obs_properties_add_path(obs_properties_t props, const char *name,
-		const char *desc)
+		const char *desc, enum obs_path_type type, const char *filter,
+		const char *default_path)
 {
 	if (!props || has_prop(props, name)) return NULL;
-	return new_prop(props, name, desc, OBS_PROPERTY_PATH);
+
+	struct obs_property *p = new_prop(props, name, desc, OBS_PROPERTY_PATH);
+	struct path_data *data = get_property_data(p);
+	data->type         = type;
+	data->default_path = bstrdup(default_path);
+
+	if (data->type == OBS_PATH_FILE)
+		data->filter = bstrdup(filter);
+
+	return p;
 }
 
 obs_property_t obs_properties_add_list(obs_properties_t props,
@@ -497,6 +520,24 @@ enum obs_text_type obs_proprety_text_type(obs_property_t p)
 	return data ? data->type : OBS_TEXT_DEFAULT;
 }
 
+enum obs_path_type obs_property_path_type(obs_property_t p)
+{
+	struct path_data *data = get_type_data(p, OBS_PROPERTY_PATH);
+	return data ? data->type : OBS_PATH_DIRECTORY;
+}
+
+const char *obs_property_path_filter(obs_property_t p)
+{
+	struct path_data *data = get_type_data(p, OBS_PROPERTY_PATH);
+	return data ? data->filter : NULL;
+}
+
+const char *obs_property_path_default_path(obs_property_t p)
+{
+	struct path_data *data = get_type_data(p, OBS_PROPERTY_PATH);
+	return data ? data->default_path : NULL;
+}
+
 enum obs_combo_type obs_property_list_type(obs_property_t p)
 {
 	struct list_data *data = get_list_data(p);

+ 27 - 1
libobs/obs-properties.h

@@ -49,6 +49,11 @@ enum obs_combo_type {
 	OBS_COMBO_TYPE_LIST,
 };
 
+enum obs_path_type {
+	OBS_PATH_FILE,
+	OBS_PATH_DIRECTORY
+};
+
 enum obs_text_type {
 	OBS_TEXT_DEFAULT,
 	OBS_TEXT_PASSWORD,
@@ -104,8 +109,26 @@ EXPORT obs_property_t obs_properties_add_text(obs_properties_t props,
 		const char *name, const char *description,
 		enum obs_text_type type);
 
+/**
+ * Adds a 'path' property.  Can be a directory or a file.
+ *
+ * If target is a file path, the filters should be this format, separated by
+ * double semi-colens, and extensions separated by space:
+ *   "Example types 1 and 2 (*.ex1 *.ex2);;Example type 3 (*.ex3)"
+ *
+ * @param  props        Properties object
+ * @param  name         Settings name
+ * @param  description  Description (display name) of the property
+ * @param  type         Type of path (directory or file)
+ * @param  filter       If type is a file path, then describes the file filter
+ *                      that the user can browse.  Items are separated via
+ *                      double semi-colens.  If multiple file types in a
+ *                      filter, separate with space.
+ */
 EXPORT obs_property_t obs_properties_add_path(obs_properties_t props,
-		const char *name, const char *description);
+		const char *name, const char *description,
+		enum obs_path_type type, const char *filter,
+		const char *default_path);
 
 EXPORT obs_property_t obs_properties_add_list(obs_properties_t props,
 		const char *name, const char *description,
@@ -152,6 +175,9 @@ EXPORT double                 obs_property_float_min(obs_property_t p);
 EXPORT double                 obs_property_float_max(obs_property_t p);
 EXPORT double                 obs_property_float_step(obs_property_t p);
 EXPORT enum obs_text_type     obs_proprety_text_type(obs_property_t p);
+EXPORT enum obs_path_type     obs_property_path_type(obs_property_t p);
+EXPORT const char *           obs_property_path_filter(obs_property_t p);
+EXPORT const char *           obs_property_path_default_path(obs_property_t p);
 EXPORT enum obs_combo_type    obs_property_list_type(obs_property_t p);
 EXPORT enum obs_combo_format  obs_property_list_format(obs_property_t p);
 

+ 1 - 1
libobs/obs-source.c

@@ -909,7 +909,7 @@ static bool update_async_texture(struct obs_source *source,
 	texture_t         tex       = source->async_texture;
 	texrender_t       texrender = source->async_convert_texrender;
 	enum convert_type type      = get_convert_type(frame->format);
-	void              *ptr;
+	uint8_t           *ptr;
 	uint32_t          linesize;
 
 	source->async_format     = frame->format;

+ 56 - 16
obs/properties-view.cpp

@@ -7,6 +7,7 @@
 #include <QComboBox>
 #include <QPushButton>
 #include <QStandardItem>
+#include <QFileDialog>
 #include "qt-wrappers.hpp"
 #include "properties-view.hpp"
 #include "obs-app.hpp"
@@ -101,12 +102,27 @@ QWidget *OBSPropertiesView::AddText(obs_property_t prop)
 	return NewWidget(prop, edit, SIGNAL(textEdited(const QString &)));
 }
 
-QWidget *OBSPropertiesView::AddPath(obs_property_t prop, QFormLayout *layout)
+void OBSPropertiesView::AddPath(obs_property_t prop, QFormLayout *layout,
+		QLabel **label)
 {
-	/* TODO */
-	UNUSED_PARAMETER(prop);
-	UNUSED_PARAMETER(layout);
-	return nullptr;
+	const char  *name      = obs_property_name(prop);
+	const char  *val       = obs_data_getstring(settings, name);
+	QLayout     *subLayout = new QHBoxLayout();
+	QLineEdit   *edit      = new QLineEdit();
+	QPushButton *button    = new QPushButton(QTStr("Browse"));
+
+	edit->setText(QT_UTF8(val));
+	edit->setReadOnly(true);
+
+	subLayout->addWidget(edit);
+	subLayout->addWidget(button);
+
+	WidgetInfo *info = new WidgetInfo(this, prop, edit);
+	connect(button, SIGNAL(clicked()), info, SLOT(ControlChanged()));
+	children.push_back(std::move(unique_ptr<WidgetInfo>(info)));
+
+	*label = new QLabel(QT_UTF8(obs_property_description(prop)));
+	layout->addRow(*label, subLayout);
 }
 
 QWidget *OBSPropertiesView::AddInt(obs_property_t prop)
@@ -286,6 +302,7 @@ void OBSPropertiesView::AddProperty(obs_property_t property,
 	if (!obs_property_visible(property))
 		return;
 
+	QLabel  *label  = nullptr;
 	QWidget *widget = nullptr;
 	bool    warning = false;
 
@@ -305,7 +322,7 @@ void OBSPropertiesView::AddProperty(obs_property_t property,
 		widget = AddText(property);
 		break;
 	case OBS_PROPERTY_PATH:
-		AddPath(property, layout);
+		AddPath(property, layout, &label);
 		break;
 	case OBS_PROPERTY_LIST:
 		widget = AddList(property, warning);
@@ -318,14 +335,11 @@ void OBSPropertiesView::AddProperty(obs_property_t property,
 		break;
 	}
 
-	if (!widget)
-		return;
-
-	if (!obs_property_enabled(property))
+	if (widget && !obs_property_enabled(property))
 		widget->setEnabled(false);
 
-	QLabel *label = nullptr;
-	if (type != OBS_PROPERTY_BOOL &&
+	if (!label &&
+	    type != OBS_PROPERTY_BOOL &&
 	    type != OBS_PROPERTY_BUTTON)
 		label = new QLabel(QT_UTF8(obs_property_description(property)));
 
@@ -337,6 +351,9 @@ void OBSPropertiesView::AddProperty(obs_property_t property,
 		label->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
 	}
 
+	if (!widget)
+		return;
+
 	layout->addRow(label, widget);
 
 	if (!lastFocused.empty())
@@ -369,10 +386,31 @@ void WidgetInfo::TextChanged(const char *setting)
 	obs_data_setstring(view->settings, setting, QT_TO_UTF8(edit->text()));
 }
 
-void WidgetInfo::PathChanged(const char *setting)
+bool WidgetInfo::PathChanged(const char *setting)
 {
-	/* TODO */
-	UNUSED_PARAMETER(setting);
+	const char    *desc         = obs_property_description(property);
+	obs_path_type type          = obs_property_path_type(property);
+	const char    *filter       = obs_property_path_filter(property);
+	const char    *default_path = obs_property_path_default_path(property);
+	QString       path;
+
+	if (type == OBS_PATH_DIRECTORY)
+		path = QFileDialog::getExistingDirectory(view,
+				QT_UTF8(desc), QT_UTF8(default_path),
+				QFileDialog::ShowDirsOnly |
+				QFileDialog::DontResolveSymlinks);
+	else if (type == OBS_PATH_FILE)
+		path = QFileDialog::getOpenFileName(view,
+				QT_UTF8(desc), QT_UTF8(default_path),
+				QT_UTF8(filter));
+
+	if (path.isEmpty())
+		return false;
+
+	QLineEdit *edit = static_cast<QLineEdit*>(widget);
+	edit->setText(path);
+	obs_data_setstring(view->settings, setting, QT_TO_UTF8(path));
+	return true;
 }
 
 void WidgetInfo::ListChanged(const char *setting)
@@ -432,10 +470,12 @@ void WidgetInfo::ControlChanged()
 	case OBS_PROPERTY_INT:     IntChanged(setting); break;
 	case OBS_PROPERTY_FLOAT:   FloatChanged(setting); break;
 	case OBS_PROPERTY_TEXT:    TextChanged(setting); break;
-	case OBS_PROPERTY_PATH:    PathChanged(setting); break;
 	case OBS_PROPERTY_LIST:    ListChanged(setting); break;
 	case OBS_PROPERTY_COLOR:   ColorChanged(setting); break;
 	case OBS_PROPERTY_BUTTON:  ButtonClicked(); return;
+	case OBS_PROPERTY_PATH:
+		if (!PathChanged(setting))
+			return;
 	}
 
 	view->callback(view->obj, view->settings);

+ 3 - 2
obs/properties-view.hpp

@@ -7,6 +7,7 @@
 
 class QFormLayout;
 class OBSPropertiesView;
+class QLabel;
 
 typedef void (*PropertiesUpdateCallback)(void *obj, obs_data_t settings);
 
@@ -24,7 +25,7 @@ private:
 	void IntChanged(const char *setting);
 	void FloatChanged(const char *setting);
 	void TextChanged(const char *setting);
-	void PathChanged(const char *setting);
+	bool PathChanged(const char *setting);
 	void ListChanged(const char *setting);
 	void ColorChanged(const char *setting);
 	void ButtonClicked();
@@ -62,7 +63,7 @@ private:
 
 	QWidget *AddCheckbox(obs_property_t prop);
 	QWidget *AddText(obs_property_t prop);
-	QWidget *AddPath(obs_property_t prop, QFormLayout *layout);
+	void AddPath(obs_property_t prop, QFormLayout *layout, QLabel **label);
 	QWidget *AddInt(obs_property_t prop);
 	QWidget *AddFloat(obs_property_t prop);
 	QWidget *AddList(obs_property_t prop, bool &warning);

+ 1 - 0
obs/window-basic-main.cpp

@@ -481,6 +481,7 @@ void OBSBasic::OBSInit()
 
 	/* TODO: this is a test, all modules will be searched for and loaded
 	 * automatically later */
+	obs_load_module("image-source");
 	obs_load_module("test-input");
 	obs_load_module("obs-ffmpeg");
 	obs_load_module("obs-libfdk");

+ 1 - 0
plugins/CMakeLists.txt

@@ -14,6 +14,7 @@ elseif("${CMAKE_SYSTEM_NAME}" MATCHES "Linux")
 	add_subdirectory(linux-v4l2)
 endif()
 
+add_subdirectory(image-source)
 add_subdirectory(obs-x264)
 add_subdirectory(obs-libfdk)
 add_subdirectory(obs-ffmpeg)

+ 11 - 0
plugins/image-source/CMakeLists.txt

@@ -0,0 +1,11 @@
+project(image-source)
+
+set(image-source_SOURCES
+	image-source.c)
+
+add_library(image-source MODULE
+	${image-source_SOURCES})
+target_link_libraries(image-source
+	libobs)
+
+install_obs_plugin(image-source)

+ 131 - 0
plugins/image-source/image-source.c

@@ -0,0 +1,131 @@
+#include <obs-module.h>
+
+#define warn(format, ...) \
+	blog(LOG_WARNING, "[image_source: '%s'] " format, \
+			obs_source_getname(context->source), ##__VA_ARGS__)
+
+struct image_source {
+	obs_source_t source;
+
+	texture_t    tex;
+	uint32_t     cx;
+	uint32_t     cy;
+};
+
+static const char *image_source_get_name(void)
+{
+	/* TODO: locale */
+	return "Image";
+}
+
+static void image_source_update(void *data, obs_data_t settings)
+{
+	struct image_source *context = data;
+	const char *file = obs_data_getstring(settings, "file");
+
+	gs_entercontext(obs_graphics());
+
+	if (context->tex) {
+		texture_destroy(context->tex);
+		context->tex = NULL;
+	}
+
+	if (file) {
+		context->tex = gs_create_texture_from_file(file);
+		if (context->tex) {
+			context->cx = texture_getwidth(context->tex);
+			context->cy = texture_getheight(context->tex);
+		} else {
+			warn("failed to load texture '%s'", file);
+			context->cx = 0;
+			context->cy = 0;
+		}
+	}
+
+	gs_leavecontext();
+}
+
+static void *image_source_create(obs_data_t settings, obs_source_t source)
+{
+	struct image_source *context = bzalloc(sizeof(struct image_source));
+	context->source = source;
+
+	image_source_update(context, settings);
+	return context;
+}
+
+static void image_source_destroy(void *data)
+{
+	struct image_source *context = data;
+
+	gs_entercontext(obs_graphics());
+	texture_destroy(context->tex);
+	gs_leavecontext();
+
+	bfree(context);
+}
+
+static uint32_t image_source_getwidth(void *data)
+{
+	struct image_source *context = data;
+	return context->cx;
+}
+
+static uint32_t image_source_getheight(void *data)
+{
+	struct image_source *context = data;
+	return context->cy;
+}
+
+static void image_source_render(void *data, effect_t effect)
+{
+	struct image_source *context = data;
+	if (!context->tex)
+		return;
+
+	effect_settexture(effect_getparambyname(effect, "image"), context->tex);
+	gs_draw_sprite(context->tex, 0, context->cx, context->cy);
+}
+
+static const char *image_filter =
+	"All formats (*.bmp *.tga *.png *.jpeg *.jpg *.gif);;"
+	"BMP Files (*.bmp);;"
+	"Targa Files (*.tga);;"
+	"PNG Files (*.png);;"
+	"JPEG Files (*.jpeg *.jpg);;"
+	"GIF Files (*.gif)";
+
+static obs_properties_t image_source_properties(void)
+{
+	obs_properties_t props = obs_properties_create();
+
+	/* TODO: locale */
+	obs_properties_add_path(props, "file", "Image file", OBS_PATH_FILE,
+			image_filter, NULL);
+
+	return props;
+}
+
+static struct obs_source_info image_source_info = {
+	.id           = "image_source",
+	.type         = OBS_SOURCE_TYPE_INPUT,
+	.output_flags = OBS_SOURCE_VIDEO,
+	.getname      = image_source_get_name,
+	.create       = image_source_create,
+	.destroy      = image_source_destroy,
+	.update       = image_source_update,
+	.getwidth     = image_source_getwidth,
+	.getheight    = image_source_getheight,
+	.video_render = image_source_render,
+	.properties   = image_source_properties
+};
+
+OBS_DECLARE_MODULE()
+
+bool obs_module_load(uint32_t libobs_version)
+{
+	obs_register_source(&image_source_info);
+
+	UNUSED_PARAMETER(libobs_version);
+	return true;
+}

+ 1 - 1
plugins/linux-xcomposite/xcompcap-main.cpp

@@ -317,7 +317,7 @@ void XCompcapMain::updateSettings(obs_data_t settings)
 	const uint8_t* texDataArr[] = { texData, 0 };
 
 	p->tex = gs_create_texture(width(), height(), cf, 1,
-			(const void**)texDataArr, 0);
+			texDataArr, 0);
 
 	delete[] texData;
 

+ 2 - 2
plugins/linux-xshm/xcursor.c

@@ -48,14 +48,14 @@ static void xcursor_create(xcursor_t *data, XFixesCursorImage *xc) {
 	if (data->tex
 	&& data->last_height == xc->width
 	&& data->last_width == xc->height) {
-		texture_setimage(data->tex, (void **) pixels,
+		texture_setimage(data->tex, (const uint8_t *) pixels,
 			xc->width * sizeof(uint32_t), False);
 	} else {
 		if (data->tex)
 			texture_destroy(data->tex);
 
 		data->tex = gs_create_texture(xc->width, xc->height,
-			GS_BGRA, 1, (const void **) &pixels, GS_DYNAMIC);
+			GS_BGRA, 1, (const uint8_t **) &pixels, GS_DYNAMIC);
 	}
 
 	bfree(pixels);

+ 14 - 0
vs/2013/OBS.sln

@@ -78,6 +78,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "win-dshow", "win-dshow\win-
 		{6F1AC2AE-6424-401A-AF9F-A771E6BEE026} = {6F1AC2AE-6424-401A-AF9F-A771E6BEE026}
 	EndProjectSection
 EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "image-source", "image-source\image-source.vcxproj", "{49B9AB5A-4CF2-4AC8-84EC-CD9E537C65DE}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Mixed Platforms = Debug|Mixed Platforms
@@ -280,6 +282,18 @@ Global
 		{E733E9CB-EA71-4E77-BD28-2D06F4AA4677}.Release|Win32.Build.0 = Release|Win32
 		{E733E9CB-EA71-4E77-BD28-2D06F4AA4677}.Release|x64.ActiveCfg = Release|x64
 		{E733E9CB-EA71-4E77-BD28-2D06F4AA4677}.Release|x64.Build.0 = Release|x64
+		{49B9AB5A-4CF2-4AC8-84EC-CD9E537C65DE}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
+		{49B9AB5A-4CF2-4AC8-84EC-CD9E537C65DE}.Debug|Mixed Platforms.Build.0 = Debug|Win32
+		{49B9AB5A-4CF2-4AC8-84EC-CD9E537C65DE}.Debug|Win32.ActiveCfg = Debug|Win32
+		{49B9AB5A-4CF2-4AC8-84EC-CD9E537C65DE}.Debug|Win32.Build.0 = Debug|Win32
+		{49B9AB5A-4CF2-4AC8-84EC-CD9E537C65DE}.Debug|x64.ActiveCfg = Debug|x64
+		{49B9AB5A-4CF2-4AC8-84EC-CD9E537C65DE}.Debug|x64.Build.0 = Debug|x64
+		{49B9AB5A-4CF2-4AC8-84EC-CD9E537C65DE}.Release|Mixed Platforms.ActiveCfg = Release|Win32
+		{49B9AB5A-4CF2-4AC8-84EC-CD9E537C65DE}.Release|Mixed Platforms.Build.0 = Release|Win32
+		{49B9AB5A-4CF2-4AC8-84EC-CD9E537C65DE}.Release|Win32.ActiveCfg = Release|Win32
+		{49B9AB5A-4CF2-4AC8-84EC-CD9E537C65DE}.Release|Win32.Build.0 = Release|Win32
+		{49B9AB5A-4CF2-4AC8-84EC-CD9E537C65DE}.Release|x64.ActiveCfg = Release|x64
+		{49B9AB5A-4CF2-4AC8-84EC-CD9E537C65DE}.Release|x64.Build.0 = Release|x64
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE

+ 171 - 0
vs/2013/image-source/image-source.vcxproj

@@ -0,0 +1,171 @@
+<?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>{49B9AB5A-4CF2-4AC8-84EC-CD9E537C65DE}</ProjectGuid>
+    <Keyword>Win32Proj</Keyword>
+    <RootNamespace>imagesource</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;IMAGESOURCE_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>../../../libobs</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalDependencies>libobs.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+    </Link>
+    <PostBuildEvent>
+      <Command>copy "$(OutDir)$(TargetName)$(TargetExt)" "../../../build/obs-plugins/32bit/$(TargetName)$(TargetExt)"</Command>
+    </PostBuildEvent>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;IMAGESOURCE_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>../../../libobs</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalDependencies>libobs.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+    </Link>
+    <PostBuildEvent>
+      <Command>copy "$(OutDir)$(TargetName)$(TargetExt)" "../../../build/obs-plugins/64bit/$(TargetName)$(TargetExt)"</Command>
+    </PostBuildEvent>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;IMAGESOURCE_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>
+      <AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+    </Link>
+    <PostBuildEvent>
+      <Command>copy "$(OutDir)$(TargetName)$(TargetExt)" "../../../build/obs-plugins/32bit/$(TargetName)$(TargetExt)"</Command>
+    </PostBuildEvent>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;IMAGESOURCE_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>
+      <AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+    </Link>
+    <PostBuildEvent>
+      <Command>copy "$(OutDir)$(TargetName)$(TargetExt)" "../../../build/obs-plugins/64bit/$(TargetName)$(TargetExt)"</Command>
+    </PostBuildEvent>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\..\plugins\image-source\image-source.c" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>

+ 22 - 0
vs/2013/image-source/image-source.vcxproj.filters

@@ -0,0 +1,22 @@
+<?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\image-source\image-source.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+</Project>

+ 5 - 4
vs/2013/libobs/libobs.vcxproj

@@ -83,6 +83,7 @@
     <ClCompile Include="..\..\..\libobs\graphics\bounds.c" />
     <ClCompile Include="..\..\..\libobs\graphics\effect-parser.c" />
     <ClCompile Include="..\..\..\libobs\graphics\effect.c" />
+    <ClCompile Include="..\..\..\libobs\graphics\graphics-ffmpeg.c" />
     <ClCompile Include="..\..\..\libobs\graphics\graphics-imports.c" />
     <ClCompile Include="..\..\..\libobs\graphics\graphics.c" />
     <ClCompile Include="..\..\..\libobs\graphics\math-extra.c" />
@@ -213,7 +214,7 @@
     <Link>
       <SubSystem>Windows</SubSystem>
       <GenerateDebugInformation>true</GenerateDebugInformation>
-      <AdditionalDependencies>jansson.lib;winmm.lib;avutil.lib;swresample.lib;swscale.lib;pthreads.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>jansson.lib;winmm.lib;avutil.lib;avformat.lib;avcodec.lib;swresample.lib;swscale.lib;pthreads.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
       <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
     </Link>
@@ -234,7 +235,7 @@
     <Link>
       <SubSystem>Windows</SubSystem>
       <GenerateDebugInformation>true</GenerateDebugInformation>
-      <AdditionalDependencies>jansson.lib;winmm.lib;avutil.lib;swresample.lib;swscale.lib;pthreads.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>jansson.lib;winmm.lib;avutil.lib;avformat.lib;avcodec.lib;swresample.lib;swscale.lib;pthreads.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
       <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
     </Link>
@@ -259,7 +260,7 @@
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <EnableCOMDATFolding>true</EnableCOMDATFolding>
       <OptimizeReferences>true</OptimizeReferences>
-      <AdditionalDependencies>jansson.lib;winmm.lib;avutil.lib;swresample.lib;swscale.lib;pthreads.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>jansson.lib;winmm.lib;avutil.lib;avformat.lib;avcodec.lib;swresample.lib;swscale.lib;pthreads.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
       <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
     </Link>
@@ -284,7 +285,7 @@
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <EnableCOMDATFolding>true</EnableCOMDATFolding>
       <OptimizeReferences>true</OptimizeReferences>
-      <AdditionalDependencies>jansson.lib;winmm.lib;avutil.lib;swresample.lib;swscale.lib;pthreads.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>jansson.lib;winmm.lib;avutil.lib;avformat.lib;avcodec.lib;swresample.lib;swscale.lib;pthreads.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
       <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
     </Link>

+ 4 - 1
vs/2013/libobs/libobs.vcxproj.filters

@@ -389,5 +389,8 @@
     <ClCompile Include="..\..\..\libobs\media-io\video-matrices.c">
       <Filter>media-io\Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\..\libobs\graphics\graphics-ffmpeg.c">
+      <Filter>graphics\Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
-</Project>
+</Project>