SAT>IP server: many fixes and additions, first "somewhat working" version

Tested only with another tvheadend as client with the DVB-T adapter.
This commit is contained in:
Jaroslav Kysela 2015-03-03 16:05:28 +01:00
parent ca0a703779
commit 7bb903a397
2 changed files with 338 additions and 145 deletions

View file

@ -24,8 +24,8 @@
#include "satip/server.h"
#define RTP_PACKETS 128
#define RTP_PAYLOAD (1356-20-8)
#define RTCP_PAYLOAD (1472-20-8)
#define RTP_PAYLOAD (7*188+12)
#define RTCP_PAYLOAD (1420)
typedef struct satip_rtp_session {
TAILQ_ENTRY(satip_rtp_session) link;
@ -47,6 +47,7 @@ typedef struct satip_rtp_session {
int um_packet;
uint16_t seq;
signal_status_t sig;
pthread_mutex_t lock;
} satip_rtp_session_t;
static pthread_mutex_t satip_rtp_lock;
@ -72,7 +73,7 @@ satip_rtp_header(satip_rtp_session_t *rtp)
data[5] = (tstamp >> 16) & 0xff;
data[6] = (tstamp >> 8) & 0xff;
data[7] = tstamp & 0xff;
memset(data + 8, 0xa5, 8);
memset(data + 8, 0xa5, 4);
}
static int
@ -83,11 +84,11 @@ satip_rtp_send(satip_rtp_session_t *rtp)
if (v->iov_len == RTP_PAYLOAD) {
packets = rtp->um_packet;
v2 = v + packets;
copy = 1;
if (v2->iov_len == RTP_PAYLOAD) {
packets++;
copy = 0;
} else
copy = 1;
}
r = udp_multisend_send(&rtp->um, rtp->fd_rtp, packets);
if (r < 0)
return r;
@ -107,7 +108,7 @@ satip_rtp_send(satip_rtp_session_t *rtp)
static int
satip_rtp_loop(satip_rtp_session_t *rtp, uint8_t *data, int len)
{
int i, pid, last_pid = -1, r;
int i, j, pid, last_pid = -1, r;
int16_t *pids = rtp->pids;
struct iovec *v = rtp->um_iovec + rtp->um_packet;
@ -115,21 +116,27 @@ satip_rtp_loop(satip_rtp_session_t *rtp, uint8_t *data, int len)
for ( ; len >= 188 ; data += 188, len -= 188) {
pid = ((data[1] & 0x1f) << 8) | data[2];
if (pid != last_pid) {
for (i = 0; i < RTSP_PIDS && pids[i] >= 0; i++)
if (pids[i] == pid)
break;
if (i >= RTSP_PIDS) continue; /* skip PID */
for (i = 0, j = -1; i < RTSP_PIDS && (j = pids[i]) >= 0; i++)
if (j == pid) goto found;
continue;
found:
last_pid = pid;
}
assert(v->iov_len + 188 <= RTP_PAYLOAD);
memcpy(v->iov_base + v->iov_len, data, 188);
v->iov_len += 188;
if (v->iov_len >= RTP_PAYLOAD) {
if (v->iov_len == RTP_PAYLOAD) {
if ((rtp->um_packet + 1) == RTP_PACKETS) {
r = satip_rtp_send(rtp);
if (r < 0)
return r;
} else
} else {
rtp->um_packet++;
satip_rtp_header(rtp);
}
v = rtp->um_iovec + rtp->um_packet;
} else {
assert(v->iov_len < RTP_PAYLOAD);
}
}
return 0;
@ -174,7 +181,9 @@ satip_rtp_thread(void *aux)
case SMT_MPEGTS:
pb = sm->sm_data;
atomic_add(&subs->ths_bytes_out, pktbuf_len(pb));
pthread_mutex_lock(&rtp->lock);
r = satip_rtp_loop(rtp, pktbuf_ptr(pb), pktbuf_len(pb));
pthread_mutex_unlock(&rtp->lock);
if (r) fatal = 1;
break;
case SMT_SIGNAL_STATUS:
@ -215,11 +224,9 @@ satip_rtp_find(void *id)
{
satip_rtp_session_t *rtp;
pthread_mutex_lock(&satip_rtp_lock);
TAILQ_FOREACH(rtp, &satip_rtp_sessions, link)
if (rtp == id)
if (rtp->id == id)
break;
pthread_mutex_unlock(&satip_rtp_lock);
return rtp;
}
@ -241,7 +248,7 @@ void satip_rtp_queue(void *id, th_subscription_t *subs,
rtp->id = id;
rtp->peer = *peer;
rtp->peer2 = *peer;
IP_PORT_SET(rtp->peer2, port + 1);
IP_PORT_SET(rtp->peer2, htons(port + 1));
rtp->port = port;
rtp->fd_rtp = fd_rtp;
rtp->fd_rtcp = fd_rtcp;
@ -253,6 +260,7 @@ void satip_rtp_queue(void *id, th_subscription_t *subs,
rtp->frontend = frontend;
rtp->dmc = *dmc;
rtp->source = source;
pthread_mutex_init(&rtp->lock, NULL);
pthread_mutex_lock(&satip_rtp_lock);
TAILQ_INSERT_TAIL(&satip_rtp_sessions, rtp, link);
@ -260,6 +268,20 @@ void satip_rtp_queue(void *id, th_subscription_t *subs,
pthread_mutex_unlock(&satip_rtp_lock);
}
void satip_rtp_update_pids(void *id, int16_t *pids)
{
satip_rtp_session_t *rtp;
pthread_mutex_lock(&satip_rtp_lock);
rtp = satip_rtp_find(id);
if (rtp) {
pthread_mutex_lock(&rtp->lock);
memcpy(rtp->pids, pids, sizeof(*pids)*RTSP_PIDS);
pthread_mutex_unlock(&rtp->lock);
}
pthread_mutex_unlock(&satip_rtp_lock);
}
void satip_rtp_close(void *id)
{
satip_rtp_session_t *rtp;
@ -268,17 +290,19 @@ void satip_rtp_close(void *id)
pthread_mutex_lock(&satip_rtp_lock);
rtp = satip_rtp_find(id);
if (rtp) {
TAILQ_REMOVE(&satip_rtp_sessions, rtp, link);
sq = rtp->sq;
pthread_mutex_lock(&sq->sq_mutex);
rtp->sq = NULL;
pthread_cond_signal(&sq->sq_cond);
pthread_mutex_unlock(&sq->sq_mutex);
pthread_mutex_unlock(&satip_rtp_lock);
pthread_join(rtp->tid, NULL);
pthread_mutex_lock(&satip_rtp_lock);
udp_multisend_free(&rtp->um);
free(rtp);
} else {
pthread_mutex_unlock(&satip_rtp_lock);
}
pthread_mutex_unlock(&satip_rtp_lock);
}
/*
@ -288,8 +312,12 @@ static const char *
satip_rtcp_fec(int fec)
{
static char buf[16];
const char *s = dvb_fec2str(fec);
char *p = buf;
const char *s;
if (fec == DVB_FEC_AUTO || fec == DVB_FEC_NONE)
return "";
s = dvb_fec2str(fec);
if (s == NULL)
return "";
strncpy(buf, s, sizeof(buf));
@ -313,8 +341,31 @@ satip_rtcp_build(satip_rtp_session_t *rtp, uint8_t *msg)
const char *bw, *tmode, *gi, *plp, *t2id, *sm, *c2tft, *ds, *specinv;
int i, len, len2, level = 0, lock = 0, quality = 0;
if (rtp->sig.signal > 0)
lock = 1;
switch (rtp->sig.signal_scale) {
case SIGNAL_STATUS_SCALE_RELATIVE:
level = MIN(240, MAX(0, (rtp->sig.signal * 245) / 0xffff));
break;
case SIGNAL_STATUS_SCALE_DECIBEL:
level = MIN(240, MAX(0, (rtp->sig.signal * 900000)));
break;
default:
break;
}
switch (rtp->sig.snr_scale) {
case SIGNAL_STATUS_SCALE_RELATIVE:
quality = MIN(15, MAX(0, (rtp->sig.signal * 16) / 0xffff));
break;
case SIGNAL_STATUS_SCALE_DECIBEL:
quality = MIN(15, MAX(0, (rtp->sig.signal * 100000)));
break;
default:
break;
}
pids[0] = 0;
for (i = len = 00; i < RTSP_PIDS && rtp->pids[i] >= 0; i++)
for (i = len = 0; i < RTSP_PIDS && rtp->pids[i] >= 0; i++)
len += snprintf(pids + len, sizeof(pids) - len, "%d,", rtp->pids[i]);
if (len && pids[len-1] == ',')
pids[len-1] = '\0';
@ -345,7 +396,7 @@ satip_rtcp_build(satip_rtp_session_t *rtp, uint8_t *msg)
snprintf(buf, sizeof(buf),
"vers=1.0;src=%d;tuner=%d,%d,%d,%d,%.f,%s,%s,%s,%s,%s,%.f,%s;pids=%s",
rtp->source, rtp->frontend, level, lock, quality,
(float)rtp->dmc.dmc_fe_freq / 1000.0,
(float)rtp->dmc.dmc_fe_freq / 1000000.0,
dvb_pol2str(rtp->dmc.u.dmc_fe_qpsk.polarisation),
delsys, msys, pilot, rolloff,
(float)rtp->dmc.u.dmc_fe_qpsk.symbol_rate / 1000.0,
@ -399,7 +450,7 @@ satip_rtcp_build(satip_rtp_session_t *rtp, uint8_t *msg)
snprintf(buf, sizeof(buf),
"vers=1.1;tuner=%d,%d,%d,%d,%.f,%s,%s,%s,%s,%s,%s,%s,%s,%s;pids=%s",
rtp->frontend, level, lock, quality,
(float)rtp->dmc.dmc_fe_freq / 1000.0,
(float)rtp->dmc.dmc_fe_freq / 1000000.0,
bw, delsys, tmode, msys, gi,
satip_rtcp_fec(rtp->dmc.u.dmc_fe_ofdm.code_rate_HP),
plp, t2id, sm, pids);
@ -425,7 +476,7 @@ satip_rtcp_build(satip_rtp_session_t *rtp, uint8_t *msg)
snprintf(buf, sizeof(buf),
"vers=1.1;tuner=%d,%d,%d,%d,%.f,%s,%s,%s,%.f,%s,%s,%s,%s;pids=%s",
rtp->frontend, level, lock, quality,
(float)rtp->dmc.dmc_fe_freq / 1000.0,
(float)rtp->dmc.dmc_fe_freq / 1000000.0,
bw, delsys, msys,
(float)rtp->dmc.u.dmc_fe_qam.symbol_rate / 1000.0,
c2tft, ds, plp, specinv, pids);
@ -434,7 +485,9 @@ satip_rtcp_build(satip_rtp_session_t *rtp, uint8_t *msg)
return 0;
}
len = len2 = strlen(buf);
len = len2 = MIN(strlen(buf), RTCP_PAYLOAD - 16);
if (len == 0)
len++;
while ((len % 4) != 0)
buf[len++] = 0;
memcpy(msg + 16, buf, len);
@ -442,8 +495,8 @@ satip_rtcp_build(satip_rtp_session_t *rtp, uint8_t *msg)
len += 16;
msg[0] = 0x80;
msg[1] = 204;
msg[2] = (len >> 8) & 0xff;
msg[3] = len & 0xff;
msg[2] = (((len - 1) / 4) >> 8) & 0xff;
msg[3] = ((len - 1) / 4) & 0xff;
msg[4] = 0;
msg[5] = 0;
msg[6] = 0;
@ -457,7 +510,7 @@ satip_rtcp_build(satip_rtp_session_t *rtp, uint8_t *msg)
msg[14] = (len2 >> 8) & 0xff;
msg[15] = len2 & 0xff;
return len2;
return len;
}
/*
@ -475,11 +528,11 @@ satip_rtcp_thread(void *aux)
while (satip_rtcp_run) {
ts.tv_sec = 0;
ts.tv_nsec = 150000000;
while (1) {
nanosleep(&ts, &ts);
if (satip_rtcp_run)
do {
r = nanosleep(&ts, &ts);
if (!satip_rtcp_run)
goto end;
} while (ts.tv_nsec);
} while (r && ts.tv_nsec);
pthread_mutex_lock(&satip_rtp_lock);
TAILQ_FOREACH(rtp, &satip_rtp_sessions, link) {
if (rtp->sq == NULL) continue;
@ -489,7 +542,7 @@ satip_rtcp_thread(void *aux)
(struct sockaddr*)&rtp->peer2,
rtp->peer2.ss_family == AF_INET6 ?
sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in));
if (r) {
if (r < 0) {
err = errno;
tcp_get_ip_str((struct sockaddr*)&rtp->peer2, addrbuf, sizeof(addrbuf));
tvhwarn("satips", "RTCP send to error %s:%d : %s",

View file

@ -36,6 +36,7 @@ typedef struct session {
int frontend;
int findex;
int src;
int run;
uint32_t nsession;
char session[9];
dvb_mux_conf_t dmc;
@ -45,6 +46,7 @@ typedef struct session {
int mux_created;
profile_chain_t prch;
th_subscription_t *subs;
int rtp_peer_port;
udp_connection_t *udp_rtp;
udp_connection_t *udp_rtcp;
} session_t;
@ -104,6 +106,8 @@ static struct session *
rtsp_new_session(int delsys, uint32_t nsession, int session)
{
struct session *rs = calloc(1, sizeof(*rs));
int i;
if (rs == NULL)
return NULL;
rs->delsys = delsys;
@ -114,6 +118,8 @@ rtsp_new_session(int delsys, uint32_t nsession, int session)
if (session_number == 0)
session_number += 9876;
}
for (i = 0; i < RTSP_PIDS; i++)
rs->pids[i] = -1;
TAILQ_INSERT_TAIL(&rtsp_sessions, rs, link);
return rs;
}
@ -145,8 +151,7 @@ static void
rtsp_session_timer_cb(void *aux)
{
session_t *rs = aux;
rtsp_close_session(rs);
rtsp_free_session(rs);
tvhwarn("satips", "session %s closed (timeout)", rs->session);
}
@ -154,7 +159,9 @@ rtsp_session_timer_cb(void *aux)
static inline void
rtsp_rearm_session_timer(session_t *rs)
{
pthread_mutex_lock(&global_lock);
gtimer_arm(&rs->timer, rtsp_session_timer_cb, rs, RTSP_TIMEOUT);
pthread_mutex_unlock(&global_lock);
}
/*
@ -163,7 +170,7 @@ rtsp_rearm_session_timer(session_t *rs)
static char *
rtsp_check_urlbase(char *u)
{
char *p;
char *p, *s;
/* expect string: rtsp://<myip>[:<myport>]/ */
if (u[0] == '\0' || strncmp(u, "rtsp://", 7))
@ -173,9 +180,9 @@ rtsp_check_urlbase(char *u)
if (p == NULL)
return NULL;
*p = '\0';
if ((p = strchr(u, ':')) != NULL) {
*p = '\0';
if (atoi(p + 1) != rtsp_port)
if ((s = strchr(u, ':')) != NULL) {
*s = '\0';
if (atoi(s + 1) != rtsp_port)
return NULL;
} else {
if (rtsp_port != 554)
@ -183,7 +190,7 @@ rtsp_check_urlbase(char *u)
}
if (strcmp(u, rtsp_ip))
return NULL;
return p;
return p + 1;
}
/*
@ -197,7 +204,9 @@ rtsp_parse_args(http_connection_t *hc, char *u)
if (strncmp(u, "stream=", 7) == 0) {
u += 7;
for (s = 0; isdigit(*s); s++);
for (s = u; isdigit(*s); s++);
if (*s == '\0')
return atoi(u);
if (*s != '?')
return -1;
*s = '\0';
@ -206,11 +215,24 @@ rtsp_parse_args(http_connection_t *hc, char *u)
} else {
if (*u != '?')
return -1;
u++;
}
http_parse_get_args(hc, u);
return stream;
}
/*
*
*/
static void
rtsp_clrpids(session_t *rs)
{
int16_t *pids = rs->pids;
int i = RTSP_PIDS;
while (*pids >= 0 && i-- > 0)
*pids++ = -1;
}
/*
*
*/
@ -228,8 +250,12 @@ rtsp_addpids(session_t *rs, int16_t *pids)
rs->pids[j] = rs->pids[j-1];
rs->pids[i] = pid;
break;
} else if (rs->pids[i] == pid)
} else if (rs->pids[i] == pid) {
break;
} else if (rs->pids[i] < 0) {
rs->pids[i] = pid;
break;
}
}
}
return 0;
@ -270,71 +296,98 @@ rtsp_clean(session_t *rs)
}
if (rs->prch.prch_pro)
profile_chain_close(&rs->prch);
if (rs->mux && rs->mux_created) {
if (rs->mux && rs->mux_created)
rs->mux->mm_delete((mpegts_mux_t *)rs->mux, 1);
rs->mux = NULL;
rs->mux_created = 0;
}
rs->mux = NULL;
rs->mux_created = 0;
}
/*
*
*/
static int
rtsp_start(http_connection_t *hc, session_t *rs, char *addrbuf)
rtsp_start
(http_connection_t *hc, session_t *rs, char *addrbuf, int newmux, int setup)
{
mpegts_network_t *mn;
mpegts_network_t *mn, *mn2;
dvb_network_t *ln;
char buf[256];
int res = HTTP_STATUS_SERVICE, qsize = 3000000;
dvb_mux_t *mux;
char buf[384];
int res = HTTP_STATUS_SERVICE, qsize = 3000000, created = 0;
if (rs->mux)
return 0;
rs->mux_created = 0;
pthread_mutex_lock(&global_lock);
LIST_FOREACH(mn, &mpegts_network_all, mn_global_link) {
ln = (dvb_network_t *)mn;
if (ln->ln_type == rs->dmc.dmc_fe_type &&
mn->mn_satip_source == rs->src)
break;
}
if (mn) {
rs->mux = dvb_network_find_mux((dvb_network_t *)mn, &rs->dmc,
if (newmux) {
mux = NULL;
mn2 = NULL;
LIST_FOREACH(mn, &mpegts_network_all, mn_global_link) {
ln = (dvb_network_t *)mn;
if (ln->ln_type == rs->dmc.dmc_fe_type &&
mn->mn_satip_source == rs->src) {
if (!mn2) mn2 = mn;
mux = dvb_network_find_mux((dvb_network_t *)mn, &rs->dmc,
MPEGTS_ONID_NONE, MPEGTS_TSID_NONE);
if (rs->mux == NULL) {
rs->mux = (dvb_mux_t *)
mn->mn_create_mux(mn, (void *)(intptr_t)rs->nsession,
if (mux) break;
}
}
if (mux == NULL && mn2) {
mux = (dvb_mux_t *)
mn->mn_create_mux(mn2, (void *)(intptr_t)rs->nsession,
MPEGTS_ONID_NONE, MPEGTS_TSID_NONE,
&rs->dmc, 0);
if (rs->mux)
rs->mux_created = 1;
if (mux)
created = 1;
}
}
if (rs->mux == NULL) {
dvb_mux_conf_str(&rs->dmc, buf, sizeof(buf));
tvhwarn("satips", "%i: unable to create mux %s", rs->frontend, buf);
goto end;
}
if (profile_chain_raw_open(&rs->prch, (mpegts_mux_t *)rs->mux, qsize))
goto endclean;
rs->subs = subscription_create_from_mux(&rs->prch, NULL,
if (mux == NULL) {
dvb_mux_conf_str(&rs->dmc, buf, sizeof(buf));
tvhwarn("satips", "%i: unable to create mux %s", rs->frontend, buf);
goto endclean;
}
if (rs->mux == mux)
goto pids;
if (rs->run)
satip_rtp_close((void *)(intptr_t)rs->stream);
rtsp_clean(rs);
rs->mux = mux;
rs->mux_created = created;
if (profile_chain_raw_open(&rs->prch, (mpegts_mux_t *)rs->mux, qsize))
goto endclean;
rs->subs = subscription_create_from_mux(&rs->prch, NULL,
config_get_int("satip_weight", 100),
"SAT>IP",
SUBSCRIPTION_FULLMUX | SUBSCRIPTION_STREAMING,
rs->prch.prch_flags |
SUBSCRIPTION_FULLMUX |
SUBSCRIPTION_STREAMING,
addrbuf, hc->hc_username,
http_arg_get(&hc->hc_args, "User-Agent"), NULL);
if (!rs->subs)
goto endclean;
satip_rtp_queue((void *)(intptr_t)rs->nsession,
rs->subs, &rs->prch.prch_sq,
hc->hc_peer, ntohs(IP_PORT(rs->udp_rtp->ip)),
rs->udp_rtp->fd, rs->udp_rtcp->fd,
rs->frontend, rs->findex, &rs->mux->lm_tuning, rs->pids);
if (!rs->subs)
goto endrtp;
if (rs->run) {
/* restart streaming */
setup = 0;
rs->run = 0;
}
} else {
pids:
satip_rtp_update_pids((void *)(intptr_t)rs->stream, rs->pids);
}
if (!setup && !rs->run) {
if (rs->mux == NULL)
goto endrtp;
satip_rtp_queue((void *)(intptr_t)rs->stream,
rs->subs, &rs->prch.prch_sq,
hc->hc_peer, rs->rtp_peer_port,
rs->udp_rtp->fd, rs->udp_rtcp->fd,
rs->frontend, rs->findex, &rs->mux->lm_tuning, rs->pids);
rs->run = 1;
}
pthread_mutex_unlock(&global_lock);
return 0;
endrtp:
satip_rtp_close((void *)(intptr_t)rs->stream);
rs->run = 0;
endclean:
rtsp_clean(rs);
end:
pthread_mutex_unlock(&global_lock);
return res;
}
@ -417,6 +470,7 @@ static int
fec_to_tvh(http_connection_t *hc)
{
switch (atoi(http_arg_get_remove(&hc->hc_req_args, "fec"))) {
case 0: return DVB_FEC_AUTO;
case 12: return DVB_FEC_1_2;
case 13: return DVB_FEC_1_3;
case 15: return DVB_FEC_1_5;
@ -548,7 +602,7 @@ parse_pids(char *p, int16_t *pids)
char *x, *saveptr;
int i = 0;
if (p == '\0') {
if (p == NULL || *p == '\0') {
pids[0] = -1;
return 0;
}
@ -559,9 +613,12 @@ parse_pids(char *p, int16_t *pids)
if (i >= RTSP_PIDS)
return -1;
pids[i] = atoi(x);
if (pids[i] < 0 || pids[i] > 8191)
if (pids[i] < 0 || pids[i] > 8191) {
pids[i] = -1;
return -1;
}
x = strtok_r(NULL, ",", &saveptr);
i++;
}
if (i == 0)
return -1;
@ -569,6 +626,27 @@ parse_pids(char *p, int16_t *pids)
return 0;
}
static int
parse_transport(http_connection_t *hc)
{
const char *s = http_arg_get(&hc->hc_args, "Transport");
const char *u;
int a, b;
if (!s || strncmp(s, "RTP/AVP;unicast;client_port=", 28))
return -1;
for (s += 28, u = s; isdigit(*u); u++);
if (*u != '-')
return -1;
a = atoi(s);
for (s = ++u; isdigit(*s); s++);
if (*s != '\0' && *s != ';')
return -1;
b = atoi(u);
if (a + 1 != b)
return -1;
return a;
}
/*
*
*/
@ -580,12 +658,12 @@ rtsp_process_play(http_connection_t *hc, int setup)
int stream, delsys = DVB_SYS_NONE, msys, fe, src, freq, pol, sr;
int fec, ro, plts, bw, tmode, mtype, gi, plp, t2id, sm, c2tft, ds, specinv;
char *u, *s;
char *pids, *addpids, *delpids;
int16_t _pids[RTSP_PIDS+1], _addpids[RTSP_PIDS+1], _delpids[RTSP_PIDS+1];
int16_t pids[RTSP_PIDS+1], addpids[RTSP_PIDS+1], delpids[RTSP_PIDS+1];
dvb_mux_conf_t *dmc;
char buf[256], addrbuf[50];
http_arg_list_t args;
http_arg_init(&args);
tcp_get_ip_str((struct sockaddr*)hc->hc_peer, addrbuf, sizeof(addrbuf));
u = tvh_strdupa(hc->hc_url);
@ -594,15 +672,16 @@ rtsp_process_play(http_connection_t *hc, int setup)
goto error2;
fe = atoi(http_arg_get_remove(&hc->hc_req_args, "fe"));
addpids = http_arg_get_remove(&hc->hc_req_args, "addpids");
if (parse_pids(addpids, _addpids)) goto error2;
delpids = http_arg_get_remove(&hc->hc_req_args, "delpids");
if (parse_pids(delpids, _delpids)) goto error2;
s = http_arg_get_remove(&hc->hc_req_args, "addpids");
if (parse_pids(s, addpids)) goto error2;
s = http_arg_get_remove(&hc->hc_req_args, "delpids");
if (parse_pids(s, delpids)) goto error2;
s = http_arg_get_remove(&hc->hc_req_args, "pids");
if (parse_pids(s, pids)) goto error2;
msys = msys_to_tvh(hc);
if (msys < 0)
goto error2;
freq = atof(http_arg_get_remove(&hc->hc_req_args, "freq")) * 1000000;
if (addpids || delpids) {
if (addpids[0] >= 0 || delpids[0] >= 0) {
if (setup)
goto error2;
if (!stream)
@ -620,34 +699,53 @@ rtsp_process_play(http_connection_t *hc, int setup)
}
if (setup) {
if (msys == DVB_SYS_NONE)
goto error;
if (delsys == DVB_SYS_NONE) goto error;
if (msys == DVB_SYS_NONE) goto error;
if (!fe) goto error;
if (freq < 1000000) goto error;
if (!rs)
rs = rtsp_new_session(msys, 0, -1);
else if (stream != rs->stream)
rs = rtsp_new_session(msys, rs->nsession, stream);
else
rtsp_close_session(rs);
r = parse_transport(hc);
if (r < 0) {
errcode = HTTP_STATUS_BAD_TRANSFER;
goto error;
}
if (rs->run && rs->rtp_peer_port != r) {
errcode = HTTP_STATUS_METHOD_INVALID;
goto error;
}
rs->frontend = fe;
rs->rtp_peer_port = r;
dmc = &rs->dmc;
} else {
if (!rs || stream != rs->stream) {
if (rs)
errcode = HTTP_STATUS_NOT_FOUND;
goto error;
}
dmc = &rs->dmc;
if (rs->mux == NULL) goto error;
if (!fe) {
fe = rs->frontend;
findex = rs->findex;
}
if (rs->frontend != fe)
goto error;
if (freq >= 1000000) {
if (delsys == DVB_SYS_NONE) goto error;
if (msys == DVB_SYS_NONE) goto error;
} else {
if (!TAILQ_EMPTY(&hc->hc_req_args)) goto error;
goto play;
}
}
if (!setup && rs->frontend == fe && TAILQ_EMPTY(&hc->hc_req_args))
goto play;
dmc = &rs->dmc;
dvb_mux_conf_init(dmc, msys);
rs->frontend = fe;
rs->findex = findex;
pids = http_arg_get_remove(&hc->hc_req_args, "pids");
if (parse_pids(pids, _pids)) goto error;
freq = atof(http_arg_get_remove(&hc->hc_req_args, "freq")) * 1000;
if (freq < 1000) goto error;
mtype = mtype_to_tvh(hc);
if (mtype == DVB_MOD_NONE) goto error;
@ -688,10 +786,14 @@ rtsp_process_play(http_connection_t *hc, int setup)
if (gi == DVB_GUARD_INTERVAL_NONE) goto error;
fec = fec_to_tvh(hc);
if (fec == DVB_FEC_NONE) goto error;
plp = atoi(http_arg_get_remove(&hc->hc_req_args, "plp"));
if (plp < 0 || plp > 255) goto error;
s = http_arg_get_remove(&hc->hc_req_args, "t2id");
t2id = s[0] ? atoi(s) : DVB_NO_STREAM_ID_FILTER;
s = http_arg_get_remove(&hc->hc_req_args, "plp");
if (s[0]) {
plp = atoi(s);
if (plp < 0 || plp > 255) goto error;
} else {
plp = DVB_NO_STREAM_ID_FILTER;
}
t2id = atoi(http_arg_get_remove(&hc->hc_req_args, "t2id"));
if (t2id < 0 || t2id > 65535) goto error;
sm = atoi(http_arg_get_remove(&hc->hc_req_args, "sm"));
if (sm < 0 || sm > 1) goto error;
@ -739,66 +841,89 @@ rtsp_process_play(http_connection_t *hc, int setup)
}
dvb_mux_conf_str(dmc, buf, sizeof(buf));
tvhdebug("satips", "%i/%s/%d: setup %s", rs->frontend, rs->session, rs->stream, buf);
dmc->dmc_fe_freq = freq;
dmc->dmc_fe_modulation = mtype;
rs->delsys = delsys;
rs->frontend = fe;
rs->findex = findex;
stream_id++;
if (stream_id == 0)
if (setup) {
stream_id++;
rs->stream = stream_id % 0x7fff;
if (stream_id == 0)
stream_id++;
rs->stream = stream_id % 0x7fff;
}
rs->src = src;
memset(&rs->udp_rtp, 0, sizeof(rs->udp_rtp));
memset(&rs->udp_rtcp, 0, sizeof(rs->udp_rtcp));
if (udp_bind_double(&rs->udp_rtp, &rs->udp_rtcp,
"satips", "rtsp", "rtcp",
addrbuf, 0, NULL,
rtsp_ip, 0, NULL,
4*1024, 4*1024,
RTP_BUFSIZE, RTCP_BUFSIZE)) {
errcode = HTTP_STATUS_INTERNAL;
goto error;
}
if (setup) {
if (pids)
rtsp_addpids(rs, _pids);
goto end;
if (udp_connect(rs->udp_rtp, "RTP", addrbuf, rs->rtp_peer_port) ||
udp_connect(rs->udp_rtcp, "RTCP", addrbuf, rs->rtp_peer_port + 1)) {
errcode = HTTP_STATUS_INTERNAL;
goto error;
}
play:
if (delpids)
rtsp_delpids(rs, _delpids);
if (addpids)
rtsp_addpids(rs, _addpids);
if ((r = rtsp_start(hc, rs, addrbuf)) < 0) {
if (pids[0] >= 0) {
rtsp_clrpids(rs);
rtsp_addpids(rs, pids);
}
if (delpids[0] >= 0)
rtsp_delpids(rs, delpids);
if (addpids[0] >= 0)
rtsp_addpids(rs, addpids);
if ((r = rtsp_start(hc, rs, addrbuf, freq >= 10000000, setup)) < 0) {
errcode = r;
goto error;
}
tvhdebug("satips", "%i/%s/%d: play", rs->frontend, rs->session, rs->stream);
end:
if (setup)
tvhdebug("satips", "setup from %s:%d, RTP: %d, RTCP: %d",
addrbuf, IP_PORT(*hc->hc_peer),
rs->rtp_peer_port, rs->rtp_peer_port + 1);
dvb_mux_conf_str(dmc, buf, sizeof(buf));
s = buf + strlen(buf);
for (r = 0; r < RTSP_PIDS; r++) {
if (rs->pids[r] < 0) break;
s += snprintf(s, sizeof(buf) - (s - buf), "%s%i",
r > 0 ? "," : " pids ", rs->pids[r]);
}
tvhdebug("satips", "%i/%s/%d: %s %s",
rs->frontend, rs->session, rs->stream,
setup ? "setup" : "play", buf);
http_arg_init(&args);
snprintf(buf, sizeof(buf), "%s;timeout=%d", rs->session, RTSP_TIMEOUT);
http_arg_set(&args, "Session", buf);
r = IP_PORT(rs->udp_rtp->ip);
snprintf(buf, sizeof(buf), "RTP/AVP;unicast;client_port=%d-%d", r, r+1);
http_arg_set(&args, "Transport", buf);
snprintf(buf, sizeof(buf), "%d", rs->stream);
http_arg_set(&args, "com.ses.streamID", buf);
if (setup) {
snprintf(buf, sizeof(buf), "%s;timeout=%d", rs->session, RTSP_TIMEOUT);
http_arg_set(&args, "Session", buf);
r = rs->rtp_peer_port;
snprintf(buf, sizeof(buf), "RTP/AVP;unicast;client_port=%d-%d", r, r+1);
http_arg_set(&args, "Transport", buf);
snprintf(buf, sizeof(buf), "%d", rs->stream);
http_arg_set(&args, "com.ses.streamID", buf);
} else {
snprintf(buf, sizeof(buf), "url=rtsp://%s/stream=%d", rtsp_ip, rs->stream);
http_arg_set(&args, "RTP-Info", buf);
}
http_send_header(hc, HTTP_STATUS_OK, NULL, 0, NULL, NULL, 0, NULL, NULL, &args);
http_arg_flush(&args);
pthread_mutex_unlock(&rtsp_lock);
http_arg_flush(&args);
return 0;
error:
pthread_mutex_unlock(&rtsp_lock);
error2:
http_error(hc, errcode);
http_arg_flush(&args);
return 0;
}
@ -811,14 +936,20 @@ rtsp_process_teardown(http_connection_t *hc)
char *u = tvh_strdupa(hc->hc_url);
struct session *rs = NULL;
http_arg_list_t args;
char addrbuf[50];
int stream;
tcp_get_ip_str((struct sockaddr*)hc->hc_peer, addrbuf, sizeof(addrbuf));
if ((u = rtsp_check_urlbase(u)) == NULL ||
(stream = rtsp_parse_args(hc, u)) < 0) {
http_error(hc, HTTP_STATUS_BAD_REQUEST);
return 0;
}
tvhdebug("satips", "teardown from %s:%d for stream %d",
addrbuf, IP_PORT(*hc->hc_peer), stream);
pthread_mutex_lock(&rtsp_lock);
rs = rtsp_find_session(hc, stream);
if (!rs || stream != rs->stream) {
@ -891,7 +1022,9 @@ rtsp_serve(int fd, void **opaque, struct sockaddr_storage *peer,
static void
rtsp_close_session(session_t *rs)
{
satip_rtp_close((void *)(intptr_t)rs->nsession);
satip_rtp_close((void *)(intptr_t)rs->stream);
rs->stream = 0;
rs->run =0;
udp_close(rs->udp_rtp);
udp_close(rs->udp_rtcp);
pthread_mutex_lock(&global_lock);
@ -906,8 +1039,8 @@ rtsp_close_session(session_t *rs)
static void
rtsp_free_session(session_t *rs)
{
gtimer_disarm(&rs->timer);
TAILQ_REMOVE(&rtsp_sessions, rs, link);
gtimer_disarm(&rs->timer);
free(rs);
}
@ -918,10 +1051,15 @@ static void
rtsp_close_sessions(void)
{
session_t *rs;
while ((rs = TAILQ_FIRST(&rtsp_sessions)) != NULL) {
rtsp_close_session(rs);
rtsp_free_session(rs);
}
do {
pthread_mutex_lock(&rtsp_lock);
rs = TAILQ_FIRST(&rtsp_sessions);
if (rs) {
rtsp_close_session(rs);
rtsp_free_session(rs);
}
pthread_mutex_unlock(&rtsp_lock);
} while (rs != NULL);
}
/*
@ -966,9 +1104,11 @@ void satip_server_rtsp_register(void)
void satip_server_rtsp_done(void)
{
pthread_mutex_lock(&global_lock);
rtsp_close_sessions();
if (rtsp_server)
tcp_server_delete(rtsp_server);
pthread_mutex_unlock(&global_lock);
rtsp_close_sessions();
pthread_mutex_lock(&global_lock);
rtsp_server = NULL;
rtsp_port = -1;
free(rtsp_ip);