Added gst-plugins-base-subtitles0.10-0.10.34 for Meego Harmattan 1.2
[mafwsubrenderer] / gst-plugins-base-subtitles0.10 / 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  * Provides methods for creating and parsing request, response and data messages.
50  *  
51  * Last reviewed on 2007-07-25 (0.10.14)
52  */
53
54 #include <string.h>
55
56 #include <gst/gstutils.h>
57 #include "gstrtspmessage.h"
58
59 typedef struct _RTSPKeyValue
60 {
61   GstRTSPHeaderField field;
62   gchar *value;
63 } RTSPKeyValue;
64
65 static void
66 key_value_foreach (GArray * array, GFunc func, gpointer user_data)
67 {
68   guint i;
69
70   g_return_if_fail (array != NULL);
71
72   for (i = 0; i < array->len; i++) {
73     (*func) (&g_array_index (array, RTSPKeyValue, i), user_data);
74   }
75 }
76
77 /**
78  * gst_rtsp_message_new:
79  * @msg: a location for the new #GstRTSPMessage
80  *
81  * Create a new initialized #GstRTSPMessage. Free with gst_rtsp_message_free().
82  *
83  * Returns: a #GstRTSPResult.
84  */
85 GstRTSPResult
86 gst_rtsp_message_new (GstRTSPMessage ** msg)
87 {
88   GstRTSPMessage *newmsg;
89
90   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
91
92   newmsg = g_new0 (GstRTSPMessage, 1);
93
94   *msg = newmsg;
95
96   return gst_rtsp_message_init (newmsg);
97 }
98
99 /**
100  * gst_rtsp_message_init:
101  * @msg: a #GstRTSPMessage
102  *
103  * Initialize @msg. This function is mostly used when @msg is allocated on the
104  * stack. The reverse operation of this is gst_rtsp_message_unset().
105  *
106  * Returns: a #GstRTSPResult.
107  */
108 GstRTSPResult
109 gst_rtsp_message_init (GstRTSPMessage * msg)
110 {
111   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
112
113   gst_rtsp_message_unset (msg);
114
115   msg->type = GST_RTSP_MESSAGE_INVALID;
116   msg->hdr_fields = g_array_new (FALSE, FALSE, sizeof (RTSPKeyValue));
117
118   return GST_RTSP_OK;
119 }
120
121 /**
122  * gst_rtsp_message_get_type:
123  * @msg: a #GstRTSPMessage
124  *
125  * Get the message type of @msg.
126  *
127  * Returns: the message type.
128  */
129 GstRTSPMsgType
130 gst_rtsp_message_get_type (GstRTSPMessage * msg)
131 {
132   g_return_val_if_fail (msg != NULL, GST_RTSP_MESSAGE_INVALID);
133
134   return msg->type;
135 }
136
137 /**
138  * gst_rtsp_message_new_request:
139  * @msg: a location for the new #GstRTSPMessage
140  * @method: the request method to use
141  * @uri: the uri of the request
142  *
143  * Create a new #GstRTSPMessage with @method and @uri and store the result
144  * request message in @msg. Free with gst_rtsp_message_free().
145  *
146  * Returns: a #GstRTSPResult.
147  */
148 GstRTSPResult
149 gst_rtsp_message_new_request (GstRTSPMessage ** msg, GstRTSPMethod method,
150     const gchar * uri)
151 {
152   GstRTSPMessage *newmsg;
153
154   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
155   g_return_val_if_fail (uri != NULL, GST_RTSP_EINVAL);
156
157   newmsg = g_new0 (GstRTSPMessage, 1);
158
159   *msg = newmsg;
160
161   return gst_rtsp_message_init_request (newmsg, method, uri);
162 }
163
164 /**
165  * gst_rtsp_message_init_request:
166  * @msg: a #GstRTSPMessage
167  * @method: the request method to use
168  * @uri: the uri of the request
169  *
170  * Initialize @msg as a request message with @method and @uri. To clear @msg
171  * again, use gst_rtsp_message_unset().
172  *
173  * Returns: a #GstRTSPResult.
174  */
175 GstRTSPResult
176 gst_rtsp_message_init_request (GstRTSPMessage * msg, GstRTSPMethod method,
177     const gchar * uri)
178 {
179   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
180   g_return_val_if_fail (uri != NULL, GST_RTSP_EINVAL);
181
182   gst_rtsp_message_unset (msg);
183
184   msg->type = GST_RTSP_MESSAGE_REQUEST;
185   msg->type_data.request.method = method;
186   msg->type_data.request.uri = g_strdup (uri);
187   msg->type_data.request.version = GST_RTSP_VERSION_1_0;
188   msg->hdr_fields = g_array_new (FALSE, FALSE, sizeof (RTSPKeyValue));
189
190   return GST_RTSP_OK;
191 }
192
193 /**
194  * gst_rtsp_message_parse_request:
195  * @msg: a #GstRTSPMessage
196  * @method: location to hold the method
197  * @uri: location to hold the uri
198  * @version: location to hold the version
199  *
200  * Parse the request message @msg and store the values @method, @uri and
201  * @version. The result locations can be #NULL if one is not interested in its
202  * value.
203  *
204  * @uri remains valid for as long as @msg is valid and unchanged.
205  *
206  * Returns: a #GstRTSPResult.
207  */
208 GstRTSPResult
209 gst_rtsp_message_parse_request (GstRTSPMessage * msg,
210     GstRTSPMethod * method, const gchar ** uri, GstRTSPVersion * version)
211 {
212   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
213   g_return_val_if_fail (msg->type == GST_RTSP_MESSAGE_REQUEST ||
214       msg->type == GST_RTSP_MESSAGE_HTTP_REQUEST, GST_RTSP_EINVAL);
215
216   if (method)
217     *method = msg->type_data.request.method;
218   if (uri)
219     *uri = msg->type_data.request.uri;
220   if (version)
221     *version = msg->type_data.request.version;
222
223   return GST_RTSP_OK;
224 }
225
226 /**
227  * gst_rtsp_message_new_response:
228  * @msg: a location for the new #GstRTSPMessage
229  * @code: the status code
230  * @reason: the status reason or #NULL
231  * @request: the request that triggered the response or #NULL
232  *
233  * Create a new response #GstRTSPMessage with @code and @reason and store the
234  * result message in @msg. Free with gst_rtsp_message_free().
235  *
236  * When @reason is #NULL, the default reason for @code will be used.
237  *
238  * When @request is not #NULL, the relevant headers will be copied to the new
239  * response message.
240  *
241  * Returns: a #GstRTSPResult.
242  */
243 GstRTSPResult
244 gst_rtsp_message_new_response (GstRTSPMessage ** msg, GstRTSPStatusCode code,
245     const gchar * reason, const GstRTSPMessage * request)
246 {
247   GstRTSPMessage *newmsg;
248
249   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
250
251   newmsg = g_new0 (GstRTSPMessage, 1);
252
253   *msg = newmsg;
254
255   return gst_rtsp_message_init_response (newmsg, code, reason, request);
256 }
257
258 /**
259  * gst_rtsp_message_init_response:
260  * @msg: a #GstRTSPMessage
261  * @code: the status code
262  * @reason: the status reason or #NULL
263  * @request: the request that triggered the response or #NULL
264  *
265  * Initialize @msg with @code and @reason.
266  *
267  * When @reason is #NULL, the default reason for @code will be used.
268  *
269  * When @request is not #NULL, the relevant headers will be copied to the new
270  * response message.
271  *
272  * Returns: a #GstRTSPResult.
273  */
274 GstRTSPResult
275 gst_rtsp_message_init_response (GstRTSPMessage * msg, GstRTSPStatusCode code,
276     const gchar * reason, const GstRTSPMessage * request)
277 {
278   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
279
280   gst_rtsp_message_unset (msg);
281
282   if (reason == NULL)
283     reason = gst_rtsp_status_as_text (code);
284
285   msg->type = GST_RTSP_MESSAGE_RESPONSE;
286   msg->type_data.response.code = code;
287   msg->type_data.response.reason = g_strdup (reason);
288   msg->type_data.response.version = GST_RTSP_VERSION_1_0;
289   msg->hdr_fields = g_array_new (FALSE, FALSE, sizeof (RTSPKeyValue));
290
291   if (request) {
292     if (request->type == GST_RTSP_MESSAGE_HTTP_REQUEST) {
293       msg->type = GST_RTSP_MESSAGE_HTTP_RESPONSE;
294       if (request->type_data.request.version != GST_RTSP_VERSION_INVALID)
295         msg->type_data.response.version = request->type_data.request.version;
296       else
297         msg->type_data.response.version = GST_RTSP_VERSION_1_1;
298     } else {
299       gchar *header;
300
301       /* copy CSEQ */
302       if (gst_rtsp_message_get_header (request, GST_RTSP_HDR_CSEQ, &header,
303               0) == GST_RTSP_OK) {
304         gst_rtsp_message_add_header (msg, GST_RTSP_HDR_CSEQ, header);
305       }
306
307       /* copy session id */
308       if (gst_rtsp_message_get_header (request, GST_RTSP_HDR_SESSION, &header,
309               0) == GST_RTSP_OK) {
310         char *pos;
311
312         header = g_strdup (header);
313         if ((pos = strchr (header, ';'))) {
314           *pos = '\0';
315         }
316         g_strchomp (header);
317         gst_rtsp_message_take_header (msg, GST_RTSP_HDR_SESSION, header);
318       }
319
320       /* FIXME copy more headers? */
321     }
322   }
323
324   return GST_RTSP_OK;
325 }
326
327 /**
328  * gst_rtsp_message_parse_response:
329  * @msg: a #GstRTSPMessage
330  * @code: location to hold the status code
331  * @reason: location to hold the status reason
332  * @version: location to hold the version
333  *
334  * Parse the response message @msg and store the values @code, @reason and
335  * @version. The result locations can be #NULL if one is not interested in its
336  * value.
337  *
338  * @reason remains valid for as long as @msg is valid and unchanged.
339  *
340  * Returns: a #GstRTSPResult.
341  */
342 GstRTSPResult
343 gst_rtsp_message_parse_response (GstRTSPMessage * msg,
344     GstRTSPStatusCode * code, const gchar ** reason, GstRTSPVersion * version)
345 {
346   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
347   g_return_val_if_fail (msg->type == GST_RTSP_MESSAGE_RESPONSE ||
348       msg->type == GST_RTSP_MESSAGE_HTTP_RESPONSE, GST_RTSP_EINVAL);
349
350   if (code)
351     *code = msg->type_data.response.code;
352   if (reason)
353     *reason = msg->type_data.response.reason;
354   if (version)
355     *version = msg->type_data.response.version;
356
357   return GST_RTSP_OK;
358 }
359
360 /**
361  * gst_rtsp_message_new_data:
362  * @msg: a location for the new #GstRTSPMessage
363  * @channel: the channel
364  *
365  * Create a new data #GstRTSPMessage with @channel and store the
366  * result message in @msg. Free with gst_rtsp_message_free().
367  *
368  * Returns: a #GstRTSPResult.
369  */
370 GstRTSPResult
371 gst_rtsp_message_new_data (GstRTSPMessage ** msg, guint8 channel)
372 {
373   GstRTSPMessage *newmsg;
374
375   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
376
377   newmsg = g_new0 (GstRTSPMessage, 1);
378
379   *msg = newmsg;
380
381   return gst_rtsp_message_init_data (newmsg, channel);
382 }
383
384 /**
385  * gst_rtsp_message_init_data:
386  * @msg: a #GstRTSPMessage
387  * @channel: a channel
388  *
389  * Initialize a new data #GstRTSPMessage for @channel.
390  *
391  * Returns: a #GstRTSPResult.
392  */
393 GstRTSPResult
394 gst_rtsp_message_init_data (GstRTSPMessage * msg, guint8 channel)
395 {
396   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
397
398   gst_rtsp_message_unset (msg);
399
400   msg->type = GST_RTSP_MESSAGE_DATA;
401   msg->type_data.data.channel = channel;
402
403   return GST_RTSP_OK;
404 }
405
406 /**
407  * gst_rtsp_message_parse_data:
408  * @msg: a #GstRTSPMessage
409  * @channel: location to hold the channel
410  *
411  * Parse the data message @msg and store the channel in @channel.
412  *
413  * Returns: a #GstRTSPResult.
414  */
415 GstRTSPResult
416 gst_rtsp_message_parse_data (GstRTSPMessage * msg, guint8 * channel)
417 {
418   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
419   g_return_val_if_fail (msg->type == GST_RTSP_MESSAGE_DATA, GST_RTSP_EINVAL);
420
421   if (channel)
422     *channel = msg->type_data.data.channel;
423
424   return GST_RTSP_OK;
425 }
426
427 /**
428  * gst_rtsp_message_unset:
429  * @msg: a #GstRTSPMessage
430  *
431  * Unset the contents of @msg so that it becomes an uninitialized
432  * #GstRTSPMessage again. This function is mostly used in combination with 
433  * gst_rtsp_message_init_request(), gst_rtsp_message_init_response() and
434  * gst_rtsp_message_init_data() on stack allocated #GstRTSPMessage structures.
435  *
436  * Returns: #GST_RTSP_OK.
437  */
438 GstRTSPResult
439 gst_rtsp_message_unset (GstRTSPMessage * msg)
440 {
441   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
442
443   switch (msg->type) {
444     case GST_RTSP_MESSAGE_INVALID:
445       break;
446     case GST_RTSP_MESSAGE_REQUEST:
447     case GST_RTSP_MESSAGE_HTTP_REQUEST:
448       g_free (msg->type_data.request.uri);
449       break;
450     case GST_RTSP_MESSAGE_RESPONSE:
451     case GST_RTSP_MESSAGE_HTTP_RESPONSE:
452       g_free (msg->type_data.response.reason);
453       break;
454     case GST_RTSP_MESSAGE_DATA:
455       break;
456     default:
457       g_return_val_if_reached (GST_RTSP_EINVAL);
458   }
459
460   if (msg->hdr_fields != NULL) {
461     guint i;
462
463     for (i = 0; i < msg->hdr_fields->len; i++) {
464       RTSPKeyValue *keyval = &g_array_index (msg->hdr_fields, RTSPKeyValue, i);
465
466       g_free (keyval->value);
467     }
468     g_array_free (msg->hdr_fields, TRUE);
469   }
470   g_free (msg->body);
471
472   memset (msg, 0, sizeof (GstRTSPMessage));
473
474   return GST_RTSP_OK;
475 }
476
477 /**
478  * gst_rtsp_message_free:
479  * @msg: a #GstRTSPMessage
480  *
481  * Free the memory used by @msg.
482  *
483  * Returns: a #GstRTSPResult.
484  */
485 GstRTSPResult
486 gst_rtsp_message_free (GstRTSPMessage * msg)
487 {
488   GstRTSPResult res;
489
490   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
491
492   res = gst_rtsp_message_unset (msg);
493   if (res == GST_RTSP_OK)
494     g_free (msg);
495
496   return res;
497 }
498
499 /**
500  * gst_rtsp_message_take_header:
501  * @msg: a #GstRTSPMessage
502  * @field: a #GstRTSPHeaderField
503  * @value: the value of the header
504  *
505  * Add a header with key @field and @value to @msg. This function takes
506  * ownership of @value.
507  *
508  * Returns: a #GstRTSPResult.
509  *
510  * Since: 0.10.23
511  */
512 GstRTSPResult
513 gst_rtsp_message_take_header (GstRTSPMessage * msg, GstRTSPHeaderField field,
514     gchar * value)
515 {
516   RTSPKeyValue key_value;
517
518   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
519   g_return_val_if_fail (value != NULL, GST_RTSP_EINVAL);
520
521   key_value.field = field;
522   key_value.value = value;
523
524   g_array_append_val (msg->hdr_fields, key_value);
525
526   return GST_RTSP_OK;
527 }
528
529 /**
530  * gst_rtsp_message_add_header:
531  * @msg: a #GstRTSPMessage
532  * @field: a #GstRTSPHeaderField
533  * @value: the value of the header
534  *
535  * Add a header with key @field and @value to @msg. This function takes a copy
536  * of @value.
537  *
538  * Returns: a #GstRTSPResult.
539  */
540 GstRTSPResult
541 gst_rtsp_message_add_header (GstRTSPMessage * msg, GstRTSPHeaderField field,
542     const gchar * value)
543 {
544   return gst_rtsp_message_take_header (msg, field, g_strdup (value));
545 }
546
547 /**
548  * gst_rtsp_message_remove_header:
549  * @msg: a #GstRTSPMessage
550  * @field: a #GstRTSPHeaderField
551  * @indx: the index of the header
552  *
553  * Remove the @indx header with key @field from @msg. If @indx equals -1, all
554  * headers will be removed.
555  *
556  * Returns: a #GstRTSPResult.
557  */
558 GstRTSPResult
559 gst_rtsp_message_remove_header (GstRTSPMessage * msg, GstRTSPHeaderField field,
560     gint indx)
561 {
562   GstRTSPResult res = GST_RTSP_ENOTIMPL;
563   guint i = 0;
564   gint cnt = 0;
565
566   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
567
568   while (i < msg->hdr_fields->len) {
569     RTSPKeyValue *key_value = &g_array_index (msg->hdr_fields, RTSPKeyValue, i);
570
571     if (key_value->field == field && (indx == -1 || cnt++ == indx)) {
572       g_free (key_value->value);
573       g_array_remove_index (msg->hdr_fields, i);
574       res = GST_RTSP_OK;
575       if (indx != -1)
576         break;
577     } else {
578       i++;
579     }
580   }
581   return res;
582 }
583
584 /**
585  * gst_rtsp_message_get_header:
586  * @msg: a #GstRTSPMessage
587  * @field: a #GstRTSPHeaderField
588  * @value: pointer to hold the result
589  * @indx: the index of the header
590  *
591  * Get the @indx header value with key @field from @msg. The result in @value
592  * stays valid as long as it remains present in @msg.
593  *
594  * Returns: #GST_RTSP_OK when @field was found, #GST_RTSP_ENOTIMPL if the key
595  * was not found.
596  */
597 GstRTSPResult
598 gst_rtsp_message_get_header (const GstRTSPMessage * msg,
599     GstRTSPHeaderField field, gchar ** value, gint indx)
600 {
601   guint i;
602   gint cnt = 0;
603
604   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
605
606   /* no header initialized, there are no headers */
607   if (msg->hdr_fields == NULL)
608     return GST_RTSP_ENOTIMPL;
609
610   for (i = 0; i < msg->hdr_fields->len; i++) {
611     RTSPKeyValue *key_value = &g_array_index (msg->hdr_fields, RTSPKeyValue, i);
612
613     if (key_value->field == field && cnt++ == indx) {
614       if (value)
615         *value = key_value->value;
616       return GST_RTSP_OK;
617     }
618   }
619
620   return GST_RTSP_ENOTIMPL;
621 }
622
623 /**
624  * gst_rtsp_message_append_headers:
625  * @msg: a #GstRTSPMessage
626  * @str: a string
627  *
628  * Append the currently configured headers in @msg to the #GString @str suitable
629  * for transmission.
630  *
631  * Returns: #GST_RTSP_OK.
632  */
633 GstRTSPResult
634 gst_rtsp_message_append_headers (const GstRTSPMessage * msg, GString * str)
635 {
636   guint i;
637
638   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
639   g_return_val_if_fail (str != NULL, GST_RTSP_EINVAL);
640
641   for (i = 0; i < msg->hdr_fields->len; i++) {
642     RTSPKeyValue *key_value;
643     const gchar *keystr;
644
645     key_value = &g_array_index (msg->hdr_fields, RTSPKeyValue, i);
646     keystr = gst_rtsp_header_as_text (key_value->field);
647
648     g_string_append_printf (str, "%s: %s\r\n", keystr, key_value->value);
649   }
650   return GST_RTSP_OK;
651 }
652
653 /**
654  * gst_rtsp_message_set_body:
655  * @msg: a #GstRTSPMessage
656  * @data: the data
657  * @size: the size of @data
658  *
659  * Set the body of @msg to a copy of @data.
660  *
661  * Returns: #GST_RTSP_OK.
662  */
663 GstRTSPResult
664 gst_rtsp_message_set_body (GstRTSPMessage * msg, const guint8 * data,
665     guint size)
666 {
667   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
668
669   return gst_rtsp_message_take_body (msg, g_memdup (data, size), size);
670 }
671
672 /**
673  * gst_rtsp_message_take_body:
674  * @msg: a #GstRTSPMessage
675  * @data: the data
676  * @size: the size of @data
677  *
678  * Set the body of @msg to @data and @size. This method takes ownership of
679  * @data.
680  *
681  * Returns: #GST_RTSP_OK.
682  */
683 GstRTSPResult
684 gst_rtsp_message_take_body (GstRTSPMessage * msg, guint8 * data, guint size)
685 {
686   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
687   g_return_val_if_fail (data != NULL || size == 0, GST_RTSP_EINVAL);
688
689   if (msg->body)
690     g_free (msg->body);
691
692   msg->body = data;
693   msg->body_size = size;
694
695   return GST_RTSP_OK;
696 }
697
698 /**
699  * gst_rtsp_message_get_body:
700  * @msg: a #GstRTSPMessage
701  * @data: location for the data
702  * @size: location for the size of @data
703  *
704  * Get the body of @msg. @data remains valid for as long as @msg is valid and
705  * unchanged.
706  *
707  * Returns: #GST_RTSP_OK.
708  */
709 GstRTSPResult
710 gst_rtsp_message_get_body (const GstRTSPMessage * msg, guint8 ** data,
711     guint * size)
712 {
713   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
714   g_return_val_if_fail (data != NULL, GST_RTSP_EINVAL);
715   g_return_val_if_fail (size != NULL, GST_RTSP_EINVAL);
716
717   *data = msg->body;
718   *size = msg->body_size;
719
720   return GST_RTSP_OK;
721 }
722
723 /**
724  * gst_rtsp_message_steal_body:
725  * @msg: a #GstRTSPMessage
726  * @data: location for the data
727  * @size: location for the size of @data
728  *
729  * Take the body of @msg and store it in @data and @size. After this method,
730  * the body and size of @msg will be set to #NULL and 0 respectively.
731  *
732  * Returns: #GST_RTSP_OK.
733  */
734 GstRTSPResult
735 gst_rtsp_message_steal_body (GstRTSPMessage * msg, guint8 ** data, guint * size)
736 {
737   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
738   g_return_val_if_fail (data != NULL, GST_RTSP_EINVAL);
739   g_return_val_if_fail (size != NULL, GST_RTSP_EINVAL);
740
741   *data = msg->body;
742   *size = msg->body_size;
743
744   msg->body = NULL;
745   msg->body_size = 0;
746
747   return GST_RTSP_OK;
748 }
749
750 static void
751 dump_key_value (gpointer data, gpointer user_data G_GNUC_UNUSED)
752 {
753   RTSPKeyValue *key_value = (RTSPKeyValue *) data;
754
755   g_print ("   key: '%s', value: '%s'\n",
756       gst_rtsp_header_as_text (key_value->field), key_value->value);
757 }
758
759 /**
760  * gst_rtsp_message_dump:
761  * @msg: a #GstRTSPMessage
762  *
763  * Dump the contents of @msg to stdout.
764  *
765  * Returns: #GST_RTSP_OK.
766  */
767 GstRTSPResult
768 gst_rtsp_message_dump (GstRTSPMessage * msg)
769 {
770   guint8 *data;
771   guint size;
772
773   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
774
775   switch (msg->type) {
776     case GST_RTSP_MESSAGE_REQUEST:
777       g_print ("RTSP request message %p\n", msg);
778       g_print (" request line:\n");
779       g_print ("   method: '%s'\n",
780           gst_rtsp_method_as_text (msg->type_data.request.method));
781       g_print ("   uri:    '%s'\n", msg->type_data.request.uri);
782       g_print ("   version: '%s'\n",
783           gst_rtsp_version_as_text (msg->type_data.request.version));
784       g_print (" headers:\n");
785       key_value_foreach (msg->hdr_fields, dump_key_value, NULL);
786       g_print (" body:\n");
787       gst_rtsp_message_get_body (msg, &data, &size);
788       gst_util_dump_mem (data, size);
789       break;
790     case GST_RTSP_MESSAGE_RESPONSE:
791       g_print ("RTSP response message %p\n", msg);
792       g_print (" status line:\n");
793       g_print ("   code:   '%d'\n", msg->type_data.response.code);
794       g_print ("   reason: '%s'\n", msg->type_data.response.reason);
795       g_print ("   version: '%s'\n",
796           gst_rtsp_version_as_text (msg->type_data.response.version));
797       g_print (" headers:\n");
798       key_value_foreach (msg->hdr_fields, dump_key_value, NULL);
799       gst_rtsp_message_get_body (msg, &data, &size);
800       g_print (" body: length %d\n", size);
801       gst_util_dump_mem (data, size);
802       break;
803     case GST_RTSP_MESSAGE_HTTP_REQUEST:
804       g_print ("HTTP request message %p\n", msg);
805       g_print (" request line:\n");
806       g_print ("   method:  '%s'\n",
807           gst_rtsp_method_as_text (msg->type_data.request.method));
808       g_print ("   uri:     '%s'\n", msg->type_data.request.uri);
809       g_print ("   version: '%s'\n",
810           gst_rtsp_version_as_text (msg->type_data.request.version));
811       g_print (" headers:\n");
812       key_value_foreach (msg->hdr_fields, dump_key_value, NULL);
813       g_print (" body:\n");
814       gst_rtsp_message_get_body (msg, &data, &size);
815       gst_util_dump_mem (data, size);
816       break;
817     case GST_RTSP_MESSAGE_HTTP_RESPONSE:
818       g_print ("HTTP response message %p\n", msg);
819       g_print (" status line:\n");
820       g_print ("   code:    '%d'\n", msg->type_data.response.code);
821       g_print ("   reason:  '%s'\n", msg->type_data.response.reason);
822       g_print ("   version: '%s'\n",
823           gst_rtsp_version_as_text (msg->type_data.response.version));
824       g_print (" headers:\n");
825       key_value_foreach (msg->hdr_fields, dump_key_value, NULL);
826       gst_rtsp_message_get_body (msg, &data, &size);
827       g_print (" body: length %d\n", size);
828       gst_util_dump_mem (data, size);
829       break;
830     case GST_RTSP_MESSAGE_DATA:
831       g_print ("RTSP data message %p\n", msg);
832       g_print (" channel: '%d'\n", msg->type_data.data.channel);
833       g_print (" size:    '%d'\n", msg->body_size);
834       gst_rtsp_message_get_body (msg, &data, &size);
835       gst_util_dump_mem (data, size);
836       break;
837     default:
838       g_print ("unsupported message type %d\n", msg->type);
839       return GST_RTSP_EINVAL;
840   }
841   return GST_RTSP_OK;
842 }