gnutv.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. /*
  2. gnutv utility
  3. Copyright (C) 2004, 2005 Manu Abraham <abraham.manu@gmail.com>
  4. Copyright (C) 2006 Andrew de Quincey (adq_dvb@lidskialf.net)
  5. This program is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 2 of the License, or
  8. (at your option) any later version.
  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. You should have received a copy of the GNU General Public License
  14. along with this program; if not, write to the Free Software
  15. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  16. */
  17. #include <stdio.h>
  18. #include <unistd.h>
  19. #include <limits.h>
  20. #include <string.h>
  21. #include <fcntl.h>
  22. #include <signal.h>
  23. #include <pthread.h>
  24. #include <sys/poll.h>
  25. #include <libdvbapi/dvbdemux.h>
  26. #include <libdvbapi/dvbaudio.h>
  27. #include <libdvbsec/dvbsec_cfg.h>
  28. #include <libucsi/mpeg/section.h>
  29. #include "gnutv.h"
  30. #include "gnutv_dvb.h"
  31. #include "gnutv_ca.h"
  32. #include "gnutv_data.h"
  33. static void signal_handler(int _signal);
  34. static int quit_app = 0;
  35. void usage(void)
  36. {
  37. static const char *_usage = "\n"
  38. " gnutv: A digital tv utility\n"
  39. " Copyright (C) 2004, 2005, 2006 Manu Abraham (manu@kromtek.com)\n"
  40. " Copyright (C) 2006 Andrew de Quincey (adq_dvb@lidskialf.net)\n\n"
  41. " usage: gnutv <options> as follows:\n"
  42. " -h help\n"
  43. " -adapter <id> adapter to use (default 0)\n"
  44. " -frontend <id> frontend to use (default 0)\n"
  45. " -demux <id> demux to use (default 0)\n"
  46. " -caslotnum <id> ca slot number to use (default 0)\n"
  47. " -channels <filename> channels.conf file.\n"
  48. " -secfile <filename> Optional sec.conf file.\n"
  49. " -secid <secid> ID of the SEC configuration to use, one of:\n"
  50. " * UNIVERSAL (default) - Europe, 10800 to 11800 MHz and 11600 to 12700 Mhz,\n"
  51. " Dual LO, loband 9750, hiband 10600 MHz.\n"
  52. " * DBS - Expressvu, North America, 12200 to 12700 MHz, Single LO, 11250 MHz.\n"
  53. " * STANDARD - 10945 to 11450 Mhz, Single LO, 10000 Mhz.\n"
  54. " * ENHANCED - Astra, 10700 to 11700 MHz, Single LO, 9750 MHz.\n"
  55. " * C-BAND - Big Dish, 3700 to 4200 MHz, Single LO, 5150 Mhz.\n"
  56. " * C-MULTI - Big Dish - Multipoint LNBf, 3700 to 4200 MHz,\n"
  57. " Dual LO, H:5150MHz, V:5750MHz.\n"
  58. " * One of the sec definitions from the secfile if supplied\n"
  59. " -buffer <size> Custom DVR buffer size\n"
  60. " -out decoder Output to hardware decoder (default)\n"
  61. " decoderabypass Output to hardware decoder using audio bypass\n"
  62. " dvr Output stream to dvr device\n"
  63. " null Do not output anything\n"
  64. " stdout Output to stdout\n"
  65. " file <filename> Output stream to file\n"
  66. " udp <address> <port> Output stream to address:port using udp\n"
  67. " udpif <address> <port> <interface> Output stream to address:port using udp\n"
  68. " forcing the specified interface\n"
  69. " rtp <address> <port> Output stream to address:port using udp-rtp\n"
  70. " rtpif <address> <port> <interface> Output stream to address:port using udp-rtp\n"
  71. " forcing the specified interface\n"
  72. " -timeout <secs> Number of seconds to output channel for\n"
  73. " (0=>exit immediately after successful tuning, default is to output forever)\n"
  74. " -cammenu Show the CAM menu\n"
  75. " -nomoveca Do not attempt to move CA descriptors from stream to programme level\n"
  76. " <channel name>\n";
  77. fprintf(stderr, "%s\n", _usage);
  78. exit(1);
  79. }
  80. int find_channel(struct dvbcfg_zapchannel *channel, void *private_data)
  81. {
  82. struct dvbcfg_zapchannel *tmpchannel = private_data;
  83. if (strcmp(channel->name, tmpchannel->name) == 0) {
  84. memcpy(tmpchannel, channel, sizeof(struct dvbcfg_zapchannel));
  85. return 1;
  86. }
  87. return 0;
  88. }
  89. int main(int argc, char *argv[])
  90. {
  91. int adapter_id = 0;
  92. int frontend_id = 0;
  93. int demux_id = 0;
  94. int caslot_num = 0;
  95. char *chanfile = "/etc/channels.conf";
  96. char *secfile = NULL;
  97. char *secid = NULL;
  98. char *channel_name = NULL;
  99. int output_type = OUTPUT_TYPE_DECODER;
  100. char *outfile = NULL;
  101. char *outhost = NULL;
  102. char *outport = NULL;
  103. char *outif = NULL;
  104. struct addrinfo *outaddrs = NULL;
  105. int timeout = -1;
  106. int moveca = 1;
  107. int cammenu = 0;
  108. int argpos = 1;
  109. struct gnutv_dvb_params gnutv_dvb_params;
  110. struct gnutv_ca_params gnutv_ca_params;
  111. int ffaudiofd = -1;
  112. int usertp = 0;
  113. int buffer_size = 0;
  114. while(argpos != argc) {
  115. if (!strcmp(argv[argpos], "-h")) {
  116. usage();
  117. } else if (!strcmp(argv[argpos], "-adapter")) {
  118. if ((argc - argpos) < 2)
  119. usage();
  120. if (sscanf(argv[argpos+1], "%i", &adapter_id) != 1)
  121. usage();
  122. argpos+=2;
  123. } else if (!strcmp(argv[argpos], "-frontend")) {
  124. if ((argc - argpos) < 2)
  125. usage();
  126. if (sscanf(argv[argpos+1], "%i", &frontend_id) != 1)
  127. usage();
  128. argpos+=2;
  129. } else if (!strcmp(argv[argpos], "-demux")) {
  130. if ((argc - argpos) < 2)
  131. usage();
  132. if (sscanf(argv[argpos+1], "%i", &demux_id) != 1)
  133. usage();
  134. argpos+=2;
  135. } else if (!strcmp(argv[argpos], "-caslotnum")) {
  136. if ((argc - argpos) < 2)
  137. usage();
  138. if (sscanf(argv[argpos+1], "%i", &caslot_num) != 1)
  139. usage();
  140. argpos+=2;
  141. } else if (!strcmp(argv[argpos], "-channels")) {
  142. if ((argc - argpos) < 2)
  143. usage();
  144. chanfile = argv[argpos+1];
  145. argpos+=2;
  146. } else if (!strcmp(argv[argpos], "-secfile")) {
  147. if ((argc - argpos) < 2)
  148. usage();
  149. secfile = argv[argpos+1];
  150. argpos+=2;
  151. } else if (!strcmp(argv[argpos], "-secid")) {
  152. if ((argc - argpos) < 2)
  153. usage();
  154. secid = argv[argpos+1];
  155. argpos+=2;
  156. } else if (!strcmp(argv[argpos], "-buffer")) {
  157. if ((argc - argpos) < 2)
  158. usage();
  159. if (sscanf(argv[argpos+1], "%i", &buffer_size) != 1)
  160. usage();
  161. if (buffer_size < 0)
  162. usage();
  163. argpos+=2;
  164. } else if (!strcmp(argv[argpos], "-out")) {
  165. if ((argc - argpos) < 2)
  166. usage();
  167. if (!strcmp(argv[argpos+1], "decoder")) {
  168. output_type = OUTPUT_TYPE_DECODER;
  169. } else if (!strcmp(argv[argpos+1], "decoderabypass")) {
  170. output_type = OUTPUT_TYPE_DECODER_ABYPASS;
  171. } else if (!strcmp(argv[argpos+1], "dvr")) {
  172. output_type = OUTPUT_TYPE_DVR;
  173. } else if (!strcmp(argv[argpos+1], "null")) {
  174. output_type = OUTPUT_TYPE_NULL;
  175. } else if (!strcmp(argv[argpos+1], "stdout")) {
  176. output_type = OUTPUT_TYPE_STDOUT;
  177. } else if (!strcmp(argv[argpos+1], "file")) {
  178. output_type = OUTPUT_TYPE_FILE;
  179. if ((argc - argpos) < 3)
  180. usage();
  181. outfile = argv[argpos+2];
  182. argpos++;
  183. } else if ((!strcmp(argv[argpos+1], "udp")) ||
  184. (!strcmp(argv[argpos+1], "rtp"))) {
  185. output_type = OUTPUT_TYPE_UDP;
  186. if ((argc - argpos) < 4)
  187. usage();
  188. if (!strcmp(argv[argpos+1], "rtp"))
  189. usertp = 1;
  190. outhost = argv[argpos+2];
  191. outport = argv[argpos+3];
  192. argpos+=2;
  193. } else if ((!strcmp(argv[argpos+1], "udpif")) ||
  194. (!strcmp(argv[argpos+1], "rtpif"))) {
  195. output_type = OUTPUT_TYPE_UDP;
  196. if ((argc - argpos) < 5)
  197. usage();
  198. if (!strcmp(argv[argpos+1], "rtpif"))
  199. usertp = 1;
  200. outhost = argv[argpos+2];
  201. outport = argv[argpos+3];
  202. outif = argv[argpos+4];
  203. argpos+=3;
  204. } else {
  205. usage();
  206. }
  207. argpos+=2;
  208. } else if (!strcmp(argv[argpos], "-timeout")) {
  209. if ((argc - argpos) < 2)
  210. usage();
  211. if (sscanf(argv[argpos+1], "%i", &timeout) != 1)
  212. usage();
  213. argpos+=2;
  214. } else if (!strcmp(argv[argpos], "-nomoveca")) {
  215. moveca = 0;
  216. argpos++;
  217. } else if (!strcmp(argv[argpos], "-cammenu")) {
  218. cammenu = 1;
  219. argpos++;
  220. } else {
  221. if ((argc - argpos) != 1)
  222. usage();
  223. channel_name = argv[argpos];
  224. argpos++;
  225. }
  226. }
  227. // the user didn't select anything!
  228. if ((channel_name == NULL) && (!cammenu))
  229. usage();
  230. // resolve host/port
  231. if ((outhost != NULL) && (outport != NULL)) {
  232. int res;
  233. struct addrinfo hints;
  234. memset(&hints, 0, sizeof(hints));
  235. hints.ai_family = AF_UNSPEC;
  236. hints.ai_socktype = SOCK_DGRAM;
  237. if ((res = getaddrinfo(outhost, outport, &hints, &outaddrs)) != 0) {
  238. fprintf(stderr, "Unable to resolve requested address: %s\n", gai_strerror(res));
  239. exit(1);
  240. }
  241. }
  242. // setup any signals
  243. signal(SIGINT, signal_handler);
  244. signal(SIGPIPE, SIG_IGN);
  245. // start the CA stuff
  246. gnutv_ca_params.adapter_id = adapter_id;
  247. gnutv_ca_params.caslot_num = caslot_num;
  248. gnutv_ca_params.cammenu = cammenu;
  249. gnutv_ca_params.moveca = moveca;
  250. gnutv_ca_start(&gnutv_ca_params);
  251. // frontend setup if a channel name was supplied
  252. if ((!cammenu) && (channel_name != NULL)) {
  253. // find the requested channel
  254. if (strlen(channel_name) >= sizeof(gnutv_dvb_params.channel.name)) {
  255. fprintf(stderr, "Channel name is too long %s\n", channel_name);
  256. exit(1);
  257. }
  258. FILE *channel_file = fopen(chanfile, "r");
  259. if (channel_file == NULL) {
  260. fprintf(stderr, "Could open channel file %s\n", chanfile);
  261. exit(1);
  262. }
  263. memcpy(gnutv_dvb_params.channel.name, channel_name, strlen(channel_name) + 1);
  264. if (dvbcfg_zapchannel_parse(channel_file, find_channel, &gnutv_dvb_params.channel) != 1) {
  265. fprintf(stderr, "Unable to find requested channel %s\n", channel_name);
  266. exit(1);
  267. }
  268. fclose(channel_file);
  269. // default SEC with a DVBS card
  270. if ((secid == NULL) && (gnutv_dvb_params.channel.fe_type == DVBFE_TYPE_DVBS))
  271. secid = "UNIVERSAL";
  272. // look it up if one were supplied
  273. gnutv_dvb_params.valid_sec = 0;
  274. if (secid != NULL) {
  275. if (dvbsec_cfg_find(secfile, secid,
  276. &gnutv_dvb_params.sec)) {
  277. fprintf(stderr, "Unable to find suitable sec/lnb configuration for channel\n");
  278. exit(1);
  279. }
  280. gnutv_dvb_params.valid_sec = 1;
  281. }
  282. // open the frontend
  283. gnutv_dvb_params.fe = dvbfe_open(adapter_id, frontend_id, 0);
  284. if (gnutv_dvb_params.fe == NULL) {
  285. fprintf(stderr, "Failed to open frontend\n");
  286. exit(1);
  287. }
  288. // failover decoder to dvr output if decoder not available
  289. if ((output_type == OUTPUT_TYPE_DECODER) ||
  290. (output_type == OUTPUT_TYPE_DECODER_ABYPASS)) {
  291. ffaudiofd = dvbaudio_open(adapter_id, 0);
  292. if (ffaudiofd < 0) {
  293. fprintf(stderr, "Cannot open decoder; defaulting to dvr output\n");
  294. output_type = OUTPUT_TYPE_DVR;
  295. }
  296. }
  297. // start the DVB stuff
  298. gnutv_dvb_params.adapter_id = adapter_id;
  299. gnutv_dvb_params.frontend_id = frontend_id;
  300. gnutv_dvb_params.demux_id = demux_id;
  301. gnutv_dvb_params.output_type = output_type;
  302. gnutv_dvb_start(&gnutv_dvb_params);
  303. // start the data stuff
  304. gnutv_data_start(output_type, ffaudiofd, adapter_id, demux_id, buffer_size, outfile, outif, outaddrs, usertp);
  305. }
  306. // the UI
  307. time_t start = 0;
  308. while(!quit_app) {
  309. if (gnutv_dvb_locked() && (start == 0))
  310. start = time(NULL);
  311. // the timeout
  312. if ((timeout != -1) && (start != 0)) {
  313. if ((time(NULL) - start) >= timeout)
  314. break;
  315. }
  316. if (cammenu)
  317. gnutv_ca_ui();
  318. else
  319. usleep(1);
  320. }
  321. // stop data handling
  322. gnutv_data_stop();
  323. // shutdown DVB stuff
  324. if (channel_name != NULL)
  325. gnutv_dvb_stop();
  326. // shutdown CA stuff
  327. gnutv_ca_stop();
  328. // done
  329. exit(0);
  330. }
  331. static void signal_handler(int _signal)
  332. {
  333. (void) _signal;
  334. if (!quit_app) {
  335. quit_app = 1;
  336. }
  337. }