424f449d7c76b40757e47c16778739e80281f672
[platform/upstream/libnice.git] / gst / gstnicesrc.c
1 /*
2  * This file is part of the Nice GLib ICE library.
3  *
4  * (C) 2006, 2007 Collabora Ltd.
5  *  Contact: Dafydd Harries
6  * (C) 2006, 2007 Nokia Corporation. All rights reserved.
7  *  Contact: Kai Vehmanen
8  *
9  * The contents of this file are subject to the Mozilla Public License Version
10  * 1.1 (the "License"); you may not use this file except in compliance with
11  * the License. You may obtain a copy of the License at
12  * http://www.mozilla.org/MPL/
13  *
14  * Software distributed under the License is distributed on an "AS IS" basis,
15  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
16  * for the specific language governing rights and limitations under the
17  * License.
18  *
19  * The Original Code is the Nice GLib ICE library.
20  *
21  * The Initial Developers of the Original Code are Collabora Ltd and Nokia
22  * Corporation. All Rights Reserved.
23  *
24  * Contributors:
25  *   Dafydd Harries, Collabora Ltd.
26  *
27  * Alternatively, the contents of this file may be used under the terms of the
28  * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
29  * case the provisions of LGPL are applicable instead of those above. If you
30  * wish to allow use of your version of this file only under the terms of the
31  * LGPL and not to allow others to use your version of this file under the
32  * MPL, indicate your decision by deleting the provisions above and replace
33  * them with the notice and other provisions required by the LGPL. If you do
34  * not delete the provisions above, a recipient may use your version of this
35  * file under either the MPL or the LGPL.
36  */
37 #ifdef HAVE_CONFIG_H
38 # include "config.h"
39 #endif
40
41 #include <string.h>
42
43 #include "gstnicesrc.h"
44
45 GST_DEBUG_CATEGORY_STATIC (nicesrc_debug);
46 #define GST_CAT_DEFAULT nicesrc_debug
47
48
49 #define BUFFER_SIZE (65536)
50
51 static GstFlowReturn
52 gst_nice_src_create (
53   GstPushSrc *basesrc,
54   GstBuffer **buffer);
55
56 static gboolean
57 gst_nice_src_unlock (
58     GstBaseSrc *basesrc);
59
60 static gboolean
61 gst_nice_src_unlock_stop (
62     GstBaseSrc *basesrc);
63
64 static void
65 gst_nice_src_set_property (
66   GObject *object,
67   guint prop_id,
68   const GValue *value,
69   GParamSpec *pspec);
70
71 static void
72 gst_nice_src_get_property (
73   GObject *object,
74   guint prop_id,
75   GValue *value,
76   GParamSpec *pspec);
77
78
79 static void
80 gst_nice_src_dispose (GObject *object);
81
82 static GstStateChangeReturn
83 gst_nice_src_change_state (
84     GstElement * element,
85     GstStateChange transition);
86
87 static GstStaticPadTemplate gst_nice_src_src_template =
88 GST_STATIC_PAD_TEMPLATE (
89     "src",
90     GST_PAD_SRC,
91     GST_PAD_ALWAYS,
92     GST_STATIC_CAPS_ANY);
93
94 G_DEFINE_TYPE (GstNiceSrc, gst_nice_src, GST_TYPE_PUSH_SRC);
95
96 enum
97 {
98   PROP_AGENT = 1,
99   PROP_STREAM,
100   PROP_COMPONENT
101 };
102
103
104 static void
105 gst_nice_src_class_init (GstNiceSrcClass *klass)
106 {
107   GstPushSrcClass *gstpushsrc_class;
108   GstBaseSrcClass *gstbasesrc_class;
109   GstElementClass *gstelement_class;
110   GObjectClass *gobject_class;
111
112   GST_DEBUG_CATEGORY_INIT (nicesrc_debug, "nicesrc",
113       0, "libnice source");
114
115   gstpushsrc_class = (GstPushSrcClass *) klass;
116   gstpushsrc_class->create = GST_DEBUG_FUNCPTR (gst_nice_src_create);
117
118   gstbasesrc_class = (GstBaseSrcClass *) klass;
119   gstbasesrc_class->unlock = GST_DEBUG_FUNCPTR (gst_nice_src_unlock);
120   gstbasesrc_class->unlock_stop = GST_DEBUG_FUNCPTR (gst_nice_src_unlock_stop);
121
122   gobject_class = (GObjectClass *) klass;
123   gobject_class->set_property = gst_nice_src_set_property;
124   gobject_class->get_property = gst_nice_src_get_property;
125   gobject_class->dispose = gst_nice_src_dispose;
126
127   gstelement_class = (GstElementClass *) klass;
128   gstelement_class->change_state = gst_nice_src_change_state;
129
130   gst_element_class_add_pad_template (gstelement_class,
131       gst_static_pad_template_get (&gst_nice_src_src_template));
132 #if GST_CHECK_VERSION (1,0,0)
133   gst_element_class_set_metadata (gstelement_class,
134 #else
135   gst_element_class_set_details_simple (gstelement_class,
136 #endif
137       "ICE source",
138       "Source",
139       "Interactive UDP connectivity establishment",
140       "Dafydd Harries <dafydd.harries@collabora.co.uk>");
141
142   g_object_class_install_property (gobject_class, PROP_AGENT,
143       g_param_spec_object (
144          "agent",
145          "Agent",
146          "The NiceAgent this source is bound to",
147          NICE_TYPE_AGENT,
148          G_PARAM_READWRITE));
149
150   g_object_class_install_property (gobject_class, PROP_STREAM,
151       g_param_spec_uint (
152          "stream",
153          "Stream ID",
154          "The ID of the stream to read from",
155          0,
156          G_MAXUINT,
157          0,
158          G_PARAM_READWRITE));
159
160   g_object_class_install_property (gobject_class, PROP_COMPONENT,
161       g_param_spec_uint (
162          "component",
163          "Component ID",
164          "The ID of the component to read from",
165          0,
166          G_MAXUINT,
167          0,
168          G_PARAM_READWRITE));
169 }
170
171 static void
172 gst_nice_src_init (GstNiceSrc *src)
173 {
174   gst_base_src_set_live (GST_BASE_SRC (src), TRUE);
175   gst_base_src_set_format (GST_BASE_SRC (src), GST_FORMAT_TIME);
176   gst_base_src_set_do_timestamp (GST_BASE_SRC (src), TRUE);
177   src->agent = NULL;
178   src->stream_id = 0;
179   src->component_id = 0;
180   src->mainctx = g_main_context_new ();
181   src->mainloop = g_main_loop_new (src->mainctx, FALSE);
182   src->unlocked = FALSE;
183   src->idle_source = NULL;
184   src->outbufs = g_queue_new ();
185 }
186
187 static void
188 gst_nice_src_read_callback (NiceAgent *agent,
189     guint stream_id,
190     guint component_id,
191     guint len,
192     gchar *buf,
193     gpointer data)
194 {
195   GstBaseSrc *basesrc = GST_BASE_SRC (data);
196   GstNiceSrc *nicesrc = GST_NICE_SRC (basesrc);
197   GstBuffer *buffer = NULL;
198
199   GST_LOG_OBJECT (agent, "Got buffer, getting out of the main loop");
200
201 #if GST_CHECK_VERSION (1,0,0)
202   buffer = gst_buffer_new_allocate (NULL, len, NULL);
203   gst_buffer_fill (buffer, 0, buf, len);
204 #else
205   buffer = gst_buffer_new_and_alloc (len);
206   memcpy (GST_BUFFER_DATA (buffer), buf, len);
207 #endif
208   GST_OBJECT_LOCK (nicesrc);
209   g_queue_push_tail (nicesrc->outbufs, buffer);
210   g_main_loop_quit (nicesrc->mainloop);
211   GST_OBJECT_UNLOCK (nicesrc);
212 }
213
214 static gboolean
215 gst_nice_src_unlock_idler (gpointer data)
216 {
217   GstNiceSrc *nicesrc = GST_NICE_SRC (data);
218
219   GST_OBJECT_LOCK (nicesrc);
220   if (nicesrc->unlocked)
221     g_main_loop_quit (nicesrc->mainloop);
222
223   if (nicesrc->idle_source) {
224     g_source_destroy (nicesrc->idle_source);
225     g_source_unref (nicesrc->idle_source);
226     nicesrc->idle_source = NULL;
227   }
228   GST_OBJECT_UNLOCK (nicesrc);
229
230   return FALSE;
231 }
232
233 static gboolean
234 gst_nice_src_unlock (GstBaseSrc *src)
235 {
236   GstNiceSrc *nicesrc = GST_NICE_SRC (src);
237
238   GST_OBJECT_LOCK (src);
239   nicesrc->unlocked = TRUE;
240
241   g_main_loop_quit (nicesrc->mainloop);
242
243   if (!nicesrc->idle_source) {
244     nicesrc->idle_source = g_idle_source_new ();
245     g_source_set_priority (nicesrc->idle_source, G_PRIORITY_HIGH);
246     g_source_set_callback (nicesrc->idle_source, gst_nice_src_unlock_idler, src, NULL);
247     g_source_attach (nicesrc->idle_source, g_main_loop_get_context (nicesrc->mainloop));
248   }
249   GST_OBJECT_UNLOCK (src);
250
251   return TRUE;
252 }
253
254 static gboolean
255 gst_nice_src_unlock_stop (GstBaseSrc *src)
256 {
257   GstNiceSrc *nicesrc = GST_NICE_SRC (src);
258
259   GST_OBJECT_LOCK (src);
260   nicesrc->unlocked = FALSE;
261   if (nicesrc->idle_source) {
262     g_source_destroy (nicesrc->idle_source);
263     g_source_unref(nicesrc->idle_source);
264   }
265   nicesrc->idle_source = NULL;
266   GST_OBJECT_UNLOCK (src);
267
268   return TRUE;
269 }
270
271 static GstFlowReturn
272 gst_nice_src_create (
273   GstPushSrc *basesrc,
274   GstBuffer **buffer)
275 {
276   GstNiceSrc *nicesrc = GST_NICE_SRC (basesrc);
277
278   GST_LOG_OBJECT (nicesrc, "create called");
279
280   GST_OBJECT_LOCK (basesrc);
281   if (nicesrc->unlocked) {
282     GST_OBJECT_UNLOCK (basesrc);
283 #if GST_CHECK_VERSION (1,0,0)
284     return GST_FLOW_FLUSHING;
285 #else
286     return GST_FLOW_WRONG_STATE;
287 #endif
288   }
289   if (g_queue_is_empty (nicesrc->outbufs)) {
290     GST_OBJECT_UNLOCK (basesrc);
291     g_main_loop_run (nicesrc->mainloop);
292     GST_OBJECT_LOCK (basesrc);
293   }
294
295   *buffer = g_queue_pop_head (nicesrc->outbufs);
296   GST_OBJECT_UNLOCK (basesrc);
297
298   if (*buffer != NULL) {
299     GST_LOG_OBJECT (nicesrc, "Got buffer, pushing");
300     return GST_FLOW_OK;
301   } else {
302     GST_LOG_OBJECT (nicesrc, "Got interrupting, returning wrong-state");
303 #if GST_CHECK_VERSION (1,0,0)
304     return GST_FLOW_FLUSHING;
305 #else
306     return GST_FLOW_WRONG_STATE;
307 #endif
308   }
309
310 }
311
312 static void
313 gst_nice_src_dispose (GObject *object)
314 {
315   GstNiceSrc *src = GST_NICE_SRC (object);
316
317   if (src->agent)
318     g_object_unref (src->agent);
319   src->agent = NULL;
320
321   if (src->mainloop)
322     g_main_loop_unref (src->mainloop);
323   src->mainloop = NULL;
324
325   if (src->mainctx)
326     g_main_context_unref (src->mainctx);
327   src->mainctx = NULL;
328
329   if (src->outbufs) {
330     g_queue_free_full (src->outbufs, (GDestroyNotify) gst_buffer_unref);
331   }
332   src->outbufs = NULL;
333
334   if (src->idle_source) {
335     g_source_destroy (src->idle_source);
336     g_source_unref(src->idle_source);
337   }
338   src->idle_source = NULL;
339
340   G_OBJECT_CLASS (gst_nice_src_parent_class)->dispose (object);
341 }
342
343 static void
344 gst_nice_src_set_property (
345   GObject *object,
346   guint prop_id,
347   const GValue *value,
348   GParamSpec *pspec)
349 {
350   GstNiceSrc *src = GST_NICE_SRC (object);
351
352   switch (prop_id)
353     {
354     case PROP_AGENT:
355       if (src->agent)
356         GST_ERROR_OBJECT (object,
357             "Changing the agent on a nice src not allowed");
358       else
359         src->agent = g_value_dup_object (value);
360       break;
361
362     case PROP_STREAM:
363       src->stream_id = g_value_get_uint (value);
364       break;
365
366     case PROP_COMPONENT:
367       src->component_id = g_value_get_uint (value);
368       break;
369
370     default:
371       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
372       break;
373     }
374 }
375
376 static void
377 gst_nice_src_get_property (
378   GObject *object,
379   guint prop_id,
380   GValue *value,
381   GParamSpec *pspec)
382 {
383   GstNiceSrc *src = GST_NICE_SRC (object);
384
385   switch (prop_id)
386     {
387     case PROP_AGENT:
388       g_value_set_object (value, src->agent);
389       break;
390
391     case PROP_STREAM:
392       g_value_set_uint (value, src->stream_id);
393       break;
394
395     case PROP_COMPONENT:
396       g_value_set_uint (value, src->component_id);
397       break;
398
399     default:
400       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
401       break;
402     }
403 }
404
405 static GstStateChangeReturn
406 gst_nice_src_change_state (GstElement * element, GstStateChange transition)
407 {
408   GstNiceSrc *src;
409   GstStateChangeReturn ret;
410
411   src = GST_NICE_SRC (element);
412
413   switch (transition) {
414     case GST_STATE_CHANGE_NULL_TO_READY:
415       if (src->agent == NULL)
416         {
417           GST_ERROR_OBJECT (element,
418               "Trying to start Nice source without an agent set");
419           return GST_STATE_CHANGE_FAILURE;
420         }
421       else if (src->stream_id == 0)
422           {
423             GST_ERROR_OBJECT (element,
424                 "Trying to start Nice source without a stream set");
425             return GST_STATE_CHANGE_FAILURE;
426           }
427       else if (src->component_id == 0)
428           {
429             GST_ERROR_OBJECT (element,
430                 "Trying to start Nice source without a component set");
431             return GST_STATE_CHANGE_FAILURE;
432           }
433       break;
434     case GST_STATE_CHANGE_PAUSED_TO_READY:
435       nice_agent_attach_recv (src->agent, src->stream_id, src->component_id,
436           src->mainctx, NULL, NULL);
437       GST_OBJECT_LOCK (src);
438       g_list_free_full (src->outbufs->head, (GDestroyNotify) gst_buffer_unref);
439       g_queue_init (src->outbufs);
440       GST_OBJECT_UNLOCK (src);
441       break;
442     case GST_STATE_CHANGE_READY_TO_PAUSED:
443     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
444     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
445     case GST_STATE_CHANGE_READY_TO_NULL:
446     default:
447       break;
448   }
449
450   ret = GST_ELEMENT_CLASS (gst_nice_src_parent_class)->change_state (element,
451       transition);
452
453   switch (transition) {
454     case GST_STATE_CHANGE_READY_TO_PAUSED:
455       nice_agent_attach_recv (src->agent, src->stream_id, src->component_id,
456           src->mainctx, gst_nice_src_read_callback, (gpointer) src);
457       break;
458     case GST_STATE_CHANGE_NULL_TO_READY:
459     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
460     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
461     case GST_STATE_CHANGE_PAUSED_TO_READY:
462     case GST_STATE_CHANGE_READY_TO_NULL:
463     default:
464       break;
465   }
466
467   return ret;
468 }
469
470