Add SAT>IP support (remote network tuners)

This commit is contained in:
Jaroslav Kysela 2014-04-09 19:52:23 +02:00
parent 4830879ab2
commit 8583936c71
12 changed files with 3179 additions and 0 deletions

View file

@ -209,6 +209,13 @@ SRCS-${CONFIG_LINUXDVB} += \
src/input/mpegts/linuxdvb/linuxdvb_rotor.c \ src/input/mpegts/linuxdvb/linuxdvb_rotor.c \
src/input/mpegts/linuxdvb/linuxdvb_en50494.c src/input/mpegts/linuxdvb/linuxdvb_en50494.c
# SATIP
SRCS-${CONFIG_SATIP_CLIENT} += \
src/input/mpegts/satip/satip.c \
src/input/mpegts/satip/satip_frontend.c \
src/input/mpegts/satip/satip_satconf.c \
src/input/mpegts/satip/satip_rtsp.c
# IPTV # IPTV
SRCS-${CONFIG_IPTV} += \ SRCS-${CONFIG_IPTV} += \
src/input/mpegts/iptv/iptv.c \ src/input/mpegts/iptv/iptv.c \

12
configure vendored
View file

@ -19,6 +19,7 @@ OPTIONS=(
"cwc:yes" "cwc:yes"
"v4l:no" "v4l:no"
"linuxdvb:yes" "linuxdvb:yes"
"satip_client:yes"
"iptv:yes" "iptv:yes"
"tsfile:yes" "tsfile:yes"
"dvbscan:yes" "dvbscan:yes"
@ -174,6 +175,17 @@ if enabled_or_auto curl; then
fi fi
fi fi
#
# SAT>IP client
#
if enabled_or_auto satip_client; then
if enabled curl; then
enable upnp
else
die "SAT>IP client requires curl enabled"
fi
fi
# #
# uriparser # uriparser
# #

View file

@ -59,6 +59,7 @@ void api_init ( void );
void api_done ( void ); void api_done ( void );
void api_idnode_init ( void ); void api_idnode_init ( void );
void api_input_init ( void ); void api_input_init ( void );
void api_input_satip_init ( void );
void api_service_init ( void ); void api_service_init ( void );
void api_channel_init ( void ); void api_channel_init ( void );
void api_mpegts_init ( void ); void api_mpegts_init ( void );

View file

@ -128,6 +128,9 @@ void tvh_input_stream_destroy ( tvh_input_stream_t *st );
#if ENABLE_LINUXDVB #if ENABLE_LINUXDVB
#include "input/mpegts/linuxdvb.h" #include "input/mpegts/linuxdvb.h"
#endif #endif
#if ENABLE_SATIP_CLIENT
#include "input/mpegts/satip/satip.h"
#endif
#endif #endif
#endif /* __TVH_INPUT_H__ */ #endif /* __TVH_INPUT_H__ */

View file

@ -51,6 +51,11 @@ mpegts_init ( int linuxdvb_mask, str_list_t *tsfiles, int tstuners )
linuxdvb_init(linuxdvb_mask); linuxdvb_init(linuxdvb_mask);
#endif #endif
/* SAT>IP DVB client */
#if ENABLE_SATIP_CLIENT
satip_init();
#endif
/* Mux schedulers */ /* Mux schedulers */
#if ENABLE_MPEGTS #if ENABLE_MPEGTS
mpegts_mux_sched_init(); mpegts_mux_sched_init();
@ -71,6 +76,9 @@ mpegts_done ( void )
#if ENABLE_LINUXDVB #if ENABLE_LINUXDVB
tvhftrace("main", linuxdvb_done); tvhftrace("main", linuxdvb_done);
#endif #endif
#if ENABLE_SATIP_CLIENT
tvhftrace("main", satip_done);
#endif
#if ENABLE_TSFILE #if ENABLE_TSFILE
tvhftrace("main", tsfile_done); tvhftrace("main", tsfile_done);
#endif #endif

View file

@ -0,0 +1,818 @@
/*
* Tvheadend - SAT-IP client
*
* Copyright (C) 2014 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 "input.h"
#include "htsbuf.h"
#include "htsmsg_xml.h"
#include "upnp.h"
#include "settings.h"
#include "satip_private.h"
#include <arpa/inet.h>
#include <openssl/sha.h>
/*
* SAT-IP client
*/
static void
satip_device_class_save ( idnode_t *in )
{
satip_device_save((satip_device_t *)in);
}
static idnode_set_t *
satip_device_class_get_childs ( idnode_t *in )
{
satip_device_t *sd = (satip_device_t *)in;
idnode_set_t *is = idnode_set_create();
satip_frontend_t *lfe;
TAILQ_FOREACH(lfe, &sd->sd_frontends, sf_link)
idnode_set_add(is, &lfe->ti_id, NULL);
return is;
}
static const char *
satip_device_class_get_title( idnode_t *in )
{
static char buf[256];
satip_device_t *sd = (satip_device_t *)in;
snprintf(buf, sizeof(buf),
"%s - %s", sd->sd_info.friendlyname, sd->sd_info.addr);
return buf;
}
const idclass_t satip_device_class =
{
.ic_class = "satip_client",
.ic_caption = "SAT>IP Client",
.ic_save = satip_device_class_save,
.ic_get_childs = satip_device_class_get_childs,
.ic_get_title = satip_device_class_get_title,
.ic_properties = (const property_t[]){
{
.type = PT_BOOL,
.id = "fullmux_ok",
.name = "Full Mux Rx mode supported",
.opts = PO_ADVANCED,
.off = offsetof(satip_device_t, sd_fullmux_ok),
},
{
.type = PT_INT,
.id = "sigscale",
.name = "Signal scale (240 or 100)",
.opts = PO_ADVANCED,
.off = offsetof(satip_device_t, sd_sig_scale),
},
{
.type = PT_INT,
.id = "pids_max",
.name = "Maximum PIDs",
.opts = PO_ADVANCED,
.off = offsetof(satip_device_t, sd_pids_max),
},
{
.type = PT_INT,
.id = "pids_len",
.name = "Maximum length of PIDs",
.opts = PO_ADVANCED,
.off = offsetof(satip_device_t, sd_pids_len),
},
{
.type = PT_BOOL,
.id = "pids_deladd",
.name = "addpids/delpids supported",
.opts = PO_ADVANCED,
.off = offsetof(satip_device_t, sd_pids_deladd),
},
{
.type = PT_STR,
.id = "addr",
.name = "IP Address",
.opts = PO_RDONLY | PO_NOSAVE,
.off = offsetof(satip_device_t, sd_info.addr),
},
{
.type = PT_STR,
.id = "device_uuid",
.name = "UUID",
.opts = PO_RDONLY,
.off = offsetof(satip_device_t, sd_info.uuid),
},
{
.type = PT_STR,
.id = "friendly",
.name = "Friendly Name",
.opts = PO_RDONLY | PO_NOSAVE,
.off = offsetof(satip_device_t, sd_info.friendlyname),
},
{
.type = PT_STR,
.id = "serialnum",
.name = "Serial Number",
.opts = PO_RDONLY | PO_NOSAVE,
.off = offsetof(satip_device_t, sd_info.serialnum),
},
{
.type = PT_STR,
.id = "tunercfg",
.name = "Tuner Configuration",
.opts = PO_RDONLY | PO_NOSAVE,
.off = offsetof(satip_device_t, sd_info.tunercfg),
},
{
.type = PT_STR,
.id = "manufacturer",
.name = "Manufacturer",
.opts = PO_RDONLY | PO_NOSAVE,
.off = offsetof(satip_device_t, sd_info.manufacturer),
},
{
.type = PT_STR,
.id = "manufurl",
.name = "Manufacturer URL",
.opts = PO_RDONLY | PO_NOSAVE,
.off = offsetof(satip_device_t, sd_info.manufacturerURL),
},
{
.type = PT_STR,
.id = "modeldesc",
.name = "Model Description",
.opts = PO_RDONLY | PO_NOSAVE,
.off = offsetof(satip_device_t, sd_info.modeldesc),
},
{
.type = PT_STR,
.id = "modelname",
.name = "Model Name",
.opts = PO_RDONLY | PO_NOSAVE,
.off = offsetof(satip_device_t, sd_info.modelname),
},
{
.type = PT_STR,
.id = "modelnum",
.name = "Model Number",
.opts = PO_RDONLY | PO_NOSAVE,
.off = offsetof(satip_device_t, sd_info.modelnum),
},
{
.type = PT_STR,
.id = "bootid",
.name = "Boot ID",
.opts = PO_RDONLY | PO_NOSAVE,
.off = offsetof(satip_device_t, sd_info.bootid),
},
{
.type = PT_STR,
.id = "configid",
.name = "Config ID",
.opts = PO_RDONLY | PO_NOSAVE,
.off = offsetof(satip_device_t, sd_info.configid),
},
{
.type = PT_STR,
.id = "deviceid",
.name = "Device ID",
.opts = PO_RDONLY | PO_NOSAVE,
.off = offsetof(satip_device_t, sd_info.deviceid),
},
{
.type = PT_STR,
.id = "presentation",
.name = "Presentation",
.opts = PO_RDONLY | PO_NOSAVE,
.off = offsetof(satip_device_t, sd_info.presentation),
},
{
.type = PT_STR,
.id = "location",
.name = "Location",
.opts = PO_RDONLY | PO_NOSAVE,
.off = offsetof(satip_device_t, sd_info.location),
},
{
.type = PT_STR,
.id = "server",
.name = "Server",
.opts = PO_RDONLY | PO_NOSAVE,
.off = offsetof(satip_device_t, sd_info.server),
},
{
.type = PT_STR,
.id = "myaddr",
.name = "Local IP Address",
.opts = PO_RDONLY | PO_NOSAVE,
.off = offsetof(satip_device_t, sd_info.myaddr),
},
{}
}
};
/*
* Create entry
*/
static void
satip_device_calc_bin_uuid( uint8_t *uuid, const char *satip_uuid )
{
SHA_CTX sha1;
SHA1_Init(&sha1);
SHA1_Update(&sha1, (void*)satip_uuid, strlen(satip_uuid));
SHA1_Final(uuid, &sha1);
}
static void
satip_device_calc_uuid( uuid_t *uuid, const char *satip_uuid )
{
uint8_t uuidbin[20];
satip_device_calc_bin_uuid(uuidbin, satip_uuid);
bin2hex(uuid->hex, sizeof(uuid->hex), uuidbin, sizeof(uuidbin));
}
static void
satip_device_hack( satip_device_t *sd )
{
if (sd->sd_info.deviceid[0] &&
strcmp(sd->sd_info.server, "Linux/1.0 UPnP/1.1 IDL4K/1.0") == 0) {
/* AXE Linux distribution - Inverto firmware */
/* version V1.13.0.105 and probably less */
/* really ugly firmware - soooooo much restrictions */
sd->sd_fullmux_ok = 0;
sd->sd_pids_max = 32;
sd->sd_pids_deladd = 0;
tvhwarn("satip", "Detected old Inverto firmware V1.13.0.105 and less");
tvhwarn("satip", "Upgrade to V1.16.0.120 - http://http://www.inverto.tv/support/ - IDL400s");
}
}
static satip_device_t *
satip_device_create( satip_device_info_t *info )
{
satip_device_t *sd = calloc(1, sizeof(satip_device_t));
uuid_t uuid;
htsmsg_t *conf = NULL, *feconf = NULL;
char *argv[10];
int i, j, n, m, fenum, t2, save = 0;
dvb_fe_type_t type;
satip_device_calc_uuid(&uuid, info->uuid);
conf = hts_settings_load("input/satip/adapters/%s", uuid.hex);
/* some sane defaults */
sd->sd_fullmux_ok = 1;
sd->sd_pids_len = 127;
sd->sd_pids_max = 32;
sd->sd_pids_deladd = 1;
sd->sd_sig_scale = 240;
if (!tvh_hardware_create0((tvh_hardware_t*)sd, &satip_device_class,
uuid.hex, conf)) {
free(sd);
return NULL;
}
TAILQ_INIT(&sd->sd_frontends);
/* we may check if uuid matches, but the SHA hash should be enough */
if (sd->sd_info.uuid)
free(sd->sd_info.uuid);
#define ASSIGN(x) sd->sd_info.x = info->x; info->x = NULL
ASSIGN(myaddr);
ASSIGN(addr);
ASSIGN(uuid);
ASSIGN(bootid);
ASSIGN(configid);
ASSIGN(deviceid);
ASSIGN(server);
ASSIGN(location);
ASSIGN(friendlyname);
ASSIGN(manufacturer);
ASSIGN(manufacturerURL);
ASSIGN(modeldesc);
ASSIGN(modelname);
ASSIGN(modelnum);
ASSIGN(serialnum);
ASSIGN(presentation);
ASSIGN(tunercfg);
#undef ASSIGN
/*
* device specific hacks
*/
satip_device_hack(sd);
if (conf)
feconf = htsmsg_get_map(conf, "frontends");
save = !conf || !feconf;
n = http_tokenize(sd->sd_info.tunercfg, argv, 10, ',');
for (i = 0, fenum = 1; i < n; i++) {
type = DVB_TYPE_NONE;
t2 = 0;
if (strncmp(argv[i], "DVBS2-", 6) == 0) {
type = DVB_TYPE_S;
m = atoi(argv[i] + 6);
} else if (strncmp(argv[i], "DVBT2-", 6) == 0) {
type = DVB_TYPE_T;
m = atoi(argv[i] + 6);
t2 = 1;
} else if (strncmp(argv[i], "DVBT-", 5) == 0) {
type = DVB_TYPE_T;
m = atoi(argv[i] + 5);
}
if (type == DVB_TYPE_NONE) {
tvhlog(LOG_ERR, "satip", "%s: bad tuner type [%s]", sd->sd_info.addr, argv[i]);
} else if (m < 0 || m > 32) {
tvhlog(LOG_ERR, "satip", "%s: bad tuner count [%s]", sd->sd_info.addr, argv[i]);
} else {
for (j = 0; j < m; j++)
if (satip_frontend_create(feconf, sd, type, t2, fenum))
fenum++;
}
}
if (save)
satip_device_save(sd);
htsmsg_destroy(conf);
return sd;
}
static satip_device_t *
satip_device_find( const char *satip_uuid )
{
tvh_hardware_t *th;
uint8_t binuuid[20];
satip_device_calc_bin_uuid(binuuid, satip_uuid);
TVH_HARDWARE_FOREACH(th) {
if (idnode_is_instance(&th->th_id, &satip_device_class) &&
memcmp(th->th_id.in_uuid, binuuid, UUID_BIN_SIZE) == 0)
return (satip_device_t *)th;
}
return NULL;
}
void
satip_device_save( satip_device_t *sd )
{
satip_frontend_t *lfe;
htsmsg_t *m, *l;
m = htsmsg_create_map();
idnode_save(&sd->th_id, m);
l = htsmsg_create_map();
TAILQ_FOREACH(lfe, &sd->sd_frontends, sf_link)
satip_frontend_save(lfe, l);
htsmsg_add_msg(m, "frontends", l);
hts_settings_save(m, "input/satip/adapters/%s",
idnode_uuid_as_str(&sd->th_id));
htsmsg_destroy(m);
}
void
satip_device_destroy( satip_device_t *sd )
{
satip_frontend_t *lfe;
lock_assert(&global_lock);
while ((lfe = TAILQ_FIRST(&sd->sd_frontends)) != NULL)
satip_frontend_delete(lfe);
#define FREEM(x) free(sd->sd_info.x)
FREEM(myaddr);
FREEM(addr);
FREEM(uuid);
FREEM(bootid);
FREEM(configid);
FREEM(deviceid);
FREEM(location);
FREEM(server);
FREEM(friendlyname);
FREEM(manufacturer);
FREEM(manufacturerURL);
FREEM(modeldesc);
FREEM(modelname);
FREEM(modelnum);
FREEM(serialnum);
FREEM(presentation);
FREEM(tunercfg);
#undef FREEM
tvh_hardware_delete((tvh_hardware_t*)sd);
free(sd);
}
/*
* Discovery job
*/
typedef struct satip_discovery {
TAILQ_ENTRY(satip_discovery) disc_link;
char *myaddr;
char *location;
char *server;
char *uuid;
char *bootid;
char *configid;
char *deviceid;
url_t url;
http_client_t *http_client;
time_t http_start;
char *desc;
} satip_discovery_t;
TAILQ_HEAD(satip_discovery_queue, satip_discovery);
static int satip_discoveries_count;
static struct satip_discovery_queue satip_discoveries;
static upnp_service_t *satip_discovery_service;
static gtimer_t satip_discovery_timer;
static gtimer_t satip_discovery_timerq;
static void
satip_discovery_destroy(satip_discovery_t *d, int unlink)
{
if (d == NULL)
return;
if (unlink) {
satip_discoveries_count--;
TAILQ_REMOVE(&satip_discoveries, d, disc_link);
}
if (d->http_client)
http_close(d->http_client);
free(d->myaddr);
free(d->location);
free(d->server);
free(d->uuid);
free(d->bootid);
free(d->configid);
free(d->deviceid);
free(d->desc);
free(d);
}
static satip_discovery_t *
satip_discovery_find(satip_discovery_t *d)
{
satip_discovery_t *sd;
TAILQ_FOREACH(sd, &satip_discoveries, disc_link)
if (strcmp(sd->uuid, d->uuid) == 0)
return sd;
return NULL;
}
static size_t
satip_discovery_http_data(void *p, void *buf, size_t len)
{
satip_discovery_t *d = p;
size_t size;
char *s;
htsmsg_t *xml = NULL, *tags, *root, *device;
const char *friendlyname, *manufacturer, *manufacturerURL, *modeldesc;
const char *modelname, *modelnum, *serialnum;
const char *presentation, *tunercfg;
const char *cs;
satip_device_info_t info;
char errbuf[100];
size = d->desc ? strlen(d->desc) : 0;
if (len + size > 16384)
goto finish;
d->desc = realloc(d->desc, size + len + 1);
memcpy(d->desc + size, buf, len);
size += len;
d->desc[size] = '\0';
s = d->desc + size - 1;
while (s != d->desc && *s != '/')
s--;
if (s != d->desc)
s--;
if (strncmp(s, "</root>", 7))
return len;
/* Parse */
xml = htsmsg_xml_deserialize(d->desc, errbuf, sizeof(errbuf));
d->desc = NULL;
if (!xml) {
tvhlog(LOG_ERR, "satip_discovery_desc", "htsmsg_xml_deserialize error %s", errbuf);
goto finish;
}
if ((tags = htsmsg_get_map(xml, "tags")) == NULL)
goto finish;
if ((root = htsmsg_get_map(tags, "root")) == NULL)
goto finish;
if ((device = htsmsg_get_map(root, "tags")) == NULL)
goto finish;
if ((device = htsmsg_get_map(device, "device")) == NULL)
goto finish;
if ((device = htsmsg_get_map(device, "tags")) == NULL)
goto finish;
if ((cs = htsmsg_xml_get_cdata_str(device, "deviceType")) == NULL)
goto finish;
if (strcmp(cs, "urn:ses-com:device:SatIPServer:1"))
goto finish;
if ((friendlyname = htsmsg_xml_get_cdata_str(device, "friendlyName")) == NULL)
goto finish;
if ((manufacturer = htsmsg_xml_get_cdata_str(device, "manufacturer")) == NULL)
goto finish;
if ((manufacturerURL = htsmsg_xml_get_cdata_str(device, "manufacturerURL")) == NULL)
goto finish;
if ((modeldesc = htsmsg_xml_get_cdata_str(device, "modelDescription")) == NULL)
goto finish;
if ((modelname = htsmsg_xml_get_cdata_str(device, "modelName")) == NULL)
goto finish;
if ((modelnum = htsmsg_xml_get_cdata_str(device, "modelNumber")) == NULL)
goto finish;
if ((serialnum = htsmsg_xml_get_cdata_str(device, "serialNumber")) == NULL)
goto finish;
if ((presentation = htsmsg_xml_get_cdata_str(device, "presentationURL")) == NULL)
goto finish;
if ((tunercfg = htsmsg_xml_get_cdata_str(device, "urn:ses-com:satipX_SATIPCAP")) == NULL)
goto finish;
info.myaddr = strdup(d->myaddr);
info.addr = strdup(d->url.host);
info.uuid = strdup(d->uuid);
info.bootid = strdup(d->bootid);
info.configid = strdup(d->configid);
info.deviceid = strdup(d->deviceid);
info.location = strdup(d->location);
info.server = strdup(d->server);
info.friendlyname = strdup(friendlyname);
info.manufacturer = strdup(manufacturer);
info.manufacturerURL = strdup(manufacturerURL);
info.modeldesc = strdup(modeldesc);
info.modelname = strdup(modelname);
info.modelnum = strdup(modelnum);
info.serialnum = strdup(serialnum);
info.presentation = strdup(presentation);
info.tunercfg = strdup(tunercfg);
htsmsg_destroy(xml);
xml = NULL;
pthread_mutex_lock(&global_lock);
if (!satip_device_find(info.uuid))
satip_device_create(&info);
pthread_mutex_unlock(&global_lock);
free(info.myaddr);
free(info.location);
free(info.server);
free(info.addr);
free(info.uuid);
free(info.bootid);
free(info.configid);
free(info.deviceid);
free(info.friendlyname);
free(info.manufacturer);
free(info.manufacturerURL);
free(info.modeldesc);
free(info.modelname);
free(info.modelnum);
free(info.serialnum);
free(info.presentation);
free(info.tunercfg);
finish:
htsmsg_destroy(xml);
return -EIO;
}
static void
satip_discovery_http_fail(void *p)
{
pthread_mutex_lock(&global_lock);
satip_discovery_destroy((satip_discovery_t *)p, 1);
pthread_mutex_unlock(&global_lock);
}
static void
satip_discovery_timerq_cb(void *aux)
{
satip_discovery_t *d, *next;
lock_assert(&global_lock);
next = TAILQ_FIRST(&satip_discoveries);
while (next) {
d = next;
next = TAILQ_NEXT(d, disc_link);
if (d->http_client) {
if (dispatch_clock - d->http_start > 4)
satip_discovery_destroy(d, 1);;
continue;
}
d->http_client = http_connect(&d->url, NULL,
satip_discovery_http_data,
satip_discovery_http_fail,
d);
if (d->http_client == NULL)
satip_discovery_destroy(d, 1);
d->http_start = dispatch_clock;
}
if (TAILQ_FIRST(&satip_discoveries))
gtimer_arm(&satip_discovery_timerq, satip_discovery_timerq_cb, NULL, 5);
}
static void
satip_discovery_service_received
(uint8_t *data, size_t len, udp_connection_t *conn,
struct sockaddr_storage *storage)
{
char *buf, *ptr, *saveptr;
char *argv[10];
char *st = NULL;
char *location = NULL;
char *server = NULL;
char *uuid = NULL;
char *bootid = NULL;
char *configid = NULL;
char *deviceid = NULL;
char sockbuf[128];
satip_discovery_t *d;
int n, i;
if (len > 8191 || satip_discoveries_count > 100)
return;
buf = alloca(len+1);
memcpy(buf, data, len);
buf[len] = '\0';
ptr = strtok_r(buf, "\r\n", &saveptr);
/* Request decoder */
if (ptr) {
if (http_tokenize(ptr, argv, 3, -1) != 3)
return;
if (conn->multicast) {
if (strcmp(argv[0], "NOTIFY"))
return;
if (strcmp(argv[1], "*"))
return;
if (strcmp(argv[2], "HTTP/1.1"))
return;
} else {
if (strcmp(argv[0], "HTTP/1.1"))
return;
if (strcmp(argv[1], "200"))
return;
}
ptr = strtok_r(NULL, "\r\n", &saveptr);
}
/* Header decoder */
while (1) {
if (ptr == NULL)
break;
if (http_tokenize(ptr, argv, 2, -1) == 2) {
if (strcmp(argv[0], "ST:") == 0)
st = argv[1];
else if (strcmp(argv[0], "LOCATION:") == 0)
location = argv[1];
else if (strcmp(argv[0], "SERVER:") == 0)
server = argv[1];
else if (strcmp(argv[0], "BOOTID.UPNP.ORG:") == 0)
bootid = argv[1];
else if (strcmp(argv[0], "CONFIGID.UPNP.ORG:") == 0)
configid = argv[1];
else if (strcmp(argv[0], "DEVICEID.SES.COM:") == 0)
deviceid = argv[1];
else if (strcmp(argv[0], "USN:") == 0) {
n = http_tokenize(argv[1], argv, 10, ':');
for (i = 0; i < n+1; i++)
if (argv[i] && strcmp(argv[i], "uuid") == 0) {
uuid = argv[++i];
break;
}
}
}
ptr = strtok_r(NULL, "\r\n", &saveptr);
}
/* Sanity checks */
if (st == NULL || strcmp(st, "urn:ses-com:device:SatIPServer:1"))
return;
if (uuid == NULL && strlen(uuid) < 16)
return;
if (location == NULL && strncmp(location, "http://", 7))
return;
if (bootid == NULL || configid == NULL || server == NULL)
return;
/* Forward information to next layer */
d = calloc(1, sizeof(satip_discovery_t));
if (inet_ntop(storage->ss_family, IP_IN_ADDR(conn->ip),
sockbuf, sizeof(sockbuf)) == NULL) {
satip_discovery_destroy(d, 0);
return;
}
d->myaddr = strdup(sockbuf);
d->location = strdup(location);
d->server = strdup(server);
d->uuid = strdup(uuid);
d->bootid = strdup(bootid);
d->configid = strdup(configid);
d->deviceid = strdup(deviceid ? deviceid : "");
if (urlparse(d->location, &d->url)) {
satip_discovery_destroy(d, 0);
return;
}
pthread_mutex_lock(&global_lock);
i = 1;
if (!satip_discovery_find(d) && !satip_device_find(d->uuid)) {
TAILQ_INSERT_TAIL(&satip_discoveries, d, disc_link);
satip_discoveries_count++;
gtimer_arm_ms(&satip_discovery_timerq, satip_discovery_timerq_cb, NULL, 250);
i = 0;
}
pthread_mutex_unlock(&global_lock);
if (i) /* duplicate */
satip_discovery_destroy(d, 0);
}
static void
satip_discovery_service_destroy(upnp_service_t *us)
{
satip_discovery_service = NULL;
}
static void
satip_discovery_timer_cb(void *aux)
{
#define MSG "\
M-SEARCH * HTTP/1.1\r\n\
HOST: 239.255.255.250:1900\r\n\
MAN: \"ssdp:discover\"\r\n\
MX: 2\r\n\
ST: urn:ses-com:device:SatIPServer:1\r\n\
\r\n"
htsbuf_queue_t q;
if (!tvheadend_running)
return;
if (!upnp_running) {
gtimer_arm(&satip_discovery_timer, satip_discovery_timer_cb, NULL, 1);
return;
}
if (satip_discovery_service == NULL) {
satip_discovery_service = upnp_service_create(upnp_service);
satip_discovery_service->us_received = satip_discovery_service_received;
satip_discovery_service->us_destroy = satip_discovery_service_destroy;
}
htsbuf_queue_init(&q, 0);
htsbuf_append(&q, MSG, sizeof(MSG)-1);
upnp_send(&q, NULL);
htsbuf_queue_flush(&q);
gtimer_arm(&satip_discovery_timer, satip_discovery_timer_cb, NULL, 3600);
#undef MSG
}
/*
* Initialization
*/
void satip_init ( void )
{
TAILQ_INIT(&satip_discoveries);
gtimer_arm(&satip_discovery_timer, satip_discovery_timer_cb, NULL, 1);
}
void satip_done ( void )
{
tvh_hardware_t *th, *n;
satip_discovery_t *d, *nd;
pthread_mutex_lock(&global_lock);
for (th = LIST_FIRST(&tvh_hardware); th != NULL; th = n) {
n = LIST_NEXT(th, th_link);
if (idnode_is_instance(&th->th_id, &satip_device_class)) {
satip_device_destroy((satip_device_t *)th);
}
}
for (d = TAILQ_FIRST(&satip_discoveries); d != NULL; d = nd) {
nd = TAILQ_NEXT(d, disc_link);
satip_discovery_destroy(d, 1);
}
pthread_mutex_unlock(&global_lock);
}

View file

@ -0,0 +1,26 @@
/*
* Tvheadend - SAT-IP DVB private data
*
* Copyright (C) 2014 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/>.
*/
#ifndef __TVH_SATIP_H__
#define __TVH_SATIP_H__
void satip_init( void );
void satip_done( void );
#endif /* __TVH_SATIP_H__ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,279 @@
/*
* Tvheadend - SAT-IP DVB private data
*
* Copyright (C) 2014 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/>.
*/
#ifndef __TVH_SATIP_PRIVATE_H__
#define __TVH_SATIP_PRIVATE_H__
#include "input.h"
#include "htsbuf.h"
#include "udp.h"
#include "satip.h"
#define SATIP_BUF_SIZE (4000*188)
typedef struct satip_device_info satip_device_info_t;
typedef struct satip_device satip_device_t;
typedef struct satip_frontend satip_frontend_t;
typedef struct satip_satconf satip_satconf_t;
struct satip_device_info
{
char *myaddr; /* IP address of this host received data from the SAT>IP device */
char *addr; /* IP address */
char *uuid;
char *bootid;
char *configid;
char *deviceid;
char *location; /*< URL of the XML file */
char *server;
char *friendlyname;
char *manufacturer;
char *manufacturerURL;
char *modeldesc;
char *modelname;
char *modelnum;
char *serialnum;
char *presentation;
char *tunercfg; /*< XML urn:ses-com:satipX_SATIPCAP contents */
};
struct satip_device
{
tvh_hardware_t;
/*
* Adapter info
*/
satip_device_info_t sd_info;
/*
* Frontends
*/
TAILQ_HEAD(,satip_frontend) sd_frontends;
/*
* RTSP
*/
int sd_fullmux_ok;
int sd_pids_max;
int sd_pids_len;
int sd_pids_deladd;
int sd_sig_scale;
int sd_rtsp_running;
pthread_t sd_rtsp_tid;
pthread_mutex_t sd_rtsp_lock;
pthread_cond_t sd_rtsp_cond;
TAILQ_HEAD(,satip_rtsp_request) sd_rtsp_queue;
time_t sd_rtsp_ping;
gtimer_t sd_rtsp_shutdown;
};
struct satip_frontend
{
mpegts_input_t;
/*
* Device
*/
satip_device_t *sf_device;
TAILQ_ENTRY(satip_frontend) sf_link;
/*
* Frontend info
*/
int sf_number;
dvb_fe_type_t sf_type;
int sf_type_t2;
int sf_udp_rtp_port;
int sf_fullmux;
/*
* Reception
*/
pthread_t sf_dvr_thread;
th_pipe_t sf_dvr_pipe;
pthread_mutex_t sf_dvr_lock;
pthread_cond_t sf_dvr_cond;
uint16_t *sf_pids;
uint16_t *sf_pids_tuned;
int sf_pids_any;
int sf_pids_any_tuned;
int sf_pids_size;
int sf_pids_count;
int sf_pids_tcount; /*< tuned count */
int sf_running;
int sf_position;
udp_connection_t *sf_rtp;
udp_connection_t *sf_rtcp;
int sf_rtp_port;
mpegts_mux_instance_t *sf_mmi;
signal_state_t sf_status;
gtimer_t sf_monitor_timer;
/*
* Configuration
*/
int sf_positions;
TAILQ_HEAD(,satip_satconf) sf_satconf;
};
struct satip_satconf
{
idnode_t sfc_id;
/*
* Parent
*/
satip_frontend_t *sfc_lfe;
TAILQ_ENTRY(satip_satconf) sfc_link;
/*
* Config
*/
int sfc_enabled;
int sfc_position;
int sfc_priority;
char *sfc_name;
/*
* Assigned networks to this SAT configuration
*/
idnode_set_t *sfc_networks;
};
/*
* Methods
*/
void satip_device_init ( void );
void satip_device_done ( void );
void satip_device_save ( satip_device_t *sd );
void satip_device_destroy ( satip_device_t *sd );
satip_frontend_t *
satip_frontend_create
( htsmsg_t *conf, satip_device_t *sd, dvb_fe_type_t type, int t2, int num );
void satip_frontend_save ( satip_frontend_t *lfe, htsmsg_t *m );
void satip_frontend_delete ( satip_frontend_t *lfe );
/*
* SAT>IP Satconf configuration
*/
void satip_satconf_save ( satip_frontend_t *lfe, htsmsg_t *m );
void satip_satconf_destroy ( satip_frontend_t *lfe );
void satip_satconf_create
( satip_frontend_t *lfe, htsmsg_t *conf );
void satip_satconf_updated_positions
( satip_frontend_t *lfe );
int satip_satconf_get_priority
( satip_frontend_t *lfe, mpegts_mux_t *mm );
int satip_satconf_get_position
( satip_frontend_t *lfe, mpegts_mux_t *mm );
/*
* RTSP part
*/
typedef enum {
SATIP_RTSP_CMD_NONE,
SATIP_RTSP_CMD_OPTIONS,
SATIP_RTSP_CMD_SETUP,
SATIP_RTSP_CMD_PLAY,
SATIP_RTSP_CMD_TEARDOWN,
SATIP_RTSP_CMD_DESCRIBE
} satip_rtsp_cmd_t;
typedef struct satip_rtsp_connection {
/* decoded answer */
int cseq;
int code;
char *header;
char *data;
/* state variables */
int sending;
satip_rtsp_cmd_t cmd;
int port;
int client_port;
int timeout;
char *session;
uint64_t stream_id;
/* internal data */
satip_device_t *device;
int fd;
char rbuf[4096];
size_t rsize;
char *wbuf;
size_t wpos;
size_t wsize;
htsbuf_queue_t wq2;
satip_rtsp_cmd_t wq2_cmd;
int wq2_loaded;
time_t ping_time;
} satip_rtsp_connection_t;
satip_rtsp_connection_t *
satip_rtsp_connection( satip_device_t *sd );
void
satip_rtsp_connection_close( satip_rtsp_connection_t *conn );
int
satip_rtsp_send_partial( satip_rtsp_connection_t *conn );
int
satip_rtsp_send( satip_rtsp_connection_t *conn, htsbuf_queue_t *q,
satip_rtsp_cmd_t cmd );
int
satip_rtsp_receive( satip_rtsp_connection_t *conn );
int
satip_rtsp_options_decode( satip_rtsp_connection_t *conn );
void
satip_rtsp_options( satip_rtsp_connection_t *conn );
int
satip_rtsp_setup_decode( satip_rtsp_connection_t *conn );
int
satip_rtsp_setup( satip_rtsp_connection_t *conn,
int src, int fe, int udp_port,
const dvb_mux_conf_t *dmc,
int connection_close );
int
satip_rtsp_play( satip_rtsp_connection_t *sd, const char *pids,
const char *addpids, const char *delpids );
int
satip_rtsp_teardown( satip_rtsp_connection_t *conn );
#endif /* __TVH_SATIP_PRIVATE_H__ */

View file

@ -0,0 +1,580 @@
/*
* Tvheadend - SAT>IP DVB RTSP client
*
* Copyright (C) 2014 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 <pthread.h>
#include <signal.h>
#include "tvheadend.h"
#include "htsbuf.h"
#include "tcp.h"
#include "http.h"
#include "satip_private.h"
/*
*
*/
satip_rtsp_connection_t *
satip_rtsp_connection( satip_device_t *sd )
{
satip_rtsp_connection_t *conn;
char errbuf[256];
conn = calloc(1, sizeof(satip_rtsp_connection_t));
htsbuf_queue_init(&conn->wq2, 0);
conn->port = 554;
conn->timeout = 60;
conn->fd = tcp_connect(sd->sd_info.addr, conn->port,
errbuf, sizeof(errbuf), 2);
if (conn->fd < 0) {
tvhlog(LOG_ERR, "satip", "RTSP - unable to connect - %s", errbuf);
free(conn);
return NULL;
}
conn->device = sd;
conn->ping_time = dispatch_clock;
return conn;
}
void
satip_rtsp_connection_close( satip_rtsp_connection_t *conn )
{
htsbuf_queue_flush(&conn->wq2);
free(conn->session);
free(conn->header);
free(conn->data);
free(conn->wbuf);
if (conn->fd > 0)
close(conn->fd);
free(conn);
}
int
satip_rtsp_send_partial( satip_rtsp_connection_t *conn )
{
ssize_t r;
conn->sending = 1;
while (1) {
r = send(conn->fd, conn->wbuf + conn->wpos, conn->wsize - conn->wpos, MSG_DONTWAIT);
if (r < 0) {
if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)
continue;
return -errno;
}
conn->wpos += r;
if (conn->wpos >= conn->wsize) {
conn->sending = 0;
return 1;
}
break;
}
return 0;
}
int
satip_rtsp_send( satip_rtsp_connection_t *conn, htsbuf_queue_t *q,
satip_rtsp_cmd_t cmd )
{
int r;
conn->ping_time = dispatch_clock;
conn->cmd = cmd;
free(conn->wbuf);
htsbuf_qprintf(q, "CSeq: %i\r\n\r\n", ++conn->cseq);
conn->wbuf = htsbuf_to_string(q);
conn->wsize = strlen(conn->wbuf);
conn->wpos = 0;
#if ENABLE_TRACE
tvhtrace("satip", "%s - sending RTSP cmd", conn->device->sd_info.addr);
tvhlog_hexdump("satip", conn->wbuf, conn->wsize);
#endif
while (!(r = satip_rtsp_send_partial(conn))) ;
return r;
}
static int
satip_rtsp_send2( satip_rtsp_connection_t *conn, htsbuf_queue_t *q,
satip_rtsp_cmd_t cmd )
{
conn->wq2_loaded = 1;
conn->wq2_cmd = cmd;
htsbuf_appendq(&conn->wq2, q);
return 1;
}
int
satip_rtsp_receive( satip_rtsp_connection_t *conn )
{
char buf[1024], *saveptr, *argv[3], *d, *p, *p1;
htsbuf_queue_t header, data;
int cseq_seen;
ssize_t r;
r = recv(conn->fd, buf, sizeof(buf), MSG_DONTWAIT);
if (r == 0)
return -ESTRPIPE;
if (r < 0) {
if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)
return 0;
return -errno;
}
#if ENABLE_TRACE
if (r > 0) {
tvhtrace("satip", "%s - received RTSP answer", conn->device->sd_info.addr);
tvhlog_hexdump("satip", buf, r);
}
#endif
if (r + conn->rsize >= sizeof(conn->rbuf))
return -EINVAL;
memcpy(conn->rbuf + conn->rsize, buf, r);
conn->rsize += r;
conn->rbuf[conn->rsize] = '\0';
if (conn->rsize > 3 &&
(d = strstr(conn->rbuf, "\r\n\r\n")) != NULL) {
*d = '\0';
htsbuf_queue_init(&header, 0);
htsbuf_queue_init(&data, 0);
p = strtok_r(conn->rbuf, "\r\n", &saveptr);
if (p == NULL)
goto fail;
tvhtrace("satip", "%s - RTSP answer '%s'", conn->device->sd_info.addr, p);
if (http_tokenize(p, argv, 3, -1) != 3)
goto fail;
if (strcmp(argv[0], "RTSP/1.0"))
goto fail;
if ((conn->code = atoi(argv[1])) <= 0)
goto fail;
cseq_seen = 0;
while ((p = strtok_r(NULL, "\r\n", &saveptr)) != NULL) {
p1 = strdup(p);
if (http_tokenize(p, argv, 2, ':') != 2)
goto fail;
if (strcmp(argv[0], "CSeq") == 0) {
cseq_seen = conn->cseq == atoi(argv[1]);
} else {
htsbuf_append(&header, p1, strlen(p1));
htsbuf_append(&header, "\n", 1);
}
free(p1);
}
if (!cseq_seen)
goto fail;
free(conn->header);
free(conn->data);
conn->header = htsbuf_to_string(&header);
conn->data = htsbuf_to_string(&data);
#if ENABLE_TRACE
tvhtrace("satip", "%s - received RTSP header", conn->device->sd_info.addr);
tvhlog_hexdump("satip", conn->header, strlen(conn->header));
if (strlen(conn->data)) {
tvhtrace("satip", "%s - received RTSP data", conn->device->sd_info.addr);
tvhlog_hexdump("satip", conn->data, strlen(conn->data));
}
#endif
htsbuf_queue_flush(&header);
htsbuf_queue_flush(&data);
conn->rsize = 0;
/* second write */
if (conn->wq2_loaded && conn->code == 200) {
r = satip_rtsp_send(conn, &conn->wq2, conn->wq2_cmd);
htsbuf_queue_flush(&conn->wq2);
conn->wq2_loaded = 0;
return r;
}
return 1;
fail:
htsbuf_queue_flush(&header);
htsbuf_queue_flush(&data);
conn->rsize = 0;
return -EINVAL;
}
/* unfinished */
return 0;
}
/*
*
*/
int
satip_rtsp_options_decode( satip_rtsp_connection_t *conn )
{
char *argv[32], *s, *saveptr;
int i, n, what = 0;
s = strtok_r(conn->header, "\n", &saveptr);
while (s) {
n = http_tokenize(s, argv, 32, ',');
if (strcmp(argv[0], "Public:") == 0)
for (i = 1; i < n; i++) {
if (strcmp(argv[i], "DESCRIBE") == 0)
what |= 1;
else if (strcmp(argv[i], "SETUP") == 0)
what |= 2;
else if (strcmp(argv[i], "PLAY") == 0)
what |= 4;
else if (strcmp(argv[i], "TEARDOWN") == 0)
what |= 8;
}
s = strtok_r(NULL, "\n", &saveptr);
}
return (conn->code != 200 && what != 0x0f) ? -1 : 1;
}
void
satip_rtsp_options( satip_rtsp_connection_t *conn )
{
htsbuf_queue_t q;
htsbuf_queue_init(&q, 0);
htsbuf_qprintf(&q,
"OPTIONS rtsp://%s/ RTSP/1.0\r\n",
conn->device->sd_info.addr);
satip_rtsp_send(conn, &q, SATIP_RTSP_CMD_OPTIONS);
htsbuf_queue_flush(&q);
}
int
satip_rtsp_setup_decode( satip_rtsp_connection_t *conn )
{
char *argv[32], *s, *saveptr;
int i, n;
if (conn->code >= 400)
return -1;
if (conn->code != 200)
return 0;
conn->client_port = 0;
s = strtok_r(conn->header, "\n", &saveptr);
while (s) {
n = http_tokenize(s, argv, 32, ';');
if (strcmp(argv[0], "Session:") == 0) {
conn->session = strdup(argv[1]);
for (i = 2; i < n; i++) {
if (strncmp(argv[i], "timeout=", 8) == 0) {
conn->timeout = atoi(argv[i] + 8);
if (conn->timeout <= 20 || conn->timeout > 3600)
return -1;
}
}
} else if (strcmp(argv[0], "com.ses.streamID:") == 0) {
conn->stream_id = atoll(argv[1]);
/* zero is valid stream id per specification */
if (argv[1][0] == '0' && argv[1][0] == '\0')
conn->stream_id = 0;
else if (conn->stream_id <= 0)
return -1;
} else if (strcmp(argv[0], "Transport:") == 0) {
if (strcmp(argv[1], "RTP/AVP"))
return -1;
if (strcmp(argv[2], "unicast"))
return -1;
for (i = 2; i < n; i++) {
if (strncmp(argv[i], "client_port=", 12) == 0)
conn->client_port = atoi(argv[i] + 12);
}
}
s = strtok_r(NULL, "\n", &saveptr);
}
return 1;
}
typedef struct tvh2satip {
int t; ///< TVH internal value
const char *s; ///< SATIP API value
} tvh2satip_t;
#define TABLE_EOD -1
static const char *
satip_rtsp_setup_find(const char *prefix, tvh2satip_t *tbl,
int src, const char *defval)
{
while (tbl->t >= 0) {
if (tbl->t == src)
return tbl->s;
tbl++;
}
tvhtrace("satip", "%s - cannot translate %d", prefix, src);
return defval;
}
#define ADD(s, d, def) \
strcat(buf, "&" #d "="), strcat(buf, satip_rtsp_setup_find(#d, d, dmc->s, def))
static void
satip_rtsp_add_val(const char *name, char *buf, uint32_t val)
{
char sec[4];
sprintf(buf + strlen(buf), "&%s=%i", name, val / 1000);
if (val % 1000) {
sprintf(sec, ".%03i", val % 1000);
if (sec[3] == '0') {
sec[3] = '\0';
if (sec[2] == '0')
sec[2] = '\0';
}
}
}
int
satip_rtsp_setup( satip_rtsp_connection_t *conn, int src, int fe,
int udp_port, const dvb_mux_conf_t *dmc,
int connection_close )
{
static tvh2satip_t msys[] = {
{ .t = DVB_SYS_DVBT, "dvbt" },
{ .t = DVB_SYS_DVBT2, "dvbt2" },
{ .t = DVB_SYS_DVBS, "dvbs" },
{ .t = DVB_SYS_DVBS2, "dvbs2" },
{ .t = TABLE_EOD }
};
static tvh2satip_t pol[] = {
{ .t = DVB_POLARISATION_HORIZONTAL, "h" },
{ .t = DVB_POLARISATION_VERTICAL, "v" },
{ .t = DVB_POLARISATION_CIRCULAR_LEFT, "l" },
{ .t = DVB_POLARISATION_CIRCULAR_RIGHT, "r" },
{ .t = TABLE_EOD }
};
static tvh2satip_t ro[] = {
{ .t = DVB_ROLLOFF_AUTO, "0.35" },
{ .t = DVB_ROLLOFF_20, "0.20" },
{ .t = DVB_ROLLOFF_25, "0.25" },
{ .t = DVB_ROLLOFF_35, "0.35" },
{ .t = TABLE_EOD }
};
static tvh2satip_t mtype[] = {
{ .t = DVB_MOD_AUTO, "auto" },
{ .t = DVB_MOD_QAM_16, "16qam" },
{ .t = DVB_MOD_QAM_32, "32qam" },
{ .t = DVB_MOD_QAM_64, "64qam" },
{ .t = DVB_MOD_QAM_128, "128qam"},
{ .t = DVB_MOD_QAM_256, "256qam"},
{ .t = DVB_MOD_QPSK, "qpsk" },
{ .t = DVB_MOD_PSK_8, "8psk" },
{ .t = TABLE_EOD }
};
static tvh2satip_t plts[] = {
{ .t = DVB_PILOT_AUTO, "auto" },
{ .t = DVB_PILOT_ON, "on" },
{ .t = DVB_PILOT_OFF, "off" },
{ .t = TABLE_EOD }
};
static tvh2satip_t fec[] = {
{ .t = DVB_FEC_AUTO, "auto" },
{ .t = DVB_FEC_1_2, "12" },
{ .t = DVB_FEC_2_3, "23" },
{ .t = DVB_FEC_3_4, "34" },
{ .t = DVB_FEC_3_5, "35" },
{ .t = DVB_FEC_4_5, "45" },
{ .t = DVB_FEC_5_6, "56" },
{ .t = DVB_FEC_7_8, "78" },
{ .t = DVB_FEC_8_9, "89" },
{ .t = DVB_FEC_9_10, "910" },
{ .t = TABLE_EOD }
};
static tvh2satip_t tmode[] = {
{ .t = DVB_TRANSMISSION_MODE_AUTO, "auto" },
{ .t = DVB_TRANSMISSION_MODE_1K, "1k" },
{ .t = DVB_TRANSMISSION_MODE_2K, "2k" },
{ .t = DVB_TRANSMISSION_MODE_4K, "4k" },
{ .t = DVB_TRANSMISSION_MODE_8K, "8k" },
{ .t = DVB_TRANSMISSION_MODE_16K, "16k" },
{ .t = DVB_TRANSMISSION_MODE_32K, "32k" },
{ .t = TABLE_EOD }
};
static tvh2satip_t gi[] = {
{ .t = DVB_GUARD_INTERVAL_AUTO, "auto" },
{ .t = DVB_GUARD_INTERVAL_1_4, "14" },
{ .t = DVB_GUARD_INTERVAL_1_8, "18" },
{ .t = DVB_GUARD_INTERVAL_1_16, "116" },
{ .t = DVB_GUARD_INTERVAL_1_32, "132" },
{ .t = DVB_GUARD_INTERVAL_1_128, "1128" },
{ .t = DVB_GUARD_INTERVAL_19_128, "19128" },
{ .t = DVB_GUARD_INTERVAL_19_256, "19256" },
{ .t = TABLE_EOD }
};
char buf[512];
htsbuf_queue_t q;
int r;
htsbuf_queue_init(&q, 0);
if (src > 0)
sprintf(buf, "src=%i&", src);
else
buf[0] = '\0';
sprintf(buf + strlen(buf), "fe=%i", fe);
satip_rtsp_add_val("freq", buf, dmc->dmc_fe_freq);
if (dmc->dmc_fe_delsys == DVB_SYS_DVBS ||
dmc->dmc_fe_delsys == DVB_SYS_DVBS2) {
satip_rtsp_add_val("sr", buf, dmc->u.dmc_fe_qpsk.symbol_rate);
ADD(dmc_fe_delsys, msys, "dvbs");
ADD(dmc_fe_modulation, mtype, "qpsk");
ADD(u.dmc_fe_qpsk.polarisation, pol, "h" );
ADD(u.dmc_fe_qpsk.fec_inner, fec, "auto");
ADD(dmc_fe_rolloff, ro, "0.35");
if (dmc->dmc_fe_pilot != DVB_PILOT_AUTO)
ADD(dmc_fe_pilot, plts, "auto");
} else {
if (dmc->u.dmc_fe_ofdm.bandwidth != DVB_BANDWIDTH_AUTO &&
dmc->u.dmc_fe_ofdm.bandwidth != DVB_BANDWIDTH_NONE)
satip_rtsp_add_val("bw", buf, dmc->u.dmc_fe_ofdm.bandwidth);
ADD(dmc_fe_delsys, msys, "dvbt");
if (dmc->dmc_fe_modulation != DVB_MOD_AUTO &&
dmc->dmc_fe_modulation != DVB_MOD_NONE &&
dmc->dmc_fe_modulation != DVB_MOD_QAM_AUTO)
ADD(dmc_fe_modulation, mtype, "64qam");
if (dmc->u.dmc_fe_ofdm.transmission_mode != DVB_TRANSMISSION_MODE_AUTO &&
dmc->u.dmc_fe_ofdm.transmission_mode != DVB_TRANSMISSION_MODE_NONE)
ADD(u.dmc_fe_ofdm.transmission_mode, tmode, "8k");
if (dmc->u.dmc_fe_ofdm.guard_interval != DVB_GUARD_INTERVAL_AUTO &&
dmc->u.dmc_fe_ofdm.guard_interval != DVB_GUARD_INTERVAL_NONE)
ADD(u.dmc_fe_ofdm.guard_interval, gi, "18");
}
tvhtrace("satip", "setup params - %s", buf);
if (conn->stream_id > 0)
htsbuf_qprintf(&q, "SETUP rtsp://%s/stream=%li?",
conn->device->sd_info.addr, conn->stream_id);
else
htsbuf_qprintf(&q, "SETUP rtsp://%s/?", conn->device->sd_info.addr);
htsbuf_qprintf(&q,
"%s RTSP/1.0\r\nTransport: RTP/AVP;unicast;client_port=%i-%i\r\n",
buf, udp_port, udp_port+1);
if (conn->session)
htsbuf_qprintf(&q, "Session: %s\r\n", conn->session);
if (connection_close)
htsbuf_qprintf(&q, "Connection: close\r\n");
r = satip_rtsp_send(conn, &q, SATIP_RTSP_CMD_SETUP);
htsbuf_queue_flush(&q);
return r;
}
static const char *
satip_rtsp_pids_strip( satip_rtsp_connection_t *conn, const char *s )
{
int maxlen = conn->device->sd_pids_len;
char *ptr;
if (s == NULL)
return NULL;
while (*s == ',')
s++;
while (strlen(s) > maxlen) {
ptr = rindex(s, ',');
if (ptr == NULL)
abort();
*ptr = '\0';
}
if (*s == '\0')
return NULL;
return s;
}
int
satip_rtsp_play( satip_rtsp_connection_t *conn, const char *pids,
const char *addpids, const char *delpids )
{
htsbuf_queue_t q;
int r, split = 0;
pids = satip_rtsp_pids_strip(conn, pids);
addpids = satip_rtsp_pids_strip(conn, addpids);
delpids = satip_rtsp_pids_strip(conn, delpids);
if (pids == NULL && addpids == NULL && delpids == NULL)
return 1;
// printf("pids = '%s' addpids = '%s' delpids = '%s'\n", pids, addpids, delpids);
htsbuf_queue_init(&q, 0);
htsbuf_qprintf(&q, "PLAY rtsp://%s/stream=%li?",
conn->device->sd_info.addr, conn->stream_id);
/* pids setup and add/del requests cannot be mixed per specification */
if (pids) {
htsbuf_qprintf(&q, "pids=%s", pids);
} else {
if (delpids)
htsbuf_qprintf(&q, "delpids=%s", delpids);
if (addpids) {
if (delpids) {
/* try to maintain the maximum request size - simple split */
if (strlen(addpids) + strlen(delpids) <= conn->device->sd_pids_len)
split = 1;
else
htsbuf_append(&q, "&", 1);
}
if (!split)
htsbuf_qprintf(&q, "addpids=%s", addpids);
}
}
htsbuf_qprintf(&q, " RTSP/1.0\r\nSession: %s\r\n", conn->session);
r = satip_rtsp_send(conn, &q, SATIP_RTSP_CMD_PLAY);
htsbuf_queue_flush(&q);
if (r || !split)
return r;
htsbuf_queue_init(&q, 0);
htsbuf_qprintf(&q, "PLAY rtsp://%s/stream=%li?",
conn->device->sd_info.addr, conn->stream_id);
htsbuf_qprintf(&q, "addpids=%s", addpids);
htsbuf_qprintf(&q, " RTSP/1.0\r\nSession: %s\r\n", conn->session);
r = satip_rtsp_send2(conn, &q, SATIP_RTSP_CMD_PLAY);
htsbuf_queue_flush(&q);
return r;
}
int
satip_rtsp_teardown( satip_rtsp_connection_t *conn )
{
int r;
htsbuf_queue_t q;
htsbuf_queue_init(&q, 0);
htsbuf_qprintf(&q,
"TEARDOWN rtsp://%s/stream=%li RTSP/1.0\r\nSession: %s\r\n",
conn->device->sd_info.addr, conn->stream_id, conn->session);
r = satip_rtsp_send(conn, &q, SATIP_RTSP_CMD_TEARDOWN);
htsbuf_queue_flush(&q);
return r;
}
#if 0
static int
satip_rtsp_describe_decode
( satip_connection_t *conn )
{
if (header == NULL)
return 1;
printf("describe: %i\n", conn->code);
printf("header:\n%s\n", conn->header);
printf("data:\n%s\n", conn->data);
return 0;
}
static void
satip_rtsp_describe( satip_connection_t *conn )
{
htsbuf_queue_t q;
htsbuf_queue_init(&q, 0);
htsbuf_qprintf(&q,
"DESCRIBE rtsp://%s/ RTSP/1.0\r\n", sd->sd_info.addr);
satip_rtsp_write(conn, &q);
htsbuf_queue_flush(&q);
}
#endif

View file

@ -0,0 +1,322 @@
/*
* Tvheadend - SAT>IP DVB satconf
*
* Copyright (C) 2014 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 "satip_private.h"
#include "settings.h"
/* **************************************************************************
* Frontend callbacks
* *************************************************************************/
static satip_satconf_t *
satip_satconf_find_ele( satip_frontend_t *lfe, mpegts_mux_t *mux )
{
satip_satconf_t *sfc;
TAILQ_FOREACH(sfc, &lfe->sf_satconf, sfc_link) {
if (idnode_set_exists(sfc->sfc_networks, &mux->mm_network->mn_id))
return sfc;
}
return NULL;
}
int
satip_satconf_get_priority
( satip_frontend_t *lfe, mpegts_mux_t *mm )
{
satip_satconf_t *sfc = satip_satconf_find_ele(lfe, mm);
return sfc->sfc_priority;
}
int
satip_satconf_get_position
( satip_frontend_t *lfe, mpegts_mux_t *mm )
{
satip_satconf_t *sfc = satip_satconf_find_ele(lfe, mm);
return sfc->sfc_position;
}
/* **************************************************************************
* Class definition
* *************************************************************************/
static const void *
satip_satconf_class_network_get( void *o )
{
satip_satconf_t *sfc = o;
return idnode_set_as_htsmsg(sfc->sfc_networks);
}
static int
satip_satconf_class_network_set( void *o, const void *p )
{
satip_satconf_t *sfc = o;
const htsmsg_t *msg = p;
mpegts_network_t *mn;
idnode_set_t *n = idnode_set_create();
htsmsg_field_t *f;
const char *str;
int i, save;
HTSMSG_FOREACH(f, msg) {
if (!(str = htsmsg_field_get_str(f))) continue;
if (!(mn = mpegts_network_find(str))) continue;
idnode_set_add(n, &mn->mn_id, NULL);
}
save = n->is_count != sfc->sfc_networks->is_count;
if (!save) {
for (i = 0; i < n->is_count; i++)
if (!idnode_set_exists(sfc->sfc_networks, n->is_array[i])) {
save = 1;
break;
}
}
if (save) {
/* update the local (antenna satconf) network list */
idnode_set_free(sfc->sfc_networks);
sfc->sfc_networks = n;
/* update the input (frontend) network list */
htsmsg_t *l = htsmsg_create_list();
satip_frontend_t *lfe = sfc->sfc_lfe;
satip_satconf_t *sfc2;
TAILQ_FOREACH(sfc2, &lfe->sf_satconf, sfc_link) {
for (i = 0; i < sfc2->sfc_networks->is_count; i++)
htsmsg_add_str(l, NULL,
idnode_uuid_as_str(sfc2->sfc_networks->is_array[i]));
}
mpegts_input_class_network_set(lfe, l);
htsmsg_destroy(l);
} else {
idnode_set_free(n);
}
return save;
}
static htsmsg_t *
satip_satconf_class_network_enum( void *o )
{
htsmsg_t *m = htsmsg_create_map();
htsmsg_t *p = htsmsg_create_map();
htsmsg_add_str(m, "type", "api");
htsmsg_add_str(m, "uri", "idnode/load");
htsmsg_add_str(m, "event", "mpegts_network");
htsmsg_add_u32(p, "enum", 1);
htsmsg_add_str(p, "class", dvb_network_dvbs_class.ic_class);
htsmsg_add_msg(m, "params", p);
return m;
}
static char *
satip_satconf_class_network_rend( void *o )
{
satip_satconf_t *sfc = o;
htsmsg_t *l = idnode_set_as_htsmsg(sfc->sfc_networks);
char *str = htsmsg_list_2_csv(l);
htsmsg_destroy(l);
return str;
}
static const char *
satip_satconf_class_get_title ( idnode_t *o )
{
return ((satip_satconf_t *)o)->sfc_name;
}
static void
satip_satconf_class_save ( idnode_t *in )
{
satip_satconf_t *sfc = (satip_satconf_t*)in;
satip_device_save(sfc->sfc_lfe->sf_device);
}
const idclass_t satip_satconf_class =
{
.ic_class = "satip_satconf",
.ic_caption = "Satconf",
.ic_get_title = satip_satconf_class_get_title,
.ic_save = satip_satconf_class_save,
.ic_properties = (const property_t[]) {
{
.type = PT_BOOL,
.id = "enabled",
.name = "Enabled",
.off = offsetof(satip_satconf_t, sfc_enabled),
},
{
.type = PT_STR,
.id = "displayname",
.name = "Name",
.off = offsetof(satip_satconf_t, sfc_name),
.notify = idnode_notify_title_changed,
},
{
.type = PT_INT,
.id = "priority",
.name = "Priority",
.off = offsetof(satip_satconf_t, sfc_priority),
.opts = PO_ADVANCED,
},
{
.type = PT_INT,
.id = "position",
.name = "Position",
.off = offsetof(satip_satconf_t, sfc_position),
.def.i = 1,
.opts = PO_RDONLY | PO_ADVANCED,
},
{
.type = PT_STR,
.id = "networks",
.name = "Networks",
.islist = 1,
.set = satip_satconf_class_network_set,
.get = satip_satconf_class_network_get,
.list = satip_satconf_class_network_enum,
.rend = satip_satconf_class_network_rend,
},
{}
}
};
/* **************************************************************************
* Creation/Config
* *************************************************************************/
static satip_satconf_t *
satip_satconf_create0
( satip_frontend_t *lfe, htsmsg_t *conf, int position )
{
static const char *tbl[] = {" (AA)", " (AB)", " (BA)", " (BB)"};
const char *uuid = NULL;
satip_satconf_t *sfc = calloc(1, sizeof(*sfc));
char buf[32];
const char *s;
/* defaults */
sfc->sfc_priority = 1;
if (conf)
uuid = htsmsg_get_str(conf, "uuid");
if (idnode_insert(&sfc->sfc_id, uuid, &satip_satconf_class)) {
free(sfc);
return NULL;
}
sfc->sfc_networks = idnode_set_create();
sfc->sfc_lfe = lfe;
sfc->sfc_position = position + 1;
TAILQ_INSERT_TAIL(&lfe->sf_satconf, sfc, sfc_link);
if (conf)
idnode_load(&sfc->sfc_id, conf);
if (sfc->sfc_name == NULL || sfc->sfc_name[0] == '\0') {
free(sfc->sfc_name);
s = position < 4 ? tbl[position] : "";
snprintf(buf, sizeof(buf), "Position #%i%s", position + 1, s);
sfc->sfc_name = strdup(buf);
}
return sfc;
}
void
satip_satconf_create
( satip_frontend_t *lfe, htsmsg_t *conf )
{
htsmsg_t *l, *e;
htsmsg_field_t *f;
int pos = 0;
if (conf && (l = htsmsg_get_list(conf, "satconf"))) {
satip_satconf_destroy(lfe);
HTSMSG_FOREACH(f, l) {
if (!(e = htsmsg_field_get_map(f))) continue;
if (satip_satconf_create0(lfe, e, pos++))
lfe->sf_positions++;
}
}
if (lfe->sf_positions == 0)
for ( ; lfe->sf_positions < 4; lfe->sf_positions++)
satip_satconf_create0(lfe, NULL, lfe->sf_positions);
}
static void
satip_satconf_destroy0
( satip_satconf_t *sfc )
{
satip_frontend_t *lfe = sfc->sfc_lfe;
TAILQ_REMOVE(&lfe->sf_satconf, sfc, sfc_link);
idnode_unlink(&sfc->sfc_id);
idnode_set_free(sfc->sfc_networks);
free(sfc->sfc_name);
free(sfc);
}
void
satip_satconf_updated_positions
( satip_frontend_t *lfe )
{
satip_satconf_t *sfc, *sfc_old;
int i;
sfc = TAILQ_FIRST(&lfe->sf_satconf);
for (i = 0; i < lfe->sf_positions; i++) {
if (sfc == NULL)
satip_satconf_create0(lfe, NULL, i);
sfc = sfc ? TAILQ_NEXT(sfc, sfc_link) : NULL;
}
while (sfc) {
sfc_old = sfc;
sfc = TAILQ_NEXT(sfc, sfc_link);
satip_satconf_destroy0(sfc_old);
}
}
void
satip_satconf_destroy ( satip_frontend_t *lfe )
{
satip_satconf_t *sfc;
while ((sfc = TAILQ_FIRST(&lfe->sf_satconf)) != NULL)
satip_satconf_destroy0(sfc);
lfe->sf_positions = 0;
}
void
satip_satconf_save ( satip_frontend_t *lfe, htsmsg_t *m )
{
satip_satconf_t *sfc;
htsmsg_t *l, *e;
l = htsmsg_create_list();
TAILQ_FOREACH(sfc, &lfe->sf_satconf, sfc_link) {
e = htsmsg_create_map();
idnode_save(&sfc->sfc_id, e);
htsmsg_add_msg(l, NULL, e);
}
htsmsg_add_msg(m, "satconf", l);
}
/******************************************************************************
* Editor Configuration
*
* vim:sts=2:ts=2:sw=2:et
*****************************************************************************/

View file

@ -134,6 +134,9 @@ const tvh_caps_t tvheadend_capabilities[] = {
#if ENABLE_LINUXDVB #if ENABLE_LINUXDVB
{ "linuxdvb", NULL }, { "linuxdvb", NULL },
#endif #endif
#if ENABLE_SATIP_CLIENT
{ "satip_client", NULL },
#endif
#if ENABLE_LIBAV #if ENABLE_LIBAV
{ "transcoding", &transcoding_enabled }, { "transcoding", &transcoding_enabled },
#endif #endif