diff options
| -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;  };  }  | 
