IPTV: add rtsp:// and rtsps:// support

This commit is contained in:
Jaroslav Kysela 2015-05-01 16:58:04 +02:00
parent 4d7d61e39a
commit 1c10cd6fe9
8 changed files with 275 additions and 6 deletions

View file

@ -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

View file

@ -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 },

View file

@ -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,

View file

@ -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));

View file

@ -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);
}

View file

@ -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__ */

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#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);
}

View file

@ -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);