a80c8dbe91f0041fa325aece7eeeb89b9529d740
[platform/upstream/gst-plugins-tizen.git] / wfdtizenmanager / gstwfdtizensrc.c
1 /*
2  * wfdtizensrc
3  *
4  * Copyright (c) 2000 - 2014 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Contact: JongHyuk Choi <jhchoi.choi@samsung.com>
7  *
8  * Permission is hereby granted, free of charge, to any person obtaining a
9  * copy of this software and associated documentation files (the "Software"),
10  * to deal in the Software without restriction, including without limitation
11  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12  * and/or sell copies of the Software, and to permit persons to whom the
13  * Software is furnished to do so, subject to the following conditions:
14  *
15  * The above copyright notice and this permission notice shall be included in
16  * all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24  * DEALINGS IN THE SOFTWARE.
25  *
26  * Alternatively, the contents of this file may be used under the
27  * GNU Lesser General Public License Version 2.1 (the "LGPL"), in
28  * which case the following provisions apply instead of the ones
29  * mentioned above:
30  *
31  * This library is free software; you can redistribute it and/or
32  * modify it under the terms of the GNU Library General Public
33  * License as published by the Free Software Foundation; either
34  * version 2 of the License, or (at your option) any later version.
35  *
36  * This library is distributed in the hope that it will be useful,
37  * but WITHOUT ANY WARRANTY; without even the implied warranty of
38  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
39  * Library General Public License for more details.
40  *
41  * You should have received a copy of the GNU Library General Public
42  * License along with this library; if not, write to the
43  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
44  * Boston, MA 02111-1307, USA.
45  */
46
47 /**
48 * SECTION:element-wfdtizensrc
49 *
50 * Makes a connection to an RTSP server and read the data.
51 * Device recognition is through wifi direct.
52 * wfdtizensrc strictly follows Wifi display specification.
53 *
54 * RTSP supports transport over TCP or UDP in unicast or multicast mode. By
55 * default wfdtizensrc will negotiate a connection in the following order:
56 * UDP unicast/UDP multicast/TCP. The order cannot be changed but the allowed
57 * protocols can be controlled with the #GstWFDTizenSrc:protocols property.
58 *
59 * wfdtizensrc currently understands WFD capability negotiation messages
60 *
61 * wfdtizensrc will internally instantiate an RTP session manager element
62 * that will handle the RTCP messages to and from the server, jitter removal,
63 * packet reordering along with providing a clock for the pipeline.
64 * This feature is implemented using the gstrtpbin element.
65 *
66 * wfdtizensrc acts like a live source and will therefore only generate data in the
67 * PLAYING state.
68 *
69 * <refsect2>
70 * <title>Example launch line</title>
71 * |[
72 * gst-launch wfdtizensrc location=rtsp://some.server/url ! fakesink
73 * ]| Establish a connection to an RTSP server and send the raw RTP packets to a
74 * fakesink.
75 * </refsect2>
76 */
77
78 #ifdef HAVE_CONFIG_H
79 #include "config.h"
80 #endif
81
82 #include <sys/ioctl.h>
83 #include <netdb.h>
84 #include <sys/socket.h>
85 #include <fcntl.h>
86 #include <netinet/in.h>
87
88 #include "gstwfdtizensrc.h"
89
90 GST_DEBUG_CATEGORY_STATIC (wfdtizensrc_debug);
91 #define GST_CAT_DEFAULT (wfdtizensrc_debug)
92
93 /* signals and args */
94 enum
95 {
96   LAST_SIGNAL
97 };
98
99 enum
100 {
101   PROP_0,
102   PROP_DO_RTCP,
103   PROP_LATENCY,
104   PROP_UDP_BUFFER_SIZE,
105   PROP_UDP_TIMEOUT,
106   PROP_LATENCY_MODE,
107   PROP_DO_REQUEST,
108   PROP_DO_FEC,
109   PROP_FEC_MAX_K,
110   PROP_FEC_MAX_P,
111   PROP_FEC_SYMBOL_LENGTH,
112   PROP_LAST
113 };
114
115 #define DEFAULT_DO_RTCP          TRUE
116 #define DEFAULT_LATENCY_MS       2000
117 #define DEFAULT_UDP_BUFFER_SIZE  0x80000
118 #define DEFAULT_UDP_TIMEOUT          10000000
119 #define DEFAULT_LATENCY_MODE   WFD_TIZEN_LATENCY_NONE
120 #define DEFAULT_DO_REQUEST       TRUE
121 #define DEFAULT_DO_FEC       TRUE
122 #define DEFAULT_FEC_MAX_K  10
123 #define DEFAULT_FEC_MAX_P  10
124 #define DEFAULT_FEC_SYMBOL_LENGTH  1500
125
126 /* object */
127 static void gst_wfd_tizen_src_set_property (GObject * object, guint prop_id,
128     const GValue * value, GParamSpec * pspec);
129 static void gst_wfd_tizen_src_get_property (GObject * object, guint prop_id,
130     GValue * value, GParamSpec * pspec);
131
132 /* wfdbasesrc */
133 static GstRTSPResult gst_wfd_tizen_src_handle_set_parameter (GstWFDBaseSrc *
134     bsrc, GstRTSPMessage * request, GstRTSPMessage * response);
135 static GstRTSPResult gst_wfd_tizen_src_handle_get_parameter (GstWFDBaseSrc *
136     bsrc, GstRTSPMessage * request, GstRTSPMessage * response);
137 static GstRTSPResult gst_wfd_tizen_src_configure_transport (GstWFDBaseSrc *
138     bsrc, GstRTSPTransport * transport);
139 static GstRTSPResult gst_wfd_tizen_src_prepare_transport (GstWFDBaseSrc * bsrc,
140     gint rtpport, gint rtcpport);
141 static gboolean gst_wfd_tizen_src_push_event (GstWFDBaseSrc * bsrc,
142     GstEvent * event);
143 static void gst_wfd_tizen_src_set_state (GstWFDBaseSrc * src, GstState state);
144 static void gst_wfd_tizen_src_cleanup (GstWFDBaseSrc * bsrc);
145
146 //static guint gst_wfd_ext_srcext_signals[LAST_SIGNAL] = { 0 };
147
148 GType
149 wfd_tizen_latency_mode_get_type (void)
150 {
151   static GType wfd_tizen_latency_mode_type = 0;
152   static const GEnumValue tizen_latency_modes[] = {
153     {WFD_TIZEN_LATENCY_NONE, "none", "none"},
154     {WFD_TIZEN_LATENCY_LOW, "Low latency mode", "low"},
155     {WFD_TIZEN_LATENCY_MID, "Mid latency mode", "mid"},
156     {WFD_TIZEN_LATENCY_HIGH, "High latency mode", "high"},
157     {0, NULL, NULL},
158   };
159
160   if (!wfd_tizen_latency_mode_type) {
161     wfd_tizen_latency_mode_type =
162         g_enum_register_static ("WFDTizenLatencyMode", tizen_latency_modes);
163   }
164   return wfd_tizen_latency_mode_type;
165 }
166
167 #define _do_init \
168     GST_DEBUG_CATEGORY_INIT (wfdtizensrc_debug, "wfdtizensrc", 0, "Wi-Fi Display Sink Tizen source");
169
170 #define gst_wfd_tizen_src_parent_class parent_class
171 G_DEFINE_TYPE_WITH_CODE (GstWFDTizenSrc, gst_wfd_tizen_src,
172     GST_TYPE_WFD_BASE_SRC, _do_init);
173
174 static void
175 gst_wfd_tizen_src_class_init (GstWFDTizenSrcClass * klass)
176 {
177   GObjectClass *gobject_class;
178   GstElementClass *gstelement_class;
179   GstWFDBaseSrcClass *gstwfdbasesrc_class;
180
181   gobject_class = (GObjectClass *) klass;
182   gstelement_class = (GstElementClass *) klass;
183   gstwfdbasesrc_class = (GstWFDBaseSrcClass *) klass;
184
185   gobject_class->set_property = gst_wfd_tizen_src_set_property;
186   gobject_class->get_property = gst_wfd_tizen_src_get_property;
187
188   g_object_class_install_property (gobject_class, PROP_DO_RTCP,
189       g_param_spec_boolean ("do-rtcp", "Do RTCP",
190           "Send RTCP packets, disable for old incompatible server.",
191           DEFAULT_DO_RTCP, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
192
193   g_object_class_install_property (gobject_class, PROP_LATENCY,
194       g_param_spec_uint ("latency", "Buffer latency in ms",
195           "Amount of ms to buffer", 0, G_MAXUINT, DEFAULT_LATENCY_MS,
196           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
197
198   g_object_class_install_property (gobject_class, PROP_UDP_BUFFER_SIZE,
199       g_param_spec_int ("udp-buffer-size", "UDP Buffer Size",
200           "Size of the kernel UDP receive buffer in bytes, 0=default",
201           0, G_MAXINT, DEFAULT_UDP_BUFFER_SIZE,
202           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
203
204   g_object_class_install_property (gobject_class, PROP_UDP_TIMEOUT,
205       g_param_spec_uint64 ("timeout", "Timeout",
206           "Fail after timeout microseconds on UDP connections (0 = disabled)",
207           0, G_MAXUINT64, DEFAULT_UDP_TIMEOUT,
208           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
209
210   g_object_class_install_property (gobject_class, PROP_LATENCY_MODE,
211       g_param_spec_enum ("latency-mode", "Latency Mode",
212           "Current latency mode", WFD_TIZEN_LATENCY_MODE,
213           DEFAULT_LATENCY_MODE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
214
215   g_object_class_install_property (gobject_class, PROP_DO_REQUEST,
216       g_param_spec_boolean ("do-request", "Enable RTP Retransmission Request",
217           "Send RTCP FB packets and handel retransmitted RTP packets.",
218           DEFAULT_DO_REQUEST, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
219
220   g_object_class_install_property (gobject_class, PROP_DO_FEC,
221       g_param_spec_boolean ("do-fec", "Enable Forward Error Correction",
222           "Enabel Forward Error Correction decoding",
223           DEFAULT_DO_FEC, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
224
225   g_object_class_install_property (gobject_class, PROP_FEC_MAX_K,
226       g_param_spec_uint ("fec-max-k",
227           "Max. size (k) for Forward Error Correction",
228           "Max. number of source symbol in a block", 1, G_MAXUINT,
229           DEFAULT_FEC_MAX_K, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
230
231   g_object_class_install_property (gobject_class, PROP_FEC_MAX_P,
232       g_param_spec_uint ("fec-max-p",
233           "Max. size (p) for Forward Error Correction",
234           "Max. number of parity symbol in a block", 0, G_MAXUINT,
235           DEFAULT_FEC_MAX_P, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
236
237   g_object_class_install_property (gobject_class, PROP_FEC_SYMBOL_LENGTH,
238       g_param_spec_uint ("fec-symbol-length",
239           "Symbol length for Forward Error Correction",
240           "Length of block symbol in bytes", 0, G_MAXUINT,
241           DEFAULT_FEC_SYMBOL_LENGTH,
242           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
243
244   gst_element_class_set_static_metadata (gstelement_class,
245       "Wi-Fi Display Sink source element", "Source/Network",
246       "Negotiate the capability and receive the RTP packets from the Wi-Fi Display source",
247       "YeJin Cho <cho.yejin@samsung.com>");
248
249   gstwfdbasesrc_class->handle_set_parameter =
250       GST_DEBUG_FUNCPTR (gst_wfd_tizen_src_handle_set_parameter);
251   gstwfdbasesrc_class->handle_get_parameter =
252       GST_DEBUG_FUNCPTR (gst_wfd_tizen_src_handle_get_parameter);
253   gstwfdbasesrc_class->configure_transport =
254       GST_DEBUG_FUNCPTR (gst_wfd_tizen_src_configure_transport);
255   gstwfdbasesrc_class->prepare_transport =
256       GST_DEBUG_FUNCPTR (gst_wfd_tizen_src_prepare_transport);
257   gstwfdbasesrc_class->push_event =
258       GST_DEBUG_FUNCPTR (gst_wfd_tizen_src_push_event);
259   gstwfdbasesrc_class->set_state =
260       GST_DEBUG_FUNCPTR (gst_wfd_tizen_src_set_state);
261   gstwfdbasesrc_class->cleanup = GST_DEBUG_FUNCPTR (gst_wfd_tizen_src_cleanup);
262 }
263
264 static void
265 gst_wfd_tizen_src_init (GstWFDTizenSrc * src)
266 {
267   gint i;
268
269   src->do_rtcp = DEFAULT_DO_RTCP;
270   src->latency = DEFAULT_LATENCY_MS;
271   src->udp_buffer_size = DEFAULT_UDP_BUFFER_SIZE;
272   src->udp_timeout = DEFAULT_UDP_TIMEOUT;
273   src->latency_mode = DEFAULT_LATENCY_MODE;
274   src->do_request = DEFAULT_DO_REQUEST;
275   src->do_fec = DEFAULT_DO_FEC;
276   src->fec_max_k = DEFAULT_FEC_MAX_K;
277   src->fec_max_p = DEFAULT_FEC_MAX_P;
278   src->fec_symbol_length = DEFAULT_FEC_SYMBOL_LENGTH;
279
280   src->fecdec = NULL;
281   src->session = NULL;
282   src->requester = NULL;
283   src->wfdrtpbuffer = NULL;
284   for (i = 0; i < 3; i++) {
285     src->channelpad[i] = NULL;
286     src->udpsrc[i] = NULL;
287     src->udpsink[i] = NULL;
288   }
289   src->blockid = 0;
290   src->blockedpad = NULL;
291 }
292
293 static void
294 gst_wfd_tizen_src_set_property (GObject * object, guint prop_id,
295     const GValue * value, GParamSpec * pspec)
296 {
297   GstWFDTizenSrc *src = GST_WFD_TIZEN_SRC (object);
298
299   switch (prop_id) {
300     case PROP_DO_RTCP:
301       src->do_rtcp = g_value_get_boolean (value);
302       break;
303     case PROP_LATENCY:
304       src->latency = g_value_get_uint (value);
305       break;
306     case PROP_UDP_BUFFER_SIZE:
307       src->udp_buffer_size = g_value_get_int (value);
308       break;
309     case PROP_UDP_TIMEOUT:
310       src->udp_timeout = g_value_get_uint64 (value);
311       break;
312     case PROP_LATENCY_MODE:
313       src->latency_mode = g_value_get_enum (value);
314       break;
315     case PROP_DO_REQUEST:
316       src->do_request = g_value_get_boolean (value);
317       break;
318     case PROP_DO_FEC:
319       src->do_fec = g_value_get_boolean (value);
320       break;
321     case PROP_FEC_MAX_K:
322       src->fec_max_k = g_value_get_uint (value);
323       break;
324     case PROP_FEC_MAX_P:
325       src->fec_max_p = g_value_get_uint (value);
326       break;
327     case PROP_FEC_SYMBOL_LENGTH:
328       src->fec_symbol_length = g_value_get_uint (value);
329       break;
330     default:
331       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
332       break;
333   }
334 }
335
336 static void
337 gst_wfd_tizen_src_get_property (GObject * object, guint prop_id, GValue * value,
338     GParamSpec * pspec)
339 {
340   GstWFDTizenSrc *src = GST_WFD_TIZEN_SRC (object);
341
342   switch (prop_id) {
343     case PROP_DO_RTCP:
344       g_value_set_boolean (value, src->do_rtcp);
345       break;
346     case PROP_LATENCY:
347       g_value_set_uint (value, src->latency);
348       break;
349     case PROP_UDP_BUFFER_SIZE:
350       g_value_set_int (value, src->udp_buffer_size);
351       break;
352     case PROP_UDP_TIMEOUT:
353       g_value_set_uint64 (value, src->udp_timeout);
354       break;
355     case PROP_LATENCY_MODE:
356       g_value_set_enum (value, src->latency_mode);
357       break;
358     case PROP_DO_REQUEST:
359       g_value_set_boolean (value, src->do_request);
360       break;
361     case PROP_DO_FEC:
362       g_value_set_boolean (value, src->do_fec);
363       break;
364     case PROP_FEC_MAX_K:
365       g_value_set_uint (value, src->fec_max_k);
366       break;
367     case PROP_FEC_MAX_P:
368       g_value_set_uint (value, src->fec_max_p);
369       break;
370     case PROP_FEC_SYMBOL_LENGTH:
371       g_value_set_uint (value, src->fec_symbol_length);
372       break;
373     default:
374       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
375       break;
376   }
377 }
378
379 static GstRTSPResult
380 gst_wfd_tizen_src_handle_mode (GstWFDTizenSrc * src, guint mode)
381 {
382   GstRTSPResult res = GST_RTSP_OK;
383
384   GST_DEBUG_OBJECT (src, "latency mode is %d", mode);
385
386   src->latency_mode = mode;
387   switch (mode) {
388     case WFD_TIZEN_LATENCY_LOW:
389       GST_DEBUG_OBJECT (src, "low latency mode");
390       if (src->wfdrtpbuffer)
391         g_object_set (src->wfdrtpbuffer, "latency", src->latency, NULL);
392       if (src->fecdec)
393         g_object_set (src->fecdec, "do-fec", FALSE, NULL);
394       if (src->requester)
395         g_object_set (src->requester, "do-request", FALSE, NULL);
396       break;
397     case WFD_TIZEN_LATENCY_MID:
398       GST_DEBUG_OBJECT (src, "mid latency mode");
399       if (src->wfdrtpbuffer)
400         g_object_set (src->wfdrtpbuffer, "latency", src->latency * 2, NULL);
401       if (src->fecdec && src->do_fec)
402         g_object_set (src->fecdec, "do-fec", TRUE, NULL);
403       if (src->requester)
404         g_object_set (src->requester, "do-request", FALSE, NULL);
405       break;
406     case WFD_TIZEN_LATENCY_HIGH:
407       GST_DEBUG_OBJECT (src, "high latency mode");
408       if (src->wfdrtpbuffer)
409         g_object_set (src->wfdrtpbuffer, "latency", src->latency * 3, NULL);
410       if (src->fecdec && src->do_fec)
411         g_object_set (src->fecdec, "do-fec", TRUE, NULL);
412       if (src->requester && src->do_request)
413         g_object_set (src->requester, "do-request", TRUE, NULL);
414       break;
415     default:
416       break;
417   }
418
419   return res;
420 }
421
422 static GstRTSPResult
423 gst_wfd_tizen_src_handle_set_parameter (GstWFDBaseSrc * bsrc,
424     GstRTSPMessage * request, GstRTSPMessage * response)
425 {
426   GstWFDTizenSrc *src = GST_WFD_TIZEN_SRC (bsrc);
427   GstRTSPResult res = GST_RTSP_OK;
428   GstWFDResult wfd_res = GST_WFD_OK;
429   GstWFDTizenMessage *msg = NULL;
430   GstRTSPMethod method;
431   GstRTSPVersion version;
432   const gchar *uri;
433   guint8 *data = NULL;
434   guint size = 0;
435
436   g_return_val_if_fail (request, GST_RTSP_EINVAL);
437   g_return_val_if_fail (response, GST_RTSP_EINVAL);
438
439   res = gst_rtsp_message_parse_request (request, &method, &uri, &version);
440   if (res < 0)
441     goto error;
442
443   if (G_UNLIKELY (method != GST_RTSP_SET_PARAMETER))
444     goto error;
445
446   res = gst_rtsp_message_get_body (request, &data, &size);
447   if (res < 0)
448     goto error;
449
450   wfd_res = gst_wfd_tizen_message_new (&msg);
451   if (wfd_res < 0)
452     goto error;
453
454   wfd_res = gst_wfd_tizen_message_parse_buffer (data, size, msg);
455   if (wfd_res != GST_WFD_OK)
456     goto error;
457
458   if (msg->tizen_retransmission) {
459     guint32 rtp_port = 0, rtcp_port = 0;
460
461     wfd_res =
462         gst_wfd_tizen_message_get_tizen_retransmission (msg, &rtp_port,
463         &rtcp_port);
464     if (wfd_res != GST_WFD_OK)
465       goto error;
466
467     GST_DEBUG_OBJECT (src, "tizen_retransmission : RTP port %d, RTCP port %d ",
468         rtp_port, rtcp_port);
469   }
470
471   if (msg->tizen_fec) {
472     guint t_max = 0, p_max = 0;
473
474     wfd_res = gst_wfd_tizen_message_get_tizen_fec (msg, &t_max, &p_max);
475     if (wfd_res != GST_WFD_OK)
476       goto error;
477
478     src->fec_max_k = t_max;
479     src->fec_max_p = p_max;
480
481     GST_DEBUG_OBJECT (src, "tizen_fec : t max %d, p max %d ", t_max, p_max);
482   }
483
484   if (msg->tizen_latency_mode) {
485     guint mode = WFD_TIZEN_LATENCY_NONE;
486
487     wfd_res = gst_wfd_tizen_message_get_tizen_latency_mode (msg, &mode);
488     if (wfd_res != GST_WFD_OK)
489       goto error;
490
491     GST_DEBUG_OBJECT (src, "tizen_latency_mode : %d", mode);
492
493     res = gst_wfd_tizen_src_handle_mode (src, mode);
494     if (res != GST_RTSP_OK)
495       goto error;
496   }
497
498   gst_wfd_tizen_message_free (msg);
499
500   return res;
501
502 /* ERRORS */
503 error:
504   {
505     if (msg)
506       gst_wfd_tizen_message_free (msg);
507
508     GST_ERROR_OBJECT (src, "Could not handle message");
509     return res;
510   }
511 }
512
513 static GstRTSPResult
514 gst_wfd_tizen_src_handle_get_parameter (GstWFDBaseSrc * bsrc,
515     GstRTSPMessage * request, GstRTSPMessage * response)
516 {
517   GstWFDTizenSrc *src = GST_WFD_TIZEN_SRC (bsrc);
518   GstRTSPResult res = GST_RTSP_OK;
519   GstWFDResult wfd_res = GST_WFD_OK;
520   GstWFDTizenMessage *msg = NULL;
521   GstRTSPMethod method;
522   GstRTSPVersion version;
523   const gchar *uri;
524   guint8 *data = NULL;
525   guint size = 0;
526
527   GString *body = NULL;
528   GString *body_length = NULL;
529
530   g_return_val_if_fail (request, GST_RTSP_EINVAL);
531   g_return_val_if_fail (response, GST_RTSP_EINVAL);
532
533   res = gst_rtsp_message_parse_request (request, &method, &uri, &version);
534   if (res < 0)
535     goto error;
536
537   if (G_UNLIKELY (method != GST_RTSP_GET_PARAMETER))
538     goto error;
539
540   res = gst_rtsp_message_get_body (request, &data, &size);
541   if (res < 0)
542     goto error;
543
544   wfd_res = gst_wfd_tizen_message_new (&msg);
545   if (wfd_res < 0)
546     goto error;
547
548   wfd_res = gst_wfd_tizen_message_parse_buffer (data, size, msg);
549   if (wfd_res != GST_WFD_OK)
550     goto error;
551
552   if (msg->tizen_retransmission) {
553     if (src->do_request) {
554       guint32 rtp_port = RETRANSMITTED_RTP_PORT, rtcp_port = RTCP_FB_PORT;
555
556       wfd_res =
557           gst_wfd_tizen_message_set_tizen_retransmission (msg, rtp_port,
558           rtcp_port);
559       if (wfd_res != GST_WFD_OK)
560         goto error;
561
562       GST_DEBUG_OBJECT (src,
563           "tizen_retransmission : RTP port %d, RTCP port %d ", rtp_port,
564           rtcp_port);
565     }
566   }
567
568   if (msg->tizen_fec) {
569     if (src->do_fec) {
570       wfd_res =
571           gst_wfd_tizen_message_set_tizen_fec (msg, src->fec_max_k,
572           src->fec_max_p);
573       if (wfd_res != GST_WFD_OK)
574         goto error;
575
576       GST_DEBUG_OBJECT (src, "tizen_fec : t max %d, p max %d ", src->fec_max_k,
577           src->fec_max_p);
578     }
579   }
580
581   if (msg->tizen_latency_mode) {
582     wfd_res =
583         gst_wfd_tizen_message_set_tizen_latency_mode (msg,
584         WFD_TIZEN_LATENCY_MID);
585     if (wfd_res != GST_WFD_OK)
586       goto error;
587
588     GST_DEBUG_OBJECT (src, "tizen_latency_mode : %d", src->latency_mode);
589   }
590
591   res = gst_rtsp_message_steal_body (response, &data, &size);
592   if (res != GST_RTSP_OK)
593     goto error;
594
595   body = g_string_new ((const gchar *) data);
596   g_string_append (body, (const gchar *) gst_wfd_tizen_message_as_text (msg));
597   if (body == NULL) {
598     GST_ERROR_OBJECT (src, "gst_wfd_tizen_message_as_text is failed");
599     goto error;
600   }
601
602   body_length = g_string_new ("");
603   g_string_append_printf (body_length, "%d", body->len);
604   GST_DEBUG_OBJECT (src, "body_length :  %s", body_length->str);
605
606   gst_rtsp_message_remove_header (response, GST_RTSP_HDR_CONTENT_LENGTH, -1);
607   gst_rtsp_message_add_header (response, GST_RTSP_HDR_CONTENT_LENGTH,
608       g_string_free (body_length, FALSE));
609
610   GST_DEBUG_OBJECT (src, "body :  %s", body->str);
611
612   res =
613       gst_rtsp_message_set_body (response, (const guint8 *) body->str,
614       body->len);
615   if (res < 0)
616     goto error;
617
618   g_string_free (body, FALSE);
619
620   gst_wfd_tizen_message_free (msg);
621
622   return res;
623
624 /* ERRORS */
625 error:
626   {
627     if (msg)
628       gst_wfd_tizen_message_free (msg);
629     GST_ERROR_OBJECT (src, "Could not handle message");
630     return res;
631   }
632
633 }
634
635 static void
636 gst_wfd_tizen_src_set_state (GstWFDBaseSrc * bsrc, GstState state)
637 {
638   GstWFDTizenSrc *src = GST_WFD_TIZEN_SRC (bsrc);
639   gint i;
640
641   GST_DEBUG_OBJECT (src, "try to set %s state",
642       gst_element_state_get_name (state));
643
644   for (i = 0; i < 3; i++) {
645     if (src->udpsrc[i])
646       gst_element_set_state (src->udpsrc[i], state);
647     if (src->udpsink[i])
648       gst_element_set_state (src->udpsink[i], state);
649   }
650
651   if (src->fecdec)
652     gst_element_set_state (src->fecdec, state);
653
654   if (src->session)
655     gst_element_set_state (src->session, state);
656
657   if (src->requester)
658     gst_element_set_state (src->requester, state);
659
660   if (src->wfdrtpbuffer)
661     gst_element_set_state (src->wfdrtpbuffer, state);
662 }
663
664 static void
665 gst_wfd_tizen_src_cleanup (GstWFDBaseSrc * bsrc)
666 {
667   GstWFDTizenSrc *src = GST_WFD_TIZEN_SRC (bsrc);
668   gint i;
669
670   GST_DEBUG_OBJECT (src, "cleanup");
671
672   for (i = 0; i < 3; i++) {
673     if (src->channelpad[i]) {
674       gst_object_unref (src->channelpad[i]);
675       src->channelpad[i] = NULL;
676     }
677     if (src->udpsrc[i]) {
678       gst_element_set_state (src->udpsrc[i], GST_STATE_NULL);
679       gst_bin_remove (GST_BIN_CAST (src), src->udpsrc[i]);
680       gst_object_unref (src->udpsrc[i]);
681       src->udpsrc[i] = NULL;
682     }
683     if (src->udpsink[i]) {
684       gst_element_set_state (src->udpsink[i], GST_STATE_NULL);
685       gst_bin_remove (GST_BIN_CAST (src), src->udpsink[i]);
686       gst_object_unref (src->udpsink[i]);
687       src->udpsink[i] = NULL;
688     }
689   }
690   if (src->fecdec) {
691     gst_element_set_state (src->fecdec, GST_STATE_NULL);
692     gst_bin_remove (GST_BIN_CAST (src), src->fecdec);
693     gst_object_unref (src->fecdec);
694     src->fecdec = NULL;
695   }
696   if (src->session) {
697     gst_element_set_state (src->session, GST_STATE_NULL);
698     gst_bin_remove (GST_BIN_CAST (src), src->session);
699     gst_object_unref (src->session);
700     src->session = NULL;
701   }
702   if (src->requester) {
703     gst_element_set_state (src->requester, GST_STATE_NULL);
704     gst_bin_remove (GST_BIN_CAST (src), src->requester);
705     gst_object_unref (src->requester);
706     src->requester = NULL;
707   }
708   if (src->wfdrtpbuffer) {
709     gst_element_set_state (src->wfdrtpbuffer, GST_STATE_NULL);
710     gst_bin_remove (GST_BIN_CAST (src), src->wfdrtpbuffer);
711     gst_object_unref (src->wfdrtpbuffer);
712     src->wfdrtpbuffer = NULL;
713   }
714 }
715
716 static GstRTSPResult
717 gst_wfd_tizen_src_prepare_transport (GstWFDBaseSrc * bsrc, gint rtpport,
718     gint rtcpport)
719 {
720   GstWFDTizenSrc *src = GST_WFD_TIZEN_SRC (bsrc);
721   GstStateChangeReturn ret;
722   GstElement *udpsrc0, *udpsrc1, *udpsrc2;
723   gint tmp_rtp, tmp_rtcp, tmp_rtcp_fb;
724   const gchar *host;
725
726   udpsrc0 = NULL;
727   udpsrc1 = NULL;
728   udpsrc2 = NULL;
729
730   if (bsrc->is_ipv6)
731     host = "udp://[::0]";
732   else
733     host = "udp://0.0.0.0";
734
735   /* try to allocate 2 UDP ports */
736   udpsrc0 = gst_element_make_from_uri (GST_URI_SRC, host, NULL, NULL);
737   if (udpsrc0 == NULL)
738     goto no_udp_protocol;
739   g_object_set (G_OBJECT (udpsrc0), "port", rtpport, "reuse", TRUE, NULL);
740
741   if (src->udp_buffer_size != 0)
742     g_object_set (G_OBJECT (udpsrc0), "buffer-size", src->udp_buffer_size,
743         NULL);
744
745   GST_DEBUG_OBJECT (src, "starting RTP on port %d", rtpport);
746   ret = gst_element_set_state (udpsrc0, GST_STATE_READY);
747   if (ret == GST_STATE_CHANGE_FAILURE) {
748     GST_ERROR_OBJECT (src, "Unable to make udpsrc from RTP port %d", rtpport);
749     goto no_ports;
750   }
751
752   g_object_get (G_OBJECT (udpsrc0), "port", &tmp_rtp, NULL);
753   GST_DEBUG_OBJECT (src, "got RTP port %d", tmp_rtp);
754
755   /* check if port is even */
756   if ((tmp_rtp & 0x01) != 0) {
757     GST_DEBUG_OBJECT (src, "RTP port not even");
758     /* port not even, free RTP udpsrc */
759     goto no_ports;
760   }
761
762   /* allocate port+1 for RTCP now */
763   udpsrc1 = gst_element_make_from_uri (GST_URI_SRC, host, NULL, NULL);
764   if (udpsrc1 == NULL)
765     goto no_udp_protocol;
766
767   /* set port */
768   g_object_set (G_OBJECT (udpsrc1), "port", rtcpport, "reuse", TRUE, NULL);
769
770   GST_DEBUG_OBJECT (src, "starting RTCP on port %d", rtcpport);
771   ret = gst_element_set_state (udpsrc1, GST_STATE_READY);
772   if (ret == GST_STATE_CHANGE_FAILURE) {
773     GST_ERROR_OBJECT (src, "Unable to make udpsrc from RTCP port %d", rtcpport);
774     goto no_ports;
775   }
776
777   /* allocate port #19120 for retransmitted RTP now */
778   udpsrc2 = gst_element_make_from_uri (GST_URI_SRC, host, NULL, NULL);
779   if (udpsrc2 == NULL)
780     goto no_udp_protocol;
781
782   /* set port */
783   g_object_set (G_OBJECT (udpsrc2), "port", RETRANSMITTED_RTP_PORT, "reuse",
784       TRUE, NULL);
785
786   if (src->udp_buffer_size != 0)
787     g_object_set (G_OBJECT (udpsrc2), "buffer-size", src->udp_buffer_size,
788         NULL);
789
790   GST_DEBUG_OBJECT (src, "starting Retransmitted RTP on port %d",
791       RETRANSMITTED_RTP_PORT);
792   ret = gst_element_set_state (udpsrc2, GST_STATE_READY);
793   if (ret == GST_STATE_CHANGE_FAILURE) {
794     GST_ERROR_OBJECT (src,
795         "Unable to make udpsrc from Retransmitted RTP port %d",
796         RETRANSMITTED_RTP_PORT);
797     goto no_ports;
798   }
799
800   /* all fine, do port check */
801   g_object_get (G_OBJECT (udpsrc0), "port", &tmp_rtp, NULL);
802   g_object_get (G_OBJECT (udpsrc1), "port", &tmp_rtcp, NULL);
803   g_object_get (G_OBJECT (udpsrc2), "port", &tmp_rtcp_fb, NULL);
804
805   /* this should not happen... */
806   if (rtpport != tmp_rtp || rtcpport != tmp_rtcp
807       || tmp_rtcp_fb != RETRANSMITTED_RTP_PORT)
808     goto port_error;
809
810   /* we keep these elements, we configure all in configure_transport when the
811    * server told us to really use the UDP ports. */
812   src->udpsrc[0] = gst_object_ref_sink (udpsrc0);
813   src->udpsrc[1] = gst_object_ref_sink (udpsrc1);
814   src->udpsrc[2] = gst_object_ref_sink (udpsrc2);
815   gst_element_set_locked_state (src->udpsrc[0], TRUE);
816   gst_element_set_locked_state (src->udpsrc[1], TRUE);
817   gst_element_set_locked_state (src->udpsrc[2], TRUE);
818
819   return GST_RTSP_OK;
820
821   /* ERRORS */
822 no_udp_protocol:
823   {
824     GST_DEBUG_OBJECT (src, "could not get UDP source");
825     goto cleanup;
826   }
827 no_ports:
828   {
829     GST_DEBUG_OBJECT (src, "could not allocate UDP port pair");
830     goto cleanup;
831   }
832 port_error:
833   {
834     GST_DEBUG_OBJECT (src,
835         "ports don't match rtp: %d<->%d, rtcp: %d<->%d, retransmitted rtp: %d<->%d",
836         tmp_rtp, rtpport, tmp_rtcp, rtcpport, tmp_rtcp_fb,
837         RETRANSMITTED_RTP_PORT);
838     goto cleanup;
839   }
840 cleanup:
841   {
842     if (udpsrc0) {
843       gst_element_set_state (udpsrc0, GST_STATE_NULL);
844       gst_object_unref (udpsrc0);
845     }
846     if (udpsrc1) {
847       gst_element_set_state (udpsrc1, GST_STATE_NULL);
848       gst_object_unref (udpsrc1);
849     }
850     if (udpsrc2) {
851       gst_element_set_state (udpsrc2, GST_STATE_NULL);
852       gst_object_unref (udpsrc2);
853     }
854     return GST_RTSP_ERROR;
855   }
856 }
857
858 static void
859 request_idr_by_requester (GstElement * requester, GstWFDTizenSrc * src)
860 {
861   GstEvent *event = NULL;
862
863   GST_DEBUG_OBJECT (src, "try to request idr");
864
865   /* Send IDR request */
866   event =
867       gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM,
868       gst_structure_new ("GstWFDIDRRequest", NULL, NULL));
869
870   if (!gst_pad_send_event (GST_WFD_BASE_SRC_CAST (src)->srcpad, event))
871     GST_WARNING_OBJECT (src, "failed to send event for idr reuest");
872 }
873
874 static void
875 on_bye_ssrc (GObject * session, guint32 ssrc, GstWFDTizenSrc * src)
876 {
877   GST_DEBUG_OBJECT (src, "source in session received BYE");
878
879   //gst_wfdtizensrc_do_stream_eos (src, manager);
880 }
881
882 static void
883 on_new_ssrc (GObject * session, guint32 ssrc, GstWFDTizenSrc * src)
884 {
885   GST_DEBUG_OBJECT (src, "source in session received NEW");
886 }
887
888 static void
889 on_timeout (GObject * session, guint32 ssrc, GstWFDTizenSrc * src)
890 {
891   GST_DEBUG_OBJECT (src, "source in session timed out");
892
893   //gst_wfdtizensrc_do_stream_eos (src, manager);
894 }
895
896 static void
897 on_ssrc_active (GObject * session, guint32 ssrc, GstWFDTizenSrc * src)
898 {
899   GST_DEBUG_OBJECT (src, "source in session  is active");
900 }
901
902 static GstCaps *
903 request_pt_map_for_wfdrtpbuffer (GstElement * wfdrtpbuffer, guint pt,
904     GstWFDTizenSrc * src)
905 {
906   GstCaps *caps;
907
908   GST_DEBUG_OBJECT (src, "getting pt map for pt %d", pt);
909
910   GST_WFD_BASE_STATE_LOCK (src);
911   caps = GST_WFD_BASE_SRC_CAST (src)->caps;
912   if (caps)
913     gst_caps_ref (caps);
914   GST_WFD_BASE_STATE_UNLOCK (src);
915
916   return caps;
917 }
918
919 static GstCaps *
920 request_pt_map_for_session (GstElement * session, guint pt,
921     GstWFDTizenSrc * src)
922 {
923   GstCaps *caps;
924
925   GST_DEBUG_OBJECT (src, "getting pt map for pt %d", pt);
926
927   GST_WFD_BASE_STATE_LOCK (src);
928   caps = GST_WFD_BASE_SRC_CAST (src)->caps;
929   if (caps)
930     gst_caps_ref (caps);
931   GST_WFD_BASE_STATE_UNLOCK (src);
932
933   return caps;
934 }
935
936 static gboolean
937 gst_wfd_tizen_src_configure_manager (GstWFDTizenSrc * src)
938 {
939   GstPad *pad = NULL;
940
941   /* construct wfdtizensrc */
942   src->fecdec = gst_element_factory_make ("alfecdecoder", "wfdtizensrc_fecdec");
943   if (G_UNLIKELY (src->fecdec == NULL)) {
944     GST_ERROR_OBJECT (src, "could not create alfecdecoder element");
945     return FALSE;
946   } else {
947     gboolean do_fec = FALSE;
948
949     do_fec = (src->latency_mode >= WFD_TIZEN_LATENCY_MID && src->do_fec);
950     g_object_set (G_OBJECT (src->fecdec), "do-fec", do_fec, NULL);
951     g_object_set (G_OBJECT (src->fecdec), "max-size-k", src->fec_max_k, NULL);
952     g_object_set (G_OBJECT (src->fecdec), "max-size-p", src->fec_max_p, NULL);
953     g_object_set (G_OBJECT (src->fecdec), "do-reorder", TRUE, NULL);
954     g_object_set (G_OBJECT (src->fecdec), "symbol-length",
955         src->fec_symbol_length, NULL);
956
957     src->channelpad[0] = gst_element_get_static_pad (src->fecdec, "sink");
958     if (G_UNLIKELY (src->channelpad[0] == NULL)) {
959       GST_ERROR_OBJECT (src, "could not create rtp channel pad");
960       return FALSE;
961     }
962
963     /* we manage session element */
964     gst_element_set_locked_state (src->fecdec, TRUE);
965
966     if (!gst_bin_add (GST_BIN_CAST (src), src->fecdec)) {
967       GST_ERROR_OBJECT (src, "failed to add alfecdecoder to wfdtizensrc");
968       return FALSE;
969     }
970   }
971
972   src->session = gst_element_factory_make ("rtpsession", "wfdtizensrc_session");
973   if (G_UNLIKELY (src->session == NULL)) {
974     GST_ERROR_OBJECT (src, "could not create gstrtpsession element");
975     return FALSE;
976   } else {
977     GstPad *sinkpad;
978
979     g_signal_connect (src->session, "on-bye-ssrc", (GCallback) on_bye_ssrc,
980         src);
981     g_signal_connect (src->session, "on-bye-timeout", (GCallback) on_timeout,
982         src);
983     g_signal_connect (src->session, "on-timeout", (GCallback) on_timeout, src);
984     g_signal_connect (src->session, "on-ssrc-active",
985         (GCallback) on_ssrc_active, src);
986     g_signal_connect (src->session, "on-new-ssrc", (GCallback) on_new_ssrc,
987         src);
988     g_signal_connect (src->session, "request-pt-map",
989         (GCallback) request_pt_map_for_session, src);
990
991     g_object_set (G_OBJECT (src->session), "rtcp-min-interval",
992         (guint64) 1000000000, NULL);
993
994     sinkpad = gst_element_get_request_pad (src->session, "recv_rtp_sink");
995     if (G_UNLIKELY (sinkpad == NULL)) {
996       GST_ERROR_OBJECT (src, "could not create rtp sink pad");
997       return FALSE;
998     }
999     gst_object_unref (sinkpad);
1000
1001     src->channelpad[1] =
1002         gst_element_get_request_pad (src->session, "recv_rtcp_sink");
1003     if (G_UNLIKELY (src->channelpad[1] == NULL)) {
1004       GST_ERROR_OBJECT (src, "could not create rtcp channel pad");
1005       return FALSE;
1006     }
1007
1008     /* we manage session element */
1009     gst_element_set_locked_state (src->session, TRUE);
1010
1011     if (!gst_bin_add (GST_BIN_CAST (src), src->session)) {
1012       GST_ERROR_OBJECT (src, "failed to add rtpsession to wfdtizensrc");
1013       return FALSE;
1014     }
1015   }
1016
1017   src->requester =
1018       gst_element_factory_make ("wfdrtprequester", "wfdtizensrc_requester");
1019   if (G_UNLIKELY (src->requester == NULL)) {
1020     GST_ERROR_OBJECT (src, "could not create wfdrtprequester element");
1021     return FALSE;
1022   } else {
1023     gboolean do_request = FALSE;
1024
1025     g_signal_connect (src->requester, "request-idr",
1026         (GCallback) request_idr_by_requester, src);
1027
1028     do_request = (src->latency_mode == WFD_TIZEN_LATENCY_HIGH
1029         && src->do_request);
1030     g_object_set (src->requester, "do-request", do_request, NULL);
1031
1032     GST_DEBUG_OBJECT (src,
1033         "getting retransmitted RTP sink pad of gstrtprequester");
1034     src->channelpad[2] =
1035         gst_element_get_request_pad (src->requester, "retransmitted_rtp_sink");
1036     if (!src->channelpad[2]) {
1037       GST_DEBUG_OBJECT (src,
1038           "fail to get retransmitted RTP sink pad of gstrtprequester");
1039       return FALSE;
1040     }
1041
1042     /* we manage requester element */
1043     gst_element_set_locked_state (src->requester, TRUE);
1044
1045     if (!gst_bin_add (GST_BIN_CAST (src), src->requester)) {
1046       GST_ERROR_OBJECT (src, "failed to add wfdrtprequester to wfdtizensrc");
1047       return FALSE;
1048     }
1049   }
1050
1051   src->wfdrtpbuffer =
1052       gst_element_factory_make ("wfdrtpbuffer", "wfdtizensrc_wfdrtpbuffer");
1053   if (G_UNLIKELY (src->wfdrtpbuffer == NULL)) {
1054     GST_ERROR_OBJECT (src, "could not create wfdrtpbuffer element");
1055     return FALSE;
1056   } else {
1057     /* configure latency and packet lost */
1058     g_object_set (src->wfdrtpbuffer, "latency", src->latency, NULL);
1059
1060     g_signal_connect (src->wfdrtpbuffer, "request-pt-map",
1061         (GCallback) request_pt_map_for_wfdrtpbuffer, src);
1062
1063     /* we manage wfdrtpbuffer element */
1064     gst_element_set_locked_state (src->wfdrtpbuffer, TRUE);
1065
1066     if (!gst_bin_add (GST_BIN_CAST (src), src->wfdrtpbuffer)) {
1067       GST_ERROR_OBJECT (src, "failed to add wfdrtpbuffer to wfdtizensrc");
1068       return FALSE;
1069     }
1070   }
1071
1072   if (!gst_element_link_many (src->fecdec, src->session, src->requester,
1073           src->wfdrtpbuffer, NULL)) {
1074     GST_ERROR_OBJECT (src, "failed to link elements for wfdtizensrc");
1075     return FALSE;
1076   }
1077
1078   if (!gst_element_sync_state_with_parent (src->fecdec)) {
1079     GST_ERROR_OBJECT (src, "failed for %s to sync state with wfdtizensrc",
1080         GST_ELEMENT_NAME (src->fecdec));
1081     return FALSE;
1082   }
1083
1084   if (!gst_element_sync_state_with_parent (src->session)) {
1085     GST_ERROR_OBJECT (src, "failed for %s to sync state with wfdtizensrc",
1086         GST_ELEMENT_NAME (src->session));
1087     return FALSE;
1088   }
1089
1090   if (!gst_element_sync_state_with_parent (src->requester)) {
1091     GST_ERROR_OBJECT (src, "failed for %s to sync state with wfdtizensrc",
1092         GST_ELEMENT_NAME (src->requester));
1093     return FALSE;
1094   }
1095
1096   if (!gst_element_sync_state_with_parent (src->wfdrtpbuffer)) {
1097     GST_ERROR_OBJECT (src, "failed for %s to sync state with wfdtizensrc",
1098         GST_ELEMENT_NAME (src->wfdrtpbuffer));
1099     return FALSE;
1100   }
1101
1102   /* set ghost pad */
1103   pad = gst_element_get_static_pad (src->wfdrtpbuffer, "src");
1104   if (G_UNLIKELY (pad == NULL)) {
1105     GST_ERROR_OBJECT (src,
1106         "failed to get src pad of wfdrtpbuffer for setting ghost pad of wfdtizensrc");
1107     return FALSE;
1108   }
1109
1110   if (!gst_wfd_base_src_set_target (GST_WFD_BASE_SRC (src), pad)) {
1111     GST_ERROR_OBJECT (src, "failed to set target pad of ghost pad");
1112     gst_object_unref (pad);
1113     return FALSE;
1114   }
1115
1116   gst_object_unref (pad);
1117   return TRUE;
1118 }
1119
1120 static gboolean
1121 gst_wfd_tizen_src_configure_udp_sinks (GstWFDTizenSrc * src,
1122     GstRTSPTransport * transport)
1123 {
1124   GstPad *pad = NULL;
1125   GSocket *socket = NULL;
1126   gint rtp_port = -1, rtcp_port = -1, rtcp_fb_port = -1;
1127   gboolean do_rtcp, do_rtcp_fb;
1128   const gchar *destination = NULL;
1129   gchar *uri = NULL;
1130   GstPad *rtcp_fb_pad = NULL;
1131
1132   /* get transport info */
1133   gst_wfd_base_src_get_transport_info (GST_WFD_BASE_SRC (src), transport,
1134       &destination, &rtp_port, &rtcp_port);
1135   rtcp_fb_port = RTCP_FB_PORT;
1136
1137   /* it's possible that the server does not want us to send RTCP in which case
1138    * the port is -1 */
1139   do_rtcp = (rtcp_port != -1 && src->session != NULL && src->do_rtcp);
1140   do_rtcp_fb = (rtcp_fb_port != -1);
1141
1142   /* we need a destination when we have RTCP RR and RTCP FB ports */
1143   if (destination == NULL && (do_rtcp_fb || do_rtcp))
1144     goto no_destination;
1145
1146   if (do_rtcp) {
1147     GstPad *rtcppad = NULL;
1148
1149     GST_DEBUG_OBJECT (src, "configure RTCP UDP sink for %s:%d", destination,
1150         rtcp_port);
1151
1152     uri = g_strdup_printf ("udp://%s:%d", destination, rtcp_port);
1153     src->udpsink[1] = gst_element_make_from_uri (GST_URI_SINK, uri, NULL, NULL);
1154     g_free (uri);
1155     if (src->udpsink[1] == NULL)
1156       goto no_sink_element;
1157
1158     /* don't join multicast group, we will have the source socket do that */
1159     /* no sync or async state changes needed */
1160     g_object_set (G_OBJECT (src->udpsink[1]), "auto-multicast", FALSE, "loop",
1161         FALSE, "sync", FALSE, "async", FALSE, NULL);
1162
1163     if (src->udpsrc[1]) {
1164       /* configure socket, we give it the same UDP socket as the udpsrc for RTCP
1165        * because some servers check the port number of where it sends RTCP to identify
1166        * the RTCP packets it receives */
1167       g_object_get (G_OBJECT (src->udpsrc[1]), "used-socket", &socket, NULL);
1168       GST_DEBUG_OBJECT (src, "RTCP UDP src has sock %p", socket);
1169       /* configure socket and make sure udpsink does not close it when shutting
1170        * down, it belongs to udpsrc after all. */
1171       g_object_set (G_OBJECT (src->udpsink[1]), "socket", socket,
1172           "close-socket", FALSE, NULL);
1173       g_object_unref (socket);
1174     }
1175
1176     /* we don't want to consider this a sink */
1177     GST_OBJECT_FLAG_UNSET (src->udpsink[1], GST_ELEMENT_FLAG_SINK);
1178
1179     /* we keep this playing always */
1180     gst_element_set_locked_state (src->udpsink[1], TRUE);
1181     gst_element_set_state (src->udpsink[1], GST_STATE_PLAYING);
1182
1183     gst_object_ref (src->udpsink[1]);
1184     gst_bin_add (GST_BIN_CAST (src), src->udpsink[1]);
1185
1186     rtcppad = gst_element_get_static_pad (src->udpsink[1], "sink");
1187
1188     /* get session RTCP pad */
1189     pad = gst_element_get_request_pad (src->session, "send_rtcp_src");
1190
1191     /* and link */
1192     if (pad && rtcppad) {
1193       gst_pad_link_full (pad, rtcppad, GST_PAD_LINK_CHECK_NOTHING);
1194     }
1195     if (pad)
1196       gst_object_unref (pad);
1197     if (rtcppad)
1198       gst_object_unref (rtcppad);
1199   }
1200
1201   if (do_rtcp_fb) {
1202     GST_DEBUG_OBJECT (src, "configure RTCP FB sink for %s:%d", destination,
1203         rtcp_fb_port);
1204
1205     uri = g_strdup_printf ("udp://%s:%d", destination, rtcp_fb_port);
1206     src->udpsink[2] = gst_element_make_from_uri (GST_URI_SINK, uri, NULL, NULL);
1207     g_free (uri);
1208     if (src->udpsink[2] == NULL)
1209       goto no_sink_element;
1210
1211     /* don't join multicast group, we will have the source socket do that */
1212     /* no sync or async state changes needed */
1213     g_object_set (G_OBJECT (src->udpsink[2]), "auto-multicast", FALSE, "loop",
1214         FALSE, "sync", FALSE, "async", FALSE, NULL);
1215
1216     g_object_set (G_OBJECT (src->udpsink[2]), "bind-port", rtcp_fb_port,
1217         "close-socket", FALSE, NULL);
1218
1219     /* we don't want to consider this a sink */
1220     GST_OBJECT_FLAG_UNSET (src->udpsink[2], GST_ELEMENT_FLAG_SINK);
1221
1222     /* we keep this playing always */
1223     gst_element_set_locked_state (src->udpsink[2], TRUE);
1224     gst_element_set_state (src->udpsink[2], GST_STATE_PLAYING);
1225
1226     gst_object_ref (src->udpsink[2]);
1227     gst_bin_add (GST_BIN_CAST (src), src->udpsink[2]);
1228
1229     /* get RTCP FB sink pad */
1230     rtcp_fb_pad = gst_element_get_static_pad (src->udpsink[2], "sink");
1231
1232     /* get requester RTCP pad */
1233     pad = gst_element_get_static_pad (src->requester, "rtcp_src");
1234
1235     /* and link */
1236     if (rtcp_fb_pad && pad) {
1237       gst_pad_link (pad, rtcp_fb_pad);
1238     }
1239     if (pad)
1240       gst_object_unref (pad);
1241     if (rtcp_fb_pad)
1242       gst_object_unref (rtcp_fb_pad);
1243   }
1244
1245   return TRUE;
1246
1247   /* ERRORS */
1248 no_destination:
1249   {
1250     GST_DEBUG_OBJECT (src, "no destination address specified");
1251     return FALSE;
1252   }
1253 no_sink_element:
1254   {
1255     GST_DEBUG_OBJECT (src, "no UDP sink element found");
1256     return FALSE;
1257   }
1258 }
1259
1260 static void
1261 pad_blocked (GstPad * pad, gboolean blocked, GstWFDTizenSrc * src)
1262 {
1263   GST_DEBUG_OBJECT (src, "pad %s:%s blocked, activating streams",
1264       GST_DEBUG_PAD_NAME (pad));
1265
1266   if (src->udpsrc[0]) {
1267     /* remove timeout, we are streaming now and timeouts will be handled by
1268      * the session manager and jitter buffer */
1269     g_object_set (G_OBJECT (src->udpsrc[0]), "timeout", (guint64) 0, NULL);
1270   }
1271
1272   /* activate the streams */
1273   gst_wfd_base_src_activate (GST_WFD_BASE_SRC (src));
1274
1275   /* unblock all pads */
1276   if (src->blockedpad && src->blockid != 0) {
1277     GST_DEBUG_OBJECT (src, "unblocking blocked pad");
1278     gst_pad_remove_probe (src->blockedpad, src->blockid);
1279     src->blockid = 0;
1280     src->blockedpad = NULL;
1281   }
1282 }
1283
1284 static gboolean
1285 gst_wfd_tizen_src_configure_udp (GstWFDTizenSrc * src)
1286 {
1287   GstPad *outpad;
1288
1289   /* we manage the UDP elements now. For unicast, the UDP sources where
1290    * allocated in the stream when we suggested a transport. */
1291   if (src->udpsrc[0]) {
1292     GstCaps *caps;
1293
1294     gst_element_set_locked_state (src->udpsrc[0], TRUE);
1295     gst_bin_add (GST_BIN_CAST (src), src->udpsrc[0]);
1296
1297     GST_DEBUG_OBJECT (src, "setting up UDP source");
1298
1299     /* configure a timeout on the UDP port. When the timeout message is
1300      * posted */
1301     g_object_set (G_OBJECT (src->udpsrc[0]), "timeout", src->udp_timeout * 1000,
1302         NULL);
1303
1304     caps = gst_caps_new_simple ("application/x-rtp",
1305         "media", G_TYPE_STRING, "video", "payload", G_TYPE_INT, 33,
1306         "clock-rate", G_TYPE_INT, 90000, NULL);
1307     g_object_set (src->udpsrc[0], "caps", caps, NULL);
1308     gst_caps_unref (caps);
1309
1310     /* get output pad of the UDP source. */
1311     outpad = gst_element_get_static_pad (src->udpsrc[0], "src");
1312
1313     /* save it so we can unblock */
1314     src->blockedpad = outpad;
1315
1316     /* configure pad block on the pad. As soon as there is dataflow on the
1317      * UDP source, we know that UDP is not blocked by a firewall and we can
1318      * configure all the streams to let the application autoplug decoders. */
1319     src->blockid =
1320         gst_pad_add_probe (src->blockedpad,
1321         GST_PAD_PROBE_TYPE_BLOCK | GST_PAD_PROBE_TYPE_BUFFER |
1322         GST_PAD_PROBE_TYPE_BUFFER_LIST, (GstPadProbeCallback) pad_blocked, src,
1323         NULL);
1324
1325     if (src->channelpad[0]) {
1326       GST_DEBUG_OBJECT (src, "connecting UDP source 0 to session");
1327       /* configure for UDP delivery, we need to connect the UDP pads to
1328        * the session plugin. */
1329       gst_pad_link_full (outpad, src->channelpad[0],
1330           GST_PAD_LINK_CHECK_NOTHING);
1331       /* we connected to pad-added signal to get pads from the manager */
1332     } else {
1333       /* leave unlinked */
1334     }
1335   }
1336
1337   /* RTCP port */
1338   if (src->udpsrc[1]) {
1339     GstCaps *caps;
1340
1341     gst_element_set_locked_state (src->udpsrc[1], TRUE);
1342     gst_bin_add (GST_BIN_CAST (src), src->udpsrc[1]);
1343
1344     caps = gst_caps_new_empty_simple ("application/x-rtcp");
1345     g_object_set (src->udpsrc[1], "caps", caps, NULL);
1346     gst_caps_unref (caps);
1347
1348     if (src->channelpad[1]) {
1349       GstPad *pad;
1350
1351       GST_DEBUG_OBJECT (src, "connecting UDP source 1 to session");
1352
1353       pad = gst_element_get_static_pad (src->udpsrc[1], "src");
1354       gst_pad_link_full (pad, src->channelpad[1], GST_PAD_LINK_CHECK_NOTHING);
1355       gst_object_unref (pad);
1356     } else {
1357       /* leave unlinked */
1358     }
1359   }
1360
1361   /* Retransmitted RTP port */
1362   if (src->udpsrc[2]) {
1363     GstCaps *caps;
1364
1365     gst_element_set_locked_state (src->udpsrc[2], TRUE);
1366     gst_bin_add (GST_BIN_CAST (src), src->udpsrc[2]);
1367
1368     caps = gst_caps_new_simple ("application/x-rtp",
1369         "media", G_TYPE_STRING, "video", "payload", G_TYPE_INT, 33,
1370         "clock-rate", G_TYPE_INT, 90000, NULL);
1371     g_object_set (src->udpsrc[2], "caps", caps, NULL);
1372     gst_caps_unref (caps);
1373
1374     if (src->channelpad[2]) {
1375       GstPad *pad;
1376
1377       GST_DEBUG_OBJECT (src, "connecting UDP source 2 to requester");
1378       pad = gst_element_get_static_pad (src->udpsrc[2], "src");
1379       gst_pad_link_full (pad, src->channelpad[2], GST_PAD_LINK_CHECK_NOTHING);
1380       gst_object_unref (pad);
1381     } else {
1382       /* leave unlinked */
1383     }
1384   }
1385
1386   return TRUE;
1387 }
1388
1389 static GstRTSPResult
1390 gst_wfd_tizen_src_configure_transport (GstWFDBaseSrc * bsrc,
1391     GstRTSPTransport * transport)
1392 {
1393   GstWFDTizenSrc *src = GST_WFD_TIZEN_SRC (bsrc);
1394   const gchar *mime;
1395
1396   g_return_val_if_fail (transport, GST_RTSP_EINVAL);
1397
1398   GST_DEBUG_OBJECT (src, "configuring transport");
1399
1400   /* get the proper mime type for this manager now */
1401   if (gst_rtsp_transport_get_mime (transport->trans, &mime) < 0)
1402     goto unknown_transport;
1403   if (!mime)
1404     goto unknown_transport;
1405
1406   /* configure the final mime type */
1407   GST_DEBUG_OBJECT (src, "setting mime to %s", mime);
1408
1409   if (!gst_wfd_tizen_src_configure_manager (src))
1410     goto no_manager;
1411
1412   switch (transport->lower_transport) {
1413     case GST_RTSP_LOWER_TRANS_TCP:
1414     case GST_RTSP_LOWER_TRANS_UDP_MCAST:
1415       goto transport_failed;
1416     case GST_RTSP_LOWER_TRANS_UDP:
1417       if (!gst_wfd_tizen_src_configure_udp (src))
1418         goto transport_failed;
1419       if (!gst_wfd_tizen_src_configure_udp_sinks (src, transport))
1420         goto transport_failed;
1421       break;
1422     default:
1423       goto unknown_transport;
1424   }
1425
1426   return GST_RTSP_OK;
1427
1428   /* ERRORS */
1429 unknown_transport:
1430   {
1431     GST_DEBUG_OBJECT (src, "unknown transport");
1432     return GST_RTSP_ERROR;
1433   }
1434 no_manager:
1435   {
1436     GST_DEBUG_OBJECT (src, "cannot configure manager");
1437     return GST_RTSP_ERROR;
1438   }
1439 transport_failed:
1440   {
1441     GST_DEBUG_OBJECT (src, "failed to configure transport");
1442     return GST_RTSP_ERROR;
1443   }
1444 }
1445
1446 static gboolean
1447 gst_wfd_tizen_src_push_event (GstWFDBaseSrc * bsrc, GstEvent * event)
1448 {
1449   GstWFDTizenSrc *src = GST_WFD_TIZEN_SRC (bsrc);
1450   gboolean res = TRUE;
1451
1452   if (src->udpsrc[0] && GST_STATE (src->udpsrc[0]) >= GST_STATE_PAUSED) {
1453     gst_event_ref (event);
1454     res = gst_element_send_event (src->udpsrc[0], event);
1455   } else if (src->channelpad[0]) {
1456     gst_event_ref (event);
1457     if (GST_PAD_IS_SRC (src->channelpad[0]))
1458       res = gst_pad_push_event (src->channelpad[0], event);
1459     else
1460       res = gst_pad_send_event (src->channelpad[0], event);
1461   }
1462
1463   if (src->udpsrc[1] && GST_STATE (src->udpsrc[1]) >= GST_STATE_PAUSED) {
1464     gst_event_ref (event);
1465     res &= gst_element_send_event (src->udpsrc[1], event);
1466   } else if (src->channelpad[1]) {
1467     gst_event_ref (event);
1468     if (GST_PAD_IS_SRC (src->channelpad[1]))
1469       res &= gst_pad_push_event (src->channelpad[1], event);
1470     else
1471       res &= gst_pad_send_event (src->channelpad[1], event);
1472   }
1473
1474   if (src->udpsrc[2] && GST_STATE (src->udpsrc[2]) >= GST_STATE_PAUSED) {
1475     gst_event_ref (event);
1476     res &= gst_element_send_event (src->udpsrc[2], event);
1477   } else if (src->channelpad[2]) {
1478     gst_event_ref (event);
1479     if (GST_PAD_IS_SRC (src->channelpad[2]))
1480       res &= gst_pad_push_event (src->channelpad[2], event);
1481     else
1482       res &= gst_pad_send_event (src->channelpad[2], event);
1483   }
1484
1485   gst_event_unref (event);
1486
1487   return res;
1488 }