From 0414d5788090bb6df728d370079e44e95b4ffd20 Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Fri, 6 Mar 2015 19:05:01 +0100 Subject: Publish ptree on port 8001 --- src/ConfigParser.cpp | 964 ++++++++++++++++++++++++++++++++++++++++++++++ src/ConfigParser.h | 58 +++ src/ConfigServer.cpp | 177 +++++++++ src/ConfigServer.h | 120 ++++++ src/DabMux.cpp | 32 +- src/Makefile.am | 3 +- src/ParserConfigfile.cpp | 965 ----------------------------------------------- src/ParserConfigfile.h | 55 --- 8 files changed, 1348 insertions(+), 1026 deletions(-) create mode 100644 src/ConfigParser.cpp create mode 100644 src/ConfigParser.h create mode 100644 src/ConfigServer.cpp create mode 100644 src/ConfigServer.h delete mode 100644 src/ParserConfigfile.cpp delete mode 100644 src/ParserConfigfile.h (limited to 'src') 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 . +*/ +#include "ConfigParser.h" + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#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 +# include +# include +# include +# include // 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 +# include +# include +# include +# include +#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 &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("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("statsserverport", 0); + + /************** READ REMOTE CONTROL PARAMETERS *************/ + ptree pt_rc = pt.get_child("remotecontrol"); + int telnetport = pt_rc.get("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("label"); + string ensemble_short_label(ensemble_label); + try { + ensemble_short_label = pt_ensemble.get("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 allservices; + + /* For each service, we keep a separate SCIdS counter */ + map 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("label"); + string serviceshortlabel(servicelabel); + try { + serviceshortlabel = pt_service.get("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 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 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("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("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("label"); + string componentshortlabel(componentlabel); + try { + componentshortlabel = pt_comp.get("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 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("destination"); + edi->dest_port = pt_edi.get("port"); + edi->source_port = pt_edi.get("sourceport"); + + edi->dump = pt_edi.get("dump"); + edi->enable_pft = pt_edi.get("enable_pft"); + edi->verbose = pt_edi.get("verbose"); + } + else { + string uri = pt_outputs.get(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("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("inputuri", ""); + + if (inputUri == "") { + try { + inputUri = pt.get("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("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("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("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("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("encoder-key",""); + zmqconfig.curve_secret_keyfile = pt.get("secret-key",""); + zmqconfig.curve_public_keyfile = pt.get("public-key",""); + + zmqconfig.enable_encryption = pt.get("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("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("subchid")); + } + catch (ptree_error &e) { + for (int i = 0; i < 64; ++i) { // Find first free subchannel + vector::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("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! } +} + diff --git a/src/ConfigParser.h b/src/ConfigParser.h new file mode 100644 index 0000000..16d2146 --- /dev/null +++ b/src/ConfigParser.h @@ -0,0 +1,58 @@ +/* + 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 + Matthias P. Braendli, matthias.braendli@mpb.li + + 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 . +*/ +#ifndef __CONFIG_PARSER_H_ +#define __CONFIG_PARSER_H_ + +#include +#include +#include "MuxElements.h" +#include "DabMux.h" +#include + +void parse_ptree(boost::property_tree::ptree& pt, + std::vector &outputs, + dabEnsemble* ensemble, + bool* enableTist, + unsigned* FICL, + bool* factumAnalyzer, + unsigned long* limit, + BaseRemoteController** rc, + int* statsServerPort, + edi_configuration_t* edi); + +void setup_subchannel_from_ptree(dabSubchannel* subchan, + boost::property_tree::ptree &pt, + dabEnsemble* ensemble, + std::string subchanuid, + BaseRemoteController* rc); + +#endif + diff --git a/src/ConfigServer.cpp b/src/ConfigServer.cpp new file mode 100644 index 0000000..e2eefd4 --- /dev/null +++ b/src/ConfigServer.cpp @@ -0,0 +1,177 @@ +/* + 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 + 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 . +*/ + +#include "ConfigServer.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include "Log.h" + +void ConfigServer::serverThread() +{ + 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, + "%s %s Config Server\n", + PACKAGE_NAME, +#if defined(GITVERSION) + GITVERSION +#else + PACKAGE_VERSION +#endif + ); + + + m_sock = socket(AF_INET, SOCK_STREAM, 0); + if (m_sock < 0) { + etiLog.level(error) << "Error opening Config Server socket: " << + strerror(errno); + m_fault = true; + return; + } + + 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 Config Server socket: " << + strerror(errno); + goto end_serverthread; + } + + if (listen(m_sock, 5) < 0) { + etiLog.level(error) << "Error listening on Config Server socket: " << + strerror(errno); + goto end_serverthread; + } + + m_running = true; + + while (m_running) { + socklen_t cli_addr_len = sizeof(cli_addr); + + /* Accept actual connection from the client */ + accepted_sock = accept(m_sock, + (struct sockaddr *)&cli_addr, + &cli_addr_len); + + if (accepted_sock < 0) { + etiLog.level(warn) << "Config 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 Config Server socket " << + strerror(errno); + close(accepted_sock); + continue; + } + + /* receive command */ + memset(buffer, 0, 256); + int n = read(accepted_sock, buffer, 255); + if (n < 0) { + etiLog.level(warn) << "Error reading from Config Server socket " << + strerror(errno); + close(accepted_sock); + continue; + } + + if (strcmp(buffer, "getconfig\n") == 0) { + boost::unique_lock lock(m_mutex); + m_pending_request = "getconfig"; + m_pending = true; + + while (m_pending) { + m_condition.wait(lock); + } + std::stringstream ss; + boost::property_tree::info_parser::write_info(ss, m_pt); + + std::string response = ss.str(); + + 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 (n < 0) { + etiLog.level(warn) << "Error writing to Config Server socket " << + strerror(errno); + } + close(accepted_sock); + } + +end_serverthread: + m_fault = true; + close(m_sock); + + } + catch (std::exception& e) { + etiLog.level(error) << "Config server caught exception: " << e.what(); + m_fault = true; + } +} + +void ConfigServer::restart() +{ + m_restarter_thread = boost::thread(&ConfigServer::restart_thread, + this, 0); +} + +// This runs in a separate thread, because +// it would take too long to be done in the main loop +// thread. +void ConfigServer::restart_thread(long) +{ + m_running = false; + + if (m_listenport) { + m_thread.interrupt(); + m_thread.join(); + } + + m_thread = boost::thread(&ConfigServer::serverThread, this); +} + diff --git a/src/ConfigServer.h b/src/ConfigServer.h new file mode 100644 index 0000000..b51ef32 --- /dev/null +++ b/src/ConfigServer.h @@ -0,0 +1,120 @@ +/* + 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 + 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 . +*/ + +#ifndef __CONFIG_SERVER_H_ +#define __CONFIG_SERVER_H_ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class ConfigServer +{ + public: + ConfigServer() : + m_listenport(0), + m_running(false), + m_fault(false) + { } + + ConfigServer(int listen_port) : + m_listenport(listen_port), + m_running(false), + m_fault(false), + m_thread(&ConfigServer::serverThread, this) + { + m_sock = 0; + } + + ~ConfigServer() + { + m_running = false; + m_fault = false; + if (m_sock) { + close(m_sock); + } + m_thread.join(); + } + + bool request_pending() { + return m_pending; + } + + void update_ptree(const boost::property_tree::ptree& pt) { + boost::unique_lock lock(m_mutex); + m_pt = pt; + m_pending = false; + + m_condition.notify_one(); + } + + bool fault_detected() { return m_fault; } + void restart(void); + + private: + void restart_thread(long); + + /******* TCP Socket Server ******/ + // no copying (because of the thread) + ConfigServer(const ConfigServer& other); + + void serverThread(void); + + int m_listenport; + + // serverThread runs in a separate thread + bool m_running; + bool m_fault; + boost::thread m_thread; + boost::thread m_restarter_thread; + + int m_sock; + + bool m_pending; + std::string m_pending_request; + boost::condition_variable m_condition; + boost::mutex m_mutex; + + boost::property_tree::ptree m_pt; +}; + + +#endif + diff --git a/src/DabMux.cpp b/src/DabMux.cpp index d0ec502..950eefe 100644 --- a/src/DabMux.cpp +++ b/src/DabMux.cpp @@ -27,6 +27,8 @@ # include "config.h" #endif +#include +#include #include #include #include @@ -124,16 +126,22 @@ typedef DWORD32 uint32_t; #include "MuxElements.h" #include "utils.h" #include "ParserCmdline.h" -#include "ParserConfigfile.h" +#include "ConfigParser.h" #include "StatsServer.h" +#include "ConfigServer.h" #include "Log.h" #include "RemoteControl.h" using namespace std; +using boost::property_tree::ptree; +using boost::property_tree::ptree_error; + /* Global stats server */ StatsServer* global_stats; +ConfigServer config_server(8001); + class MuxInitException : public exception { public: @@ -337,6 +345,8 @@ int main(int argc, char *argv[]) edi_conf.dump = false; edi_conf.enable_pft = false; + ptree pt; + struct timeval mnsc_time; @@ -360,7 +370,9 @@ int main(int argc, char *argv[]) } try { - parse_configfile(conf_file, outputs, ensemble, &enableTist, &FICL, + read_info(conf_file, pt); + + parse_ptree(pt, outputs, ensemble, &enableTist, &FICL, &factumAnalyzer, &limit, &rc, &statsserverport, &edi_conf); } catch (runtime_error &e) { @@ -525,11 +537,10 @@ int main(int argc, char *argv[]) for (output = outputs.begin(); output != outputs.end() ; ++output) { + if (0) { #if defined(HAVE_OUTPUT_FILE) - if ((*output)->outputProto == "file") { + } else if ((*output)->outputProto == "file") { (*output)->output = new DabOutputFile(); -#else // !defined(HAVE_OUTPUT_FILE) - if (0) { #endif // defined(HAVE_OUTPUT_FILE) #if defined(HAVE_OUTPUT_FIFO) } else if ((*output)->outputProto == "fifo") { @@ -2168,6 +2179,17 @@ int main(int argc, char *argv[]) "Detected Statistics Server fault, restarting it"; global_stats->restart(); } + + if (fc->FCT % 10 == 0) { + if (config_server.fault_detected()) { + config_server.restart(); + } + + if (config_server.request_pending()) { + config_server.update_ptree(pt); + } + + } } } diff --git a/src/Makefile.am b/src/Makefile.am index b65b9d2..aa1f86a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -79,7 +79,7 @@ odr_dabmux_SOURCES =DabMux.cpp DabMux.h \ MuxElements.cpp MuxElements.h \ RemoteControl.cpp RemoteControl.h \ ParserCmdline.cpp ParserCmdline.h \ - ParserConfigfile.cpp ParserConfigfile.h \ + ParserConfig.cpp ParserConfig.h \ Eti.h Eti.cpp \ Log.h Log.cpp \ UdpSocket.h UdpSocket.cpp \ @@ -93,6 +93,7 @@ odr_dabmux_SOURCES =DabMux.cpp DabMux.h \ ReedSolomon.h ReedSolomon.cpp \ mpeg.h mpeg.c \ StatsServer.h StatsServer.cpp \ + ConfigServer.h ConfigServer.cpp \ TcpServer.h TcpServer.cpp \ TcpSocket.h TcpSocket.cpp \ zmq.hpp diff --git a/src/ParserConfigfile.cpp b/src/ParserConfigfile.cpp deleted file mode 100644 index 976bbf1..0000000 --- a/src/ParserConfigfile.cpp +++ /dev/null @@ -1,965 +0,0 @@ -/* - 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 command-line parser reads settings from a configuration file - whose definition is given in doc/example.config - */ -/* - 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 . -*/ -#include "ParserConfigfile.h" - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#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 -# include -# include -# include -# include // 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 -# include -# include -# include -# include -#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_configfile(string configuration_file, - vector &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; - ptree pt; - - read_info(configuration_file, pt); - /******************** 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("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("statsserverport", 0); - - /************** READ REMOTE CONTROL PARAMETERS *************/ - ptree pt_rc = pt.get_child("remotecontrol"); - int telnetport = pt_rc.get("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("label"); - string ensemble_short_label(ensemble_label); - try { - ensemble_short_label = pt_ensemble.get("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 allservices; - - /* For each service, we keep a separate SCIdS counter */ - map 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("label"); - string serviceshortlabel(servicelabel); - try { - serviceshortlabel = pt_service.get("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 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 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("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("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("label"); - string componentshortlabel(componentlabel); - try { - componentshortlabel = pt_comp.get("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 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("destination"); - edi->dest_port = pt_edi.get("port"); - edi->source_port = pt_edi.get("sourceport"); - - edi->dump = pt_edi.get("dump"); - edi->enable_pft = pt_edi.get("enable_pft"); - edi->verbose = pt_edi.get("verbose"); - } - else { - string uri = pt_outputs.get(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("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("inputuri", ""); - - if (inputUri == "") { - try { - inputUri = pt.get("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("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("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("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("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("encoder-key",""); - zmqconfig.curve_secret_keyfile = pt.get("secret-key",""); - zmqconfig.curve_public_keyfile = pt.get("public-key",""); - - zmqconfig.enable_encryption = pt.get("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("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("subchid")); - } - catch (ptree_error &e) { - for (int i = 0; i < 64; ++i) { // Find first free subchannel - vector::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("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! } -} - diff --git a/src/ParserConfigfile.h b/src/ParserConfigfile.h deleted file mode 100644 index 95951d1..0000000 --- a/src/ParserConfigfile.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - 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 - Matthias P. Braendli, matthias.braendli@mpb.li - - The command-line parser reads settings from a configuration file - whose definition is given in doc/example.config - */ -/* - 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 . -*/ -#ifndef _PARSER_CONFIGFILE -#define _PARSER_CONFIGFILE - -#include -#include -#include "MuxElements.h" -#include "DabMux.h" -#include - -void parse_configfile(std::string configuration_file, - std::vector &outputs, - dabEnsemble* ensemble, - bool* enableTist, - unsigned* FICL, - bool* factumAnalyzer, - unsigned long* limit, - BaseRemoteController** rc, - int* statsServerPort, - edi_configuration_t* edi); - -void setup_subchannel_from_ptree(dabSubchannel* subchan, - boost::property_tree::ptree &pt, - dabEnsemble* ensemble, - std::string subchanuid, - BaseRemoteController* rc); - -#endif - -- cgit v1.2.3