From 76c3336e4e8cceb7969b7ef4449be16adf507bc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Mon, 29 Nov 2010 19:26:34 +0000 Subject: [PATCH] Drop RTSP support. It's crappy and I no longer care about it --- Makefile | 2 - src/http.c | 3 - src/rtp.c | 136 ------- src/rtp.h | 39 -- src/rtsp.c | 1076 ---------------------------------------------------- src/rtsp.h | 30 -- 6 files changed, 1286 deletions(-) delete mode 100644 src/rtp.c delete mode 100644 src/rtp.h delete mode 100644 src/rtsp.c delete mode 100644 src/rtsp.h diff --git a/Makefile b/Makefile index aaba9a3c..338a18a0 100644 --- a/Makefile +++ b/Makefile @@ -55,8 +55,6 @@ SRCS = src/main.c \ src/parser_latm.c \ src/tsdemux.c \ src/bitstream.c \ - src/rtsp.c \ - src/rtp.c \ src/htsp.c \ src/serviceprobe.c \ src/htsmsg.c \ diff --git a/src/http.c b/src/http.c index def767d8..ea8a4f98 100644 --- a/src/http.c +++ b/src/http.c @@ -32,7 +32,6 @@ #include "tvheadend.h" #include "tcp.h" #include "http.h" -#include "rtsp.h" #include "access.h" static void *http_server; @@ -498,7 +497,6 @@ process_request(http_connection_t *hc, htsbuf_queue_t *spill) switch(hc->hc_version) { case RTSP_VERSION_1_0: - rval = rtsp_process_request(hc); break; case HTTP_VERSION_1_0: @@ -780,7 +778,6 @@ http_serve(int fd, void *opaque, struct sockaddr_in *peer, free(hc.hc_username); free(hc.hc_password); - rtsp_disconncet(&hc); http_arg_flush(&hc.hc_args); http_arg_flush(&hc.hc_req_args); diff --git a/src/rtp.c b/src/rtp.c deleted file mode 100644 index 990dd6a3..00000000 --- a/src/rtp.c +++ /dev/null @@ -1,136 +0,0 @@ -/* - * tvheadend, RTP interface - * Copyright (C) 2007 Andreas Öman - * - * 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 -#include -#include -#include -#include -#include -#include -#include - -#include "tvheadend.h" -#include "rtp.h" - -void -rtp_send_mpv(rtp_send_t *sender, void *opaque, rtp_stream_t *rs, - const uint8_t *data, size_t len, - int64_t pts) -{ - uint32_t flags = 0; - int s; - int payloadsize = RTP_MAX_PACKET_SIZE - (4 + 4 + 4 + 4); - uint8_t *buf; - - if(data[0] != 0x00 || data[1] != 0x00 || data[2] != 0x01) - return; // Not a startcode, something is fishy - - if(data[3] == 0xb3) { - // Sequence Start code, set Begin-Of-Sequence - flags |= 1 << 13; - } - while(len > 0) { - - s = len > payloadsize ? payloadsize : len; - - buf = rs->rs_buf; - buf[0] = 0x80; - buf[1] = 32 | (len == payloadsize ? 0x80 : 0); - buf[2] = rs->rs_seq >> 8; - buf[3] = rs->rs_seq; - - buf[4] = pts >> 24; - buf[5] = pts >> 16; - buf[6] = pts >> 8; - buf[7] = pts; - - buf[8] = 0; - buf[9] = 0; - buf[10] = 0; - buf[11] = 0; - - buf[12] = flags >> 24; - buf[13] = flags >> 16; - buf[14] = flags >> 8; - buf[15] = flags; - - memcpy(buf + 16, data, s); - - len -= s; - data += s; - - sender(opaque, buf, s + 16); - rs->rs_seq++; - - flags = 0; - - } - assert(len == 0); -} - - -void -rtp_send_mpa(rtp_send_t *sender, void *opaque, rtp_stream_t *rs, - const uint8_t *data, size_t len, - int64_t pts) -{ - int payloadsize = RTP_MAX_PACKET_SIZE - (4 + 4 + 4 + 4); - int offset = 0, s; - uint8_t *buf; - - while(len > 0) { - - s = len > payloadsize ? payloadsize : len; - - buf = rs->rs_buf; - buf[0] = 0x80; - buf[1] = 14; - buf[2] = rs->rs_seq >> 8; - buf[3] = rs->rs_seq; - - buf[4] = pts >> 24; - buf[5] = pts >> 16; - buf[6] = pts >> 8; - buf[7] = pts; - - buf[8] = 0; - buf[9] = 0; - buf[10] = 0; - buf[11] = 0; - - buf[12] = 0; - buf[13] = 0; - buf[14] = offset >> 8; - buf[15] = offset; - - memcpy(buf + 16, data, s); - - len -= s; - data += s; - - sender(opaque, buf, s + 16); - rs->rs_seq++; - - offset += s; - - } - assert(len == 0); -} diff --git a/src/rtp.h b/src/rtp.h deleted file mode 100644 index 9cd111bf..00000000 --- a/src/rtp.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Tvheadend, RTP streamer - * Copyright (C) 2007, 2009 Andreas Öman - * - * 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 . - */ - -#ifndef RTP_H_ -#define RTP_H_ - -typedef void (rtp_send_t)(void *opaque, void *buf, size_t len); - -#define RTP_MAX_PACKET_SIZE 1472 - -typedef struct rtp_stream { - uint16_t rs_seq; - - int rs_ptr; - uint8_t rs_buf[RTP_MAX_PACKET_SIZE]; -} rtp_stream_t; - -void rtp_send_mpv(rtp_send_t *sender, void *opaque, rtp_stream_t *rs, - const uint8_t *data, size_t len, int64_t pts); - -void rtp_send_mpa(rtp_send_t *sender, void *opaque, rtp_stream_t *rs, - const uint8_t *data, size_t len, int64_t pts); - -#endif /* RTP_H_ */ diff --git a/src/rtsp.c b/src/rtsp.c deleted file mode 100644 index 7d54bb29..00000000 --- a/src/rtsp.c +++ /dev/null @@ -1,1076 +0,0 @@ -/* - * tvheadend, RTSP interface - * Copyright (C) 2007 Andreas Öman - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "tvheadend.h" -#include "channels.h" -#include "subscriptions.h" -#include "tcp.h" -#include "http.h" -#include "access.h" -#include "rtsp.h" -#include "rtp.h" -#include "streaming.h" -#include "transports.h" - -TAILQ_HEAD(rtsp_packet_queue, rtsp_packet); - -/** - * - */ -typedef struct rtsp_packet { - TAILQ_ENTRY(rtsp_packet) rp_link; - - int rp_payloadsize; - uint8_t rp_payload[0]; - -} rtsp_packet_t; - - -/** - * - */ -typedef struct rtsp { - int rtsp_refcount; // Should only be modified with global_lock held - - struct th_subscription *rtsp_sub; - - - /* Startup */ - const char *rtsp_start_error; - struct streaming_start *rtsp_ss; - pthread_mutex_t rtsp_start_mutex; - pthread_cond_t rtsp_start_cond; - - - - streaming_target_t rtsp_input; - - LIST_HEAD(, rtsp_stream) rtsp_streams; - - int rtsp_running; - - char rtsp_session_id[65]; - - struct rtsp_packet_queue rtsp_pqueue; - int rtsp_pqueue_size; - - pthread_t rtsp_thread; - int rtsp_run_thread; - int rtsp_tcp_socket; - - pthread_mutex_t rtsp_mutex; - pthread_cond_t rtsp_cond; - -} rtsp_t; - - -/** - * - */ -typedef struct rtsp_stream { - LIST_ENTRY(rtsp_stream) rs_link; - rtsp_t *rs_rtsp; - - int rs_index; // Source index - int rs_output; // Output index (set by RTSP client) - - int rs_send_sock; - int rs_recv_sock; - - int rs_type; - rtp_stream_t rs_rtp; - rtp_send_t *rs_sender; - - int rs_interleaved_stream; - -} rtsp_stream_t; - - -#define rtsp_printf(hc, fmt...) htsbuf_qprintf(&(hc)->hc_reply, fmt) - -#define RTSP_STATUS_OK 200 -#define RTSP_STATUS_UNAUTHORIZED 401 -#define RTSP_STATUS_METHOD 405 -#define RTSP_STATUS_SESSION 454 -#define RTSP_STATUS_TRANSPORT 461 -#define RTSP_STATUS_INTERNAL 500 -#define RTSP_STATUS_SERVICE 503 - - - -/* - * RTSP return code to string - */ -static const char * -rtsp_err2str(int err) -{ - switch(err) { - case RTSP_STATUS_OK: return "OK"; - case RTSP_STATUS_UNAUTHORIZED: return "Unauthorized"; - case RTSP_STATUS_METHOD: return "Method Not Allowed"; - case RTSP_STATUS_SESSION: return "Session Not Found"; - case RTSP_STATUS_TRANSPORT: return "Unsupported transport"; - case RTSP_STATUS_INTERNAL: return "Internal Server Error"; - case RTSP_STATUS_SERVICE: return "Service Unavailable"; - case 403: return "Permission denied"; - case 459: return "Aggregate operation not allowed"; - default: - return "Error"; - } -} - -/* - * Return an error - */ -static int -rtsp_error(http_connection_t *hc, int error, const char *errstr) -{ - char *c; - - if(errstr == NULL) - errstr = rtsp_err2str(error); - - tvhlog(LOG_ERR, "RTSP", "%s: %s -- %s", - inet_ntoa(hc->hc_peer->sin_addr), hc->hc_url_orig, errstr); - - htsbuf_qprintf(&hc->hc_reply, "RTSP/1.0 %d %s\r\n", error, errstr); - if((c = http_arg_get(&hc->hc_args, "cseq")) != NULL) - htsbuf_qprintf(&hc->hc_reply, "CSeq: %s\r\n", c); - if(error == HTTP_STATUS_UNAUTHORIZED) - htsbuf_qprintf(&hc->hc_reply, - "WWW-Authenticate: Basic realm=\"tvheadend\"\r\n"); - htsbuf_qprintf(&hc->hc_reply, "\r\n"); - return 0; -} - - -/** - * - */ -static void * -rtsp_tcp_thread(void *aux) -{ - rtsp_t *rtsp = aux; - rtsp_packet_t *rp; - - pthread_mutex_lock(&rtsp->rtsp_mutex); - - while(1) { - - if(!rtsp->rtsp_run_thread) - break; - - if((rp = TAILQ_FIRST(&rtsp->rtsp_pqueue)) == NULL) { - pthread_cond_wait(&rtsp->rtsp_cond, &rtsp->rtsp_mutex); - continue; - } - - TAILQ_REMOVE(&rtsp->rtsp_pqueue, rp, rp_link); - rtsp->rtsp_pqueue_size -= rp->rp_payloadsize; - - pthread_mutex_unlock(&rtsp->rtsp_mutex); - - if(write(rtsp->rtsp_tcp_socket, rp->rp_payload, rp->rp_payloadsize)) {} - free(rp); - - pthread_mutex_lock(&rtsp->rtsp_mutex); - } - - pthread_mutex_unlock(&rtsp->rtsp_mutex); - return NULL; -} - - -/** - * - */ -static void -start_tcp_writer(rtsp_t *rtsp, int fd) -{ - if(rtsp->rtsp_run_thread) - return; - - rtsp->rtsp_tcp_socket = fd; - rtsp->rtsp_run_thread = 1; - pthread_create(&rtsp->rtsp_thread, NULL, rtsp_tcp_thread, rtsp); -} - - -/** - * - */ -static void -rtsp_stream_teardown(rtsp_stream_t *rs) -{ - if(rs->rs_send_sock != -1) - close(rs->rs_send_sock); - - if(rs->rs_recv_sock != -1) - close(rs->rs_recv_sock); - - LIST_REMOVE(rs, rs_link); - free(rs); -} - - -/** - * - */ -static void -rtsp_destroy_unref(rtsp_t *rtsp) -{ - rtsp_stream_t *rs; - rtsp_packet_t *rp; - - lock_assert(&global_lock); - - if(rtsp->rtsp_refcount > 1) { - rtsp->rtsp_refcount--; - return; - } - - if(rtsp->rtsp_sub != NULL) - subscription_unsubscribe(rtsp->rtsp_sub); - - if(rtsp->rtsp_ss != NULL) - streaming_start_unref(rtsp->rtsp_ss); - - while((rs = LIST_FIRST(&rtsp->rtsp_streams)) != NULL) - rtsp_stream_teardown(rs); - - if(rtsp->rtsp_run_thread) { - rtsp->rtsp_run_thread = 0; - pthread_cond_signal(&rtsp->rtsp_cond); - - pthread_join(rtsp->rtsp_thread, NULL); - } - - while((rp = TAILQ_FIRST(&rtsp->rtsp_pqueue)) != NULL) { - TAILQ_REMOVE(&rtsp->rtsp_pqueue, rp, rp_link); - free(rp); - } - - free(rtsp); -} - - - -/** - * - */ -static int -genrand32(uint8_t *r) -{ - int fd, n; - - if((fd = tvh_open("/dev/urandom", O_RDONLY, 0)) < 0) - return -1; - - n = read(fd, r, 32); - close(fd); - return n != 32; -} - - -/** - * - */ -static rtsp_t * -rtsp_get_session(http_connection_t *hc) -{ - rtsp_t *rtsp; - uint8_t r[32]; - int i; - - if(hc->hc_rtsp_session != NULL) - return hc->hc_rtsp_session; - - rtsp = hc->hc_rtsp_session = calloc(1, sizeof(rtsp_t)); - - if(genrand32(r)) - return NULL; - - for(i = 0; i < 32; i++) - sprintf(rtsp->rtsp_session_id + i * 2, "%02x", r[i]); - - TAILQ_INIT(&rtsp->rtsp_pqueue); - pthread_cond_init(&rtsp->rtsp_cond, NULL); - pthread_mutex_init(&rtsp->rtsp_mutex, NULL); - - pthread_cond_init(&rtsp->rtsp_start_cond, NULL); - pthread_mutex_init(&rtsp->rtsp_start_mutex, NULL); - return rtsp; -} - -/** - * Return 0 if OK, ~0 if fail - */ -static int -rtsp_check_session(http_connection_t *hc, rtsp_t *rtsp, int none_is_ok) -{ - char *ses = http_arg_get(&hc->hc_args, "session"); - - if(none_is_ok && ses == NULL) - return 0; - - return ses == NULL || strcmp(rtsp->rtsp_session_id, ses); -} - -/** - * - */ -static void -rtsp_send_tcp(void *opaque, void *buf, size_t len) -{ - rtsp_stream_t *rs = opaque; - rtsp_t *rtsp = rs->rs_rtsp; - rtsp_packet_t *rp = malloc(sizeof(rtsp_packet_t) + len + 4); - - rp->rp_payload[0] = '$'; - rp->rp_payload[1] = rs->rs_interleaved_stream; - rp->rp_payload[2] = len >> 8; - rp->rp_payload[3] = len; - - memcpy(rp->rp_payload + 4, buf, len); - rp->rp_payloadsize = len + 4; - - pthread_mutex_lock(&rtsp->rtsp_mutex); - TAILQ_INSERT_TAIL(&rtsp->rtsp_pqueue, rp, rp_link); - rtsp->rtsp_pqueue_size += rp->rp_payloadsize; - pthread_cond_signal(&rtsp->rtsp_cond); - pthread_mutex_unlock(&rtsp->rtsp_mutex); -} - - -/** - * - */ -static void -rtsp_send_udp(void *opaque, void *buf, size_t len) -{ - rtsp_stream_t *rs = opaque; - if(write(rs->rs_send_sock, buf, len)) {} -} - - - - -/** - * - */ -static void -rtsp_streaming_send(rtsp_t *rtsp, th_pkt_t *pkt) -{ - rtsp_stream_t *rs; - - pkt = pkt_merge_header(pkt); - - LIST_FOREACH(rs, &rtsp->rtsp_streams, rs_link) - if(rs->rs_index == pkt->pkt_componentindex) - break; - - if(rs == NULL) - return; - - switch(rs->rs_type) { - case SCT_MPEG2VIDEO: - rtp_send_mpv(rs->rs_sender, rs, &rs->rs_rtp, - pktbuf_ptr(pkt->pkt_payload), - pktbuf_len(pkt->pkt_payload), - pkt->pkt_pts); - break; - case SCT_MPEG2AUDIO: - rtp_send_mpa(rs->rs_sender, rs, &rs->rs_rtp, - pktbuf_ptr(pkt->pkt_payload), - pktbuf_len(pkt->pkt_payload), - pkt->pkt_pts); - break; - } -} - - - - -/** - * - */ -static void -rtsp_streaming_input(void *opaque, streaming_message_t *sm) -{ - rtsp_t *rtsp = opaque; - - switch(sm->sm_type) { - case SMT_START: - - pthread_mutex_lock(&rtsp->rtsp_start_mutex); - - assert(rtsp->rtsp_ss == NULL); - rtsp->rtsp_ss = sm->sm_data; - - pthread_cond_signal(&rtsp->rtsp_start_cond); - pthread_mutex_unlock(&rtsp->rtsp_start_mutex); - - sm->sm_data = NULL; // steal reference - break; - - case SMT_STOP: - pthread_mutex_lock(&rtsp->rtsp_start_mutex); - - assert(rtsp->rtsp_ss != NULL); - streaming_start_unref(rtsp->rtsp_ss); - rtsp->rtsp_ss = NULL; - - pthread_cond_signal(&rtsp->rtsp_start_cond); - pthread_mutex_unlock(&rtsp->rtsp_start_mutex); - break; - - case SMT_PACKET: - if(rtsp->rtsp_running) { - th_pkt_t *pkt = pkt_merge_header(sm->sm_data); - rtsp_streaming_send(rtsp, pkt); - pkt_ref_dec(pkt); - sm->sm_data = NULL; - } - break; - - case SMT_TRANSPORT_STATUS: - if(sm->sm_code & TSS_PACKETS) { - - } else if(sm->sm_code & (TSS_GRACEPERIOD | TSS_ERRORS)) { - - pthread_mutex_lock(&rtsp->rtsp_start_mutex); - rtsp->rtsp_start_error = transport_tss2text(sm->sm_code); - pthread_cond_signal(&rtsp->rtsp_start_cond); - pthread_mutex_unlock(&rtsp->rtsp_start_mutex); - } - break; - - case SMT_NOSTART: - pthread_mutex_lock(&rtsp->rtsp_start_mutex); - rtsp->rtsp_start_error = streaming_code2txt(sm->sm_code); - pthread_cond_signal(&rtsp->rtsp_start_cond); - pthread_mutex_unlock(&rtsp->rtsp_start_mutex); - break; - - case SMT_MPEGTS: - break; - - default: - abort(); - } - streaming_msg_free(sm); -} - -/** - * Attach the url to an internal resource (channel or transport) - */ -static int -rtsp_subscribe(http_connection_t *hc, rtsp_t *rtsp, - char *title, size_t titlelen, - char *baseurl, size_t baseurllen) -{ - char *components[5]; - int nc; - char *url = hc->hc_url; - int pri = 150; - int subflags = 0; - th_subscription_t *s; - char urlprefix[128]; - char buf[INET_ADDRSTRLEN + 1]; - - inet_ntop(AF_INET, &hc->hc_self->sin_addr, buf, sizeof(buf)); - snprintf(urlprefix, sizeof(urlprefix), - "rtsp://%s:%d", buf, ntohs(hc->hc_self->sin_port)); - - if(rtsp->rtsp_sub != NULL) { - rtsp_error(hc, 400, "Please teardown first"); - return -1; - } - - if(!strncasecmp(url, "rtsp://", strlen("rtsp://"))) { - url += strlen("rtsp://"); - url = strchr(url, '/'); - if(url == NULL) { - rtsp_error(hc, RTSP_STATUS_SERVICE, "Invalid URL"); - return -1; - } - url++; - } - - nc = http_tokenize(url, components, 5, '/'); - - if(nc < 2 || nc > 3) - return -1; - - http_deescape(components[1]); - - rtsp->rtsp_start_error = NULL; - rtsp->rtsp_ss = NULL; - streaming_target_init(&rtsp->rtsp_input, rtsp_streaming_input, rtsp, 0); - - - if(!strcmp(components[0], "channel")) { - channel_t *ch; - scopedgloballock(); - - if((ch = channel_find_by_name(components[1], 0, 0)) == NULL) { - rtsp_error(hc, RTSP_STATUS_SERVICE, "Channel name not found"); - return -1; - } - - s = subscription_create_from_channel(ch, pri, "RTSP", &rtsp->rtsp_input, - subflags); - - snprintf(baseurl, baseurllen, "%s/channelid/%d", urlprefix, ch->ch_id); - snprintf(title, titlelen, "%s", ch->ch_name); - - } else if(!strcmp(components[0], "channelid")) { - channel_t *ch; - scopedgloballock(); - - if((ch = channel_find_by_identifier(atoi(components[1]))) == NULL) { - rtsp_error(hc, RTSP_STATUS_SERVICE, "Channel ID not found"); - return -1; - } - - s = subscription_create_from_channel(ch, pri, "RTSP", &rtsp->rtsp_input, - subflags); - - snprintf(baseurl, baseurllen, "%s/channelid/%d", urlprefix, ch->ch_id); - snprintf(title, titlelen, "%s", ch->ch_name); - - } else if(!strcmp(components[0], "service")) { - th_transport_t *t; - scopedgloballock(); - - if((t = transport_find_by_identifier(components[1])) == NULL) { - rtsp_error(hc, RTSP_STATUS_SERVICE, "Transport ID not found"); - return -1; - } - s = subscription_create_from_transport(t, "RTSP", &rtsp->rtsp_input, - subflags); - - snprintf(baseurl, baseurllen, "%s/service/%s", - urlprefix, t->tht_identifier); - snprintf(title, titlelen, "%s", t->tht_identifier); - - } else { - rtsp_error(hc, RTSP_STATUS_SERVICE, "Invalid URL"); - return -1; - } - - pthread_mutex_lock(&rtsp->rtsp_start_mutex); - while(rtsp->rtsp_start_error == NULL && rtsp->rtsp_ss == NULL) - pthread_cond_wait(&rtsp->rtsp_start_cond, &rtsp->rtsp_start_mutex); - - if(rtsp->rtsp_start_error != NULL) { - pthread_mutex_unlock(&rtsp->rtsp_start_mutex); - - - pthread_mutex_lock(&global_lock); - subscription_unsubscribe(s); - pthread_mutex_unlock(&global_lock); - - rtsp_error(hc, RTSP_STATUS_SERVICE, rtsp->rtsp_start_error); - return -1; - } - - pthread_mutex_unlock(&rtsp->rtsp_start_mutex); - - rtsp->rtsp_sub = s; - return 0; -} - - -/** - * RTSP OPTIONS - */ -static int -rtsp_cmd_options(http_connection_t *hc) -{ - char *c; - - rtsp_printf(hc, - "RTSP/1.0 200 OK\r\n" - "Public: DESCRIBE, SETUP, TEARDOWN, PLAY\r\n"); - - if((c = http_arg_get(&hc->hc_args, "cseq")) != NULL) - rtsp_printf(hc, "CSeq: %s\r\n", c); - rtsp_printf(hc, "\r\n"); - return 0; -} - - -/** - * Get stream by index - */ -static const streaming_start_component_t * -get_ssc_by_index(rtsp_t *rtsp, int wanted_idx) -{ - const streaming_start_t *ss = rtsp->rtsp_ss; - int i; - for(i = 0; i < ss->ss_num_components; i++) - if(ss->ss_components[i].ssc_index == wanted_idx) - return &ss->ss_components[i]; - return NULL; -} - - - - - - -/** - * RTSP DESCRIBE - */ -static int -rtsp_cmd_describe(http_connection_t *hc, rtsp_t *rtsp) -{ - char sdp[1000]; - char baseurl[128]; - char *c; - extern const char *htsversion; - const streaming_start_t *ss; - int i; - char title[128]; - - if(rtsp_subscribe(hc, rtsp, title, sizeof(title), baseurl, sizeof(baseurl))) - return 0; - - snprintf(sdp, sizeof(sdp), - "v=0\r\n" - "o=- 0 0 IN IPV4 127.0.0.1\r\n" - "s=%s\r\n" - "a=tool:HTS Tvheadend %s\r\n", - title, htsversion); - - ss = rtsp->rtsp_ss; - - for(i = 0; i < ss->ss_num_components; i++) { - const streaming_start_component_t *ssc = &ss->ss_components[i]; - - char controlurl[256]; - snprintf(controlurl, sizeof(controlurl), "%s/streamid=%d", - baseurl, ssc->ssc_index); - - switch(ssc->ssc_type) { - case SCT_MPEG2VIDEO: - tvh_strlcatf(sdp, sizeof(sdp), - "m=video 0 RTP/AVP 32\r\n" - "a=control:%s\r\n", controlurl); - break; - case SCT_MPEG2AUDIO: - tvh_strlcatf(sdp, sizeof(sdp), - "m=audio 0 RTP/AVP 14\r\n" - "a=control:%s\r\n", controlurl); - break; - } - } - - rtsp_printf(hc, - "RTSP/1.0 200 OK\r\n" - "Content-Type: application/sdp\r\n" - "Content-Length: %d\r\n", - strlen(sdp)); - - if((c = http_arg_get(&hc->hc_args, "cseq")) != NULL) - rtsp_printf(hc, "CSeq: %s\r\n", c); - - rtsp_printf(hc, "\r\n%s", sdp); - return 0; -} - -/** - * Create rs stream from streaming_start_componen - */ -static rtsp_stream_t * -rs_by_ssc(rtsp_t *rtsp, const streaming_start_component_t *ssc) -{ - rtsp_stream_t *rs = calloc(1, sizeof(rtsp_stream_t)); - rs->rs_rtsp = rtsp; - rs->rs_index = ssc->ssc_index; - rs->rs_type = ssc->ssc_type; - return rs; -} - - -/** - * Setup a TCP stream - */ -static int -rtsp_setup_tcp(http_connection_t *hc, rtsp_t *rtsp, - const streaming_start_component_t *ssc, - char *params[], int np) -{ - rtsp_stream_t *rs; - int navp; - char *avp[2]; - int nvalues; - char *values[2]; - int interleaved[2]; - int i; - const char *c; - - interleaved[0] = -1; - interleaved[1] = -1; - - for(i = 1; i < np; i++) { - if((navp = http_tokenize(params[i], avp, 2, '=')) == 0) - continue; - - if(navp == 2 && !strcmp(avp[0], "interleaved")) { - nvalues = http_tokenize(avp[1], values, 2, '-'); - if(nvalues > 0) interleaved[0] = atoi(values[0]); - if(nvalues > 1) interleaved[1] = atoi(values[1]); - } - } - - if(interleaved[0] == -1 || interleaved[1] == -1) - return rtsp_error(hc, RTSP_STATUS_TRANSPORT, - "No interleaved values selected by client"); - - rs = rs_by_ssc(rtsp, ssc); - - rs->rs_interleaved_stream = interleaved[0]; - rs->rs_sender = rtsp_send_tcp; - - rs->rs_send_sock = -1; - rs->rs_recv_sock = -1; - - LIST_INSERT_HEAD(&rtsp->rtsp_streams, rs, rs_link); - - start_tcp_writer(rtsp, hc->hc_fd); - - rtsp_printf(hc, - "RTSP/1.0 200 OK\r\n" - "Session: %s\r\n" - "Transport: RTP/AVP/TCP;interleaved=%d-%d\r\n", - rtsp->rtsp_session_id, - interleaved[0], interleaved[1]); - - if((c = http_arg_get(&hc->hc_args, "cseq")) != NULL) - rtsp_printf(hc, "CSeq: %s\r\n", c); - - rtsp_printf(hc, "\r\n"); - return 0; -} - - -/** - * Setup an UDP stream - */ -static int -rtsp_setup_udp(http_connection_t *hc, rtsp_t *rtsp, - const streaming_start_component_t *ssc, - char *params[], int np) -{ - const char *c; - int i; - int ismulticast = 1; /* multicast is default according to RFC */ - int navp; - char *avp[2]; - int nports; - char *ports[2]; - int client_ports[2]; - int server_ports[2]; - struct sockaddr_in dst; - int attempt = 0; - rtsp_stream_t *rs; - socklen_t slen; - struct sockaddr_in sin; - int fd[2]; - - client_ports[0] = 0; - client_ports[1] = 0; - - for(i = 1; i < np; i++) { - if((navp = http_tokenize(params[i], avp, 2, '=')) == 0) - continue; - - if(navp == 1 && !strcmp(avp[0], "unicast")) { - ismulticast = 0; - - } else if(navp == 2 && !strcmp(avp[0], "client_port")) { - nports = http_tokenize(avp[1], ports, 2, '-'); - if(nports > 0) client_ports[0] = atoi(ports[0]); - if(nports > 1) client_ports[1] = atoi(ports[1]); - } - } - - if(ismulticast) - return rtsp_error(hc, RTSP_STATUS_TRANSPORT, "Multicast is not supported"); - - if(!client_ports[0] || !client_ports[1]) - return rtsp_error(hc, RTSP_STATUS_TRANSPORT, - "No UDP ports selected by client"); - - - retry: - fd[0] = tvh_socket(AF_INET, SOCK_DGRAM, 0); - - memset(&sin, 0, sizeof(struct sockaddr_in)); - sin.sin_family = AF_INET; - - if(bind(fd[0], (struct sockaddr *)&sin, sizeof(sin)) == -1) { - close(fd[0]); - // XXX log - return rtsp_error(hc, RTSP_STATUS_TRANSPORT, NULL); - } - - slen = sizeof(struct sockaddr_in); - getsockname(fd[0], (struct sockaddr *)&sin, &slen); - - server_ports[0] = ntohs(sin.sin_port); - server_ports[1] = server_ports[0] + 1; - - sin.sin_port = htons(server_ports[1]); - - fd[1] = tvh_socket(AF_INET, SOCK_DGRAM, 0); - - if(bind(fd[1], (struct sockaddr *)&sin, sizeof(sin)) == -1) { - close(fd[0]); - close(fd[1]); - - attempt++; - if(attempt == 100) { - // XXX log - return rtsp_error(hc, RTSP_STATUS_TRANSPORT, NULL); - } - goto retry; - } - - memcpy(&dst, hc->hc_peer, sizeof(struct sockaddr_in)); - dst.sin_port = htons(client_ports[0]); - - if(connect(fd[0], (struct sockaddr *)&dst, sizeof(dst))) { - close(fd[0]); - close(fd[1]); - // XXX log - return rtsp_error(hc, RTSP_STATUS_TRANSPORT, NULL); - } - - rs = rs_by_ssc(rtsp, ssc); - rs->rs_send_sock = fd[0]; - rs->rs_recv_sock = fd[1]; - rs->rs_sender = rtsp_send_udp; - - LIST_INSERT_HEAD(&rtsp->rtsp_streams, rs, rs_link); - - tvhlog(LOG_DEBUG, "RTSP", "%s: %s -- Stream %s to UDP port %d", - inet_ntoa(hc->hc_peer->sin_addr), hc->hc_url_orig, - streaming_component_type2txt(ssc->ssc_type), - client_ports[0]); - - rtsp_printf(hc, - "RTSP/1.0 200 OK\r\n" - "Session: %s\r\n" - "Transport: RTP/AVP/UDP;unicast;client_port=%d-%d;" - "server_port=%d-%d\r\n", - rtsp->rtsp_session_id, - client_ports[0], - client_ports[1], - server_ports[0], - server_ports[1]); - - if((c = http_arg_get(&hc->hc_args, "cseq")) != NULL) - rtsp_printf(hc, "CSeq: %s\r\n", c); - rtsp_printf(hc, "\r\n"); - return 0; -} - - -/** - * RTSP SETUP - */ -static int -rtsp_cmd_setup(http_connection_t *hc, rtsp_t *rtsp) -{ - char *transports[10]; - char *params[10]; - char *t; - int nt, i, np; - int streamid; - char *remain; - const streaming_start_component_t *ssc; - - remain = strstr(hc->hc_url, "streamid="); - if(remain == NULL) { - rtsp_error(hc, RTSP_STATUS_SERVICE, "SETUP: URL does not resolve"); - return 0; - } - - if(rtsp->rtsp_ss == NULL) { - rtsp_error(hc, RTSP_STATUS_SERVICE, "Not described"); - return 0; - } - - streamid = atoi(remain + strlen("streamid=")); - if((ssc = get_ssc_by_index(rtsp, streamid)) == NULL) { - return rtsp_error(hc, RTSP_STATUS_SERVICE, "Stream not found"); - } - - if((t = http_arg_get(&hc->hc_args, "transport")) == NULL) { - rtsp_error(hc, RTSP_STATUS_TRANSPORT, NULL); - return 0; - } - - nt = http_tokenize(t, transports, 10, ','); - - /* Select a transport we can accept */ - - for(i = 0; i < nt; i++) { - np = http_tokenize(transports[i], params, 10, ';'); - if(np == 0) - continue; - - if(!strcasecmp(params[0], "RTP/AVP/UDP") || - !strcasecmp(params[0], "RTP/AVP")) - return rtsp_setup_udp(hc, rtsp, ssc, params, np); - - if(!strcasecmp(params[0], "RTP/AVP/TCP")) - return rtsp_setup_tcp(hc, rtsp, ssc, params, np); - } - - rtsp_error(hc, RTSP_STATUS_TRANSPORT, NULL); - return 0; -} - - -/** - * RTSP PLAY - */ -static int -rtsp_cmd_play(http_connection_t *hc, rtsp_t *rtsp) -{ - char *c; - - if(rtsp_check_session(hc, rtsp, 0)) - return rtsp_error(hc, RTSP_STATUS_SERVICE, "Invalid session ID"); - - rtsp->rtsp_running = 1; - - rtsp_printf(hc, - "RTSP/1.0 200 OK\r\n" - "Session: %s\r\n", - rtsp->rtsp_session_id); - - if((c = http_arg_get(&hc->hc_args, "cseq")) != NULL) - rtsp_printf(hc, "CSeq: %s\r\n", c); - - rtsp_printf(hc, "\r\n"); - return 0; -} - -/** - * RTSP TEARDOWN - */ -static int -rtsp_cmd_teardown(http_connection_t *hc, rtsp_t *rtsp) -{ - char *c; - - if(rtsp_check_session(hc, rtsp, 0)) - return rtsp_error(hc, RTSP_STATUS_SERVICE, "Invalid session ID"); - - assert(hc->hc_rtsp_session == rtsp); - - rtsp_printf(hc, - "RTSP/1.0 200 OK\r\n"); - - if((c = http_arg_get(&hc->hc_args, "cseq")) != NULL) - rtsp_printf(hc, "CSeq: %s\r\n", c); - - rtsp_printf(hc, "\r\n"); - - pthread_mutex_lock(&global_lock); - rtsp_destroy_unref(hc->hc_rtsp_session); - hc->hc_rtsp_session = NULL; - pthread_mutex_unlock(&global_lock); - return 0; -} - - - -/* - * RTSP connection state machine & parser - */ -int -rtsp_process_request(http_connection_t *hc) -{ - int r; - if(http_access_verify(hc, ACCESS_STREAMING)) { - rtsp_error(hc, RTSP_STATUS_UNAUTHORIZED, NULL); - r = 0; - } else { - rtsp_t *rtsp = rtsp_get_session(hc); - - switch(hc->hc_cmd) { - case RTSP_CMD_OPTIONS: - r = rtsp_cmd_options(hc); - break; - case RTSP_CMD_DESCRIBE: - rtsp_cmd_describe(hc, rtsp); - break; - case RTSP_CMD_SETUP: - rtsp_cmd_setup(hc, rtsp); - break; - case RTSP_CMD_PLAY: - rtsp_cmd_play(hc, rtsp); - break; - case RTSP_CMD_TEARDOWN: - rtsp_cmd_teardown(hc, rtsp); - break; - case RTSP_CMD_PAUSE: - rtsp_error(hc, RTSP_STATUS_METHOD, "Pause is not supported"); - break; - default: - rtsp_error(hc, RTSP_STATUS_METHOD, NULL); - break; - } - } - - if(!r) - tcp_write_queue(hc->hc_fd, &hc->hc_reply); - - return 0; -} - -/* - * - */ -void -rtsp_disconncet(http_connection_t *hc) -{ - pthread_mutex_lock(&global_lock); - if(hc->hc_rtsp_session != NULL) - rtsp_destroy_unref(hc->hc_rtsp_session); - pthread_mutex_unlock(&global_lock); -} diff --git a/src/rtsp.h b/src/rtsp.h deleted file mode 100644 index 2469fc50..00000000 --- a/src/rtsp.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * tvheadend, RTSP interface - * Copyright (C) 2007 Andreas Öman - * - * 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 . - */ - -#ifndef RTSP_H_ -#define RTSP_H_ - -#include "http.h" - -int rtsp_process_request(http_connection_t *hc); - -void rtsp_disconncet(http_connection_t *hc); - -void rtsp_init(void); - -#endif /* RTSP_H_ */