Merge commit '1f57d60129b0e297cd197c6031c4439b30a6b503'
authorMichael Niedermayer <michaelni@gmx.at>
Tue, 30 Jul 2013 09:57:37 +0000 (11:57 +0200)
committerMichael Niedermayer <michaelni@gmx.at>
Tue, 30 Jul 2013 09:57:43 +0000 (11:57 +0200)
* commit '1f57d60129b0e297cd197c6031c4439b30a6b503':
  rtsp: Support RFC4570 (source specific multicast) more properly.

Merged-by: Michael Niedermayer <michaelni@gmx.at>
1  2 
libavformat/rtsp.c
libavformat/rtsp.h

diff --combined libavformat/rtsp.c
@@@ -2,24 -2,23 +2,24 @@@
   * RTSP/SDP client
   * Copyright (c) 2002 Fabrice Bellard
   *
 - * This file is part of Libav.
 + * This file is part of FFmpeg.
   *
 - * Libav is free software; you can redistribute it and/or
 + * FFmpeg is free software; you can redistribute it and/or
   * modify it under the terms of the GNU Lesser General Public
   * License as published by the Free Software Foundation; either
   * version 2.1 of the License, or (at your option) any later version.
   *
 - * Libav is distributed in the hope that it will be useful,
 + * FFmpeg 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
   * Lesser General Public License for more details.
   *
   * You should have received a copy of the GNU Lesser General Public
 - * License along with Libav; if not, write to the Free Software
 + * License along with FFmpeg; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
   */
  
 +#include "libavutil/avassert.h"
  #include "libavutil/base64.h"
  #include "libavutil/avstring.h"
  #include "libavutil/intreadwrite.h"
@@@ -89,9 -88,7 +89,9 @@@ const AVOption ff_rtsp_options[] = 
      { "min_port", "Minimum local UDP port", OFFSET(rtp_port_min), AV_OPT_TYPE_INT, {.i64 = RTSP_RTP_PORT_MIN}, 0, 65535, DEC|ENC },
      { "max_port", "Maximum local UDP port", OFFSET(rtp_port_max), AV_OPT_TYPE_INT, {.i64 = RTSP_RTP_PORT_MAX}, 0, 65535, DEC|ENC },
      { "timeout", "Maximum timeout (in seconds) to wait for incoming connections. -1 is infinite. Implies flag listen", OFFSET(initial_timeout), AV_OPT_TYPE_INT, {.i64 = -1}, INT_MIN, INT_MAX, DEC },
 +    { "stimeout", "timeout (in micro seconds) of socket i/o operations.", OFFSET(stimeout), AV_OPT_TYPE_INT, {.i64 = 0}, INT_MIN, INT_MAX, DEC },
      RTSP_REORDERING_OPTS(),
 +    { "user-agent", "override User-Agent header", OFFSET(user_agent), AV_OPT_TYPE_STRING, {.str = LIBAVFORMAT_IDENT}, 0, 0, DEC },
      { NULL },
  };
  
@@@ -289,8 -286,27 +289,27 @@@ typedef struct SDPParseState 
      struct sockaddr_storage default_ip;
      int            default_ttl;
      int            skip_media;  ///< set if an unknown m= line occurs
+     int nb_default_include_source_addrs; /**< Number of source-specific multicast include source IP address (from SDP content) */
+     struct RTSPSource **default_include_source_addrs; /**< Source-specific multicast include source IP address (from SDP content) */
+     int nb_default_exclude_source_addrs; /**< Number of source-specific multicast exclude source IP address (from SDP content) */
+     struct RTSPSource **default_exclude_source_addrs; /**< Source-specific multicast exclude source IP address (from SDP content) */
  } SDPParseState;
  
+ static void copy_default_source_addrs(struct RTSPSource **addrs, int count,
+                                       struct RTSPSource ***dest, int *dest_count)
+ {
+     RTSPSource *rtsp_src, *rtsp_src2;
+     int i;
+     for (i = 0; i < count; i++) {
+         rtsp_src = addrs[i];
+         rtsp_src2 = av_malloc(sizeof(*rtsp_src2));
+         if (!rtsp_src2)
+             continue;
+         memcpy(rtsp_src2, rtsp_src, sizeof(*rtsp_src));
+         dynarray_add(dest, dest_count, rtsp_src2);
+     }
+ }
  static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1,
                             int letter, const char *buf)
  {
      int payload_type, i;
      AVStream *st;
      RTSPStream *rtsp_st;
+     RTSPSource *rtsp_src;
      struct sockaddr_storage sdp_ip;
      int ttl;
  
          rtsp_st->sdp_ip = s1->default_ip;
          rtsp_st->sdp_ttl = s1->default_ttl;
  
+         copy_default_source_addrs(s1->default_include_source_addrs,
+                                   s1->nb_default_include_source_addrs,
+                                   &rtsp_st->include_source_addrs,
+                                   &rtsp_st->nb_include_source_addrs);
+         copy_default_source_addrs(s1->default_exclude_source_addrs,
+                                   s1->nb_default_exclude_source_addrs,
+                                   &rtsp_st->exclude_source_addrs,
+                                   &rtsp_st->nb_exclude_source_addrs);
          get_word(buf1, sizeof(buf1), &p); /* port */
          rtsp_st->sdp_port = atoi(buf1);
  
              p += strspn(p, SPACE_CHARS);
              if (av_strstart(p, "inline:", &p))
                  get_word(rtsp_st->crypto_params, sizeof(rtsp_st->crypto_params), &p);
-         } else if (av_strstart(p, "source-filter:", &p) && s->nb_streams > 0) {
+         } else if (av_strstart(p, "source-filter:", &p)) {
+             int exclude = 0;
              get_word(buf1, sizeof(buf1), &p);
-             if (strcmp(buf1, "incl"))
+             if (strcmp(buf1, "incl") && strcmp(buf1, "excl"))
                  return;
+             exclude = !strcmp(buf1, "excl");
  
              get_word(buf1, sizeof(buf1), &p);
              if (strcmp(buf1, "IN") != 0)
                  return;
              get_word(buf1, sizeof(buf1), &p);
-             if (strcmp(buf1, "IP4") && strcmp(buf1, "IP6"))
+             if (strcmp(buf1, "IP4") && strcmp(buf1, "IP6") && strcmp(buf1, "*"))
                  return;
-             // not checking that the destination address actually matches
+             // not checking that the destination address actually matches or is wildcard
              get_word(buf1, sizeof(buf1), &p);
  
-             rtsp_st = rt->rtsp_streams[rt->nb_rtsp_streams - 1];
-             get_word(rtsp_st->source_addr, sizeof(rtsp_st->source_addr), &p);
+             while (*p != '\0') {
+                 rtsp_src = av_mallocz(sizeof(*rtsp_src));
+                 if (!rtsp_src)
+                     return;
+                 get_word(rtsp_src->addr, sizeof(rtsp_src->addr), &p);
+                 if (exclude) {
+                     if (s->nb_streams == 0) {
+                         dynarray_add(&s1->default_exclude_source_addrs, &s1->nb_default_exclude_source_addrs, rtsp_src);
+                     } else {
+                         rtsp_st = rt->rtsp_streams[rt->nb_rtsp_streams - 1];
+                         dynarray_add(&rtsp_st->exclude_source_addrs, &rtsp_st->nb_exclude_source_addrs, rtsp_src);
+                     }
+                 } else {
+                     if (s->nb_streams == 0) {
+                         dynarray_add(&s1->default_include_source_addrs, &s1->nb_default_include_source_addrs, rtsp_src);
+                     } else {
+                         rtsp_st = rt->rtsp_streams[rt->nb_rtsp_streams - 1];
+                         dynarray_add(&rtsp_st->include_source_addrs, &rtsp_st->nb_include_source_addrs, rtsp_src);
+                     }
+                 }
+             }
          } else {
              if (rt->server_type == RTSP_SERVER_WMS)
                  ff_wms_parse_sdp_a_line(s, p);
@@@ -538,7 -585,7 +588,7 @@@ int ff_sdp_parse(AVFormatContext *s, co
  {
      RTSPState *rt = s->priv_data;
      const char *p;
-     int letter;
+     int letter, i;
      /* Some SDP lines, particularly for Realmedia or ASF RTSP streams,
       * contain long SDP lines containing complete ASF Headers (several
       * kB) or arrays of MDPR (RM stream descriptor) headers plus
          if (*p == '\n')
              p++;
      }
+     for (i = 0; i < s1->nb_default_include_source_addrs; i++)
+         av_free(s1->default_include_source_addrs[i]);
+     av_freep(&s1->default_include_source_addrs);
+     for (i = 0; i < s1->nb_default_exclude_source_addrs; i++)
+         av_free(s1->default_exclude_source_addrs[i]);
+     av_freep(&s1->default_exclude_source_addrs);
      rt->p = av_malloc(sizeof(struct pollfd)*2*(rt->nb_rtsp_streams+1));
      if (!rt->p) return AVERROR(ENOMEM);
      return 0;
@@@ -618,7 -673,7 +676,7 @@@ void ff_rtsp_undo_setup(AVFormatContex
  void ff_rtsp_close_streams(AVFormatContext *s)
  {
      RTSPState *rt = s->priv_data;
-     int i;
+     int i, j;
      RTSPStream *rtsp_st;
  
      ff_rtsp_undo_setup(s);
              if (rtsp_st->dynamic_handler && rtsp_st->dynamic_protocol_context)
                  rtsp_st->dynamic_handler->free(
                      rtsp_st->dynamic_protocol_context);
+             for (j = 0; j < rtsp_st->nb_include_source_addrs; j++)
+                 av_free(rtsp_st->include_source_addrs[j]);
+             av_freep(&rtsp_st->include_source_addrs);
+             for (j = 0; j < rtsp_st->nb_exclude_source_addrs; j++)
+                 av_free(rtsp_st->exclude_source_addrs[j]);
+             av_freep(&rtsp_st->exclude_source_addrs);
              av_free(rtsp_st);
          }
      }
@@@ -660,7 -722,7 +725,7 @@@ int ff_rtsp_open_transport_ctx(AVFormat
          s->ctx_flags |= AVFMTCTX_NOHEADER;
  
      if (s->oformat && CONFIG_RTSP_MUXER) {
 -        int ret = ff_rtp_chain_mux_open(&rtsp_st->transport_priv, s, st,
 +        int ret = ff_rtp_chain_mux_open((AVFormatContext **)&rtsp_st->transport_priv, s, st,
                                          rtsp_st->rtp_handle,
                                          RTSP_TCP_MAX_PACKET_SIZE,
                                          rtsp_st->stream_index);
@@@ -1152,7 -1214,7 +1217,7 @@@ static int rtsp_send_cmd_with_content_a
      if (headers)
          av_strlcat(buf, headers, sizeof(buf));
      av_strlcatf(buf, sizeof(buf), "CSeq: %d\r\n", rt->seq);
 -    av_strlcatf(buf, sizeof(buf), "User-Agent: %s\r\n", LIBAVFORMAT_IDENT);
 +    av_strlcatf(buf, sizeof(buf), "User-Agent: %s\r\n",  rt->user_agent);
      if (rt->session_id[0] != '\0' && (!headers ||
          !strstr(headers, "\nIf-Match:"))) {
          av_strlcatf(buf, sizeof(buf), "Session: %s\r\n", rt->session_id);
@@@ -1263,6 -1325,10 +1328,6 @@@ int ff_rtsp_make_setup_request(AVFormat
      /* default timeout: 1 minute */
      rt->timeout = 60;
  
 -    /* for each stream, make the setup request */
 -    /* XXX: we assume the same server is used for the control of each
 -     * RTSP stream */
 -
      /* Choose a random starting offset within the first half of the
       * port range, to allow for a number of ports to try even if the offset
       * happens to be at the end of the random range. */
                                 &s->interrupt_callback, NULL))
                      goto rtp_opened;
              }
 -
              av_log(s, AV_LOG_ERROR, "Unable to open an input RTP port\n");
              err = AVERROR(EIO);
              goto fail;
@@@ -1635,8 -1702,7 +1700,8 @@@ redirect
          }
      } else {
          /* open the tcp connection */
 -        ff_url_join(tcpname, sizeof(tcpname), "tcp", NULL, host, port, NULL);
 +        ff_url_join(tcpname, sizeof(tcpname), "tcp", NULL, host, port,
 +                    "?timeout=%d", rt->stimeout);
          if (ffurl_open(&rt->rtsp_hd, tcpname, AVIO_FLAG_READ_WRITE,
                         &s->interrupt_callback, NULL) < 0) {
              err = AVERROR(EIO);
@@@ -2057,6 -2123,17 +2122,17 @@@ static int sdp_probe(AVProbeData *p1
      return 0;
  }
  
+ static void append_source_addrs(char *buf, int size, const char *name,
+                                 int count, struct RTSPSource **addrs)
+ {
+     int i;
+     if (!count)
+         return;
+     av_strlcatf(buf, size, "&%s=%s", name, addrs[0]->addr);
+     for (i = 1; i < count; i++)
+         av_strlcatf(buf, size, ",%s", addrs[i]->addr);
+ }
  static int sdp_read_header(AVFormatContext *s)
  {
      RTSPState *rt = s->priv_data;
                          "?localport=%d&ttl=%d&connect=%d", rtsp_st->sdp_port,
                          rtsp_st->sdp_ttl,
                          rt->rtsp_flags & RTSP_FLAG_FILTER_SRC ? 1 : 0);
-             if (rtsp_st->source_addr[0])
-                 av_strlcatf(url, sizeof(url), "&sources=%s", rtsp_st->source_addr);
+             append_source_addrs(url, sizeof(url), "sources",
+                                 rtsp_st->nb_include_source_addrs,
+                                 rtsp_st->include_source_addrs);
+             append_source_addrs(url, sizeof(url), "block",
+                                 rtsp_st->nb_exclude_source_addrs,
+                                 rtsp_st->exclude_source_addrs);
              if (ffurl_open(&rtsp_st->rtp_handle, url, AVIO_FLAG_READ_WRITE,
                             &s->interrupt_callback, NULL) < 0) {
                  err = AVERROR_INVALIDDATA;
diff --combined libavformat/rtsp.h
@@@ -2,20 -2,20 +2,20 @@@
   * RTSP definitions
   * Copyright (c) 2002 Fabrice Bellard
   *
 - * This file is part of Libav.
 + * This file is part of FFmpeg.
   *
 - * Libav is free software; you can redistribute it and/or
 + * FFmpeg is free software; you can redistribute it and/or
   * modify it under the terms of the GNU Lesser General Public
   * License as published by the Free Software Foundation; either
   * version 2.1 of the License, or (at your option) any later version.
   *
 - * Libav is distributed in the hope that it will be useful,
 + * FFmpeg 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
   * Lesser General Public License for more details.
   *
   * You should have received a copy of the GNU Lesser General Public
 - * License along with Libav; if not, write to the Free Software
 + * License along with FFmpeg; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
   */
  #ifndef AVFORMAT_RTSP_H
@@@ -75,7 -75,7 +75,7 @@@ enum RTSPControlTransport 
  #define RTSP_DEFAULT_NB_AUDIO_CHANNELS 1
  #define RTSP_DEFAULT_AUDIO_SAMPLERATE 44100
  #define RTSP_RTP_PORT_MIN 5000
 -#define RTSP_RTP_PORT_MAX 10000
 +#define RTSP_RTP_PORT_MAX 65000
  
  /**
   * This describes a single item in the "Transport:" line of one stream as
@@@ -390,20 -390,10 +390,20 @@@ typedef struct RTSPState 
       */
      int initial_timeout;
  
 +    /**
 +     * timeout of socket i/o operations.
 +     */
 +    int stimeout;
 +
      /**
       * Size of RTP packet reordering queue.
       */
      int reordering_queue_size;
 +
 +    /**
 +     * User-Agent string
 +     */
 +    char *user_agent;
  } RTSPState;
  
  #define RTSP_FLAG_FILTER_SRC  0x1    /**< Filter incoming UDP packets -
  #define RTSP_FLAG_LISTEN      0x2    /**< Wait for incoming connections. */
  #define RTSP_FLAG_CUSTOM_IO   0x4    /**< Do all IO via the AVIOContext. */
  
+ typedef struct RTSPSource {
+     char addr[128]; /**< Source-specific multicast include source IP address (from SDP content) */
+ } RTSPSource;
  /**
   * Describe a single stream, as identified by a single m= line block in the
   * SDP content. In the case of RDT, one RTSPStream can represent multiple
@@@ -435,7 -429,10 +439,10 @@@ typedef struct RTSPStream 
      //@{
      int sdp_port;             /**< port (from SDP content) */
      struct sockaddr_storage sdp_ip; /**< IP address (from SDP content) */
-     char source_addr[100];    /**< Source-specific multicast source IP address (from SDP content) */
+     int nb_include_source_addrs; /**< Number of source-specific multicast include source IP addresses (from SDP content) */
+     struct RTSPSource **include_source_addrs; /**< Source-specific multicast include source IP addresses (from SDP content) */
+     int nb_exclude_source_addrs; /**< Number of source-specific multicast exclude source IP addresses (from SDP content) */
+     struct RTSPSource **exclude_source_addrs; /**< Source-specific multicast exclude source IP addresses (from SDP content) */
      int sdp_ttl;              /**< IP Time-To-Live (from SDP content) */
      int sdp_payload_type;     /**< payload type */
      //@}