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