From f374887e7a27831771304a09a74d6cd54f8ef4e4 Mon Sep 17 00:00:00 2001
From: dknn <yoann.blein@free.fr>
Date: Thu, 21 Jun 2012 20:06:42 +0200
Subject: Middle part of the outgoing media pipeline


diff --git a/Swiften/SConscript b/Swiften/SConscript
index f95ef45..a08cb6e 100644
--- a/Swiften/SConscript
+++ b/Swiften/SConscript
@@ -253,7 +253,8 @@ if env["SCONS_STAGE"] == "build" :
 			"LinkLocal",
 			"StreamManagement",
 			"Component",
-			"AdHoc"
+			"AdHoc",
+			"ScreenSharing",
 		])
 	if env["build_examples"] :
 		SConscript(dirs = [
diff --git a/Swiften/ScreenSharing/SConscript b/Swiften/ScreenSharing/SConscript
new file mode 100644
index 0000000..21b8aa9
--- /dev/null
+++ b/Swiften/ScreenSharing/SConscript
@@ -0,0 +1,11 @@
+Import("swiften_env", "env")
+
+sources = [
+		"VideoFrame.cpp",
+		"VideoEncoder.cpp",
+		"VP8Encoder.cpp",
+	]
+
+swiften_env.Append(SWIFTEN_OBJECTS = swiften_env.SwiftenObject(sources))
+
+env.Append(UNITTEST_SOURCES = [])
diff --git a/Swiften/ScreenSharing/VP8Encoder.cpp b/Swiften/ScreenSharing/VP8Encoder.cpp
new file mode 100644
index 0000000..20c73f9
--- /dev/null
+++ b/Swiften/ScreenSharing/VP8Encoder.cpp
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2012 Yoann Blein
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <Swiften/ScreenSharing/VP8Encoder.h>
+#include <Swiften/Base/Log.h>
+#include "vpx/vp8cx.h"
+
+namespace Swift {
+
+VP8Encoder::VP8Encoder(unsigned int  width, unsigned int  height) :
+	codecInterface(vpx_codec_vp8_cx()), imageBuffer(0), codecFlags(0), frameFlags(0), frameNumber(0)
+{
+	SWIFT_LOG(debug) << "VP8 Encoder:" << vpx_codec_iface_name(codecInterface) << std::endl;
+
+	// Populate encoder configuration
+	vpx_codec_err_t err = vpx_codec_enc_config_default(codecInterface, &codecConfig, 0);
+	if (err) {
+		SWIFT_LOG(debug) << "VP8 Encoder: Failed to get config, " << vpx_codec_err_to_string(err) << std::endl;
+		// TODO: exception
+	}
+
+	// Update the default configuration with our settings
+	codecConfig.rc_target_bitrate = width * height * codecConfig.rc_target_bitrate / codecConfig.g_w / codecConfig.g_h; // Sample config on VP8 doc
+	//codecConfig.rc_target_bitrate = 90000; // ?
+	codecConfig.g_w = width;
+	codecConfig.g_h = height;
+	//codecConfig.g_threads = ?;
+
+	// codecFlags = VPX_CODEC_USE_OUTPUT_PARTITION; // TODO: adapt packetization and decoder
+
+	updateCodecConfig(); // TODO: Handle and re-throw exceptions
+}
+
+VP8Encoder::~VP8Encoder()
+{
+	vpx_codec_destroy(&codecContext);
+	vpx_img_free(imageBuffer);
+}
+
+void VP8Encoder::updateCodecConfig()
+{
+	if (imageBuffer)
+		vpx_img_free(imageBuffer);
+
+	vpx_codec_err_t err = vpx_codec_enc_init(&codecContext, codecInterface, &codecConfig, codecFlags);
+	if (err) {
+		SWIFT_LOG(debug) << "VP8 Encoder: Failed to initialize encoder, " << vpx_codec_err_to_string(err) << std::endl;
+		// TODO: exception
+	}
+
+	imageBuffer = vpx_img_alloc(0, VPX_IMG_FMT_YV12, codecConfig.g_w, codecConfig.g_h, 1);
+	if (!imageBuffer) {
+		SWIFT_LOG(debug) << "VP8 Encoder: Failed to allocate image" << std::endl;
+		// TODO: exception
+	}
+}
+
+void VP8Encoder::encodingLoop()
+{
+	while (!stop) {
+		VideoFrame::ref frame = popWhenFrameIsAvailable();
+		if (stop)
+			return;
+
+		if (!convertRGB24toYV12inBuffer(frame)) {
+			SWIFT_LOG(debug) << "VP8 Encoder: Failed to convert frame: Image buffer not initialized" << std::endl;
+			return;
+		}
+
+		vpx_codec_err_t err = vpx_codec_encode(&codecContext, imageBuffer, frameNumber, 1, frameFlags, VPX_DL_REALTIME);
+		if (err) {
+			SWIFT_LOG(debug) << "VP8 Encoder: Failed to encode frame, " << vpx_codec_err_to_string(err) << std::endl;
+			// TODO: signal ?
+			return;
+		}
+
+		const vpx_codec_cx_pkt_t *pkt;
+		vpx_codec_iter_t iter = NULL;
+		while ((pkt = vpx_codec_get_cx_data(&codecContext, &iter))) {
+			switch (pkt->kind) {
+				case VPX_CODEC_CX_FRAME_PKT:
+					// TODO: Packetize this frame data
+					break;
+				default:
+					break;
+			}
+		}
+
+		++frameNumber;
+	}
+}
+
+bool VP8Encoder::convertRGB24toYV12inBuffer(const VideoFrame::ref frame)
+{
+	if (!imageBuffer)
+		return false;
+
+	uint8_t *yPlane = imageBuffer->planes[VPX_PLANE_Y];
+	uint8_t *uPlane = imageBuffer->planes[VPX_PLANE_U];
+	uint8_t *vPlane = imageBuffer->planes[VPX_PLANE_V];
+
+	unsigned int width = frame->width;
+	unsigned int height = frame->height;
+	unsigned int len = width * height;
+	const std::vector<uint8_t> &data = frame->data;
+
+	for (unsigned int  i = 0; i < len; ++i) {
+		yPlane[i] = data[i * 3];
+	}
+
+	for (unsigned int line = 0; line < height / 2; ++line) {
+		for (unsigned int col = 0; col < width / 2; ++col) {
+			const uint8_t *p1 = &data[3 * (2 * (line * width + col))];
+			const uint8_t *p2 = &data[3 * (2 * (line * width + col) + 1)];
+			const uint8_t *p3 = &data[3 * (2 * (line * width + col) + width)];
+			const uint8_t *p4 = &data[3 * (2 * (line * width + col) + width + 1)];
+
+			int r = (p1[0] + p2[0] + p3[0] + p4[0]) * 0.25;
+			int g = (p1[1] + p2[1] + p3[1] + p4[1]) * 0.25;
+			int b = (p1[2] + p2[2] + p3[2] + p4[2]) * 0.25;
+
+			uPlane[line * imageBuffer->stride[VPX_PLANE_U] + col] = ((-38 * r - 74 * g + 112 * b + 128) >> 8) + 128;
+			vPlane[line * imageBuffer->stride[VPX_PLANE_V] + col] = ((112 * r - 94 * g - 18  * b + 128) >> 8) + 128;
+		}
+	}
+	return true;
+}
+
+}
diff --git a/Swiften/ScreenSharing/VP8Encoder.h b/Swiften/ScreenSharing/VP8Encoder.h
new file mode 100644
index 0000000..b512058
--- /dev/null
+++ b/Swiften/ScreenSharing/VP8Encoder.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2012 Yoann Blein
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#ifndef VP8ENCODER_H
+#define VP8ENCODER_H
+
+#pragma once
+
+#include <Swiften/ScreenSharing/VideoEncoder.h>
+#include <Swiften/Base/boost_bsignals.h>
+
+
+#define VPX_CODEC_DISABLE_COMPAT 1 // Recomended
+#include "vpx/vpx_encoder.h"
+
+
+namespace Swift {
+	class VP8Encoder : public VideoEncoder {
+		public:
+			VP8Encoder(unsigned int width, unsigned int height);
+			virtual ~VP8Encoder();
+
+			void updateCodecConfig();
+
+		public:
+			boost::signal<void (const vpx_codec_cx_pkt_t *)> onNewFrameEncoded;
+
+		protected:
+			virtual void encodingLoop();
+
+		private:
+			bool convertRGB24toYV12inBuffer(const VideoFrame::ref frame);
+
+		private:
+			vpx_codec_iface_t* codecInterface;
+			vpx_codec_ctx_t codecContext;
+			vpx_codec_enc_cfg_t codecConfig;
+			vpx_image_t *imageBuffer;
+			vpx_codec_flags_t codecFlags;
+			vpx_enc_frame_flags_t frameFlags;
+			int frameNumber;
+	};
+
+}
+
+
+#endif // VP8ENCODER_H
diff --git a/Swiften/ScreenSharing/VideoEncoder.cpp b/Swiften/ScreenSharing/VideoEncoder.cpp
new file mode 100644
index 0000000..40d9bef
--- /dev/null
+++ b/Swiften/ScreenSharing/VideoEncoder.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2012 Yoann Blein
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <Swiften/ScreenSharing/VideoEncoder.h>
+
+namespace Swift {
+
+void VideoEncoder::addFrame(VideoFrame::ref frame)
+{
+	boost::mutex::scoped_lock lock(queueMutex);
+	frames.push(frame);
+	lock.unlock();
+	queueCondVar.notify_one();
+}
+
+void VideoEncoder::startEncoding()
+{
+	stop = false;
+	encodingThread = boost::thread(&VideoEncoder::encodingLoop, this);
+}
+
+void VideoEncoder::stopEncoding()
+{
+	stop = true;
+	encodingThread.join();
+}
+
+VideoFrame::ref VideoEncoder::popWhenFrameIsAvailable()
+{
+	boost::mutex::scoped_lock lock(queueMutex);
+	while (frames.empty()) {
+		queueCondVar.wait(lock);
+	}
+	VideoFrame::ref popped = frames.front();
+	frames.pop();
+	return popped;
+}
+
+}
diff --git a/Swiften/ScreenSharing/VideoEncoder.h b/Swiften/ScreenSharing/VideoEncoder.h
new file mode 100644
index 0000000..d98b854
--- /dev/null
+++ b/Swiften/ScreenSharing/VideoEncoder.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2012 Yoann Blein
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#ifndef VIDEOENCODER_H
+#define VIDEOENCODER_H
+
+#include <Swiften/ScreenSharing/VideoFrame.h>
+#include <boost/thread/thread.hpp>
+#include <boost/thread/mutex.hpp>
+#include <boost/thread/condition_variable.hpp>
+#include <stdint.h>
+#include <queue>
+
+
+namespace Swift {
+	class VideoEncoder {
+		public:
+			VideoEncoder() {}
+			virtual ~VideoEncoder() {}
+
+			void addFrame(VideoFrame::ref frame);
+
+			void startEncoding();
+			void stopEncoding();
+
+		protected:
+			virtual void encodingLoop() = 0;
+			VideoFrame::ref popWhenFrameIsAvailable();
+
+		protected:
+			bool stop;
+
+		private:
+			std::queue<VideoFrame::ref> frames;
+			boost::mutex queueMutex;
+			boost::condition_variable queueCondVar;
+			boost::thread encodingThread;
+	};
+}
+
+#endif // VIDEOENCODER_H
diff --git a/Swiften/ScreenSharing/VideoFrame.cpp b/Swiften/ScreenSharing/VideoFrame.cpp
new file mode 100644
index 0000000..d36ec5c
--- /dev/null
+++ b/Swiften/ScreenSharing/VideoFrame.cpp
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2012 Yoann Blein
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <Swiften/ScreenSharing/VideoFrame.h>
+
+namespace Swift {
+
+VideoFrame::VideoFrame(int width, int height, uint8_t *rgb24data)
+	: width(width), height(height)
+{
+	int len = width * height;
+	data.insert(data.begin(), rgb24data, rgb24data + len);
+}
+
+}
diff --git a/Swiften/ScreenSharing/VideoFrame.h b/Swiften/ScreenSharing/VideoFrame.h
new file mode 100644
index 0000000..1d98062
--- /dev/null
+++ b/Swiften/ScreenSharing/VideoFrame.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2012 Yoann Blein
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#ifndef VIDEOFRAME_H
+#define VIDEOFRAME_H
+
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+#include <vector>
+#include <stdint.h>
+
+namespace Swift {
+	class VideoFrame {
+		public:
+			typedef boost::shared_ptr<VideoFrame> ref;
+
+			VideoFrame(int width, int height, uint8_t *rgb24data);
+
+			int width;
+			int height;
+			std::vector<uint8_t> data;
+	};
+}
+
+#endif // VIDEOFRAME_H
-- 
cgit v0.10.2-6-g49f6