The HTTP Streamer can be enabled via the _streamer_enabled_ daemon parameter. When the Streamer is active the daemon starts capturing the configured _Sinks_ up to the maximum number of channels configured by the _streamer_channels_ parameters. The captured PCM samples are split into _streamer_files_num_ files of _streamer_file_duration_ duration (in seconds) for each sink, compressed using AAC LC codec and served via HTTP. The HTTP streamer requires the libfaac-dev package to compile. Please note that since the HTTP Streamer uses the RAVENNA ALSA device for capturing it's not possible to use such device for other audio captures.
268 lines
8.2 KiB
C++
268 lines
8.2 KiB
C++
//
|
|
// session_manager.hpp
|
|
//
|
|
// Copyright (c) 2019 2020 Andrea Bondavalli. All rights reserved.
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
//
|
|
|
|
#ifndef _SESSION_MANAGER_HPP_
|
|
#define _SESSION_MANAGER_HPP_
|
|
|
|
#include <future>
|
|
#include <list>
|
|
#include <map>
|
|
#include <shared_mutex>
|
|
#include <thread>
|
|
#include <chrono>
|
|
|
|
#include "config.hpp"
|
|
#include "driver_interface.hpp"
|
|
#include "browser.hpp"
|
|
#include "igmp.hpp"
|
|
#include "sap.hpp"
|
|
|
|
struct StreamSource {
|
|
uint8_t id{0};
|
|
bool enabled{false};
|
|
std::string name;
|
|
std::string io;
|
|
uint32_t max_samples_per_packet{0};
|
|
std::string codec;
|
|
std::string address;
|
|
uint8_t ttl{0};
|
|
uint8_t payload_type{0};
|
|
uint8_t dscp{0};
|
|
bool refclk_ptp_traceable{false};
|
|
std::vector<uint8_t> map;
|
|
};
|
|
|
|
struct StreamSink {
|
|
uint8_t id;
|
|
std::string name;
|
|
std::string io;
|
|
bool use_sdp{false};
|
|
std::string source;
|
|
std::string sdp;
|
|
uint32_t delay{0};
|
|
bool ignore_refclk_gmid{false};
|
|
std::vector<uint8_t> map;
|
|
};
|
|
|
|
struct SinkStreamStatus {
|
|
bool is_rtp_seq_id_error{false};
|
|
bool is_rtp_ssrc_error{false};
|
|
bool is_rtp_payload_type_error{false};
|
|
bool is_rtp_sac_error{false};
|
|
bool is_receiving_rtp_packet{false};
|
|
bool is_muted{false};
|
|
bool is_some_muted{false};
|
|
bool is_all_muted{false};
|
|
int min_time{0};
|
|
};
|
|
|
|
struct PTPConfig {
|
|
uint8_t domain{0};
|
|
uint8_t dscp{0};
|
|
};
|
|
|
|
struct PTPStatus {
|
|
std::string status;
|
|
std::string gmid;
|
|
int32_t jitter{0};
|
|
};
|
|
|
|
struct StreamInfo {
|
|
TRTP_stream_info stream;
|
|
uint64_t handle{0};
|
|
bool enabled{false};
|
|
bool refclk_ptp_traceable{false};
|
|
bool ignore_refclk_gmid{false};
|
|
std::string io;
|
|
bool sink_use_sdp{true};
|
|
std::string sink_source;
|
|
std::string sink_sdp;
|
|
uint32_t session_id{0};
|
|
uint32_t session_version{0};
|
|
SDPOrigin origin;
|
|
};
|
|
|
|
class SessionManager {
|
|
public:
|
|
constexpr static uint8_t stream_id_max = 63;
|
|
|
|
static std::shared_ptr<SessionManager> create(
|
|
std::shared_ptr<DriverManager> driver,
|
|
std::shared_ptr<Browser> browser,
|
|
std::shared_ptr<Config> config);
|
|
SessionManager() = delete;
|
|
SessionManager(const SessionManager&) = delete;
|
|
SessionManager& operator=(const SessionManager&) = delete;
|
|
virtual ~SessionManager() = default;
|
|
|
|
// session manager interface
|
|
bool init() {
|
|
if (!running_) {
|
|
running_ = true;
|
|
g_session_version = std::chrono::system_clock::now().time_since_epoch() /
|
|
std::chrono::seconds(1);
|
|
// to have an increasing session versions between restarts
|
|
res_ = std::async(std::launch::async, &SessionManager::worker, this);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool terminate() {
|
|
if (running_) {
|
|
running_ = false;
|
|
auto ret = res_.get();
|
|
for (const auto& source : get_sources()) {
|
|
remove_source(source.id);
|
|
}
|
|
for (const auto& sink : get_sinks()) {
|
|
remove_sink(sink.id);
|
|
}
|
|
return ret;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
std::error_code add_source(const StreamSource& source);
|
|
std::error_code get_source(uint8_t id, StreamSource& source) const;
|
|
std::list<StreamSource> get_sources() const;
|
|
std::error_code get_source_sdp(uint32_t id, std::string& sdp) const;
|
|
std::error_code remove_source(uint32_t id);
|
|
uint8_t get_source_id(const std::string& name) const;
|
|
|
|
enum class SourceObserverType { add_source, remove_source, update_source };
|
|
using SourceObserver = std::function<
|
|
bool(uint8_t id, const std::string& name, const std::string& sdp)>;
|
|
void add_source_observer(SourceObserverType type, const SourceObserver& cb);
|
|
|
|
enum class SinkObserverType { add_sink, remove_sink };
|
|
using SinkObserver = std::function<
|
|
bool(uint8_t id, const std::string& name)>;
|
|
void add_sink_observer(SinkObserverType type, const SinkObserver& cb);
|
|
|
|
using PtpStatusObserver = std::function<bool(const std::string& status)>;
|
|
void add_ptp_status_observer(const PtpStatusObserver& cb);
|
|
|
|
std::error_code add_sink(const StreamSink& sink);
|
|
std::error_code get_sink(uint8_t id, StreamSink& sink) const;
|
|
std::list<StreamSink> get_sinks() const;
|
|
std::error_code get_sink_status(uint32_t id, SinkStreamStatus& status) const;
|
|
std::error_code remove_sink(uint32_t id);
|
|
uint8_t get_sink_id(const std::string& name) const;
|
|
|
|
std::error_code set_ptp_config(const PTPConfig& config);
|
|
std::error_code set_driver_config(std::string_view name,
|
|
uint32_t value) const;
|
|
void get_ptp_config(PTPConfig& config) const;
|
|
void get_ptp_status(PTPStatus& status) const;
|
|
|
|
bool load_status();
|
|
bool save_status() const;
|
|
|
|
size_t process_sap();
|
|
|
|
protected:
|
|
constexpr static const char ptp_primary_mcast_addr[] = "224.0.1.129";
|
|
constexpr static const char ptp_pdelay_mcast_addr[] = "224.0.1.107";
|
|
|
|
std::list<StreamSink> get_updated_sinks(
|
|
const std::list<RemoteSource>& sources_list);
|
|
void update_sinks();
|
|
|
|
void on_add_source(const StreamSource& source, const StreamInfo& info);
|
|
void on_remove_source(const StreamInfo& info);
|
|
|
|
void on_add_sink(const StreamSink& sink, const StreamInfo& info);
|
|
void on_remove_sink(const StreamInfo& info);
|
|
|
|
void on_ptp_status_changed(const std::string& status) const;
|
|
|
|
void on_update_sources();
|
|
|
|
std::string get_removed_source_sdp_(uint32_t id,
|
|
uint32_t src_addr,
|
|
uint32_t session_id,
|
|
uint32_t session_version) const;
|
|
std::string get_source_sdp_(uint32_t id, const StreamInfo& info) const;
|
|
StreamSource get_source_(uint8_t id, const StreamInfo& info) const;
|
|
StreamSink get_sink_(uint8_t id, const StreamInfo& info) const;
|
|
|
|
bool sink_is_still_valid(const std::string sdp,
|
|
const std::list<RemoteSource> sources_list) const;
|
|
|
|
bool parse_sdp(const std::string& sdp, StreamInfo& info) const;
|
|
bool worker();
|
|
// singleton, use create() to build
|
|
explicit SessionManager(std::shared_ptr<DriverManager> driver,
|
|
std::shared_ptr<Browser> browser,
|
|
std::shared_ptr<Config> config)
|
|
: browser_(browser), driver_(driver), config_(config) {
|
|
ptp_config_.domain = config->get_ptp_domain();
|
|
ptp_config_.dscp = config->get_ptp_dscp();
|
|
};
|
|
|
|
private:
|
|
std::shared_ptr<Browser> browser_;
|
|
std::shared_ptr<DriverManager> driver_;
|
|
std::shared_ptr<Config> config_;
|
|
std::future<bool> res_;
|
|
std::atomic_bool running_{false};
|
|
|
|
/* current sources */
|
|
std::map<uint8_t /* id */, StreamInfo> sources_;
|
|
std::map<std::string, uint8_t /* id */> source_names_;
|
|
mutable std::shared_mutex sources_mutex_;
|
|
|
|
/* current sinks */
|
|
std::map<uint8_t /* id */, StreamInfo> sinks_;
|
|
std::map<std::string, uint8_t /* id */> sink_names_;
|
|
mutable std::shared_mutex sinks_mutex_;
|
|
|
|
/* current announced sources */
|
|
std::map<uint32_t /* msg_id_hash */,
|
|
std::tuple<uint32_t /* src_addr */,
|
|
uint32_t /* session_id */,
|
|
uint32_t /* session_version */> >
|
|
announced_sources_;
|
|
|
|
/* number of deletions sent for a a deleted source */
|
|
std::unordered_map<uint32_t /* msg_id_hash */, int /* count */>
|
|
deleted_sources_count_;
|
|
|
|
PTPConfig ptp_config_;
|
|
PTPStatus ptp_status_;
|
|
mutable std::shared_mutex ptp_mutex_;
|
|
|
|
std::list<SourceObserver> add_source_observers_;
|
|
std::list<SourceObserver> remove_source_observers_;
|
|
std::list<SourceObserver> update_source_observers_;
|
|
std::list<PtpStatusObserver> ptp_status_observers_;
|
|
std::list<SinkObserver> add_sink_observers_;
|
|
std::list<SinkObserver> remove_sink_observers_;
|
|
std::list<SinkObserver> update_sink_observers_;
|
|
|
|
SAP sap_{config_->get_sap_mcast_addr()};
|
|
IGMP igmp_;
|
|
uint32_t last_sink_update_{0};
|
|
|
|
/* used to handle session versioning */
|
|
inline static std::atomic<uint32_t> g_session_version{0};
|
|
};
|
|
|
|
#endif
|