aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/ConfigParser.cpp98
-rw-r--r--src/ConfigParser.h15
-rw-r--r--src/DabMultiplexer.cpp1809
-rw-r--r--src/DabMultiplexer.h489
-rw-r--r--src/DabMux.cpp1970
-rw-r--r--src/DabMux.h364
-rw-r--r--src/Makefile.am12
-rw-r--r--src/ManagementServer.cpp22
-rw-r--r--src/ManagementServer.h28
-rw-r--r--src/MuxElements.cpp2
-rw-r--r--src/MuxElements.h2
-rw-r--r--src/RemoteControl.cpp134
-rw-r--r--src/RemoteControl.h45
-rw-r--r--src/dabOutput/dabOutput.h40
-rw-r--r--src/utils.cpp22
-rw-r--r--src/utils.h5
16 files changed, 2634 insertions, 2423 deletions
diff --git a/src/ConfigParser.cpp b/src/ConfigParser.cpp
index 718946b..254a385 100644
--- a/src/ConfigParser.cpp
+++ b/src/ConfigParser.cpp
@@ -125,14 +125,8 @@ int hexparse(std::string input)
void parse_ptree(boost::property_tree::ptree& pt,
- vector<dabOutput*> &outputs,
- dabEnsemble* ensemble,
- bool* enableTist,
- unsigned* FICL,
- bool* factumAnalyzer,
- unsigned long* limit,
- BaseRemoteController** rc,
- int* mgmtserverport,
+ boost::shared_ptr<dabEnsemble> ensemble,
+ boost::shared_ptr<BaseRemoteController> rc,
edi_configuration_t* edi
)
{
@@ -150,38 +144,11 @@ void parse_ptree(boost::property_tree::ptree& pt,
ensemble->mode = 0;
}
- if (ensemble->mode == 3) {
- *FICL = 32;
- }
- else {
- *FICL = 24;
- }
-
- /* Number of frames to generate */
- *limit = pt_general.get("nbframes", 0);
-
/* Enable Logging to syslog conditionally */
if (pt_general.get<bool>("syslog", false)) {
etiLog.register_backend(new LogToSyslog()); // TODO don't leak the LogToSyslog backend
}
- *factumAnalyzer = pt_general.get("writescca", false);
-
- *enableTist = pt_general.get("tist", false);
-
- *mgmtserverport = pt_general.get<int>("managementport",
- pt_general.get<int>("statsserverport", 0) );
-
- /************** READ REMOTE CONTROL PARAMETERS *************/
- ptree pt_rc = pt.get_child("remotecontrol");
- int telnetport = pt_rc.get<int>("telnetport", 0);
-
- if (telnetport != 0) {
- *rc = new RemoteControllerTelnet(telnetport);
- }
- else {
- *rc = new RemoteControllerDummy();
- }
/******************** READ ENSEMBLE PARAMETERS *************/
ptree pt_ensemble = pt.get_child("ensemble");
@@ -266,7 +233,6 @@ void parse_ptree(boost::property_tree::ptree& pt,
ptree pt_service = it->second;
DabService* service = new DabService(serviceuid);
ensemble->services.push_back(service);
- service->enrol_at(**rc);
int success = -5;
@@ -338,7 +304,7 @@ void parse_ptree(boost::property_tree::ptree& pt,
try {
setup_subchannel_from_ptree(subchan, it->second, ensemble,
- subchanuid, *rc);
+ subchanuid, rc);
}
catch (runtime_error &e) {
etiLog.log(error,
@@ -407,8 +373,6 @@ void parse_ptree(boost::property_tree::ptree& pt,
DabComponent* component = new DabComponent(componentuid);
- component->enrol_at(**rc);
-
component->serviceId = service->id;
component->subchId = subchannel->id;
component->SCIdS = SCIdS_per_service[service]++;
@@ -493,65 +457,13 @@ void parse_ptree(boost::property_tree::ptree& pt,
}
- /******************** READ OUTPUT PARAMETERS ***************/
- map<string, dabOutput*> alloutputs;
- ptree pt_outputs = pt.get_child("outputs");
- for (ptree::iterator it = pt_outputs.begin(); it != pt_outputs.end(); ++it) {
- string outputuid = it->first;
-
- if (outputuid == "edi") {
- ptree pt_edi = pt_outputs.get_child("edi");
-
- edi->enabled = true;
-
- edi->dest_addr = pt_edi.get<string>("destination");
- edi->dest_port = pt_edi.get<unsigned int>("port");
- edi->source_port = pt_edi.get<unsigned int>("sourceport");
-
- edi->dump = pt_edi.get<bool>("dump");
- edi->enable_pft = pt_edi.get<bool>("enable_pft");
- edi->verbose = pt_edi.get<bool>("verbose");
- }
- else {
- string uri = pt_outputs.get<string>(outputuid);
-
- size_t proto_pos = uri.find("://");
- if (proto_pos == std::string::npos) {
- stringstream ss;
- ss << "Output with uid " << outputuid << " no protocol defined!";
- throw runtime_error(ss.str());
- }
-
- char* uri_c = new char[512];
- memset(uri_c, 0, 512);
- uri.copy(uri_c, 511);
-
- uri_c[proto_pos] = '\0';
-
- char* outputName = uri_c + proto_pos + 3;
-
- dabOutput* output = new dabOutput(uri_c, outputName);
- outputs.push_back(output);
-
- // keep outputs in map, and check for uniqueness of the uid
- if (alloutputs.count(outputuid) == 0) {
- alloutputs[outputuid] = output;
- }
- else {
- stringstream ss;
- ss << "output with uid " << outputuid << " not unique!";
- throw runtime_error(ss.str());
- }
- }
- }
-
}
void setup_subchannel_from_ptree(dabSubchannel* subchan,
boost::property_tree::ptree &pt,
- dabEnsemble* ensemble,
+ boost::shared_ptr<dabEnsemble> ensemble,
string subchanuid,
- BaseRemoteController* rc)
+ boost::shared_ptr<BaseRemoteController> rc)
{
using boost::property_tree::ptree;
using boost::property_tree::ptree_error;
diff --git a/src/ConfigParser.h b/src/ConfigParser.h
index 4af010a..e2dc6ce 100644
--- a/src/ConfigParser.h
+++ b/src/ConfigParser.h
@@ -36,23 +36,18 @@
#include "MuxElements.h"
#include "DabMux.h"
#include <boost/property_tree/ptree.hpp>
+#include <boost/shared_ptr.hpp>
void parse_ptree(boost::property_tree::ptree& pt,
- std::vector<dabOutput*> &outputs,
- dabEnsemble* ensemble,
- bool* enableTist,
- unsigned* FICL,
- bool* factumAnalyzer,
- unsigned long* limit,
- BaseRemoteController** rc,
- int* mgmtserverport,
+ boost::shared_ptr<dabEnsemble> ensemble,
+ boost::shared_ptr<BaseRemoteController> rc,
edi_configuration_t* edi);
void setup_subchannel_from_ptree(dabSubchannel* subchan,
boost::property_tree::ptree &pt,
- dabEnsemble* ensemble,
+ boost::shared_ptr<dabEnsemble> ensemble,
std::string subchanuid,
- BaseRemoteController* rc);
+ boost::shared_ptr<BaseRemoteController> rc);
#endif
diff --git a/src/DabMultiplexer.cpp b/src/DabMultiplexer.cpp
new file mode 100644
index 0000000..55de493
--- /dev/null
+++ b/src/DabMultiplexer.cpp
@@ -0,0 +1,1809 @@
+/*
+ Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+ 2011, 2012 Her Majesty the Queen in Right of Canada (Communications
+ Research Center Canada)
+
+ Copyright (C) 2015
+ Matthias P. Braendli, matthias.braendli@mpb.li
+ */
+/*
+ This file is part of ODR-DabMux.
+
+ ODR-DabMux is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ ODR-DabMux is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with ODR-DabMux. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "DabMultiplexer.h"
+#include "ConfigParser.h"
+
+using namespace std;
+using namespace boost;
+
+static unsigned char Padding_FIB[] = {
+ 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+
+// Protection levels and bitrates for UEP.
+const unsigned char ProtectionLevelTable[64] = {
+ 4, 3, 2, 1, 0,
+ 4, 3, 2, 1, 0,
+ 4, 3, 2, 1,
+ 4, 3, 2, 1, 0,
+ 4, 3, 2, 1, 0,
+ 4, 3, 2, 1, 0,
+ 4, 3, 2, 1,
+ 4, 3, 2, 1, 0,
+ 4, 3, 2, 1, 0,
+ 4, 3, 2, 1, 0,
+ 4, 3, 2, 1, 0,
+ 4, 3, 2, 1, 0,
+ 4, 3, 1,
+ 4, 2, 0
+};
+
+const unsigned short BitRateTable[64] = {
+ 32, 32, 32, 32, 32,
+ 48, 48, 48, 48, 48,
+ 56, 56, 56, 56,
+ 64, 64, 64, 64, 64,
+ 80, 80, 80, 80, 80,
+ 96, 96, 96, 96, 96,
+ 112, 112, 112, 112,
+ 128, 128, 128, 128, 128,
+ 160, 160, 160, 160, 160,
+ 192, 192, 192, 192, 192,
+ 224, 224, 224, 224, 224,
+ 256, 256, 256, 256, 256,
+ 320, 320, 320,
+ 384, 384, 384
+};
+
+DabMultiplexer::DabMultiplexer(
+ boost::shared_ptr<BaseRemoteController> rc,
+ boost::property_tree::ptree pt) :
+ m_pt(pt),
+ m_rc(rc),
+ timestamp(0),
+ MNSC_increment_time(false),
+ m_watermarkSize(0),
+ m_watermarkPos(0),
+ sync(0x49C5F8),
+ currentFrame(0),
+ insertFIG(0),
+ rotateFIB(0)
+{
+ prepare_watermark();
+
+ ensemble = boost::shared_ptr<dabEnsemble>(new dabEnsemble);
+}
+
+void DabMultiplexer::prepare_watermark()
+{
+ uint8_t buffer[sizeof(m_watermarkData) / 2];
+ snprintf((char*)buffer, sizeof(buffer),
+ "%s %s, compiled at %s, %s",
+ PACKAGE_NAME, PACKAGE_VERSION, __DATE__, __TIME__);
+
+ memset(m_watermarkData, 0, sizeof(m_watermarkData));
+ m_watermarkData[0] = 0x55; // Sync
+ m_watermarkData[1] = 0x55;
+ m_watermarkSize = 16;
+ for (unsigned i = 0; i < strlen((char*)buffer); ++i) {
+ for (int j = 0; j < 8; ++j) {
+ uint8_t bit = (buffer[m_watermarkPos >> 3] >> (7 - (m_watermarkPos & 0x07))) & 1;
+ m_watermarkData[m_watermarkSize >> 3] |= bit << (7 - (m_watermarkSize & 0x07));
+ ++m_watermarkSize;
+ bit = 1;
+ m_watermarkData[m_watermarkSize >> 3] |= bit << (7 - (m_watermarkSize & 0x07));
+ ++m_watermarkSize;
+ ++m_watermarkPos;
+ }
+ }
+ m_watermarkPos = 0;
+}
+
+// Run a set of checks on the configuration
+void DabMultiplexer::prepare()
+{
+ parse_ptree(m_pt, ensemble, m_rc, &edi_conf);
+
+ ensemble->enrol_at(m_rc);
+
+ prepare_subchannels();
+ prepare_services_components();
+ prepare_data_inputs();
+
+ if (ensemble->subchannels.size() == 0) {
+ etiLog.log(error, "can't multiplex no subchannel!\n");
+ throw MuxInitException();
+ }
+
+ vector<dabSubchannel*>::iterator subchannel =
+ ensemble->subchannels.end() - 1;
+
+ if ((*subchannel)->startAddress + getSizeCu((*subchannel)) > 864) {
+ etiLog.log(error, "Total size in CU exceeds 864\n");
+ printSubchannels(ensemble->subchannels);
+ throw MuxInitException();
+ }
+
+ /* These iterators are used to fill the respective FIG.
+ * It is necessary to cycle through all the FIGs that have
+ * to be transmitted because they do not fit in a single
+ * FIB.
+ *
+ * ETSI EN 300 799 Clauses 5.2 and 8.1
+ */
+ serviceProgFIG0_2 = ensemble->services.end();
+ serviceDataFIG0_2 = ensemble->services.end();
+ componentProgFIG0_8 = ensemble->components.end();
+ componentDataFIG0_8 = ensemble->components.end();
+ componentFIG0_13 = ensemble->components.end();
+ transmitFIG0_13programme = false;
+ serviceFIG0_17 = ensemble->services.end();
+ subchannelFIG0_1 = ensemble->subchannels.end();
+
+
+ /* TODO:
+ * In a SFN, when reconfiguring the ensemble, the multiplexer
+ * has to be restarted (odr-dabmux doesn't support reconfiguration).
+ * Ideally, we must be able to restart transmission s.t. the receiver
+ * synchronisation is preserved.
+ */
+ gettimeofday(&mnsc_time, NULL);
+
+#if ENABLE_EDI
+ // Defaults for edi
+ edi_conf.enabled = db.get_numeric("output.edi.enabled", 0) == 1;
+ if (edi_conf.enabled) {
+ edi_conf.dest_addr = db.get("output.edi.dest_addr");
+ edi_conf.dest_port = db.get_numeric("output.edi.dest_port");
+ edi_conf.source_port = db.get_numeric("output.edi.source_port");
+ edi_conf.dump = db.get_numeric("output.edi.dump") == 1;
+ edi_conf.enable_pft = db.get_numeric("output.edi.enable_pft") == 1;
+ }
+#endif // ENABLE_EDI
+}
+
+
+// Check and adjust subchannels
+void DabMultiplexer::prepare_subchannels()
+{
+ set<unsigned char> ids;
+ vector<dabSubchannel*>::iterator subchannel;
+ 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);
+ throw MuxInitException();
+ }
+ }
+}
+
+// Check and adjust services and components
+void DabMultiplexer::prepare_services_components()
+{
+ set<uint32_t> ids;
+ dabProtection* protection = NULL;
+
+ vector<DabComponent*>::iterator component;
+ vector<dabSubchannel*>::iterator subchannel;
+
+ for (auto service : ensemble->services) {
+ if (ids.find(service->id) != ids.end()) {
+ etiLog.log(error,
+ "Service id 0x%x (%u) is set more than once!\n",
+ service->id, service->id);
+ throw MuxInitException();
+ }
+
+ // Get first component of this service
+ component = getComponent(ensemble->components, service->id);
+ if (component == ensemble->components.end()) {
+ etiLog.log(error,
+ "Service id 0x%x (%u) includes no component!\n",
+ service->id, service->id);
+ throw MuxInitException();
+ }
+
+ // Adjust service type from this first component
+ switch (service->getType(ensemble)) {
+ case 0: // Audio
+ service->program = true;
+ break;
+ case 1:
+ case 2:
+ case 3:
+ service->program = false;
+ break;
+ default:
+ etiLog.log(error,
+ "Error, unknown service type: %u\n",
+ service->getType(ensemble));
+ throw MuxInitException();
+ }
+
+ service->enrol_at(m_rc);
+
+ // Adjust components type for DAB+
+ while (component != ensemble->components.end()) {
+ subchannel =
+ getSubchannel(ensemble->subchannels, (*component)->subchId);
+ if (subchannel == ensemble->subchannels.end()) {
+ etiLog.log(error, "Error, service %u component "
+ "links to the invalid subchannel %u\n",
+ (*component)->serviceId, (*component)->subchId);
+ throw MuxInitException();
+ }
+
+ protection = &(*subchannel)->protection;
+ switch ((*subchannel)->type) {
+ case Audio:
+ {
+ if (protection->form == EEP) {
+ (*component)->type = 0x3f; // DAB+
+ }
+ }
+ break;
+ case DataDmb:
+ case Fidc:
+ case Packet:
+ break;
+ default:
+ etiLog.log(error,
+ "Error, unknown subchannel type\n");
+ throw MuxInitException();
+ }
+ component = getComponent(ensemble->components,
+ service->id, component);
+ }
+ }
+
+ // Init packet components SCId
+ int cur_packetid = 0;
+ for (auto component : ensemble->components) {
+ subchannel = getSubchannel(ensemble->subchannels,
+ component->subchId);
+ if (subchannel == ensemble->subchannels.end()) {
+ etiLog.log(error,
+ "Subchannel %i does not exist for component "
+ "of service %i\n",
+ component->subchId, component->serviceId);
+ throw MuxInitException();
+ }
+ if ((*subchannel)->type != Packet) continue;
+
+ component->packet.id = cur_packetid++;
+
+ component->enrol_at(m_rc);
+
+ }
+
+}
+
+void DabMultiplexer::prepare_data_inputs()
+{
+ dabProtection* protection = NULL;
+ vector<dabSubchannel*>::iterator subchannel;
+
+ // Prepare and check the data inputs
+ for (subchannel = ensemble->subchannels.begin();
+ subchannel != ensemble->subchannels.end();
+ ++subchannel) {
+ protection = &(*subchannel)->protection;
+ if (subchannel == ensemble->subchannels.begin()) {
+ (*subchannel)->startAddress = 0;
+ } else {
+ (*subchannel)->startAddress = (*(subchannel - 1))->startAddress +
+ getSizeCu(*(subchannel - 1));
+ }
+ if ((*subchannel)->input->open((*subchannel)->inputUri) == -1) {
+ perror((*subchannel)->inputUri.c_str());
+ throw MuxInitException();
+ }
+
+ // TODO Check errors
+ int subch_bitrate = (*subchannel)->input->setBitrate( (*subchannel)->bitrate);
+ if (subch_bitrate <= 0) {
+ etiLog.level(error) << "can't set bitrate for source " <<
+ (*subchannel)->inputUri;
+ throw MuxInitException();
+ }
+ (*subchannel)->bitrate = subch_bitrate;
+
+ /* Use EEP unless we find a UEP configuration
+ * UEP is only used for MPEG audio, but some bitrates don't
+ * have a UEP profile (EN 300 401 Clause 6.2.1).
+ * For these bitrates, we must switch to EEP.
+ *
+ * AAC audio and data is already EEP
+ */
+ if (protection->form == UEP) {
+ protection->form = EEP;
+ for (int i = 0; i < 64; i++) {
+ if ( (*subchannel)->bitrate == BitRateTable[i] &&
+ protection->level == ProtectionLevelTable[i] ) {
+ protection->form = UEP;
+ protection->uep.tableIndex = i;
+ }
+ }
+ }
+
+ /* EEP B can only be used for subchannels with bitrates
+ * multiple of 32kbit/s
+ */
+ if ( protection->form == EEP &&
+ protection->eep.profile == EEP_B &&
+ subch_bitrate % 32 != 0 ) {
+ etiLog.level(error) <<
+ "Cannot use EEP_B protection for subchannel " <<
+ (*subchannel)->inputUri <<
+ ": bitrate not multiple of 32kbit/s";
+ throw MuxInitException();
+ }
+ }
+}
+
+/* Each call creates one ETI frame */
+void DabMultiplexer::mux_frame(std::vector<boost::shared_ptr<DabOutput> >& outputs)
+{
+ int cur;
+ time_t date;
+ unsigned char etiFrame[6144];
+ unsigned short index = 0;
+
+ vector<DabService*>::iterator service;
+ vector<DabComponent*>::iterator component;
+ vector<dabSubchannel*>::iterator subchannel;
+
+ // FIC Length, DAB Mode I, II, IV -> FICL = 24, DAB Mode III -> FICL = 32
+ unsigned FICL = (ensemble->mode == 3 ? 32 : 24);
+
+ // For EDI, save ETI(LI) Management data into a TAG Item DETI
+ TagDETI edi_tagDETI;
+ TagStarPTR edi_tagStarPtr;
+ list<TagESTn> edi_subchannels;
+ map<dabSubchannel*, TagESTn*> edi_subchannelToTag;
+
+ // The above Tag Items will be assembled into a TAG Packet
+ TagPacket edi_tagpacket;
+
+ edi_tagDETI.atstf = 0; // TODO add ATST support
+
+ date = getDabTime();
+
+ // Initialise the ETI frame
+ memset(etiFrame, 0, 6144);
+
+ /**********************************************************************
+ ********** Section SYNC of ETI(NI, G703) *************************
+ **********************************************************************/
+
+ // See ETS 300 799 Clause 6
+ eti_SYNC *etiSync = (eti_SYNC *) etiFrame;
+
+ etiSync->ERR = edi_tagDETI.stat = 0xFF; // ETS 300 799, 5.2, no error
+
+ //****** Field FSYNC *****//
+ // See ETS 300 799, 6.2.1.2
+ sync ^= 0xffffff;
+ etiSync->FSYNC = sync;
+
+ /**********************************************************************
+ *********** Section LIDATA of ETI(NI, G703) **********************
+ **********************************************************************/
+
+ // See ETS 300 799 Figure 5 for a better overview of these fields.
+
+ //****** Section FC ***************************************************/
+ // 4 octets, starts at offset 4
+ eti_FC *fc = (eti_FC *) &etiFrame[4];
+
+ //****** FCT ******//
+ // Incremente for each frame, overflows at 249
+ fc->FCT = currentFrame % 250;
+ edi_tagDETI.dflc = currentFrame % 5000;
+
+ //****** FICF ******//
+ // Fast Information Channel Flag, 1 bit, =1 if FIC present
+ fc->FICF = edi_tagDETI.ficf = 1;
+
+ //****** NST ******//
+ /* Number of audio of data sub-channels, 7 bits, 0-64.
+ * In the 15-frame period immediately preceding a multiplex
+ * re-configuration, NST can take the value 0 (see annex E).
+ */
+ fc->NST = ensemble->subchannels.size();
+
+ //****** FP ******//
+ /* Frame Phase, 3 bit counter, tells the COFDM generator
+ * when to insert the TII. Is also used by the MNSC.
+ */
+ fc->FP = edi_tagDETI.fp = currentFrame & 0x7;
+
+ //****** MID ******//
+ //Mode Identity, 2 bits, 01 ModeI, 10 modeII, 11 ModeIII, 00 ModeIV
+ fc->MID = edi_tagDETI.mid = ensemble->mode; //mode 2 needs 3 FIB, 3*32octets = 96octets
+
+ //****** FL ******//
+ /* Frame Length, 11 bits, nb of words(4 bytes) in STC, EOH and MST
+ * if NST=0, FL=1+FICL words, FICL=24 or 32 depending on the mode.
+ * The FL is given in words (4 octets), see ETS 300 799 5.3.6 for details
+ */
+ unsigned short FLtmp = 1 + FICL + (fc->NST);
+
+ for (subchannel = ensemble->subchannels.begin();
+ subchannel != ensemble->subchannels.end();
+ ++subchannel) {
+ // Add STLsbch
+ FLtmp += getSizeWord(*subchannel);
+ }
+
+ fc->setFrameLength(FLtmp);
+ index = 8;
+
+ /******* Section STC **************************************************/
+ // Stream Characterization,
+ // number of channels * 4 octets = nb octets total
+ int edi_stream_id = 1;
+ for (subchannel = ensemble->subchannels.begin();
+ subchannel != ensemble->subchannels.end();
+ ++subchannel) {
+ dabProtection* protection = &(*subchannel)->protection;
+ eti_STC *sstc = (eti_STC *) & etiFrame[index];
+
+ sstc->SCID = (*subchannel)->id;
+ sstc->startAddress_high = (*subchannel)->startAddress / 256;
+ sstc->startAddress_low = (*subchannel)->startAddress % 256;
+ // depends on the desired protection form
+ if (protection->form == UEP) {
+ sstc->TPL = 0x10 |
+ ProtectionLevelTable[protection->uep.tableIndex];
+ }
+ else if (protection->form == EEP) {
+ sstc->TPL = 0x20 | (protection->eep.GetOption() << 2) | protection->level;
+ }
+
+ // Sub-channel Stream Length, multiple of 64 bits
+ sstc->STL_high = getSizeDWord(*subchannel) / 256;
+ sstc->STL_low = getSizeDWord(*subchannel) % 256;
+
+ TagESTn tag_ESTn(edi_stream_id++);
+ tag_ESTn.scid = (*subchannel)->id;
+ tag_ESTn.sad = (*subchannel)->startAddress;
+ tag_ESTn.tpl = sstc->TPL;
+ tag_ESTn.rfa = 0; // two bits
+ tag_ESTn.mst_length = getSizeByte(*subchannel) / 8;
+ assert(getSizeByte(*subchannel) % 8 == 0);
+
+ edi_subchannels.push_back(tag_ESTn);
+ edi_subchannelToTag[*subchannel] = &edi_subchannels.back();
+ index += 4;
+ }
+
+ /******* Section EOH **************************************************/
+ // End of Header 4 octets
+ eti_EOH *eoh = (eti_EOH *) & etiFrame[index];
+
+ //MNSC Multiplex Network Signalling Channel, 2 octets
+
+ eoh->MNSC = 0;
+
+ struct tm *time_tm = gmtime(&mnsc_time.tv_sec);
+ switch (fc->FP & 0x3)
+ {
+ case 0:
+ if (MNSC_increment_time)
+ {
+ MNSC_increment_time = false;
+ mnsc_time.tv_sec += 1;
+ }
+ {
+
+ eti_MNSC_TIME_0 *mnsc = (eti_MNSC_TIME_0 *) &eoh->MNSC;
+ // Set fields according to ETS 300 799 -- 5.5.1 and A.2.2
+ mnsc->type = 0;
+ mnsc->identifier = 0;
+ mnsc->rfa = 0;
+ }
+ break;
+ case 1:
+ {
+ eti_MNSC_TIME_1 *mnsc = (eti_MNSC_TIME_1 *) &eoh->MNSC;
+ mnsc->setFromTime(time_tm);
+ mnsc->accuracy = 1;
+ mnsc->sync_to_frame = 1;
+ }
+ break;
+ case 2:
+ {
+ eti_MNSC_TIME_2 *mnsc = (eti_MNSC_TIME_2 *) &eoh->MNSC;
+ mnsc->setFromTime(time_tm);
+ }
+ break;
+ case 3:
+ {
+ eti_MNSC_TIME_3 *mnsc = (eti_MNSC_TIME_3 *) &eoh->MNSC;
+ mnsc->setFromTime(time_tm);
+ }
+ break;
+ }
+
+ edi_tagDETI.mnsc = eoh->MNSC;
+
+ // CRC Cyclic Redundancy Checksum of the FC, STC and MNSC, 2 octets
+ unsigned short nbBytesCRC = 4 + ((fc->NST) * 4) + 2;
+
+ unsigned short CRCtmp = 0xFFFF;
+ CRCtmp = crc16(CRCtmp, &etiFrame[4], nbBytesCRC);
+ CRCtmp ^= 0xffff;
+ eoh->CRC = htons(CRCtmp);
+
+ /******* Section MST **************************************************/
+ // Main Stream Data, if FICF=1 the first 96 or 128 bytes carry the FIC
+ // (depending on mode)
+ index = ((fc->NST) + 2 + 1) * 4;
+ edi_tagDETI.fic_data = &etiFrame[index];
+ edi_tagDETI.fic_length = FICL * 4;
+
+ // FIC Insertion
+ FIGtype0* fig0;
+ FIGtype0_0 *fig0_0;
+ FIGtype0_1 *figtype0_1;
+
+ FIG_01_SubChannel_ShortF *fig0_1subchShort;
+ FIG_01_SubChannel_LongF *fig0_1subchLong1;
+
+ FIGtype0_2 *fig0_2;
+
+ FIGtype0_2_Service *fig0_2serviceAudio;
+ FIGtype0_2_Service_data *fig0_2serviceData;
+ FIGtype0_2_audio_component* audio_description;
+ FIGtype0_2_data_component* data_description;
+ FIGtype0_2_packet_component* packet_description;
+
+ FIGtype0_3_header *fig0_3_header;
+ FIGtype0_3_data *fig0_3_data;
+ FIGtype0_9 *fig0_9;
+ FIGtype0_10 *fig0_10;
+ FIGtype0_17_programme *programme;
+
+ FIGtype1_0 *fig1_0;
+ FIGtype1_1 *fig1_1;
+ FIGtype1_5 *fig1_5;
+
+ tm* timeData;
+
+ unsigned char figSize = 0;
+
+ // FIB 0 Insertion
+ switch (insertFIG) {
+
+ case 0:
+ case 4:
+ case 8:
+ case 12:
+ // FIG type 0/0, Multiplex Configuration Info (MCI),
+ // Ensemble information
+ fig0_0 = (FIGtype0_0 *) & etiFrame[index];
+
+ fig0_0->FIGtypeNumber = 0;
+ fig0_0->Length = 5;
+ fig0_0->CN = 0;
+ fig0_0->OE = 0;
+ fig0_0->PD = 0;
+ fig0_0->Extension = 0;
+
+ fig0_0->EId = htons(ensemble->id);
+ fig0_0->Change = 0;
+ fig0_0->Al = 0;
+ fig0_0->CIFcnt_hight = (currentFrame / 250) % 20;
+ fig0_0->CIFcnt_low = (currentFrame % 250);
+ index = index + 6;
+ figSize += 6;
+
+ break;
+
+ case 1:
+ case 6:
+ case 10:
+ case 13:
+ // FIG type 0/1, MIC, Sub-Channel Organization,
+ // one instance of the part for each subchannel
+ figtype0_1 = (FIGtype0_1 *) & etiFrame[index];
+
+ figtype0_1->FIGtypeNumber = 0;
+ figtype0_1->Length = 1;
+ figtype0_1->CN = 0;
+ figtype0_1->OE = 0;
+ figtype0_1->PD = 0;
+ figtype0_1->Extension = 1;
+ index = index + 2;
+ figSize += 2;
+
+ // Rotate through the subchannels until there is no more
+ // space in the FIG0/1
+ if (subchannelFIG0_1 == ensemble->subchannels.end()) {
+ subchannelFIG0_1 = ensemble->subchannels.begin();
+ }
+
+ for (; subchannelFIG0_1 != ensemble->subchannels.end();
+ ++subchannelFIG0_1) {
+ dabProtection* protection = &(*subchannelFIG0_1)->protection;
+
+ if ( (protection->form == UEP && figSize > 27) ||
+ (protection->form == EEP && figSize > 26) ) {
+ break;
+ }
+
+ if (protection->form == UEP) {
+ fig0_1subchShort =
+ (FIG_01_SubChannel_ShortF*) &etiFrame[index];
+ fig0_1subchShort->SubChId = (*subchannelFIG0_1)->id;
+
+ fig0_1subchShort->StartAdress_high =
+ (*subchannelFIG0_1)->startAddress / 256;
+ fig0_1subchShort->StartAdress_low =
+ (*subchannelFIG0_1)->startAddress % 256;
+
+ fig0_1subchShort->Short_Long_form = 0;
+ fig0_1subchShort->TableSwitch = 0;
+ fig0_1subchShort->TableIndex =
+ protection->uep.tableIndex;
+
+ index = index + 3;
+ figSize += 3;
+ figtype0_1->Length += 3;
+ }
+ else if (protection->form == EEP) {
+ fig0_1subchLong1 =
+ (FIG_01_SubChannel_LongF*) &etiFrame[index];
+ fig0_1subchLong1->SubChId = (*subchannelFIG0_1)->id;
+
+ fig0_1subchLong1->StartAdress_high =
+ (*subchannelFIG0_1)->startAddress / 256;
+ fig0_1subchLong1->StartAdress_low =
+ (*subchannelFIG0_1)->startAddress % 256;
+
+ fig0_1subchLong1->Short_Long_form = 1;
+ fig0_1subchLong1->Option = protection->eep.GetOption();
+ fig0_1subchLong1->ProtectionLevel =
+ protection->level;
+
+ fig0_1subchLong1->Sub_ChannelSize_high =
+ getSizeCu(*subchannelFIG0_1) / 256;
+ fig0_1subchLong1->Sub_ChannelSize_low =
+ getSizeCu(*subchannelFIG0_1) % 256;
+
+ index = index + 4;
+ figSize += 4;
+ figtype0_1->Length += 4;
+ }
+ }
+ break;
+
+ case 2:
+ case 9:
+ case 11:
+ case 14:
+ // FIG type 0/2, MCI, Service Organization, one instance of
+ // FIGtype0_2_Service for each subchannel
+ fig0_2 = NULL;
+ cur = 0;
+
+ // Rotate through the subchannels until there is no more
+ // space in the FIG0/1
+ if (serviceProgFIG0_2 == ensemble->services.end()) {
+ serviceProgFIG0_2 = ensemble->services.begin();
+ }
+
+ for (; serviceProgFIG0_2 != ensemble->services.end();
+ ++serviceProgFIG0_2) {
+ if (!(*serviceProgFIG0_2)->nbComponent(ensemble->components)) {
+ continue;
+ }
+
+ if ((*serviceProgFIG0_2)->getType(ensemble) != 0) {
+ continue;
+ }
+
+ ++cur;
+
+ if (fig0_2 == NULL) {
+ fig0_2 = (FIGtype0_2 *) & etiFrame[index];
+
+ fig0_2->FIGtypeNumber = 0;
+ fig0_2->Length = 1;
+ fig0_2->CN = 0;
+ fig0_2->OE = 0;
+ fig0_2->PD = 0;
+ fig0_2->Extension = 2;
+ index = index + 2;
+ figSize += 2;
+ }
+
+ if (figSize + 3
+ + (*serviceProgFIG0_2)->nbComponent(ensemble->components)
+ * 2 > 30) {
+ break;
+ }
+
+ fig0_2serviceAudio = (FIGtype0_2_Service*) &etiFrame[index];
+
+ fig0_2serviceAudio->SId = htons((*serviceProgFIG0_2)->id);
+ fig0_2serviceAudio->Local_flag = 0;
+ fig0_2serviceAudio->CAId = 0;
+ fig0_2serviceAudio->NbServiceComp =
+ (*serviceProgFIG0_2)->nbComponent(ensemble->components);
+ index += 3;
+ fig0_2->Length += 3;
+ figSize += 3;
+
+ int curCpnt = 0;
+ for (component = getComponent(ensemble->components,
+ (*serviceProgFIG0_2)->id);
+ component != ensemble->components.end();
+ component = getComponent(ensemble->components,
+ (*serviceProgFIG0_2)->id, component)) {
+ subchannel = getSubchannel(ensemble->subchannels,
+ (*component)->subchId);
+
+ if (subchannel == ensemble->subchannels.end()) {
+ etiLog.log(error,
+ "Subchannel %i does not exist for component "
+ "of service %i\n",
+ (*component)->subchId, (*component)->serviceId);
+ throw MuxInitException();
+ }
+
+ switch ((*subchannel)->type) {
+ case Audio:
+ audio_description =
+ (FIGtype0_2_audio_component*)&etiFrame[index];
+ audio_description->TMid = 0;
+ audio_description->ASCTy = (*component)->type;
+ audio_description->SubChId = (*subchannel)->id;
+ audio_description->PS = ((curCpnt == 0) ? 1 : 0);
+ audio_description->CA_flag = 0;
+ break;
+ case DataDmb:
+ data_description =
+ (FIGtype0_2_data_component*)&etiFrame[index];
+ data_description->TMid = 1;
+ data_description->DSCTy = (*component)->type;
+ data_description->SubChId = (*subchannel)->id;
+ data_description->PS = ((curCpnt == 0) ? 1 : 0);
+ data_description->CA_flag = 0;
+ break;
+ case Packet:
+ packet_description =
+ (FIGtype0_2_packet_component*)&etiFrame[index];
+ packet_description->TMid = 3;
+ packet_description->setSCId((*component)->packet.id);
+ packet_description->PS = ((curCpnt == 0) ? 1 : 0);
+ packet_description->CA_flag = 0;
+ break;
+ default:
+ etiLog.log(error,
+ "Component type not supported\n");
+ throw MuxInitException();
+ }
+ index += 2;
+ fig0_2->Length += 2;
+ figSize += 2;
+ if (figSize > 30) {
+ etiLog.log(error,
+ "Sorry, no space left in FIG 0/2 to insert "
+ "component %i of program service %i.\n",
+ curCpnt, cur);
+ throw MuxInitException();
+ }
+ ++curCpnt;
+ }
+ }
+ break;
+
+ case 3:
+ fig0_2 = NULL;
+ cur = 0;
+
+ if (serviceDataFIG0_2 == ensemble->services.end()) {
+ serviceDataFIG0_2 = ensemble->services.begin();
+ }
+ for (; serviceDataFIG0_2 != ensemble->services.end();
+ ++serviceDataFIG0_2) {
+ if (!(*serviceDataFIG0_2)->nbComponent(ensemble->components)) {
+ continue;
+ }
+
+ unsigned char type = (*serviceDataFIG0_2)->getType(ensemble);
+ if ((type == 0) || (type == 2)) {
+ continue;
+ }
+
+ ++cur;
+
+ if (fig0_2 == NULL) {
+ fig0_2 = (FIGtype0_2 *) & etiFrame[index];
+
+ fig0_2->FIGtypeNumber = 0;
+ fig0_2->Length = 1;
+ fig0_2->CN = 0;
+ fig0_2->OE = 0;
+ fig0_2->PD = 1;
+ fig0_2->Extension = 2;
+ index = index + 2;
+ figSize += 2;
+ }
+
+ if (figSize + 5
+ + (*serviceDataFIG0_2)->nbComponent(ensemble->components)
+ * 2 > 30) {
+ break;
+ }
+
+ fig0_2serviceData =
+ (FIGtype0_2_Service_data*) &etiFrame[index];
+
+ fig0_2serviceData->SId = htonl((*serviceDataFIG0_2)->id);
+ fig0_2serviceData->Local_flag = 0;
+ fig0_2serviceData->CAId = 0;
+ fig0_2serviceData->NbServiceComp =
+ (*serviceDataFIG0_2)->nbComponent(ensemble->components);
+ fig0_2->Length += 5;
+ index += 5;
+ figSize += 5;
+
+ int curCpnt = 0;
+ for (component = getComponent(ensemble->components,
+ (*serviceDataFIG0_2)->id);
+ component != ensemble->components.end();
+ component = getComponent(ensemble->components,
+ (*serviceDataFIG0_2)->id, component)) {
+ subchannel = getSubchannel(ensemble->subchannels,
+ (*component)->subchId);
+ if (subchannel == ensemble->subchannels.end()) {
+ etiLog.log(error,
+ "Subchannel %i does not exist for component "
+ "of service %i\n",
+ (*component)->subchId, (*component)->serviceId);
+ throw MuxInitException();
+ }
+
+ switch ((*subchannel)->type) {
+ case Audio:
+ audio_description =
+ (FIGtype0_2_audio_component*)&etiFrame[index];
+ audio_description->TMid = 0;
+ audio_description->ASCTy = (*component)->type;
+ audio_description->SubChId = (*subchannel)->id;
+ audio_description->PS = ((curCpnt == 0) ? 1 : 0);
+ audio_description->CA_flag = 0;
+ break;
+ case DataDmb:
+ data_description =
+ (FIGtype0_2_data_component*)&etiFrame[index];
+ data_description->TMid = 1;
+ data_description->DSCTy = (*component)->type;
+ data_description->SubChId = (*subchannel)->id;
+ data_description->PS = ((curCpnt == 0) ? 1 : 0);
+ data_description->CA_flag = 0;
+ break;
+ case Packet:
+ packet_description =
+ (FIGtype0_2_packet_component*)&etiFrame[index];
+ packet_description->TMid = 3;
+ packet_description->setSCId((*component)->packet.id);
+ packet_description->PS = ((curCpnt == 0) ? 1 : 0);
+ packet_description->CA_flag = 0;
+ break;
+ default:
+ etiLog.log(error,
+ "Component type not supported\n");
+ throw MuxInitException();
+ }
+ index += 2;
+ fig0_2->Length += 2;
+ figSize += 2;
+ if (figSize > 30) {
+ etiLog.log(error,
+ "Sorry, no place left in FIG 0/2 to insert "
+ "component %i of data service %i.\n",
+ curCpnt, cur);
+ throw MuxInitException();
+ }
+ ++curCpnt;
+ }
+ }
+ break;
+
+ case 5:
+ fig0_3_header = NULL;
+
+ for (component = ensemble->components.begin();
+ component != ensemble->components.end();
+ ++component) {
+ subchannel = getSubchannel(ensemble->subchannels,
+ (*component)->subchId);
+
+ if (subchannel == ensemble->subchannels.end()) {
+ etiLog.log(error,
+ "Subchannel %i does not exist for component "
+ "of service %i\n",
+ (*component)->subchId, (*component)->serviceId);
+ throw MuxInitException();
+ }
+
+ if ((*subchannel)->type != Packet)
+ continue;
+
+ if (fig0_3_header == NULL) {
+ fig0_3_header = (FIGtype0_3_header*)&etiFrame[index];
+ fig0_3_header->FIGtypeNumber = 0;
+ fig0_3_header->Length = 1;
+ fig0_3_header->CN = 0;
+ fig0_3_header->OE = 0;
+ fig0_3_header->PD = 0;
+ fig0_3_header->Extension = 3;
+
+ index += 2;
+ figSize += 2;
+ }
+
+ bool factumAnalyzer = m_pt.get("general.writescca", false);
+
+ /* Warning: When bit SCCA_flag is unset(0), the multiplexer
+ * R&S does not send the SCCA field. But, in the Factum ETI
+ * analyzer, if this field is not there, it is an error.
+ */
+ fig0_3_data = (FIGtype0_3_data*)&etiFrame[index];
+ fig0_3_data->setSCId((*component)->packet.id);
+ fig0_3_data->rfa = 0;
+ fig0_3_data->SCCA_flag = 0;
+ // if 0, datagroups are used
+ fig0_3_data->DG_flag = !(*component)->packet.datagroup;
+ fig0_3_data->rfu = 0;
+ fig0_3_data->DSCTy = (*component)->type;
+ fig0_3_data->SubChId = (*subchannel)->id;
+ fig0_3_data->setPacketAddress((*component)->packet.address);
+ if (factumAnalyzer) {
+ fig0_3_data->SCCA = 0;
+ }
+
+ fig0_3_header->Length += 5;
+ index += 5;
+ figSize += 5;
+ if (factumAnalyzer) {
+ fig0_3_header->Length += 2;
+ index += 2;
+ figSize += 2;
+ }
+
+ if (figSize > 30) {
+ etiLog.log(error,
+ "can't add to Fic Fig 0/3, "
+ "too much packet service\n");
+ throw MuxInitException();
+ }
+ }
+ break;
+
+ case 7:
+ fig0 = NULL;
+ if (serviceFIG0_17 == ensemble->services.end()) {
+ serviceFIG0_17 = ensemble->services.begin();
+ }
+ for (; serviceFIG0_17 != ensemble->services.end();
+ ++serviceFIG0_17) {
+
+ if ( (*serviceFIG0_17)->pty == 0 &&
+ (*serviceFIG0_17)->language == 0) {
+ continue;
+ }
+
+ if (fig0 == NULL) {
+ fig0 = (FIGtype0*)&etiFrame[index];
+ fig0->FIGtypeNumber = 0;
+ fig0->Length = 1;
+ fig0->CN = 0;
+ fig0->OE = 0;
+ fig0->PD = 0;
+ fig0->Extension = 17;
+ index += 2;
+ figSize += 2;
+ }
+
+ if ((*serviceFIG0_17)->language == 0) {
+ if (figSize + 4 > 30) {
+ break;
+ }
+ }
+ else {
+ if (figSize + 5 > 30) {
+ break;
+ }
+ }
+
+ programme =
+ (FIGtype0_17_programme*)&etiFrame[index];
+ programme->SId = htons((*serviceFIG0_17)->id);
+ programme->SD = 1;
+ programme->PS = 0;
+ programme->L = (*serviceFIG0_17)->language != 0;
+ programme->CC = 0;
+ programme->Rfa = 0;
+ programme->NFC = 0;
+ if ((*serviceFIG0_17)->language == 0) {
+ etiFrame[index + 3] = (*serviceFIG0_17)->pty;
+ fig0->Length += 4;
+ index += 4;
+ figSize += 4;
+ }
+ else {
+ etiFrame[index + 3] = (*serviceFIG0_17)->language;
+ etiFrame[index + 4] = (*serviceFIG0_17)->pty;
+ fig0->Length += 5;
+ index += 5;
+ figSize += 5;
+ }
+ }
+ break;
+ }
+
+ if (figSize > 30) {
+ etiLog.log(error,
+ "FIG too big (%i > 30)\n", figSize);
+ throw MuxInitException();
+ }
+
+ memcpy(&etiFrame[index], Padding_FIB, 30 - figSize);
+ index += 30 - figSize;
+
+ CRCtmp = 0xffff;
+ CRCtmp = crc16(CRCtmp, &etiFrame[(index - 30)], 30);
+ CRCtmp ^= 0xffff;
+ etiFrame[index++] = ((char *) &CRCtmp)[1];
+ etiFrame[index++] = ((char *) &CRCtmp)[0];
+
+ figSize = 0;
+ // FIB 1 insertion
+ switch (rotateFIB) {
+ case 0: // FIG 0/8 program
+ fig0 = NULL;
+
+ if (componentProgFIG0_8 == ensemble->components.end()) {
+ componentProgFIG0_8 = ensemble->components.begin();
+ }
+ for (; componentProgFIG0_8 != ensemble->components.end();
+ ++componentProgFIG0_8) {
+ service = getService(*componentProgFIG0_8,
+ ensemble->services);
+ subchannel = getSubchannel(ensemble->subchannels,
+ (*componentProgFIG0_8)->subchId);
+ if (subchannel == ensemble->subchannels.end()) {
+ etiLog.log(error,
+ "Subchannel %i does not exist for component "
+ "of service %i\n",
+ (*componentProgFIG0_8)->subchId,
+ (*componentProgFIG0_8)->serviceId);
+ throw MuxInitException();
+ }
+
+ if (!(*service)->program)
+ continue;
+
+ if (fig0 == NULL) {
+ fig0 = (FIGtype0*)&etiFrame[index];
+ fig0->FIGtypeNumber = 0;
+ fig0->Length = 1;
+ fig0->CN = 0;
+ fig0->OE = 0;
+ fig0->PD = 0;
+ fig0->Extension = 8;
+ index += 2;
+ figSize += 2;
+ }
+
+ if ((*subchannel)->type == Packet) { // Data packet
+ if (figSize > 30 - 5) {
+ break;
+ }
+ etiFrame[index] =
+ ((*componentProgFIG0_8)->serviceId >> 8) & 0xFF;
+ etiFrame[index+1] =
+ ((*componentProgFIG0_8)->serviceId) & 0xFF;
+ fig0->Length += 2;
+ index += 2;
+ figSize += 2;
+
+ FIGtype0_8_long* definition =
+ (FIGtype0_8_long*)&etiFrame[index];
+ memset(definition, 0, 3);
+ definition->ext = 0; // no rfa
+ definition->SCIdS = (*componentProgFIG0_8)->SCIdS;
+ definition->LS = 1;
+ definition->setSCId((*componentProgFIG0_8)->packet.id);
+ fig0->Length += 3;
+ index += 3; // 8 minus rfa
+ figSize += 3;
+ }
+ else { // Audio, data stream or FIDC
+ if (figSize > 30 - 4) {
+ break;
+ }
+ etiFrame[index] =
+ ((*componentProgFIG0_8)->serviceId >> 8) & 0xFF;
+ etiFrame[index+1] =
+ ((*componentProgFIG0_8)->serviceId) & 0xFF;
+
+ fig0->Length += 2;
+ index += 2;
+ figSize += 2;
+
+ FIGtype0_8_short* definition =
+ (FIGtype0_8_short*)&etiFrame[index];
+ memset(definition, 0, 2);
+ definition->ext = 0; // no rfa
+ definition->SCIdS = (*componentProgFIG0_8)->SCIdS;
+ definition->LS = 0;
+ definition->MscFic = 0;
+ definition->Id = (*componentProgFIG0_8)->subchId;
+ fig0->Length += 2;
+ index += 2; // 4 minus rfa
+ figSize += 2;
+ }
+ }
+ break;
+
+ case 1: // FIG 0/8 data
+ fig0 = NULL;
+
+ if (componentDataFIG0_8 == ensemble->components.end()) {
+ componentDataFIG0_8 = ensemble->components.begin();
+ }
+ for (; componentDataFIG0_8 != ensemble->components.end();
+ ++componentDataFIG0_8) {
+ service = getService(*componentDataFIG0_8,
+ ensemble->services);
+
+ subchannel = getSubchannel(ensemble->subchannels,
+ (*componentDataFIG0_8)->subchId);
+
+ if (subchannel == ensemble->subchannels.end()) {
+ etiLog.log(error,
+ "Subchannel %i does not exist for component "
+ "of service %i\n",
+ (*componentDataFIG0_8)->subchId,
+ (*componentDataFIG0_8)->serviceId);
+ throw MuxInitException();
+ }
+
+ if ((*service)->program)
+ continue;
+
+ if (fig0 == NULL) {
+ fig0 = (FIGtype0*)&etiFrame[index];
+ fig0->FIGtypeNumber = 0;
+ fig0->Length = 1;
+ fig0->CN = 0;
+ fig0->OE = 0;
+ fig0->PD = 1;
+ fig0->Extension = 8;
+ index += 2;
+ figSize += 2;
+ }
+
+ if ((*subchannel)->type == Packet) { // Data packet
+ if (figSize > 30 - 7) {
+ break;
+ }
+ etiFrame[index] =
+ ((*componentDataFIG0_8)->serviceId >> 8) & 0xFF;
+ etiFrame[index+1] =
+ ((*componentDataFIG0_8)->serviceId) & 0xFF;
+ fig0->Length += 4;
+ index += 4;
+ figSize += 4;
+
+ FIGtype0_8_long* definition =
+ (FIGtype0_8_long*)&etiFrame[index];
+ memset(definition, 0, 3);
+ definition->ext = 0; // no rfa
+ definition->SCIdS = (*componentDataFIG0_8)->SCIdS;
+ definition->LS = 1;
+ definition->setSCId((*componentDataFIG0_8)->packet.id);
+ fig0->Length += 3;
+ index += 3; // 8 minus rfa
+ figSize += 3;
+ }
+ else { // Audio, data stream or FIDC
+ if (figSize > 30 - 6) {
+ break;
+ }
+ etiFrame[index] =
+ ((*componentDataFIG0_8)->serviceId >> 8) & 0xFF;
+ etiFrame[index+1] =
+ ((*componentDataFIG0_8)->serviceId) & 0xFF;
+ fig0->Length += 4;
+ index += 4;
+ figSize += 4;
+
+ FIGtype0_8_short* definition =
+ (FIGtype0_8_short*)&etiFrame[index];
+ memset(definition, 0, 2);
+ definition->ext = 0; // no rfa
+ definition->SCIdS = (*componentDataFIG0_8)->SCIdS;
+ definition->LS = 0;
+ definition->MscFic = 0;
+ definition->Id = (*componentDataFIG0_8)->subchId;
+ fig0->Length += 2;
+ index += 2; // 4 minus rfa
+ figSize += 2;
+ }
+ }
+ break;
+
+ case 3:
+ // FIG type 1/0, Service Information (SI), Ensemble Label
+ fig1_0 = (FIGtype1_0 *) & etiFrame[index];
+
+ fig1_0->Length = 21;
+ fig1_0->FIGtypeNumber = 1;
+ fig1_0->Extension = 0;
+ fig1_0->OE = 0;
+ fig1_0->Charset = 0;
+ fig1_0->EId = htons(ensemble->id);
+ index = index + 4;
+
+ ensemble->label.writeLabel(&etiFrame[index]);
+ index = index + 16;
+
+ etiFrame[index++] = ensemble->label.flag() >> 8;
+ etiFrame[index++] = ensemble->label.flag() & 0xFF;
+
+ figSize += 22;
+ break;
+
+ case 5:
+ case 6:
+ // FIG 0 / 13
+ fig0 = NULL;
+
+ if (componentFIG0_13 == ensemble->components.end()) {
+ componentFIG0_13 = ensemble->components.begin();
+
+ transmitFIG0_13programme = !transmitFIG0_13programme;
+ // Alternate between data and and programme FIG0/13,
+ // do not mix fig0 with PD=0 with extension 13 stuff
+ // that actually needs PD=1, and vice versa
+ }
+
+ for (; componentFIG0_13 != ensemble->components.end();
+ ++componentFIG0_13) {
+
+ subchannel = getSubchannel(ensemble->subchannels,
+ (*componentFIG0_13)->subchId);
+
+ if (subchannel == ensemble->subchannels.end()) {
+ etiLog.log(error,
+ "Subchannel %i does not exist for component "
+ "of service %i\n",
+ (*componentFIG0_13)->subchId,
+ (*componentFIG0_13)->serviceId);
+ throw MuxInitException();
+ }
+
+ if ( transmitFIG0_13programme &&
+ (*subchannel)->type == Audio &&
+ (*componentFIG0_13)->audio.uaType != 0xffff) {
+ if (fig0 == NULL) {
+ fig0 = (FIGtype0*)&etiFrame[index];
+ fig0->FIGtypeNumber = 0;
+ fig0->Length = 1;
+ fig0->CN = 0;
+ fig0->OE = 0;
+ fig0->PD = 0;
+ fig0->Extension = 13;
+ index += 2;
+ figSize += 2;
+ }
+
+ if (figSize > 30 - (3+4+11)) {
+ break;
+ }
+
+ FIG0_13_shortAppInfo* info =
+ (FIG0_13_shortAppInfo*)&etiFrame[index];
+ info->SId = htonl((*componentFIG0_13)->serviceId) >> 16;
+ info->SCIdS = (*componentFIG0_13)->SCIdS;
+ info->No = 1;
+ index += 3;
+ figSize += 3;
+ fig0->Length += 3;
+
+ FIG0_13_app* app = (FIG0_13_app*)&etiFrame[index];
+ app->setType((*componentFIG0_13)->audio.uaType);
+ app->length = 4;
+ app->xpad = htonl(0x0cbc0000);
+ /* xpad meaning
+ CA = 0
+ CAOrg = 0
+ Rfu = 0
+ AppTy(5) = 12 (MOT, start of X-PAD data group)
+ DG = 0 (MSC data groups used)
+ Rfu = 0
+ DSCTy(6) = 60 (MOT)
+ CAOrg(16) = 0
+ */
+
+ index += 2 + app->length;
+ figSize += 2 + app->length;
+ fig0->Length += 2 + app->length;
+ }
+ else if (!transmitFIG0_13programme &&
+ (*subchannel)->type == Packet &&
+ (*componentFIG0_13)->packet.appType != 0xffff) {
+
+ if (fig0 == NULL) {
+ fig0 = (FIGtype0*)&etiFrame[index];
+ fig0->FIGtypeNumber = 0;
+ fig0->Length = 1;
+ fig0->CN = 0;
+ fig0->OE = 0;
+ fig0->PD = 1;
+ fig0->Extension = 13;
+ index += 2;
+ figSize += 2;
+ }
+
+ if (figSize > 30 - (5+2)) {
+ break;
+ }
+
+ FIG0_13_longAppInfo* info =
+ (FIG0_13_longAppInfo*)&etiFrame[index];
+ info->SId = htonl((*componentFIG0_13)->serviceId);
+ info->SCIdS = (*componentFIG0_13)->SCIdS;
+ info->No = 1;
+ index += 5;
+ figSize += 5;
+ fig0->Length += 5;
+
+ FIG0_13_app* app = (FIG0_13_app*)&etiFrame[index];
+ app->setType((*componentFIG0_13)->packet.appType);
+ app->length = 0;
+ index += 2;
+ figSize += 2;
+ fig0->Length += 2;
+ }
+ }
+ break;
+
+ case 7:
+ //Time and country identifier
+ fig0_10 = (FIGtype0_10 *) & etiFrame[index];
+
+ fig0_10->FIGtypeNumber = 0;
+ fig0_10->Length = 5;
+ fig0_10->CN = 0;
+ fig0_10->OE = 0;
+ fig0_10->PD = 0;
+ fig0_10->Extension = 10;
+ index = index + 2;
+
+ timeData = gmtime(&date);
+
+ fig0_10->RFU = 0;
+ fig0_10->setMJD(gregorian2mjd(timeData->tm_year + 1900,
+ timeData->tm_mon + 1,
+ timeData->tm_mday));
+ fig0_10->LSI = 0;
+ fig0_10->ConfInd = (m_watermarkData[m_watermarkPos >> 3] >>
+ (7 - (m_watermarkPos & 0x07))) & 1;
+ if (++m_watermarkPos == m_watermarkSize) {
+ m_watermarkPos = 0;
+ }
+ fig0_10->UTC = 0;
+ fig0_10->setHours(timeData->tm_hour);
+ fig0_10->Minutes = timeData->tm_min;
+ index = index + 4;
+ figSize += 6;
+
+ fig0_9 = (FIGtype0_9*)&etiFrame[index];
+ fig0_9->FIGtypeNumber = 0;
+ fig0_9->Length = 4;
+ fig0_9->CN = 0;
+ fig0_9->OE = 0;
+ fig0_9->PD = 0;
+ fig0_9->Extension = 9;
+
+ fig0_9->ext = 0;
+ fig0_9->lto = 0; // Unique LTO for ensemble
+
+ if (ensemble->lto_auto) {
+ time_t now = time(NULL);
+ struct tm* ltime = localtime(&now);
+ time_t now2 = timegm(ltime);
+ ensemble->lto = (now2 - now) / 1800;
+ }
+
+ if (ensemble->lto >= 0) {
+ fig0_9->ensembleLto = ensemble->lto;
+ }
+ else {
+ /* Convert to 1-complement representation */
+ fig0_9->ensembleLto = (-ensemble->lto) | (1<<5);
+ }
+
+ fig0_9->ensembleEcc = ensemble->ecc;
+ fig0_9->tableId = ensemble->international_table;
+ index += 5;
+ figSize += 5;
+
+ break;
+ }
+
+ assert(figSize <= 30);
+ memcpy(&etiFrame[index], Padding_FIB, 30 - figSize);
+ index += 30 - figSize;
+
+ CRCtmp = 0xffff;
+ CRCtmp = crc16(CRCtmp, &etiFrame[(index - 30)], 30);
+ CRCtmp ^= 0xffff;
+ etiFrame[index++] = ((char *) &CRCtmp)[1];
+ etiFrame[index++] = ((char *) &CRCtmp)[0];
+
+
+ figSize = 0;
+ // FIB 2 insertion
+ if (rotateFIB < ensemble->services.size()) {
+ service = ensemble->services.begin() + rotateFIB;
+
+ // FIG type 1/1, SI, Service label, one instance per subchannel
+ if ((*service)->getType(ensemble) == 0) {
+ fig1_1 = (FIGtype1_1 *) & etiFrame[index];
+
+ fig1_1->FIGtypeNumber = 1;
+ fig1_1->Length = 21;
+ fig1_1->Charset = 0;
+ fig1_1->OE = 0;
+ fig1_1->Extension = 1;
+
+ fig1_1->Sld = htons((*service)->id);
+ index += 4;
+ figSize += 4;
+ }
+ else {
+ fig1_5 = (FIGtype1_5 *) & etiFrame[index];
+ fig1_5->FIGtypeNumber = 1;
+ fig1_5->Length = 23;
+ fig1_5->Charset = 0;
+ fig1_5->OE = 0;
+ fig1_5->Extension = 5;
+
+ fig1_5->SId = htonl((*service)->id);
+ index += 6;
+ figSize += 6;
+ }
+ (*service)->label.writeLabel(&etiFrame[index]);
+ index += 16;
+ figSize += 16;
+ etiFrame[index++] = (*service)->label.flag() >> 8;
+ etiFrame[index++] = (*service)->label.flag() & 0xFF;
+ figSize += 2;
+ }
+ else if (rotateFIB <
+ ensemble->services.size() + ensemble->components.size()) {
+ component = ensemble->components.begin() +
+ (rotateFIB - ensemble->services.size());
+
+ service = getService(*component, ensemble->services);
+
+ subchannel =
+ getSubchannel(ensemble->subchannels, (*component)->subchId);
+
+ if (not (*component)->label.long_label().empty() ) {
+ if ((*service)->getType(ensemble) == 0) { // Programme
+ FIGtype1_4_programme *fig1_4;
+ fig1_4 = (FIGtype1_4_programme*)&etiFrame[index];
+
+ fig1_4->FIGtypeNumber = 1;
+ fig1_4->Length = 22;
+ fig1_4->Charset = 0;
+ fig1_4->OE = 0;
+ fig1_4->Extension = 4;
+ fig1_4->PD = 0;
+ fig1_4->rfa = 0;
+ fig1_4->SCIdS = (*component)->SCIdS;
+
+ fig1_4->SId = htons((*service)->id);
+ index += 5;
+ figSize += 5;
+ }
+ else { // Data
+ FIGtype1_4_data *fig1_4;
+ fig1_4 = (FIGtype1_4_data *) & etiFrame[index];
+ fig1_4->FIGtypeNumber = 1;
+ fig1_4->Length = 24;
+ fig1_4->Charset = 0;
+ fig1_4->OE = 0;
+ fig1_4->Extension = 4;
+ fig1_4->PD = 1;
+ fig1_4->rfa = 0;
+ fig1_4->SCIdS = (*component)->SCIdS;
+
+ fig1_4->SId = htonl((*service)->id);
+ index += 7;
+ figSize += 7;
+ }
+ (*component)->label.writeLabel(&etiFrame[index]);
+ index += 16;
+ figSize += 16;
+
+ etiFrame[index++] = (*component)->label.flag() >> 8;
+ etiFrame[index++] = (*component)->label.flag() & 0xFF;
+ figSize += 2;
+ }
+ }
+ memcpy(&etiFrame[index], Padding_FIB, 30 - figSize);
+ index += 30 - figSize;
+
+ CRCtmp = 0xffff;
+ CRCtmp = crc16(CRCtmp, &etiFrame[(index - 30)], 30);
+ CRCtmp ^= 0xffff;
+ etiFrame[index++] = ((char *) &CRCtmp)[1];
+ etiFrame[index++] = ((char *) &CRCtmp)[0];
+
+ /* ETSI EN 300 799 Table 2:
+ * Only TM3 has a FIB count to CIF count that is
+ * not 3 to 1.
+ */
+ if (ensemble->mode == 3) {
+ memcpy(&etiFrame[index], Padding_FIB, 30);
+ index += 30;
+
+ CRCtmp = 0xffff;
+ CRCtmp = crc16(CRCtmp, &etiFrame[(index - 30)], 30);
+ CRCtmp ^= 0xffff;
+ etiFrame[index++] = ((char *) &CRCtmp)[1];
+ etiFrame[index++] = ((char *) &CRCtmp)[0];
+ }
+
+ if (ensemble->services.size() > 30) {
+ etiLog.log(error,
+ "Sorry, but this software currently can't write "
+ "Service Label of more than 30 services.\n");
+ throw MuxInitException();
+ }
+
+ // counter for FIG 0/0
+ insertFIG = (insertFIG + 1) % 16;
+
+ // We rotate through the FIBs every 30 frames
+ rotateFIB = (rotateFIB + 1) % 30;
+
+ /**********************************************************************
+ ****** Input Data Reading *******************************************
+ **********************************************************************/
+
+ for (subchannel = ensemble->subchannels.begin();
+ subchannel != ensemble->subchannels.end();
+ ++subchannel) {
+
+ TagESTn* tag = edi_subchannelToTag[*subchannel];
+
+ int sizeSubchannel = getSizeByte(*subchannel);
+ int result = (*subchannel)->input->readFrame(
+ &etiFrame[index], sizeSubchannel);
+
+ if (result < 0) {
+ etiLog.log(info,
+ "Subchannel %d read failed at ETI frame number: %d\n",
+ (*subchannel)->id, currentFrame);
+ }
+
+ // save pointer to Audio or Data Stream into correct TagESTn for EDI
+ tag->mst_data = &etiFrame[index];
+
+ index += sizeSubchannel;
+ }
+
+
+ index = (3 + fc->NST + FICL) * 4;
+ for (subchannel = ensemble->subchannels.begin();
+ subchannel != ensemble->subchannels.end();
+ ++subchannel) {
+ index += getSizeByte(*subchannel);
+ }
+
+ /******* Section EOF **************************************************/
+ // End of Frame, 4 octets
+ index = (FLtmp + 1 + 1) * 4;
+ eti_EOF *eof = (eti_EOF *) & etiFrame[index];
+
+ // CRC of Main Stream data (MST), 16 bits
+ index = ((fc->NST) + 2 + 1) * 4; // MST position
+
+ unsigned short MSTsize = ((FLtmp) - 1 - (fc->NST)) * 4; // data size
+
+ CRCtmp = 0xffff;
+ CRCtmp = crc16(CRCtmp, &etiFrame[index], MSTsize);
+ CRCtmp ^= 0xffff;
+ eof->CRC = htons(CRCtmp);
+
+ //RFU, Reserved for future use, 2 bytes, should be 0xFFFF
+ eof->RFU = htons(0xFFFF);
+
+ /******* Section TIST *************************************************/
+ // TimeStamps, 24 bits + 1 octet
+ index = (FLtmp + 2 + 1) * 4;
+ eti_TIST *tist = (eti_TIST *) & etiFrame[index];
+
+ bool enableTist = m_pt.get("general.tist", false);
+ if (enableTist) {
+ tist->TIST = htonl(timestamp) | 0xff;
+ }
+ else {
+ tist->TIST = htonl(0xffffff) | 0xff;
+ }
+
+ timestamp += 3 << 17;
+ if (timestamp > 0xf9ffff)
+ {
+ timestamp -= 0xfa0000;
+
+ // Also update MNSC time for next frame
+ MNSC_increment_time = true;
+ }
+
+
+
+ /**********************************************************************
+ *********** Section FRPD *****************************************
+ **********************************************************************/
+
+ int frame_size = (FLtmp + 1 + 1 + 1 + 1) * 4;
+
+ // Give the data to the outputs
+ for (auto output : outputs) {
+ if (output->Write(etiFrame, frame_size) == -1) {
+ etiLog.level(error) <<
+ "Can't write to output " <<
+ output->get_info();
+ }
+ }
+
+#ifdef DUMP_BRIDGE
+ dumpBytes(dumpData, sizeSubChannel, stderr);
+#endif // DUMP_BRIDGE
+
+#if ENABLE_EDI
+ /**********************************************************************
+ *********** Finalise and send EDI ********************************
+ **********************************************************************/
+
+ if (edi_conf.enabled) {
+ // put tags *ptr, DETI and all subchannels into one TagPacket
+ edi_tagpacket.tag_items.push_back(&edi_tagStarPtr);
+ edi_tagpacket.tag_items.push_back(&edi_tagDETI);
+
+ list<TagESTn>::iterator tag;
+ for (tag = edi_subchannels.begin(); tag != edi_subchannels.end(); ++tag) {
+ edi_tagpacket.tag_items.push_back(&(*tag));
+ }
+
+ // Assemble into one AF Packet
+ AFPacket edi_afpacket = edi_afPacketiser.Assemble(edi_tagpacket);
+
+ if (edi_conf.enable_pft) {
+ // Apply PFT layer to AF Packet (Reed Solomon FEC and Fragmentation)
+ vector< PFTFragment > edi_fragments =
+ edi_pft.Assemble(edi_afpacket);
+
+ // Send over ethernet
+ vector< vector<uint8_t> >::iterator edi_frag;
+ for (edi_frag = edi_fragments.begin();
+ edi_frag != edi_fragments.end();
+ ++edi_frag) {
+
+ UdpPacket udppacket;
+
+ InetAddress& addr = udppacket.getAddress();
+ addr.setAddress(edi_conf.dest_addr.c_str());
+ addr.setPort(edi_conf.dest_port);
+
+ udppacket.addData(&(edi_frag->front()), edi_frag->size());
+
+ edi_output.send(udppacket);
+
+ if (edi_conf.dump) {
+ std::ostream_iterator<uint8_t> debug_iterator(edi_debug_file);
+ std::copy(edi_frag->begin(), edi_frag->end(), debug_iterator);
+ }
+ }
+
+ if (edi_conf.verbose) {
+ fprintf(stderr, "EDI number of PFT fragments %zu\n",
+ edi_fragments.size());
+ }
+ }
+ else {
+ // Send over ethernet
+
+ UdpPacket udppacket;
+
+ InetAddress& addr = udppacket.getAddress();
+ addr.setAddress(edi_conf.dest_addr.c_str());
+ addr.setPort(edi_conf.dest_port);
+
+ udppacket.addData(&(edi_afpacket.front()), edi_afpacket.size());
+
+ edi_output.send(udppacket);
+ }
+
+ if (edi_conf.dump) {
+ std::ostream_iterator<uint8_t> debug_iterator(edi_debug_file);
+ std::copy(edi_afpacket.begin(), edi_afpacket.end(), debug_iterator);
+ }
+ }
+#endif // ENABLE_EDI
+
+ if (currentFrame % 100 == 0) {
+ etiLog.log(info, "ETI frame number %i Time: %d, no TIST\n",
+ currentFrame, mnsc_time.tv_sec);
+ }
+
+#if _DEBUG
+ /**********************************************************************
+ *********** Output a small message *********************************
+ **********************************************************************/
+ if (currentFrame % 100 == 0) {
+ if (enableTist) {
+ etiLog.log(info, "ETI frame number %i Timestamp: %d + %f\n",
+ currentFrame, mnsc_time.tv_sec,
+ (timestamp & 0xFFFFFF) / 16384000.0);
+ }
+ else {
+ etiLog.log(info, "ETI frame number %i Time: %d, no TIST\n",
+ currentFrame, mnsc_time.tv_sec);
+ }
+ }
+#endif
+
+ currentFrame++;
+}
+
+void DabMultiplexer::print_info(void)
+{
+ return;
+ // Print settings before starting
+ etiLog.log(info, "--- Multiplex configuration ---");
+ printEnsemble(ensemble);
+
+ etiLog.log(info, "--- Subchannels list ---");
+ printSubchannels(ensemble->subchannels);
+
+ etiLog.log(info, "--- Services list ---");
+ printServices(ensemble->services);
+
+ etiLog.log(info, "--- Components list ---");
+ printComponents(ensemble->components);
+}
+
diff --git a/src/DabMultiplexer.h b/src/DabMultiplexer.h
new file mode 100644
index 0000000..ebedc82
--- /dev/null
+++ b/src/DabMultiplexer.h
@@ -0,0 +1,489 @@
+/*
+ Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+ 2011, 2012 Her Majesty the Queen in Right of Canada (Communications
+ Research Center Canada)
+
+ Copyright (C) 2015
+ Matthias P. Braendli, matthias.braendli@mpb.li
+ */
+/*
+ This file is part of ODR-DabMux.
+
+ ODR-DabMux is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ ODR-DabMux is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with ODR-DabMux. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __DAB_MULTIPLEXER_H__
+#define __DAB_MULTIPLEXER_H__
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "dabOutput/dabOutput.h"
+#include "dabOutput/edi/TagItems.h"
+#include "dabOutput/edi/TagPacket.h"
+#include "dabOutput/edi/AFPacket.h"
+#include "dabOutput/edi/PFT.h"
+#include "crc.h"
+#include "utils.h"
+#include "UdpSocket.h"
+#include "InetAddress.h"
+#include "dabUtils.h"
+#include "PcDebug.h"
+#include "MuxElements.h"
+#include "RemoteControl.h"
+#include "Eti.h"
+#include <exception>
+#include <vector>
+#include <string>
+#include <boost/shared_ptr.hpp>
+#include <boost/property_tree/ptree.hpp>
+
+class MuxInitException : public std::exception
+{
+ public:
+ MuxInitException(const std::string m = "ODR-DabMux initialisation error")
+ throw()
+ : msg(m) {}
+ ~MuxInitException(void) throw() {}
+ const char* what() const throw() { return msg.c_str(); }
+ private:
+ std::string msg;
+};
+
+class DabMultiplexer {
+ public:
+ DabMultiplexer(boost::shared_ptr<BaseRemoteController> rc,
+ boost::property_tree::ptree pt);
+ void prepare(void);
+
+ unsigned long getCurrentFrame() { return currentFrame; }
+
+ void mux_frame(std::vector<boost::shared_ptr<DabOutput> >& outputs);
+
+ void print_info(void);
+
+ private:
+ void prepare_watermark(void);
+ void prepare_subchannels(void);
+ void prepare_services_components(void);
+ void prepare_data_inputs(void);
+
+ boost::property_tree::ptree m_pt;
+ boost::shared_ptr<BaseRemoteController> m_rc;
+
+ unsigned timestamp;
+ bool MNSC_increment_time;
+ struct timeval mnsc_time;
+
+ edi_configuration_t edi_conf;
+
+
+ uint8_t m_watermarkData[128];
+ size_t m_watermarkSize;
+ size_t m_watermarkPos;
+
+ uint32_t sync;
+ unsigned long currentFrame;
+
+ /* FIG carousel logic and iterators */
+ unsigned int insertFIG;
+ unsigned int rotateFIB;
+
+ std::vector<DabService*>::iterator serviceProgFIG0_2;
+ std::vector<DabService*>::iterator serviceDataFIG0_2;
+ std::vector<DabService*>::iterator serviceFIG0_17;
+
+ std::vector<DabComponent*>::iterator componentProgFIG0_8;
+ std::vector<DabComponent*>::iterator componentDataFIG0_8;
+ std::vector<DabComponent*>::iterator componentFIG0_13;
+ // Alternate between programme and data
+ bool transmitFIG0_13programme;
+
+
+ std::vector<dabSubchannel*>::iterator subchannelFIG0_1;
+
+ boost::shared_ptr<dabEnsemble> ensemble;
+};
+
+// DAB Mode
+#define DEFAULT_DAB_MODE 2
+
+// Taille de la trame de donnee, sous-canal 3, nb de paquets de 64bits,
+// STL3 * 8 = x kbytes par trame ETI
+
+// Data bitrate in kbits/s. Must be 64 kb/s multiple.
+#define DEFAULT_DATA_BITRATE 384
+#define DEFAULT_PACKET_BITRATE 32
+
+/* default ensemble parameters. Label must be max 16 chars, short label
+ * a subset of the label, max 8 chars
+ */
+#define DEFAULT_ENSEMBLE_LABEL "ODR Dab Mux"
+#define DEFAULT_ENSEMBLE_SHORT_LABEL "ODRMux"
+#define DEFAULT_ENSEMBLE_ID 0xc000
+#define DEFAULT_ENSEMBLE_ECC 0xa1
+
+// start value for default service IDs (if not overridden by configuration)
+#define DEFAULT_SERVICE_ID 50
+#define DEFAULT_PACKET_ADDRESS 0
+
+
+/*****************************************************************************
+ ***************** Definition of FIG structures ****************************
+ *****************************************************************************/
+
+#ifdef _WIN32
+# pragma pack(push)
+#endif
+
+struct FIGtype0 {
+ uint8_t Length:5;
+ uint8_t FIGtypeNumber:3;
+ uint8_t Extension:5;
+ uint8_t PD:1;
+ uint8_t OE:1;
+ uint8_t CN:1;
+} PACKED;
+
+
+struct FIGtype0_0 {
+ uint8_t Length:5;
+ uint8_t FIGtypeNumber:3;
+ uint8_t Extension:5;
+ uint8_t PD:1;
+ uint8_t OE:1;
+ uint8_t CN:1;
+
+ uint16_t EId;
+ uint8_t CIFcnt_hight:5;
+ uint8_t Al:1;
+ uint8_t Change:2;
+ uint8_t CIFcnt_low:8;
+} PACKED;
+
+
+struct FIGtype0_2 {
+ uint8_t Length:5;
+ uint8_t FIGtypeNumber:3;
+ uint8_t Extension:5;
+ uint8_t PD:1;
+ uint8_t OE:1;
+ uint8_t CN:1;
+} PACKED;
+
+
+struct FIGtype0_2_Service {
+ uint16_t SId;
+ uint8_t NbServiceComp:4;
+ uint8_t CAId:3;
+ uint8_t Local_flag:1;
+} PACKED;
+
+
+struct FIGtype0_2_Service_data {
+ uint32_t SId;
+ uint8_t NbServiceComp:4;
+ uint8_t CAId:3;
+ uint8_t Local_flag:1;
+} PACKED;
+
+
+struct FIGtype0_2_audio_component {
+ uint8_t ASCTy:6;
+ uint8_t TMid:2;
+ uint8_t CA_flag:1;
+ uint8_t PS:1;
+ uint8_t SubChId:6;
+} PACKED;
+
+
+struct FIGtype0_2_data_component {
+ uint8_t DSCTy:6;
+ uint8_t TMid:2;
+ uint8_t CA_flag:1;
+ uint8_t PS:1;
+ uint8_t SubChId:6;
+} PACKED;
+
+
+struct FIGtype0_2_packet_component {
+ uint8_t SCId_high:6;
+ uint8_t TMid:2;
+ uint8_t CA_flag:1;
+ uint8_t PS:1;
+ uint8_t SCId_low:6;
+ void setSCId(uint16_t SCId) {
+ SCId_high = SCId >> 6;
+ SCId_low = SCId & 0x3f;
+ }
+} PACKED;
+
+
+struct FIGtype0_3_header {
+ uint8_t Length:5;
+ uint8_t FIGtypeNumber:3;
+ uint8_t Extension:5;
+ uint8_t PD:1;
+ uint8_t OE:1;
+ uint8_t CN:1;
+} PACKED;
+
+
+/* Warning: When bit SCCA_flag is unset(0), the multiplexer R&S does not send
+ * the SCCA field. But, in the Factum ETI analyzer, if this field is not there,
+ * it is an error.
+ */
+struct FIGtype0_3_data {
+ uint8_t SCId_high;
+ uint8_t SCCA_flag:1;
+ uint8_t rfa:3;
+ uint8_t SCId_low:4;
+ uint8_t DSCTy:6;
+ uint8_t rfu:1;
+ uint8_t DG_flag:1;
+ uint8_t Packet_address_high:2;
+ uint8_t SubChId:6;
+ uint8_t Packet_address_low;
+ uint16_t SCCA;
+ void setSCId(uint16_t SCId) {
+ SCId_high = SCId >> 4;
+ SCId_low = SCId & 0xf;
+ }
+ void setPacketAddress(uint16_t address) {
+ Packet_address_high = address >> 8;
+ Packet_address_low = address & 0xff;
+ }
+} PACKED;
+
+
+struct FIGtype0_8_short {
+ uint8_t SCIdS:4;
+ uint8_t rfa_1:3;
+ uint8_t ext:1;
+ uint8_t Id:6;
+ uint8_t MscFic:1;
+ uint8_t LS:1;
+ uint8_t rfa_2;
+} PACKED;
+
+
+struct FIGtype0_8_long {
+ uint8_t SCIdS:4;
+ uint8_t rfa_1:3;
+ uint8_t ext:1;
+ uint8_t SCId_high:4;
+ uint8_t rfa:3;
+ uint8_t LS:1;
+ uint8_t SCId_low;
+ uint8_t rfa_2;
+ void setSCId(uint16_t id) {
+ SCId_high = id >> 8;
+ SCId_low = id & 0xff;
+ }
+ uint16_t getSCid() {
+ return (SCId_high << 8) | SCId_low;
+ }
+} PACKED;
+
+
+struct FIGtype0_9 {
+ uint8_t Length:5;
+ uint8_t FIGtypeNumber:3;
+ uint8_t Extension:5;
+ uint8_t PD:1;
+ uint8_t OE:1;
+ uint8_t CN:1;
+
+ uint8_t ensembleLto:6;
+ uint8_t lto:1;
+ uint8_t ext:1;
+ uint8_t ensembleEcc;
+ uint8_t tableId;
+} PACKED;
+
+
+struct FIGtype0_10 {
+ uint8_t Length:5;
+ uint8_t FIGtypeNumber:3;
+ uint8_t Extension:5;
+ uint8_t PD:1;
+ uint8_t OE:1;
+ uint8_t CN:1;
+
+ uint8_t MJD_high:7;
+ uint8_t RFU:1;
+ uint8_t MJD_med;
+ uint8_t Hours_high:3;
+ uint8_t UTC:1;
+ uint8_t ConfInd:1;
+ uint8_t LSI:1;
+ uint8_t MJD_low:2;
+ uint8_t Minutes:6;
+ uint8_t Hours_low:2;
+ void setMJD(uint32_t date) {
+ MJD_high = (date >> 10) & 0x7f;
+ MJD_med = (date >> 2) & 0xff;
+ MJD_low = date & 0x03;
+ }
+ void setHours(uint16_t hours) {
+ Hours_high = (hours >> 2) & 0x07;
+ Hours_low = hours & 0x03;
+ }
+} PACKED;
+
+
+struct FIGtype0_17_programme {
+ uint16_t SId;
+ uint8_t NFC:2;
+ uint8_t Rfa:2;
+ uint8_t CC:1;
+ uint8_t L:1;
+ uint8_t PS:1;
+ uint8_t SD:1;
+} PACKED;
+
+
+struct FIGtype0_1 {
+ uint8_t Length:5;
+ uint8_t FIGtypeNumber:3;
+ uint8_t Extension:5;
+ uint8_t PD:1;
+ uint8_t OE:1;
+ uint8_t CN:1;
+} PACKED;
+
+
+struct FIG_01_SubChannel_ShortF {
+ uint8_t StartAdress_high:2;
+ uint8_t SubChId:6;
+ uint8_t StartAdress_low:8;
+ uint8_t TableIndex:6;
+ uint8_t TableSwitch:1;
+ uint8_t Short_Long_form:1;
+} PACKED;
+
+
+struct FIG_01_SubChannel_LongF {
+ uint8_t StartAdress_high:2;
+ uint8_t SubChId:6;
+ uint8_t StartAdress_low:8;
+ uint8_t Sub_ChannelSize_high:2;
+ uint8_t ProtectionLevel:2;
+ uint8_t Option:3;
+ uint8_t Short_Long_form:1;
+ uint8_t Sub_ChannelSize_low:8;
+} PACKED;
+
+
+// See EN 300 401, Clause 8.1.20 for the FIG0_13 description
+struct FIG0_13_shortAppInfo {
+ uint16_t SId;
+ uint8_t No:4;
+ uint8_t SCIdS:4;
+} PACKED;
+
+
+struct FIG0_13_longAppInfo {
+ uint32_t SId;
+ uint8_t No:4;
+ uint8_t SCIdS:4;
+} PACKED;
+
+
+struct FIG0_13_app {
+ uint8_t typeHigh;
+ uint8_t length:5;
+ uint8_t typeLow:3;
+ void setType(uint16_t type) {
+ typeHigh = type >> 3;
+ typeLow = type & 0x1f;
+ }
+ uint32_t xpad;
+} PACKED;
+
+#define FIG0_13_APPTYPE_SLIDESHOW 0x2
+#define FIG0_13_APPTYPE_WEBSITE 0x3
+#define FIG0_13_APPTYPE_TPEG 0x4
+#define FIG0_13_APPTYPE_DGPS 0x5
+#define FIG0_13_APPTYPE_TMC 0x6
+#define FIG0_13_APPTYPE_EPG 0x7
+#define FIG0_13_APPTYPE_DABJAVA 0x8
+#define FIG0_13_APPTYPE_JOURNALINE 0x441
+
+
+struct FIGtype1_0 {
+ uint8_t Length:5;
+ uint8_t FIGtypeNumber:3;
+ uint8_t Extension:3;
+ uint8_t OE:1;
+ uint8_t Charset:4;
+
+ uint16_t EId;
+} PACKED;
+
+
+struct FIGtype1_1 {
+ uint8_t Length:5;
+ uint8_t FIGtypeNumber:3;
+ uint8_t Extension:3;
+ uint8_t OE:1;
+ uint8_t Charset:4;
+
+ uint16_t Sld;
+} PACKED;
+
+
+struct FIGtype1_5 {
+ uint8_t Length:5;
+ uint8_t FIGtypeNumber:3;
+ uint8_t Extension:3;
+ uint8_t OE:1;
+ uint8_t Charset:4;
+ uint32_t SId;
+} PACKED;
+
+
+struct FIGtype1_4_programme {
+ uint8_t Length:5;
+ uint8_t FIGtypeNumber:3;
+ uint8_t Extension:3;
+ uint8_t OE:1;
+ uint8_t Charset:4;
+ uint8_t SCIdS:4;
+ uint8_t rfa:3;
+ uint8_t PD:1;
+ uint16_t SId;
+} PACKED;
+
+
+struct FIGtype1_4_data {
+ uint8_t Length:5;
+ uint8_t FIGtypeNumber:3;
+ uint8_t Extension:3;
+ uint8_t OE:1;
+ uint8_t Charset:4;
+ uint8_t SCIdS:4;
+ uint8_t rfa:3;
+ uint8_t PD:1;
+ uint32_t SId;
+} PACKED;
+
+
+#ifdef _WIN32
+# pragma pack(pop)
+#endif
+
+#endif
+
diff --git a/src/DabMux.cpp b/src/DabMux.cpp
index 107431b..9fdc560 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;
@@ -257,105 +213,13 @@ int main(int argc, char *argv[])
}
#endif
- 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
@@ -369,8 +233,6 @@ int main(int argc, char *argv[])
try {
read_info(conf_file, pt);
- parse_ptree(pt, outputs, ensemble, &enableTist, &FICL,
- &factumAnalyzer, &limit, &rc, &mgmtserverport, &edi_conf);
}
catch (runtime_error &e) {
throw MuxInitException(e.what());
@@ -387,9 +249,6 @@ 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) {
throw MuxInitException(e.what());
@@ -408,17 +267,27 @@ int main(int argc, char *argv[])
}
#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 << " " <<
@@ -429,271 +298,126 @@ 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();
- }
+ /******************** 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 ENABLE_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->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->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");
- // 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->dump = pt_edi.get<bool>("dump");
+ edi->enable_pft = pt_edi.get<bool>("enable_pft");
+ edi->verbose = pt_edi.get<bool>("verbose");
+#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 ENABLE_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;
@@ -702,21 +426,6 @@ int main(int argc, char *argv[])
}
- /* 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");
}
@@ -740,1450 +449,44 @@ int main(int argc, char *argv[])
// The AF Packet will be protected with reed-solomon and split in fragments
PFT edi_pft(207, 3, edi_conf);
+#endif
+ ssize_t limit = pt.get("general.nbframes", 0);
+
+ etiLog.level(info) << "Start loop";
/* Each iteration of the main loop creates one ETI frame */
+ ssize_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
-
- //****** 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;
- }
- }
- 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;
- case Fidc:
- 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;
- case Fidc:
- 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;
-
- ensemble->label.writeLabel(&etiFrame[index]);
- index = index + 16;
-
- etiFrame[index++] = ensemble->label.flag() >> 8;
- etiFrame[index++] = ensemble->label.flag() & 0xFF;
-
- figSize += 22;
- break;
-
- case 5:
- case 6:
- // FIG 0 / 13
- fig0 = NULL;
-
- if (componentFIG0_13 == ensemble->components.end()) {
- componentFIG0_13 = ensemble->components.begin();
-
- transmitFIG0_13programme = !transmitFIG0_13programme;
- // Alternate between data and and programme FIG0/13,
- // do not mix fig0 with PD=0 with extension 13 stuff
- // that actually needs PD=1, and vice versa
- }
-
- for (; componentFIG0_13 != ensemble->components.end();
- ++componentFIG0_13) {
-
- subchannel = getSubchannel(ensemble->subchannels,
- (*componentFIG0_13)->subchId);
-
- if (subchannel == ensemble->subchannels.end()) {
- etiLog.log(error,
- "Subchannel %i does not exist for component "
- "of service %i\n",
- (*componentFIG0_13)->subchId,
- (*componentFIG0_13)->serviceId);
- 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;
+ mux.mux_frame(outputs);
+ if (limit && currentFrame >= limit) {
break;
}
- assert(figSize <= 30);
- memcpy(&etiFrame[index], Padding_FIB, 30 - figSize);
- index += 30 - figSize;
-
- CRCtmp = 0xffff;
- CRCtmp = crc16(CRCtmp, &etiFrame[(index - 30)], 30);
- CRCtmp ^= 0xffff;
- etiFrame[index++] = ((char *) &CRCtmp)[1];
- etiFrame[index++] = ((char *) &CRCtmp)[0];
-
-
- figSize = 0;
- // FIB 2 insertion
- if (rotateFIB < ensemble->services.size()) {
- service = ensemble->services.begin() + rotateFIB;
-
- // FIG type 1/1, SI, Service label, one instance per subchannel
- if ((*service)->getType(ensemble) == 0) {
- fig1_1 = (FIGtype1_1 *) & etiFrame[index];
-
- fig1_1->FIGtypeNumber = 1;
- fig1_1->Length = 21;
- fig1_1->Charset = 0;
- fig1_1->OE = 0;
- fig1_1->Extension = 1;
-
- fig1_1->Sld = htons((*service)->id);
- index += 4;
- figSize += 4;
- }
- else {
- fig1_5 = (FIGtype1_5 *) & etiFrame[index];
- fig1_5->FIGtypeNumber = 1;
- fig1_5->Length = 23;
- fig1_5->Charset = 0;
- fig1_5->OE = 0;
- fig1_5->Extension = 5;
-
- fig1_5->SId = htonl((*service)->id);
- index += 6;
- figSize += 6;
- }
- (*service)->label.writeLabel(&etiFrame[index]);
- index += 16;
- figSize += 16;
- etiFrame[index++] = (*service)->label.flag() >> 8;
- etiFrame[index++] = (*service)->label.flag() & 0xFF;
- figSize += 2;
- }
- else if (rotateFIB <
- ensemble->services.size() + ensemble->components.size()) {
- component = ensemble->components.begin() +
- (rotateFIB - ensemble->services.size());
-
- service = getService(*component, ensemble->services);
-
- subchannel =
- getSubchannel(ensemble->subchannels, (*component)->subchId);
-
- if (!((*component)->label.long_label().empty())) {
- if ((*service)->getType(ensemble) == 0) { // Programme
- FIGtype1_4_programme *fig1_4;
- fig1_4 = (FIGtype1_4_programme*)&etiFrame[index];
-
- fig1_4->FIGtypeNumber = 1;
- fig1_4->Length = 22;
- fig1_4->Charset = 0;
- fig1_4->OE = 0;
- fig1_4->Extension = 4;
- fig1_4->PD = 0;
- fig1_4->rfa = 0;
- fig1_4->SCIdS = (*component)->SCIdS;
-
- fig1_4->SId = htons((*service)->id);
- index += 5;
- figSize += 5;
- }
- else { // Data
- FIGtype1_4_data *fig1_4;
- fig1_4 = (FIGtype1_4_data *) & etiFrame[index];
- fig1_4->FIGtypeNumber = 1;
- fig1_4->Length = 24;
- fig1_4->Charset = 0;
- fig1_4->OE = 0;
- fig1_4->Extension = 4;
- fig1_4->PD = 1;
- fig1_4->rfa = 0;
- fig1_4->SCIdS = (*component)->SCIdS;
-
- fig1_4->SId = htonl((*service)->id);
- index += 7;
- figSize += 7;
- }
- (*component)->label.writeLabel(&etiFrame[index]);
- index += 16;
- figSize += 16;
-
- etiFrame[index++] = (*component)->label.flag() >> 8;
- etiFrame[index++] = (*component)->label.flag() & 0xFF;
- figSize += 2;
- }
- }
- memcpy(&etiFrame[index], Padding_FIB, 30 - figSize);
- index += 30 - figSize;
-
- CRCtmp = 0xffff;
- CRCtmp = crc16(CRCtmp, &etiFrame[(index - 30)], 30);
- CRCtmp ^= 0xffff;
- etiFrame[index++] = ((char *) &CRCtmp)[1];
- etiFrame[index++] = ((char *) &CRCtmp)[0];
-
- /* ETSI EN 300 799 Table 2:
- * Only TM3 has a FIB count to CIF count that is
- * not 3 to 1.
- */
- if (ensemble->mode == 3) {
- memcpy(&etiFrame[index], Padding_FIB, 30);
- index += 30;
-
- CRCtmp = 0xffff;
- CRCtmp = crc16(CRCtmp, &etiFrame[(index - 30)], 30);
- CRCtmp ^= 0xffff;
- etiFrame[index++] = ((char *) &CRCtmp)[1];
- etiFrame[index++] = ((char *) &CRCtmp)[0];
- }
-
- if (ensemble->services.size() > 30) {
- etiLog.log(error,
- "Sorry, but this software currently can't write "
- "Service Label of more than 30 services.\n");
- 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)) {
- }
- */
+ /* else if (mgmt_server.retrieve_new_ptree(pt)) {
+ }
+ */
}
}
+ etiLog.level(info) << "Goodbye";
}
catch (const MuxInitException& except) {
etiLog.level(error) << "Multiplex initialisation aborted: " <<
@@ -2198,30 +501,7 @@ int main(int argc, char *argv[])
etiLog.log(debug, "exiting...\n");
fflush(stderr);
- // close files fichiers
- for (subchannel = ensemble->subchannels.begin();
- subchannel != ensemble->subchannels.end();
- ++subchannel) {
- if ((*subchannel)->input != NULL) {
- (*subchannel)->input->close();
- }
- delete (*subchannel)->input;
- }
- for (output = outputs.begin() ; output != outputs.end(); ++output) {
- if ((*output)->output) {
- (*output)->output->Close();
- delete ((*output)->output);
- }
- }
- for_each(ensemble->components.begin(), ensemble->components.end(), free);
- for_each(ensemble->services.begin(), ensemble->services.end(), free);
- for_each(ensemble->subchannels.begin(), ensemble->subchannels.end(), free);
- for_each(outputs.begin(), outputs.end(), free);
- ensemble->components.clear();
- ensemble->services.clear();
- ensemble->subchannels.clear();
- delete ensemble;
outputs.clear();
UdpSocket::clean();
diff --git a/src/DabMux.h b/src/DabMux.h
index 89868ac..5dda759 100644
--- a/src/DabMux.h
+++ b/src/DabMux.h
@@ -31,6 +31,7 @@
#include <stdint.h>
#include <string>
#include <vector>
+#include "DabMultiplexer.h"
#include "RemoteControl.h"
#include "dabOutput/dabOutput.h"
#include "dabInput.h"
@@ -43,368 +44,5 @@
# include <sys/time.h>
#endif
-
-// DAB Mode
-#define DEFAULT_DAB_MODE 2
-
-// Taille de la trame de donnee, sous-canal 3, nb de paquets de 64bits,
-// STL3 * 8 = x kbytes par trame ETI
-
-// Data bitrate in kbits/s. Must be 64 kb/s multiple.
-#define DEFAULT_DATA_BITRATE 384
-#define DEFAULT_PACKET_BITRATE 32
-
-/* default ensemble parameters. Label must be max 16 chars, short label
- * a subset of the label, max 8 chars
- */
-#define DEFAULT_ENSEMBLE_LABEL "ODR Dab Mux"
-#define DEFAULT_ENSEMBLE_SHORT_LABEL "ODRMux"
-#define DEFAULT_ENSEMBLE_ID 0xc000
-#define DEFAULT_ENSEMBLE_ECC 0xa1
-
-// start value for default service IDs (if not overridden by configuration)
-#define DEFAULT_SERVICE_ID 50
-#define DEFAULT_PACKET_ADDRESS 0
-
-/*****************************************************************************
- ***************** Definition of FIG structures ****************************
- *****************************************************************************/
-struct FIGtype0 {
- uint8_t Length:5;
- uint8_t FIGtypeNumber:3;
- uint8_t Extension:5;
- uint8_t PD:1;
- uint8_t OE:1;
- uint8_t CN:1;
-} PACKED;
-
-
-struct FIGtype0_0 {
- uint8_t Length:5;
- uint8_t FIGtypeNumber:3;
- uint8_t Extension:5;
- uint8_t PD:1;
- uint8_t OE:1;
- uint8_t CN:1;
-
- uint16_t EId;
- uint8_t CIFcnt_hight:5;
- uint8_t Al:1;
- uint8_t Change:2;
- uint8_t CIFcnt_low:8;
-} PACKED;
-
-
-struct FIGtype0_2 {
- uint8_t Length:5;
- uint8_t FIGtypeNumber:3;
- uint8_t Extension:5;
- uint8_t PD:1;
- uint8_t OE:1;
- uint8_t CN:1;
-} PACKED;
-
-
-struct FIGtype0_2_Service {
- uint16_t SId;
- uint8_t NbServiceComp:4;
- uint8_t CAId:3;
- uint8_t Local_flag:1;
-} PACKED;
-
-
-struct FIGtype0_2_Service_data {
- uint32_t SId;
- uint8_t NbServiceComp:4;
- uint8_t CAId:3;
- uint8_t Local_flag:1;
-} PACKED;
-
-
-struct FIGtype0_2_audio_component {
- uint8_t ASCTy:6;
- uint8_t TMid:2;
- uint8_t CA_flag:1;
- uint8_t PS:1;
- uint8_t SubChId:6;
-} PACKED;
-
-
-struct FIGtype0_2_data_component {
- uint8_t DSCTy:6;
- uint8_t TMid:2;
- uint8_t CA_flag:1;
- uint8_t PS:1;
- uint8_t SubChId:6;
-} PACKED;
-
-
-struct FIGtype0_2_packet_component {
- uint8_t SCId_high:6;
- uint8_t TMid:2;
- uint8_t CA_flag:1;
- uint8_t PS:1;
- uint8_t SCId_low:6;
- void setSCId(uint16_t SCId) {
- SCId_high = SCId >> 6;
- SCId_low = SCId & 0x3f;
- }
-} PACKED;
-
-
-struct FIGtype0_3_header {
- uint8_t Length:5;
- uint8_t FIGtypeNumber:3;
- uint8_t Extension:5;
- uint8_t PD:1;
- uint8_t OE:1;
- uint8_t CN:1;
-} PACKED;
-
-
-/* Warning: When bit SCCA_flag is unset(0), the multiplexer R&S does not send
- * the SCCA field. But, in the Factum ETI analyzer, if this field is not there,
- * it is an error.
- */
-struct FIGtype0_3_data {
- uint8_t SCId_high;
- uint8_t SCCA_flag:1;
- uint8_t rfa:3;
- uint8_t SCId_low:4;
- uint8_t DSCTy:6;
- uint8_t rfu:1;
- uint8_t DG_flag:1;
- uint8_t Packet_address_high:2;
- uint8_t SubChId:6;
- uint8_t Packet_address_low;
- uint16_t SCCA;
- void setSCId(uint16_t SCId) {
- SCId_high = SCId >> 4;
- SCId_low = SCId & 0xf;
- }
- void setPacketAddress(uint16_t address) {
- Packet_address_high = address >> 8;
- Packet_address_low = address & 0xff;
- }
-} PACKED;
-
-
-struct FIGtype0_8_short {
- uint8_t SCIdS:4;
- uint8_t rfa_1:3;
- uint8_t ext:1;
- uint8_t Id:6;
- uint8_t MscFic:1;
- uint8_t LS:1;
- uint8_t rfa_2;
-} PACKED;
-
-
-struct FIGtype0_8_long {
- uint8_t SCIdS:4;
- uint8_t rfa_1:3;
- uint8_t ext:1;
- uint8_t SCId_high:4;
- uint8_t rfa:3;
- uint8_t LS:1;
- uint8_t SCId_low;
- uint8_t rfa_2;
- void setSCId(uint16_t id) {
- SCId_high = id >> 8;
- SCId_low = id & 0xff;
- }
- uint16_t getSCid() {
- return (SCId_high << 8) | SCId_low;
- }
-} PACKED;
-
-
-struct FIGtype0_9 {
- uint8_t Length:5;
- uint8_t FIGtypeNumber:3;
- uint8_t Extension:5;
- uint8_t PD:1;
- uint8_t OE:1;
- uint8_t CN:1;
-
- uint8_t ensembleLto:6;
- uint8_t lto:1;
- uint8_t ext:1;
- uint8_t ensembleEcc;
- uint8_t tableId;
-} PACKED;
-
-
-struct FIGtype0_10 {
- uint8_t Length:5;
- uint8_t FIGtypeNumber:3;
- uint8_t Extension:5;
- uint8_t PD:1;
- uint8_t OE:1;
- uint8_t CN:1;
-
- uint8_t MJD_high:7;
- uint8_t RFU:1;
- uint8_t MJD_med;
- uint8_t Hours_high:3;
- uint8_t UTC:1;
- uint8_t ConfInd:1;
- uint8_t LSI:1;
- uint8_t MJD_low:2;
- uint8_t Minutes:6;
- uint8_t Hours_low:2;
- void setMJD(uint32_t date) {
- MJD_high = (date >> 10) & 0x7f;
- MJD_med = (date >> 2) & 0xff;
- MJD_low = date & 0x03;
- }
- void setHours(uint16_t hours) {
- Hours_high = (hours >> 2) & 0x07;
- Hours_low = hours & 0x03;
- }
-} PACKED;
-
-
-struct FIGtype0_17_programme {
- uint16_t SId;
- uint8_t NFC:2;
- uint8_t Rfa:2;
- uint8_t CC:1;
- uint8_t L:1;
- uint8_t PS:1;
- uint8_t SD:1;
-} PACKED;
-
-
-struct FIGtype0_1 {
- uint8_t Length:5;
- uint8_t FIGtypeNumber:3;
- uint8_t Extension:5;
- uint8_t PD:1;
- uint8_t OE:1;
- uint8_t CN:1;
-} PACKED;
-
-
-struct FIG_01_SubChannel_ShortF {
- uint8_t StartAdress_high:2;
- uint8_t SubChId:6;
- uint8_t StartAdress_low:8;
- uint8_t TableIndex:6;
- uint8_t TableSwitch:1;
- uint8_t Short_Long_form:1;
-} PACKED;
-
-
-struct FIG_01_SubChannel_LongF {
- uint8_t StartAdress_high:2;
- uint8_t SubChId:6;
- uint8_t StartAdress_low:8;
- uint8_t Sub_ChannelSize_high:2;
- uint8_t ProtectionLevel:2;
- uint8_t Option:3;
- uint8_t Short_Long_form:1;
- uint8_t Sub_ChannelSize_low:8;
-} PACKED;
-
-
-// See EN 300 401, Clause 8.1.20 for the FIG0_13 description
-struct FIG0_13_shortAppInfo {
- uint16_t SId;
- uint8_t No:4;
- uint8_t SCIdS:4;
-} PACKED;
-
-
-struct FIG0_13_longAppInfo {
- uint32_t SId;
- uint8_t No:4;
- uint8_t SCIdS:4;
-} PACKED;
-
-
-struct FIG0_13_app {
- uint8_t typeHigh;
- uint8_t length:5;
- uint8_t typeLow:3;
- void setType(uint16_t type) {
- typeHigh = type >> 3;
- typeLow = type & 0x1f;
- }
- uint32_t xpad;
-} PACKED;
-
-#define FIG0_13_APPTYPE_SLIDESHOW 0x2
-#define FIG0_13_APPTYPE_WEBSITE 0x3
-#define FIG0_13_APPTYPE_TPEG 0x4
-#define FIG0_13_APPTYPE_DGPS 0x5
-#define FIG0_13_APPTYPE_TMC 0x6
-#define FIG0_13_APPTYPE_EPG 0x7
-#define FIG0_13_APPTYPE_DABJAVA 0x8
-#define FIG0_13_APPTYPE_JOURNALINE 0x441
-
-
-struct FIGtype1_0 {
- uint8_t Length:5;
- uint8_t FIGtypeNumber:3;
- uint8_t Extension:3;
- uint8_t OE:1;
- uint8_t Charset:4;
-
- uint16_t EId;
-} PACKED;
-
-
-struct FIGtype1_1 {
- uint8_t Length:5;
- uint8_t FIGtypeNumber:3;
- uint8_t Extension:3;
- uint8_t OE:1;
- uint8_t Charset:4;
-
- uint16_t Sld;
-} PACKED;
-
-
-struct FIGtype1_5 {
- uint8_t Length:5;
- uint8_t FIGtypeNumber:3;
- uint8_t Extension:3;
- uint8_t OE:1;
- uint8_t Charset:4;
- uint32_t SId;
-} PACKED;
-
-
-struct FIGtype1_4_programme {
- uint8_t Length:5;
- uint8_t FIGtypeNumber:3;
- uint8_t Extension:3;
- uint8_t OE:1;
- uint8_t Charset:4;
- uint8_t SCIdS:4;
- uint8_t rfa:3;
- uint8_t PD:1;
- uint16_t SId;
-} PACKED;
-
-
-struct FIGtype1_4_data {
- uint8_t Length:5;
- uint8_t FIGtypeNumber:3;
- uint8_t Extension:3;
- uint8_t OE:1;
- uint8_t Charset:4;
- uint8_t SCIdS:4;
- uint8_t rfa:3;
- uint8_t PD:1;
- uint32_t SId;
-} PACKED;
-
-
-#ifdef _WIN32
-# pragma pack(pop)
-#endif
-
#endif
diff --git a/src/Makefile.am b/src/Makefile.am
index 6ed0921..9a774c7 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -39,17 +39,11 @@ bin_PROGRAMS=odr-dabmux odr-bridgetest
ZMQ_LIBS =
endif
-
-odr_dabmux_CPPFLAGS =-I$(FARSYNC_DIR) $(GITVERSION_FLAGS) \
- -Wall -Wextra -Wno-unused-parameter \
- -Wswitch-enum \
- -Wdisabled-optimization -Wvla \
- -Wuninitialized \
- -Wc++11-compat \
- -Wcast-align -Wcast-qual
-
+odr_dabmux_CFLAGS =-Wall -std=c11 -I$(FARSYNC_DIR) $(GITVERSION_FLAGS)
+odr_dabmux_CXXFLAGS =-Wall -std=c++11 -I$(FARSYNC_DIR) $(GITVERSION_FLAGS)
odr_dabmux_LDADD =$(FEC_LIBS) $(ZMQ_LIBS) -lpthread -lboost_thread -lboost_system
odr_dabmux_SOURCES =DabMux.cpp DabMux.h \
+ DabMultiplexer.cpp DabMultiplexer.h \
dabInput.h dabInput.cpp \
dabInputBridgeUdp.h dabInputBridgeUdp.cpp \
dabInputDabplusFifo.h dabInputDabplusFifo.cpp \
diff --git a/src/ManagementServer.cpp b/src/ManagementServer.cpp
index 78f1c9d..0aa9ed8 100644
--- a/src/ManagementServer.cpp
+++ b/src/ManagementServer.cpp
@@ -39,6 +39,18 @@
#include "ManagementServer.h"
#include "Log.h"
+ManagementServer& get_mgmt_server()
+{
+ static ManagementServer mgmt_server;
+
+ return mgmt_server;
+
+ /* Warning, do not use the mgmt_server in the destructor
+ * of another global object: you don't know which one
+ * gets destroyed first
+ */
+}
+
void ManagementServer::registerInput(InputStat* is)
{
boost::mutex::scoped_lock lock(m_statsmutex);
@@ -71,7 +83,7 @@ bool ManagementServer::isInputRegistered(std::string& id)
if (m_inputStats.count(id) == 0) {
etiLog.level(error) <<
- "Stats Server id '" <<
+ "Management Server: id '" <<
id << "' does was not registered";
return false;
}
@@ -253,7 +265,7 @@ void ManagementServer::serverThread()
n = write(accepted_sock, welcome_msg, welcome_msg_len);
if (n < 0) {
etiLog.level(warn) <<
- "MGMT: Error writing to Stats Server socket " <<
+ "MGMT: Error writing to Server socket " <<
strerror(errno);
close(accepted_sock);
continue;
@@ -264,7 +276,7 @@ void ManagementServer::serverThread()
int n = read(accepted_sock, buffer, 255);
if (n < 0) {
etiLog.level(warn) <<
- "MGMT: Error reading from Stats Server socket " <<
+ "MGMT: Error reading from Server socket " <<
strerror(errno);
close(accepted_sock);
continue;
@@ -383,12 +395,12 @@ void ManagementServer::update_ptree(const boost::property_tree::ptree& pt)
void InputStat::registerAtServer()
{
- mgmt_server->registerInput(this);
+ get_mgmt_server().registerInput(this);
}
InputStat::~InputStat()
{
- mgmt_server->unregisterInput(m_name);
+ get_mgmt_server().unregisterInput(m_name);
}
std::string InputStat::encodeValuesJSON()
diff --git a/src/ManagementServer.h b/src/ManagementServer.h
index 273f576..c71f6d2 100644
--- a/src/ManagementServer.h
+++ b/src/ManagementServer.h
@@ -317,21 +317,9 @@ class ManagementServer
{
public:
ManagementServer() :
- m_listenport(0),
m_running(false),
m_fault(false),
- m_pending(false)
- { }
-
- ManagementServer(int listen_port) :
- m_listenport(listen_port),
- m_running(false),
- m_fault(false),
- m_thread(&ManagementServer::serverThread, this),
- m_pending(false)
- {
- m_sock = 0;
- }
+ m_pending(false) { }
~ManagementServer()
{
@@ -340,10 +328,20 @@ class ManagementServer
m_pending = false;
if (m_sock) {
close(m_sock);
+ m_thread.interrupt();
}
m_thread.join();
}
+ void open(int listenport)
+ {
+ m_listenport = listenport;
+ if (m_listenport > 0) {
+ m_sock = 0;
+ m_thread = boost::thread(&ManagementServer::serverThread, this);
+ }
+ }
+
/* Un-/Register a statistics data source */
void registerInput(InputStat* is);
void unregisterInput(std::string id);
@@ -420,7 +418,9 @@ class ManagementServer
boost::property_tree::ptree m_pt;
};
-extern ManagementServer* mgmt_server;
+// If necessary construct the management server singleton and return
+// a reference to it
+ManagementServer& get_mgmt_server();
#endif
diff --git a/src/MuxElements.cpp b/src/MuxElements.cpp
index a452220..0a52f0b 100644
--- a/src/MuxElements.cpp
+++ b/src/MuxElements.cpp
@@ -288,7 +288,7 @@ const string DabComponent::get_parameter(const string& parameter) const
}
-unsigned char DabService::getType(dabEnsemble* ensemble)
+unsigned char DabService::getType(boost::shared_ptr<dabEnsemble> ensemble)
{
vector<dabSubchannel*>::iterator subchannel;
vector<DabComponent*>::iterator component =
diff --git a/src/MuxElements.h b/src/MuxElements.h
index a116427..f4def85 100644
--- a/src/MuxElements.h
+++ b/src/MuxElements.h
@@ -281,7 +281,7 @@ class DabService : public RemoteControllable
unsigned char language;
bool program;
- unsigned char getType(dabEnsemble* ensemble);
+ unsigned char getType(boost::shared_ptr<dabEnsemble> ensemble);
unsigned char nbComponent(std::vector<DabComponent*>& components);
DabLabel label;
diff --git a/src/RemoteControl.cpp b/src/RemoteControl.cpp
index e46bc8d..723ba9b 100644
--- a/src/RemoteControl.cpp
+++ b/src/RemoteControl.cpp
@@ -27,6 +27,7 @@
#include <iostream>
#include <string>
#include <boost/asio.hpp>
+#include <boost/bind.hpp>
#include <boost/thread.hpp>
#include "Log.h"
@@ -35,6 +36,12 @@
using boost::asio::ip::tcp;
using namespace std;
+RemoteControllerTelnet::~RemoteControllerTelnet()
+{
+ m_running = false;
+ m_io_service.stop();
+ m_child_thread.join();
+}
void RemoteControllerTelnet::restart()
{
@@ -47,85 +54,120 @@ void RemoteControllerTelnet::restart()
// thread.
void RemoteControllerTelnet::restart_thread(long)
{
+ etiLog.level(warn) << "RC: Restart Telnet server";
+
m_running = false;
+ m_io_service.stop();
- if (m_port) {
- m_child_thread.interrupt();
- m_child_thread.join();
- }
+ m_child_thread.join();
m_child_thread = boost::thread(&RemoteControllerTelnet::process, this, 0);
}
-void RemoteControllerTelnet::process(long)
+void RemoteControllerTelnet::handle_accept(
+ const boost::system::error_code& boost_error,
+ boost::shared_ptr< boost::asio::ip::tcp::socket > socket,
+ boost::asio::ip::tcp::acceptor& acceptor)
{
- m_welcome = "ODR-DabMux Remote Control CLI\nWrite 'help' for help.\n**********\n";
- m_prompt = "> ";
+
+ const std::string welcome = "ODR-DabMux Remote Control CLI\n"
+ "Write 'help' for help.\n"
+ "**********\n";
+ const std::string prompt = "> ";
std::string in_message;
size_t length;
- try {
- boost::asio::io_service io_service;
- tcp::acceptor acceptor(io_service, tcp::endpoint(
- boost::asio::ip::address::from_string("127.0.0.1"), m_port) );
-
- while (m_running) {
- in_message = "";
+ if (boost_error)
+ {
+ etiLog.level(error) << "RC: Error accepting connection";
+ return;
+ }
- tcp::socket socket(io_service);
+ try {
+ etiLog.level(info) << "RC: Accepted";
- acceptor.accept(socket);
+ boost::system::error_code ignored_error;
- boost::system::error_code ignored_error;
+ boost::asio::write(*socket, boost::asio::buffer(welcome),
+ boost::asio::transfer_all(),
+ ignored_error);
- boost::asio::write(socket, boost::asio::buffer(m_welcome),
+ while (m_running && in_message != "quit") {
+ boost::asio::write(*socket, boost::asio::buffer(prompt),
boost::asio::transfer_all(),
ignored_error);
- while (m_running && in_message != "quit") {
- boost::asio::write(socket, boost::asio::buffer(m_prompt),
- boost::asio::transfer_all(),
- ignored_error);
-
- in_message = "";
+ in_message = "";
- boost::asio::streambuf buffer;
- length = boost::asio::read_until( socket, buffer, "\n", ignored_error);
+ boost::asio::streambuf buffer;
+ length = boost::asio::read_until(*socket, buffer, "\n", ignored_error);
- std::istream str(&buffer);
- std::getline(str, in_message);
+ std::istream str(&buffer);
+ std::getline(str, in_message);
- if (length == 0) {
- etiLog.level(info) << "RC: Connection terminated";
- break;
- }
+ if (length == 0) {
+ etiLog.level(info) << "RC: Connection terminated";
+ break;
+ }
- while (in_message.length() > 0 &&
- (in_message[in_message.length()-1] == '\r' ||
- in_message[in_message.length()-1] == '\n')) {
- in_message.erase(in_message.length()-1, 1);
- }
+ while (in_message.length() > 0 &&
+ (in_message[in_message.length()-1] == '\r' ||
+ in_message[in_message.length()-1] == '\n')) {
+ in_message.erase(in_message.length()-1, 1);
+ }
- if (in_message.length() == 0) {
- continue;
- }
+ if (in_message.length() == 0) {
+ continue;
+ }
- etiLog.level(info) << "RC: Got message '" << in_message << "'";
+ etiLog.level(info) << "RC: Got message '" << in_message << "'";
- dispatch_command(socket, in_message);
- }
- etiLog.level(info) << "RC: Closing socket";
- socket.close();
+ dispatch_command(*socket, in_message);
}
+ etiLog.level(info) << "RC: Closing socket";
+ socket->close();
}
catch (std::exception& e)
{
etiLog.level(error) << "Remote control caught exception: " << e.what();
- m_fault = true;
}
}
+void RemoteControllerTelnet::process(long)
+{
+ m_running = true;
+
+ while (m_running) {
+ m_io_service.reset();
+
+ tcp::acceptor acceptor(m_io_service, tcp::endpoint(
+ boost::asio::ip::address::from_string("127.0.0.1"), m_port) );
+
+
+ // Add a job to start accepting connections.
+ boost::shared_ptr<tcp::socket> socket(
+ new tcp::socket(acceptor.get_io_service()));
+
+ // Add an accept call to the service. This will prevent io_service::run()
+ // from returning.
+ etiLog.level(warn) << "RC: Waiting on connection";
+ acceptor.async_accept(*socket,
+ boost::bind(&RemoteControllerTelnet::handle_accept,
+ this,
+ boost::asio::placeholders::error,
+ socket,
+ boost::ref(acceptor)));
+
+ // Process event loop.
+ m_io_service.run();
+ }
+
+ etiLog.level(warn) << "RC: Leaving";
+ m_fault = true;
+}
+
+
void RemoteControllerTelnet::dispatch_command(tcp::socket& socket, string command)
{
vector<string> cmd = tokenise_(command);
diff --git a/src/RemoteControl.h b/src/RemoteControl.h
index 16881b4..46a828f 100644
--- a/src/RemoteControl.h
+++ b/src/RemoteControl.h
@@ -32,6 +32,7 @@
#include <list>
#include <map>
#include <string>
+#include <atomic>
#include <iostream>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
@@ -104,6 +105,10 @@ class RemoteControllable {
controller.enrol(this);
}
+ virtual void enrol_at(boost::shared_ptr<BaseRemoteController> controller) {
+ controller->enrol(this);
+ }
+
/* Return a list of possible parameters that can be set */
virtual std::list<std::string> get_supported_parameters() const {
std::list<std::string> parameterlist;
@@ -137,25 +142,23 @@ class RemoteControllable {
*/
class RemoteControllerTelnet : public BaseRemoteController {
public:
- RemoteControllerTelnet()
- : m_running(false), m_fault(false),
+ RemoteControllerTelnet() :
+ m_running(false),
+ m_io_service(),
+ m_fault(false),
m_port(0) { }
- RemoteControllerTelnet(int port)
- : m_running(true), m_fault(false),
- m_child_thread(&RemoteControllerTelnet::process, this, 0),
+ RemoteControllerTelnet(int port) :
+ m_running(false),
+ m_io_service(),
+ m_fault(false),
m_port(port)
- { }
-
- ~RemoteControllerTelnet() {
- m_running = false;
- m_fault = false;
- if (m_port) {
- m_child_thread.interrupt();
- m_child_thread.join();
- }
+ {
+ restart();
}
+ ~RemoteControllerTelnet();
+
void enrol(RemoteControllable* controllable) {
m_cohort.push_back(controllable);
}
@@ -174,6 +177,11 @@ class RemoteControllerTelnet : public BaseRemoteController {
void reply(boost::asio::ip::tcp::socket& socket, std::string message);
+ void handle_accept(
+ const boost::system::error_code& boost_error,
+ boost::shared_ptr< boost::asio::ip::tcp::socket > socket,
+ boost::asio::ip::tcp::acceptor& acceptor);
+
RemoteControllerTelnet& operator=(const RemoteControllerTelnet& other);
RemoteControllerTelnet(const RemoteControllerTelnet& other);
@@ -237,10 +245,12 @@ class RemoteControllerTelnet : public BaseRemoteController {
return controllable->set_parameter(param, value);
}
- bool m_running;
+ std::atomic<bool> m_running;
+
+ boost::asio::io_service m_io_service;
/* This is set to true if a fault occurred */
- bool m_fault;
+ std::atomic<bool> m_fault;
boost::thread m_restarter_thread;
boost::thread m_child_thread;
@@ -248,9 +258,6 @@ class RemoteControllerTelnet : public BaseRemoteController {
/* This controller commands the controllables in the cohort */
std::list<RemoteControllable*> m_cohort;
- std::string m_welcome;
- std::string m_prompt;
-
int m_port;
};
diff --git a/src/dabOutput/dabOutput.h b/src/dabOutput/dabOutput.h
index 18f3848..f6980fe 100644
--- a/src/dabOutput/dabOutput.h
+++ b/src/dabOutput/dabOutput.h
@@ -78,6 +78,8 @@ class DabOutput
virtual int Close() = 0;
virtual ~DabOutput() {}
+
+ virtual std::string get_info() = 0;
};
// ----- used in File and Fifo outputs
@@ -111,7 +113,12 @@ class DabOutputFile : public DabOutput
int Write(void* buffer, int size);
int Close();
+ std::string get_info() {
+ return "file://" + filename_;
+ }
+
protected:
+ std::string filename_;
int file_;
EtiFileType type_;
unsigned long nbFrames_;
@@ -126,6 +133,11 @@ class DabOutputFifo : public DabOutputFile
~DabOutputFifo() {}
int Write(void* buffer, int size);
+
+ std::string get_info() {
+ return "fifo://" + filename_;
+ }
+
};
// -------------- RAW socket -----------
@@ -162,7 +174,12 @@ class DabOutputRaw : public DabOutput
int Open(const char* name);
int Write(void* buffer, int size);
int Close();
+
+ std::string get_info() {
+ return "raw://" + filename_;
+ }
private:
+ std::string filename_;
#ifdef _WIN32
HANDLE socket_;
#else
@@ -197,7 +214,11 @@ class DabOutputUdp : public DabOutput
int Write(void* buffer, int size);
int Close() { return 0; }
+ std::string get_info() {
+ return "udp://" + uri_;
+ }
private:
+ std::string uri_;
UdpSocket* socket_;
UdpPacket* packet_;
};
@@ -230,9 +251,14 @@ class DabOutputTcp : public DabOutput
int Write(void* buffer, int size);
int Close();
+ std::string get_info() {
+ return "tcp://" + uri_;
+ }
+
TcpServer* server;
TcpSocket* client;
private:
+ std::string uri_;
pthread_t thread_;
};
@@ -252,7 +278,12 @@ class DabOutputSimul : public DabOutput
int Open(const char* name);
int Write(void* buffer, int size);
int Close() { return 0; }
+
+ std::string get_info() {
+ return "simul://" + name_;
+ }
private:
+ std::string name_;
#ifdef _WIN32
DWORD startTime_;
#else
@@ -304,6 +335,7 @@ class DabOutputZMQ : public DabOutput
{
public:
DabOutputZMQ() :
+ endpoint_(""),
zmq_proto_(""), zmq_context_(1),
zmq_pub_sock_(zmq_context_, ZMQ_PUB),
zmq_message_ix(0)
@@ -312,6 +344,7 @@ class DabOutputZMQ : public DabOutput
}
DabOutputZMQ(std::string zmq_proto) :
+ endpoint_(""),
zmq_proto_(zmq_proto), zmq_context_(1),
zmq_pub_sock_(zmq_context_, ZMQ_PUB),
zmq_message_ix(0)
@@ -323,7 +356,11 @@ class DabOutputZMQ : public DabOutput
zmq_pub_sock_.close();
}
- int Open(const char* name);
+ std::string get_info() {
+ return "zmq: " + zmq_proto_ + "://" + endpoint_;
+ }
+
+ int Open(const char* endpoint);
int Write(void* buffer, int size);
int Close();
private:
@@ -334,6 +371,7 @@ class DabOutputZMQ : public DabOutput
/* Forbid copy constructor */
}
+ std::string endpoint_;
std::string zmq_proto_;
zmq::context_t zmq_context_; // handle for the zmq context
zmq::socket_t zmq_pub_sock_; // handle for the zmq publisher socket
diff --git a/src/utils.cpp b/src/utils.cpp
index e360d93..50591ae 100644
--- a/src/utils.cpp
+++ b/src/utils.cpp
@@ -3,7 +3,7 @@
2011, 2012 Her Majesty the Queen in Right of Canada (Communications
Research Center Canada)
- Copyright (C) 2013, 2014 Matthias P. Braendli
+ Copyright (C) 2013, 2014, 2015 Matthias P. Braendli
http://mpb.li
*/
/*
@@ -24,6 +24,7 @@
*/
#include <cstring>
#include <iostream>
+#include <boost/shared_ptr.hpp>
#include "DabMux.h"
#include "utils.h"
@@ -73,7 +74,7 @@ void header_message()
fprintf(stderr,
"(Communications Research Centre Canada) All rights reserved.\n\n");
fprintf(stderr,
- "Copyright (C) 2013, 2014 Matthias P. Braendli\n");
+ "Copyright (C) 2013, 2014, 2015 Matthias P. Braendli\n");
fprintf(stderr,
"http://opendigitalradio.org\n\n");
@@ -352,21 +353,14 @@ void printUsage(char *name, FILE* out)
}
#endif
-void printOutputs(vector<dabOutput*>& outputs)
+void printOutputs(vector<boost::shared_ptr<DabOutput> >& outputs)
{
- vector<dabOutput*>::const_iterator output;
int index = 0;
- for (output = outputs.begin(); output != outputs.end(); ++output) {
+ for (auto output : outputs) {
etiLog.log(info, "Output %i", index);
- etiLog.level(info) << " protocol: " <<
- (*output)->outputProto;
-
- etiLog.level(info) << " name: " <<
- (*output)->outputName.c_str();
- // Daboutputfile mangles with outputName, inserting \0 to
- // cut the string in several parts. That doesn't work
- // with stl strings. Thats why the .c_str()
+ etiLog.level(info) << " URI: " <<
+ output->get_info();
++index;
}
@@ -528,7 +522,7 @@ void printSubchannels(vector<dabSubchannel*>& subchannels)
}
}
-void printEnsemble(dabEnsemble* ensemble)
+void printEnsemble(const boost::shared_ptr<dabEnsemble> ensemble)
{
etiLog.log(info, "Ensemble");
etiLog.log(info, " id: 0x%lx (%lu)", ensemble->id, ensemble->id);
diff --git a/src/utils.h b/src/utils.h
index ee2340f..8197210 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -29,6 +29,7 @@
#define _UTILS_H
#include <cstdio>
+#include "MuxElements.h"
time_t getDabTime();
@@ -49,7 +50,7 @@ void printUsageConfigfile(char *name, FILE* out = stderr);
/* The following four utility functions display a
* description of all outputs, services, components
* resp. subchannels*/
-void printOutputs(std::vector<dabOutput*>& outputs);
+void printOutputs(std::vector<boost::shared_ptr<DabOutput> >& outputs);
void printServices(std::vector<DabService*>& services);
@@ -58,7 +59,7 @@ void printComponents(std::vector<DabComponent*>& components);
void printSubchannels(std::vector<dabSubchannel*>& subchannels);
/* Print information about the whole ensemble */
-void printEnsemble(dabEnsemble* ensemble);
+void printEnsemble(const boost::shared_ptr<dabEnsemble> ensemble);
/* Print detailed component information */
void printComponent(DabComponent* component);