]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
Move RTSP common code from SAT>IP to the common place
authorJaroslav Kysela <perex@perex.cz>
Tue, 15 Apr 2014 15:30:47 +0000 (17:30 +0200)
committerJaroslav Kysela <perex@perex.cz>
Mon, 5 May 2014 20:00:35 +0000 (22:00 +0200)
Makefile
src/http.h
src/httpc.c
src/input/mpegts/satip/satip_frontend.c
src/input/mpegts/satip/satip_private.h
src/input/mpegts/satip/satip_rtsp.c
src/rtsp.c [new file with mode: 0644]

index eaa63ad5f3ca2e383be9a3289993b3af5c9bb941..6ec18b03719b5f3a1b627727c98a7ff90cf58ef1 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -114,6 +114,7 @@ SRCS =  src/version.c \
        src/service_mapper.c \
        src/input.c \
        src/httpc.c \
+       src/rtsp.c \
        src/fsmonitor.c \
        src/cron.c \
 
index 6abe0ea63b51283f5faea0771032214ed14c8998..08ab0c3d8dce9553de464447ee9a0e60bfafcbd7 100644 (file)
@@ -84,7 +84,8 @@ typedef enum http_state {
   HTTP_CON_SENT,
   HTTP_CON_RECEIVING,
   HTTP_CON_DONE,
-  HTTP_CON_IDLE
+  HTTP_CON_IDLE,
+  HTTP_CON_OK
 } http_state_t;
 
 typedef enum http_cmd {
@@ -265,6 +266,13 @@ struct http_client {
 
   int          hc_cseq;         /* RTSP */
   int          hc_rcseq;        /* RTSP - expected cseq */
+  char        *hc_rtsp_session;
+  char        *hc_rtp_dest;
+  int          hc_rtp_port;
+  int          hc_rtpc_port;
+  int          hc_rtp_multicast:1;
+  long         hc_rtsp_stream_id;
+  int          hc_rtp_timeout;
 
   struct http_client_ssl *hc_ssl; /* ssl internals */
 
@@ -284,15 +292,45 @@ http_client_connect ( void *aux, http_ver_t ver,
 void http_client_register ( http_client_t *hc );
 void http_client_close ( http_client_t *hc );
 
-int
-http_client_send( http_client_t *hc, http_cmd_t cmd,
-                  const char *path, const char *query,
-                  http_arg_list_t *header, void *body, size_t body_size );
-int
-http_client_simple( http_client_t *hc, const url_t *url);
-int
-http_client_clear_state( http_client_t *hc );
-int
-http_client_run( http_client_t *hc );
+int http_client_send( http_client_t *hc, http_cmd_t cmd,
+                      const char *path, const char *query,
+                      http_arg_list_t *header, void *body, size_t body_size );
+int http_client_simple( http_client_t *hc, const url_t *url);
+int http_client_clear_state( http_client_t *hc );
+int http_client_run( http_client_t *hc );
+
+/*
+ * RTSP helpers
+ */
+
+int rtsp_send( http_client_t *hc, http_cmd_t cmd, const char *path,
+               const char *query, http_arg_list_t *hdr );
+                      
+void rtsp_clear_session( http_client_t *hc );
+
+int rtsp_options_decode( http_client_t *hc );
+static inline int rtsp_options( http_client_t *hc ) {
+  return rtsp_send(hc, RTSP_CMD_OPTIONS, NULL, NULL, NULL);
+}
+
+int rtsp_setup_decode( http_client_t *hc, int satip );
+int rtsp_setup( http_client_t *hc, const char *path, const char *query,
+                const char *multicast_addr, int rtp_port, int rtpc_port );
+
+static inline int
+rtsp_play( http_client_t *hc, const char *path, const char *query ) {
+  return rtsp_send(hc, RTSP_CMD_PLAY, path, query, NULL);
+}
+
+static inline int
+rtsp_teardown( http_client_t *hc, const char *path, const char *query ) {
+  return rtsp_send(hc, RTSP_CMD_TEARDOWN, path, query, NULL);
+}
+
+int rtsp_describe_decode( http_client_t *hc );
+static inline int
+rtsp_describe( http_client_t *hc, const char *path, const char *query ) {
+  return rtsp_send(hc, RTSP_CMD_DESCRIBE, path, query, NULL);
+}
 
 #endif /* HTTP_H_ */
index 5e3a523c2bdfadf05d77df0823f9a11f809c5ca7..c9c845b65ad5c75b70b161561b2f652d716a267e 100644 (file)
@@ -581,7 +581,8 @@ http_client_finish( http_client_t *hc )
       return http_client_flush(hc, res);
   }
   hc->hc_hsize = hc->hc_csize = 0;
-  if (hc->hc_handle_location &&
+  if (hc->hc_version != RTSP_VERSION_1_0 &&
+      hc->hc_handle_location &&
       (hc->hc_code == HTTP_STATUS_MOVED ||
        hc->hc_code == HTTP_STATUS_FOUND ||
        hc->hc_code == HTTP_STATUS_SEE_OTHER ||
@@ -927,7 +928,12 @@ header:
     goto next_header;
   }
   hc->hc_rpos = 0;
-  hc->hc_in_data = 1;
+  if (hc->hc_version == RTSP_VERSION_1_0 && !hc->hc_csize) {
+    hc->hc_csize = -1;
+    hc->hc_in_data = 0;
+  } else {
+    hc->hc_in_data = 1;
+  }
   res = http_client_data_received(hc, hc->hc_rbuf + hc->hc_hsize, len);
   if (res < 0)
     return http_client_flush(hc, res);
@@ -1168,6 +1174,7 @@ http_client_connect
   hc             = calloc(1, sizeof(http_client_t));
   hc->hc_aux     = aux;
   hc->hc_io_size = 1024;
+  hc->hc_rtsp_stream_id = -1;
 
   TAILQ_INIT(&hc->hc_args);
   TAILQ_INIT(&hc->hc_wqueue);
@@ -1216,6 +1223,7 @@ http_client_close ( http_client_t *hc )
   while ((wcmd = TAILQ_FIRST(&hc->hc_wqueue)) != NULL)
     http_client_cmd_destroy(hc, wcmd);
   http_client_ssl_free(hc);
+  rtsp_clear_session(hc);
   free(hc->hc_location);
   free(hc->hc_rbuf);
   free(hc->hc_data);
index 7d24de8662f1395a5e477562d19e36719f8c87af..859e556c558855f2119a04be56cea8b679cedab5 100644 (file)
@@ -525,12 +525,13 @@ satip_frontend_store_pids(char *buf, uint16_t *pids, int count)
 }
 
 static void
-satip_frontend_pid_changed( satip_rtsp_connection_t *rtsp,
+satip_frontend_pid_changed( http_client_t *rtsp,
                             satip_frontend_t *lfe, const char *name )
 {
   char *add, *del;
   int i, j, r, count, any = lfe->sf_pids_any;
   int deleted;
+  int max_pids_len = lfe->sf_device->sd_pids_len;
 
   if (!lfe->sf_running)
     return;
@@ -552,7 +553,7 @@ satip_frontend_pid_changed( satip_rtsp_connection_t *rtsp,
     lfe->sf_pids_tcount = lfe->sf_pids_count;
     pthread_mutex_unlock(&lfe->sf_dvr_lock);
 
-    r = satip_rtsp_play(rtsp,  "all", NULL, NULL);
+    r = satip_rtsp_play(rtsp,  "all", NULL, NULL, max_pids_len);
 
   } else if (!lfe->sf_device->sd_pids_deladd ||
              lfe->sf_pids_any_tuned ||
@@ -572,7 +573,7 @@ satip_frontend_pid_changed( satip_rtsp_connection_t *rtsp,
     lfe->sf_pids_tcount = lfe->sf_pids_count;
     pthread_mutex_unlock(&lfe->sf_dvr_lock);
 
-    r = satip_rtsp_play(rtsp, add, NULL, NULL);
+    r = satip_rtsp_play(rtsp, add, NULL, NULL, max_pids_len);
 
   } else {
 
@@ -638,7 +639,7 @@ satip_frontend_pid_changed( satip_rtsp_connection_t *rtsp,
     pthread_mutex_unlock(&lfe->sf_dvr_lock);
 
     if (add[0] != '\0' || del[0] != '\0')
-      r = satip_rtsp_play(rtsp, NULL, add, del);
+      r = satip_rtsp_play(rtsp, NULL, add, del, max_pids_len);
     else
       r = 0;
   }
@@ -651,9 +652,10 @@ static void *
 satip_frontend_input_thread ( void *aux )
 {
 #define PKTS 64
+#define HTTP_CMD_NONE 9874
   satip_frontend_t *lfe = aux;
   mpegts_mux_instance_t *mmi = lfe->sf_mmi;
-  satip_rtsp_connection_t *rtsp;
+  http_client_t *rtsp;
   dvb_mux_t *lm;
   char buf[256];
   uint8_t tsb[PKTS][1356 + 128];
@@ -666,7 +668,6 @@ satip_frontend_input_thread ( void *aux )
   size_t c;
   int tc;
   tvhpoll_event_t ev[4];
-  tvhpoll_event_t evr;
   tvhpoll_t *efd;
   int changing = 0, ms = -1, fatal = 0;
   uint32_t seq = -1, nseq;
@@ -678,7 +679,8 @@ satip_frontend_input_thread ( void *aux )
 
   lm = (dvb_mux_t *)mmi->mmi_mux;
 
-  rtsp = satip_rtsp_connection(lfe->sf_device);
+  rtsp = http_client_connect(lfe, RTSP_VERSION_1_0, "rstp",
+                             lfe->sf_device->sd_info.addr, 554);
   if (rtsp == NULL)
     return NULL;
 
@@ -692,13 +694,13 @@ satip_frontend_input_thread ( void *aux )
   ev[1].fd                 = lfe->sf_rtcp->fd;
   ev[1].data.ptr           = lfe->sf_rtcp;
   ev[2].events             = TVHPOLL_IN;
-  ev[2].fd                 = rtsp->fd;
+  ev[2].fd                 = rtsp->hc_fd;
   ev[2].data.ptr           = rtsp;
-  evr                      = ev[2];
   ev[3].events             = TVHPOLL_IN;
   ev[3].fd                 = lfe->sf_dvr_pipe.rd;
   ev[3].data.ptr           = NULL;
   tvhpoll_add(efd, ev, 4);
+  rtsp->hc_efd = efd;
 
   /* Read */
   memset(&msg, 0, sizeof(msg));
@@ -709,10 +711,10 @@ satip_frontend_input_thread ( void *aux )
     iov[i].iov_len            = sizeof(tsb[0]);
   }
 
+
   r = satip_rtsp_setup(rtsp,
                        lfe->sf_position, lfe->sf_number,
-                       lfe->sf_rtp_port, &lm->lm_tuning,
-                       1);
+                       lfe->sf_rtp_port, &lm->lm_tuning);
   if (r < 0) {
     tvherror("satip", "%s - failed to tune", buf);
     return NULL;
@@ -722,18 +724,6 @@ satip_frontend_input_thread ( void *aux )
 
   while (tvheadend_running && !fatal) {
 
-    if (rtsp->sending) {
-      if ((evr.events & TVHPOLL_OUT) == 0) {
-        evr.events |= TVHPOLL_OUT;
-        tvhpoll_add(efd, &evr, 1);
-      }
-    } else {
-      if (evr.events & TVHPOLL_OUT) {
-        evr.events &= ~TVHPOLL_OUT;
-        tvhpoll_add(efd, &evr, 1);
-      }
-    }
-    
     nfds = tvhpoll_wait(efd, ev, 1, ms);
 
     if (nfds > 0 && ev[0].data.ptr == NULL) {
@@ -747,7 +737,7 @@ satip_frontend_input_thread ( void *aux )
       break;
     }
 
-    if (changing && rtsp->cmd == SATIP_RTSP_CMD_NONE) {
+    if (changing && rtsp->hc_cmd == HTTP_CMD_NONE) {
       ms = -1;
       changing = 0;
       satip_frontend_pid_changed(rtsp, lfe, buf);
@@ -757,31 +747,32 @@ satip_frontend_input_thread ( void *aux )
     if (nfds < 1) continue;
 
     if (ev[0].data.ptr == rtsp) {
-      r = satip_rtsp_run(rtsp);
+      r = http_client_run(rtsp);
       if (r < 0) {
         tvhlog(LOG_ERR, "satip", "%s - RTSP error %d (%s) [%i-%i]",
-               buf, r, strerror(-r), rtsp->cmd, rtsp->code);
+               buf, r, strerror(-r), rtsp->hc_cmd, rtsp->hc_code);
         fatal = 1;
-      } else if (r == SATIP_RTSP_READ_DONE) {
-        switch (rtsp->cmd) {
-        case SATIP_RTSP_CMD_OPTIONS:
-          r = satip_rtsp_options_decode(rtsp);
+      } else if (r == HTTP_CON_DONE) {
+        switch (rtsp->hc_cmd) {
+        case RTSP_CMD_OPTIONS:
+          r = rtsp_options_decode(rtsp);
           if (r < 0) {
             tvhlog(LOG_ERR, "satip", "%s - RTSP OPTIONS error %d (%s) [%i-%i]",
-                   buf, r, strerror(-r), rtsp->cmd, rtsp->code);
+                   buf, r, strerror(-r), rtsp->hc_cmd, rtsp->hc_code);
             fatal = 1;
           }
           break;
-        case SATIP_RTSP_CMD_SETUP:
-          r = satip_rtsp_setup_decode(rtsp);
-          if (r < 0 || rtsp->client_port != lfe->sf_rtp_port) {
+        case RTSP_CMD_SETUP:
+          r = rtsp_setup_decode(rtsp, 1);
+          if (r < 0 || rtsp->hc_rtp_port != lfe->sf_rtp_port ||
+                       rtsp->hc_rtpc_port != lfe->sf_rtp_port + 1) {
             tvhlog(LOG_ERR, "satip", "%s - RTSP SETUP error %d (%s) [%i-%i]",
-                   buf, r, strerror(-r), rtsp->cmd, rtsp->code);
+                   buf, r, strerror(-r), rtsp->hc_cmd, rtsp->hc_code);
             fatal = 1;
           } else {
             tvhdebug("satip", "%s #%i - new session %s stream id %li",
-                        lfe->sf_device->sd_info.addr, lfe->sf_number,
-                        rtsp->session, rtsp->stream_id);
+                        rtsp->hc_host, lfe->sf_number,
+                        rtsp->hc_rtsp_session, rtsp->hc_rtsp_stream_id);
             pthread_mutex_lock(&global_lock);
             satip_frontend_default_tables(lfe, mmi->mmi_mux);
             pthread_mutex_unlock(&global_lock);
@@ -789,21 +780,21 @@ satip_frontend_input_thread ( void *aux )
           }
           break;
         default:
-          if (rtsp->code >= 400) {
+          if (rtsp->hc_code >= 400) {
             tvhlog(LOG_ERR, "satip", "%s - RTSP cmd error %d (%s) [%i-%i]",
-                   buf, r, strerror(-r), rtsp->cmd, rtsp->code);
+                   buf, r, strerror(-r), rtsp->hc_cmd, rtsp->hc_code);
             fatal = 1;
           }
           break;
         }
-        rtsp->cmd = SATIP_RTSP_CMD_NONE;
+        rtsp->hc_cmd = HTTP_CMD_NONE;
       }
     }
 
     /* We need to keep the session alive */
-    if (rtsp->ping_time + rtsp->timeout / 2 < dispatch_clock &&
-        rtsp->cmd == SATIP_RTSP_CMD_NONE)
-      satip_rtsp_options(rtsp);
+    if (rtsp->hc_ping_time + rtsp->hc_rtp_timeout / 2 < dispatch_clock &&
+        rtsp->hc_cmd == HTTP_CMD_NONE)
+      rtsp_options(rtsp);
 
     if (ev[0].data.ptr == lfe->sf_rtcp) {
       c = recv(lfe->sf_rtcp->fd, rtcp, sizeof(rtcp), MSG_DONTWAIT);
@@ -875,29 +866,26 @@ satip_frontend_input_thread ( void *aux )
   ev[2].data.ptr           = NULL;
   tvhpoll_rem(efd, ev, 3);
 
-  if (rtsp->stream_id) {
-    r = satip_rtsp_teardown(rtsp);
+  if (rtsp->hc_rtsp_stream_id >= 0) {
+    snprintf((char *)rtcp, sizeof(rtcp), "/stream=%li", rtsp->hc_rtsp_stream_id);
+    r = rtsp_teardown(rtsp, (char *)rtcp, NULL);
     if (r < 0) {
       tvhtrace("satip", "%s - bad teardown", buf);
     } else {
-      if (r == SATIP_RTSP_INCOMPLETE) {
-        evr.events |= TVHPOLL_OUT;
-        tvhpoll_add(efd, &evr, 1);
-      }
-      r = 0;
-      while (r == SATIP_RTSP_INCOMPLETE) {
-        if (!rtsp->sending) {
-          evr.events &= ~TVHPOLL_OUT;
-          tvhpoll_add(efd, &evr, 1);
-        }
+      while (1) {
+        r = http_client_run(rtsp);
+        if (r != HTTP_CON_RECEIVING && r != HTTP_CON_SENDING)
+          break;
         nfds = tvhpoll_wait(efd, ev, 1, -1);
-        if (nfds < 0)
+        if (nfds <= 0) {
+          if (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK)
+            continue;
           break;
-        r = satip_rtsp_run(rtsp);
+        }
       }
     }
   }
-  satip_rtsp_connection_close(rtsp);
+  http_client_close(rtsp);
 
   tvhpoll_destroy(efd);
   return NULL;
index 67554e5b58b9ae9fc86b6d8c3d5c34395649fd7b..7b096b524f4e1089600d31a5140e0db59667eedc 100644 (file)
@@ -23,6 +23,7 @@
 #include "input.h"
 #include "htsbuf.h"
 #include "udp.h"
+#include "http.h"
 #include "satip.h"
 
 #define SATIP_BUF_SIZE    (4000*188)
@@ -201,92 +202,14 @@ int satip_satconf_get_position
  * 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;
-
-#define SATIP_RTSP_OK         1
-#define SATIP_RTSP_READ_DONE  1
-#define SATIP_RTSP_SEND_DONE  1
-#define SATIP_RTSP_INCOMPLETE 0
-
-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;
-  size_t           hsize;        /* header size */
-  size_t           csize;        /* contents size (exclude header) */
-  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_run( 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,
+satip_rtsp_setup( http_client_t *hc,
                   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 );
-
-int
-satip_rtsp_describe_decode( satip_rtsp_connection_t *conn );
+                  const dvb_mux_conf_t *dmc );
 
 int
-satip_rtsp_describe( satip_rtsp_connection_t *conn );
+satip_rtsp_play( http_client_t *hc, const char *pids,
+                 const char *addpids, const char *delpids,
+                 int max_pids_len );
 
 #endif /* __TVH_SATIP_PRIVATE_H__ */
index be473a848cbb5d05b770cb9775cec074890702d2..d5bc0d45eb56eb6cbeb18d19d55a614bc6e699f8 100644 (file)
 /*
  *
  */
-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 ||
-          errno == EINPROGRESS)
-        return SATIP_RTSP_INCOMPLETE;
-      return -errno;
-    }
-    conn->wpos += r;
-    if (conn->wpos >= conn->wsize) {
-      conn->sending = 0;
-      return SATIP_RTSP_SEND_DONE;
-    }
-    break;
-  }
-  return SATIP_RTSP_INCOMPLETE;
-}
-
-int
-satip_rtsp_send( satip_rtsp_connection_t *conn, htsbuf_queue_t *q,
-                 satip_rtsp_cmd_t cmd )
-{
-  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
-  return satip_rtsp_send_partial(conn);
-}
-
-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 SATIP_RTSP_SEND_DONE;
-}
-
-static char *
-satip_rtsp_hstrip(char *h)
-{
-  while (*h && *h <= ' ')
-    h++;
-  return h;
-}
-
-int
-satip_rtsp_run( satip_rtsp_connection_t *conn )
-{
-  char buf[1024], *saveptr, *argv[3], *d, *p, *p1;
-  htsbuf_queue_t header;
-  int cseq_seen;
-  ssize_t r;
-  size_t len;
-
-  if (conn->sending) {
-    r = satip_rtsp_send_partial(conn);
-    if (r < 0 || r == SATIP_RTSP_INCOMPLETE)
-      return 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 SATIP_RTSP_INCOMPLETE;
-    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->csize && conn->rsize > 3 &&
-      (d = strstr(conn->rbuf, "\r\n\r\n")) != NULL) {
-    conn->hsize = d - conn->rbuf + 4;
-    *d = '\0';
-    htsbuf_queue_init(&header, 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) {
-      if (strncasecmp(p, "CSeq:", 5) == 0) {
-        p1 = satip_rtsp_hstrip(p + 5);
-        if (p1)
-          cseq_seen = conn->cseq == atoi(p1);
-      } else if (strncasecmp(p, "Content-Length:", 15) == 0) {
-        conn->csize = atoll(p + 15);
-      } else {
-        htsbuf_append(&header, p, strlen(p));
-        htsbuf_append(&header, "\n", 1);
-      }
-    }
-    if (!cseq_seen)
-      goto fail;
-    free(conn->header);
-    conn->header = htsbuf_to_string(&header);
-    htsbuf_queue_flush(&header);
-    free(conn->data);
-    conn->data   = NULL;
-    if (!conn->csize)
-      goto processed;
-    if (conn->rsize > conn->hsize)
-      goto data;
-  } else if (conn->hsize + conn->csize >= conn->rsize) {
-data:
-    conn->data = malloc(conn->csize + 1);
-    memcpy(conn->data, conn->rbuf + conn->hsize, conn->csize);
-    conn->data[conn->csize] = '\0';
-processed:
-    len = conn->hsize + conn->csize;
-    memcpy(conn->rbuf, conn->rbuf + len, conn->rsize - len);
-    conn->rsize -= len;
-#if ENABLE_TRACE
-    tvhtrace("satip", "%s - received RTSP header", conn->device->sd_info.addr);
-    tvhlog_hexdump("satip", conn->header, strlen(conn->header));
-    if (conn->csize) {
-      tvhtrace("satip", "%s - received RTSP data", conn->device->sd_info.addr);
-      tvhlog_hexdump("satip", conn->data, conn->csize);
-    }
-#endif
-    conn->hsize = conn->csize = 0;
-    /* second write */
-    if (conn->wq2_loaded && conn->code == 200 && !conn->rsize) {
-      r =  satip_rtsp_send(conn, &conn->wq2, conn->wq2_cmd);
-      htsbuf_queue_flush(&conn->wq2);
-      conn->wq2_loaded = 0;
-      return r;
-    }
-    return SATIP_RTSP_READ_DONE;
-fail:
-    htsbuf_queue_flush(&header);
-    conn->rsize = 0;
-    return -EINVAL;
-  }
-  return SATIP_RTSP_INCOMPLETE;
-}
-
-/*
- *
- */
-
-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 (strcasecmp(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) ? -EIO : SATIP_RTSP_OK;
-}
-
-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 != 200)
-    return -EIO;
-  conn->client_port = 0;
-  s = strtok_r(conn->header, "\n", &saveptr);
-  while (s) {
-    n = http_tokenize(s, argv, 32, ';');
-    if (strcasecmp(argv[0], "Session:") == 0) {
-      conn->session = strdup(argv[1]);
-      for (i = 2; i < n; i++) {
-        if (strncasecmp(argv[i], "timeout=", 8) == 0) {
-          conn->timeout = atoi(argv[i] + 8);
-          if (conn->timeout <= 20 || conn->timeout > 3600)
-            return -EIO;
-        }
-      }
-    } else if (strcasecmp(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 -EIO;
-    } else if (strcasecmp(argv[0], "Transport:") == 0) {
-      if (strcasecmp(argv[1], "RTP/AVP"))
-        return -EIO;
-      if (strcasecmp(argv[2], "unicast"))
-        return -EIO;
-      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 SATIP_RTSP_OK;
-}
 
 typedef struct tvh2satip {
   int         t; ///< TVH internal value
@@ -354,9 +69,8 @@ satip_rtsp_add_val(const char *name, char *buf, uint32_t val)
 }
 
 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 )
+satip_rtsp_setup( http_client_t *hc, int src, int fe,
+                  int udp_port, const dvb_mux_conf_t *dmc )
 {
   static tvh2satip_t msys[] = {
     { .t = DVB_SYS_DVBT,                      "dvbt"  },
@@ -432,10 +146,9 @@ satip_rtsp_setup( satip_rtsp_connection_t *conn, int src, int fe,
   };
 
   char buf[512];
-  htsbuf_queue_t q;
-  int r;
+  char *stream = NULL;
+  char _stream[32];
 
-  htsbuf_queue_init(&q, 0);
   if (src > 0)
     sprintf(buf, "src=%i&", src);
   else
@@ -469,27 +182,15 @@ satip_rtsp_setup( satip_rtsp_connection_t *conn, int src, int fe,
       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;
+  if (hc->hc_rtsp_stream_id >= 0)
+    snprintf(stream = _stream, sizeof(_stream), "/stream=%li",
+             hc->hc_rtsp_stream_id);
+  return rtsp_setup(hc, stream, buf, NULL, udp_port, udp_port + 1);
 }
 
 static const char *
-satip_rtsp_pids_strip( satip_rtsp_connection_t *conn, const char *s )
+satip_rtsp_pids_strip( const char *s, int maxlen )
 {
-  int maxlen = conn->device->sd_pids_len;
   char *ptr;
 
   if (s == NULL)
@@ -508,15 +209,19 @@ satip_rtsp_pids_strip( satip_rtsp_connection_t *conn, const char *s )
 }
 
 int
-satip_rtsp_play( satip_rtsp_connection_t *conn, const char *pids,
-                 const char *addpids, const char *delpids )
+satip_rtsp_play( http_client_t *hc, const char *pids,
+                 const char *addpids, const char *delpids,
+                 int max_pids_len )
 {
   htsbuf_queue_t q;
+  char *stream = NULL;
+  char _stream[32];
+  char *query;
   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);
+  pids    = satip_rtsp_pids_strip(pids   , max_pids_len);
+  addpids = satip_rtsp_pids_strip(addpids, max_pids_len);
+  delpids = satip_rtsp_pids_strip(delpids, max_pids_len);
 
   if (pids == NULL && addpids == NULL && delpids == NULL)
     return -EINVAL;
@@ -524,8 +229,6 @@ satip_rtsp_play( satip_rtsp_connection_t *conn, const char *pids,
   //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);
@@ -535,7 +238,7 @@ satip_rtsp_play( satip_rtsp_connection_t *conn, const char *pids,
     if (addpids) {
       if (delpids) {
         /* try to maintain the maximum request size - simple split */
-        if (strlen(addpids) + strlen(delpids) <= conn->device->sd_pids_len)
+        if (strlen(addpids) + strlen(delpids) <= max_pids_len)
           split = 1;
         else
           htsbuf_append(&q, "&", 1);
@@ -544,55 +247,11 @@ satip_rtsp_play( satip_rtsp_connection_t *conn, const char *pids,
         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 < 0 || !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;
-}
-
-int
-satip_rtsp_describe_decode( satip_rtsp_connection_t *conn )
-{
-  printf("describe: %i\n", conn->code);
-  printf("header:\n%s\n",  conn->header);
-  printf("data:\n%s\n",    conn->data);
-  return SATIP_RTSP_SEND_DONE;
-}
-
-int
-satip_rtsp_describe( satip_rtsp_connection_t *conn )
-{
-  int r;
-
-  htsbuf_queue_t q;
-  htsbuf_queue_init(&q, 0);
-  htsbuf_qprintf(&q,
-           "DESCRIBE rtsp://%s/ RTSP/1.0\r\n", conn->device->sd_info.addr);
-  r = satip_rtsp_send(conn, &q, SATIP_RTSP_CMD_DESCRIBE);
-  htsbuf_queue_flush(&q);
+  if (hc->hc_rtsp_stream_id >= 0)
+    snprintf(stream = _stream, sizeof(_stream), "/stream=%li",
+             hc->hc_rtsp_stream_id);
+  query = htsbuf_to_string(&q);
+  r = rtsp_play(hc, stream, query);
+  free(query);
   return r;
 }
diff --git a/src/rtsp.c b/src/rtsp.c
new file mode 100644 (file)
index 0000000..339dc0f
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ *  Tvheadend - RTSP routines
+ *
+ *  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"
+
+/*
+ * Utils
+ */
+int
+rtsp_send( http_client_t *hc, http_cmd_t cmd,
+           const char *path, const char *query,
+           http_arg_list_t *hdr )
+{
+  http_arg_list_t h;
+  size_t blen = 7 + strlen(hc->hc_host) + (path ? strlen(path) : 1) + 1;
+  char *buf = alloca(blen);
+
+  if (hc->hc_rtsp_session && cmd != RTSP_CMD_OPTIONS) {
+    if (hdr == NULL) {
+      hdr = &h;
+      http_arg_init(&h);
+    }
+    http_arg_set(hdr, "Session", hc->hc_rtsp_session);
+  }
+  snprintf(buf, blen, "rtsp://%s%s", hc->hc_host, path ? path : "/");
+  return http_client_send(hc, cmd, buf, query, hdr, NULL, 0);
+}
+
+void
+rtsp_clear_session( http_client_t *hc )
+{
+  free(hc->hc_rtsp_session);
+  free(hc->hc_rtp_dest);
+  hc->hc_rtp_port       = 0;
+  hc->hc_rtpc_port      = 0;
+  hc->hc_rtsp_session   = NULL;
+  hc->hc_rtp_dest       = NULL;
+  hc->hc_rtp_multicast  = 0;
+  hc->hc_rtsp_stream_id = -1;
+  hc->hc_rtp_timeout    = 60;
+}
+
+/*
+ * Options
+ */
+
+int
+rtsp_options_decode( http_client_t *hc )
+{
+  char *argv[32], *p;
+  int i, n, what = 0;
+
+  p = http_arg_get(&hc->hc_args, "Public");
+  n = http_tokenize(p, argv, 32, ',');
+  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;
+  }
+  return (hc->hc_code != 200 && what != 0x0f) ? -EIO : HTTP_CON_OK;
+}
+
+int
+rtsp_setup_decode( http_client_t *hc, int satip )
+{
+  char *argv[32], *argv2[2], *p;
+  int i, n, j;
+
+#if 0
+  { http_arg_t *ra;
+  TAILQ_FOREACH(ra, &hc->hc_args, link)
+    printf("  %s: %s\n", ra->key, ra->val); }
+#endif
+  rtsp_clear_session(hc);
+  if (hc->hc_code != 200)
+    return -EIO;
+  p = http_arg_get(&hc->hc_args, "Session");
+  if (p == NULL)
+    return -EIO;
+  n = http_tokenize(p, argv, 32, ';');
+  if (n < 1)
+    return -EIO;
+  hc->hc_rtsp_session = strdup(argv[0]);
+  for (i = 1; i < n; i++) {
+    if (strncasecmp(argv[i], "timeout=", 8) == 0) {
+      hc->hc_rtp_timeout = atoi(argv[i] + 8);
+      if (hc->hc_rtp_timeout <= 20 || hc->hc_rtp_timeout > 3600)
+        return -EIO;
+    }
+  }
+  if (satip) {
+    p = http_arg_get(&hc->hc_args, "com.ses.streamID");
+    if (p == NULL)
+      return -EIO;
+    /* zero is valid stream id per specification */
+    while (*p && (*p == '0' || *p < ' '))
+      p++;
+    if (p[0] == '0' && p[1] == '\0') {
+      hc->hc_rtsp_stream_id = 0;
+    } else {
+      hc->hc_rtsp_stream_id = atoll(p);
+      if (hc->hc_rtsp_stream_id <= 0)
+        return -EIO;
+    }
+  }
+  p = http_arg_get(&hc->hc_args, "Transport");
+  if (p == NULL)
+    return -EIO;
+  n = http_tokenize(p, argv, 32, ';');
+  if (n < 3)
+    return -EIO;
+  if (strcasecmp(argv[0], "RTP/AVP"))
+    return -EIO;
+  hc->hc_rtp_multicast = strcasecmp(argv[1], "multicast") == 0;
+  if (strcasecmp(argv[1], "unicast") && !hc->hc_rtp_multicast)
+    return -EIO;
+  for (i = 2; i < n; i++) {
+    if (strncmp(argv[i], "destination=", 12) == 0)
+      hc->hc_rtp_dest = strdup(argv[i] + 12);
+    else if (strncmp(argv[i], "client_port=", 12) == 0) {
+      j = http_tokenize(argv[i] + 12, argv2, 2, '-');
+      if (j > 0) {
+        hc->hc_rtp_port = atoi(argv2[0]);
+        if (hc->hc_rtp_port <= 0)
+          return -EIO;
+        if (j > 1) {
+          hc->hc_rtpc_port = atoi(argv2[1]);
+          if (hc->hc_rtpc_port <= 0)
+            return -EIO;
+        }
+      } else {
+        return -EIO;
+      }
+    }
+  }
+  return HTTP_CON_OK;
+}
+
+int
+rtsp_setup( http_client_t *hc,
+            const char *path, const char *query,
+            const char *multicast_addr,
+            int rtp_port, int rtpc_port )
+{
+  http_arg_list_t h;
+  char transport[256];
+
+  if (multicast_addr) {
+    snprintf(transport, sizeof(transport),
+      "RTP/AVP;multicast;destination=%s;ttl=1;client_port=%i-%i",
+      multicast_addr, rtp_port, rtpc_port);
+  } else {
+    snprintf(transport, sizeof(transport),
+      "RTP/AVP;unicast;client_port=%i-%i", rtp_port, rtpc_port);
+  }
+
+  http_arg_init(&h);
+  http_arg_set(&h, "Transport", transport);
+  return rtsp_send(hc, RTSP_CMD_SETUP, path, query, &h);
+}
+
+int
+rtsp_describe_decode( http_client_t *hc )
+{
+  http_arg_t *ra;  
+
+  /* TODO: Probably rewrite the data to the htsmsg tree ? */
+  printf("describe: %i\n", hc->hc_code);
+  TAILQ_FOREACH(ra, &hc->hc_args, link)
+    printf("  %s: %s\n", ra->key, ra->val);
+  printf("data:\n%s\n",    hc->hc_data);
+  return HTTP_CON_OK;
+}