/* Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Her Majesty the Queen in Right of Canada (Communications Research Center Canada) */ /* This file is part of CRC-DabMux. CRC-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. CRC-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 CRC-DabMux. If not, see . */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include #include #include #include using namespace std; #include #include #include #include #ifdef _WIN32 # include # include # include # include # include // For types... typedef u_char uint8_t; typedef WORD uint16_t; typedef DWORD32 uint32_t; # ifndef __MINGW32__ # include "xgetopt.h" # endif # define read _read # define snprintf _snprintf # define sleep(a) Sleep((a) * 1000) #else # include # include # include # include # include # include # include # include # include #endif #include #include #include #include #ifdef _WIN32 # pragma warning ( disable : 4103 ) # include "Eti.h" # pragma warning ( default : 4103 ) #else # include "Eti.h" #endif #include "dabInputFile.h" #include "dabInputFifo.h" #include "dabInputMpegFile.h" #include "dabInputMpegFifo.h" #include "dabInputDabplusFile.h" #include "dabInputDabplusFifo.h" #include "dabInputPacketFile.h" #include "dabInputEnhancedPacketFile.h" #include "dabInputEnhancedFifo.h" #include "dabInputUdp.h" #include "dabInputBridgeUdp.h" #include "dabInputSlip.h" #include "dabInputTest.h" #include "dabInputPrbs.h" #include "dabInputRawFile.h" #include "dabInputRawFifo.h" #include "dabInputDmbFile.h" #include "dabInputDmbUdp.h" #include "dabOutput.h" #include "crc.h" #include "UdpSocket.h" #include "InetAddress.h" #include "dabUtils.h" #include "PcDebug.h" // 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 // Etiquettes des sous-canaux et de l'ensemble, 16 characteres incluant les // espaces #define DEFAULT_ENSEMBLE_LABEL "CRC-Dr.Radio" #define DEFAULT_ENSEMBLE_SHORT_LABEL 0xe000 #define DEFAULT_ENSEMBLE_ID 0xc000 #define DEFAULT_ENSEMBLE_ECC 0xa1 //Numeros des sous-canaux #define DEFAULT_SERVICE_ID 50 #define DEFAULT_PACKET_ADDRESS 0 struct dabOutput { const char* outputProto; const char* outputName; void* data; dabOutputOperations operations; }; struct dabLabel { char text[16]; uint16_t flag; }; struct dabService; struct dabComponent; struct dabSubchannel; struct dabEnsemble { uint16_t id; uint8_t ecc; dabLabel label; uint8_t mode; vector services; vector components; vector subchannels; }; struct dabProtectionShort { unsigned char tableSwitch; unsigned char tableIndex; }; struct dabProtectionLong { unsigned char option; }; struct dabProtection { unsigned char level; unsigned char form; union { dabProtectionShort shortForm; dabProtectionLong longForm; }; }; struct dabSubchannel { const char* inputProto; const char* inputName; void* data; dabInputOperations operations; unsigned char id; unsigned char type; uint16_t startAddress; uint16_t bitrate; dabProtection protection; }; class SubchannelId : public std::binary_function { public: bool operator()(const dabSubchannel* subchannel, const int id) const { return subchannel->id == id; } }; vector::iterator getSubchannel( vector& subchannels, int id) { return find_if( subchannels.begin(), subchannels.end(), bind2nd(SubchannelId(), id) ); } struct dabAudioComponent { }; struct dabDataComponent { }; struct dabFidcComponent { }; struct dabPacketComponent { uint16_t id; uint16_t address; uint16_t appType; bool datagroup; }; struct dabComponent { dabLabel label; uint32_t serviceId; uint8_t subchId; uint8_t type; uint8_t SCIdS; union { dabAudioComponent audio; dabDataComponent data; dabFidcComponent fidc; dabPacketComponent packet; }; bool isPacketComponent(vector& subchannels) { if (subchId > 63) { etiLog.printHeader(TcpLog::ERR, "You must define subchannel id in the " "packet component before defining packet "); return false; } if (getSubchannel(subchannels, subchId) == subchannels.end()) { etiLog.printHeader(TcpLog::ERR, "Invalid subchannel id in the packet component " "for defining packet "); return false; } if ((*getSubchannel(subchannels, subchId))->type != 3) { etiLog.printHeader(TcpLog::ERR, "Invalid component type for defining packet "); return false; } return true; } }; vector::iterator getComponent( vector& components, uint32_t serviceId, vector::iterator current) { if (current == components.end()) { current = components.begin(); } else { ++current; } while (current != components.end()) { if ((*current)->serviceId == serviceId) { return current; } ++current; } return components.end(); } vector::iterator getComponent( vector& components, uint32_t serviceId) { return getComponent(components, serviceId, components.end()); } struct dabService { dabLabel label; uint32_t id; unsigned char pty; unsigned char language; bool program; unsigned char getType(dabEnsemble* ensemble) { vector::iterator subchannel; vector::iterator component = getComponent(ensemble->components, id); if (component == ensemble->components.end()) { return 4; } subchannel = getSubchannel(ensemble->subchannels, (*component)->subchId); if (subchannel == ensemble->subchannels.end()) { return 8; } return (*subchannel)->type; } unsigned char nbComponent(vector& components) { int nb = 0; vector::iterator current; for (current = components.begin(); current != components.end(); ++current) { if ((*current)->serviceId == id) { ++nb; } } return nb; } }; vector::iterator getService( dabComponent* component, vector& services) { vector::iterator service; for (service = services.begin(); service != services.end(); ++service) { if ((*service)->id == component->serviceId) { break; } } return service; } /****************************************************************************** ***************** Definitions des stuctures des FIGs ********************** ******************************************************************************/ 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; 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; } } PACKED; 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 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 }; /****************************************************************************** ************* Tables pour l'identification des fichiers mp2 **************** *****************************************************************************/ const unsigned short Bit_Rate_SpecifiedTable[16] = { 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0 }; const unsigned short Sub_Channel_SizeTable[64] = { 16, 21, 24, 29, 35, 24, 29, 35, 42, 52, 29, 35, 42, 52, 32, 42, 48, 58, 70, 40, 52, 58, 70, 84, 48, 58, 70, 84, 104, 58, 70, 84, 104, 64, 84, 96, 116, 140, 80, 104, 116, 140, 168, 96, 116, 140, 168, 208, 116, 140, 168, 208, 232, 128, 168, 192, 232, 280, 160, 208, 280, 192, 280, 416 }; 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 }; unsigned short getSizeByte(dabSubchannel* subchannel) { return subchannel->bitrate * 3; } unsigned short getSizeWord(dabSubchannel* subchannel) { return (subchannel->bitrate * 3) >> 2; } unsigned short getSizeDWord(dabSubchannel* subchannel) { return (subchannel->bitrate * 3) >> 3; } unsigned short getSizeCu(dabSubchannel* subchannel) { if (subchannel->protection.form == 0) { return Sub_Channel_SizeTable[subchannel-> protection.shortForm.tableIndex]; } else { dabProtectionLong* protection = &subchannel->protection.longForm; switch (protection->option) { case 0: switch (subchannel->protection.level) { case 0: return (subchannel->bitrate * 12) >> 3; break; case 1: return subchannel->bitrate; break; case 2: return (subchannel->bitrate * 6) >> 3; break; case 3: return (subchannel->bitrate >> 1); break; default: // Should not happens etiLog.print(TcpLog::ERR, "Bad protection level on " "subchannel\n"); return 0; } break; case 1: switch (subchannel->protection.level) { case 0: return (subchannel->bitrate * 27) >> 5; break; case 1: return (subchannel->bitrate * 21) >> 5; break; case 2: return (subchannel->bitrate * 18) >> 5; break; case 3: return (subchannel->bitrate * 15) >> 5; break; default: // Should not happens etiLog.print(TcpLog::ERR, "Bad protection level on subchannel\n"); return 0; } break; default: etiLog.print(TcpLog::ERR, "Invalid protection option\n"); return 0; } } return 0; } time_t getDabTime() { static time_t oldTime = 0; static int offset = 0; if (oldTime == 0) { oldTime = time(NULL); } else { offset+= 24; if (offset >= 1000) { offset -= 1000; ++oldTime; } } return oldTime; } void printUsage(char *name, FILE* out = stderr) { fprintf(out, "NAME\n"); fprintf(out, " %s - A software DAB multiplexer\n", name); fprintf(out, "\nSYNOPSYS\n"); fprintf(out, " %s" " [ensemble]" " [subchannel1 subchannel2 ...]" " [service1 component1 [component2 ...] service2 ...]" " [output1 ...]" " [-h]" " [-m mode]" " [-n nbFrames]" " [-o]" " [-s]" " [-V]" " [-z]" "\n", name); fprintf(out, "\n Where ensemble =" " [-i ensembleId]" " [-L label]" " [-l sLabel]" "\n"); fprintf(out, "\n Where subchannel =" " -(A | B | D | E | F | M | P | T) inputName" " [-b bitrate]" " [-i subchannelId]" " [-k]" " [-p protection]" "\n"); fprintf(out, "\n Where service =" " -S" " [-g language]" " [-i serviceId]" " [-L label]" " [-l sLabel]" " [-y PTy]" "\n"); fprintf(out, "\n Where component =" " -C" " [-a address]" " [-d]" " [-f figType]" " [-i subchannelId]" " [-L label]" " [-l sLabel]" " [-t type]" "\n"); fprintf(out, "\nDESCRIPTION\n"); fprintf(out, " %s is a software multiplexer that generates an ETI stream from\n" " audio and data streams. Because of its software based architecture,\n" " many typical DAB services can be generated and multiplexed on a single\n" " PC platform with live or pre-recorded sources.\n" "\n" " A DAB multiplex configuration is composed of one ensemble. An ensemble\n" " is the entity that receivers tune to and process. An ensemble contains\n" " several services. A service is the listener-selectable output. Each\n" " service contains one mandatory service component which is called pri-\n" " mary component. An audio primary component define a program service\n" " while a data primary component define a data service. Service can con-\n" " tain additional components which are called secondary components. Maxi-\n" " mum total number of components is 12 for program services and 11 for\n" " data services. A service component is a link to one subchannel (of Fast\n" " Information Data Channel). A subchannel is the physical space used\n" " within the common interleaved frame.\n" "\n" " __________________________________________________\n" " | CRC-Ensemble | ENSEMBLE\n" " |__________________________________________________|\n" " | | |\n" " | | |\n" " _______V______ _______V______ _______V______\n" " | CRC-Service1 | | CRC-Service2 | | CRC-Service3 | SERVICES\n" " |______________| |______________| |______________|\n" " | | | | |______ |\n" " | | | | | |\n" " __V__ __V__ __V__ __V__ __V__ __V__\n" " | SC1 | | SC2 | | SC3 | | SC4 | | SC5 | | SC6 | SERVICE\n" " |_____| |_____| |_____| |_____| |_____| |_____| COMPONENTS\n" " | | _____| | | ____|\n" " | | | | | |\n" " __V________V__V______________V________V___V_______ COMMON\n" " | SubCh1 | SubCh9 | ... | SubCh3 | SubCh60 | ... | INTERLEAVED\n" " |________|________|_______|________|_________|_____| FRAME\n" " Figure 1: An example of a DAB multiplex configuration\n", name); fprintf(out, "\nGENERAL OPTIONS\n"); fprintf(out, " -h : get this help\n"); fprintf(out, " -m : DAB mode (default: 2)\n"); fprintf(out, " -n nbFrames : number of frames to produce\n"); fprintf(out, " -o : turn on TCP log on port 12222\n"); fprintf(out, " -V : print version information and " "exit\n"); fprintf(out, " -z : write SCCA field for Factum ETI" " analyzer\n"); fprintf(out, "\nINPUT OPTIONS\n"); fprintf(out, " -A : set audio service\n"); fprintf(out, " -B : set CRC-Bridge data service\n"); fprintf(out, " -D : set data service\n"); fprintf(out, " -E : set enhanced packet service\n"); fprintf(out, " -F : set DAB+ service\n"); fprintf(out, " -M : set DMB service\n"); fprintf(out, " -P : set packet service\n"); fprintf(out, " -T : set test service\n"); fprintf(out, " inputName : name of file for audio and " "packet service and Udp input address for data service " "([address]:port)\n"); fprintf(out, " -a : packet address (default: 0x%x " "(%i))\n", DEFAULT_PACKET_ADDRESS, DEFAULT_PACKET_ADDRESS); fprintf(out, " -b bitrate : bitrate (in kbits/s) of the " "subchannel (default: audio 1st frame, data %i, packet %i)\n", DEFAULT_DATA_BITRATE, DEFAULT_PACKET_BITRATE); fprintf(out, " -c : set the extendend country code ECC " "(default: %u (0x%2x)\n"); fprintf(out, " -d : turn on datagroups in packet " "mode\n"); fprintf(out, " -f figType : user application type in FIG " "0/13 for packet mode\n"); fprintf(out, " -g language : Primary service component " "language: english=9, french=15\n"); fprintf(out, " -i id : service|subchannel|" "serviceComponent id (default: )\n"); fprintf(out, " -k : set non-blocking file input " "(audio and packet only)\n"); fprintf(out, " -L label : label of service " " (default: CRC-Audio)\n"); fprintf(out, " -l sLabel : short label flag of service " " (default: 0xf040)\n"); fprintf(out, " -p protection : protection level (default: 3)\n"); fprintf(out, " -s : enable TIST, synchronized on 1PPS at level 2\n"); fprintf(out, " -t type : audio/data service component type" " (default: 0)\n"); fprintf(out, " audio: foreground=0, " "background=1, multi-channel=2\n"); fprintf(out, " data: unspecified=0, TMC=1, " "EWS=2, ITTS=3, paging=4, TDC=5, DMB=24, IP=59, MOT=60, " "proprietary=61\n"); fprintf(out, " -y PTy : Primary service component program" " type international code\n"); fprintf(out, "\nOUTPUT OPTIONS\n"); fprintf(out, " -O output : name of the output in format " "scheme://[address][:port][/name]\n" " where scheme is (raw|udp|tcp|file|fifo|simul)\n" ); } bool running = true; void signalHandler(int signum) { #ifdef _WIN32 etiLog.print(TcpLog::DBG, "\npid: %i\n", _getpid()); #else etiLog.print(TcpLog::DBG, "\npid: %i, ppid: %i\n", getpid(), getppid()); #endif etiLog.print(TcpLog::DBG, "Signal handler called with signal "); switch (signum) { #ifndef _WIN32 case SIGHUP: etiLog.print(TcpLog::DBG, "SIGHUP\n"); break; case SIGQUIT: etiLog.print(TcpLog::DBG, "SIGQUIT\n"); break; case SIGPIPE: etiLog.print(TcpLog::DBG, "SIGPIPE\n"); return; break; #endif case SIGINT: etiLog.print(TcpLog::DBG, "SIGINT\n"); break; case SIGTERM: etiLog.print(TcpLog::DBG, "SIGTERM\n"); etiLog.print(TcpLog::DBG, "Exiting software\n"); exit(0); break; default: etiLog.print(TcpLog::DBG, "number %i\n", signum); } #ifndef _WIN32 killpg(0, SIGPIPE); #endif running = false; } void printEnsemble(dabEnsemble* ensemble) { char label[17]; memcpy(label, ensemble->label.text, 16); label[16] = 0; etiLog.printHeader(TcpLog::INFO, "Ensemble\n"); etiLog.printHeader(TcpLog::INFO, " id: 0x%lx (%lu)\n", ensemble->id, ensemble->id); etiLog.printHeader(TcpLog::INFO, " ecc: 0x%x (%u)\n", ensemble->ecc, ensemble->ecc); etiLog.printHeader(TcpLog::INFO, " label: %s\n", label); etiLog.printHeader(TcpLog::INFO, " short label: "); for (int i = 0; i < 32; ++i) { if (ensemble->label.flag & 0x8000 >> i) { etiLog.printHeader(TcpLog::INFO, "%c", ensemble->label.text[i]); } } etiLog.printHeader(TcpLog::INFO, " (0x%x)\n", ensemble->label.flag); etiLog.printHeader(TcpLog::INFO, " mode: %u\n", ensemble->mode); } void printSubchannels(vector& subchannels) { vector::iterator subchannel; int index = 0; for (subchannel = subchannels.begin(); subchannel != subchannels.end(); ++subchannel) { dabProtection* protection = &(*subchannel)->protection; etiLog.printHeader(TcpLog::INFO, "Subchannel %i\n", index); etiLog.printHeader(TcpLog::INFO, " input\n"); etiLog.printHeader(TcpLog::INFO, " protocol: %s\n", (*subchannel)->inputProto); etiLog.printHeader(TcpLog::INFO, " name: %s\n", (*subchannel)->inputName); etiLog.printHeader(TcpLog::INFO, " type: "); switch ((*subchannel)->type) { case 0: etiLog.printHeader(TcpLog::INFO, "audio\n"); break; case 1: etiLog.printHeader(TcpLog::INFO, "data\n"); break; case 2: etiLog.printHeader(TcpLog::INFO, "fidc\n"); break; case 3: etiLog.printHeader(TcpLog::INFO, "packet\n"); break; default: etiLog.printHeader(TcpLog::INFO, "Unknown data type " "(service->type)\n"); break; } etiLog.printHeader(TcpLog::INFO, " id: %i\n", (*subchannel)->id); etiLog.printHeader(TcpLog::INFO, " bitrate: %i\n", (*subchannel)->bitrate); etiLog.printHeader(TcpLog::INFO, " protection: "); if (protection->form == 0) { etiLog.printHeader(TcpLog::INFO, "UEP %i\n", protection->level + 1); } else { etiLog.printHeader(TcpLog::INFO, "EEP %i-%c\n", protection->level + 1, protection->longForm.option == 0 ? 'A' : 'B'); } if (protection->form == 0) { etiLog.printHeader(TcpLog::INFO, " form: short\n switch: %i\n index: %i\n", protection->shortForm.tableSwitch, protection->shortForm.tableIndex); } else { etiLog.printHeader(TcpLog::INFO, " form: long\n option: %i\n level: %i\n", protection->longForm.option, (*subchannel)->protection.level); } etiLog.printHeader(TcpLog::INFO, " SAD: %i\n", (*subchannel)->startAddress); etiLog.printHeader(TcpLog::INFO, " size (CU): %i\n", getSizeCu(*subchannel)); ++index; } } void printComponent(dabComponent* component) { char label[17]; memcpy(label, component->label.text, 16); label[16] = 0; if (label[0] == 0) { sprintf(label, ""); } etiLog.printHeader(TcpLog::INFO, " service id: %i\n", component->serviceId); etiLog.printHeader(TcpLog::INFO, " subchannel id: %i\n", component->subchId); etiLog.printHeader(TcpLog::INFO, " label: %s\n", label); etiLog.printHeader(TcpLog::INFO, " short label: "); for (int i = 0; i < 32; ++i) { if (component->label.flag & 0x8000 >> i) { etiLog.printHeader(TcpLog::INFO, "%c", component->label.text[i]); } } etiLog.printHeader(TcpLog::INFO, " (0x%x)\n", component->label.flag); etiLog.printHeader(TcpLog::INFO, " service component type: 0x%x (%u)\n", component->type, component->type); etiLog.printHeader(TcpLog::INFO, " (packet) id: %u\n", component->packet.id); etiLog.printHeader(TcpLog::INFO, " (packet) address: %u\n", component->packet.address); etiLog.printHeader(TcpLog::INFO, " (packet) app type: %u\n", component->packet.appType); etiLog.printHeader(TcpLog::INFO, " (packet) datagroup: %u\n", component->packet.datagroup); } void printComponents(vector& components) { vector::const_iterator current; unsigned int index = 0; for (current = components.begin(); current != components.end(); ++current) { etiLog.printHeader(TcpLog::INFO, "Component %i\n", index); printComponent(*current); ++index; } } void printServices(vector& services) { vector::const_iterator current; int index = 0; for (current = services.begin(); current != services.end(); ++current) { char label[17]; memcpy(label, (*current)->label.text, 16); label[16] = 0; etiLog.printHeader(TcpLog::INFO, "Service %i\n", index); etiLog.printHeader(TcpLog::INFO, " label: %s\n", label); etiLog.printHeader(TcpLog::INFO, " short label: "); for (int i = 0; i < 32; ++i) { if ((*current)->label.flag & 0x8000 >> i) { etiLog.printHeader(TcpLog::INFO, "%c", (*current)->label.text[i]); } } etiLog.printHeader(TcpLog::INFO, " (0x%x)\n", (*current)->label.flag); etiLog.printHeader(TcpLog::INFO, " id: 0x%lx (%lu)\n", (*current)->id, (*current)->id); etiLog.printHeader(TcpLog::INFO, " pty: 0x%x (%u)\n", (*current)->pty, (*current)->pty); etiLog.printHeader(TcpLog::INFO, " language: 0x%x (%u)\n", (*current)->language, (*current)->language); ++index; } } void printOutputs(vector& outputs) { vector::const_iterator output; int index = 0; for (output = outputs.begin(); output != outputs.end(); ++output) { etiLog.printHeader(TcpLog::INFO, "Output %i\n", index); etiLog.printHeader(TcpLog::INFO, " protocol: %s\n", (*output)->outputProto); etiLog.printHeader(TcpLog::INFO, " name: %s\n", (*output)->outputName); ++index; } } int main(int argc, char *argv[]) { etiLog.printHeader(TcpLog::INFO, "Welcome to %s %s, compiled at %s, %s\n\n", PACKAGE_NAME, PACKAGE_VERSION, __DATE__, __TIME__); etiLog.printHeader(TcpLog::INFO, "Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011\n" "Her Majesty the Queen in Right of Canada,\n" "(Communications Research Centre Canada) All rights reserved.\n\n"); etiLog.printHeader(TcpLog::INFO, "Input URLs supported:"); #if defined(HAVE_INPUT_PRBS) etiLog.printHeader(TcpLog::INFO, " prbs"); #endif #if defined(HAVE_INPUT_TEST) etiLog.printHeader(TcpLog::INFO, " test"); #endif #if defined(HAVE_INPUT_SLIP) etiLog.printHeader(TcpLog::INFO, " slip"); #endif #if defined(HAVE_INPUT_UDP) etiLog.printHeader(TcpLog::INFO, " udp"); #endif #if defined(HAVE_INPUT_FIFO) etiLog.printHeader(TcpLog::INFO, " fifo"); #endif #if defined(HAVE_INPUT_FILE) etiLog.printHeader(TcpLog::INFO, " file"); #endif etiLog.printHeader(TcpLog::INFO, "\n"); etiLog.printHeader(TcpLog::INFO, "Inputs format supported:"); #if defined(HAVE_FORMAT_RAW) etiLog.printHeader(TcpLog::INFO, " raw"); #endif #if defined(HAVE_FORMAT_BRIDGE) etiLog.printHeader(TcpLog::INFO, " bridge"); #endif #if defined(HAVE_FORMAT_MPEG) etiLog.printHeader(TcpLog::INFO, " mpeg"); #endif #if defined(HAVE_FORMAT_PACKET) etiLog.printHeader(TcpLog::INFO, " packet"); #endif #if defined(HAVE_FORMAT_DMB) etiLog.printHeader(TcpLog::INFO, " dmb"); #endif #if defined(HAVE_FORMAT_EPM) etiLog.printHeader(TcpLog::INFO, " epm"); #endif etiLog.printHeader(TcpLog::INFO, "\n"); etiLog.printHeader(TcpLog::INFO, "Output URLs supported:"); #if defined(HAVE_OUTPUT_FILE) etiLog.printHeader(TcpLog::INFO, " file"); #endif #if defined(HAVE_OUTPUT_FIFO) etiLog.printHeader(TcpLog::INFO, " fifo"); #endif #if defined(HAVE_OUTPUT_UDP) etiLog.printHeader(TcpLog::INFO, " udp"); #endif #if defined(HAVE_OUTPUT_TCP) etiLog.printHeader(TcpLog::INFO, " tcp"); #endif #if defined(HAVE_OUTPUT_RAW) etiLog.printHeader(TcpLog::INFO, " raw"); #endif #if defined(HAVE_OUTPUT_SIMUL) etiLog.printHeader(TcpLog::INFO, " simul"); #endif etiLog.printHeader(TcpLog::INFO, "\n\n"); /* for (int signum = 1; signum < 16; ++signum) { signal(signum, signalHandler); }*/ #ifndef _WIN32 signal(SIGHUP, signalHandler); signal(SIGQUIT, signalHandler); #endif signal(SIGINT, signalHandler); signal(SIGTERM, signalHandler); //signal(SIGPIPE, signalHandler); #ifdef _WIN32 if (SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST) == 0) { etiLog.printHeader(TcpLog::WARNING, "Can't increase priority: %s\n", strerror(errno)); } #else if (setpriority(PRIO_PROCESS, 0, -20) == -1) { etiLog.printHeader(TcpLog::WARNING, "Can't increase priority: %s\n", strerror(errno)); } #endif /*sched_param scheduler; scheduler.sched_priority = 99; // sched_get_priority_max(SCHED_RR) if (sched_setscheduler(0, SCHED_RR, &scheduler) == -1) { etiLog.print(TcpLog::WARNING, "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; strncpy(ensemble->label.text, DEFAULT_ENSEMBLE_LABEL, 16); ensemble->mode = DEFAULT_DAB_MODE; ensemble->label.flag = DEFAULT_ENSEMBLE_SHORT_LABEL; ensemble->id = DEFAULT_ENSEMBLE_ID; ensemble->ecc = DEFAULT_ENSEMBLE_ECC; vector outputs; vector::iterator service = ensemble->services.end(); vector::iterator serviceProgramInd; vector::iterator serviceDataInd; vector::iterator servicePty; vector::iterator component = ensemble->components.end(); vector::iterator componentIndicatorProgram; vector::iterator componentIndicatorData; vector::iterator subchannel = ensemble->subchannels.end(); vector::iterator subchannelIndicator; vector::iterator output; dabProtection* protection = NULL; unsigned int currentFrame; int returnCode = 0; int result; 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); unsigned long sync = 0x49C5F8; unsigned short FLtmp = 0; unsigned short nbBytesCRC = 0; unsigned short CRCtmp = 0xFFFF; unsigned short MSTsize = 0; unsigned int insertFIG = 0; unsigned int alterneFIB = 0; bool factumAnalyzer = false; unsigned long limit = 0; time_t date; unsigned timestamp = 0xffffff; char* progName = strrchr(argv[0], '/'); if (progName == NULL) { progName = argv[0]; } else { ++progName; } while (1) { int c = getopt(argc, argv, "A:B:CD:E:F:L:M:O:P:STVa:b:c:de:f:g:hi:kl:m:n:op:st:y:z"); if (c == -1) { break; } switch (c) { case 'O': outputs.push_back(new dabOutput); output = outputs.end() - 1; memset(*output, 0, sizeof(dabOutput)); (*output)->outputProto = NULL; (*output)->outputName = NULL; (*output)->data = NULL; (*output)->operations = dabOutputDefaultOperations; char* proto; proto = strstr(optarg, "://"); if (proto == NULL) { etiLog.printHeader(TcpLog::ERR, "No protocol defined for output\n"); returnCode = -1; goto EXIT; } else { (*output)->outputProto = optarg; (*output)->outputName = proto + 3; *proto = 0; } subchannel = ensemble->subchannels.end(); protection = NULL; component = ensemble->components.end(); service = ensemble->services.end(); break; case 'S': ensemble->services.push_back(new dabService); subchannel = ensemble->subchannels.end(); protection = NULL; component = ensemble->components.end(); service = ensemble->services.end() - 1; output = outputs.end(); memset((*service)->label.text, 0, 16); sprintf((*service)->label.text, "CRC-Service%i", (int)ensemble->services.size()); (*service)->label.flag = 0xe01f; (*service)->id = DEFAULT_SERVICE_ID + ensemble->services.size(); (*service)->pty = 0; (*service)->language = 0; currentFrame = 0; // Will be used temporaly for SCIdS break; case 'C': if (service == ensemble->services.end()) { etiLog.printHeader(TcpLog::ERR, "You must define a service" " before using option -%c\n", c); printUsage(progName); returnCode = -1; goto EXIT; } ensemble->components.push_back(new dabComponent); component = ensemble->components.end() - 1; subchannel = ensemble->subchannels.end(); protection = NULL; output = outputs.end(); memset(*component, 0, sizeof(dabComponent)); memset((*component)->label.text, 0, 16); (*component)->label.flag = 0xffff; (*component)->serviceId = (*service)->id; (*component)->subchId = (*(ensemble->subchannels.end() - 1))->id; (*component)->SCIdS = currentFrame++; break; case 'A': case 'B': case 'D': case 'E': case 'F': case 'M': case 'P': case 'T': if (optarg == NULL && c != 'T') { etiLog.printHeader(TcpLog::ERR, "Missing parameter for option -%c\n", c); printUsage(progName); returnCode = -1; goto EXIT; } ensemble->subchannels.push_back(new dabSubchannel); subchannel = ensemble->subchannels.end() - 1; protection = &(*subchannel)->protection; component = ensemble->components.end(); service = ensemble->services.end(); output = outputs.end(); if (c != 'T') { (*subchannel)->inputName = optarg; } else { (*subchannel)->inputName = NULL; } if (0) { #if defined(HAVE_INPUT_FILE) && defined(HAVE_FORMAT_MPEG) } else if (c == 'A') { (*subchannel)->inputProto = "file"; (*subchannel)->type = 0; (*subchannel)->bitrate = 0; (*subchannel)->operations = dabInputMpegFileOperations; #endif // defined(HAVE_INPUT_FILE) && defined(HAVE_FORMAT_MPEG) #if defined(HAVE_FORMAT_DABPLUS) } else if (c == 'F') { (*subchannel)->type = 0; (*subchannel)->bitrate = 32; char* proto; proto = strstr(optarg, "://"); if (proto == NULL) { (*subchannel)->inputProto = "file"; } else { (*subchannel)->inputProto = optarg; (*subchannel)->inputName = proto + 3; *proto = 0; } if (0) { #if defined(HAVE_INPUT_FILE) } else if (strcmp((*subchannel)->inputProto, "file") == 0) { (*subchannel)->operations = dabInputDabplusFileOperations; #endif // defined(HAVE_INPUT_FILE) } else { etiLog.printHeader(TcpLog::ERR, "Invalid protocol for DAB+ input (%s)\n", (*subchannel)->inputProto); printUsage(progName); returnCode = -1; goto EXIT; } #endif // defined(HAVE_FORMAT_DABPLUS) } else if (c == 'B') { char* proto; proto = strstr(optarg, "://"); if (proto == NULL) { (*subchannel)->inputProto = "udp"; } else { (*subchannel)->inputProto = optarg; (*subchannel)->inputName = proto + 3; *proto = 0; } if (0) { #if defined(HAVE_FORMAT_BRIDGE) #if defined(HAVE_INPUT_UDP) } else if (strcmp((*subchannel)->inputProto, "udp") == 0) { (*subchannel)->operations = dabInputBridgeUdpOperations; #endif // defined(HAVE_INPUT_UDP) #if defined(HAVE_INPUT_SLIP) } else if (strcmp((*subchannel)->inputProto, "slip") == 0) { (*subchannel)->operations = dabInputSlipOperations; #endif // defined(HAVE_INPUT_SLIP) #endif // defined(HAVE_FORMAT_BRIDGE) } } else if (c == 'D') { char* proto; proto = strstr(optarg, "://"); if (proto == NULL) { (*subchannel)->inputProto = "udp"; } else { (*subchannel)->inputProto = optarg; (*subchannel)->inputName = proto + 3; *proto = 0; } if (0) { #if defined(HAVE_INPUT_UDP) } else if (strcmp((*subchannel)->inputProto, "udp") == 0) { (*subchannel)->operations = dabInputUdpOperations; #endif #if defined(HAVE_INPUT_PRBS) && defined(HAVE_FORMAT_RAW) } else if (strcmp((*subchannel)->inputProto, "prbs") == 0) { (*subchannel)->operations = dabInputPrbsOperations; #endif #if defined(HAVE_INPUT_FILE) && defined(HAVE_FORMAT_RAW) } else if (strcmp((*subchannel)->inputProto, "file") == 0) { (*subchannel)->operations = dabInputRawFileOperations; #endif } else if (strcmp((*subchannel)->inputProto, "fifo") == 0) { (*subchannel)->operations = dabInputRawFifoOperations; } else { etiLog.printHeader(TcpLog::ERR, "Invalid protocol for data input (%s)\n", (*subchannel)->inputProto); printUsage(progName); returnCode = -1; goto EXIT; } (*subchannel)->type = 1; (*subchannel)->bitrate = DEFAULT_DATA_BITRATE; #if defined(HAVE_INPUT_TEST) && defined(HAVE_FORMAT_RAW) } else if (c == 'T') { (*subchannel)->inputProto = "test"; (*subchannel)->type = 1; (*subchannel)->bitrate = DEFAULT_DATA_BITRATE; (*subchannel)->operations = dabInputTestOperations; #endif // defined(HAVE_INPUT_TEST)) && defined(HAVE_FORMAT_RAW) #ifdef HAVE_FORMAT_PACKET } else if (c == 'P') { (*subchannel)->inputProto = "file"; (*subchannel)->type = 3; (*subchannel)->bitrate = DEFAULT_PACKET_BITRATE; #ifdef HAVE_INPUT_FILE (*subchannel)->operations = dabInputPacketFileOperations; #elif defined(HAVE_INPUT_FIFO) (*subchannel)->operations = dabInputFifoOperations; #else # pragma error("Must defined at least one packet input") #endif // defined(HAVE_INPUT_FILE) #ifdef HAVE_FORMAT_EPM } else if (c == 'E') { (*subchannel)->inputProto = "file"; (*subchannel)->type = 3; (*subchannel)->bitrate = DEFAULT_PACKET_BITRATE; (*subchannel)->operations = dabInputEnhancedPacketFileOperations; #endif // defined(HAVE_FORMAT_EPM) #endif // defined(HAVE_FORMAT_PACKET) #ifdef HAVE_FORMAT_DMB } else if (c == 'M') { char* proto; proto = strstr(optarg, "://"); if (proto == NULL) { (*subchannel)->inputProto = "udp"; } else { (*subchannel)->inputProto = optarg; (*subchannel)->inputName = proto + 3; *proto = 0; } if (strcmp((*subchannel)->inputProto, "udp") == 0) { (*subchannel)->operations = dabInputDmbUdpOperations; } else if (strcmp((*subchannel)->inputProto, "file") == 0) { (*subchannel)->operations = dabInputDmbFileOperations; } else { etiLog.printHeader(TcpLog::ERR, "Invalid protocol for DMB input (%s)\n", (*subchannel)->inputProto); printUsage(progName); returnCode = -1; goto EXIT; } (*subchannel)->type = 1; (*subchannel)->bitrate = DEFAULT_DATA_BITRATE; #endif } else { etiLog.printHeader(TcpLog::ERR, "Service '%c' not yet coded!\n", c); returnCode = -1; goto EXIT; } (*subchannel)->operations.init(&(*subchannel)->data); for (int i = 0; i < 64; ++i) { // Find first free subchannel subchannel = getSubchannel(ensemble->subchannels, i); if (subchannel == ensemble->subchannels.end()) { subchannel = ensemble->subchannels.end() - 1; (*subchannel)->id = i; break; } } (*subchannel)->startAddress = 0; if (c == 'A') { protection->form = 0; protection->level = 2; protection->shortForm.tableSwitch = 0; protection->shortForm.tableIndex = 0; } else { protection->level = 2; protection->form = 1; protection->longForm.option = 0; } break; case 'L': if (optarg == NULL) { etiLog.printHeader(TcpLog::ERR, "Missing parameter for option -L\n"); printUsage(progName); returnCode = -1; goto EXIT; } if (service == ensemble->services.end()) { memset(ensemble->label.text, 0, 16); strncpy(ensemble->label.text, optarg, 16); ensemble->label.flag = 0xff00; } else if (component != ensemble->components.end()) { memset((*component)->label.text, 0, 16); strncpy((*component)->label.text, optarg, 16); (*component)->label.flag = 0xff00; } else { // Service memset((*service)->label.text, 0, 16); strncpy((*service)->label.text, optarg, 16); (*service)->label.flag = 0xff00; } // TODO Check strlen before doing short label // TODO Check if short label already set break; case 'V': goto EXIT; case 'l': if (optarg == NULL) { etiLog.printHeader(TcpLog::ERR, "Missing parameter for option -l\n"); printUsage(progName); returnCode = -1; goto EXIT; } if (service == ensemble->services.end()) { char* end; ensemble->label.flag = strtoul(optarg, &end, 0); if (*end != 0) { end = optarg; ensemble->label.flag = 0; for (int i = 0; i < 32; ++i) { if (*end == ensemble->label.text[i]) { ensemble->label.flag |= 0x8000 >> i; if (*(++end) == 0) { break; } } } if (*end != 0) { etiLog.printHeader(TcpLog::ERR, "Error at '%c' in ensemble short label '%s'!\n" "Not in label '%s'!\n", *end, optarg, ensemble->label.text); returnCode = -1; goto EXIT; } } int count = 0; for (int i = 0; i < 16; ++i) { if (ensemble->label.flag & (1 << i)) { ++count; } } if (count > 8) { etiLog.printHeader(TcpLog::ERR, "Ensemble short label too long!\n" "Must be < 8 characters.\n"); returnCode = -1; goto EXIT; } } else if (component != ensemble->components.end()) { char* end; (*component)->label.flag = strtoul(optarg, &end, 0); if (*end != 0) { end = optarg; (*component)->label.flag = 0; for (int i = 0; i < 32; ++i) { if (*end == (*component)->label.text[i]) { (*component)->label.flag |= 0x8000 >> i; if (*(++end) == 0) { break; } } } if (*end != 0) { etiLog.printHeader(TcpLog::ERR, "Error at '%c' in component short label '%s'!\n" "Not in label '%s'!\n", *end, optarg, (*component)->label.text); returnCode = -1; goto EXIT; } } int count = 0; for (int i = 0; i < 16; ++i) { if ((*component)->label.flag & (1 << i)) { ++count; } } if (count > 8) { etiLog.printHeader(TcpLog::ERR, "Service '%s' short label too long!\n" "Must be < 8 characters.\n", (*component)->label.text); returnCode = -1; goto EXIT; } } else { char* end; (*service)->label.flag = strtoul(optarg, &end, 0); if (*end != 0) { end = optarg; (*service)->label.flag = 0; for (int i = 0; i < 32; ++i) { if (*end == (*service)->label.text[i]) { (*service)->label.flag |= 0x8000 >> i; if (*(++end) == 0) { break; } } } if (*end != 0) { etiLog.printHeader(TcpLog::ERR, "Error at '%c' in service short label '%s'!\n" "Not in label '%s'!\n", *end, optarg, (*service)->label.text); returnCode = -1; goto EXIT; } } int count = 0; for (int i = 0; i < 16; ++i) { if ((*service)->label.flag & (1 << i)) { ++count; } } if (count > 8) { etiLog.printHeader(TcpLog::ERR, "Service '%s' short label too long!\n" "Must be < 8 characters.\n", (*service)->label.text); returnCode = -1; goto EXIT; } } break; case 'i': if (optarg == NULL) { etiLog.printHeader(TcpLog::ERR, "Missing parameter for option -i\n"); printUsage(progName); returnCode = -1; goto EXIT; } if (component != ensemble->components.end()) { (*component)->subchId = strtoul(optarg, NULL, 0); } else if (subchannel != ensemble->subchannels.end()) { (*subchannel)->id = strtoul(optarg, NULL, 0); } else if (service != ensemble->services.end()) { (*service)->id = strtoul(optarg, NULL, 0); if ((*service)->id == 0) { etiLog.printHeader(TcpLog::ERR, "Service id 0 is invalid\n"); returnCode = -1; goto EXIT; } } else { ensemble->id = strtoul(optarg, NULL, 0); } break; case 'b': if (optarg == NULL) { etiLog.printHeader(TcpLog::ERR, "Missing parameter for option -b\n"); printUsage(progName); returnCode = -1; goto EXIT; } if (subchannel == ensemble->subchannels.end()) { etiLog.printHeader(TcpLog::ERR, "You must define a subchannel first!\n"); printUsage(progName); returnCode = -1; goto EXIT; } (*subchannel)->bitrate = strtoul(optarg, NULL, 0); if (((*subchannel)->bitrate & 0x7) != 0) { (*subchannel)->bitrate += 8; (*subchannel)->bitrate &= ~0x7; etiLog.printHeader(TcpLog::WARNING, "bitrate must be multiple of 8 -> ceiling to %i\n", (*subchannel)->bitrate); } break; case 'c': if (optarg == NULL) { etiLog.printHeader(TcpLog::ERR, "Missing parameter for option -c\n"); printUsage(progName); returnCode = -1; goto EXIT; } ensemble->ecc = strtoul(optarg, NULL, 0); break; #if defined(HAVE_INPUT_FIFO) && defined(HAVE_INPUT_FILE) case 'k': if (subchannel == ensemble->subchannels.end()) { etiLog.printHeader(TcpLog::ERR, "You must define a subchannel first!\n"); printUsage(progName); returnCode = -1; goto EXIT; } switch ((*subchannel)->type) { #ifdef HAVE_FORMAT_PACKET case 3: (*subchannel)->operations.clean(&(*subchannel)->data); if ((*subchannel)->operations == dabInputPacketFileOperations) { (*subchannel)->operations = dabInputFifoOperations; #ifdef HAVE_FORMAT_EPM } else if ((*subchannel)->operations == dabInputEnhancedPacketFileOperations) { (*subchannel)->operations = dabInputEnhancedFifoOperations; #endif // defined(HAVE_FORMAT_EPM) } else { etiLog.printHeader(TcpLog::ERR, "Error, wrong packet subchannel operations!\n"); returnCode = -1; goto EXIT; } (*subchannel)->operations.init(&(*subchannel)->data); (*subchannel)->inputProto = "fifo"; break; #endif // defined(HAVE_FORMAT_PACKET) #ifdef HAVE_FORMAT_MPEG case 0: (*subchannel)->operations.clean(&(*subchannel)->data); if ((*subchannel)->operations == dabInputMpegFileOperations) { (*subchannel)->operations = dabInputMpegFifoOperations; } else if ((*subchannel)->operations == dabInputDabplusFileOperations) { (*subchannel)->operations = dabInputDabplusFifoOperations; } else { etiLog.printHeader(TcpLog::ERR, "Error, wrong audio subchannel operations!\n"); returnCode = -1; goto EXIT; } (*subchannel)->operations.init(&(*subchannel)->data); (*subchannel)->inputProto = "fifo"; break; #endif // defined(HAVE_FORMAT_MPEG) default: etiLog.printHeader(TcpLog::ERR, "sorry, non-blocking input file is " "only valid with audio or packet services\n"); returnCode = -1; goto EXIT; } break; #endif // defined(HAVE_INPUT_FIFO) && defined(HAVE_INPUT_FILE) case 'p': int level; if (optarg == NULL) { etiLog.printHeader(TcpLog::ERR, "Missing parameter for option -P\n"); printUsage(progName); returnCode = -1; goto EXIT; } if (subchannel == ensemble->subchannels.end()) { etiLog.printHeader(TcpLog::ERR, "You must define a subchannel first!\n"); printUsage(progName); returnCode = -1; goto EXIT; } level = strtoul(optarg, NULL, 0) - 1; if (protection->form == 0) { if ((level < 0) || (level > 4)) { etiLog.printHeader(TcpLog::ERR, "protection level must be between " "1 to 5 inclusively (current = %i)\n", level); returnCode = -1; goto EXIT; } } else { if ((level < 0) || (level > 3)) { etiLog.printHeader(TcpLog::ERR, "protection level must be between " "1 to 4 inclusively (current = %i)\n", level); returnCode = -1; goto EXIT; } } protection->level = level; break; case 'm': if (optarg) { ensemble->mode = strtoul(optarg, NULL, 0); if ((ensemble->mode < 1) || (ensemble->mode > 4)) { etiLog.printHeader(TcpLog::ERR, "Mode must be between 1-4\n"); returnCode = -1; goto EXIT; } if (ensemble->mode == 4) ensemble->mode = 0; if (ensemble->mode == 3) { FICL = 32; } else { FICL = 24; } } else { etiLog.printHeader(TcpLog::ERR, "Missing parameter for option -m\n"); printUsage(progName); returnCode = -1; goto EXIT; } break; case 'n': if (optarg) { limit = strtoul(optarg, NULL, 0); } else { etiLog.printHeader(TcpLog::ERR, "Missing parameter for option -n\n"); printUsage(progName); returnCode = -1; goto EXIT; } break; case 'o': etiLog.open("createETI", 0, 12222); break; case 't': if (optarg == NULL) { etiLog.printHeader(TcpLog::ERR, "Missing parameter for option -t\n"); printUsage(progName); returnCode = -1; goto EXIT; } if (component == ensemble->components.end()) { etiLog.printHeader(TcpLog::ERR, "You must define a component before setting " "service type!\n"); printUsage(progName); returnCode = -1; goto EXIT; } (*component)->type = strtoul(optarg, NULL, 0); break; case 'a': if (component == ensemble->components.end()) { etiLog.printHeader(TcpLog::ERR, "You must define a component before setting " "packet address!\n"); printUsage(progName); returnCode = -1; goto EXIT; } if (!(*component)->isPacketComponent(ensemble->subchannels)) { etiLog.printHeader(TcpLog::ERR, "address\n"); printUsage(progName); returnCode = -1; goto EXIT; } (*component)->packet.address = strtoul(optarg, NULL, 0); break; case 'd': if (component == ensemble->components.end()) { etiLog.printHeader(TcpLog::ERR, "You must define a component before setting " "datagroup!\n"); printUsage(progName); returnCode = -1; goto EXIT; } if (!(*component)->isPacketComponent(ensemble->subchannels)) { etiLog.printHeader(TcpLog::ERR, "datagroup\n"); printUsage(progName); returnCode = -1; goto EXIT; } (*component)->packet.datagroup = true; break; case 'f': if (component == ensemble->components.end()) { etiLog.printHeader(TcpLog::ERR, "You must define a component first!\n"); printUsage(progName); returnCode = -1; goto EXIT; } if (!(*component)->isPacketComponent(ensemble->subchannels)) { etiLog.printHeader(TcpLog::ERR, "application type\n"); printUsage(progName); returnCode = -1; goto EXIT; } (*component)->packet.appType = strtoul(optarg, NULL, 0); break; case 'g': if (service == ensemble->services.end()) { etiLog.printHeader(TcpLog::ERR, "You must define a service" " before using option -%c\n", c); printUsage(progName); returnCode = -1; goto EXIT; } (*service)->language = strtoul(optarg, NULL, 0); break; case 's': { struct timeval tv; gettimeofday(&tv, NULL); unsigned _8ms = (tv.tv_usec / 1000) / 8; unsigned _1ms = (tv.tv_usec - (_8ms * 8000)) / 1000; unsigned _4us = 20; unsigned _488ns = 0; unsigned _61ns = 0; timestamp = (((((((_8ms << 3) | _1ms) << 8) | _4us) << 3) | _488ns) << 8) | _61ns; } break; case 'y': if (service == ensemble->services.end()) { etiLog.printHeader(TcpLog::ERR, "You must define a service" " before using option -%c\n", c); printUsage(progName); returnCode = -1; goto EXIT; } (*service)->pty = strtoul(optarg, NULL, 0); break; case 'z': factumAnalyzer = true; break; case '?': returnCode = -1; case 'h': printUsage(progName, stdout); goto EXIT; default: etiLog.printHeader(TcpLog::ERR, "Option '%c' not coded yet\n", c); returnCode = -1; goto EXIT; } } if (optind < argc) { etiLog.printHeader(TcpLog::ERR, "Too much parameters:"); while (optind < argc) { etiLog.printHeader(TcpLog::ERR, " %s", argv[optind++]); } etiLog.printHeader(TcpLog::ERR, "\n"); printUsage(progName); returnCode = -1; goto EXIT; } if (outputs.size() == 0) { etiLog.printHeader(TcpLog::WARNING, "no output defined\n"); outputs.push_back(new dabOutput); output = outputs.end() - 1; memset(*output, 0, sizeof(dabOutput)); (*output)->outputProto = "simul"; (*output)->outputName = NULL; (*output)->data = NULL; (*output)->operations = dabOutputDefaultOperations; printf("Press enter to continue\n"); getchar(); } // Check and adjust subchannels { set ids; for (subchannel = ensemble->subchannels.begin(); subchannel != ensemble->subchannels.end(); ++subchannel) { if (ids.find((*subchannel)->id) != ids.end()) { etiLog.printHeader(TcpLog::ERR, "Subchannel %u is set more than once!\n", (*subchannel)->id); returnCode = -1; goto EXIT; } } } // 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.printHeader(TcpLog::ERR, "Service id 0x%x (%u) is set more than once!\n", (*service)->id, (*service)->id); returnCode = -1; goto EXIT; } // Get first component of this service component = getComponent(ensemble->components, (*service)->id); if (component == ensemble->components.end()) { etiLog.printHeader(TcpLog::ERR, "Service %u includes no component!\n"); returnCode = -1; goto EXIT; } // 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.printHeader(TcpLog::ERR, "Error, unknown service type: %u\n", (*service)->getType(ensemble)); returnCode = -1; goto EXIT; } // Adjust components type for DAB+ while (component != ensemble->components.end()) { subchannel = getSubchannel(ensemble->subchannels, (*component)->subchId); if (subchannel == ensemble->subchannels.end()) { etiLog.printHeader(TcpLog::ERR, "Error, service %u component " "links to the invalid subchannel %u\n", (*component)->serviceId, (*component)->subchId); returnCode = -1; goto EXIT; } protection = &(*subchannel)->protection; switch ((*subchannel)->type) { case 0: // Audio { if (protection->form != 0) { (*component)->type = 0x3f; // DAB+ } } break; case 1: case 2: case 3: break; default: etiLog.printHeader(TcpLog::ERR, "Error, unknown subchannel type\n"); returnCode = -1; goto EXIT; } component = getComponent(ensemble->components, (*service)->id, component); } } } for (output = outputs.begin(); output != outputs.end() ; ++output) { #if defined(HAVE_OUTPUT_FILE) if (strcmp((*output)->outputProto, "file") == 0) { (*output)->operations = dabOutputFileOperations; #else // !defined(HAVE_OUTPUT_FILE) if (0) { #endif // defined(HAVE_OUTPUT_FILE) #if defined(HAVE_OUTPUT_FIFO) } else if (strcmp((*output)->outputProto, "fifo") == 0) { (*output)->operations = dabOutputFifoOperations; #endif // !defined(HAVE_OUTPUT_FIFO) #if defined(HAVE_OUTPUT_RAW) } else if (strcmp((*output)->outputProto, "raw") == 0) { (*output)->operations = dabOutputRawOperations; #endif // defined(HAVE_OUTPUT_RAW) #if defined(HAVE_OUTPUT_UDP) } else if (strcmp((*output)->outputProto, "udp") == 0) { (*output)->operations = dabOutputUdpOperations; #endif // defined(HAVE_OUTPUT_UDP) #if defined(HAVE_OUTPUT_TCP) } else if (strcmp((*output)->outputProto, "tcp") == 0) { (*output)->operations = dabOutputTcpOperations; #endif // defined(HAVE_OUTPUT_TCP) #if defined(HAVE_OUTPUT_SIMUL) } else if (strcmp((*output)->outputProto, "simul") == 0) { (*output)->operations = dabOutputSimulOperations; #endif // defined(HAVE_OUTPUT_SIMUL) } else { etiLog.printHeader(TcpLog::ERR, "Output protcol unknown: %s\n", (*output)->outputProto); goto EXIT; } if ((*output)->operations.init(&(*output)->data) == -1) { etiLog.printHeader(TcpLog::ERR, "Unable to init output %s://%s\n", (*output)->outputProto, (*output)->outputName); return -1; } if ((*output)->operations.open((*output)->data, (*output)->outputName) == -1) { etiLog.printHeader(TcpLog::ERR, "Unable to open output %s://%s\n", (*output)->outputProto, (*output)->outputName); return -1; } } //Relatif aux fichiers d'entre 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)->operations.open((*subchannel)->data, (*subchannel)->inputName) == -1) { perror((*subchannel)->inputName); returnCode = -1; goto EXIT; } // TODO Check errors int result = (*subchannel)->operations.setBitrate( &(*subchannel)->operations, (*subchannel)->data, (*subchannel)->bitrate); if (result <= 0) { etiLog.printHeader(TcpLog::ERR, "can't set bitrate for source %s\n", (*subchannel)->inputName); returnCode = -1; goto EXIT; } (*subchannel)->bitrate = result; if (protection->form == 0) { protection->form = 1; for (int i = 0; i < 64; i++) { if ((*subchannel)->bitrate == BitRateTable[i]) { if (protection->level == ProtectionLevelTable[i]) { protection->form = 0; protection->shortForm.tableIndex = i; } } } } } if (ensemble->subchannels.size() == 0) { etiLog.printHeader(TcpLog::ERR, "can't multiplexed no subchannel!\n"); returnCode = -1; goto EXIT; } subchannel = ensemble->subchannels.end() - 1; if ((*subchannel)->startAddress + getSizeCu((*subchannel)) > 864) { etiLog.printHeader(TcpLog::ERR, "Total size in CU exceeds 864\n"); printSubchannels(ensemble->subchannels); returnCode = -1; goto EXIT; } // 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.printHeader(TcpLog::ERR, "Subchannel %i does not exist for component " "of service %i\n", (*component)->subchId, (*component)->serviceId); returnCode = -1; goto EXIT; } if ((*subchannel)->type != 3) continue; (*component)->packet.id = cur++; } //Initialisation a 0 des cases de la trame ETI memset(etiFrame, 0, 6144); // Print settings before starting etiLog.printHeader(TcpLog::INFO, "\n--- Multiplex configuration ---\n"); printEnsemble(ensemble); etiLog.printHeader(TcpLog::INFO, "\n--- Subchannels list ---\n"); printSubchannels(ensemble->subchannels); etiLog.printHeader(TcpLog::INFO, "\n--- Services list ---\n"); printServices(ensemble->services); etiLog.printHeader(TcpLog::INFO, "\n--- Components list ---\n"); printComponents(ensemble->components); etiLog.printHeader(TcpLog::INFO, "\n--- Output list ---\n"); printOutputs(outputs); etiLog.printHeader(TcpLog::INFO, "\n"); /*************************************************************************** ********** Boucle principale, chaque passage cree une trame ************ **************************************************************************/ serviceProgramInd = ensemble->services.end(); serviceDataInd = ensemble->services.end(); componentIndicatorProgram = ensemble->components.end(); componentIndicatorData = ensemble->components.end(); servicePty = ensemble->services.end(); subchannelIndicator = ensemble->subchannels.end(); for (currentFrame = 0; running; currentFrame++) { if ((limit > 0) && (currentFrame >= limit)) { break; } date = getDabTime(); //Initialisation a 0 des cases de la trame memset(etiFrame, 0, 6144); /********************************************************************** ********** Section SYNC du ETI(NI, G703) ************************* **********************************************************************/ //declare une instance d'une structure eti_SYNC eti_SYNC *etiSync = (eti_SYNC *) etiFrame; //****** Section ERR ******// // 1 octet etiSync->ERR = 0xFF; //Indique qu'il n'y a pas d'erreur //****** Section FSYNC *****// // 3 octets, pour la synchronisation, alterne a chaque trame entre les // deux symboles sync ^= 0xffffff; etiSync->FSYNC = sync; /********************************************************************** *********** Section LIDATA du ETI(NI, G703) ********************** **********************************************************************/ //****** Section FC ***************************************************/ // 4 octets // declare une instance d une structure eti_FC et la place dans la trame eti_FC *fc = (eti_FC *) & etiFrame[4]; //****** FCT **********************// //Incremente a chaque trame, de 0 a 249, 1 octet fc->FCT = currentFrame % 250; //****** FICF ******// //Fast Information Channel Flag, 1 bit, =1 si le FIC est present fc->FICF = 1; //****** NST ******// //Number of Stream, 7 bits, 0-64, 0 si reconfiguration du multiplex fc->NST = ensemble->subchannels.size(); //****** FP ******// // Frame Phase, 3 bits, compteur sur 3 bits, permet au COFDM generator // de savoir quand inserer le TII utilise egalement par le MNST fc->FP = currentFrame & 0x7; //****** MID ******// //Mode Identity, 2 bits, 01 ModeI, 10 modeII, 11 ModeIII, 00 ModeIV fc->MID = ensemble->mode; //mode 2 demande 3 FIB, 3*32octets = 96octets //****** FL ******// //Frame Length, 11 bits, nb of words(4 bytes) in STC, EOH and MST // si NST=0, FL=1+FICL words, FICL=24 ou 32 selon le mode //en word ( 4 bytes), +1 pour la partie EOH FLtmp = 1 + FICL + (fc->NST); for (subchannel = ensemble->subchannels.begin(); subchannel != ensemble->subchannels.end(); ++subchannel) { //Taille d'une trame audio mp2 en paquet de 64 bits FLtmp += getSizeWord(*subchannel); } fc->setFrameLength(FLtmp); index = 8; /******* Section STC **************************************************/ // Stream Characterization, // number of channel * 4 octets = nb octets total 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; //devrait changer selon le mode de protection desire if (protection->form == 0) { sstc->TPL = 0x10 | ProtectionLevelTable[protection->shortForm.tableIndex]; } else { sstc->TPL = 0x20 | (protection->longForm.option << 2) | (protection->level); } //Sub-channel Stream Length, multiple de 64 bits sstc->STL_high = getSizeDWord(*subchannel) / 256; sstc->STL_low = getSizeDWord(*subchannel) % 256; 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; //CRC Cyclic Redundancy Checksum du FC, STC et 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, si FICF=1 alors les 96 ou 128 premiers octets // transportent le FIC selon le mode index = ((fc->NST) + 2 + 1) * 4; //Insertion du FIC 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; //Insertion du FIB 0 switch (insertFIG) { case 0: case 4: // 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: // FIG type 0/1, MIC, Sub-Channel Organization, une instance de la // sous-partie par sous-canal 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; //Sous-partie du FIG type 0/1 if (subchannelIndicator == ensemble->subchannels.end()) { subchannelIndicator = ensemble->subchannels.begin(); } for (; subchannelIndicator != ensemble->subchannels.end(); ++subchannelIndicator) { protection = &(*subchannelIndicator)->protection; if ((protection->form == 0 && figSize > 27) || (protection->form != 0 && figSize > 26)) { break; } if (protection->form == 0) { fig0_1subchShort = (FIG_01_SubChannel_ShortF*) &etiFrame[index]; fig0_1subchShort->SubChId = (*subchannelIndicator)->id; fig0_1subchShort->StartAdress_high = (*subchannelIndicator)->startAddress / 256; fig0_1subchShort->StartAdress_low = (*subchannelIndicator)->startAddress % 256; fig0_1subchShort->Short_Long_form = 0; fig0_1subchShort->TableSwitch = 0; fig0_1subchShort->TableIndex = protection->shortForm.tableIndex; index = index + 3; figSize += 3; figtype0_1->Length += 3; } else { fig0_1subchLong1 = (FIG_01_SubChannel_LongF*) &etiFrame[index]; fig0_1subchLong1->SubChId = (*subchannelIndicator)->id; fig0_1subchLong1->StartAdress_high = (*subchannelIndicator)->startAddress / 256; fig0_1subchLong1->StartAdress_low = (*subchannelIndicator)->startAddress % 256; fig0_1subchLong1->Short_Long_form = 1; fig0_1subchLong1->Option = protection->longForm.option; fig0_1subchLong1->ProtectionLevel = protection->level; fig0_1subchLong1->Sub_ChannelSize_high = getSizeCu(*subchannelIndicator) / 256; fig0_1subchLong1->Sub_ChannelSize_low = getSizeCu(*subchannelIndicator) % 256; index = index + 4; figSize += 4; figtype0_1->Length += 4; } } break; case 2: // FIG type 0/2, MCI, Service Organization, une instance de // FIGtype0_2_Service par sous-canal fig0_2 = NULL; cur = 0; if (serviceProgramInd == ensemble->services.end()) { serviceProgramInd = ensemble->services.begin(); } for (; serviceProgramInd != ensemble->services.end(); ++serviceProgramInd) { if (!(*serviceProgramInd)->nbComponent(ensemble->components)) { continue; } if ((*serviceProgramInd)->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 + (*serviceProgramInd)->nbComponent(ensemble->components) * 2 > 30) { break; } fig0_2serviceAudio = (FIGtype0_2_Service*) &etiFrame[index]; fig0_2serviceAudio->SId = htons((*serviceProgramInd)->id); fig0_2serviceAudio->Local_flag = 0; fig0_2serviceAudio->CAId = 0; fig0_2serviceAudio->NbServiceComp = (*serviceProgramInd)->nbComponent(ensemble->components); index += 3; fig0_2->Length += 3; figSize += 3; int curCpnt = 0; for (component = getComponent(ensemble->components, (*serviceProgramInd)->id); component != ensemble->components.end(); component = getComponent(ensemble->components, (*serviceProgramInd)->id, component)) { subchannel = getSubchannel(ensemble->subchannels, (*component)->subchId); if (subchannel == ensemble->subchannels.end()) { etiLog.printHeader(TcpLog::ERR, "Subchannel %i does not exist for component " "of service %i\n", (*component)->subchId, (*component)->serviceId); returnCode = -1; goto EXIT; } switch ((*subchannel)->type) { case 0: // 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 1: // Data 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 3: // 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.printHeader(TcpLog::ERR, "Component type not supported\n"); returnCode = -1; goto EXIT; } index += 2; fig0_2->Length += 2; figSize += 2; if (figSize > 30) { etiLog.printHeader(TcpLog::ERR, "Sorry, no place left in FIG 0/2 to insert " "component %i of program service %i.\n", curCpnt, cur); returnCode = -1; goto EXIT; } ++curCpnt; } } break; case 3: fig0_2 = NULL; cur = 0; if (serviceDataInd == ensemble->services.end()) { serviceDataInd = ensemble->services.begin(); } for (; serviceDataInd != ensemble->services.end(); ++serviceDataInd) { if (!(*serviceDataInd)->nbComponent(ensemble->components)) { continue; } unsigned char type = (*serviceDataInd)->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 + (*serviceDataInd)->nbComponent(ensemble->components) * 2 > 30) { break; } fig0_2serviceData = (FIGtype0_2_Service_data*) &etiFrame[index]; fig0_2serviceData->SId = htonl((*serviceDataInd)->id); fig0_2serviceData->Local_flag = 0; fig0_2serviceData->CAId = 0; fig0_2serviceData->NbServiceComp = (*serviceDataInd)->nbComponent(ensemble->components); fig0_2->Length += 5; index += 5; figSize += 5; int curCpnt = 0; for (component = getComponent(ensemble->components, (*serviceDataInd)->id); component != ensemble->components.end(); component = getComponent(ensemble->components, (*serviceDataInd)->id, component)) { subchannel = getSubchannel(ensemble->subchannels, (*component)->subchId); if (subchannel == ensemble->subchannels.end()) { etiLog.printHeader(TcpLog::ERR, "Subchannel %i does not exist for component " "of service %i\n", (*component)->subchId, (*component)->serviceId); returnCode = -1; goto EXIT; } switch ((*subchannel)->type) { case 0: // 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 1: // Data 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 3: // 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.printHeader(TcpLog::ERR, "Component type not supported\n"); returnCode = -1; goto EXIT; } index += 2; fig0_2->Length += 2; figSize += 2; if (figSize > 30) { etiLog.printHeader(TcpLog::ERR, "Sorry, no place left in FIG 0/2 to insert " "component %i of data service %i.\n", curCpnt, cur); returnCode = -1; goto EXIT; } ++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.printHeader(TcpLog::ERR, "Subchannel %i does not exist for component " "of service %i\n", (*component)->subchId, (*component)->serviceId); returnCode = -1; goto EXIT; } if ((*subchannel)->type != 3) 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.printHeader(TcpLog::ERR, "can't add to Fic Fig 0/3, " "too much packet service\n"); returnCode = -1; goto EXIT; } } break; case 7: fig0 = NULL; if (servicePty == ensemble->services.end()) { servicePty = ensemble->services.begin(); } for (; servicePty != ensemble->services.end(); ++servicePty) { if ((*servicePty)->pty == 0 && (*servicePty)->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 ((*servicePty)->language == 0) { if (figSize + 4 > 30) { break; } } else { if (figSize + 5 > 30) { break; } } programme = (FIGtype0_17_programme*)&etiFrame[index]; programme->SId = htons((*servicePty)->id); programme->SD = 1; programme->PS = 0; programme->L = (*servicePty)->language != 0; programme->CC = 0; programme->Rfa = 0; programme->NFC = 0; if ((*servicePty)->language == 0) { etiFrame[index + 3] = (*servicePty)->pty; fig0->Length += 4; index += 4; figSize += 4; } else { etiFrame[index + 3] = (*servicePty)->language; etiFrame[index + 4] = (*servicePty)->pty; fig0->Length += 5; index += 5; figSize += 5; } } break; } if (figSize > 30) { etiLog.printHeader(TcpLog::ERR, "FIG too big (%i > 30)\n", figSize); returnCode = -1; goto EXIT; } 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; // Insertion du FIB 1 switch (alterneFIB) { case 0: // FIG 0/8 program fig0 = NULL; if (componentIndicatorProgram == ensemble->components.end()) { componentIndicatorProgram = ensemble->components.begin(); } for (; componentIndicatorProgram != ensemble->components.end(); ++componentIndicatorProgram) { service = getService(*componentIndicatorProgram, ensemble->services); subchannel = getSubchannel(ensemble->subchannels, (*componentIndicatorProgram)->subchId); if (subchannel == ensemble->subchannels.end()) { etiLog.printHeader(TcpLog::ERR, "Subchannel %i does not exist for component " "of service %i\n", (*componentIndicatorProgram)->subchId, (*componentIndicatorProgram)->serviceId); returnCode = -1; goto EXIT; } 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 == 3) { // Data packet if (figSize > 30 - 5) { break; } *((uint16_t*)&etiFrame[index]) = htons((*componentIndicatorProgram)->serviceId); 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 = (*componentIndicatorProgram)->SCIdS; definition->LS = 1; definition->setSCId((*componentIndicatorProgram)->packet.id); fig0->Length += 3; index += 3; // 8 minus rfa figSize += 3; } else { // Audio, data stream or FIDC if (figSize > 30 - 4) { break; } *((uint16_t*)&etiFrame[index]) = htons((*componentIndicatorProgram)->serviceId); 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 = (*componentIndicatorProgram)->SCIdS; definition->LS = 0; definition->MscFic = 0; definition->Id = (*componentIndicatorProgram)->subchId; fig0->Length += 2; index += 2; // 4 minus rfa figSize += 2; } } break; case 1: // FIG 0/8 data fig0 = NULL; if (componentIndicatorData == ensemble->components.end()) { componentIndicatorData = ensemble->components.begin(); } for (; componentIndicatorData != ensemble->components.end(); ++componentIndicatorData) { service = getService(*componentIndicatorData, ensemble->services); subchannel = getSubchannel(ensemble->subchannels, (*componentIndicatorData)->subchId); if (subchannel == ensemble->subchannels.end()) { etiLog.printHeader(TcpLog::ERR, "Subchannel %i does not exist for component " "of service %i\n", (*componentIndicatorData)->subchId, (*componentIndicatorData)->serviceId); returnCode = -1; goto EXIT; } 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 == 3) { // Data packet if (figSize > 30 - 7) { break; } *((uint32_t*)&etiFrame[index]) = htonl((*componentIndicatorData)->serviceId); 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 = (*componentIndicatorData)->SCIdS; definition->LS = 1; definition->setSCId((*componentIndicatorData)->packet.id); fig0->Length += 3; index += 3; // 8 minus rfa figSize += 3; } else { // Audio, data stream or FIDC if (figSize > 30 - 6) { break; } *((uint32_t*)&etiFrame[index]) = htonl((*componentIndicatorData)->serviceId); 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 = (*componentIndicatorData)->SCIdS; definition->LS = 0; definition->MscFic = 0; definition->Id = (*componentIndicatorData)->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++] = ((char *) &ensemble->label.flag)[1]; etiFrame[index++] = ((char *) &ensemble->label.flag)[0]; figSize += 22; break; case 5: // FIG 0 / 13 fig0 = NULL; for (component = ensemble->components.begin(); component != ensemble->components.end(); ++component) { subchannel = getSubchannel(ensemble->subchannels, (*component)->subchId); if (subchannel == ensemble->subchannels.end()) { etiLog.printHeader(TcpLog::ERR, "Subchannel %i does not exist for component " "of service %i\n", (*component)->subchId, (*component)->serviceId); returnCode = -1; goto EXIT; } if ((*subchannel)->type != 3) continue; if ((*component)->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; } FIG0_13_longAppInfo* info = (FIG0_13_longAppInfo*)&etiFrame[index]; info->SId = htonl((*component)->serviceId); info->SCIdS = (*component)->SCIdS; info->No = 1; index += 5; figSize += 5; fig0->Length += 5; FIG0_13_app* app = (FIG0_13_app*)&etiFrame[index]; app->setType((*component)->packet.appType); app->length = 0; index += 2; figSize += 2; fig0->Length += 2; if (figSize > 30) { etiLog.printHeader(TcpLog::ERR, "can't add to Fic Fig 0/13, " "too much packet service\n"); returnCode = -1; goto EXIT; } } } 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 = localtime(&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; fig0_9->ensembleLto = 0; fig0_9->ensembleEcc = ensemble->ecc; fig0_9->tableId = 0x2; index += 5; figSize += 5; break; } 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; // Insertion FIB 2 if (alterneFIB < ensemble->services.size()) { service = ensemble->services.begin() + alterneFIB; // FIG type 1/1, SI, Service label, une instance par sous-canal 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++] = ((char *) &(*service)->label.flag)[1]; etiFrame[index++] = ((char *) &(*service)->label.flag)[0]; figSize += 2; } else if (alterneFIB < ensemble->services.size() + ensemble->components.size()) { component = ensemble->components.begin() + (alterneFIB - 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++] = ((char *) &(*component)->label.flag)[1]; etiFrame[index++] = ((char *) &(*component)->label.flag)[0]; 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]; 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.printHeader(TcpLog::ERR, "Sorry, but this software currently can't write " "Service Label of more than 30 services.\n"); returnCode = -1; goto EXIT; } // compteur pour FIG 0/0 insertFIG = (insertFIG + 1) % 8; // compteur pour inserer FIB a toutes les 30 trames alterneFIB = (alterneFIB + 1) % 30; /********************************************************************** ****** Section de lecture de donnees ******************************* **********************************************************************/ //Lecture des donnees dans les fichiers d'entree for (subchannel = ensemble->subchannels.begin(); subchannel != ensemble->subchannels.end(); ++subchannel) { int sizeSubchannel = getSizeByte(*subchannel); if ((*subchannel)->operations.lock != NULL) { (*subchannel)->operations.lock((*subchannel)->data); } result = (*subchannel)->operations.readFrame( &(*subchannel)->operations, (*subchannel)->data, &etiFrame[index], sizeSubchannel); if ((*subchannel)->operations.unlock != NULL) { (*subchannel)->operations.unlock((*subchannel)->data); } if (result < 0) { etiLog.print(TcpLog::INFO, "ETI frame number: %i\n", currentFrame); } 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 sur le Main Stream data (MST), sur 16 bits index = ((fc->NST) + 2 + 1) * 4; //position du MST MSTsize = ((FLtmp) - 1 - (fc->NST)) * 4; //nb d'octets de donnees 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]; tist->TIST = htonl(timestamp) | 0xff; if (timestamp != 0xffffff) { timestamp += 3 << 17; if (timestamp > 0xf9ffff) { timestamp -= 0xfa0000; } } /********************************************************************** *********** Section FRPD ***************************************** **********************************************************************/ //Donne le nombre total d'octets utils dans la trame index = (FLtmp + 1 + 1 + 1 + 1) * 4; for (output = outputs.begin() ; output != outputs.end(); ++output) { if ((*output)->operations.write((*output)->data, etiFrame, index) == -1) { etiLog.print(TcpLog::ERR, "Can't write to output %s://%s\n", (*output)->outputProto, (*output)->outputName); } } #ifdef DUMP_BRIDGE dumpBytes(dumpData, sizeSubChannel, stderr); #endif // DUMP_BRIDGE if (currentFrame % 100 == 0) { etiLog.print(TcpLog::INFO, "ETI frame number %i \n", currentFrame); } } EXIT: etiLog.print(TcpLog::DBG, "exiting...\n"); //fermeture des fichiers for (subchannel = ensemble->subchannels.begin(); subchannel != ensemble->subchannels.end(); ++subchannel) { (*subchannel)->operations.close((*subchannel)->data); (*subchannel)->operations.clean(&(*subchannel)->data); } for (output = outputs.begin() ; output != outputs.end(); ++output) { (*output)->operations.close((*output)->data); (*output)->operations.clean(&(*output)->data); } 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(); free(ensemble); outputs.clear(); UdpSocket::clean(); if (returnCode < 0) { etiLog.print(TcpLog::EMERG, "...aborting\n"); } else { etiLog.print(TcpLog::DBG, "...done\n"); } return returnCode; }