summaryrefslogtreecommitdiffstats
path: root/src/DabMux.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/DabMux.cpp')
-rw-r--r--src/DabMux.cpp3549
1 files changed, 3549 insertions, 0 deletions
diff --git a/src/DabMux.cpp b/src/DabMux.cpp
new file mode 100644
index 0000000..59278f9
--- /dev/null
+++ b/src/DabMux.cpp
@@ -0,0 +1,3549 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <iostream>
+#include <fstream>
+#include <iomanip>
+#include <cstdio>
+#include <cstring>
+#include <errno.h>
+
+using namespace std;
+#include <vector>
+#include <set>
+#include <functional>
+#include <algorithm>
+
+#ifdef _WIN32
+# include <time.h>
+# include <process.h>
+# include <io.h>
+# include <conio.h>
+# include <winsock2.h> // 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 <netinet/in.h>
+# include <unistd.h>
+# include <sys/time.h>
+# include <sys/wait.h>
+# include <sys/socket.h>
+# include <sys/ioctl.h>
+# include <sys/times.h>
+
+# include <linux/if_packet.h>
+# include <linux/netdevice.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <signal.h>
+
+#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<dabService*> services;
+ vector<dabComponent*> components;
+ vector<dabSubchannel*> 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 <dabSubchannel*, int, bool> {
+public:
+ bool operator()(const dabSubchannel* subchannel, const int id) const {
+ return subchannel->id == id;
+ }
+};
+
+
+vector<dabSubchannel*>::iterator getSubchannel(
+ vector<dabSubchannel*>& 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<dabSubchannel*>& 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<dabComponent*>::iterator getComponent(
+ vector<dabComponent*>& components,
+ uint32_t serviceId,
+ vector<dabComponent*>::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<dabComponent*>::iterator getComponent(
+ vector<dabComponent*>& 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<dabSubchannel*>::iterator subchannel;
+ vector<dabComponent*>::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<dabComponent*>& components)
+ {
+ int nb = 0;
+ vector<dabComponent*>::iterator current;
+
+ for (current = components.begin(); current != components.end();
+ ++current) {
+ if ((*current)->serviceId == id) {
+ ++nb;
+ }
+ }
+ return nb;
+ }
+};
+
+
+vector<dabService*>::iterator getService(
+ dabComponent* component,
+ vector<dabService*>& services)
+{
+ vector<dabService*>::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<n> : 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<n> : 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<n> : service|subchannel|"
+ "serviceComponent id <n> (default: <n>)\n");
+ fprintf(out, " -k : set non-blocking file input "
+ "(audio and packet only)\n");
+ fprintf(out, " -L label<n> : label of service <n>"
+ " (default: CRC-Audio<n>)\n");
+ fprintf(out, " -l sLabel<n> : short label flag of service <n>"
+ " (default: 0xf040)\n");
+ fprintf(out, " -p protection<n> : 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<dabSubchannel*>& subchannels)
+{
+ vector<dabSubchannel*>::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, "<none>");
+ }
+
+ 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<dabComponent*>& components)
+{
+ vector<dabComponent*>::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<dabService*>& services)
+{
+ vector<dabService*>::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<dabOutput*>& outputs)
+{
+ vector<dabOutput*>::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<dabOutput*> outputs;
+ vector<dabService*>::iterator service = ensemble->services.end();
+ vector<dabService*>::iterator serviceProgramInd;
+ vector<dabService*>::iterator serviceDataInd;
+ vector<dabService*>::iterator servicePty;
+ vector<dabComponent*>::iterator component = ensemble->components.end();
+ vector<dabComponent*>::iterator componentIndicatorProgram;
+ vector<dabComponent*>::iterator componentIndicatorData;
+ vector<dabSubchannel*>::iterator subchannel = ensemble->subchannels.end();
+ vector<dabSubchannel*>::iterator subchannelIndicator;
+ vector<dabOutput*>::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<unsigned char> 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<uint32_t> 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;
+}