From f374887e7a27831771304a09a74d6cd54f8ef4e4 Mon Sep 17 00:00:00 2001 From: dknn 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 +#include +#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 &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 +#include + + +#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 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 + +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 +#include +#include +#include +#include +#include + + +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 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 + +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 +#include +#include + +namespace Swift { + class VideoFrame { + public: + typedef boost::shared_ptr ref; + + VideoFrame(int width, int height, uint8_t *rgb24data); + + int width; + int height; + std::vector data; + }; +} + +#endif // VIDEOFRAME_H -- cgit v0.10.2-6-g49f6