codec: subsdec: fix variable shadowing
[vlc.git] / modules / access / satip.c
blobc4e2446afaf14809895314d1d412d423b8371aec
1 /*****************************************************************************
2 * satip.c: SAT>IP input module
3 *****************************************************************************
4 * Copyright © 2016 VLC authors and VideoLAN
5 * Copyright © 2016 jusst technologies GmbH
6 * Copyright © 2016 Videolabs SAS
7 * Copyright © 2016 Julian Scheel
9 * Authors: Julian Scheel <julian@jusst.de>
11 * This program is free software; you can redistribute it and/or modify it
12 * under the terms of the GNU Lesser General Public License as published by
13 * the Free Software Foundation; either version 2.1 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public License
22 * along with this program; if not, write to the Free Software Foundation,
23 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 *****************************************************************************/
26 #include "config.h"
28 #include <unistd.h>
29 #include <ctype.h>
30 #ifdef HAVE_SYS_UIO_H
31 # include <sys/uio.h>
32 #endif
34 #include <vlc_common.h>
35 #include <vlc_plugin.h>
36 #include <vlc_access.h>
37 #include <vlc_network.h>
38 #include <vlc_block.h>
39 #include <vlc_rand.h>
40 #include <vlc_url.h>
41 #include <vlc_interrupt.h>
43 #ifdef HAVE_POLL
44 #include <poll.h>
45 #endif
46 #include <fcntl.h>
47 #include <errno.h>
48 #include <assert.h>
50 #define RTSP_DEFAULT_PORT 554
51 #define RTSP_RECEIVE_BUFFER 2048
52 #define RTP_HEADER_SIZE 12
53 #define VLEN 100
54 #define KEEPALIVE_INTERVAL 60
55 #define KEEPALIVE_MARGIN 5
57 static int satip_open(vlc_object_t *);
58 static void satip_close(vlc_object_t *);
60 #define BUFFER_TEXT N_("Receive buffer")
61 #define BUFFER_LONGTEXT N_("UDP receive buffer size (bytes)")
63 #define MULTICAST_TEXT N_("Request multicast stream")
64 #define MULTICAST_LONGTEXT N_("Request server to send stream as multicast")
66 #define SATIP_HOST_TEXT N_("Host")
68 vlc_module_begin()
69 set_shortname("satip")
70 set_description( N_("SAT>IP Receiver Plugin") )
71 set_capability("access", 201)
72 set_callbacks(satip_open, satip_close)
73 set_category(CAT_INPUT)
74 set_subcategory(SUBCAT_INPUT_ACCESS)
75 add_integer("satip-buffer", 0x400000, BUFFER_TEXT, BUFFER_LONGTEXT, true)
76 add_bool("satip-multicast", false, MULTICAST_TEXT, MULTICAST_LONGTEXT, true)
77 add_string("satip-host", "", SATIP_HOST_TEXT, SATIP_HOST_TEXT, true)
78 change_safe()
79 add_shortcut("rtsp", "satip")
80 vlc_module_end()
82 enum rtsp_state {
83 RTSP_IDLE,
84 RTSP_DESCRIBE,
85 RTSP_SETUP,
86 RTSP_PLAY,
87 RTSP_RUNNING
90 enum rtsp_result {
91 RTSP_RESULT_OK = 200,
94 #define UDP_ADDRESS_LEN 16
95 typedef struct
97 char *content_base;
98 char *control;
99 char session_id[64];
100 uint16_t stream_id;
101 int keepalive_interval;
103 char udp_address[UDP_ADDRESS_LEN];
104 uint16_t udp_port;
106 int tcp_sock;
107 int udp_sock;
108 int rtcp_sock;
110 enum rtsp_state state;
111 int cseq;
113 size_t fifo_size;
114 block_fifo_t *fifo;
115 vlc_thread_t thread;
116 uint16_t last_seq_nr;
118 bool woken;
119 } access_sys_t;
121 VLC_FORMAT(3, 4)
122 static void net_Printf(stream_t *access, int fd, const char *fmt, ...)
124 va_list ap;
125 char *str;
126 int val;
128 va_start(ap, fmt);
129 val = vasprintf(&str, fmt, ap);
130 va_end(ap);
132 if (val >= 0) {
133 net_Write(access, fd, str, val);
134 free(str);
138 static void parse_session(char *request_line, char *session, unsigned max, int *timeout) {
139 char *state;
140 char *tok;
142 tok = strtok_r(request_line, ";", &state);
143 if (tok == NULL)
144 return;
145 memcpy(session, tok, __MIN(strlen(tok), max - 1));
147 while ((tok = strtok_r(NULL, ";", &state)) != NULL) {
148 if (strncmp(tok, "timeout=", 8) == 0) {
149 *timeout = atoi(tok + 8);
150 if (*timeout > 5)
151 *timeout -= KEEPALIVE_MARGIN;
152 else if (*timeout > 0)
153 *timeout = 1;
158 static int parse_port(char *str, uint16_t *port)
160 int p = atoi(str);
161 if (p < 0 || p > UINT16_MAX)
162 return VLC_EBADVAR;
164 *port = p;
166 return 0;
169 static int parse_transport(stream_t *access, char *request_line) {
170 access_sys_t *sys = access->p_sys;
171 char *state;
172 char *tok;
173 int err;
175 tok = strtok_r(request_line, ";", &state);
176 if (tok == NULL || strncmp(tok, "RTP/AVP", 7) != 0)
177 return VLC_EGENERIC;
179 tok = strtok_r(NULL, ";", &state);
180 if (tok == NULL || strncmp(tok, "multicast", 9) != 0)
181 return 0;
183 while ((tok = strtok_r(NULL, ";", &state)) != NULL) {
184 if (strncmp(tok, "destination=", 12) == 0) {
185 memcpy(sys->udp_address, tok + 12, __MIN(strlen(tok + 12), UDP_ADDRESS_LEN - 1));
186 } else if (strncmp(tok, "port=", 5) == 0) {
187 char port[6];
188 char *end;
190 memset(port, 0x00, 6);
191 memcpy(port, tok + 5, __MIN(strlen(tok + 5), 5));
192 if ((end = strstr(port, "-")) != NULL)
193 *end = '\0';
194 err = parse_port(port, &sys->udp_port);
195 if (err)
196 return err;
200 return 0;
204 * Semi-interruptible net_Gets replacement.
205 * If an interruption is occuring it will fallback to non-interruptible read
206 * with a given timeout before it returns.
208 * interrupted: Informs the caller whether an interrupt occured or not
210 static char *net_readln_timeout(vlc_object_t *obj, int fd, int timeout, bool *interrupted)
212 char *buf = NULL;
213 size_t size = 0, len = 0;
214 bool intr = false;
216 for (;;)
218 if (len == size)
220 if (unlikely(size >= (1 << 16)))
222 errno = EMSGSIZE;
223 goto error; /* put sane buffer size limit */
226 char *newbuf = realloc(buf, size + 1024);
227 if (unlikely(newbuf == NULL))
228 goto error;
229 buf = newbuf;
230 size += 1024;
232 assert(len < size);
234 ssize_t val = 0;
235 if (!intr) {
236 val = vlc_recv_i11e(fd, buf + len, size - len, MSG_PEEK);
237 if (val <= 0 && errno == EINTR) {
238 intr = true;
239 if (interrupted)
240 *interrupted = true;
241 continue;
243 if (val <= 0)
244 goto error;
245 } else {
246 struct pollfd pfd = {
247 .fd = fd,
248 .events = POLLIN,
250 int ret;
252 while((ret = poll(&pfd, 1, timeout)) < 0)
255 val = recv(fd, buf + len, size - len, MSG_PEEK);
256 if (val <= 0)
257 goto error;
260 char *end = memchr(buf + len, '\n', val);
261 if (end != NULL)
262 val = (end + 1) - (buf + len);
263 if (recv(fd, buf + len, val, 0) != val)
264 goto error;
265 len += val;
266 if (end != NULL)
267 break;
270 assert(len > 0);
271 buf[--len] = '\0';
272 if (len > 0 && buf[--len] == '\r')
273 buf[len] = '\0';
274 return buf;
275 error:
276 msg_Err(obj, "read error: %s", vlc_strerror_c(errno));
277 free(buf);
278 return NULL;
281 #define skip_whitespace(x) while(*x == ' ') x++
282 static enum rtsp_result rtsp_handle(stream_t *access, bool *interrupted) {
283 access_sys_t *sys = access->p_sys;
284 uint8_t buffer[512];
285 int rtsp_result = 0;
286 bool have_header = false;
287 size_t content_length = 0;
288 size_t read = 0;
289 char *in, *val;
291 /* Parse header */
292 while (!have_header) {
293 in = net_readln_timeout((vlc_object_t*)access, sys->tcp_sock, 5000,
294 interrupted);
295 if (in == NULL)
296 break;
298 if (strncmp(in, "RTSP/1.0 ", 9) == 0) {
299 rtsp_result = atoi(in + 9);
300 } else if (strncmp(in, "Content-Base:", 13) == 0) {
301 free(sys->content_base);
303 val = in + 13;
304 skip_whitespace(val);
306 sys->content_base = strdup(val);
307 } else if (strncmp(in, "Content-Length:", 15) == 0) {
308 val = in + 16;
309 skip_whitespace(val);
311 content_length = atoi(val);
312 } else if (strncmp("Session:", in, 8) == 0) {
313 val = in + 8;
314 skip_whitespace(val);
316 parse_session(val, sys->session_id, 64, &sys->keepalive_interval);
317 } else if (strncmp("Transport:", in, 10) == 0) {
318 val = in + 10;
319 skip_whitespace(val);
321 if (parse_transport(access, val) != 0) {
322 rtsp_result = VLC_EGENERIC;
323 break;
325 } else if (strncmp("com.ses.streamID:", in, 17) == 0) {
326 val = in + 17;
327 skip_whitespace(val);
329 sys->stream_id = atoi(val);
330 } else if (in[0] == '\0') {
331 have_header = true;
334 free(in);
337 /* Discard further content */
338 while (content_length > 0 &&
339 (read = net_Read(access, sys->tcp_sock, buffer, __MIN(sizeof(buffer), content_length))))
340 content_length -= read;
342 return rtsp_result;
345 #ifdef HAVE_RECVMMSG
346 static void satip_cleanup_blocks(void *data)
348 block_t **input_blocks = data;
350 for (size_t i = 0; i < VLEN; i++)
351 if (input_blocks[i] != NULL)
352 block_Release(input_blocks[i]);
354 #endif
356 static int check_rtp_seq(stream_t *access, block_t *block)
358 access_sys_t *sys = access->p_sys;
359 uint16_t seq_nr = block->p_buffer[2] << 8 | block->p_buffer[3];
361 if (seq_nr == sys->last_seq_nr) {
362 msg_Warn(access, "Received duplicate packet (seq_nr=%"PRIu16")", seq_nr);
363 return VLC_EGENERIC;
364 } else if (seq_nr < (uint16_t)(sys->last_seq_nr + 1)) {
365 msg_Warn(access, "Received out of order packet (seq_nr=%"PRIu16" < %"PRIu16")",
366 seq_nr, sys->last_seq_nr);
367 return VLC_EGENERIC;
368 } else if (++sys->last_seq_nr > 1 && seq_nr > sys->last_seq_nr) {
369 msg_Warn(access, "Gap in seq_nr (%"PRIu16" > %"PRIu16"), probably lost a packet",
370 seq_nr, sys->last_seq_nr);
373 sys->last_seq_nr = seq_nr;
374 return 0;
377 static void satip_teardown(void *data) {
378 stream_t *access = data;
379 access_sys_t *sys = access->p_sys;
380 int ret;
382 if (sys->tcp_sock > 0) {
383 if (sys->session_id[0] > 0) {
384 char discard_buf[32];
385 struct pollfd pfd = {
386 .fd = sys->tcp_sock,
387 .events = POLLOUT,
389 char *msg;
391 ssize_t len = asprintf(&msg, "TEARDOWN %s RTSP/1.0\r\n"
392 "CSeq: %d\r\n"
393 "Session: %s\r\n\r\n",
394 sys->control, sys->cseq++, sys->session_id);
395 if (len < 0)
396 return;
398 /* make socket non-blocking, to avoid blocking when output buffer
399 * has not enough space */
400 #ifndef _WIN32
401 fcntl(sys->tcp_sock, F_SETFL, fcntl(sys->tcp_sock, F_GETFL) | O_NONBLOCK);
402 #else
403 ioctlsocket(sys->tcp_sock, FIONBIO, &(unsigned long){ 1 });
404 #endif
406 for (unsigned sent = 0; sent < len;) {
407 ret = poll(&pfd, 1, 5000);
408 if (ret == 0) {
409 msg_Err(access, "Timed out sending RTSP teardown\n");
410 free(msg);
411 return;
414 ret = send(sys->tcp_sock, msg + sent, len, MSG_NOSIGNAL);
415 if (ret < 0) {
416 msg_Err(access, "Failed to send RTSP teardown: %d\n", ret);
417 free(msg);
418 return;
420 sent += ret;
422 free(msg);
424 if (rtsp_handle(access, NULL) != RTSP_RESULT_OK) {
425 msg_Err(access, "Failed to teardown RTSP session");
426 return;
429 /* Some SATIP servers send a few empty extra bytes after TEARDOWN.
430 * Try to read them, to avoid a TCP socket reset */
431 while (recv(sys->tcp_sock, discard_buf, sizeof(discard_buf), 0) > 0);
433 /* Extra sleep for compatibility with some satip servers, that
434 * can't handle new sessions right after teardown */
435 vlc_tick_sleep(VLC_TICK_FROM_MS(150));
440 #define RECV_TIMEOUT VLC_TICK_FROM_SEC(2)
441 static void *satip_thread(void *data) {
442 stream_t *access = data;
443 access_sys_t *sys = access->p_sys;
444 int sock = sys->udp_sock;
445 vlc_tick_t last_recv = vlc_tick_now();
446 ssize_t len;
447 vlc_tick_t next_keepalive = vlc_tick_now() + vlc_tick_from_sec(sys->keepalive_interval);
448 #ifdef HAVE_RECVMMSG
449 struct mmsghdr msgs[VLEN];
450 struct iovec iovecs[VLEN];
451 block_t *input_blocks[VLEN];
452 int retval;
454 for (size_t i = 0; i < VLEN; i++) {
455 memset(&msgs[i], 0, sizeof (msgs[i]));
456 msgs[i].msg_hdr.msg_iov = &iovecs[i];
457 msgs[i].msg_hdr.msg_iovlen = 1;
458 iovecs[i].iov_base = NULL;
459 iovecs[i].iov_len = RTSP_RECEIVE_BUFFER;
460 input_blocks[i] = NULL;
462 #else
463 struct pollfd ufd;
465 ufd.fd = sock;
466 ufd.events = POLLIN;
467 #endif
469 while (last_recv > vlc_tick_now() - RECV_TIMEOUT) {
470 #ifdef HAVE_RECVMMSG
471 for (size_t i = 0; i < VLEN; i++) {
472 if (input_blocks[i] != NULL)
473 continue;
475 input_blocks[i] = block_Alloc(RTSP_RECEIVE_BUFFER);
476 if (unlikely(input_blocks[i] == NULL))
477 break;
479 iovecs[i].iov_base = input_blocks[i]->p_buffer;
482 vlc_cleanup_push(satip_cleanup_blocks, input_blocks);
483 retval = recvmmsg(sock, msgs, VLEN, MSG_WAITFORONE, NULL);
484 vlc_cleanup_pop();
485 if (retval == -1)
486 continue;
488 last_recv = vlc_tick_now();
489 for (int i = 0; i < retval; ++i) {
490 block_t *block = input_blocks[i];
492 len = msgs[i].msg_len;
493 if (check_rtp_seq(access, block))
494 continue;
496 block->p_buffer += RTP_HEADER_SIZE;
497 block->i_buffer = len - RTP_HEADER_SIZE;
498 block_FifoPut(sys->fifo, block);
499 input_blocks[i] = NULL;
501 #else
502 if (poll(&ufd, 1, 20) == -1)
503 continue;
505 block_t *block = block_Alloc(RTSP_RECEIVE_BUFFER);
506 if (block == NULL) {
507 msg_Err(access, "Failed to allocate memory for input buffer");
508 break;
511 block_cleanup_push(block);
512 len = recv(sock, block->p_buffer, RTSP_RECEIVE_BUFFER, 0);
513 vlc_cleanup_pop();
515 if (len < RTP_HEADER_SIZE) {
516 block_Release(block);
517 continue;
520 if (check_rtp_seq(access, block)) {
521 block_Release(block);
522 continue;
524 last_recv = vlc_tick_now();
525 block->p_buffer += RTP_HEADER_SIZE;
526 block->i_buffer = len - RTP_HEADER_SIZE;
527 block_FifoPut(sys->fifo, block);
528 #endif
530 if (sys->keepalive_interval > 0 && vlc_tick_now() > next_keepalive) {
531 net_Printf(access, sys->tcp_sock,
532 "OPTIONS %s RTSP/1.0\r\n"
533 "CSeq: %d\r\n"
534 "Session: %s\r\n\r\n",
535 sys->control, sys->cseq++, sys->session_id);
536 if (rtsp_handle(access, NULL) != RTSP_RESULT_OK)
537 msg_Warn(access, "Failed to keepalive RTSP session");
539 next_keepalive = vlc_tick_now() + vlc_tick_from_sec(sys->keepalive_interval);
543 #ifdef HAVE_RECVMMSG
544 satip_cleanup_blocks(input_blocks);
545 #endif
546 msg_Dbg(access, "timed out waiting for data...");
547 vlc_fifo_Lock(sys->fifo);
548 sys->woken = true;
549 vlc_fifo_Signal(sys->fifo);
550 vlc_fifo_Unlock(sys->fifo);
552 return NULL;
555 static block_t* satip_block(stream_t *access, bool *restrict eof) {
556 access_sys_t *sys = access->p_sys;
557 block_t *block;
559 vlc_fifo_Lock(sys->fifo);
561 while (vlc_fifo_IsEmpty(sys->fifo)) {
562 if (sys->woken)
563 break;
564 vlc_fifo_Wait(sys->fifo);
567 if ((block = vlc_fifo_DequeueUnlocked(sys->fifo)) == NULL)
568 *eof = true;
569 sys->woken = false;
570 vlc_fifo_Unlock(sys->fifo);
572 return block;
575 static int satip_control(stream_t *access, int i_query, va_list args) {
576 bool *pb_bool;
578 switch(i_query)
580 case STREAM_CAN_CONTROL_PACE:
581 case STREAM_CAN_SEEK:
582 case STREAM_CAN_PAUSE:
583 pb_bool = va_arg(args, bool *);
584 *pb_bool = false;
585 break;
587 case STREAM_GET_PTS_DELAY:
588 *va_arg(args, vlc_tick_t *) =
589 VLC_TICK_FROM_MS(var_InheritInteger(access, "live-caching"));
590 break;
592 default:
593 return VLC_EGENERIC;
596 return VLC_SUCCESS;
599 /* Bind two adjacent free ports, of which the first one is even (for RTP data)
600 * and the second is odd (RTCP). This is a requirement of the satip
601 * specification */
602 static int satip_bind_ports(stream_t *access)
604 access_sys_t *sys = access->p_sys;
605 uint8_t rnd;
607 vlc_rand_bytes(&rnd, 1);
608 sys->udp_port = 9000 + (rnd * 2); /* randomly chosen, even start point */
609 while (sys->udp_sock < 0) {
610 sys->udp_sock = net_OpenDgram(access, "0.0.0.0", sys->udp_port, NULL,
611 0, IPPROTO_UDP);
612 if (sys->udp_sock < 0) {
613 if (sys->udp_port == 65534)
614 break;
616 sys->udp_port += 2;
617 continue;
620 sys->rtcp_sock = net_OpenDgram(access, "0.0.0.0", sys->udp_port + 1, NULL,
621 0, IPPROTO_UDP);
622 if (sys->rtcp_sock < 0) {
623 close(sys->udp_sock);
624 sys->udp_port += 2;
625 continue;
629 if (sys->udp_sock < 0) {
630 msg_Err(access, "Could not open two adjacent ports for RTP and RTCP data");
631 return VLC_EGENERIC;
634 return 0;
637 static int satip_open(vlc_object_t *obj)
639 stream_t *access = (stream_t *)obj;
640 access_sys_t *sys;
641 vlc_url_t url;
643 bool multicast = var_InheritBool(access, "satip-multicast");
645 access->p_sys = sys = vlc_obj_calloc(obj, 1, sizeof(*sys));
646 if (sys == NULL)
647 return VLC_ENOMEM;
649 msg_Dbg(access, "try to open '%s'", access->psz_url);
651 char *psz_host = var_InheritString(access, "satip-host");
653 sys->udp_sock = -1;
654 sys->rtcp_sock = -1;
655 sys->tcp_sock = -1;
657 /* convert url to lowercase, some famous m3u playlists for satip contain
658 * uppercase parameters while most (all?) satip servers do only understand
659 * parameters matching lowercase spelling as defined in the specification
660 * */
661 char *psz_lower_url = strdup(access->psz_url);
662 if (psz_lower_url == NULL)
664 free( psz_host );
665 return VLC_ENOMEM;
668 for (unsigned i = 0; i < strlen(psz_lower_url); i++)
669 psz_lower_url[i] = tolower(psz_lower_url[i]);
671 vlc_UrlParse(&url, psz_lower_url);
672 if (url.i_port <= 0)
673 url.i_port = RTSP_DEFAULT_PORT;
674 if (psz_host == NULL && url.psz_host )
675 psz_host = strdup(url.psz_host);
676 if (psz_host == NULL )
677 goto error;
679 if (url.psz_host == NULL || url.psz_host[0] == '\0')
681 msg_Dbg(access, "malformed URL: %s", psz_lower_url);
682 goto error;
685 msg_Dbg(access, "connect to host '%s'", psz_host);
686 sys->tcp_sock = net_Connect(access, psz_host, url.i_port, SOCK_STREAM, 0);
687 if (sys->tcp_sock < 0) {
688 msg_Err(access, "Failed to connect to RTSP server %s:%d",
689 psz_host, url.i_port);
690 goto error;
692 setsockopt (sys->tcp_sock, SOL_SOCKET, SO_KEEPALIVE, &(int){ 1 }, sizeof (int));
694 if (asprintf(&sys->content_base, "rtsp://%s:%d/", psz_host,
695 url.i_port) < 0) {
696 sys->content_base = NULL;
697 goto error;
700 sys->last_seq_nr = 0;
701 sys->keepalive_interval = (KEEPALIVE_INTERVAL - KEEPALIVE_MARGIN);
703 vlc_url_t setup_url = url;
705 // substitute "sat.ip" if present with an the host IP that was fetched during device discovery
706 if( !strncasecmp( setup_url.psz_host, "sat.ip", 6 ) ) {
707 setup_url.psz_host = psz_host;
710 // reverse the satip protocol trick, as SAT>IP believes to be RTSP
711 if( setup_url.psz_protocol == NULL ||
712 strncasecmp( setup_url.psz_protocol, "satip", 5 ) == 0 )
714 setup_url.psz_protocol = (char *)"rtsp";
717 char *psz_setup_url = vlc_uri_compose(&setup_url);
718 if( psz_setup_url == NULL )
719 goto error;
721 if (multicast) {
722 net_Printf(access, sys->tcp_sock,
723 "SETUP %s RTSP/1.0\r\n"
724 "CSeq: %d\r\n"
725 "Transport: RTP/AVP;multicast\r\n\r\n",
726 psz_setup_url, sys->cseq++);
727 } else {
728 /* open UDP socket to acquire a free port to use */
729 if (satip_bind_ports(access)) {
730 free(psz_setup_url);
731 goto error;
734 net_Printf(access, sys->tcp_sock,
735 "SETUP %s RTSP/1.0\r\n"
736 "CSeq: %d\r\n"
737 "Transport: RTP/AVP;unicast;client_port=%d-%d\r\n\r\n",
738 psz_setup_url, sys->cseq++, sys->udp_port, sys->udp_port + 1);
740 free(psz_setup_url);
742 bool interrupted = false;
743 if (rtsp_handle(access, &interrupted) != RTSP_RESULT_OK) {
744 msg_Err(access, "Failed to setup RTSP session");
745 goto error;
748 if (asprintf(&sys->control, "%sstream=%d", sys->content_base, sys->stream_id) < 0) {
749 sys->control = NULL;
750 goto error;
753 if (interrupted) {
754 msg_Warn(access, "SETUP was interrupted, abort startup");
755 goto error;
758 /* Extra sleep for compatibility with some satip servers, that
759 * can't handle PLAY right after SETUP */
760 if (vlc_msleep_i11e(VLC_TICK_FROM_MS(50)) < 0)
761 goto error;
763 /* Open UDP socket for reading if not done */
764 if (multicast) {
765 sys->udp_sock = net_OpenDgram(access, sys->udp_address, sys->udp_port, "", sys->udp_port, IPPROTO_UDP);
766 if (sys->udp_sock < 0) {
767 msg_Err(access, "Failed to open UDP socket for listening.");
768 goto error;
771 sys->rtcp_sock = net_OpenDgram(access, sys->udp_address, sys->udp_port + 1, "", sys->udp_port + 1, IPPROTO_UDP);
772 if (sys->rtcp_sock < 0) {
773 msg_Err(access, "Failed to open RTCP socket for listening.");
774 goto error;
778 net_Printf(access, sys->tcp_sock,
779 "PLAY %s RTSP/1.0\r\n"
780 "CSeq: %d\r\n"
781 "Session: %s\r\n\r\n",
782 sys->control, sys->cseq++, sys->session_id);
784 if (rtsp_handle(access, NULL) != RTSP_RESULT_OK) {
785 msg_Err(access, "Failed to play RTSP session");
786 goto error;
789 sys->fifo = block_FifoNew();
790 if (!sys->fifo) {
791 msg_Err(access, "Failed to allocate block fifo.");
792 goto error;
794 sys->fifo_size = var_InheritInteger(access, "satip-buffer");
796 if (vlc_clone(&sys->thread, satip_thread, access, VLC_THREAD_PRIORITY_INPUT)) {
797 msg_Err(access, "Failed to create worker thread.");
798 goto error;
801 access->pf_control = satip_control;
802 access->pf_block = satip_block;
804 free(psz_host);
805 free(psz_lower_url);
806 return VLC_SUCCESS;
808 error:
809 free(psz_host);
810 free(psz_lower_url);
811 vlc_UrlClean(&url);
813 satip_teardown(access);
815 if (sys->fifo)
816 block_FifoRelease(sys->fifo);
817 if (sys->udp_sock >= 0)
818 net_Close(sys->udp_sock);
819 if (sys->rtcp_sock >= 0)
820 net_Close(sys->rtcp_sock);
821 if (sys->tcp_sock >= 0)
822 net_Close(sys->tcp_sock);
824 free(sys->content_base);
825 free(sys->control);
826 return VLC_EGENERIC;
829 static void satip_close(vlc_object_t *obj)
831 stream_t *access = (stream_t *)obj;
832 access_sys_t *sys = access->p_sys;
834 vlc_cancel(sys->thread);
835 vlc_join(sys->thread, NULL);
837 satip_teardown(access);
839 block_FifoRelease(sys->fifo);
840 net_Close(sys->udp_sock);
841 net_Close(sys->rtcp_sock);
842 net_Close(sys->tcp_sock);
843 free(sys->content_base);
844 free(sys->control);