diff options
author | Matthias P. Braendli <matthias.braendli@mpb.li> | 2018-02-20 19:00:22 +0100 |
---|---|---|
committer | Matthias P. Braendli <matthias.braendli@mpb.li> | 2018-02-20 19:00:22 +0100 |
commit | e4396c73c894c4730d4d28b3caefc26ce41cdd4a (patch) | |
tree | bde5a32583d181ea026d52ea7f0d020eeb257519 | |
parent | b09c89b9edda0abe2ab69df5c0d0723db43ad945 (diff) | |
download | dabmux-e4396c73c894c4730d4d28b3caefc26ce41cdd4a.tar.gz dabmux-e4396c73c894c4730d4d28b3caefc26ce41cdd4a.tar.bz2 dabmux-e4396c73c894c4730d4d28b3caefc26ce41cdd4a.zip |
Rework other_ensemble to support OE=1 in FIG0/24
-rw-r--r-- | doc/servicelinking.mux | 28 | ||||
-rw-r--r-- | src/ConfigParser.cpp | 147 | ||||
-rw-r--r-- | src/MuxElements.h | 12 | ||||
-rw-r--r-- | src/fig/FIG0_24.cpp | 65 | ||||
-rw-r--r-- | src/fig/FIG0_24.h | 4 |
5 files changed, 182 insertions, 74 deletions
diff --git a/doc/servicelinking.mux b/doc/servicelinking.mux index bdfbd90..438cb4e 100644 --- a/doc/servicelinking.mux +++ b/doc/servicelinking.mux @@ -177,15 +177,37 @@ services { srv-fu { id 0x8daa label "Funk" + } + srv-ri { + id 0x8dab + label "Rick" + } +} + +; We can announce the presence of a service in another ensemble using FIG0/24, +; both for services we carry in this ensemble (OE=0) and for services that +; only exist in another ensemble (OE=1) +other-services { + ; you can freely chose the unique id + srv-fu { + ; If this ensemble contains a service with this id, OE will be set to 0. + ; Otherwise, OE=1 + id 0x8daa ; If this service is present in other ensembles, it can be announced ; through FIG0/24. other_ensembles is a comma separated list of ; ensemble IDs (decimal or hexadecimal with 0x prefix) other_ensembles "0x4ffe,0x4ffd" } - srv-ri { - id 0x8dab - label "Rick" + + ; For a more efficient usage of the FIC capacity, it is better to first enumerate + ; the services that we carry in the ensemble (OE=0), followed by the foreign services. + ; This avoids having to send FIG0 headers every time the OE flag switches. + srv-foreign { + id 0x8daf + other_ensembles "0x4ffd" + + ; Only Audio type services are supported } } diff --git a/src/ConfigParser.cpp b/src/ConfigParser.cpp index 0ab3d43..a0bf97e 100644 --- a/src/ConfigParser.cpp +++ b/src/ConfigParser.cpp @@ -286,7 +286,7 @@ static void parse_freq_info(ptree& pt, } break; } // switch(rm) } - catch (ptree_error &e) { + catch (const ptree_error &e) { throw runtime_error("invalid configuration for FI " + fle_uid); } @@ -312,6 +312,53 @@ static void parse_freq_info(ptree& pt, } // if FI present } +static void parse_other_service_linking(ptree& pt, + std::shared_ptr<dabEnsemble> ensemble) +{ + auto pt_other_services = pt.get_child_optional("other-services"); + if (pt_other_services) + { + for (const auto& it_service : *pt_other_services) { + const string srv_uid = it_service.first; + const ptree pt_srv = it_service.second; + + ServiceOtherEnsembleInfo info; + try { + info.service_id = hexparse(pt_srv.get<string>("id")); + + auto oelist = pt_srv.get<std::string>("other_ensembles", ""); + + if (not oelist.empty()) { + vector<string> oe_string_list; + boost::split(oe_string_list, oelist, boost::is_any_of(",")); + + for (const auto& oe_string : oe_string_list) { + if (oe_string == "") { + continue; + } + try { + info.other_ensembles.push_back(hexparse(oe_string)); + } + catch (const std::exception& e) { + etiLog.level(warn) << "Cannot parse '" << oelist << + "' other_ensembles for service " << srv_uid << + ": " << e.what(); + } + } + + ensemble->service_other_ensemble.push_back(move(info)); + } + } + catch (const std::exception &e) { + etiLog.level(warn) << + "Cannot parse other_ensembles for service " << srv_uid << + ": " << e.what(); + } + + } // for over services + } // if other-services present +} + static void parse_general(ptree& pt, std::shared_ptr<dabEnsemble> ensemble) { @@ -381,7 +428,7 @@ static void parse_general(ptree& pt, ensemble_short_label = pt_ensemble.get<string>("shortlabel"); success = ensemble->label.setLabel(ensemble_label, ensemble_short_label); } - catch (ptree_error &e) { + catch (const ptree_error &e) { etiLog.level(warn) << "Ensemble short label undefined, " "truncating label " << ensemble_label; @@ -430,9 +477,9 @@ static void parse_general(ptree& pt, ensemble->clusters.push_back(cl); } } - catch (ptree_error& e) { - etiLog.level(info) << "No announcements defined in ensemble"; - etiLog.level(debug) << "because " << e.what(); + catch (const ptree_error& e) { + etiLog.level(info) << + "No announcements defined in ensemble because " << e.what(); } } @@ -490,7 +537,7 @@ void parse_ptree( try { service->clusters.push_back(hexparse(cluster_s)); } - catch (std::logic_error& e) { + catch (const std::exception& e) { etiLog.level(warn) << "Cannot parse '" << clusterlist << "' announcement clusters for service " << serviceuid << ": " << e.what(); @@ -502,7 +549,7 @@ void parse_ptree( "is empty, but announcements are defined"; } } - catch (ptree_error& e) { + catch (const ptree_error& e) { service->ASu = 0; service->clusters.clear(); @@ -510,30 +557,6 @@ void parse_ptree( serviceuid; } - try { - auto oelist = pt_service.get<std::string>("other_ensembles", ""); - vector<string> oe_string_list; - boost::split(oe_string_list, oelist, boost::is_any_of(",")); - - for (const auto& oe_string : oe_string_list) { - if (oe_string == "") { - continue; - } - try { - service->other_ensembles.push_back(hexparse(oe_string)); - } - catch (std::logic_error& e) { - etiLog.level(warn) << "Cannot parse '" << oelist << - "' announcement clusters for service " << serviceuid << - ": " << e.what(); - } - } - } - catch (ptree_error& e) { - etiLog.level(info) << "No other_sensmbles information for Service " << - serviceuid; - } - int success = -5; string servicelabel = pt_service.get<string>("label"); @@ -542,7 +565,7 @@ void parse_ptree( serviceshortlabel = pt_service.get<string>("shortlabel"); success = service->label.setLabel(servicelabel, serviceshortlabel); } - catch (ptree_error &e) { + catch (const ptree_error &e) { etiLog.level(warn) << "Service short label undefined, " "truncating label " << servicelabel; @@ -587,6 +610,43 @@ void parse_ptree( } service->language = hexparse(pt_service.get("language", "0")); + try { + auto oelist = pt_service.get<std::string>("other_ensembles", ""); + + if (not oelist.empty()) { + etiLog.level(warn) << + "You are using the deprecated other_ensembles inside " + "'services' specification. Please see doc/servicelinking.mux " + "for the new syntax."; + + vector<string> oe_string_list; + boost::split(oe_string_list, oelist, boost::is_any_of(",")); + + ServiceOtherEnsembleInfo oe_info; + oe_info.service_id = service->id; + + for (const auto& oe_string : oe_string_list) { + if (oe_string == "") { + continue; + } + try { + oe_info.other_ensembles.push_back(hexparse(oe_string)); + } + catch (const std::exception& e) { + etiLog.level(warn) << "Cannot parse '" << oelist << + "' other_ensembles for service " << serviceuid << + ": " << e.what(); + } + } + + ensemble->service_other_ensemble.push_back(move(oe_info)); + } + } + catch (const ptree_error& e) { + etiLog.level(info) << "No other_ensembles information for Service " << + serviceuid; + } + allservices[serviceuid] = service; // Set the service's SCIds to zero @@ -612,7 +672,7 @@ void parse_ptree( setup_subchannel_from_ptree(subchan, it->second, ensemble, subchanuid); } - catch (runtime_error &e) { + catch (const runtime_error &e) { etiLog.log(error, "%s\n", e.what()); throw; @@ -649,7 +709,7 @@ void parse_ptree( } service = allservices[service_uid]; } - catch (ptree_error &e) { + catch (const ptree_error &e) { stringstream ss; ss << "Component with uid " << componentuid << " is missing service definition!"; throw runtime_error(ss.str()); @@ -666,7 +726,7 @@ void parse_ptree( } subchannel = allsubchans[subchan_uid]; } - catch (ptree_error &e) { + catch (const ptree_error &e) { stringstream ss; ss << "Component with uid " << componentuid << " is missing subchannel definition!"; throw runtime_error(ss.str()); @@ -691,7 +751,7 @@ void parse_ptree( componentshortlabel = pt_comp.get<string>("shortlabel"); success = component->label.setLabel(componentlabel, componentshortlabel); } - catch (ptree_error &e) { + catch (const ptree_error &e) { if (not componentlabel.empty()) { etiLog.level(warn) << "Component short label undefined, " "truncating label " << componentlabel; @@ -773,6 +833,7 @@ void parse_ptree( parse_linkage(pt, ensemble); parse_freq_info(pt, ensemble); + parse_other_service_linking(pt, ensemble); } static Inputs::dab_input_zmq_config_t setup_zmq_input( @@ -784,7 +845,7 @@ static Inputs::dab_input_zmq_config_t setup_zmq_input( try { zmqconfig.buffer_size = pt.get<int>("zmq-buffer"); } - catch (ptree_error &e) { + catch (const ptree_error &e) { stringstream ss; ss << "Subchannel " << subchanuid << ": " << "no zmq-buffer defined!"; throw runtime_error(ss.str()); @@ -792,7 +853,7 @@ static Inputs::dab_input_zmq_config_t setup_zmq_input( try { zmqconfig.prebuffering = pt.get<int>("zmq-prebuffering"); } - catch (ptree_error &e) { + catch (const ptree_error &e) { stringstream ss; ss << "Subchannel " << subchanuid << ": " << "no zmq-prebuffer defined!"; throw runtime_error(ss.str()); @@ -817,7 +878,7 @@ static void setup_subchannel_from_ptree(shared_ptr<DabSubchannel>& subchan, try { type = pt.get<string>("type"); } - catch (ptree_error &e) { + catch (const ptree_error &e) { stringstream ss; ss << "Subchannel with uid " << subchanuid << " has no type defined!"; throw runtime_error(ss.str()); @@ -832,7 +893,7 @@ static void setup_subchannel_from_ptree(shared_ptr<DabSubchannel>& subchan, try { inputUri = pt.get<string>("inputfile"); } - catch (ptree_error &e) { + catch (const ptree_error &e) { stringstream ss; ss << "Subchannel with uid " << subchanuid << " has no inputUri defined!"; throw runtime_error(ss.str()); @@ -973,7 +1034,7 @@ static void setup_subchannel_from_ptree(shared_ptr<DabSubchannel>& subchan, throw runtime_error(ss.str()); } } - catch (ptree_error &e) { + catch (const ptree_error &e) { throw runtime_error("Error, no bitrate defined for subchannel " + subchanuid); } @@ -984,7 +1045,7 @@ static void setup_subchannel_from_ptree(shared_ptr<DabSubchannel>& subchan, throw runtime_error("Invalid subchannel id " + to_string(subchan->id)); } } - catch (ptree_error &e) { + catch (const ptree_error &e) { for (int i = 0; i < 64; ++i) { // Find first free subchannel auto subchannel = getSubchannel(ensemble->subchannels, i); if (subchannel == ensemble->subchannels.end()) { @@ -1040,7 +1101,7 @@ static void setup_subchannel_from_ptree(shared_ptr<DabSubchannel>& subchan, } protection->level = level - 1; } - catch (ptree_error &e) { + catch (const ptree_error &e) { stringstream ss; ss << "Subchannel with uid " << subchanuid << ": protection level undefined!"; diff --git a/src/MuxElements.h b/src/MuxElements.h index b5d1285..6b8b718 100644 --- a/src/MuxElements.h +++ b/src/MuxElements.h @@ -196,6 +196,7 @@ class DabComponent; class DabSubchannel; class LinkageSet; struct FrequencyInformation; +struct ServiceOtherEnsembleInfo; using vec_sp_component = std::vector<std::shared_ptr<DabComponent> >; using vec_sp_service = std::vector<std::shared_ptr<DabService> >; @@ -248,6 +249,7 @@ class dabEnsemble : public RemoteControllable { std::vector<std::shared_ptr<AnnouncementCluster> > clusters; std::vector<std::shared_ptr<LinkageSet> > linkagesets; std::vector<std::shared_ptr<FrequencyInformation> > frequency_information; + std::vector<ServiceOtherEnsembleInfo> service_other_ensemble; }; @@ -420,9 +422,6 @@ class DabService : public RemoteControllable uint16_t ASu = 0; std::vector<uint8_t> clusters; - // Ensembles in which this service is also available, used for FIG0/24 - std::vector<uint16_t> other_ensembles; - subchannel_type_t getType(const std::shared_ptr<dabEnsemble> ensemble) const; bool isProgramme(const std::shared_ptr<dabEnsemble>& ensemble) const; unsigned char nbComponent(const vec_sp_component& components) const; @@ -437,6 +436,13 @@ class DabService : public RemoteControllable virtual const std::string get_parameter(const std::string& parameter) const; }; +/* Represent an entry for FIG0/24 */ +struct ServiceOtherEnsembleInfo { + uint32_t service_id = 0; + // Ensembles in which this service is also available + std::vector<uint16_t> other_ensembles; +}; + enum class ServiceLinkType {DAB, FM, DRM, AMSS}; /* Represent one link inside a linkage set */ diff --git a/src/fig/FIG0_24.cpp b/src/fig/FIG0_24.cpp index a790440..7dcefcd 100644 --- a/src/fig/FIG0_24.cpp +++ b/src/fig/FIG0_24.cpp @@ -32,7 +32,6 @@ * * - The CEI using length=0, because this information is not available in the * remote control, and there is no runtime changes. - * - Announcing information about other ensembles (OE=1) */ namespace FIC { @@ -61,7 +60,7 @@ FIG0_24::FIG0_24(FIGRuntimeInformation *rti) : FillStatus FIG0_24::fill(uint8_t *buf, size_t max_size) { -#define FIG0_24_TRACE discard +#define FIG0_24_TRACE debug using namespace std; FillStatus fs; @@ -74,34 +73,52 @@ FillStatus FIG0_24::fill(uint8_t *buf, size_t max_size) " ********************************"; if (not m_initialised) { - serviceFIG0_24 = ensemble->services.begin(); + serviceFIG0_24 = ensemble->service_other_ensemble.begin(); m_initialised = true; } - const auto last_service = ensemble->services.end(); + const auto last_service = ensemble->service_other_ensemble.end(); + + bool last_oe = false; // Rotate through the subchannels until there is no more // space for (; serviceFIG0_24 != last_service; ++serviceFIG0_24) { - const auto type = (*serviceFIG0_24)->getType(ensemble); - const auto isProgramme = (*serviceFIG0_24)->isProgramme(ensemble); - etiLog.log(FIG0_24_TRACE, "FIG0_24::fill loop SId=%04x %s/%s", - (*serviceFIG0_24)->id, + shared_ptr<DabService> service; + for (const auto& local_service : ensemble->services) { + if (local_service->id == serviceFIG0_24->service_id) { + service = local_service; + break; + } + } + + subchannel_type_t type = subchannel_type_t::Audio; + bool isProgramme = true; + bool oe = true; + + if (service) { + oe = false; + type = service->getType(ensemble); + isProgramme = service->isProgramme(ensemble); + } + + etiLog.log(FIG0_24_TRACE, "FIG0_24::fill loop OE=%d SId=%04x %s/%s", + oe, + serviceFIG0_24->service_id, m_inserting_audio_not_data ? "AUDIO" : "DATA", type == subchannel_type_t::Audio ? "Audio" : type == subchannel_type_t::Packet ? "Packet" : type == subchannel_type_t::DataDmb ? "DataDmb" : type == subchannel_type_t::Fidc ? "Fidc" : "?"); - // Skip services that are not in other ensembles - if ((*serviceFIG0_24)->other_ensembles.empty()) { - continue; + if (last_oe != oe) { + fig0 = nullptr; } const ssize_t required_size = (isProgramme ? 2 : 4) + 1 + - (*serviceFIG0_24)->other_ensembles.size() * 2; + serviceFIG0_24->other_ensembles.size() * 2; if (fig0 == nullptr) { @@ -116,8 +133,9 @@ FillStatus FIG0_24::fill(uint8_t *buf, size_t max_size) fig0->FIGtypeNumber = 0; fig0->Length = 1; // CN according to ETSI TS 103 176, Clause 5.3.4.1 - fig0->CN = (serviceFIG0_24 == ensemble->services.begin() ? 0 : 1); - fig0->OE = 0; + bool isFirst = serviceFIG0_24 == ensemble->service_other_ensemble.begin(); + fig0->CN = (isFirst ? 0 : 1); + fig0->OE = oe; fig0->PD = isProgramme ? 0 : 1; fig0->Extension = 24; buf += 2; @@ -132,33 +150,33 @@ FillStatus FIG0_24::fill(uint8_t *buf, size_t max_size) if (type == subchannel_type_t::Audio) { auto fig0_2serviceAudio = (FIGtype0_24_audioservice*)buf; - fig0_2serviceAudio->SId = htons((*serviceFIG0_24)->id); + fig0_2serviceAudio->SId = htons(serviceFIG0_24->service_id); fig0_2serviceAudio->rfa = 0; fig0_2serviceAudio->CAId = 0; - fig0_2serviceAudio->Length = (*serviceFIG0_24)->other_ensembles.size(); + fig0_2serviceAudio->Length = serviceFIG0_24->other_ensembles.size(); buf += 3; fig0->Length += 3; remaining -= 3; etiLog.log(FIG0_24_TRACE, "FIG0_24::fill audio SId=%04x", - (*serviceFIG0_24)->id); + serviceFIG0_24->service_id); } else { auto fig0_2serviceData = (FIGtype0_24_dataservice*)buf; - fig0_2serviceData->SId = htonl((*serviceFIG0_24)->id); + fig0_2serviceData->SId = htonl(serviceFIG0_24->service_id); fig0_2serviceData->rfa = 0; fig0_2serviceData->CAId = 0; - fig0_2serviceData->Length = (*serviceFIG0_24)->other_ensembles.size(); + fig0_2serviceData->Length = serviceFIG0_24->other_ensembles.size(); buf += 4; fig0->Length += 4; remaining -= 4; etiLog.log(FIG0_24_TRACE, "FIG0_24::fill data SId=%04x", - (*serviceFIG0_24)->id); + serviceFIG0_24->service_id); } - for (const uint16_t oe : (*serviceFIG0_24)->other_ensembles) { + for (const uint16_t oe : serviceFIG0_24->other_ensembles) { buf[0] = oe >> 8; buf[1] = oe & 0xFF; @@ -174,8 +192,9 @@ FillStatus FIG0_24::fill(uint8_t *buf, size_t max_size) m_initialised = false; } - etiLog.log(FIG0_24_TRACE, "FIG0_24::loop end complete=%d", - fs.complete_fig_transmitted ? 1 : 0); + etiLog.log(FIG0_24_TRACE, "FIG0_24::loop end complete=%d %d", + fs.complete_fig_transmitted ? 1 : 0, + max_size - remaining); fs.num_bytes_written = max_size - remaining; return fs; diff --git a/src/fig/FIG0_24.h b/src/fig/FIG0_24.h index 1c17f6b..785e9bc 100644 --- a/src/fig/FIG0_24.h +++ b/src/fig/FIG0_24.h @@ -3,7 +3,7 @@ 2011, 2012 Her Majesty the Queen in Right of Canada (Communications Research Center Canada) - Copyright (C) 2017 + Copyright (C) 2018 Matthias P. Braendli, matthias.braendli@mpb.li */ /* @@ -47,7 +47,7 @@ class FIG0_24 : public IFIG FIGRuntimeInformation *m_rti; bool m_initialised; bool m_inserting_audio_not_data; - vec_sp_service::iterator serviceFIG0_24; + std::vector<ServiceOtherEnsembleInfo>::iterator serviceFIG0_24; }; } |