Merge commit '67351924fa91dea4339109100a4c0689f006581f'
[ffmpeg.git] / libavformat / rtpproto.c
index 21a96d2..0706cae 100644 (file)
@@ -2,20 +2,20 @@
  * RTP network protocol
  * 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
  */
 
@@ -44,7 +44,7 @@
 
 typedef struct RTPContext {
     const AVClass *class;
-    URLContext *rtp_hd, *rtcp_hd;
+    URLContext *rtp_hd, *rtcp_hd, *fec_hd;
     int rtp_fd, rtcp_fd, nb_ssm_include_addrs, nb_ssm_exclude_addrs;
     struct sockaddr_storage **ssm_include_addrs, **ssm_exclude_addrs;
     int write_to_source;
@@ -55,8 +55,10 @@ typedef struct RTPContext {
     int rtcp_port, local_rtpport, local_rtcpport;
     int connect;
     int pkt_size;
+    int dscp;
     char *sources;
     char *block;
+    char *fec_options_str;
 } RTPContext;
 
 #define OFFSET(x) offsetof(RTPContext, x)
@@ -68,11 +70,13 @@ static const AVOption options[] = {
     { "rtcp_port",          "Custom rtcp port",                                                 OFFSET(rtcp_port),       AV_OPT_TYPE_INT,    { .i64 = -1 },    -1, INT_MAX, .flags = D|E },
     { "local_rtpport",      "Local rtp port",                                                   OFFSET(local_rtpport),   AV_OPT_TYPE_INT,    { .i64 = -1 },    -1, INT_MAX, .flags = D|E },
     { "local_rtcpport",     "Local rtcp port",                                                  OFFSET(local_rtcpport),  AV_OPT_TYPE_INT,    { .i64 = -1 },    -1, INT_MAX, .flags = D|E },
-    { "connect",            "Connect socket",                                                   OFFSET(connect),         AV_OPT_TYPE_INT,    { .i64 =  0 },     0, 1,       .flags = D|E },
-    { "write_to_source",    "Send packets to the source address of the latest received packet", OFFSET(write_to_source), AV_OPT_TYPE_INT,    { .i64 =  0 },     0, 1,       .flags = D|E },
+    { "connect",            "Connect socket",                                                   OFFSET(connect),         AV_OPT_TYPE_BOOL,   { .i64 =  0 },     0, 1,       .flags = D|E },
+    { "write_to_source",    "Send packets to the source address of the latest received packet", OFFSET(write_to_source), AV_OPT_TYPE_BOOL,   { .i64 =  0 },     0, 1,       .flags = D|E },
     { "pkt_size",           "Maximum packet size",                                              OFFSET(pkt_size),        AV_OPT_TYPE_INT,    { .i64 = -1 },    -1, INT_MAX, .flags = D|E },
+    { "dscp",               "DSCP class",                                                       OFFSET(dscp),            AV_OPT_TYPE_INT,    { .i64 = -1 },    -1, INT_MAX, .flags = D|E },
     { "sources",            "Source list",                                                      OFFSET(sources),         AV_OPT_TYPE_STRING, { .str = NULL },               .flags = D|E },
     { "block",              "Block list",                                                       OFFSET(block),           AV_OPT_TYPE_STRING, { .str = NULL },               .flags = D|E },
+    { "fec",                "FEC",                                                              OFFSET(fec_options_str), AV_OPT_TYPE_STRING, { .str = NULL },               .flags = E },
     { NULL }
 };
 
@@ -239,6 +243,9 @@ static void build_udp_url(RTPContext *s,
         url_add_option(buf, buf_size, "pkt_size=%d", s->pkt_size);
     if (s->connect)
         url_add_option(buf, buf_size, "connect=1");
+    if (s->dscp >= 0)
+        url_add_option(buf, buf_size, "dscp=%d", s->dscp);
+    url_add_option(buf, buf_size, "fifo_size=0");
     if (include_sources && include_sources[0])
         url_add_option(buf, buf_size, "sources=%s", include_sources);
     if (exclude_sources && exclude_sources[0])
@@ -298,6 +305,7 @@ static void rtp_parse_addr_list(URLContext *h, char *buf,
  *         'sources=ip[,ip]'  : list allowed source IP addresses
  *         'block=ip[,ip]'    : list disallowed source IP addresses
  *         'write_to_source=0/1' : send packets to the source address of the latest received packet
+ *         'dscp=n'           : set DSCP value to n (QoS)
  * deprecated option:
  *         'localport=n'      : set the local port to n
  *
@@ -310,12 +318,16 @@ static void rtp_parse_addr_list(URLContext *h, char *buf,
 static int rtp_open(URLContext *h, const char *uri, int flags)
 {
     RTPContext *s = h->priv_data;
+    AVDictionary *fec_opts = NULL;
     int rtp_port;
     char hostname[256], include_sources[1024] = "", exclude_sources[1024] = "";
     char *sources = include_sources, *block = exclude_sources;
+    char *fec_protocol = NULL;
     char buf[1024];
     char path[1024];
     const char *p;
+    int i, max_retry_count = 3;
+    int rtcpflags;
 
     av_url_split(NULL, 0, NULL, 0, hostname, sizeof(hostname), &rtp_port,
                  path, sizeof(path), uri);
@@ -349,6 +361,9 @@ static int rtp_open(URLContext *h, const char *uri, int flags)
         if (av_find_info_tag(buf, sizeof(buf), "write_to_source", p)) {
             s->write_to_source = strtol(buf, NULL, 10);
         }
+        if (av_find_info_tag(buf, sizeof(buf), "dscp", p)) {
+            s->dscp = strtol(buf, NULL, 10);
+        }
         if (av_find_info_tag(buf, sizeof(buf), "sources", p)) {
             av_strlcpy(include_sources, buf, sizeof(include_sources));
 
@@ -366,19 +381,73 @@ static int rtp_open(URLContext *h, const char *uri, int flags)
         }
     }
 
-    build_udp_url(s, buf, sizeof(buf),
-                  hostname, rtp_port, s->local_rtpport, sources, block);
-    if (ffurl_open(&s->rtp_hd, buf, flags, &h->interrupt_callback, NULL,
-                   h->protocols, h) < 0)
-        goto fail;
-    if (s->local_rtpport >= 0 && s->local_rtcpport < 0)
-        s->local_rtcpport = ff_udp_get_local_port(s->rtp_hd) + 1;
+    if (s->fec_options_str) {
+        p = s->fec_options_str;
+
+        if (!(fec_protocol = av_get_token(&p, "="))) {
+            av_log(h, AV_LOG_ERROR, "Failed to parse the FEC protocol value\n");
+            goto fail;
+        }
+        if (strcmp(fec_protocol, "prompeg")) {
+            av_log(h, AV_LOG_ERROR, "Unsupported FEC protocol %s\n", fec_protocol);
+            goto fail;
+        }
+
+        p = s->fec_options_str + strlen(fec_protocol);
+        while (*p && *p == '=') p++;
 
-    build_udp_url(s, buf, sizeof(buf),
-                  hostname, s->rtcp_port, s->local_rtcpport, sources, block);
-    if (ffurl_open(&s->rtcp_hd, buf, flags, &h->interrupt_callback, NULL,
-                   h->protocols, h) < 0)
-        goto fail;
+        if (av_dict_parse_string(&fec_opts, p, "=", ":", 0) < 0) {
+            av_log(h, AV_LOG_ERROR, "Failed to parse the FEC options\n");
+            goto fail;
+        }
+        if (s->ttl > 0) {
+            snprintf(buf, sizeof (buf), "%d", s->ttl);
+            av_dict_set(&fec_opts, "ttl", buf, 0);
+        }
+    }
+
+    for (i = 0; i < max_retry_count; i++) {
+        build_udp_url(s, buf, sizeof(buf),
+                      hostname, rtp_port, s->local_rtpport,
+                      sources, block);
+        if (ffurl_open_whitelist(&s->rtp_hd, buf, flags, &h->interrupt_callback,
+                                 NULL, h->protocol_whitelist, h->protocol_blacklist, h) < 0)
+            goto fail;
+        s->local_rtpport = ff_udp_get_local_port(s->rtp_hd);
+        if(s->local_rtpport == 65535) {
+            s->local_rtpport = -1;
+            continue;
+        }
+        rtcpflags = flags | AVIO_FLAG_WRITE;
+        if (s->local_rtcpport < 0) {
+            s->local_rtcpport = s->local_rtpport + 1;
+            build_udp_url(s, buf, sizeof(buf),
+                          hostname, s->rtcp_port, s->local_rtcpport,
+                          sources, block);
+            if (ffurl_open_whitelist(&s->rtcp_hd, buf, rtcpflags,
+                                     &h->interrupt_callback, NULL,
+                                     h->protocol_whitelist, h->protocol_blacklist, h) < 0) {
+                s->local_rtpport = s->local_rtcpport = -1;
+                continue;
+            }
+            break;
+        }
+        build_udp_url(s, buf, sizeof(buf),
+                      hostname, s->rtcp_port, s->local_rtcpport,
+                      sources, block);
+        if (ffurl_open_whitelist(&s->rtcp_hd, buf, rtcpflags, &h->interrupt_callback,
+                                 NULL, h->protocol_whitelist, h->protocol_blacklist, h) < 0)
+            goto fail;
+        break;
+    }
+
+    s->fec_hd = NULL;
+    if (fec_protocol) {
+        ff_url_join(buf, sizeof(buf), fec_protocol, NULL, hostname, rtp_port, NULL);
+        if (ffurl_open_whitelist(&s->fec_hd, buf, flags, &h->interrupt_callback,
+                             &fec_opts, h->protocol_whitelist, h->protocol_blacklist, h) < 0)
+            goto fail;
+    }
 
     /* just to ease handle access. XXX: need to suppress direct handle
        access */
@@ -387,6 +456,10 @@ static int rtp_open(URLContext *h, const char *uri, int flags)
 
     h->max_packet_size = s->rtp_hd->max_packet_size;
     h->is_streamed = 1;
+
+    av_free(fec_protocol);
+    av_dict_free(&fec_opts);
+
     return 0;
 
  fail:
@@ -394,6 +467,9 @@ static int rtp_open(URLContext *h, const char *uri, int flags)
         ffurl_close(s->rtp_hd);
     if (s->rtcp_hd)
         ffurl_close(s->rtcp_hd);
+    ffurl_closep(&s->fec_hd);
+    av_free(fec_protocol);
+    av_dict_free(&fec_opts);
     return AVERROR(EIO);
 }
 
@@ -441,7 +517,7 @@ static int rtp_read(URLContext *h, uint8_t *buf, int size)
 static int rtp_write(URLContext *h, const uint8_t *buf, int size)
 {
     RTPContext *s = h->priv_data;
-    int ret;
+    int ret, ret_fec;
     URLContext *hd;
 
     if (size < 2)
@@ -510,7 +586,17 @@ static int rtp_write(URLContext *h, const uint8_t *buf, int size)
         hd = s->rtp_hd;
     }
 
-    ret = ffurl_write(hd, buf, size);
+    if ((ret = ffurl_write(hd, buf, size)) < 0) {
+        return ret;
+    }
+
+    if (s->fec_hd && !RTP_PT_IS_RTCP(buf[1])) {
+        if ((ret_fec = ffurl_write(s->fec_hd, buf, size)) < 0) {
+            av_log(h, AV_LOG_ERROR, "Failed to send FEC\n");
+            return ret_fec;
+        }
+    }
+
     return ret;
 }
 
@@ -520,14 +606,15 @@ static int rtp_close(URLContext *h)
     int i;
 
     for (i = 0; i < s->nb_ssm_include_addrs; i++)
-        av_free(s->ssm_include_addrs[i]);
+        av_freep(&s->ssm_include_addrs[i]);
     av_freep(&s->ssm_include_addrs);
     for (i = 0; i < s->nb_ssm_exclude_addrs; i++)
-        av_free(s->ssm_exclude_addrs[i]);
+        av_freep(&s->ssm_exclude_addrs[i]);
     av_freep(&s->ssm_exclude_addrs);
 
     ffurl_close(s->rtp_hd);
     ffurl_close(s->rtcp_hd);
+    ffurl_closep(&s->fec_hd);
     return 0;
 }