#include "libavutil/base64.h"
#include "libavutil/avstring.h"
#include "libavutil/intreadwrite.h"
+#include "libavutil/random_seed.h"
#include "avformat.h"
#include <sys/time.h>
#include "internal.h"
#include "network.h"
#include "os_support.h"
+#include "http.h"
#include "rtsp.h"
#include "rtpdec.h"
static void sdp_parse_fmtp(AVStream *st, const char *p)
{
char attr[256];
- /* Vorbis setup headers can be up to 12KB and are sent base64
- * encoded, giving a 12KB * (4/3) = 16KB FMTP line. */
- char value[16384];
+ char value[4096];
int i;
RTSPStream *rtsp_st = st->priv_data;
AVCodecContext *codec = st->codec;
* "rulebooks" describing their properties. Therefore, the SDP line
* buffer is large.
*
- * The Vorbis FMTP line can be up to 16KB - see sdp_parse_fmtp. */
+ * The Vorbis FMTP line can be up to 16KB - see xiph_parse_sdp_line
+ * in rtpdec_xiph.c. */
char buf[16384], *q;
SDPParseState sdp_parse_state, *s1 = &sdp_parse_state;
return 0;
}
-void ff_rtsp_send_cmd_with_content_async(AVFormatContext *s,
- const char *method, const char *url,
- const char *headers,
- const unsigned char *send_content,
- int send_content_length)
+int ff_rtsp_send_cmd_with_content_async(AVFormatContext *s,
+ const char *method, const char *url,
+ const char *headers,
+ const unsigned char *send_content,
+ int send_content_length)
{
RTSPState *rt = s->priv_data;
- char buf[4096];
+ char buf[4096], *out_buf;
+ char base64buf[AV_BASE64_SIZE(sizeof(buf))];
+ /* Add in RTSP headers */
+ out_buf = buf;
rt->seq++;
snprintf(buf, sizeof(buf), "%s %s RTSP/1.0\r\n", method, url);
if (headers)
av_strlcatf(buf, sizeof(buf), "Content-Length: %d\r\n", send_content_length);
av_strlcat(buf, "\r\n", sizeof(buf));
+ /* base64 encode rtsp if tunneling */
+ if (rt->control_transport == RTSP_MODE_TUNNEL) {
+ av_base64_encode(base64buf, sizeof(base64buf), buf, strlen(buf));
+ out_buf = base64buf;
+ }
+
dprintf(s, "Sending:\n%s--\n", buf);
- url_write(rt->rtsp_hd, buf, strlen(buf));
- if (send_content_length > 0 && send_content)
- url_write(rt->rtsp_hd, send_content, send_content_length);
+ url_write(rt->rtsp_hd_out, out_buf, strlen(out_buf));
+ if (send_content_length > 0 && send_content) {
+ if (rt->control_transport == RTSP_MODE_TUNNEL) {
+ av_log(s, AV_LOG_ERROR, "tunneling of RTSP requests "
+ "with content data not supported\n");
+ return AVERROR_PATCHWELCOME;
+ }
+ url_write(rt->rtsp_hd_out, send_content, send_content_length);
+ }
rt->last_cmd_time = av_gettime();
+
+ return 0;
}
-void ff_rtsp_send_cmd_async(AVFormatContext *s, const char *method,
- const char *url, const char *headers)
+int ff_rtsp_send_cmd_async(AVFormatContext *s, const char *method,
+ const char *url, const char *headers)
{
- ff_rtsp_send_cmd_with_content_async(s, method, url, headers, NULL, 0);
+ return ff_rtsp_send_cmd_with_content_async(s, method, url, headers, NULL, 0);
}
-void ff_rtsp_send_cmd(AVFormatContext *s, const char *method, const char *url,
- const char *headers, RTSPMessageHeader *reply,
- unsigned char **content_ptr)
+int ff_rtsp_send_cmd(AVFormatContext *s, const char *method, const char *url,
+ const char *headers, RTSPMessageHeader *reply,
+ unsigned char **content_ptr)
{
- ff_rtsp_send_cmd_with_content(s, method, url, headers, reply,
- content_ptr, NULL, 0);
+ return ff_rtsp_send_cmd_with_content(s, method, url, headers, reply,
+ content_ptr, NULL, 0);
}
-void ff_rtsp_send_cmd_with_content(AVFormatContext *s,
- const char *method, const char *url,
- const char *header,
- RTSPMessageHeader *reply,
- unsigned char **content_ptr,
- const unsigned char *send_content,
- int send_content_length)
+int ff_rtsp_send_cmd_with_content(AVFormatContext *s,
+ const char *method, const char *url,
+ const char *header,
+ RTSPMessageHeader *reply,
+ unsigned char **content_ptr,
+ const unsigned char *send_content,
+ int send_content_length)
{
RTSPState *rt = s->priv_data;
HTTPAuthType cur_auth_type;
+ int ret;
retry:
cur_auth_type = rt->auth_state.auth_type;
- ff_rtsp_send_cmd_with_content_async(s, method, url, header,
- send_content, send_content_length);
+ if ((ret = ff_rtsp_send_cmd_with_content_async(s, method, url, header,
+ send_content,
+ send_content_length)))
+ return ret;
- ff_rtsp_read_reply(s, reply, content_ptr, 0);
+ if ((ret = ff_rtsp_read_reply(s, reply, content_ptr, 0) ) < 0)
+ return ret;
if (reply->status_code == 401 && cur_auth_type == HTTP_AUTH_NONE &&
rt->auth_state.auth_type != HTTP_AUTH_NONE)
goto retry;
+
+ return 0;
}
/**
return 0;
}
+void ff_rtsp_close_connections(AVFormatContext *s)
+{
+ RTSPState *rt = s->priv_data;
+ if (rt->rtsp_hd_out != rt->rtsp_hd) url_close(rt->rtsp_hd_out);
+ url_close(rt->rtsp_hd);
+}
+
int ff_rtsp_connect(AVFormatContext *s)
{
RTSPState *rt = s->priv_data;
char host[1024], path[1024], tcpname[1024], cmd[2048], auth[128];
char *option_list, *option, *filename;
- URLContext *rtsp_hd;
+ URLContext *rtsp_hd, *rtsp_hd_out;
int port, err, tcp_fd;
RTSPMessageHeader reply1 = {}, *reply = &reply1;
int lower_transport_mask = 0;
if (!ff_network_init())
return AVERROR(EIO);
redirect:
+ rt->control_transport = RTSP_MODE_PLAIN;
/* extract hostname and port */
ff_url_split(NULL, 0, auth, sizeof(auth),
host, sizeof(host), &port, path, sizeof(path), s->filename);
lower_transport_mask |= (1<< RTSP_LOWER_TRANSPORT_UDP_MULTICAST);
} else if (!strcmp(option, "tcp")) {
lower_transport_mask |= (1<< RTSP_LOWER_TRANSPORT_TCP);
+ } else if(!strcmp(option, "http")) {
+ lower_transport_mask |= (1<< RTSP_LOWER_TRANSPORT_TCP);
+ rt->control_transport = RTSP_MODE_TUNNEL;
} else {
/* Write options back into the buffer, using memmove instead
* of strcpy since the strings may overlap. */
/* Only UDP or TCP - UDP multicast isn't supported. */
lower_transport_mask &= (1 << RTSP_LOWER_TRANSPORT_UDP) |
(1 << RTSP_LOWER_TRANSPORT_TCP);
- if (!lower_transport_mask) {
+ if (!lower_transport_mask || rt->control_transport == RTSP_MODE_TUNNEL) {
av_log(s, AV_LOG_ERROR, "Unsupported lower transport method, "
"only UDP and TCP are supported for output.\n");
err = AVERROR(EINVAL);
ff_url_join(rt->control_uri, sizeof(rt->control_uri), "rtsp", NULL,
host, port, "%s", path);
- /* open the tcp connexion */
- ff_url_join(tcpname, sizeof(tcpname), "tcp", NULL, host, port, NULL);
- if (url_open(&rtsp_hd, tcpname, URL_RDWR) < 0) {
- err = AVERROR(EIO);
- goto fail;
+ if (rt->control_transport == RTSP_MODE_TUNNEL) {
+ /* set up initial handshake for tunneling */
+ char httpname[1024];
+ char sessioncookie[17];
+ char headers[1024];
+
+ ff_url_join(httpname, sizeof(httpname), "http", NULL, host, port, "%s", path);
+ snprintf(sessioncookie, sizeof(sessioncookie), "%08x%08x",
+ av_get_random_seed(), av_get_random_seed());
+
+ /* GET requests */
+ if (url_open(&rtsp_hd, httpname, URL_RDONLY) < 0) {
+ err = AVERROR(EIO);
+ goto fail;
+ }
+
+ /* generate GET headers */
+ snprintf(headers, sizeof(headers),
+ "x-sessioncookie: %s\r\n"
+ "Accept: application/x-rtsp-tunnelled\r\n"
+ "Pragma: no-cache\r\n"
+ "Cache-Control: no-cache\r\n",
+ sessioncookie);
+ ff_http_set_headers(rtsp_hd, headers);
+
+ /* complete the connection */
+ if (url_read(rtsp_hd, NULL, 0)) {
+ err = AVERROR(EIO);
+ goto fail;
+ }
+
+ /* POST requests */
+ if (url_open(&rtsp_hd_out, httpname, URL_WRONLY) < 0 ) {
+ err = AVERROR(EIO);
+ goto fail;
+ }
+
+ /* generate POST headers */
+ snprintf(headers, sizeof(headers),
+ "x-sessioncookie: %s\r\n"
+ "Content-Type: application/x-rtsp-tunnelled\r\n"
+ "Pragma: no-cache\r\n"
+ "Cache-Control: no-cache\r\n"
+ "Content-Length: 32767\r\n"
+ "Expires: Sun, 9 Jan 1972 00:00:00 GMT\r\n",
+ sessioncookie);
+ ff_http_set_headers(rtsp_hd_out, headers);
+ ff_http_set_chunked_transfer_encoding(rtsp_hd_out, 0);
+
+ } else {
+ /* open the tcp connection */
+ ff_url_join(tcpname, sizeof(tcpname), "tcp", NULL, host, port, NULL);
+ if (url_open(&rtsp_hd, tcpname, URL_RDWR) < 0) {
+ err = AVERROR(EIO);
+ goto fail;
+ }
+ rtsp_hd_out = rtsp_hd;
}
rt->rtsp_hd = rtsp_hd;
+ rt->rtsp_hd_out = rtsp_hd_out;
rt->seq = 0;
tcp_fd = url_get_file_handle(rtsp_hd);
return 0;
fail:
ff_rtsp_close_streams(s);
- url_close(rt->rtsp_hd);
+ ff_rtsp_close_connections(s);
if (reply->status_code >=300 && reply->status_code < 400 && s->iformat) {
av_strlcpy(s->filename, reply->location, sizeof(s->filename));
av_log(s, AV_LOG_INFO, "Status %d: Redirecting to %s\n",
static int rtsp_read_header(AVFormatContext *s,
AVFormatParameters *ap)
{
- RTSPState *rt = s->priv_data;
int ret;
ret = ff_rtsp_connect(s);
} else {
if (rtsp_read_play(s) < 0) {
ff_rtsp_close_streams(s);
- url_close(rt->rtsp_hd);
+ ff_rtsp_close_connections(s);
return AVERROR_INVALIDDATA;
}
}
ff_rtsp_send_cmd_async(s, "TEARDOWN", rt->control_uri, NULL);
ff_rtsp_close_streams(s);
- url_close(rt->rtsp_hd);
+ ff_rtsp_close_connections(s);
ff_network_close();
return 0;
}