gst_plugins_base/gst-libs/gst/rtsp/gstrtspmessage.c
changeset 0 0e761a78d257
child 8 4a7fac7dd34a
equal deleted inserted replaced
-1:000000000000 0:0e761a78d257
       
     1 /* GStreamer
       
     2  * Copyright (C) <2005,2006> Wim Taymans <wim@fluendo.com>
       
     3  *               <2006> Lutz Mueller <lutz at topfrose dot de>
       
     4  *
       
     5  * This library is free software; you can redistribute it and/or
       
     6  * modify it under the terms of the GNU Library General Public
       
     7  * License as published by the Free Software Foundation; either
       
     8  * version 2 of the License, or (at your option) any later version.
       
     9  *
       
    10  * This library is distributed in the hope that it will be useful,
       
    11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    13  * Library General Public License for more details.
       
    14  *
       
    15  * You should have received a copy of the GNU Library General Public
       
    16  * License along with this library; if not, write to the
       
    17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
       
    18  * Boston, MA 02111-1307, USA.
       
    19  */
       
    20 /*
       
    21  * Unless otherwise indicated, Source Code is licensed under MIT license.
       
    22  * See further explanation attached in License Statement (distributed in the file
       
    23  * LICENSE).
       
    24  *
       
    25  * Permission is hereby granted, free of charge, to any person obtaining a copy of
       
    26  * this software and associated documentation files (the "Software"), to deal in
       
    27  * the Software without restriction, including without limitation the rights to
       
    28  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
       
    29  * of the Software, and to permit persons to whom the Software is furnished to do
       
    30  * so, subject to the following conditions:
       
    31  *
       
    32  * The above copyright notice and this permission notice shall be included in all
       
    33  * copies or substantial portions of the Software.
       
    34  *
       
    35  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       
    36  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       
    37  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
       
    38  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       
    39  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
       
    40  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
       
    41  * SOFTWARE.
       
    42  */
       
    43 
       
    44 /**
       
    45  * SECTION:gstrtspmessage
       
    46  * @short_description: RTSP messages
       
    47  * @see_also: gstrtspconnection
       
    48  *  
       
    49  * <refsect2>
       
    50  * <para>
       
    51  * Provides methods for creating and parsing request, response and data messages.
       
    52  * </para>
       
    53  * </refsect2>
       
    54  *  
       
    55  * Last reviewed on 2007-07-25 (0.10.14)
       
    56  */
       
    57 
       
    58 #include <string.h>
       
    59 
       
    60 #include "gstrtspmessage.h"
       
    61 
       
    62 typedef struct _RTSPKeyValue
       
    63 {
       
    64   GstRTSPHeaderField field;
       
    65   gchar *value;
       
    66 } RTSPKeyValue;
       
    67 
       
    68 static void
       
    69 key_value_foreach (GArray * array, GFunc func, gpointer user_data)
       
    70 {
       
    71   guint i;
       
    72 
       
    73   g_return_if_fail (array != NULL);
       
    74 
       
    75   for (i = 0; i < array->len; i++) {
       
    76     (*func) (&g_array_index (array, RTSPKeyValue, i), user_data);
       
    77   }
       
    78 }
       
    79 
       
    80 /**
       
    81  * gst_rtsp_message_new:
       
    82  * @msg: a location for the new #GstRTSPMessage
       
    83  *
       
    84  * Create a new initialized #GstRTSPMessage.
       
    85  *
       
    86  * Returns: a #GstRTSPResult. Free with gst_rtsp_message_free().
       
    87  */
       
    88 GstRTSPResult
       
    89 gst_rtsp_message_new (GstRTSPMessage ** msg)
       
    90 {
       
    91   GstRTSPMessage *newmsg;
       
    92 
       
    93   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
       
    94 
       
    95   newmsg = g_new0 (GstRTSPMessage, 1);
       
    96 
       
    97   *msg = newmsg;
       
    98 
       
    99   return gst_rtsp_message_init (newmsg);
       
   100 }
       
   101 
       
   102 /**
       
   103  * gst_rtsp_message_init:
       
   104  * @msg: a #GstRTSPMessage
       
   105  *
       
   106  * Initialize @msg. This function is mostly used when @msg is allocated on the
       
   107  * stack. The reverse operation of this is gst_rtsp_message_unset().
       
   108  *
       
   109  * Returns: a #GstRTSPResult.
       
   110  */
       
   111 GstRTSPResult
       
   112 gst_rtsp_message_init (GstRTSPMessage * msg)
       
   113 {
       
   114   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
       
   115 
       
   116   gst_rtsp_message_unset (msg);
       
   117 
       
   118   msg->type = GST_RTSP_MESSAGE_INVALID;
       
   119   msg->hdr_fields = g_array_new (FALSE, FALSE, sizeof (RTSPKeyValue));
       
   120 
       
   121   return GST_RTSP_OK;
       
   122 }
       
   123 
       
   124 /**
       
   125  * gst_rtsp_message_get_type:
       
   126  * @msg: a #GstRTSPMessage
       
   127  *
       
   128  * Get the message type of @msg.
       
   129  *
       
   130  * Returns: the message type.
       
   131  */
       
   132 GstRTSPMsgType
       
   133 gst_rtsp_message_get_type (GstRTSPMessage * msg)
       
   134 {
       
   135   g_return_val_if_fail (msg != NULL, GST_RTSP_MESSAGE_INVALID);
       
   136 
       
   137   return msg->type;
       
   138 }
       
   139 
       
   140 /**
       
   141  * gst_rtsp_message_new_request:
       
   142  * @msg: a location for the new #GstRTSPMessage
       
   143  * @method: the request method to use
       
   144  * @uri: the uri of the request
       
   145  *
       
   146  * Create a new #GstRTSPMessage with @method and @uri and store the result
       
   147  * request message in @msg. 
       
   148  *
       
   149  * Returns: a #GstRTSPResult. Free with gst_rtsp_message_free().
       
   150  */
       
   151 GstRTSPResult
       
   152 gst_rtsp_message_new_request (GstRTSPMessage ** msg, GstRTSPMethod method,
       
   153     const gchar * uri)
       
   154 {
       
   155   GstRTSPMessage *newmsg;
       
   156 
       
   157   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
       
   158   g_return_val_if_fail (uri != NULL, GST_RTSP_EINVAL);
       
   159 
       
   160   newmsg = g_new0 (GstRTSPMessage, 1);
       
   161 
       
   162   *msg = newmsg;
       
   163 
       
   164   return gst_rtsp_message_init_request (newmsg, method, uri);
       
   165 }
       
   166 
       
   167 /**
       
   168  * gst_rtsp_message_init_request:
       
   169  * @msg: a #GstRTSPMessage
       
   170  * @method: the request method to use
       
   171  * @uri: the uri of the request
       
   172  *
       
   173  * Initialize @msg as a request message with @method and @uri. To clear @msg
       
   174  * again, use gst_rtsp_message_unset().
       
   175  *
       
   176  * Returns: a #GstRTSPResult.
       
   177  */
       
   178 GstRTSPResult
       
   179 gst_rtsp_message_init_request (GstRTSPMessage * msg, GstRTSPMethod method,
       
   180     const gchar * uri)
       
   181 {
       
   182   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
       
   183   g_return_val_if_fail (uri != NULL, GST_RTSP_EINVAL);
       
   184 
       
   185   gst_rtsp_message_unset (msg);
       
   186 
       
   187   msg->type = GST_RTSP_MESSAGE_REQUEST;
       
   188   msg->type_data.request.method = method;
       
   189   msg->type_data.request.uri = g_strdup (uri);
       
   190   msg->type_data.request.version = GST_RTSP_VERSION_1_0;
       
   191   msg->hdr_fields = g_array_new (FALSE, FALSE, sizeof (RTSPKeyValue));
       
   192 
       
   193   return GST_RTSP_OK;
       
   194 }
       
   195 
       
   196 /**
       
   197  * gst_rtsp_message_parse_request:
       
   198  * @msg: a #GstRTSPMessage
       
   199  * @method: location to hold the method
       
   200  * @uri: location to hold the uri
       
   201  * @version: location to hold the version
       
   202  *
       
   203  * Parse the request message @msg and store the values @method, @uri and
       
   204  * @version. The result locations can be #NULL if one is not interested in its
       
   205  * value.
       
   206  *
       
   207  * @uri remains valid for as long as @msg is valid and unchanged.
       
   208  *
       
   209  * Returns: a #GstRTSPResult.
       
   210  */
       
   211 GstRTSPResult
       
   212 gst_rtsp_message_parse_request (GstRTSPMessage * msg,
       
   213     GstRTSPMethod * method, const gchar ** uri, GstRTSPVersion * version)
       
   214 {
       
   215   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
       
   216   g_return_val_if_fail (msg->type != GST_RTSP_MESSAGE_REQUEST, GST_RTSP_EINVAL);
       
   217 
       
   218   if (method)
       
   219     *method = msg->type_data.request.method;
       
   220   if (uri)
       
   221     *uri = msg->type_data.request.uri;
       
   222   if (version)
       
   223     *version = msg->type_data.request.version;
       
   224 
       
   225   return GST_RTSP_OK;
       
   226 }
       
   227 
       
   228 /**
       
   229  * gst_rtsp_message_new_response:
       
   230  * @msg: a location for the new #GstRTSPMessage
       
   231  * @code: the status code
       
   232  * @reason: the status reason or #NULL
       
   233  * @request: the request that triggered the response or #NULL
       
   234  *
       
   235  * Create a new response #GstRTSPMessage with @code and @reason and store the
       
   236  * result message in @msg. 
       
   237  *
       
   238  * When @reason is #NULL, the default reason for @code will be used.
       
   239  *
       
   240  * When @request is not #NULL, the relevant headers will be copied to the new
       
   241  * response message.
       
   242  *
       
   243  * Returns: a #GstRTSPResult. Free with gst_rtsp_message_free().
       
   244  */
       
   245 GstRTSPResult
       
   246 gst_rtsp_message_new_response (GstRTSPMessage ** msg, GstRTSPStatusCode code,
       
   247     const gchar * reason, const GstRTSPMessage * request)
       
   248 {
       
   249   GstRTSPMessage *newmsg;
       
   250 
       
   251   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
       
   252 
       
   253   newmsg = g_new0 (GstRTSPMessage, 1);
       
   254 
       
   255   *msg = newmsg;
       
   256 
       
   257   return gst_rtsp_message_init_response (newmsg, code, reason, request);
       
   258 }
       
   259 
       
   260 /**
       
   261  * gst_rtsp_message_init_response:
       
   262  * @msg: a #GstRTSPMessage
       
   263  * @code: the status code
       
   264  * @reason: the status reason or #NULL
       
   265  * @request: the request that triggered the response or #NULL
       
   266  *
       
   267  * Initialize @msg with @code and @reason.
       
   268  *
       
   269  * When @reason is #NULL, the default reason for @code will be used.
       
   270  *
       
   271  * When @request is not #NULL, the relevant headers will be copied to the new
       
   272  * response message.
       
   273  *
       
   274  * Returns: a #GstRTSPResult.
       
   275  */
       
   276 GstRTSPResult
       
   277 gst_rtsp_message_init_response (GstRTSPMessage * msg, GstRTSPStatusCode code,
       
   278     const gchar * reason, const GstRTSPMessage * request)
       
   279 {
       
   280   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
       
   281 
       
   282   gst_rtsp_message_unset (msg);
       
   283 
       
   284   if (reason == NULL)
       
   285     reason = gst_rtsp_status_as_text (code);
       
   286 
       
   287   msg->type = GST_RTSP_MESSAGE_RESPONSE;
       
   288   msg->type_data.response.code = code;
       
   289   msg->type_data.response.reason = g_strdup (reason);
       
   290   msg->type_data.response.version = GST_RTSP_VERSION_1_0;
       
   291   msg->hdr_fields = g_array_new (FALSE, FALSE, sizeof (RTSPKeyValue));
       
   292 
       
   293   if (request) {
       
   294     gchar *header;
       
   295 
       
   296     /* copy CSEQ */
       
   297     if (gst_rtsp_message_get_header (request, GST_RTSP_HDR_CSEQ, &header,
       
   298             0) == GST_RTSP_OK) {
       
   299       gst_rtsp_message_add_header (msg, GST_RTSP_HDR_CSEQ, header);
       
   300     }
       
   301 
       
   302     /* copy session id */
       
   303     if (gst_rtsp_message_get_header (request, GST_RTSP_HDR_SESSION, &header,
       
   304             0) == GST_RTSP_OK) {
       
   305       char *pos;
       
   306 
       
   307       header = g_strdup (header);
       
   308       if ((pos = strchr (header, ';'))) {
       
   309         *pos = '\0';
       
   310       }
       
   311       g_strchomp (header);
       
   312       gst_rtsp_message_add_header (msg, GST_RTSP_HDR_SESSION, header);
       
   313       g_free (header);
       
   314     }
       
   315 
       
   316     /* FIXME copy more headers? */
       
   317   }
       
   318 
       
   319   return GST_RTSP_OK;
       
   320 }
       
   321 
       
   322 
       
   323 /**
       
   324  * gst_rtsp_message_parse_response:
       
   325  * @msg: a #GstRTSPMessage
       
   326  * @code: location to hold the status code
       
   327  * @reason: location to hold the status reason
       
   328  * @version: location to hold the version
       
   329  *
       
   330  * Parse the response message @msg and store the values @code, @reason and
       
   331  * @version. The result locations can be #NULL if one is not interested in its
       
   332  * value.
       
   333  *
       
   334  * @reason remains valid for as long as @msg is valid and unchanged.
       
   335  *
       
   336  * Returns: a #GstRTSPResult.
       
   337  */
       
   338 GstRTSPResult
       
   339 gst_rtsp_message_parse_response (GstRTSPMessage * msg,
       
   340     GstRTSPStatusCode * code, const gchar ** reason, GstRTSPVersion * version)
       
   341 {
       
   342   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
       
   343   g_return_val_if_fail (msg->type != GST_RTSP_MESSAGE_RESPONSE,
       
   344       GST_RTSP_EINVAL);
       
   345 
       
   346   if (code)
       
   347     *code = msg->type_data.response.code;
       
   348   if (reason)
       
   349     *reason = msg->type_data.response.reason;
       
   350   if (version)
       
   351     *version = msg->type_data.response.version;
       
   352 
       
   353   return GST_RTSP_OK;
       
   354 }
       
   355 
       
   356 /**
       
   357  * gst_rtsp_message_new_data:
       
   358  * @msg: a location for the new #GstRTSPMessage
       
   359  * @channel: the channel
       
   360  *
       
   361  * Create a new data #GstRTSPMessage with @channel and store the
       
   362  * result message in @msg. 
       
   363  *
       
   364  * Returns: a #GstRTSPResult. Free with gst_rtsp_message_free().
       
   365  */
       
   366 GstRTSPResult
       
   367 gst_rtsp_message_new_data (GstRTSPMessage ** msg, guint8 channel)
       
   368 {
       
   369   GstRTSPMessage *newmsg;
       
   370 
       
   371   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
       
   372 
       
   373   newmsg = g_new0 (GstRTSPMessage, 1);
       
   374 
       
   375   *msg = newmsg;
       
   376 
       
   377   return gst_rtsp_message_init_data (newmsg, channel);
       
   378 }
       
   379 
       
   380 /**
       
   381  * gst_rtsp_message_init_data:
       
   382  * @msg: a #GstRTSPMessage
       
   383  * @channel: a channel
       
   384  *
       
   385  * Initialize a new data #GstRTSPMessage for @channel.
       
   386  *
       
   387  * Returns: a #GstRTSPResult.
       
   388  */
       
   389 GstRTSPResult
       
   390 gst_rtsp_message_init_data (GstRTSPMessage * msg, guint8 channel)
       
   391 {
       
   392   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
       
   393 
       
   394   gst_rtsp_message_unset (msg);
       
   395 
       
   396   msg->type = GST_RTSP_MESSAGE_DATA;
       
   397   msg->type_data.data.channel = channel;
       
   398 
       
   399   return GST_RTSP_OK;
       
   400 }
       
   401 
       
   402 /**
       
   403  * gst_rtsp_message_parse_data:
       
   404  * @msg: a #GstRTSPMessage
       
   405  * @channel: location to hold the channel
       
   406  *
       
   407  * Parse the data message @msg and store the channel in @channel.
       
   408  *
       
   409  * Returns: a #GstRTSPResult.
       
   410  */
       
   411 GstRTSPResult
       
   412 gst_rtsp_message_parse_data (GstRTSPMessage * msg, guint8 * channel)
       
   413 {
       
   414   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
       
   415   g_return_val_if_fail (msg->type != GST_RTSP_MESSAGE_DATA, GST_RTSP_EINVAL);
       
   416 
       
   417   if (channel)
       
   418     *channel = msg->type_data.data.channel;
       
   419 
       
   420   return GST_RTSP_OK;
       
   421 }
       
   422 
       
   423 /**
       
   424  * gst_rtsp_message_unset:
       
   425  * @msg: a #GstRTSPMessage
       
   426  *
       
   427  * Unset the concents of @msg so that it becomes an uninitialized
       
   428  * #GstRTSPMessage again. This function is mostly used in combination with 
       
   429  * gst_rtsp_message_init_request(), gst_rtsp_message_init_response() and
       
   430  * gst_rtsp_message_init_data() on stack allocated #GstRTSPMessage structures.
       
   431  *
       
   432  * Returns: #GST_RTSP_OK.
       
   433  */
       
   434 GstRTSPResult
       
   435 gst_rtsp_message_unset (GstRTSPMessage * msg)
       
   436 {
       
   437   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
       
   438 
       
   439   switch (msg->type) {
       
   440     case GST_RTSP_MESSAGE_INVALID:
       
   441       break;
       
   442     case GST_RTSP_MESSAGE_REQUEST:
       
   443       g_free (msg->type_data.request.uri);
       
   444       break;
       
   445     case GST_RTSP_MESSAGE_RESPONSE:
       
   446       g_free (msg->type_data.response.reason);
       
   447       break;
       
   448     case GST_RTSP_MESSAGE_DATA:
       
   449       break;
       
   450     default:
       
   451       g_return_val_if_reached (GST_RTSP_EINVAL);
       
   452   }
       
   453 
       
   454   if (msg->hdr_fields != NULL) {
       
   455     guint i;
       
   456 
       
   457     for (i = 0; i < msg->hdr_fields->len; i++) {
       
   458       RTSPKeyValue *keyval = &g_array_index (msg->hdr_fields, RTSPKeyValue, i);
       
   459 
       
   460       g_free (keyval->value);
       
   461     }
       
   462     g_array_free (msg->hdr_fields, TRUE);
       
   463   }
       
   464   g_free (msg->body);
       
   465 
       
   466   memset (msg, 0, sizeof *msg);
       
   467 
       
   468   return GST_RTSP_OK;
       
   469 }
       
   470 
       
   471 /**
       
   472  * gst_rtsp_message_free:
       
   473  * @msg: a #GstRTSPMessage
       
   474  *
       
   475  * Free the memory used by @msg.
       
   476  *
       
   477  * Returns: a #GstRTSPResult.
       
   478  */
       
   479 GstRTSPResult
       
   480 gst_rtsp_message_free (GstRTSPMessage * msg)
       
   481 {
       
   482   GstRTSPResult res;
       
   483 
       
   484   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
       
   485 
       
   486   res = gst_rtsp_message_unset (msg);
       
   487   if (res == GST_RTSP_OK)
       
   488     g_free (msg);
       
   489 
       
   490   return res;
       
   491 }
       
   492 
       
   493 /**
       
   494  * gst_rtsp_message_add_header:
       
   495  * @msg: a #GstRTSPMessage
       
   496  * @field: a #GstRTSPHeaderField
       
   497  * @value: the value of the header
       
   498  *
       
   499  * Add a header with key @field and @value to @msg.
       
   500  *
       
   501  * Returns: a #GstRTSPResult.
       
   502  */
       
   503 GstRTSPResult
       
   504 gst_rtsp_message_add_header (GstRTSPMessage * msg, GstRTSPHeaderField field,
       
   505     const gchar * value)
       
   506 {
       
   507   RTSPKeyValue key_value;
       
   508 
       
   509   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
       
   510   g_return_val_if_fail (value != NULL, GST_RTSP_EINVAL);
       
   511 
       
   512   key_value.field = field;
       
   513   key_value.value = g_strdup (value);
       
   514 
       
   515   g_array_append_val (msg->hdr_fields, key_value);
       
   516 
       
   517   return GST_RTSP_OK;
       
   518 }
       
   519 
       
   520 /**
       
   521  * gst_rtsp_message_remove_header:
       
   522  * @msg: a #GstRTSPMessage
       
   523  * @field: a #GstRTSPHeaderField
       
   524  * @indx: the index of the header
       
   525  *
       
   526  * Remove the @indx header with key @field from @msg. If @indx equals -1, all
       
   527  * headers will be removed.
       
   528  *
       
   529  * Returns: a #GstRTSPResult.
       
   530  */
       
   531 GstRTSPResult
       
   532 gst_rtsp_message_remove_header (GstRTSPMessage * msg, GstRTSPHeaderField field,
       
   533     gint indx)
       
   534 {
       
   535   GstRTSPResult res = GST_RTSP_ENOTIMPL;
       
   536   guint i = 0;
       
   537   gint cnt = 0;
       
   538 
       
   539   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
       
   540 
       
   541   while (i < msg->hdr_fields->len) {
       
   542     RTSPKeyValue key_value = g_array_index (msg->hdr_fields, RTSPKeyValue, i);
       
   543 
       
   544     if (key_value.field == field && (indx == -1 || cnt++ == indx)) {
       
   545       g_array_remove_index (msg->hdr_fields, i);
       
   546       res = GST_RTSP_OK;
       
   547       if (indx != -1)
       
   548         break;
       
   549     } else {
       
   550       i++;
       
   551     }
       
   552   }
       
   553   return res;
       
   554 }
       
   555 
       
   556 /**
       
   557  * gst_rtsp_message_get_header:
       
   558  * @msg: a #GstRTSPMessage
       
   559  * @field: a #GstRTSPHeaderField
       
   560  * @value: pointer to hold the result
       
   561  * @indx: the index of the header
       
   562  *
       
   563  * Get the @indx header value with key @field from @msg.
       
   564  *
       
   565  * Returns: #GST_RTSP_OK when @field was found, #GST_RTSP_ENOTIMPL if the key
       
   566  * was not found.
       
   567  */
       
   568 GstRTSPResult
       
   569 gst_rtsp_message_get_header (const GstRTSPMessage * msg,
       
   570     GstRTSPHeaderField field, gchar ** value, gint indx)
       
   571 {
       
   572   guint i;
       
   573   gint cnt = 0;
       
   574 
       
   575   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
       
   576 
       
   577   for (i = 0; i < msg->hdr_fields->len; i++) {
       
   578     RTSPKeyValue key_value = g_array_index (msg->hdr_fields, RTSPKeyValue, i);
       
   579 
       
   580     if (key_value.field == field && cnt++ == indx) {
       
   581       if (value)
       
   582         *value = key_value.value;
       
   583       return GST_RTSP_OK;
       
   584     }
       
   585   }
       
   586 
       
   587   return GST_RTSP_ENOTIMPL;
       
   588 }
       
   589 
       
   590 /**
       
   591  * gst_rtsp_message_append_headers:
       
   592  * @msg: a #GstRTSPMessage
       
   593  * @str: a string
       
   594  *
       
   595  * Append the currently configured headers in @msg to the #GString @str suitable
       
   596  * for transmission.
       
   597  *
       
   598  * Returns: #GST_RTSP_OK.
       
   599  */
       
   600 GstRTSPResult
       
   601 gst_rtsp_message_append_headers (const GstRTSPMessage * msg, GString * str)
       
   602 {
       
   603   guint i;
       
   604 
       
   605   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
       
   606   g_return_val_if_fail (str != NULL, GST_RTSP_EINVAL);
       
   607 
       
   608   for (i = 0; i < msg->hdr_fields->len; i++) {
       
   609     RTSPKeyValue key_value = g_array_index (msg->hdr_fields, RTSPKeyValue, i);
       
   610     const gchar *keystr = gst_rtsp_header_as_text (key_value.field);
       
   611 
       
   612     g_string_append_printf (str, "%s: %s\r\n", keystr, key_value.value);
       
   613   }
       
   614   return GST_RTSP_OK;
       
   615 }
       
   616 
       
   617 /**
       
   618  * gst_rtsp_message_set_body:
       
   619  * @msg: a #GstRTSPMessage
       
   620  * @data: the data
       
   621  * @size: the size of @data
       
   622  *
       
   623  * Set the body of @msg to a copy of @data.
       
   624  *
       
   625  * Returns: #GST_RTSP_OK.
       
   626  */
       
   627 GstRTSPResult
       
   628 gst_rtsp_message_set_body (GstRTSPMessage * msg, const guint8 * data,
       
   629     guint size)
       
   630 {
       
   631   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
       
   632 
       
   633   return gst_rtsp_message_take_body (msg, g_memdup (data, size), size);
       
   634 }
       
   635 
       
   636 /**
       
   637  * gst_rtsp_message_take_body:
       
   638  * @msg: a #GstRTSPMessage
       
   639  * @data: the data
       
   640  * @size: the size of @data
       
   641  *
       
   642  * Set the body of @msg to @data and @size. This method takes ownership of
       
   643  * @data.
       
   644  *
       
   645  * Returns: #GST_RTSP_OK.
       
   646  */
       
   647 GstRTSPResult
       
   648 gst_rtsp_message_take_body (GstRTSPMessage * msg, guint8 * data, guint size)
       
   649 {
       
   650   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
       
   651   g_return_val_if_fail (data != NULL || size == 0, GST_RTSP_EINVAL);
       
   652 
       
   653   if (msg->body)
       
   654     g_free (msg->body);
       
   655 
       
   656   msg->body = data;
       
   657   msg->body_size = size;
       
   658 
       
   659   return GST_RTSP_OK;
       
   660 }
       
   661 
       
   662 /**
       
   663  * gst_rtsp_message_get_body:
       
   664  * @msg: a #GstRTSPMessage
       
   665  * @data: location for the data
       
   666  * @size: location for the size of @data
       
   667  *
       
   668  * Get the body of @msg. @data remains valid for as long as @msg is valid and
       
   669  * unchanged.
       
   670  *
       
   671  * Returns: #GST_RTSP_OK.
       
   672  */
       
   673 GstRTSPResult
       
   674 gst_rtsp_message_get_body (const GstRTSPMessage * msg, guint8 ** data,
       
   675     guint * size)
       
   676 {
       
   677   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
       
   678   g_return_val_if_fail (data != NULL, GST_RTSP_EINVAL);
       
   679   g_return_val_if_fail (size != NULL, GST_RTSP_EINVAL);
       
   680 
       
   681   *data = msg->body;
       
   682   *size = msg->body_size;
       
   683 
       
   684   return GST_RTSP_OK;
       
   685 }
       
   686 
       
   687 /**
       
   688  * gst_rtsp_message_steal_body:
       
   689  * @msg: a #GstRTSPMessage
       
   690  * @data: location for the data
       
   691  * @size: location for the size of @data
       
   692  *
       
   693  * Take the body of @msg and store it in @data and @size. After this method,
       
   694  * the body and size of @msg will be set to #NULL and 0 respectively.
       
   695  *
       
   696  * Returns: #GST_RTSP_OK.
       
   697  */
       
   698 GstRTSPResult
       
   699 gst_rtsp_message_steal_body (GstRTSPMessage * msg, guint8 ** data, guint * size)
       
   700 {
       
   701   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
       
   702   g_return_val_if_fail (data != NULL, GST_RTSP_EINVAL);
       
   703   g_return_val_if_fail (size != NULL, GST_RTSP_EINVAL);
       
   704 
       
   705   *data = msg->body;
       
   706   *size = msg->body_size;
       
   707 
       
   708   msg->body = NULL;
       
   709   msg->body_size = 0;
       
   710 
       
   711   return GST_RTSP_OK;
       
   712 }
       
   713 
       
   714 static void
       
   715 dump_mem (guint8 * mem, guint size)
       
   716 {
       
   717   guint i, j;
       
   718   GString *string = g_string_sized_new (50);
       
   719   GString *chars = g_string_sized_new (18);
       
   720 
       
   721   i = j = 0;
       
   722   while (i < size) {
       
   723     if (g_ascii_isprint (mem[i]))
       
   724       g_string_append_printf (chars, "%c", mem[i]);
       
   725     else
       
   726       g_string_append_printf (chars, ".");
       
   727 
       
   728     g_string_append_printf (string, "%02x ", mem[i]);
       
   729 
       
   730     j++;
       
   731     i++;
       
   732 
       
   733     if (j == 16 || i == size) {
       
   734       g_print ("%08x (%p): %-48.48s %-16.16s\n", i - j, mem + i - j,
       
   735           string->str, chars->str);
       
   736       g_string_set_size (string, 0);
       
   737       g_string_set_size (chars, 0);
       
   738       j = 0;
       
   739     }
       
   740   }
       
   741   g_string_free (string, TRUE);
       
   742   g_string_free (chars, TRUE);
       
   743 }
       
   744 
       
   745 static void
       
   746 dump_key_value (gpointer data, gpointer user_data)
       
   747 {
       
   748   RTSPKeyValue *key_value = (RTSPKeyValue *) data;
       
   749 
       
   750   g_print ("   key: '%s', value: '%s'\n",
       
   751       gst_rtsp_header_as_text (key_value->field), key_value->value);
       
   752 }
       
   753 
       
   754 /**
       
   755  * gst_rtsp_message_dump:
       
   756  * @msg: a #GstRTSPMessage
       
   757  *
       
   758  * Dump the contents of @msg to stdout.
       
   759  *
       
   760  * Returns: #GST_RTSP_OK.
       
   761  */
       
   762 GstRTSPResult
       
   763 gst_rtsp_message_dump (GstRTSPMessage * msg)
       
   764 {
       
   765   guint8 *data;
       
   766   guint size;
       
   767 
       
   768   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
       
   769 
       
   770   switch (msg->type) {
       
   771     case GST_RTSP_MESSAGE_REQUEST:
       
   772       g_print ("RTSP request message %p\n", msg);
       
   773       g_print (" request line:\n");
       
   774       g_print ("   method: '%s'\n",
       
   775           gst_rtsp_method_as_text (msg->type_data.request.method));
       
   776       g_print ("   uri:    '%s'\n", msg->type_data.request.uri);
       
   777       g_print ("   version: '%s'\n",
       
   778           gst_rtsp_version_as_text (msg->type_data.request.version));
       
   779       g_print (" headers:\n");
       
   780       key_value_foreach (msg->hdr_fields, dump_key_value, NULL);
       
   781       g_print (" body:\n");
       
   782       gst_rtsp_message_get_body (msg, &data, &size);
       
   783       dump_mem (data, size);
       
   784       break;
       
   785     case GST_RTSP_MESSAGE_RESPONSE:
       
   786       g_print ("RTSP response message %p\n", msg);
       
   787       g_print (" status line:\n");
       
   788       g_print ("   code:   '%d'\n", msg->type_data.response.code);
       
   789       g_print ("   reason: '%s'\n", msg->type_data.response.reason);
       
   790       g_print ("   version: '%s'\n",
       
   791           gst_rtsp_version_as_text (msg->type_data.response.version));
       
   792       g_print (" headers:\n");
       
   793       key_value_foreach (msg->hdr_fields, dump_key_value, NULL);
       
   794       gst_rtsp_message_get_body (msg, &data, &size);
       
   795       g_print (" body: length %d\n", size);
       
   796       dump_mem (data, size);
       
   797       break;
       
   798     case GST_RTSP_MESSAGE_DATA:
       
   799       g_print ("RTSP data message %p\n", msg);
       
   800       g_print (" channel: '%d'\n", msg->type_data.data.channel);
       
   801       g_print (" size:    '%d'\n", msg->body_size);
       
   802       gst_rtsp_message_get_body (msg, &data, &size);
       
   803       dump_mem (data, size);
       
   804       break;
       
   805     default:
       
   806       g_print ("unsupported message type %d\n", msg->type);
       
   807       return GST_RTSP_EINVAL;
       
   808   }
       
   809   return GST_RTSP_OK;
       
   810 }