rtspsrc: check port-range format
[platform/upstream/gst-plugins-good.git] / gst / rtsp / gstrtspsrc.c
index 156ea27..9f806ec 100644 (file)
@@ -231,6 +231,8 @@ gst_rtsp_src_ntp_time_source_get_type (void)
 #define DEFAULT_TLS_INTERACTION     NULL
 #define DEFAULT_DO_RETRANSMISSION        TRUE
 #define DEFAULT_NTP_TIME_SOURCE  NTP_TIME_SOURCE_NTP
+#define DEFAULT_USER_AGENT       "GStreamer/" PACKAGE_VERSION
+#define DEFAULT_MAX_RTCP_RTP_TIME_DIFF 1000
 
 enum
 {
@@ -267,7 +269,9 @@ enum
   PROP_TLS_DATABASE,
   PROP_TLS_INTERACTION,
   PROP_DO_RETRANSMISSION,
-  PROP_NTP_TIME_SOURCE
+  PROP_NTP_TIME_SOURCE,
+  PROP_USER_AGENT,
+  PROP_MAX_RTCP_RTP_TIME_DIFF
 };
 
 #define GST_TYPE_RTSP_NAT_METHOD (gst_rtsp_nat_method_get_type())
@@ -719,6 +723,25 @@ gst_rtspsrc_class_init (GstRTSPSrcClass * klass)
           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
   /**
+   * GstRTSPSrc::user-agent:
+   *
+   * The string to set in the User-Agent header.
+   *
+   * Since: 1.6
+   */
+  g_object_class_install_property (gobject_class, PROP_USER_AGENT,
+      g_param_spec_string ("user-agent", "User Agent",
+          "The User-Agent string to send to the server",
+          DEFAULT_USER_AGENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (gobject_class, PROP_MAX_RTCP_RTP_TIME_DIFF,
+      g_param_spec_int ("max-rtcp-rtp-time-diff", "Max RTCP RTP Time Diff",
+          "Maximum amount of time in ms that the RTP time in RTCP SRs "
+          "is allowed to be ahead (-1 disabled)", -1, G_MAXINT,
+          DEFAULT_MAX_RTCP_RTP_TIME_DIFF,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  /**
    * GstRTSPSrc::handle-request:
    * @rtspsrc: a #GstRTSPSrc
    * @request: a #GstRTSPMessage
@@ -864,6 +887,8 @@ gst_rtspsrc_init (GstRTSPSrc * src)
   src->tls_interaction = DEFAULT_TLS_INTERACTION;
   src->do_retransmission = DEFAULT_DO_RETRANSMISSION;
   src->ntp_time_source = DEFAULT_NTP_TIME_SOURCE;
+  src->user_agent = g_strdup (DEFAULT_USER_AGENT);
+  src->max_rtcp_rtp_time_diff = DEFAULT_MAX_RTCP_RTP_TIME_DIFF;
 
   /* get a list of all extensions */
   src->extensions = gst_rtsp_ext_list_get ();
@@ -898,6 +923,7 @@ gst_rtspsrc_finalize (GObject * object)
   g_free (rtspsrc->user_id);
   g_free (rtspsrc->user_pw);
   g_free (rtspsrc->multi_iface);
+  g_free (rtspsrc->user_agent);
 
   if (rtspsrc->sdp) {
     gst_sdp_message_free (rtspsrc->sdp);
@@ -1056,26 +1082,22 @@ gst_rtspsrc_set_property (GObject * object, guint prop_id, const GValue * value,
       gst_rtspsrc_set_proxy (rtspsrc, g_value_get_string (value));
       break;
     case PROP_PROXY_ID:
-      if (rtspsrc->prop_proxy_id)
-        g_free (rtspsrc->prop_proxy_id);
+      g_free (rtspsrc->prop_proxy_id);
       rtspsrc->prop_proxy_id = g_value_dup_string (value);
       break;
     case PROP_PROXY_PW:
-      if (rtspsrc->prop_proxy_pw)
-        g_free (rtspsrc->prop_proxy_pw);
+      g_free (rtspsrc->prop_proxy_pw);
       rtspsrc->prop_proxy_pw = g_value_dup_string (value);
       break;
     case PROP_RTP_BLOCKSIZE:
       rtspsrc->rtp_blocksize = g_value_get_uint (value);
       break;
     case PROP_USER_ID:
-      if (rtspsrc->user_id)
-        g_free (rtspsrc->user_id);
+      g_free (rtspsrc->user_id);
       rtspsrc->user_id = g_value_dup_string (value);
       break;
     case PROP_USER_PW:
-      if (rtspsrc->user_pw)
-        g_free (rtspsrc->user_pw);
+      g_free (rtspsrc->user_pw);
       rtspsrc->user_pw = g_value_dup_string (value);
       break;
     case PROP_BUFFER_MODE:
@@ -1086,10 +1108,8 @@ gst_rtspsrc_set_property (GObject * object, guint prop_id, const GValue * value,
       const gchar *str;
 
       str = g_value_get_string (value);
-      if (str) {
-        sscanf (str, "%u-%u",
-            &rtspsrc->client_port_range.min, &rtspsrc->client_port_range.max);
-      } else {
+      if (sscanf (str, "%u-%u", &rtspsrc->client_port_range.min,
+              &rtspsrc->client_port_range.max) != 2) {
         rtspsrc->client_port_range.min = 0;
         rtspsrc->client_port_range.max = 0;
       }
@@ -1141,6 +1161,13 @@ gst_rtspsrc_set_property (GObject * object, guint prop_id, const GValue * value,
     case PROP_NTP_TIME_SOURCE:
       rtspsrc->ntp_time_source = g_value_get_enum (value);
       break;
+    case PROP_USER_AGENT:
+      g_free (rtspsrc->user_agent);
+      rtspsrc->user_agent = g_value_dup_string (value);
+      break;
+    case PROP_MAX_RTCP_RTP_TIME_DIFF:
+      rtspsrc->max_rtcp_rtp_time_diff = g_value_get_int (value);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -1281,6 +1308,12 @@ gst_rtspsrc_get_property (GObject * object, guint prop_id, GValue * value,
     case PROP_NTP_TIME_SOURCE:
       g_value_set_enum (value, rtspsrc->ntp_time_source);
       break;
+    case PROP_USER_AGENT:
+      g_value_set_string (value, rtspsrc->user_agent);
+      break;
+    case PROP_MAX_RTCP_RTP_TIME_DIFF:
+      g_value_set_int (value, rtspsrc->max_rtcp_rtp_time_diff);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -1460,6 +1493,7 @@ gst_rtspsrc_collect_payloads (GstRTSPSrc * src, const GstSDPMessage * sdp,
 {
   guint i, len;
   const gchar *proto;
+  GstCaps *global_caps;
 
   /* get proto */
   proto = gst_sdp_media_get_proto (media);
@@ -1477,10 +1511,17 @@ gst_rtspsrc_collect_payloads (GstRTSPSrc * src, const GstSDPMessage * sdp,
   else
     goto unknown_proto;
 
+  /* Parse global SDP attributes once */
+  global_caps = gst_caps_new_empty_simple ("application/x-unknown");
+  GST_DEBUG ("mapping sdp session level attributes to caps");
+  gst_rtspsrc_sdp_attributes_to_caps (sdp->attributes, global_caps);
+  GST_DEBUG ("mapping sdp media level attributes to caps");
+  gst_rtspsrc_sdp_attributes_to_caps (media->attributes, global_caps);
+
   len = gst_sdp_media_formats_len (media);
   for (i = 0; i < len; i++) {
     gint pt;
-    GstCaps *caps;
+    GstCaps *caps, *outcaps;
     GstStructure *s;
     const gchar *enc;
     PtMapItem item;
@@ -1503,19 +1544,23 @@ gst_rtspsrc_collect_payloads (GstRTSPSrc * src, const GstSDPMessage * sdp,
       if (strcmp (enc, "X-ASF-PF") == 0)
         stream->container = TRUE;
     }
-    GST_DEBUG ("mapping sdp session level attributes to caps");
-    gst_rtspsrc_sdp_attributes_to_caps (sdp->attributes, caps);
-    GST_DEBUG ("mapping sdp media level attributes to caps");
-    gst_rtspsrc_sdp_attributes_to_caps (media->attributes, caps);
+
+    /* Merge in global caps */
+    /* Intersect will merge in missing fields to the current caps */
+    outcaps = gst_caps_intersect (caps, global_caps);
+    gst_caps_unref (caps);
 
     /* the first pt will be the default */
     if (stream->ptmap->len == 0)
       stream->default_pt = pt;
 
     item.pt = pt;
-    item.caps = caps;
+    item.caps = outcaps;
+
     g_array_append_val (stream->ptmap, item);
   }
+
+  gst_caps_unref (global_caps);
   return;
 
 no_proto:
@@ -1835,7 +1880,6 @@ static gboolean
 parse_keymgmt (const gchar * keymgmt, GstCaps * caps)
 {
   gboolean res = FALSE;
-  gchar *p, *kmpid;
   gsize size;
   guchar *data;
   GstMIKEYMessage *msg;
@@ -1843,17 +1887,28 @@ parse_keymgmt (const gchar * keymgmt, GstCaps * caps)
   const gchar *srtp_cipher;
   const gchar *srtp_auth;
 
-  p = (gchar *) keymgmt;
+  {
+    gchar *orig_value;
+    gchar *p, *kmpid;
 
-  SKIP_SPACES (p);
-  if (*p == '\0')
-    return FALSE;
+    p = orig_value = g_strdup (keymgmt);
 
-  PARSE_STRING (p, " ", kmpid);
-  if (!g_str_equal (kmpid, "mikey"))
-    return FALSE;
+    SKIP_SPACES (p);
+    if (*p == '\0') {
+      g_free (orig_value);
+      return FALSE;
+    }
+
+    PARSE_STRING (p, " ", kmpid);
+    if (kmpid == NULL || !g_str_equal (kmpid, "mikey")) {
+      g_free (orig_value);
+      return FALSE;
+    }
+    data = g_base64_decode (p, &size);
+
+    g_free (orig_value);        /* Don't need this any more */
+  }
 
-  data = g_base64_decode (p, &size);
   if (data == NULL)
     return FALSE;
 
@@ -1961,6 +2016,7 @@ parse_keymgmt (const gchar * keymgmt, GstCaps * caps)
         gst_buffer_new_wrapped (g_memdup (pkd->key_data, pkd->key_len),
         pkd->key_len);
     gst_caps_set_simple (caps, "srtp-key", GST_TYPE_BUFFER, buf, NULL);
+    gst_buffer_unref (buf);
   }
 
   gst_caps_set_simple (caps,
@@ -2152,6 +2208,11 @@ gst_rtspsrc_media_to_caps (gint pt, const GstSDPMedia * media)
       for (i = 0; pairs[i]; i++) {
         gchar *valpos;
         const gchar *val, *key;
+        gint j;
+        const gchar *reserved_keys[] =
+            { "media", "payload", "clock-rate", "encoding-name",
+          "encoding-params"
+        };
 
         /* the key may not have a '=', the value can have other '='s */
         valpos = strstr (pairs[i], "=");
@@ -2170,6 +2231,19 @@ gst_rtspsrc_media_to_caps (gint pt, const GstSDPMedia * media)
         }
         /* strip the key of spaces, convert key to lowercase but not the value. */
         key = g_strstrip (pairs[i]);
+
+        /* skip keys from the fmtp, which we already use ourselves for the
+         * caps. Some software is adding random things like clock-rate into
+         * the fmtp, and we would otherwise here set a string-typed clock-rate
+         * in the caps... and thus fail to create valid RTP caps
+         */
+        for (j = 0; j < G_N_ELEMENTS (reserved_keys); j++) {
+          if (g_ascii_strcasecmp (reserved_keys[j], key) == 0) {
+            key = "";
+            break;
+          }
+        }
+
         if (strlen (key) > 1) {
           tmp = g_ascii_strdown (key, -1);
           gst_structure_set (s, tmp, G_TYPE_STRING, val, NULL);
@@ -3429,6 +3503,11 @@ gst_rtspsrc_stream_configure_manager (GstRTSPSrc * src, GstRTSPStream * stream,
             NULL);
       }
 
+      if (g_object_class_find_property (klass, "max-rtcp-rtp-time-diff")) {
+        g_object_set (src->manager, "max-rtcp-rtp-time-diff",
+            src->max_rtcp_rtp_time_diff, NULL);
+      }
+
       /* buffer mode pauses are handled by adding offsets to buffer times,
        * but some depayloaders may have a hard time syncing output times
        * with such input times, e.g. container ones, most notably ASF */
@@ -4425,8 +4504,7 @@ gst_rtsp_conninfo_connect (GstRTSPSrc * src, GstRTSPConnInfo * info,
     if ((res = gst_rtsp_connection_create (info->url, &info->connection)) < 0)
       goto could_not_create;
 
-    if (info->url_str)
-      g_free (info->url_str);
+    g_free (info->url_str);
     info->url_str = gst_rtsp_url_get_request_uri (info->url);
 
     GST_DEBUG_OBJECT (src, "sanitized uri %s", info->url_str);
@@ -4549,6 +4627,23 @@ gst_rtspsrc_connection_flush (GstRTSPSrc * src, gboolean flush)
   GST_RTSP_STATE_UNLOCK (src);
 }
 
+static GstRTSPResult
+gst_rtspsrc_init_request (GstRTSPSrc * src, GstRTSPMessage * msg,
+    GstRTSPMethod method, const gchar * uri)
+{
+  GstRTSPResult res;
+
+  res = gst_rtsp_message_init_request (msg, method, uri);
+  if (res < 0)
+    return res;
+
+  /* set user-agent */
+  if (src->user_agent)
+    gst_rtsp_message_add_header (msg, GST_RTSP_HDR_USER_AGENT, src->user_agent);
+
+  return res;
+}
+
 /* FIXME, handle server request, reply with OK, for now */
 static GstRTSPResult
 gst_rtspsrc_handle_request (GstRTSPSrc * src, GstRTSPConnection * conn,
@@ -4625,7 +4720,7 @@ gst_rtspsrc_send_keep_alive (GstRTSPSrc * src)
   if (control == NULL)
     goto no_control;
 
-  res = gst_rtsp_message_init_request (&request, method, control);
+  res = gst_rtspsrc_init_request (src, &request, method, control);
   if (res < 0)
     goto send_error;
 
@@ -5882,6 +5977,7 @@ gst_rtspsrc_send (GstRTSPSrc * src, GstRTSPConnection * conn,
 
     switch (int_code) {
       case GST_RTSP_STS_UNAUTHORIZED:
+      case GST_RTSP_STS_NOT_FOUND:
         if (gst_rtspsrc_setup_auth (src, response)) {
           /* Try the request/response again after configuring the auth info
            * and loop again */
@@ -6277,10 +6373,11 @@ default_srtcp_params (void)
   GstBuffer *buf;
   guint8 *key_data;
 #define KEY_SIZE 30
+  guint data_size = GST_ROUND_UP_4 (KEY_SIZE);
 
   /* create a random key */
-  key_data = g_malloc (KEY_SIZE);
-  for (i = 0; i < KEY_SIZE; i += 4)
+  key_data = g_malloc (data_size);
+  for (i = 0; i < data_size; i += 4)
     GST_WRITE_UINT32_BE (key_data + i, g_random_int ());
 
   buf = gst_buffer_new_wrapped (key_data, KEY_SIZE);
@@ -6562,7 +6659,7 @@ gst_rtspsrc_setup_streams (GstRTSPSrc * src, gboolean async)
 
     /* create SETUP request */
     res =
-        gst_rtsp_message_init_request (&request, GST_RTSP_SETUP,
+        gst_rtspsrc_init_request (src, &request, GST_RTSP_SETUP,
         stream->conninfo.location);
     if (res < 0) {
       g_free (transports);
@@ -7099,7 +7196,7 @@ restart:
   /* create OPTIONS */
   GST_DEBUG_OBJECT (src, "create options...");
   res =
-      gst_rtsp_message_init_request (&request, GST_RTSP_OPTIONS,
+      gst_rtspsrc_init_request (src, &request, GST_RTSP_OPTIONS,
       src->conninfo.url_str);
   if (res < 0)
     goto create_request_failed;
@@ -7122,7 +7219,7 @@ restart:
   /* create DESCRIBE */
   GST_DEBUG_OBJECT (src, "create describe...");
   res =
-      gst_rtsp_message_init_request (&request, GST_RTSP_DESCRIBE,
+      gst_rtspsrc_init_request (src, &request, GST_RTSP_DESCRIBE,
       src->conninfo.url_str);
   if (res < 0)
     goto create_request_failed;
@@ -7156,7 +7253,7 @@ restart:
   }
 
   /* it could be that the DESCRIBE method was not implemented */
-  if (!src->methods & GST_RTSP_DESCRIBE)
+  if (!(src->methods & GST_RTSP_DESCRIBE))
     goto no_describe;
 
   /* check if reply is SDP */
@@ -7338,7 +7435,7 @@ gst_rtspsrc_close (GstRTSPSrc * src, gboolean async, gboolean only_close)
 
     /* do TEARDOWN */
     res =
-        gst_rtsp_message_init_request (&request, GST_RTSP_TEARDOWN, setup_url);
+        gst_rtspsrc_init_request (src, &request, GST_RTSP_TEARDOWN, setup_url);
     if (res < 0)
       goto create_request_failed;
 
@@ -7652,7 +7749,7 @@ gst_rtspsrc_play (GstRTSPSrc * src, GstSegment * segment, gboolean async)
     }
 
     /* do play */
-    res = gst_rtsp_message_init_request (&request, GST_RTSP_PLAY, setup_url);
+    res = gst_rtspsrc_init_request (src, &request, GST_RTSP_PLAY, setup_url);
     if (res < 0)
       goto create_request_failed;
 
@@ -7866,7 +7963,7 @@ gst_rtspsrc_pause (GstRTSPSrc * src, gboolean async)
           ("Sending PAUSE request"));
 
     if ((res =
-            gst_rtsp_message_init_request (&request, GST_RTSP_PAUSE,
+            gst_rtspsrc_init_request (src, &request, GST_RTSP_PAUSE,
                 setup_url)) < 0)
       goto create_request_failed;