diff --git a/Makefile b/Makefile index eaa63ad5..6ec18b03 100644 --- 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 \ diff --git a/src/http.h b/src/http.h index 6abe0ea6..08ab0c3d 100644 --- a/src/http.h +++ b/src/http.h @@ -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_ */ diff --git a/src/httpc.c b/src/httpc.c index 5e3a523c..c9c845b6 100644 --- a/src/httpc.c +++ b/src/httpc.c @@ -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); diff --git a/src/input/mpegts/satip/satip_frontend.c b/src/input/mpegts/satip/satip_frontend.c index 7d24de86..859e556c 100644 --- a/src/input/mpegts/satip/satip_frontend.c +++ b/src/input/mpegts/satip/satip_frontend.c @@ -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); - } - nfds = tvhpoll_wait(efd, ev, 1, -1); - if (nfds < 0) + while (1) { + r = http_client_run(rtsp); + if (r != HTTP_CON_RECEIVING && r != HTTP_CON_SENDING) break; - r = satip_rtsp_run(rtsp); + nfds = tvhpoll_wait(efd, ev, 1, -1); + if (nfds <= 0) { + if (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK) + continue; + break; + } } } } - satip_rtsp_connection_close(rtsp); + http_client_close(rtsp); tvhpoll_destroy(efd); return NULL; diff --git a/src/input/mpegts/satip/satip_private.h b/src/input/mpegts/satip/satip_private.h index 67554e5b..7b096b52 100644 --- a/src/input/mpegts/satip/satip_private.h +++ b/src/input/mpegts/satip/satip_private.h @@ -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 ); + const dvb_mux_conf_t *dmc ); 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 ); - -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__ */ diff --git a/src/input/mpegts/satip/satip_rtsp.c b/src/input/mpegts/satip/satip_rtsp.c index be473a84..d5bc0d45 100644 --- a/src/input/mpegts/satip/satip_rtsp.c +++ b/src/input/mpegts/satip/satip_rtsp.c @@ -28,291 +28,6 @@ /* * */ -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 index 00000000..339dc0fd --- /dev/null +++ b/src/rtsp.c @@ -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 . + */ + +#include +#include +#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; +}