瀏覽代碼

Implement automatic video scaling (if requested)

Add a scaler interface (defaults to swscale), and if a separate output
wants to use a different scale or format than the default output format,
allow a scaler instance to be created automatically for that output,
which will then receive the new scaled output.
jp9000 11 年之前
父節點
當前提交
f2d4de3c03

+ 14 - 5
libobs/CMakeLists.txt

@@ -1,5 +1,9 @@
 project(libobs)
 project(libobs)
 
 
+find_package(Libswscale REQUIRED)
+include_directories(${Libswscale_INCLUDE_DIR})
+add_definitions(${Libswscale_DEFINITIONS})
+
 find_package(Libswresample REQUIRED)
 find_package(Libswresample REQUIRED)
 include_directories(${Libswresample_INCLUDE_DIR})
 include_directories(${Libswresample_INCLUDE_DIR})
 add_definitions(${Libswresample_DEFINITIONS})
 add_definitions(${Libswresample_DEFINITIONS})
@@ -96,15 +100,19 @@ set(libobs_graphics_HEADERS
 
 
 set(libobs_mediaio_SOURCES
 set(libobs_mediaio_SOURCES
 	media-io/video-io.c
 	media-io/video-io.c
-	media-io/audio-resampler-ffmpeg.c
+	media-io/audio-io.c
+	media-io/video-frame.c
 	media-io/format-conversion.c
 	media-io/format-conversion.c
-	media-io/audio-io.c)
+	media-io/audio-resampler-ffmpeg.c
+	media-io/video-scaler-ffmpeg.c)
 set(libobs_mediaio_HEADERS
 set(libobs_mediaio_HEADERS
 	media-io/media-io-defs.h
 	media-io/media-io-defs.h
-	media-io/format-conversion.h
 	media-io/video-io.h
 	media-io/video-io.h
+	media-io/audio-io.h
+	media-io/video-frame.h
+	media-io/format-conversion.h
 	media-io/audio-resampler.h
 	media-io/audio-resampler.h
-	media-io/audio-io.h)
+	media-io/video-scaler.h)
 
 
 set(libobs_util_SOURCES
 set(libobs_util_SOURCES
 	util/base.c
 	util/base.c
@@ -196,10 +204,11 @@ set_target_properties(libobs PROPERTIES
 	SOVERSION "0")
 	SOVERSION "0")
 target_link_libraries(libobs
 target_link_libraries(libobs
 	${libobs_PLATFORM_DEPS}
 	${libobs_PLATFORM_DEPS}
+	${Libswscale_LIBRARIES}
 	${Libswresample_LIBRARIES}
 	${Libswresample_LIBRARIES}
 	${Libavutil_LIBRARIES})
 	${Libavutil_LIBRARIES})
 
 
 install_obs_core(libobs)
 install_obs_core(libobs)
 install_obs_data(libobs ../build/data/libobs libobs)
 install_obs_data(libobs ../build/data/libobs libobs)
 
 
-obs_fixup_install_target(libobs PATH ${Libswresample_LIBRARIES} ${Libavutil_LIBRARIES})
+obs_fixup_install_target(libobs PATH ${Libswscale_LIBRARIES} ${Libswresample_LIBRARIES} ${Libavutil_LIBRARIES})

+ 16 - 4
libobs/media-io/audio-io.c

@@ -465,7 +465,7 @@ static size_t audio_get_input_idx(audio_t video,
 	return DARRAY_INVALID;
 	return DARRAY_INVALID;
 }
 }
 
 
-static inline void audio_input_init(struct audio_input *input,
+static inline bool audio_input_init(struct audio_input *input,
 		struct audio_output *audio)
 		struct audio_output *audio)
 {
 {
 	if (input->conversion.format          != audio->info.format          ||
 	if (input->conversion.format          != audio->info.format          ||
@@ -484,16 +484,25 @@ static inline void audio_input_init(struct audio_input *input,
 		};
 		};
 
 
 		input->resampler = audio_resampler_create(&to, &from);
 		input->resampler = audio_resampler_create(&to, &from);
+		if (!input->resampler) {
+			blog(LOG_WARNING, "audio_input_init: Failed to "
+			                  "create resampler");
+			return false;
+		}
 	} else {
 	} else {
 		input->resampler = NULL;
 		input->resampler = NULL;
 	}
 	}
+
+	return true;
 }
 }
 
 
-void audio_output_connect(audio_t audio,
+bool audio_output_connect(audio_t audio,
 		struct audio_convert_info *conversion,
 		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)
 {
 {
+	bool success = false;
+
 	pthread_mutex_lock(&audio->input_mutex);
 	pthread_mutex_lock(&audio->input_mutex);
 
 
 	if (audio_get_input_idx(audio, callback, param) == DARRAY_INVALID) {
 	if (audio_get_input_idx(audio, callback, param) == DARRAY_INVALID) {
@@ -510,11 +519,14 @@ void audio_output_connect(audio_t audio,
 				audio->info.samples_per_sec;
 				audio->info.samples_per_sec;
 		}
 		}
 
 
-		audio_input_init(&input, audio);
-		da_push_back(audio->inputs, &input);
+		success = audio_input_init(&input, audio);
+		if (success)
+			da_push_back(audio->inputs, &input);
 	}
 	}
 
 
 	pthread_mutex_unlock(&audio->input_mutex);
 	pthread_mutex_unlock(&audio->input_mutex);
+
+	return success;
 }
 }
 
 
 void audio_output_disconnect(audio_t audio,
 void audio_output_disconnect(audio_t audio,

+ 1 - 1
libobs/media-io/audio-io.h

@@ -164,7 +164,7 @@ 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,
+EXPORT bool audio_output_connect(audio_t video,
 		struct audio_convert_info *conversion,
 		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);

+ 86 - 0
libobs/media-io/video-frame.c

@@ -0,0 +1,86 @@
+/******************************************************************************
+    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 "video-frame.h"
+
+#define ALIGN_SIZE(size, align) \
+	size = (((size)+(align-1)) & (~(align-1)))
+
+/* messy code alarm */
+void video_frame_init(struct video_frame *frame, enum video_format format,
+		uint32_t width, uint32_t height)
+{
+	size_t size;
+	size_t offsets[MAX_AV_PLANES];
+	int    alignment = base_get_alignment();
+
+	memset(frame, 0, sizeof(struct video_frame));
+	memset(offsets, 0, sizeof(offsets));
+
+	switch (format) {
+	case VIDEO_FORMAT_NONE:
+		return;
+
+	case VIDEO_FORMAT_I420:
+		size = width * height;
+		ALIGN_SIZE(size, alignment);
+		offsets[0] = size;
+		size += (width/2) * (height/2);
+		ALIGN_SIZE(size, alignment);
+		offsets[1] = size;
+		size += (width/2) * (height/2);
+		ALIGN_SIZE(size, alignment);
+		frame->data[0] = bmalloc(size);
+		frame->data[1] = (uint8_t*)frame->data[0] + offsets[0];
+		frame->data[2] = (uint8_t*)frame->data[0] + offsets[1];
+		frame->linesize[0] = width;
+		frame->linesize[1] = width/2;
+		frame->linesize[2] = width/2;
+		break;
+
+	case VIDEO_FORMAT_NV12:
+		size = width * height;
+		ALIGN_SIZE(size, alignment);
+		offsets[0] = size;
+		size += (width/2) * (height/2) * 2;
+		ALIGN_SIZE(size, alignment);
+		frame->data[0] = bmalloc(size);
+		frame->data[1] = (uint8_t*)frame->data[0] + offsets[0];
+		frame->linesize[0] = width;
+		frame->linesize[1] = width;
+		break;
+
+	case VIDEO_FORMAT_YVYU:
+	case VIDEO_FORMAT_YUY2:
+	case VIDEO_FORMAT_UYVY:
+		size = width * height * 2;
+		ALIGN_SIZE(size, alignment);
+		frame->data[0] = bmalloc(size);
+		frame->linesize[0] = width*2;
+		break;
+
+	case VIDEO_FORMAT_RGBA:
+	case VIDEO_FORMAT_BGRA:
+	case VIDEO_FORMAT_BGRX:
+		size = width * height * 4;
+		ALIGN_SIZE(size, alignment);
+		frame->data[0] = bmalloc(size);
+		frame->linesize[0] = width*4;
+		break;
+	}
+}
+

+ 53 - 0
libobs/media-io/video-frame.h

@@ -0,0 +1,53 @@
+/******************************************************************************
+    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 "../util/bmem.h"
+#include "video-io.h"
+
+struct video_frame {
+	uint8_t  *data[MAX_AV_PLANES];
+	uint32_t linesize[MAX_AV_PLANES];
+};
+
+EXPORT void video_frame_init(struct video_frame *frame,
+		enum video_format format, uint32_t width, uint32_t height);
+
+static inline void video_frame_free(struct video_frame *frame)
+{
+	if (frame) {
+		bfree(frame->data[0]);
+		memset(frame, 0, sizeof(struct video_frame));
+	}
+}
+
+static inline struct video_frame *video_frame_create(
+		enum video_format format, uint32_t width, uint32_t height)
+{
+	struct video_frame *frame;
+
+	frame = (struct video_frame*)bzalloc(sizeof(struct video_frame));
+	video_frame_init(frame, format, width, height);
+	return frame;
+}
+
+static inline void video_frame_destroy(struct video_frame *frame)
+{
+	if (frame) {
+		bfree(frame->data[0]);
+		bfree(frame);
+	}
+}

+ 111 - 20
libobs/media-io/video-io.c

@@ -23,13 +23,28 @@
 
 
 #include "format-conversion.h"
 #include "format-conversion.h"
 #include "video-io.h"
 #include "video-io.h"
+#include "video-frame.h"
+#include "video-scaler.h"
+
+#define MAX_CONVERT_BUFFERS 3
 
 
 struct video_input {
 struct video_input {
-	struct video_convert_info conversion;
-	void (*callback)(void *param, const struct video_frame *frame);
+	struct video_scale_info   conversion;
+	video_scaler_t            scaler;
+	struct video_frame        frame[MAX_CONVERT_BUFFERS];
+	int                       cur_frame;
+
+	void (*callback)(void *param, const struct video_data *frame);
 	void *param;
 	void *param;
 };
 };
 
 
+static inline void video_input_free(struct video_input *input)
+{
+	for (size_t i = 0; i < MAX_CONVERT_BUFFERS; i++)
+		video_frame_free(&input->frame[i]);
+	video_scaler_destroy(input->scaler);
+}
+
 struct video_output {
 struct video_output {
 	struct video_output_info   info;
 	struct video_output_info   info;
 
 
@@ -37,8 +52,8 @@ struct video_output {
 	pthread_mutex_t            data_mutex;
 	pthread_mutex_t            data_mutex;
 	event_t                    stop_event;
 	event_t                    stop_event;
 
 
-	struct video_frame         cur_frame;
-	struct video_frame         next_frame;
+	struct video_data          cur_frame;
+	struct video_data          next_frame;
 	bool                       new_frame;
 	bool                       new_frame;
 
 
 	event_t                    update_event;
 	event_t                    update_event;
@@ -61,6 +76,34 @@ static inline void video_swapframes(struct video_output *video)
 	}
 	}
 }
 }
 
 
+static inline bool scale_video_output(struct video_input *input,
+		struct video_data *data)
+{
+	bool success = true;
+
+	if (input->scaler) {
+		struct video_frame *frame;
+
+		if (++input->cur_frame == MAX_CONVERT_BUFFERS)
+			input->cur_frame = 0;
+
+		frame = &input->frame[input->cur_frame];
+
+		success = video_scaler_scale(input->scaler,
+				frame->data, frame->linesize,
+				data->data, data->linesize);
+
+		if (success) {
+			for (size_t i = 0; i < MAX_AV_PLANES; i++) {
+				data->data[i]     = frame->data[i];
+				data->linesize[i] = frame->linesize[i];
+			}
+		}
+	}
+
+	return success;
+}
+
 static inline void video_output_cur_frame(struct video_output *video)
 static inline void video_output_cur_frame(struct video_output *video)
 {
 {
 	if (!video->cur_frame.data[0])
 	if (!video->cur_frame.data[0])
@@ -68,10 +111,10 @@ static inline void video_output_cur_frame(struct video_output *video)
 
 
 	pthread_mutex_lock(&video->input_mutex);
 	pthread_mutex_lock(&video->input_mutex);
 
 
-	/* TODO: conversion */
 	for (size_t i = 0; i < video->inputs.num; i++) {
 	for (size_t i = 0; i < video->inputs.num; i++) {
 		struct video_input *input = video->inputs.array+i;
 		struct video_input *input = video->inputs.array+i;
-		input->callback(input->param, &video->cur_frame);
+		if (scale_video_output(input, &video->cur_frame))
+			input->callback(input->param, &video->cur_frame);
 	}
 	}
 
 
 	pthread_mutex_unlock(&video->input_mutex);
 	pthread_mutex_unlock(&video->input_mutex);
@@ -151,7 +194,10 @@ void video_output_close(video_t video)
 
 
 	video_output_stop(video);
 	video_output_stop(video);
 
 
+	for (size_t i = 0; i < video->inputs.num; i++)
+		video_input_free(&video->inputs.array[i]);
 	da_free(video->inputs);
 	da_free(video->inputs);
+
 	event_destroy(&video->update_event);
 	event_destroy(&video->update_event);
 	event_destroy(&video->stop_event);
 	event_destroy(&video->stop_event);
 	pthread_mutex_destroy(&video->data_mutex);
 	pthread_mutex_destroy(&video->data_mutex);
@@ -160,7 +206,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, const struct video_frame *frame),
+		void (*callback)(void *param, const struct video_data *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++) {
@@ -172,47 +218,92 @@ 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_convert_info *conversion,
-		void (*callback)(void *param, const struct video_frame *frame),
+static inline bool video_input_init(struct video_input *input,
+		struct video_output *video)
+{
+	if (input->conversion.width  != video->info.width ||
+	    input->conversion.height != video->info.height ||
+	    input->conversion.format != video->info.format) {
+		struct video_scale_info from = {
+			.format = video->info.format,
+			.width  = video->info.width,
+			.height = video->info.height,
+		};
+
+		int ret = video_scaler_create(&input->scaler,
+				&input->conversion, &from,
+				VIDEO_SCALE_FAST_BILINEAR);
+		if (ret != VIDEO_SCALER_SUCCESS) {
+			if (ret == VIDEO_SCALER_BAD_CONVERSION)
+				blog(LOG_WARNING, "video_input_init: Bad "
+				                  "scale conversion type");
+			else
+				blog(LOG_WARNING, "video_input_init: Failed to "
+						  "create scaler");
+
+			return false;
+		}
+
+		for (size_t i = 0; i < MAX_CONVERT_BUFFERS; i++)
+			video_frame_init(&input->frame[i],
+					input->conversion.format,
+					input->conversion.width,
+					input->conversion.height);
+	}
+
+	return true;
+}
+
+bool video_output_connect(video_t video,
+		struct video_scale_info *conversion,
+		void (*callback)(void *param, const struct video_data *frame),
 		void *param)
 		void *param)
 {
 {
+	bool success = false;
+
 	pthread_mutex_lock(&video->input_mutex);
 	pthread_mutex_lock(&video->input_mutex);
 
 
 	if (video_get_input_idx(video, callback, param) == DARRAY_INVALID) {
 	if (video_get_input_idx(video, callback, param) == DARRAY_INVALID) {
 		struct video_input input;
 		struct video_input input;
+		memset(&input, 0, sizeof(input));
+
 		input.callback = callback;
 		input.callback = callback;
 		input.param    = param;
 		input.param    = param;
 
 
-		/* TODO: conversion */
 		if (conversion) {
 		if (conversion) {
 			input.conversion = *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.conversion.format    = video->info.format;
 			input.conversion.format    = video->info.format;
 			input.conversion.width     = video->info.width;
 			input.conversion.width     = video->info.width;
 			input.conversion.height    = video->info.height;
 			input.conversion.height    = video->info.height;
 		}
 		}
 
 
-		da_push_back(video->inputs, &input);
+		if (input.conversion.width == 0)
+			input.conversion.width = video->info.width;
+		if (input.conversion.height == 0)
+			input.conversion.height = video->info.height;
+
+		success = video_input_init(&input, video);
+		if (success)
+			da_push_back(video->inputs, &input);
 	}
 	}
 
 
 	pthread_mutex_unlock(&video->input_mutex);
 	pthread_mutex_unlock(&video->input_mutex);
+
+	return success;
 }
 }
 
 
 void video_output_disconnect(video_t video,
 void video_output_disconnect(video_t video,
-		void (*callback)(void *param, const struct video_frame *frame),
+		void (*callback)(void *param, const struct video_data *frame),
 		void *param)
 		void *param)
 {
 {
 	pthread_mutex_lock(&video->input_mutex);
 	pthread_mutex_lock(&video->input_mutex);
 
 
 	size_t idx = video_get_input_idx(video, callback, param);
 	size_t idx = video_get_input_idx(video, callback, param);
-	if (idx != DARRAY_INVALID)
+	if (idx != DARRAY_INVALID) {
+		video_input_free(video->inputs.array+idx);
 		da_erase(video->inputs, idx);
 		da_erase(video->inputs, idx);
+	}
 
 
 	pthread_mutex_unlock(&video->input_mutex);
 	pthread_mutex_unlock(&video->input_mutex);
 }
 }
@@ -222,7 +313,7 @@ const struct video_output_info *video_output_getinfo(video_t video)
 	return &video->info;
 	return &video->info;
 }
 }
 
 
-void video_output_frame(video_t video, struct video_frame *frame)
+void video_output_swap_frame(video_t video, struct video_data *frame)
 {
 {
 	pthread_mutex_lock(&video->data_mutex);
 	pthread_mutex_lock(&video->data_mutex);
 	video->next_frame = *frame;
 	video->next_frame = *frame;

+ 10 - 16
libobs/media-io/video-io.h

@@ -18,13 +18,13 @@
 #pragma once
 #pragma once
 
 
 #include "media-io-defs.h"
 #include "media-io-defs.h"
-#include "../util/c99defs.h"
+#include "video-scaler.h"
 
 
 #ifdef __cplusplus
 #ifdef __cplusplus
 extern "C" {
 extern "C" {
 #endif
 #endif
 
 
-/* Base video output component.  Use this to create an video output track. */
+/* Base video output component.  Use this to create a video output track. */
 
 
 struct video_output;
 struct video_output;
 typedef struct video_output *video_t;
 typedef struct video_output *video_t;
@@ -47,7 +47,7 @@ enum video_format {
 	VIDEO_FORMAT_BGRX,
 	VIDEO_FORMAT_BGRX,
 };
 };
 
 
-struct video_frame {
+struct video_data {
 	const uint8_t     *data[MAX_AV_PLANES];
 	const uint8_t     *data[MAX_AV_PLANES];
 	uint32_t          linesize[MAX_AV_PLANES];
 	uint32_t          linesize[MAX_AV_PLANES];
 	uint64_t          timestamp;
 	uint64_t          timestamp;
@@ -63,12 +63,6 @@ struct video_output_info {
 	uint32_t          height;
 	uint32_t          height;
 };
 };
 
 
-struct video_convert_info {
-	enum video_format format;
-	uint32_t          width;
-	uint32_t          height;
-};
-
 static inline bool format_is_yuv(enum video_format format)
 static inline bool format_is_yuv(enum video_format format)
 {
 {
 	switch (format) {
 	switch (format) {
@@ -95,20 +89,20 @@ static inline bool format_is_yuv(enum video_format format)
 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_convert_info *conversion,
-		void (*callback)(void *param, const struct video_frame *frame),
+EXPORT bool video_output_connect(video_t video,
+		struct video_scale_info *conversion,
+		void (*callback)(void *param, const struct video_data *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, const struct video_frame *frame),
+		void (*callback)(void *param, const struct video_data *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);
-EXPORT void     video_output_frame(video_t video, struct video_frame *frame);
-EXPORT bool     video_output_wait(video_t video);
+EXPORT void video_output_swap_frame(video_t video, struct video_data *frame);
+EXPORT bool video_output_wait(video_t video);
 EXPORT uint64_t video_getframetime(video_t video);
 EXPORT uint64_t video_getframetime(video_t video);
 EXPORT uint64_t video_gettime(video_t video);
 EXPORT uint64_t video_gettime(video_t video);
-EXPORT void     video_output_stop(video_t video);
+EXPORT void video_output_stop(video_t video);
 
 
 #ifdef __cplusplus
 #ifdef __cplusplus
 }
 }

+ 146 - 0
libobs/media-io/video-scaler-ffmpeg.c

@@ -0,0 +1,146 @@
+/******************************************************************************
+    Copyright (C) 2014 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 "../util/bmem.h"
+#include "video-scaler.h"
+
+#include <libswscale/swscale.h>
+
+struct video_scaler {
+	struct SwsContext *swscale;
+	int src_height;
+};
+
+static inline enum AVPixelFormat get_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_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 inline int get_ffmpeg_scale_type(enum video_scale_type type)
+{
+	switch (type) {
+	case VIDEO_SCALE_POINT:         return SWS_POINT;
+	case VIDEO_SCALE_FAST_BILINEAR: return SWS_FAST_BILINEAR;
+	case VIDEO_SCALE_BILINEAR:      return SWS_BILINEAR | SWS_AREA;
+	case VIDEO_SCALE_BICUBIC:       return SWS_BICUBIC;
+	}
+
+	return SWS_POINT;
+}
+
+static inline const int *get_ffmpeg_coeffs(enum video_colorspace cs)
+{
+	switch (cs) {
+	case VIDEO_CS_601: return sws_getCoefficients(SWS_CS_ITU601);
+	case VIDEO_CS_709: return sws_getCoefficients(SWS_CS_ITU709);
+	}
+
+	return sws_getCoefficients(SWS_CS_ITU601);
+}
+
+#define FIXED_1_0 (1<<16)
+
+int video_scaler_create(video_scaler_t *scaler_out,
+		const struct video_scale_info *dst,
+		const struct video_scale_info *src,
+		enum video_scale_type type)
+{
+	enum AVPixelFormat format_src = get_ffmpeg_video_format(src->format);
+	enum AVPixelFormat format_dst = get_ffmpeg_video_format(dst->format);
+	int                scale_type = get_ffmpeg_scale_type(type);
+	const int          *coeff_src = get_ffmpeg_coeffs(src->colorspace);
+	const int          *coeff_dst = get_ffmpeg_coeffs(dst->colorspace);
+	struct video_scaler *scaler;
+	int ret;
+
+	if (!scaler_out)
+		return VIDEO_SCALER_FAILED;
+
+	if (format_src == AV_PIX_FMT_NONE ||
+	    format_dst == AV_PIX_FMT_NONE)
+		return VIDEO_SCALER_BAD_CONVERSION;
+
+	scaler = bzalloc(sizeof(struct video_scaler));
+	scaler->src_height = src->height;
+
+	scaler->swscale = sws_getCachedContext(NULL,
+			src->width, src->height, format_src,
+			dst->width, dst->height, format_dst,
+			scale_type, NULL, NULL, NULL);
+	if (!scaler->swscale) {
+		blog(LOG_WARNING, "video_scaler_create: Could not create "
+		                  "swscale");
+		goto fail;
+	}
+
+	ret = sws_setColorspaceDetails(scaler->swscale,
+			coeff_src, src->full_range,
+			coeff_dst, dst->full_range,
+			0, FIXED_1_0, FIXED_1_0);
+	if (ret < 0) {
+		blog(LOG_DEBUG, "video_scaler_create: "
+		                "sws_setColorspaceDetails failed, ignoring");
+	}
+
+	*scaler_out = scaler;
+	return VIDEO_SCALER_SUCCESS;
+
+fail:
+	video_scaler_destroy(scaler);
+	return VIDEO_SCALER_FAILED;
+}
+
+void video_scaler_destroy(video_scaler_t scaler)
+{
+	if (scaler) {
+		sws_freeContext(scaler->swscale);
+		bfree(scaler);
+	}
+}
+
+bool video_scaler_scale(video_scaler_t scaler,
+		uint8_t *output[], const uint32_t out_linesize[],
+		const uint8_t *const input[], const uint32_t in_linesize[])
+{
+	if (!scaler)
+		return false;
+
+	int ret = sws_scale(scaler->swscale,
+			input, in_linesize,
+			0, scaler->src_height,
+			output, out_linesize);
+	if (ret <= 0) {
+		blog(LOG_DEBUG, "video_scaler_scale: sws_scale failed: %d",
+				ret);
+		return false;
+	}
+
+	return true;
+}

+ 68 - 0
libobs/media-io/video-scaler.h

@@ -0,0 +1,68 @@
+/******************************************************************************
+    Copyright (C) 2014 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 "video-io.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct video_scaler;
+typedef struct video_scaler *video_scaler_t;
+
+enum video_scale_type {
+	VIDEO_SCALE_POINT         = 0,
+	VIDEO_SCALE_FAST_BILINEAR = 1,
+	VIDEO_SCALE_DEFAULT       = VIDEO_SCALE_FAST_BILINEAR,
+	VIDEO_SCALE_BILINEAR      = 2,
+	VIDEO_SCALE_BICUBIC       = 3,
+};
+
+enum video_colorspace {
+	VIDEO_CS_601              = 0,
+	VIDEO_CS_DEFAULT          = VIDEO_CS_601,
+	VIDEO_CS_709              = 1,
+};
+
+struct video_scale_info {
+	enum video_format     format;
+	uint32_t              width;
+	uint32_t              height;
+	bool                  full_range;
+	enum video_colorspace colorspace;
+};
+
+#define VIDEO_SCALER_SUCCESS         0
+#define VIDEO_SCALER_BAD_CONVERSION -1
+#define VIDEO_SCALER_FAILED         -2
+
+EXPORT int video_scaler_create(video_scaler_t *scaler,
+		const struct video_scale_info *dst,
+		const struct video_scale_info *src,
+		enum video_scale_type type);
+EXPORT void video_scaler_destroy(video_scaler_t scaler);
+
+EXPORT bool video_scaler_scale(video_scaler_t scaler,
+		uint8_t *output[], const uint32_t out_linesize[],
+		const uint8_t *const input[], const uint32_t in_linesize[]);
+
+#ifdef __cplusplus
+}
+#endif

+ 8 - 61
libobs/obs-source.c

@@ -18,6 +18,7 @@
 #include <inttypes.h>
 #include <inttypes.h>
 
 
 #include "media-io/format-conversion.h"
 #include "media-io/format-conversion.h"
+#include "media-io/video-frame.h"
 #include "util/platform.h"
 #include "util/platform.h"
 #include "callback/calldata.h"
 #include "callback/calldata.h"
 #include "graphics/matrix3.h"
 #include "graphics/matrix3.h"
@@ -166,72 +167,18 @@ fail:
 	return NULL;
 	return NULL;
 }
 }
 
 
-#define ALIGN_SIZE(size, align) \
-	size = (((size)+(align-1)) & (~(align-1)))
-
-/* messy code alarm */
-void source_frame_init(struct source_frame *frame,
-		enum video_format format, uint32_t width, uint32_t height)
+void source_frame_init(struct source_frame *frame, enum video_format format,
+		uint32_t width, uint32_t height)
 {
 {
-	size_t size;
-	size_t offsets[MAX_AV_PLANES];
-	int    alignment = base_get_alignment();
-
-	memset(offsets, 0, sizeof(offsets));
+	struct video_frame vid_frame;
+	video_frame_init(&vid_frame, format, width, height);
 	frame->format = format;
 	frame->format = format;
 	frame->width  = width;
 	frame->width  = width;
 	frame->height = height;
 	frame->height = height;
 
 
-	switch (format) {
-	case VIDEO_FORMAT_NONE:
-		return;
-
-	case VIDEO_FORMAT_I420:
-		size = width * height;
-		ALIGN_SIZE(size, alignment);
-		offsets[0] = size;
-		size += (width/2) * (height/2);
-		ALIGN_SIZE(size, alignment);
-		offsets[1] = size;
-		size += (width/2) * (height/2);
-		ALIGN_SIZE(size, alignment);
-		frame->data[0] = bmalloc(size);
-		frame->data[1] = (uint8_t*)frame->data[0] + offsets[0];
-		frame->data[2] = (uint8_t*)frame->data[0] + offsets[1];
-		frame->linesize[0] = width;
-		frame->linesize[1] = width/2;
-		frame->linesize[2] = width/2;
-		break;
-
-	case VIDEO_FORMAT_NV12:
-		size = width * height;
-		ALIGN_SIZE(size, alignment);
-		offsets[0] = size;
-		size += (width/2) * (height/2) * 2;
-		ALIGN_SIZE(size, alignment);
-		frame->data[0] = bmalloc(size);
-		frame->data[1] = (uint8_t*)frame->data[0] + offsets[0];
-		frame->linesize[0] = width;
-		frame->linesize[1] = width;
-		break;
-
-	case VIDEO_FORMAT_YVYU:
-	case VIDEO_FORMAT_YUY2:
-	case VIDEO_FORMAT_UYVY:
-		size = width * height * 2;
-		ALIGN_SIZE(size, alignment);
-		frame->data[0] = bmalloc(size);
-		frame->linesize[0] = width*2;
-		break;
-
-	case VIDEO_FORMAT_RGBA:
-	case VIDEO_FORMAT_BGRA:
-	case VIDEO_FORMAT_BGRX:
-		size = width * height * 4;
-		ALIGN_SIZE(size, alignment);
-		frame->data[0] = bmalloc(size);
-		frame->linesize[0] = width*4;
-		break;
+	for (size_t i = 0; i < MAX_AV_PLANES; i++) {
+		frame->data[i]     = vid_frame.data[i];
+		frame->linesize[i] = vid_frame.linesize[i];
 	}
 	}
 }
 }
 
 

+ 8 - 8
libobs/obs-video.c

@@ -238,7 +238,7 @@ static inline void render_video(struct obs_core_video *video, int cur_texture,
 
 
 /* TODO: replace with more optimal conversion */
 /* TODO: replace with more optimal conversion */
 static inline bool download_frame(struct obs_core_video *video,
 static inline bool download_frame(struct obs_core_video *video,
-		int prev_texture, struct video_frame *frame)
+		int prev_texture, struct video_data *frame)
 {
 {
 	stagesurf_t surface = video->copy_surfaces[prev_texture];
 	stagesurf_t surface = video->copy_surfaces[prev_texture];
 
 
@@ -290,7 +290,7 @@ static inline uint32_t make_aligned_linesize_offset(uint32_t offset,
 }
 }
 
 
 static void fix_gpu_converted_alignment(struct obs_core_video *video,
 static void fix_gpu_converted_alignment(struct obs_core_video *video,
-		struct video_frame *frame, int cur_texture)
+		struct video_data *frame, int cur_texture)
 {
 {
 	struct source_frame *new_frame = &video->convert_frames[cur_texture];
 	struct source_frame *new_frame = &video->convert_frames[cur_texture];
 	uint32_t src_linesize = frame->linesize[0];
 	uint32_t src_linesize = frame->linesize[0];
@@ -317,7 +317,7 @@ static void fix_gpu_converted_alignment(struct obs_core_video *video,
 }
 }
 
 
 static bool set_gpu_converted_data(struct obs_core_video *video,
 static bool set_gpu_converted_data(struct obs_core_video *video,
-		struct video_frame *frame, int cur_texture)
+		struct video_data *frame, int cur_texture)
 {
 {
 	if (frame->linesize[0] == video->output_width*4) {
 	if (frame->linesize[0] == video->output_width*4) {
 		for (size_t i = 0; i < 3; i++) {
 		for (size_t i = 0; i < 3; i++) {
@@ -337,7 +337,7 @@ static bool set_gpu_converted_data(struct obs_core_video *video,
 }
 }
 
 
 static bool convert_frame(struct obs_core_video *video,
 static bool convert_frame(struct obs_core_video *video,
-		struct video_frame *frame,
+		struct video_data *frame,
 		const struct video_output_info *info, int cur_texture)
 		const struct video_output_info *info, int cur_texture)
 {
 {
 	struct source_frame *new_frame = &video->convert_frames[cur_texture];
 	struct source_frame *new_frame = &video->convert_frames[cur_texture];
@@ -368,7 +368,7 @@ static bool convert_frame(struct obs_core_video *video,
 }
 }
 
 
 static inline void output_video_data(struct obs_core_video *video,
 static inline void output_video_data(struct obs_core_video *video,
-		struct video_frame *frame, int cur_texture)
+		struct video_data *frame, int cur_texture)
 {
 {
 	const struct video_output_info *info;
 	const struct video_output_info *info;
 	info = video_output_getinfo(video->video);
 	info = video_output_getinfo(video->video);
@@ -382,7 +382,7 @@ static inline void output_video_data(struct obs_core_video *video,
 			return;
 			return;
 	}
 	}
 
 
-	video_output_frame(video->video, frame);
+	video_output_swap_frame(video->video, frame);
 }
 }
 
 
 static inline void output_frame(uint64_t timestamp)
 static inline void output_frame(uint64_t timestamp)
@@ -390,10 +390,10 @@ static inline void output_frame(uint64_t timestamp)
 	struct obs_core_video *video = &obs->video;
 	struct obs_core_video *video = &obs->video;
 	int cur_texture  = video->cur_texture;
 	int cur_texture  = video->cur_texture;
 	int prev_texture = cur_texture == 0 ? NUM_TEXTURES-1 : cur_texture-1;
 	int prev_texture = cur_texture == 0 ? NUM_TEXTURES-1 : cur_texture-1;
-	struct video_frame frame;
+	struct video_data frame;
 	bool frame_ready;
 	bool frame_ready;
 
 
-	memset(&frame, 0, sizeof(struct video_frame));
+	memset(&frame, 0, sizeof(struct video_data));
 	frame.timestamp = timestamp;
 	frame.timestamp = timestamp;
 
 
 	gs_entercontext(obs_graphics());
 	gs_entercontext(obs_graphics());

+ 11 - 11
plugins/obs-ffmpeg/obs-ffmpeg-output.c

@@ -399,7 +399,7 @@ static inline int64_t rescale_ts(int64_t val, AVCodecContext *context,
 
 
 #define YUV420_PLANES 3
 #define YUV420_PLANES 3
 
 
-static inline void copy_data(AVPicture *pic, const struct video_frame *frame,
+static inline void copy_data(AVPicture *pic, const struct video_data *frame,
 		int height)
 		int height)
 {
 {
 	for (int plane = 0; plane < YUV420_PLANES; plane++) {
 	for (int plane = 0; plane < YUV420_PLANES; plane++) {
@@ -420,7 +420,7 @@ static inline void copy_data(AVPicture *pic, const struct video_frame *frame,
 	}
 	}
 }
 }
 
 
-static void receive_video(void *param, const struct video_frame *frame)
+static void receive_video(void *param, const struct video_data *frame)
 {
 {
 	struct ffmpeg_output *output = param;
 	struct ffmpeg_output *output = param;
 	struct ffmpeg_data   *data   = &output->ff_data;
 	struct ffmpeg_data   *data   = &output->ff_data;
@@ -574,17 +574,17 @@ static bool ffmpeg_output_start(void *data)
 	if (!ffmpeg_data_init(&output->ff_data, filename_test))
 	if (!ffmpeg_data_init(&output->ff_data, filename_test))
 		return false;
 		return false;
 
 
-	struct audio_convert_info aci;
-	aci.samples_per_sec = SPS_TODO;
-	aci.format          = AUDIO_FORMAT_FLOAT_PLANAR;
-	aci.speakers        = SPEAKERS_STEREO;
+	struct audio_convert_info aci = {
+		.samples_per_sec = SPS_TODO,
+		.format          = AUDIO_FORMAT_FLOAT_PLANAR,
+		.speakers        = SPEAKERS_STEREO
+	};
 
 
-	struct video_convert_info vci;
-	vci.format          = VIDEO_FORMAT_I420;
-	vci.width           = 0;
-	vci.height          = 0;
+	struct video_scale_info vsi = {
+		.format = VIDEO_FORMAT_I420
+	};
 
 
-	video_output_connect(video, &vci, receive_video, output);
+	video_output_connect(video, &vsi, receive_video, output);
 	audio_output_connect(audio, &aci, receive_audio, output);
 	audio_output_connect(audio, &aci, receive_audio, output);
 	output->active = true;
 	output->active = true;
 
 

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

@@ -42,7 +42,9 @@
     <ClInclude Include="..\..\..\libobs\media-io\audio-io.h" />
     <ClInclude Include="..\..\..\libobs\media-io\audio-io.h" />
     <ClInclude Include="..\..\..\libobs\media-io\audio-resampler.h" />
     <ClInclude Include="..\..\..\libobs\media-io\audio-resampler.h" />
     <ClInclude Include="..\..\..\libobs\media-io\format-conversion.h" />
     <ClInclude Include="..\..\..\libobs\media-io\format-conversion.h" />
+    <ClInclude Include="..\..\..\libobs\media-io\video-frame.h" />
     <ClInclude Include="..\..\..\libobs\media-io\video-io.h" />
     <ClInclude Include="..\..\..\libobs\media-io\video-io.h" />
+    <ClInclude Include="..\..\..\libobs\media-io\video-scaler.h" />
     <ClInclude Include="..\..\..\libobs\obs-data.h" />
     <ClInclude Include="..\..\..\libobs\obs-data.h" />
     <ClInclude Include="..\..\..\libobs\obs-defs.h" />
     <ClInclude Include="..\..\..\libobs\obs-defs.h" />
     <ClInclude Include="..\..\..\libobs\obs-encoder.h" />
     <ClInclude Include="..\..\..\libobs\obs-encoder.h" />
@@ -92,7 +94,9 @@
     <ClCompile Include="..\..\..\libobs\media-io\audio-io.c" />
     <ClCompile Include="..\..\..\libobs\media-io\audio-io.c" />
     <ClCompile Include="..\..\..\libobs\media-io\audio-resampler-ffmpeg.c" />
     <ClCompile Include="..\..\..\libobs\media-io\audio-resampler-ffmpeg.c" />
     <ClCompile Include="..\..\..\libobs\media-io\format-conversion.c" />
     <ClCompile Include="..\..\..\libobs\media-io\format-conversion.c" />
+    <ClCompile Include="..\..\..\libobs\media-io\video-frame.c" />
     <ClCompile Include="..\..\..\libobs\media-io\video-io.c" />
     <ClCompile Include="..\..\..\libobs\media-io\video-io.c" />
+    <ClCompile Include="..\..\..\libobs\media-io\video-scaler-ffmpeg.c" />
     <ClCompile Include="..\..\..\libobs\obs-data.c" />
     <ClCompile Include="..\..\..\libobs\obs-data.c" />
     <ClCompile Include="..\..\..\libobs\obs-display.c" />
     <ClCompile Include="..\..\..\libobs\obs-display.c" />
     <ClCompile Include="..\..\..\libobs\obs-encoder.c" />
     <ClCompile Include="..\..\..\libobs\obs-encoder.c" />
@@ -200,7 +204,7 @@
     <Link>
     <Link>
       <SubSystem>Windows</SubSystem>
       <SubSystem>Windows</SubSystem>
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <GenerateDebugInformation>true</GenerateDebugInformation>
-      <AdditionalDependencies>avutil.lib;swresample.lib;pthreads.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>avutil.lib;swresample.lib;swscale.lib;pthreads.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
       <AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
     </Link>
     </Link>
     <PostBuildEvent>
     <PostBuildEvent>
@@ -221,7 +225,7 @@
     <Link>
     <Link>
       <SubSystem>Windows</SubSystem>
       <SubSystem>Windows</SubSystem>
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <GenerateDebugInformation>true</GenerateDebugInformation>
-      <AdditionalDependencies>avutil.lib;swresample.lib;pthreads.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>avutil.lib;swresample.lib;swscale.lib;pthreads.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
       <AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
     </Link>
     </Link>
     <PostBuildEvent>
     <PostBuildEvent>
@@ -246,7 +250,7 @@
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <EnableCOMDATFolding>true</EnableCOMDATFolding>
       <EnableCOMDATFolding>true</EnableCOMDATFolding>
       <OptimizeReferences>true</OptimizeReferences>
       <OptimizeReferences>true</OptimizeReferences>
-      <AdditionalDependencies>avutil.lib;swresample.lib;pthreads.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>avutil.lib;swresample.lib;swscale.lib;pthreads.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
       <AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
     </Link>
     </Link>
     <PostBuildEvent>
     <PostBuildEvent>
@@ -271,7 +275,7 @@
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <EnableCOMDATFolding>true</EnableCOMDATFolding>
       <EnableCOMDATFolding>true</EnableCOMDATFolding>
       <OptimizeReferences>true</OptimizeReferences>
       <OptimizeReferences>true</OptimizeReferences>
-      <AdditionalDependencies>avutil.lib;swresample.lib;pthreads.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>avutil.lib;swresample.lib;swscale.lib;pthreads.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
       <AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
     </Link>
     </Link>
     <PostBuildEvent>
     <PostBuildEvent>

+ 12 - 0
vs/2013/libobs/libobs.vcxproj.filters

@@ -207,6 +207,12 @@
     <ClInclude Include="..\..\..\libobs\obs-properties.h">
     <ClInclude Include="..\..\..\libobs\obs-properties.h">
       <Filter>libobs\Header Files</Filter>
       <Filter>libobs\Header Files</Filter>
     </ClInclude>
     </ClInclude>
+    <ClInclude Include="..\..\..\libobs\media-io\video-scaler.h">
+      <Filter>media-io\Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\..\libobs\media-io\video-frame.h">
+      <Filter>media-io\Header Files</Filter>
+    </ClInclude>
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <ClCompile Include="..\..\..\libobs\obs-output.c">
     <ClCompile Include="..\..\..\libobs\obs-output.c">
@@ -347,5 +353,11 @@
     <ClCompile Include="..\..\..\libobs\obs-view.c">
     <ClCompile Include="..\..\..\libobs\obs-view.c">
       <Filter>libobs\Source Files</Filter>
       <Filter>libobs\Source Files</Filter>
     </ClCompile>
     </ClCompile>
+    <ClCompile Include="..\..\..\libobs\media-io\video-scaler-ffmpeg.c">
+      <Filter>media-io\Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\..\libobs\media-io\video-frame.c">
+      <Filter>media-io\Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
   </ItemGroup>
 </Project>
 </Project>