Skip to content
Snippets Groups Projects
streams.hpp 5.49 KiB
Newer Older
Nicolas Pope's avatar
Nicolas Pope committed
/**
 * @file streams.hpp
 * @copyright Copyright (c) 2022 University of Turku, MIT License
 * @author Nicolas Pope
 */

#pragma once

#include <ftl/handle.hpp>
#include <ftl/threads.hpp>
#include <ftl/protocol/channels.hpp>
#include <ftl/protocol/channelSet.hpp>
#include <ftl/protocol/packet.hpp>
#include <ftl/protocol/frameid.hpp>
Nicolas Pope's avatar
Nicolas Pope committed
#include <string>
#include <vector>
#include <unordered_set>

namespace ftl {
namespace protocol {

/* Represents a request for data through a stream */
struct Request {
	FrameID id;
Nicolas Pope's avatar
Nicolas Pope committed
	ftl::protocol::Channel channel;
	int bitrate;
	int count;
	ftl::protocol::Codec codec;
};

using RequestCallback = std::function<bool(const ftl::protocol::Request&)>;

using StreamCallback = std::function<bool(const ftl::protocol::StreamPacket &, const ftl::protocol::Packet &)>;

enum struct StreamProperty {
Nicolas Pope's avatar
Nicolas Pope committed
    kInvalid = 0,
    kLooping,
    kSpeed,
    kBitrate,
	kMaxBitrate,
    kAdaptiveBitrate,
	kObservers,
	kURI
Nicolas Pope's avatar
Nicolas Pope committed
};

enum struct StreamType {
    kMixed,
    kUnknown,
    kLive,
    kRecorded
};

/**
 * Base stream class to be implemented. Provides encode and decode functionality
 * around a generic packet read and write mechanism. Some specialisations will
 * provide and automatically handle control signals.
 *
 * Streams are bidirectional, frames can be both received and written.
 */
class Stream {
	public:
	virtual ~Stream() {};

	virtual std::string name() const;

Nicolas Pope's avatar
Nicolas Pope committed
	/**
	 * Obtain all packets for next frame. The provided callback function is
	 * called once for every packet. This function might continue to call the
	 * callback even after the read function returns, for example with a
	 * NetStream.
	 */
	ftl::Handle onPacket(const StreamCallback &cb) { return cb_.on(cb); }

	ftl::Handle onRequest(const std::function<bool(const Request &)> &cb) { return request_cb_.on(cb); }
Nicolas Pope's avatar
Nicolas Pope committed

	virtual bool post(const ftl::protocol::StreamPacket &, const ftl::protocol::Packet &)=0;

    // TODO: Add methods for: pause, paused, statistics
Nicolas Pope's avatar
Nicolas Pope committed

	/**
	 * Start the stream. Calls to the onPacket callback will only occur after
	 * a call to this function (and before a call to end()).
	 */
	virtual bool begin()=0;

	virtual bool end()=0;

	/**
	 * Is the stream active? Generally true if begin() has been called, false
	 * initially and after end(). However, it may go false for other reasons.
	 * If false, no calls to onPacket will occur and posts will be ignored.
	 */
	virtual bool active()=0;

	/**
	 * @brief Clear all state. This will remove all information about available
	 * and enabled frames or channels. You will then need to enable frames and
	 * channels again. If active the stream will remain active.
	 * 
Nicolas Pope's avatar
Nicolas Pope committed
	 */
	virtual void reset();

	/**
	 * @brief Re-request all channels and state. This will also cause video encoding
	 * to generate new I-frames as if a new connection is made. All persistent data
	 * channels would also become available. For file streams this would reset the
	 * stream to the beginning of the file.
	 * 
	 */
	virtual void refresh();

	/**
	 * @brief Check if a frame is available.
	 * 
	 * @param id Frameset and frame number
	 * @return true if data is available for the frame
	 * @return false if no data has been seen
Nicolas Pope's avatar
Nicolas Pope committed
	 */
	bool available(FrameID id) const;

	bool available(FrameID id, ftl::protocol::Channel channel) const;

	bool available(FrameID id, const ftl::protocol::ChannelSet channels) const;

	ftl::Handle onAvailable(const std::function<bool(FrameID, ftl::protocol::Channel)> &cb) { return avail_cb_.on(cb); }
Nicolas Pope's avatar
Nicolas Pope committed

	/**
	 * @brief Get all channels seen for a frame. If the frame does not exist then
	 * an empty set is returned.
	 * 
	 * @param id Frameset and frame number
	 * @return Set of all seen channels, or empty. 
Nicolas Pope's avatar
Nicolas Pope committed
	 */
	ftl::protocol::ChannelSet channels(FrameID id) const;
Nicolas Pope's avatar
Nicolas Pope committed

	ftl::protocol::ChannelSet enabledChannels(FrameID id) const;
Nicolas Pope's avatar
Nicolas Pope committed

	/**
	 * @brief Get all available frames in the stream.
	 * 
	 * @return Set of frame IDs
Nicolas Pope's avatar
Nicolas Pope committed
	 */
	std::unordered_set<FrameID> frames() const;

	/**
	 * @brief Get all enabled frames in the stream.
	 * 
	 * @return Set of frame IDs
	 */
	std::unordered_set<FrameID> enabled() const;

	/**
	 * @brief Check if a frame is enabled.
	 * 
	 * @param id Frameset and frame number
	 * @return true if data for this frame is enabled.
	 * @return false if data not enabled or frame does not exist.
	 */
	bool enabled(FrameID id) const;

	bool enabled(FrameID id, ftl::protocol::Channel channel) const;
Nicolas Pope's avatar
Nicolas Pope committed

	/**
	 * Number of framesets in stream.
	 */
	inline size_t size() const { return state_.size(); }

	virtual bool enable(FrameID id);

	virtual bool enable(FrameID id, ftl::protocol::Channel channel);

	virtual bool enable(FrameID id, const ftl::protocol::ChannelSet &channels);

	// TODO: Disable

	virtual void setProperty(ftl::protocol::StreamProperty opt, int value)=0;

	virtual int getProperty(ftl::protocol::StreamProperty opt)=0;

	virtual bool supportsProperty(ftl::protocol::StreamProperty opt)=0;

	virtual StreamType type() const { return StreamType::kUnknown; }
Nicolas Pope's avatar
Nicolas Pope committed
	protected:
	void trigger(const ftl::protocol::StreamPacket &spkt, const ftl::protocol::Packet &pkt);
Nicolas Pope's avatar
Nicolas Pope committed

	void seen(FrameID id, ftl::protocol::Channel channel);

	void request(const Request &req);

	mutable SHARED_MUTEX mtx_;
Nicolas Pope's avatar
Nicolas Pope committed

	private:
	struct FSState {
		bool enabled = false;
Nicolas Pope's avatar
Nicolas Pope committed
		ftl::protocol::ChannelSet selected;
		ftl::protocol::ChannelSet available;
		// TODO: Add a name and metadata
Nicolas Pope's avatar
Nicolas Pope committed
	};

	ftl::Handler<const ftl::protocol::StreamPacket&, const ftl::protocol::Packet&> cb_;
	ftl::Handler<const Request &> request_cb_;
	ftl::Handler<FrameID, ftl::protocol::Channel> avail_cb_;
	std::unordered_map<int, FSState> state_;