diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/ConfigParser.cpp | 134 | ||||
| -rw-r--r-- | src/ConfigParser.h | 16 | ||||
| -rw-r--r-- | src/DabMultiplexer.cpp | 1843 | ||||
| -rw-r--r-- | src/DabMultiplexer.h | 510 | ||||
| -rw-r--r-- | src/DabMux.cpp | 2027 | ||||
| -rw-r--r-- | src/DabMux.h | 364 | ||||
| -rw-r--r-- | src/Log.h | 4 | ||||
| -rw-r--r-- | src/Makefile.am | 6 | ||||
| -rw-r--r-- | src/ManagementServer.cpp | 237 | ||||
| -rw-r--r-- | src/ManagementServer.h | 77 | ||||
| -rw-r--r-- | src/MuxElements.cpp | 80 | ||||
| -rw-r--r-- | src/MuxElements.h | 62 | ||||
| -rw-r--r-- | src/RemoteControl.cpp | 134 | ||||
| -rw-r--r-- | src/RemoteControl.h | 45 | ||||
| -rw-r--r-- | src/TcpSocket.cpp | 2 | ||||
| -rw-r--r-- | src/crc.c | 6 | ||||
| -rw-r--r-- | src/dabInputZmq.cpp | 11 | ||||
| -rw-r--r-- | src/dabOutput/dabOutput.h | 40 | ||||
| -rw-r--r-- | src/dabOutput/dabOutputFifo.cpp | 1 | ||||
| -rw-r--r-- | src/dabOutput/dabOutputFile.cpp | 1 | ||||
| -rw-r--r-- | src/dabOutput/edi/AFPacket.h | 2 | ||||
| -rw-r--r-- | src/dabOutput/edi/PFT.h | 8 | ||||
| -rw-r--r-- | src/mpeg.c | 4 | ||||
| -rw-r--r-- | src/utils.cpp | 60 | ||||
| -rw-r--r-- | src/utils.h | 8 | ||||
| -rw-r--r-- | src/zmqinput-keygen.c | 1 | 
26 files changed, 2997 insertions, 2686 deletions
| diff --git a/src/ConfigParser.cpp b/src/ConfigParser.cpp index 443e26d..6092e66 100644 --- a/src/ConfigParser.cpp +++ b/src/ConfigParser.cpp @@ -125,15 +125,8 @@ int hexparse(std::string input)  void parse_ptree(boost::property_tree::ptree& pt, -        vector<dabOutput*> &outputs, -        dabEnsemble* ensemble, -        bool* enableTist, -        unsigned* FICL, -        bool* factumAnalyzer, -        unsigned long* limit, -        BaseRemoteController** rc, -        int* mgmtserverport, -        edi_configuration_t* edi +        boost::shared_ptr<dabEnsemble> ensemble, +        boost::shared_ptr<BaseRemoteController> rc          )  {      using boost::property_tree::ptree; @@ -150,38 +143,11 @@ void parse_ptree(boost::property_tree::ptree& pt,          ensemble->mode = 0;      } -    if (ensemble->mode == 3) { -        *FICL = 32; -    } -    else { -        *FICL = 24; -    } - -    /* Number of frames to generate */ -    *limit = pt_general.get("nbframes", 0); -      /* Enable Logging to syslog conditionally */      if (pt_general.get<bool>("syslog", false)) {          etiLog.register_backend(new LogToSyslog()); // TODO don't leak the LogToSyslog backend      } -    *factumAnalyzer = pt_general.get("writescca", false); - -    *enableTist = pt_general.get("tist", false); - -    *mgmtserverport = pt_general.get<int>("managementport", -                      pt_general.get<int>("statsserverport", 0) ); - -    /************** READ REMOTE CONTROL PARAMETERS *************/ -    ptree pt_rc = pt.get_child("remotecontrol"); -    int telnetport = pt_rc.get<int>("telnetport", 0); - -    if (telnetport != 0) { -        *rc = new RemoteControllerTelnet(telnetport); -    } -    else { -        *rc = new RemoteControllerDummy(); -    }      /******************** READ ENSEMBLE PARAMETERS *************/      ptree pt_ensemble = pt.get_child("ensemble"); @@ -254,19 +220,35 @@ void parse_ptree(boost::property_tree::ptree& pt,      /******************** READ SERVICES PARAMETERS *************/ -    map<string, DabService*> allservices; +    map<string, shared_ptr<DabService> > allservices;      /* For each service, we keep a separate SCIdS counter */ -    map<DabService*, int> SCIdS_per_service; +    map<shared_ptr<DabService>, int> SCIdS_per_service;      ptree pt_services = pt.get_child("services");      for (ptree::iterator it = pt_services.begin();              it != pt_services.end(); ++it) {          string serviceuid = it->first;          ptree pt_service = it->second; -        DabService* service = new DabService(serviceuid); -        ensemble->services.push_back(service); -        service->enrol_at(**rc); + +        shared_ptr<DabService> service; + +        bool service_already_existing = false; + +        for (auto srv : ensemble->services) +        { +            if (srv->uid == serviceuid) { +                service = srv; +                service_already_existing = true; +                break; +            } +        } + +        if (not service_already_existing) { +            auto new_srv = make_shared<DabService>(serviceuid); +            ensemble->services.push_back(new_srv); +            service = new_srv; +        }          int success = -5; @@ -332,13 +314,13 @@ void parse_ptree(boost::property_tree::ptree& pt,      ptree pt_subchans = pt.get_child("subchannels");      for (ptree::iterator it = pt_subchans.begin(); it != pt_subchans.end(); ++it) {          string subchanuid = it->first; -        dabSubchannel* subchan = new dabSubchannel(); +        dabSubchannel* subchan = new dabSubchannel(subchanuid);          ensemble->subchannels.push_back(subchan);          try {              setup_subchannel_from_ptree(subchan, it->second, ensemble, -                    subchanuid, *rc); +                    subchanuid, rc);          }          catch (runtime_error &e) {              etiLog.log(error, @@ -365,7 +347,7 @@ void parse_ptree(boost::property_tree::ptree& pt,          string componentuid = it->first;          ptree pt_comp = it->second; -        DabService* service; +        shared_ptr<DabService> service;          try {              // Those two uids serve as foreign keys to select the service+subchannel              string service_uid = pt_comp.get<string>("service"); @@ -407,8 +389,6 @@ void parse_ptree(boost::property_tree::ptree& pt,          DabComponent* component = new DabComponent(componentuid); -        component->enrol_at(**rc); -          component->serviceId = service->id;          component->subchId = subchannel->id;          component->SCIdS = SCIdS_per_service[service]++; @@ -495,65 +475,13 @@ void parse_ptree(boost::property_tree::ptree& pt,      } -    /******************** READ OUTPUT PARAMETERS ***************/ -    map<string, dabOutput*> alloutputs; -    ptree pt_outputs = pt.get_child("outputs"); -    for (ptree::iterator it = pt_outputs.begin(); it != pt_outputs.end(); ++it) { -        string outputuid = it->first; - -        if (outputuid == "edi") { -            ptree pt_edi = pt_outputs.get_child("edi"); - -            edi->enabled     = true; - -            edi->dest_addr   = pt_edi.get<string>("destination"); -            edi->dest_port   = pt_edi.get<unsigned int>("port"); -            edi->source_port = pt_edi.get<unsigned int>("sourceport"); - -            edi->dump        = pt_edi.get<bool>("dump"); -            edi->enable_pft  = pt_edi.get<bool>("enable_pft"); -            edi->verbose     = pt_edi.get<bool>("verbose"); -        } -        else { -            string uri = pt_outputs.get<string>(outputuid); - -            size_t proto_pos = uri.find("://"); -            if (proto_pos == std::string::npos) { -                stringstream ss; -                ss << "Output with uid " << outputuid << " no protocol defined!"; -                throw runtime_error(ss.str()); -            } - -            char* uri_c = new char[512]; -            memset(uri_c, 0, 512); -            uri.copy(uri_c, 511); - -            uri_c[proto_pos] = '\0'; - -            char* outputName = uri_c + proto_pos + 3; - -            dabOutput* output = new dabOutput(uri_c, outputName); -            outputs.push_back(output); - -            // keep outputs in map, and check for uniqueness of the uid -            if (alloutputs.count(outputuid) == 0) { -                alloutputs[outputuid] = output; -            } -            else { -                stringstream ss; -                ss << "output with uid " << outputuid << " not unique!"; -                throw runtime_error(ss.str()); -            } -        } -    } -  }  void setup_subchannel_from_ptree(dabSubchannel* subchan,          boost::property_tree::ptree &pt, -        dabEnsemble* ensemble, +        boost::shared_ptr<dabEnsemble> ensemble,          string subchanuid, -        BaseRemoteController* rc) +        boost::shared_ptr<BaseRemoteController> rc)  {      using boost::property_tree::ptree;      using boost::property_tree::ptree_error; @@ -857,7 +785,7 @@ void setup_subchannel_from_ptree(dabSubchannel* subchan,      if (nonblock) {          switch (subchan->type) {  #ifdef HAVE_FORMAT_PACKET -            case 3: +            case Packet:                  if (operations == dabInputPacketFileOperations) {                      operations = dabInputFifoOperations;  #ifdef HAVE_FORMAT_EPM @@ -873,7 +801,7 @@ void setup_subchannel_from_ptree(dabSubchannel* subchan,                  break;  #endif // defined(HAVE_FORMAT_PACKET)  #ifdef HAVE_FORMAT_MPEG -            case 0: +            case Audio:                  if (operations == dabInputMpegFileOperations) {                      operations = dabInputMpegFifoOperations;                  } else if (operations == dabInputDabplusFileOperations) { @@ -886,6 +814,8 @@ void setup_subchannel_from_ptree(dabSubchannel* subchan,                  }                  break;  #endif // defined(HAVE_FORMAT_MPEG) +            case DataDmb: +            case Fidc:              default:                  stringstream ss;                  ss << "Subchannel with uid " << subchanuid << diff --git a/src/ConfigParser.h b/src/ConfigParser.h index 4af010a..1eec783 100644 --- a/src/ConfigParser.h +++ b/src/ConfigParser.h @@ -36,23 +36,17 @@  #include "MuxElements.h"  #include "DabMux.h"  #include <boost/property_tree/ptree.hpp> +#include <boost/shared_ptr.hpp>  void parse_ptree(boost::property_tree::ptree& pt, -        std::vector<dabOutput*> &outputs, -        dabEnsemble* ensemble, -        bool* enableTist, -        unsigned* FICL, -        bool* factumAnalyzer, -        unsigned long* limit, -        BaseRemoteController** rc, -        int* mgmtserverport, -        edi_configuration_t* edi); +        boost::shared_ptr<dabEnsemble> ensemble, +        boost::shared_ptr<BaseRemoteController> rc);  void setup_subchannel_from_ptree(dabSubchannel* subchan,          boost::property_tree::ptree &pt, -        dabEnsemble* ensemble, +        boost::shared_ptr<dabEnsemble> ensemble,          std::string subchanuid, -        BaseRemoteController* rc); +        boost::shared_ptr<BaseRemoteController> rc);  #endif diff --git a/src/DabMultiplexer.cpp b/src/DabMultiplexer.cpp new file mode 100644 index 0000000..4ca93aa --- /dev/null +++ b/src/DabMultiplexer.cpp @@ -0,0 +1,1843 @@ +/* +   Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, +   2011, 2012 Her Majesty the Queen in Right of Canada (Communications +   Research Center Canada) + +   Copyright (C) 2015 +   Matthias P. Braendli, matthias.braendli@mpb.li +   */ +/* +   This file is part of ODR-DabMux. + +   ODR-DabMux 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 (at your option) any later version. + +   ODR-DabMux 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 ODR-DabMux.  If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "DabMultiplexer.h" +#include "ConfigParser.h" +#include <boost/make_shared.hpp> + +using namespace std; +using namespace boost; + +static unsigned char Padding_FIB[] = { +    0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + + +// Protection levels and bitrates for UEP. +const unsigned char ProtectionLevelTable[64] = { +    4, 3, 2, 1, 0, +    4, 3, 2, 1, 0, +    4, 3, 2, 1, +    4, 3, 2, 1, 0, +    4, 3, 2, 1, 0, +    4, 3, 2, 1, 0, +    4, 3, 2, 1, +    4, 3, 2, 1, 0, +    4, 3, 2, 1, 0, +    4, 3, 2, 1, 0, +    4, 3, 2, 1, 0, +    4, 3, 2, 1, 0, +    4, 3, 1, +    4, 2, 0 +}; + +const unsigned short BitRateTable[64] = { +    32, 32, 32, 32, 32, +    48, 48, 48, 48, 48, +    56, 56, 56, 56, +    64, 64, 64, 64, 64, +    80, 80, 80, 80, 80, +    96, 96, 96, 96, 96, +    112, 112, 112, 112, +    128, 128, 128, 128, 128, +    160, 160, 160, 160, 160, +    192, 192, 192, 192, 192, +    224, 224, 224, 224, 224, +    256, 256, 256, 256, 256, +    320, 320, 320, +    384, 384, 384 +}; + +DabMultiplexer::DabMultiplexer( +        boost::shared_ptr<BaseRemoteController> rc, +        boost::property_tree::ptree pt) : +    m_pt(pt), +    m_rc(rc), +    timestamp(0), +    MNSC_increment_time(false), +    m_watermarkSize(0), +    m_watermarkPos(0), +    sync(0x49C5F8), +    currentFrame(0), +    insertFIG(0), +    rotateFIB(0) +{ +    prepare_watermark(); + +    ensemble = boost::make_shared<dabEnsemble>(); +} + +void DabMultiplexer::prepare_watermark() +{ +    uint8_t buffer[sizeof(m_watermarkData) / 2]; +    snprintf((char*)buffer, sizeof(buffer), +            "%s %s, compiled at %s, %s", +            PACKAGE_NAME, PACKAGE_VERSION, __DATE__, __TIME__); + +    memset(m_watermarkData, 0, sizeof(m_watermarkData)); +    m_watermarkData[0] = 0x55; // Sync +    m_watermarkData[1] = 0x55; +    m_watermarkSize = 16; +    for (unsigned i = 0; i < strlen((char*)buffer); ++i) { +        for (int j = 0; j < 8; ++j) { +            uint8_t bit = (buffer[m_watermarkPos >> 3] >> (7 - (m_watermarkPos & 0x07))) & 1; +            m_watermarkData[m_watermarkSize >> 3] |= bit << (7 - (m_watermarkSize & 0x07)); +            ++m_watermarkSize; +            bit = 1; +            m_watermarkData[m_watermarkSize >> 3] |= bit << (7 - (m_watermarkSize & 0x07)); +            ++m_watermarkSize; +            ++m_watermarkPos; +        } +    } +    m_watermarkPos = 0; +} + +void DabMultiplexer::update_config(boost::property_tree::ptree pt) +{ +    ensemble_next = boost::make_shared<dabEnsemble>(); + +    m_pt_next = pt; + +    reconfigure(); +} + +void DabMultiplexer::reconfigure() +{ +    parse_ptree(m_pt_next, ensemble_next, m_rc); +} + +void DabMultiplexer::set_edi_config(const edi_configuration_t& new_edi_conf) +{ +    edi_conf = new_edi_conf; + +#if HAVE_OUTPUT_EDI +    if (edi_conf.verbose) { +        etiLog.log(info, "Setup EDI"); +    } + +    if (edi_conf.dump) { +        edi_debug_file.open("./edi.debug"); +    } + +    if (edi_conf.enabled) { +        edi_output.create(edi_conf.source_port); +    } + +    if (edi_conf.verbose) { +        etiLog.log(info, "EDI set up"); +    } + +    // The TagPacket will then be placed into an AFPacket +    AFPacketiser afPacketiser; +    edi_afPacketiser = afPacketiser; + +    // The AF Packet will be protected with reed-solomon and split in fragments +    PFT pft(207, 3, edi_conf); +    edi_pft = pft; +#endif +} + + +// Run a set of checks on the configuration +void DabMultiplexer::prepare() +{ +    parse_ptree(m_pt, ensemble, m_rc); + +    ensemble->enrol_at(m_rc); + +    prepare_subchannels(); +    prepare_services_components(); +    prepare_data_inputs(); + +    if (ensemble->subchannels.size() == 0) { +        etiLog.log(error, "can't multiplex no subchannel!\n"); +        throw MuxInitException(); +    } + +    vector<dabSubchannel*>::iterator subchannel = +        ensemble->subchannels.end() - 1; + +    if ((*subchannel)->startAddress + getSizeCu((*subchannel)) > 864) { +        etiLog.log(error, "Total size in CU exceeds 864\n"); +        printSubchannels(ensemble->subchannels); +        throw MuxInitException(); +    } + +    /* These iterators are used to fill the respective FIG. +     * It is necessary to cycle through all the FIGs that have +     * to be transmitted because they do not fit in a single +     * FIB. +     * +     * ETSI EN 300 799 Clauses 5.2 and 8.1 +     */ +    serviceProgFIG0_2 = ensemble->services.end(); +    serviceDataFIG0_2 = ensemble->services.end(); +    componentProgFIG0_8 = ensemble->components.end(); +    componentDataFIG0_8 = ensemble->components.end(); +    componentFIG0_13 = ensemble->components.end(); +    transmitFIG0_13programme = false; +    serviceFIG0_17 = ensemble->services.end(); +    subchannelFIG0_1 = ensemble->subchannels.end(); + + +    /* TODO: +     * In a SFN, when reconfiguring the ensemble, the multiplexer +     * has to be restarted (odr-dabmux doesn't support reconfiguration). +     * Ideally, we must be able to restart transmission s.t. the receiver +     * synchronisation is preserved. +     */ +    gettimeofday(&mnsc_time, NULL); +} + + +// Check and adjust subchannels +void DabMultiplexer::prepare_subchannels() +{ +    set<unsigned char> ids; + +    for (auto subchannel : ensemble->subchannels) { +        if (ids.find(subchannel->id) != ids.end()) { +            etiLog.log(error, +                    "Subchannel %u is set more than once!\n", +                    subchannel->id); +            throw MuxInitException(); +        } +        ids.insert(subchannel->id); +    } +} + +// Check and adjust services and components +void DabMultiplexer::prepare_services_components() +{ +    set<uint32_t> ids; +    dabProtection* protection = NULL; + +    vector<DabComponent*>::iterator component; +    vector<dabSubchannel*>::iterator subchannel; + +    for (auto service : ensemble->services) { +        if (ids.find(service->id) != ids.end()) { +            etiLog.log(error, +                    "Service id 0x%x (%u) is set more than once!\n", +                    service->id, service->id); +            throw MuxInitException(); +        } + +        // Get first component of this service +        component = getComponent(ensemble->components, service->id); +        if (component == ensemble->components.end()) { +            etiLog.log(error, +                    "Service id 0x%x (%u) includes no component!\n", +                    service->id, service->id); +            throw MuxInitException(); +        } + +        // Adjust service type from this first component +        switch (service->getType(ensemble)) { +            case 0: // Audio +                service->program = true; +                break; +            case 1: +            case 2: +            case 3: +                service->program = false; +                break; +            default: +                etiLog.log(error, +                        "Error, unknown service type: %u\n", +                        service->getType(ensemble)); +                throw MuxInitException(); +        } + +        service->enrol_at(m_rc); + +        // Adjust components type for DAB+ +        while (component != ensemble->components.end()) { +            subchannel = +                getSubchannel(ensemble->subchannels, (*component)->subchId); +            if (subchannel == ensemble->subchannels.end()) { +                etiLog.log(error, "Error, service %u component " +                        "links to the invalid subchannel %u\n", +                        (*component)->serviceId, (*component)->subchId); +                throw MuxInitException(); +            } + +            protection = &(*subchannel)->protection; +            switch ((*subchannel)->type) { +                case Audio: +                    { +                        if (protection->form == EEP) { +                            (*component)->type = 0x3f;  // DAB+ +                        } +                    } +                    break; +                case DataDmb: +                case Fidc: +                case Packet: +                    break; +                default: +                    etiLog.log(error, +                            "Error, unknown subchannel type\n"); +                    throw MuxInitException(); +            } +            component = getComponent(ensemble->components, +                    service->id, component); +        } +    } + +    // Init packet components SCId +    int cur_packetid = 0; +    for (auto component : ensemble->components) { +        subchannel = getSubchannel(ensemble->subchannels, +                component->subchId); +        if (subchannel == ensemble->subchannels.end()) { +            etiLog.log(error, +                    "Subchannel %i does not exist for component " +                    "of service %i\n", +                    component->subchId, component->serviceId); +            throw MuxInitException(); +        } +        if ((*subchannel)->type != Packet) continue; + +        component->packet.id = cur_packetid++; + +        component->enrol_at(m_rc); + +    } + +} + +void DabMultiplexer::prepare_data_inputs() +{ +    dabProtection* protection = NULL; +    vector<dabSubchannel*>::iterator subchannel; + +    // Prepare and check the data inputs +    for (subchannel = ensemble->subchannels.begin(); +            subchannel != ensemble->subchannels.end(); +            ++subchannel) { +        protection = &(*subchannel)->protection; +        if (subchannel == ensemble->subchannels.begin()) { +            (*subchannel)->startAddress = 0; +        } else { +            (*subchannel)->startAddress = (*(subchannel - 1))->startAddress + +                getSizeCu(*(subchannel - 1)); +        } +        if ((*subchannel)->input->open((*subchannel)->inputUri) == -1) { +            perror((*subchannel)->inputUri.c_str()); +            throw MuxInitException(); +        } + +        // TODO Check errors +        int subch_bitrate = (*subchannel)->input->setBitrate( (*subchannel)->bitrate); +        if (subch_bitrate <= 0) { +            etiLog.level(error) << "can't set bitrate for source " << +                (*subchannel)->inputUri; +            throw MuxInitException(); +        } +        (*subchannel)->bitrate = subch_bitrate; + +        /* Use EEP unless we find a UEP configuration +         * UEP is only used for MPEG audio, but some bitrates don't +         * have a UEP profile (EN 300 401 Clause 6.2.1). +         * For these bitrates, we must switch to EEP. +         * +         * AAC audio and data is already EEP +         */ +        if (protection->form == UEP) { +            protection->form = EEP; +            for (int i = 0; i < 64; i++) { +                if ( (*subchannel)->bitrate == BitRateTable[i] && +                        protection->level == ProtectionLevelTable[i] ) { +                    protection->form = UEP; +                    protection->uep.tableIndex = i; +                } +            } +        } + +        /* EEP B can only be used for subchannels with bitrates +         * multiple of 32kbit/s +         */ +        if ( protection->form == EEP && +             protection->eep.profile == EEP_B && +             subch_bitrate % 32 != 0 ) { +            etiLog.level(error) << +                "Cannot use EEP_B protection for subchannel " << +                (*subchannel)->inputUri << +                ": bitrate not multiple of 32kbit/s"; +            throw MuxInitException(); +        } +    } +} + +/*  Each call creates one ETI frame */ +void DabMultiplexer::mux_frame(std::vector<boost::shared_ptr<DabOutput> >& outputs) +{ +    int cur; +    time_t date; +    unsigned char etiFrame[6144]; +    unsigned short index = 0; + +    vector<std::shared_ptr<DabService> >::iterator service; +    vector<DabComponent*>::iterator component; +    vector<dabSubchannel*>::iterator subchannel; + +    // FIC Length, DAB Mode I, II, IV -> FICL = 24, DAB Mode III -> FICL = 32 +    unsigned FICL  = (ensemble->mode == 3 ? 32 : 24); + +    // For EDI, save ETI(LI) Management data into a TAG Item DETI +    TagDETI edi_tagDETI; +    TagStarPTR edi_tagStarPtr; +    list<TagESTn> edi_subchannels; +    map<dabSubchannel*, TagESTn*> edi_subchannelToTag; + +    // The above Tag Items will be assembled into a TAG Packet +    TagPacket edi_tagpacket; + +    edi_tagDETI.atstf = 0; // TODO add ATST support + +    date = getDabTime(); + +    // Initialise the ETI frame +    memset(etiFrame, 0, 6144); + +    /********************************************************************** +     **********   Section SYNC of ETI(NI, G703)   ************************* +     **********************************************************************/ + +    // See ETS 300 799 Clause 6 +    eti_SYNC *etiSync = (eti_SYNC *) etiFrame; + +    etiSync->ERR = edi_tagDETI.stat = 0xFF; // ETS 300 799, 5.2, no error + +    //****** Field FSYNC *****// +    // See ETS 300 799, 6.2.1.2 +    sync ^= 0xffffff; +    etiSync->FSYNC = sync; + +    /********************************************************************** +     ***********   Section LIDATA of ETI(NI, G703)   ********************** +     **********************************************************************/ + +    // See ETS 300 799 Figure 5 for a better overview of these fields. + +    //****** Section FC ***************************************************/ +    // 4 octets, starts at offset 4 +    eti_FC *fc = (eti_FC *) &etiFrame[4]; + +    //****** FCT ******// +    // Incremente for each frame, overflows at 249 +    fc->FCT = currentFrame % 250; +    edi_tagDETI.dflc = currentFrame % 5000; + +    //****** FICF ******// +    // Fast Information Channel Flag, 1 bit, =1 if FIC present +    fc->FICF = edi_tagDETI.ficf = 1; + +    //****** NST ******// +    /* Number of audio of data sub-channels, 7 bits, 0-64. +     * In the 15-frame period immediately preceding a multiplex +     * re-configuration, NST can take the value 0 (see annex E). +     */ +    fc->NST = ensemble->subchannels.size(); + +    //****** FP ******// +    /* Frame Phase, 3 bit counter, tells the COFDM generator +     * when to insert the TII. Is also used by the MNSC. +     */ +    fc->FP = edi_tagDETI.fp = currentFrame & 0x7; + +    //****** MID ******// +    //Mode Identity, 2 bits, 01 ModeI, 10 modeII, 11 ModeIII, 00 ModeIV +    fc->MID = edi_tagDETI.mid = ensemble->mode;      //mode 2 needs 3 FIB, 3*32octets = 96octets + +    //****** FL ******// +    /* Frame Length, 11 bits, nb of words(4 bytes) in STC, EOH and MST +     * if NST=0, FL=1+FICL words, FICL=24 or 32 depending on the mode. +     * The FL is given in words (4 octets), see ETS 300 799 5.3.6 for details +     */ +    unsigned short FLtmp = 1 + FICL + (fc->NST); + +    for (subchannel = ensemble->subchannels.begin(); +            subchannel != ensemble->subchannels.end(); +            ++subchannel) { +        // Add STLsbch +        FLtmp += getSizeWord(*subchannel); +    } + +    fc->setFrameLength(FLtmp); +    index = 8; + +    /******* Section STC **************************************************/ +    // Stream Characterization, +    //  number of channels * 4 octets = nb octets total +    int edi_stream_id = 1; +    for (subchannel = ensemble->subchannels.begin(); +            subchannel != ensemble->subchannels.end(); +            ++subchannel) { +        dabProtection* protection = &(*subchannel)->protection; +        eti_STC *sstc = (eti_STC *) & etiFrame[index]; + +        sstc->SCID = (*subchannel)->id; +        sstc->startAddress_high = (*subchannel)->startAddress / 256; +        sstc->startAddress_low = (*subchannel)->startAddress % 256; +        // depends on the desired protection form +        if (protection->form == UEP) { +            sstc->TPL = 0x10 | +                ProtectionLevelTable[protection->uep.tableIndex]; +        } +        else if (protection->form == EEP) { +            sstc->TPL = 0x20 | (protection->eep.GetOption() << 2) | protection->level; +        } + +        // Sub-channel Stream Length, multiple of 64 bits +        sstc->STL_high = getSizeDWord(*subchannel) / 256; +        sstc->STL_low = getSizeDWord(*subchannel) % 256; + +        TagESTn tag_ESTn(edi_stream_id++); +        tag_ESTn.scid = (*subchannel)->id; +        tag_ESTn.sad = (*subchannel)->startAddress; +        tag_ESTn.tpl = sstc->TPL; +        tag_ESTn.rfa = 0; // two bits +        tag_ESTn.mst_length = getSizeByte(*subchannel) / 8; +        assert(getSizeByte(*subchannel) % 8 == 0); + +        edi_subchannels.push_back(tag_ESTn); +        edi_subchannelToTag[*subchannel] = &edi_subchannels.back(); +        index += 4; +    } + +    /******* Section EOH **************************************************/ +    // End of Header 4 octets +    eti_EOH *eoh = (eti_EOH *) & etiFrame[index]; + +    //MNSC Multiplex Network Signalling Channel, 2 octets + +    eoh->MNSC = 0; + +    struct tm *time_tm = gmtime(&mnsc_time.tv_sec); +    switch (fc->FP & 0x3) +    { +        case 0: +            if (MNSC_increment_time) +            { +                MNSC_increment_time = false; +                mnsc_time.tv_sec += 1; +            } +            { + +                eti_MNSC_TIME_0 *mnsc = (eti_MNSC_TIME_0 *) &eoh->MNSC; +                // Set fields according to ETS 300 799 -- 5.5.1 and A.2.2 +                mnsc->type = 0; +                mnsc->identifier = 0; +                mnsc->rfa = 0; +            } +            break; +        case 1: +            { +                eti_MNSC_TIME_1 *mnsc = (eti_MNSC_TIME_1 *) &eoh->MNSC; +                mnsc->setFromTime(time_tm); +                mnsc->accuracy = 1; +                mnsc->sync_to_frame = 1; +            } +            break; +        case 2: +            { +                eti_MNSC_TIME_2 *mnsc = (eti_MNSC_TIME_2 *) &eoh->MNSC; +                mnsc->setFromTime(time_tm); +            } +            break; +        case 3: +            { +                eti_MNSC_TIME_3 *mnsc = (eti_MNSC_TIME_3 *) &eoh->MNSC; +                mnsc->setFromTime(time_tm); +            } +            break; +    } + +    edi_tagDETI.mnsc = eoh->MNSC; + +    // CRC Cyclic Redundancy Checksum of the FC, STC and MNSC, 2 octets +    unsigned short nbBytesCRC = 4 + ((fc->NST) * 4) + 2; + +    unsigned short CRCtmp = 0xFFFF; +    CRCtmp = crc16(CRCtmp, &etiFrame[4], nbBytesCRC); +    CRCtmp ^= 0xffff; +    eoh->CRC = htons(CRCtmp); + +    /******* Section MST **************************************************/ +    // Main Stream Data, if FICF=1 the first 96 or 128 bytes carry the FIC +    // (depending on mode) +    index = ((fc->NST) + 2 + 1) * 4; +    edi_tagDETI.fic_data = &etiFrame[index]; +    edi_tagDETI.fic_length = FICL * 4; + +    // FIC Insertion +    FIGtype0* fig0; +    FIGtype0_0 *fig0_0; +    FIGtype0_1 *figtype0_1; + +    FIG_01_SubChannel_ShortF *fig0_1subchShort; +    FIG_01_SubChannel_LongF *fig0_1subchLong1; + +    FIGtype0_2 *fig0_2; + +    FIGtype0_2_Service *fig0_2serviceAudio; +    FIGtype0_2_Service_data *fig0_2serviceData; +    FIGtype0_2_audio_component* audio_description; +    FIGtype0_2_data_component* data_description; +    FIGtype0_2_packet_component* packet_description; + +    FIGtype0_3_header *fig0_3_header; +    FIGtype0_3_data *fig0_3_data; +    FIGtype0_9 *fig0_9; +    FIGtype0_10 *fig0_10; +    FIGtype0_17_programme *programme; + +    FIGtype1_0 *fig1_0; +    FIGtype1_1 *fig1_1; +    FIGtype1_5 *fig1_5; + +    tm* timeData; + +    unsigned char figSize = 0; + +    // FIB 0 Insertion +    switch (insertFIG) { + +        case 0: +        case 4: +        case 8: +        case 12: +            // FIG type 0/0, Multiplex Configuration Info (MCI), +            //  Ensemble information +            fig0_0 = (FIGtype0_0 *) & etiFrame[index]; + +            fig0_0->FIGtypeNumber = 0; +            fig0_0->Length = 5; +            fig0_0->CN = 0; +            fig0_0->OE = 0; +            fig0_0->PD = 0; +            fig0_0->Extension = 0; + +            fig0_0->EId = htons(ensemble->id); +            fig0_0->Change = 0; +            fig0_0->Al = 0; +            fig0_0->CIFcnt_hight = (currentFrame / 250) % 20; +            fig0_0->CIFcnt_low = (currentFrame % 250); +            index = index + 6; +            figSize += 6; + +            break; + +        case 1: +        case 6: +        case 10: +        case 13: +            // FIG type 0/1, MIC, Sub-Channel Organization, +            // one instance of the part for each subchannel +            figtype0_1 = (FIGtype0_1 *) & etiFrame[index]; + +            figtype0_1->FIGtypeNumber = 0; +            figtype0_1->Length = 1; +            figtype0_1->CN = 0; +            figtype0_1->OE = 0; +            figtype0_1->PD = 0; +            figtype0_1->Extension = 1; +            index = index + 2; +            figSize += 2; + +            // Rotate through the subchannels until there is no more +            // space in the FIG0/1 +            if (subchannelFIG0_1 == ensemble->subchannels.end()) { +                subchannelFIG0_1 = ensemble->subchannels.begin(); +            } + +            for (; subchannelFIG0_1 != ensemble->subchannels.end(); +                    ++subchannelFIG0_1) { +                dabProtection* protection = &(*subchannelFIG0_1)->protection; + +                if ( (protection->form == UEP && figSize > 27) || +                        (protection->form == EEP && figSize > 26) ) { +                    break; +                } + +                if (protection->form == UEP) { +                    fig0_1subchShort = +                        (FIG_01_SubChannel_ShortF*) &etiFrame[index]; +                    fig0_1subchShort->SubChId = (*subchannelFIG0_1)->id; + +                    fig0_1subchShort->StartAdress_high = +                        (*subchannelFIG0_1)->startAddress / 256; +                    fig0_1subchShort->StartAdress_low = +                        (*subchannelFIG0_1)->startAddress % 256; + +                    fig0_1subchShort->Short_Long_form = 0; +                    fig0_1subchShort->TableSwitch = 0; +                    fig0_1subchShort->TableIndex = +                        protection->uep.tableIndex; + +                    index = index + 3; +                    figSize += 3; +                    figtype0_1->Length += 3; +                } +                else if (protection->form == EEP) { +                    fig0_1subchLong1 = +                        (FIG_01_SubChannel_LongF*) &etiFrame[index]; +                    fig0_1subchLong1->SubChId = (*subchannelFIG0_1)->id; + +                    fig0_1subchLong1->StartAdress_high = +                        (*subchannelFIG0_1)->startAddress / 256; +                    fig0_1subchLong1->StartAdress_low = +                        (*subchannelFIG0_1)->startAddress % 256; + +                    fig0_1subchLong1->Short_Long_form = 1; +                    fig0_1subchLong1->Option = protection->eep.GetOption(); +                    fig0_1subchLong1->ProtectionLevel = +                        protection->level; + +                    fig0_1subchLong1->Sub_ChannelSize_high = +                        getSizeCu(*subchannelFIG0_1) / 256; +                    fig0_1subchLong1->Sub_ChannelSize_low = +                        getSizeCu(*subchannelFIG0_1) % 256; + +                    index = index + 4; +                    figSize += 4; +                    figtype0_1->Length += 4; +                } +            } +            break; + +        case 2: +        case 9: +        case 11: +        case 14: +            // FIG type 0/2, MCI, Service Organization, one instance of +            // FIGtype0_2_Service for each subchannel +            fig0_2 = NULL; +            cur = 0; + +            // Rotate through the subchannels until there is no more +            // space in the FIG0/1 +            if (serviceProgFIG0_2 == ensemble->services.end()) { +                serviceProgFIG0_2 = ensemble->services.begin(); +            } + +            for (; serviceProgFIG0_2 != ensemble->services.end(); +                    ++serviceProgFIG0_2) { +                if (!(*serviceProgFIG0_2)->nbComponent(ensemble->components)) { +                    continue; +                } + +                if ((*serviceProgFIG0_2)->getType(ensemble) != 0) { +                    continue; +                } + +                ++cur; + +                if (fig0_2 == NULL) { +                    fig0_2 = (FIGtype0_2 *) & etiFrame[index]; + +                    fig0_2->FIGtypeNumber = 0; +                    fig0_2->Length = 1; +                    fig0_2->CN = 0; +                    fig0_2->OE = 0; +                    fig0_2->PD = 0; +                    fig0_2->Extension = 2; +                    index = index + 2; +                    figSize += 2; +                } + +                if (figSize + 3 +                        + (*serviceProgFIG0_2)->nbComponent(ensemble->components) +                        * 2 > 30) { +                    break; +                } + +                fig0_2serviceAudio = (FIGtype0_2_Service*) &etiFrame[index]; + +                fig0_2serviceAudio->SId = htons((*serviceProgFIG0_2)->id); +                fig0_2serviceAudio->Local_flag = 0; +                fig0_2serviceAudio->CAId = 0; +                fig0_2serviceAudio->NbServiceComp = +                    (*serviceProgFIG0_2)->nbComponent(ensemble->components); +                index += 3; +                fig0_2->Length += 3; +                figSize += 3; + +                int curCpnt = 0; +                for (component = getComponent(ensemble->components, +                            (*serviceProgFIG0_2)->id); +                        component != ensemble->components.end(); +                        component = getComponent(ensemble->components, +                            (*serviceProgFIG0_2)->id, component)) { +                    subchannel = getSubchannel(ensemble->subchannels, +                            (*component)->subchId); + +                    if (subchannel == ensemble->subchannels.end()) { +                        etiLog.log(error, +                                "Subchannel %i does not exist for component " +                                "of service %i\n", +                                (*component)->subchId, (*component)->serviceId); +                        throw MuxInitException(); +                    } + +                    switch ((*subchannel)->type) { +                        case Audio: +                            audio_description = +                                (FIGtype0_2_audio_component*)&etiFrame[index]; +                            audio_description->TMid    = 0; +                            audio_description->ASCTy   = (*component)->type; +                            audio_description->SubChId = (*subchannel)->id; +                            audio_description->PS      = ((curCpnt == 0) ? 1 : 0); +                            audio_description->CA_flag = 0; +                            break; +                        case DataDmb: +                            data_description = +                                (FIGtype0_2_data_component*)&etiFrame[index]; +                            data_description->TMid    = 1; +                            data_description->DSCTy   = (*component)->type; +                            data_description->SubChId = (*subchannel)->id; +                            data_description->PS      = ((curCpnt == 0) ? 1 : 0); +                            data_description->CA_flag = 0; +                            break; +                        case Packet: +                            packet_description = +                                (FIGtype0_2_packet_component*)&etiFrame[index]; +                            packet_description->TMid    = 3; +                            packet_description->setSCId((*component)->packet.id); +                            packet_description->PS      = ((curCpnt == 0) ? 1 : 0); +                            packet_description->CA_flag = 0; +                            break; +                        default: +                            etiLog.log(error, +                                    "Component type not supported\n"); +                            throw MuxInitException(); +                    } +                    index += 2; +                    fig0_2->Length += 2; +                    figSize += 2; +                    if (figSize > 30) { +                        etiLog.log(error, +                                "Sorry, no space left in FIG 0/2 to insert " +                                "component %i of program service %i.\n", +                                curCpnt, cur); +                        throw MuxInitException(); +                    } +                    ++curCpnt; +                } +            } +            break; + +        case 3: +            fig0_2 = NULL; +            cur = 0; + +            if (serviceDataFIG0_2 == ensemble->services.end()) { +                serviceDataFIG0_2 = ensemble->services.begin(); +            } +            for (; serviceDataFIG0_2 != ensemble->services.end(); +                    ++serviceDataFIG0_2) { +                if (!(*serviceDataFIG0_2)->nbComponent(ensemble->components)) { +                    continue; +                } + +                unsigned char type = (*serviceDataFIG0_2)->getType(ensemble); +                if ((type == 0) || (type == 2)) { +                    continue; +                } + +                ++cur; + +                if (fig0_2 == NULL) { +                    fig0_2 = (FIGtype0_2 *) & etiFrame[index]; + +                    fig0_2->FIGtypeNumber = 0; +                    fig0_2->Length = 1; +                    fig0_2->CN = 0; +                    fig0_2->OE = 0; +                    fig0_2->PD = 1; +                    fig0_2->Extension = 2; +                    index = index + 2; +                    figSize += 2; +                } + +                if (figSize + 5 +                        + (*serviceDataFIG0_2)->nbComponent(ensemble->components) +                        * 2 > 30) { +                    break; +                } + +                fig0_2serviceData = +                    (FIGtype0_2_Service_data*) &etiFrame[index]; + +                fig0_2serviceData->SId = htonl((*serviceDataFIG0_2)->id); +                fig0_2serviceData->Local_flag = 0; +                fig0_2serviceData->CAId = 0; +                fig0_2serviceData->NbServiceComp = +                    (*serviceDataFIG0_2)->nbComponent(ensemble->components); +                fig0_2->Length += 5; +                index += 5; +                figSize += 5; + +                int curCpnt = 0; +                for (component = getComponent(ensemble->components, +                            (*serviceDataFIG0_2)->id); +                        component != ensemble->components.end(); +                        component = getComponent(ensemble->components, +                            (*serviceDataFIG0_2)->id, component)) { +                    subchannel = getSubchannel(ensemble->subchannels, +                            (*component)->subchId); +                    if (subchannel == ensemble->subchannels.end()) { +                        etiLog.log(error, +                                "Subchannel %i does not exist for component " +                                "of service %i\n", +                                (*component)->subchId, (*component)->serviceId); +                        throw MuxInitException(); +                    } + +                    switch ((*subchannel)->type) { +                        case Audio: +                            audio_description = +                                (FIGtype0_2_audio_component*)&etiFrame[index]; +                            audio_description->TMid = 0; +                            audio_description->ASCTy = (*component)->type; +                            audio_description->SubChId = (*subchannel)->id; +                            audio_description->PS = ((curCpnt == 0) ? 1 : 0); +                            audio_description->CA_flag = 0; +                            break; +                        case DataDmb: +                            data_description = +                                (FIGtype0_2_data_component*)&etiFrame[index]; +                            data_description->TMid = 1; +                            data_description->DSCTy = (*component)->type; +                            data_description->SubChId = (*subchannel)->id; +                            data_description->PS = ((curCpnt == 0) ? 1 : 0); +                            data_description->CA_flag = 0; +                            break; +                        case Packet: +                            packet_description = +                                (FIGtype0_2_packet_component*)&etiFrame[index]; +                            packet_description->TMid = 3; +                            packet_description->setSCId((*component)->packet.id); +                            packet_description->PS = ((curCpnt == 0) ? 1 : 0); +                            packet_description->CA_flag = 0; +                            break; +                        default: +                            etiLog.log(error, +                                    "Component type not supported\n"); +                            throw MuxInitException(); +                    } +                    index += 2; +                    fig0_2->Length += 2; +                    figSize += 2; +                    if (figSize > 30) { +                        etiLog.log(error, +                                "Sorry, no place left in FIG 0/2 to insert " +                                "component %i of data service %i.\n", +                                curCpnt, cur); +                        throw MuxInitException(); +                    } +                    ++curCpnt; +                } +            } +            break; + +        case 5: +            fig0_3_header = NULL; + +            for (component = ensemble->components.begin(); +                    component != ensemble->components.end(); +                    ++component) { +                subchannel = getSubchannel(ensemble->subchannels, +                        (*component)->subchId); + +                if (subchannel == ensemble->subchannels.end()) { +                    etiLog.log(error, +                            "Subchannel %i does not exist for component " +                            "of service %i\n", +                            (*component)->subchId, (*component)->serviceId); +                    throw MuxInitException(); +                } + +                if ((*subchannel)->type != Packet) +                    continue; + +                if (fig0_3_header == NULL) { +                    fig0_3_header = (FIGtype0_3_header*)&etiFrame[index]; +                    fig0_3_header->FIGtypeNumber = 0; +                    fig0_3_header->Length = 1; +                    fig0_3_header->CN = 0; +                    fig0_3_header->OE = 0; +                    fig0_3_header->PD = 0; +                    fig0_3_header->Extension = 3; + +                    index += 2; +                    figSize += 2; +                } + +                bool factumAnalyzer = m_pt.get("general.writescca", false); + +                /*  Warning: When bit SCCA_flag is unset(0), the multiplexer +                 *  R&S does not send the SCCA field. But, in the Factum ETI +                 *  analyzer, if this field is not there, it is an error. +                 */ +                fig0_3_data = (FIGtype0_3_data*)&etiFrame[index]; +                fig0_3_data->setSCId((*component)->packet.id); +                fig0_3_data->rfa = 0; +                fig0_3_data->SCCA_flag = 0; +                // if 0, datagroups are used +                fig0_3_data->DG_flag = !(*component)->packet.datagroup; +                fig0_3_data->rfu = 0; +                fig0_3_data->DSCTy = (*component)->type; +                fig0_3_data->SubChId = (*subchannel)->id; +                fig0_3_data->setPacketAddress((*component)->packet.address); +                if (factumAnalyzer) { +                    fig0_3_data->SCCA = 0; +                } + +                fig0_3_header->Length += 5; +                index += 5; +                figSize += 5; +                if (factumAnalyzer) { +                    fig0_3_header->Length += 2; +                    index += 2; +                    figSize += 2; +                } + +                if (figSize > 30) { +                    etiLog.log(error, +                            "can't add to Fic Fig 0/3, " +                            "too much packet service\n"); +                    throw MuxInitException(); +                } +            } +            break; + +        case 7: +            fig0 = NULL; +            if (serviceFIG0_17 == ensemble->services.end()) { +                serviceFIG0_17 = ensemble->services.begin(); +            } +            for (; serviceFIG0_17 != ensemble->services.end(); +                    ++serviceFIG0_17) { + +                if (    (*serviceFIG0_17)->pty == 0 && +                        (*serviceFIG0_17)->language == 0) { +                    continue; +                } + +                if (fig0 == NULL) { +                    fig0 = (FIGtype0*)&etiFrame[index]; +                    fig0->FIGtypeNumber = 0; +                    fig0->Length = 1; +                    fig0->CN = 0; +                    fig0->OE = 0; +                    fig0->PD = 0; +                    fig0->Extension = 17; +                    index += 2; +                    figSize += 2; +                } + +                if ((*serviceFIG0_17)->language == 0) { +                    if (figSize + 4 > 30) { +                        break; +                    } +                } +                else { +                    if (figSize + 5 > 30) { +                        break; +                    } +                } + +                programme = +                    (FIGtype0_17_programme*)&etiFrame[index]; +                programme->SId = htons((*serviceFIG0_17)->id); +                programme->SD = 1; +                programme->PS = 0; +                programme->L = (*serviceFIG0_17)->language != 0; +                programme->CC = 0; +                programme->Rfa = 0; +                programme->NFC = 0; +                if ((*serviceFIG0_17)->language == 0) { +                    etiFrame[index + 3] = (*serviceFIG0_17)->pty; +                    fig0->Length += 4; +                    index += 4; +                    figSize += 4; +                } +                else { +                    etiFrame[index + 3] = (*serviceFIG0_17)->language; +                    etiFrame[index + 4] = (*serviceFIG0_17)->pty; +                    fig0->Length += 5; +                    index += 5; +                    figSize += 5; +                } +            } +            break; +    } + +    if (figSize > 30) { +        etiLog.log(error, +                "FIG too big (%i > 30)\n", figSize); +        throw MuxInitException(); +    } + +    memcpy(&etiFrame[index], Padding_FIB, 30 - figSize); +    index += 30 - figSize; + +    CRCtmp = 0xffff; +    CRCtmp = crc16(CRCtmp, &etiFrame[(index - 30)], 30); +    CRCtmp ^= 0xffff; +    etiFrame[index++] = ((char *) &CRCtmp)[1]; +    etiFrame[index++] = ((char *) &CRCtmp)[0]; + +    figSize = 0; +    // FIB 1 insertion +    switch (rotateFIB) { +        case 0:     // FIG 0/8 program +            fig0 = NULL; + +            if (componentProgFIG0_8 == ensemble->components.end()) { +                componentProgFIG0_8 = ensemble->components.begin(); +            } +            for (; componentProgFIG0_8 != ensemble->components.end(); +                    ++componentProgFIG0_8) { +                service = getService(*componentProgFIG0_8, +                        ensemble->services); +                subchannel = getSubchannel(ensemble->subchannels, +                        (*componentProgFIG0_8)->subchId); +                if (subchannel == ensemble->subchannels.end()) { +                    etiLog.log(error, +                            "Subchannel %i does not exist for component " +                            "of service %i\n", +                            (*componentProgFIG0_8)->subchId, +                            (*componentProgFIG0_8)->serviceId); +                    throw MuxInitException(); +                } + +                if (!(*service)->program) +                    continue; + +                if (fig0 == NULL) { +                    fig0 = (FIGtype0*)&etiFrame[index]; +                    fig0->FIGtypeNumber = 0; +                    fig0->Length = 1; +                    fig0->CN = 0; +                    fig0->OE = 0; +                    fig0->PD = 0; +                    fig0->Extension = 8; +                    index += 2; +                    figSize += 2; +                } + +                if ((*subchannel)->type == Packet) { // Data packet +                    if (figSize > 30 - 5) { +                        break; +                    } +                    etiFrame[index] = +                        ((*componentProgFIG0_8)->serviceId >> 8) & 0xFF; +                    etiFrame[index+1] = +                        ((*componentProgFIG0_8)->serviceId) & 0xFF; +                    fig0->Length += 2; +                    index += 2; +                    figSize += 2; + +                    FIGtype0_8_long* definition = +                        (FIGtype0_8_long*)&etiFrame[index]; +                    memset(definition, 0, 3); +                    definition->ext = 0;    // no rfa +                    definition->SCIdS = (*componentProgFIG0_8)->SCIdS; +                    definition->LS = 1; +                    definition->setSCId((*componentProgFIG0_8)->packet.id); +                    fig0->Length += 3; +                    index += 3;             // 8 minus rfa +                    figSize += 3; +                } +                else {    // Audio, data stream or FIDC +                    if (figSize > 30 - 4) { +                        break; +                    } +                    etiFrame[index] = +                        ((*componentProgFIG0_8)->serviceId >> 8) & 0xFF; +                    etiFrame[index+1] = +                        ((*componentProgFIG0_8)->serviceId) & 0xFF; + +                    fig0->Length += 2; +                    index += 2; +                    figSize += 2; + +                    FIGtype0_8_short* definition = +                        (FIGtype0_8_short*)&etiFrame[index]; +                    memset(definition, 0, 2); +                    definition->ext = 0;    // no rfa +                    definition->SCIdS = (*componentProgFIG0_8)->SCIdS; +                    definition->LS = 0; +                    definition->MscFic = 0; +                    definition->Id = (*componentProgFIG0_8)->subchId; +                    fig0->Length += 2; +                    index += 2;             // 4 minus rfa +                    figSize += 2; +                } +            } +            break; + +        case 1:     // FIG 0/8 data +            fig0 = NULL; + +            if (componentDataFIG0_8 == ensemble->components.end()) { +                componentDataFIG0_8 = ensemble->components.begin(); +            } +            for (; componentDataFIG0_8 != ensemble->components.end(); +                    ++componentDataFIG0_8) { +                service = getService(*componentDataFIG0_8, +                        ensemble->services); + +                subchannel = getSubchannel(ensemble->subchannels, +                        (*componentDataFIG0_8)->subchId); + +                if (subchannel == ensemble->subchannels.end()) { +                    etiLog.log(error, +                            "Subchannel %i does not exist for component " +                            "of service %i\n", +                            (*componentDataFIG0_8)->subchId, +                            (*componentDataFIG0_8)->serviceId); +                    throw MuxInitException(); +                } + +                if ((*service)->program) +                    continue; + +                if (fig0 == NULL) { +                    fig0 = (FIGtype0*)&etiFrame[index]; +                    fig0->FIGtypeNumber = 0; +                    fig0->Length = 1; +                    fig0->CN = 0; +                    fig0->OE = 0; +                    fig0->PD = 1; +                    fig0->Extension = 8; +                    index += 2; +                    figSize += 2; +                } + +                if ((*subchannel)->type == Packet) { // Data packet +                    if (figSize > 30 - 7) { +                        break; +                    } +                    etiFrame[index] = +                        ((*componentDataFIG0_8)->serviceId >> 8) & 0xFF; +                    etiFrame[index+1] = +                        ((*componentDataFIG0_8)->serviceId) & 0xFF; +                    fig0->Length += 4; +                    index += 4; +                    figSize += 4; + +                    FIGtype0_8_long* definition = +                        (FIGtype0_8_long*)&etiFrame[index]; +                    memset(definition, 0, 3); +                    definition->ext = 0;    // no rfa +                    definition->SCIdS = (*componentDataFIG0_8)->SCIdS; +                    definition->LS = 1; +                    definition->setSCId((*componentDataFIG0_8)->packet.id); +                    fig0->Length += 3; +                    index += 3;             // 8 minus rfa +                    figSize += 3; +                } +                else {    // Audio, data stream or FIDC +                    if (figSize > 30 - 6) { +                        break; +                    } +                    etiFrame[index] = +                        ((*componentDataFIG0_8)->serviceId >> 8) & 0xFF; +                    etiFrame[index+1] = +                        ((*componentDataFIG0_8)->serviceId) & 0xFF; +                    fig0->Length += 4; +                    index += 4; +                    figSize += 4; + +                    FIGtype0_8_short* definition = +                        (FIGtype0_8_short*)&etiFrame[index]; +                    memset(definition, 0, 2); +                    definition->ext = 0;    // no rfa +                    definition->SCIdS = (*componentDataFIG0_8)->SCIdS; +                    definition->LS = 0; +                    definition->MscFic = 0; +                    definition->Id = (*componentDataFIG0_8)->subchId; +                    fig0->Length += 2; +                    index += 2;             // 4 minus rfa +                    figSize += 2; +                } +            } +            break; + +        case 3: +            // FIG type 1/0, Service Information (SI), Ensemble Label +            fig1_0 = (FIGtype1_0 *) & etiFrame[index]; + +            fig1_0->Length = 21; +            fig1_0->FIGtypeNumber = 1; +            fig1_0->Extension = 0; +            fig1_0->OE = 0; +            fig1_0->Charset = 0; +            fig1_0->EId = htons(ensemble->id); +            index = index + 4; + +            ensemble->label.writeLabel(&etiFrame[index]); +            index = index + 16; + +            etiFrame[index++] = ensemble->label.flag() >> 8; +            etiFrame[index++] = ensemble->label.flag() & 0xFF; + +            figSize += 22; +            break; + +        case 5: +        case 6: +            // FIG 0 / 13 +            fig0 = NULL; + +            if (componentFIG0_13 == ensemble->components.end()) { +                componentFIG0_13 = ensemble->components.begin(); + +                transmitFIG0_13programme = !transmitFIG0_13programme; +                // Alternate between data and and programme FIG0/13, +                // do not mix fig0 with PD=0 with extension 13 stuff +                // that actually needs PD=1, and vice versa +            } + +            for (; componentFIG0_13 != ensemble->components.end(); +                    ++componentFIG0_13) { + +                subchannel = getSubchannel(ensemble->subchannels, +                        (*componentFIG0_13)->subchId); + +                if (subchannel == ensemble->subchannels.end()) { +                    etiLog.log(error, +                            "Subchannel %i does not exist for component " +                            "of service %i\n", +                            (*componentFIG0_13)->subchId, +                            (*componentFIG0_13)->serviceId); +                    throw MuxInitException(); +                } + +                if (    transmitFIG0_13programme && +                        (*subchannel)->type == Audio && +                        (*componentFIG0_13)->audio.uaType != 0xffff) { +                    if (fig0 == NULL) { +                        fig0 = (FIGtype0*)&etiFrame[index]; +                        fig0->FIGtypeNumber = 0; +                        fig0->Length = 1; +                        fig0->CN = 0; +                        fig0->OE = 0; +                        fig0->PD = 0; +                        fig0->Extension = 13; +                        index += 2; +                        figSize += 2; +                    } + +                    if (figSize > 30 - (3+4+11)) { +                        break; +                    } + +                    FIG0_13_shortAppInfo* info = +                        (FIG0_13_shortAppInfo*)&etiFrame[index]; +                    info->SId = htonl((*componentFIG0_13)->serviceId) >> 16; +                    info->SCIdS = (*componentFIG0_13)->SCIdS; +                    info->No = 1; +                    index += 3; +                    figSize += 3; +                    fig0->Length += 3; + +                    FIG0_13_app* app = (FIG0_13_app*)&etiFrame[index]; +                    app->setType((*componentFIG0_13)->audio.uaType); +                    app->length = 4; +                    app->xpad = htonl(0x0cbc0000); +                    /* xpad meaning +                       CA        = 0 +                       CAOrg     = 0 +                       Rfu       = 0 +                       AppTy(5)  = 12 (MOT, start of X-PAD data group) +                       DG        = 0 (MSC data groups used) +                       Rfu       = 0 +                       DSCTy(6)  = 60 (MOT) +                       CAOrg(16) = 0 +                       */ + +                    index += 2 + app->length; +                    figSize += 2 + app->length; +                    fig0->Length += 2 + app->length; +                } +                else if (!transmitFIG0_13programme && +                        (*subchannel)->type == Packet && +                        (*componentFIG0_13)->packet.appType != 0xffff) { + +                    if (fig0 == NULL) { +                        fig0 = (FIGtype0*)&etiFrame[index]; +                        fig0->FIGtypeNumber = 0; +                        fig0->Length = 1; +                        fig0->CN = 0; +                        fig0->OE = 0; +                        fig0->PD = 1; +                        fig0->Extension = 13; +                        index += 2; +                        figSize += 2; +                    } + +                    if (figSize > 30 - (5+2)) { +                        break; +                    } + +                    FIG0_13_longAppInfo* info = +                        (FIG0_13_longAppInfo*)&etiFrame[index]; +                    info->SId = htonl((*componentFIG0_13)->serviceId); +                    info->SCIdS = (*componentFIG0_13)->SCIdS; +                    info->No = 1; +                    index += 5; +                    figSize += 5; +                    fig0->Length += 5; + +                    FIG0_13_app* app = (FIG0_13_app*)&etiFrame[index]; +                    app->setType((*componentFIG0_13)->packet.appType); +                    app->length = 0; +                    index += 2; +                    figSize += 2; +                    fig0->Length += 2; +                } +            } +            break; + +        case 7: +            //Time and country identifier +            fig0_10 = (FIGtype0_10 *) & etiFrame[index]; + +            fig0_10->FIGtypeNumber = 0; +            fig0_10->Length = 5; +            fig0_10->CN = 0; +            fig0_10->OE = 0; +            fig0_10->PD = 0; +            fig0_10->Extension = 10; +            index = index + 2; + +            timeData = gmtime(&date); + +            fig0_10->RFU = 0; +            fig0_10->setMJD(gregorian2mjd(timeData->tm_year + 1900, +                        timeData->tm_mon + 1, +                        timeData->tm_mday)); +            fig0_10->LSI = 0; +            fig0_10->ConfInd = (m_watermarkData[m_watermarkPos >> 3] >> +                    (7 - (m_watermarkPos & 0x07))) & 1; +            if (++m_watermarkPos == m_watermarkSize) { +                m_watermarkPos = 0; +            } +            fig0_10->UTC = 0; +            fig0_10->setHours(timeData->tm_hour); +            fig0_10->Minutes = timeData->tm_min; +            index = index + 4; +            figSize += 6; + +            fig0_9 = (FIGtype0_9*)&etiFrame[index]; +            fig0_9->FIGtypeNumber = 0; +            fig0_9->Length = 4; +            fig0_9->CN = 0; +            fig0_9->OE = 0; +            fig0_9->PD = 0; +            fig0_9->Extension = 9; + +            fig0_9->ext = 0; +            fig0_9->lto = 0; // Unique LTO for ensemble + +            if (ensemble->lto_auto) { +                time_t now = time(NULL); +                struct tm* ltime = localtime(&now); +                time_t now2 = timegm(ltime); +                ensemble->lto = (now2 - now) / 1800; +            } + +            if (ensemble->lto >= 0) { +                fig0_9->ensembleLto = ensemble->lto; +            } +            else { +                /* Convert to 1-complement representation */ +                fig0_9->ensembleLto = (-ensemble->lto) | (1<<5); +            } + +            fig0_9->ensembleEcc = ensemble->ecc; +            fig0_9->tableId = ensemble->international_table; +            index += 5; +            figSize += 5; + +            break; +    } + +    assert(figSize <= 30); +    memcpy(&etiFrame[index], Padding_FIB, 30 - figSize); +    index += 30 - figSize; + +    CRCtmp = 0xffff; +    CRCtmp = crc16(CRCtmp, &etiFrame[(index - 30)], 30); +    CRCtmp ^= 0xffff; +    etiFrame[index++] = ((char *) &CRCtmp)[1]; +    etiFrame[index++] = ((char *) &CRCtmp)[0]; + + +    figSize = 0; +    // FIB 2 insertion +    if (rotateFIB < ensemble->services.size()) { +        service = ensemble->services.begin() + rotateFIB; + +        // FIG type 1/1, SI, Service label, one instance per subchannel +        if ((*service)->getType(ensemble) == 0) { +            fig1_1 = (FIGtype1_1 *) & etiFrame[index]; + +            fig1_1->FIGtypeNumber = 1; +            fig1_1->Length = 21; +            fig1_1->Charset = 0; +            fig1_1->OE = 0; +            fig1_1->Extension = 1; + +            fig1_1->Sld = htons((*service)->id); +            index += 4; +            figSize += 4; +        } +        else { +            fig1_5 = (FIGtype1_5 *) & etiFrame[index]; +            fig1_5->FIGtypeNumber = 1; +            fig1_5->Length = 23; +            fig1_5->Charset = 0; +            fig1_5->OE = 0; +            fig1_5->Extension = 5; + +            fig1_5->SId = htonl((*service)->id); +            index += 6; +            figSize += 6; +        } +        (*service)->label.writeLabel(&etiFrame[index]); +        index += 16; +        figSize += 16; +        etiFrame[index++] = (*service)->label.flag() >> 8; +        etiFrame[index++] = (*service)->label.flag() & 0xFF; +        figSize += 2; +    } +    else if (rotateFIB < +            ensemble->services.size() + ensemble->components.size()) { +        component = ensemble->components.begin() + +            (rotateFIB - ensemble->services.size()); + +        service = getService(*component, ensemble->services); + +        subchannel = +            getSubchannel(ensemble->subchannels, (*component)->subchId); + +        if (not (*component)->label.long_label().empty() ) { +            if ((*service)->getType(ensemble) == 0) {   // Programme +                FIGtype1_4_programme *fig1_4; +                fig1_4 = (FIGtype1_4_programme*)&etiFrame[index]; + +                fig1_4->FIGtypeNumber = 1; +                fig1_4->Length = 22; +                fig1_4->Charset = 0; +                fig1_4->OE = 0; +                fig1_4->Extension = 4; +                fig1_4->PD = 0; +                fig1_4->rfa = 0; +                fig1_4->SCIdS = (*component)->SCIdS; + +                fig1_4->SId = htons((*service)->id); +                index += 5; +                figSize += 5; +            } +            else {    // Data +                FIGtype1_4_data *fig1_4; +                fig1_4 = (FIGtype1_4_data *) & etiFrame[index]; +                fig1_4->FIGtypeNumber = 1; +                fig1_4->Length = 24; +                fig1_4->Charset = 0; +                fig1_4->OE = 0; +                fig1_4->Extension = 4; +                fig1_4->PD = 1; +                fig1_4->rfa = 0; +                fig1_4->SCIdS = (*component)->SCIdS; + +                fig1_4->SId = htonl((*service)->id); +                index += 7; +                figSize += 7; +            } +            (*component)->label.writeLabel(&etiFrame[index]); +            index += 16; +            figSize += 16; + +            etiFrame[index++] = (*component)->label.flag() >> 8; +            etiFrame[index++] = (*component)->label.flag() & 0xFF; +            figSize += 2; +        } +    } +    memcpy(&etiFrame[index], Padding_FIB, 30 - figSize); +    index += 30 - figSize; + +    CRCtmp = 0xffff; +    CRCtmp = crc16(CRCtmp, &etiFrame[(index - 30)], 30); +    CRCtmp ^= 0xffff; +    etiFrame[index++] = ((char *) &CRCtmp)[1]; +    etiFrame[index++] = ((char *) &CRCtmp)[0]; + +    /* ETSI EN 300 799 Table 2: +     * Only TM3 has a FIB count to CIF count that is +     * not 3 to 1. +     */ +    if (ensemble->mode == 3) { +        memcpy(&etiFrame[index], Padding_FIB, 30); +        index += 30; + +        CRCtmp = 0xffff; +        CRCtmp = crc16(CRCtmp, &etiFrame[(index - 30)], 30); +        CRCtmp ^= 0xffff; +        etiFrame[index++] = ((char *) &CRCtmp)[1]; +        etiFrame[index++] = ((char *) &CRCtmp)[0]; +    } + +    if (ensemble->services.size() > 30) { +        etiLog.log(error, +                "Sorry, but this software currently can't write " +                "Service Label of more than 30 services.\n"); +        throw MuxInitException(); +    } + +    // counter for FIG 0/0 +    insertFIG = (insertFIG + 1) % 16; + +    // We rotate through the FIBs every 30 frames +    rotateFIB = (rotateFIB + 1) % 30; + +    /********************************************************************** +     ******  Input Data Reading ******************************************* +     **********************************************************************/ + +    for (subchannel = ensemble->subchannels.begin(); +            subchannel != ensemble->subchannels.end(); +            ++subchannel) { + +        TagESTn* tag = edi_subchannelToTag[*subchannel]; + +        int sizeSubchannel = getSizeByte(*subchannel); +        int result = (*subchannel)->input->readFrame( +                &etiFrame[index], sizeSubchannel); + +        if (result < 0) { +            etiLog.log(info, +                    "Subchannel %d read failed at ETI frame number: %d\n", +                    (*subchannel)->id, currentFrame); +        } + +        // save pointer to Audio or Data Stream into correct TagESTn for EDI +        tag->mst_data = &etiFrame[index]; + +        index += sizeSubchannel; +    } + + +    index = (3 + fc->NST + FICL) * 4; +    for (subchannel = ensemble->subchannels.begin(); +            subchannel != ensemble->subchannels.end(); +            ++subchannel) { +        index += getSizeByte(*subchannel); +    } + +    /******* Section EOF **************************************************/ +    // End of Frame, 4 octets +    index = (FLtmp + 1 + 1) * 4; +    eti_EOF *eof = (eti_EOF *) & etiFrame[index]; + +    // CRC of Main Stream data (MST), 16 bits +    index = ((fc->NST) + 2 + 1) * 4;            // MST position + +    unsigned short MSTsize = ((FLtmp) - 1 - (fc->NST)) * 4;    // data size + +    CRCtmp = 0xffff; +    CRCtmp = crc16(CRCtmp, &etiFrame[index], MSTsize); +    CRCtmp ^= 0xffff; +    eof->CRC = htons(CRCtmp); + +    //RFU, Reserved for future use, 2 bytes, should be 0xFFFF +    eof->RFU = htons(0xFFFF); + +    /******* Section TIST *************************************************/ +    // TimeStamps, 24 bits + 1 octet +    index = (FLtmp + 2 + 1) * 4; +    eti_TIST *tist = (eti_TIST *) & etiFrame[index]; + +    bool enableTist = m_pt.get("general.tist", false); +    if (enableTist) { +        tist->TIST = htonl(timestamp) | 0xff; +    } +    else { +        tist->TIST = htonl(0xffffff) | 0xff; +    } + +    timestamp += 3 << 17; +    if (timestamp > 0xf9ffff) +    { +        timestamp -= 0xfa0000; + +        // Also update MNSC time for next frame +        MNSC_increment_time = true; +    } + + + +    /**********************************************************************  +     ***********   Section FRPD   ***************************************** +     **********************************************************************/ + +    int frame_size = (FLtmp + 1 + 1 + 1 + 1) * 4; + +    // Give the data to the outputs +    for (auto output : outputs) { +        if (output->Write(etiFrame, frame_size) == -1) { +            etiLog.level(error) << +                "Can't write to output " << +                output->get_info(); +        } +    } + +#ifdef DUMP_BRIDGE +    dumpBytes(dumpData, sizeSubChannel, stderr); +#endif // DUMP_BRIDGE + +#if HAVE_OUTPUT_EDI +    /**********************************************************************  +     ***********   Finalise and send EDI   ******************************** +     **********************************************************************/ + +    if (edi_conf.enabled) { +        // put tags *ptr, DETI and all subchannels into one TagPacket +        edi_tagpacket.tag_items.push_back(&edi_tagStarPtr); +        edi_tagpacket.tag_items.push_back(&edi_tagDETI); + +        list<TagESTn>::iterator tag; +        for (tag = edi_subchannels.begin(); tag != edi_subchannels.end(); ++tag) { +            edi_tagpacket.tag_items.push_back(&(*tag)); +        } + +        // Assemble into one AF Packet +        AFPacket edi_afpacket = edi_afPacketiser.Assemble(edi_tagpacket); + +        if (edi_conf.enable_pft) { +            // Apply PFT layer to AF Packet (Reed Solomon FEC and Fragmentation) +            vector< PFTFragment > edi_fragments = +                edi_pft.Assemble(edi_afpacket); + +            // Send over ethernet +            vector< vector<uint8_t> >::iterator edi_frag; +            for (edi_frag = edi_fragments.begin(); +                    edi_frag != edi_fragments.end(); +                    ++edi_frag) { + +                UdpPacket udppacket; + +                InetAddress& addr = udppacket.getAddress(); +                addr.setAddress(edi_conf.dest_addr.c_str()); +                addr.setPort(edi_conf.dest_port); + +                udppacket.addData(&(edi_frag->front()), edi_frag->size()); + +                edi_output.send(udppacket); + +                if (edi_conf.dump) { +                    std::ostream_iterator<uint8_t> debug_iterator(edi_debug_file); +                    std::copy(edi_frag->begin(), edi_frag->end(), debug_iterator); +                } +            } + +            if (edi_conf.verbose) { +                fprintf(stderr, "EDI number of PFT fragments %zu\n", +                        edi_fragments.size()); +            } +        } +        else { +            // Send over ethernet + +            UdpPacket udppacket; + +            InetAddress& addr = udppacket.getAddress(); +            addr.setAddress(edi_conf.dest_addr.c_str()); +            addr.setPort(edi_conf.dest_port); + +            udppacket.addData(&(edi_afpacket.front()), edi_afpacket.size()); + +            edi_output.send(udppacket); +        } + +        if (edi_conf.dump) { +            std::ostream_iterator<uint8_t> debug_iterator(edi_debug_file); +            std::copy(edi_afpacket.begin(), edi_afpacket.end(), debug_iterator); +        } +    } +#endif // HAVE_OUTPUT_EDI + +    if (currentFrame % 100 == 0) { +        etiLog.log(info, "ETI frame number %i Time: %d, no TIST\n", +                currentFrame, mnsc_time.tv_sec); +    } + +#if _DEBUG +    /**********************************************************************  +     ***********   Output a small message ********************************* +     **********************************************************************/ +    if (currentFrame % 100 == 0) { +        if (enableTist) { +            etiLog.log(info, "ETI frame number %i Timestamp: %d + %f\n", +                    currentFrame, mnsc_time.tv_sec, +                    (timestamp & 0xFFFFFF) / 16384000.0); +        } +        else { +            etiLog.log(info, "ETI frame number %i Time: %d, no TIST\n", +                    currentFrame, mnsc_time.tv_sec); +        } +    } +#endif + +    currentFrame++; +} + +void DabMultiplexer::print_info(void) +{ +    return; +    // Print settings before starting +    etiLog.log(info, "--- Multiplex configuration ---"); +    printEnsemble(ensemble); + +    etiLog.log(info, "--- Subchannels list ---"); +    printSubchannels(ensemble->subchannels); + +    etiLog.log(info, "--- Services list ---"); +    printServices(ensemble->services); + +    etiLog.log(info, "--- Components list ---"); +    printComponents(ensemble->components); +} + diff --git a/src/DabMultiplexer.h b/src/DabMultiplexer.h new file mode 100644 index 0000000..27cc063 --- /dev/null +++ b/src/DabMultiplexer.h @@ -0,0 +1,510 @@ +/* +   Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, +   2011, 2012 Her Majesty the Queen in Right of Canada (Communications +   Research Center Canada) + +   Copyright (C) 2015 +   Matthias P. Braendli, matthias.braendli@mpb.li +   */ +/* +   This file is part of ODR-DabMux. + +   ODR-DabMux 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 (at your option) any later version. + +   ODR-DabMux 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 ODR-DabMux.  If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __DAB_MULTIPLEXER_H__ +#define __DAB_MULTIPLEXER_H__ + +#ifdef HAVE_CONFIG_H +#   include "config.h" +#endif + +#include "dabOutput/dabOutput.h" +#include "dabOutput/edi/TagItems.h" +#include "dabOutput/edi/TagPacket.h" +#include "dabOutput/edi/AFPacket.h" +#include "dabOutput/edi/PFT.h" +#include "crc.h" +#include "utils.h" +#include "UdpSocket.h" +#include "InetAddress.h" +#include "dabUtils.h" +#include "PcDebug.h" +#include "MuxElements.h" +#include "RemoteControl.h" +#include "Eti.h" +#include <exception> +#include <vector> +#include <memory> +#include <string> +#include <boost/shared_ptr.hpp> +#include <boost/property_tree/ptree.hpp> + +class MuxInitException : public std::exception +{ +    public: +        MuxInitException(const std::string m = "ODR-DabMux initialisation error") +            throw() +            : msg(m) {} +        ~MuxInitException(void) throw() {} +        const char* what() const throw() { return msg.c_str(); } +    private: +        std::string msg; +}; + +class DabMultiplexer { +    public: +        DabMultiplexer(boost::shared_ptr<BaseRemoteController> rc, +               boost::property_tree::ptree pt); +        void prepare(void); + +        unsigned long getCurrentFrame() { return currentFrame; } + +        void mux_frame(std::vector<boost::shared_ptr<DabOutput> >& outputs); + +        void print_info(void); + +        void update_config(boost::property_tree::ptree pt); + +        void set_edi_config(const edi_configuration_t& new_edi_conf); + +    private: +        void prepare_watermark(void); +        void prepare_subchannels(void); +        void prepare_services_components(void); +        void prepare_data_inputs(void); +        void reconfigure(void); + +        boost::property_tree::ptree m_pt; +        boost::shared_ptr<BaseRemoteController> m_rc; + +        unsigned timestamp; +        bool MNSC_increment_time; +        struct timeval mnsc_time; + +        edi_configuration_t edi_conf; + + +        uint8_t m_watermarkData[128]; +        size_t  m_watermarkSize; +        size_t  m_watermarkPos; + +        uint32_t sync; +        unsigned long currentFrame; + +        /* FIG carousel logic and iterators */ +        unsigned int insertFIG; +        unsigned int rotateFIB; + +        std::vector<std::shared_ptr<DabService> >::iterator serviceProgFIG0_2; +        std::vector<std::shared_ptr<DabService> >::iterator serviceDataFIG0_2; +        std::vector<std::shared_ptr<DabService> >::iterator serviceFIG0_17; + +        std::vector<DabComponent*>::iterator componentProgFIG0_8; +        std::vector<DabComponent*>::iterator componentDataFIG0_8; +        std::vector<DabComponent*>::iterator componentFIG0_13; +        // Alternate between programme and data +        bool transmitFIG0_13programme; + + +        std::vector<dabSubchannel*>::iterator subchannelFIG0_1; + +        boost::shared_ptr<dabEnsemble> ensemble; + +        // Multiplex reconfiguration requires two sets of configurations +        boost::property_tree::ptree m_pt_next; +        boost::shared_ptr<dabEnsemble> ensemble_next; + +#if HAVE_OUTPUT_EDI +        std::ofstream edi_debug_file; +        UdpSocket edi_output; + +        // The TagPacket will then be placed into an AFPacket +        AFPacketiser edi_afPacketiser; + +        // The AF Packet will be protected with reed-solomon and split in fragments +        PFT edi_pft; +#endif // HAVE_OUTPUT_EDI +}; + +// DAB Mode +#define DEFAULT_DAB_MODE    2 + +// Taille de la trame de donnee, sous-canal 3, nb de paquets de 64bits, +// STL3 * 8 = x kbytes par trame ETI + +// Data bitrate in kbits/s. Must be 64 kb/s multiple. +#define DEFAULT_DATA_BITRATE    384 +#define DEFAULT_PACKET_BITRATE  32 + +/* default ensemble parameters. Label must be max 16 chars, short label + * a subset of the label, max 8 chars + */ +#define DEFAULT_ENSEMBLE_LABEL          "ODR Dab Mux" +#define DEFAULT_ENSEMBLE_SHORT_LABEL    "ODRMux" +#define DEFAULT_ENSEMBLE_ID             0xc000 +#define DEFAULT_ENSEMBLE_ECC            0xa1 + +// start value for default service IDs (if not overridden by configuration) +#define DEFAULT_SERVICE_ID      50 +#define DEFAULT_PACKET_ADDRESS  0 + + +/***************************************************************************** + *****************   Definition of FIG structures **************************** + *****************************************************************************/ + +#ifdef _WIN32 +#   pragma pack(push) +#endif + +struct FIGtype0 { +    uint8_t Length:5; +    uint8_t FIGtypeNumber:3; +    uint8_t Extension:5; +    uint8_t PD:1; +    uint8_t OE:1; +    uint8_t CN:1; +} PACKED; + + +struct FIGtype0_0 { +    uint8_t Length:5; +    uint8_t FIGtypeNumber:3; +    uint8_t Extension:5; +    uint8_t PD:1; +    uint8_t OE:1; +    uint8_t CN:1; + +    uint16_t EId; +    uint8_t CIFcnt_hight:5; +    uint8_t Al:1; +    uint8_t Change:2; +    uint8_t CIFcnt_low:8; +} PACKED; + + +struct FIGtype0_2 { +    uint8_t Length:5; +    uint8_t FIGtypeNumber:3; +    uint8_t Extension:5; +    uint8_t PD:1; +    uint8_t OE:1; +    uint8_t CN:1; +} PACKED; + + +struct FIGtype0_2_Service { +    uint16_t SId; +    uint8_t NbServiceComp:4; +    uint8_t CAId:3; +    uint8_t Local_flag:1; +} PACKED; + + +struct FIGtype0_2_Service_data { +    uint32_t SId; +    uint8_t NbServiceComp:4; +    uint8_t CAId:3; +    uint8_t Local_flag:1; +} PACKED; + + +struct FIGtype0_2_audio_component { +    uint8_t ASCTy:6; +    uint8_t TMid:2; +    uint8_t CA_flag:1; +    uint8_t PS:1; +    uint8_t SubChId:6; +} PACKED; + + +struct FIGtype0_2_data_component { +    uint8_t DSCTy:6; +    uint8_t TMid:2; +    uint8_t CA_flag:1; +    uint8_t PS:1; +    uint8_t SubChId:6; +} PACKED; + + +struct FIGtype0_2_packet_component { +    uint8_t SCId_high:6; +    uint8_t TMid:2; +    uint8_t CA_flag:1; +    uint8_t PS:1; +    uint8_t SCId_low:6; +    void setSCId(uint16_t SCId) { +        SCId_high = SCId >> 6; +        SCId_low = SCId & 0x3f; +    } +} PACKED; + + +struct FIGtype0_3_header { +    uint8_t Length:5; +    uint8_t FIGtypeNumber:3; +    uint8_t Extension:5; +    uint8_t PD:1; +    uint8_t OE:1; +    uint8_t CN:1; +} PACKED; + + +/* Warning: When bit SCCA_flag is unset(0), the multiplexer R&S does not send + * the SCCA field. But, in the Factum ETI analyzer, if this field is not there, + * it is an error. + */ +struct FIGtype0_3_data { +    uint8_t SCId_high; +    uint8_t SCCA_flag:1; +    uint8_t rfa:3; +    uint8_t SCId_low:4; +    uint8_t DSCTy:6; +    uint8_t rfu:1; +    uint8_t DG_flag:1; +    uint8_t Packet_address_high:2; +    uint8_t SubChId:6; +    uint8_t Packet_address_low; +    uint16_t SCCA; +    void setSCId(uint16_t SCId) { +        SCId_high = SCId >> 4; +        SCId_low = SCId & 0xf; +    } +    void setPacketAddress(uint16_t address) { +        Packet_address_high = address >> 8; +        Packet_address_low = address & 0xff; +    } +} PACKED; + + +struct FIGtype0_8_short { +    uint8_t SCIdS:4; +    uint8_t rfa_1:3; +    uint8_t ext:1; +    uint8_t Id:6; +    uint8_t MscFic:1; +    uint8_t LS:1; +    uint8_t rfa_2; +} PACKED; + + +struct FIGtype0_8_long { +    uint8_t SCIdS:4; +    uint8_t rfa_1:3; +    uint8_t ext:1; +    uint8_t SCId_high:4; +    uint8_t rfa:3; +    uint8_t LS:1; +    uint8_t SCId_low; +    uint8_t rfa_2; +    void setSCId(uint16_t id) { +        SCId_high = id >> 8; +        SCId_low = id & 0xff; +    } +    uint16_t getSCid() { +        return (SCId_high << 8) | SCId_low; +    } +} PACKED; + + +struct FIGtype0_9 { +    uint8_t Length:5; +    uint8_t FIGtypeNumber:3; +    uint8_t Extension:5; +    uint8_t PD:1; +    uint8_t OE:1; +    uint8_t CN:1; + +    uint8_t ensembleLto:6; +    uint8_t lto:1; +    uint8_t ext:1; +    uint8_t ensembleEcc; +    uint8_t tableId; +} PACKED; + + +struct FIGtype0_10 { +    uint8_t Length:5; +    uint8_t FIGtypeNumber:3; +    uint8_t Extension:5; +    uint8_t PD:1; +    uint8_t OE:1; +    uint8_t CN:1; + +    uint8_t MJD_high:7; +    uint8_t RFU:1; +    uint8_t MJD_med; +    uint8_t Hours_high:3; +    uint8_t UTC:1; +    uint8_t ConfInd:1; +    uint8_t LSI:1; +    uint8_t MJD_low:2; +    uint8_t Minutes:6; +    uint8_t Hours_low:2; +    void setMJD(uint32_t date) { +        MJD_high = (date >> 10) & 0x7f; +        MJD_med = (date >> 2) & 0xff; +        MJD_low = date & 0x03; +    } +    void setHours(uint16_t hours) { +        Hours_high = (hours >> 2) & 0x07; +        Hours_low = hours & 0x03; +    } +} PACKED; + + +struct FIGtype0_17_programme { +    uint16_t SId; +    uint8_t NFC:2; +    uint8_t Rfa:2; +    uint8_t CC:1; +    uint8_t L:1; +    uint8_t PS:1; +    uint8_t SD:1; +} PACKED; + + +struct FIGtype0_1 { +    uint8_t Length:5; +    uint8_t FIGtypeNumber:3; +    uint8_t Extension:5; +    uint8_t PD:1; +    uint8_t OE:1; +    uint8_t CN:1; +} PACKED; + + +struct FIG_01_SubChannel_ShortF { +    uint8_t StartAdress_high:2; +    uint8_t SubChId:6; +    uint8_t StartAdress_low:8; +    uint8_t TableIndex:6; +    uint8_t TableSwitch:1; +    uint8_t Short_Long_form:1; +} PACKED; + + +struct FIG_01_SubChannel_LongF { +    uint8_t StartAdress_high:2; +    uint8_t SubChId:6; +    uint8_t StartAdress_low:8; +    uint8_t Sub_ChannelSize_high:2; +    uint8_t ProtectionLevel:2; +    uint8_t Option:3; +    uint8_t Short_Long_form:1; +    uint8_t Sub_ChannelSize_low:8; +} PACKED; + + +// See EN 300 401, Clause 8.1.20 for the FIG0_13 description +struct FIG0_13_shortAppInfo { +    uint16_t SId; +    uint8_t No:4; +    uint8_t SCIdS:4; +} PACKED; + + +struct FIG0_13_longAppInfo { +    uint32_t SId; +    uint8_t No:4; +    uint8_t SCIdS:4; +} PACKED; + + +struct FIG0_13_app { +    uint8_t typeHigh; +    uint8_t length:5; +    uint8_t typeLow:3; +    void setType(uint16_t type) { +        typeHigh = type >> 3; +        typeLow = type & 0x1f; +    } +    uint32_t xpad; +} PACKED; + +#define FIG0_13_APPTYPE_SLIDESHOW  0x2 +#define FIG0_13_APPTYPE_WEBSITE    0x3 +#define FIG0_13_APPTYPE_TPEG       0x4 +#define FIG0_13_APPTYPE_DGPS       0x5 +#define FIG0_13_APPTYPE_TMC        0x6 +#define FIG0_13_APPTYPE_EPG        0x7 +#define FIG0_13_APPTYPE_DABJAVA    0x8 +#define FIG0_13_APPTYPE_JOURNALINE 0x441 + + +struct FIGtype1_0 { +    uint8_t Length:5; +    uint8_t FIGtypeNumber:3; +    uint8_t Extension:3; +    uint8_t OE:1; +    uint8_t Charset:4; + +    uint16_t EId; +} PACKED; + + +struct FIGtype1_1 { +    uint8_t Length:5; +    uint8_t FIGtypeNumber:3; +    uint8_t Extension:3; +    uint8_t OE:1; +    uint8_t Charset:4; + +    uint16_t Sld; +} PACKED; + + +struct FIGtype1_5 { +    uint8_t Length:5; +    uint8_t FIGtypeNumber:3; +    uint8_t Extension:3; +    uint8_t OE:1; +    uint8_t Charset:4; +    uint32_t SId; +} PACKED; + + +struct FIGtype1_4_programme { +    uint8_t Length:5; +    uint8_t FIGtypeNumber:3; +    uint8_t Extension:3; +    uint8_t OE:1; +    uint8_t Charset:4; +    uint8_t SCIdS:4; +    uint8_t rfa:3; +    uint8_t PD:1; +    uint16_t SId; +} PACKED; + + +struct FIGtype1_4_data { +    uint8_t Length:5; +    uint8_t FIGtypeNumber:3; +    uint8_t Extension:3; +    uint8_t OE:1; +    uint8_t Charset:4; +    uint8_t SCIdS:4; +    uint8_t rfa:3; +    uint8_t PD:1; +    uint32_t SId; +} PACKED; + + +#ifdef _WIN32 +#   pragma pack(pop) +#endif + +#endif + diff --git a/src/DabMux.cpp b/src/DabMux.cpp index 8ef30ba..20dc31d 100644 --- a/src/DabMux.cpp +++ b/src/DabMux.cpp @@ -29,6 +29,7 @@  #   include "config.h"  #endif +#include <boost/shared_ptr.hpp>  #include <boost/property_tree/ptree.hpp>  #include <boost/property_tree/info_parser.hpp>  #include <cstdio> @@ -138,51 +139,6 @@ using boost::property_tree::ptree;  using boost::property_tree::ptree_error; -/* Global stats and config server */ -ManagementServer* mgmt_server; - -class MuxInitException : public exception -{ -    public: -        MuxInitException(const string m = "DABMUX initialisation error") throw() -            : msg(m) {} -        ~MuxInitException(void) throw() {} -        const char* what() const throw() { return msg.c_str(); } -    private: -        string msg; -}; - -static unsigned char Padding_FIB[] = { -    0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - - -// Protection levels and bitrates for UEP. -const unsigned char ProtectionLevelTable[64] = { -    4, 3, 2, 1, 0, 4, 3, 2, -    1, 0, 4, 3, 2, 1, 4, 3, -    2, 1, 0, 4, 3, 2, 1, 0, -    4, 3, 2, 1, 0, 4, 3, 2, -    1, 4, 3, 2, 1, 0, 4, 3, -    2, 1, 0, 4, 3, 2, 1, 0, -    4, 3, 2, 1, 0, 4, 3, 2, -    1, 0, 4, 3, 1, 4, 2, 0 -}; - -const unsigned short BitRateTable[64] = { -    32, 32, 32, 32, 32, 48, 48, 48, -    48, 48, 56, 56, 56, 56, 64, 64, -    64, 64, 64, 80, 80, 80, 80, 80, -    96, 96, 96, 96, 96, 112, 112, 112, -    112, 128, 128, 128, 128, 128, 160, 160, -    160, 160, 160, 192, 192, 192, 192, 192, -    224, 224, 224, 224, 224, 256, 256, 256, -    256, 256, 320, 320, 320, 384, 384, 384 -}; -  volatile sig_atomic_t running = 1; @@ -247,117 +203,23 @@ int main(int argc, char *argv[])                  strerror(errno));      }  #else -    if (setpriority(PRIO_PROCESS, 0, -20) == -1) { -        etiLog.log(warn, "Can't increase priority: %s\n", -                strerror(errno)); +    // Use the lowest real-time priority for this thread, and switch to real-time scheduling +    const int policy = SCHED_RR; +    sched_param sp; +    sp.sched_priority = sched_get_priority_min(policy); +    int thread_prio_ret = pthread_setschedparam(pthread_self(), policy, &sp); +    if (thread_prio_ret != 0) { +        etiLog.level(error) << "Could not set real-time priority for thread:" << thread_prio_ret;      }  #endif -    /*sched_param scheduler; -      scheduler.sched_priority = 99;        // sched_get_priority_max(SCHED_RR) -      if (sched_setscheduler(0, SCHED_RR, &scheduler) == -1) { -      etiLog.log(warn, "Can't increased priority: %s\n", -      strerror(errno)); -      }*/ - -    uint8_t watermarkData[128]; -    size_t watermarkSize = 0; -    size_t watermarkPos = 0; -    { -        uint8_t buffer[sizeof(watermarkData) / 2]; -        snprintf((char*)buffer, sizeof(buffer), -                "%s %s, compiled at %s, %s", -                PACKAGE_NAME, PACKAGE_VERSION, __DATE__, __TIME__); -        memset(watermarkData, 0, sizeof(watermarkData)); -        watermarkData[0] = 0x55; // Sync -        watermarkData[1] = 0x55; -        watermarkSize = 16; -        for (unsigned i = 0; i < strlen((char*)buffer); ++i) { -            for (int j = 0; j < 8; ++j) { -                uint8_t bit = (buffer[watermarkPos >> 3] >> (7 - (watermarkPos & 0x07))) & 1; -                watermarkData[watermarkSize >> 3] |= bit << (7 - (watermarkSize & 0x07)); -                ++watermarkSize; -                bit = 1; -                watermarkData[watermarkSize >> 3] |= bit << (7 - (watermarkSize & 0x07)); -                ++watermarkSize; -                ++watermarkPos; -            } -        } -    } -    watermarkPos = 0; - - -    dabEnsemble* ensemble = new dabEnsemble; -    ensemble->label.setLabel(DEFAULT_ENSEMBLE_LABEL, DEFAULT_ENSEMBLE_SHORT_LABEL); -    ensemble->mode = DEFAULT_DAB_MODE; -    ensemble->id = DEFAULT_ENSEMBLE_ID; -    ensemble->ecc = DEFAULT_ENSEMBLE_ECC; - -    vector<dabOutput*> outputs; -    vector<DabService*>::iterator service = ensemble->services.end(); -    vector<DabService*>::iterator serviceProgFIG0_2; -    vector<DabService*>::iterator serviceDataFIG0_2; -    vector<DabService*>::iterator serviceFIG0_17; -    vector<DabComponent*>::iterator component = ensemble->components.end(); -    vector<DabComponent*>::iterator componentProgFIG0_8; -    vector<DabComponent*>::iterator componentFIG0_13; -    bool  transmitFIG0_13programme = false; // Alternate between programme and data -    vector<DabComponent*>::iterator componentDataFIG0_8; -    vector<dabSubchannel*>::iterator subchannel = ensemble->subchannels.end(); -    vector<dabSubchannel*>::iterator subchannelFIG0_1; -    vector<dabOutput*>::iterator output; -    dabProtection* protection = NULL; - -    BaseRemoteController* rc = NULL; - -    unsigned long currentFrame; -    int returnCode = 0; -    int cur; -    unsigned char etiFrame[6144]; -    unsigned short index = 0; -    // FIC Length, DAB Mode I, II, IV -> FICL = 24, DAB Mode III -> FICL = 32 -    unsigned FICL  = (ensemble->mode == 3 ? 32 : 24); - -    uint32_t sync = 0x49C5F8; -    unsigned short FLtmp = 0; -    unsigned short nbBytesCRC = 0; -    unsigned short CRCtmp = 0xFFFF; -    unsigned short MSTsize = 0; - -    unsigned int insertFIG = 0; -    unsigned int rotateFIB = 0; - -    bool factumAnalyzer = false; -    unsigned long limit = 0; -    time_t date; -    bool enableTist = false; -    unsigned timestamp = 0; - -    int mgmtserverport = 0; - -    edi_configuration_t edi_conf; - -    // Defaults for edi -    edi_conf.enabled     = false; -    edi_conf.dest_addr   = ""; -    edi_conf.dest_port   = 0; -    edi_conf.source_port = 0; -    edi_conf.dump        = false; -    edi_conf.enable_pft  = false; -    ptree pt; -    struct timeval mnsc_time; -    /* TODO: -     * In a SFN, when reconfiguring the ensemble, the multiplexer -     * has to be restarted (odr-dabmux doesn't support reconfiguration). -     * Ideally, we must be able to restart transmission s.t. the receiver -     * synchronisation is preserved. -     */ -    gettimeofday(&mnsc_time, NULL); +    int returnCode = 0; -    bool MNSC_increment_time = false; +    ptree pt; +    std::vector<boost::shared_ptr<DabOutput> > outputs;      try {          if (argc == 2) { // Assume the only argument is a config file @@ -365,19 +227,15 @@ int main(int argc, char *argv[])              if (conf_file == "-h") {                  printUsage(argv[0], stdout); -                throw MuxInitException(); +                throw MuxInitException("Nothing to do");              }              try {                  read_info(conf_file, pt); -                parse_ptree(pt, outputs, ensemble, &enableTist, &FICL, -                        &factumAnalyzer, &limit, &rc, &mgmtserverport, &edi_conf);              }              catch (runtime_error &e) { -                etiLog.log(error, "Configuration file parsing error: %s\n", -                        e.what()); -                throw MuxInitException(); +                throw MuxInitException(e.what());              }          }          else if (argc > 1 && strncmp(argv[1], "-e", 2) == 0) { // use external config file @@ -391,14 +249,9 @@ int main(int argc, char *argv[])                  string conf_file = argv[2];                  read_info(conf_file, pt); - -                parse_ptree(pt, outputs, ensemble, &enableTist, &FICL, -                        &factumAnalyzer, &limit, &rc, &mgmtserverport, &edi_conf);              }              catch (runtime_error &e) { -                etiLog.log(error, "Configuration file parsing error: %s\n", -                        e.what()); -                throw MuxInitException(); +                throw MuxInitException(e.what());              }          }  #if ENABLE_CMDLINE_OPTIONS @@ -410,22 +263,31 @@ int main(int argc, char *argv[])          }  #else          else { -            etiLog.level(error) << "You must specify the configuration file"; -            throw MuxInitException(); +            throw MuxInitException("No configuration file specified");          }  #endif -        if (mgmtserverport != 0) { -            mgmt_server = new ManagementServer(mgmtserverport); +        int mgmtserverport = pt.get<int>("general.managementport", +                             pt.get<int>("general.statsserverport", 0) ); + +        /* Management: stats and config server */ +        get_mgmt_server().open(mgmtserverport); + +        /************** READ REMOTE CONTROL PARAMETERS *************/ +        int telnetport = pt.get<int>("remotecontrol.telnetport", 0); + +        boost::shared_ptr<BaseRemoteController> rc; + +        if (telnetport != 0) { +            rc = boost::shared_ptr<RemoteControllerTelnet>( +                    new RemoteControllerTelnet(telnetport));          }          else { -            mgmt_server = new ManagementServer(); -        } - -        if (rc) { -            ensemble->enrol_at(*rc); +            rc = boost::shared_ptr<RemoteControllerDummy>( +                    new RemoteControllerDummy());          } +        DabMultiplexer mux(rc, pt);          etiLog.level(info) <<                  PACKAGE_NAME << " " << @@ -436,1759 +298,181 @@ int main(int argc, char *argv[])  #endif                  " starting up"; -        if (outputs.size() == 0) { -            etiLog.log(emerg, "no output defined"); -            throw MuxInitException(); -        } -        // Check and adjust subchannels -        { -            set<unsigned char> ids; -            for (subchannel = ensemble->subchannels.begin(); -                    subchannel != ensemble->subchannels.end(); -                    ++subchannel) { -                if (ids.find((*subchannel)->id) != ids.end()) { -                    etiLog.log(error, -                            "Subchannel %u is set more than once!\n", -                            (*subchannel)->id); -                    returnCode = -1; -                    throw MuxInitException(); -                } +        edi_configuration_t edi_conf; + +        /******************** READ OUTPUT PARAMETERS ***************/ +        set<string> all_output_names; +        ptree pt_outputs = pt.get_child("outputs"); +        for (auto ptree_pair : pt_outputs) { +            string outputuid = ptree_pair.first; + +            // check for uniqueness of the uid +            if (all_output_names.count(outputuid) == 0) { +                all_output_names.insert(outputuid); +            } +            else { +                stringstream ss; +                ss << "output with uid " << outputuid << " not unique!"; +                throw runtime_error(ss.str());              } -        } -        // Check and adjust services and components -        { -            set<uint32_t> ids; -            for (service = ensemble->services.begin(); -                    service != ensemble->services.end(); -                    ++service) { -                if (ids.find((*service)->id) != ids.end()) { -                    etiLog.log(error, -                            "Service id 0x%x (%u) is set more than once!\n", -                            (*service)->id, (*service)->id); -                    returnCode = -1; -                    throw MuxInitException(); -                } +            if (outputuid == "edi") { +#if HAVE_OUTPUT_EDI +                ptree pt_edi = pt_outputs.get_child("edi"); -                // Get first component of this service -                component = getComponent(ensemble->components, (*service)->id); -                if (component == ensemble->components.end()) { -                    etiLog.log(error, -                            "Service id 0x%x (%u) includes no component!\n", -                            (*service)->id, (*service)->id); -                    returnCode = -1; -                    throw MuxInitException(); -                } +                edi_conf.enabled     = true; -                // Adjust service type from this first component -                switch ((*service)->getType(ensemble)) { -                case 0: // Audio -                    (*service)->program = true; -                    break; -                case 1: -                case 2: -                case 3: -                    (*service)->program = false; -                    break; -                default: -                    etiLog.log(error, -                            "Error, unknown service type: %u\n", (*service)->getType(ensemble)); -                    returnCode = -1; -                    throw MuxInitException(); -                } +                edi_conf.dest_addr   = pt_edi.get<string>("destination"); +                edi_conf.dest_port   = pt_edi.get<unsigned int>("port"); +                edi_conf.source_port = pt_edi.get<unsigned int>("sourceport"); -                // Adjust components type for DAB+ -                while (component != ensemble->components.end()) { -                    subchannel = -                        getSubchannel(ensemble->subchannels, (*component)->subchId); -                    if (subchannel == ensemble->subchannels.end()) { -                        etiLog.log(error, "Error, service %u component " -                                "links to the invalid subchannel %u\n", -                                (*component)->serviceId, (*component)->subchId); -                        returnCode = -1; -                        throw MuxInitException(); -                    } - -                    protection = &(*subchannel)->protection; -                    switch ((*subchannel)->type) { -                    case Audio: -                        { -                            if (protection->form == EEP) { -                                (*component)->type = 0x3f;  // DAB+ -                            } -                        } -                        break; -                    case DataDmb: -                    case Fidc: -                    case Packet: -                        break; -                    default: -                        etiLog.log(error, -                                "Error, unknown subchannel type\n"); -                        returnCode = -1; -                        throw MuxInitException(); -                    } -                    component = getComponent(ensemble->components, -                            (*service)->id, component); -                } +                edi_conf.dump        = pt_edi.get<bool>("dump"); +                edi_conf.enable_pft  = pt_edi.get<bool>("enable_pft"); +                edi_conf.verbose     = pt_edi.get<bool>("verbose"); + +                mux.set_edi_config(edi_conf); +#else +                throw runtime_error("EDI output not compiled in"); +#endif              } -        } +            else { +                string uri = pt_outputs.get<string>(outputuid); + +                size_t proto_pos = uri.find("://"); +                if (proto_pos == std::string::npos) { +                    stringstream ss; +                    ss << "Output with uid " << outputuid << " no protocol defined!"; +                    throw runtime_error(ss.str()); +                } + +                string proto = uri.substr(0, proto_pos); +                string location = uri.substr(proto_pos + 3); +                DabOutput *output; -        for (output = outputs.begin(); output != outputs.end() ; ++output) { -            if (0) { +                if (0) {  #if defined(HAVE_OUTPUT_FILE) -            } else if ((*output)->outputProto == "file") { -                (*output)->output = new DabOutputFile(); +                } else if (proto == "file") { +                    output = new DabOutputFile();  #endif // defined(HAVE_OUTPUT_FILE)  #if defined(HAVE_OUTPUT_FIFO) -            } else if ((*output)->outputProto == "fifo") { -                (*output)->output = new DabOutputFifo(); +                } else if (proto == "fifo") { +                    output = new DabOutputFifo();  #endif // !defined(HAVE_OUTPUT_FIFO)  #if defined(HAVE_OUTPUT_RAW) -            } else if ((*output)->outputProto == "raw") { -                (*output)->output = new DabOutputRaw(); +                } else if (proto == "raw") { +                    output = new DabOutputRaw();  #endif // defined(HAVE_OUTPUT_RAW)  #if defined(HAVE_OUTPUT_UDP) -            } else if ((*output)->outputProto == "udp") { -                (*output)->output = new DabOutputUdp(); +                } else if (proto == "udp") { +                    output = new DabOutputUdp();  #endif // defined(HAVE_OUTPUT_UDP)  #if defined(HAVE_OUTPUT_TCP) -            } else if ((*output)->outputProto == "tcp") { -                (*output)->output = new DabOutputTcp(); +                } else if (proto == "tcp") { +                    output = new DabOutputTcp();  #endif // defined(HAVE_OUTPUT_TCP)  #if defined(HAVE_OUTPUT_SIMUL) -            } else if ((*output)->outputProto == "simul") { -                (*output)->output = new DabOutputSimul(); +                } else if (proto == "simul") { +                    output = new DabOutputSimul();  #endif // defined(HAVE_OUTPUT_SIMUL)  #if defined(HAVE_OUTPUT_ZEROMQ) -            } else if ((*output)->outputProto == "zmq+tcp") { -                (*output)->output = new DabOutputZMQ("tcp"); -            } else if ((*output)->outputProto == "zmq+ipc") { -                (*output)->output = new DabOutputZMQ("ipc"); -            } else if ((*output)->outputProto == "zmq+pgm") { -                (*output)->output = new DabOutputZMQ("pgm"); -            } else if ((*output)->outputProto == "zmq+epgm") { -                (*output)->output = new DabOutputZMQ("epgm"); +                } else if (proto == "zmq+tcp") { +                    output = new DabOutputZMQ("tcp"); +                } else if (proto == "zmq+ipc") { +                    output = new DabOutputZMQ("ipc"); +                } else if (proto == "zmq+pgm") { +                    output = new DabOutputZMQ("pgm"); +                } else if (proto == "zmq+epgm") { +                    output = new DabOutputZMQ("epgm");  #endif // defined(HAVE_OUTPUT_ZEROMQ) -            } else { -                etiLog.log(error, "Output protocol unknown: %s\n", -                        (*output)->outputProto.c_str()); -                throw MuxInitException(); -            } - -            if ((*output)->output == NULL) { -                etiLog.log(error, "Unable to init output %s://%s\n", -                        (*output)->outputProto.c_str(), (*output)->outputName.c_str()); -                return -1; -            } -            if ((*output)->output->Open((*output)->outputName) -                    == -1) { -                etiLog.log(error, "Unable to open output %s://%s\n", -                        (*output)->outputProto.c_str(), (*output)->outputName.c_str()); -                return -1; -            } -        } +                } else { +                    etiLog.level(error) << "Output protocol unknown: " << proto; +                    throw MuxInitException(); +                } -        // Prepare and check the data inputs -        for (subchannel = ensemble->subchannels.begin(); -                subchannel != ensemble->subchannels.end(); -                ++subchannel) { -            protection = &(*subchannel)->protection; -            if (subchannel == ensemble->subchannels.begin()) { -                (*subchannel)->startAddress = 0; -            } else { -                (*subchannel)->startAddress = (*(subchannel - 1))->startAddress + -                    getSizeCu(*(subchannel - 1)); -            } -            if ((*subchannel)->input->open((*subchannel)->inputUri) == -1) { -                perror((*subchannel)->inputUri.c_str()); -                returnCode = -1; -                throw MuxInitException(); -            } +                if (output == NULL) { +                    etiLog.level(error) << +                        "Unable to init output " << +                        uri; +                    return -1; +                } -            // TODO Check errors -            int subch_bitrate = (*subchannel)->input->setBitrate( (*subchannel)->bitrate); -            if (subch_bitrate <= 0) { -                etiLog.level(error) << "can't set bitrate for source " << -                        (*subchannel)->inputUri; -                returnCode = -1; -                throw MuxInitException(); -            } -            (*subchannel)->bitrate = subch_bitrate; - -            /* Use EEP unless we find a UEP configuration -             * UEP is only used for MPEG audio, but some bitrates don't -             * have a UEP profile (EN 300 401 Clause 6.2.1). -             * For these bitrates, we must switch to EEP. -             * -             * AAC audio and data is already EEP -             */ -            if (protection->form == UEP) { -                protection->form = EEP; -                for (int i = 0; i < 64; i++) { -                    if ( (*subchannel)->bitrate == BitRateTable[i] && -                         protection->level == ProtectionLevelTable[i] ) { -                        protection->form = UEP; -                        protection->uep.tableIndex = i; -                    } +                if (output->Open(location) == -1) { +                    etiLog.level(error) << +                        "Unable to open output " << +                        uri; +                    return -1;                  } -            } -            /* EEP B can only be used for subchannels with bitrates -             * multiple of 32kbit/s -             */ -            if (    protection->form == EEP && -                    protection->eep.profile == EEP_B && -                    subch_bitrate % 32 != 0 ) { -                etiLog.level(error) << -                    "Cannot use EEP_B protection for subchannel " << -                    (*subchannel)->inputUri << -                    ": bitrate not multiple of 32kbit/s"; -                returnCode = -1; -                throw MuxInitException(); +                boost::shared_ptr<DabOutput> dabout(output); +                outputs.push_back(dabout); +              }          } -        if (ensemble->subchannels.size() == 0) { -            etiLog.log(error, "can't multiplex no subchannel!\n"); -            returnCode = -1; -            throw MuxInitException(); -        } -        subchannel = ensemble->subchannels.end() - 1; -        if ((*subchannel)->startAddress + getSizeCu((*subchannel)) > 864) { -            etiLog.log(error, "Total size in CU exceeds 864\n"); -            printSubchannels(ensemble->subchannels); -            returnCode = -1; +        if (outputs.size() == 0) { +            etiLog.log(emerg, "no output defined");              throw MuxInitException();          } -        // Init packet components SCId -        cur = 0; -        for (component = ensemble->components.begin(); -                component != ensemble->components.end(); -                ++component) { -            subchannel = getSubchannel(ensemble->subchannels, -                    (*component)->subchId); -            if (subchannel == ensemble->subchannels.end()) { -                etiLog.log(error, -                        "Subchannel %i does not exist for component " -                        "of service %i\n", -                        (*component)->subchId, (*component)->serviceId); -                returnCode = -1; -                throw MuxInitException(); -            } -            if ((*subchannel)->type != Packet) continue; - -            (*component)->packet.id = cur++; -        } - -        // Print settings before starting -        etiLog.log(info, "--- Multiplex configuration ---"); -        printEnsemble(ensemble); - -        etiLog.log(info, "--- Subchannels list ---"); -        printSubchannels(ensemble->subchannels); - -        etiLog.log(info, "--- Services list ---"); -        printServices(ensemble->services); - -        etiLog.log(info, "--- Components list ---"); -        printComponents(ensemble->components); +        mux.prepare(); +        mux.print_info();          etiLog.log(info, "--- Output list ---");          printOutputs(outputs); +#if HAVE_OUTPUT_EDI          if (edi_conf.enabled) {              etiLog.level(warn) << "EXPERIMENTAL EDI OUTPUT ENABLED!";              etiLog.level(info) << "edi to " << edi_conf.dest_addr << ":" << edi_conf.dest_port;              etiLog.level(info) << "source port " << edi_conf.source_port;              etiLog.level(info) << "verbose     " << edi_conf.verbose;          } +#endif +        size_t limit = pt.get("general.nbframes", 0); -        /* These iterators are used to fill the respective FIG. -         * It is necessary to cycle through all the FIGs that have -         * to be transmitted because they do not fit in a single -         * FIB. -         * -         * ETSI EN 300 799 Clauses 5.2 and 8.1 -         */ -        serviceProgFIG0_2 = ensemble->services.end(); -        serviceDataFIG0_2 = ensemble->services.end(); -        componentProgFIG0_8 = ensemble->components.end(); -        componentFIG0_13 = ensemble->components.end(); -        componentDataFIG0_8 = ensemble->components.end(); -        serviceFIG0_17 = ensemble->services.end(); -        subchannelFIG0_1 = ensemble->subchannels.end(); - -        if (edi_conf.verbose) { -            etiLog.log(info, "Setup EDI debug"); -        } -        std::ofstream edi_debug_file; - -        if (edi_conf.dump) { -            edi_debug_file.open("./edi.debug"); -        } -        UdpSocket edi_output; - -        if (edi_conf.enabled) { -            edi_output.create(edi_conf.source_port); -        } - -        if (edi_conf.verbose) { -            etiLog.log(info, "EDI debug set up"); -        } - -        // The TagPacket will then be placed into an AFPacket -        AFPacketiser edi_afPacketiser(edi_conf.verbose); - -        // The AF Packet will be protected with reed-solomon and split in fragments -        PFT edi_pft(207, 3, edi_conf); - +        etiLog.level(info) << "Start loop";          /*   Each iteration of the main loop creates one ETI frame */ +        size_t currentFrame;          for (currentFrame = 0; running; currentFrame++) { -            if ((limit > 0) && (currentFrame >= limit)) { -                break; -            } - -            // For EDI, save ETI(LI) Management data into a TAG Item DETI -            TagDETI edi_tagDETI; -            TagStarPTR edi_tagStarPtr; -            list<TagESTn> edi_subchannels; -            map<dabSubchannel*, TagESTn*> edi_subchannelToTag; - -            // The above Tag Items will be assembled into a TAG Packet -            TagPacket edi_tagpacket; - -            edi_tagDETI.atstf = 0; // TODO add ATST support - -            date = getDabTime(); - -            // Initialise the ETI frame -            memset(etiFrame, 0, 6144); - -            /********************************************************************** -             **********   Section SYNC of ETI(NI, G703)   ************************* -             **********************************************************************/ - -            // See ETS 300 799 Clause 6 -            eti_SYNC *etiSync = (eti_SYNC *) etiFrame; - -            etiSync->ERR = edi_tagDETI.stat = 0xFF; // ETS 300 799, 5.2, no error +            mux.mux_frame(outputs); -            //****** Field FSYNC *****// -            // See ETS 300 799, 6.2.1.2 -            sync ^= 0xffffff; -            etiSync->FSYNC = sync; - -            /********************************************************************** -             ***********   Section LIDATA of ETI(NI, G703)   ********************** -             **********************************************************************/ - -            // See ETS 300 799 Figure 5 for a better overview of these fields. - -            //****** Section FC ***************************************************/ -            // 4 octets, starts at offset 4 -            eti_FC *fc = (eti_FC *) &etiFrame[4]; - -            //****** FCT ******// -            // Incremente for each frame, overflows at 249 -            fc->FCT = currentFrame % 250; -            edi_tagDETI.dflc = currentFrame % 5000; - -            //****** FICF ******// -            // Fast Information Channel Flag, 1 bit, =1 if FIC present -            fc->FICF = edi_tagDETI.ficf = 1; - -            //****** NST ******// -            /* Number of audio of data sub-channels, 7 bits, 0-64. -             * In the 15-frame period immediately preceding a multiplex -             * re-configuration, NST can take the value 0 (see annex E). -             */ -            fc->NST = ensemble->subchannels.size(); - -            //****** FP ******// -            /* Frame Phase, 3 bit counter, tells the COFDM generator -             * when to insert the TII. Is also used by the MNSC. -             */ -            fc->FP = edi_tagDETI.fp = currentFrame & 0x7; - -            //****** MID ******// -            //Mode Identity, 2 bits, 01 ModeI, 10 modeII, 11 ModeIII, 00 ModeIV -            fc->MID = edi_tagDETI.mid = ensemble->mode;      //mode 2 needs 3 FIB, 3*32octets = 96octets - -            //****** FL ******// -            /* Frame Length, 11 bits, nb of words(4 bytes) in STC, EOH and MST -             * if NST=0, FL=1+FICL words, FICL=24 or 32 depending on the mode. -             * The FL is given in words (4 octets), see ETS 300 799 5.3.6 for details -             */ -            FLtmp = 1 + FICL + (fc->NST); - -            for (subchannel = ensemble->subchannels.begin(); -                    subchannel != ensemble->subchannels.end(); -                    ++subchannel) { -                // Add STLsbch -                FLtmp += getSizeWord(*subchannel); -            } - -            fc->setFrameLength(FLtmp); -            index = 8; - -            /******* Section STC **************************************************/ -            // Stream Characterization, -            //  number of channels * 4 octets = nb octets total -            int edi_stream_id = 1; -            for (subchannel = ensemble->subchannels.begin(); -                    subchannel != ensemble->subchannels.end(); -                    ++subchannel) { -                protection = &(*subchannel)->protection; -                eti_STC *sstc = (eti_STC *) & etiFrame[index]; - -                sstc->SCID = (*subchannel)->id; -                sstc->startAddress_high = (*subchannel)->startAddress / 256; -                sstc->startAddress_low = (*subchannel)->startAddress % 256; -                // depends on the desired protection form -                if (protection->form == UEP) { -                    sstc->TPL = 0x10 | -                        ProtectionLevelTable[protection->uep.tableIndex]; -                } -                else if (protection->form == EEP) { -                    sstc->TPL = 0x20 | (protection->eep.GetOption() << 2) | protection->level; -                } - -                // Sub-channel Stream Length, multiple of 64 bits -                sstc->STL_high = getSizeDWord(*subchannel) / 256; -                sstc->STL_low = getSizeDWord(*subchannel) % 256; - -                TagESTn tag_ESTn(edi_stream_id++); -                tag_ESTn.scid = (*subchannel)->id; -                tag_ESTn.sad = (*subchannel)->startAddress; -                tag_ESTn.tpl = sstc->TPL; -                tag_ESTn.rfa = 0; // two bits -                tag_ESTn.mst_length = getSizeByte(*subchannel) / 8; -                assert(getSizeByte(*subchannel) % 8 == 0); - -                edi_subchannels.push_back(tag_ESTn); -                edi_subchannelToTag[*subchannel] = &edi_subchannels.back(); -                index += 4; -            } - -            /******* Section EOH **************************************************/ -            // End of Header 4 octets -            eti_EOH *eoh = (eti_EOH *) & etiFrame[index]; - -            //MNSC Multiplex Network Signalling Channel, 2 octets - -            eoh->MNSC = 0; - -            struct tm *time_tm = gmtime(&mnsc_time.tv_sec); -            switch (fc->FP & 0x3) -            { -                case 0: -                    if (MNSC_increment_time) -                    { -                        MNSC_increment_time = false; -                        mnsc_time.tv_sec += 1; -                    } -                    { - -                        eti_MNSC_TIME_0 *mnsc = (eti_MNSC_TIME_0 *) &eoh->MNSC; -                        // Set fields according to ETS 300 799 -- 5.5.1 and A.2.2 -                        mnsc->type = 0; -                        mnsc->identifier = 0; -                        mnsc->rfa = 0; -                    } -                    break; -                case 1: -                    { -                        eti_MNSC_TIME_1 *mnsc = (eti_MNSC_TIME_1 *) &eoh->MNSC; -                        mnsc->setFromTime(time_tm); -                        mnsc->accuracy = 1; -                        mnsc->sync_to_frame = 1; -                    } -                    break; -                case 2: -                    { -                        eti_MNSC_TIME_2 *mnsc = (eti_MNSC_TIME_2 *) &eoh->MNSC; -                        mnsc->setFromTime(time_tm); -                    } -                    break; -                case 3: -                    { -                        eti_MNSC_TIME_3 *mnsc = (eti_MNSC_TIME_3 *) &eoh->MNSC; -                        mnsc->setFromTime(time_tm); -                    } -                    break; -            } - -            edi_tagDETI.mnsc = eoh->MNSC; - -            // CRC Cyclic Redundancy Checksum of the FC, STC and MNSC, 2 octets -            nbBytesCRC = 4 + ((fc->NST) * 4) + 2; - -            CRCtmp = 0xffff; -            CRCtmp = crc16(CRCtmp, &etiFrame[4], nbBytesCRC); -            CRCtmp ^= 0xffff; -            eoh->CRC = htons(CRCtmp); - -            /******* Section MST **************************************************/ -            // Main Stream Data, if FICF=1 the first 96 or 128 bytes carry the FIC -            // (depending on mode) -            index = ((fc->NST) + 2 + 1) * 4; -            edi_tagDETI.fic_data = &etiFrame[index]; -            edi_tagDETI.fic_length = FICL * 4; - -            // FIC Insertion -            FIGtype0* fig0; -            FIGtype0_0 *fig0_0; -            FIGtype0_1 *figtype0_1; - -            FIG_01_SubChannel_ShortF *fig0_1subchShort; -            FIG_01_SubChannel_LongF *fig0_1subchLong1; - -            FIGtype0_2 *fig0_2; - -            FIGtype0_2_Service *fig0_2serviceAudio; -            FIGtype0_2_Service_data *fig0_2serviceData; -            FIGtype0_2_audio_component* audio_description; -            FIGtype0_2_data_component* data_description; -            FIGtype0_2_packet_component* packet_description; - -            FIGtype0_3_header *fig0_3_header; -            FIGtype0_3_data *fig0_3_data; -            FIGtype0_9 *fig0_9; -            FIGtype0_10 *fig0_10; -            FIGtype0_17_programme *programme; - -            FIGtype1_0 *fig1_0; -            FIGtype1_1 *fig1_1; -            FIGtype1_5 *fig1_5; - -            tm* timeData; - -            unsigned char figSize = 0; - -            // FIB 0 Insertion -            switch (insertFIG) { - -            case 0: -            case 4: -            case 8: -            case 12: -                // FIG type 0/0, Multiplex Configuration Info (MCI), -                //  Ensemble information -                fig0_0 = (FIGtype0_0 *) & etiFrame[index]; - -                fig0_0->FIGtypeNumber = 0; -                fig0_0->Length = 5; -                fig0_0->CN = 0; -                fig0_0->OE = 0; -                fig0_0->PD = 0; -                fig0_0->Extension = 0; - -                fig0_0->EId = htons(ensemble->id); -                fig0_0->Change = 0; -                fig0_0->Al = 0; -                fig0_0->CIFcnt_hight = (currentFrame / 250) % 20; -                fig0_0->CIFcnt_low = (currentFrame % 250); -                index = index + 6; -                figSize += 6; - -                break; - -            case 1: -            case 6: -            case 10: -            case 13: -                // FIG type 0/1, MIC, Sub-Channel Organization, -                // one instance of the part for each subchannel -                figtype0_1 = (FIGtype0_1 *) & etiFrame[index]; - -                figtype0_1->FIGtypeNumber = 0; -                figtype0_1->Length = 1; -                figtype0_1->CN = 0; -                figtype0_1->OE = 0; -                figtype0_1->PD = 0; -                figtype0_1->Extension = 1; -                index = index + 2; -                figSize += 2; - -                // Rotate through the subchannels until there is no more -                // space in the FIG0/1 -                if (subchannelFIG0_1 == ensemble->subchannels.end()) { -                    subchannelFIG0_1 = ensemble->subchannels.begin(); -                } - -                for (; subchannelFIG0_1 != ensemble->subchannels.end(); -                        ++subchannelFIG0_1) { -                    protection = &(*subchannelFIG0_1)->protection; - -                    if ( (protection->form == UEP && figSize > 27) || -                         (protection->form == EEP && figSize > 26) ) { -                        break; -                    } - -                    if (protection->form == UEP) { -                        fig0_1subchShort = -                            (FIG_01_SubChannel_ShortF*) &etiFrame[index]; -                        fig0_1subchShort->SubChId = (*subchannelFIG0_1)->id; - -                        fig0_1subchShort->StartAdress_high = -                            (*subchannelFIG0_1)->startAddress / 256; -                        fig0_1subchShort->StartAdress_low = -                            (*subchannelFIG0_1)->startAddress % 256; - -                        fig0_1subchShort->Short_Long_form = 0; -                        fig0_1subchShort->TableSwitch = 0; -                        fig0_1subchShort->TableIndex = -                            protection->uep.tableIndex; - -                        index = index + 3; -                        figSize += 3; -                        figtype0_1->Length += 3; -                    } -                    else if (protection->form == EEP) { -                        fig0_1subchLong1 = -                            (FIG_01_SubChannel_LongF*) &etiFrame[index]; -                        fig0_1subchLong1->SubChId = (*subchannelFIG0_1)->id; - -                        fig0_1subchLong1->StartAdress_high = -                            (*subchannelFIG0_1)->startAddress / 256; -                        fig0_1subchLong1->StartAdress_low = -                            (*subchannelFIG0_1)->startAddress % 256; - -                        fig0_1subchLong1->Short_Long_form = 1; -                        fig0_1subchLong1->Option = protection->eep.GetOption(); -                        fig0_1subchLong1->ProtectionLevel = -                            protection->level; - -                        fig0_1subchLong1->Sub_ChannelSize_high = -                            getSizeCu(*subchannelFIG0_1) / 256; -                        fig0_1subchLong1->Sub_ChannelSize_low = -                            getSizeCu(*subchannelFIG0_1) % 256; - -                        index = index + 4; -                        figSize += 4; -                        figtype0_1->Length += 4; -                    } -                } +            if (limit && currentFrame >= limit) {                  break; - -            case 2: -            case 9: -            case 11: -            case 14: -                // FIG type 0/2, MCI, Service Organization, one instance of -                // FIGtype0_2_Service for each subchannel -                fig0_2 = NULL; -                cur = 0; - -                // Rotate through the subchannels until there is no more -                // space in the FIG0/1 -                if (serviceProgFIG0_2 == ensemble->services.end()) { -                    serviceProgFIG0_2 = ensemble->services.begin(); -                } - -                for (; serviceProgFIG0_2 != ensemble->services.end(); -                        ++serviceProgFIG0_2) { -                    if (!(*serviceProgFIG0_2)->nbComponent(ensemble->components)) { -                        continue; -                    } - -                    if ((*serviceProgFIG0_2)->getType(ensemble) != 0) { -                        continue; -                    } - -                    ++cur; - -                    if (fig0_2 == NULL) { -                        fig0_2 = (FIGtype0_2 *) & etiFrame[index]; - -                        fig0_2->FIGtypeNumber = 0; -                        fig0_2->Length = 1; -                        fig0_2->CN = 0; -                        fig0_2->OE = 0; -                        fig0_2->PD = 0; -                        fig0_2->Extension = 2; -                        index = index + 2; -                        figSize += 2; -                    } - -                    if (figSize + 3 -                            + (*serviceProgFIG0_2)->nbComponent(ensemble->components) -                            * 2 > 30) { -                        break; -                    } - -                    fig0_2serviceAudio = (FIGtype0_2_Service*) &etiFrame[index]; - -                    fig0_2serviceAudio->SId = htons((*serviceProgFIG0_2)->id); -                    fig0_2serviceAudio->Local_flag = 0; -                    fig0_2serviceAudio->CAId = 0; -                    fig0_2serviceAudio->NbServiceComp = -                        (*serviceProgFIG0_2)->nbComponent(ensemble->components); -                    index += 3; -                    fig0_2->Length += 3; -                    figSize += 3; - -                    int curCpnt = 0; -                    for (component = getComponent(ensemble->components, -                                (*serviceProgFIG0_2)->id); -                            component != ensemble->components.end(); -                            component = getComponent(ensemble->components, -                                (*serviceProgFIG0_2)->id, component)) { -                        subchannel = getSubchannel(ensemble->subchannels, -                                (*component)->subchId); - -                        if (subchannel == ensemble->subchannels.end()) { -                            etiLog.log(error, -                                    "Subchannel %i does not exist for component " -                                    "of service %i\n", -                                    (*component)->subchId, (*component)->serviceId); -                            returnCode = -1; -                            throw MuxInitException(); -                        } - -                        switch ((*subchannel)->type) { -                        case Audio: -                            audio_description = -                                (FIGtype0_2_audio_component*)&etiFrame[index]; -                            audio_description->TMid    = 0; -                            audio_description->ASCTy   = (*component)->type; -                            audio_description->SubChId = (*subchannel)->id; -                            audio_description->PS      = ((curCpnt == 0) ? 1 : 0); -                            audio_description->CA_flag = 0; -                            break; -                        case DataDmb: -                            data_description = -                                (FIGtype0_2_data_component*)&etiFrame[index]; -                            data_description->TMid    = 1; -                            data_description->DSCTy   = (*component)->type; -                            data_description->SubChId = (*subchannel)->id; -                            data_description->PS      = ((curCpnt == 0) ? 1 : 0); -                            data_description->CA_flag = 0; -                            break; -                        case Packet: -                            packet_description = -                                (FIGtype0_2_packet_component*)&etiFrame[index]; -                            packet_description->TMid    = 3; -                            packet_description->setSCId((*component)->packet.id); -                            packet_description->PS      = ((curCpnt == 0) ? 1 : 0); -                            packet_description->CA_flag = 0; -                            break; -                        default: -                            etiLog.log(error, -                                    "Component type not supported\n"); -                            returnCode = -1; -                            throw MuxInitException(); -                        } -                        index += 2; -                        fig0_2->Length += 2; -                        figSize += 2; -                        if (figSize > 30) { -                            etiLog.log(error, -                                    "Sorry, no space left in FIG 0/2 to insert " -                                    "component %i of program service %i.\n", -                                    curCpnt, cur); -                            returnCode = -1; -                            throw MuxInitException(); -                        } -                        ++curCpnt; -                    } -                } -                break; - -            case 3: -                fig0_2 = NULL; -                cur = 0; - -                if (serviceDataFIG0_2 == ensemble->services.end()) { -                    serviceDataFIG0_2 = ensemble->services.begin(); -                } -                for (; serviceDataFIG0_2 != ensemble->services.end(); -                        ++serviceDataFIG0_2) { -                    if (!(*serviceDataFIG0_2)->nbComponent(ensemble->components)) { -                        continue; -                    } - -                    unsigned char type = (*serviceDataFIG0_2)->getType(ensemble); -                    if ((type == 0) || (type == 2)) { -                        continue; -                    } - -                    ++cur; - -                    if (fig0_2 == NULL) { -                        fig0_2 = (FIGtype0_2 *) & etiFrame[index]; - -                        fig0_2->FIGtypeNumber = 0; -                        fig0_2->Length = 1; -                        fig0_2->CN = 0; -                        fig0_2->OE = 0; -                        fig0_2->PD = 1; -                        fig0_2->Extension = 2; -                        index = index + 2; -                        figSize += 2; -                    } - -                    if (figSize + 5 -                            + (*serviceDataFIG0_2)->nbComponent(ensemble->components) -                            * 2 > 30) { -                        break; -                    } - -                    fig0_2serviceData = -                        (FIGtype0_2_Service_data*) &etiFrame[index]; - -                    fig0_2serviceData->SId = htonl((*serviceDataFIG0_2)->id); -                    fig0_2serviceData->Local_flag = 0; -                    fig0_2serviceData->CAId = 0; -                    fig0_2serviceData->NbServiceComp = -                        (*serviceDataFIG0_2)->nbComponent(ensemble->components); -                    fig0_2->Length += 5; -                    index += 5; -                    figSize += 5; - -                    int curCpnt = 0; -                    for (component = getComponent(ensemble->components, -                                (*serviceDataFIG0_2)->id); -                            component != ensemble->components.end(); -                            component = getComponent(ensemble->components, -                                (*serviceDataFIG0_2)->id, component)) { -                        subchannel = getSubchannel(ensemble->subchannels, -                                (*component)->subchId); -                        if (subchannel == ensemble->subchannels.end()) { -                            etiLog.log(error, -                                    "Subchannel %i does not exist for component " -                                    "of service %i\n", -                                    (*component)->subchId, (*component)->serviceId); -                            returnCode = -1; -                            throw MuxInitException(); -                        } - -                        switch ((*subchannel)->type) { -                        case Audio: -                            audio_description = -                                (FIGtype0_2_audio_component*)&etiFrame[index]; -                            audio_description->TMid = 0; -                            audio_description->ASCTy = (*component)->type; -                            audio_description->SubChId = (*subchannel)->id; -                            audio_description->PS = ((curCpnt == 0) ? 1 : 0); -                            audio_description->CA_flag = 0; -                            break; -                        case DataDmb: -                            data_description = -                                (FIGtype0_2_data_component*)&etiFrame[index]; -                            data_description->TMid = 1; -                            data_description->DSCTy = (*component)->type; -                            data_description->SubChId = (*subchannel)->id; -                            data_description->PS = ((curCpnt == 0) ? 1 : 0); -                            data_description->CA_flag = 0; -                            break; -                        case Packet: -                            packet_description = -                                (FIGtype0_2_packet_component*)&etiFrame[index]; -                            packet_description->TMid = 3; -                            packet_description->setSCId((*component)->packet.id); -                            packet_description->PS = ((curCpnt == 0) ? 1 : 0); -                            packet_description->CA_flag = 0; -                            break; -                        default: -                            etiLog.log(error, -                                    "Component type not supported\n"); -                            returnCode = -1; -                            throw MuxInitException(); -                        } -                        index += 2; -                        fig0_2->Length += 2; -                        figSize += 2; -                        if (figSize > 30) { -                            etiLog.log(error, -                                    "Sorry, no place left in FIG 0/2 to insert " -                                    "component %i of data service %i.\n", -                                    curCpnt, cur); -                            returnCode = -1; -                            throw MuxInitException(); -                        } -                        ++curCpnt; -                    } -                } -                break; - -            case 5: -                fig0_3_header = NULL; - -                for (component = ensemble->components.begin(); -                        component != ensemble->components.end(); -                        ++component) { -                    subchannel = getSubchannel(ensemble->subchannels, -                            (*component)->subchId); - -                    if (subchannel == ensemble->subchannels.end()) { -                        etiLog.log(error, -                                "Subchannel %i does not exist for component " -                                "of service %i\n", -                                (*component)->subchId, (*component)->serviceId); -                        returnCode = -1; -                        throw MuxInitException(); -                    } - -                    if ((*subchannel)->type != Packet) -                        continue; - -                    if (fig0_3_header == NULL) { -                        fig0_3_header = (FIGtype0_3_header*)&etiFrame[index]; -                        fig0_3_header->FIGtypeNumber = 0; -                        fig0_3_header->Length = 1; -                        fig0_3_header->CN = 0; -                        fig0_3_header->OE = 0; -                        fig0_3_header->PD = 0; -                        fig0_3_header->Extension = 3; - -                        index += 2; -                        figSize += 2; -                    } - -                    /*  Warning: When bit SCCA_flag is unset(0), the multiplexer -                     *  R&S does not send the SCCA field. But, in the Factum ETI -                     *  analyzer, if this field is not there, it is an error. -                     */ -                    fig0_3_data = (FIGtype0_3_data*)&etiFrame[index]; -                    fig0_3_data->setSCId((*component)->packet.id); -                    fig0_3_data->rfa = 0; -                    fig0_3_data->SCCA_flag = 0; -                    // if 0, datagroups are used -                    fig0_3_data->DG_flag = !(*component)->packet.datagroup; -                    fig0_3_data->rfu = 0; -                    fig0_3_data->DSCTy = (*component)->type; -                    fig0_3_data->SubChId = (*subchannel)->id; -                    fig0_3_data->setPacketAddress((*component)->packet.address); -                    if (factumAnalyzer) { -                        fig0_3_data->SCCA = 0; -                    } - -                    fig0_3_header->Length += 5; -                    index += 5; -                    figSize += 5; -                    if (factumAnalyzer) { -                        fig0_3_header->Length += 2; -                        index += 2; -                        figSize += 2; -                    } - -                    if (figSize > 30) { -                        etiLog.log(error, -                                "can't add to Fic Fig 0/3, " -                                "too much packet service\n"); -                        returnCode = -1; -                        throw MuxInitException(); -                    } -                } -                break; - -            case 7: -                fig0 = NULL; -                if (serviceFIG0_17 == ensemble->services.end()) { -                    serviceFIG0_17 = ensemble->services.begin(); -                } -                for (; serviceFIG0_17 != ensemble->services.end(); -                        ++serviceFIG0_17) { - -                    if (    (*serviceFIG0_17)->pty == 0 && -                            (*serviceFIG0_17)->language == 0) { -                        continue; -                    } - -                    if (fig0 == NULL) { -                        fig0 = (FIGtype0*)&etiFrame[index]; -                        fig0->FIGtypeNumber = 0; -                        fig0->Length = 1; -                        fig0->CN = 0; -                        fig0->OE = 0; -                        fig0->PD = 0; -                        fig0->Extension = 17; -                        index += 2; -                        figSize += 2; -                    } - -                    if ((*serviceFIG0_17)->language == 0) { -                        if (figSize + 4 > 30) { -                            break; -                        } -                    } -                    else { -                        if (figSize + 5 > 30) { -                            break; -                        } -                    } - -                    programme = -                        (FIGtype0_17_programme*)&etiFrame[index]; -                    programme->SId = htons((*serviceFIG0_17)->id); -                    programme->SD = 1; -                    programme->PS = 0; -                    programme->L = (*serviceFIG0_17)->language != 0; -                    programme->CC = 0; -                    programme->Rfa = 0; -                    programme->NFC = 0; -                    if ((*serviceFIG0_17)->language == 0) { -                        etiFrame[index + 3] = (*serviceFIG0_17)->pty; -                        fig0->Length += 4; -                        index += 4; -                        figSize += 4; -                    } -                    else { -                        etiFrame[index + 3] = (*serviceFIG0_17)->language; -                        etiFrame[index + 4] = (*serviceFIG0_17)->pty; -                        fig0->Length += 5; -                        index += 5; -                        figSize += 5; -                    } -                } -                break; -            } - -            if (figSize > 30) { -                etiLog.log(error, -                        "FIG too big (%i > 30)\n", figSize); -                returnCode = -1; -                throw MuxInitException(); -            } - -            memcpy(&etiFrame[index], Padding_FIB, 30 - figSize); -            index += 30 - figSize; - -            CRCtmp = 0xffff; -            CRCtmp = crc16(CRCtmp, &etiFrame[(index - 30)], 30); -            CRCtmp ^= 0xffff; -            etiFrame[index++] = ((char *) &CRCtmp)[1]; -            etiFrame[index++] = ((char *) &CRCtmp)[0]; - -            figSize = 0; -            // FIB 1 insertion -            switch (rotateFIB) { -            case 0:     // FIG 0/8 program -                fig0 = NULL; - -                if (componentProgFIG0_8 == ensemble->components.end()) { -                    componentProgFIG0_8 = ensemble->components.begin(); -                } -                for (; componentProgFIG0_8 != ensemble->components.end(); -                        ++componentProgFIG0_8) { -                    service = getService(*componentProgFIG0_8, -                            ensemble->services); -                    subchannel = getSubchannel(ensemble->subchannels, -                            (*componentProgFIG0_8)->subchId); -                    if (subchannel == ensemble->subchannels.end()) { -                        etiLog.log(error, -                                "Subchannel %i does not exist for component " -                                "of service %i\n", -                                (*componentProgFIG0_8)->subchId, -                                (*componentProgFIG0_8)->serviceId); -                        returnCode = -1; -                        throw MuxInitException(); -                    } - -                    if (!(*service)->program) -                        continue; - -                    if (fig0 == NULL) { -                        fig0 = (FIGtype0*)&etiFrame[index]; -                        fig0->FIGtypeNumber = 0; -                        fig0->Length = 1; -                        fig0->CN = 0; -                        fig0->OE = 0; -                        fig0->PD = 0; -                        fig0->Extension = 8; -                        index += 2; -                        figSize += 2; -                    } - -                    if ((*subchannel)->type == Packet) { // Data packet -                        if (figSize > 30 - 5) { -                            break; -                        } -                        etiFrame[index] = -                            ((*componentProgFIG0_8)->serviceId >> 8) & 0xFF; -                        etiFrame[index+1] = -                            ((*componentProgFIG0_8)->serviceId) & 0xFF; -                        fig0->Length += 2; -                        index += 2; -                        figSize += 2; - -                        FIGtype0_8_long* definition = -                            (FIGtype0_8_long*)&etiFrame[index]; -                        memset(definition, 0, 3); -                        definition->ext = 0;    // no rfa -                        definition->SCIdS = (*componentProgFIG0_8)->SCIdS; -                        definition->LS = 1; -                        definition->setSCId((*componentProgFIG0_8)->packet.id); -                        fig0->Length += 3; -                        index += 3;             // 8 minus rfa -                        figSize += 3; -                    } -                    else {    // Audio, data stream or FIDC -                        if (figSize > 30 - 4) { -                            break; -                        } -                        etiFrame[index] = -                            ((*componentProgFIG0_8)->serviceId >> 8) & 0xFF; -                        etiFrame[index+1] = -                            ((*componentProgFIG0_8)->serviceId) & 0xFF; - -                        fig0->Length += 2; -                        index += 2; -                        figSize += 2; - -                        FIGtype0_8_short* definition = -                            (FIGtype0_8_short*)&etiFrame[index]; -                        memset(definition, 0, 2); -                        definition->ext = 0;    // no rfa -                        definition->SCIdS = (*componentProgFIG0_8)->SCIdS; -                        definition->LS = 0; -                        definition->MscFic = 0; -                        definition->Id = (*componentProgFIG0_8)->subchId; -                        fig0->Length += 2; -                        index += 2;             // 4 minus rfa -                        figSize += 2; -                    } -                } -                break; - -            case 1:     // FIG 0/8 data -                fig0 = NULL; - -                if (componentDataFIG0_8 == ensemble->components.end()) { -                    componentDataFIG0_8 = ensemble->components.begin(); -                } -                for (; componentDataFIG0_8 != ensemble->components.end(); -                        ++componentDataFIG0_8) { -                    service = getService(*componentDataFIG0_8, -                            ensemble->services); - -                    subchannel = getSubchannel(ensemble->subchannels, -                            (*componentDataFIG0_8)->subchId); - -                    if (subchannel == ensemble->subchannels.end()) { -                        etiLog.log(error, -                                "Subchannel %i does not exist for component " -                                "of service %i\n", -                                (*componentDataFIG0_8)->subchId, -                                (*componentDataFIG0_8)->serviceId); -                        returnCode = -1; -                        throw MuxInitException(); -                    } - -                    if ((*service)->program) -                        continue; - -                    if (fig0 == NULL) { -                        fig0 = (FIGtype0*)&etiFrame[index]; -                        fig0->FIGtypeNumber = 0; -                        fig0->Length = 1; -                        fig0->CN = 0; -                        fig0->OE = 0; -                        fig0->PD = 1; -                        fig0->Extension = 8; -                        index += 2; -                        figSize += 2; -                    } - -                    if ((*subchannel)->type == Packet) { // Data packet -                        if (figSize > 30 - 7) { -                            break; -                        } -                        etiFrame[index] = -                            ((*componentDataFIG0_8)->serviceId >> 8) & 0xFF; -                        etiFrame[index+1] = -                            ((*componentDataFIG0_8)->serviceId) & 0xFF; -                        fig0->Length += 4; -                        index += 4; -                        figSize += 4; - -                        FIGtype0_8_long* definition = -                            (FIGtype0_8_long*)&etiFrame[index]; -                        memset(definition, 0, 3); -                        definition->ext = 0;    // no rfa -                        definition->SCIdS = (*componentDataFIG0_8)->SCIdS; -                        definition->LS = 1; -                        definition->setSCId((*componentDataFIG0_8)->packet.id); -                        fig0->Length += 3; -                        index += 3;             // 8 minus rfa -                        figSize += 3; -                    } -                    else {    // Audio, data stream or FIDC -                        if (figSize > 30 - 6) { -                            break; -                        } -                        etiFrame[index] = -                            ((*componentDataFIG0_8)->serviceId >> 8) & 0xFF; -                        etiFrame[index+1] = -                            ((*componentDataFIG0_8)->serviceId) & 0xFF; -                        fig0->Length += 4; -                        index += 4; -                        figSize += 4; - -                        FIGtype0_8_short* definition = -                            (FIGtype0_8_short*)&etiFrame[index]; -                        memset(definition, 0, 2); -                        definition->ext = 0;    // no rfa -                        definition->SCIdS = (*componentDataFIG0_8)->SCIdS; -                        definition->LS = 0; -                        definition->MscFic = 0; -                        definition->Id = (*componentDataFIG0_8)->subchId; -                        fig0->Length += 2; -                        index += 2;             // 4 minus rfa -                        figSize += 2; -                    } -                } -                break; - -            case 3: -                // FIG type 1/0, Service Information (SI), Ensemble Label -                fig1_0 = (FIGtype1_0 *) & etiFrame[index]; - -                fig1_0->Length = 21; -                fig1_0->FIGtypeNumber = 1; -                fig1_0->Extension = 0; -                fig1_0->OE = 0; -                fig1_0->Charset = 0; -                fig1_0->EId = htons(ensemble->id); -                index = index + 4; - -                memcpy(&etiFrame[index], ensemble->label.text(), 16); -                index = index + 16; - -                etiFrame[index++] = ensemble->label.flag() >> 8; -                etiFrame[index++] = ensemble->label.flag() & 0xFF; - -                figSize += 22; -                break; - -            case 5: -            case 6: -                // FIG 0 / 13 -                fig0 = NULL; - -                if (componentFIG0_13 == ensemble->components.end()) { -                    componentFIG0_13 = ensemble->components.begin(); - -                    transmitFIG0_13programme = !transmitFIG0_13programme; -                    // Alternate between data and and programme FIG0/13, -                    // do not mix fig0 with PD=0 with extension 13 stuff -                    // that actually needs PD=1, and vice versa -                } - -                for (; componentFIG0_13 != ensemble->components.end(); -                        ++componentFIG0_13) { - -                    subchannel = getSubchannel(ensemble->subchannels, -                            (*componentFIG0_13)->subchId); - -                    if (subchannel == ensemble->subchannels.end()) { -                        etiLog.log(error, -                                "Subchannel %i does not exist for component " -                                "of service %i\n", -                                (*componentFIG0_13)->subchId, -                                (*componentFIG0_13)->serviceId); -                        returnCode = -1; -                        throw MuxInitException(); -                    } - -                    if (    transmitFIG0_13programme && -                            (*subchannel)->type == Audio && -                            (*componentFIG0_13)->audio.uaType != 0xffff) { -                        if (fig0 == NULL) { -                            fig0 = (FIGtype0*)&etiFrame[index]; -                            fig0->FIGtypeNumber = 0; -                            fig0->Length = 1; -                            fig0->CN = 0; -                            fig0->OE = 0; -                            fig0->PD = 0; -                            fig0->Extension = 13; -                            index += 2; -                            figSize += 2; -                        } - -                        if (figSize > 30 - (3+4+11)) { -                            break; -                        } - -                        FIG0_13_shortAppInfo* info = -                            (FIG0_13_shortAppInfo*)&etiFrame[index]; -                        info->SId = htonl((*componentFIG0_13)->serviceId) >> 16; -                        info->SCIdS = (*componentFIG0_13)->SCIdS; -                        info->No = 1; -                        index += 3; -                        figSize += 3; -                        fig0->Length += 3; - -                        FIG0_13_app* app = (FIG0_13_app*)&etiFrame[index]; -                        app->setType((*componentFIG0_13)->audio.uaType); -                        app->length = 4; -                        app->xpad = htonl(0x0cbc0000); -                        /* xpad meaning -                           CA        = 0 -                           CAOrg     = 0 -                           Rfu       = 0 -                           AppTy(5)  = 12 (MOT, start of X-PAD data group) -                           DG        = 0 (MSC data groups used) -                           Rfu       = 0 -                           DSCTy(6)  = 60 (MOT) -                           CAOrg(16) = 0 -                        */ - -                        index += 2 + app->length; -                        figSize += 2 + app->length; -                        fig0->Length += 2 + app->length; -                    } -                    else if (!transmitFIG0_13programme && -                            (*subchannel)->type == Packet && -                            (*componentFIG0_13)->packet.appType != 0xffff) { - -                        if (fig0 == NULL) { -                            fig0 = (FIGtype0*)&etiFrame[index]; -                            fig0->FIGtypeNumber = 0; -                            fig0->Length = 1; -                            fig0->CN = 0; -                            fig0->OE = 0; -                            fig0->PD = 1; -                            fig0->Extension = 13; -                            index += 2; -                            figSize += 2; -                        } - -                        if (figSize > 30 - (5+2)) { -                            break; -                        } - -                        FIG0_13_longAppInfo* info = -                            (FIG0_13_longAppInfo*)&etiFrame[index]; -                        info->SId = htonl((*componentFIG0_13)->serviceId); -                        info->SCIdS = (*componentFIG0_13)->SCIdS; -                        info->No = 1; -                        index += 5; -                        figSize += 5; -                        fig0->Length += 5; - -                        FIG0_13_app* app = (FIG0_13_app*)&etiFrame[index]; -                        app->setType((*componentFIG0_13)->packet.appType); -                        app->length = 0; -                        index += 2; -                        figSize += 2; -                        fig0->Length += 2; -                    } -                } -                break; - -            case 7: -                //Time and country identifier -                fig0_10 = (FIGtype0_10 *) & etiFrame[index]; - -                fig0_10->FIGtypeNumber = 0; -                fig0_10->Length = 5; -                fig0_10->CN = 0; -                fig0_10->OE = 0; -                fig0_10->PD = 0; -                fig0_10->Extension = 10; -                index = index + 2; - -                timeData = gmtime(&date); - -                fig0_10->RFU = 0; -                fig0_10->setMJD(gregorian2mjd(timeData->tm_year + 1900, -                            timeData->tm_mon + 1, -                            timeData->tm_mday)); -                fig0_10->LSI = 0; -                fig0_10->ConfInd = (watermarkData[watermarkPos >> 3] >> -                        (7 - (watermarkPos & 0x07))) & 1; -                if (++watermarkPos == watermarkSize) { -                    watermarkPos = 0; -                } -                fig0_10->UTC = 0; -                fig0_10->setHours(timeData->tm_hour); -                fig0_10->Minutes = timeData->tm_min; -                index = index + 4; -                figSize += 6; - -                fig0_9 = (FIGtype0_9*)&etiFrame[index]; -                fig0_9->FIGtypeNumber = 0; -                fig0_9->Length = 4; -                fig0_9->CN = 0; -                fig0_9->OE = 0; -                fig0_9->PD = 0; -                fig0_9->Extension = 9; - -                fig0_9->ext = 0; -                fig0_9->lto = 0; // Unique LTO for ensemble - -                if (ensemble->lto_auto) { -                    time_t now = time(NULL); -                    struct tm* ltime = localtime(&now); -                    time_t now2 = timegm(ltime); -                    ensemble->lto = (now2 - now) / 1800; -                } - -                if (ensemble->lto >= 0) { -                    fig0_9->ensembleLto = ensemble->lto; -                } -                else { -                    /* Convert to 1-complement representation */ -                    fig0_9->ensembleLto = (-ensemble->lto) | (1<<5); -                } - -                fig0_9->ensembleEcc = ensemble->ecc; -                fig0_9->tableId = ensemble->international_table; -                index += 5; -                figSize += 5; - -                break; -            } - -            assert(figSize <= 30); -            memcpy(&etiFrame[index], Padding_FIB, 30 - figSize); -            index += 30 - figSize; - -            CRCtmp = 0xffff; -            CRCtmp = crc16(CRCtmp, &etiFrame[(index - 30)], 30); -            CRCtmp ^= 0xffff; -            etiFrame[index++] = ((char *) &CRCtmp)[1]; -            etiFrame[index++] = ((char *) &CRCtmp)[0]; - - -            figSize = 0; -            // FIB 2 insertion -            if (rotateFIB < ensemble->services.size()) { -                service = ensemble->services.begin() + rotateFIB; - -                // FIG type 1/1, SI, Service label, one instance per subchannel -                if ((*service)->getType(ensemble) == 0) { -                    fig1_1 = (FIGtype1_1 *) & etiFrame[index]; - -                    fig1_1->FIGtypeNumber = 1; -                    fig1_1->Length = 21; -                    fig1_1->Charset = 0; -                    fig1_1->OE = 0; -                    fig1_1->Extension = 1; - -                    fig1_1->Sld = htons((*service)->id); -                    index += 4; -                    figSize += 4; -                } -                else { -                    fig1_5 = (FIGtype1_5 *) & etiFrame[index]; -                    fig1_5->FIGtypeNumber = 1; -                    fig1_5->Length = 23; -                    fig1_5->Charset = 0; -                    fig1_5->OE = 0; -                    fig1_5->Extension = 5; - -                    fig1_5->SId = htonl((*service)->id); -                    index += 6; -                    figSize += 6; -                } -                memcpy(&etiFrame[index], (*service)->label.text(), 16); -                index += 16; -                figSize += 16; -                etiFrame[index++] = (*service)->label.flag() >> 8; -                etiFrame[index++] = (*service)->label.flag() & 0xFF; -                figSize += 2; -            } -            else if (rotateFIB < -                    ensemble->services.size() + ensemble->components.size()) { -                component = ensemble->components.begin() + -                    (rotateFIB - ensemble->services.size()); - -                service = getService(*component, ensemble->services); - -                subchannel = -                    getSubchannel(ensemble->subchannels, (*component)->subchId); - -                if ((*component)->label.text()[0] != 0) { -                    if ((*service)->getType(ensemble) == 0) {   // Programme -                        FIGtype1_4_programme *fig1_4; -                        fig1_4 = (FIGtype1_4_programme*)&etiFrame[index]; - -                        fig1_4->FIGtypeNumber = 1; -                        fig1_4->Length = 22; -                        fig1_4->Charset = 0; -                        fig1_4->OE = 0; -                        fig1_4->Extension = 4; -                        fig1_4->PD = 0; -                        fig1_4->rfa = 0; -                        fig1_4->SCIdS = (*component)->SCIdS; - -                        fig1_4->SId = htons((*service)->id); -                        index += 5; -                        figSize += 5; -                    } -                    else {    // Data -                        FIGtype1_4_data *fig1_4; -                        fig1_4 = (FIGtype1_4_data *) & etiFrame[index]; -                        fig1_4->FIGtypeNumber = 1; -                        fig1_4->Length = 24; -                        fig1_4->Charset = 0; -                        fig1_4->OE = 0; -                        fig1_4->Extension = 4; -                        fig1_4->PD = 1; -                        fig1_4->rfa = 0; -                        fig1_4->SCIdS = (*component)->SCIdS; - -                        fig1_4->SId = htonl((*service)->id); -                        index += 7; -                        figSize += 7; -                    } -                    memcpy(&etiFrame[index], (*component)->label.text(), 16); -                    index += 16; -                    figSize += 16; - -                    etiFrame[index++] = (*component)->label.flag() >> 8; -                    etiFrame[index++] = (*component)->label.flag() & 0xFF; -                    figSize += 2; -                } -            } -            memcpy(&etiFrame[index], Padding_FIB, 30 - figSize); -            index += 30 - figSize; - -            CRCtmp = 0xffff; -            CRCtmp = crc16(CRCtmp, &etiFrame[(index - 30)], 30); -            CRCtmp ^= 0xffff; -            etiFrame[index++] = ((char *) &CRCtmp)[1]; -            etiFrame[index++] = ((char *) &CRCtmp)[0]; - -            /* ETSI EN 300 799 Table 2: -             * Only TM3 has a FIB count to CIF count that is -             * not 3 to 1. -             */ -            if (ensemble->mode == 3) { -                memcpy(&etiFrame[index], Padding_FIB, 30); -                index += 30; - -                CRCtmp = 0xffff; -                CRCtmp = crc16(CRCtmp, &etiFrame[(index - 30)], 30); -                CRCtmp ^= 0xffff; -                etiFrame[index++] = ((char *) &CRCtmp)[1]; -                etiFrame[index++] = ((char *) &CRCtmp)[0]; -            } - -            if (ensemble->services.size() > 30) { -                etiLog.log(error, -                        "Sorry, but this software currently can't write " -                        "Service Label of more than 30 services.\n"); -                returnCode = -1; -                throw MuxInitException(); -            } - -            // counter for FIG 0/0 -            insertFIG = (insertFIG + 1) % 16; - -            // We rotate through the FIBs every 30 frames -            rotateFIB = (rotateFIB + 1) % 30; - -            /********************************************************************** -             ******  Input Data Reading ******************************************* -             **********************************************************************/ - -            for (subchannel = ensemble->subchannels.begin(); -                    subchannel != ensemble->subchannels.end(); -                    ++subchannel) { - -                TagESTn* tag = edi_subchannelToTag[*subchannel]; - -                int sizeSubchannel = getSizeByte(*subchannel); -                int result = (*subchannel)->input->readFrame( -                        &etiFrame[index], sizeSubchannel); - -                if (result < 0) { -                    etiLog.log(info, -                            "Subchannel %d read failed at ETI frame number: %d\n", -                            (*subchannel)->id, currentFrame); -                } - -                // save pointer to Audio or Data Stream into correct TagESTn for EDI -                tag->mst_data = &etiFrame[index]; - -                index += sizeSubchannel; -            } - - -            index = (3 + fc->NST + FICL) * 4; -            for (subchannel = ensemble->subchannels.begin(); -                    subchannel != ensemble->subchannels.end(); -                    ++subchannel) { -                index += getSizeByte(*subchannel); -            } - -            /******* Section EOF **************************************************/ -            // End of Frame, 4 octets -            index = (FLtmp + 1 + 1) * 4; -            eti_EOF *eof = (eti_EOF *) & etiFrame[index]; - -            // CRC of Main Stream data (MST), 16 bits -            index = ((fc->NST) + 2 + 1) * 4;            // MST position -            MSTsize = ((FLtmp) - 1 - (fc->NST)) * 4;    // data size - -            CRCtmp = 0xffff; -            CRCtmp = crc16(CRCtmp, &etiFrame[index], MSTsize); -            CRCtmp ^= 0xffff; -            eof->CRC = htons(CRCtmp); - -            //RFU, Reserved for future use, 2 bytes, should be 0xFFFF -            eof->RFU = htons(0xFFFF); - -            /******* Section TIST *************************************************/ -            // TimeStamps, 24 bits + 1 octet -            index = (FLtmp + 2 + 1) * 4; -            eti_TIST *tist = (eti_TIST *) & etiFrame[index]; - -            if (enableTist) { -                tist->TIST = htonl(timestamp) | 0xff; -            } -            else { -                tist->TIST = htonl(0xffffff) | 0xff; -            } - -            timestamp += 3 << 17; -            if (timestamp > 0xf9ffff) -            { -                timestamp -= 0xfa0000; - -                // Also update MNSC time for next frame -                MNSC_increment_time = true; -            } - - - -            /**********************************************************************  -             ***********   Section FRPD   ***************************************** -             **********************************************************************/ - -            int frame_size = (FLtmp + 1 + 1 + 1 + 1) * 4; - -            // Give the data to the outputs -            for (output = outputs.begin() ; output != outputs.end(); ++output) { -                if ((*output)->output->Write(etiFrame, frame_size) -                        == -1) { -                    etiLog.log(error, "Can't write to output %s://%s\n", -                            (*output)->outputProto.c_str(), (*output)->outputName.c_str()); -                } -            } - -#ifdef DUMP_BRIDGE -            dumpBytes(dumpData, sizeSubChannel, stderr); -#endif // DUMP_BRIDGE - -            /**********************************************************************  -             ***********   Finalise and send EDI   ******************************** -             **********************************************************************/ - -            if (edi_conf.enabled) { -                // put tags *ptr, DETI and all subchannels into one TagPacket -                edi_tagpacket.tag_items.push_back(&edi_tagStarPtr); -                edi_tagpacket.tag_items.push_back(&edi_tagDETI); - -                list<TagESTn>::iterator tag; -                for (tag = edi_subchannels.begin(); tag != edi_subchannels.end(); ++tag) { -                    edi_tagpacket.tag_items.push_back(&(*tag)); -                } - -                // Assemble into one AF Packet -                AFPacket edi_afpacket = edi_afPacketiser.Assemble(edi_tagpacket); - -                if (edi_conf.enable_pft) { -                    // Apply PFT layer to AF Packet (Reed Solomon FEC and Fragmentation) -                    vector< PFTFragment > edi_fragments = -                        edi_pft.Assemble(edi_afpacket); - -                    // Send over ethernet -                    vector< vector<uint8_t> >::iterator edi_frag; -                    for (edi_frag = edi_fragments.begin(); -                            edi_frag != edi_fragments.end(); -                            ++edi_frag) { - -                        UdpPacket udppacket; - -                        InetAddress& addr = udppacket.getAddress(); -                        addr.setAddress(edi_conf.dest_addr.c_str()); -                        addr.setPort(edi_conf.dest_port); - -                        udppacket.addData(&(edi_frag->front()), edi_frag->size()); - -                        edi_output.send(udppacket); - -                        if (edi_conf.dump) { -                            std::ostream_iterator<uint8_t> debug_iterator(edi_debug_file); -                            std::copy(edi_frag->begin(), edi_frag->end(), debug_iterator); -                        } -                    } - -                    if (edi_conf.verbose) { -                        fprintf(stderr, "EDI number of PFT fragments %zu\n", -                                edi_fragments.size()); -                    } -                } -                else { -                    // Send over ethernet - -                    UdpPacket udppacket; - -                    InetAddress& addr = udppacket.getAddress(); -                    addr.setAddress(edi_conf.dest_addr.c_str()); -                    addr.setPort(edi_conf.dest_port); - -                    udppacket.addData(&(edi_afpacket.front()), edi_afpacket.size()); - -                    edi_output.send(udppacket); -                } - -                if (edi_conf.dump) { -                    std::ostream_iterator<uint8_t> debug_iterator(edi_debug_file); -                    std::copy(edi_afpacket.begin(), edi_afpacket.end(), debug_iterator); -                } -            } - -#if _DEBUG -            /**********************************************************************  -             ***********   Output a small message ********************************* -             **********************************************************************/ -            if (currentFrame % 100 == 0) { -                if (enableTist) { -                    etiLog.log(info, "ETI frame number %i Timestamp: %d + %f\n", -                            currentFrame, mnsc_time.tv_sec,  -                            (timestamp & 0xFFFFFF) / 16384000.0); -                } -                else { -                    etiLog.log(info, "ETI frame number %i Time: %d, no TIST\n", -                            currentFrame, mnsc_time.tv_sec); -                }              } -#endif              /* Check every six seconds if the remote control is still working */ -            if (rc && fc->FCT == 249 && rc->fault_detected()) { -                    etiLog.level(warn) << "Detected Remote Control fault, restarting it"; -                    rc->restart(); +            if ((currentFrame % 250 == 249) && rc->fault_detected()) { +                etiLog.level(warn) << "Detected Remote Control fault, restarting it"; +                rc->restart();              }              /* Same for statistics server */ -            if (mgmt_server && fc->FCT % 10 == 0) { -                if (mgmt_server->fault_detected()) { +            if (currentFrame % 10 == 0) { +                ManagementServer& mgmt_server = get_mgmt_server(); + +                if (mgmt_server.fault_detected()) {                      etiLog.level(warn) << -                        "Detected Statistics Server fault, restarting it"; -                    mgmt_server->restart(); +                        "Detected Management Server fault, restarting it"; +                    mgmt_server.restart(); +                } +                else if (mgmt_server.request_pending()) { +                    mgmt_server.update_ptree(pt);                  } -                else if (mgmt_server->request_pending()) { -                    mgmt_server->update_ptree(pt); +                else if (mgmt_server.retrieve_new_ptree(pt)) { +                    etiLog.level(warn) << +                        "Detected configuration change"; +                    mux.update_config(pt);                  }              }          } - +        etiLog.level(info) << "Goodbye";      }      catch (const MuxInitException& except) { -        etiLog.level(error) << "Caught multiplex initialisation error: " << +        etiLog.level(error) << "Multiplex initialisation aborted: " <<              except.what();      }      catch (const std::invalid_argument& except) { @@ -2200,30 +484,7 @@ int main(int argc, char *argv[])      etiLog.log(debug, "exiting...\n");      fflush(stderr); -    // close files fichiers -    for (subchannel = ensemble->subchannels.begin(); -            subchannel != ensemble->subchannels.end(); -            ++subchannel) { -        if ((*subchannel)->input != NULL) { -            (*subchannel)->input->close(); -        } -        delete (*subchannel)->input; -    } -    for (output = outputs.begin() ; output != outputs.end(); ++output) { -        if ((*output)->output) { -            (*output)->output->Close(); -            delete ((*output)->output); -        } -    } -    for_each(ensemble->components.begin(), ensemble->components.end(), free); -    for_each(ensemble->services.begin(), ensemble->services.end(), free); -    for_each(ensemble->subchannels.begin(), ensemble->subchannels.end(), free); -    for_each(outputs.begin(), outputs.end(), free); -    ensemble->components.clear(); -    ensemble->services.clear(); -    ensemble->subchannels.clear(); -    delete ensemble;      outputs.clear();      UdpSocket::clean(); diff --git a/src/DabMux.h b/src/DabMux.h index 89868ac..5dda759 100644 --- a/src/DabMux.h +++ b/src/DabMux.h @@ -31,6 +31,7 @@  #include <stdint.h>  #include <string>  #include <vector> +#include "DabMultiplexer.h"  #include "RemoteControl.h"  #include "dabOutput/dabOutput.h"  #include "dabInput.h" @@ -43,368 +44,5 @@  #   include <sys/time.h>  #endif - -// DAB Mode -#define DEFAULT_DAB_MODE    2 - -// Taille de la trame de donnee, sous-canal 3, nb de paquets de 64bits, -// STL3 * 8 = x kbytes par trame ETI - -// Data bitrate in kbits/s. Must be 64 kb/s multiple. -#define DEFAULT_DATA_BITRATE    384 -#define DEFAULT_PACKET_BITRATE  32 - -/* default ensemble parameters. Label must be max 16 chars, short label - * a subset of the label, max 8 chars - */ -#define DEFAULT_ENSEMBLE_LABEL          "ODR Dab Mux" -#define DEFAULT_ENSEMBLE_SHORT_LABEL    "ODRMux" -#define DEFAULT_ENSEMBLE_ID             0xc000 -#define DEFAULT_ENSEMBLE_ECC            0xa1 - -// start value for default service IDs (if not overridden by configuration) -#define DEFAULT_SERVICE_ID      50 -#define DEFAULT_PACKET_ADDRESS  0 - -/***************************************************************************** - *****************   Definition of FIG structures **************************** - *****************************************************************************/ -struct FIGtype0 { -    uint8_t Length:5; -    uint8_t FIGtypeNumber:3; -    uint8_t Extension:5; -    uint8_t PD:1; -    uint8_t OE:1; -    uint8_t CN:1; -} PACKED; - - -struct FIGtype0_0 { -    uint8_t Length:5; -    uint8_t FIGtypeNumber:3; -    uint8_t Extension:5; -    uint8_t PD:1; -    uint8_t OE:1; -    uint8_t CN:1; - -    uint16_t EId; -    uint8_t CIFcnt_hight:5; -    uint8_t Al:1; -    uint8_t Change:2; -    uint8_t CIFcnt_low:8; -} PACKED; - - -struct FIGtype0_2 { -    uint8_t Length:5; -    uint8_t FIGtypeNumber:3; -    uint8_t Extension:5; -    uint8_t PD:1; -    uint8_t OE:1; -    uint8_t CN:1; -} PACKED; - - -struct FIGtype0_2_Service { -    uint16_t SId; -    uint8_t NbServiceComp:4; -    uint8_t CAId:3; -    uint8_t Local_flag:1; -} PACKED; - - -struct FIGtype0_2_Service_data { -    uint32_t SId; -    uint8_t NbServiceComp:4; -    uint8_t CAId:3; -    uint8_t Local_flag:1; -} PACKED; - - -struct FIGtype0_2_audio_component { -    uint8_t ASCTy:6; -    uint8_t TMid:2; -    uint8_t CA_flag:1; -    uint8_t PS:1; -    uint8_t SubChId:6; -} PACKED; - - -struct FIGtype0_2_data_component { -    uint8_t DSCTy:6; -    uint8_t TMid:2; -    uint8_t CA_flag:1; -    uint8_t PS:1; -    uint8_t SubChId:6; -} PACKED; - - -struct FIGtype0_2_packet_component { -    uint8_t SCId_high:6; -    uint8_t TMid:2; -    uint8_t CA_flag:1; -    uint8_t PS:1; -    uint8_t SCId_low:6; -    void setSCId(uint16_t SCId) { -        SCId_high = SCId >> 6; -        SCId_low = SCId & 0x3f; -    } -} PACKED; - - -struct FIGtype0_3_header { -    uint8_t Length:5; -    uint8_t FIGtypeNumber:3; -    uint8_t Extension:5; -    uint8_t PD:1; -    uint8_t OE:1; -    uint8_t CN:1; -} PACKED; - - -/* Warning: When bit SCCA_flag is unset(0), the multiplexer R&S does not send - * the SCCA field. But, in the Factum ETI analyzer, if this field is not there, - * it is an error. - */ -struct FIGtype0_3_data { -    uint8_t SCId_high; -    uint8_t SCCA_flag:1; -    uint8_t rfa:3; -    uint8_t SCId_low:4; -    uint8_t DSCTy:6; -    uint8_t rfu:1; -    uint8_t DG_flag:1; -    uint8_t Packet_address_high:2; -    uint8_t SubChId:6; -    uint8_t Packet_address_low; -    uint16_t SCCA; -    void setSCId(uint16_t SCId) { -        SCId_high = SCId >> 4; -        SCId_low = SCId & 0xf; -    } -    void setPacketAddress(uint16_t address) { -        Packet_address_high = address >> 8; -        Packet_address_low = address & 0xff; -    } -} PACKED; - - -struct FIGtype0_8_short { -    uint8_t SCIdS:4; -    uint8_t rfa_1:3; -    uint8_t ext:1; -    uint8_t Id:6; -    uint8_t MscFic:1; -    uint8_t LS:1; -    uint8_t rfa_2; -} PACKED; - - -struct FIGtype0_8_long { -    uint8_t SCIdS:4; -    uint8_t rfa_1:3; -    uint8_t ext:1; -    uint8_t SCId_high:4; -    uint8_t rfa:3; -    uint8_t LS:1; -    uint8_t SCId_low; -    uint8_t rfa_2; -    void setSCId(uint16_t id) { -        SCId_high = id >> 8; -        SCId_low = id & 0xff; -    } -    uint16_t getSCid() { -        return (SCId_high << 8) | SCId_low; -    } -} PACKED; - - -struct FIGtype0_9 { -    uint8_t Length:5; -    uint8_t FIGtypeNumber:3; -    uint8_t Extension:5; -    uint8_t PD:1; -    uint8_t OE:1; -    uint8_t CN:1; - -    uint8_t ensembleLto:6; -    uint8_t lto:1; -    uint8_t ext:1; -    uint8_t ensembleEcc; -    uint8_t tableId; -} PACKED; - - -struct FIGtype0_10 { -    uint8_t Length:5; -    uint8_t FIGtypeNumber:3; -    uint8_t Extension:5; -    uint8_t PD:1; -    uint8_t OE:1; -    uint8_t CN:1; - -    uint8_t MJD_high:7; -    uint8_t RFU:1; -    uint8_t MJD_med; -    uint8_t Hours_high:3; -    uint8_t UTC:1; -    uint8_t ConfInd:1; -    uint8_t LSI:1; -    uint8_t MJD_low:2; -    uint8_t Minutes:6; -    uint8_t Hours_low:2; -    void setMJD(uint32_t date) { -        MJD_high = (date >> 10) & 0x7f; -        MJD_med = (date >> 2) & 0xff; -        MJD_low = date & 0x03; -    } -    void setHours(uint16_t hours) { -        Hours_high = (hours >> 2) & 0x07; -        Hours_low = hours & 0x03; -    } -} PACKED; - - -struct FIGtype0_17_programme { -    uint16_t SId; -    uint8_t NFC:2; -    uint8_t Rfa:2; -    uint8_t CC:1; -    uint8_t L:1; -    uint8_t PS:1; -    uint8_t SD:1; -} PACKED; - - -struct FIGtype0_1 { -    uint8_t Length:5; -    uint8_t FIGtypeNumber:3; -    uint8_t Extension:5; -    uint8_t PD:1; -    uint8_t OE:1; -    uint8_t CN:1; -} PACKED; - - -struct FIG_01_SubChannel_ShortF { -    uint8_t StartAdress_high:2; -    uint8_t SubChId:6; -    uint8_t StartAdress_low:8; -    uint8_t TableIndex:6; -    uint8_t TableSwitch:1; -    uint8_t Short_Long_form:1; -} PACKED; - - -struct FIG_01_SubChannel_LongF { -    uint8_t StartAdress_high:2; -    uint8_t SubChId:6; -    uint8_t StartAdress_low:8; -    uint8_t Sub_ChannelSize_high:2; -    uint8_t ProtectionLevel:2; -    uint8_t Option:3; -    uint8_t Short_Long_form:1; -    uint8_t Sub_ChannelSize_low:8; -} PACKED; - - -// See EN 300 401, Clause 8.1.20 for the FIG0_13 description -struct FIG0_13_shortAppInfo { -    uint16_t SId; -    uint8_t No:4; -    uint8_t SCIdS:4; -} PACKED; - - -struct FIG0_13_longAppInfo { -    uint32_t SId; -    uint8_t No:4; -    uint8_t SCIdS:4; -} PACKED; - - -struct FIG0_13_app { -    uint8_t typeHigh; -    uint8_t length:5; -    uint8_t typeLow:3; -    void setType(uint16_t type) { -        typeHigh = type >> 3; -        typeLow = type & 0x1f; -    } -    uint32_t xpad; -} PACKED; - -#define FIG0_13_APPTYPE_SLIDESHOW  0x2 -#define FIG0_13_APPTYPE_WEBSITE    0x3 -#define FIG0_13_APPTYPE_TPEG       0x4 -#define FIG0_13_APPTYPE_DGPS       0x5 -#define FIG0_13_APPTYPE_TMC        0x6 -#define FIG0_13_APPTYPE_EPG        0x7 -#define FIG0_13_APPTYPE_DABJAVA    0x8 -#define FIG0_13_APPTYPE_JOURNALINE 0x441 - - -struct FIGtype1_0 { -    uint8_t Length:5; -    uint8_t FIGtypeNumber:3; -    uint8_t Extension:3; -    uint8_t OE:1; -    uint8_t Charset:4; - -    uint16_t EId; -} PACKED; - - -struct FIGtype1_1 { -    uint8_t Length:5; -    uint8_t FIGtypeNumber:3; -    uint8_t Extension:3; -    uint8_t OE:1; -    uint8_t Charset:4; - -    uint16_t Sld; -} PACKED; - - -struct FIGtype1_5 { -    uint8_t Length:5; -    uint8_t FIGtypeNumber:3; -    uint8_t Extension:3; -    uint8_t OE:1; -    uint8_t Charset:4; -    uint32_t SId; -} PACKED; - - -struct FIGtype1_4_programme { -    uint8_t Length:5; -    uint8_t FIGtypeNumber:3; -    uint8_t Extension:3; -    uint8_t OE:1; -    uint8_t Charset:4; -    uint8_t SCIdS:4; -    uint8_t rfa:3; -    uint8_t PD:1; -    uint16_t SId; -} PACKED; - - -struct FIGtype1_4_data { -    uint8_t Length:5; -    uint8_t FIGtypeNumber:3; -    uint8_t Extension:3; -    uint8_t OE:1; -    uint8_t Charset:4; -    uint8_t SCIdS:4; -    uint8_t rfa:3; -    uint8_t PD:1; -    uint32_t SId; -} PACKED; - - -#ifdef _WIN32 -#   pragma pack(pop) -#endif -  #endif @@ -73,10 +73,12 @@ class LogToSyslog : public LogBackend {              int syslog_level = LOG_EMERG;              switch (level) {                  case debug: syslog_level = LOG_DEBUG; break; -                case alert: syslog_level = LOG_ALERT; break;                  case info:  syslog_level = LOG_INFO; break; +                /* we don't have the notice level */                  case warn:  syslog_level = LOG_WARNING; break;                  case error: syslog_level = LOG_ERR; break; +                default:    syslog_level = LOG_CRIT; break; +                case alert: syslog_level = LOG_ALERT; break;                  case emerg: syslog_level = LOG_EMERG; break;              } diff --git a/src/Makefile.am b/src/Makefile.am index f3bce5e..0e55b52 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -21,7 +21,7 @@  # along with ODR-DabMux.  If not, see <http://www.gnu.org/licenses/>.  if IS_GIT_REPO -GITVERSION_FLAGS = -DGITVERSION="\"`git describe`\"" +GITVERSION_FLAGS = -DGITVERSION="\"`git describe --dirty`\""  else  GITVERSION_FLAGS =  endif @@ -39,9 +39,11 @@ bin_PROGRAMS=odr-dabmux odr-bridgetest  ZMQ_LIBS    =  endif -odr_dabmux_CPPFLAGS =-Wall -I$(FARSYNC_DIR) $(GITVERSION_FLAGS) +odr_dabmux_CFLAGS   =-Wall -I$(FARSYNC_DIR) $(GITVERSION_FLAGS) +odr_dabmux_CXXFLAGS =-Wall -I$(FARSYNC_DIR) $(GITVERSION_FLAGS)  odr_dabmux_LDADD    =$(FEC_LIBS) $(ZMQ_LIBS) -lpthread -lboost_thread -lboost_system  odr_dabmux_SOURCES  =DabMux.cpp DabMux.h \ +					 DabMultiplexer.cpp DabMultiplexer.h \                       dabInput.h dabInput.cpp \                       dabInputBridgeUdp.h dabInputBridgeUdp.cpp \                       dabInputDabplusFifo.h dabInputDabplusFifo.cpp \ diff --git a/src/ManagementServer.cpp b/src/ManagementServer.cpp index 9ebdfeb..9687278 100644 --- a/src/ManagementServer.cpp +++ b/src/ManagementServer.cpp @@ -39,6 +39,18 @@  #include "ManagementServer.h"  #include "Log.h" +ManagementServer& get_mgmt_server() +{ +    static ManagementServer mgmt_server; + +    return mgmt_server; + +    /* Warning, do not use the mgmt_server in the destructor +     * of another global object: you don't know which one +     * gets destroyed first +     */ +} +  void ManagementServer::registerInput(InputStat* is)  {      boost::mutex::scoped_lock lock(m_statsmutex); @@ -47,7 +59,7 @@ void ManagementServer::registerInput(InputStat* is)      if (m_inputStats.count(id) == 1) {          etiLog.level(error) << -            "Double registration in Stats Server with id '" << +            "Double registration in MGMT Server with id '" <<              id << "'";          return;      } @@ -71,7 +83,7 @@ bool ManagementServer::isInputRegistered(std::string& id)      if (m_inputStats.count(id) == 0) {          etiLog.level(error) << -            "Stats Server id '" << +            "Management Server: id '" <<              id << "' does was not registered";          return false;      } @@ -185,132 +197,139 @@ void ManagementServer::restart_thread(long)  void ManagementServer::serverThread()  { +    m_running = true;      m_fault = false; -    try { -        int accepted_sock; -        char buffer[256]; -        char welcome_msg[256]; -        struct sockaddr_in serv_addr, cli_addr; -        int n; - -        int welcome_msg_len = snprintf(welcome_msg, 256, -                "{ \"service\": \"" -                "%s %s Stats Server\" }\n", -                PACKAGE_NAME, -#if defined(GITVERSION) -                GITVERSION -#else -                PACKAGE_VERSION -#endif -                ); - +    std::stringstream bind_addr; +    bind_addr << "tcp://127.0.0.1:" << m_listenport; +    m_zmq_sock.bind(bind_addr.str().c_str()); -        m_sock = socket(AF_INET, SOCK_STREAM, 0); -        if (m_sock < 0) { -            etiLog.level(error) << "Error opening Stats Server socket: " << -                strerror(errno); -            m_fault = true; -            return; -        } +    while (m_running) { +        zmq::message_t zmq_message; +        m_zmq_sock.recv(&zmq_message); -        memset(&serv_addr, 0, sizeof(serv_addr)); -        serv_addr.sin_family = AF_INET; -        serv_addr.sin_addr.s_addr = INADDR_ANY; // TODO listen only on 127.0.0.1 -        serv_addr.sin_port = htons(m_listenport); -        if (bind(m_sock, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { -            etiLog.level(error) << "Error binding Stats Server socket: " << -                strerror(errno); -            goto end_serverthread; -        } +        handle_message(zmq_message); +    } -        if (listen(m_sock, 5) < 0) { -            etiLog.level(error) << "Error listening on Stats Server socket: " << -                strerror(errno); -            goto end_serverthread; -        } +    m_fault = true; +} -        m_running = true; +bool ManagementServer::handle_setptree( +        zmq::message_t& zmq_message, std::stringstream& answer) +{ +    try { +        if (zmq_message.more()) { +            zmq::message_t zmq_new_ptree; +            m_zmq_sock.recv(&zmq_new_ptree); +            std::string new_ptree( +                    (char*)zmq_new_ptree.data(), zmq_new_ptree.size() +                    ); -        while (m_running) { -            socklen_t cli_addr_len = sizeof(cli_addr); +            etiLog.level(info) << "Received ptree " << new_ptree; -            /* Accept actual connection from the client */ -            accepted_sock = accept(m_sock, -                    (struct sockaddr *)&cli_addr, -                    &cli_addr_len); +            boost::unique_lock<boost::mutex> lock(m_configmutex); +            m_pt.clear(); -            if (accepted_sock < 0) { -                etiLog.level(warn) << "Stats Server cound not accept connection: " << -                    strerror(errno); -                continue; -            } -            /* Send welcome message with version */ -            n = write(accepted_sock, welcome_msg, welcome_msg_len); -            if (n < 0) { -                etiLog.level(warn) << "Error writing to Stats Server socket " << -                    strerror(errno); -                close(accepted_sock); -                continue; -            } +            std::stringstream json_stream; +            json_stream << new_ptree; +            boost::property_tree::json_parser::read_json(json_stream, m_pt); -            /* receive command */ -            memset(buffer, 0, 256); -            int n = read(accepted_sock, buffer, 255); -            if (n < 0) { -                etiLog.level(warn) << "Error reading from Stats Server socket " << -                    strerror(errno); -                close(accepted_sock); -                continue; -            } +            m_retrieve_pending = true; +            answer << "OK"; -            if (strcmp(buffer, "config\n") == 0) { -                std::string json = getStatConfigJSON(); -                n = write(accepted_sock, json.c_str(), json.size()); -            } -            else if (strcmp(buffer, "values\n") == 0) { -                std::string json = getValuesJSON(); -                n = write(accepted_sock, json.c_str(), json.size()); -            } -            else if (strcmp(buffer, "state\n") == 0) { -                std::string json = getStateJSON(); -                n = write(accepted_sock, json.c_str(), json.size()); -            } -            if (strcmp(buffer, "getptree\n") == 0) { -                boost::unique_lock<boost::mutex> lock(m_configmutex); -                m_pending = true; +            return true; +        } +        else { +            etiLog.level(error) << +                "MGMT: setptree command is missing data."; +        } +    } +    catch (std::exception& e) { +        etiLog.level(error) << +            "MGMT: setptree error." << e.what(); +    } +    return false; +} -                while (m_pending) { -                    m_condition.wait(lock); -                } -                std::stringstream ss; -                boost::property_tree::json_parser::write_json(ss, m_pt); +void ManagementServer::handle_message(zmq::message_t& zmq_message) +{ +    std::stringstream answer; +    std::string data((char*)zmq_message.data(), zmq_message.size()); -                std::string response = ss.str(); +    try { +        etiLog.level(info) << "RC: Accepted"; -                n = write(accepted_sock, response.c_str(), response.size()); -            } -            else { -                int len = snprintf(buffer, 256, "Invalid command\n"); -                n = write(accepted_sock, buffer, len); -            } +        if (data == "info") { +            answer << +                "{ \"service\": \"" << +                PACKAGE_NAME << " " << +#if defined(GITVERSION) +                GITVERSION << +#else +                PACKAGE_VERSION << +#endif +                " MGMT Server\" }\n"; +        } +        else if (data == "config") { +            answer << getStatConfigJSON(); +        } +        else if (data == "values") { +            answer << getValuesJSON(); +        } +        else if (data == "state") { +            answer << getStateJSON(); +        } +        else if (data == "setptree") { +            handle_setptree(zmq_message, answer); +        } +        else if (data == "getptree") { +            boost::unique_lock<boost::mutex> lock(m_configmutex); +            m_pending = true; -            if (n < 0) { -                etiLog.level(warn) << "Error writing to Stats Server socket " << -                    strerror(errno); +            while (m_pending && !m_retrieve_pending) { +                m_condition.wait(lock);              } -            close(accepted_sock); +            boost::property_tree::json_parser::write_json(answer, m_pt); +        } +        else { +            answer << "Invalid command";          } -end_serverthread: -        m_fault = true; -        close(m_sock); - +        std::string answerstr(answer.str()); +        m_zmq_sock.send(answerstr.c_str(), answerstr.size());      }      catch (std::exception& e) { -        etiLog.level(error) << "Statistics server caught exception: " << e.what(); -        m_fault = true; +        etiLog.level(error) << +            "MGMT server caught exception: " << +            e.what(); +    } +} + +bool ManagementServer::retrieve_new_ptree(boost::property_tree::ptree& pt) +{ +    boost::unique_lock<boost::mutex> lock(m_configmutex); + +    if (m_retrieve_pending) +    { +        pt = m_pt; + +        m_retrieve_pending = false; +        m_condition.notify_one(); +        return true; +    } + +    return false; +} + +void ManagementServer::update_ptree(const boost::property_tree::ptree& pt) +{ +    if (m_running) { +        boost::unique_lock<boost::mutex> lock(m_configmutex); +        m_pt = pt; +        m_pending = false; + +        m_condition.notify_one();      }  } @@ -318,12 +337,12 @@ end_serverthread:  void InputStat::registerAtServer()  { -    mgmt_server->registerInput(this); +    get_mgmt_server().registerInput(this);  }  InputStat::~InputStat()  { -    mgmt_server->unregisterInput(m_name); +    get_mgmt_server().unregisterInput(m_name);  }  std::string InputStat::encodeValuesJSON() diff --git a/src/ManagementServer.h b/src/ManagementServer.h index fa3f170..836dee4 100644 --- a/src/ManagementServer.h +++ b/src/ManagementServer.h @@ -7,7 +7,7 @@      http://www.opendigitalradio.org -   A TCP Socket server that serves state information and statistics for +   A server that serves state information and statistics for     monitoring purposes, and also serves the internal configuration     property tree. @@ -15,7 +15,7 @@          http://munin-monitoring.org/     but is not specific to it. -   The TCP Server responds in JSON, and accepts the commands: +   The responds in JSON, and accepts the commands:      - config      - values        Inspired by the munin equivalent @@ -27,6 +27,7 @@        Returns the internal boost property_tree that contains the         multiplexer configuration DB. +   The server is using REQ/REP ZeroMQ sockets.     */  /*     This file is part of ODR-DabMux. @@ -52,15 +53,12 @@  #   include "config.h"  #endif -#include <sys/socket.h> -#include <netinet/in.h> -#include <unistd.h> -#include <netdb.h> -#include <arpa/inet.h> -#include <pthread.h> +#include "zmq.hpp"  #include <string>  #include <map> +#include <atomic>  #include <boost/thread.hpp> +#include <boost/bind.hpp>  #include <boost/property_tree/ptree.hpp>  #include <boost/property_tree/json_parser.hpp>  #include <ctime> @@ -317,33 +315,30 @@ class ManagementServer  {      public:          ManagementServer() : -            m_listenport(0), +            m_zmq_context(), +            m_zmq_sock(m_zmq_context, ZMQ_REP),              m_running(false),              m_fault(false), -            m_pending(false) -            { } - -        ManagementServer(int listen_port) : -            m_listenport(listen_port), -            m_running(false), -            m_fault(false), -            m_thread(&ManagementServer::serverThread, this), -            m_pending(false) -            { -                m_sock = 0; -            } +            m_pending(false) { }          ~ManagementServer()          {              m_running = false;              m_fault = false;              m_pending = false; -            if (m_sock) { -                close(m_sock); -            } + +            // TODO notify              m_thread.join();          } +        void open(int listenport) +        { +            m_listenport = listenport; +            if (m_listenport > 0) { +                m_thread = boost::thread(&ManagementServer::serverThread, this); +            } +        } +          /* Un-/Register a statistics data source */          void registerInput(InputStat* is);          void unregisterInput(std::string id); @@ -351,18 +346,16 @@ class ManagementServer          /* Ask if there is a configuration request pending */          bool request_pending() { return m_pending; } +        /* Load a ptree given by the management server. +         * +         * Returns true if the ptree was updated +         */ +        bool retrieve_new_ptree(boost::property_tree::ptree& pt); +          /* Update the copy of the configuration property tree and notify the           * update to the internal server thread.           */ -        void update_ptree(const boost::property_tree::ptree& pt) { -            if (m_running) { -                boost::unique_lock<boost::mutex> lock(m_configmutex); -                m_pt = pt; -                m_pending = false; - -                m_condition.notify_one(); -            } -        } +        void update_ptree(const boost::property_tree::ptree& pt);          bool fault_detected() { return m_fault; }          void restart(void); @@ -370,24 +363,27 @@ class ManagementServer      private:          void restart_thread(long); -        /******* TCP Socket Server ******/ +        /******* Server ******/ +        zmq::context_t m_zmq_context; +        zmq::socket_t  m_zmq_sock; +          // no copying (because of the thread)          ManagementServer(const ManagementServer& other);          void serverThread(void); +        void handle_message(zmq::message_t& zmq_message); +        bool handle_setptree(zmq::message_t& zmq_message, std::stringstream& answer);          bool isInputRegistered(std::string& id);          int m_listenport;          // serverThread runs in a separate thread -        bool m_running; -        bool m_fault; +        std::atomic<bool> m_running; +        std::atomic<bool> m_fault;          boost::thread m_thread;          boost::thread m_restarter_thread; -        int m_sock; -          /******* Statistics Data ********/          std::map<std::string, InputStat*> m_inputStats; @@ -415,13 +411,16 @@ class ManagementServer          /******** Configuration Data *******/          bool m_pending; +        bool m_retrieve_pending;          boost::condition_variable m_condition;          mutable boost::mutex m_configmutex;          boost::property_tree::ptree m_pt;  }; -extern ManagementServer* mgmt_server; +// If necessary construct the management server singleton and return +// a reference to it +ManagementServer& get_mgmt_server();  #endif diff --git a/src/MuxElements.cpp b/src/MuxElements.cpp index e8871d1..4bd82cd 100644 --- a/src/MuxElements.cpp +++ b/src/MuxElements.cpp @@ -24,6 +24,7 @@  */  #include <vector> +#include <algorithm>  #include "MuxElements.h"  #include <boost/algorithm/string.hpp> @@ -43,29 +44,26 @@ const unsigned short Sub_Channel_SizeTable[64] = {  using namespace std; -int DabLabel::setLabel(const std::string& text) +int DabLabel::setLabel(const std::string& label)  { -    int len = text.length(); -    if (len > 16) +    size_t len = label.length(); +    if (len > DABLABEL_LENGTH)          return -3; -    memset(m_text, 0, 17); -    memcpy(m_text, text.c_str(), len); -      m_flag = 0xFF00; // truncate the label to the eight first characters +    m_label = label; +      return 0;  } -int DabLabel::setLabel(const std::string& text, const std::string& short_label) +int DabLabel::setLabel(const std::string& label, const std::string& short_label)  {      DabLabel newlabel; -    memset(newlabel.m_text, 0, 17); -    int len = text.length(); -    if (len > 16) -        return -3; -    memcpy(newlabel.m_text, text.c_str(), len); +    int result = newlabel.setLabel(label); +    if (result < 0) +        return result;      /* First check if we can actually create the short label */      int flag = newlabel.setShortLabel(short_label); @@ -73,8 +71,8 @@ int DabLabel::setLabel(const std::string& text, const std::string& short_label)          return flag;      // short label is valid. -    memcpy(this->m_text, newlabel.m_text, 17); -    this->m_flag = flag & 0xFFFF; +    m_flag = flag & 0xFFFF; +    m_label = newlabel.m_label;      return 0;  } @@ -104,8 +102,8 @@ int DabLabel::setShortLabel(const std::string& slabel)      /* Iterate over the label and set the bits in the flag       * according to the characters in the slabel       */ -    for (int i = 0; i < 32; ++i) { -        if (*slab == this->m_text[i]) { +    for (size_t i = 0; i < m_label.size(); ++i) { +        if (*slab == m_label[i]) {              flag |= 0x8000 >> i;              if (*(++slab) == 0) {                  break; @@ -117,7 +115,7 @@ int DabLabel::setShortLabel(const std::string& slabel)       * we went through the whole label, the short label       * cannot be represented       */ -    if (*slab != 0) { +    if (*slab != '\0') {          return -1;      } @@ -138,15 +136,22 @@ int DabLabel::setShortLabel(const std::string& slabel)  const string DabLabel::short_label() const  {      stringstream shortlabel; -    for (int i = 0; i < 32; ++i) { +    for (size_t i = 0; i < m_label.size(); ++i) {          if (m_flag & 0x8000 >> i) { -            shortlabel << m_text[i]; +            shortlabel << m_label[i];          }      }      return shortlabel.str();  } +void DabLabel::writeLabel(uint8_t* buf) const +{ +    memset(buf, ' ', DABLABEL_LENGTH); +    if (m_label.size() <= DABLABEL_LENGTH) { +        std::copy(m_label.begin(), m_label.end(), (char*)buf); +    } +}  vector<dabSubchannel*>::iterator getSubchannel(          vector<dabSubchannel*>& subchannels, int id) @@ -186,19 +191,19 @@ vector<DabComponent*>::iterator getComponent(      return getComponent(components, serviceId, components.end());  } -vector<DabService*>::iterator getService( +std::vector<std::shared_ptr<DabService> >::iterator getService(          DabComponent* component, -        vector<DabService*>& services) +        std::vector<std::shared_ptr<DabService> >& services)  { -    vector<DabService*>::iterator service; - -    for (service = services.begin(); service != services.end(); ++service) { -        if ((*service)->id == component->serviceId) { -            break; +    size_t i = 0; +    for (auto service : services) { +        if (service->id == component->serviceId) { +            return services.begin() + i;          } +        i++;      } -    return service; +    throw std::runtime_error("Service not included in any component");  }  bool DabComponent::isPacketComponent(vector<dabSubchannel*>& subchannels) @@ -224,9 +229,6 @@ bool DabComponent::isPacketComponent(vector<dabSubchannel*>& subchannels)  void DabComponent::set_parameter(const string& parameter,          const string& value)  { -    stringstream ss(value); -    ss.exceptions ( stringstream::failbit | stringstream::badbit ); -      if (parameter == "label") {          vector<string> fields;          boost::split(fields, value, boost::is_any_of(",")); @@ -274,10 +276,7 @@ const string DabComponent::get_parameter(const string& parameter) const  {      stringstream ss;      if (parameter == "label") { -        char l[17]; -        l[16] = '\0'; -        memcpy(l, label.text(), 16); -        ss << l << "," << label.short_label(); +        ss << label.long_label() << "," << label.short_label();      }      else {          ss << "Parameter '" << parameter << @@ -289,7 +288,7 @@ const string DabComponent::get_parameter(const string& parameter) const  } -unsigned char DabService::getType(dabEnsemble* ensemble) +unsigned char DabService::getType(boost::shared_ptr<dabEnsemble> ensemble)  {      vector<dabSubchannel*>::iterator subchannel;      vector<DabComponent*>::iterator component = @@ -322,9 +321,6 @@ unsigned char DabService::nbComponent(vector<DabComponent*>& components)  void DabService::set_parameter(const string& parameter,          const string& value)  { -    stringstream ss(value); -    ss.exceptions ( stringstream::failbit | stringstream::badbit ); -      if (parameter == "label") {          vector<string> fields;          boost::split(fields, value, boost::is_any_of(",")); @@ -372,10 +368,7 @@ const string DabService::get_parameter(const string& parameter) const  {      stringstream ss;      if (parameter == "label") { -        char l[17]; -        l[16] = '\0'; -        memcpy(l, label.text(), 16); -        ss << l << "," << label.short_label(); +        ss << label.long_label() << "," << label.short_label();      }      else {          ss << "Parameter '" << parameter << @@ -388,9 +381,6 @@ const string DabService::get_parameter(const string& parameter) const  void dabEnsemble::set_parameter(const string& parameter, const string& value)  { -    stringstream ss(value); -    ss.exceptions ( stringstream::failbit | stringstream::badbit ); -      if (parameter == "localtimeoffset") {          if (value == "auto") {              lto_auto = true; diff --git a/src/MuxElements.h b/src/MuxElements.h index 3653ea4..ada7ce3 100644 --- a/src/MuxElements.h +++ b/src/MuxElements.h @@ -3,7 +3,7 @@     2011, 2012 Her Majesty the Queen in Right of Canada (Communications     Research Center Canada) -   Copyright (C) 2014 +   Copyright (C) 2014, 2015     Matthias P. Braendli, matthias.braendli@mpb.li     This file defines all data structures used in DabMux to represent @@ -29,6 +29,7 @@  #define _MUX_ELEMENTS  #include <vector> +#include <memory>  #include <string>  #include <functional>  #include <algorithm> @@ -51,6 +52,7 @@ struct dabOutput {      DabOutput* output;  }; +#define DABLABEL_LENGTH 16  class DabLabel  { @@ -61,7 +63,7 @@ class DabLabel           *          -2 if the short_label is too long           *          -3 if the text is too long           */ -        int setLabel(const std::string& text, const std::string& short_label); +        int setLabel(const std::string& label, const std::string& short_label);          /* Same as above, but sets the flag to 0xff00, truncating at 8           * characters. @@ -69,17 +71,28 @@ class DabLabel           * returns:  0 on success           *          -3 if the text is too long           */ -        int setLabel(const std::string& text); +        int setLabel(const std::string& label); + +        /* Write the label to the 16-byte buffer given in buf +         * In the DAB standard, the label is 16 bytes long, and is +         * padded using spaces. +         */ +        void writeLabel(uint8_t* buf) const; -        const char* text() const { return m_text; }          uint16_t flag() const { return m_flag; } +        const std::string long_label() const { return m_label; }          const std::string short_label() const;      private: -        // In the DAB standard, the label is 16 chars. -        // We keep it here zero-terminated -        char m_text[17]; +        /* The flag field selects which label characters make +         * up the short label +         */          uint16_t m_flag; + +        /* The m_label is not padded in any way */ +        std::string m_label; + +        /* Checks and calculates the flag */          int setShortLabel(const std::string& slabel);  }; @@ -118,7 +131,7 @@ class dabEnsemble : public RemoteControllable {          int international_table; -        std::vector<DabService*> services; +        std::vector<std::shared_ptr<DabService> > services;          std::vector<DabComponent*> components;          std::vector<dabSubchannel*> subchannels;  }; @@ -166,7 +179,16 @@ enum dab_subchannel_type_t {      Packet = 3  }; -struct dabSubchannel { +class dabSubchannel +{ +public: +    dabSubchannel(std::string& uid) : +            uid(uid) +    { +    } + +    std::string uid; +      std::string inputUri;      DabInputBase* input;      unsigned char id; @@ -220,12 +242,15 @@ struct dabPacketComponent {  class DabComponent : public RemoteControllable  {      public: -        DabComponent(std::string uid) : -            RemoteControllable(uid) +        DabComponent(std::string& uid) : +            RemoteControllable(uid), +            uid(uid)          {              RC_ADD_PARAMETER(label, "Label and shortlabel [label,short]");          } +        std::string uid; +          DabLabel label;          uint32_t serviceId;          uint8_t subchId; @@ -258,18 +283,21 @@ class DabComponent : public RemoteControllable  class DabService : public RemoteControllable  {      public: -        DabService(std::string uid) : -            RemoteControllable(uid) +        DabService(std::string& uid) : +            RemoteControllable(uid), +            uid(uid)          {              RC_ADD_PARAMETER(label, "Label and shortlabel [label,short]");          } +        std::string uid; +          uint32_t id;          unsigned char pty;          unsigned char language;          bool program; -        unsigned char getType(dabEnsemble* ensemble); +        unsigned char getType(boost::shared_ptr<dabEnsemble> ensemble);          unsigned char nbComponent(std::vector<DabComponent*>& components);          DabLabel label; @@ -300,9 +328,9 @@ std::vector<DabComponent*>::iterator getComponent(          std::vector<DabComponent*>& components,          uint32_t serviceId); -std::vector<DabService*>::iterator getService( +std::vector<std::shared_ptr<DabService> >::iterator getService(          DabComponent* component, -        std::vector<DabService*>& services); +        std::vector<std::shared_ptr<DabService> >& services);  unsigned short getSizeCu(dabSubchannel* subchannel); @@ -312,5 +340,5 @@ unsigned short getSizeByte(dabSubchannel* subchannel);  unsigned short getSizeWord(dabSubchannel* subchannel); -  #endif + diff --git a/src/RemoteControl.cpp b/src/RemoteControl.cpp index e46bc8d..723ba9b 100644 --- a/src/RemoteControl.cpp +++ b/src/RemoteControl.cpp @@ -27,6 +27,7 @@  #include <iostream>  #include <string>  #include <boost/asio.hpp> +#include <boost/bind.hpp>  #include <boost/thread.hpp>  #include "Log.h" @@ -35,6 +36,12 @@  using boost::asio::ip::tcp;  using namespace std; +RemoteControllerTelnet::~RemoteControllerTelnet() +{ +    m_running = false; +    m_io_service.stop(); +    m_child_thread.join(); +}  void RemoteControllerTelnet::restart()  { @@ -47,85 +54,120 @@ void RemoteControllerTelnet::restart()  // thread.  void RemoteControllerTelnet::restart_thread(long)  { +    etiLog.level(warn) << "RC: Restart Telnet server"; +      m_running = false; +    m_io_service.stop(); -    if (m_port) { -        m_child_thread.interrupt(); -        m_child_thread.join(); -    } +    m_child_thread.join();      m_child_thread = boost::thread(&RemoteControllerTelnet::process, this, 0);  } -void RemoteControllerTelnet::process(long) +void RemoteControllerTelnet::handle_accept( +        const boost::system::error_code& boost_error, +        boost::shared_ptr< boost::asio::ip::tcp::socket > socket, +        boost::asio::ip::tcp::acceptor& acceptor)  { -    m_welcome = "ODR-DabMux Remote Control CLI\nWrite 'help' for help.\n**********\n"; -    m_prompt = "> "; + +    const std::string welcome = "ODR-DabMux Remote Control CLI\n" +                                "Write 'help' for help.\n" +                                "**********\n"; +    const std::string prompt = "> ";      std::string in_message;      size_t length; -    try { -        boost::asio::io_service io_service; -        tcp::acceptor acceptor(io_service, tcp::endpoint( -                    boost::asio::ip::address::from_string("127.0.0.1"), m_port) ); - -        while (m_running) { -            in_message = ""; +    if (boost_error) +    { +        etiLog.level(error) << "RC: Error accepting connection"; +        return; +    } -            tcp::socket socket(io_service); +    try { +        etiLog.level(info) << "RC: Accepted"; -            acceptor.accept(socket); +        boost::system::error_code ignored_error; -            boost::system::error_code ignored_error; +        boost::asio::write(*socket, boost::asio::buffer(welcome), +                boost::asio::transfer_all(), +                ignored_error); -            boost::asio::write(socket, boost::asio::buffer(m_welcome), +        while (m_running && in_message != "quit") { +            boost::asio::write(*socket, boost::asio::buffer(prompt),                      boost::asio::transfer_all(),                      ignored_error); -            while (m_running && in_message != "quit") { -                boost::asio::write(socket, boost::asio::buffer(m_prompt), -                        boost::asio::transfer_all(), -                        ignored_error); - -                in_message = ""; +            in_message = ""; -                boost::asio::streambuf buffer; -                length = boost::asio::read_until( socket, buffer, "\n", ignored_error); +            boost::asio::streambuf buffer; +            length = boost::asio::read_until(*socket, buffer, "\n", ignored_error); -                std::istream str(&buffer);  -                std::getline(str, in_message); +            std::istream str(&buffer); +            std::getline(str, in_message); -                if (length == 0) { -                    etiLog.level(info) << "RC: Connection terminated"; -                    break; -                } +            if (length == 0) { +                etiLog.level(info) << "RC: Connection terminated"; +                break; +            } -                while (in_message.length() > 0 &&  -                        (in_message[in_message.length()-1] == '\r' || -                         in_message[in_message.length()-1] == '\n')) { -                    in_message.erase(in_message.length()-1, 1); -                } +            while (in_message.length() > 0 && +                    (in_message[in_message.length()-1] == '\r' || +                     in_message[in_message.length()-1] == '\n')) { +                in_message.erase(in_message.length()-1, 1); +            } -                if (in_message.length() == 0) { -                    continue; -                } +            if (in_message.length() == 0) { +                continue; +            } -                etiLog.level(info) << "RC: Got message '" << in_message << "'"; +            etiLog.level(info) << "RC: Got message '" << in_message << "'"; -                dispatch_command(socket, in_message); -            } -            etiLog.level(info) << "RC: Closing socket"; -            socket.close(); +            dispatch_command(*socket, in_message);          } +        etiLog.level(info) << "RC: Closing socket"; +        socket->close();      }      catch (std::exception& e)      {          etiLog.level(error) << "Remote control caught exception: " << e.what(); -        m_fault = true;      }  } +void RemoteControllerTelnet::process(long) +{ +    m_running = true; + +    while (m_running) { +        m_io_service.reset(); + +        tcp::acceptor acceptor(m_io_service, tcp::endpoint( +                    boost::asio::ip::address::from_string("127.0.0.1"), m_port) ); + + +        // Add a job to start accepting connections. +        boost::shared_ptr<tcp::socket> socket( +                new tcp::socket(acceptor.get_io_service())); + +        // Add an accept call to the service.  This will prevent io_service::run() +        // from returning. +        etiLog.level(warn) << "RC: Waiting on connection"; +        acceptor.async_accept(*socket, +                boost::bind(&RemoteControllerTelnet::handle_accept, +                    this, +                    boost::asio::placeholders::error, +                    socket, +                    boost::ref(acceptor))); + +        // Process event loop. +        m_io_service.run(); +    } + +    etiLog.level(warn) << "RC: Leaving"; +    m_fault = true; +} + +  void RemoteControllerTelnet::dispatch_command(tcp::socket& socket, string command)  {      vector<string> cmd = tokenise_(command); diff --git a/src/RemoteControl.h b/src/RemoteControl.h index 16881b4..46a828f 100644 --- a/src/RemoteControl.h +++ b/src/RemoteControl.h @@ -32,6 +32,7 @@  #include <list>  #include <map>  #include <string> +#include <atomic>  #include <iostream>  #include <boost/bind.hpp>  #include <boost/shared_ptr.hpp> @@ -104,6 +105,10 @@ class RemoteControllable {              controller.enrol(this);          } +        virtual void enrol_at(boost::shared_ptr<BaseRemoteController> controller) { +            controller->enrol(this); +        } +          /* Return a list of possible parameters that can be set */          virtual std::list<std::string> get_supported_parameters() const {              std::list<std::string> parameterlist; @@ -137,25 +142,23 @@ class RemoteControllable {   */  class RemoteControllerTelnet : public BaseRemoteController {      public: -        RemoteControllerTelnet() -            : m_running(false), m_fault(false), +        RemoteControllerTelnet() : +            m_running(false), +            m_io_service(), +            m_fault(false),              m_port(0) { } -        RemoteControllerTelnet(int port) -            : m_running(true), m_fault(false), -            m_child_thread(&RemoteControllerTelnet::process, this, 0), +        RemoteControllerTelnet(int port) : +            m_running(false), +            m_io_service(), +            m_fault(false),              m_port(port) -        { } - -        ~RemoteControllerTelnet() { -            m_running = false; -            m_fault = false; -            if (m_port) { -                m_child_thread.interrupt(); -                m_child_thread.join(); -            } +        { +            restart();          } +        ~RemoteControllerTelnet(); +          void enrol(RemoteControllable* controllable) {              m_cohort.push_back(controllable);          } @@ -174,6 +177,11 @@ class RemoteControllerTelnet : public BaseRemoteController {          void reply(boost::asio::ip::tcp::socket& socket, std::string message); +        void handle_accept( +                const boost::system::error_code& boost_error, +                boost::shared_ptr< boost::asio::ip::tcp::socket > socket, +                boost::asio::ip::tcp::acceptor& acceptor); +          RemoteControllerTelnet& operator=(const RemoteControllerTelnet& other);          RemoteControllerTelnet(const RemoteControllerTelnet& other); @@ -237,10 +245,12 @@ class RemoteControllerTelnet : public BaseRemoteController {              return controllable->set_parameter(param, value);          } -        bool m_running; +        std::atomic<bool> m_running; + +        boost::asio::io_service m_io_service;          /* This is set to true if a fault occurred */ -        bool m_fault; +        std::atomic<bool> m_fault;          boost::thread m_restarter_thread;          boost::thread m_child_thread; @@ -248,9 +258,6 @@ class RemoteControllerTelnet : public BaseRemoteController {          /* This controller commands the controllables in the cohort */          std::list<RemoteControllable*> m_cohort; -        std::string m_welcome; -        std::string m_prompt; -          int m_port;  }; diff --git a/src/TcpSocket.cpp b/src/TcpSocket.cpp index 89fefd2..75b320f 100644 --- a/src/TcpSocket.cpp +++ b/src/TcpSocket.cpp @@ -266,7 +266,7 @@ int TcpSocket::write(const void* data, int size)    // ignore BROKENPIPE signal (we handle it instead)  //  void* old_sigpipe = signal ( SIGPIPE, SIG_IGN );    // try to send data -  int ret = send(listenSocket, (char*)data, size, 0 /*MSG_NOSIGNAL*/	); +  int ret = send(listenSocket, (const char*)data, size, 0 /*MSG_NOSIGNAL*/	);    // restore the BROKENPIPE handling  //  signal ( SIGPIPE,  (__sighandler_t)old_sigpipe );    if (ret == SOCKET_ERROR) { @@ -236,7 +236,7 @@ void init_crc32tab(uint32_t l_code, uint32_t l_init)  uint8_t crc8(uint8_t l_crc, const void *lp_data, unsigned l_nb)  { -    uint8_t* data = (uint8_t*)lp_data; +    const uint8_t* data = (const uint8_t*)lp_data;      while (l_nb--) {          l_crc = crc8tab[l_crc ^ *(data++)];      } @@ -246,7 +246,7 @@ uint8_t crc8(uint8_t l_crc, const void *lp_data, unsigned l_nb)  uint16_t crc16(uint16_t l_crc, const void *lp_data, unsigned l_nb)  { -    uint8_t* data = (uint8_t*)lp_data; +    const uint8_t* data = (const uint8_t*)lp_data;      while (l_nb--) {          l_crc =              (l_crc << 8) ^ crc16tab[(l_crc >> 8) ^ *(data++)]; @@ -257,7 +257,7 @@ uint16_t crc16(uint16_t l_crc, const void *lp_data, unsigned l_nb)  uint32_t crc32(uint32_t l_crc, const void *lp_data, unsigned l_nb)  { -    uint8_t* data = (uint8_t*)lp_data; +    const uint8_t* data = (const uint8_t*)lp_data;      while (l_nb--) {          l_crc =              (l_crc << 8) ^ crc32tab[((l_crc >> 24) ^ *(data++)) & 0xff]; diff --git a/src/dabInputZmq.cpp b/src/dabInputZmq.cpp index 9d39945..5c20baf 100644 --- a/src/dabInputZmq.cpp +++ b/src/dabInputZmq.cpp @@ -386,9 +386,9 @@ int DabInputZmqMPEG::readFromSocket(size_t framesize)          }          else if (m_enable_input) {              // copy the input frame blockwise into the frame_buffer -            uint8_t* frame = new uint8_t[framesize]; -            memcpy(frame, data, framesize); -            m_frame_buffer.push_back(frame); +            uint8_t* framedata = new uint8_t[framesize]; +            memcpy(framedata, data, framesize); +            m_frame_buffer.push_back(framedata);          }          else {              return 0; @@ -397,7 +397,7 @@ int DabInputZmqMPEG::readFromSocket(size_t framesize)      else {          etiLog.level(error) <<              "inputZMQ " << m_name << -            " wrong data size: recv'd " << msg.size() << +            " wrong data size: recv'd " << msg.size() << " Bytes" <<              ", need " << framesize << ".";          messageReceived = false;      } @@ -498,9 +498,6 @@ int DabInputZmqAAC::readFromSocket(size_t framesize)  void DabInputZmqBase::set_parameter(const string& parameter,          const string& value)  { -    stringstream ss(value); -    ss.exceptions ( stringstream::failbit | stringstream::badbit ); -      if (parameter == "buffer") {          size_t new_limit = atol(value.c_str()); diff --git a/src/dabOutput/dabOutput.h b/src/dabOutput/dabOutput.h index 18f3848..f6980fe 100644 --- a/src/dabOutput/dabOutput.h +++ b/src/dabOutput/dabOutput.h @@ -78,6 +78,8 @@ class DabOutput          virtual int Close() = 0;          virtual ~DabOutput() {} + +        virtual std::string get_info() = 0;  };  // ----- used in File and Fifo outputs @@ -111,7 +113,12 @@ class DabOutputFile : public DabOutput          int Write(void* buffer, int size);          int Close(); +        std::string get_info() { +            return "file://" + filename_; +        } +      protected: +        std::string filename_;          int file_;          EtiFileType type_;          unsigned long nbFrames_; @@ -126,6 +133,11 @@ class DabOutputFifo : public DabOutputFile          ~DabOutputFifo() {}          int Write(void* buffer, int size); + +        std::string get_info() { +            return "fifo://" + filename_; +        } +  };  // -------------- RAW socket ----------- @@ -162,7 +174,12 @@ class DabOutputRaw : public DabOutput          int Open(const char* name);          int Write(void* buffer, int size);          int Close(); + +        std::string get_info() { +            return "raw://" + filename_; +        }      private: +        std::string filename_;  #ifdef _WIN32          HANDLE socket_;  #else @@ -197,7 +214,11 @@ class DabOutputUdp : public DabOutput          int Write(void* buffer, int size);          int Close() { return 0; } +        std::string get_info() { +            return "udp://" + uri_; +        }      private: +        std::string uri_;          UdpSocket* socket_;          UdpPacket* packet_;  }; @@ -230,9 +251,14 @@ class DabOutputTcp : public DabOutput          int Write(void* buffer, int size);          int Close(); +        std::string get_info() { +            return "tcp://" + uri_; +        } +          TcpServer* server;          TcpSocket* client;      private: +        std::string uri_;          pthread_t thread_;  }; @@ -252,7 +278,12 @@ class DabOutputSimul : public DabOutput          int Open(const char* name);          int Write(void* buffer, int size);          int Close() { return 0; } + +        std::string get_info() { +            return "simul://" + name_; +        }      private: +        std::string name_;  #ifdef _WIN32          DWORD startTime_;  #else @@ -304,6 +335,7 @@ class DabOutputZMQ : public DabOutput  {      public:          DabOutputZMQ() : +            endpoint_(""),              zmq_proto_(""), zmq_context_(1),              zmq_pub_sock_(zmq_context_, ZMQ_PUB),              zmq_message_ix(0) @@ -312,6 +344,7 @@ class DabOutputZMQ : public DabOutput          }          DabOutputZMQ(std::string zmq_proto) : +            endpoint_(""),              zmq_proto_(zmq_proto), zmq_context_(1),              zmq_pub_sock_(zmq_context_, ZMQ_PUB),              zmq_message_ix(0) @@ -323,7 +356,11 @@ class DabOutputZMQ : public DabOutput              zmq_pub_sock_.close();          } -        int Open(const char* name); +        std::string get_info() { +            return "zmq: " + zmq_proto_ + "://" + endpoint_; +        } + +        int Open(const char* endpoint);          int Write(void* buffer, int size);          int Close();      private: @@ -334,6 +371,7 @@ class DabOutputZMQ : public DabOutput              /* Forbid copy constructor */          } +        std::string endpoint_;          std::string zmq_proto_;          zmq::context_t zmq_context_; // handle for the zmq context          zmq::socket_t zmq_pub_sock_; // handle for the zmq publisher socket diff --git a/src/dabOutput/dabOutputFifo.cpp b/src/dabOutput/dabOutputFifo.cpp index de9579e..6b1c016 100644 --- a/src/dabOutput/dabOutputFifo.cpp +++ b/src/dabOutput/dabOutputFifo.cpp @@ -59,6 +59,7 @@ int DabOutputFifo::Write(void* buffer, int size)              if (write(this->file_, padding, 6144 - size) == -1)                  goto FIFO_WRITE_ERROR;              break; +        case ETI_FILE_TYPE_NONE:          default:              etiLog.log(error, "File type is not supported.\n");              return -1; diff --git a/src/dabOutput/dabOutputFile.cpp b/src/dabOutput/dabOutputFile.cpp index f26ddfd..d7fd5c7 100644 --- a/src/dabOutput/dabOutputFile.cpp +++ b/src/dabOutput/dabOutputFile.cpp @@ -112,6 +112,7 @@ int DabOutputFile::Write(void* buffer, int size)          memset(padding, 0x55, 6144 - size);          if (write(this->file_, padding, 6144 - size) == -1) goto FILE_WRITE_ERROR;          break; +    case ETI_FILE_TYPE_NONE:      default:          etiLog.log(error, "File type is not supported.\n");          return -1; diff --git a/src/dabOutput/edi/AFPacket.h b/src/dabOutput/edi/AFPacket.h index 5f62456..9b189b8 100644 --- a/src/dabOutput/edi/AFPacket.h +++ b/src/dabOutput/edi/AFPacket.h @@ -39,6 +39,8 @@ typedef std::vector<uint8_t> AFPacket;  class AFPacketiser  {      public: +        AFPacketiser() : +            m_verbose(false) {};          AFPacketiser(bool verbose) :              m_verbose(verbose) {}; diff --git a/src/dabOutput/edi/PFT.h b/src/dabOutput/edi/PFT.h index 4aae817..9c6f7bd 100644 --- a/src/dabOutput/edi/PFT.h +++ b/src/dabOutput/edi/PFT.h @@ -49,6 +49,14 @@ class PFT      public:          static const int ParityBytes = 48; +        PFT() : +            m_k(207), +            m_m(3), +            m_dest_port(12000), +            m_pseq(0), +            m_verbose(false) +        { } +          PFT(unsigned int RSDataWordLength,              unsigned int NumRecoverableFragments,              const edi_configuration_t &conf) : @@ -25,7 +25,7 @@  #include <errno.h> -const static short bitrateArray[4][4][16] = { +static const short bitrateArray[4][4][16] = {      { // MPEG 2.5          {  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,             -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1 }, // layer invalid @@ -69,7 +69,7 @@ const static short bitrateArray[4][4][16] = {  }; -const static int samplingrateArray[4][4] = { +static const int samplingrateArray[4][4] = {      { 11025, 12000, 8000, 0 },  // MPEG 2.5      { -1, -1, -1, -1 },         // MPEG invalid      { 22050, 24000, 16000, 0 }, // MPEG 2 diff --git a/src/utils.cpp b/src/utils.cpp index 7c3c516..42937c1 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -3,7 +3,7 @@     2011, 2012 Her Majesty the Queen in Right of Canada (Communications     Research Center Canada) -   Copyright (C) 2013, 2014 Matthias P. Braendli +   Copyright (C) 2013, 2014, 2015 Matthias P. Braendli     http://mpb.li  */  /* @@ -24,6 +24,7 @@  */  #include <cstring>  #include <iostream> +#include <boost/shared_ptr.hpp>  #include "DabMux.h"  #include "utils.h" @@ -73,7 +74,7 @@ void header_message()      fprintf(stderr,              "(Communications Research Centre Canada) All rights reserved.\n\n");      fprintf(stderr, -            "Copyright (C) 2013, 2014 Matthias P. Braendli\n"); +            "Copyright (C) 2013, 2014, 2015 Matthias P. Braendli\n");      fprintf(stderr,              "http://opendigitalradio.org\n\n"); @@ -220,12 +221,12 @@ void printUsage(char *name, FILE* out)              "  within the common interleaved frame.\n"              "\n"              "   __________________________________________________\n" -            "  |                   CRC-Ensemble                   |  ENSEMBLE\n" +            "  |                     Ensemble                     |  ENSEMBLE\n"              "  |__________________________________________________|\n"              "          |                 |                 |\n"              "          |                 |                 |\n"              "   _______V______    _______V______    _______V______\n" -            "  | CRC-Service1 |  | CRC-Service2 |  | CRC-Service3 |  SERVICES\n" +            "  |   Service 1  |  |   Service 2  |  |   Service 3  |  SERVICES\n"              "  |______________|  |______________|  |______________|\n"              "     |        |        |        | |______         |\n"              "     |        |        |        |        |        |\n" @@ -330,12 +331,12 @@ void printUsage(char *name, FILE* out)              "  within the common interleaved frame.\n"              "\n"              "   __________________________________________________\n" -            "  |                   CRC-Ensemble                   |  ENSEMBLE\n" +            "  |                     Ensemble                     |  ENSEMBLE\n"              "  |__________________________________________________|\n"              "          |                 |                 |\n"              "          |                 |                 |\n"              "   _______V______    _______V______    _______V______\n" -            "  | CRC-Service1 |  | CRC-Service2 |  | CRC-Service3 |  SERVICES\n" +            "  |   Service 1  |  |   Service 2  |  |   Service 3  |  SERVICES\n"              "  |______________|  |______________|  |______________|\n"              "     |        |        |        | |______         |\n"              "     |        |        |        |        |        |\n" @@ -352,47 +353,40 @@ void printUsage(char *name, FILE* out)  }  #endif -void printOutputs(vector<dabOutput*>& outputs) +void printOutputs(vector<boost::shared_ptr<DabOutput> >& outputs)  { -    vector<dabOutput*>::const_iterator output;      int index = 0; -    for (output = outputs.begin(); output != outputs.end(); ++output) { +    for (auto output : outputs) {          etiLog.log(info, "Output      %i", index); -        etiLog.level(info) << "  protocol: " << -                (*output)->outputProto; - -        etiLog.level(info) << "  name:     " << -                (*output)->outputName.c_str(); -        // Daboutputfile mangles with outputName, inserting \0 to -        // cut the string in several parts. That doesn't work -        // with stl strings. Thats why the .c_str() +        etiLog.level(info) << "  URI: " << +            output->get_info();          ++index;      }  } -void printServices(vector<DabService*>& services) +void printServices(const vector<shared_ptr<DabService> >& services)  { -    vector<DabService*>::const_iterator current;      int index = 0; -    for (current = services.begin(); current != services.end(); ++current) { +    for (auto service : services) { -        etiLog.level(info) << "Service       " << (*current)->get_rc_name(); -        etiLog.level(info) << " label:       " << (*current)->label.text(); +        etiLog.level(info) << "Service       " << service->get_rc_name(); +        etiLog.level(info) << " label:       " << +                service->label.long_label();          etiLog.level(info) << " short label: " << -                (*current)->label.short_label(); +                service->label.short_label(); -        etiLog.log(info, " (0x%x)", (*current)->label.flag()); -        etiLog.log(info, " id:          0x%lx (%lu)", (*current)->id, -                (*current)->id); +        etiLog.log(info, " (0x%x)", service->label.flag()); +        etiLog.log(info, " id:          0x%lx (%lu)", service->id, +                service->id); -        etiLog.log(info, " pty:         0x%x (%u)", (*current)->pty, -                (*current)->pty); +        etiLog.log(info, " pty:         0x%x (%u)", service->pty, +                service->pty);          etiLog.log(info, " language:    0x%x (%u)", -                (*current)->language, (*current)->language); +                service->language, service->language);          ++index;      }  } @@ -413,7 +407,8 @@ void printComponent(DabComponent* component)  {      etiLog.log(info, " service id:             %i", component->serviceId);      etiLog.log(info, " subchannel id:          %i", component->subchId); -    etiLog.log(info, " label:                  %s", component->label.text()); +    etiLog.level(info) << " label:                  " << +            component->label.long_label();      etiLog.level(info) << " short label:            " <<              component->label.short_label(); @@ -526,12 +521,13 @@ void printSubchannels(vector<dabSubchannel*>& subchannels)      }  } -void printEnsemble(dabEnsemble* ensemble) +void printEnsemble(const boost::shared_ptr<dabEnsemble> ensemble)  {      etiLog.log(info, "Ensemble");      etiLog.log(info, " id:          0x%lx (%lu)", ensemble->id, ensemble->id);      etiLog.log(info, " ecc:         0x%x (%u)", ensemble->ecc, ensemble->ecc); -    etiLog.log(info, " label:       %s", ensemble->label.text()); +    etiLog.level(info) << " label:       " << +            ensemble->label.long_label();      etiLog.level(info) << " short label: " <<              ensemble->label.short_label(); diff --git a/src/utils.h b/src/utils.h index ee2340f..1756adf 100644 --- a/src/utils.h +++ b/src/utils.h @@ -29,6 +29,7 @@  #define _UTILS_H  #include <cstdio> +#include "MuxElements.h"  time_t getDabTime(); @@ -49,17 +50,18 @@ void printUsageConfigfile(char *name, FILE* out = stderr);  /* The following four utility functions display a   * description of all outputs, services, components   * resp. subchannels*/ -void printOutputs(std::vector<dabOutput*>& outputs); +void printOutputs(std::vector<boost::shared_ptr<DabOutput> >& outputs); -void printServices(std::vector<DabService*>& services); +void printServices(std::vector<std::shared_ptr<DabService> >& services);  void printComponents(std::vector<DabComponent*>& components);  void printSubchannels(std::vector<dabSubchannel*>& subchannels);  /* Print information about the whole ensemble */ -void printEnsemble(dabEnsemble* ensemble); +void printEnsemble(const boost::shared_ptr<dabEnsemble> ensemble);  /* Print detailed component information */  void printComponent(DabComponent* component);  #endif + diff --git a/src/zmqinput-keygen.c b/src/zmqinput-keygen.c index 0169837..8bf61ac 100644 --- a/src/zmqinput-keygen.c +++ b/src/zmqinput-keygen.c @@ -58,6 +58,7 @@ int main(int argc, char** argv)      int rc = zmq_curve_keypair(public_key, secret_key);      if (rc != 0) {          fprintf(stderr, "key generation failed\n"); +        return 1;      }      int fdpub = creat(pubkeyfile, S_IRUSR | S_IWUSR); | 
