iptv: re-structure IPTV code, including adding back in IPv6 support

Backends for handling various protocols are now modular. It's not
well tested and I think the APIs could probably be improved/simplified.
This commit is contained in:
Adam Sutton 2013-11-24 12:09:57 +00:00
parent cf97cbafa7
commit d4d92c4217
5 changed files with 528 additions and 327 deletions

View file

@ -31,126 +31,56 @@
#include <unistd.h>
#include <regex.h>
#include <errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <net/if.h>
#if defined(PLATFORM_FREEBSD)
# ifndef IPV6_ADD_MEMBERSHIP
# define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
# define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP
# endif
#endif
/* **************************************************************************
* IPTV state
* *************************************************************************/
/*
* Globals
*/
iptv_input_t iptv_input;
iptv_network_t iptv_network;
tvhpoll_t *iptv_poll;
pthread_t iptv_thread;
pthread_mutex_t iptv_lock;
/* **************************************************************************
* IPTV handlers
* *************************************************************************/
/*
* URL processing - TODO: move to a library
*/
static RB_HEAD(,iptv_handler) iptv_handlers;
typedef struct url
static int
ih_cmp ( iptv_handler_t *a, iptv_handler_t *b )
{
char scheme[16];
char user[128];
char pass[128];
char host[256];
uint16_t port;
char path[256];
} url_t;
#define UC "[a-z0-9_\\-\\.!£$%^&]"
#define PC UC
#define HC "[a-z0-9\\-\\.]"
#define URL_RE "^(\\w+)://(("UC"+)(:("PC"+))?@)?("HC"+)(:([0-9]+))?(/.*)?"
regex_t urlre;
static int
urlparse ( const char *str, url_t *url )
{
regmatch_t m[12];
char buf[16];
memset(m, 0, sizeof(m));
if (regexec(&urlre, str, 12, m, 0))
return 1;
#define copy(x, i)\
{\
int len = m[i].rm_eo - m[i].rm_so;\
if (len >= sizeof(x) - 1)\
len = sizeof(x) - 1;\
memcpy(x, str+m[i].rm_so, len);\
x[len] = 0;\
}(void)0
copy(url->scheme, 1);
copy(url->user, 3);
copy(url->pass, 5);
copy(url->host, 6);
copy(url->path, 9);
copy(buf, 8);
url->port = atoi(buf);
return 0;
return strcasecmp(a->scheme, b->scheme);
}
/*
* HTTP client
*/
static int http_connect (url_t *url)
void
iptv_handler_register ( iptv_handler_t *ih, int num )
{
int fd, c, i;
char buf[1024];
if (!url->port)
url->port = strcmp("https", url->scheme) ? 80 : 443;
/* Make connection */
// TODO: move connection to thread
// TODO: this is really only for testing and to allow use of TVH webserver as input
tvhlog(LOG_DEBUG, "iptv", "connecting to http %s %d",
url->host, url->port);
fd = tcp_connect(url->host, url->port, buf, sizeof(buf), 10);
if (fd < 0) {
tvhlog(LOG_ERR, "iptv", "tcp_connect() failed %s", buf);
return -1;
iptv_handler_t *r;
while (num) {
r = RB_INSERT_SORTED(&iptv_handlers, ih, link, ih_cmp);
if (r)
tvhwarn("iptv", "attempt to re-register handler for %s",
ih->scheme);
num--;
ih++;
}
/* Send request (VERY basic) */
c = snprintf(buf, sizeof(buf), "GET %s HTTP/1.1\r\n", url->path);
tvh_write(fd, buf, c);
c = snprintf(buf, sizeof(buf), "Hostname: %s\r\n", url->host);
tvh_write(fd, buf, c);
tvh_write(fd, "\r\n", 2);
/* Read back header */
// TODO: do this properly
i = 0;
while (1) {
if (!(c = read(fd, buf+i, 1)))
continue;
i++;
if (i == 4 && !strncmp(buf, "\r\n\r\n", 4))
break;
memmove(buf, buf+1, 3); i = 3;
}
return fd;
}
/*
* Input definition
*/
static iptv_handler_t *
iptv_handler_find ( const char *scheme )
{
iptv_handler_t ih;
ih.scheme = scheme;
return RB_FIND(&iptv_handlers, &ih, link, ih_cmp);
}
/* **************************************************************************
* IPTV input
* *************************************************************************/
static const char *
iptv_input_class_get_title ( idnode_t *self )
{
@ -167,170 +97,39 @@ const idclass_t iptv_input_class = {
}
};
/*
* HTTP
*/
static int
iptv_input_start_http
( iptv_mux_t *im, url_t *url, const char *name )
{
/* Setup connection */
return http_connect(url);
}
/*
* UDP
*
* TODO: add IPv6 support
*/
static int
iptv_input_start_udp
( iptv_mux_t *im, url_t *url, const char *name )
{
int fd, solip, rxsize, reuse;
struct ifreq ifr;
struct ip_mreqn m;
struct addrinfo *addr;
struct sockaddr_in sin;
/* Open socket */
if ((fd = tvh_socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
tvherror("iptv", "%s - failed to create socket [%s]",
name, strerror(errno));
return -1;
}
/* Bind to interface */
if (im->mm_iptv_interface && *im->mm_iptv_interface) {
printf("have interface\n");
memset(&ifr, 0, sizeof(ifr));
snprintf(ifr.ifr_name, IFNAMSIZ, "%s", im->mm_iptv_interface);
if (ioctl(fd, SIOCGIFINDEX, &ifr)) {
tvherror("iptv", "%s - could not find interface %s",
name, im->mm_iptv_interface);
goto error;
}
}
/* Lookup hostname */
if (getaddrinfo(url->host, NULL, NULL, &addr)) {
tvherror("iptv", "%s - failed to lookup host %s", name, url->host);
return -1;
}
/* Bind to address */
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(url->port); // TODO: default?
sin.sin_addr.s_addr = ((struct sockaddr_in*)addr->ai_addr)->sin_addr.s_addr;
freeaddrinfo(addr);
reuse = 1;
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)) == -1) {
tvherror("iptv", "%s - cannot bind %s:%d [%s]",
name, inet_ntoa(sin.sin_addr), 0, strerror(errno));
goto error;
}
/* Join IPv4 group */
memset(&m, 0, sizeof(m));
m.imr_multiaddr.s_addr = sin.sin_addr.s_addr;
m.imr_address.s_addr = 0;
#if defined(PLATFORM_LINUX)
m.imr_ifindex = ifr.ifr_ifindex;
#elif defined(PLATFORM_FREEBSD)
m.imr_ifindex = ifr.ifr_index;
#endif
#ifdef SOL_IP
solip = SOL_IP;
#else
{
struct protoent *pent;
pent = getprotobyname("ip");
solip = (pent != NULL) ? pent->p_proto : 0;
}
#endif
if (setsockopt(fd, solip, IP_ADD_MEMBERSHIP, &m,
sizeof(struct ip_mreqn)) == -1) {
tvherror("iptv", "%s - cannot join %s [%s]",
name, inet_ntoa(m.imr_multiaddr), strerror(errno));
goto error;
}
/* Increase RX buffer size */
rxsize = IPTV_PKT_SIZE;
if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rxsize, sizeof(rxsize)) == -1)
tvhwarn("iptv", "%s - cannot increase UDP rx buffer size [%s]",
name, strerror(errno));
/* Done */
return fd;
error:
close(fd);
return -1;
}
static int
iptv_input_start_mux ( mpegts_input_t *mi, mpegts_mux_instance_t *mmi )
{
int fd, ret = SM_CODE_TUNING_FAILED;
int ret = SM_CODE_TUNING_FAILED;
iptv_mux_t *im = (iptv_mux_t*)mmi->mmi_mux;
iptv_handler_t *ih;
assert(mmi == &im->mm_iptv_instance);
char buf[256];
url_t url;
/* Already active */
if (im->mm_active)
return 0;
/* Parse URL */
im->mm_display_name((mpegts_mux_t*)im, buf, sizeof(buf));
if (urlparse(im->mm_iptv_url ?: "", &url)) {
tvherror("iptv", "%s - invalid URL [%s]", buf, im->mm_iptv_url);
return ret;
}
pthread_mutex_lock(&iptv_lock);
if (!im->mm_active) {
/* HTTP */
// TODO: this needs to happen in a thread
if (!strcmp("http", url.scheme))
fd = iptv_input_start_http(im, &url, buf);
/* UDP/RTP (both mcast) */
else if (!strcmp(url.scheme, "udp") ||
!strcmp(url.scheme, "rtp"))
fd = iptv_input_start_udp(im, &url, buf);
/* Unknown */
else {
tvherror("iptv", "%s - invalid scheme [%s]", buf, url.scheme);
fd = -1;
}
/* OK */
if (fd != -1) {
tvhpoll_event_t ev;
memset(&ev, 0, sizeof(ev));
ev.events = TVHPOLL_IN;
ev.fd = ev.data.fd = im->mm_iptv_fd = fd;
if (tvhpoll_add(iptv_poll, &ev, 1) == -1) {
tvherror("iptv", "%s - failed to add to poll q", buf);
close(fd);
} else {
im->mm_active = mmi;
im->mm_iptv_pos = 0;
im->mm_iptv_tsb = calloc(1, IPTV_PKT_SIZE);
/* Install table handlers */
mpegts_table_add(mmi->mmi_mux, DVB_PAT_BASE, DVB_PAT_MASK,
dvb_pat_callback, NULL, "pat",
MT_QUICKREQ| MT_CRC, DVB_PAT_PID);
ret = 0;
}
}
/* Find scheme handler */
ih = iptv_handler_find(url.scheme);
if (!ih) {
tvherror("iptv", "%s - unsupported scheme [%s]", buf, url.scheme);
return ret;
}
/* Start */
pthread_mutex_lock(&iptv_lock);
im->mm_active = mmi;
im->im_handler = ih;
ret = ih->start(im, &url);
pthread_mutex_unlock(&iptv_lock);
return ret;
@ -341,21 +140,26 @@ iptv_input_stop_mux ( mpegts_input_t *mi, mpegts_mux_instance_t *mmi )
{
iptv_mux_t *im = (iptv_mux_t*)mmi->mmi_mux;
assert(mmi == &im->mm_iptv_instance);
// Not active??
if (!im->mm_active)
return;
pthread_mutex_lock(&iptv_lock);
if (im->mm_active) {
/* Stop */
im->im_handler->stop(im);
// TODO: multicast will require additional work
// TODO: need to leave group?
/* Close file */
/* Close file */
if (im->mm_iptv_fd > 0) {
close(im->mm_iptv_fd); // removes from poll
im->mm_iptv_fd = -1;
/* Free memory */
free(im->mm_iptv_tsb);
im->mm_iptv_tsb = NULL;
}
/* Free memory */
free(im->mm_iptv_tsb);
im->mm_iptv_tsb = NULL;
pthread_mutex_unlock(&iptv_lock);
}
@ -380,9 +184,10 @@ iptv_input_display_name ( mpegts_input_t *mi, char *buf, size_t len )
static void *
iptv_input_thread ( void *aux )
{
int hlen, nfds, fd, r;
int nfds;
ssize_t len;
size_t off;
iptv_mux_t *im;
mpegts_mux_t *mm;
tvhpoll_event_t ev;
while ( 1 ) {
@ -395,71 +200,76 @@ iptv_input_thread ( void *aux )
} else if ( nfds == 0 ) {
continue;
}
fd = ev.data.fd;
im = ev.data.ptr;
/* Find mux */
pthread_mutex_lock(&iptv_lock);
LIST_FOREACH(mm, &iptv_network.mn_muxes, mm_network_link) {
if (((iptv_mux_t*)mm)->mm_iptv_fd == fd)
break;
}
if (!mm) {
pthread_mutex_unlock(&iptv_lock);
continue;
}
im = (iptv_mux_t*)mm;
/* Read data */
//tvhtrace("iptv", "read(%d)", fd);
r = read(fd, im->mm_iptv_tsb+im->mm_iptv_pos,
IPTV_PKT_SIZE-im->mm_iptv_pos);
/* No longer active */
if (!im->mm_active)
goto done;
/* Error */
if (r < 0) {
/* Get data */
off = 0;
if ((len = im->im_handler->read(im, &off)) < 0) {
tvhlog(LOG_ERR, "iptv", "read() error %s", strerror(errno));
// TODO: close and remove?
continue;
}
r += im->mm_iptv_pos;
/* RTP */
hlen = 0;
if (!strncmp(im->mm_iptv_url, "rtp", 3)) {
if (r < 12)
goto done;
if ((im->mm_iptv_tsb[0] & 0xC0) != 0x80)
goto done;
if ((im->mm_iptv_tsb[1] & 0x7F) != 33)
goto done;
hlen = ((im->mm_iptv_tsb[0] & 0xf) * 4) + 12;
if (im->mm_iptv_tsb[0] & 0x10) {
if (r < hlen+4)
goto done;
hlen += (im->mm_iptv_tsb[hlen+2] << 8) | (im->mm_iptv_tsb[hlen+3]*4);
hlen += 4;
}
if (r < hlen || ((r - hlen) % 188) != 0)
goto done;
im->im_handler->stop(im);
goto done;
}
/* TS */
#if 0
tvhtrace("iptv", "recv data %d (%d)", (int)r, hlen);
tvhlog_hexdump("iptv", im->mm_iptv_tsb, r);
#endif
im->mm_iptv_pos = mpegts_input_recv_packets((mpegts_input_t*)&iptv_input,
&im->mm_iptv_instance,
im->mm_iptv_tsb+hlen, r-hlen, NULL, NULL, "iptv");
//tvhtrace("iptv", "pos = %d", im->mm_iptv_pos);
/* Pass on */
im->mm_iptv_pos
= mpegts_input_recv_packets((mpegts_input_t*)&iptv_input,
im->mm_active,
im->mm_iptv_tsb+off, len,
NULL, NULL, "iptv");
done:
pthread_mutex_unlock(&iptv_lock);
}
return NULL;
}
/*
* Network definition
*/
void
iptv_input_mux_started ( iptv_mux_t *im )
{
tvhpoll_event_t ev = { 0 };
char buf[256];
im->mm_display_name((mpegts_mux_t*)im, buf, sizeof(buf));
/* Allocate input buffer */
im->mm_iptv_pos = 0;
im->mm_iptv_tsb = calloc(1, IPTV_PKT_SIZE);
/* Setup poll */
if (im->mm_iptv_fd > 0) {
ev.fd = im->mm_iptv_fd;
ev.events = TVHPOLL_IN;
ev.data.ptr = im;
/* Error? */
if (tvhpoll_add(iptv_poll, &ev, 1) == -1) {
tvherror("iptv", "%s - failed to add to poll q", buf);
close(im->mm_iptv_fd);
im->mm_iptv_fd = -1;
return;
}
}
/* Install table handlers */
// Note: we don't install NIT as we can't do mux discovery
// TODO: not currently installing ATSC handler
mpegts_table_add((mpegts_mux_t*)im, DVB_SDT_BASE, DVB_SDT_MASK,
dvb_sdt_callback, NULL, "sdt",
MT_QUICKREQ | MT_CRC, DVB_SDT_PID);
mpegts_table_add((mpegts_mux_t*)im, DVB_PAT_BASE, DVB_PAT_MASK,
dvb_pat_callback, NULL, "pat",
MT_QUICKREQ | MT_CRC, DVB_PAT_PID);
}
/* **************************************************************************
* IPTV network
* *************************************************************************/
extern const idclass_t mpegts_network_class;
const idclass_t iptv_network_class = {
.ic_super = &mpegts_network_class,
@ -492,18 +302,16 @@ iptv_network_mux_class ( mpegts_network_t *mm )
return &iptv_mux_class;
}
/*
* Intialise and load config
*/
/* **************************************************************************
* IPTV initialise
* *************************************************************************/
void iptv_init ( void )
{
pthread_t tid;
/* Initialise URL RE */
if (regcomp(&urlre, URL_RE, REG_ICASE | REG_EXTENDED)) {
tvherror("iptv", "failed to compile regexp");
exit(1);
}
/* Register handlers */
iptv_http_init();
/* Init Input */
mpegts_input_create0((mpegts_input_t*)&iptv_input,
@ -529,7 +337,6 @@ void iptv_init ( void )
tvhthread_create(&tid, NULL, mpegts_input_table_thread, &iptv_input, 1);
/* Setup TS thread */
// TODO: could set this up only when needed
iptv_poll = tvhpoll_create(10);
pthread_mutex_init(&iptv_lock, NULL);
tvhthread_create(&iptv_thread, NULL, iptv_input_thread, NULL, 1);
@ -538,7 +345,6 @@ void iptv_init ( void )
iptv_mux_load_all();
}
/******************************************************************************
* Editor Configuration
*

View file

@ -0,0 +1,122 @@
/*
* IPTV - HTTP/HTTPS handler
*
* Copyright (C) 2013 Adam Sutton
*
* 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"
#if ENABLE_CURL
/*
* Connected
*/
static void
iptv_http_conn ( void *p )
{
pthread_mutex_lock(&global_lock);
iptv_input_mux_started(p);
pthread_mutex_unlock(&global_lock);
}
/*
* Receive data
*/
static size_t
iptv_http_data
( void *p, void *buf, size_t len )
{
uint8_t *tsb;
size_t ret = len;
iptv_mux_t *im = p;
pthread_mutex_lock(&iptv_lock);
tsb = im->mm_iptv_tsb + im->mm_iptv_pos;
len = MIN(len, IPTV_PKT_SIZE - im->mm_iptv_pos);
memcpy(tsb, buf, len);
im->mm_iptv_pos
= mpegts_input_recv_packets((mpegts_input_t*)&iptv_input,
im->mm_active,
im->mm_iptv_tsb,
im->mm_iptv_pos + len,
NULL, NULL, "iptv");
pthread_mutex_unlock(&iptv_lock);
return ret;
}
/*
* Setup HTTP(S) connection
*/
static int
iptv_http_start
( iptv_mux_t *im, const url_t *u )
{
http_client_t *hc;
if (!(hc = http_connect(u, iptv_http_conn, iptv_http_data, NULL, im)))
return SM_CODE_TUNING_FAILED;
im->im_data = hc;
return 0;
}
/*
* Stop connection
*/
static void
iptv_http_stop
( iptv_mux_t *im )
{
http_close(im->im_data);
}
/*
* Initialise HTTP handler
*/
void
iptv_http_init ( void )
{
static iptv_handler_t ih[] = {
{
.scheme = "http",
.start = iptv_http_start,
.stop = iptv_http_stop,
},
{
.scheme = "https",
.start = iptv_http_start,
.stop = iptv_http_stop,
}
};
iptv_handler_register(ih, 2);
}
#else /* ENABLE_CURL */
void
iptv_http_init ( void )
{
}
#endif /* ENABLE_CURL */

View file

@ -23,19 +23,41 @@
#include "input/mpegts.h"
#include "input/mpegts/iptv.h"
#include "htsbuf.h"
#include "url.h"
#define IPTV_PKT_SIZE (300*188)
extern pthread_mutex_t iptv_lock;
typedef struct iptv_input iptv_input_t;
typedef struct iptv_network iptv_network_t;
typedef struct iptv_mux iptv_mux_t;
typedef struct iptv_service iptv_service_t;
typedef struct iptv_handler iptv_handler_t;
struct iptv_handler
{
const char *scheme;
int (*start) ( iptv_mux_t *im, const url_t *url );
void (*stop) ( iptv_mux_t *im );
// Return number of available bytes, with optional offset
// from start of mux buffer (useful for things with wrapper
// around TS)
ssize_t (*read) ( iptv_mux_t *im, size_t *off );
RB_ENTRY(iptv_handler) link;
};
void iptv_handler_register ( iptv_handler_t *ih, int num );
struct iptv_input
{
mpegts_input_t;
};
void iptv_input_mux_started ( iptv_mux_t *im );
struct iptv_network
{
mpegts_network_t;
@ -52,6 +74,10 @@ struct iptv_mux
uint8_t *mm_iptv_tsb;
int mm_iptv_pos;
iptv_handler_t *im_handler;
void *im_data;
};
iptv_mux_t* iptv_mux_create ( const char *uuid, htsmsg_t *conf );
@ -68,7 +94,10 @@ iptv_service_t *iptv_service_create0
extern iptv_input_t iptv_input;
extern iptv_network_t iptv_network;
void iptv_mux_load_all ( void );
void iptv_mux_load_all ( void );
void iptv_http_init ( void );
void iptv_udp_init ( void );
#endif /* __IPTV_PRIVATE_H__ */

View file

@ -0,0 +1,243 @@
/*
* IPTV - UDP/RTP handler
*
* Copyright (C) 2013 Adam Sutton
*
* 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 <sys/socket.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <assert.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#if defined(PLATFORM_LINUX)
#include <linux/netdevice.h>
#elif defined(PLATFORM_FREEBSD)
# include <netdb.h>
# include <net/if.h>
# ifndef IPV6_ADD_MEMBERSHIP
# define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
# define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP
# endif
#endif
/*
* Connect UDP/RTP
*/
static int
iptv_udp_start ( iptv_mux_t *im, const url_t *url )
{
int fd, solip, rxsize, reuse = 1, ipv6 = 0;
struct ifreq ifr;
struct in_addr saddr;
struct in6_addr s6addr;
char name[256], buf[256];
im->mm_display_name((mpegts_mux_t*)im, buf, sizeof(buf));
/* Determine if this is IPv6 */
if (!inet_pton(AF_INET, url->host, &saddr)) {
ipv6 = 1;
if (!inet_pton(AF_INET6, url->host, &s6addr)) {
tvherror("iptv", "%s - failed to process host", name);
return SM_CODE_TUNING_FAILED;
}
}
/* Open socket */
if ((fd = tvh_socket(ipv6 ? AF_INET6 : AF_INET, SOCK_DGRAM, 0)) == -1) {
tvherror("iptv", "%s - failed to create socket [%s]",
name, strerror(errno));
return SM_CODE_TUNING_FAILED;
}
/* Mark reuse address */
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
/* Bind to interface */
memset(&ifr, 0, sizeof(ifr));
if (im->mm_iptv_interface && *im->mm_iptv_interface) {
snprintf(ifr.ifr_name, IFNAMSIZ, "%s", im->mm_iptv_interface);
if (ioctl(fd, SIOCGIFINDEX, &ifr)) {
tvherror("iptv", "%s - could not find interface %s",
name, im->mm_iptv_interface);
goto error;
}
}
/* IPv4 */
if (!ipv6) {
struct ip_mreqn m;
struct sockaddr_in sin;
memset(&m, 0, sizeof(m));
memset(&sin, 0, sizeof(sin));
/* Bind */
sin.sin_family = AF_INET;
sin.sin_port = htons(url->port);
sin.sin_addr = saddr;
if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)) == -1) {
inet_ntop(AF_INET, &sin.sin_addr, buf, sizeof(buf));
tvherror("iptv", "%s - cannot bind %s:%hd [e=%s]",
name, buf, ntohs(sin.sin_port), strerror(errno));
goto error;
}
/* Join group */
m.imr_multiaddr = sin.sin_addr;
m.imr_address.s_addr = 0;
#if defined(PLATFORM_LINUX)
m.imr_ifindex = ifr.ifr_ifindex;
#elif defined(PLATFORM_FREEBSD)
m.imr_ifindex = ifr.ifr_index;
#endif
#ifdef SOL_IP
solip = SOL_IP;
#else
{
struct protoent *pent;
pent = getprotobyname("ip");
solip = (pent != NULL) ? pent->p_proto : 0;
}
#endif
if (setsockopt(fd, solip, IP_ADD_MEMBERSHIP, &m, sizeof(m))) {
inet_ntop(AF_INET, &m.imr_multiaddr, buf, sizeof(buf));
tvherror("iptv", "%s - cannot join %s [%s]",
name, buf, strerror(errno));
goto error;
}
/* Bind to IPv6 group */
} else {
struct ipv6_mreq m;
struct sockaddr_in6 sin;
memset(&m, 0, sizeof(m));
memset(&sin, 0, sizeof(sin));
/* Bind */
sin.sin6_family = AF_INET6;
sin.sin6_port = htons(url->port);
sin.sin6_addr = s6addr;
if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)) == -1) {
inet_ntop(AF_INET6, &sin.sin6_addr, buf, sizeof(buf));
tvherror("iptv", "%s - cannot bind %s:%hd [e=%s]",
name, buf, ntohs(sin.sin6_port), strerror(errno));
goto error;
}
/* Join group */
m.ipv6mr_multiaddr = sin.sin6_addr;
#if defined(PLATFORM_LINUX)
m.ipv6mr_interface = ifr.ifr_ifindex;
#elif defined(PLATFORM_FREEBSD)
m.ipv6mr_interface = ifr.ifr_index;
#endif
#ifdef SOL_IPV6
if (setsockopt(fd, SOL_IPV6, IPV6_ADD_MEMBERSHIP, &m, sizeof(m))) {
inet_ntop(AF_INET, &m.ipv6mr_multiaddr, buf, sizeof(buf));
tvherror("iptv", "%s - cannot join %s [%s]",
name, buf, strerror(errno));
goto error;
}
#else
tvherror("iptv", "IPv6 multicast not supported");
goto error;
#endif
}
/* Increase RX buffer size */
rxsize = IPTV_PKT_SIZE;
if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rxsize, sizeof(rxsize)) == -1)
tvhwarn("iptv", "%s - cannot increase UDP rx buffer size [%s]",
name, strerror(errno));
/* Done */
return fd;
error:
close(fd);
return -1;
}
static ssize_t
iptv_udp_read ( iptv_mux_t *im, size_t *off )
{
/* UDP/RTP should not have TS packets straddling datagrams, I think! */
im->mm_iptv_pos = 0;
/* Read */
return read(im->mm_iptv_fd, im->mm_iptv_tsb, IPTV_PKT_SIZE);
}
static ssize_t
iptv_rtp_read ( iptv_mux_t *im, size_t *off )
{
ssize_t len, hlen;
/* Raw packet */
len = iptv_udp_read(im, NULL);
if (len < 0)
return -1;
/* Strip RTP header */
if (len < 12)
return 0; // ignore
if ((im->mm_iptv_tsb[0] & 0xC0) != 0x80)
return 0;
if ((im->mm_iptv_tsb[1] & 0x7F) != 33)
return 0;
hlen = ((im->mm_iptv_tsb[0] & 0xf) * 4) + 12;
if (im->mm_iptv_tsb[0] & 0x10) {
if (len < hlen+4)
return 0;
hlen += (im->mm_iptv_tsb[hlen+2] << 8) | (im->mm_iptv_tsb[hlen+3]*4);
hlen += 4;
}
if (len < hlen || ((len - hlen) % 188) != 0)
return 0;
/* OK */
*off = hlen;
return len-hlen;
}
/*
* Initialise UDP handler
*/
void
iptv_udp_init ( void )
{
static iptv_handler_t ih[] = {
{
.scheme = "udp",
.start = iptv_udp_start,
.read = iptv_udp_read,
},
{
.scheme = "rtp",
.start = iptv_udp_start,
.read = iptv_rtp_read,
}
};
iptv_handler_register(ih, 2);
}

View file

@ -732,6 +732,7 @@ main(int argc, char **argv)
timeshift_init();
#endif
http_client_init();
tcp_server_init(opt_ipv6);
http_server_init(opt_bindaddr);
webui_init();