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/

config.c 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643
  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. for (i=1;i<32;i++) {
  100. CHANNEL *channel = NULL;
  101. int service_id = ini_get_int(ini, 0, "Channel%d:service_id", i);
  102. if (!service_id)
  103. continue;
  104. int is_radio = ini_get_bool(ini, 0, "Channel%d:radio", i);
  105. char *id = ini_get_string(ini, NULL, "Channel%d:id", i);
  106. if (!id) {
  107. LOGf("CONFIG: Channel%d have no defined id\n", i);
  108. continue;
  109. }
  110. char *name = ini_get_string(ini, NULL, "Channel%d:name", i);
  111. if (!name) {
  112. LOGf("CONFIG: Channel%d have no defined name\n", i);
  113. continue;
  114. }
  115. for (j=1;j<8;j++) {
  116. char *source = ini_get_string(ini, NULL, "Channel%d:source%d", i, j);
  117. if (j == 1 && !source) {
  118. source = ini_get_string(ini, NULL, "Channel%d:source", i);
  119. }
  120. if (source) {
  121. if (!is_valid_url(source)) {
  122. LOGf("CONFIG: Invalid url: %s\n", source);
  123. continue;
  124. }
  125. // Init channel
  126. if (channel == NULL) {
  127. channel = channel_new(service_id, is_radio, id, name, source, i);
  128. } else {
  129. chansrc_add(channel, source);
  130. }
  131. }
  132. }
  133. char *worktime = ini_get_string(ini, NULL, "Channel%d:worktime", i);
  134. int sh=0,sm=0,eh=0,em=0;
  135. if (worktime && sscanf(worktime, "%02d:%02d-%02d:%02d", &sh, &sm, &eh, &em) == 4) {
  136. channel->worktime_start = sh * 3600 + sm * 60;
  137. channel->worktime_end = eh * 3600 + em * 60;
  138. }
  139. // Channel is initialized, add it to channels list
  140. if (channel) {
  141. num_channels++;
  142. list_add(channels, channel);
  143. }
  144. }
  145. // iniparser_dump(ini, stderr);
  146. iniparser_freedict(&ini);
  147. // Check for channel changes
  148. struct timeval tv;
  149. gettimeofday(&tv, NULL);
  150. unsigned int randstate = tv.tv_usec;
  151. int cookie = rand_r(&randstate);
  152. /* Save current channels config */
  153. LIST *old_channels = conf->channels;
  154. /* Switch new channels config */
  155. conf->channels = channels;
  156. /* Rewrite restreamer channels */
  157. LNODE *lc, *lr, *lctmp, *lrtmp;
  158. CHANNEL *chan;
  159. list_lock(conf->inputs); // Unlocked after second list_for_each(restreamer)
  160. list_lock(conf->channels);
  161. list_for_each(conf->channels, lc, lctmp) {
  162. chan = lc->data;
  163. list_for_each(conf->inputs, lr, lrtmp) {
  164. if (strcmp(chan->name, ((INPUT *)lr->data)->name)==0) {
  165. INPUT *restr = lr->data;
  166. /* Mark the restreamer as valid */
  167. restr->cookie = cookie;
  168. /* Check if current source exists in new channel configuration */
  169. int src_found = -1;
  170. char *old_source = restr->channel->source;
  171. for (i=0; i<chan->num_src; i++) {
  172. if (strcmp(old_source, chan->sources[i]) == 0) {
  173. src_found = i;
  174. }
  175. }
  176. if (src_found > -1) {
  177. /* New configuration contains existing source, just update the reference */
  178. chansrc_set(chan, src_found);
  179. restr->channel = chan;
  180. } else {
  181. /* New configuration *DO NOT* contain existing source. Force reconnect */
  182. LOGf("INPUT: Source changed | Channel: %s srv_fd: %d Old:%s New:%s\n", chan->name, restr->sock, restr->channel->source, chan->source);
  183. /* The order is important! */
  184. chansrc_set(chan, chan->num_src-1); /* Set source to last one. On reconnect next source will be used. */
  185. restr->channel = chan;
  186. restr->reconnect = 1;
  187. }
  188. break;
  189. }
  190. }
  191. }
  192. list_unlock(conf->channels);
  193. /* Kill restreamers that serve channels that no longer exist */
  194. list_for_each(conf->inputs, lr, lrtmp) {
  195. INPUT *r = lr->data;
  196. /* This restreamer should no longer serve clients */
  197. if (r->cookie != cookie) {
  198. proxy_log(r, "Remove");
  199. /* Replace channel reference with real object and instruct free_restreamer to free it */
  200. r->channel = channel_new(r->channel->service_id, r->channel->radio, r->channel->id, r->channel->name, r->channel->source, r->channel->index);
  201. r->freechannel = 1;
  202. r->dienow = 1;
  203. }
  204. }
  205. list_unlock(conf->inputs);
  206. /* Free old_channels */
  207. list_free(&old_channels, NULL, (void (*)(void **))channel_free);
  208. LOGf("CONFIG: %d channels loaded\n", num_channels);
  209. return num_channels;
  210. }
  211. int config_load_global(CONFIG *conf) {
  212. LOGf("CONFIG: Loading global config %s\n", conf->global_conf);
  213. dictionary *ini = iniparser_load(conf->global_conf);
  214. if (!ini) {
  215. LOGf("CONFIG: Error loading global config (%s)\n", conf->global_conf);
  216. return 0;
  217. }
  218. conf->network_id = ini_get_int(ini, 0, "Global:network_id");
  219. conf->timeouts.pat = ini_get_int(ini, 100, "Timeouts:pat");
  220. conf->timeouts.pmt = ini_get_int(ini, 200, "Timeouts:pmt");
  221. conf->timeouts.sdt = ini_get_int(ini, 500, "Timeouts:sdt");
  222. conf->timeouts.nit = ini_get_int(ini, 2000, "Timeouts:nit");
  223. conf->timeouts.eit = ini_get_int(ini, 1000, "Timeouts:eit");
  224. conf->timeouts.tdt = ini_get_int(ini, 7500, "Timeouts:tdt");
  225. conf->timeouts.tot = ini_get_int(ini, 1500, "Timeouts:tot");
  226. conf->timeouts.stats = ini_get_int(ini, 0, "Timeouts:stats");
  227. //iniparser_dump(ini, stderr);
  228. iniparser_freedict(&ini);
  229. return 1;
  230. }
  231. int config_load_nit(CONFIG *conf) {
  232. LOGf("CONFIG: Loading nit config %s\n", conf->nit_conf);
  233. dictionary *ini = iniparser_load(conf->nit_conf);
  234. if (!ini) {
  235. LOGf("CONFIG: Error loading nit config (%s)\n", conf->nit_conf);
  236. return 0;
  237. }
  238. conf->network_name = ini_get_string_copy(ini, NULL, "Global:network_name");
  239. int i;
  240. for (i=1;i<32;i++) {
  241. uint16_t ts_id = ini_get_int (ini, 0, "Transponder%d:transport_stream_id", i);
  242. char *freq = ini_get_string(ini, NULL, "Transponder%d:frequency", i);
  243. char *mod = ini_get_string(ini, NULL, "Transponder%d:modulation", i);
  244. char *sr = ini_get_string(ini, NULL, "Transponder%d:symbol_rate", i);
  245. if (ts_id && freq && mod && sr) {
  246. if (strlen(freq) == 9 && strlen(sr) == 8) {
  247. list_add(conf->nit, nit_new(ts_id, freq, mod, sr));
  248. }
  249. }
  250. }
  251. //iniparser_dump(ini, stderr);
  252. iniparser_freedict(&ini);
  253. return 1;
  254. }
  255. static void config_reset_channels_epg(CONFIG *conf) {
  256. LNODE *lc, *lctmp;
  257. list_lock(conf->channels);
  258. list_for_each(conf->channels, lc, lctmp) {
  259. CHANNEL *c = lc->data;
  260. channel_free_epg(c);
  261. }
  262. list_unlock(conf->channels);
  263. conf->epg_conf_mtime = 0;
  264. }
  265. static EPG_ENTRY *config_load_epg_entry(dictionary *ini, char *entry, char *channel) {
  266. EPG_ENTRY *e = NULL;
  267. time_t start = ini_get_int(ini, 0, "%s-%s:start", channel, entry);
  268. int duration = ini_get_int(ini, 0, "%s-%s:duration", channel, entry);
  269. char *event = ini_get_string(ini, NULL, "%s-%s:event", channel, entry);
  270. char *sdesc = ini_get_string(ini, NULL, "%s-%s:sdescr", channel, entry);
  271. char *ldesc = ini_get_string(ini, NULL, "%s-%s:descr", channel, entry);
  272. char *enc = ini_get_string(ini, NULL, "%s-%s:encoding", channel, entry);
  273. if (start && duration && event) {
  274. e = epg_new(start, duration, enc, event, sdesc, ldesc);
  275. }
  276. return e;
  277. }
  278. static void config_channel_init_epg(CONFIG *conf, CHANNEL *c, EPG_ENTRY *now, EPG_ENTRY *next) {
  279. int updated = 0;
  280. if (epg_changed(now, c->epg_now)) {
  281. LOGf("EPG : %s | Now data changed\n", c->id);
  282. updated++;
  283. }
  284. if (epg_changed(next, c->epg_next)) {
  285. LOGf("EPG : %s | Next data changed\n", c->id);
  286. updated++;
  287. }
  288. if (!updated)
  289. return;
  290. // LOGf("EPG : %s | Version %d\n", c->epg_version);
  291. c->epg_version++;
  292. struct ts_eit *eit_now = ts_eit_alloc_init_pf(c->service_id, conf->transport_stream_id, conf->network_id, 0, 1);
  293. eit_now->section_header->version_number = c->epg_version;
  294. if (now) {
  295. ts_eit_add_short_event_descriptor(eit_now, now->event_id, 1, now->start, now->duration, now->event, now->short_desc);
  296. ts_eit_add_extended_event_descriptor(eit_now, now->event_id, 1, now->start, now->duration, now->long_desc);
  297. }
  298. ts_eit_regenerate_packets(eit_now);
  299. struct ts_eit *eit_next = ts_eit_alloc_init_pf(c->service_id, conf->transport_stream_id, conf->network_id, 1, 1);
  300. eit_next->section_header->version_number = c->epg_version;
  301. if (next) {
  302. ts_eit_add_short_event_descriptor(eit_next, next->event_id, 1, next->start, next->duration, next->event, next->short_desc);
  303. ts_eit_add_extended_event_descriptor(eit_next, next->event_id, 1, next->start, next->duration, next->long_desc);
  304. }
  305. ts_eit_regenerate_packets(eit_next);
  306. channel_free_epg(c);
  307. c->epg_now = now;
  308. c->epg_next = next;
  309. if (now || next) {
  310. c->eit_now = eit_now;
  311. c->eit_next = eit_next;
  312. //LOGf(" ***** NOW ******\n");
  313. //ts_eit_dump(eit_now);
  314. //LOGf(" ***** NEXT *****\n");
  315. //ts_eit_dump(eit_next);
  316. } else {
  317. ts_eit_free(&eit_now);
  318. ts_eit_free(&eit_next);
  319. }
  320. }
  321. int config_load_epg(CONFIG *conf) {
  322. struct stat st;
  323. if (stat(conf->epg_conf, &st) == 0) {
  324. if (st.st_mtime > conf->epg_conf_mtime) {
  325. // Set config last change time
  326. conf->epg_conf_mtime = st.st_mtime;
  327. } else {
  328. // LOGf("CONFIG: EPG config not changed since last check.\n");
  329. return 0; // The config has not changed!
  330. }
  331. } else {
  332. // Config file not found!
  333. LOGf("CONFIG: EPG config file is not found (%s)\n", conf->epg_conf);
  334. config_reset_channels_epg(conf);
  335. return 0;
  336. }
  337. // LOGf("CONFIG: Loading EPG config %s\n", conf->epg_conf);
  338. dictionary *ini = iniparser_load(conf->epg_conf);
  339. if (!ini) {
  340. LOGf("CONFIG: Error parsing EPG config (%s)\n", conf->epg_conf);
  341. config_reset_channels_epg(conf);
  342. return 0;
  343. }
  344. LNODE *lc, *lctmp;
  345. list_lock(conf->channels);
  346. list_for_each(conf->channels, lc, lctmp) {
  347. CHANNEL *c = lc->data;
  348. EPG_ENTRY *enow = config_load_epg_entry(ini, "now", c->id);
  349. EPG_ENTRY *enext = config_load_epg_entry(ini, "next", c->id);
  350. config_channel_init_epg(conf, c, enow, enext);
  351. }
  352. list_unlock(conf->channels);
  353. //iniparser_dump(ini, stderr);
  354. iniparser_freedict(&ini);
  355. return 1;
  356. }
  357. extern char *program_id;
  358. static void show_usage(void) {
  359. printf("%s\n", program_id);
  360. puts(copyright);
  361. puts("");
  362. puts("Identification:");
  363. puts("\t-i ident\tServer ident. (default: ux/mptsd)");
  364. puts("");
  365. puts("Server settings:");
  366. puts("\t-b addr\t\tLocal IP address to bind. (default: 0.0.0.0)");
  367. puts("\t-p port\t\tPort to listen. (default: 0)");
  368. puts("\t-d pidfile\tDaemonize with pidfile");
  369. puts("\t-l host\t\tSyslog host (default: disabled)");
  370. puts("\t-L port\t\tSyslog port (default: 514)");
  371. puts("");
  372. puts("Configuration files:");
  373. puts("\t-g file\t\tGlobal configuration file (default: mptsd.conf)");
  374. puts("\t-c file\t\tChannels configuration file (default: mptsd_channels.conf)");
  375. puts("\t-e file\t\tEPG configuration file (default: mptsd_epg.conf)");
  376. puts("\t-n file\t\tNIT configuration file (default: mptsd_nit.conf)");
  377. puts("");
  378. puts("Output settings:");
  379. puts("\t-O ip\t\tOutput udp address");
  380. puts("\t-P ip\t\tOutput udp port (default: 5000)");
  381. puts("\t-o ip\t\tOutput interface address (default: 0.0.0.0)");
  382. puts("\t-t ttl\t\tSet multicast ttl (default: 1)");
  383. puts("");
  384. puts("\t-B Mbps\t\tOutput bitrate in Mbps (default: 38.00)");
  385. puts("\t-m mode\t\tPCR mode (modes list bellow)");
  386. puts("\t\t\t- Mode 0: do not touch PCRs (default)");
  387. puts("\t\t\t- Mode 1: move PCRs to their calculated place");
  388. puts("\t\t\t- Mode 2: rewrite PCRs using output bitrate");
  389. puts("\t\t\t- Mode 3: move PCRs and rewrite them");
  390. puts("");
  391. puts("Other settings:");
  392. puts("\t-q\t\tQuiet");
  393. puts("\t-D\t\tDebug");
  394. puts("");
  395. puts("\t-W\t\tWrite output file");
  396. puts("\t-E\t\tWrite input file");
  397. puts("");
  398. }
  399. void config_load(CONFIG *conf, int argc, char **argv) {
  400. int j, ttl;
  401. conf->multicast_ttl = 1;
  402. conf->output->out_port = 5000;
  403. conf->output_bitrate = 38;
  404. conf->logport = 514;
  405. conf->server_port = 0;
  406. conf->server_socket = -1;
  407. while ((j = getopt(argc, argv, "i:b:p:g:c:n:e:d:t:o:O:P:l:L:B:m:qDHhWE")) != -1) {
  408. switch (j) {
  409. case 'i':
  410. conf->ident = strdup(optarg);
  411. conf->logident = strdup(optarg);
  412. char *c = conf->logident;
  413. while (*c) {
  414. if (*c=='/')
  415. *c='-';
  416. c++;
  417. }
  418. break;
  419. case 'b':
  420. conf->server_addr = strdup(optarg);
  421. break;
  422. case 'p':
  423. conf->server_port = atoi(optarg);
  424. break;
  425. case 'd':
  426. conf->pidfile = strdup(optarg);
  427. break;
  428. case 'g':
  429. conf->global_conf = strdup(optarg);
  430. break;
  431. case 'c':
  432. conf->channels_conf = strdup(optarg);
  433. break;
  434. case 'n':
  435. conf->nit_conf = strdup(optarg);
  436. break;
  437. case 'e':
  438. conf->epg_conf = strdup(optarg);
  439. break;
  440. case 'o':
  441. if (inet_aton(optarg, &conf->output_intf) == 0) {
  442. fprintf(stderr, "Invalid interface address: %s\n", optarg);
  443. exit(1);
  444. }
  445. break;
  446. case 'O':
  447. if (inet_aton(optarg, &(conf->output->out_host)) == 0) {
  448. fprintf(stderr, "Invalid output address: %s\n", optarg);
  449. exit(1);
  450. }
  451. break;
  452. case 'P':
  453. conf->output->out_port = atoi(optarg);
  454. if (!conf->output->out_port || conf->output->out_port < 1024) {
  455. fprintf(stderr, "Invalid output port: %s\n", optarg);
  456. exit(1);
  457. }
  458. break;
  459. case 'm':
  460. conf->pcr_mode = atoi(optarg);
  461. if (conf->pcr_mode < 0 || conf->pcr_mode > 4)
  462. conf->pcr_mode = 0;
  463. break;
  464. case 't':
  465. ttl = atoi(optarg);
  466. conf->multicast_ttl = (ttl && ttl < 127) ? ttl : 1;
  467. break;
  468. case 'l':
  469. conf->loghost = strdup(optarg);
  470. conf->syslog_active = 1;
  471. break;
  472. case 'L':
  473. conf->logport = atoi(optarg);
  474. break;
  475. case 'B':
  476. conf->output_bitrate = atof(optarg);
  477. if (conf->output_bitrate < 2 || conf->output_bitrate > 75) {
  478. fprintf(stderr, "Invalid output bitrate: %.2f (valid 2-75)\n", conf->output_bitrate);
  479. exit(1);
  480. }
  481. break;
  482. case 'W':
  483. conf->write_output_file = 1;
  484. output_open_file(conf->output);
  485. break;
  486. case 'E':
  487. conf->write_input_file = 1;
  488. break;
  489. case 'D':
  490. conf->debug = 1;
  491. break;
  492. case 'q':
  493. conf->quiet = 1;
  494. break;
  495. case 'H':
  496. case 'h':
  497. show_usage();
  498. exit(0);
  499. break;
  500. }
  501. }
  502. if (!conf->output->out_host.s_addr) {
  503. fprintf(stderr, "ERROR: Output address is not set (use -O x.x.x.x)\n");
  504. show_usage();
  505. goto ERR;
  506. }
  507. // Set defaults
  508. if (!conf->ident) {
  509. conf->ident = strdup("ux/mptsd");
  510. conf->logident = strdup("ux-mptsd");
  511. }
  512. if (!conf->global_conf) {
  513. conf->global_conf = strdup("mptsd.conf");
  514. }
  515. if (!conf->channels_conf) {
  516. conf->channels_conf = strdup("mptsd_channels.conf");
  517. }
  518. if (!conf->nit_conf) {
  519. conf->nit_conf = strdup("mptsd_nit.conf");
  520. }
  521. if (!conf->epg_conf) {
  522. conf->epg_conf = strdup("mptsd_epg.conf");
  523. }
  524. // Align bitrate to 1 packet (1316 bytes)
  525. conf->output_bitrate *= 1000000; // In bytes
  526. conf->output_packets_per_sec = ceil(conf->output_bitrate / (1316 * 8));
  527. conf->output_bitrate = conf->output_packets_per_sec * (1316 * 8);
  528. conf->output_tmout = 1000000 / conf->output_packets_per_sec;
  529. if (conf->server_port)
  530. init_server_socket(conf->server_addr, conf->server_port, &conf->server, &conf->server_socket);
  531. if (!conf->quiet) {
  532. printf("Configuration:\n");
  533. printf("\tServer ident : %s\n", conf->ident);
  534. printf("\tGlobal config : %s\n", conf->global_conf);
  535. printf("\tChannels config : %s\n", conf->channels_conf);
  536. printf("\tNIT config : %s\n", conf->nit_conf);
  537. printf("\tOutput addr : udp://%s:%d\n", inet_ntoa(conf->output->out_host), conf->output->out_port);
  538. if (conf->output_intf.s_addr)
  539. printf("\tOutput iface addr : %s\n", inet_ntoa(conf->output_intf));
  540. printf("\tMulticast ttl : %d\n", conf->multicast_ttl);
  541. if (conf->syslog_active) {
  542. printf("\tSyslog host : %s\n", conf->loghost);
  543. printf("\tSyslog port : %d\n", conf->logport);
  544. } else {
  545. printf("\tSyslog disabled.\n");
  546. }
  547. printf("\tOutput bitrate : %.0f bps, %.2f Kbps, %.2f Mbps\n", conf->output_bitrate, conf->output_bitrate / 1000, conf->output_bitrate / 1000000);
  548. printf("\tOutput pkt tmout : %ld us\n", conf->output_tmout);
  549. printf("\tPackets per second: %ld\n", conf->output_packets_per_sec);
  550. printf("\tPCR mode : %s\n",
  551. conf->pcr_mode == 0 ? "Do not touch PCRs" :
  552. conf->pcr_mode == 1 ? "Move PCRs to their calculated place" :
  553. conf->pcr_mode == 2 ? "Rewrite PCRs using output bitrate" :
  554. conf->pcr_mode == 3 ? "Move PCRs and rewrite them" : "???"
  555. );
  556. if (conf->write_output_file)
  557. printf("\tWrite output file\n");
  558. if (conf->write_input_file)
  559. printf("\tWrite input file(s)\n");
  560. }
  561. pthread_t sleepthread;
  562. if (pthread_create(&sleepthread, NULL, &calibrate_sleep, conf) == 0) {
  563. pthread_join(sleepthread, NULL);
  564. } else {
  565. perror("calibrate_thread");
  566. exit(1);
  567. }
  568. if (!conf->output_tmout)
  569. exit(1);
  570. output_buffer_alloc(conf->output, conf->output_bitrate);
  571. if (!config_load_global(conf))
  572. goto ERR;
  573. if (!config_load_nit(conf))
  574. goto ERR;
  575. if (!config_load_channels(conf))
  576. goto ERR;
  577. config_load_epg(conf);
  578. return;
  579. ERR:
  580. config_free(&conf);
  581. exit(1);
  582. }