From 967f07827c7b5d62464b4bd4f4b3c37f2d459a8f Mon Sep 17 00:00:00 2001
From: Nicolas Pope <nicolas.pope@utu.fi>
Date: Tue, 17 May 2022 09:11:32 +0000
Subject: [PATCH] #14 Add channel disabling

---
 include/ftl/protocol/broadcaster.hpp |   6 +
 include/ftl/protocol/muxer.hpp       |   6 +
 include/ftl/protocol/streams.hpp     |  26 +-
 src/streams/broadcaster.cpp          |  30 ++
 src/streams/muxer.cpp                |  21 +
 src/streams/streams.cpp              |  26 ++
 test/muxer_unit.cpp                  | 549 +++++++++++++++------------
 7 files changed, 411 insertions(+), 253 deletions(-)

diff --git a/include/ftl/protocol/broadcaster.hpp b/include/ftl/protocol/broadcaster.hpp
index a514299..55c88d0 100644
--- a/include/ftl/protocol/broadcaster.hpp
+++ b/include/ftl/protocol/broadcaster.hpp
@@ -51,6 +51,12 @@ class Broadcast : public Stream {
 
     bool enable(FrameID id, const ftl::protocol::ChannelSet &channels) override;
 
+    void disable(FrameID id) override;
+
+    void disable(FrameID id, ftl::protocol::Channel channel) override;
+
+    void disable(FrameID id, const ftl::protocol::ChannelSet &channels) override;
+
     StreamType type() const override;
 
  private:
diff --git a/include/ftl/protocol/muxer.hpp b/include/ftl/protocol/muxer.hpp
index 298b32a..1967dad 100644
--- a/include/ftl/protocol/muxer.hpp
+++ b/include/ftl/protocol/muxer.hpp
@@ -69,6 +69,12 @@ class Muxer : public Stream {
 
     bool enable(FrameID id, const ftl::protocol::ChannelSet &channels) override;
 
+    void disable(FrameID id) override;
+
+    void disable(FrameID id, ftl::protocol::Channel channel) override;
+
+    void disable(FrameID id, const ftl::protocol::ChannelSet &channels) override;
+
     void setProperty(ftl::protocol::StreamProperty opt, std::any value) override;
 
     std::any getProperty(ftl::protocol::StreamProperty opt) override;
diff --git a/include/ftl/protocol/streams.hpp b/include/ftl/protocol/streams.hpp
index e1d4542..f8af208 100644
--- a/include/ftl/protocol/streams.hpp
+++ b/include/ftl/protocol/streams.hpp
@@ -284,7 +284,31 @@ class Stream {
      */
     virtual bool enable(FrameID id, const ftl::protocol::ChannelSet &channels);
 
-    // TODO(Nick): Disable
+    /**
+     * @brief Disable an entire frame. If the frame is not available or is already
+     * disabled then this method has no effect.
+     * 
+     * @param id 
+     */
+    virtual void disable(FrameID id);
+
+    /**
+     * @brief Disable a specific channel in a frame. If not available or already
+     * disabled then this method has no effect.
+     * 
+     * @param id 
+     * @param channel 
+     */
+    virtual void disable(FrameID id, ftl::protocol::Channel channel);
+
+    /**
+     * @brief Disable a set of channels in a frame. If not available or already
+     * disabled then this method has no effect.
+     * 
+     * @param id 
+     * @param channels 
+     */
+    virtual void disable(FrameID id, const ftl::protocol::ChannelSet &channels);
 
     /**
      * @brief Set a stream property to a new value. If the property is not supported,
diff --git a/src/streams/broadcaster.cpp b/src/streams/broadcaster.cpp
index 8380357..441a755 100644
--- a/src/streams/broadcaster.cpp
+++ b/src/streams/broadcaster.cpp
@@ -134,6 +134,36 @@ bool Broadcast::enable(FrameID id, const ftl::protocol::ChannelSet &channels) {
     return r;
 }
 
+void Broadcast::disable(FrameID id) {
+    {
+        SHARED_LOCK(mtx_, lk);
+        for (auto &s : streams_) {
+            s.stream->disable(id);
+        }
+    }
+    Stream::disable(id);
+}
+
+void Broadcast::disable(FrameID id, ftl::protocol::Channel channel) {
+    {
+        SHARED_LOCK(mtx_, lk);
+        for (auto &s : streams_) {
+            s.stream->disable(id, channel);
+        }
+    }
+    Stream::disable(id, channel);
+}
+
+void Broadcast::disable(FrameID id, const ftl::protocol::ChannelSet &channels) {
+    {
+        SHARED_LOCK(mtx_, lk);
+        for (auto &s : streams_) {
+            s.stream->disable(id, channels);
+        }
+    }
+    Stream::disable(id, channels);
+}
+
 void Broadcast::setProperty(ftl::protocol::StreamProperty opt, std::any value) {}
 
 std::any Broadcast::getProperty(ftl::protocol::StreamProperty opt) {
diff --git a/src/streams/muxer.cpp b/src/streams/muxer.cpp
index 74fa38b..edba691 100644
--- a/src/streams/muxer.cpp
+++ b/src/streams/muxer.cpp
@@ -201,6 +201,27 @@ bool Muxer::enable(FrameID id, const ftl::protocol::ChannelSet &channels) {
     return r;
 }
 
+void Muxer::disable(FrameID id) {
+    auto p = _mapToOutput(id);
+    if (!p.second) return;
+    p.second->stream->disable(p.first);
+    Stream::disable(id);
+}
+
+void Muxer::disable(FrameID id, ftl::protocol::Channel channel) {
+    auto p = _mapToOutput(id);
+    if (!p.second) return;
+    p.second->stream->disable(p.first, channel);
+    Stream::disable(id, channel);
+}
+
+void Muxer::disable(FrameID id, const ftl::protocol::ChannelSet &channels) {
+    auto p = _mapToOutput(id);
+    if (!p.second) return;
+    p.second->stream->disable(p.first, channels);
+    Stream::disable(id, channels);
+}
+
 void Muxer::setProperty(ftl::protocol::StreamProperty opt, std::any value) {
     for (auto &s : streams_) {
         s.stream->setProperty(opt, value);
diff --git a/src/streams/streams.cpp b/src/streams/streams.cpp
index 15c6b1d..d2d2fb1 100644
--- a/src/streams/streams.cpp
+++ b/src/streams/streams.cpp
@@ -121,6 +121,32 @@ bool Stream::enable(FrameID id, const ftl::protocol::ChannelSet &channels) {
     return true;
 }
 
+void Stream::disable(FrameID id) {
+    UNIQUE_LOCK(mtx_, lk);
+    auto &p = state_[id];
+    p.enabled = false;
+}
+
+void Stream::disable(FrameID id, ftl::protocol::Channel channel) {
+    UNIQUE_LOCK(mtx_, lk);
+    auto &p = state_[id];
+    p.selected.erase(channel);
+    if (p.selected.size() == 0) {
+        p.enabled = false;
+    }
+}
+
+void Stream::disable(FrameID id, const ftl::protocol::ChannelSet &channels) {
+    UNIQUE_LOCK(mtx_, lk);
+    auto &p = state_[id];
+    for (const auto &c : channels) {
+        p.selected.erase(c);
+    }
+    if (p.selected.size() == 0) {
+        p.enabled = false;
+    }
+}
+
 void Stream::reset() {
     UNIQUE_LOCK(mtx_, lk);
     state_.clear();
diff --git a/test/muxer_unit.cpp b/test/muxer_unit.cpp
index e1ab51f..4b8263e 100644
--- a/test/muxer_unit.cpp
+++ b/test/muxer_unit.cpp
@@ -15,25 +15,25 @@ using ftl::protocol::ChannelSet;
 using ftl::protocol::FrameID;
 
 class TestStream : public ftl::protocol::Stream {
-	public:
-	TestStream() {};
-	~TestStream() {};
+    public:
+    TestStream() {};
+    ~TestStream() {};
 
-	bool post(const ftl::protocol::StreamPacket &spkt, const ftl::protocol::DataPacket &pkt) {
-		seen(FrameID(spkt.streamID, spkt.frame_number), spkt.channel);
-		trigger(spkt, pkt);
-		return true;
-	}
+    bool post(const ftl::protocol::StreamPacket &spkt, const ftl::protocol::DataPacket &pkt) {
+        seen(FrameID(spkt.streamID, spkt.frame_number), spkt.channel);
+        trigger(spkt, pkt);
+        return true;
+    }
 
-	bool begin() override { return true; }
-	bool end() override { return true; }
-	bool active() override { return true; }
+    bool begin() override { return true; }
+    bool end() override { return true; }
+    bool active() override { return true; }
 
-	void setProperty(ftl::protocol::StreamProperty opt, std::any value) override {}
+    void setProperty(ftl::protocol::StreamProperty opt, std::any value) override {}
 
-	std::any getProperty(ftl::protocol::StreamProperty opt) override { return 0; }
+    std::any getProperty(ftl::protocol::StreamProperty opt) override { return 0; }
 
-	bool supportsProperty(ftl::protocol::StreamProperty opt) override { return true; }
+    bool supportsProperty(ftl::protocol::StreamProperty opt) override { return true; }
 
     void forceSeen(FrameID id, Channel channel) {
         seen(id, channel);
@@ -46,237 +46,237 @@ class TestStream : public ftl::protocol::Stream {
 
 TEST_CASE("Muxer post, distinct framesets", "[stream]") {
 
-	std::unique_ptr<Muxer> mux = std::make_unique<Muxer>();
-	REQUIRE(mux);
-
-	SECTION("write with one stream fails") {
-		std::shared_ptr<Stream> s = std::make_shared<TestStream>();
-		REQUIRE(s);
-
-		mux->add(s);
-
-		ftl::protocol::StreamPacket tspkt = {4,0,0,1, Channel::kColour};
-
-		auto h = s->onPacket([&tspkt](const StreamPacket &spkt, const DataPacket &pkt) {
-			tspkt = spkt;
-			return true;
-		});
-
-		REQUIRE( !mux->post({4,100,0,1,ftl::protocol::Channel::kColour},{}) );
-		REQUIRE( tspkt.timestamp == 0 );
-	}
-
-	SECTION("write to previously read") {
-
-		std::shared_ptr<Stream> s1 = std::make_shared<TestStream>();
-		REQUIRE(s1);
-		std::shared_ptr<Stream> s2 = std::make_shared<TestStream>();
-		REQUIRE(s2);
-
-		mux->add(s1);
-		mux->add(s2);
-
-		ftl::protocol::StreamPacket tspkt = {4,0,0,1,Channel::kColour};
-		auto h = mux->onPacket([&tspkt](const StreamPacket &spkt, const DataPacket &pkt) {
-			tspkt = spkt;
-			return true;
-		});
-
-		REQUIRE( s1->post({4,100,0,0,Channel::kColour},{}) );
-		REQUIRE( tspkt.streamID == 0 );
-		REQUIRE( tspkt.timestamp == 100 );
-		REQUIRE( tspkt.frame_number == 0 );
-
-		REQUIRE( s2->post({4,101,0,0,Channel::kColour},{}) );
-		REQUIRE( tspkt.streamID == 1 );
-		REQUIRE( tspkt.timestamp == 101 );
-		REQUIRE( tspkt.frame_number == 0 );
-
-		StreamPacket tspkt2 = {4,0,0,1,Channel::kColour};
-		StreamPacket tspkt3 = {4,0,0,1,Channel::kColour};
-		auto h2 = s1->onPacket([&tspkt2](const StreamPacket &spkt, const DataPacket &pkt) {
-			tspkt2 = spkt;
-			return true;
-		});
-		auto h3 = s2->onPacket([&tspkt3](const StreamPacket &spkt, const DataPacket &pkt) {
-			tspkt3 = spkt;
-			return true;
-		});
-
-		REQUIRE( mux->post({4,200,1,0,Channel::kColour},{}) );
-		REQUIRE( tspkt3.timestamp == 200 );
-		REQUIRE( tspkt3.streamID == 0 );
-		REQUIRE( tspkt3.frame_number == 0 );
-	}
+    std::unique_ptr<Muxer> mux = std::make_unique<Muxer>();
+    REQUIRE(mux);
+
+    SECTION("write with one stream fails") {
+        std::shared_ptr<Stream> s = std::make_shared<TestStream>();
+        REQUIRE(s);
+
+        mux->add(s);
+
+        ftl::protocol::StreamPacket tspkt = {4,0,0,1, Channel::kColour};
+
+        auto h = s->onPacket([&tspkt](const StreamPacket &spkt, const DataPacket &pkt) {
+            tspkt = spkt;
+            return true;
+        });
+
+        REQUIRE( !mux->post({4,100,0,1,ftl::protocol::Channel::kColour},{}) );
+        REQUIRE( tspkt.timestamp == 0 );
+    }
+
+    SECTION("write to previously read") {
+
+        std::shared_ptr<Stream> s1 = std::make_shared<TestStream>();
+        REQUIRE(s1);
+        std::shared_ptr<Stream> s2 = std::make_shared<TestStream>();
+        REQUIRE(s2);
+
+        mux->add(s1);
+        mux->add(s2);
+
+        ftl::protocol::StreamPacket tspkt = {4,0,0,1,Channel::kColour};
+        auto h = mux->onPacket([&tspkt](const StreamPacket &spkt, const DataPacket &pkt) {
+            tspkt = spkt;
+            return true;
+        });
+
+        REQUIRE( s1->post({4,100,0,0,Channel::kColour},{}) );
+        REQUIRE( tspkt.streamID == 0 );
+        REQUIRE( tspkt.timestamp == 100 );
+        REQUIRE( tspkt.frame_number == 0 );
+
+        REQUIRE( s2->post({4,101,0,0,Channel::kColour},{}) );
+        REQUIRE( tspkt.streamID == 1 );
+        REQUIRE( tspkt.timestamp == 101 );
+        REQUIRE( tspkt.frame_number == 0 );
+
+        StreamPacket tspkt2 = {4,0,0,1,Channel::kColour};
+        StreamPacket tspkt3 = {4,0,0,1,Channel::kColour};
+        auto h2 = s1->onPacket([&tspkt2](const StreamPacket &spkt, const DataPacket &pkt) {
+            tspkt2 = spkt;
+            return true;
+        });
+        auto h3 = s2->onPacket([&tspkt3](const StreamPacket &spkt, const DataPacket &pkt) {
+            tspkt3 = spkt;
+            return true;
+        });
+
+        REQUIRE( mux->post({4,200,1,0,Channel::kColour},{}) );
+        REQUIRE( tspkt3.timestamp == 200 );
+        REQUIRE( tspkt3.streamID == 0 );
+        REQUIRE( tspkt3.frame_number == 0 );
+    }
 }
 
 TEST_CASE("Muxer post, single frameset", "[stream]") {
 
-	std::unique_ptr<Muxer> mux = std::make_unique<Muxer>();
-	REQUIRE(mux);
-
-	SECTION("write to previously read") {
-		std::shared_ptr<Stream> s1 = std::make_shared<TestStream>();
-		REQUIRE(s1);
-		std::shared_ptr<Stream> s2 = std::make_shared<TestStream>();
-		REQUIRE(s2);
-
-		mux->add(s1,1);
-		mux->add(s2,1);
-
-		StreamPacket tspkt = {4,0,0,1,Channel::kColour};
-		auto h = mux->onPacket([&tspkt](const StreamPacket &spkt, const DataPacket &pkt) {
-			tspkt = spkt;
-			return true;
-		});
-
-		REQUIRE( s1->post({4,100,0,0,Channel::kColour},{}) );
-		REQUIRE( tspkt.streamID == 1 );
-		REQUIRE( tspkt.frame_number == 0 );
-
-		REQUIRE( s2->post({4,101,0,0,Channel::kColour},{}) );
-		REQUIRE( tspkt.streamID == 1 );
-		REQUIRE( tspkt.frame_number == 1 );
-
-		StreamPacket tspkt2 = {4,0,4,4,Channel::kColour};
-		StreamPacket tspkt3 = {4,0,4,4,Channel::kColour};
-		auto h2 = s1->onPacket([&tspkt2](const StreamPacket &spkt, const DataPacket &pkt) {
-			tspkt2 = spkt;
-			return true;
-		});
-		auto h3 = s2->onPacket([&tspkt3](const StreamPacket &spkt, const DataPacket &pkt) {
-			tspkt3 = spkt;
-			return true;
-		});
-
-		REQUIRE( mux->post({4,200,1,1,Channel::kColour},{}) );
-		REQUIRE( tspkt2.streamID == 4 );
-		REQUIRE( tspkt2.frame_number == 4 );
-		REQUIRE( tspkt3.streamID == 0 );
-		REQUIRE( tspkt3.frame_number == 0 );
-
-		REQUIRE( mux->post({4,200,1,0,Channel::kColour},{}) );
-		REQUIRE( tspkt2.streamID == 0 );
-		REQUIRE( tspkt2.frame_number == 0 );
-	}
+    std::unique_ptr<Muxer> mux = std::make_unique<Muxer>();
+    REQUIRE(mux);
+
+    SECTION("write to previously read") {
+        std::shared_ptr<Stream> s1 = std::make_shared<TestStream>();
+        REQUIRE(s1);
+        std::shared_ptr<Stream> s2 = std::make_shared<TestStream>();
+        REQUIRE(s2);
+
+        mux->add(s1,1);
+        mux->add(s2,1);
+
+        StreamPacket tspkt = {4,0,0,1,Channel::kColour};
+        auto h = mux->onPacket([&tspkt](const StreamPacket &spkt, const DataPacket &pkt) {
+            tspkt = spkt;
+            return true;
+        });
+
+        REQUIRE( s1->post({4,100,0,0,Channel::kColour},{}) );
+        REQUIRE( tspkt.streamID == 1 );
+        REQUIRE( tspkt.frame_number == 0 );
+
+        REQUIRE( s2->post({4,101,0,0,Channel::kColour},{}) );
+        REQUIRE( tspkt.streamID == 1 );
+        REQUIRE( tspkt.frame_number == 1 );
+
+        StreamPacket tspkt2 = {4,0,4,4,Channel::kColour};
+        StreamPacket tspkt3 = {4,0,4,4,Channel::kColour};
+        auto h2 = s1->onPacket([&tspkt2](const StreamPacket &spkt, const DataPacket &pkt) {
+            tspkt2 = spkt;
+            return true;
+        });
+        auto h3 = s2->onPacket([&tspkt3](const StreamPacket &spkt, const DataPacket &pkt) {
+            tspkt3 = spkt;
+            return true;
+        });
+
+        REQUIRE( mux->post({4,200,1,1,Channel::kColour},{}) );
+        REQUIRE( tspkt2.streamID == 4 );
+        REQUIRE( tspkt2.frame_number == 4 );
+        REQUIRE( tspkt3.streamID == 0 );
+        REQUIRE( tspkt3.frame_number == 0 );
+
+        REQUIRE( mux->post({4,200,1,0,Channel::kColour},{}) );
+        REQUIRE( tspkt2.streamID == 0 );
+        REQUIRE( tspkt2.frame_number == 0 );
+    }
 }
 
 TEST_CASE("Muxer read", "[stream]") {
-	std::unique_ptr<Muxer> mux = std::make_unique<Muxer>();
-	REQUIRE(mux);
-
-	SECTION("read with two writing streams") {
-		std::shared_ptr<Stream> s1 = std::make_shared<TestStream>();
-		REQUIRE(s1);
-		std::shared_ptr<Stream> s2 = std::make_shared<TestStream>();
-		REQUIRE(s2);
-
-		mux->add(s1, 0);
-		mux->add(s2, 0);
-
-		StreamPacket tspkt = {4,0,0,1,Channel::kColour};
-		auto h = mux->onPacket([&tspkt](const StreamPacket &spkt, const DataPacket &pkt) {
-			tspkt = spkt;
-			return true;
-		});
-
-		REQUIRE( s1->post({4,100,0,0,Channel::kColour},{}) );
-		REQUIRE( tspkt.timestamp == 100 );
-		REQUIRE( tspkt.frame_number == 0 );
-
-		REQUIRE( s2->post({4,101,0,0,Channel::kColour},{}) );
-		REQUIRE( tspkt.timestamp == 101 );
-		REQUIRE( tspkt.frame_number == 1 );
-
-		REQUIRE( s1->post({4,102,0,1,Channel::kColour},{}) );
-		REQUIRE( tspkt.timestamp == 102 );
-		REQUIRE( tspkt.frame_number == 2 );
-
-		REQUIRE( s2->post({4,103,0,1,Channel::kColour},{}) );
-		REQUIRE( tspkt.timestamp == 103 );
-		REQUIRE( tspkt.frame_number == 3 );
-	}
-
-	SECTION("read consistency with two writing streams") {
-		std::shared_ptr<Stream> s1 = std::make_shared<TestStream>();
-		REQUIRE(s1);
-		std::shared_ptr<Stream> s2 = std::make_shared<TestStream>();
-		REQUIRE(s2);
-
-		mux->add(s1, 0);
-		mux->add(s2, 0);
-
-		StreamPacket tspkt = {4,0,0,1,Channel::kColour};
-		auto h = mux->onPacket([&tspkt](const StreamPacket &spkt, const DataPacket &pkt) {
-			tspkt = spkt;
-			return true;
-		});
-
-		REQUIRE( s1->post({4,100,0,0,Channel::kColour},{}) );
-		REQUIRE( tspkt.timestamp == 100 );
-		REQUIRE( tspkt.frame_number == 0 );
-
-		REQUIRE( s2->post({4,101,0,0,Channel::kColour},{}) );
-		REQUIRE( tspkt.timestamp == 101 );
-		REQUIRE( tspkt.frame_number == 1 );
-
-		REQUIRE( s1->post({4,102,0,0,Channel::kColour},{}) );
-		REQUIRE( tspkt.timestamp == 102 );
-		REQUIRE( tspkt.frame_number == 0 );
-
-		REQUIRE( s2->post({4,103,0,0,Channel::kColour},{}) );
-		REQUIRE( tspkt.timestamp == 103 );
-		REQUIRE( tspkt.frame_number == 1 );
-	}
+    std::unique_ptr<Muxer> mux = std::make_unique<Muxer>();
+    REQUIRE(mux);
+
+    SECTION("read with two writing streams") {
+        std::shared_ptr<Stream> s1 = std::make_shared<TestStream>();
+        REQUIRE(s1);
+        std::shared_ptr<Stream> s2 = std::make_shared<TestStream>();
+        REQUIRE(s2);
+
+        mux->add(s1, 0);
+        mux->add(s2, 0);
+
+        StreamPacket tspkt = {4,0,0,1,Channel::kColour};
+        auto h = mux->onPacket([&tspkt](const StreamPacket &spkt, const DataPacket &pkt) {
+            tspkt = spkt;
+            return true;
+        });
+
+        REQUIRE( s1->post({4,100,0,0,Channel::kColour},{}) );
+        REQUIRE( tspkt.timestamp == 100 );
+        REQUIRE( tspkt.frame_number == 0 );
+
+        REQUIRE( s2->post({4,101,0,0,Channel::kColour},{}) );
+        REQUIRE( tspkt.timestamp == 101 );
+        REQUIRE( tspkt.frame_number == 1 );
+
+        REQUIRE( s1->post({4,102,0,1,Channel::kColour},{}) );
+        REQUIRE( tspkt.timestamp == 102 );
+        REQUIRE( tspkt.frame_number == 2 );
+
+        REQUIRE( s2->post({4,103,0,1,Channel::kColour},{}) );
+        REQUIRE( tspkt.timestamp == 103 );
+        REQUIRE( tspkt.frame_number == 3 );
+    }
+
+    SECTION("read consistency with two writing streams") {
+        std::shared_ptr<Stream> s1 = std::make_shared<TestStream>();
+        REQUIRE(s1);
+        std::shared_ptr<Stream> s2 = std::make_shared<TestStream>();
+        REQUIRE(s2);
+
+        mux->add(s1, 0);
+        mux->add(s2, 0);
+
+        StreamPacket tspkt = {4,0,0,1,Channel::kColour};
+        auto h = mux->onPacket([&tspkt](const StreamPacket &spkt, const DataPacket &pkt) {
+            tspkt = spkt;
+            return true;
+        });
+
+        REQUIRE( s1->post({4,100,0,0,Channel::kColour},{}) );
+        REQUIRE( tspkt.timestamp == 100 );
+        REQUIRE( tspkt.frame_number == 0 );
+
+        REQUIRE( s2->post({4,101,0,0,Channel::kColour},{}) );
+        REQUIRE( tspkt.timestamp == 101 );
+        REQUIRE( tspkt.frame_number == 1 );
+
+        REQUIRE( s1->post({4,102,0,0,Channel::kColour},{}) );
+        REQUIRE( tspkt.timestamp == 102 );
+        REQUIRE( tspkt.frame_number == 0 );
+
+        REQUIRE( s2->post({4,103,0,0,Channel::kColour},{}) );
+        REQUIRE( tspkt.timestamp == 103 );
+        REQUIRE( tspkt.frame_number == 1 );
+    }
 }
 
 TEST_CASE("Muxer read multi-frameset", "[stream]") {
-	std::unique_ptr<Muxer> mux = std::make_unique<Muxer>();
-	REQUIRE(mux);
-
-	//SECTION("read with two writing streams") {
-
-		std::shared_ptr<Stream> s1 = std::make_shared<TestStream>();
-		REQUIRE(s1);
-		std::shared_ptr<Stream> s2 = std::make_shared<TestStream>();
-		REQUIRE(s2);
-		std::shared_ptr<Stream> s3 = std::make_shared<TestStream>();
-		REQUIRE(s3);
-		std::shared_ptr<Stream> s4 = std::make_shared<TestStream>();
-		REQUIRE(s4);
-
-		mux->add(s1,0);
-		mux->add(s2,1);
-		mux->add(s3,0);
-		mux->add(s4,1);
-
-		StreamPacket tspkt = {4,0,0,1,Channel::kColour};
-		auto h = mux->onPacket([&tspkt](const StreamPacket &spkt, const DataPacket &pkt) {
-			tspkt = spkt;
-			return true;
-		});
-
-		REQUIRE( s1->post({4,100,0,0,Channel::kColour},{}) );
-		REQUIRE( tspkt.streamID == 0 );
-		REQUIRE( tspkt.frame_number == 0 );
-
-		REQUIRE( s2->post({4,101,0,0,Channel::kColour},{}) );
-		REQUIRE( tspkt.streamID == 1 );
-		REQUIRE( tspkt.frame_number == 0 );
-
-		REQUIRE( s3->post({4,102,0,0,Channel::kColour},{}) );
-		REQUIRE( tspkt.streamID == 0 );
-		REQUIRE( tspkt.frame_number == 1 );
-
-		REQUIRE( s4->post({4,103,0,0,Channel::kColour},{}) );
-		REQUIRE( tspkt.streamID == 1 );
-		REQUIRE( tspkt.frame_number == 1 );
-	//}
+    std::unique_ptr<Muxer> mux = std::make_unique<Muxer>();
+    REQUIRE(mux);
+
+    //SECTION("read with two writing streams") {
+
+        std::shared_ptr<Stream> s1 = std::make_shared<TestStream>();
+        REQUIRE(s1);
+        std::shared_ptr<Stream> s2 = std::make_shared<TestStream>();
+        REQUIRE(s2);
+        std::shared_ptr<Stream> s3 = std::make_shared<TestStream>();
+        REQUIRE(s3);
+        std::shared_ptr<Stream> s4 = std::make_shared<TestStream>();
+        REQUIRE(s4);
+
+        mux->add(s1,0);
+        mux->add(s2,1);
+        mux->add(s3,0);
+        mux->add(s4,1);
+
+        StreamPacket tspkt = {4,0,0,1,Channel::kColour};
+        auto h = mux->onPacket([&tspkt](const StreamPacket &spkt, const DataPacket &pkt) {
+            tspkt = spkt;
+            return true;
+        });
+
+        REQUIRE( s1->post({4,100,0,0,Channel::kColour},{}) );
+        REQUIRE( tspkt.streamID == 0 );
+        REQUIRE( tspkt.frame_number == 0 );
+
+        REQUIRE( s2->post({4,101,0,0,Channel::kColour},{}) );
+        REQUIRE( tspkt.streamID == 1 );
+        REQUIRE( tspkt.frame_number == 0 );
+
+        REQUIRE( s3->post({4,102,0,0,Channel::kColour},{}) );
+        REQUIRE( tspkt.streamID == 0 );
+        REQUIRE( tspkt.frame_number == 1 );
+
+        REQUIRE( s4->post({4,103,0,0,Channel::kColour},{}) );
+        REQUIRE( tspkt.streamID == 1 );
+        REQUIRE( tspkt.frame_number == 1 );
+    //}
 }
 
 TEST_CASE("Muxer enable", "[stream]") {
-	std::unique_ptr<Muxer> mux = std::make_unique<Muxer>();
-	REQUIRE(mux);
+    std::unique_ptr<Muxer> mux = std::make_unique<Muxer>();
+    REQUIRE(mux);
 
     std::shared_ptr<TestStream> s1 = std::make_shared<TestStream>();
     REQUIRE(s1);
@@ -284,7 +284,7 @@ TEST_CASE("Muxer enable", "[stream]") {
     REQUIRE(s2);
 
     mux->add(s1);
-	mux->add(s2);
+    mux->add(s2);
 
     SECTION("enable frame id") {
         FrameID id1(0, 1);
@@ -350,9 +350,54 @@ TEST_CASE("Muxer enable", "[stream]") {
     }
 }
 
+TEST_CASE("Muxer disable", "[stream]") {
+    std::unique_ptr<Muxer> mux = std::make_unique<Muxer>();
+    REQUIRE(mux);
+
+    std::shared_ptr<TestStream> s1 = std::make_shared<TestStream>();
+    REQUIRE(s1);
+    std::shared_ptr<TestStream> s2 = std::make_shared<TestStream>();
+    REQUIRE(s2);
+
+    mux->add(s1);
+    mux->add(s2);
+
+    SECTION("disable frame id") {
+        FrameID id1(0, 1);
+        s1->forceSeen(id1, Channel::kColour);
+        FrameID id2(1, 1);
+        s2->forceSeen(id2, Channel::kColour);
+
+        REQUIRE( mux->enable(id1) );
+        REQUIRE( mux->enable(id2) );
+        REQUIRE(s1->enabled(id1));
+        REQUIRE(s2->enabled(id2));
+        mux->disable(id1);
+        REQUIRE(!s1->enabled(id1));
+        REQUIRE(s2->enabled(id2));
+        mux->disable(id2);
+        REQUIRE(!s1->enabled(id1));
+        REQUIRE(!s2->enabled(id2));
+    }
+
+    SECTION("disable frame channel") {
+        FrameID id1(0, 1);
+        s1->forceSeen(id1, Channel::kColour);
+        s1->forceSeen(id1, Channel::kDepth);
+
+        REQUIRE( mux->enable(id1, Channel::kColour) );
+        REQUIRE( mux->enable(id1, Channel::kDepth) );
+        REQUIRE(s1->enabled(id1, Channel::kColour));
+        REQUIRE(s1->enabled(id1, Channel::kDepth));
+        mux->disable(id1, Channel::kColour);
+        REQUIRE(!s1->enabled(id1, Channel::kColour));
+        REQUIRE(s1->enabled(id1, Channel::kDepth));
+    }
+}
+
 TEST_CASE("Muxer available", "[stream]") {
-	std::unique_ptr<Muxer> mux = std::make_unique<Muxer>();
-	REQUIRE(mux);
+    std::unique_ptr<Muxer> mux = std::make_unique<Muxer>();
+    REQUIRE(mux);
 
     std::shared_ptr<TestStream> s1 = std::make_shared<TestStream>();
     REQUIRE(s1);
@@ -360,7 +405,7 @@ TEST_CASE("Muxer available", "[stream]") {
     REQUIRE(s2);
 
     mux->add(s1);
-	mux->add(s2);
+    mux->add(s2);
 
     SECTION("available id when seen") {
         FrameID id1(0, 1);
@@ -420,8 +465,8 @@ TEST_CASE("Muxer available", "[stream]") {
 }
 
 TEST_CASE("Muxer onAvailable", "[stream]") {
-	std::unique_ptr<Muxer> mux = std::make_unique<Muxer>();
-	REQUIRE(mux);
+    std::unique_ptr<Muxer> mux = std::make_unique<Muxer>();
+    REQUIRE(mux);
 
     std::shared_ptr<TestStream> s1 = std::make_shared<TestStream>();
     REQUIRE(s1);
@@ -429,7 +474,7 @@ TEST_CASE("Muxer onAvailable", "[stream]") {
     REQUIRE(s2);
 
     mux->add(s1);
-	mux->add(s2);
+    mux->add(s2);
 
     SECTION("available event when seen") {
         FrameID id1(0, 1);
@@ -455,8 +500,8 @@ TEST_CASE("Muxer onAvailable", "[stream]") {
 }
 
 TEST_CASE("Muxer frames", "[stream]") {
-	std::unique_ptr<Muxer> mux = std::make_unique<Muxer>();
-	REQUIRE(mux);
+    std::unique_ptr<Muxer> mux = std::make_unique<Muxer>();
+    REQUIRE(mux);
 
     std::shared_ptr<TestStream> s1 = std::make_shared<TestStream>();
     REQUIRE(s1);
@@ -465,7 +510,7 @@ TEST_CASE("Muxer frames", "[stream]") {
 
     SECTION("unique framesets list correct") {
         mux->add(s1);
-	    mux->add(s2);
+        mux->add(s2);
 
         FrameID id1(0, 1);
         FrameID id2(1, 1);
@@ -489,7 +534,7 @@ TEST_CASE("Muxer frames", "[stream]") {
 
     SECTION("merged framesets list correct") {
         mux->add(s1, 1);
-	    mux->add(s2, 1);
+        mux->add(s2, 1);
 
         FrameID id1(0, 0);
         FrameID id2(0, 1);
@@ -511,8 +556,8 @@ TEST_CASE("Muxer frames", "[stream]") {
 }
 
 TEST_CASE("Muxer channels", "[stream]") {
-	std::unique_ptr<Muxer> mux = std::make_unique<Muxer>();
-	REQUIRE(mux);
+    std::unique_ptr<Muxer> mux = std::make_unique<Muxer>();
+    REQUIRE(mux);
 
     std::shared_ptr<TestStream> s1 = std::make_shared<TestStream>();
     REQUIRE(s1);
@@ -520,7 +565,7 @@ TEST_CASE("Muxer channels", "[stream]") {
     REQUIRE(s2);
 
     mux->add(s1);
-	mux->add(s2);
+    mux->add(s2);
 
     SECTION("correct channels for valid frame") {
         FrameID id1(0, 1);
@@ -542,8 +587,8 @@ TEST_CASE("Muxer channels", "[stream]") {
 }
 
 TEST_CASE("Muxer enabledChannels", "[stream]") {
-	std::unique_ptr<Muxer> mux = std::make_unique<Muxer>();
-	REQUIRE(mux);
+    std::unique_ptr<Muxer> mux = std::make_unique<Muxer>();
+    REQUIRE(mux);
 
     std::shared_ptr<TestStream> s1 = std::make_shared<TestStream>();
     REQUIRE(s1);
@@ -551,7 +596,7 @@ TEST_CASE("Muxer enabledChannels", "[stream]") {
     REQUIRE(s2);
 
     mux->add(s1);
-	mux->add(s2);
+    mux->add(s2);
 
     SECTION("correct channels for valid frame") {
         FrameID id1(0, 1);
@@ -576,8 +621,8 @@ TEST_CASE("Muxer enabledChannels", "[stream]") {
 }
 
 TEST_CASE("Muxer onError", "[stream]") {
-	std::unique_ptr<Muxer> mux = std::make_unique<Muxer>();
-	REQUIRE(mux);
+    std::unique_ptr<Muxer> mux = std::make_unique<Muxer>();
+    REQUIRE(mux);
 
     std::shared_ptr<TestStream> s1 = std::make_shared<TestStream>();
     REQUIRE(s1);
@@ -585,7 +630,7 @@ TEST_CASE("Muxer onError", "[stream]") {
     REQUIRE(s2);
 
     mux->add(s1);
-	mux->add(s2);
+    mux->add(s2);
 
     ftl::protocol::Error seenErr = ftl::protocol::Error::kNoError;
     auto h = mux->onError([&seenErr](ftl::protocol::Error err, const std::string &str) {
-- 
GitLab