diff --git a/applications/gui2/src/modules/thumbnails.cpp b/applications/gui2/src/modules/thumbnails.cpp
index 52b7b54ef358c6e49c8643060cdc663e0c35020a..7455c9c0a029a07a8de1daf693715507221f3d82 100644
--- a/applications/gui2/src/modules/thumbnails.cpp
+++ b/applications/gui2/src/modules/thumbnails.cpp
@@ -27,6 +27,10 @@ ThumbnailsController::~ThumbnailsController() {
 
 }
 
+void ThumbnailsController::removeFrameset(uint32_t id) {
+	io->feed()->remove(id);
+}
+
 void ThumbnailsController::show_thumbnails() {
 	auto thumb_view = new ftl::gui2::Thumbnails(screen, this);
 
diff --git a/applications/gui2/src/modules/thumbnails.hpp b/applications/gui2/src/modules/thumbnails.hpp
index 353a7e98ff40fcc27d1e39bb47bbae0e9bf47cc2..c24f4641de814670a2523f05091c8e75a614fa34 100644
--- a/applications/gui2/src/modules/thumbnails.hpp
+++ b/applications/gui2/src/modules/thumbnails.hpp
@@ -22,6 +22,8 @@ public:
 
 	std::vector<ftl::data::FrameSetPtr> getFrameSets();
 
+	void removeFrameset(uint32_t id);
+
 private:
 	std::mutex mtx_;
 	std::map<unsigned int, ftl::data::FrameSetPtr> framesets_;
diff --git a/applications/gui2/src/views/thumbnails.cpp b/applications/gui2/src/views/thumbnails.cpp
index c382f8d0b1625bf06a16e7bb4387e4cf65f9aa0c..79c4857d71c4e3d4802ae01cc8341f36d977e8e5 100644
--- a/applications/gui2/src/views/thumbnails.cpp
+++ b/applications/gui2/src/views/thumbnails.cpp
@@ -20,6 +20,9 @@
 #include <nanogui/tabwidget.h>
 #include <nanogui/vscrollpanel.h>
 #include <nanogui/layout.h>
+#include <nanogui/popup.h>
+
+#include <loguru.hpp>
 
 using ftl::gui2::ThumbView;
 using ftl::gui2::Thumbnails;
@@ -58,8 +61,10 @@ ThumbView::ThumbView(nanogui::Widget *parent, ThumbnailsController *control, ftl
 }
 
 bool ThumbView::mouseButtonEvent(const nanogui::Vector2i &p, int button, bool down, int modifiers) {
-	if (!down) {
-		ctrl_->show_camera(id_);
+	if (button == 0) {
+		if (!down) {
+			ctrl_->show_camera(id_);
+		}
 	}
 	return true;
 }
@@ -101,11 +106,45 @@ Thumbnails::Thumbnails(ftl::gui2::Screen *parent, ftl::gui2::ThumbnailsControlle
 
 	tabwidget_ = new nanogui::TabWidget(this);
 	tabwidget_->setFixedSize(size());
+
+	context_menu_ = new nanogui::Window(parent, "");
+	context_menu_->setVisible(false);
+	context_menu_->setLayout(new nanogui::BoxLayout(nanogui::Orientation::Vertical));
+
+	auto *button = new nanogui::Button(context_menu_, "Remove");
+	button->setCallback([this]() {
+		int ix = tabwidget_->activeTab();
+		LOG(INFO) << "REMOVE FSID " << ix;
+
+		tabwidget_->removeTab(ix);
+		thumbnails_.erase(ix);
+		context_menu_->setVisible(false);
+		ctrl_->removeFrameset(ix);
+		//screen()->performLayout();
+	});
 }
 
 
 Thumbnails::~Thumbnails() {
+	if (context_menu_->parent()->getRefCount() > 0) {
+		context_menu_->setVisible(false);
+		context_menu_->dispose();
+	}
+}
 
+bool Thumbnails::mouseButtonEvent(const nanogui::Vector2i &p, int button, bool down, int modifiers) {
+	bool r = View::mouseButtonEvent(p, button, down, modifiers);
+
+	if (button == 1) {
+		if (!down) {
+			context_menu_->setPosition(p - mPos);
+			context_menu_->setVisible(true);
+			return true;
+		}
+	} else {
+		context_menu_->setVisible(false);
+	}
+	return r;
 }
 
 void Thumbnails::updateThumbnails() {
diff --git a/applications/gui2/src/views/thumbnails.hpp b/applications/gui2/src/views/thumbnails.hpp
index 4aab2dc139cea1ce9af7642426dad20829a82309..c9440967de7cd297ee6fef9fa888e7904262f499 100644
--- a/applications/gui2/src/views/thumbnails.hpp
+++ b/applications/gui2/src/views/thumbnails.hpp
@@ -20,6 +20,8 @@ public:
 
 	virtual void draw(NVGcontext *ctx) override;
 
+	bool mouseButtonEvent(const nanogui::Vector2i &p, int button, bool down, int modifiers) override;
+
 private:
 	void updateThumbnails();
 	void addTab(unsigned int fsid);
@@ -38,6 +40,8 @@ private:
 
 	nanogui::Vector2i thumbsize_ = nanogui::Vector2i(320,180);
 
+	nanogui::Window *context_menu_;
+
 public:
 	EIGEN_MAKE_ALIGNED_OPERATOR_NEW
 };
diff --git a/components/common/cpp/src/timer.cpp b/components/common/cpp/src/timer.cpp
index 890f7dc839a17e848f5392146072d180f2067afd..0efe520928c5916a23d4c7b4ad7af533dbaa4a2f 100644
--- a/components/common/cpp/src/timer.cpp
+++ b/components/common/cpp/src/timer.cpp
@@ -223,7 +223,10 @@ static void trigger_jobs() {
 			bool doremove = !pj->job.trigger(ts);
 			pj->active = false;
 			active_jobs--;
+			lk.unlock();
 			if (doremove) removeJob(pj->id);
+			lk.lock();
+			break;
 		} else {
 			ftl::pool.push([pj,ts](int id) {
 				bool doremove = !pj->job.trigger(ts);
diff --git a/components/streams/include/ftl/streams/receiver.hpp b/components/streams/include/ftl/streams/receiver.hpp
index bb78bb3dbbcde50c25c689c8cda5ab3b51124ace..ef95922626fbf2cf6e3ceaa7517168f94a9bf35a 100644
--- a/components/streams/include/ftl/streams/receiver.hpp
+++ b/components/streams/include/ftl/streams/receiver.hpp
@@ -43,6 +43,8 @@ class Receiver : public ftl::Configurable, public ftl::data::Generator {
 
 	void processPackets(const ftl::codecs::StreamPacket &spkt, const ftl::codecs::Packet &pkt);
 
+	void removeBuilder(uint32_t id);
+
 	private:
 	ftl::stream::Stream *stream_;
 	ftl::data::Pool *pool_;
diff --git a/components/streams/src/feed.cpp b/components/streams/src/feed.cpp
index e2566317a79b6aabeb34a5729261686d9545cfb2..2aabbd396ca51d7b1589690d3df9778f5fe55a61 100644
--- a/components/streams/src/feed.cpp
+++ b/components/streams/src/feed.cpp
@@ -366,9 +366,29 @@ void Feed::remove(uint32_t id) {
 
 	// TODO: Actual delete of source
 	// If stream source, remove from muxer
+	if (streams_.count(id)) {
+		auto &streams = streams_[id];
+		for (auto *s : streams) {
+			stream_->remove(s);
+			delete s;
+		}
+
+		streams_.erase(id);
+	} else if (devices_.count(id)) {
+			
+	} else if (renderers_.count(id)) {
+
+	}
 
-	// If device or render source, remove builder from receiver
+	if (latest_.count(id)) latest_.erase(id);
 
+	for (auto i = fsid_lookup_.begin(); i != fsid_lookup_.end();) {
+		if (i->second == id) {
+			i = fsid_lookup_.erase(i);
+		} else {
+			++i;
+		}
+	}
 }
 
 ftl::operators::Graph* Feed::addPipeline(uint32_t fsid) {
diff --git a/components/streams/src/filestream.cpp b/components/streams/src/filestream.cpp
index b887e028a0a1895d351ebb7052c979f1dfd7f131..501c815cbdea840716afebb659a238e65aebb35d 100644
--- a/components/streams/src/filestream.cpp
+++ b/components/streams/src/filestream.cpp
@@ -387,11 +387,13 @@ bool File::begin(bool dorun) {
 }
 
 bool File::end() {
-	UNIQUE_LOCK(mutex_, lk);
 	if (!active_) return false;
 	active_ = false;
+	
 	timer_.cancel();
 
+	UNIQUE_LOCK(mutex_, lk);
+
 	if (mode_ == Mode::Read) {
 		if (istream_) {
 			istream_->close();
diff --git a/components/streams/src/receiver.cpp b/components/streams/src/receiver.cpp
index 5b6f6b5756bd821c93f37e1c4c034cc73004810d..a37c4bb3087fa4b1264ff891de3a4419821f2879 100644
--- a/components/streams/src/receiver.cpp
+++ b/components/streams/src/receiver.cpp
@@ -83,6 +83,12 @@ ftl::streams::BaseBuilder &Receiver::builder(uint32_t id) {
 	}
 }
 
+void Receiver::removeBuilder(uint32_t id) {
+	UNIQUE_LOCK(mutex_, lk);
+	auto i = builders_.find(id);
+	if (i != builders_.end()) builders_.erase(i);
+}
+
 void Receiver::registerBuilder(const std::shared_ptr<ftl::streams::BaseBuilder> &b) {
 	auto i = builders_.find(b->id());
 	if (i != builders_.end()) throw FTL_Error("Builder already exists");