f15dad810d53cba4f4505ea05b75ed6bffd21377
[platform/upstream/gstreamer.git] / gst-libs / gst / rtsp / gstrtspmessage.c
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_take_header (msg, GST_RTSP_HDR_SESSION, header);
313     }
314
315     /* FIXME copy more headers? */
316   }
317
318   return GST_RTSP_OK;
319 }
320
321
322 /**
323  * gst_rtsp_message_parse_response:
324  * @msg: a #GstRTSPMessage
325  * @code: location to hold the status code
326  * @reason: location to hold the status reason
327  * @version: location to hold the version
328  *
329  * Parse the response message @msg and store the values @code, @reason and
330  * @version. The result locations can be #NULL if one is not interested in its
331  * value.
332  *
333  * @reason remains valid for as long as @msg is valid and unchanged.
334  *
335  * Returns: a #GstRTSPResult.
336  */
337 GstRTSPResult
338 gst_rtsp_message_parse_response (GstRTSPMessage * msg,
339     GstRTSPStatusCode * code, const gchar ** reason, GstRTSPVersion * version)
340 {
341   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
342   g_return_val_if_fail (msg->type == GST_RTSP_MESSAGE_RESPONSE,
343       GST_RTSP_EINVAL);
344
345   if (code)
346     *code = msg->type_data.response.code;
347   if (reason)
348     *reason = msg->type_data.response.reason;
349   if (version)
350     *version = msg->type_data.response.version;
351
352   return GST_RTSP_OK;
353 }
354
355 /**
356  * gst_rtsp_message_new_data:
357  * @msg: a location for the new #GstRTSPMessage
358  * @channel: the channel
359  *
360  * Create a new data #GstRTSPMessage with @channel and store the
361  * result message in @msg. 
362  *
363  * Returns: a #GstRTSPResult. Free with gst_rtsp_message_free().
364  */
365 GstRTSPResult
366 gst_rtsp_message_new_data (GstRTSPMessage ** msg, guint8 channel)
367 {
368   GstRTSPMessage *newmsg;
369
370   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
371
372   newmsg = g_new0 (GstRTSPMessage, 1);
373
374   *msg = newmsg;
375
376   return gst_rtsp_message_init_data (newmsg, channel);
377 }
378
379 /**
380  * gst_rtsp_message_init_data:
381  * @msg: a #GstRTSPMessage
382  * @channel: a channel
383  *
384  * Initialize a new data #GstRTSPMessage for @channel.
385  *
386  * Returns: a #GstRTSPResult.
387  */
388 GstRTSPResult
389 gst_rtsp_message_init_data (GstRTSPMessage * msg, guint8 channel)
390 {
391   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
392
393   gst_rtsp_message_unset (msg);
394
395   msg->type = GST_RTSP_MESSAGE_DATA;
396   msg->type_data.data.channel = channel;
397
398   return GST_RTSP_OK;
399 }
400
401 /**
402  * gst_rtsp_message_parse_data:
403  * @msg: a #GstRTSPMessage
404  * @channel: location to hold the channel
405  *
406  * Parse the data message @msg and store the channel in @channel.
407  *
408  * Returns: a #GstRTSPResult.
409  */
410 GstRTSPResult
411 gst_rtsp_message_parse_data (GstRTSPMessage * msg, guint8 * channel)
412 {
413   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
414   g_return_val_if_fail (msg->type != GST_RTSP_MESSAGE_DATA, GST_RTSP_EINVAL);
415
416   if (channel)
417     *channel = msg->type_data.data.channel;
418
419   return GST_RTSP_OK;
420 }
421
422 /**
423  * gst_rtsp_message_unset:
424  * @msg: a #GstRTSPMessage
425  *
426  * Unset the concents of @msg so that it becomes an uninitialized
427  * #GstRTSPMessage again. This function is mostly used in combination with 
428  * gst_rtsp_message_init_request(), gst_rtsp_message_init_response() and
429  * gst_rtsp_message_init_data() on stack allocated #GstRTSPMessage structures.
430  *
431  * Returns: #GST_RTSP_OK.
432  */
433 GstRTSPResult
434 gst_rtsp_message_unset (GstRTSPMessage * msg)
435 {
436   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
437
438   switch (msg->type) {
439     case GST_RTSP_MESSAGE_INVALID:
440       break;
441     case GST_RTSP_MESSAGE_REQUEST:
442       g_free (msg->type_data.request.uri);
443       break;
444     case GST_RTSP_MESSAGE_RESPONSE:
445       g_free (msg->type_data.response.reason);
446       break;
447     case GST_RTSP_MESSAGE_DATA:
448       break;
449     default:
450       g_return_val_if_reached (GST_RTSP_EINVAL);
451   }
452
453   if (msg->hdr_fields != NULL) {
454     guint i;
455
456     for (i = 0; i < msg->hdr_fields->len; i++) {
457       RTSPKeyValue *keyval = &g_array_index (msg->hdr_fields, RTSPKeyValue, i);
458
459       g_free (keyval->value);
460     }
461     g_array_free (msg->hdr_fields, TRUE);
462   }
463   g_free (msg->body);
464
465   memset (msg, 0, sizeof *msg);
466
467   return GST_RTSP_OK;
468 }
469
470 /**
471  * gst_rtsp_message_free:
472  * @msg: a #GstRTSPMessage
473  *
474  * Free the memory used by @msg.
475  *
476  * Returns: a #GstRTSPResult.
477  */
478 GstRTSPResult
479 gst_rtsp_message_free (GstRTSPMessage * msg)
480 {
481   GstRTSPResult res;
482
483   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
484
485   res = gst_rtsp_message_unset (msg);
486   if (res == GST_RTSP_OK)
487     g_free (msg);
488
489   return res;
490 }
491
492 /**
493  * gst_rtsp_message_take_header:
494  * @msg: a #GstRTSPMessage
495  * @field: a #GstRTSPHeaderField
496  * @value: the value of the header
497  *
498  * Add a header with key @field and @value to @msg. This function takes
499  * ownership of @value.
500  *
501  * Returns: a #GstRTSPResult.
502  *
503  * Since: 0.10.23
504  */
505 GstRTSPResult
506 gst_rtsp_message_take_header (GstRTSPMessage * msg, GstRTSPHeaderField field,
507     gchar * value)
508 {
509   RTSPKeyValue key_value;
510
511   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
512   g_return_val_if_fail (value != NULL, GST_RTSP_EINVAL);
513
514   key_value.field = field;
515   key_value.value = value;
516
517   g_array_append_val (msg->hdr_fields, key_value);
518
519   return GST_RTSP_OK;
520 }
521
522 /**
523  * gst_rtsp_message_add_header:
524  * @msg: a #GstRTSPMessage
525  * @field: a #GstRTSPHeaderField
526  * @value: the value of the header
527  *
528  * Add a header with key @field and @value to @msg. This function takes a copy
529  * of @value.
530  *
531  * Returns: a #GstRTSPResult.
532  */
533 GstRTSPResult
534 gst_rtsp_message_add_header (GstRTSPMessage * msg, GstRTSPHeaderField field,
535     const gchar * value)
536 {
537   return gst_rtsp_message_take_header (msg, field, g_strdup (value));
538 }
539
540 /**
541  * gst_rtsp_message_remove_header:
542  * @msg: a #GstRTSPMessage
543  * @field: a #GstRTSPHeaderField
544  * @indx: the index of the header
545  *
546  * Remove the @indx header with key @field from @msg. If @indx equals -1, all
547  * headers will be removed.
548  *
549  * Returns: a #GstRTSPResult.
550  */
551 GstRTSPResult
552 gst_rtsp_message_remove_header (GstRTSPMessage * msg, GstRTSPHeaderField field,
553     gint indx)
554 {
555   GstRTSPResult res = GST_RTSP_ENOTIMPL;
556   guint i = 0;
557   gint cnt = 0;
558
559   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
560
561   while (i < msg->hdr_fields->len) {
562     RTSPKeyValue key_value = g_array_index (msg->hdr_fields, RTSPKeyValue, i);
563
564     if (key_value.field == field && (indx == -1 || cnt++ == indx)) {
565       g_array_remove_index (msg->hdr_fields, i);
566       g_free (key_value.value);
567       res = GST_RTSP_OK;
568       if (indx != -1)
569         break;
570     } else {
571       i++;
572     }
573   }
574   return res;
575 }
576
577 /**
578  * gst_rtsp_message_get_header:
579  * @msg: a #GstRTSPMessage
580  * @field: a #GstRTSPHeaderField
581  * @value: pointer to hold the result
582  * @indx: the index of the header
583  *
584  * Get the @indx header value with key @field from @msg.
585  *
586  * Returns: #GST_RTSP_OK when @field was found, #GST_RTSP_ENOTIMPL if the key
587  * was not found.
588  */
589 GstRTSPResult
590 gst_rtsp_message_get_header (const GstRTSPMessage * msg,
591     GstRTSPHeaderField field, gchar ** value, gint indx)
592 {
593   guint i;
594   gint cnt = 0;
595
596   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
597
598   for (i = 0; i < msg->hdr_fields->len; i++) {
599     RTSPKeyValue key_value = g_array_index (msg->hdr_fields, RTSPKeyValue, i);
600
601     if (key_value.field == field && cnt++ == indx) {
602       if (value)
603         *value = key_value.value;
604       return GST_RTSP_OK;
605     }
606   }
607
608   return GST_RTSP_ENOTIMPL;
609 }
610
611 /**
612  * gst_rtsp_message_append_headers:
613  * @msg: a #GstRTSPMessage
614  * @str: a string
615  *
616  * Append the currently configured headers in @msg to the #GString @str suitable
617  * for transmission.
618  *
619  * Returns: #GST_RTSP_OK.
620  */
621 GstRTSPResult
622 gst_rtsp_message_append_headers (const GstRTSPMessage * msg, GString * str)
623 {
624   guint i;
625
626   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
627   g_return_val_if_fail (str != NULL, GST_RTSP_EINVAL);
628
629   for (i = 0; i < msg->hdr_fields->len; i++) {
630     RTSPKeyValue key_value = g_array_index (msg->hdr_fields, RTSPKeyValue, i);
631     const gchar *keystr = gst_rtsp_header_as_text (key_value.field);
632
633     g_string_append_printf (str, "%s: %s\r\n", keystr, key_value.value);
634   }
635   return GST_RTSP_OK;
636 }
637
638 /**
639  * gst_rtsp_message_set_body:
640  * @msg: a #GstRTSPMessage
641  * @data: the data
642  * @size: the size of @data
643  *
644  * Set the body of @msg to a copy of @data.
645  *
646  * Returns: #GST_RTSP_OK.
647  */
648 GstRTSPResult
649 gst_rtsp_message_set_body (GstRTSPMessage * msg, const guint8 * data,
650     guint size)
651 {
652   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
653
654   return gst_rtsp_message_take_body (msg, g_memdup (data, size), size);
655 }
656
657 /**
658  * gst_rtsp_message_take_body:
659  * @msg: a #GstRTSPMessage
660  * @data: the data
661  * @size: the size of @data
662  *
663  * Set the body of @msg to @data and @size. This method takes ownership of
664  * @data.
665  *
666  * Returns: #GST_RTSP_OK.
667  */
668 GstRTSPResult
669 gst_rtsp_message_take_body (GstRTSPMessage * msg, guint8 * data, guint size)
670 {
671   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
672   g_return_val_if_fail (data != NULL || size == 0, GST_RTSP_EINVAL);
673
674   if (msg->body)
675     g_free (msg->body);
676
677   msg->body = data;
678   msg->body_size = size;
679
680   return GST_RTSP_OK;
681 }
682
683 /**
684  * gst_rtsp_message_get_body:
685  * @msg: a #GstRTSPMessage
686  * @data: location for the data
687  * @size: location for the size of @data
688  *
689  * Get the body of @msg. @data remains valid for as long as @msg is valid and
690  * unchanged.
691  *
692  * Returns: #GST_RTSP_OK.
693  */
694 GstRTSPResult
695 gst_rtsp_message_get_body (const GstRTSPMessage * msg, guint8 ** data,
696     guint * size)
697 {
698   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
699   g_return_val_if_fail (data != NULL, GST_RTSP_EINVAL);
700   g_return_val_if_fail (size != NULL, GST_RTSP_EINVAL);
701
702   *data = msg->body;
703   *size = msg->body_size;
704
705   return GST_RTSP_OK;
706 }
707
708 /**
709  * gst_rtsp_message_steal_body:
710  * @msg: a #GstRTSPMessage
711  * @data: location for the data
712  * @size: location for the size of @data
713  *
714  * Take the body of @msg and store it in @data and @size. After this method,
715  * the body and size of @msg will be set to #NULL and 0 respectively.
716  *
717  * Returns: #GST_RTSP_OK.
718  */
719 GstRTSPResult
720 gst_rtsp_message_steal_body (GstRTSPMessage * msg, guint8 ** data, guint * size)
721 {
722   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
723   g_return_val_if_fail (data != NULL, GST_RTSP_EINVAL);
724   g_return_val_if_fail (size != NULL, GST_RTSP_EINVAL);
725
726   *data = msg->body;
727   *size = msg->body_size;
728
729   msg->body = NULL;
730   msg->body_size = 0;
731
732   return GST_RTSP_OK;
733 }
734
735 static void
736 dump_mem (guint8 * mem, guint size)
737 {
738   guint i, j;
739   GString *string = g_string_sized_new (50);
740   GString *chars = g_string_sized_new (18);
741
742   i = j = 0;
743   while (i < size) {
744     if (g_ascii_isprint (mem[i]))
745       g_string_append_printf (chars, "%c", mem[i]);
746     else
747       g_string_append_printf (chars, ".");
748
749     g_string_append_printf (string, "%02x ", mem[i]);
750
751     j++;
752     i++;
753
754     if (j == 16 || i == size) {
755       g_print ("%08x (%p): %-48.48s %-16.16s\n", i - j, mem + i - j,
756           string->str, chars->str);
757       g_string_set_size (string, 0);
758       g_string_set_size (chars, 0);
759       j = 0;
760     }
761   }
762   g_string_free (string, TRUE);
763   g_string_free (chars, TRUE);
764 }
765
766 static void
767 dump_key_value (gpointer data, gpointer user_data)
768 {
769   RTSPKeyValue *key_value = (RTSPKeyValue *) data;
770
771   g_print ("   key: '%s', value: '%s'\n",
772       gst_rtsp_header_as_text (key_value->field), key_value->value);
773 }
774
775 /**
776  * gst_rtsp_message_dump:
777  * @msg: a #GstRTSPMessage
778  *
779  * Dump the contents of @msg to stdout.
780  *
781  * Returns: #GST_RTSP_OK.
782  */
783 GstRTSPResult
784 gst_rtsp_message_dump (GstRTSPMessage * msg)
785 {
786   guint8 *data;
787   guint size;
788
789   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
790
791   switch (msg->type) {
792     case GST_RTSP_MESSAGE_REQUEST:
793       g_print ("RTSP request message %p\n", msg);
794       g_print (" request line:\n");
795       g_print ("   method: '%s'\n",
796           gst_rtsp_method_as_text (msg->type_data.request.method));
797       g_print ("   uri:    '%s'\n", msg->type_data.request.uri);
798       g_print ("   version: '%s'\n",
799           gst_rtsp_version_as_text (msg->type_data.request.version));
800       g_print (" headers:\n");
801       key_value_foreach (msg->hdr_fields, dump_key_value, NULL);
802       g_print (" body:\n");
803       gst_rtsp_message_get_body (msg, &data, &size);
804       dump_mem (data, size);
805       break;
806     case GST_RTSP_MESSAGE_RESPONSE:
807       g_print ("RTSP response message %p\n", msg);
808       g_print (" status line:\n");
809       g_print ("   code:   '%d'\n", msg->type_data.response.code);
810       g_print ("   reason: '%s'\n", msg->type_data.response.reason);
811       g_print ("   version: '%s'\n",
812           gst_rtsp_version_as_text (msg->type_data.response.version));
813       g_print (" headers:\n");
814       key_value_foreach (msg->hdr_fields, dump_key_value, NULL);
815       gst_rtsp_message_get_body (msg, &data, &size);
816       g_print (" body: length %d\n", size);
817       dump_mem (data, size);
818       break;
819     case GST_RTSP_MESSAGE_DATA:
820       g_print ("RTSP data message %p\n", msg);
821       g_print (" channel: '%d'\n", msg->type_data.data.channel);
822       g_print (" size:    '%d'\n", msg->body_size);
823       gst_rtsp_message_get_body (msg, &data, &size);
824       dump_mem (data, size);
825       break;
826     default:
827       g_print ("unsupported message type %d\n", msg->type);
828       return GST_RTSP_EINVAL;
829   }
830   return GST_RTSP_OK;
831 }