iptv: updated to work with new API

This isn't fully tested and no doubt there are configuration issues to
be resolved. But I was able to receive an RTP stream using negge's server.
This commit is contained in:
Adam Sutton 2013-08-17 19:50:52 +01:00
parent 275845ca19
commit 1c2a0b05a1
3 changed files with 280 additions and 103 deletions

View file

@ -20,11 +20,28 @@
#include "iptv_private.h"
#include "tcp.h"
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <assert.h>
#include <string.h>
#include <regex.h>
#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
/*
* Globals
@ -41,46 +58,47 @@ pthread_mutex_t iptv_lock;
typedef struct url
{
char buf[2048];
const char *scheme;
const char *host;
uint16_t port;
const char *path;
char scheme[16];
char user[128];
char pass[128];
char host[256];
uint16_t port;
char path[256];
} url_t;
static int
url_parse ( url_t *up, const char *urlstr )
#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 )
{
char *t1, *t2;
strcpy(up->buf, urlstr);
/* Scheme */
up->scheme = t1 = up->buf;
if (!(t2 = strstr(t1, "://"))) {
regmatch_t m[12];
char buf[16];
memset(m, 0, sizeof(m));
if (regexec(&urlre, str, 12, m, 0))
return 1;
}
*t2 = 0;
/* Host */
up->host = t1 = t2 + 3;
up->path = NULL;
if ((t2 = strstr(t1, "/"))) {
*t2 = 0;
up->path = t2 + 1;
}
/* Port */
up->port = 0;
if (!(t2 = strstr(up->host, ":"))) {
if (!strcmp(up->scheme, "https"))
up->port = 443;
else if (!strcmp(up->scheme, "http"))
up->port = 80;
} else {
*t2 = 0;
up->port = atoi(t2+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;
}
@ -93,7 +111,7 @@ static int http_connect (const char *urlstr)
int fd, c, i;
char buf[1024];
url_t url;
if (url_parse(&url, urlstr))
if (urlparse(urlstr, &url))
return -1;
/* Make connection */
@ -152,35 +170,149 @@ const idclass_t iptv_input_class = {
* HTTP
*/
static int
iptv_input_start_http ( iptv_mux_t *im )
iptv_input_start_http
( iptv_mux_t *im, url_t *url, const char *name )
{
/* Setup connection */
im->mm_iptv_fd = http_connect(im->mm_iptv_url);
return http_connect("TODO");
}
return im->mm_iptv_fd <= 0 ? SM_CODE_TUNING_FAILED : 0;
/*
* 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 = 65536;//262142;
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 ret = SM_CODE_TUNING_FAILED;
int fd, ret = SM_CODE_TUNING_FAILED;
iptv_mux_t *im = (iptv_mux_t*)mmi->mmi_mux;
assert(mmi == &im->mm_iptv_instance);
char buf[256];
url_t 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 (!strncmp(im->mm_iptv_url, "http", 4))
ret = iptv_input_start_http(im);
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 (!ret) {
if (fd != -1) {
struct epoll_event ev;
memset(&ev, 0, sizeof(ev));
ev.events = EPOLLIN;
ev.data.fd = im->mm_iptv_fd;
ev.data.fd = im->mm_iptv_fd = fd;
epoll_ctl(iptv_poll_fd, EPOLL_CTL_ADD, im->mm_iptv_fd, &ev);
im->mm_active = mmi;
@ -190,6 +322,7 @@ iptv_input_start_mux ( mpegts_input_t *mi, mpegts_mux_instance_t *mmi )
MT_QUICKREQ| MT_CRC, DVB_PAT_PID);
// TODO: need to fire mux start event
ret = 0;
}
}
pthread_mutex_unlock(&iptv_lock);
@ -233,11 +366,10 @@ iptv_input_display_name ( mpegts_input_t *mi, char *buf, size_t len )
snprintf(buf, len, "IPTV");
}
static void *
iptv_input_thread ( void *aux )
{
int nfds, fd, r, pos = 0;
int hlen, nfds, fd, r, pos = 0;
uint8_t tsb[65536];
struct epoll_event ev;
iptv_mux_t *im;
@ -278,13 +410,33 @@ iptv_input_thread ( void *aux )
}
im = (iptv_mux_t*)mm;
/* Raw TS */
if (!strncmp(im->mm_iptv_url, "http", 4)) {
pos = mpegts_input_recv_packets((mpegts_input_t*)&iptv_input,
&im->mm_iptv_instance,
tsb, r, NULL, NULL, "iptv");
/* RTP */
hlen = 0;
if (!strncmp(im->mm_iptv_url, "rtp", 3)) {
if (r < 12)
goto done;
if ((tsb[0] & 0xC0) != 0x80)
goto done;
if ((tsb[1] & 0x7F) != 33)
goto done;
hlen = ((tsb[0] & 0xf) * 4) + 12;
if (tsb[0] & 0x10) {
if (r < hlen+4)
goto done;
hlen += (tsb[hlen+2] << 8) | (tsb[hlen+3]*4);
hlen += 4;
}
if (r < hlen || ((r - hlen) % 188) != 0)
goto done;
}
/* TS */
pos = mpegts_input_recv_packets((mpegts_input_t*)&iptv_input,
&im->mm_iptv_instance,
tsb+hlen, r-hlen, NULL, NULL, "iptv");
if(pos)
printf("pos = %d\n", pos);
done:
pthread_mutex_unlock(&iptv_lock);
}
return NULL;
@ -310,6 +462,13 @@ iptv_network_create_mux
return NULL;
}
static mpegts_mux_t *
iptv_network_create_mux2
( mpegts_network_t *mm, htsmsg_t *conf )
{
return (mpegts_mux_t*)iptv_mux_create(NULL, conf);
}
static mpegts_service_t *
iptv_network_create_service
( mpegts_mux_t *mm, uint16_t sid, uint16_t pmt_pid )
@ -318,12 +477,25 @@ iptv_network_create_service
iptv_service_create(NULL, (iptv_mux_t*)mm, sid, pmt_pid);
}
static const idclass_t *
iptv_network_mux_class ( mpegts_network_t *mm )
{
extern const idclass_t iptv_mux_class;
return &iptv_mux_class;
}
/*
* Intialise and load config
*/
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);
}
/* Init Input */
mpegts_input_create0((mpegts_input_t*)&iptv_input,
@ -340,6 +512,8 @@ void iptv_init ( void )
&iptv_network_class, NULL, "IPTV Network", NULL);
iptv_network.mn_create_mux = iptv_network_create_mux;
iptv_network.mn_create_service = iptv_network_create_service;
iptv_network.mn_mux_class = iptv_network_mux_class;
iptv_network.mn_mux_create2 = iptv_network_create_mux2;
/* Link */
mpegts_input_set_network((mpegts_input_t*)&iptv_input,

View file

@ -32,29 +32,64 @@ const idclass_t iptv_mux_class =
.ic_class = "iptv_mux",
.ic_caption = "IPTV Multiplex",
.ic_properties = (const property_t[]){
#if 0
{ PROPDEF1("iptv_url", "URL",
PT_STR, iptv_mux_t, mm_iptv_url) },
{ PROPDEF1("iptv_interface", "Interface",
PT_STR, iptv_mux_t, mm_iptv_interface) },
#endif
{
.type = PT_STR,
.id = "iptv_url",
.name = "URL",
.off = offsetof(iptv_mux_t, mm_iptv_url),
},
{
.type = PT_STR,
.id = "iptv_interface",
.name = "Interface",
.off = offsetof(iptv_mux_t, mm_iptv_interface),
},
{}
}
};
static void
iptv_mux_config_save ( mpegts_mux_t *mm )
{
htsmsg_t *c = htsmsg_create_map();
mpegts_mux_save(mm, c);
hts_settings_save(c, "input/iptv/muxes/%s/config",
idnode_uuid_as_str(&mm->mm_id));
htsmsg_destroy(c);
}
static void
iptv_mux_delete ( mpegts_mux_t *mm )
{
hts_settings_remove("input/iptv/muxes/%s/config",
idnode_uuid_as_str(&mm->mm_id));
mpegts_mux_delete(mm);
}
static void
iptv_mux_display_name ( mpegts_mux_t *mm, char *buf, size_t len )
{
iptv_mux_t *im = (iptv_mux_t*)mm;
strncpy(buf, im->mm_iptv_url, len);
}
/*
* Create
*/
iptv_mux_t *
iptv_mux_create ( const char *uuid, const char *url )
iptv_mux_create ( const char *uuid, htsmsg_t *conf )
{
/* Create Mux */
iptv_mux_t *im =
mpegts_mux_create(iptv_mux, NULL,
mpegts_mux_create(iptv_mux, uuid,
(mpegts_network_t*)&iptv_network,
MPEGTS_ONID_NONE, MPEGTS_TSID_NONE, NULL);
if (url)
im->mm_iptv_url = strdup(url);
MPEGTS_ONID_NONE, MPEGTS_TSID_NONE, conf);
/* Callbacks */
im->mm_display_name = iptv_mux_display_name;
im->mm_config_save = iptv_mux_config_save;
im->mm_delete = iptv_mux_delete;
/* Create Instance */
mpegts_mux_instance_create0(&im->mm_iptv_instance,
@ -69,50 +104,17 @@ iptv_mux_create ( const char *uuid, const char *url )
/*
* Load
*/
static void
iptv_mux_load_one ( iptv_mux_t *im, htsmsg_t *c )
{
#if 0
const char *str;
/* Load core */
mpegts_mux_load_one((mpegts_mux_t*)im, c);
/* URL */
if ((str = htsmsg_get_str(c, "iptv_url")))
tvh_str_update(&im->mm_iptv_url, str);
#endif
}
void
iptv_mux_load_all ( void )
{
htsmsg_t *s, *m;
htsmsg_t *s, *e;
htsmsg_field_t *f;
iptv_mux_t *im;
if ((s = hts_settings_load_r(1, "input/mpegts/iptv/muxes"))) {
if ((s = hts_settings_load_r(1, "input/iptv/muxes"))) {
HTSMSG_FOREACH(f, s) {
if (!(m = htsmsg_get_map_by_field(f)) || !(m = htsmsg_get_map(m, "config"))) {
tvhlog(LOG_ERR, "iptv", "failed to load mux config %s", f->hmf_name);
continue;
}
printf("UUID: %s\n", f->hmf_name);
printf("CONFIG:\n");
htsmsg_print(m);
/* Create */
im = iptv_mux_create(f->hmf_name, NULL);
if (!im) {
tvhlog(LOG_ERR, "iptv", "failed to load mux config %s", f->hmf_name);
continue;
}
/* Configure */
iptv_mux_load_one(im, m);
/* Load services */
iptv_service_load_all(im, f->hmf_name);
if (!(e = htsmsg_get_map_by_field(f))) continue;
if (!(e = htsmsg_get_map(e, "config"))) continue;
(void)iptv_mux_create(f->hmf_name, e);
}
}
}

View file

@ -46,9 +46,10 @@ struct iptv_mux
int mm_iptv_fd;
mpegts_mux_instance_t mm_iptv_instance;
char *mm_iptv_url;
char *mm_iptv_interface;
};
iptv_mux_t* iptv_mux_create ( const char *uuid, const char *url );
iptv_mux_t* iptv_mux_create ( const char *uuid, htsmsg_t *conf );
struct iptv_service
{