From 76e7f0f79c908bf7d0a447ea643dbcdde8f064d2 Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Sat, 21 Mar 2015 14:30:09 +0100 Subject: Start big refactoring Multiplexer in separate object Replace pointers by shared_ptr Switch to C++11 --- src/ConfigParser.cpp | 98 +-- src/ConfigParser.h | 15 +- src/DabMultiplexer.cpp | 1809 +++++++++++++++++++++++++++++++++++++++++ src/DabMultiplexer.h | 489 +++++++++++ src/DabMux.cpp | 1970 +++------------------------------------------ src/DabMux.h | 364 +-------- src/Makefile.am | 12 +- src/ManagementServer.cpp | 22 +- src/ManagementServer.h | 28 +- src/MuxElements.cpp | 2 +- src/MuxElements.h | 2 +- src/RemoteControl.cpp | 134 +-- src/RemoteControl.h | 45 +- src/dabOutput/dabOutput.h | 40 +- src/utils.cpp | 22 +- src/utils.h | 5 +- 16 files changed, 2634 insertions(+), 2423 deletions(-) create mode 100644 src/DabMultiplexer.cpp create mode 100644 src/DabMultiplexer.h 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 &outputs, - dabEnsemble* ensemble, - bool* enableTist, - unsigned* FICL, - bool* factumAnalyzer, - unsigned long* limit, - BaseRemoteController** rc, - int* mgmtserverport, + boost::shared_ptr ensemble, + boost::shared_ptr 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("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("managementport", - pt_general.get("statsserverport", 0) ); - - /************** READ REMOTE CONTROL PARAMETERS *************/ - ptree pt_rc = pt.get_child("remotecontrol"); - int telnetport = pt_rc.get("telnetport", 0); - - if (telnetport != 0) { - *rc = new RemoteControllerTelnet(telnetport); - } - else { - *rc = new RemoteControllerDummy(); - } /******************** READ ENSEMBLE PARAMETERS *************/ ptree pt_ensemble = pt.get_child("ensemble"); @@ -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 alloutputs; - ptree pt_outputs = pt.get_child("outputs"); - for (ptree::iterator it = pt_outputs.begin(); it != pt_outputs.end(); ++it) { - string outputuid = it->first; - - if (outputuid == "edi") { - ptree pt_edi = pt_outputs.get_child("edi"); - - edi->enabled = true; - - edi->dest_addr = pt_edi.get("destination"); - edi->dest_port = pt_edi.get("port"); - edi->source_port = pt_edi.get("sourceport"); - - edi->dump = pt_edi.get("dump"); - edi->enable_pft = pt_edi.get("enable_pft"); - edi->verbose = pt_edi.get("verbose"); - } - else { - string uri = pt_outputs.get(outputuid); - - size_t proto_pos = uri.find("://"); - if (proto_pos == std::string::npos) { - stringstream ss; - ss << "Output with uid " << outputuid << " no protocol defined!"; - throw runtime_error(ss.str()); - } - - char* uri_c = new char[512]; - memset(uri_c, 0, 512); - uri.copy(uri_c, 511); - - uri_c[proto_pos] = '\0'; - - char* outputName = uri_c + proto_pos + 3; - - dabOutput* output = new dabOutput(uri_c, outputName); - outputs.push_back(output); - - // keep outputs in map, and check for uniqueness of the uid - if (alloutputs.count(outputuid) == 0) { - alloutputs[outputuid] = output; - } - else { - stringstream ss; - ss << "output with uid " << outputuid << " not unique!"; - throw runtime_error(ss.str()); - } - } - } - } void setup_subchannel_from_ptree(dabSubchannel* subchan, boost::property_tree::ptree &pt, - dabEnsemble* ensemble, + boost::shared_ptr ensemble, string subchanuid, - BaseRemoteController* rc) + boost::shared_ptr 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 +#include void parse_ptree(boost::property_tree::ptree& pt, - std::vector &outputs, - dabEnsemble* ensemble, - bool* enableTist, - unsigned* FICL, - bool* factumAnalyzer, - unsigned long* limit, - BaseRemoteController** rc, - int* mgmtserverport, + boost::shared_ptr ensemble, + boost::shared_ptr rc, edi_configuration_t* edi); void setup_subchannel_from_ptree(dabSubchannel* subchan, boost::property_tree::ptree &pt, - dabEnsemble* ensemble, + boost::shared_ptr ensemble, std::string subchanuid, - BaseRemoteController* rc); + boost::shared_ptr 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 . +*/ + +#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 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(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::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 ids; + vector::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 ids; + dabProtection* protection = NULL; + + vector::iterator component; + vector::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::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 >& outputs) +{ + int cur; + time_t date; + unsigned char etiFrame[6144]; + unsigned short index = 0; + + vector::iterator service; + vector::iterator component; + vector::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 edi_subchannels; + map 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::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 >::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 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 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 . +*/ + +#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 +#include +#include +#include +#include + +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 rc, + boost::property_tree::ptree pt); + void prepare(void); + + unsigned long getCurrentFrame() { return currentFrame; } + + void mux_frame(std::vector >& 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 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::iterator serviceProgFIG0_2; + std::vector::iterator serviceDataFIG0_2; + std::vector::iterator serviceFIG0_17; + + std::vector::iterator componentProgFIG0_8; + std::vector::iterator componentDataFIG0_8; + std::vector::iterator componentFIG0_13; + // Alternate between programme and data + bool transmitFIG0_13programme; + + + std::vector::iterator subchannelFIG0_1; + + boost::shared_ptr 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 #include #include #include @@ -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 outputs; - vector::iterator service = ensemble->services.end(); - vector::iterator serviceProgFIG0_2; - vector::iterator serviceDataFIG0_2; - vector::iterator serviceFIG0_17; - vector::iterator component = ensemble->components.end(); - vector::iterator componentProgFIG0_8; - vector::iterator componentFIG0_13; - bool transmitFIG0_13programme = false; // Alternate between programme and data - vector::iterator componentDataFIG0_8; - vector::iterator subchannel = ensemble->subchannels.end(); - vector::iterator subchannelFIG0_1; - vector::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 > 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("general.managementport", + pt.get("general.statsserverport", 0) ); + + /* Management: stats and config server */ + get_mgmt_server().open(mgmtserverport); + + /************** READ REMOTE CONTROL PARAMETERS *************/ + int telnetport = pt.get("remotecontrol.telnetport", 0); + + boost::shared_ptr rc; + + if (telnetport != 0) { + rc = boost::shared_ptr( + new RemoteControllerTelnet(telnetport)); } else { - mgmt_server = new ManagementServer(); - } - - if (rc) { - ensemble->enrol_at(*rc); + rc = boost::shared_ptr( + 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 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 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 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("destination"); + edi->dest_port = pt_edi.get("port"); + edi->source_port = pt_edi.get("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("dump"); + edi->enable_pft = pt_edi.get("enable_pft"); + edi->verbose = pt_edi.get("verbose"); +#endif } - } + else { + string uri = pt_outputs.get(outputuid); + + size_t proto_pos = uri.find("://"); + if (proto_pos == std::string::npos) { + stringstream ss; + ss << "Output with uid " << outputuid << " no protocol defined!"; + throw runtime_error(ss.str()); + } + + 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 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 edi_subchannels; - map 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::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 >::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 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 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 #include #include +#include "DabMultiplexer.h" #include "RemoteControl.h" #include "dabOutput/dabOutput.h" #include "dabInput.h" @@ -43,368 +44,5 @@ # include #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 ensemble) { vector::iterator subchannel; vector::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 ensemble); unsigned char nbComponent(std::vector& 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 #include #include +#include #include #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 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 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 #include #include +#include #include #include #include @@ -104,6 +105,10 @@ class RemoteControllable { controller.enrol(this); } + virtual void enrol_at(boost::shared_ptr controller) { + controller->enrol(this); + } + /* Return a list of possible parameters that can be set */ virtual std::list get_supported_parameters() const { std::list 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 m_running; + + boost::asio::io_service m_io_service; /* This is set to true if a fault occurred */ - bool m_fault; + std::atomic 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 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 #include +#include #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& outputs) +void printOutputs(vector >& outputs) { - vector::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& subchannels) } } -void printEnsemble(dabEnsemble* ensemble) +void printEnsemble(const boost::shared_ptr 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 +#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& outputs); +void printOutputs(std::vector >& outputs); void printServices(std::vector& services); @@ -58,7 +59,7 @@ void printComponents(std::vector& components); void printSubchannels(std::vector& subchannels); /* Print information about the whole ensemble */ -void printEnsemble(dabEnsemble* ensemble); +void printEnsemble(const boost::shared_ptr ensemble); /* Print detailed component information */ void printComponent(DabComponent* component); -- cgit v1.2.3