diff --git a/applications/gui2/src/modules/camera.cpp b/applications/gui2/src/modules/camera.cpp
index bc6839a356204d179d0b53b546b1c22435b333c4..a1d2e352ec062a4aa958b58903eba6cad2ecfc1e 100644
--- a/applications/gui2/src/modules/camera.cpp
+++ b/applications/gui2/src/modules/camera.cpp
@@ -1,9 +1,11 @@
 #include "camera.hpp"
 
 #include "../views/camera3d.hpp"
+#include <ftl/rgbd/capabilities.hpp>
 
 using ftl::gui2::Camera;
 using ftl::codecs::Channel;
+using ftl::rgbd::Capability;
 
 void Camera::init() {
 
@@ -31,11 +33,38 @@ void Camera::activate(ftl::data::FrameID id) {
 		speaker->setVolume(v);
 	});
 
+	has_seen_frame_ = false;
+
 	filter = io->feed()->filter(std::unordered_set<unsigned int>{id.frameset()}, {Channel::Left, Channel::Depth});
 	filter->on(
 		[this, speaker = io->speaker(), panel = this->panel](ftl::data::FrameSetPtr fs){
 			if (paused || !view) { return true; }
-			std::atomic_store(&current_fs_, fs);
+
+			// For first data, extract useful things
+			if (!has_seen_frame_) {
+				has_seen_frame_ = true;
+
+				// TODO: Create the view here...
+
+				if (fs->frames[frame_idx].has(Channel::Capabilities)) {
+					const auto &cap = fs->frames[frame_idx].get<std::unordered_set<Capability>>(Channel::Capabilities);
+					LOG(INFO) << "Camera Capabilities:";
+					for (auto c : cap) {
+						LOG(INFO) << " -- " << (int)c;
+					}
+				}
+
+				if (fs->frames[frame_idx].has(Channel::MetaData)) {
+					const auto &meta = fs->frames[frame_idx].get<std::map<std::string,std::string>>(Channel::MetaData);
+					LOG(INFO) << "Camera Frame Meta Data:";
+					for (auto m : meta) {
+						LOG(INFO) << " -- " << m.first << " = " << m.second;
+					}
+				}
+			}
+
+			//std::atomic_store(&current_fs_, fs);  // Not needed and deprecated in C++20
+			current_fs_ = fs;
 
 			// Deal with audio
 			if (fs->frames[frame_idx].hasOwn(Channel::AudioStereo)) {
diff --git a/applications/gui2/src/modules/camera.hpp b/applications/gui2/src/modules/camera.hpp
index 4a17f749d06b12dc4ab4940157ac2dd11c3eeac0..e2d5439e8cb7dbb4a65de187b3dabeb6c7309900 100644
--- a/applications/gui2/src/modules/camera.hpp
+++ b/applications/gui2/src/modules/camera.hpp
@@ -38,6 +38,7 @@ private:
 	ftl::codecs::Channel channel = ftl::codecs::Channel::Colour;
 	ftl::stream::Feed::Filter *filter = nullptr;
 	std::atomic_bool paused = false; // TODO: implement in InputOutput
+	bool has_seen_frame_ = false;
 
 	ftl::data::FrameSetPtr current_fs_;
 	ftl::cuda::TextureObject<uchar4> current_frame_;
diff --git a/components/codecs/include/ftl/codecs/channels.hpp b/components/codecs/include/ftl/codecs/channels.hpp
index 417de06051f1458f30ee75e309a2b078a79740f7..bcbaad4b8e5ac234d51c9099d7b4d11820f94a45 100644
--- a/components/codecs/include/ftl/codecs/channels.hpp
+++ b/components/codecs/include/ftl/codecs/channels.hpp
@@ -53,7 +53,7 @@ enum struct Channel : int {
 	Index           = 68,
 	Control			= 69,	// For stream and encoder control
 	Settings3		= 70,
-	Name			= 71,
+	MetaData		= 71,	// Map of string pairs (key, value)
 	Capabilities	= 72,	// Unordered set of int capabilities
 
 	Data			= 2048,	// Custom data, any codec.
diff --git a/components/renderers/cpp/src/overlay.cpp b/components/renderers/cpp/src/overlay.cpp
index 2472ade58f32eb832677c265dece1cb5424fdf74..947662acd1d3c315b0d69f23cfacf0680fea7ac8 100644
--- a/components/renderers/cpp/src/overlay.cpp
+++ b/components/renderers/cpp/src/overlay.cpp
@@ -299,7 +299,8 @@ void Overlay::draw(ftl::data::FrameSet &fs, ftl::rgbd::Frame &frame, const Eigen
 
 			auto pose = f.getPose(); //.inverse() * state.getPose();
 
-			auto name = fs.frames[i].get<std::string>(Channel::Name);
+			auto meta = fs.frames[i].get<std::map<std::string,std::string>>(Channel::MetaData);
+			std::string name = (meta.count("name")) ? meta["name"] : "NoName";
 			_drawOutlinedShape(Shape::CAMERA, frame.getPose().inverse() * pose, Eigen::Vector3f(0.2f,0.2f,0.2f), make_uchar4(255,0,0,80), make_uchar4(255,0,0,255));
 			_drawAxis(frame.getPose().inverse() * pose, Eigen::Vector3f(0.2f, 0.2f, 0.2f));
 
diff --git a/components/rgbd-sources/include/ftl/rgbd/capabilities.hpp b/components/rgbd-sources/include/ftl/rgbd/capabilities.hpp
index c43b47e042adb9396067a66bbaae28fb748cddec..9f8ba139963d938c96ca0d9ad8192e39c8756fff 100644
--- a/components/rgbd-sources/include/ftl/rgbd/capabilities.hpp
+++ b/components/rgbd-sources/include/ftl/rgbd/capabilities.hpp
@@ -10,18 +10,20 @@ namespace rgbd {
  * To be added to the capabilities channel to indicate what the source device
  * is capable of.
  */
-enum class Capabilities : int {
+enum class Capability : int {
 	DEPTH = 0,	// Has depth data
 	MOVABLE,	// Is a pose controllable camera
 	STEREO,		// Has right colour
 	ACTIVE,		// An active depth sensor
 	VIDEO,		// Is video and not just static
-	FEEDBACK	// Can respond to feedback data (not just movable)
+	FEEDBACK,	// Can respond to feedback data (not just movable)
+	ADJUSTABLE,	// Camera properties can be changed (exposure etc)
+	VIRTUAL		// Is not a physical camera
 };
 
 }
 }
 
-MSGPACK_ADD_ENUM(ftl::rgbd::Capabilities);
+MSGPACK_ADD_ENUM(ftl::rgbd::Capability);
 
 #endif
\ No newline at end of file
diff --git a/components/rgbd-sources/src/init.cpp b/components/rgbd-sources/src/init.cpp
index 4633d3e937a5e04371d691250083fe3b843d7066..712b933cae220e3a88e1ddada193b263fbb03d9e 100644
--- a/components/rgbd-sources/src/init.cpp
+++ b/components/rgbd-sources/src/init.cpp
@@ -8,8 +8,8 @@ using ftl::data::StorageMode;
 bool ftl_video_init =
 	ftl::data::make_channel<ftl::rgbd::Camera>(Channel::Calibration, "calibration", ftl::data::StorageMode::PERSISTENT) &&
 	ftl::data::make_channel<ftl::rgbd::Camera>(Channel::Pose, "pose", ftl::data::StorageMode::PERSISTENT) &&
-	ftl::data::make_channel<ftl::rgbd::Camera>(Channel::Calibration2, "calibration_right", ftl::data::StorageMode::PERSISTENT) &&
-	ftl::data::make_channel<ftl::rgbd::Camera>(Channel::Name, "name", ftl::data::StorageMode::PERSISTENT);
+	ftl::data::make_channel<ftl::rgbd::Camera>(Channel::Calibration2, "calibration_right", ftl::data::StorageMode::PERSISTENT); // &&
+	//ftl::data::make_channel<ftl::rgbd::Camera>(Channel::Name, "name", ftl::data::StorageMode::PERSISTENT);
 
 bool ftl_video_initialised() {
 	return ftl_video_init;
diff --git a/components/rgbd-sources/src/sources/stereovideo/stereovideo.cpp b/components/rgbd-sources/src/sources/stereovideo/stereovideo.cpp
index 191c5ed83e0d97d8b90683bca7336e74ea79148a..56ad3b08831bdce80b8dcf2e060bc30ad9f7aad6 100644
--- a/components/rgbd-sources/src/sources/stereovideo/stereovideo.cpp
+++ b/components/rgbd-sources/src/sources/stereovideo/stereovideo.cpp
@@ -22,9 +22,12 @@
 #include "ftl/operators/disparity.hpp"
 #include "ftl/operators/mask.hpp"
 
+#include <ftl/rgbd/capabilities.hpp>
+
 #include "ftl/threads.hpp"
 #include "calibrate.hpp"
 #include "opencv.hpp"
+#include <unordered_set>
 
 #ifdef HAVE_PYLON
 #include "pylon.hpp"
@@ -34,6 +37,7 @@ using ftl::rgbd::detail::Calibrate;
 using ftl::rgbd::detail::StereoVideoSource;
 using ftl::codecs::Channel;
 using std::string;
+using ftl::rgbd::Capability;
 
 ftl::rgbd::detail::Device::Device(nlohmann::json &config) : Configurable(config) {
 
@@ -248,7 +252,19 @@ void StereoVideoSource::updateParameters(ftl::rgbd::Frame &frame) {
 
 	frame.setPose() = pose;
 
-	frame.create<std::string>(Channel::Name) = host_->value("name", host_->getID());
+	auto &meta = frame.create<std::map<std::string,std::string>>(Channel::MetaData);
+	meta["name"] = host_->value("name", host_->getID());
+	meta["uri"] = host_->value("uri", std::string(""));
+
+	if (!frame.has(Channel::Capabilities)) {
+		auto &cap = frame.create<std::unordered_set<Capability>>(Channel::Capabilities);
+		cap.emplace(Capability::VIDEO);
+
+		if (lsrc_->isStereo()) {
+			cap.emplace(Capability::STEREO);
+			cap.emplace(Capability::DEPTH);
+		}
+	}
 
 	cv::Mat K;
 
diff --git a/components/streams/src/builder.cpp b/components/streams/src/builder.cpp
index f8e8c9df1fc74777cfccc4e5ecc0275de16c69bc..4575ce3c9a073ae25a1be13604db5af1dace6d7b 100644
--- a/components/streams/src/builder.cpp
+++ b/components/streams/src/builder.cpp
@@ -356,7 +356,7 @@ void ForeignBuilder::_schedule() {
 			// Calling onFrameset but without all frames so mark as partial
 			if (static_cast<size_t>(fs->count) < fs->frames.size()) fs->set(ftl::data::FSFlag::PARTIAL);
 
-			for (auto &f : fs->frames) f.store();
+			//for (auto &f : fs->frames) f.store();
 			fs->store();
 
 			//UNIQUE_LOCK(fs->mutex(), lk2);
diff --git a/components/structures/include/ftl/data/new_frame.hpp b/components/structures/include/ftl/data/new_frame.hpp
index 1641423fea374bef21495de98672665a042c0c02..59f9b5781045a8a0ae3530c914109dae083b1e39 100644
--- a/components/structures/include/ftl/data/new_frame.hpp
+++ b/components/structures/include/ftl/data/new_frame.hpp
@@ -509,9 +509,9 @@ template <typename T>
 const T &ftl::data::Frame::get(ftl::codecs::Channel c) const {
 	const auto &d = _getData(c);
 
-	if (d.status == ftl::data::ChannelStatus::ENCODED) {
+	if (d.status != ftl::data::ChannelStatus::INVALID && !d.data.has_value() && d.encoded.size() > 0) {
 		UNIQUE_LOCK(parent_->mutex(), lk);
-		if (d.status == ftl::data::ChannelStatus::ENCODED) {
+		if (!d.data.has_value()) {
 			// Do a decode now and change the status
 			d.status = ftl::data::ChannelStatus::DISPATCHED;
 			decode_type<T>(d.data, d.encoded.front().data);
@@ -519,6 +519,7 @@ const T &ftl::data::Frame::get(ftl::codecs::Channel c) const {
 	}
 
 	if (d.status != ftl::data::ChannelStatus::INVALID) {
+		if (!d.data.has_value()) throw FTL_Error("'get' does not have value (" << static_cast<unsigned int>(c) << ")");
 		auto *p = std::any_cast<T>(&d.data);
 		if (!p) throw FTL_Error("'get' wrong type for channel (" << static_cast<unsigned int>(c) << ")");
 		return *p;
diff --git a/components/structures/src/new_frame.cpp b/components/structures/src/new_frame.cpp
index bcbc96d21feaf71823e5d5b26f21856a74134b9b..7872f2d823d907b409ab88fd3a1d5b5f584fff80 100644
--- a/components/structures/src/new_frame.cpp
+++ b/components/structures/src/new_frame.cpp
@@ -243,10 +243,12 @@ void Frame::store() {
 		if (ftl::data::isPersistent(c.first) && hasOwn(c.first)) {
 			auto &d = data_[c.first];
 			auto &pd = parent_->data_[c.first];
-			pd.data = d.data;
-			//pd.encoded = std::move(d.encoded);
+			pd.data = std::move(d.data);
+			pd.encoded = std::move(d.encoded);
+			//if (d.status == ChannelStatus::ENCODED) LOG(INFO) << "STORE ENCODED: " << (int)c.first;
 			pd.status = ChannelStatus::VALID;
 			//data_.erase(c.first);
+			d.status = ChannelStatus::INVALID;
 		}
 
 		parent_->change_.trigger(*this, c.first);