]> git.sesse.net Git - ffmpeg/blobdiff - libavformat/rtsp.c
Add a lowercase parameter to ff_data_to_hex
[ffmpeg] / libavformat / rtsp.c
index 60f13275e5e7e9dcdc69460061dddf6ea2daf184..f466574758584568d88a3112520e788b223c52e4 100644 (file)
@@ -29,7 +29,9 @@
 #include <sys/select.h>
 #endif
 #include <strings.h>
+#include "internal.h"
 #include "network.h"
+#include "os_support.h"
 #include "rtsp.h"
 
 #include "rtpdec.h"
@@ -446,8 +448,8 @@ static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1,
             rtsp_st = st->priv_data;
 
             /* XXX: may need to add full url resolution */
-            url_split(proto, sizeof(proto), NULL, 0, NULL, 0,
-                      NULL, NULL, 0, p);
+            ff_url_split(proto, sizeof(proto), NULL, 0, NULL, 0,
+                         NULL, NULL, 0, p);
             if (proto[0] == '\0') {
                 /* relative control URL */
                 if (rtsp_st->control_url[strlen(rtsp_st->control_url)-1]!='/')
@@ -582,7 +584,13 @@ void ff_rtsp_close_streams(AVFormatContext *s)
                 if (s->oformat) {
                     AVFormatContext *rtpctx = rtsp_st->transport_priv;
                     av_write_trailer(rtpctx);
-                    url_fclose(rtpctx->pb);
+                    if (rt->lower_transport == RTSP_LOWER_TRANSPORT_TCP) {
+                        uint8_t *ptr;
+                        url_close_dyn_buf(rtpctx->pb, &ptr);
+                        av_free(ptr);
+                    } else {
+                        url_fclose(rtpctx->pb);
+                    }
                     av_metadata_free(&rtpctx->streams[0]->metadata);
                     av_metadata_free(&rtpctx->metadata);
                     av_free(rtpctx->streams[0]);
@@ -607,7 +615,10 @@ void ff_rtsp_close_streams(AVFormatContext *s)
     av_freep(&rt->auth_b64);
 }
 
-static void *rtsp_rtp_mux_open(AVFormatContext *s, AVStream *st, URLContext *handle) {
+static void *rtsp_rtp_mux_open(AVFormatContext *s, AVStream *st,
+                               URLContext *handle)
+{
+    RTSPState *rt = s->priv_data;
     AVFormatContext *rtpctx;
     int ret;
     AVOutputFormat *rtp_format = av_guess_format("rtp", NULL, NULL);
@@ -630,17 +641,29 @@ static void *rtsp_rtp_mux_open(AVFormatContext *s, AVStream *st, URLContext *han
     /* Copy other stream parameters. */
     rtpctx->streams[0]->sample_aspect_ratio = st->sample_aspect_ratio;
 
+    /* Set the synchronized start time. */
+    rtpctx->start_time_realtime = rt->start_time;
+
     /* Remove the local codec, link to the original codec
      * context instead, to give the rtp muxer access to
      * codec parameters. */
     av_free(rtpctx->streams[0]->codec);
     rtpctx->streams[0]->codec = st->codec;
 
-    url_fdopen(&rtpctx->pb, handle);
+    if (handle) {
+        url_fdopen(&rtpctx->pb, handle);
+    } else
+        url_open_dyn_packet_buf(&rtpctx->pb, RTSP_TCP_MAX_PACKET_SIZE);
     ret = av_write_header(rtpctx);
 
     if (ret) {
-        url_fclose(rtpctx->pb);
+        if (handle) {
+            url_fclose(rtpctx->pb);
+        } else {
+            uint8_t *ptr;
+            url_close_dyn_buf(rtpctx->pb, &ptr);
+            av_free(ptr);
+        }
         av_free(rtpctx->streams[0]);
         av_free(rtpctx);
         return NULL;
@@ -853,7 +876,7 @@ void ff_rtsp_parse_line(RTSPMessageHeader *reply, const char *buf)
 }
 
 /* skip a RTP/TCP interleaved packet */
-static void rtsp_skip_packet(AVFormatContext *s)
+void ff_rtsp_skip_packet(AVFormatContext *s)
 {
     RTSPState *rt = s->priv_data;
     int ret, len, len1;
@@ -909,7 +932,7 @@ int ff_rtsp_read_reply(AVFormatContext *s, RTSPMessageHeader *reply,
                 if (return_on_interleaved_data) {
                     return 1;
                 } else
-                rtsp_skip_packet(s);
+                    ff_rtsp_skip_packet(s);
             } else if (ch != '\r') {
                 if ((q - buf) < sizeof(buf) - 1)
                     *q++ = ch;
@@ -951,6 +974,11 @@ int ff_rtsp_read_reply(AVFormatContext *s, RTSPMessageHeader *reply,
     else
         av_free(content);
 
+    if (rt->seq != reply->seq) {
+        av_log(s, AV_LOG_WARNING, "CSeq %d expected, %d received.\n",
+            rt->seq, reply->seq);
+    }
+
     /* EOS */
     if (reply->notice == 2101 /* End-of-Stream Reached */      ||
         reply->notice == 2104 /* Start-of-Stream Reached */    ||
@@ -1006,9 +1034,7 @@ void ff_rtsp_send_cmd(AVFormatContext *s,
                       const char *cmd, RTSPMessageHeader *reply,
                       unsigned char **content_ptr)
 {
-    ff_rtsp_send_cmd_async(s, cmd);
-
-    ff_rtsp_read_reply(s, reply, content_ptr, 0);
+    ff_rtsp_send_cmd_with_content(s, cmd, reply, content_ptr, NULL, 0);
 }
 
 void ff_rtsp_send_cmd_with_content(AVFormatContext *s,
@@ -1288,10 +1314,9 @@ static int rtsp_read_play(AVFormatContext *s)
     return 0;
 }
 
-static int rtsp_setup_input_streams(AVFormatContext *s)
+static int rtsp_setup_input_streams(AVFormatContext *s, RTSPMessageHeader *reply)
 {
     RTSPState *rt = s->priv_data;
-    RTSPMessageHeader reply1, *reply = &reply1;
     char cmd[1024];
     unsigned char *content = NULL;
     int ret;
@@ -1300,7 +1325,7 @@ static int rtsp_setup_input_streams(AVFormatContext *s)
     snprintf(cmd, sizeof(cmd),
              "DESCRIBE %s RTSP/1.0\r\n"
              "Accept: application/sdp\r\n",
-             s->filename);
+             rt->control_uri);
     if (rt->server_type == RTSP_SERVER_REAL) {
         /**
          * The Require: attribute is needed for proper streaming from
@@ -1327,23 +1352,42 @@ static int rtsp_setup_input_streams(AVFormatContext *s)
     return 0;
 }
 
-static int rtsp_setup_output_streams(AVFormatContext *s)
+static int rtsp_setup_output_streams(AVFormatContext *s, const char *addr)
 {
     RTSPState *rt = s->priv_data;
     RTSPMessageHeader reply1, *reply = &reply1;
     char cmd[1024];
     int i;
     char *sdp;
+    AVFormatContext sdp_ctx, *ctx_array[1];
+
+    rt->start_time = av_gettime();
 
     /* Announce the stream */
     snprintf(cmd, sizeof(cmd),
              "ANNOUNCE %s RTSP/1.0\r\n"
              "Content-Type: application/sdp\r\n",
-             s->filename);
+             rt->control_uri);
     sdp = av_mallocz(8192);
     if (sdp == NULL)
         return AVERROR(ENOMEM);
-    if (avf_sdp_create(&s, 1, sdp, 8192)) {
+    /* We create the SDP based on the RTSP AVFormatContext where we
+     * aren't allowed to change the filename field. (We create the SDP
+     * based on the RTSP context since the contexts for the RTP streams
+     * don't exist yet.) In order to specify a custom URL with the actual
+     * peer IP instead of the originally specified hostname, we create
+     * a temporary copy of the AVFormatContext, where the custom URL is set.
+     *
+     * FIXME: Create the SDP without copying the AVFormatContext.
+     * This either requires setting up the RTP stream AVFormatContexts
+     * already here (complicating things immensely) or getting a more
+     * flexible SDP creation interface.
+     */
+    sdp_ctx = *s;
+    ff_url_join(sdp_ctx.filename, sizeof(sdp_ctx.filename),
+                "rtsp", NULL, addr, -1, NULL);
+    ctx_array[0] = &sdp_ctx;
+    if (avf_sdp_create(ctx_array, 1, sdp, 8192)) {
         av_free(sdp);
         return AVERROR_INVALIDDATA;
     }
@@ -1366,7 +1410,7 @@ static int rtsp_setup_output_streams(AVFormatContext *s)
         st->priv_data = rtsp_st;
         rtsp_st->stream_index = i;
 
-        av_strlcpy(rtsp_st->control_url, s->filename, sizeof(rtsp_st->control_url));
+        av_strlcpy(rtsp_st->control_url, rt->control_uri, sizeof(rtsp_st->control_url));
         /* Note, this must match the relative uri set in the sdp content */
         av_strlcatf(rtsp_st->control_url, sizeof(rtsp_st->control_url),
                     "/streamid=%d", i);
@@ -1381,17 +1425,19 @@ int ff_rtsp_connect(AVFormatContext *s)
     char host[1024], path[1024], tcpname[1024], cmd[2048], auth[128];
     char *option_list, *option, *filename;
     URLContext *rtsp_hd;
-    int port, err;
+    int port, err, tcp_fd;
     RTSPMessageHeader reply1, *reply = &reply1;
     int lower_transport_mask = 0;
     char real_challenge[64];
+    struct sockaddr_storage peer;
+    socklen_t peer_len = sizeof(peer);
 
     if (!ff_network_init())
         return AVERROR(EIO);
 redirect:
     /* extract hostname and port */
-    url_split(NULL, 0, auth, sizeof(auth),
-              host, sizeof(host), &port, path, sizeof(path), s->filename);
+    ff_url_split(NULL, 0, auth, sizeof(auth),
+                 host, sizeof(host), &port, path, sizeof(path), s->filename);
     if (*auth) {
         int auth_len = strlen(auth), b64_len = ((auth_len + 2) / 3) * 4 + 1;
 
@@ -1406,9 +1452,11 @@ redirect:
         port = RTSP_DEFAULT_PORT;
 
     /* search for options */
-    option_list = strchr(path, '?');
+    option_list = strrchr(path, '?');
     if (option_list) {
-        filename = strchr(s->filename, '?');
+        /* Strip out the RTSP specific options, write out the rest of
+         * the options back into the same string. */
+        filename = option_list;
         while (option_list) {
             /* move the option pointer */
             option = ++option_list;
@@ -1418,14 +1466,17 @@ redirect:
 
             /* handle the options */
             if (!strcmp(option, "udp")) {
-                lower_transport_mask = (1<< RTSP_LOWER_TRANSPORT_UDP);
+                lower_transport_mask |= (1<< RTSP_LOWER_TRANSPORT_UDP);
             } else if (!strcmp(option, "multicast")) {
-                lower_transport_mask = (1<< RTSP_LOWER_TRANSPORT_UDP_MULTICAST);
+                lower_transport_mask |= (1<< RTSP_LOWER_TRANSPORT_UDP_MULTICAST);
             } else if (!strcmp(option, "tcp")) {
-                lower_transport_mask = (1<< RTSP_LOWER_TRANSPORT_TCP);
+                lower_transport_mask |= (1<< RTSP_LOWER_TRANSPORT_TCP);
             } else {
-                strcpy(++filename, option);
-                filename += strlen(option);
+                /* Write options back into the buffer, using memmove instead
+                 * of strcpy since the strings may overlap. */
+                int len = strlen(option);
+                memmove(++filename, option, len);
+                filename += len;
                 if (option_list) *filename = '&';
             }
         }
@@ -1436,11 +1487,12 @@ redirect:
         lower_transport_mask = (1 << RTSP_LOWER_TRANSPORT_NB) - 1;
 
     if (s->oformat) {
-        /* Only UDP output is supported at the moment. */
-        lower_transport_mask &= 1 << RTSP_LOWER_TRANSPORT_UDP;
+        /* 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) {
             av_log(s, AV_LOG_ERROR, "Unsupported lower transport method, "
-                                    "only UDP is supported for output.\n");
+                                    "only UDP and TCP are supported for output.\n");
             err = AVERROR(EINVAL);
             goto fail;
         }
@@ -1455,13 +1507,22 @@ redirect:
     rt->rtsp_hd = rtsp_hd;
     rt->seq = 0;
 
+    tcp_fd = url_get_file_handle(rtsp_hd);
+    if (!getpeername(tcp_fd, (struct sockaddr*) &peer, &peer_len)) {
+        getnameinfo((struct sockaddr*) &peer, peer_len, host, sizeof(host),
+                    NULL, 0, NI_NUMERICHOST);
+    }
+
+    /* Construct the URI used in request; this is similar to s->filename,
+     * but with authentication credentials removed and RTSP specific options
+     * stripped out. */
+    ff_url_join(rt->control_uri, sizeof(rt->control_uri), "rtsp", NULL,
+                host, port, "%s", path);
     /* request options supported by the server; this also detects server
      * type */
-    av_strlcpy(rt->control_uri, s->filename,
-               sizeof(rt->control_uri));
     for (rt->server_type = RTSP_SERVER_RTP;;) {
         snprintf(cmd, sizeof(cmd),
-                 "OPTIONS %s RTSP/1.0\r\n", s->filename);
+                 "OPTIONS %s RTSP/1.0\r\n", rt->control_uri);
         if (rt->server_type == RTSP_SERVER_REAL)
             av_strlcat(cmd,
                        /**
@@ -1496,9 +1557,9 @@ redirect:
     }
 
     if (s->iformat)
-        err = rtsp_setup_input_streams(s);
+        err = rtsp_setup_input_streams(s, reply);
     else
-        err = rtsp_setup_output_streams(s);
+        err = rtsp_setup_output_streams(s, host);
     if (err)
         goto fail;
 
@@ -1612,7 +1673,9 @@ static int udp_read_packet(AVFormatContext *s, RTSPStream **prtsp_st,
             if (tcp_fd != -1 && FD_ISSET(tcp_fd, &rfds)) {
                 RTSPMessageHeader reply;
 
-                ff_rtsp_read_reply(s, &reply, NULL, 0);
+                ret = ff_rtsp_read_reply(s, &reply, NULL, 0);
+                if (ret < 0)
+                    return ret;
                 /* XXX: parse message */
                 if (rt->state != RTSP_STATE_STREAMING)
                     return 0;
@@ -1882,7 +1945,7 @@ static int rtsp_read_close(AVFormatContext *s)
 #endif
     snprintf(cmd, sizeof(cmd),
              "TEARDOWN %s RTSP/1.0\r\n",
-             s->filename);
+             rt->control_uri);
     ff_rtsp_send_cmd_async(s, cmd);
 
     ff_rtsp_close_streams(s);