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