]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
SAT>IP server: many fixes and additions, first "somewhat working" version
authorJaroslav Kysela <perex@perex.cz>
Tue, 3 Mar 2015 15:05:28 +0000 (16:05 +0100)
committerJaroslav Kysela <perex@perex.cz>
Wed, 11 Mar 2015 20:41:12 +0000 (21:41 +0100)
Tested only with another tvheadend as client with the DVB-T adapter.

src/satip/rtp.c
src/satip/rtsp.c

index a4d313bcfbb9010328352d0dc1239aee7efd3b60..6c0d895b8205807e08dd614b49883f28089e59ca 100644 (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",
index ebde1155f86dc6a2401c04eeb28e6ac715024557..6bae9edd1ab15fca99d8d781410a6e252d3314c1 100644 (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);