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.

network.c 9.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <string.h>
  5. #include <pthread.h>
  6. #include <errno.h>
  7. #include <arpa/inet.h>
  8. #include <netinet/in.h>
  9. #include <regex.h>
  10. #include "libfuncs/io.h"
  11. #include "libfuncs/log.h"
  12. #include "libfuncs/list.h"
  13. #include "libfuncs/asyncdns.h"
  14. #include "libtsfuncs/tsfuncs.h"
  15. #include "data.h"
  16. #include "config.h"
  17. extern char *server_sig;
  18. extern char *server_ver;
  19. extern CONFIG *config;
  20. int connect_udp(struct sockaddr_in send_to) {
  21. int sendsock = socket(AF_INET, SOCK_DGRAM, 0);
  22. if (sendsock < 0) {
  23. LOGf("socket(SOCK_DGRAM): %s\n", strerror(errno));
  24. return -1;
  25. }
  26. int on = 1;
  27. setsockopt(sendsock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
  28. // subscribe to multicast group
  29. // LOGf("Using ttl %d\n", multicast_ttl);
  30. if (IN_MULTICAST(ntohl(send_to.sin_addr.s_addr))) {
  31. if (setsockopt(sendsock, IPPROTO_IP, IP_MULTICAST_TTL, &config->multicast_ttl, sizeof(config->multicast_ttl)) < 0) {
  32. LOGf("setsockopt(IP_MUTICAST_TTL): %s\n", strerror(errno));
  33. close(sendsock);
  34. return -1;
  35. }
  36. if (setsockopt(sendsock, IPPROTO_IP, IP_MULTICAST_IF, &config->output_intf, sizeof(config->output_intf)) < 0) {
  37. LOGf("setsockopt(IP_MUTICAST_IF, %s): %s\n", strerror(errno), inet_ntoa(config->output_intf));
  38. close(sendsock);
  39. return -1;
  40. }
  41. }
  42. int writebuflen = 1316 * 100;
  43. if (setsockopt(sendsock, SOL_SOCKET, SO_SNDBUF, (const char *)&writebuflen, sizeof(writebuflen)) < 0)
  44. log_perror("play(): setsockopt(SO_SNDBUF)", errno);
  45. // call connect to get errors
  46. if (connect(sendsock, (struct sockaddr *)&send_to, sizeof send_to)) {
  47. LOGf("udp_connect() error: %s\n", strerror(errno));
  48. close(sendsock);
  49. return -1;
  50. }
  51. return sendsock;
  52. }
  53. void connect_output(OUTPUT *o) {
  54. struct sockaddr_in sock;
  55. sock.sin_family = AF_INET;
  56. sock.sin_port = htons(o->out_port);
  57. sock.sin_addr = o->out_host;
  58. o->out_sock = connect_udp(sock);
  59. if (o->out_sock > -1) {
  60. //LOGf("OUTPUT: Connected out_fd: %i | Output: udp://%s:%d\n", o->out_sock, inet_ntoa(o->out_host), o->out_port);
  61. } else {
  62. LOGf("ERROR: Can't connect output | Output: udp://%s:%d\n", inet_ntoa(o->out_host), o->out_port);
  63. exit(1);
  64. }
  65. }
  66. /*
  67. On the last try, send no-signal to clients and exit
  68. otherwise wait a little bit before trying again
  69. */
  70. #define DO_RECONNECT do \
  71. { \
  72. chansrc_free(&src); \
  73. if (retries == 0) { \
  74. return -1; \
  75. } else { \
  76. if (errno != EHOSTUNREACH) /* When host is unreachable there is already a delay of ~4 secs per try so no sleep is needed */ \
  77. usleep(PROXY_RETRY_TIMEOUT * 1000); \
  78. if (r->dienow) \
  79. return -1; \
  80. return 1; \
  81. } \
  82. } while(0)
  83. #define FATAL_ERROR do \
  84. { \
  85. chansrc_free(&src); \
  86. return -1; \
  87. } while (0)
  88. /*
  89. Returns:
  90. -1 = exit thread
  91. 1 = retry
  92. 0 = connected ok
  93. */
  94. int connect_source(INPUT *r, int retries, int readbuflen, int *http_code) {
  95. CHANSRC *src = chansrc_init(r->channel->source);
  96. if (!src) {
  97. LOGf("ERR : Can't parse channel source | Channel: %s Source: %s\n", r->channel->name, r->channel->source);
  98. FATAL_ERROR;
  99. }
  100. r->connected = 0;
  101. r->reconnect = 0;
  102. int active = 1;
  103. int dret = async_resolve_host(src->host, src->port, &(r->src_sockname), 5000, &active);
  104. if (dret != 0) {
  105. if (dret == 1)
  106. proxy_log(r, "Can't resolve host");
  107. if (dret == 2)
  108. proxy_log(r, "Timeout resolving host");
  109. DO_RECONNECT;
  110. }
  111. proxy_log(r, "Connecting");
  112. char buf[1024];
  113. *http_code = 0;
  114. if (src->sproto == tcp_sock) {
  115. r->sock = socket(PF_INET, SOCK_STREAM, 0);
  116. if (r->sock < 0) {
  117. log_perror("play(): Could not create SOCK_STREAM socket.", errno);
  118. FATAL_ERROR;
  119. }
  120. //proxy_log(r, "Add");
  121. if (do_connect(r->sock, (struct sockaddr *)&(r->src_sockname), sizeof(r->src_sockname), PROXY_CONNECT_TIMEOUT) < 0) {
  122. LOGf("ERR : Error connecting to %s srv_fd: %i err: %s\n", r->channel->source, r->sock, strerror(errno));
  123. DO_RECONNECT;
  124. }
  125. snprintf(buf,sizeof(buf)-1, "GET /%s HTTP/1.0\nHost: %s:%u\nX-Smart-Client: yes\nUser-Agent: %s %s (%s)\n\n",
  126. src->path, src->host, src->port, server_sig, server_ver, config->ident);
  127. buf[sizeof(buf)-1] = 0;
  128. fdwrite(r->sock, buf, strlen(buf));
  129. char xresponse[128];
  130. memset(xresponse, 0, sizeof(xresponse));
  131. memset(buf, 0, sizeof(buf));
  132. regmatch_t res[4];
  133. while (fdgetline(r->sock,buf,sizeof(buf)-1)) {
  134. if (buf[0] == '\n' || buf[0] == '\r')
  135. break;
  136. if (strstr(buf,"HTTP/1.") != NULL) {
  137. regex_t http_response;
  138. regcomp(&http_response, "^HTTP/1.[0-1] (([0-9]{3}) .*)", REG_EXTENDED);
  139. if (regexec(&http_response,buf,3,res,0) != REG_NOMATCH) {
  140. char codestr[4];
  141. if ((unsigned int)res[1].rm_eo-res[1].rm_so < sizeof(xresponse)) {
  142. strncpy(xresponse, &buf[res[1].rm_so], res[1].rm_eo-res[1].rm_so);
  143. xresponse[res[1].rm_eo-res[1].rm_so] = '\0';
  144. chomp(xresponse);
  145. strncpy(codestr, &buf[res[2].rm_so], res[2].rm_eo-res[2].rm_so);
  146. codestr[3] = 0;
  147. *http_code = atoi(codestr);
  148. }
  149. }
  150. regfree(&http_response);
  151. }
  152. if (*http_code == 504) { // Extract extra error code
  153. if (strstr(buf, "X-ErrorCode: ") != NULL) {
  154. *http_code = atoi(buf+13);
  155. break;
  156. }
  157. }
  158. }
  159. if (*http_code == 0) { // No valid HTTP response, retry
  160. LOGf("DEBUG: Server returned not valid HTTP code | srv_fd: %i\n", r->sock);
  161. DO_RECONNECT;
  162. }
  163. if (*http_code == 504) { // No signal, exit
  164. LOGf("ERR : Get no-signal for %s from %s on srv_fd: %i\n", r->channel->name, r->channel->source, r->sock);
  165. FATAL_ERROR;
  166. }
  167. if (*http_code > 300) { // Unhandled or error codes, exit
  168. LOGf("ERR : Get code %i for %s from %s on srv_fd: %i exiting.\n", *http_code, r->channel->name, r->channel->source, r->sock);
  169. FATAL_ERROR;
  170. }
  171. // connected ok, continue
  172. } else {
  173. if (!IN_MULTICAST(ntohl(r->src_sockname.sin_addr.s_addr))) {
  174. LOGf("ERR : %s is not multicast address\n", r->channel->source);
  175. FATAL_ERROR;
  176. }
  177. struct ip_mreq mreq;
  178. struct sockaddr_in receiving_from;
  179. r->sock = socket(PF_INET, SOCK_DGRAM, 0);
  180. if (r->sock < 0) {
  181. log_perror("play(): Could not create SOCK_DGRAM socket.", errno);
  182. FATAL_ERROR;
  183. }
  184. // LOGf("CONN : Listening on multicast socket %s srv_fd: %i retries left: %i\n", r->channel->source, r->sock, retries);
  185. int on = 1;
  186. setsockopt(r->sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
  187. // subscribe to multicast group
  188. memcpy(&mreq.imr_multiaddr, &(r->src_sockname.sin_addr), sizeof(struct in_addr));
  189. mreq.imr_interface.s_addr = htonl(INADDR_ANY);
  190. if (setsockopt(r->sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
  191. LOGf("ERR : Failed to add IP membership on %s srv_fd: %i\n", r->channel->source, r->sock);
  192. FATAL_ERROR;
  193. }
  194. // bind to the socket so data can be read
  195. memset(&receiving_from, 0, sizeof(receiving_from));
  196. receiving_from.sin_family = AF_INET;
  197. receiving_from.sin_addr = r->src_sockname.sin_addr;
  198. receiving_from.sin_port = htons(src->port);
  199. if (bind(r->sock, (struct sockaddr *) &receiving_from, sizeof(receiving_from)) < 0) {
  200. LOGf("ERR : Failed to bind to %s srv_fd: %i\n", r->channel->source, r->sock);
  201. FATAL_ERROR;
  202. }
  203. }
  204. if (setsockopt(r->sock, SOL_SOCKET, SO_RCVBUF, (const char *)&readbuflen, sizeof(readbuflen)) < 0)
  205. log_perror("play(): setsockopt(SO_RCVBUF)", errno);
  206. r->connected = 1;
  207. // proxy_log(r, "Connected");
  208. chansrc_free(&src);
  209. return 0;
  210. }
  211. /* Start: 3 seconds on connect */
  212. /* In connection: Max UDP timeout == 3 seconds (read) + 2 seconds (connect) == 5 seconds */
  213. #define UDP_READ_RETRIES 3
  214. #define UDP_READ_TIMEOUT 1000
  215. /* Start: 1/4 seconds on connect */
  216. /* In connection: Max TCP timeout == 5 seconds (read) + 2 seconds (connect) == 7 seconds */
  217. /* In connection: Max TCP timeout == 5 seconds (read) + 8 seconds (connect, host unrch) == 13 seconds */
  218. #define TCP_READ_RETRIES 5
  219. #define TCP_READ_TIMEOUT 1000
  220. /*
  221. Returns:
  222. 0 = synced ok
  223. 1 = not synced, reconnect
  224. */
  225. int mpeg_sync(INPUT *r, channel_source source_proto) {
  226. time_t sync_start = time(NULL);
  227. unsigned int sync_packets = 0;
  228. unsigned int read_bytes = 0;
  229. char syncframe[188];
  230. int _timeout = TCP_READ_TIMEOUT;
  231. int _retries = TCP_READ_RETRIES;
  232. if (source_proto == udp_sock) {
  233. _timeout = UDP_READ_TIMEOUT;
  234. _retries = UDP_READ_RETRIES;
  235. }
  236. do {
  237. if (r->dienow)
  238. return 1;
  239. resync:
  240. if (fdread_ex(r->sock, syncframe, 1, _timeout, _retries, 1) != 1) {
  241. proxy_log(r, "mpeg_sync fdread() timeoutA");
  242. return 1; // reconnect
  243. }
  244. // LOGf("DEBUG: Read 0x%02x Offset %u Sync: %u\n", (uint8_t)syncframe[0], read_bytes, sync_packets);
  245. read_bytes++;
  246. if (syncframe[0] == 0x47) {
  247. ssize_t rdsz = fdread_ex(r->sock, syncframe, 188-1, _timeout, _retries, 1);
  248. if (rdsz != 188-1) {
  249. proxy_log(r, "mpeg_sync fdread() timeoutB");
  250. return 1; // reconnect
  251. }
  252. read_bytes += 188-1;
  253. if (++sync_packets == 7) // sync 7 packets
  254. break;
  255. goto resync;
  256. } else {
  257. sync_packets = 0;
  258. }
  259. if (read_bytes > FRAME_PACKET_SIZE) { // Can't sync in 1316 bytes
  260. proxy_log(r, "mpeg_sync can't sync after 1316 bytes");
  261. return 1; // reconnect
  262. }
  263. if (sync_start+2 <= time(NULL)) { // Do not sync in two seconds
  264. proxy_log(r, "mpeg_sync can't sync in 2 seconds");
  265. return 1; // reconnect
  266. }
  267. } while (1);
  268. if (read_bytes-FRAME_PACKET_SIZE != 0)
  269. LOGf("INPUT : [%-12s] TS synced after %u bytes\n", r->channel->id, read_bytes-FRAME_PACKET_SIZE);
  270. return 0;
  271. }