diff options
Diffstat (limited to 'gst')
| -rw-r--r-- | gst/rtsp/COPYING.MIT | 23 | ||||
| -rw-r--r-- | gst/rtsp/gstrtspsrc.c | 625 | ||||
| -rw-r--r-- | gst/rtsp/gstrtspsrc.h | 6 | ||||
| -rw-r--r-- | gst/rtsp/rtspconnection.c | 43 | ||||
| -rw-r--r-- | gst/rtsp/rtspdefs.c | 35 | ||||
| -rw-r--r-- | gst/rtsp/rtspdefs.h | 3 | ||||
| -rw-r--r-- | gst/rtsp/rtspextwms.c | 6 | ||||
| -rw-r--r-- | gst/rtsp/rtspmessage.c | 116 | ||||
| -rw-r--r-- | gst/rtsp/rtspmessage.h | 58 | ||||
| -rw-r--r-- | gst/rtsp/sdpmessage.c | 28 | 
10 files changed, 644 insertions, 299 deletions
| diff --git a/gst/rtsp/COPYING.MIT b/gst/rtsp/COPYING.MIT new file mode 100644 index 00000000..7e8010b4 --- /dev/null +++ b/gst/rtsp/COPYING.MIT @@ -0,0 +1,23 @@ +/* + * Unless otherwise indicated, Source Code is licensed under MIT license. + * See further explanation attached in License Statement (distributed in the file + * LICENSE). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ diff --git a/gst/rtsp/gstrtspsrc.c b/gst/rtsp/gstrtspsrc.c index 6af4c68b..1ec70b88 100644 --- a/gst/rtsp/gstrtspsrc.c +++ b/gst/rtsp/gstrtspsrc.c @@ -95,9 +95,8 @@  #include "gstrtspsrc.h"  #include "sdp.h" -#if 0 -#define WITH_EXT_REAL -#endif +/* define for experimental real support */ +#undef WITH_EXT_REAL  #include "rtspextwms.h"  #ifdef WITH_EXT_REAL @@ -165,6 +164,11 @@ gst_rtsp_lower_trans_get_type (void)  static void gst_rtspsrc_base_init (gpointer g_class);  static void gst_rtspsrc_finalize (GObject * object); +static void gst_rtspsrc_set_property (GObject * object, guint prop_id, +    const GValue * value, GParamSpec * pspec); +static void gst_rtspsrc_get_property (GObject * object, guint prop_id, +    GValue * value, GParamSpec * pspec); +  static void gst_rtspsrc_uri_handler_init (gpointer g_iface,      gpointer iface_data);  static GstCaps *gst_rtspsrc_media_to_caps (gint pt, SDPMedia * media); @@ -173,11 +177,6 @@ static GstStateChangeReturn gst_rtspsrc_change_state (GstElement * element,      GstStateChange transition);  static void gst_rtspsrc_handle_message (GstBin * bin, GstMessage * message); -static void gst_rtspsrc_set_property (GObject * object, guint prop_id, -    const GValue * value, GParamSpec * pspec); -static void gst_rtspsrc_get_property (GObject * object, guint prop_id, -    GValue * value, GParamSpec * pspec); -  static gboolean gst_rtspsrc_open (GstRTSPSrc * src);  static gboolean gst_rtspsrc_play (GstRTSPSrc * src);  static gboolean gst_rtspsrc_pause (GstRTSPSrc * src); @@ -186,6 +185,7 @@ static gboolean gst_rtspsrc_close (GstRTSPSrc * src);  static gboolean gst_rtspsrc_uri_set_uri (GstURIHandler * handler,      const gchar * uri); +static gboolean gst_rtspsrc_activate_streams (GstRTSPSrc * src);  static void gst_rtspsrc_loop (GstRTSPSrc * src);  /* commands we send to out loop to notify it of events */ @@ -397,6 +397,7 @@ gst_rtspsrc_create_stream (GstRTSPSrc * src, SDPMessage * sdp, gint idx)    /* we mark the pad as not linked, we will mark it as OK when we add the pad to     * the element. */    stream->last_ret = GST_FLOW_NOT_LINKED; +  stream->added = FALSE;    stream->id = src->numstreams++;    /* we must have a payload. No payload means we cannot create caps */ @@ -448,21 +449,74 @@ gst_rtspsrc_create_stream (GstRTSPSrc * src, SDPMessage * sdp, gint idx)    return stream;  } -#if 0  static void -gst_rtspsrc_free_stream (GstRTSPSrc * src, GstRTSPStream * stream) +gst_rtspsrc_stream_free (GstRTSPSrc * src, GstRTSPStream * stream)  { -  if (stream->caps) { +  gint i; + +  GST_DEBUG_OBJECT (src, "free stream %p", stream); + +  if (stream->caps)      gst_caps_unref (stream->caps); -  } +    g_free (stream->setup_url); -  src->streams = g_list_remove (src->streams, stream); -  src->numstreams--; +  for (i = 0; i < 2; i++) { +    GstElement *udpsrc = stream->udpsrc[i]; + +    if (udpsrc) { +      GstPad *pad; + +      /* unlink the pad */ +      pad = gst_element_get_pad (udpsrc, "src"); +      if (stream->channelpad[i]) { +        gst_pad_unlink (pad, stream->channelpad[i]); +        gst_object_unref (stream->channelpad[i]); +        stream->channelpad[i] = NULL; +      } +      gst_element_set_state (udpsrc, GST_STATE_NULL); +      gst_bin_remove (GST_BIN_CAST (src), udpsrc); +      gst_object_unref (stream->udpsrc[i]); +      stream->udpsrc[i] = NULL; +    } +  } +  if (stream->sess) { +    gst_element_set_state (stream->sess, GST_STATE_NULL); +    gst_bin_remove (GST_BIN_CAST (src), stream->sess); +    stream->sess = NULL; +  } +  if (stream->srcpad) { +    gst_pad_set_active (stream->srcpad, FALSE); +    if (stream->added) { +      gst_element_remove_pad (GST_ELEMENT_CAST (src), stream->srcpad); +      stream->added = FALSE; +    } +    stream->srcpad = NULL; +  }    g_free (stream);  } -#endif + +static void +gst_rtspsrc_cleanup (GstRTSPSrc * src) +{ +  GList *walk; + +  GST_DEBUG_OBJECT (src, "cleanup"); + +  for (walk = src->streams; walk; walk = g_list_next (walk)) { +    GstRTSPStream *stream = (GstRTSPStream *) walk->data; + +    gst_rtspsrc_stream_free (src, stream); +  } +  g_list_free (src->streams); +  src->streams = NULL; +  src->numstreams = 0; +  if (src->props) +    gst_structure_free (src->props); +  src->props = NULL; +} +  #define PARSE_INT(p, del, res)          \  G_STMT_START {                          \ @@ -724,8 +778,12 @@ again:    /* we keep these elements, we configure all in configure_transport when the     * server told us to really use the UDP ports. */ -  stream->udpsrc[0] = udpsrc0; -  stream->udpsrc[1] = udpsrc1; +  stream->udpsrc[0] = gst_object_ref (udpsrc0); +  stream->udpsrc[1] = gst_object_ref (udpsrc1); + +  /* they are ours now */ +  gst_object_sink (udpsrc0); +  gst_object_sink (udpsrc1);    return TRUE; @@ -780,6 +838,44 @@ cleanup:    }  } +static void +pad_unblocked (GstPad * pad, gboolean blocked, GstRTSPSrc * src) +{ +} + +static void +pad_blocked (GstPad * pad, gboolean blocked, GstRTSPSrc * src) +{ +  GST_DEBUG_OBJECT (src, "pad %s:%s blocked, activating streams", +      GST_DEBUG_PAD_NAME (pad)); + +  gst_pad_set_blocked_async (pad, FALSE, (GstPadBlockCallback) pad_unblocked, +      src); + +  /* activate the streams */ +  GST_OBJECT_LOCK (src); +  if (!src->need_activate) +    goto was_ok; + +  src->need_activate = FALSE; +  GST_OBJECT_UNLOCK (src); + +  gst_rtspsrc_activate_streams (src); + +  return; + +was_ok: +  { +    GST_OBJECT_UNLOCK (src); +    return; +  } +} + +/* sets up all elements needed for streaming over the specified transport. + * Does not yet expose the element pads, this will be done when there is actuall + * dataflow detected, which might never happen when UDP is blocked in a + * firewall, for example. + */  static gboolean  gst_rtspsrc_stream_configure_transport (GstRTSPStream * stream,      RTSPTransport * transport) @@ -799,15 +895,17 @@ gst_rtspsrc_stream_configure_transport (GstRTSPStream * stream,    s = gst_caps_get_structure (stream->caps, 0); +  /* get the proper mime type for this stream now */    if ((res = rtsp_transport_get_mime (transport->trans, &mime)) < 0)      goto no_mime;    if (!mime)      goto no_mime; -  GST_DEBUG_OBJECT (src, "setting mime to %s", mime);    /* configure the final mime type */ +  GST_DEBUG_OBJECT (src, "setting mime to %s", mime);    gst_structure_set_name (s, mime); +  /* find a manager */    if ((res = rtsp_transport_get_manager (transport->trans, &manager)) < 0)      goto no_manager; @@ -824,6 +922,8 @@ gst_rtspsrc_stream_configure_transport (GstRTSPStream * stream,      if (ret != GST_STATE_CHANGE_SUCCESS)        goto start_session_failure; +    /* we stream directly to the manager, FIXME, pad names should not be +     * hardcoded. */      stream->channelpad[0] = gst_element_get_pad (stream->sess, "sinkrtp");      stream->channelpad[1] = gst_element_get_pad (stream->sess, "sinkrtcp");    } @@ -847,22 +947,24 @@ gst_rtspsrc_stream_configure_transport (GstRTSPStream * stream,          stream->udpsrc[i] = NULL;        }      } +      /* no session manager, send data to srcpad directly */      if (!stream->channelpad[0]) {        GST_DEBUG_OBJECT (src, "no manager, creating pad"); +      /* create a new pad we will use to stream to */        name = g_strdup_printf ("stream%d", stream->id);        template = gst_static_pad_template_get (&rtptemplate); -      stream->srcpad = gst_pad_new_from_template (template, name); +      stream->channelpad[0] = gst_pad_new_from_template (template, name);        gst_object_unref (template);        g_free (name); -      gst_pad_use_fixed_caps (stream->srcpad); -      gst_pad_set_caps (stream->srcpad, stream->caps); +      /* set caps and activate */ +      gst_pad_use_fixed_caps (stream->channelpad[0]); +      gst_pad_set_caps (stream->channelpad[0], stream->caps); +      gst_pad_set_active (stream->channelpad[0], TRUE); -      stream->channelpad[0] = gst_object_ref (stream->srcpad); -      gst_pad_set_active (stream->srcpad, TRUE); -      gst_element_add_pad (GST_ELEMENT_CAST (src), stream->srcpad); +      outpad = gst_object_ref (stream->channelpad[0]);      } else {        GST_DEBUG_OBJECT (src, "using manager source pad");        outpad = gst_element_get_pad (stream->sess, "srcrtp"); @@ -884,6 +986,10 @@ gst_rtspsrc_stream_configure_transport (GstRTSPStream * stream,          if (stream->udpsrc[0] == NULL)            goto no_element; +        /* take ownership */ +        gst_object_ref (stream->udpsrc[0]); +        gst_object_sink (stream->udpsrc[0]); +          /* change state */          gst_element_set_state (stream->udpsrc[0], GST_STATE_PAUSED);        } @@ -897,6 +1003,10 @@ gst_rtspsrc_stream_configure_transport (GstRTSPStream * stream,          if (stream->udpsrc[1] == NULL)            goto no_element; +        /* take ownership */ +        gst_object_ref (stream->udpsrc[0]); +        gst_object_sink (stream->udpsrc[0]); +          gst_element_set_state (stream->udpsrc[1], GST_STATE_PAUSED);        }      } @@ -904,8 +1014,6 @@ gst_rtspsrc_stream_configure_transport (GstRTSPStream * stream,      /* we manage the UDP elements now. For unicast, the UDP sources where       * allocated in the stream when we suggested a transport. */      if (stream->udpsrc[0]) { -      GstPad *pad; -        gst_bin_add (GST_BIN_CAST (src), stream->udpsrc[0]);        GST_DEBUG_OBJECT (src, "setting up UDP source"); @@ -913,22 +1021,31 @@ gst_rtspsrc_stream_configure_transport (GstRTSPStream * stream,        /* set caps */        g_object_set (G_OBJECT (stream->udpsrc[0]), "caps", stream->caps, NULL); -      /* configure a timeout on the UDP port */ +      /* configure a timeout on the UDP port. When the timeout message is +       * posted, we assume UDP transport is not possible. We reconnect using TCP +       * if we can. */        g_object_set (G_OBJECT (stream->udpsrc[0]), "timeout", src->timeout,            NULL); +      /* get output pad of the UDP source. */ +      outpad = gst_element_get_pad (stream->udpsrc[0], "src"); + +      /* configure pad block on the pad. As soon as there is dataflow on the +       * UDP source, we know that UDP is not blocked by a firewall and we can +       * configure all the streams to let the application autoplug decoders. */ +      gst_pad_set_blocked_async (outpad, TRUE, +          (GstPadBlockCallback) pad_blocked, src); +        if (stream->channelpad[0]) {          GST_DEBUG_OBJECT (src, "connecting UDP source 0 to manager");          /* configure for UDP delivery, we need to connect the UDP pads to           * the session plugin. */ -        pad = gst_element_get_pad (stream->udpsrc[0], "src"); -        gst_pad_link (pad, stream->channelpad[0]); -        gst_object_unref (pad); - +        gst_pad_link (outpad, stream->channelpad[0]); +        gst_object_unref (outpad); +        /* the real output pad is that of the session manager */          outpad = gst_element_get_pad (stream->sess, "srcrtp");        } else {          GST_DEBUG_OBJECT (src, "using UDP src pad as output"); -        outpad = gst_element_get_pad (stream->udpsrc[0], "src");        }      } @@ -947,13 +1064,14 @@ gst_rtspsrc_stream_configure_transport (GstRTSPStream * stream,      }    } -  if (outpad && !stream->srcpad) { +  if (outpad) {      GST_DEBUG_OBJECT (src, "creating ghostpad");      gst_pad_use_fixed_caps (outpad);      gst_pad_set_caps (outpad, stream->caps); -    /* create ghostpad */ +    /* create ghostpad, don't add just yet, this will be done when we activate +     * the stream. */      name = g_strdup_printf ("stream%d", stream->id);      template = gst_static_pad_template_get (&rtptemplate);      stream->srcpad = gst_ghost_pad_new_from_template (name, outpad, template); @@ -961,10 +1079,6 @@ gst_rtspsrc_stream_configure_transport (GstRTSPStream * stream,      g_free (name);      gst_object_unref (outpad); - -    /* and add */ -    gst_pad_set_active (stream->srcpad, TRUE); -    gst_element_add_pad (GST_ELEMENT_CAST (src), stream->srcpad);    }    /* mark pad as ok */    stream->last_ret = GST_FLOW_OK; @@ -994,6 +1108,35 @@ start_session_failure:    }  } +/* Adds the source pads of all configured streams to the element. + * This code is performed when we detected dataflow. + * + * We detect dataflow from either the _loop function or with pad probes on the + * udp sources. + */ +static gboolean +gst_rtspsrc_activate_streams (GstRTSPSrc * src) +{ +  GList *walk; + +  for (walk = src->streams; walk; walk = g_list_next (walk)) { +    GstRTSPStream *stream = (GstRTSPStream *) walk->data; + +    gst_pad_set_active (stream->srcpad, TRUE); +    /* add the pad */ +    if (!stream->added) { +      gst_element_add_pad (GST_ELEMENT_CAST (src), stream->srcpad); +      stream->added = TRUE; +    } +  } + +  /* if we got here all was configured. We have dynamic pads so we notify that +   * we are done */ +  gst_element_no_more_pads (GST_ELEMENT_CAST (src)); + +  return TRUE; +} +  static gint  find_stream_by_channel (GstRTSPStream * stream, gconstpointer a)  { @@ -1144,6 +1287,11 @@ gst_rtspsrc_loop_interleaved (GstRTSPSrc * src)    GST_DEBUG_OBJECT (src, "pushing data of size %d on channel %d", size,        channel); +  if (src->need_activate) { +    gst_rtspsrc_activate_streams (src); +    src->need_activate = FALSE; +  } +    /* chain to the peer pad */    if (GST_PAD_IS_SINK (outpad))      ret = gst_pad_chain (outpad, buf); @@ -1239,49 +1387,45 @@ gst_rtspsrc_loop_udp (GstRTSPSrc * src)    }    GST_OBJECT_UNLOCK (src); -  if (restart) { -    gst_rtspsrc_pause (src); +  /* no need to restart, we're done */ +  if (!restart) +    goto done; -    if (src->task) { -      /* stop task, we cannot join as this would deadlock */ -      gst_task_stop (src->task); -      /* and free the task so that _close will not stop/join it again. */ -      gst_object_unref (GST_OBJECT (src->task)); -      src->task = NULL; -    } -    gst_rtspsrc_close (src); - -    /* see if we have TCP left to try */ -    if (src->cur_protocols & RTSP_LOWER_TRANS_TCP) { -      gchar *url, *pos; - -      /* We post a warning message now to inform the user -       * that nothing happened. It's most likely a firewall thing. */ -      GST_ELEMENT_WARNING (src, RESOURCE, READ, (NULL), -          ("Could not receive any UDP packets for %.4f seconds, maybe your " -              "firewall is blocking it. Retrying using a TCP connection.", -              (gdouble) src->timeout / 1000000)); -      /* we can try only TCP now */ -      src->cur_protocols = RTSP_LOWER_TRANS_TCP; - -      pos = strstr (src->location, "://"); -      if (!pos) -        goto weird_url; - -      url = g_strdup_printf ("rtspt://%s", pos + 3); - -      gst_element_post_message (GST_ELEMENT_CAST (src), -          gst_message_new_element (GST_OBJECT_CAST (src), -              gst_structure_new ("redirect", -                  "new-location", G_TYPE_STRING, url, NULL))); -      g_free (url); -    } else { -      src->cur_protocols = 0; -      /* no transport possible, post an error and stop */ -      GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL), -          ("Could not connect to server, no protocols left")); -    } +  /* We post a warning message now to inform the user +   * that nothing happened. It's most likely a firewall thing. */ +  GST_ELEMENT_WARNING (src, RESOURCE, READ, (NULL), +      ("Could not receive any UDP packets for %.4f seconds, maybe your " +          "firewall is blocking it. Retrying using a TCP connection.", +          (gdouble) src->timeout / 1000000)); +  /* we can try only TCP now */ +  src->cur_protocols = RTSP_LOWER_TRANS_TCP; + +  /* pause to prepare for a restart */ +  gst_rtspsrc_pause (src); + +  if (src->task) { +    /* stop task, we cannot join as this would deadlock */ +    gst_task_stop (src->task); +    /* and free the task so that _close will not stop/join it again. */ +    gst_object_unref (GST_OBJECT (src->task)); +    src->task = NULL;    } +  /* close and cleanup our state */ +  gst_rtspsrc_close (src); + +  /* see if we have TCP left to try */ +  if (!(src->cur_protocols & RTSP_LOWER_TRANS_TCP)) +    goto no_protocols; + +  /* open new connection using tcp */ +  if (!gst_rtspsrc_open (src)) +    goto open_failed; + +  /* start playback */ +  if (!gst_rtspsrc_play (src)) +    goto play_failed; + +done:    return;    /* ERRORS */ @@ -1292,10 +1436,22 @@ stopping:      gst_task_pause (src->task);      return;    } -weird_url: +no_protocols:    { +    src->cur_protocols = 0; +    /* no transport possible, post an error and stop */      GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL), -        ("Could not redirect, location %s is invalid", src->location)); +        ("Could not connect to server, no protocols left")); +    return; +  } +open_failed: +  { +    GST_DEBUG_OBJECT (src, "open failed"); +    return; +  } +play_failed: +  { +    GST_DEBUG_OBJECT (src, "play failed");      return;    }  } @@ -1414,10 +1570,8 @@ next:    /* store new content base if any */    rtsp_message_get_header (response, RTSP_HDR_CONTENT_BASE, &content_base); -  if (content_base) { -    g_free (src->content_base); -    src->content_base = g_strdup (content_base); -  } +  g_free (src->content_base); +  src->content_base = g_strdup (content_base);    if (src->extension && src->extension->after_send)      src->extension->after_send (src->extension, request, response); @@ -1509,7 +1663,7 @@ gst_rtspsrc_parse_methods (GstRTSPSrc * src, RTSPMessage * response)      method = rtsp_find_method (stripped);      /* keep bitfield of supported methods */ -    if (method != -1) +    if (method != RTSP_INVALID)        src->methods |= method;    }    g_strfreev (options); @@ -1556,6 +1710,7 @@ gst_rtspsrc_create_transports_string (GstRTSPSrc * src,    if (*transports != NULL)      return RTSP_OK; +  /* the default RTSP transports */    result = g_strdup ("");    if (protocols & RTSP_LOWER_TRANS_UDP) {      gchar *new; @@ -1602,7 +1757,7 @@ failed:  }  static RTSPResult -gst_rtspsrc_configure_transports (GstRTSPStream * stream, gchar ** transports) +gst_rtspsrc_prepare_transports (GstRTSPStream * stream, gchar ** transports)  {    GstRTSPSrc *src;    gint nr_udp, nr_int; @@ -1669,111 +1824,33 @@ failed:  }  static gboolean -gst_rtspsrc_open (GstRTSPSrc * src) +gst_rtspsrc_setup_streams (GstRTSPSrc * src)  { +  GList *walk;    RTSPResult res;    RTSPMessage request = { 0 };    RTSPMessage response = { 0 }; -  guint8 *data; -  guint size; -  gint i, n_streams; -  SDPMessage sdp = { 0 }; -  RTSPLowerTrans protocols;    GstRTSPStream *stream = NULL; -  gchar *respcont = NULL; - -  /* reset our state */ -  src->free_channel = 0; -  src->interleaved = FALSE; -  gst_segment_init (&src->segment, GST_FORMAT_TIME); - -  /* can't continue without a valid url */ -  if (G_UNLIKELY (src->url == NULL)) -    goto no_url; - -  /* create connection */ -  GST_DEBUG_OBJECT (src, "creating connection (%s)...", src->location); -  if ((res = rtsp_connection_create (src->url, &src->connection)) < 0) -    goto could_not_create; - -  /* connect */ -  GST_DEBUG_OBJECT (src, "connecting (%s)...", src->location); -  if ((res = rtsp_connection_connect (src->connection)) < 0) -    goto could_not_connect; - -  /* create OPTIONS */ -  GST_DEBUG_OBJECT (src, "create options..."); -  res = rtsp_message_init_request (&request, RTSP_OPTIONS, src->location); -  if (res < 0) -    goto create_request_failed; - -  /* send OPTIONS */ -  GST_DEBUG_OBJECT (src, "send options..."); -  if (!gst_rtspsrc_send (src, &request, &response, NULL)) -    goto send_error; - -  /* parse OPTIONS */ -  if (!gst_rtspsrc_parse_methods (src, &response)) -    goto methods_error; - -  /* create DESCRIBE */ -  GST_DEBUG_OBJECT (src, "create describe..."); -  res = rtsp_message_init_request (&request, RTSP_DESCRIBE, src->location); -  if (res < 0) -    goto create_request_failed; - -  /* we only accept SDP for now */ -  rtsp_message_add_header (&request, RTSP_HDR_ACCEPT, "application/sdp"); - -  /* prepare global stream caps properties */ -  if (src->props) -    gst_structure_remove_all_fields (src->props); -  else -    src->props = gst_structure_empty_new ("RTSP Properties"); - -  /* send DESCRIBE */ -  GST_DEBUG_OBJECT (src, "send describe..."); -  if (!gst_rtspsrc_send (src, &request, &response, NULL)) -    goto send_error; - -  /* check if reply is SDP */ -  rtsp_message_get_header (&response, RTSP_HDR_CONTENT_TYPE, &respcont); -  /* could not be set but since the request returned OK, we assume it -   * was SDP, else check it. */ -  if (respcont) { -    if (!g_ascii_strcasecmp (respcont, "application/sdp") == 0) -      goto wrong_content_type; -  } - -  /* get message body and parse as SDP */ -  rtsp_message_get_body (&response, &data, &size); - -  GST_DEBUG_OBJECT (src, "parse SDP..."); -  sdp_message_init (&sdp); -  sdp_message_parse_buffer (data, size, &sdp); - -  if (src->debug) -    sdp_message_dump (&sdp); - -  if (src->extension && src->extension->parse_sdp) -    src->extension->parse_sdp (src->extension, &sdp); +  RTSPLowerTrans protocols;    /* we initially allow all configured lower transports. based on the URL     * transports and the replies from the server we narrow them down. */    protocols = src->url->transports & src->cur_protocols; -  /* setup streams */ -  n_streams = sdp_message_medias_len (&sdp); -  for (i = 0; i < n_streams; i++) { +  /* reset some state */ +  src->free_channel = 0; +  src->interleaved = FALSE; + +  for (walk = src->streams; walk; walk = g_list_next (walk)) {      gchar *transports; -    /* create stream from the media, can never return NULL */ -    stream = gst_rtspsrc_create_stream (src, &sdp, i); +    stream = (GstRTSPStream *) walk->data;      /* see if we need to configure this stream */      if (src->extension && src->extension->configure_stream) {        if (!src->extension->configure_stream (src->extension, stream)) { -        GST_DEBUG_OBJECT (src, "skipping stream %d, disabled by extension", i); +        GST_DEBUG_OBJECT (src, "skipping stream %p, disabled by extension", +            stream);          continue;        }      } @@ -1800,29 +1877,30 @@ gst_rtspsrc_open (GstRTSPSrc * src)      /* skip setup if we have no URL for it */      if (stream->setup_url == NULL) { -      GST_DEBUG_OBJECT (src, "skipping stream %d, no setup", i); +      GST_DEBUG_OBJECT (src, "skipping stream %p, no setup", stream);        continue;      } -    GST_DEBUG_OBJECT (src, "doing setup of stream %d with %s", i, +    GST_DEBUG_OBJECT (src, "doing setup of stream %p with %s", stream,          stream->setup_url); -    /* create SETUP request */ -    res = rtsp_message_init_request (&request, RTSP_SETUP, stream->setup_url); -    if (res < 0) -      goto create_request_failed; -      /* create a string with all the transports */      res = gst_rtspsrc_create_transports_string (src, protocols, &transports);      if (res < 0)        goto setup_transport_failed; -    /* replace placeholders with real values */ -    res = gst_rtspsrc_configure_transports (stream, &transports); +    /* replace placeholders with real values, this function will optionally +     * allocate UDP ports and other info needed to execute the setup request */ +    res = gst_rtspsrc_prepare_transports (stream, &transports);      if (res < 0)        goto setup_transport_failed; -    /* select transport, copy is made when adding to header */ +    /* create SETUP request */ +    res = rtsp_message_init_request (&request, RTSP_SETUP, stream->setup_url); +    if (res < 0) +      goto create_request_failed; + +    /* select transport, copy is made when adding to header so we can free it. */      rtsp_message_add_header (&request, RTSP_HDR_TRANSPORT, transports);      g_free (transports); @@ -1847,7 +1925,7 @@ gst_rtspsrc_open (GstRTSPSrc * src)         * are configured in the same way */        switch (transport.lower_transport) {          case RTSP_LOWER_TRANS_TCP: -          GST_DEBUG_OBJECT (src, "stream %d as TCP interleaved", i); +          GST_DEBUG_OBJECT (src, "stream %p as TCP interleaved", stream);            protocols = RTSP_LOWER_TRANS_TCP;            src->interleaved = TRUE;            /* update free channels */ @@ -1859,28 +1937,28 @@ gst_rtspsrc_open (GstRTSPSrc * src)            break;          case RTSP_LOWER_TRANS_UDP_MCAST:            /* only allow multicast for other streams */ -          GST_DEBUG_OBJECT (src, "stream %d as UDP multicast", i); +          GST_DEBUG_OBJECT (src, "stream %p as UDP multicast", stream);            protocols = RTSP_LOWER_TRANS_UDP_MCAST;            break;          case RTSP_LOWER_TRANS_UDP:            /* only allow unicast for other streams */ -          GST_DEBUG_OBJECT (src, "stream %d as UDP unicast", i); +          GST_DEBUG_OBJECT (src, "stream %p as UDP unicast", stream);            protocols = RTSP_LOWER_TRANS_UDP;            break;          default: -          GST_DEBUG_OBJECT (src, "stream %d unknown transport %d", i, +          GST_DEBUG_OBJECT (src, "stream %p unknown transport %d", stream,                transport.lower_transport);            break;        }        if (!stream->container || !src->interleaved) { -        /* now configure the stream with the transport */ +        /* now configure the stream with the selected transport */          if (!gst_rtspsrc_stream_configure_transport (stream, &transport)) {            GST_DEBUG_OBJECT (src, -              "could not configure stream %d transport, skipping stream", i); +              "could not configure stream %p transport, skipping stream", +              stream);          }        } -        /* clean up our transport struct */        rtsp_transport_init (&transport);      } @@ -1888,9 +1966,145 @@ gst_rtspsrc_open (GstRTSPSrc * src)    if (src->extension && src->extension->stream_select)      src->extension->stream_select (src->extension); -  /* if we got here all was configured. We have dynamic pads so we notify that -   * we are done */ -  gst_element_no_more_pads (GST_ELEMENT_CAST (src)); +  /* we need to activate the streams when we detect activity */ +  src->need_activate = TRUE; + +  return TRUE; + +  /* ERRORS */ +create_request_failed: +  { +    gchar *str = rtsp_strresult (res); + +    GST_ELEMENT_ERROR (src, LIBRARY, INIT, (NULL), +        ("Could not create request. (%s)", str)); +    g_free (str); +    goto cleanup_error; +  } +setup_transport_failed: +  { +    GST_ELEMENT_ERROR (src, RESOURCE, SETTINGS, (NULL), +        ("Could not setup transport.")); +    goto cleanup_error; +  } +send_error: +  { +    gchar *str = rtsp_strresult (res); + +    GST_ELEMENT_ERROR (src, RESOURCE, WRITE, (NULL), +        ("Could not send message. (%s)", str)); +    g_free (str); +    goto cleanup_error; +  } +no_transport: +  { +    GST_ELEMENT_ERROR (src, RESOURCE, SETTINGS, (NULL), +        ("Server did not select transport.")); +    goto cleanup_error; +  } +cleanup_error: +  { +    rtsp_message_unset (&request); +    rtsp_message_unset (&response); +    return FALSE; +  } +} + +static gboolean +gst_rtspsrc_open (GstRTSPSrc * src) +{ +  RTSPResult res; +  RTSPMessage request = { 0 }; +  RTSPMessage response = { 0 }; +  guint8 *data; +  guint size; +  gint i, n_streams; +  SDPMessage sdp = { 0 }; +  GstRTSPStream *stream = NULL; +  gchar *respcont = NULL; + +  /* reset our state */ +  gst_segment_init (&src->segment, GST_FORMAT_TIME); + +  /* can't continue without a valid url */ +  if (G_UNLIKELY (src->url == NULL)) +    goto no_url; + +  /* create connection */ +  GST_DEBUG_OBJECT (src, "creating connection (%s)...", src->location); +  if ((res = rtsp_connection_create (src->url, &src->connection)) < 0) +    goto could_not_create; + +  /* connect */ +  GST_DEBUG_OBJECT (src, "connecting (%s)...", src->location); +  if ((res = rtsp_connection_connect (src->connection)) < 0) +    goto could_not_connect; + +  /* create OPTIONS */ +  GST_DEBUG_OBJECT (src, "create options..."); +  res = rtsp_message_init_request (&request, RTSP_OPTIONS, src->location); +  if (res < 0) +    goto create_request_failed; + +  /* send OPTIONS */ +  GST_DEBUG_OBJECT (src, "send options..."); +  if (!gst_rtspsrc_send (src, &request, &response, NULL)) +    goto send_error; + +  /* parse OPTIONS */ +  if (!gst_rtspsrc_parse_methods (src, &response)) +    goto methods_error; + +  /* create DESCRIBE */ +  GST_DEBUG_OBJECT (src, "create describe..."); +  res = rtsp_message_init_request (&request, RTSP_DESCRIBE, src->location); +  if (res < 0) +    goto create_request_failed; + +  /* we only accept SDP for now */ +  rtsp_message_add_header (&request, RTSP_HDR_ACCEPT, "application/sdp"); + +  /* prepare global stream caps properties */ +  if (src->props) +    gst_structure_remove_all_fields (src->props); +  else +    src->props = gst_structure_empty_new ("RTSP Properties"); + +  /* send DESCRIBE */ +  GST_DEBUG_OBJECT (src, "send describe..."); +  if (!gst_rtspsrc_send (src, &request, &response, NULL)) +    goto send_error; + +  /* check if reply is SDP */ +  rtsp_message_get_header (&response, RTSP_HDR_CONTENT_TYPE, &respcont); +  /* could not be set but since the request returned OK, we assume it +   * was SDP, else check it. */ +  if (respcont) { +    if (!g_ascii_strcasecmp (respcont, "application/sdp") == 0) +      goto wrong_content_type; +  } + +  /* get message body and parse as SDP */ +  rtsp_message_get_body (&response, &data, &size); + +  GST_DEBUG_OBJECT (src, "parse SDP..."); +  sdp_message_init (&sdp); +  sdp_message_parse_buffer (data, size, &sdp); + +  if (src->debug) +    sdp_message_dump (&sdp); + +  if (src->extension && src->extension->parse_sdp) +    src->extension->parse_sdp (src->extension, &sdp); + +  /* create streams */ +  n_streams = sdp_message_medias_len (&sdp); +  for (i = 0; i < n_streams; i++) { +    stream = gst_rtspsrc_create_stream (src, &sdp, i); +  } + +  /* setup streams */ +  gst_rtspsrc_setup_streams (src);    /* clean up any messages */    rtsp_message_unset (&request); @@ -1952,18 +2166,6 @@ wrong_content_type:          ("Server does not support SDP, got %s.", respcont));      goto cleanup_error;    } -setup_transport_failed: -  { -    GST_ELEMENT_ERROR (src, RESOURCE, SETTINGS, (NULL), -        ("Could not setup transport.")); -    goto cleanup_error; -  } -no_transport: -  { -    GST_ELEMENT_ERROR (src, RESOURCE, SETTINGS, (NULL), -        ("Server did not select transport.")); -    goto cleanup_error; -  }  cleanup_error:    {      rtsp_message_unset (&request); @@ -2018,6 +2220,13 @@ gst_rtspsrc_close (GstRTSPSrc * src)    if ((res = rtsp_connection_close (src->connection)) < 0)      goto close_failed; +  /* free connection */ +  rtsp_connection_free (src->connection); +  src->connection = NULL; + +  /* cleanup */ +  gst_rtspsrc_cleanup (src); +    return TRUE;    /* ERRORS */ diff --git a/gst/rtsp/gstrtspsrc.h b/gst/rtsp/gstrtspsrc.h index cdc7c7cd..fa030b48 100644 --- a/gst/rtsp/gstrtspsrc.h +++ b/gst/rtsp/gstrtspsrc.h @@ -78,11 +78,12 @@ typedef struct _GstRTSPStream GstRTSPStream;  struct _GstRTSPStream {    gint          id; -  GstRTSPSrc   *parent; +  GstRTSPSrc   *parent; /* parent, no extra ref to parent is taken */    /* pad we expose or NULL when it does not have an actual pad */    GstPad       *srcpad;    GstFlowReturn last_ret; +  gboolean      added;    /* for interleaved mode */    gint          channel[2]; @@ -124,6 +125,7 @@ struct _GstRTSPSrc {    gint             numstreams;    GList           *streams;    GstStructure    *props; +  gboolean         need_activate;    /* properties */    gchar           *location; @@ -141,8 +143,6 @@ struct _GstRTSPSrc {    gint             methods;    RTSPConnection  *connection; -  RTSPMessage     *request; -  RTSPMessage     *response;    RTSPExtensionCtx *extension;  }; diff --git a/gst/rtsp/rtspconnection.c b/gst/rtsp/rtspconnection.c index a0603911..69454069 100644 --- a/gst/rtsp/rtspconnection.c +++ b/gst/rtsp/rtspconnection.c @@ -119,7 +119,6 @@ rtsp_connection_create (RTSPUrl * url, RTSPConnection ** conn)    gint ret;    RTSPConnection *newconn; -  g_return_val_if_fail (url != NULL, RTSP_EINVAL);    g_return_val_if_fail (conn != NULL, RTSP_EINVAL);    newconn = g_new (RTSPConnection, 1); @@ -139,6 +138,7 @@ rtsp_connection_create (RTSPUrl * url, RTSPConnection ** conn)  #endif    newconn->url = url; +  newconn->fd = -1;    newconn->cseq = 0;    newconn->session_id[0] = 0;    newconn->state = RTSP_STATE_INIT; @@ -169,6 +169,8 @@ rtsp_connection_connect (RTSPConnection * conn)    RTSPUrl *url;    g_return_val_if_fail (conn != NULL, RTSP_EINVAL); +  g_return_val_if_fail (conn->url != NULL, RTSP_EINVAL); +  g_return_val_if_fail (conn->fd < 0, RTSP_EINVAL);    url = conn->url; @@ -252,11 +254,23 @@ rtsp_connection_send (RTSPConnection * conn, RTSPMessage * message)    str = g_string_new (""); -  /* create request string, add CSeq */ -  g_string_append_printf (str, "%s %s RTSP/1.0\r\n" -      "CSeq: %d\r\n", -      rtsp_method_as_text (message->type_data.request.method), -      message->type_data.request.uri, conn->cseq); +  switch (message->type) { +    case RTSP_MESSAGE_REQUEST: +      /* create request string, add CSeq */ +      g_string_append_printf (str, "%s %s RTSP/1.0\r\n" +          "CSeq: %d\r\n", +          rtsp_method_as_text (message->type_data.request.method), +          message->type_data.request.uri, conn->cseq++); +      break; +    case RTSP_MESSAGE_RESPONSE: +      /* create response string */ +      g_string_append_printf (str, "RTSP/1.0 %d %s\r\n", +          message->type_data.response.code, message->type_data.response.reason); +      break; +    default: +      g_assert_not_reached (); +      break; +  }    /* append session id if we have one */    if (conn->session_id[0] != '\0') { @@ -300,8 +314,6 @@ rtsp_connection_send (RTSPConnection * conn, RTSPMessage * message)    }    g_string_free (str, TRUE); -  conn->cseq++; -    return RTSP_OK;  #ifdef G_OS_WIN32 @@ -328,14 +340,16 @@ write_error:  static RTSPResult  read_line (gint fd, gchar * buffer, guint size)  { -  gint idx; +  guint idx;    gchar c;    gint r;    idx = 0;    while (TRUE) {      r = read (fd, &c, 1); -    if (r < 1) { +    if (r == 0) { +      goto eof; +    } else if (r < 0) {        if (errno != EAGAIN && errno != EINTR)          goto read_error;      } else { @@ -352,6 +366,10 @@ read_line (gint fd, gchar * buffer, guint size)    return RTSP_OK; +eof: +  { +    return RTSP_EEOF; +  }  read_error:    {      return RTSP_ESYS; @@ -435,7 +453,7 @@ parse_request_line (gchar * buffer, RTSPMessage * msg)    read_string (methodstr, sizeof (methodstr), &bptr);    method = rtsp_find_method (methodstr); -  if (method == -1) +  if (method == RTSP_INVALID)      goto wrong_method;    read_string (urlstr, sizeof (urlstr), &bptr); @@ -476,7 +494,7 @@ parse_line (gchar * buffer, RTSPMessage * msg)    bptr++;    field = rtsp_find_header_field (key); -  if (field != -1) { +  if (field != RTSP_HDR_INVALID) {      while (g_ascii_isspace (*bptr))        bptr++;      rtsp_message_add_header (msg, field, bptr); @@ -737,6 +755,7 @@ rtsp_connection_close (RTSPConnection * conn)    gint res;    g_return_val_if_fail (conn != NULL, RTSP_EINVAL); +  g_return_val_if_fail (conn->fd >= 0, RTSP_EINVAL);    res = CLOSE_SOCKET (conn->fd);  #ifdef G_OS_WIN32 diff --git a/gst/rtsp/rtspdefs.c b/gst/rtsp/rtspdefs.c index 63b68a3f..6c96e1f4 100644 --- a/gst/rtsp/rtspdefs.c +++ b/gst/rtsp/rtspdefs.c @@ -57,7 +57,7 @@ static const gchar *rtsp_results[] = {    "Out of memory",    "Cannot resolve host",    "Function not implemented", -  "System error: '%s'", +  "System error: %s",    "Parse error",    "Error on WSAStartup",    "Windows sockets are not version 0x202", @@ -142,11 +142,14 @@ static const gchar *rtsp_headers[] = {    NULL  }; -#define DEF_STATUS(c,t) +#define DEF_STATUS(c, t) \ +  g_hash_table_insert (statuses, GUINT_TO_POINTER(c), t) -void +GHashTable *  rtsp_init_status (void)  { +  GHashTable *statuses = g_hash_table_new (NULL, NULL); +    DEF_STATUS (RTSP_STS_CONTINUE, "Continue");    DEF_STATUS (RTSP_STS_OK, "OK");    DEF_STATUS (RTSP_STS_CREATED, "Created"); @@ -196,6 +199,8 @@ rtsp_init_status (void)    DEF_STATUS (RTSP_STS_RTSP_VERSION_NOT_SUPPORTED,        "RTSP Version not supported");    DEF_STATUS (RTSP_STS_OPTION_NOT_SUPPORTED, "Option not supported"); + +  return statuses;  }  gchar * @@ -229,7 +234,7 @@ rtsp_method_as_text (RTSPMethod method)  {    gint i; -  if (method == 0) +  if (method == RTSP_INVALID)      return NULL;    i = 0; @@ -243,19 +248,21 @@ rtsp_method_as_text (RTSPMethod method)  const gchar *  rtsp_header_as_text (RTSPHeaderField field)  { -  return rtsp_headers[field]; +  if (field == RTSP_HDR_INVALID) +    return NULL; +  else +    return rtsp_headers[field - 1];  }  const gchar *  rtsp_status_as_text (RTSPStatusCode code)  { -  return NULL; -} +  static GHashTable *statuses; -const gchar * -rtsp_status_to_string (RTSPStatusCode code) -{ -  return NULL; +  if (G_UNLIKELY (statuses == NULL)) +    statuses = rtsp_init_status (); + +  return g_hash_table_lookup (statuses, GUINT_TO_POINTER (code));  }  RTSPHeaderField @@ -265,10 +272,10 @@ rtsp_find_header_field (gchar * header)    for (idx = 0; rtsp_headers[idx]; idx++) {      if (g_ascii_strcasecmp (rtsp_headers[idx], header) == 0) { -      return idx; +      return idx + 1;      }    } -  return -1; +  return RTSP_HDR_INVALID;  }  RTSPMethod @@ -281,5 +288,5 @@ rtsp_find_method (gchar * method)        return (1 << idx);      }    } -  return -1; +  return RTSP_INVALID;  } diff --git a/gst/rtsp/rtspdefs.h b/gst/rtsp/rtspdefs.h index 61ec3bd1..43436188 100644 --- a/gst/rtsp/rtspdefs.h +++ b/gst/rtsp/rtspdefs.h @@ -100,6 +100,8 @@ typedef enum {  } RTSPMethod;  typedef enum { +  RTSP_HDR_INVALID, +    /*     * R = Request     * r = response @@ -217,7 +219,6 @@ gchar*          rtsp_strresult          (RTSPResult result);  const gchar*    rtsp_method_as_text     (RTSPMethod method);  const gchar*    rtsp_header_as_text     (RTSPHeaderField field);  const gchar*    rtsp_status_as_text     (RTSPStatusCode code); -const gchar*    rtsp_status_to_string   (RTSPStatusCode code);  RTSPHeaderField rtsp_find_header_field  (gchar *header);  RTSPMethod      rtsp_find_method        (gchar *method); diff --git a/gst/rtsp/rtspextwms.c b/gst/rtsp/rtspextwms.c index 44776d67..2f1fbdaa 100644 --- a/gst/rtsp/rtspextwms.c +++ b/gst/rtsp/rtspextwms.c @@ -87,7 +87,7 @@ rtsp_ext_wms_after_send (RTSPExtensionCtx * ctx, RTSPMessage * req,        gchar *server = NULL;        rtsp_message_get_header (resp, RTSP_HDR_SERVER, &server); -      if (g_str_has_prefix (server, SERVER_PREFIX)) +      if (server && g_str_has_prefix (server, SERVER_PREFIX))          rext->active = TRUE;        else          rext->active = FALSE; @@ -152,9 +152,13 @@ rtsp_ext_wms_configure_stream (RTSPExtensionCtx * ctx, GstRTSPStream * stream)    s = gst_caps_get_structure (stream->caps, 0);    encoding = gst_structure_get_string (s, "encoding-name"); +  if (!encoding) +    return TRUE; +    GST_DEBUG_OBJECT (src, "%" GST_PTR_FORMAT " encoding-name: %s", stream->caps,        encoding); +  /* rtx streams do not need to be configured */    if (!strcmp (encoding, "x-wms-rtx"))      return FALSE; diff --git a/gst/rtsp/rtspmessage.c b/gst/rtsp/rtspmessage.c index 515ccb46..ac227ac2 100644 --- a/gst/rtsp/rtspmessage.c +++ b/gst/rtsp/rtspmessage.c @@ -41,10 +41,41 @@   * SOFTWARE.   */ +#include <string.h> +  #include "rtspmessage.h"  RTSPResult -rtsp_message_new_request (RTSPMessage ** msg, RTSPMethod method, gchar * uri) +rtsp_message_new (RTSPMessage ** msg) +{ +  RTSPMessage *newmsg; + +  g_return_val_if_fail (msg != NULL, RTSP_EINVAL); + +  newmsg = g_new0 (RTSPMessage, 1); + +  *msg = newmsg; + +  return rtsp_message_init (newmsg); +} + +RTSPResult +rtsp_message_init (RTSPMessage * msg) +{ +  g_return_val_if_fail (msg != NULL, RTSP_EINVAL); + +  rtsp_message_unset (msg); + +  msg->type = RTSP_MESSAGE_INVALID; +  msg->hdr_fields = +      g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free); + +  return RTSP_OK; +} + +RTSPResult +rtsp_message_new_request (RTSPMessage ** msg, RTSPMethod method, +    const gchar * uri)  {    RTSPMessage *newmsg; @@ -59,10 +90,11 @@ rtsp_message_new_request (RTSPMessage ** msg, RTSPMethod method, gchar * uri)  }  RTSPResult -rtsp_message_init_request (RTSPMessage * msg, RTSPMethod method, gchar * uri) +rtsp_message_init_request (RTSPMessage * msg, RTSPMethod method, +    const gchar * uri)  { -  g_return_val_if_fail (uri != NULL, RTSP_EINVAL);    g_return_val_if_fail (msg != NULL, RTSP_EINVAL); +  g_return_val_if_fail (uri != NULL, RTSP_EINVAL);    rtsp_message_unset (msg); @@ -77,13 +109,11 @@ rtsp_message_init_request (RTSPMessage * msg, RTSPMethod method, gchar * uri)  RTSPResult  rtsp_message_new_response (RTSPMessage ** msg, RTSPStatusCode code, -    gchar * reason, RTSPMessage * request) +    const gchar * reason, const RTSPMessage * request)  {    RTSPMessage *newmsg; -  g_return_val_if_fail (reason != NULL, RTSP_EINVAL);    g_return_val_if_fail (msg != NULL, RTSP_EINVAL); -  g_return_val_if_fail (request != NULL, RTSP_EINVAL);    newmsg = g_new0 (RTSPMessage, 1); @@ -94,13 +124,15 @@ rtsp_message_new_response (RTSPMessage ** msg, RTSPStatusCode code,  RTSPResult  rtsp_message_init_response (RTSPMessage * msg, RTSPStatusCode code, -    gchar * reason, RTSPMessage * request) +    const gchar * reason, const RTSPMessage * request)  {    g_return_val_if_fail (msg != NULL, RTSP_EINVAL); -  g_return_val_if_fail (reason != NULL, RTSP_EINVAL);    rtsp_message_unset (msg); +  if (reason == NULL) +    reason = rtsp_status_as_text (code); +    msg->type = RTSP_MESSAGE_RESPONSE;    msg->type_data.response.code = code;    msg->type_data.response.reason = g_strdup (reason); @@ -108,7 +140,25 @@ rtsp_message_init_response (RTSPMessage * msg, RTSPStatusCode code,        g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free);    if (request) { -    /* FIXME copy headers */ +    gchar *header; + +    if (rtsp_message_get_header (request, RTSP_HDR_CSEQ, &header) == RTSP_OK) { +      rtsp_message_add_header (msg, RTSP_HDR_CSEQ, header); +    } + +    if (rtsp_message_get_header (request, RTSP_HDR_SESSION, &header) == RTSP_OK) { +      char *pos; + +      header = g_strdup (header); +      if ((pos = strchr (header, ';'))) { +        *pos = '\0'; +      } +      g_strchomp (header); +      rtsp_message_add_header (msg, RTSP_HDR_SESSION, header); +      g_free (header); +    } + +    /* FIXME copy more headers? */    }    return RTSP_OK; @@ -132,22 +182,28 @@ rtsp_message_unset (RTSPMessage * msg)  {    g_return_val_if_fail (msg != NULL, RTSP_EINVAL); -  msg->type = RTSP_MESSAGE_INVALID; -  msg->type_data.request.method = RTSP_INVALID; -  g_free (msg->type_data.request.uri); -  msg->type_data.request.uri = NULL; - -  msg->type_data.response.code = RTSP_STS_INVALID; -  g_free (msg->type_data.response.reason); -  msg->type_data.response.reason = NULL; +  switch (msg->type) { +    case RTSP_MESSAGE_INVALID: +      break; +    case RTSP_MESSAGE_REQUEST: +      g_free (msg->type_data.request.uri); +      break; +    case RTSP_MESSAGE_RESPONSE: +      g_free (msg->type_data.response.reason); +      break; +    case RTSP_MESSAGE_DATA: +      break; +    default: +      g_assert_not_reached (); +      break; +  }    if (msg->hdr_fields != NULL)      g_hash_table_destroy (msg->hdr_fields); -  msg->hdr_fields = NULL;    g_free (msg->body); -  msg->body = NULL; -  msg->body_size = 0; + +  memset (msg, 0, sizeof *msg);    return RTSP_OK;  } @@ -168,7 +224,7 @@ rtsp_message_free (RTSPMessage * msg)  RTSPResult  rtsp_message_add_header (RTSPMessage * msg, RTSPHeaderField field, -    gchar * value) +    const gchar * value)  {    g_return_val_if_fail (msg != NULL, RTSP_EINVAL);    g_return_val_if_fail (value != NULL, RTSP_EINVAL); @@ -190,25 +246,25 @@ rtsp_message_remove_header (RTSPMessage * msg, RTSPHeaderField field)  }  RTSPResult -rtsp_message_get_header (RTSPMessage * msg, RTSPHeaderField field, +rtsp_message_get_header (const RTSPMessage * msg, RTSPHeaderField field,      gchar ** value)  {    gchar *val;    g_return_val_if_fail (msg != NULL, RTSP_EINVAL); -  g_return_val_if_fail (value != NULL, RTSP_EINVAL);    val = g_hash_table_lookup (msg->hdr_fields, GINT_TO_POINTER (field));    if (val == NULL)      return RTSP_ENOTIMPL; -  *value = val; +  if (value) +    *value = val;    return RTSP_OK;  }  RTSPResult -rtsp_message_set_body (RTSPMessage * msg, guint8 * data, guint size) +rtsp_message_set_body (RTSPMessage * msg, const guint8 * data, guint size)  {    g_return_val_if_fail (msg != NULL, RTSP_EINVAL); @@ -231,7 +287,7 @@ rtsp_message_take_body (RTSPMessage * msg, guint8 * data, guint size)  }  RTSPResult -rtsp_message_get_body (RTSPMessage * msg, guint8 ** data, guint * size) +rtsp_message_get_body (const RTSPMessage * msg, guint8 ** data, guint * size)  {    g_return_val_if_fail (msg != NULL, RTSP_EINVAL);    g_return_val_if_fail (data != NULL, RTSP_EINVAL); @@ -260,7 +316,7 @@ rtsp_message_steal_body (RTSPMessage * msg, guint8 ** data, guint * size)  }  static void -dump_mem (guint8 * mem, gint size) +dump_mem (guint8 * mem, guint size)  {    guint i, j;    GString *string = g_string_sized_new (50); @@ -309,7 +365,7 @@ rtsp_message_dump (RTSPMessage * msg)    switch (msg->type) {      case RTSP_MESSAGE_REQUEST: -      g_print ("request message %p\n", msg); +      g_print ("RTSP request message %p\n", msg);        g_print (" request line:\n");        g_print ("   method: '%s'\n",            rtsp_method_as_text (msg->type_data.request.method)); @@ -321,7 +377,7 @@ rtsp_message_dump (RTSPMessage * msg)        dump_mem (data, size);        break;      case RTSP_MESSAGE_RESPONSE: -      g_print ("response message %p\n", msg); +      g_print ("RTSP response message %p\n", msg);        g_print (" status line:\n");        g_print ("   code:   '%d'\n", msg->type_data.response.code);        g_print ("   reason: '%s'\n", msg->type_data.response.reason); @@ -332,7 +388,7 @@ rtsp_message_dump (RTSPMessage * msg)        dump_mem (data, size);        break;      case RTSP_MESSAGE_DATA: -      g_print ("data message %p\n", msg); +      g_print ("RTSP data message %p\n", msg);        g_print (" channel: '%d'\n", msg->type_data.data.channel);        g_print (" size:    '%d'\n", msg->body_size);        rtsp_message_get_body (msg, &data, &size); diff --git a/gst/rtsp/rtspmessage.h b/gst/rtsp/rtspmessage.h index 111f4fe8..2b91432d 100644 --- a/gst/rtsp/rtspmessage.h +++ b/gst/rtsp/rtspmessage.h @@ -82,27 +82,53 @@ typedef struct _RTSPMessage  } RTSPMessage; -RTSPResult      rtsp_message_new_request        (RTSPMessage **msg, RTSPMethod method, gchar *uri); -RTSPResult      rtsp_message_init_request       (RTSPMessage *msg, RTSPMethod method, gchar *uri); - -RTSPResult      rtsp_message_new_response       (RTSPMessage **msg, RTSPStatusCode code, gchar *reason,  -                                                 RTSPMessage *request); -RTSPResult      rtsp_message_init_response      (RTSPMessage *msg, RTSPStatusCode code, gchar *reason,  -                                                 RTSPMessage *request); -RTSPResult      rtsp_message_init_data          (RTSPMessage *msg, gint channel); +RTSPResult      rtsp_message_new                (RTSPMessage **msg); +RTSPResult      rtsp_message_init               (RTSPMessage *msg); + +RTSPResult      rtsp_message_new_request        (RTSPMessage **msg, +                                                 RTSPMethod method, +                                                 const gchar *uri); +RTSPResult      rtsp_message_init_request       (RTSPMessage *msg, +                                                 RTSPMethod method, +                                                 const gchar *uri); + +RTSPResult      rtsp_message_new_response       (RTSPMessage **msg, +                                                 RTSPStatusCode code, +                                                 const gchar *reason, +                                                 const RTSPMessage *request); +RTSPResult      rtsp_message_init_response      (RTSPMessage *msg, +                                                 RTSPStatusCode code, +                                                 const gchar *reason, +                                                 const RTSPMessage *request); + +RTSPResult      rtsp_message_init_data          (RTSPMessage *msg, +                                                 gint channel);  RTSPResult      rtsp_message_unset              (RTSPMessage *msg);  RTSPResult      rtsp_message_free               (RTSPMessage *msg); -RTSPResult      rtsp_message_add_header         (RTSPMessage *msg, RTSPHeaderField field, gchar *value); -RTSPResult      rtsp_message_remove_header      (RTSPMessage *msg, RTSPHeaderField field); -RTSPResult      rtsp_message_get_header         (RTSPMessage *msg, RTSPHeaderField field, gchar **value); - -RTSPResult      rtsp_message_set_body           (RTSPMessage *msg, guint8 *data, guint size); -RTSPResult      rtsp_message_take_body          (RTSPMessage *msg, guint8 *data, guint size); -RTSPResult      rtsp_message_get_body           (RTSPMessage *msg, guint8 **data, guint *size); -RTSPResult      rtsp_message_steal_body         (RTSPMessage *msg, guint8 **data, guint *size); +RTSPResult      rtsp_message_add_header         (RTSPMessage *msg, +                                                 RTSPHeaderField field, +                                                 const gchar *value); +RTSPResult      rtsp_message_remove_header      (RTSPMessage *msg, +                                                 RTSPHeaderField field); +RTSPResult      rtsp_message_get_header         (const RTSPMessage *msg, +                                                 RTSPHeaderField field, +                                                 gchar **value); + +RTSPResult      rtsp_message_set_body           (RTSPMessage *msg, +                                                 const guint8 *data, +                                                 guint size); +RTSPResult      rtsp_message_take_body          (RTSPMessage *msg, +                                                 guint8 *data, +                                                 guint size); +RTSPResult      rtsp_message_get_body           (const RTSPMessage *msg, +                                                 guint8 **data, +                                                 guint *size); +RTSPResult      rtsp_message_steal_body         (RTSPMessage *msg, +                                                 guint8 **data, +                                                 guint *size);  RTSPResult      rtsp_message_dump               (RTSPMessage *msg); diff --git a/gst/rtsp/sdpmessage.c b/gst/rtsp/sdpmessage.c index a9430cf0..e7d2d7eb 100644 --- a/gst/rtsp/sdpmessage.c +++ b/gst/rtsp/sdpmessage.c @@ -322,7 +322,7 @@ DEFINE_ARRAY_P_GETTER (attribute, attributes, SDPAttribute);  gchar *  sdp_message_get_attribute_val_n (SDPMessage * msg, gchar * key, guint nth)  { -  gint i; +  guint i;    for (i = 0; i < msg->attributes->len; i++) {      SDPAttribute *attr; @@ -423,7 +423,7 @@ sdp_media_get_attribute (SDPMedia * media, guint idx)  gchar *  sdp_media_get_attribute_val_n (SDPMedia * media, gchar * key, guint nth)  { -  gint i; +  guint i;    for (i = 0; i < media->attributes->len; i++) {      SDPAttribute *attr; @@ -454,9 +454,9 @@ sdp_media_get_format (SDPMedia * media, guint idx)  }  static void -read_string (gchar * dest, gint size, gchar ** src) +read_string (gchar * dest, guint size, gchar ** src)  { -  gint idx; +  guint idx;    idx = 0;    /* skip spaces */ @@ -473,9 +473,9 @@ read_string (gchar * dest, gint size, gchar ** src)  }  static void -read_string_del (gchar * dest, gint size, gchar del, gchar ** src) +read_string_del (gchar * dest, guint size, gchar del, gchar ** src)  { -  gint idx; +  guint idx;    idx = 0;    /* skip spaces */ @@ -582,7 +582,7 @@ sdp_parse_line (SDPContext * c, gchar type, gchar * buffer)      case 'm':      {        gchar *slash; -      SDPMedia nmedia = { 0 }; +      SDPMedia nmedia = {.media = NULL };        c->state = SDP_MEDIA;        sdp_media_init (&nmedia); @@ -622,7 +622,7 @@ sdp_message_parse_buffer (guint8 * data, guint size, SDPMessage * msg)    SDPContext c;    gchar type;    gchar buffer[MAX_LINE_LEN]; -  gint idx = 0; +  guint idx = 0;    g_return_val_if_fail (msg != NULL, RTSP_EINVAL);    g_return_val_if_fail (data != NULL, RTSP_EINVAL); @@ -672,7 +672,7 @@ print_media (SDPMedia * media)    g_print ("   num_ports:   '%d'\n", media->num_ports);    g_print ("   proto:       '%s'\n", media->proto);    if (media->fmts->len > 0) { -    gint i; +    guint i;      g_print ("   formats:\n");      for (i = 0; i < media->fmts->len; i++) { @@ -684,7 +684,7 @@ print_media (SDPMedia * media)    g_print ("    type:       '%s'\n", media->key.type);    g_print ("    data:       '%s'\n", media->key.data);    if (media->attributes->len > 0) { -    gint i; +    guint i;      g_print ("   attributes:\n");      for (i = 0; i < media->attributes->len; i++) { @@ -714,7 +714,7 @@ sdp_message_dump (SDPMessage * msg)    g_print (" uri:           '%s'\n", msg->uri);    if (msg->emails->len > 0) { -    gint i; +    guint i;      g_print (" emails:\n");      for (i = 0; i < msg->emails->len; i++) { @@ -722,7 +722,7 @@ sdp_message_dump (SDPMessage * msg)      }    }    if (msg->phones->len > 0) { -    gint i; +    guint i;      g_print (" phones:\n");      for (i = 0; i < msg->phones->len; i++) { @@ -739,7 +739,7 @@ sdp_message_dump (SDPMessage * msg)    g_print ("  type:         '%s'\n", msg->key.type);    g_print ("  data:         '%s'\n", msg->key.data);    if (msg->attributes->len > 0) { -    gint i; +    guint i;      g_print (" attributes:\n");      for (i = 0; i < msg->attributes->len; i++) { @@ -749,7 +749,7 @@ sdp_message_dump (SDPMessage * msg)      }    }    if (msg->medias->len > 0) { -    gint i; +    guint i;      g_print (" medias:\n");      for (i = 0; i < msg->medias->len; i++) { | 
