tsdecrypt reads and decrypts CSA encrypted incoming mpeg transport stream over UDP/RTP using code words obtained from OSCAM or similar CAM server. tsdecrypt communicates with CAM server using cs378x (camd35 over tcp) protocol or newcamd protocol. https://georgi.unixsol.org/programs/tsdecrypt/
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.

tsdecrypt.c 21KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611
  1. /*
  2. * tsdecrypt
  3. * Copyright (C) 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 <stdlib.h>
  19. #include <unistd.h>
  20. #include <getopt.h>
  21. #include <string.h>
  22. #include <sys/types.h>
  23. #include <sys/stat.h>
  24. #include <signal.h>
  25. #include <fcntl.h>
  26. #include <errno.h>
  27. #include "data.h"
  28. #include "util.h"
  29. #include "camd.h"
  30. #include "process.h"
  31. #include "udp.h"
  32. #define FIRST_REPORT_SEC 3
  33. #define PROGRAM_NAME "tsdecrypt"
  34. static const char *program_id = PROGRAM_NAME " v" VERSION " (" GIT_VER ", build " BUILD_ID ")";
  35. static int keep_running = 1;
  36. static void LOG_func(const char *msg) {
  37. char date[64];
  38. struct tm tm;
  39. time_t now;
  40. now = time(NULL);
  41. localtime_r(&now, &tm);
  42. strftime(date, sizeof(date), "%F %H:%M:%S", localtime(&now));
  43. fprintf(stderr, "%s | %s", date, msg);
  44. }
  45. static const struct option long_options[] = {
  46. { "ident", required_argument, NULL, 'i' },
  47. { "daemon", required_argument, NULL, 'd' },
  48. { "syslog-host", required_argument, NULL, 'l' },
  49. { "syslog-port", required_argument, NULL, 'L' },
  50. { "input", required_argument, NULL, 'I' },
  51. { "input-rtp", no_argument, NULL, 'R' },
  52. { "input-ignore-disc", no_argument, NULL, 'z' },
  53. { "output", required_argument, NULL, 'O' },
  54. { "output-intf", required_argument, NULL, 'o' },
  55. { "output-ttl", required_argument, NULL, 't' },
  56. { "output-filter", no_argument, NULL, 'p' },
  57. { "ca-system", required_argument, NULL, 'c' },
  58. { "caid", required_argument, NULL, 'C' },
  59. { "camd-server", required_argument, NULL, 's' },
  60. { "camd-user", required_argument, NULL, 'U' },
  61. { "camd-pass", required_argument, NULL, 'P' },
  62. { "camd-pkt-delay", required_argument, NULL, 'y' },
  63. { "emm", no_argument, NULL, 'e' },
  64. { "emm-pid", required_argument, NULL, 'Z' },
  65. { "emm-only", no_argument, NULL, 'E' },
  66. { "emm-report-time", required_argument, NULL, 'f' },
  67. { "ecm-pid", required_argument, NULL, 'X' },
  68. { "ecm-report-time", required_argument, NULL, 'H' },
  69. { "ecm-irdeto-type", required_argument, NULL, 'G' },
  70. { "ecm-no-log", no_argument , NULL, 'K' },
  71. { "cw-warn-time", required_argument, NULL, 'J' },
  72. { "debug", required_argument, NULL, 'D' },
  73. { "help", no_argument, NULL, 'h' },
  74. { "version", no_argument, NULL, 'V' },
  75. { 0, 0, 0, 0 }
  76. };
  77. static void show_help(struct ts *ts) {
  78. printf("%s\n", program_id);
  79. printf("Copyright (c) 2011 Unix Solutions Ltd.\n");
  80. printf("\n");
  81. printf(" Usage: " PROGRAM_NAME " [opts]\n");
  82. printf("\n");
  83. printf("Daemon options:\n");
  84. printf(" -i --ident <server> | Format PROVIDER/CHANNEL. Default: empty\n");
  85. printf(" -d --daemon <pidfile> | Daemonize program and write pid file.\n");
  86. printf(" -l --syslog-host <host> | Syslog server address. Default: disabled\n");
  87. printf(" -L --syslog-port <port> | Syslog server port. Default: %d\n", ts->syslog_port);
  88. printf("\n");
  89. printf("Input options:\n");
  90. printf(" -I --input <source> | Where to read from. File or multicast address.\n");
  91. printf(" . -I 224.0.0.1:5000 (multicast receive)\n");
  92. printf(" . -I file.ts (read from file)\n");
  93. printf(" . -I - (read from stdin) (default)\n");
  94. printf(" -R --input-rtp | Enable RTP input\n");
  95. printf(" -z --input-ignore-disc | Do not report discontinuty errors in input.\n");
  96. printf("\n");
  97. printf("Output options:\n");
  98. printf(" -O --output <dest> | Where to send output. File or multicast address.\n");
  99. printf(" . -O 239.0.0.1:5000 (multicast send)\n");
  100. printf(" . -O file.ts (write to file)\n");
  101. printf(" . -O - (write to stdout) (default)\n");
  102. printf(" -o --output-intf <addr> | Set multicast output interface. Default: %s\n", inet_ntoa(ts->output.intf));
  103. printf(" -t --output-ttl <ttl> | Set multicast ttl. Default: %d\n", ts->output.ttl);
  104. printf(" -p --output-filter | Enable or disable output filter. Default: %s\n", ts->pid_filter ? "enabled" : "disabled");
  105. printf("\n");
  106. printf("CA options:\n");
  107. printf(" -c --ca-system <ca_sys> | Process input EMM/ECM from <ca_sys>.\n");
  108. printf(" | Valid systems are: CONAX (default), CRYPTOWORKS,\n");
  109. printf(" . IRDETO, SECA (MEDIAGUARD), VIACCESS,\n");
  110. printf(" . VIDEOGUARD (NDS), NAGRA and DRECRYPT.\n");
  111. printf(" -C --caid <caid> | Set CAID. Default: Taken from --ca-system.\n");
  112. printf("\n");
  113. printf("CAMD server options:\n");
  114. printf(" -s --camd-server <addr> | Set CAMD server ip address and port (1.2.3.4:2233).\n");
  115. printf(" -U --camd-user <user> | Set CAMD server user. Default: %s\n", ts->camd35.user);
  116. printf(" -P --camd-pass <pass> | Set CAMD server password. Default: %s\n", ts->camd35.pass);
  117. printf(" -y --camd-pkt-delay <us> | Sleep <us> usec between sending ECM/EMM\n");
  118. printf(" . packets to CAMD. Default: %d\n", ts->packet_delay);
  119. printf("\n");
  120. printf("EMM options:\n");
  121. printf(" -e --emm | Enable sending EMM's to CAMD. Default: %s\n", ts->emm_send ? "enabled" : "disabled");
  122. printf(" -E --emm-only | Send only EMMs to CAMD, skipping ECMs and without\n");
  123. printf(" . decoding the input stream.\n");
  124. printf(" -Z --emm-pid <pid> | Force EMM pid. Default: none\n");
  125. printf(" -f --emm-report-time <sec> | Report each <sec> seconds how much EMMs have been\n");
  126. printf(" . received/processed. Set <sec> to 0 to disable\n");
  127. printf(" . the reports. Default: %d sec\n", ts->emm_report_interval);
  128. printf("\n");
  129. printf("ECM options:\n");
  130. printf(" -X --ecm-pid <pid> | Force ECM pid. Default: none\n");
  131. printf(" -H --ecm-report-time <sec> | Report each <sec> how much ECMs and CWs have been\n");
  132. printf(" . processed/skipped. Set <sec> to 0 to disable\n");
  133. printf(" . the reports. Default: %d sec\n", ts->ecm_report_interval);
  134. printf(" -G --ecm-irdeto-type <int> | Process IRDETO ECMs with type X /0..3/. Default: %d\n", ts->irdeto_ecm);
  135. printf(" -K --ecm-no-log | Disable ECM and code words logging.\n");
  136. printf(" -J --cw-warn-time <sec> | Warn if no valid code word has been received.\n");
  137. printf(" . Set <sec> to 0 to disable. Default: %d sec\n", ts->cw_warn_sec);
  138. printf("\n");
  139. printf("Misc options:\n");
  140. printf(" -D --debug <level> | Message debug level.\n");
  141. printf(" . 0 = default messages\n");
  142. printf(" . 1 = show PSI tables\n");
  143. printf(" . 2 = show EMMs\n");
  144. printf(" . 3 = show duplicate ECMs\n");
  145. printf(" . 4 = packet debug\n");
  146. printf(" . 5 = packet debug + packet dump\n");
  147. printf(" -h --help | Show help screen.\n");
  148. printf(" -V --version | Show program version.\n");
  149. printf("\n");
  150. }
  151. static int parse_io_param(struct io *io, char *opt, int open_flags, mode_t open_mode) {
  152. io->type = WTF_IO;
  153. char *p = strrchr(opt, ':');
  154. if (!p) {
  155. io->type = FILE_IO;
  156. if (strcmp(opt, "-") != 0) {
  157. io->fd = open(opt, open_flags, open_mode);
  158. if (io->fd < 0) {
  159. fprintf(stderr, "ERROR: Can not open file (%s): %s\n", opt, strerror(errno));
  160. exit(1);
  161. }
  162. }
  163. io->fname = strdup(opt);
  164. return 0;
  165. }
  166. *p = 0x00;
  167. io->type = NET_IO;
  168. io->port = atoi(p + 1);
  169. if (inet_aton(opt, &io->addr) == 0)
  170. return 1;
  171. return 0;
  172. }
  173. static void parse_options(struct ts *ts, int argc, char **argv) {
  174. int j, i, ca_err = 0, server_err = 1, input_addr_err = 0, output_addr_err = 0, output_intf_err = 0, ident_err = 0;
  175. while ( (j = getopt_long(argc, argv, "i:d:l:L:I:RzO:o:t:pc:C:s:U:P:y:eZ:Ef:X:G:KJ:D:hV", long_options, NULL)) != -1 ) {
  176. char *p = NULL;
  177. switch (j) {
  178. case 'i':
  179. strncpy(ts->ident, optarg, sizeof(ts->ident) - 1);
  180. ts->ident[sizeof(ts->ident) - 1] = 0;
  181. break;
  182. case 'd':
  183. strncpy(ts->pidfile, optarg, sizeof(ts->pidfile) - 1);
  184. ts->pidfile[sizeof(ts->pidfile) - 1] = 0;
  185. ts->daemonize = 1;
  186. break;
  187. case 'l':
  188. strncpy(ts->syslog_host, optarg, sizeof(ts->syslog_host) - 1);
  189. ts->syslog_host[sizeof(ts->syslog_host) - 1] = 0;
  190. ts->syslog_active = 1;
  191. break;
  192. case 'L':
  193. ts->syslog_port = atoi(optarg);
  194. break;
  195. case 'I':
  196. input_addr_err = parse_io_param(&ts->input, optarg, O_RDONLY, 0);
  197. break;
  198. case 'R':
  199. ts->rtp_input = !ts->rtp_input;
  200. break;
  201. case 'z':
  202. ts->ts_discont = !ts->ts_discont;
  203. break;
  204. case 'O':
  205. output_addr_err = parse_io_param(&ts->output, optarg,
  206. O_CREAT | O_WRONLY | O_TRUNC,
  207. S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
  208. break;
  209. case 'o':
  210. if (inet_aton(optarg, &ts->output.intf) == 0)
  211. output_intf_err = 1;
  212. break;
  213. case 't':
  214. ts->output.ttl = atoi(optarg);
  215. break;
  216. case 'p':
  217. ts->pid_filter = !ts->pid_filter;
  218. break;
  219. case 'c':
  220. if (strcasecmp("IRDETO", optarg) == 0)
  221. ts->req_CA_sys = CA_IRDETO;
  222. else if (strcasecmp("CONNAX", optarg) == 0 || strcasecmp("CONAX", optarg) == 0)
  223. ts->req_CA_sys = CA_CONAX;
  224. else if (strcasecmp("CRYPTOWORKS", optarg) == 0)
  225. ts->req_CA_sys = CA_CRYPTOWORKS;
  226. else if (strcasecmp("SECA", optarg) == 0 || strcasecmp("MEDIAGUARD", optarg) == 0)
  227. ts->req_CA_sys = CA_SECA;
  228. else if (strcasecmp("VIACCESS", optarg) == 0)
  229. ts->req_CA_sys = CA_VIACCESS;
  230. else if (strcasecmp("VIDEOGUARD", optarg) == 0 || strcasecmp("NDS", optarg) == 0)
  231. ts->req_CA_sys = CA_VIDEOGUARD;
  232. else if (strcasecmp("NAGRA", optarg) == 0)
  233. ts->req_CA_sys = CA_NAGRA;
  234. else if (strcasecmp("DRE-CRYPT", optarg) == 0 || strcasecmp("DRECRYPT", optarg) == 0)
  235. ts->req_CA_sys = CA_DRECRYPT;
  236. else
  237. ca_err = 1;
  238. break;
  239. case 'C':
  240. ts->forced_caid = strtoul(optarg, NULL, 0) & 0xffff;
  241. break;
  242. case 's':
  243. p = strrchr(optarg, ':');
  244. if (p) {
  245. *p = 0x00;
  246. ts->camd35.server_port = atoi(p + 1);
  247. }
  248. if (inet_aton(optarg, &ts->camd35.server_addr) == 0)
  249. server_err = 1;
  250. else
  251. server_err = 0;
  252. break;
  253. case 'U':
  254. strncpy(ts->camd35.user, optarg, sizeof(ts->camd35.user) - 1);
  255. ts->camd35.user[sizeof(ts->camd35.user) - 1] = 0;
  256. break;
  257. case 'P':
  258. strncpy(ts->camd35.pass, optarg, sizeof(ts->camd35.pass) - 1);
  259. ts->camd35.pass[sizeof(ts->camd35.pass) - 1] = 0;
  260. break;
  261. case 'y':
  262. ts->packet_delay = atoi(optarg);
  263. if (ts->packet_delay < 0 || ts->packet_delay > 1000000)
  264. ts->packet_delay = 0;
  265. break;
  266. case 'e':
  267. ts->emm_send = !ts->emm_send;
  268. break;
  269. case 'Z':
  270. ts->forced_emm_pid = strtoul(optarg, NULL, 0) & 0x1fff;
  271. break;
  272. case 'E':
  273. ts->emm_only = 1;
  274. ts->emm_send = 1;
  275. break;
  276. case 'f':
  277. ts->emm_report_interval = strtoul(optarg, NULL, 10);
  278. if (ts->emm_report_interval > 86400)
  279. ts->emm_report_interval = 86400;
  280. break;
  281. case 'X':
  282. ts->forced_ecm_pid = strtoul(optarg, NULL, 0) & 0x1fff;
  283. break;
  284. case 'H':
  285. ts->ecm_report_interval = strtoul(optarg, NULL, 10);
  286. if (ts->ecm_report_interval > 86400)
  287. ts->ecm_report_interval = 86400;
  288. break;
  289. case 'G':
  290. ts->irdeto_ecm = atoi(optarg);
  291. break;
  292. case 'K':
  293. ts->ecm_cw_log = 0;
  294. break;
  295. case 'J':
  296. ts->cw_warn_sec = strtoul(optarg, NULL, 10);
  297. if (ts->cw_warn_sec > 86400)
  298. ts->cw_warn_sec = 86400;
  299. break;
  300. case 'D':
  301. ts->debug_level = atoi(optarg);
  302. break;
  303. case 'h':
  304. show_help(ts);
  305. exit(0);
  306. case 'V':
  307. printf("%s\n", program_id);
  308. exit(0);
  309. }
  310. }
  311. if (ts->syslog_active && !ts->ident[0])
  312. ident_err = 1;
  313. if (ident_err || ca_err || server_err || input_addr_err || output_addr_err || ts->input.type == WTF_IO || ts->output.type == WTF_IO) {
  314. show_help(ts);
  315. if (ident_err)
  316. fprintf(stderr, "ERROR: Syslog is enabled but ident was not set.\n");
  317. if (ca_err)
  318. fprintf(stderr, "ERROR: Requested CA system is unsupported.\n");
  319. if (server_err)
  320. fprintf(stderr, "ERROR: CAMD server IP address is not set or it is invalid.\n");
  321. if (input_addr_err)
  322. fprintf(stderr, "ERROR: Input IP address is invalid.\n");
  323. if (output_addr_err)
  324. fprintf(stderr, "ERROR: Output IP address is invalid.\n");
  325. if (output_intf_err)
  326. fprintf(stderr, "ERROR: Output interface address is invalid.\n");
  327. exit(1);
  328. }
  329. if (ts->ident[0])
  330. ts_LOGf("Ident : %s\n", ts->ident);
  331. else
  332. ts_LOGf("Ident : *NOT SET*\n");
  333. if (ts->pidfile[0])
  334. ts_LOGf("Daemonize : %s pid file.\n", ts->pidfile);
  335. else
  336. ts_LOGf("Daemonize : no daemon\n");
  337. if (ts->syslog_active)
  338. ts_LOGf("Syslog : %s:%d\n", ts->syslog_host, ts->syslog_port);
  339. else
  340. ts_LOGf("Syslog : disabled\n");
  341. if (ts->forced_caid)
  342. ts->req_CA_sys = ts_get_CA_sys(ts->forced_caid);
  343. if (!ts->forced_caid)
  344. ts_LOGf("CA System : %s\n", ts_get_CA_sys_txt(ts->req_CA_sys));
  345. else
  346. ts_LOGf("CA System : %s | CAID: 0x%04x (%d)\n",
  347. ts_get_CA_sys_txt(ts->req_CA_sys),
  348. ts->forced_caid, ts->forced_caid);
  349. if (ts->input.type == NET_IO) {
  350. ts_LOGf("Input addr : %s://%s:%u/\n",
  351. ts->rtp_input ? "rtp" : "udp",
  352. inet_ntoa(ts->input.addr), ts->input.port);
  353. } else if (ts->input.type == FILE_IO) {
  354. ts_LOGf("Input file : %s\n", ts->input.fd == 0 ? "STDIN" : ts->input.fname);
  355. }
  356. if (ts->req_CA_sys == CA_IRDETO)
  357. ts_LOGf("Irdeto ECM : %d\n", ts->irdeto_ecm);
  358. if (!ts->emm_only)
  359. {
  360. if (ts->output.type == NET_IO) {
  361. ts_LOGf("Output addr: udp://%s:%u/\n", inet_ntoa(ts->output.addr), ts->output.port);
  362. ts_LOGf("Output intf: %s\n", inet_ntoa(ts->output.intf));
  363. ts_LOGf("Output ttl : %d\n", ts->output.ttl);
  364. } else if (ts->output.type == FILE_IO) {
  365. ts_LOGf("Output file: %s\n", ts->output.fd == 1 ? "STDOUT" : ts->output.fname);
  366. }
  367. ts_LOGf("PID filter : %s\n", ts->pid_filter ? "enabled" : "disabled");
  368. }
  369. ts_LOGf("Server addr: tcp://%s:%u/\n", inet_ntoa(ts->camd35.server_addr), ts->camd35.server_port);
  370. ts_LOGf("Server user: %s\n", ts->camd35.user);
  371. ts_LOGf("Server pass: %s\n", ts->camd35.pass);
  372. if (ts->packet_delay)
  373. ts_LOGf("Pkt sleep : %d us (%d ms)\n", ts->packet_delay, ts->packet_delay / 1000);
  374. ts_LOGf("TS discont : %s\n", ts->ts_discont ? "report" : "ignore");
  375. ts->threaded = !(ts->input.type == FILE_IO && ts->input.fd != 0);
  376. if (ts->emm_send && ts->emm_report_interval)
  377. ts_LOGf("EMM report : %d sec\n", ts->emm_report_interval);
  378. if (ts->emm_send && ts->emm_report_interval == 0)
  379. ts_LOGf("EMM report : disabled\n");
  380. if (ts->forced_emm_pid)
  381. ts_LOGf("EMM pid : 0x%04x (%d)\n", ts->forced_emm_pid, ts->forced_emm_pid);
  382. if (ts->emm_only) {
  383. ts_LOGf("EMM only : %s\n", ts->emm_only ? "yes" : "no");
  384. } else {
  385. ts_LOGf("EMM send : %s\n", ts->emm_send ? "enabled" : "disabled");
  386. ts_LOGf("Decoding : %s\n", ts->threaded ? "threaded" : "single thread");
  387. }
  388. if (!ts->emm_only && ts->ecm_report_interval)
  389. ts_LOGf("ECM report : %d sec\n", ts->emm_report_interval);
  390. if (!ts->emm_only && ts->ecm_report_interval == 0)
  391. ts_LOGf("ECM report : disabled\n");
  392. if (ts->forced_ecm_pid)
  393. ts_LOGf("ECM pid : 0x%04x (%d)\n", ts->forced_ecm_pid, ts->forced_ecm_pid);
  394. if (!ts->emm_only && ts->cw_warn_sec)
  395. ts_LOGf("CW warning : %d sec\n", ts->cw_warn_sec);
  396. if (!ts->emm_only && ts->cw_warn_sec)
  397. ts_LOGf("CW warning : disabled\n");
  398. if (!ts->ecm_cw_log)
  399. ts_LOGf("ECM/CW log : disabled\n");
  400. for (i=0; i<(int)sizeof(ts->ident); i++) {
  401. if (!ts->ident[i])
  402. break;
  403. if (ts->ident[i] == '/')
  404. ts->ident[i] = '-';
  405. }
  406. }
  407. static void report_emms(struct ts *ts, time_t now) {
  408. ts_LOGf("EMM | Received %u and processed %u in %lu seconds.\n",
  409. ts->emm_seen_count,
  410. ts->emm_processed_count,
  411. now - ts->emm_last_report);
  412. ts->emm_last_report = now;
  413. ts->emm_seen_count = 0;
  414. ts->emm_processed_count = 0;
  415. }
  416. static void report_ecms(struct ts *ts, time_t now) {
  417. ts_LOGf("ECM | Received %u (%u dup) and processed %u in %lu seconds.\n",
  418. ts->ecm_seen_count,
  419. ts->ecm_duplicate_count,
  420. ts->ecm_processed_count,
  421. now - ts->ecm_last_report);
  422. ts->ecm_last_report = now;
  423. ts->ecm_seen_count = 0;
  424. ts->ecm_duplicate_count = 0;
  425. ts->ecm_processed_count = 0;
  426. }
  427. static void report_cw_warn(struct ts *ts, time_t now) {
  428. ts_LOGf("CW | *** No valid CW was received for %lu seconds!\n", now - ts->cw_last_warn);
  429. ts->cw_last_warn = now;
  430. }
  431. static void do_reports(struct ts *ts) {
  432. static int first_emm_report = 1;
  433. static int first_ecm_report = 1;
  434. time_t now = time(NULL);
  435. if (ts->emm_send && ts->emm_report_interval) {
  436. if (first_emm_report && now >= ts->emm_last_report) {
  437. first_emm_report = 0;
  438. ts->emm_last_report -= FIRST_REPORT_SEC;
  439. report_emms(ts, now);
  440. } else if ((time_t)(ts->emm_last_report + ts->emm_report_interval) <= now) {
  441. report_emms(ts, now);
  442. }
  443. }
  444. if (!ts->emm_only && ts->ecm_report_interval) {
  445. if (first_ecm_report && now >= ts->ecm_last_report) {
  446. first_ecm_report = 0;
  447. ts->ecm_last_report -= FIRST_REPORT_SEC;
  448. report_ecms(ts, now);
  449. } else if ((time_t)(ts->ecm_last_report + ts->ecm_report_interval) <= now) {
  450. report_ecms(ts, now);
  451. }
  452. }
  453. if (!ts->emm_only && !ts->key.is_valid_cw) {
  454. if ((time_t)(ts->cw_last_warn + ts->cw_warn_sec) <= now) {
  455. report_cw_warn(ts, now);
  456. }
  457. }
  458. }
  459. void signal_quit(int sig) {
  460. if (!keep_running)
  461. raise(sig);
  462. keep_running = 0;
  463. ts_LOGf("Killed %s with signal %d\n", program_id, sig);
  464. signal(sig, SIG_DFL);
  465. }
  466. #define RTP_HDR_SZ 12
  467. int main(int argc, char **argv) {
  468. ssize_t readen;
  469. int have_data = 1;
  470. uint8_t ts_packet[FRAME_SIZE + RTP_HDR_SZ];
  471. uint8_t rtp_hdr[2][RTP_HDR_SZ];
  472. int rtp_hdr_pos = 0, num_packets = 0;
  473. struct ts ts;
  474. memset(rtp_hdr[0], 0, RTP_HDR_SZ);
  475. memset(rtp_hdr[1], 0, RTP_HDR_SZ);
  476. data_init(&ts);
  477. parse_options(&ts, argc, argv);
  478. if (ts.pidfile[0])
  479. daemonize(ts.pidfile);
  480. if (!ts.syslog_active) {
  481. ts_set_log_func(LOG_func);
  482. } else {
  483. ts_set_log_func(LOG);
  484. log_init(ts.ident, ts.syslog_active, ts.daemonize != 1, ts.syslog_host, ts.syslog_port);
  485. }
  486. ts_LOGf("Start %s\n", program_id);
  487. if (ts.input.type == NET_IO && udp_connect_input(&ts.input) < 1)
  488. goto EXIT;
  489. if (ts.output.type == NET_IO && udp_connect_output(&ts.output) < 1)
  490. goto EXIT;
  491. signal(SIGCHLD, SIG_IGN);
  492. signal(SIGPIPE, SIG_IGN);
  493. signal(SIGINT , signal_quit);
  494. signal(SIGTERM, signal_quit);
  495. if (&ts.threaded) {
  496. pthread_create(&ts.decode_thread, NULL, &decode_thread, &ts);
  497. pthread_create(&ts.write_thread, NULL , &write_thread , &ts);
  498. }
  499. ts.emm_last_report = time(NULL) + FIRST_REPORT_SEC;
  500. ts.ecm_last_report = time(NULL) + FIRST_REPORT_SEC;
  501. camd_start(&ts);
  502. do {
  503. do_reports(&ts);
  504. if (ts.input.type == NET_IO) {
  505. set_log_io_errors(0);
  506. if (!ts.rtp_input) {
  507. readen = fdread_ex(ts.input.fd, (char *)ts_packet, FRAME_SIZE, 250, 4, 1);
  508. } else {
  509. readen = fdread_ex(ts.input.fd, (char *)ts_packet, FRAME_SIZE + RTP_HDR_SZ, 250, 4, 1);
  510. if (readen > RTP_HDR_SZ) {
  511. memcpy(rtp_hdr[rtp_hdr_pos], ts_packet, RTP_HDR_SZ);
  512. memmove(ts_packet, ts_packet + RTP_HDR_SZ, FRAME_SIZE);
  513. readen -= RTP_HDR_SZ;
  514. uint16_t ssrc = (rtp_hdr[rtp_hdr_pos][2] << 8) | rtp_hdr[rtp_hdr_pos][3];
  515. uint16_t pssrc = (rtp_hdr[!rtp_hdr_pos][2] << 8) | rtp_hdr[!rtp_hdr_pos][3];
  516. rtp_hdr_pos = !rtp_hdr_pos;
  517. if (pssrc + 1 != ssrc && (ssrc != 0 && pssrc != 0xffff) && num_packets > 2)
  518. if (ts.ts_discont)
  519. ts_LOGf("--- | RTP discontinuity last_ssrc %5d, curr_ssrc %5d, lost %d packet\n",
  520. pssrc, ssrc, ((ssrc - pssrc)-1) & 0xffff);
  521. num_packets++;
  522. }
  523. }
  524. set_log_io_errors(1);
  525. if (readen < 0)
  526. ts_LOGf("--- | Input read timeout.\n");
  527. } else {
  528. readen = read(ts.input.fd, ts_packet, FRAME_SIZE);
  529. have_data = !(readen <= 0);
  530. }
  531. if (readen > 0)
  532. process_packets(&ts, ts_packet, readen);
  533. if (!keep_running)
  534. break;
  535. } while (have_data);
  536. EXIT:
  537. camd_stop(&ts);
  538. if (ts.threaded) {
  539. ts.decode_stop = 1;
  540. ts.write_stop = 1;
  541. pthread_join(ts.decode_thread, NULL);
  542. pthread_join(ts.write_thread, NULL);
  543. }
  544. data_free(&ts);
  545. ts_LOGf("Stop %s\n", program_id);
  546. if (ts.syslog_active)
  547. log_close();
  548. if (ts.daemonize)
  549. unlink(ts.pidfile);
  550. exit(0);
  551. }