// // browser.cpp // // 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 . // #include "browser.hpp" using namespace std::chrono; using second_t = duration >; std::shared_ptr Browser::create( std::shared_ptr config) { // no need to be thread-safe here static std::weak_ptr instance; if (auto ptr = instance.lock()) { return ptr; } auto ptr = std::shared_ptr(new Browser(config)); instance = ptr; return ptr; } std::list Browser::get_remote_sources() const { std::list sources_list; std::shared_lock sources_lock(sources_mutex_); // return list of remote sources ordered by name for (const auto& source: sources_.get()) { sources_list.push_back(source); } return sources_list; } static std::string sdp_get_subject(const std::string& sdp) { std::stringstream ssstrem(sdp); std::string line; while (getline(ssstrem, line, '\n')) { if (line.substr(0, 2) == "s=") { return line.substr(2); } } return ""; } bool Browser::worker() { sap_.set_multicast_interface(config_->get_ip_addr_str()); // Join SAP muticast address igmp_.join(config_->get_ip_addr_str(), config_->get_sap_mcast_addr()); auto sap_timepoint = steady_clock::now(); int sap_interval = 10; auto mdns_timepoint = steady_clock::now(); int mdns_interval = 10; while (running_) { bool is_announce; uint16_t msg_id_hash; uint32_t addr; std::string sdp; if (sap_.receive(is_announce, msg_id_hash, addr, sdp)) { std::stringstream ss; ss << "sap:" << std::hex << addr << msg_id_hash; std::string id(ss.str()); BOOST_LOG_TRIVIAL(debug) << "browser:: received SAP message for " << id; std::unique_lock sources_lock(sources_mutex_); auto it = sources_.get().find(id); if (it == sources_.end()) { // Source is not in the map if (is_announce) { BOOST_LOG_TRIVIAL(info) << "browser:: adding SAP source " << id; // annoucement, add new source RemoteSource source; source.id = id; source.sdp = sdp; source.source = "SAP"; source.address = ip::address_v4(ntohl(addr)).to_string(); source.name = sdp_get_subject(sdp); source.last_seen = duration_cast(steady_clock::now() - startup_).count(); source.announce_period = 360; //default period sources_.insert(source); } } else { // Source is already in the map if (is_announce) { BOOST_LOG_TRIVIAL(debug) << "browser:: refreshing SAP source " << it->id; // annoucement, update last seen and announce period auto upd_source{*it}; uint32_t last_seen = duration_cast(steady_clock::now() - startup_).count(); upd_source.announce_period = last_seen - upd_source.last_seen; upd_source.last_seen = last_seen; sources_.replace(it, upd_source); } else { BOOST_LOG_TRIVIAL(info) << "browser:: removing SAP source " << it->id; // deletion, remove entry sources_.erase(it); } } } // check if it's time to update the SAP remote sources if ((duration_cast(steady_clock::now() - sap_timepoint).count()) > sap_interval) { sap_timepoint = steady_clock::now(); // remove all sessions no longer announced auto offset = duration_cast(steady_clock::now() - startup_).count(); std::unique_lock sources_lock(sources_mutex_); for (auto it = sources_.begin(); it != sources_.end();) { if (it->source == "SAP" && (offset - it->last_seen) > (it->announce_period * 10)) { // remove from remote SAP sources BOOST_LOG_TRIVIAL(info) << "browser:: SAP source " << it->id << " timeout"; it = sources_.erase(it); } else { it++; } } } // check if it's time to process the mDNS RTSP sources if ((duration_cast(steady_clock::now() - mdns_timepoint).count()) > mdns_interval) { mdns_timepoint = steady_clock::now(); process_results(); } } return true; } void Browser::on_new_rtsp_source(const std::string& name, const std::string& domain, const RTSPSSource& s) { uint32_t last_seen = duration_cast(steady_clock::now() - startup_).count(); std::unique_lock sources_lock(sources_mutex_); if (sources_.get().find(s.id) == sources_.end()) { BOOST_LOG_TRIVIAL(info) << "browser:: adding RTSP source " << s.id; sources_.insert({ s.id, s.source, s.address, name, s.sdp, last_seen, 0 }); } } void Browser::on_remove_rtsp_source(const std::string& name, const std::string& domain) { std::unique_lock sources_lock(sources_mutex_); auto& name_idx = sources_.get(); for (auto it = name_idx.find(name); it != name_idx.end(); it++) { if (it->source == "mDNS") { BOOST_LOG_TRIVIAL(info) << "browser:: removing RTSP source " << it->id; name_idx.erase(it); break; } } } bool Browser::init() { if (!running_) { /* init mDNS client */ if (config_->get_mdns_enabled() && !MDNSClient::init()) { return false; } running_ = true; res_ = std::async(std::launch::async, &Browser::worker, this); } return true; } bool Browser::terminate() { if (running_) { running_ = false; /* wait for worker to exit */ res_.get(); /* terminate mDNS client */ if (config_->get_mdns_enabled()) { MDNSClient::terminate(); } } return true; }