summaryrefslogtreecommitdiffstats
path: root/src/DabMux.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/DabMux.cpp')
-rw-r--r--src/DabMux.cpp2027
1 files changed, 144 insertions, 1883 deletions
diff --git a/src/DabMux.cpp b/src/DabMux.cpp
index 8ef30ba..20dc31d 100644
--- a/src/DabMux.cpp
+++ b/src/DabMux.cpp
@@ -29,6 +29,7 @@
# include "config.h"
#endif
+#include <boost/shared_ptr.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/info_parser.hpp>
#include <cstdio>
@@ -138,51 +139,6 @@ using boost::property_tree::ptree;
using boost::property_tree::ptree_error;
-/* Global stats and config server */
-ManagementServer* mgmt_server;
-
-class MuxInitException : public exception
-{
- public:
- MuxInitException(const string m = "DABMUX initialisation error") throw()
- : msg(m) {}
- ~MuxInitException(void) throw() {}
- const char* what() const throw() { return msg.c_str(); }
- private:
- string msg;
-};
-
-static unsigned char Padding_FIB[] = {
- 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
-};
-
-
-// Protection levels and bitrates for UEP.
-const unsigned char ProtectionLevelTable[64] = {
- 4, 3, 2, 1, 0, 4, 3, 2,
- 1, 0, 4, 3, 2, 1, 4, 3,
- 2, 1, 0, 4, 3, 2, 1, 0,
- 4, 3, 2, 1, 0, 4, 3, 2,
- 1, 4, 3, 2, 1, 0, 4, 3,
- 2, 1, 0, 4, 3, 2, 1, 0,
- 4, 3, 2, 1, 0, 4, 3, 2,
- 1, 0, 4, 3, 1, 4, 2, 0
-};
-
-const unsigned short BitRateTable[64] = {
- 32, 32, 32, 32, 32, 48, 48, 48,
- 48, 48, 56, 56, 56, 56, 64, 64,
- 64, 64, 64, 80, 80, 80, 80, 80,
- 96, 96, 96, 96, 96, 112, 112, 112,
- 112, 128, 128, 128, 128, 128, 160, 160,
- 160, 160, 160, 192, 192, 192, 192, 192,
- 224, 224, 224, 224, 224, 256, 256, 256,
- 256, 256, 320, 320, 320, 384, 384, 384
-};
-
volatile sig_atomic_t running = 1;
@@ -247,117 +203,23 @@ int main(int argc, char *argv[])
strerror(errno));
}
#else
- if (setpriority(PRIO_PROCESS, 0, -20) == -1) {
- etiLog.log(warn, "Can't increase priority: %s\n",
- strerror(errno));
+ // Use the lowest real-time priority for this thread, and switch to real-time scheduling
+ const int policy = SCHED_RR;
+ sched_param sp;
+ sp.sched_priority = sched_get_priority_min(policy);
+ int thread_prio_ret = pthread_setschedparam(pthread_self(), policy, &sp);
+ if (thread_prio_ret != 0) {
+ etiLog.level(error) << "Could not set real-time priority for thread:" << thread_prio_ret;
}
#endif
- /*sched_param scheduler;
- scheduler.sched_priority = 99; // sched_get_priority_max(SCHED_RR)
- if (sched_setscheduler(0, SCHED_RR, &scheduler) == -1) {
- etiLog.log(warn, "Can't increased priority: %s\n",
- strerror(errno));
- }*/
-
- uint8_t watermarkData[128];
- size_t watermarkSize = 0;
- size_t watermarkPos = 0;
- {
- uint8_t buffer[sizeof(watermarkData) / 2];
- snprintf((char*)buffer, sizeof(buffer),
- "%s %s, compiled at %s, %s",
- PACKAGE_NAME, PACKAGE_VERSION, __DATE__, __TIME__);
- memset(watermarkData, 0, sizeof(watermarkData));
- watermarkData[0] = 0x55; // Sync
- watermarkData[1] = 0x55;
- watermarkSize = 16;
- for (unsigned i = 0; i < strlen((char*)buffer); ++i) {
- for (int j = 0; j < 8; ++j) {
- uint8_t bit = (buffer[watermarkPos >> 3] >> (7 - (watermarkPos & 0x07))) & 1;
- watermarkData[watermarkSize >> 3] |= bit << (7 - (watermarkSize & 0x07));
- ++watermarkSize;
- bit = 1;
- watermarkData[watermarkSize >> 3] |= bit << (7 - (watermarkSize & 0x07));
- ++watermarkSize;
- ++watermarkPos;
- }
- }
- }
- watermarkPos = 0;
-
-
- dabEnsemble* ensemble = new dabEnsemble;
- ensemble->label.setLabel(DEFAULT_ENSEMBLE_LABEL, DEFAULT_ENSEMBLE_SHORT_LABEL);
- ensemble->mode = DEFAULT_DAB_MODE;
- ensemble->id = DEFAULT_ENSEMBLE_ID;
- ensemble->ecc = DEFAULT_ENSEMBLE_ECC;
-
- vector<dabOutput*> outputs;
- vector<DabService*>::iterator service = ensemble->services.end();
- vector<DabService*>::iterator serviceProgFIG0_2;
- vector<DabService*>::iterator serviceDataFIG0_2;
- vector<DabService*>::iterator serviceFIG0_17;
- vector<DabComponent*>::iterator component = ensemble->components.end();
- vector<DabComponent*>::iterator componentProgFIG0_8;
- vector<DabComponent*>::iterator componentFIG0_13;
- bool transmitFIG0_13programme = false; // Alternate between programme and data
- vector<DabComponent*>::iterator componentDataFIG0_8;
- vector<dabSubchannel*>::iterator subchannel = ensemble->subchannels.end();
- vector<dabSubchannel*>::iterator subchannelFIG0_1;
- vector<dabOutput*>::iterator output;
- dabProtection* protection = NULL;
-
- BaseRemoteController* rc = NULL;
-
- unsigned long currentFrame;
- int returnCode = 0;
- int cur;
- unsigned char etiFrame[6144];
- unsigned short index = 0;
- // FIC Length, DAB Mode I, II, IV -> FICL = 24, DAB Mode III -> FICL = 32
- unsigned FICL = (ensemble->mode == 3 ? 32 : 24);
-
- uint32_t sync = 0x49C5F8;
- unsigned short FLtmp = 0;
- unsigned short nbBytesCRC = 0;
- unsigned short CRCtmp = 0xFFFF;
- unsigned short MSTsize = 0;
-
- unsigned int insertFIG = 0;
- unsigned int rotateFIB = 0;
-
- bool factumAnalyzer = false;
- unsigned long limit = 0;
- time_t date;
- bool enableTist = false;
- unsigned timestamp = 0;
-
- int mgmtserverport = 0;
-
- edi_configuration_t edi_conf;
-
- // Defaults for edi
- edi_conf.enabled = false;
- edi_conf.dest_addr = "";
- edi_conf.dest_port = 0;
- edi_conf.source_port = 0;
- edi_conf.dump = false;
- edi_conf.enable_pft = false;
- ptree pt;
- struct timeval mnsc_time;
- /* TODO:
- * In a SFN, when reconfiguring the ensemble, the multiplexer
- * has to be restarted (odr-dabmux doesn't support reconfiguration).
- * Ideally, we must be able to restart transmission s.t. the receiver
- * synchronisation is preserved.
- */
- gettimeofday(&mnsc_time, NULL);
+ int returnCode = 0;
- bool MNSC_increment_time = false;
+ ptree pt;
+ std::vector<boost::shared_ptr<DabOutput> > outputs;
try {
if (argc == 2) { // Assume the only argument is a config file
@@ -365,19 +227,15 @@ int main(int argc, char *argv[])
if (conf_file == "-h") {
printUsage(argv[0], stdout);
- throw MuxInitException();
+ throw MuxInitException("Nothing to do");
}
try {
read_info(conf_file, pt);
- parse_ptree(pt, outputs, ensemble, &enableTist, &FICL,
- &factumAnalyzer, &limit, &rc, &mgmtserverport, &edi_conf);
}
catch (runtime_error &e) {
- etiLog.log(error, "Configuration file parsing error: %s\n",
- e.what());
- throw MuxInitException();
+ throw MuxInitException(e.what());
}
}
else if (argc > 1 && strncmp(argv[1], "-e", 2) == 0) { // use external config file
@@ -391,14 +249,9 @@ int main(int argc, char *argv[])
string conf_file = argv[2];
read_info(conf_file, pt);
-
- parse_ptree(pt, outputs, ensemble, &enableTist, &FICL,
- &factumAnalyzer, &limit, &rc, &mgmtserverport, &edi_conf);
}
catch (runtime_error &e) {
- etiLog.log(error, "Configuration file parsing error: %s\n",
- e.what());
- throw MuxInitException();
+ throw MuxInitException(e.what());
}
}
#if ENABLE_CMDLINE_OPTIONS
@@ -410,22 +263,31 @@ int main(int argc, char *argv[])
}
#else
else {
- etiLog.level(error) << "You must specify the configuration file";
- throw MuxInitException();
+ throw MuxInitException("No configuration file specified");
}
#endif
- if (mgmtserverport != 0) {
- mgmt_server = new ManagementServer(mgmtserverport);
+ int mgmtserverport = pt.get<int>("general.managementport",
+ pt.get<int>("general.statsserverport", 0) );
+
+ /* Management: stats and config server */
+ get_mgmt_server().open(mgmtserverport);
+
+ /************** READ REMOTE CONTROL PARAMETERS *************/
+ int telnetport = pt.get<int>("remotecontrol.telnetport", 0);
+
+ boost::shared_ptr<BaseRemoteController> rc;
+
+ if (telnetport != 0) {
+ rc = boost::shared_ptr<RemoteControllerTelnet>(
+ new RemoteControllerTelnet(telnetport));
}
else {
- mgmt_server = new ManagementServer();
- }
-
- if (rc) {
- ensemble->enrol_at(*rc);
+ rc = boost::shared_ptr<RemoteControllerDummy>(
+ new RemoteControllerDummy());
}
+ DabMultiplexer mux(rc, pt);
etiLog.level(info) <<
PACKAGE_NAME << " " <<
@@ -436,1759 +298,181 @@ int main(int argc, char *argv[])
#endif
" starting up";
- if (outputs.size() == 0) {
- etiLog.log(emerg, "no output defined");
- throw MuxInitException();
- }
- // Check and adjust subchannels
- {
- set<unsigned char> ids;
- for (subchannel = ensemble->subchannels.begin();
- subchannel != ensemble->subchannels.end();
- ++subchannel) {
- if (ids.find((*subchannel)->id) != ids.end()) {
- etiLog.log(error,
- "Subchannel %u is set more than once!\n",
- (*subchannel)->id);
- returnCode = -1;
- throw MuxInitException();
- }
+ edi_configuration_t edi_conf;
+
+ /******************** READ OUTPUT PARAMETERS ***************/
+ set<string> all_output_names;
+ ptree pt_outputs = pt.get_child("outputs");
+ for (auto ptree_pair : pt_outputs) {
+ string outputuid = ptree_pair.first;
+
+ // check for uniqueness of the uid
+ if (all_output_names.count(outputuid) == 0) {
+ all_output_names.insert(outputuid);
+ }
+ else {
+ stringstream ss;
+ ss << "output with uid " << outputuid << " not unique!";
+ throw runtime_error(ss.str());
}
- }
- // Check and adjust services and components
- {
- set<uint32_t> ids;
- for (service = ensemble->services.begin();
- service != ensemble->services.end();
- ++service) {
- if (ids.find((*service)->id) != ids.end()) {
- etiLog.log(error,
- "Service id 0x%x (%u) is set more than once!\n",
- (*service)->id, (*service)->id);
- returnCode = -1;
- throw MuxInitException();
- }
+ if (outputuid == "edi") {
+#if HAVE_OUTPUT_EDI
+ ptree pt_edi = pt_outputs.get_child("edi");
- // Get first component of this service
- component = getComponent(ensemble->components, (*service)->id);
- if (component == ensemble->components.end()) {
- etiLog.log(error,
- "Service id 0x%x (%u) includes no component!\n",
- (*service)->id, (*service)->id);
- returnCode = -1;
- throw MuxInitException();
- }
+ edi_conf.enabled = true;
- // Adjust service type from this first component
- switch ((*service)->getType(ensemble)) {
- case 0: // Audio
- (*service)->program = true;
- break;
- case 1:
- case 2:
- case 3:
- (*service)->program = false;
- break;
- default:
- etiLog.log(error,
- "Error, unknown service type: %u\n", (*service)->getType(ensemble));
- returnCode = -1;
- throw MuxInitException();
- }
+ edi_conf.dest_addr = pt_edi.get<string>("destination");
+ edi_conf.dest_port = pt_edi.get<unsigned int>("port");
+ edi_conf.source_port = pt_edi.get<unsigned int>("sourceport");
- // Adjust components type for DAB+
- while (component != ensemble->components.end()) {
- subchannel =
- getSubchannel(ensemble->subchannels, (*component)->subchId);
- if (subchannel == ensemble->subchannels.end()) {
- etiLog.log(error, "Error, service %u component "
- "links to the invalid subchannel %u\n",
- (*component)->serviceId, (*component)->subchId);
- returnCode = -1;
- throw MuxInitException();
- }
-
- protection = &(*subchannel)->protection;
- switch ((*subchannel)->type) {
- case Audio:
- {
- if (protection->form == EEP) {
- (*component)->type = 0x3f; // DAB+
- }
- }
- break;
- case DataDmb:
- case Fidc:
- case Packet:
- break;
- default:
- etiLog.log(error,
- "Error, unknown subchannel type\n");
- returnCode = -1;
- throw MuxInitException();
- }
- component = getComponent(ensemble->components,
- (*service)->id, component);
- }
+ edi_conf.dump = pt_edi.get<bool>("dump");
+ edi_conf.enable_pft = pt_edi.get<bool>("enable_pft");
+ edi_conf.verbose = pt_edi.get<bool>("verbose");
+
+ mux.set_edi_config(edi_conf);
+#else
+ throw runtime_error("EDI output not compiled in");
+#endif
}
- }
+ else {
+ string uri = pt_outputs.get<string>(outputuid);
+
+ size_t proto_pos = uri.find("://");
+ if (proto_pos == std::string::npos) {
+ stringstream ss;
+ ss << "Output with uid " << outputuid << " no protocol defined!";
+ throw runtime_error(ss.str());
+ }
+
+ string proto = uri.substr(0, proto_pos);
+ string location = uri.substr(proto_pos + 3);
+ DabOutput *output;
- for (output = outputs.begin(); output != outputs.end() ; ++output) {
- if (0) {
+ if (0) {
#if defined(HAVE_OUTPUT_FILE)
- } else if ((*output)->outputProto == "file") {
- (*output)->output = new DabOutputFile();
+ } else if (proto == "file") {
+ output = new DabOutputFile();
#endif // defined(HAVE_OUTPUT_FILE)
#if defined(HAVE_OUTPUT_FIFO)
- } else if ((*output)->outputProto == "fifo") {
- (*output)->output = new DabOutputFifo();
+ } else if (proto == "fifo") {
+ output = new DabOutputFifo();
#endif // !defined(HAVE_OUTPUT_FIFO)
#if defined(HAVE_OUTPUT_RAW)
- } else if ((*output)->outputProto == "raw") {
- (*output)->output = new DabOutputRaw();
+ } else if (proto == "raw") {
+ output = new DabOutputRaw();
#endif // defined(HAVE_OUTPUT_RAW)
#if defined(HAVE_OUTPUT_UDP)
- } else if ((*output)->outputProto == "udp") {
- (*output)->output = new DabOutputUdp();
+ } else if (proto == "udp") {
+ output = new DabOutputUdp();
#endif // defined(HAVE_OUTPUT_UDP)
#if defined(HAVE_OUTPUT_TCP)
- } else if ((*output)->outputProto == "tcp") {
- (*output)->output = new DabOutputTcp();
+ } else if (proto == "tcp") {
+ output = new DabOutputTcp();
#endif // defined(HAVE_OUTPUT_TCP)
#if defined(HAVE_OUTPUT_SIMUL)
- } else if ((*output)->outputProto == "simul") {
- (*output)->output = new DabOutputSimul();
+ } else if (proto == "simul") {
+ output = new DabOutputSimul();
#endif // defined(HAVE_OUTPUT_SIMUL)
#if defined(HAVE_OUTPUT_ZEROMQ)
- } else if ((*output)->outputProto == "zmq+tcp") {
- (*output)->output = new DabOutputZMQ("tcp");
- } else if ((*output)->outputProto == "zmq+ipc") {
- (*output)->output = new DabOutputZMQ("ipc");
- } else if ((*output)->outputProto == "zmq+pgm") {
- (*output)->output = new DabOutputZMQ("pgm");
- } else if ((*output)->outputProto == "zmq+epgm") {
- (*output)->output = new DabOutputZMQ("epgm");
+ } else if (proto == "zmq+tcp") {
+ output = new DabOutputZMQ("tcp");
+ } else if (proto == "zmq+ipc") {
+ output = new DabOutputZMQ("ipc");
+ } else if (proto == "zmq+pgm") {
+ output = new DabOutputZMQ("pgm");
+ } else if (proto == "zmq+epgm") {
+ output = new DabOutputZMQ("epgm");
#endif // defined(HAVE_OUTPUT_ZEROMQ)
- } else {
- etiLog.log(error, "Output protocol unknown: %s\n",
- (*output)->outputProto.c_str());
- throw MuxInitException();
- }
-
- if ((*output)->output == NULL) {
- etiLog.log(error, "Unable to init output %s://%s\n",
- (*output)->outputProto.c_str(), (*output)->outputName.c_str());
- return -1;
- }
- if ((*output)->output->Open((*output)->outputName)
- == -1) {
- etiLog.log(error, "Unable to open output %s://%s\n",
- (*output)->outputProto.c_str(), (*output)->outputName.c_str());
- return -1;
- }
- }
+ } else {
+ etiLog.level(error) << "Output protocol unknown: " << proto;
+ throw MuxInitException();
+ }
- // Prepare and check the data inputs
- for (subchannel = ensemble->subchannels.begin();
- subchannel != ensemble->subchannels.end();
- ++subchannel) {
- protection = &(*subchannel)->protection;
- if (subchannel == ensemble->subchannels.begin()) {
- (*subchannel)->startAddress = 0;
- } else {
- (*subchannel)->startAddress = (*(subchannel - 1))->startAddress +
- getSizeCu(*(subchannel - 1));
- }
- if ((*subchannel)->input->open((*subchannel)->inputUri) == -1) {
- perror((*subchannel)->inputUri.c_str());
- returnCode = -1;
- throw MuxInitException();
- }
+ if (output == NULL) {
+ etiLog.level(error) <<
+ "Unable to init output " <<
+ uri;
+ return -1;
+ }
- // TODO Check errors
- int subch_bitrate = (*subchannel)->input->setBitrate( (*subchannel)->bitrate);
- if (subch_bitrate <= 0) {
- etiLog.level(error) << "can't set bitrate for source " <<
- (*subchannel)->inputUri;
- returnCode = -1;
- throw MuxInitException();
- }
- (*subchannel)->bitrate = subch_bitrate;
-
- /* Use EEP unless we find a UEP configuration
- * UEP is only used for MPEG audio, but some bitrates don't
- * have a UEP profile (EN 300 401 Clause 6.2.1).
- * For these bitrates, we must switch to EEP.
- *
- * AAC audio and data is already EEP
- */
- if (protection->form == UEP) {
- protection->form = EEP;
- for (int i = 0; i < 64; i++) {
- if ( (*subchannel)->bitrate == BitRateTable[i] &&
- protection->level == ProtectionLevelTable[i] ) {
- protection->form = UEP;
- protection->uep.tableIndex = i;
- }
+ if (output->Open(location) == -1) {
+ etiLog.level(error) <<
+ "Unable to open output " <<
+ uri;
+ return -1;
}
- }
- /* EEP B can only be used for subchannels with bitrates
- * multiple of 32kbit/s
- */
- if ( protection->form == EEP &&
- protection->eep.profile == EEP_B &&
- subch_bitrate % 32 != 0 ) {
- etiLog.level(error) <<
- "Cannot use EEP_B protection for subchannel " <<
- (*subchannel)->inputUri <<
- ": bitrate not multiple of 32kbit/s";
- returnCode = -1;
- throw MuxInitException();
+ boost::shared_ptr<DabOutput> dabout(output);
+ outputs.push_back(dabout);
+
}
}
- if (ensemble->subchannels.size() == 0) {
- etiLog.log(error, "can't multiplex no subchannel!\n");
- returnCode = -1;
- throw MuxInitException();
- }
- subchannel = ensemble->subchannels.end() - 1;
- if ((*subchannel)->startAddress + getSizeCu((*subchannel)) > 864) {
- etiLog.log(error, "Total size in CU exceeds 864\n");
- printSubchannels(ensemble->subchannels);
- returnCode = -1;
+ if (outputs.size() == 0) {
+ etiLog.log(emerg, "no output defined");
throw MuxInitException();
}
- // Init packet components SCId
- cur = 0;
- for (component = ensemble->components.begin();
- component != ensemble->components.end();
- ++component) {
- subchannel = getSubchannel(ensemble->subchannels,
- (*component)->subchId);
- if (subchannel == ensemble->subchannels.end()) {
- etiLog.log(error,
- "Subchannel %i does not exist for component "
- "of service %i\n",
- (*component)->subchId, (*component)->serviceId);
- returnCode = -1;
- throw MuxInitException();
- }
- if ((*subchannel)->type != Packet) continue;
-
- (*component)->packet.id = cur++;
- }
-
- // Print settings before starting
- etiLog.log(info, "--- Multiplex configuration ---");
- printEnsemble(ensemble);
-
- etiLog.log(info, "--- Subchannels list ---");
- printSubchannels(ensemble->subchannels);
-
- etiLog.log(info, "--- Services list ---");
- printServices(ensemble->services);
-
- etiLog.log(info, "--- Components list ---");
- printComponents(ensemble->components);
+ mux.prepare();
+ mux.print_info();
etiLog.log(info, "--- Output list ---");
printOutputs(outputs);
+#if HAVE_OUTPUT_EDI
if (edi_conf.enabled) {
etiLog.level(warn) << "EXPERIMENTAL EDI OUTPUT ENABLED!";
etiLog.level(info) << "edi to " << edi_conf.dest_addr << ":" << edi_conf.dest_port;
etiLog.level(info) << "source port " << edi_conf.source_port;
etiLog.level(info) << "verbose " << edi_conf.verbose;
}
+#endif
+ size_t limit = pt.get("general.nbframes", 0);
- /* These iterators are used to fill the respective FIG.
- * It is necessary to cycle through all the FIGs that have
- * to be transmitted because they do not fit in a single
- * FIB.
- *
- * ETSI EN 300 799 Clauses 5.2 and 8.1
- */
- serviceProgFIG0_2 = ensemble->services.end();
- serviceDataFIG0_2 = ensemble->services.end();
- componentProgFIG0_8 = ensemble->components.end();
- componentFIG0_13 = ensemble->components.end();
- componentDataFIG0_8 = ensemble->components.end();
- serviceFIG0_17 = ensemble->services.end();
- subchannelFIG0_1 = ensemble->subchannels.end();
-
- if (edi_conf.verbose) {
- etiLog.log(info, "Setup EDI debug");
- }
- std::ofstream edi_debug_file;
-
- if (edi_conf.dump) {
- edi_debug_file.open("./edi.debug");
- }
- UdpSocket edi_output;
-
- if (edi_conf.enabled) {
- edi_output.create(edi_conf.source_port);
- }
-
- if (edi_conf.verbose) {
- etiLog.log(info, "EDI debug set up");
- }
-
- // The TagPacket will then be placed into an AFPacket
- AFPacketiser edi_afPacketiser(edi_conf.verbose);
-
- // The AF Packet will be protected with reed-solomon and split in fragments
- PFT edi_pft(207, 3, edi_conf);
-
+ etiLog.level(info) << "Start loop";
/* Each iteration of the main loop creates one ETI frame */
+ size_t currentFrame;
for (currentFrame = 0; running; currentFrame++) {
- if ((limit > 0) && (currentFrame >= limit)) {
- break;
- }
-
- // For EDI, save ETI(LI) Management data into a TAG Item DETI
- TagDETI edi_tagDETI;
- TagStarPTR edi_tagStarPtr;
- list<TagESTn> edi_subchannels;
- map<dabSubchannel*, TagESTn*> edi_subchannelToTag;
-
- // The above Tag Items will be assembled into a TAG Packet
- TagPacket edi_tagpacket;
-
- edi_tagDETI.atstf = 0; // TODO add ATST support
-
- date = getDabTime();
-
- // Initialise the ETI frame
- memset(etiFrame, 0, 6144);
-
- /**********************************************************************
- ********** Section SYNC of ETI(NI, G703) *************************
- **********************************************************************/
-
- // See ETS 300 799 Clause 6
- eti_SYNC *etiSync = (eti_SYNC *) etiFrame;
-
- etiSync->ERR = edi_tagDETI.stat = 0xFF; // ETS 300 799, 5.2, no error
+ mux.mux_frame(outputs);
- //****** Field FSYNC *****//
- // See ETS 300 799, 6.2.1.2
- sync ^= 0xffffff;
- etiSync->FSYNC = sync;
-
- /**********************************************************************
- *********** Section LIDATA of ETI(NI, G703) **********************
- **********************************************************************/
-
- // See ETS 300 799 Figure 5 for a better overview of these fields.
-
- //****** Section FC ***************************************************/
- // 4 octets, starts at offset 4
- eti_FC *fc = (eti_FC *) &etiFrame[4];
-
- //****** FCT ******//
- // Incremente for each frame, overflows at 249
- fc->FCT = currentFrame % 250;
- edi_tagDETI.dflc = currentFrame % 5000;
-
- //****** FICF ******//
- // Fast Information Channel Flag, 1 bit, =1 if FIC present
- fc->FICF = edi_tagDETI.ficf = 1;
-
- //****** NST ******//
- /* Number of audio of data sub-channels, 7 bits, 0-64.
- * In the 15-frame period immediately preceding a multiplex
- * re-configuration, NST can take the value 0 (see annex E).
- */
- fc->NST = ensemble->subchannels.size();
-
- //****** FP ******//
- /* Frame Phase, 3 bit counter, tells the COFDM generator
- * when to insert the TII. Is also used by the MNSC.
- */
- fc->FP = edi_tagDETI.fp = currentFrame & 0x7;
-
- //****** MID ******//
- //Mode Identity, 2 bits, 01 ModeI, 10 modeII, 11 ModeIII, 00 ModeIV
- fc->MID = edi_tagDETI.mid = ensemble->mode; //mode 2 needs 3 FIB, 3*32octets = 96octets
-
- //****** FL ******//
- /* Frame Length, 11 bits, nb of words(4 bytes) in STC, EOH and MST
- * if NST=0, FL=1+FICL words, FICL=24 or 32 depending on the mode.
- * The FL is given in words (4 octets), see ETS 300 799 5.3.6 for details
- */
- FLtmp = 1 + FICL + (fc->NST);
-
- for (subchannel = ensemble->subchannels.begin();
- subchannel != ensemble->subchannels.end();
- ++subchannel) {
- // Add STLsbch
- FLtmp += getSizeWord(*subchannel);
- }
-
- fc->setFrameLength(FLtmp);
- index = 8;
-
- /******* Section STC **************************************************/
- // Stream Characterization,
- // number of channels * 4 octets = nb octets total
- int edi_stream_id = 1;
- for (subchannel = ensemble->subchannels.begin();
- subchannel != ensemble->subchannels.end();
- ++subchannel) {
- protection = &(*subchannel)->protection;
- eti_STC *sstc = (eti_STC *) & etiFrame[index];
-
- sstc->SCID = (*subchannel)->id;
- sstc->startAddress_high = (*subchannel)->startAddress / 256;
- sstc->startAddress_low = (*subchannel)->startAddress % 256;
- // depends on the desired protection form
- if (protection->form == UEP) {
- sstc->TPL = 0x10 |
- ProtectionLevelTable[protection->uep.tableIndex];
- }
- else if (protection->form == EEP) {
- sstc->TPL = 0x20 | (protection->eep.GetOption() << 2) | protection->level;
- }
-
- // Sub-channel Stream Length, multiple of 64 bits
- sstc->STL_high = getSizeDWord(*subchannel) / 256;
- sstc->STL_low = getSizeDWord(*subchannel) % 256;
-
- TagESTn tag_ESTn(edi_stream_id++);
- tag_ESTn.scid = (*subchannel)->id;
- tag_ESTn.sad = (*subchannel)->startAddress;
- tag_ESTn.tpl = sstc->TPL;
- tag_ESTn.rfa = 0; // two bits
- tag_ESTn.mst_length = getSizeByte(*subchannel) / 8;
- assert(getSizeByte(*subchannel) % 8 == 0);
-
- edi_subchannels.push_back(tag_ESTn);
- edi_subchannelToTag[*subchannel] = &edi_subchannels.back();
- index += 4;
- }
-
- /******* Section EOH **************************************************/
- // End of Header 4 octets
- eti_EOH *eoh = (eti_EOH *) & etiFrame[index];
-
- //MNSC Multiplex Network Signalling Channel, 2 octets
-
- eoh->MNSC = 0;
-
- struct tm *time_tm = gmtime(&mnsc_time.tv_sec);
- switch (fc->FP & 0x3)
- {
- case 0:
- if (MNSC_increment_time)
- {
- MNSC_increment_time = false;
- mnsc_time.tv_sec += 1;
- }
- {
-
- eti_MNSC_TIME_0 *mnsc = (eti_MNSC_TIME_0 *) &eoh->MNSC;
- // Set fields according to ETS 300 799 -- 5.5.1 and A.2.2
- mnsc->type = 0;
- mnsc->identifier = 0;
- mnsc->rfa = 0;
- }
- break;
- case 1:
- {
- eti_MNSC_TIME_1 *mnsc = (eti_MNSC_TIME_1 *) &eoh->MNSC;
- mnsc->setFromTime(time_tm);
- mnsc->accuracy = 1;
- mnsc->sync_to_frame = 1;
- }
- break;
- case 2:
- {
- eti_MNSC_TIME_2 *mnsc = (eti_MNSC_TIME_2 *) &eoh->MNSC;
- mnsc->setFromTime(time_tm);
- }
- break;
- case 3:
- {
- eti_MNSC_TIME_3 *mnsc = (eti_MNSC_TIME_3 *) &eoh->MNSC;
- mnsc->setFromTime(time_tm);
- }
- break;
- }
-
- edi_tagDETI.mnsc = eoh->MNSC;
-
- // CRC Cyclic Redundancy Checksum of the FC, STC and MNSC, 2 octets
- nbBytesCRC = 4 + ((fc->NST) * 4) + 2;
-
- CRCtmp = 0xffff;
- CRCtmp = crc16(CRCtmp, &etiFrame[4], nbBytesCRC);
- CRCtmp ^= 0xffff;
- eoh->CRC = htons(CRCtmp);
-
- /******* Section MST **************************************************/
- // Main Stream Data, if FICF=1 the first 96 or 128 bytes carry the FIC
- // (depending on mode)
- index = ((fc->NST) + 2 + 1) * 4;
- edi_tagDETI.fic_data = &etiFrame[index];
- edi_tagDETI.fic_length = FICL * 4;
-
- // FIC Insertion
- FIGtype0* fig0;
- FIGtype0_0 *fig0_0;
- FIGtype0_1 *figtype0_1;
-
- FIG_01_SubChannel_ShortF *fig0_1subchShort;
- FIG_01_SubChannel_LongF *fig0_1subchLong1;
-
- FIGtype0_2 *fig0_2;
-
- FIGtype0_2_Service *fig0_2serviceAudio;
- FIGtype0_2_Service_data *fig0_2serviceData;
- FIGtype0_2_audio_component* audio_description;
- FIGtype0_2_data_component* data_description;
- FIGtype0_2_packet_component* packet_description;
-
- FIGtype0_3_header *fig0_3_header;
- FIGtype0_3_data *fig0_3_data;
- FIGtype0_9 *fig0_9;
- FIGtype0_10 *fig0_10;
- FIGtype0_17_programme *programme;
-
- FIGtype1_0 *fig1_0;
- FIGtype1_1 *fig1_1;
- FIGtype1_5 *fig1_5;
-
- tm* timeData;
-
- unsigned char figSize = 0;
-
- // FIB 0 Insertion
- switch (insertFIG) {
-
- case 0:
- case 4:
- case 8:
- case 12:
- // FIG type 0/0, Multiplex Configuration Info (MCI),
- // Ensemble information
- fig0_0 = (FIGtype0_0 *) & etiFrame[index];
-
- fig0_0->FIGtypeNumber = 0;
- fig0_0->Length = 5;
- fig0_0->CN = 0;
- fig0_0->OE = 0;
- fig0_0->PD = 0;
- fig0_0->Extension = 0;
-
- fig0_0->EId = htons(ensemble->id);
- fig0_0->Change = 0;
- fig0_0->Al = 0;
- fig0_0->CIFcnt_hight = (currentFrame / 250) % 20;
- fig0_0->CIFcnt_low = (currentFrame % 250);
- index = index + 6;
- figSize += 6;
-
- break;
-
- case 1:
- case 6:
- case 10:
- case 13:
- // FIG type 0/1, MIC, Sub-Channel Organization,
- // one instance of the part for each subchannel
- figtype0_1 = (FIGtype0_1 *) & etiFrame[index];
-
- figtype0_1->FIGtypeNumber = 0;
- figtype0_1->Length = 1;
- figtype0_1->CN = 0;
- figtype0_1->OE = 0;
- figtype0_1->PD = 0;
- figtype0_1->Extension = 1;
- index = index + 2;
- figSize += 2;
-
- // Rotate through the subchannels until there is no more
- // space in the FIG0/1
- if (subchannelFIG0_1 == ensemble->subchannels.end()) {
- subchannelFIG0_1 = ensemble->subchannels.begin();
- }
-
- for (; subchannelFIG0_1 != ensemble->subchannels.end();
- ++subchannelFIG0_1) {
- protection = &(*subchannelFIG0_1)->protection;
-
- if ( (protection->form == UEP && figSize > 27) ||
- (protection->form == EEP && figSize > 26) ) {
- break;
- }
-
- if (protection->form == UEP) {
- fig0_1subchShort =
- (FIG_01_SubChannel_ShortF*) &etiFrame[index];
- fig0_1subchShort->SubChId = (*subchannelFIG0_1)->id;
-
- fig0_1subchShort->StartAdress_high =
- (*subchannelFIG0_1)->startAddress / 256;
- fig0_1subchShort->StartAdress_low =
- (*subchannelFIG0_1)->startAddress % 256;
-
- fig0_1subchShort->Short_Long_form = 0;
- fig0_1subchShort->TableSwitch = 0;
- fig0_1subchShort->TableIndex =
- protection->uep.tableIndex;
-
- index = index + 3;
- figSize += 3;
- figtype0_1->Length += 3;
- }
- else if (protection->form == EEP) {
- fig0_1subchLong1 =
- (FIG_01_SubChannel_LongF*) &etiFrame[index];
- fig0_1subchLong1->SubChId = (*subchannelFIG0_1)->id;
-
- fig0_1subchLong1->StartAdress_high =
- (*subchannelFIG0_1)->startAddress / 256;
- fig0_1subchLong1->StartAdress_low =
- (*subchannelFIG0_1)->startAddress % 256;
-
- fig0_1subchLong1->Short_Long_form = 1;
- fig0_1subchLong1->Option = protection->eep.GetOption();
- fig0_1subchLong1->ProtectionLevel =
- protection->level;
-
- fig0_1subchLong1->Sub_ChannelSize_high =
- getSizeCu(*subchannelFIG0_1) / 256;
- fig0_1subchLong1->Sub_ChannelSize_low =
- getSizeCu(*subchannelFIG0_1) % 256;
-
- index = index + 4;
- figSize += 4;
- figtype0_1->Length += 4;
- }
- }
+ if (limit && currentFrame >= limit) {
break;
-
- case 2:
- case 9:
- case 11:
- case 14:
- // FIG type 0/2, MCI, Service Organization, one instance of
- // FIGtype0_2_Service for each subchannel
- fig0_2 = NULL;
- cur = 0;
-
- // Rotate through the subchannels until there is no more
- // space in the FIG0/1
- if (serviceProgFIG0_2 == ensemble->services.end()) {
- serviceProgFIG0_2 = ensemble->services.begin();
- }
-
- for (; serviceProgFIG0_2 != ensemble->services.end();
- ++serviceProgFIG0_2) {
- if (!(*serviceProgFIG0_2)->nbComponent(ensemble->components)) {
- continue;
- }
-
- if ((*serviceProgFIG0_2)->getType(ensemble) != 0) {
- continue;
- }
-
- ++cur;
-
- if (fig0_2 == NULL) {
- fig0_2 = (FIGtype0_2 *) & etiFrame[index];
-
- fig0_2->FIGtypeNumber = 0;
- fig0_2->Length = 1;
- fig0_2->CN = 0;
- fig0_2->OE = 0;
- fig0_2->PD = 0;
- fig0_2->Extension = 2;
- index = index + 2;
- figSize += 2;
- }
-
- if (figSize + 3
- + (*serviceProgFIG0_2)->nbComponent(ensemble->components)
- * 2 > 30) {
- break;
- }
-
- fig0_2serviceAudio = (FIGtype0_2_Service*) &etiFrame[index];
-
- fig0_2serviceAudio->SId = htons((*serviceProgFIG0_2)->id);
- fig0_2serviceAudio->Local_flag = 0;
- fig0_2serviceAudio->CAId = 0;
- fig0_2serviceAudio->NbServiceComp =
- (*serviceProgFIG0_2)->nbComponent(ensemble->components);
- index += 3;
- fig0_2->Length += 3;
- figSize += 3;
-
- int curCpnt = 0;
- for (component = getComponent(ensemble->components,
- (*serviceProgFIG0_2)->id);
- component != ensemble->components.end();
- component = getComponent(ensemble->components,
- (*serviceProgFIG0_2)->id, component)) {
- subchannel = getSubchannel(ensemble->subchannels,
- (*component)->subchId);
-
- if (subchannel == ensemble->subchannels.end()) {
- etiLog.log(error,
- "Subchannel %i does not exist for component "
- "of service %i\n",
- (*component)->subchId, (*component)->serviceId);
- returnCode = -1;
- throw MuxInitException();
- }
-
- switch ((*subchannel)->type) {
- case Audio:
- audio_description =
- (FIGtype0_2_audio_component*)&etiFrame[index];
- audio_description->TMid = 0;
- audio_description->ASCTy = (*component)->type;
- audio_description->SubChId = (*subchannel)->id;
- audio_description->PS = ((curCpnt == 0) ? 1 : 0);
- audio_description->CA_flag = 0;
- break;
- case DataDmb:
- data_description =
- (FIGtype0_2_data_component*)&etiFrame[index];
- data_description->TMid = 1;
- data_description->DSCTy = (*component)->type;
- data_description->SubChId = (*subchannel)->id;
- data_description->PS = ((curCpnt == 0) ? 1 : 0);
- data_description->CA_flag = 0;
- break;
- case Packet:
- packet_description =
- (FIGtype0_2_packet_component*)&etiFrame[index];
- packet_description->TMid = 3;
- packet_description->setSCId((*component)->packet.id);
- packet_description->PS = ((curCpnt == 0) ? 1 : 0);
- packet_description->CA_flag = 0;
- break;
- default:
- etiLog.log(error,
- "Component type not supported\n");
- returnCode = -1;
- throw MuxInitException();
- }
- index += 2;
- fig0_2->Length += 2;
- figSize += 2;
- if (figSize > 30) {
- etiLog.log(error,
- "Sorry, no space left in FIG 0/2 to insert "
- "component %i of program service %i.\n",
- curCpnt, cur);
- returnCode = -1;
- throw MuxInitException();
- }
- ++curCpnt;
- }
- }
- break;
-
- case 3:
- fig0_2 = NULL;
- cur = 0;
-
- if (serviceDataFIG0_2 == ensemble->services.end()) {
- serviceDataFIG0_2 = ensemble->services.begin();
- }
- for (; serviceDataFIG0_2 != ensemble->services.end();
- ++serviceDataFIG0_2) {
- if (!(*serviceDataFIG0_2)->nbComponent(ensemble->components)) {
- continue;
- }
-
- unsigned char type = (*serviceDataFIG0_2)->getType(ensemble);
- if ((type == 0) || (type == 2)) {
- continue;
- }
-
- ++cur;
-
- if (fig0_2 == NULL) {
- fig0_2 = (FIGtype0_2 *) & etiFrame[index];
-
- fig0_2->FIGtypeNumber = 0;
- fig0_2->Length = 1;
- fig0_2->CN = 0;
- fig0_2->OE = 0;
- fig0_2->PD = 1;
- fig0_2->Extension = 2;
- index = index + 2;
- figSize += 2;
- }
-
- if (figSize + 5
- + (*serviceDataFIG0_2)->nbComponent(ensemble->components)
- * 2 > 30) {
- break;
- }
-
- fig0_2serviceData =
- (FIGtype0_2_Service_data*) &etiFrame[index];
-
- fig0_2serviceData->SId = htonl((*serviceDataFIG0_2)->id);
- fig0_2serviceData->Local_flag = 0;
- fig0_2serviceData->CAId = 0;
- fig0_2serviceData->NbServiceComp =
- (*serviceDataFIG0_2)->nbComponent(ensemble->components);
- fig0_2->Length += 5;
- index += 5;
- figSize += 5;
-
- int curCpnt = 0;
- for (component = getComponent(ensemble->components,
- (*serviceDataFIG0_2)->id);
- component != ensemble->components.end();
- component = getComponent(ensemble->components,
- (*serviceDataFIG0_2)->id, component)) {
- subchannel = getSubchannel(ensemble->subchannels,
- (*component)->subchId);
- if (subchannel == ensemble->subchannels.end()) {
- etiLog.log(error,
- "Subchannel %i does not exist for component "
- "of service %i\n",
- (*component)->subchId, (*component)->serviceId);
- returnCode = -1;
- throw MuxInitException();
- }
-
- switch ((*subchannel)->type) {
- case Audio:
- audio_description =
- (FIGtype0_2_audio_component*)&etiFrame[index];
- audio_description->TMid = 0;
- audio_description->ASCTy = (*component)->type;
- audio_description->SubChId = (*subchannel)->id;
- audio_description->PS = ((curCpnt == 0) ? 1 : 0);
- audio_description->CA_flag = 0;
- break;
- case DataDmb:
- data_description =
- (FIGtype0_2_data_component*)&etiFrame[index];
- data_description->TMid = 1;
- data_description->DSCTy = (*component)->type;
- data_description->SubChId = (*subchannel)->id;
- data_description->PS = ((curCpnt == 0) ? 1 : 0);
- data_description->CA_flag = 0;
- break;
- case Packet:
- packet_description =
- (FIGtype0_2_packet_component*)&etiFrame[index];
- packet_description->TMid = 3;
- packet_description->setSCId((*component)->packet.id);
- packet_description->PS = ((curCpnt == 0) ? 1 : 0);
- packet_description->CA_flag = 0;
- break;
- default:
- etiLog.log(error,
- "Component type not supported\n");
- returnCode = -1;
- throw MuxInitException();
- }
- index += 2;
- fig0_2->Length += 2;
- figSize += 2;
- if (figSize > 30) {
- etiLog.log(error,
- "Sorry, no place left in FIG 0/2 to insert "
- "component %i of data service %i.\n",
- curCpnt, cur);
- returnCode = -1;
- throw MuxInitException();
- }
- ++curCpnt;
- }
- }
- break;
-
- case 5:
- fig0_3_header = NULL;
-
- for (component = ensemble->components.begin();
- component != ensemble->components.end();
- ++component) {
- subchannel = getSubchannel(ensemble->subchannels,
- (*component)->subchId);
-
- if (subchannel == ensemble->subchannels.end()) {
- etiLog.log(error,
- "Subchannel %i does not exist for component "
- "of service %i\n",
- (*component)->subchId, (*component)->serviceId);
- returnCode = -1;
- throw MuxInitException();
- }
-
- if ((*subchannel)->type != Packet)
- continue;
-
- if (fig0_3_header == NULL) {
- fig0_3_header = (FIGtype0_3_header*)&etiFrame[index];
- fig0_3_header->FIGtypeNumber = 0;
- fig0_3_header->Length = 1;
- fig0_3_header->CN = 0;
- fig0_3_header->OE = 0;
- fig0_3_header->PD = 0;
- fig0_3_header->Extension = 3;
-
- index += 2;
- figSize += 2;
- }
-
- /* Warning: When bit SCCA_flag is unset(0), the multiplexer
- * R&S does not send the SCCA field. But, in the Factum ETI
- * analyzer, if this field is not there, it is an error.
- */
- fig0_3_data = (FIGtype0_3_data*)&etiFrame[index];
- fig0_3_data->setSCId((*component)->packet.id);
- fig0_3_data->rfa = 0;
- fig0_3_data->SCCA_flag = 0;
- // if 0, datagroups are used
- fig0_3_data->DG_flag = !(*component)->packet.datagroup;
- fig0_3_data->rfu = 0;
- fig0_3_data->DSCTy = (*component)->type;
- fig0_3_data->SubChId = (*subchannel)->id;
- fig0_3_data->setPacketAddress((*component)->packet.address);
- if (factumAnalyzer) {
- fig0_3_data->SCCA = 0;
- }
-
- fig0_3_header->Length += 5;
- index += 5;
- figSize += 5;
- if (factumAnalyzer) {
- fig0_3_header->Length += 2;
- index += 2;
- figSize += 2;
- }
-
- if (figSize > 30) {
- etiLog.log(error,
- "can't add to Fic Fig 0/3, "
- "too much packet service\n");
- returnCode = -1;
- throw MuxInitException();
- }
- }
- break;
-
- case 7:
- fig0 = NULL;
- if (serviceFIG0_17 == ensemble->services.end()) {
- serviceFIG0_17 = ensemble->services.begin();
- }
- for (; serviceFIG0_17 != ensemble->services.end();
- ++serviceFIG0_17) {
-
- if ( (*serviceFIG0_17)->pty == 0 &&
- (*serviceFIG0_17)->language == 0) {
- continue;
- }
-
- if (fig0 == NULL) {
- fig0 = (FIGtype0*)&etiFrame[index];
- fig0->FIGtypeNumber = 0;
- fig0->Length = 1;
- fig0->CN = 0;
- fig0->OE = 0;
- fig0->PD = 0;
- fig0->Extension = 17;
- index += 2;
- figSize += 2;
- }
-
- if ((*serviceFIG0_17)->language == 0) {
- if (figSize + 4 > 30) {
- break;
- }
- }
- else {
- if (figSize + 5 > 30) {
- break;
- }
- }
-
- programme =
- (FIGtype0_17_programme*)&etiFrame[index];
- programme->SId = htons((*serviceFIG0_17)->id);
- programme->SD = 1;
- programme->PS = 0;
- programme->L = (*serviceFIG0_17)->language != 0;
- programme->CC = 0;
- programme->Rfa = 0;
- programme->NFC = 0;
- if ((*serviceFIG0_17)->language == 0) {
- etiFrame[index + 3] = (*serviceFIG0_17)->pty;
- fig0->Length += 4;
- index += 4;
- figSize += 4;
- }
- else {
- etiFrame[index + 3] = (*serviceFIG0_17)->language;
- etiFrame[index + 4] = (*serviceFIG0_17)->pty;
- fig0->Length += 5;
- index += 5;
- figSize += 5;
- }
- }
- break;
- }
-
- if (figSize > 30) {
- etiLog.log(error,
- "FIG too big (%i > 30)\n", figSize);
- returnCode = -1;
- throw MuxInitException();
- }
-
- memcpy(&etiFrame[index], Padding_FIB, 30 - figSize);
- index += 30 - figSize;
-
- CRCtmp = 0xffff;
- CRCtmp = crc16(CRCtmp, &etiFrame[(index - 30)], 30);
- CRCtmp ^= 0xffff;
- etiFrame[index++] = ((char *) &CRCtmp)[1];
- etiFrame[index++] = ((char *) &CRCtmp)[0];
-
- figSize = 0;
- // FIB 1 insertion
- switch (rotateFIB) {
- case 0: // FIG 0/8 program
- fig0 = NULL;
-
- if (componentProgFIG0_8 == ensemble->components.end()) {
- componentProgFIG0_8 = ensemble->components.begin();
- }
- for (; componentProgFIG0_8 != ensemble->components.end();
- ++componentProgFIG0_8) {
- service = getService(*componentProgFIG0_8,
- ensemble->services);
- subchannel = getSubchannel(ensemble->subchannels,
- (*componentProgFIG0_8)->subchId);
- if (subchannel == ensemble->subchannels.end()) {
- etiLog.log(error,
- "Subchannel %i does not exist for component "
- "of service %i\n",
- (*componentProgFIG0_8)->subchId,
- (*componentProgFIG0_8)->serviceId);
- returnCode = -1;
- throw MuxInitException();
- }
-
- if (!(*service)->program)
- continue;
-
- if (fig0 == NULL) {
- fig0 = (FIGtype0*)&etiFrame[index];
- fig0->FIGtypeNumber = 0;
- fig0->Length = 1;
- fig0->CN = 0;
- fig0->OE = 0;
- fig0->PD = 0;
- fig0->Extension = 8;
- index += 2;
- figSize += 2;
- }
-
- if ((*subchannel)->type == Packet) { // Data packet
- if (figSize > 30 - 5) {
- break;
- }
- etiFrame[index] =
- ((*componentProgFIG0_8)->serviceId >> 8) & 0xFF;
- etiFrame[index+1] =
- ((*componentProgFIG0_8)->serviceId) & 0xFF;
- fig0->Length += 2;
- index += 2;
- figSize += 2;
-
- FIGtype0_8_long* definition =
- (FIGtype0_8_long*)&etiFrame[index];
- memset(definition, 0, 3);
- definition->ext = 0; // no rfa
- definition->SCIdS = (*componentProgFIG0_8)->SCIdS;
- definition->LS = 1;
- definition->setSCId((*componentProgFIG0_8)->packet.id);
- fig0->Length += 3;
- index += 3; // 8 minus rfa
- figSize += 3;
- }
- else { // Audio, data stream or FIDC
- if (figSize > 30 - 4) {
- break;
- }
- etiFrame[index] =
- ((*componentProgFIG0_8)->serviceId >> 8) & 0xFF;
- etiFrame[index+1] =
- ((*componentProgFIG0_8)->serviceId) & 0xFF;
-
- fig0->Length += 2;
- index += 2;
- figSize += 2;
-
- FIGtype0_8_short* definition =
- (FIGtype0_8_short*)&etiFrame[index];
- memset(definition, 0, 2);
- definition->ext = 0; // no rfa
- definition->SCIdS = (*componentProgFIG0_8)->SCIdS;
- definition->LS = 0;
- definition->MscFic = 0;
- definition->Id = (*componentProgFIG0_8)->subchId;
- fig0->Length += 2;
- index += 2; // 4 minus rfa
- figSize += 2;
- }
- }
- break;
-
- case 1: // FIG 0/8 data
- fig0 = NULL;
-
- if (componentDataFIG0_8 == ensemble->components.end()) {
- componentDataFIG0_8 = ensemble->components.begin();
- }
- for (; componentDataFIG0_8 != ensemble->components.end();
- ++componentDataFIG0_8) {
- service = getService(*componentDataFIG0_8,
- ensemble->services);
-
- subchannel = getSubchannel(ensemble->subchannels,
- (*componentDataFIG0_8)->subchId);
-
- if (subchannel == ensemble->subchannels.end()) {
- etiLog.log(error,
- "Subchannel %i does not exist for component "
- "of service %i\n",
- (*componentDataFIG0_8)->subchId,
- (*componentDataFIG0_8)->serviceId);
- returnCode = -1;
- throw MuxInitException();
- }
-
- if ((*service)->program)
- continue;
-
- if (fig0 == NULL) {
- fig0 = (FIGtype0*)&etiFrame[index];
- fig0->FIGtypeNumber = 0;
- fig0->Length = 1;
- fig0->CN = 0;
- fig0->OE = 0;
- fig0->PD = 1;
- fig0->Extension = 8;
- index += 2;
- figSize += 2;
- }
-
- if ((*subchannel)->type == Packet) { // Data packet
- if (figSize > 30 - 7) {
- break;
- }
- etiFrame[index] =
- ((*componentDataFIG0_8)->serviceId >> 8) & 0xFF;
- etiFrame[index+1] =
- ((*componentDataFIG0_8)->serviceId) & 0xFF;
- fig0->Length += 4;
- index += 4;
- figSize += 4;
-
- FIGtype0_8_long* definition =
- (FIGtype0_8_long*)&etiFrame[index];
- memset(definition, 0, 3);
- definition->ext = 0; // no rfa
- definition->SCIdS = (*componentDataFIG0_8)->SCIdS;
- definition->LS = 1;
- definition->setSCId((*componentDataFIG0_8)->packet.id);
- fig0->Length += 3;
- index += 3; // 8 minus rfa
- figSize += 3;
- }
- else { // Audio, data stream or FIDC
- if (figSize > 30 - 6) {
- break;
- }
- etiFrame[index] =
- ((*componentDataFIG0_8)->serviceId >> 8) & 0xFF;
- etiFrame[index+1] =
- ((*componentDataFIG0_8)->serviceId) & 0xFF;
- fig0->Length += 4;
- index += 4;
- figSize += 4;
-
- FIGtype0_8_short* definition =
- (FIGtype0_8_short*)&etiFrame[index];
- memset(definition, 0, 2);
- definition->ext = 0; // no rfa
- definition->SCIdS = (*componentDataFIG0_8)->SCIdS;
- definition->LS = 0;
- definition->MscFic = 0;
- definition->Id = (*componentDataFIG0_8)->subchId;
- fig0->Length += 2;
- index += 2; // 4 minus rfa
- figSize += 2;
- }
- }
- break;
-
- case 3:
- // FIG type 1/0, Service Information (SI), Ensemble Label
- fig1_0 = (FIGtype1_0 *) & etiFrame[index];
-
- fig1_0->Length = 21;
- fig1_0->FIGtypeNumber = 1;
- fig1_0->Extension = 0;
- fig1_0->OE = 0;
- fig1_0->Charset = 0;
- fig1_0->EId = htons(ensemble->id);
- index = index + 4;
-
- memcpy(&etiFrame[index], ensemble->label.text(), 16);
- index = index + 16;
-
- etiFrame[index++] = ensemble->label.flag() >> 8;
- etiFrame[index++] = ensemble->label.flag() & 0xFF;
-
- figSize += 22;
- break;
-
- case 5:
- case 6:
- // FIG 0 / 13
- fig0 = NULL;
-
- if (componentFIG0_13 == ensemble->components.end()) {
- componentFIG0_13 = ensemble->components.begin();
-
- transmitFIG0_13programme = !transmitFIG0_13programme;
- // Alternate between data and and programme FIG0/13,
- // do not mix fig0 with PD=0 with extension 13 stuff
- // that actually needs PD=1, and vice versa
- }
-
- for (; componentFIG0_13 != ensemble->components.end();
- ++componentFIG0_13) {
-
- subchannel = getSubchannel(ensemble->subchannels,
- (*componentFIG0_13)->subchId);
-
- if (subchannel == ensemble->subchannels.end()) {
- etiLog.log(error,
- "Subchannel %i does not exist for component "
- "of service %i\n",
- (*componentFIG0_13)->subchId,
- (*componentFIG0_13)->serviceId);
- returnCode = -1;
- throw MuxInitException();
- }
-
- if ( transmitFIG0_13programme &&
- (*subchannel)->type == Audio &&
- (*componentFIG0_13)->audio.uaType != 0xffff) {
- if (fig0 == NULL) {
- fig0 = (FIGtype0*)&etiFrame[index];
- fig0->FIGtypeNumber = 0;
- fig0->Length = 1;
- fig0->CN = 0;
- fig0->OE = 0;
- fig0->PD = 0;
- fig0->Extension = 13;
- index += 2;
- figSize += 2;
- }
-
- if (figSize > 30 - (3+4+11)) {
- break;
- }
-
- FIG0_13_shortAppInfo* info =
- (FIG0_13_shortAppInfo*)&etiFrame[index];
- info->SId = htonl((*componentFIG0_13)->serviceId) >> 16;
- info->SCIdS = (*componentFIG0_13)->SCIdS;
- info->No = 1;
- index += 3;
- figSize += 3;
- fig0->Length += 3;
-
- FIG0_13_app* app = (FIG0_13_app*)&etiFrame[index];
- app->setType((*componentFIG0_13)->audio.uaType);
- app->length = 4;
- app->xpad = htonl(0x0cbc0000);
- /* xpad meaning
- CA = 0
- CAOrg = 0
- Rfu = 0
- AppTy(5) = 12 (MOT, start of X-PAD data group)
- DG = 0 (MSC data groups used)
- Rfu = 0
- DSCTy(6) = 60 (MOT)
- CAOrg(16) = 0
- */
-
- index += 2 + app->length;
- figSize += 2 + app->length;
- fig0->Length += 2 + app->length;
- }
- else if (!transmitFIG0_13programme &&
- (*subchannel)->type == Packet &&
- (*componentFIG0_13)->packet.appType != 0xffff) {
-
- if (fig0 == NULL) {
- fig0 = (FIGtype0*)&etiFrame[index];
- fig0->FIGtypeNumber = 0;
- fig0->Length = 1;
- fig0->CN = 0;
- fig0->OE = 0;
- fig0->PD = 1;
- fig0->Extension = 13;
- index += 2;
- figSize += 2;
- }
-
- if (figSize > 30 - (5+2)) {
- break;
- }
-
- FIG0_13_longAppInfo* info =
- (FIG0_13_longAppInfo*)&etiFrame[index];
- info->SId = htonl((*componentFIG0_13)->serviceId);
- info->SCIdS = (*componentFIG0_13)->SCIdS;
- info->No = 1;
- index += 5;
- figSize += 5;
- fig0->Length += 5;
-
- FIG0_13_app* app = (FIG0_13_app*)&etiFrame[index];
- app->setType((*componentFIG0_13)->packet.appType);
- app->length = 0;
- index += 2;
- figSize += 2;
- fig0->Length += 2;
- }
- }
- break;
-
- case 7:
- //Time and country identifier
- fig0_10 = (FIGtype0_10 *) & etiFrame[index];
-
- fig0_10->FIGtypeNumber = 0;
- fig0_10->Length = 5;
- fig0_10->CN = 0;
- fig0_10->OE = 0;
- fig0_10->PD = 0;
- fig0_10->Extension = 10;
- index = index + 2;
-
- timeData = gmtime(&date);
-
- fig0_10->RFU = 0;
- fig0_10->setMJD(gregorian2mjd(timeData->tm_year + 1900,
- timeData->tm_mon + 1,
- timeData->tm_mday));
- fig0_10->LSI = 0;
- fig0_10->ConfInd = (watermarkData[watermarkPos >> 3] >>
- (7 - (watermarkPos & 0x07))) & 1;
- if (++watermarkPos == watermarkSize) {
- watermarkPos = 0;
- }
- fig0_10->UTC = 0;
- fig0_10->setHours(timeData->tm_hour);
- fig0_10->Minutes = timeData->tm_min;
- index = index + 4;
- figSize += 6;
-
- fig0_9 = (FIGtype0_9*)&etiFrame[index];
- fig0_9->FIGtypeNumber = 0;
- fig0_9->Length = 4;
- fig0_9->CN = 0;
- fig0_9->OE = 0;
- fig0_9->PD = 0;
- fig0_9->Extension = 9;
-
- fig0_9->ext = 0;
- fig0_9->lto = 0; // Unique LTO for ensemble
-
- if (ensemble->lto_auto) {
- time_t now = time(NULL);
- struct tm* ltime = localtime(&now);
- time_t now2 = timegm(ltime);
- ensemble->lto = (now2 - now) / 1800;
- }
-
- if (ensemble->lto >= 0) {
- fig0_9->ensembleLto = ensemble->lto;
- }
- else {
- /* Convert to 1-complement representation */
- fig0_9->ensembleLto = (-ensemble->lto) | (1<<5);
- }
-
- fig0_9->ensembleEcc = ensemble->ecc;
- fig0_9->tableId = ensemble->international_table;
- index += 5;
- figSize += 5;
-
- break;
- }
-
- assert(figSize <= 30);
- memcpy(&etiFrame[index], Padding_FIB, 30 - figSize);
- index += 30 - figSize;
-
- CRCtmp = 0xffff;
- CRCtmp = crc16(CRCtmp, &etiFrame[(index - 30)], 30);
- CRCtmp ^= 0xffff;
- etiFrame[index++] = ((char *) &CRCtmp)[1];
- etiFrame[index++] = ((char *) &CRCtmp)[0];
-
-
- figSize = 0;
- // FIB 2 insertion
- if (rotateFIB < ensemble->services.size()) {
- service = ensemble->services.begin() + rotateFIB;
-
- // FIG type 1/1, SI, Service label, one instance per subchannel
- if ((*service)->getType(ensemble) == 0) {
- fig1_1 = (FIGtype1_1 *) & etiFrame[index];
-
- fig1_1->FIGtypeNumber = 1;
- fig1_1->Length = 21;
- fig1_1->Charset = 0;
- fig1_1->OE = 0;
- fig1_1->Extension = 1;
-
- fig1_1->Sld = htons((*service)->id);
- index += 4;
- figSize += 4;
- }
- else {
- fig1_5 = (FIGtype1_5 *) & etiFrame[index];
- fig1_5->FIGtypeNumber = 1;
- fig1_5->Length = 23;
- fig1_5->Charset = 0;
- fig1_5->OE = 0;
- fig1_5->Extension = 5;
-
- fig1_5->SId = htonl((*service)->id);
- index += 6;
- figSize += 6;
- }
- memcpy(&etiFrame[index], (*service)->label.text(), 16);
- index += 16;
- figSize += 16;
- etiFrame[index++] = (*service)->label.flag() >> 8;
- etiFrame[index++] = (*service)->label.flag() & 0xFF;
- figSize += 2;
- }
- else if (rotateFIB <
- ensemble->services.size() + ensemble->components.size()) {
- component = ensemble->components.begin() +
- (rotateFIB - ensemble->services.size());
-
- service = getService(*component, ensemble->services);
-
- subchannel =
- getSubchannel(ensemble->subchannels, (*component)->subchId);
-
- if ((*component)->label.text()[0] != 0) {
- if ((*service)->getType(ensemble) == 0) { // Programme
- FIGtype1_4_programme *fig1_4;
- fig1_4 = (FIGtype1_4_programme*)&etiFrame[index];
-
- fig1_4->FIGtypeNumber = 1;
- fig1_4->Length = 22;
- fig1_4->Charset = 0;
- fig1_4->OE = 0;
- fig1_4->Extension = 4;
- fig1_4->PD = 0;
- fig1_4->rfa = 0;
- fig1_4->SCIdS = (*component)->SCIdS;
-
- fig1_4->SId = htons((*service)->id);
- index += 5;
- figSize += 5;
- }
- else { // Data
- FIGtype1_4_data *fig1_4;
- fig1_4 = (FIGtype1_4_data *) & etiFrame[index];
- fig1_4->FIGtypeNumber = 1;
- fig1_4->Length = 24;
- fig1_4->Charset = 0;
- fig1_4->OE = 0;
- fig1_4->Extension = 4;
- fig1_4->PD = 1;
- fig1_4->rfa = 0;
- fig1_4->SCIdS = (*component)->SCIdS;
-
- fig1_4->SId = htonl((*service)->id);
- index += 7;
- figSize += 7;
- }
- memcpy(&etiFrame[index], (*component)->label.text(), 16);
- index += 16;
- figSize += 16;
-
- etiFrame[index++] = (*component)->label.flag() >> 8;
- etiFrame[index++] = (*component)->label.flag() & 0xFF;
- figSize += 2;
- }
- }
- memcpy(&etiFrame[index], Padding_FIB, 30 - figSize);
- index += 30 - figSize;
-
- CRCtmp = 0xffff;
- CRCtmp = crc16(CRCtmp, &etiFrame[(index - 30)], 30);
- CRCtmp ^= 0xffff;
- etiFrame[index++] = ((char *) &CRCtmp)[1];
- etiFrame[index++] = ((char *) &CRCtmp)[0];
-
- /* ETSI EN 300 799 Table 2:
- * Only TM3 has a FIB count to CIF count that is
- * not 3 to 1.
- */
- if (ensemble->mode == 3) {
- memcpy(&etiFrame[index], Padding_FIB, 30);
- index += 30;
-
- CRCtmp = 0xffff;
- CRCtmp = crc16(CRCtmp, &etiFrame[(index - 30)], 30);
- CRCtmp ^= 0xffff;
- etiFrame[index++] = ((char *) &CRCtmp)[1];
- etiFrame[index++] = ((char *) &CRCtmp)[0];
- }
-
- if (ensemble->services.size() > 30) {
- etiLog.log(error,
- "Sorry, but this software currently can't write "
- "Service Label of more than 30 services.\n");
- returnCode = -1;
- throw MuxInitException();
- }
-
- // counter for FIG 0/0
- insertFIG = (insertFIG + 1) % 16;
-
- // We rotate through the FIBs every 30 frames
- rotateFIB = (rotateFIB + 1) % 30;
-
- /**********************************************************************
- ****** Input Data Reading *******************************************
- **********************************************************************/
-
- for (subchannel = ensemble->subchannels.begin();
- subchannel != ensemble->subchannels.end();
- ++subchannel) {
-
- TagESTn* tag = edi_subchannelToTag[*subchannel];
-
- int sizeSubchannel = getSizeByte(*subchannel);
- int result = (*subchannel)->input->readFrame(
- &etiFrame[index], sizeSubchannel);
-
- if (result < 0) {
- etiLog.log(info,
- "Subchannel %d read failed at ETI frame number: %d\n",
- (*subchannel)->id, currentFrame);
- }
-
- // save pointer to Audio or Data Stream into correct TagESTn for EDI
- tag->mst_data = &etiFrame[index];
-
- index += sizeSubchannel;
- }
-
-
- index = (3 + fc->NST + FICL) * 4;
- for (subchannel = ensemble->subchannels.begin();
- subchannel != ensemble->subchannels.end();
- ++subchannel) {
- index += getSizeByte(*subchannel);
- }
-
- /******* Section EOF **************************************************/
- // End of Frame, 4 octets
- index = (FLtmp + 1 + 1) * 4;
- eti_EOF *eof = (eti_EOF *) & etiFrame[index];
-
- // CRC of Main Stream data (MST), 16 bits
- index = ((fc->NST) + 2 + 1) * 4; // MST position
- MSTsize = ((FLtmp) - 1 - (fc->NST)) * 4; // data size
-
- CRCtmp = 0xffff;
- CRCtmp = crc16(CRCtmp, &etiFrame[index], MSTsize);
- CRCtmp ^= 0xffff;
- eof->CRC = htons(CRCtmp);
-
- //RFU, Reserved for future use, 2 bytes, should be 0xFFFF
- eof->RFU = htons(0xFFFF);
-
- /******* Section TIST *************************************************/
- // TimeStamps, 24 bits + 1 octet
- index = (FLtmp + 2 + 1) * 4;
- eti_TIST *tist = (eti_TIST *) & etiFrame[index];
-
- if (enableTist) {
- tist->TIST = htonl(timestamp) | 0xff;
- }
- else {
- tist->TIST = htonl(0xffffff) | 0xff;
- }
-
- timestamp += 3 << 17;
- if (timestamp > 0xf9ffff)
- {
- timestamp -= 0xfa0000;
-
- // Also update MNSC time for next frame
- MNSC_increment_time = true;
- }
-
-
-
- /**********************************************************************
- *********** Section FRPD *****************************************
- **********************************************************************/
-
- int frame_size = (FLtmp + 1 + 1 + 1 + 1) * 4;
-
- // Give the data to the outputs
- for (output = outputs.begin() ; output != outputs.end(); ++output) {
- if ((*output)->output->Write(etiFrame, frame_size)
- == -1) {
- etiLog.log(error, "Can't write to output %s://%s\n",
- (*output)->outputProto.c_str(), (*output)->outputName.c_str());
- }
- }
-
-#ifdef DUMP_BRIDGE
- dumpBytes(dumpData, sizeSubChannel, stderr);
-#endif // DUMP_BRIDGE
-
- /**********************************************************************
- *********** Finalise and send EDI ********************************
- **********************************************************************/
-
- if (edi_conf.enabled) {
- // put tags *ptr, DETI and all subchannels into one TagPacket
- edi_tagpacket.tag_items.push_back(&edi_tagStarPtr);
- edi_tagpacket.tag_items.push_back(&edi_tagDETI);
-
- list<TagESTn>::iterator tag;
- for (tag = edi_subchannels.begin(); tag != edi_subchannels.end(); ++tag) {
- edi_tagpacket.tag_items.push_back(&(*tag));
- }
-
- // Assemble into one AF Packet
- AFPacket edi_afpacket = edi_afPacketiser.Assemble(edi_tagpacket);
-
- if (edi_conf.enable_pft) {
- // Apply PFT layer to AF Packet (Reed Solomon FEC and Fragmentation)
- vector< PFTFragment > edi_fragments =
- edi_pft.Assemble(edi_afpacket);
-
- // Send over ethernet
- vector< vector<uint8_t> >::iterator edi_frag;
- for (edi_frag = edi_fragments.begin();
- edi_frag != edi_fragments.end();
- ++edi_frag) {
-
- UdpPacket udppacket;
-
- InetAddress& addr = udppacket.getAddress();
- addr.setAddress(edi_conf.dest_addr.c_str());
- addr.setPort(edi_conf.dest_port);
-
- udppacket.addData(&(edi_frag->front()), edi_frag->size());
-
- edi_output.send(udppacket);
-
- if (edi_conf.dump) {
- std::ostream_iterator<uint8_t> debug_iterator(edi_debug_file);
- std::copy(edi_frag->begin(), edi_frag->end(), debug_iterator);
- }
- }
-
- if (edi_conf.verbose) {
- fprintf(stderr, "EDI number of PFT fragments %zu\n",
- edi_fragments.size());
- }
- }
- else {
- // Send over ethernet
-
- UdpPacket udppacket;
-
- InetAddress& addr = udppacket.getAddress();
- addr.setAddress(edi_conf.dest_addr.c_str());
- addr.setPort(edi_conf.dest_port);
-
- udppacket.addData(&(edi_afpacket.front()), edi_afpacket.size());
-
- edi_output.send(udppacket);
- }
-
- if (edi_conf.dump) {
- std::ostream_iterator<uint8_t> debug_iterator(edi_debug_file);
- std::copy(edi_afpacket.begin(), edi_afpacket.end(), debug_iterator);
- }
- }
-
-#if _DEBUG
- /**********************************************************************
- *********** Output a small message *********************************
- **********************************************************************/
- if (currentFrame % 100 == 0) {
- if (enableTist) {
- etiLog.log(info, "ETI frame number %i Timestamp: %d + %f\n",
- currentFrame, mnsc_time.tv_sec,
- (timestamp & 0xFFFFFF) / 16384000.0);
- }
- else {
- etiLog.log(info, "ETI frame number %i Time: %d, no TIST\n",
- currentFrame, mnsc_time.tv_sec);
- }
}
-#endif
/* Check every six seconds if the remote control is still working */
- if (rc && fc->FCT == 249 && rc->fault_detected()) {
- etiLog.level(warn) << "Detected Remote Control fault, restarting it";
- rc->restart();
+ if ((currentFrame % 250 == 249) && rc->fault_detected()) {
+ etiLog.level(warn) << "Detected Remote Control fault, restarting it";
+ rc->restart();
}
/* Same for statistics server */
- if (mgmt_server && fc->FCT % 10 == 0) {
- if (mgmt_server->fault_detected()) {
+ if (currentFrame % 10 == 0) {
+ ManagementServer& mgmt_server = get_mgmt_server();
+
+ if (mgmt_server.fault_detected()) {
etiLog.level(warn) <<
- "Detected Statistics Server fault, restarting it";
- mgmt_server->restart();
+ "Detected Management Server fault, restarting it";
+ mgmt_server.restart();
+ }
+ else if (mgmt_server.request_pending()) {
+ mgmt_server.update_ptree(pt);
}
- else if (mgmt_server->request_pending()) {
- mgmt_server->update_ptree(pt);
+ else if (mgmt_server.retrieve_new_ptree(pt)) {
+ etiLog.level(warn) <<
+ "Detected configuration change";
+ mux.update_config(pt);
}
}
}
-
+ etiLog.level(info) << "Goodbye";
}
catch (const MuxInitException& except) {
- etiLog.level(error) << "Caught multiplex initialisation error: " <<
+ etiLog.level(error) << "Multiplex initialisation aborted: " <<
except.what();
}
catch (const std::invalid_argument& except) {
@@ -2200,30 +484,7 @@ int main(int argc, char *argv[])
etiLog.log(debug, "exiting...\n");
fflush(stderr);
- // close files fichiers
- for (subchannel = ensemble->subchannels.begin();
- subchannel != ensemble->subchannels.end();
- ++subchannel) {
- if ((*subchannel)->input != NULL) {
- (*subchannel)->input->close();
- }
- delete (*subchannel)->input;
- }
- for (output = outputs.begin() ; output != outputs.end(); ++output) {
- if ((*output)->output) {
- (*output)->output->Close();
- delete ((*output)->output);
- }
- }
- for_each(ensemble->components.begin(), ensemble->components.end(), free);
- for_each(ensemble->services.begin(), ensemble->services.end(), free);
- for_each(ensemble->subchannels.begin(), ensemble->subchannels.end(), free);
- for_each(outputs.begin(), outputs.end(), free);
- ensemble->components.clear();
- ensemble->services.clear();
- ensemble->subchannels.clear();
- delete ensemble;
outputs.clear();
UdpSocket::clean();