From: Jaroslav Kysela Date: Fri, 1 May 2015 14:58:04 +0000 (+0200) Subject: IPTV: add rtsp:// and rtsps:// support X-Git-Tag: v4.1~84 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1c10cd6fe96b670c096e0af9c25739e0fd06ba90;p=thirdparty%2Ftvheadend.git IPTV: add rtsp:// and rtsps:// support --- diff --git a/Makefile b/Makefile index 6d1126416..98b84ee35 100644 --- a/Makefile +++ b/Makefile @@ -289,6 +289,7 @@ SRCS-${CONFIG_IPTV} += \ src/input/mpegts/iptv/iptv_service.c \ src/input/mpegts/iptv/iptv_http.c \ src/input/mpegts/iptv/iptv_udp.c \ + src/input/mpegts/iptv/iptv_rtsp.c \ src/input/mpegts/iptv/iptv_pipe.c # TSfile diff --git a/src/http.c b/src/http.c index c95ee00f7..70e912011 100644 --- a/src/http.c +++ b/src/http.c @@ -42,6 +42,7 @@ void *http_server; static http_path_list_t http_paths; static struct strtab HTTP_cmdtab[] = { + { "NONE", HTTP_CMD_NONE }, { "GET", HTTP_CMD_GET }, { "HEAD", HTTP_CMD_HEAD }, { "POST", HTTP_CMD_POST }, diff --git a/src/http.h b/src/http.h index 17638018e..d9241d262 100644 --- a/src/http.h +++ b/src/http.h @@ -100,6 +100,7 @@ typedef enum http_state { } http_state_t; typedef enum http_cmd { + HTTP_CMD_NONE, HTTP_CMD_GET, HTTP_CMD_HEAD, HTTP_CMD_POST, diff --git a/src/input/mpegts/iptv/iptv.c b/src/input/mpegts/iptv/iptv.c index 87ca500d2..407c92cf9 100644 --- a/src/input/mpegts/iptv/iptv.c +++ b/src/input/mpegts/iptv/iptv.c @@ -271,6 +271,13 @@ iptv_input_stop_mux ( mpegts_input_t *mi, mpegts_mux_instance_t *mmi ) im->mm_iptv_fd = -1; } + /* Close file2 */ + if (im->mm_iptv_fd2 > 0) { + udp_close(im->mm_iptv_connection2); // removes from poll + im->mm_iptv_connection2 = NULL; + im->mm_iptv_fd2 = -1; + } + /* Free memory */ sbuf_free(&im->mm_iptv_buffer); @@ -379,6 +386,22 @@ iptv_input_fd_started ( iptv_mux_t *im ) return -1; } } + + /* Setup poll2 */ + if (im->mm_iptv_fd2 > 0) { + ev.fd = im->mm_iptv_fd2; + ev.events = TVHPOLL_IN; + ev.data.ptr = im; + + /* Error? */ + if (tvhpoll_add(iptv_poll, &ev, 1) == -1) { + mpegts_mux_nice_name((mpegts_mux_t*)im, buf, sizeof(buf)); + tvherror("iptv", "%s - failed to add to poll q (2)", buf); + close(im->mm_iptv_fd2); + im->mm_iptv_fd2 = -1; + return -1; + } + } return 0; } @@ -581,6 +604,7 @@ void iptv_init ( void ) /* Register handlers */ iptv_http_init(); iptv_udp_init(); + iptv_rtsp_init(); iptv_pipe_init(); iptv_input = calloc(1, sizeof(iptv_input_t)); diff --git a/src/input/mpegts/iptv/iptv_http.c b/src/input/mpegts/iptv/iptv_http.c index 17dbabf25..1f136f438 100644 --- a/src/input/mpegts/iptv/iptv_http.c +++ b/src/input/mpegts/iptv/iptv_http.c @@ -27,6 +27,9 @@ static int iptv_http_header ( http_client_t *hc ) { + if (hc->hc_aux == NULL) + return 0; + /* multiple headers for redirections */ if (hc->hc_code == HTTP_STATUS_OK) { pthread_mutex_lock(&global_lock); @@ -45,6 +48,9 @@ iptv_http_data { iptv_mux_t *im = hc->hc_aux; + if (im == NULL) + return 0; + pthread_mutex_lock(&iptv_lock); tsdebug_write((mpegts_mux_t *)im, buf, len); @@ -93,8 +99,11 @@ static void iptv_http_stop ( iptv_mux_t *im ) { + http_client_t *hc = im->im_data; + + hc->hc_aux = NULL; pthread_mutex_unlock(&iptv_lock); - http_client_close(im->im_data); + http_client_close(hc); pthread_mutex_lock(&iptv_lock); } diff --git a/src/input/mpegts/iptv/iptv_private.h b/src/input/mpegts/iptv/iptv_private.h index d7f1af3ad..688e25fd9 100644 --- a/src/input/mpegts/iptv/iptv_private.h +++ b/src/input/mpegts/iptv/iptv_private.h @@ -83,6 +83,8 @@ struct iptv_mux int mm_iptv_streaming_priority; int mm_iptv_fd; udp_connection_t *mm_iptv_connection; + int mm_iptv_fd2; + udp_connection_t *mm_iptv_connection2; char *mm_iptv_url; char *mm_iptv_url_sane; char *mm_iptv_interface; @@ -123,7 +125,10 @@ void iptv_mux_load_all ( void ); void iptv_http_init ( void ); void iptv_udp_init ( void ); -void iptv_pipe_init ( void ); +void iptv_rtsp_init ( void ); +void iptv_pipe_init ( void ); + +ssize_t iptv_rtp_read ( iptv_mux_t *im, udp_multirecv_t *um ); #endif /* __IPTV_PRIVATE_H__ */ diff --git a/src/input/mpegts/iptv/iptv_rtsp.c b/src/input/mpegts/iptv/iptv_rtsp.c new file mode 100644 index 000000000..360e116fc --- /dev/null +++ b/src/input/mpegts/iptv/iptv_rtsp.c @@ -0,0 +1,221 @@ +/* + * IPTV - RTSP/RTSPS handler + * + * Copyright (C) 2015 Jaroslav Kysela + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "tvheadend.h" +#include "iptv_private.h" +#include "http.h" +#include "udp.h" + +typedef struct { + http_client_t *hc; + udp_multirecv_t um; + char *url; + gtimer_t alive_timer; +} rtsp_priv_t; + +/* + * Alive timeout + */ +static void +iptv_rtsp_alive_cb ( void *aux ) +{ + iptv_mux_t *im = aux; + rtsp_priv_t *rp = im->im_data; + + rtsp_options(rp->hc); + gtimer_arm(&rp->alive_timer, iptv_rtsp_alive_cb, im, rp->hc->hc_rtp_timeout / 2); +} + +/* + * Connected + */ +static int +iptv_rtsp_header ( http_client_t *hc ) +{ + iptv_mux_t *im = hc->hc_aux; + rtsp_priv_t *rp = im->im_data; + int r; + + if (im == NULL) + return 0; + + if (hc->hc_code != HTTP_STATUS_OK) { + tvherror("iptv", "invalid error code %d for '%s'", hc->hc_code, im->mm_iptv_url); + return 0; + } + + switch (hc->hc_cmd) { + case RTSP_CMD_SETUP: + r = rtsp_setup_decode(hc, 0); + if (r >= 0) + rtsp_play(hc, rp->url, ""); + break; + case RTSP_CMD_PLAY: + hc->hc_cmd = HTTP_CMD_NONE; + pthread_mutex_lock(&global_lock); + iptv_input_mux_started(hc->hc_aux); + gtimer_arm(&rp->alive_timer, iptv_rtsp_alive_cb, im, hc->hc_rtp_timeout / 2); + pthread_mutex_unlock(&global_lock); + break; + default: + break; + } + + return 0; +} + +/* + * Receive data + */ +static int +iptv_rtsp_data + ( http_client_t *hc, void *buf, size_t len ) +{ + iptv_mux_t *im = hc->hc_aux; + + if (im == NULL) + return 0; + + if (len > 0) + tvherror("iptv", "unknown data %zd received for '%s'", len, im->mm_iptv_url); + + return 0; +} + +/* + * Setup RTSP(S) connection + */ +static int +iptv_rtsp_start + ( iptv_mux_t *im, const char *raw, const url_t *u ) +{ + rtsp_priv_t *rp; + http_client_t *hc; + udp_connection_t *rtp, *rtpc; + int r; + + if (!(hc = http_client_connect(im, RTSP_VERSION_1_0, u->scheme, + u->host, u->port, NULL))) + return SM_CODE_TUNING_FAILED; + + if (udp_bind_double(&rtp, &rtpc, + "IPTV", "rtp", "rtcp", + NULL, 0, NULL, + 128*1024, 16384, 4*1024, 4*1024) < 0) { + http_client_close(hc); + return SM_CODE_TUNING_FAILED; + } + + hc->hc_hdr_received = iptv_rtsp_header; + hc->hc_data_received = iptv_rtsp_data; + hc->hc_handle_location = 1; /* allow redirects */ + http_client_register(hc); /* register to the HTTP thread */ + r = rtsp_setup(hc, u->path, u->query, NULL, + ntohs(IP_PORT(rtp->ip)), + ntohs(IP_PORT(rtpc->ip))); + if (r < 0) { + udp_close(rtpc); + udp_close(rtp); + http_client_close(hc); + return SM_CODE_TUNING_FAILED; + } + + rp = calloc(1, sizeof(*rp)); + rp->hc = hc; + udp_multirecv_init(&rp->um, IPTV_PKTS, IPTV_PKT_PAYLOAD); + rp->url = strdup(u->raw); + + im->im_data = rp; + im->mm_iptv_fd = rtp->fd; + im->mm_iptv_connection = rtp; + im->mm_iptv_fd2 = rtpc->fd; + im->mm_iptv_connection2 = rtpc; + + return 0; +} + +/* + * Stop connection + */ +static void +iptv_rtsp_stop + ( iptv_mux_t *im ) +{ + rtsp_priv_t *rp = im->im_data; + + lock_assert(&global_lock); + + if (rp == NULL) + return; + im->im_data = NULL; + rp->hc->hc_aux = NULL; + pthread_mutex_unlock(&iptv_lock); + gtimer_disarm(&rp->alive_timer); + udp_multirecv_free(&rp->um); + http_client_close(rp->hc); + free(rp->url); + free(rp); + pthread_mutex_lock(&iptv_lock); +} + +/* + * Read data + */ +static ssize_t +iptv_rtsp_read ( iptv_mux_t *im ) +{ + rtsp_priv_t *rp = im->im_data; + udp_multirecv_t *um = &rp->um; + ssize_t r; + uint8_t buf[1500]; + + /* RTPC - ignore all incoming packets for now */ + do { + r = recv(im->mm_iptv_fd2, buf, sizeof(buf), MSG_DONTWAIT); + } while (r > 0); + + r = iptv_rtp_read(im, um); + if (r < 0 && ERRNO_AGAIN(errno)) + r = 0; + return r; +} + +/* + * Initialise RTSP handler + */ + +void +iptv_rtsp_init ( void ) +{ + static iptv_handler_t ih[] = { + { + .scheme = "rtsp", + .start = iptv_rtsp_start, + .stop = iptv_rtsp_stop, + .read = iptv_rtsp_read, + }, + { + .scheme = "rtsps", + .start = iptv_rtsp_start, + .stop = iptv_rtsp_stop, + .read = iptv_rtsp_read, + } + }; + iptv_handler_register(ih, 2); +} diff --git a/src/input/mpegts/iptv/iptv_udp.c b/src/input/mpegts/iptv/iptv_udp.c index 3989cc1d0..e561c375a 100644 --- a/src/input/mpegts/iptv/iptv_udp.c +++ b/src/input/mpegts/iptv/iptv_udp.c @@ -92,14 +92,13 @@ iptv_udp_read ( iptv_mux_t *im ) return res; } -static ssize_t -iptv_rtp_read ( iptv_mux_t *im ) +ssize_t +iptv_rtp_read ( iptv_mux_t *im, udp_multirecv_t *um ) { ssize_t len, hlen; uint8_t *rtp; int i, n; struct iovec *iovec; - udp_multirecv_t *um = im->im_data; ssize_t res = 0; n = udp_multirecv_read(um, im->mm_iptv_fd, IPTV_PKTS, &iovec); @@ -145,6 +144,14 @@ iptv_rtp_read ( iptv_mux_t *im ) return res; } +static ssize_t +iptv_udp_rtp_read ( iptv_mux_t *im ) +{ + udp_multirecv_t *um = im->im_data; + + return iptv_rtp_read(im, um); +} + /* * Initialise UDP handler */ @@ -163,7 +170,7 @@ iptv_udp_init ( void ) .scheme = "rtp", .start = iptv_udp_start, .stop = iptv_udp_stop, - .read = iptv_rtp_read, + .read = iptv_udp_rtp_read, } }; iptv_handler_register(ih, 2);