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! } +} + |