mptsd reads mpegts streams from udp/multicast or http and combines them into one multiple program stream that is suitable for outputting to DVB-C modulator. Tested with Dektec DTE-3114 Quad QAM Modulator and used in production in small DVB-C networks. https://georgi.unixsol.org/programs/mptsd/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

config.c 20KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669
  1. /*
  2. * mptsd configuration
  3. * Copyright (C) 2010-2011 Unix Solutions Ltd.
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License version 2
  7. * as published by the Free Software Foundation.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program; if not, write to the Free Software
  16. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  17. */
  18. #include <stdio.h>
  19. #include <stdlib.h>
  20. #include <unistd.h>
  21. #include <string.h>
  22. #include <errno.h>
  23. #include <arpa/inet.h>
  24. #include <netinet/in.h>
  25. #include <sys/types.h>
  26. #include <sys/socket.h>
  27. #include <sys/types.h>
  28. #include <sys/stat.h>
  29. #include <sys/time.h>
  30. #include <sys/stat.h>
  31. #include <signal.h>
  32. #include <fcntl.h>
  33. #include <regex.h>
  34. #include <math.h>
  35. #include <netdb.h> // for uint32_t
  36. #include "libfuncs/log.h"
  37. #include "libfuncs/list.h"
  38. #include "libfuncs/server.h"
  39. #include "libtsfuncs/tsfuncs.h"
  40. #include "data.h"
  41. #include "sleep.h"
  42. #include "config.h"
  43. #include "inidict.h"
  44. #include "iniparser.h"
  45. extern char *server_sig;
  46. extern char *server_ver;
  47. extern char *copyright;
  48. int is_valid_url(char *url) {
  49. regex_t re;
  50. regmatch_t res[5];
  51. int ret;
  52. regcomp(&re, "^([a-z]+)://([^:/?]+):?([0-9]*)/?(.*)", REG_EXTENDED);
  53. ret = regexec(&re,url,5,res,0);
  54. regfree(&re);
  55. return ret == 0;
  56. }
  57. CONFIG *config_alloc() {
  58. CONFIG *conf = calloc(1, sizeof(CONFIG));
  59. conf->inputs = list_new("input");
  60. conf->nit = list_new("nit");
  61. conf->output = output_new();
  62. return conf;
  63. }
  64. void config_free(CONFIG **pconf) {
  65. CONFIG *conf = *pconf;
  66. if (conf) {
  67. conf->output->dienow = 1;
  68. list_free(&conf->nit, NULL, (void (*)(void **))nit_free);
  69. list_free(&conf->inputs, NULL, (void (*)(void **))input_free);
  70. list_free(&conf->channels, NULL, (void (*)(void **))channel_free);
  71. output_free(&conf->output);
  72. FREE(conf->ident);
  73. FREE(conf->pidfile);
  74. FREE(conf->server_addr);
  75. FREE(conf->loghost);
  76. FREE(conf->logident);
  77. FREE(conf->global_conf);
  78. FREE(conf->channels_conf);
  79. FREE(conf->nit_conf);
  80. FREE(conf->epg_conf);
  81. FREE(conf->network_name);
  82. FREE(conf->provider_name);
  83. FREE(*pconf);
  84. }
  85. }
  86. int config_load_channels(CONFIG *conf) {
  87. int num_channels = 0, i, j;
  88. LOGf("CONFIG: Loading channels config %s\n", conf->channels_conf);
  89. dictionary *ini = iniparser_load(conf->channels_conf);
  90. if (!ini) {
  91. LOGf("CONFIG: Error loading channels config (%s)\n", conf->channels_conf);
  92. return 0;
  93. }
  94. //iniparser_dump(ini, stdout);
  95. LIST *channels = list_new("channels");
  96. // Parse channels file
  97. conf->provider_name = ini_get_string_copy(ini, NULL, "Global:provider_name");
  98. conf->transport_stream_id = ini_get_int(ini, 0, "Global:transport_stream_id");
  99. conf->frequency = ini_get_string_copy(ini, NULL, "Global:frequency");
  100. conf->modulation = ini_get_string_copy(ini, NULL, "Global:modulation");
  101. conf->symbol_rate = ini_get_string_copy(ini, NULL, "Global:symbol_rate");
  102. // conf->epg_source = ini_get_string(ini, NULL, "EPG:source");
  103. for (i=1;i<32;i++) {
  104. CHANNEL *channel = NULL;
  105. int service_id = ini_get_int(ini, 0, "Channel%d:service_id", i);
  106. if (!service_id)
  107. continue;
  108. int is_radio = ini_get_bool(ini, 0, "Channel%d:radio", i);
  109. int lcn = ini_get_int(ini, 0, "Channel%d:lcn", i);
  110. int is_lcn_visible = ini_get_bool(ini, 0, "Channel%d:lcn_visible", i);
  111. char *id = ini_get_string(ini, NULL, "Channel%d:id", i);
  112. if (!id) {
  113. LOGf("CONFIG: Channel%d have no defined id\n", i);
  114. continue;
  115. }
  116. char *name = ini_get_string(ini, NULL, "Channel%d:name", i);
  117. if (!name) {
  118. LOGf("CONFIG: Channel%d have no defined name\n", i);
  119. continue;
  120. }
  121. for (j=1;j<8;j++) {
  122. char *source = ini_get_string(ini, NULL, "Channel%d:source%d", i, j);
  123. if (j == 1 && !source) {
  124. source = ini_get_string(ini, NULL, "Channel%d:source", i);
  125. }
  126. if (source) {
  127. if (!is_valid_url(source)) {
  128. LOGf("CONFIG: Invalid url: %s\n", source);
  129. continue;
  130. }
  131. // Init channel
  132. if (channel == NULL) {
  133. channel = channel_new(service_id, is_radio, id, name, source, lcn, is_lcn_visible);
  134. } else {
  135. chansrc_add(channel, source);
  136. }
  137. }
  138. }
  139. char *worktime = ini_get_string(ini, NULL, "Channel%d:worktime", i);
  140. int sh=0,sm=0,eh=0,em=0;
  141. if (worktime && sscanf(worktime, "%02d:%02d-%02d:%02d", &sh, &sm, &eh, &em) == 4) {
  142. channel->worktime_start = sh * 3600 + sm * 60;
  143. channel->worktime_end = eh * 3600 + em * 60;
  144. }
  145. // Channel is initialized, add it to channels list
  146. if (channel) {
  147. num_channels++;
  148. list_add(channels, channel);
  149. }
  150. }
  151. // iniparser_dump(ini, stderr);
  152. iniparser_freedict(&ini);
  153. // Check for channel changes
  154. struct timeval tv;
  155. gettimeofday(&tv, NULL);
  156. unsigned int randstate = tv.tv_usec;
  157. int cookie = rand_r(&randstate);
  158. /* Save current channels config */
  159. LIST *old_channels = conf->channels;
  160. /* Switch new channels config */
  161. conf->channels = channels;
  162. /* Rewrite restreamer channels */
  163. LNODE *lc, *lr, *lctmp, *lrtmp;
  164. CHANNEL *chan;
  165. list_lock(conf->inputs); // Unlocked after second list_for_each(restreamer)
  166. list_lock(conf->channels);
  167. list_for_each(conf->channels, lc, lctmp) {
  168. chan = lc->data;
  169. list_for_each(conf->inputs, lr, lrtmp) {
  170. if (strcmp(chan->name, ((INPUT *)lr->data)->name)==0) {
  171. INPUT *restr = lr->data;
  172. /* Mark the restreamer as valid */
  173. restr->cookie = cookie;
  174. /* Check if current source exists in new channel configuration */
  175. int src_found = -1;
  176. char *old_source = restr->channel->source;
  177. for (i=0; i<chan->num_src; i++) {
  178. if (strcmp(old_source, chan->sources[i]) == 0) {
  179. src_found = i;
  180. }
  181. }
  182. if (src_found > -1) {
  183. /* New configuration contains existing source, just update the reference */
  184. chansrc_set(chan, src_found);
  185. restr->channel = chan;
  186. } else {
  187. /* New configuration *DO NOT* contain existing source. Force reconnect */
  188. LOGf("INPUT: Source changed | Channel: %s srv_fd: %d Old:%s New:%s\n", chan->name, restr->sock, restr->channel->source, chan->source);
  189. /* The order is important! */
  190. chansrc_set(chan, chan->num_src-1); /* Set source to last one. On reconnect next source will be used. */
  191. restr->channel = chan;
  192. restr->reconnect = 1;
  193. }
  194. break;
  195. }
  196. }
  197. }
  198. list_unlock(conf->channels);
  199. /* Kill restreamers that serve channels that no longer exist */
  200. list_for_each(conf->inputs, lr, lrtmp) {
  201. INPUT *r = lr->data;
  202. /* This restreamer should no longer serve clients */
  203. if (r->cookie != cookie) {
  204. proxy_log(r, "Remove");
  205. /* Replace channel reference with real object and instruct free_restreamer to free it */
  206. r->channel = channel_new(r->channel->service_id, r->channel->radio, r->channel->id, r->channel->name, r->channel->source, r->channel->lcn, r->channel->lcn_visible);
  207. r->freechannel = 1;
  208. r->dienow = 1;
  209. }
  210. }
  211. list_unlock(conf->inputs);
  212. /* Free old_channels */
  213. list_free(&old_channels, NULL, (void (*)(void **))channel_free);
  214. LOGf("CONFIG: %d channels loaded\n", num_channels);
  215. return num_channels;
  216. }
  217. int config_load_global(CONFIG *conf) {
  218. LOGf("CONFIG: Loading global config %s\n", conf->global_conf);
  219. dictionary *ini = iniparser_load(conf->global_conf);
  220. if (!ini) {
  221. LOGf("CONFIG: Error loading global config (%s)\n", conf->global_conf);
  222. return 0;
  223. }
  224. conf->network_id = ini_get_int(ini, 0, "Global:network_id");
  225. conf->timeouts.pat = ini_get_int(ini, 100, "Timeouts:pat");
  226. conf->timeouts.pmt = ini_get_int(ini, 200, "Timeouts:pmt");
  227. conf->timeouts.sdt = ini_get_int(ini, 500, "Timeouts:sdt");
  228. conf->timeouts.nit = ini_get_int(ini, 2000, "Timeouts:nit");
  229. conf->timeouts.eit = ini_get_int(ini, 1000, "Timeouts:eit");
  230. conf->timeouts.tdt = ini_get_int(ini, 7500, "Timeouts:tdt");
  231. conf->timeouts.tot = ini_get_int(ini, 1500, "Timeouts:tot");
  232. conf->timeouts.stats = ini_get_int(ini, 0, "Timeouts:stats");
  233. //iniparser_dump(ini, stderr);
  234. iniparser_freedict(&ini);
  235. return 1;
  236. }
  237. int config_load_nit(CONFIG *conf) {
  238. LOGf("CONFIG: Loading nit config %s\n", conf->nit_conf);
  239. dictionary *ini = iniparser_load(conf->nit_conf);
  240. if (!ini) {
  241. LOGf("CONFIG: Error loading nit config (%s)\n", conf->nit_conf);
  242. return 0;
  243. }
  244. conf->network_name = ini_get_string_copy(ini, NULL, "Global:network_name");
  245. int i;
  246. for (i=1;i<32;i++) {
  247. uint16_t ts_id = ini_get_int (ini, 0, "Transponder%d:transport_stream_id", i);
  248. char *freq = ini_get_string(ini, NULL, "Transponder%d:frequency", i);
  249. char *mod = ini_get_string(ini, NULL, "Transponder%d:modulation", i);
  250. char *sr = ini_get_string(ini, NULL, "Transponder%d:symbol_rate", i);
  251. if (ts_id && freq && mod && sr) {
  252. if (strlen(freq) == 9 && strlen(sr) == 8) {
  253. list_add(conf->nit, nit_new(ts_id, freq, mod, sr));
  254. }
  255. }
  256. }
  257. //iniparser_dump(ini, stderr);
  258. iniparser_freedict(&ini);
  259. return 1;
  260. }
  261. static void config_reset_channels_epg(CONFIG *conf) {
  262. LNODE *lc, *lctmp;
  263. list_lock(conf->channels);
  264. list_for_each(conf->channels, lc, lctmp) {
  265. CHANNEL *c = lc->data;
  266. channel_free_epg(c);
  267. }
  268. list_unlock(conf->channels);
  269. conf->epg_conf_mtime = 0;
  270. }
  271. static EPG_ENTRY *config_load_epg_entry(dictionary *ini, char *entry, char *channel) {
  272. EPG_ENTRY *e = NULL;
  273. time_t start = ini_get_int(ini, 0, "%s-%s:start", channel, entry);
  274. int duration = ini_get_int(ini, 0, "%s-%s:duration", channel, entry);
  275. char *event = ini_get_string(ini, NULL, "%s-%s:event", channel, entry);
  276. char *sdesc = ini_get_string(ini, NULL, "%s-%s:sdescr", channel, entry);
  277. char *ldesc = ini_get_string(ini, NULL, "%s-%s:descr", channel, entry);
  278. char *enc = ini_get_string(ini, NULL, "%s-%s:encoding", channel, entry);
  279. if (start && duration && event) {
  280. e = epg_new(start, duration, enc, event, sdesc, ldesc);
  281. }
  282. return e;
  283. }
  284. static void config_channel_init_epg(CONFIG *conf, CHANNEL *c, EPG_ENTRY *now, EPG_ENTRY *next) {
  285. int updated = 0;
  286. if (epg_changed(now, c->epg_now)) {
  287. LOGf("EPG : %s | Now data changed\n", c->id);
  288. updated++;
  289. }
  290. if (epg_changed(next, c->epg_next)) {
  291. LOGf("EPG : %s | Next data changed\n", c->id);
  292. updated++;
  293. }
  294. if (!updated)
  295. return;
  296. // LOGf("EPG : %s | Version %d\n", c->epg_version);
  297. c->epg_version++;
  298. struct ts_eit *eit_now = ts_eit_alloc_init_pf(c->service_id, conf->transport_stream_id, conf->network_id, 0, 1);
  299. eit_now->section_header->version_number = c->epg_version;
  300. if (now) {
  301. ts_eit_add_short_event_descriptor(eit_now, now->event_id, 1, now->start, now->duration, now->event, now->short_desc);
  302. ts_eit_add_extended_event_descriptor(eit_now, now->event_id, 1, now->start, now->duration, now->long_desc);
  303. }
  304. ts_eit_regenerate_packets(eit_now);
  305. struct ts_eit *eit_next = ts_eit_alloc_init_pf(c->service_id, conf->transport_stream_id, conf->network_id, 1, 1);
  306. eit_next->section_header->version_number = c->epg_version;
  307. if (next) {
  308. ts_eit_add_short_event_descriptor(eit_next, next->event_id, 1, next->start, next->duration, next->event, next->short_desc);
  309. ts_eit_add_extended_event_descriptor(eit_next, next->event_id, 1, next->start, next->duration, next->long_desc);
  310. }
  311. ts_eit_regenerate_packets(eit_next);
  312. channel_free_epg(c);
  313. c->epg_now = now;
  314. c->epg_next = next;
  315. if (now || next) {
  316. c->eit_now = eit_now;
  317. c->eit_next = eit_next;
  318. //LOGf(" ***** NOW ******\n");
  319. //ts_eit_dump(eit_now);
  320. //LOGf(" ***** NEXT *****\n");
  321. //ts_eit_dump(eit_next);
  322. } else {
  323. ts_eit_free(&eit_now);
  324. ts_eit_free(&eit_next);
  325. }
  326. }
  327. int config_load_epg(CONFIG *conf) {
  328. struct stat st;
  329. if (stat(conf->epg_conf, &st) == 0) {
  330. if (st.st_mtime > conf->epg_conf_mtime) {
  331. // Set config last change time
  332. conf->epg_conf_mtime = st.st_mtime;
  333. } else {
  334. // LOGf("CONFIG: EPG config not changed since last check.\n");
  335. return 0; // The config has not changed!
  336. }
  337. } else {
  338. // Config file not found!
  339. LOGf("CONFIG: EPG config file is not found (%s)\n", conf->epg_conf);
  340. config_reset_channels_epg(conf);
  341. return 0;
  342. }
  343. // LOGf("CONFIG: Loading EPG config %s\n", conf->epg_conf);
  344. dictionary *ini = iniparser_load(conf->epg_conf);
  345. if (!ini) {
  346. LOGf("CONFIG: Error parsing EPG config (%s)\n", conf->epg_conf);
  347. config_reset_channels_epg(conf);
  348. return 0;
  349. }
  350. LNODE *lc, *lctmp;
  351. list_lock(conf->channels);
  352. list_for_each(conf->channels, lc, lctmp) {
  353. CHANNEL *c = lc->data;
  354. EPG_ENTRY *enow = config_load_epg_entry(ini, "now", c->id);
  355. EPG_ENTRY *enext = config_load_epg_entry(ini, "next", c->id);
  356. config_channel_init_epg(conf, c, enow, enext);
  357. }
  358. list_unlock(conf->channels);
  359. //iniparser_dump(ini, stderr);
  360. iniparser_freedict(&ini);
  361. return 1;
  362. }
  363. extern char *program_id;
  364. static void show_usage() {
  365. printf("%s\n", program_id);
  366. puts(copyright);
  367. puts("");
  368. puts("Identification:");
  369. puts("\t-i ident\tServer ident. (default: ux/mptsd)");
  370. puts("");
  371. puts("Server settings:");
  372. puts("\t-b addr\t\tLocal IP address to bind. (default: 0.0.0.0)");
  373. puts("\t-p port\t\tPort to listen. (default: 0)");
  374. puts("UDP Server settings:");
  375. puts("\t-s addr\t\tLocal IP address to bind. (default: 0.0.0.0)");
  376. puts("\t-u port\t\tPort to listen. (default: 2000)");
  377. puts("\t-d pidfile\tDaemonize with pidfile");
  378. puts("\t-l host\t\tSyslog host (default: disabled)");
  379. puts("\t-L port\t\tSyslog port (default: 514)");
  380. puts("");
  381. puts("Configuration files:");
  382. puts("\t-g file\t\tGlobal configuration file (default: mptsd.conf)");
  383. puts("\t-c file\t\tChannels configuration file (default: mptsd_channels.conf)");
  384. puts("\t-e file\t\tEPG configuration file (default: mptsd_epg.conf)");
  385. puts("\t-n file\t\tNIT configuration file (default: mptsd_nit.conf)");
  386. puts("");
  387. puts("Output settings:");
  388. puts("\t-O ip\t\tOutput udp address");
  389. puts("\t-P ip\t\tOutput udp port (default: 5000)");
  390. puts("\t-o ip\t\tOutput interface address (default: 0.0.0.0)");
  391. puts("\t-t ttl\t\tSet multicast ttl (default: 1)");
  392. puts("");
  393. puts("\t-B Mbps\t\tOutput bitrate in Mbps (default: 38.00)");
  394. puts("\t-m mode\t\tPCR mode (modes list bellow)");
  395. puts("\t\t\t- Mode 0: do not touch PCRs (default)");
  396. puts("\t\t\t- Mode 1: move PCRs to their calculated place");
  397. puts("\t\t\t- Mode 2: rewrite PCRs using output bitrate");
  398. puts("\t\t\t- Mode 3: move PCRs and rewrite them");
  399. puts("");
  400. puts("Other settings:");
  401. puts("\t-q\t\tQuiet");
  402. puts("\t-D\t\tDebug");
  403. puts("");
  404. puts("\t-W\t\tWrite output file");
  405. puts("\t-E\t\tWrite input file");
  406. puts("");
  407. }
  408. void config_load(CONFIG *conf, int argc, char **argv) {
  409. int j, ttl;
  410. conf->multicast_ttl = 1;
  411. conf->output->out_port = 5000;
  412. conf->output_bitrate = 38;
  413. conf->logport = 514;
  414. conf->server_port = 0;
  415. conf->server_socket = -1;
  416. conf->udp_server_port = 2000;
  417. conf->udp_server_socket = -1;
  418. while ((j = getopt(argc, argv, "i:b:p:s:u:g:c:n:e:d:t:o:O:P:l:L:B:m:qDHhWE")) != -1) {
  419. switch (j) {
  420. case 'i':
  421. conf->ident = strdup(optarg);
  422. conf->logident = strdup(optarg);
  423. char *c = conf->logident;
  424. while (*c) {
  425. if (*c=='/')
  426. *c='-';
  427. c++;
  428. }
  429. break;
  430. case 'b':
  431. conf->server_addr = strdup(optarg);
  432. break;
  433. case 'p':
  434. conf->server_port = atoi(optarg);
  435. break;
  436. case 's':
  437. conf->udp_server_addr = strdup(optarg);
  438. break;
  439. case 'u':
  440. conf->udp_server_port = atoi(optarg);
  441. break;
  442. case 'd':
  443. conf->pidfile = strdup(optarg);
  444. break;
  445. case 'g':
  446. conf->global_conf = strdup(optarg);
  447. break;
  448. case 'c':
  449. conf->channels_conf = strdup(optarg);
  450. break;
  451. case 'n':
  452. conf->nit_conf = strdup(optarg);
  453. break;
  454. case 'e':
  455. conf->epg_conf = strdup(optarg);
  456. break;
  457. case 'o':
  458. if (inet_aton(optarg, &conf->output_intf) == 0) {
  459. fprintf(stderr, "Invalid interface address: %s\n", optarg);
  460. exit(1);
  461. }
  462. break;
  463. case 'O':
  464. if (inet_aton(optarg, &(conf->output->out_host)) == 0) {
  465. fprintf(stderr, "Invalid output address: %s\n", optarg);
  466. exit(1);
  467. }
  468. break;
  469. case 'P':
  470. conf->output->out_port = atoi(optarg);
  471. if (!conf->output->out_port || conf->output->out_port < 1024) {
  472. fprintf(stderr, "Invalid output port: %s\n", optarg);
  473. exit(1);
  474. }
  475. break;
  476. case 'm':
  477. conf->pcr_mode = atoi(optarg);
  478. if (conf->pcr_mode < 0 || conf->pcr_mode > 4)
  479. conf->pcr_mode = 0;
  480. break;
  481. case 't':
  482. ttl = atoi(optarg);
  483. conf->multicast_ttl = (ttl && ttl < 127) ? ttl : 1;
  484. break;
  485. case 'l':
  486. conf->loghost = strdup(optarg);
  487. conf->syslog_active = 1;
  488. break;
  489. case 'L':
  490. conf->logport = atoi(optarg);
  491. break;
  492. case 'B':
  493. conf->output_bitrate = atof(optarg);
  494. if (conf->output_bitrate < 2 || conf->output_bitrate > 75) {
  495. fprintf(stderr, "Invalid output bitrate: %.2f (valid 2-75)\n", conf->output_bitrate);
  496. exit(1);
  497. }
  498. break;
  499. case 'W':
  500. conf->write_output_file = 1;
  501. output_open_file(conf->output);
  502. break;
  503. case 'E':
  504. conf->write_input_file = 1;
  505. break;
  506. case 'D':
  507. conf->debug = 1;
  508. break;
  509. case 'q':
  510. conf->quiet = 1;
  511. break;
  512. case 'H':
  513. case 'h':
  514. show_usage(0);
  515. exit(0);
  516. break;
  517. }
  518. }
  519. if (!conf->output->out_host.s_addr) {
  520. fprintf(stderr, "ERROR: Output address is not set (use -O x.x.x.x)\n");
  521. show_usage();
  522. goto ERR;
  523. }
  524. // Set defaults
  525. if (!conf->ident) {
  526. conf->ident = strdup("ux/mptsd");
  527. conf->logident = strdup("ux-mptsd");
  528. }
  529. if (!conf->global_conf) {
  530. conf->global_conf = strdup("mptsd.conf");
  531. }
  532. if (!conf->channels_conf) {
  533. conf->channels_conf = strdup("mptsd_channels.conf");
  534. }
  535. if (!conf->nit_conf) {
  536. conf->nit_conf = strdup("mptsd_nit.conf");
  537. }
  538. if (!conf->epg_conf) {
  539. conf->epg_conf = strdup("mptsd_epg.conf");
  540. }
  541. // Align bitrate to 1 packet (1316 bytes)
  542. conf->output_bitrate *= 1000000; // In bytes
  543. conf->output_packets_per_sec = ceil(conf->output_bitrate / (1316 * 8));
  544. conf->output_bitrate = conf->output_packets_per_sec * (1316 * 8);
  545. conf->output_tmout = 1000000 / conf->output_packets_per_sec;
  546. if (conf->server_port)
  547. init_server_socket(conf->server_addr, conf->server_port, &conf->server, &conf->server_socket);
  548. // if (conf->udp_server_port)
  549. // init_server_socket_udp(conf->server_addr, conf->udp_server_port, &conf->server, &conf->udp_server_socket);
  550. if (!conf->quiet) {
  551. printf("Configuration:\n");
  552. printf("\tServer ident : %s\n", conf->ident);
  553. printf("\tGlobal config : %s\n", conf->global_conf);
  554. printf("\tChannels config : %s\n", conf->channels_conf);
  555. printf("\tNIT config : %s\n", conf->nit_conf);
  556. printf("\tOutput addr : udp://%s:%d\n", inet_ntoa(conf->output->out_host), conf->output->out_port);
  557. if (conf->output_intf.s_addr)
  558. printf("\tOutput iface addr : %s\n", inet_ntoa(conf->output_intf));
  559. printf("\tMulticast ttl : %d\n", conf->multicast_ttl);
  560. if (conf->syslog_active) {
  561. printf("\tSyslog host : %s\n", conf->loghost);
  562. printf("\tSyslog port : %d\n", conf->logport);
  563. } else {
  564. printf("\tSyslog disabled.\n");
  565. }
  566. printf("\tOutput bitrate : %.0f bps, %.2f Kbps, %.2f Mbps\n", conf->output_bitrate, conf->output_bitrate / 1000, conf->output_bitrate / 1000000);
  567. printf("\tOutput pkt tmout : %ld us\n", conf->output_tmout);
  568. printf("\tPackets per second: %ld\n", conf->output_packets_per_sec);
  569. printf("\tPCR mode : %s\n",
  570. conf->pcr_mode == 0 ? "Do not touch PCRs" :
  571. conf->pcr_mode == 1 ? "Move PCRs to their calculated place" :
  572. conf->pcr_mode == 2 ? "Rewrite PCRs using output bitrate" :
  573. conf->pcr_mode == 3 ? "Move PCRs and rewrite them" : "???"
  574. );
  575. if (conf->write_output_file)
  576. printf("\tWrite output file\n");
  577. if (conf->write_input_file)
  578. printf("\tWrite input file(s)\n");
  579. }
  580. pthread_t sleepthread;
  581. if (pthread_create(&sleepthread, NULL, &calibrate_sleep, conf) == 0) {
  582. pthread_join(sleepthread, NULL);
  583. } else {
  584. perror("calibrate_thread");
  585. exit(1);
  586. }
  587. if (!conf->output_tmout)
  588. exit(1);
  589. output_buffer_alloc(conf->output, conf->output_bitrate);
  590. if (!config_load_global(conf))
  591. goto ERR;
  592. if (!config_load_nit(conf))
  593. goto ERR;
  594. if (!config_load_channels(conf))
  595. goto ERR;
  596. if (!config_load_epg(conf))
  597. LOGf("CONFIG: Run without EIT (%s)\n", conf->epg_conf);
  598. return;
  599. ERR:
  600. config_free(&conf);
  601. exit(1);
  602. }