From 7bb903a397ef2f3a783081def4f3c76dce7528ff Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Tue, 3 Mar 2015 16:05:28 +0100 Subject: [PATCH] SAT>IP server: many fixes and additions, first "somewhat working" version Tested only with another tvheadend as client with the DVB-T adapter. --- src/satip/rtp.c | 117 ++++++++++----- src/satip/rtsp.c | 366 ++++++++++++++++++++++++++++++++--------------- 2 files changed, 338 insertions(+), 145 deletions(-) diff --git a/src/satip/rtp.c b/src/satip/rtp.c index a4d313bc..6c0d895b 100644 --- a/src/satip/rtp.c +++ b/src/satip/rtp.c @@ -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", diff --git a/src/satip/rtsp.c b/src/satip/rtsp.c index ebde1155..6bae9edd 100644 --- a/src/satip/rtsp.c +++ b/src/satip/rtsp.c @@ -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://[:]/ */ 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);