diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 2110e14be06187c48e66ab7f1ba5771420854b47..931dc387ad081a77bc71696ffd857dce153e9c19 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -65,7 +65,7 @@ windows-vision:
     - master
   stage: all
   variables:
-    CMAKE_ARGS: '-DWITH_OPTFLOW=TRUE -DBUILD_VISION=TRUE -DBUILD_CALIBRATION=FALSE -DBUILDRECONSTRUCT=FALSE -DBUILDRENDERER=FALSE -DBUILD_TESTING=FALSE -DBUILD_TESTS=FALSE'
+    CMAKE_ARGS: '-DENABLE_PROFILER=TRUE -DWITH_OPTFLOW=TRUE -DBUILD_VISION=TRUE -DBUILD_CALIBRATION=FALSE -DBUILDRECONSTRUCT=FALSE -DBUILDRENDERER=FALSE -DBUILD_TESTING=FALSE -DBUILD_TESTS=FALSE'
     DEPLOY_DIR: 'D:/Shared/AutoDeploy'
   tags:
     - win
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7a34946d6ecaf0047693d61d116d9701ed454459..c3c25a47751f03c062f58cf93ca38c0210d4e92c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -22,6 +22,7 @@ option(BUILD_GUI "Enable the GUI" ON)
 option(BUILD_CALIBRATION "Enable the calibration component" OFF)
 option(BUILD_TOOLS "Compile developer and research tools" ON)
 option(BUILD_TESTS "Compile all unit and integration tests" ON)
+option(ENABLE_PROFILER "Enable builtin performance profiling" OFF)
 
 set(THREADS_PREFER_PTHREAD_FLAG ON)
 set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/")
diff --git a/applications/gui/src/camera.cpp b/applications/gui/src/camera.cpp
index 82cb19091a57ffa2a8c855aa7d980f35c0fda727..6db9462d6a3d9d31c8786dc97cef2e12df2b6cec 100644
--- a/applications/gui/src/camera.cpp
+++ b/applications/gui/src/camera.cpp
@@ -3,6 +3,8 @@
 #include "screen.hpp"
 #include <nanogui/glutil.h>
 
+#include <ftl/profiler.hpp>
+
 #include <opencv2/imgproc.hpp>
 #include <opencv2/imgcodecs.hpp>
 
@@ -294,16 +296,19 @@ void ftl::gui::Camera::_draw(std::vector<ftl::rgbd::FrameSet*> &fss) {
 	frame_.reset();
 	frame_.setOrigin(&state_);
 
-	renderer_->begin(frame_);
-	for (auto *fs : fss) {
-		if (!usesFrameset(fs->id)) continue;
+	{
+		FTL_Profile("Render",0.034);
+		renderer_->begin(frame_);
+		for (auto *fs : fss) {
+			if (!usesFrameset(fs->id)) continue;
 
-		// FIXME: Should perhaps remain locked until after end is called?
-		// Definitely: causes flashing if not.
-		UNIQUE_LOCK(fs->mtx,lk);
-		renderer_->submit(fs, Channels<0>(channel_), transforms_[fs->id]);
+			// FIXME: Should perhaps remain locked until after end is called?
+			// Definitely: causes flashing if not.
+			UNIQUE_LOCK(fs->mtx,lk);
+			renderer_->submit(fs, Channels<0>(channel_), transforms_[fs->id]);
+		}
+		renderer_->end();
 	}
-	renderer_->end();
 
 	if (!post_pipe_) {
 		post_pipe_ = ftl::config::create<ftl::operators::Graph>(screen_->root(), "post_filters");
diff --git a/applications/gui/src/src_window.cpp b/applications/gui/src/src_window.cpp
index ba9c4d93bbda87e265d57214e69317ac60e06c8b..cce1942e0c9556b68108533f0fb2dac9419579e9 100644
--- a/applications/gui/src/src_window.cpp
+++ b/applications/gui/src/src_window.cpp
@@ -4,6 +4,8 @@
 #include "camera.hpp"
 #include "scene.hpp"
 
+#include <ftl/profiler.hpp>
+
 #include <nanogui/imageview.h>
 #include <nanogui/textbox.h>
 #include <nanogui/slider.h>
@@ -182,7 +184,10 @@ bool SourceWindow::_processFrameset(ftl::rgbd::FrameSet &fs, bool fromstream) {
 			if (!fs.frames[i].isGPU(Channel::Colour)) fs.frames[i].upload(Channels<0>(Channel::Colour), pre_pipelines_[fs.id]->getStream());
 		}
 
-		pre_pipelines_[fs.id]->apply(fs, fs, 0);
+		{
+			FTL_Profile("Prepipe",0.020);
+			pre_pipelines_[fs.id]->apply(fs, fs, 0);
+		}
 
 		fs.swapTo(*framesets_[fs.id]);
 	}
diff --git a/components/common/cpp/CMakeLists.txt b/components/common/cpp/CMakeLists.txt
index 81816912e9758bb57ffbdae16da3ee8a1d5d0af0..57ec51ace3ab13823105f2c5936e419e020809a6 100644
--- a/components/common/cpp/CMakeLists.txt
+++ b/components/common/cpp/CMakeLists.txt
@@ -10,6 +10,7 @@ set(COMMONSRC
 	src/cuda_common.cpp
 	src/ctpl_stl.cpp
 	src/timer.cpp
+	src/profiler.cpp
 )
 
 check_function_exists(uriParseSingleUriA HAVE_URIPARSESINGLE)
diff --git a/components/common/cpp/include/ftl/config.h.in b/components/common/cpp/include/ftl/config.h.in
index bcbebd958a9b6f1c6aff58f9c0907a882905ae15..3de97a9c00c73afa572bbd9f9aad51cd1f87b784 100644
--- a/components/common/cpp/include/ftl/config.h.in
+++ b/components/common/cpp/include/ftl/config.h.in
@@ -27,6 +27,8 @@
 #cmakedefine HAVE_PORTAUDIO
 #cmakedefine HAVE_X11
 
+#cmakedefine ENABLE_PROFILER
+
 extern const char *FTL_BRANCH;
 extern const char *FTL_VERSION_LONG;
 extern const char *FTL_VERSION;
diff --git a/components/common/cpp/include/ftl/profiler.hpp b/components/common/cpp/include/ftl/profiler.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..f33ff0e5b90225db24ed29af25fcf271df3ab59a
--- /dev/null
+++ b/components/common/cpp/include/ftl/profiler.hpp
@@ -0,0 +1,32 @@
+#ifndef _FTL_PROFILER_HPP_
+#define _FTL_PROFILER_HPP_
+
+#include <ftl/config.h>
+#include <string>
+
+namespace ftl {
+
+class Profiler {
+	public:
+	Profiler(const std::string &id, const std::string &label, double limit);
+	~Profiler();
+
+	static void verbosity(int v) { verb_ = v; }
+
+	private:
+	double stime_;
+	double limit_;
+	std::string label_;
+
+	static int verb_;
+};
+
+}
+
+#ifdef ENABLE_PROFILER
+#define FTL_Profile(LABEL, LIMIT) ftl::Profiler __profile(__func__, LABEL, LIMIT)
+#else
+#define FTL_Profile(LABEL, LIMIT)
+#endif
+
+#endif  // _FTL_PROFILER_HPP_
diff --git a/components/common/cpp/include/ftl/timer.hpp b/components/common/cpp/include/ftl/timer.hpp
index 266278b40eaec59342f11ab0a8065fa5e329669c..378cd3d273621a6c40ac07f0f03dec14675192e5 100644
--- a/components/common/cpp/include/ftl/timer.hpp
+++ b/components/common/cpp/include/ftl/timer.hpp
@@ -72,6 +72,8 @@ void setInterval(int ms);
 int getInterval();
 
 int64_t get_time();
+int64_t get_time_micro();
+double get_time_seconds();
 
 /**
  * Add the specified number of milliseconds to the clock when generating
diff --git a/components/common/cpp/src/configuration.cpp b/components/common/cpp/src/configuration.cpp
index 457e14f604c637b7bb5afbb62b84485b5acce20e..9a1b4e9cdd08f152e0b09647e7da7126d16197d6 100644
--- a/components/common/cpp/src/configuration.cpp
+++ b/components/common/cpp/src/configuration.cpp
@@ -22,6 +22,8 @@
 #include <ftl/timer.hpp>
 #include <ftl/cuda_common.hpp>
 
+#include <ftl/profiler.hpp>
+
 #include <fstream>
 #include <string>
 #include <map>
@@ -750,6 +752,10 @@ Configurable *ftl::config::configure(int argc, char **argv, const std::string &r
 	rootcfg->set("paths", paths);
 	process_options(rootcfg, options);
 
+	if (rootcfg->get<int>("profiler")) {
+		ftl::Profiler::verbosity(*rootcfg->get<int>("profiler"));
+	}
+
 	if (rootcfg->get<std::string>("branch")) {
 		ftl::branch_name = *rootcfg->get<std::string>("branch");
 	}
diff --git a/components/common/cpp/src/profiler.cpp b/components/common/cpp/src/profiler.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d2298874cde95e553509e2aa22a51a0ba6ea45b8
--- /dev/null
+++ b/components/common/cpp/src/profiler.cpp
@@ -0,0 +1,21 @@
+#include <ftl/profiler.hpp>
+#include <ftl/timer.hpp>
+#include <loguru.hpp>
+
+using ftl::Profiler;
+
+int Profiler::verb_ = 0;
+
+Profiler::Profiler(const std::string &id, const std::string &label, double limit)
+		: limit_(limit), label_(label) {
+
+	stime_ = ftl::timer::get_time_seconds();
+}
+
+Profiler::~Profiler() {
+	double etime = ftl::timer::get_time_seconds();
+
+	if ((etime-stime_ > limit_ && verb_ > 0) || verb_ > 1) {
+		LOG(INFO) << " -- Profiler --: " << label_ << " took " << (etime-stime_)*1000.0 << "ms";
+	}
+}
diff --git a/components/common/cpp/src/timer.cpp b/components/common/cpp/src/timer.cpp
index 28e1911b6132b45ee438a40cd0d389c0db454558..d58386c0174444471d9279425dad8093bebcf677 100644
--- a/components/common/cpp/src/timer.cpp
+++ b/components/common/cpp/src/timer.cpp
@@ -44,6 +44,14 @@ int64_t ftl::timer::get_time() {
 	return time_point_cast<milliseconds>(high_resolution_clock::now()).time_since_epoch().count()+clock_adjust;
 }
 
+int64_t ftl::timer::get_time_micro() {
+	return time_point_cast<std::chrono::microseconds>(high_resolution_clock::now()).time_since_epoch().count()+(clock_adjust*1000);
+}
+
+double ftl::timer::get_time_seconds() {
+	return time_point_cast<std::chrono::microseconds>(high_resolution_clock::now()).time_since_epoch().count() / 1000000.0 + (static_cast<double>(clock_adjust) / 1000.0);
+}
+
 static void waitTimePoint() {
 	auto start = high_resolution_clock::now();
 	int64_t now = get_time();
diff --git a/components/operators/src/nvopticalflow.cpp b/components/operators/src/nvopticalflow.cpp
index 36c0161ebf7cb45fe3a74d4c722d2cc2d4a7f2e3..17a5a58559d60a70b9bcb9758d37f6beed9d4753 100644
--- a/components/operators/src/nvopticalflow.cpp
+++ b/components/operators/src/nvopticalflow.cpp
@@ -46,7 +46,7 @@ bool NVOpticalFlow::apply(Frame &in, Frame &out, cudaStream_t stream) {
 	auto cvstream = cv::cuda::StreamAccessor::wrapStream(stream);
 	auto &flow = out.create<GpuMat>(channel_out_);
 
-	cv::cuda::cvtColor(in.get<GpuMat>(channel_in_), left_gray_, cv::COLOR_BGR2GRAY, 0, cvstream);
+	cv::cuda::cvtColor(in.get<GpuMat>(channel_in_), left_gray_, cv::COLOR_BGRA2GRAY, 0, cvstream);
 
 	nvof_->calc(left_gray_, left_gray_prev_, flow, cvstream);
 	std::swap(left_gray_, left_gray_prev_);
diff --git a/components/rgbd-sources/src/group.cpp b/components/rgbd-sources/src/group.cpp
index 0f9673bfea671b11dd70cfb17a7e66591c953ce1..70c8402fc2e7784ec90b32aeee1d35a2fa302efc 100644
--- a/components/rgbd-sources/src/group.cpp
+++ b/components/rgbd-sources/src/group.cpp
@@ -2,6 +2,7 @@
 #include <ftl/rgbd/source.hpp>
 #include <ftl/timer.hpp>
 #include <ftl/operators/operator.hpp>
+#include <ftl/profiler.hpp>
 
 #include <chrono>
 
@@ -47,6 +48,7 @@ void Group::addSource(ftl::rgbd::Source *src) {
 	src->setCallback([this,ix,src](int64_t timestamp, ftl::rgbd::Frame &frame) {
 		// FIXME: Not safe for multiple sources
 		if (pipeline_) {
+			FTL_Profile("GroupPipe", 0.03);
 			pipeline_->apply(frame, frame, 0);
 		}
 
diff --git a/components/rgbd-sources/src/sources/stereovideo/calibrate.cpp b/components/rgbd-sources/src/sources/stereovideo/calibrate.cpp
index aaa4b54f5e6f105354b88976fd854f6a31cedd16..5b7875097fd4f7f25757c7c4d17f76d6e8748a5d 100644
--- a/components/rgbd-sources/src/sources/stereovideo/calibrate.cpp
+++ b/components/rgbd-sources/src/sources/stereovideo/calibrate.cpp
@@ -346,3 +346,13 @@ void Calibrate::rectifyStereo(cv::Mat &l, cv::Mat &r) {
 	cv::remap(l, l, map1_.first, map2_.first, cv::INTER_LINEAR, 0, cv::Scalar());
 	cv::remap(r, r, map1_.second, map2_.second, cv::INTER_LINEAR, 0, cv::Scalar());
 }
+
+void Calibrate::rectifyLeft(cv::Mat &l) {
+	if (!rectify_) { return; }
+	cv::remap(l, l, map1_.first, map2_.first, cv::INTER_LINEAR, 0, cv::Scalar());
+}
+
+void Calibrate::rectifyRight(cv::Mat &r) {
+	if (!rectify_) { return; }
+	cv::remap(r, r, map1_.second, map2_.second, cv::INTER_LINEAR, 0, cv::Scalar());
+}
diff --git a/components/rgbd-sources/src/sources/stereovideo/calibrate.hpp b/components/rgbd-sources/src/sources/stereovideo/calibrate.hpp
index 58313b1eb88813c15405f33a21b7c56889cf5822..bec1c93388cfd20f8b78d60f7cf332f78e9244a5 100644
--- a/components/rgbd-sources/src/sources/stereovideo/calibrate.hpp
+++ b/components/rgbd-sources/src/sources/stereovideo/calibrate.hpp
@@ -39,6 +39,16 @@ class Calibrate : public ftl::Configurable {
 	 */
 	void rectifyStereo(cv::Mat &l, cv::Mat &r);
 
+	/**
+	 * @brief	Rectify and undistort left image (CPU)
+	 */
+	void rectifyLeft(cv::Mat &l);
+
+	/**
+	 * @brief	Rectify and undistort right image (CPU)
+	 */
+	void rectifyRight(cv::Mat &r);
+
 	void updateCalibration(const ftl::rgbd::Camera &p);
 	
 	/**
diff --git a/components/rgbd-sources/src/sources/stereovideo/local.cpp b/components/rgbd-sources/src/sources/stereovideo/local.cpp
index b03428df00a2ad56853a34533458cedd07835cd6..eed169bd2b3d073e544f09e101053e50103fc018 100644
--- a/components/rgbd-sources/src/sources/stereovideo/local.cpp
+++ b/components/rgbd-sources/src/sources/stereovideo/local.cpp
@@ -6,7 +6,8 @@
 
 #include <string>
 #include <chrono>
-#include <thread>
+#include <ftl/threads.hpp>
+#include <ftl/profiler.hpp>
 
 #include "local.hpp"
 #include "calibrate.hpp"
@@ -87,139 +88,16 @@ LocalSource::LocalSource(nlohmann::json &config)
 	dheight_ = value("depth_height", height_);
 
 	// Allocate page locked host memory for fast GPU transfer
-	left_hm_ = cv::cuda::HostMem(dheight_, dwidth_, CV_8UC3);
-	right_hm_ = cv::cuda::HostMem(dheight_, dwidth_, CV_8UC3);
-	hres_hm_ = cv::cuda::HostMem(height_, width_, CV_8UC3);
+	left_hm_ = cv::cuda::HostMem(dheight_, dwidth_, CV_8UC4);
+	right_hm_ = cv::cuda::HostMem(dheight_, dwidth_, CV_8UC4);
+	hres_hm_ = cv::cuda::HostMem(height_, width_, CV_8UC4);
 }
 
 LocalSource::LocalSource(nlohmann::json &config, const string &vid)
 	:	Configurable(config), timestamp_(0.0) {
 	LOG(FATAL) << "Stereo video file sources no longer supported";
- /*
-	//flip_ = value("flip", false);
-	//flip_v_ = value("flip_vert", false);
-	nostereo_ = value("nostereo", false);
-	//downsize_ = value("scale", 1.0f);
-
-	if (vid == "") {
-		LOG(FATAL) << "No video file specified";
-		camera_a_ = nullptr;
-		camera_b_ = nullptr;
-		return;
-	}
-
-	camera_a_ = new VideoCapture(vid.c_str());
-	camera_b_ = nullptr;
-
-	if (!camera_a_->isOpened()) {
-		delete camera_a_;
-		camera_a_ = nullptr;
-		LOG(FATAL) << "Unable to load video file";
-		return;
-	}
-
-	// Read first frame to determine stereo
-	Mat frame;
-	if (!camera_a_->read(frame)) {
-		LOG(FATAL) << "No data in video file";
-	}
-
-	if (frame.cols >= 2*frame.rows) {
-		LOG(INFO) << "Video size : " << frame.cols/2 << "x" << frame.rows;
-		width_ = frame.cols / 2;
-		height_ = frame.rows;
-		stereo_ = true;
-	} else {
-		LOG(INFO) << "Video size : " << frame.cols << "x" << frame.rows;
-		width_ = frame.cols;
-		height_ = frame.rows;
-		stereo_ = false;
-	}
-
-	dwidth_ = value("depth_width", width_);
-	dheight_ = value("depth_height", height_);
-
-	// Allocate page locked host memory for fast GPU transfer
-	left_hm_ = cv::cuda::HostMem(dheight_, dwidth_, CV_8UC3);
-	right_hm_ = cv::cuda::HostMem(dheight_, dwidth_, CV_8UC3);
-	hres_hm_ = cv::cuda::HostMem(height_, width_, CV_8UC3);
-
-	//tps_ = 1.0 / value("max_fps", 25.0);
-	*/
 }
 
-/*bool LocalSource::left(cv::Mat &l) {
-	if (!camera_a_) return false;
-
-	if (!camera_a_->grab()) {
-		LOG(ERROR) << "Unable to grab from camera A";
-		return false;
-	}
-
-	// Record timestamp
-	timestamp_ = duration_cast<duration<double>>(
-			high_resolution_clock::now().time_since_epoch()).count();
-
-	if (camera_b_ || !stereo_) {
-		if (!camera_a_->retrieve(l)) {
-			LOG(ERROR) << "Unable to read frame from camera A";
-			return false;
-		}
-	} else {
-		Mat frame;
-		if (!camera_a_->retrieve(frame)) {
-			LOG(ERROR) << "Unable to read frame from video";
-			return false;
-		}
-
-		int resx = frame.cols / 2;
-		if (flip_) {
-			l = Mat(frame, Rect(resx, 0, frame.cols-resx, frame.rows));
-		} else {
-			l = Mat(frame, Rect(0, 0, resx, frame.rows));
-		}
-	}
-
-	return true;
-}*/
-
-/*bool LocalSource::right(cv::Mat &r) {
-	if (!camera_a_->grab()) {
-		LOG(ERROR) << "Unable to grab from camera A";
-		return false;
-	}
-	if (camera_b_ && !camera_b_->grab()) {
-		LOG(ERROR) << "Unable to grab from camera B";
-		return false;
-	}
-
-	// Record timestamp
-	timestamp_ = duration_cast<duration<double>>(
-			high_resolution_clock::now().time_since_epoch()).count();
-
-	if (camera_b_ || !stereo_) {
-		if (camera_b_ && !camera_b_->retrieve(r)) {
-			LOG(ERROR) << "Unable to read frame from camera B";
-			return false;
-		}
-	} else {
-		Mat frame;
-		if (!camera_a_) return false;
-		if (!camera_a_->retrieve(frame)) {
-			LOG(ERROR) << "Unable to read frame from video";
-			return false;
-		}
-
-		int resx = frame.cols / 2;
-		if (flip_) {
-			r = Mat(frame, Rect(0, 0, resx, frame.rows));
-		} else {
-			r = Mat(frame, Rect(resx, 0, frame.cols-resx, frame.rows));
-		}
-	}
-
-	return true;
-}*/
 
 bool LocalSource::grab() {
 	if (!camera_a_) return false;
@@ -251,43 +129,81 @@ bool LocalSource::get(cv::cuda::GpuMat &l_out, cv::cuda::GpuMat &r_out, cv::cuda
 
 	if (!camera_a_) return false;
 
+	std::future<bool> future_b;
+	if (camera_b_) {
+		future_b = std::move(ftl::pool.push([this,&rfull,&r,c,&r_out,&stream](int id) {
+			if (!camera_b_->retrieve(frame_r_)) {
+				LOG(ERROR) << "Unable to read frame from camera B";
+				return false;
+			}
+
+			cv::cvtColor(frame_r_, rfull, cv::COLOR_BGR2BGRA);
+
+			if (stereo_) {
+				c->rectifyRight(rfull);
+
+				if (hasHigherRes()) {
+					// TODO: Use threads?
+					cv::resize(rfull, r, r.size(), 0.0, 0.0, cv::INTER_CUBIC);
+				}
+			}
+
+			r_out.upload(r, stream);
+			return true;
+		}));
+	}
+
 	if (camera_b_) {
+		//FTL_Profile("Camera Retrieve", 0.01);
 		// TODO: Use threads here?
-		if (!camera_a_->retrieve(lfull)) {
+		if (!camera_a_->retrieve(frame_l_)) {
 			LOG(ERROR) << "Unable to read frame from camera A";
 			return false;
 		}
 
-		if (camera_b_ && !camera_b_->retrieve(rfull)) {
+		/*if (camera_b_ && !camera_b_->retrieve(rfull)) {
 			LOG(ERROR) << "Unable to read frame from camera B";
 			return false;
-		}
+		}*/
 	} else {
-		if (!camera_a_->read(lfull)) {
+		if (!camera_a_->read(frame_l_)) {
 			LOG(ERROR) << "Unable to read frame from camera A";
 			return false;
 		}
 	}
 
+	cv::cvtColor(frame_l_, lfull, cv::COLOR_BGR2BGRA);
+
 	if (stereo_) {
-		c->rectifyStereo(lfull, rfull);
+		//FTL_Profile("Rectification", 0.01);
+		//c->rectifyStereo(lfull, rfull);
+		c->rectifyLeft(lfull);
 		
 		// Need to resize
-		if (hasHigherRes()) {
+		//if (hasHigherRes()) {
 			// TODO: Use threads?
-			cv::resize(rfull, r, r.size(), 0.0, 0.0, cv::INTER_CUBIC);
-		}
+		//	cv::resize(rfull, r, r.size(), 0.0, 0.0, cv::INTER_CUBIC);
+		//}
 	}
 
 	if (hasHigherRes()) {
+		//FTL_Profile("Frame Resize", 0.01);
 		cv::resize(lfull, l, l.size(), 0.0, 0.0, cv::INTER_CUBIC);
 		hres_out.upload(hres, stream);
 	} else {
 		hres_out = cv::cuda::GpuMat();
 	}
 
-	l_out.upload(l, stream);
-	r_out.upload(r, stream);
+	{
+		//FTL_Profile("Upload", 0.05);
+		l_out.upload(l, stream);
+	}
+	//r_out.upload(r, stream);
+
+	if (camera_b_) {
+		//FTL_Profile("WaitCamB", 0.05);
+		future_b.wait();
+	}
 
 	return true;
 }
diff --git a/components/rgbd-sources/src/sources/stereovideo/local.hpp b/components/rgbd-sources/src/sources/stereovideo/local.hpp
index d25e03cac5b684d23e201341018ef6a4e91e9543..5615e550d01eedb580923810aac8a6d01959169b 100644
--- a/components/rgbd-sources/src/sources/stereovideo/local.hpp
+++ b/components/rgbd-sources/src/sources/stereovideo/local.hpp
@@ -61,6 +61,9 @@ class LocalSource : public Configurable {
 	cv::cuda::HostMem right_hm_;
 	cv::cuda::HostMem hres_hm_;
 	cv::Mat rtmp_;
+
+	cv::Mat frame_l_;
+	cv::Mat frame_r_;
 };
 
 }
diff --git a/components/rgbd-sources/src/sources/stereovideo/stereovideo.cpp b/components/rgbd-sources/src/sources/stereovideo/stereovideo.cpp
index 0184d1f0444a3ff3e81f32db967f9580cd2dac74..683c6a98f4146230a3d2bb90e55fec457138ba60 100644
--- a/components/rgbd-sources/src/sources/stereovideo/stereovideo.cpp
+++ b/components/rgbd-sources/src/sources/stereovideo/stereovideo.cpp
@@ -5,7 +5,8 @@
 
 #include "stereovideo.hpp"
 
-#include "ftl/configuration.hpp"
+#include <ftl/configuration.hpp>
+#include <ftl/profiler.hpp>
 
 #include <nlohmann/json.hpp>
 
@@ -204,8 +205,8 @@ void StereoVideoSource::updateParameters() {
 	float doff = static_cast<float>(-calib_->getQ().at<double>(3,3) * baseline);
 
 	double d_resolution = this->host_->getConfig().value<double>("depth_resolution", 0.0);
-	float min_depth = this->host_->getConfig().value<double>("min_depth", 0.0);
-	float max_depth = this->host_->getConfig().value<double>("max_depth", 15.0);
+	float min_depth = this->host_->getConfig().value<double>("min_depth", 0.45);
+	float max_depth = this->host_->getConfig().value<double>("max_depth", 12.0);
 
 	// left
 	
@@ -261,6 +262,8 @@ bool StereoVideoSource::capture(int64_t ts) {
 }
 
 bool StereoVideoSource::retrieve() {
+	FTL_Profile("Stereo Retrieve", 0.03);
+	
 	auto &frame = frames_[0];
 	frame.reset();
 	frame.setOrigin(&state_);
diff --git a/components/streams/src/receiver.cpp b/components/streams/src/receiver.cpp
index 6338b1943520ef992be559f36ec2f22ac1987fdf..8d3aef21af0f5ad40f94d3b146660f6ef8d169bb 100644
--- a/components/streams/src/receiver.cpp
+++ b/components/streams/src/receiver.cpp
@@ -1,5 +1,6 @@
 #include <ftl/streams/receiver.hpp>
 #include <ftl/codecs/depth_convert_cuda.hpp>
+#include <ftl/profiler.hpp>
 
 #include <opencv2/cudaimgproc.hpp>
 
@@ -151,6 +152,8 @@ void Receiver::_processAudio(const StreamPacket &spkt, const Packet &pkt) {
 }
 
 void Receiver::_processVideo(const StreamPacket &spkt, const Packet &pkt) {
+	FTL_Profile("VideoPacket", 0.02);
+
 	const ftl::codecs::Channel rchan = spkt.channel;
 	const unsigned int channum = (unsigned int)spkt.channel;
 	InternalVideoStates &ividstate = _getVideoFrame(spkt);
@@ -177,6 +180,7 @@ void Receiver::_processVideo(const StreamPacket &spkt, const Packet &pkt) {
 
 	// Do the actual decode into the surface buffer
 	try {
+		FTL_Profile("Decode", 0.015);
 		if (!decoder->decode(pkt, surface)) {
 			LOG(ERROR) << "Decode failed on channel " << (int)spkt.channel;
 			return;
diff --git a/components/streams/src/sender.cpp b/components/streams/src/sender.cpp
index a16e0734907b02b58d8f3eef32ae520e468369e4..1b448648fe5ef51fd3f4ccf0440c79ebbe4fe8a9 100644
--- a/components/streams/src/sender.cpp
+++ b/components/streams/src/sender.cpp
@@ -1,5 +1,6 @@
 #include <ftl/streams/sender.hpp>
 #include <ftl/codecs/depth_convert_cuda.hpp>
+#include <ftl/profiler.hpp>
 
 #include <opencv2/cudaimgproc.hpp>
 
@@ -125,6 +126,8 @@ void Sender::post(ftl::rgbd::FrameSet &fs) {
 
 	bool do_inject = !do_inject_.test_and_set();
 
+	FTL_Profile("SenderPost", 0.02);
+
     for (size_t i=0; i<fs.frames.size(); ++i) {
         const auto &frame = fs.frames[i];
 
@@ -290,6 +293,8 @@ void Sender::_encodeChannel(ftl::rgbd::FrameSet &fs, Channel c, bool reset) {
 				cv::Rect roi = _generateROI(fs, cc, offset);
 				cv::cuda::GpuMat sroi = tile.surface(roi);
 
+				FTL_Profile("Encoder",0.02);
+
 				if (enc->encode(sroi, pkt)) {
 					stream_->post(spkt, pkt);