rtsp: fix g_return condition
[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 <gst/gstutils.h>
61 #include "gstrtspmessage.h"
62
63 typedef struct _RTSPKeyValue
64 {
65   GstRTSPHeaderField field;
66   gchar *value;
67 } RTSPKeyValue;
68
69 static void
70 key_value_foreach (GArray * array, GFunc func, gpointer user_data)
71 {
72   guint i;
73
74   g_return_if_fail (array != NULL);
75
76   for (i = 0; i < array->len; i++) {
77     (*func) (&g_array_index (array, RTSPKeyValue, i), user_data);
78   }
79 }
80
81 /**
82  * gst_rtsp_message_new:
83  * @msg: a location for the new #GstRTSPMessage
84  *
85  * Create a new initialized #GstRTSPMessage. Free with gst_rtsp_message_free().
86  *
87  * Returns: a #GstRTSPResult.
88  */
89 GstRTSPResult
90 gst_rtsp_message_new (GstRTSPMessage ** msg)
91 {
92   GstRTSPMessage *newmsg;
93
94   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
95
96   newmsg = g_new0 (GstRTSPMessage, 1);
97
98   *msg = newmsg;
99
100   return gst_rtsp_message_init (newmsg);
101 }
102
103 /**
104  * gst_rtsp_message_init:
105  * @msg: a #GstRTSPMessage
106  *
107  * Initialize @msg. This function is mostly used when @msg is allocated on the
108  * stack. The reverse operation of this is gst_rtsp_message_unset().
109  *
110  * Returns: a #GstRTSPResult.
111  */
112 GstRTSPResult
113 gst_rtsp_message_init (GstRTSPMessage * msg)
114 {
115   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
116
117   gst_rtsp_message_unset (msg);
118
119   msg->type = GST_RTSP_MESSAGE_INVALID;
120   msg->hdr_fields = g_array_new (FALSE, FALSE, sizeof (RTSPKeyValue));
121
122   return GST_RTSP_OK;
123 }
124
125 /**
126  * gst_rtsp_message_get_type:
127  * @msg: a #GstRTSPMessage
128  *
129  * Get the message type of @msg.
130  *
131  * Returns: the message type.
132  */
133 GstRTSPMsgType
134 gst_rtsp_message_get_type (GstRTSPMessage * msg)
135 {
136   g_return_val_if_fail (msg != NULL, GST_RTSP_MESSAGE_INVALID);
137
138   return msg->type;
139 }
140
141 /**
142  * gst_rtsp_message_new_request:
143  * @msg: a location for the new #GstRTSPMessage
144  * @method: the request method to use
145  * @uri: the uri of the request
146  *
147  * Create a new #GstRTSPMessage with @method and @uri and store the result
148  * request message in @msg. Free with gst_rtsp_message_free().
149  *
150  * Returns: a #GstRTSPResult.
151  */
152 GstRTSPResult
153 gst_rtsp_message_new_request (GstRTSPMessage ** msg, GstRTSPMethod method,
154     const gchar * uri)
155 {
156   GstRTSPMessage *newmsg;
157
158   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
159   g_return_val_if_fail (uri != NULL, GST_RTSP_EINVAL);
160
161   newmsg = g_new0 (GstRTSPMessage, 1);
162
163   *msg = newmsg;
164
165   return gst_rtsp_message_init_request (newmsg, method, uri);
166 }
167
168 /**
169  * gst_rtsp_message_init_request:
170  * @msg: a #GstRTSPMessage
171  * @method: the request method to use
172  * @uri: the uri of the request
173  *
174  * Initialize @msg as a request message with @method and @uri. To clear @msg
175  * again, use gst_rtsp_message_unset().
176  *
177  * Returns: a #GstRTSPResult.
178  */
179 GstRTSPResult
180 gst_rtsp_message_init_request (GstRTSPMessage * msg, GstRTSPMethod method,
181     const gchar * uri)
182 {
183   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
184   g_return_val_if_fail (uri != NULL, GST_RTSP_EINVAL);
185
186   gst_rtsp_message_unset (msg);
187
188   msg->type = GST_RTSP_MESSAGE_REQUEST;
189   msg->type_data.request.method = method;
190   msg->type_data.request.uri = g_strdup (uri);
191   msg->type_data.request.version = GST_RTSP_VERSION_1_0;
192   msg->hdr_fields = g_array_new (FALSE, FALSE, sizeof (RTSPKeyValue));
193
194   return GST_RTSP_OK;
195 }
196
197 /**
198  * gst_rtsp_message_parse_request:
199  * @msg: a #GstRTSPMessage
200  * @method: location to hold the method
201  * @uri: location to hold the uri
202  * @version: location to hold the version
203  *
204  * Parse the request message @msg and store the values @method, @uri and
205  * @version. The result locations can be #NULL if one is not interested in its
206  * value.
207  *
208  * @uri remains valid for as long as @msg is valid and unchanged.
209  *
210  * Returns: a #GstRTSPResult.
211  */
212 GstRTSPResult
213 gst_rtsp_message_parse_request (GstRTSPMessage * msg,
214     GstRTSPMethod * method, const gchar ** uri, GstRTSPVersion * version)
215 {
216   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
217   g_return_val_if_fail (msg->type == GST_RTSP_MESSAGE_REQUEST, GST_RTSP_EINVAL);
218
219   if (method)
220     *method = msg->type_data.request.method;
221   if (uri)
222     *uri = msg->type_data.request.uri;
223   if (version)
224     *version = msg->type_data.request.version;
225
226   return GST_RTSP_OK;
227 }
228
229 /**
230  * gst_rtsp_message_new_response:
231  * @msg: a location for the new #GstRTSPMessage
232  * @code: the status code
233  * @reason: the status reason or #NULL
234  * @request: the request that triggered the response or #NULL
235  *
236  * Create a new response #GstRTSPMessage with @code and @reason and store the
237  * result message in @msg. Free with gst_rtsp_message_free().
238  *
239  * When @reason is #NULL, the default reason for @code will be used.
240  *
241  * When @request is not #NULL, the relevant headers will be copied to the new
242  * response message.
243  *
244  * Returns: a #GstRTSPResult.
245  */
246 GstRTSPResult
247 gst_rtsp_message_new_response (GstRTSPMessage ** msg, GstRTSPStatusCode code,
248     const gchar * reason, const GstRTSPMessage * request)
249 {
250   GstRTSPMessage *newmsg;
251
252   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
253
254   newmsg = g_new0 (GstRTSPMessage, 1);
255
256   *msg = newmsg;
257
258   return gst_rtsp_message_init_response (newmsg, code, reason, request);
259 }
260
261 /**
262  * gst_rtsp_message_init_response:
263  * @msg: a #GstRTSPMessage
264  * @code: the status code
265  * @reason: the status reason or #NULL
266  * @request: the request that triggered the response or #NULL
267  *
268  * Initialize @msg with @code and @reason.
269  *
270  * When @reason is #NULL, the default reason for @code will be used.
271  *
272  * When @request is not #NULL, the relevant headers will be copied to the new
273  * response message.
274  *
275  * Returns: a #GstRTSPResult.
276  */
277 GstRTSPResult
278 gst_rtsp_message_init_response (GstRTSPMessage * msg, GstRTSPStatusCode code,
279     const gchar * reason, const GstRTSPMessage * request)
280 {
281   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
282
283   gst_rtsp_message_unset (msg);
284
285   if (reason == NULL)
286     reason = gst_rtsp_status_as_text (code);
287
288   msg->type = GST_RTSP_MESSAGE_RESPONSE;
289   msg->type_data.response.code = code;
290   msg->type_data.response.reason = g_strdup (reason);
291   msg->type_data.response.version = GST_RTSP_VERSION_1_0;
292   msg->hdr_fields = g_array_new (FALSE, FALSE, sizeof (RTSPKeyValue));
293
294   if (request) {
295     gchar *header;
296
297     /* copy CSEQ */
298     if (gst_rtsp_message_get_header (request, GST_RTSP_HDR_CSEQ, &header,
299             0) == GST_RTSP_OK) {
300       gst_rtsp_message_add_header (msg, GST_RTSP_HDR_CSEQ, header);
301     }
302
303     /* copy session id */
304     if (gst_rtsp_message_get_header (request, GST_RTSP_HDR_SESSION, &header,
305             0) == GST_RTSP_OK) {
306       char *pos;
307
308       header = g_strdup (header);
309       if ((pos = strchr (header, ';'))) {
310         *pos = '\0';
311       }
312       g_strchomp (header);
313       gst_rtsp_message_take_header (msg, GST_RTSP_HDR_SESSION, 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. Free with gst_rtsp_message_free().
363  *
364  * Returns: a #GstRTSPResult.
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_take_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. This function takes
500  * ownership of @value.
501  *
502  * Returns: a #GstRTSPResult.
503  *
504  * Since: 0.10.23
505  */
506 GstRTSPResult
507 gst_rtsp_message_take_header (GstRTSPMessage * msg, GstRTSPHeaderField field,
508     gchar * value)
509 {
510   RTSPKeyValue key_value;
511
512   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
513   g_return_val_if_fail (value != NULL, GST_RTSP_EINVAL);
514
515   key_value.field = field;
516   key_value.value = value;
517
518   g_array_append_val (msg->hdr_fields, key_value);
519
520   return GST_RTSP_OK;
521 }
522
523 /**
524  * gst_rtsp_message_add_header:
525  * @msg: a #GstRTSPMessage
526  * @field: a #GstRTSPHeaderField
527  * @value: the value of the header
528  *
529  * Add a header with key @field and @value to @msg. This function takes a copy
530  * of @value.
531  *
532  * Returns: a #GstRTSPResult.
533  */
534 GstRTSPResult
535 gst_rtsp_message_add_header (GstRTSPMessage * msg, GstRTSPHeaderField field,
536     const gchar * value)
537 {
538   return gst_rtsp_message_take_header (msg, field, g_strdup (value));
539 }
540
541 /**
542  * gst_rtsp_message_remove_header:
543  * @msg: a #GstRTSPMessage
544  * @field: a #GstRTSPHeaderField
545  * @indx: the index of the header
546  *
547  * Remove the @indx header with key @field from @msg. If @indx equals -1, all
548  * headers will be removed.
549  *
550  * Returns: a #GstRTSPResult.
551  */
552 GstRTSPResult
553 gst_rtsp_message_remove_header (GstRTSPMessage * msg, GstRTSPHeaderField field,
554     gint indx)
555 {
556   GstRTSPResult res = GST_RTSP_ENOTIMPL;
557   guint i = 0;
558   gint cnt = 0;
559
560   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
561
562   while (i < msg->hdr_fields->len) {
563     RTSPKeyValue *key_value = &g_array_index (msg->hdr_fields, RTSPKeyValue, i);
564
565     if (key_value->field == field && (indx == -1 || cnt++ == indx)) {
566       g_free (key_value->value);
567       g_array_remove_index (msg->hdr_fields, i);
568       res = GST_RTSP_OK;
569       if (indx != -1)
570         break;
571     } else {
572       i++;
573     }
574   }
575   return res;
576 }
577
578 /**
579  * gst_rtsp_message_get_header:
580  * @msg: a #GstRTSPMessage
581  * @field: a #GstRTSPHeaderField
582  * @value: pointer to hold the result
583  * @indx: the index of the header
584  *
585  * Get the @indx header value with key @field from @msg. The result in @value
586  * stays valid as long as it remains present in @msg.
587  *
588  * Returns: #GST_RTSP_OK when @field was found, #GST_RTSP_ENOTIMPL if the key
589  * was not found.
590  */
591 GstRTSPResult
592 gst_rtsp_message_get_header (const GstRTSPMessage * msg,
593     GstRTSPHeaderField field, gchar ** value, gint indx)
594 {
595   guint i;
596   gint cnt = 0;
597
598   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
599
600   /* no header initialized, there are no headers */
601   if (msg->hdr_fields == NULL)
602     return GST_RTSP_ENOTIMPL;
603
604   for (i = 0; i < msg->hdr_fields->len; i++) {
605     RTSPKeyValue *key_value = &g_array_index (msg->hdr_fields, RTSPKeyValue, i);
606
607     if (key_value->field == field && cnt++ == indx) {
608       if (value)
609         *value = key_value->value;
610       return GST_RTSP_OK;
611     }
612   }
613
614   return GST_RTSP_ENOTIMPL;
615 }
616
617 /**
618  * gst_rtsp_message_append_headers:
619  * @msg: a #GstRTSPMessage
620  * @str: a string
621  *
622  * Append the currently configured headers in @msg to the #GString @str suitable
623  * for transmission.
624  *
625  * Returns: #GST_RTSP_OK.
626  */
627 GstRTSPResult
628 gst_rtsp_message_append_headers (const GstRTSPMessage * msg, GString * str)
629 {
630   guint i;
631
632   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
633   g_return_val_if_fail (str != NULL, GST_RTSP_EINVAL);
634
635   for (i = 0; i < msg->hdr_fields->len; i++) {
636     RTSPKeyValue *key_value;
637     const gchar *keystr;
638
639     key_value = &g_array_index (msg->hdr_fields, RTSPKeyValue, i);
640     keystr = gst_rtsp_header_as_text (key_value->field);
641
642     g_string_append_printf (str, "%s: %s\r\n", keystr, key_value->value);
643   }
644   return GST_RTSP_OK;
645 }
646
647 /**
648  * gst_rtsp_message_set_body:
649  * @msg: a #GstRTSPMessage
650  * @data: the data
651  * @size: the size of @data
652  *
653  * Set the body of @msg to a copy of @data.
654  *
655  * Returns: #GST_RTSP_OK.
656  */
657 GstRTSPResult
658 gst_rtsp_message_set_body (GstRTSPMessage * msg, const guint8 * data,
659     guint size)
660 {
661   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
662
663   return gst_rtsp_message_take_body (msg, g_memdup (data, size), size);
664 }
665
666 /**
667  * gst_rtsp_message_take_body:
668  * @msg: a #GstRTSPMessage
669  * @data: the data
670  * @size: the size of @data
671  *
672  * Set the body of @msg to @data and @size. This method takes ownership of
673  * @data.
674  *
675  * Returns: #GST_RTSP_OK.
676  */
677 GstRTSPResult
678 gst_rtsp_message_take_body (GstRTSPMessage * msg, guint8 * data, guint size)
679 {
680   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
681   g_return_val_if_fail (data != NULL || size == 0, GST_RTSP_EINVAL);
682
683   if (msg->body)
684     g_free (msg->body);
685
686   msg->body = data;
687   msg->body_size = size;
688
689   return GST_RTSP_OK;
690 }
691
692 /**
693  * gst_rtsp_message_get_body:
694  * @msg: a #GstRTSPMessage
695  * @data: location for the data
696  * @size: location for the size of @data
697  *
698  * Get the body of @msg. @data remains valid for as long as @msg is valid and
699  * unchanged.
700  *
701  * Returns: #GST_RTSP_OK.
702  */
703 GstRTSPResult
704 gst_rtsp_message_get_body (const GstRTSPMessage * msg, guint8 ** data,
705     guint * size)
706 {
707   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
708   g_return_val_if_fail (data != NULL, GST_RTSP_EINVAL);
709   g_return_val_if_fail (size != NULL, GST_RTSP_EINVAL);
710
711   *data = msg->body;
712   *size = msg->body_size;
713
714   return GST_RTSP_OK;
715 }
716
717 /**
718  * gst_rtsp_message_steal_body:
719  * @msg: a #GstRTSPMessage
720  * @data: location for the data
721  * @size: location for the size of @data
722  *
723  * Take the body of @msg and store it in @data and @size. After this method,
724  * the body and size of @msg will be set to #NULL and 0 respectively.
725  *
726  * Returns: #GST_RTSP_OK.
727  */
728 GstRTSPResult
729 gst_rtsp_message_steal_body (GstRTSPMessage * msg, guint8 ** data, guint * size)
730 {
731   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
732   g_return_val_if_fail (data != NULL, GST_RTSP_EINVAL);
733   g_return_val_if_fail (size != NULL, GST_RTSP_EINVAL);
734
735   *data = msg->body;
736   *size = msg->body_size;
737
738   msg->body = NULL;
739   msg->body_size = 0;
740
741   return GST_RTSP_OK;
742 }
743
744 static void
745 dump_key_value (gpointer data, gpointer user_data)
746 {
747   RTSPKeyValue *key_value = (RTSPKeyValue *) data;
748
749   g_print ("   key: '%s', value: '%s'\n",
750       gst_rtsp_header_as_text (key_value->field), key_value->value);
751 }
752
753 /**
754  * gst_rtsp_message_dump:
755  * @msg: a #GstRTSPMessage
756  *
757  * Dump the contents of @msg to stdout.
758  *
759  * Returns: #GST_RTSP_OK.
760  */
761 GstRTSPResult
762 gst_rtsp_message_dump (GstRTSPMessage * msg)
763 {
764   guint8 *data;
765   guint size;
766
767   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
768
769   switch (msg->type) {
770     case GST_RTSP_MESSAGE_REQUEST:
771       g_print ("RTSP request message %p\n", msg);
772       g_print (" request line:\n");
773       g_print ("   method: '%s'\n",
774           gst_rtsp_method_as_text (msg->type_data.request.method));
775       g_print ("   uri:    '%s'\n", msg->type_data.request.uri);
776       g_print ("   version: '%s'\n",
777           gst_rtsp_version_as_text (msg->type_data.request.version));
778       g_print (" headers:\n");
779       key_value_foreach (msg->hdr_fields, dump_key_value, NULL);
780       g_print (" body:\n");
781       gst_rtsp_message_get_body (msg, &data, &size);
782       gst_util_dump_mem (data, size);
783       break;
784     case GST_RTSP_MESSAGE_RESPONSE:
785       g_print ("RTSP response message %p\n", msg);
786       g_print (" status line:\n");
787       g_print ("   code:   '%d'\n", msg->type_data.response.code);
788       g_print ("   reason: '%s'\n", msg->type_data.response.reason);
789       g_print ("   version: '%s'\n",
790           gst_rtsp_version_as_text (msg->type_data.response.version));
791       g_print (" headers:\n");
792       key_value_foreach (msg->hdr_fields, dump_key_value, NULL);
793       gst_rtsp_message_get_body (msg, &data, &size);
794       g_print (" body: length %d\n", size);
795       gst_util_dump_mem (data, size);
796       break;
797     case GST_RTSP_MESSAGE_DATA:
798       g_print ("RTSP data message %p\n", msg);
799       g_print (" channel: '%d'\n", msg->type_data.data.channel);
800       g_print (" size:    '%d'\n", msg->body_size);
801       gst_rtsp_message_get_body (msg, &data, &size);
802       gst_util_dump_mem (data, size);
803       break;
804     default:
805       g_print ("unsupported message type %d\n", msg->type);
806       return GST_RTSP_EINVAL;
807   }
808   return GST_RTSP_OK;
809 }