diff options
| author | Matthias P. Braendli <matthias.braendli@mpb.li> | 2015-03-06 19:05:01 +0100 | 
|---|---|---|
| committer | Matthias P. Braendli <matthias.braendli@mpb.li> | 2015-03-06 19:05:01 +0100 | 
| commit | 0414d5788090bb6df728d370079e44e95b4ffd20 (patch) | |
| tree | 4294702d0e38b87f9fa3396a2edd2e2a522c520f /src/ConfigParser.cpp | |
| parent | 43635f1d8a96c9711d7004d9bf6114eedb8e6ccd (diff) | |
| download | dabmux-0414d5788090bb6df728d370079e44e95b4ffd20.tar.gz dabmux-0414d5788090bb6df728d370079e44e95b4ffd20.tar.bz2 dabmux-0414d5788090bb6df728d370079e44e95b4ffd20.zip | |
Publish ptree on port 8001
Diffstat (limited to 'src/ConfigParser.cpp')
| -rw-r--r-- | src/ConfigParser.cpp | 964 | 
1 files changed, 964 insertions, 0 deletions
| diff --git a/src/ConfigParser.cpp b/src/ConfigParser.cpp new file mode 100644 index 0000000..2c4ab63 --- /dev/null +++ b/src/ConfigParser.cpp @@ -0,0 +1,964 @@ +/* +   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) 2014, 2015 +   Matthias P. Braendli, matthias.braendli@mpb.li + +    http://www.opendigitalradio.org + +    The Configuration parser sets up the ensemble according +     to the configuration given in a boost property tree, which +     is directly derived from a config file. + +     The format of the configuration is given in doc/example.mux +   */ +/* +   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 "ConfigParser.h" + +#ifdef HAVE_CONFIG_H +#   include "config.h" +#endif + +#include <boost/property_tree/ptree.hpp> +#include <exception> +#include <iostream> +#include <vector> +#include <stdint.h> +#include <string> +#include <map> +#include <cstring> +#include "dabOutput/dabOutput.h" +#include "dabInput.h" +#include "utils.h" +#include "dabInputFile.h" +#include "dabInputFifo.h" +#include "dabInputMpegFile.h" +#include "dabInputMpegFifo.h" +#include "dabInputDabplusFile.h" +#include "dabInputDabplusFifo.h" +#include "dabInputPacketFile.h" +#include "dabInputEnhancedPacketFile.h" +#include "dabInputEnhancedFifo.h" +#include "dabInputUdp.h" +#include "dabInputBridgeUdp.h" +#include "dabInputSlip.h" +#include "dabInputTest.h" +#include "dabInputPrbs.h" +#include "dabInputRawFile.h" +#include "dabInputRawFifo.h" +#include "dabInputDmbFile.h" +#include "dabInputDmbUdp.h" +#include "dabInputZmq.h" +#include "DabMux.h" +#include "StatsServer.h" + + +#ifdef _WIN32 +#   pragma warning ( disable : 4103 ) +#   include "Eti.h" +#   pragma warning ( default : 4103 ) +#else +#   include "Eti.h" +#endif + + +#ifdef _WIN32 +#   include <time.h> +#   include <process.h> +#   include <io.h> +#   include <conio.h> +#   include <winsock2.h> // For types... +typedef u_char uint8_t; +typedef WORD uint16_t; +typedef DWORD32 uint32_t; + +#   ifndef __MINGW32__ +#       include "xgetopt.h" +#   endif +#   define read _read +#   define snprintf _snprintf  +#   define sleep(a) Sleep((a) * 1000) +#else +#   include <unistd.h> +#   include <sys/time.h> +#   include <sys/wait.h> +#   include <sys/ioctl.h> +#   include <sys/times.h> +#endif + +using namespace std; + +/* a helper class to parse hexadecimal ids */ +int hexparse(std::string input) +{ +    int value; +    if (input.find("0x") == 0) { +        value = strtoll(input.c_str() + 2, NULL, 16); +    } +    else { +        value = strtoll(input.c_str(), NULL, 10); +    } + +    if (errno == ERANGE) { +        throw runtime_error("hex conversion: value out of range"); +    } + +    return value; +} + + +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* statsServerPort, +        edi_configuration_t* edi +        ) +{ +    using boost::property_tree::ptree; +    using boost::property_tree::ptree_error; +    /******************** READ GENERAL OPTIONS *****************/ +    ptree pt_general = pt.get_child("general"); + +    /* Dab mode logic */ +    ensemble->mode = pt_general.get("dabmode", 2); +    if ((ensemble->mode < 1) || (ensemble->mode > 4)) { +        throw runtime_error("Mode must be between 1-4"); +    } +    if (ensemble->mode == 4) { +        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); + +    *statsServerPort = 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"); + +    /* Ensemble ID */ +    ensemble->id = hexparse(pt_ensemble.get("id", "0")); + +    /* Extended Country Code */ +    ensemble->ecc = hexparse(pt_ensemble.get("ecc", "0")); + +    ensemble->international_table = pt_ensemble.get("international-table", 0); + +    string lto_auto = pt_ensemble.get("local-time-offset", ""); +    if (lto_auto == "auto") { +        ensemble->lto_auto = true; +        ensemble->lto = 0; +    } +    else { +        double lto_hours = pt_ensemble.get("local-time-offset", 0.0); +        if (round(lto_hours * 2) != lto_hours * 2) { +            etiLog.level(error) << "Ensemble local time offset " << +                lto_hours << "h cannot be expressed in half-hour blocks."; +            throw runtime_error("ensemble local-time-offset definition error"); +        } +        if (lto_hours > 12 || lto_hours < -12) { +            etiLog.level(error) << "Ensemble local time offset " << +                lto_hours << "h out of bounds [-12, +12]."; +            throw runtime_error("ensemble local-time-offset definition error"); +        } +        ensemble->lto_auto = false; +        ensemble->lto = abs(rint(lto_hours * 2)); +    } + +    int success = -5; +    string ensemble_label = pt_ensemble.get<string>("label"); +    string ensemble_short_label(ensemble_label); +    try { +        ensemble_short_label = pt_ensemble.get<string>("shortlabel"); +        success = ensemble->label.setLabel(ensemble_label, ensemble_short_label); +    } +    catch (ptree_error &e) { +        etiLog.level(warn) << "Ensemble short label undefined, " +            "truncating label " << ensemble_label; + +        success = ensemble->label.setLabel(ensemble_label); +    } +    switch (success) +    { +        case 0: +            break; +        case -1: +            etiLog.level(error) << "Ensemble short label " << +                ensemble_short_label << " is not subset of label '" << +                ensemble_label << "'"; +            throw runtime_error("ensemble label definition error"); +        case -2: +            etiLog.level(error) << "Ensemble short label " << +                ensemble_short_label << " is too long (max 8 characters)"; +            throw runtime_error("ensemble label definition error"); +        case -3: +            etiLog.level(error) << "Ensemble label " << +                ensemble_label << " is too long (max 16 characters)"; +            throw runtime_error("ensemble label definition error"); +        default: +            etiLog.level(error) << +                "Ensemble short label definition: program error !"; +            abort(); +    } + + +    /******************** READ SERVICES PARAMETERS *************/ + +    map<string, DabService*> allservices; + +    /* For each service, we keep a separate SCIdS counter */ +    map<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); + +        int success = -5; + +        string servicelabel = pt_service.get<string>("label"); +        string serviceshortlabel(servicelabel); +        try { +            serviceshortlabel = pt_service.get<string>("shortlabel"); +            success = service->label.setLabel(servicelabel, serviceshortlabel); +        } +        catch (ptree_error &e) { +            etiLog.level(warn) << "Service short label undefined, " +                "truncating label " << servicelabel; + +            success = service->label.setLabel(servicelabel); +        } +        switch (success) +        { +            case 0: +                break; +            case -1: +                etiLog.level(error) << "Service short label " << +                    serviceshortlabel << " is not subset of label '" << +                    servicelabel << "'"; +                throw runtime_error("service label definition error"); +            case -2: +                etiLog.level(error) << "Service short label " << +                    serviceshortlabel << " is too long (max 8 characters)"; +                throw runtime_error("service label definition error"); +            case -3: +                etiLog.level(error) << "Service label " << +                    servicelabel << " is too long (max 16 characters)"; +                throw runtime_error("service label definition error"); +            default: +                etiLog.level(error) << +                    "Service short label definition: program error !"; +                abort(); +        } + +        stringstream def_serviceid; +        def_serviceid << DEFAULT_SERVICE_ID + ensemble->services.size(); +        service->id = hexparse(pt_service.get("id", def_serviceid.str())); +        service->pty = hexparse(pt_service.get("pty", "0")); +        service->language = hexparse(pt_service.get("language", "0")); + +        // keep service in map, and check for uniqueness of the UID +        if (allservices.count(serviceuid) == 0) { +            allservices[serviceuid] = service; + +            // Set the service's SCIds to zero +            SCIdS_per_service[service] = 0; +        } +        else { +            stringstream ss; +            ss << "Service with uid " << serviceuid << " not unique!"; +            throw runtime_error(ss.str()); +        } +    } + + +    /******************** READ SUBCHAN PARAMETERS **************/ +    map<string, dabSubchannel*> allsubchans; + +    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(); + +        ensemble->subchannels.push_back(subchan); + +        try { +            setup_subchannel_from_ptree(subchan, it->second, ensemble, +                    subchanuid, *rc); +        } +        catch (runtime_error &e) { +            etiLog.log(error, +                    "%s\n", e.what()); +            throw e; +        } + + +        // keep subchannels in map, and check for uniqueness of the UID +        if (allsubchans.count(subchanuid) == 0) { +            allsubchans[subchanuid] = subchan; +        } +        else { +            stringstream ss; +            ss << "Subchannel with uid " << subchanuid << " not unique!"; +            throw runtime_error(ss.str()); +        } +    } + +    /******************** READ COMPONENT PARAMETERS ************/ +    map<string, DabComponent*> allcomponents; +    ptree pt_components = pt.get_child("components"); +    for (ptree::iterator it = pt_components.begin(); it != pt_components.end(); ++it) { +        string componentuid = it->first; +        ptree pt_comp = it->second; + +        DabService* service; +        try { +            // Those two uids serve as foreign keys to select the service+subchannel +            string service_uid = pt_comp.get<string>("service"); +            if (allservices.count(service_uid) != 1) { +                stringstream ss; +                ss << "Component with uid " << componentuid << " is refers to unknown service " +                    << service_uid << " !"; +                throw runtime_error(ss.str()); +            } +            service = allservices[service_uid]; +        } +        catch (ptree_error &e) { +            stringstream ss; +            ss << "Component with uid " << componentuid << " is missing service definition!"; +            throw runtime_error(ss.str()); +        } + +        dabSubchannel* subchannel; +        try { +            string subchan_uid = pt_comp.get<string>("subchannel"); +            if (allsubchans.count(subchan_uid) != 1) { +                stringstream ss; +                ss << "Component with uid " << componentuid << " is refers to unknown subchannel " +                    << subchan_uid << " !"; +                throw runtime_error(ss.str()); +            } +            subchannel = allsubchans[subchan_uid]; +        } +        catch (ptree_error &e) { +            stringstream ss; +            ss << "Component with uid " << componentuid << " is missing subchannel definition!"; +            throw runtime_error(ss.str()); +        } + +        int figType = hexparse(pt_comp.get("figtype", "-1")); +        int packet_address = hexparse(pt_comp.get("address", "-1")); +        int packet_datagroup = pt_comp.get("datagroup", false); +        uint8_t component_type = hexparse(pt_comp.get("type", "0")); + +        DabComponent* component = new DabComponent(componentuid); + +        component->enrol_at(**rc); + +        component->serviceId = service->id; +        component->subchId = subchannel->id; +        component->SCIdS = SCIdS_per_service[service]++; +        component->type = component_type; + +        int success = -5; +        string componentlabel = pt_comp.get<string>("label"); +        string componentshortlabel(componentlabel); +        try { +            componentshortlabel = pt_comp.get<string>("shortlabel"); +            success = component->label.setLabel(componentlabel, componentshortlabel); +        } +        catch (ptree_error &e) { +            etiLog.level(warn) << "Component short label undefined, " +                "truncating label " << componentlabel; + +            success = component->label.setLabel(componentlabel); +        } +        switch (success) +        { +            case 0: +                break; +            case -1: +                etiLog.level(error) << "Component short label " << +                    componentshortlabel << " is not subset of label '" << +                    componentlabel << "'"; +                throw runtime_error("component label definition error"); +            case -2: +                etiLog.level(error) << "Component short label " << +                    componentshortlabel << " is too long (max 8 characters)"; +                throw runtime_error("component label definition error"); +            case -3: +                etiLog.level(error) << "Component label " << +                    componentlabel << " is too long (max 16 characters)"; +                throw runtime_error("component label definition error"); +            default: +                etiLog.level(error) << +                    "Component short label definition: program error !"; +                abort(); +        } + +        if (figType != -1) { +            if (figType >= (1<<12)) { +                stringstream ss; +                ss << "Component with uid " << componentuid << +                    ": figtype '" << figType << "' is too large !"; +                throw runtime_error(ss.str()); +            } + +            if (component->isPacketComponent(ensemble->subchannels)) { +                component->packet.appType = figType; + +            } +            else { +                component->audio.uaType = figType; +            } + +            if (packet_address != -1) { +                if (! component->isPacketComponent(ensemble->subchannels)) { +                    stringstream ss; +                    ss << "Component with uid " << componentuid << +                        " is not packet, cannot have address defined !"; +                    throw runtime_error(ss.str()); +                } + +                component->packet.address = packet_address; +            } +            if (packet_datagroup) { +                if (! component->isPacketComponent(ensemble->subchannels)) { +                    stringstream ss; +                    ss << "Component with uid " << componentuid << +                        " is not packet, cannot have datagroup enabled !"; +                    throw runtime_error(ss.str()); +                } + +                component->packet.datagroup = packet_datagroup; +            } + +        } + +        ensemble->components.push_back(component); + +    } + +    /******************** 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, +        string subchanuid, +        BaseRemoteController* rc) +{ +    using boost::property_tree::ptree; +    using boost::property_tree::ptree_error; + +    string type; +    /* Read type first */ +    try { +        type = pt.get<string>("type"); +    } +    catch (ptree_error &e) { +        stringstream ss; +        ss << "Subchannel with uid " << subchanuid << " has no type defined!"; +        throw runtime_error(ss.str()); +    } + +    string inputUri = ""; +    // fail if no inputUri given unless type is test +    if (type != "test") { +        inputUri = pt.get<string>("inputuri", ""); + +        if (inputUri == "") { +            try { +                inputUri = pt.get<string>("inputfile"); +            } +            catch (ptree_error &e) { +                stringstream ss; +                ss << "Subchannel with uid " << subchanuid << " has no inputUri defined!"; +                throw runtime_error(ss.str()); +            } +        } +    } + +    string proto; +    size_t protopos = inputUri.find("://"); +    if (protopos == string::npos) { +        proto = "file"; +    } +    else { +        proto = inputUri.substr(0, protopos); +    } + +    subchan->inputUri = inputUri; + +    /* The input is of the old_style type, +     * with the struct of function pointers, +     * and needs to be a DabInputCompatible +     */ +    bool input_is_old_style = true; +    dabInputOperations operations; +    dabProtection* protection = &subchan->protection; + + +    if (0) { +#if defined(HAVE_FORMAT_MPEG) +    } else if (type == "audio") { +        subchan->type = Audio; +        subchan->bitrate = 0; + +        if (0) { +#if defined(HAVE_INPUT_FILE) +        } else if (proto == "file") { +            operations = dabInputMpegFileOperations; +#endif // defined(HAVE_INPUT_FILE) +#if defined(HAVE_INPUT_ZEROMQ) +        } +        else if (proto == "tcp"  || +                 proto == "epmg" || +                 proto == "ipc") { +            input_is_old_style = false; + +            dab_input_zmq_config_t zmqconfig; + +            try { +                zmqconfig.buffer_size = pt.get<int>("zmq-buffer"); +            } +            catch (ptree_error &e) { +                stringstream ss; +                ss << "ZMQ Subchannel with uid " << subchanuid << +                    " has no zmq-buffer defined!"; +                throw runtime_error(ss.str()); +            } +            try { +                zmqconfig.prebuffering = pt.get<int>("zmq-prebuffering"); +            } +            catch (ptree_error &e) { +                stringstream ss; +                ss << "ZMQ Subchannel with uid " << subchanuid << +                    " has no zmq-buffer defined!"; +                throw runtime_error(ss.str()); +            } +            zmqconfig.enable_encryption = false; + +            DabInputZmqMPEG* inzmq = +                new DabInputZmqMPEG(subchanuid, zmqconfig); +            inzmq->enrol_at(*rc); +            subchan->input     = inzmq; + +            if (proto == "epmg") { +                etiLog.level(warn) << "Using untested epmg:// zeromq input"; +            } +            else if (proto == "ipc") { +                etiLog.level(warn) << "Using untested ipc:// zeromq input"; +            } +#endif // defined(HAVE_INPUT_ZEROMQ) +        } else { +            stringstream ss; +            ss << "Subchannel with uid " << subchanuid << +                ": Invalid protocol for MPEG input (" << +                proto << ")" << endl; +            throw runtime_error(ss.str()); +        } +#endif // defined(HAVE_INPUT_FILE) && defined(HAVE_FORMAT_MPEG) +#if defined(HAVE_FORMAT_DABPLUS) +    } else if (type == "dabplus") { +        subchan->type = Audio; +        subchan->bitrate = 32; + +        if (0) { +#if defined(HAVE_INPUT_FILE) +        } else if (proto == "file") { +            operations = dabInputDabplusFileOperations; +#endif // defined(HAVE_INPUT_FILE) +#if defined(HAVE_INPUT_ZEROMQ) +        } +        else if (proto == "tcp"  || +                 proto == "epmg" || +                 proto == "ipc") { +            input_is_old_style = false; + +            dab_input_zmq_config_t zmqconfig; + +            try { +                zmqconfig.buffer_size = pt.get<int>("zmq-buffer"); +            } +            catch (ptree_error &e) { +                stringstream ss; +                ss << "ZMQ Subchannel with uid " << subchanuid << +                    " has no zmq-buffer defined!"; +                throw runtime_error(ss.str()); +            } + +            try { +                zmqconfig.prebuffering = pt.get<int>("zmq-prebuffering"); +            } +            catch (ptree_error &e) { +                stringstream ss; +                ss << "ZMQ Subchannel with uid " << subchanuid << +                    " has no zmq-buffer defined!"; +                throw runtime_error(ss.str()); +            } + +            zmqconfig.curve_encoder_keyfile = pt.get<string>("encoder-key",""); +            zmqconfig.curve_secret_keyfile = pt.get<string>("secret-key",""); +            zmqconfig.curve_public_keyfile = pt.get<string>("public-key",""); + +            zmqconfig.enable_encryption = pt.get<int>("encryption", 0); + +            DabInputZmqAAC* inzmq = +                new DabInputZmqAAC(subchanuid, zmqconfig); + +            inzmq->enrol_at(*rc); +            subchan->input     = inzmq; + +            if (proto == "epmg") { +                etiLog.level(warn) << "Using untested epmg:// zeromq input"; +            } +            else if (proto == "ipc") { +                etiLog.level(warn) << "Using untested ipc:// zeromq input"; +            } +#endif // defined(HAVE_INPUT_ZEROMQ) +        } else { +            stringstream ss; +            ss << "Subchannel with uid " << subchanuid << +                ": Invalid protocol for DAB+ input (" << +                proto << ")" << endl; +            throw runtime_error(ss.str()); +        } +#endif // defined(HAVE_FORMAT_DABPLUS) +    } else if (type == "bridge") { +        // TODO default proto should be udp:// +        if (0) { +#if defined(HAVE_FORMAT_BRIDGE) +#if defined(HAVE_INPUT_UDP) +        } else if (proto == "udp") { +            operations = dabInputBridgeUdpOperations; +#endif // defined(HAVE_INPUT_UDP) +#if defined(HAVE_INPUT_SLIP) +        } else if (proto == "slip") { +            operations = dabInputSlipOperations; +#endif // defined(HAVE_INPUT_SLIP) +#endif // defined(HAVE_FORMAT_BRIDGE) +        } +    } else if (type == "data") { +        // TODO default proto should be udp:// +        if (0) { +#if defined(HAVE_INPUT_UDP) +        } else if (proto == "udp") { +            operations = dabInputUdpOperations; +#endif +#if defined(HAVE_INPUT_PRBS) && defined(HAVE_FORMAT_RAW) +        } else if (proto == "prbs") { +            operations = dabInputPrbsOperations; +#endif +#if defined(HAVE_INPUT_FILE) && defined(HAVE_FORMAT_RAW) +        } else if (proto == "file") { +            operations = dabInputRawFileOperations; +#endif +        } else if (proto == "fifo") { +            operations = dabInputRawFifoOperations; +        } else { +            stringstream ss; +            ss << "Subchannel with uid " << subchanuid <<  +                ": Invalid protocol for data input (" << +                proto << ")" << endl; +            throw runtime_error(ss.str()); +        } + +        subchan->type = DataDmb; +        subchan->bitrate = DEFAULT_DATA_BITRATE; +#if defined(HAVE_INPUT_TEST) && defined(HAVE_FORMAT_RAW) +    } else if (type == "test") { +        subchan->type = DataDmb; +        subchan->bitrate = DEFAULT_DATA_BITRATE; +        operations = dabInputTestOperations; +#endif // defined(HAVE_INPUT_TEST)) && defined(HAVE_FORMAT_RAW) +#ifdef HAVE_FORMAT_PACKET +    } else if (type == "packet") { +        subchan->type = Packet; +        subchan->bitrate = DEFAULT_PACKET_BITRATE; +#ifdef HAVE_INPUT_FILE +        operations = dabInputPacketFileOperations; +#elif defined(HAVE_INPUT_FIFO) +        operations = dabInputFifoOperations; +#else +#   pragma error("Must define at least one packet input") +#endif // defined(HAVE_INPUT_FILE) +#ifdef HAVE_FORMAT_EPM +    } else if (type == "enhancedpacked") { +        subchan->type = Packet; +        subchan->bitrate = DEFAULT_PACKET_BITRATE; +        operations = dabInputEnhancedPacketFileOperations; +#endif // defined(HAVE_FORMAT_EPM) +#endif // defined(HAVE_FORMAT_PACKET) +#ifdef HAVE_FORMAT_DMB +    } else if (type == "dmb") { +        // TODO default proto should be UDP +        if (0) { +#if defined(HAVE_INPUT_UDP) +        } else if (proto == "udp") { +            operations = dabInputDmbUdpOperations; +#endif +        } else if (proto == "file") { +            operations = dabInputDmbFileOperations; +        } else { +            stringstream ss; +            ss << "Subchannel with uid " << subchanuid <<  +                ": Invalid protocol for DMB input (" << +                proto << ")" << endl; +            throw runtime_error(ss.str()); +        } + +        subchan->type = DataDmb; +        subchan->bitrate = DEFAULT_DATA_BITRATE; +#endif +    } else { +        stringstream ss; +        ss << "Subchannel with uid " << subchanuid << " has unknown type!"; +        throw runtime_error(ss.str()); +    } +    subchan->startAddress = 0; + +    if (type == "audio") { +        protection->form = UEP; +        protection->level = 2; +        protection->uep.tableIndex = 0; +    } else { +        protection->level = 2; +        protection->form = EEP; +        protection->eep.profile = EEP_A; +    } + +    /* Get bitrate */ +    try { +        subchan->bitrate = pt.get<int>("bitrate"); +        if ((subchan->bitrate & 0x7) != 0) { +            stringstream ss; +            ss << "Subchannel with uid " << subchanuid << +                ": Bitrate (" << subchan->bitrate << " not a multiple of 8!"; +            throw runtime_error(ss.str()); +        } +    } +    catch (ptree_error &e) { +        stringstream ss; +        ss << "Error, no bitrate defined for subchannel " << subchanuid; +        throw runtime_error(ss.str()); +    } + +#if defined(HAVE_INPUT_FIFO) && defined(HAVE_INPUT_FILE) +    /* Get nonblock */ +    bool nonblock = pt.get("nonblock", false); +    if (nonblock) { +        switch (subchan->type) { +#ifdef HAVE_FORMAT_PACKET +            case 3: +                if (operations == dabInputPacketFileOperations) { +                    operations = dabInputFifoOperations; +#ifdef HAVE_FORMAT_EPM +                } else if (operations == dabInputEnhancedPacketFileOperations) { +                    operations = dabInputEnhancedFifoOperations; +#endif // defined(HAVE_FORMAT_EPM) +                } else { +                    stringstream ss; +                    ss << "Error, wrong packet operations for subchannel " << +                        subchanuid; +                    throw runtime_error(ss.str()); +                } +                break; +#endif // defined(HAVE_FORMAT_PACKET) +#ifdef HAVE_FORMAT_MPEG +            case 0: +                if (operations == dabInputMpegFileOperations) { +                    operations = dabInputMpegFifoOperations; +                } else if (operations == dabInputDabplusFileOperations) { +                    operations = dabInputDabplusFifoOperations; +                } else { +                    stringstream ss; +                    ss << "Error, wrong audio operations for subchannel " << +                        subchanuid; +                    throw runtime_error(ss.str()); +                } +                break; +#endif // defined(HAVE_FORMAT_MPEG) +            default: +                stringstream ss; +                ss << "Subchannel with uid " << subchanuid << +                    " non-blocking I/O only available for audio or packet services!"; +                throw runtime_error(ss.str()); +        } +#endif // defined(HAVE_INPUT_FIFO) && defined(HAVE_INPUT_FILE) +    } + + +    /* Get id */ + +    try { +        subchan->id = hexparse(pt.get<std::string>("subchid")); +    } +    catch (ptree_error &e) { +        for (int i = 0; i < 64; ++i) { // Find first free subchannel +            vector<dabSubchannel*>::iterator subchannel = getSubchannel(ensemble->subchannels, i); +            if (subchannel == ensemble->subchannels.end()) { +                subchannel = ensemble->subchannels.end() - 1; +                subchan->id = i; +                break; +            } +        } +    } + +    /* Get optional protection profile */ +    string profile = pt.get("protection-profile", ""); + +    if (profile == "EEP_A") { +        protection->form = EEP; +        protection->eep.profile = EEP_A; +    } +    else if (profile == "EEP_B") { +        protection->form = EEP; +        protection->eep.profile = EEP_B; +    } +    else if (profile == "UEP") { +        protection->form = UEP; +    } + +    /* Get protection level */ +    try { +        int level = pt.get<int>("protection"); + +        if (protection->form == UEP) { +            if ((level < 1) || (level > 5)) { +                stringstream ss; +                ss << "Subchannel with uid " << subchanuid << +                    ": protection level must be between " +                    "1 to 5 inclusively (current = " << level << " )"; +                throw runtime_error(ss.str()); +            } +        } +        else if (protection->form == EEP) { +            if ((level < 1) || (level > 4)) { +                stringstream ss; +                ss << "Subchannel with uid " << subchanuid << +                    ": protection level must be between " +                    "1 to 4 inclusively (current = " << level << " )"; +                throw runtime_error(ss.str()); +            } +        } +        protection->level = level - 1; +    } +    catch (ptree_error &e) { +        stringstream ss; +        ss << "Subchannel with uid " << subchanuid << +            ": protection level undefined!"; +        throw runtime_error(ss.str()); +    } + +    /* Create object */ +    if (input_is_old_style) { +        subchan->input = new DabInputCompatible(operations); +    } +    // else { it's already been created! } +} + | 
