Add AL-FEC feature
[platform/upstream/gst-rtsp-server.git] / gst / rtsp-server / gstwfdmessage-ext.c
1 /* GStreamer
2  * Copyright (C) <2005,2006> Wim Taymans <wim@fluendo.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19 /*
20  * Unless otherwise indicated, Source Code is licensed under MIT license.
21  * See further explanation attached in License Statement (distributed in the file
22  * LICENSE).
23  *
24  * Permission is hereby granted, free of charge, to any person obtaining a copy of
25  * this software and associated documentation files (the "Software"), to deal in
26  * the Software without restriction, including without limitation the rights to
27  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
28  * of the Software, and to permit persons to whom the Software is furnished to do
29  * so, subject to the following conditions:
30  *
31  * The above copyright notice and this permission notice shall be included in all
32  * copies or substantial portions of the Software.
33  *
34  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
35  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
36  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
37  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
38  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
39  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
40  * SOFTWARE.
41  */
42
43 /**
44  * SECTION:gstwfdmessage
45  * @short_description: Helper methods for dealing with WFD messages
46  *
47  * <refsect2>
48  * <para>
49  * The GstWFDMessage helper functions makes it easy to parse and create WFD
50  * messages.
51  * </para>
52  * </refsect2>
53  */
54
55 #ifdef HAVE_CONFIG_H
56 #include "config.h"
57 #endif
58
59 #include <stdio.h>
60 #include <stdlib.h>
61 #include <string.h>
62
63 //#include <gio/gio.h>
64
65 #include "gstwfdmessage-ext.h"
66
67 #define FREE_STRING(field)              g_free (field); (field) = NULL
68 #define REPLACE_STRING(field, val)      FREE_STRING(field); (field) = g_strdup (val)
69
70 G_DEFINE_BOXED_TYPE (GstWFDExtMessage, gst_wfd_ext_message, NULL, NULL);
71
72 /**
73  * gst_wfd_ext_message_new:
74  * @msg: (out) (transfer full): pointer to new #GstWFDExtMessage
75  *
76  * Allocate a new GstWFDExtMessage and store the result in @msg.
77  *
78  * Returns: a #GstWFDResult.
79  */
80 GstWFDResult
81 gst_wfd_ext_message_new (GstWFDExtMessage ** msg)
82 {
83   GstWFDExtMessage *newmsg;
84
85   g_return_val_if_fail (msg != NULL, GST_WFD_EINVAL);
86
87   newmsg = g_new0 (GstWFDExtMessage, 1);
88
89   *msg = newmsg;
90
91   return gst_wfd_ext_message_init (newmsg);
92 }
93
94 /**
95  * gst_wfd_ext_message_init:
96  * @msg: a #GstWFDExtMessage
97  *
98  * Initialize @msg so that its contents are as if it was freshly allocated
99  * with gst_wfd_ext_message_new(). This function is mostly used to initialize a message
100  * allocated on the stack. gst_wfd_ext_message_uninit() undoes this operation.
101  *
102  * When this function is invoked on newly allocated data (with malloc or on the
103  * stack), its contents should be set to 0 before calling this function.
104  *
105  * Returns: a #GstWFDResult.
106  */
107 GstWFDResult
108 gst_wfd_ext_message_init (GstWFDExtMessage * msg)
109 {
110   g_return_val_if_fail (msg != NULL, GST_WFD_EINVAL);
111
112   return GST_WFD_OK;
113 }
114
115 /**
116  * gst_wfd_ext_message_uninit:
117  * @msg: a #GstWFDExtMessage
118  *
119  * Free all resources allocated in @msg. @msg should not be used anymore after
120  * this function. This function should be used when @msg was allocated on the
121  * stack and initialized with gst_wfd_ext_message_init().
122  *
123  * Returns: a #GstWFDResult.
124  */
125 GstWFDResult
126 gst_wfd_ext_message_uninit (GstWFDExtMessage * msg)
127 {
128   g_return_val_if_fail (msg != NULL, GST_WFD_EINVAL);
129
130   if (msg->tizen_retransmission) {
131     FREE_STRING (msg->tizen_retransmission);
132   }
133
134   if (msg->tizen_fec) {
135     FREE_STRING (msg->tizen_fec);
136   }
137
138   if (msg->tizen_latency_mode) {
139     FREE_STRING (msg->tizen_latency_mode);
140   }
141
142   return GST_WFD_OK;
143 }
144
145 static void
146 _read_string_space_ended (gchar * dest, guint size, gchar * src)
147 {
148   guint idx = 0;
149
150   while (!g_ascii_isspace (*src) && *src != '\0') {
151     if (idx < size - 1)
152       dest[idx++] = *src;
153     src++;
154   }
155
156   if (size > 0)
157     dest[idx] = '\0';
158
159   return;
160 }
161
162
163 static void
164 _read_string_attr_and_value (gchar * attr, gchar * value, guint tsize,
165     guint vsize, gchar del, gchar * src)
166 {
167   guint idx;
168
169   idx = 0;
170
171   while (*src != del && *src != '\0') {
172     if (idx < tsize - 1)
173       attr[idx++] = *src;
174     src++;
175   }
176
177   if (tsize > 0)
178     attr[idx] = '\0';
179
180   src++;
181   idx = 0;
182
183   while (*src != '\0') {
184     if (idx < vsize - 1)
185       value[idx++] = *src;
186     src++;
187   }
188
189   if (vsize > 0)
190     value[idx] = '\0';
191
192   return;
193 }
194
195 static void
196 gst_wfd_parse_attribute (gchar * buffer, GstWFDExtMessage * msg)
197 {
198   gchar attr[8192] = { 0 };
199   gchar value[8192] = { 0 };
200   gchar temp[8192] = { 0 };
201   gchar *p = buffer;
202   gchar *v = value;
203
204 #define WFD_SKIP_SPACE(q) if (*q && g_ascii_isspace (*q)) q++
205 #define WFD_SKIP_EQUAL(q) if (*q && *q == '=') q++
206 #define WFD_SKIP_COMMA(q) if (*q && g_ascii_ispunct (*q)) q++
207 #define WFD_READ_STRING(field) _read_string_space_ended (temp, sizeof (temp), v); v+=strlen(temp); REPLACE_STRING (field, temp)
208 #define WFD_READ_UINT32(field) _read_string_space_ended (temp, sizeof (temp), v); v+=strlen(temp); field = strtoul (temp, NULL, 16)
209 #define WFD_READ_UINT32_DIGIT(field) _read_string_space_ended (temp, sizeof (temp), v); v+=strlen(temp); field = strtoul (temp, NULL, 10)
210
211   _read_string_attr_and_value (attr, value, sizeof (attr), sizeof (value), ':',
212       p);
213
214   if (!g_strcmp0 (attr, GST_STRING_TIZEN_WFD_RESTRANSMISSION)) {
215     msg->tizen_retransmission = g_new0 (GstWFDTizenRetransmission, 1);
216     if (strlen (v)) {
217       if (!strstr (v, "none")) {
218         WFD_SKIP_SPACE (v);
219         WFD_READ_UINT32_DIGIT (msg->tizen_retransmission->rtp_port);
220         WFD_SKIP_SPACE (v);
221         WFD_READ_UINT32_DIGIT (msg->tizen_retransmission->rtcp_port);
222       }
223     }
224   }
225   else if (!g_strcmp0 (attr, GST_STRING_TIZEN_WFD_FEC)) {
226     msg->tizen_fec = g_new0 (GstWFDTizenFec, 1);
227     if (strlen (v)) {
228       if (!strstr (v, "none")) {
229         WFD_SKIP_SPACE (v);
230         WFD_READ_UINT32_DIGIT (msg->tizen_fec->t_max);
231         WFD_SKIP_SPACE (v);
232         WFD_READ_UINT32_DIGIT (msg->tizen_fec->p_max);
233       }
234     }
235   } else if (!g_strcmp0 (attr, GST_STRING_TIZEN_WFD_LATENCY_MODE)) {
236     msg->tizen_latency_mode = g_new0 (GstWFDTizenLatencyMode, 1);
237     if (strlen (v)) {
238       if (strstr (v, GST_STRING_TIZEN_WFD_LATENCY_LOW)) {
239         msg->tizen_latency_mode->latency_mode = GST_WFD_TIZEN_LATENCY_LOW;
240       } else if (strstr (v, GST_STRING_TIZEN_WFD_LATENCY_MID)) {
241         msg->tizen_latency_mode->latency_mode = GST_WFD_TIZEN_LATENCY_MID;
242       } else if (strstr (v, GST_STRING_TIZEN_WFD_LATENCY_HIGH)) {
243         msg->tizen_latency_mode->latency_mode = GST_WFD_TIZEN_LATENCY_HIGH;
244       } else {
245         msg->tizen_latency_mode->latency_mode = GST_WFD_TIZEN_LATENCY_NONE;
246       }
247     }
248   }
249
250   return;
251 }
252
253 /**
254  * gst_wfd_ext_message_parse_buffer:
255  * @data: the start of the buffer
256  * @size: the size of the buffer
257  * @msg: the result #GstSDPMessage
258  *
259  * Parse the contents of @size bytes pointed to by @data and store the result in
260  * @msg.
261  *
262  * Returns: #GST_SDP_OK on success.
263  */
264 GstWFDResult
265 gst_wfd_ext_message_parse_buffer (const guint8 * data, guint size,
266     GstWFDExtMessage * msg)
267 {
268   gchar *p;
269   gchar buffer[255] = { 0 };
270   guint idx = 0;
271
272   g_return_val_if_fail (msg != NULL, GST_WFD_EINVAL);
273   g_return_val_if_fail (data != NULL, GST_WFD_EINVAL);
274   g_return_val_if_fail (size != 0, GST_WFD_EINVAL);
275
276   p = (gchar *) data;
277   while (TRUE) {
278
279     if (*p == '\0')
280       break;
281
282     idx = 0;
283     while (*p != '\n' && *p != '\r' && *p != '\0') {
284       if (idx < sizeof (buffer) - 1)
285         buffer[idx++] = *p;
286       p++;
287     }
288     buffer[idx] = '\0';
289     gst_wfd_parse_attribute (buffer, msg);
290
291     if (*p == '\0')
292       break;
293     p += 2;
294   }
295   return GST_WFD_OK;
296 }
297
298 /**
299  * gst_wfd_ext_message_free:
300  * @msg: a #GstWFDExtMessage
301  *
302  * Free all resources allocated by @msg. @msg should not be used anymore after
303  * this function. This function should be used when @msg was dynamically
304  * allocated with gst_wfd_ext_message_new().
305  *
306  * Returns: a #GstWFDResult.
307  */
308 GstWFDResult
309 gst_wfd_ext_message_free (GstWFDExtMessage * msg)
310 {
311   g_return_val_if_fail (msg != NULL, GST_WFD_EINVAL);
312
313   gst_wfd_ext_message_uninit (msg);
314   g_free (msg);
315
316   return GST_WFD_OK;
317 }
318
319 /**
320  * gst_wfd_ext_message_as_text:
321  * @msg: a #GstWFDExtMessage
322  *
323  * Convert the contents of @msg to a text string.
324  *
325  * Returns: A dynamically allocated string representing the WFD description.
326  */
327 gchar *
328 gst_wfd_ext_message_as_text (const GstWFDExtMessage * msg)
329 {
330   /* change all vars so they match rfc? */
331   GString *lines;
332
333   g_return_val_if_fail (msg != NULL, NULL);
334
335   lines = g_string_new ("");
336
337   if (msg->tizen_retransmission) {
338     g_string_append_printf (lines, GST_STRING_TIZEN_WFD_RESTRANSMISSION);
339     g_string_append_printf (lines, ":");
340     g_string_append_printf (lines, " %d", msg->tizen_retransmission->rtp_port);
341     g_string_append_printf (lines, " %d", msg->tizen_retransmission->rtcp_port);
342     g_string_append_printf (lines, "\r\n");
343   }
344
345   if (msg->tizen_fec) {
346     g_string_append_printf (lines, GST_STRING_TIZEN_WFD_FEC);
347     g_string_append_printf (lines, ":");
348     g_string_append_printf (lines, " %d", msg->tizen_fec->t_max);
349     g_string_append_printf (lines, " %d", msg->tizen_fec->p_max);
350     g_string_append_printf (lines, "\r\n");
351   }
352
353   if (msg->tizen_latency_mode) {
354     g_string_append_printf (lines, GST_STRING_TIZEN_WFD_LATENCY_MODE);
355     g_string_append_printf (lines, ":");
356
357     if (msg->tizen_latency_mode->latency_mode == GST_WFD_TIZEN_LATENCY_LOW)
358       g_string_append_printf (lines, " " GST_STRING_TIZEN_WFD_LATENCY_LOW);
359     else if (msg->tizen_latency_mode->latency_mode == GST_WFD_TIZEN_LATENCY_MID)
360       g_string_append_printf (lines, " " GST_STRING_TIZEN_WFD_LATENCY_MID);
361     else if (msg->tizen_latency_mode->latency_mode ==
362         GST_WFD_TIZEN_LATENCY_HIGH)
363       g_string_append_printf (lines, " " GST_STRING_TIZEN_WFD_LATENCY_HIGH);
364     else
365       g_string_append_printf (lines, " none");
366
367     g_string_append_printf (lines, "\r\n");
368   }
369
370   return g_string_free (lines, FALSE);
371 }
372
373 gchar *
374 gst_wfd_ext_message_param_names_as_text (const GstWFDExtMessage * msg)
375 {
376   /* change all vars so they match rfc? */
377   GString *lines;
378   g_return_val_if_fail (msg != NULL, NULL);
379
380   lines = g_string_new ("");
381
382   if (msg->tizen_retransmission) {
383     g_string_append_printf (lines, GST_STRING_TIZEN_WFD_RESTRANSMISSION);
384     g_string_append_printf (lines, "\r\n");
385   }
386   if (msg->tizen_fec) {
387     g_string_append_printf (lines, GST_STRING_TIZEN_WFD_FEC);
388     g_string_append_printf (lines, "\r\n");
389   }
390   if (msg->tizen_latency_mode) {
391     g_string_append_printf (lines, GST_STRING_TIZEN_WFD_LATENCY_MODE);
392     g_string_append_printf (lines, "\r\n");
393   }
394
395   return g_string_free (lines, FALSE);
396 }
397
398 /**
399  * gst_wfd_ext_message_dump:
400  * @msg: a #GstWFDExtMessage
401  *
402  * Dump the parsed contents of @msg to stdout.
403  *
404  * Returns: a #GstWFDResult.
405  */
406 GstWFDResult
407 gst_wfd_ext_message_dump (const GstWFDExtMessage * msg)
408 {
409   g_return_val_if_fail (msg != NULL, GST_WFD_EINVAL);
410
411   if (msg->tizen_retransmission) {
412     g_print ("tizen_wfd_retransmission: %d %d",
413         msg->tizen_retransmission->rtp_port,
414         msg->tizen_retransmission->rtcp_port);
415     g_print ("\r\n");
416   }
417
418   if (msg->tizen_fec) {
419     g_print ("tizen_wfd_fec: %d %d", msg->tizen_fec->t_max, msg->tizen_fec->p_max);
420     g_print ("\r\n");
421   }
422
423   if (msg->tizen_latency_mode) {
424     g_print ("tizen_wfd_latency_mode:");
425
426     if (msg->tizen_latency_mode->latency_mode == GST_WFD_TIZEN_LATENCY_LOW)
427       g_print (" low");
428     else if (msg->tizen_latency_mode->latency_mode == GST_WFD_TIZEN_LATENCY_MID)
429       g_print (" mid");
430     else if (msg->tizen_latency_mode->latency_mode == GST_WFD_TIZEN_LATENCY_HIGH)
431       g_print (" high");
432     else
433       g_print (" none");
434
435     g_print ("\r\n");
436   }
437   return GST_WFD_OK;
438 }
439
440 GstWFDResult
441 gst_wfd_ext_message_set_tizen_retransmission (GstWFDExtMessage * msg,
442     guint rtp_port, guint rtcp_port)
443 {
444   g_return_val_if_fail (msg != NULL, GST_WFD_EINVAL);
445
446   if (!msg->tizen_retransmission)
447     msg->tizen_retransmission = g_new0 (GstWFDTizenRetransmission, 1);
448
449   msg->tizen_retransmission->rtp_port = rtp_port;
450   msg->tizen_retransmission->rtcp_port = rtcp_port;
451
452   return GST_WFD_OK;
453 }
454
455 GstWFDResult
456 gst_wfd_ext_message_get_tizen_retransmission (GstWFDExtMessage * msg,
457     guint * rtp_port, guint * rtcp_port)
458 {
459   g_return_val_if_fail (msg != NULL, GST_WFD_EINVAL);
460   g_return_val_if_fail (msg->tizen_retransmission != NULL, GST_WFD_EINVAL);
461   g_return_val_if_fail (rtp_port != NULL, GST_WFD_EINVAL);
462   g_return_val_if_fail (rtcp_port != NULL, GST_WFD_EINVAL);
463
464   *rtp_port = msg->tizen_retransmission->rtp_port;
465   *rtcp_port = msg->tizen_retransmission->rtcp_port;
466
467   return GST_WFD_OK;
468 }
469
470 GstWFDResult
471 gst_wfd_ext_message_set_tizen_fec (GstWFDExtMessage * msg, guint t_max,
472     guint p_max)
473 {
474   g_return_val_if_fail (msg != NULL, GST_WFD_EINVAL);
475
476   if (!msg->tizen_fec)
477     msg->tizen_fec = g_new0 (GstWFDTizenFec, 1);
478
479   msg->tizen_fec->t_max = t_max;
480   msg->tizen_fec->p_max = p_max;
481
482   return GST_WFD_OK;
483 }
484
485 GstWFDResult
486 gst_wfd_ext_message_get_tizen_fec (GstWFDExtMessage * msg, guint * t_max,
487     guint * p_max)
488 {
489   g_return_val_if_fail (msg != NULL, GST_WFD_EINVAL);
490   g_return_val_if_fail (msg->tizen_fec != NULL, GST_WFD_EINVAL);
491   g_return_val_if_fail (t_max != NULL, GST_WFD_EINVAL);
492   g_return_val_if_fail (p_max != NULL, GST_WFD_EINVAL);
493
494   *t_max = msg->tizen_fec->t_max;
495   *p_max = msg->tizen_fec->p_max;
496
497   return GST_WFD_OK;
498 }
499
500 GstWFDResult
501 gst_wfd_ext_message_set_tizen_latency_mode (GstWFDExtMessage * msg,
502     guint latency_mode)
503 {
504   g_return_val_if_fail (msg != NULL, GST_WFD_EINVAL);
505
506   if (!msg->tizen_latency_mode)
507     msg->tizen_latency_mode = g_new0 (GstWFDTizenLatencyMode, 1);
508
509   msg->tizen_latency_mode->latency_mode = latency_mode;
510
511   return GST_WFD_OK;
512 }
513
514 GstWFDResult
515 gst_wfd_ext_message_get_tizen_latency_mode (GstWFDExtMessage * msg,
516     guint * latency_mode)
517 {
518   g_return_val_if_fail (msg != NULL, GST_WFD_EINVAL);
519   g_return_val_if_fail (msg->tizen_latency_mode != NULL, GST_WFD_EINVAL);
520   g_return_val_if_fail (latency_mode != NULL, GST_WFD_EINVAL);
521
522   *latency_mode = msg->tizen_latency_mode->latency_mode;
523
524   return GST_WFD_OK;
525 }