Merge CAPS branch
[platform/upstream/gstreamer.git] / plugins / elements / gstshaper.c
1 /* GStreamer
2  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3  *                    2000 Wim Taymans <wtay@chello.be>
4  *
5  * gstshaper.c: 
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23
24 #include <stdlib.h>
25
26 #ifdef HAVE_CONFIG_H
27 #  include "config.h"
28 #endif
29
30 #include "gstshaper.h"
31
32 GST_DEBUG_CATEGORY_STATIC (gst_shaper_debug);
33 #define GST_CAT_DEFAULT gst_shaper_debug
34
35 GstElementDetails gst_shaper_details = GST_ELEMENT_DETAILS (
36   "Shaper",
37   "Generic",
38   "Synchronizes streams on different pads",
39   "Wim Taymans <wim.taymans@chello.be>"
40 );
41
42
43 /* Shaper signals and args */
44 enum {
45   /* FILL ME */
46   LAST_SIGNAL
47 };
48
49 enum {
50   ARG_0,
51   ARG_POLICY,
52   ARG_SILENT,
53   ARG_LAST_MESSAGE,
54 };
55
56 typedef struct
57 {
58   GstPad        *sinkpad;
59   GstPad        *srcpad;
60   GstBuffer     *buffer;
61 } GstShaperConnection;
62
63 GstStaticPadTemplate shaper_src_template = GST_STATIC_PAD_TEMPLATE (
64   "src%d",
65   GST_PAD_SRC,
66   GST_PAD_SOMETIMES,
67   GST_STATIC_CAPS_ANY
68 );
69
70 GstStaticPadTemplate shaper_sink_template = GST_STATIC_PAD_TEMPLATE (
71   "sink%d",
72   GST_PAD_SINK,
73   GST_PAD_REQUEST,
74   GST_STATIC_CAPS_ANY
75 );
76
77 #define GST_TYPE_SHAPER_POLICY (gst_shaper_policy_get_type())
78 static GType
79 gst_shaper_policy_get_type (void)
80 {
81   static GType shaper_policy_type = 0;
82   static GEnumValue shaper_policy[] = {
83     { SHAPER_POLICY_TIMESTAMPS,         "1", "sync on timestamps"},
84     { SHAPER_POLICY_BUFFERSIZE,         "2", "sync on buffer size"},
85     {0, NULL, NULL},
86   };
87   if (!shaper_policy_type) {
88     shaper_policy_type = g_enum_register_static ("GstShaperPolicy", shaper_policy);
89   }
90   return shaper_policy_type;
91 }
92
93 static void     gst_shaper_base_init            (gpointer g_class);
94 static void     gst_shaper_class_init           (GstShaperClass *klass);
95 static void     gst_shaper_init                 (GstShaper *shaper);
96
97 static void     gst_shaper_set_property         (GObject *object, guint prop_id, 
98                                                  const GValue *value, GParamSpec *pspec);
99 static void     gst_shaper_get_property         (GObject *object, guint prop_id, 
100                                                  GValue *value, GParamSpec *pspec);
101
102 static GstPad*  gst_shaper_request_new_pad      (GstElement *element, GstPadTemplate *templ, 
103                                                  const gchar *unused);
104
105 static void     gst_shaper_loop                 (GstElement *element);
106
107 static GstElementClass *parent_class = NULL;
108 /* static guint gst_shaper_signals[LAST_SIGNAL] = { 0 }; */
109
110 GType
111 gst_shaper_get_type (void) 
112 {
113   static GType shaper_type = 0;
114
115   if (!shaper_type) {
116     static const GTypeInfo shaper_info = {
117       sizeof(GstShaperClass),
118       gst_shaper_base_init,
119       NULL,
120       (GClassInitFunc)gst_shaper_class_init,
121       NULL,
122       NULL,
123       sizeof(GstShaper),
124       0,
125       (GInstanceInitFunc)gst_shaper_init,
126     };
127     shaper_type = g_type_register_static (GST_TYPE_ELEMENT, "GstShaper", &shaper_info, 0);
128   
129     GST_DEBUG_CATEGORY_INIT (gst_shaper_debug, "shaper", 0, "shaper element");
130   }
131   return shaper_type;
132 }
133
134 static void
135 gst_shaper_base_init (gpointer g_class)
136 {
137   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
138   
139   gst_element_class_set_details (gstelement_class, &gst_shaper_details);
140   gst_element_class_add_pad_template (gstelement_class,
141       gst_static_pad_template_get (&shaper_src_template));
142   gst_element_class_add_pad_template (gstelement_class,
143       gst_static_pad_template_get (&shaper_sink_template));
144 }
145
146 static void 
147 gst_shaper_class_init (GstShaperClass *klass) 
148 {
149   GObjectClass *gobject_class;
150   GstElementClass *gstelement_class;
151
152   gobject_class = (GObjectClass*) klass;
153   gstelement_class = (GstElementClass*) klass;
154
155   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
156
157   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_POLICY,
158     g_param_spec_enum ("policy", "Policy", "Shaper policy",
159                        GST_TYPE_SHAPER_POLICY, SHAPER_POLICY_TIMESTAMPS, G_PARAM_READWRITE));
160   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SILENT,
161     g_param_spec_boolean ("silent", "silent", "silent",
162                           FALSE, G_PARAM_READWRITE)); 
163   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_LAST_MESSAGE,
164     g_param_spec_string ("last-message", "last-message", "last-message",
165                          NULL, G_PARAM_READABLE)); 
166
167   gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_shaper_set_property);  
168   gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_shaper_get_property);
169
170   gstelement_class->request_new_pad = GST_DEBUG_FUNCPTR (gst_shaper_request_new_pad);
171 }
172
173 static GstCaps*
174 gst_shaper_getcaps (GstPad *pad)
175 {
176   GstPad *otherpad;
177   GstShaperConnection *connection;
178
179   connection = gst_pad_get_element_private (pad);
180
181   otherpad = (pad == connection->srcpad ? connection->sinkpad : connection->srcpad);
182
183   return gst_caps_copy (gst_pad_get_allowed_caps (otherpad));
184 }
185
186 static GList*
187 gst_shaper_get_internal_link (GstPad *pad)
188 {
189   GList *res = NULL;
190   GstShaperConnection *connection;
191   GstPad *otherpad;
192
193   connection = gst_pad_get_element_private (pad);
194
195   otherpad = (pad == connection->srcpad ? connection->sinkpad : connection->srcpad);
196
197   res = g_list_prepend (res, otherpad);
198
199   return res;
200 }
201
202 static GstPadLinkReturn
203 gst_shaper_link (GstPad *pad, const GstCaps *caps)
204 {
205   GstPad *otherpad;
206   GstShaperConnection *connection;
207
208   connection = gst_pad_get_element_private (pad);
209
210   otherpad = (pad == connection->srcpad ? connection->sinkpad : connection->srcpad);
211
212   return gst_pad_proxy_link (otherpad, caps);
213 }
214
215 static GstShaperConnection*
216 gst_shaper_create_connection (GstShaper *shaper)
217 {
218   GstShaperConnection *connection;
219   gchar *padname;
220
221   shaper->nconnections++;
222
223   connection = g_new0 (GstShaperConnection, 1);
224
225   padname = g_strdup_printf ("sink%d", shaper->nconnections);
226   connection->sinkpad = gst_pad_new_from_template (
227       gst_static_pad_template_get (&shaper_sink_template), padname);
228   g_free (padname);
229   gst_pad_set_getcaps_function (connection->sinkpad, gst_shaper_getcaps);
230   gst_pad_set_internal_link_function (connection->sinkpad, gst_shaper_get_internal_link);
231   gst_pad_set_link_function (connection->sinkpad, gst_shaper_link);
232   gst_pad_set_element_private (connection->sinkpad, connection);
233   gst_element_add_pad (GST_ELEMENT (shaper), connection->sinkpad);
234
235   padname = g_strdup_printf ("src%d", shaper->nconnections);
236   connection->srcpad = gst_pad_new_from_template (
237       gst_static_pad_template_get (&shaper_src_template), padname);
238   g_free (padname);
239   gst_pad_set_getcaps_function (connection->srcpad, gst_shaper_getcaps);
240   gst_pad_set_internal_link_function (connection->srcpad, gst_shaper_get_internal_link);
241   gst_pad_set_link_function (connection->srcpad, gst_shaper_link);
242   gst_pad_set_element_private (connection->srcpad, connection);
243   gst_element_add_pad (GST_ELEMENT (shaper), connection->srcpad);
244
245   shaper->connections = g_slist_prepend (shaper->connections, connection);
246
247   return connection;
248 }
249
250 static GstPad*
251 gst_shaper_request_new_pad (GstElement *element, GstPadTemplate *templ, const gchar *unused)
252 {
253   GstShaper *shaper = GST_SHAPER (element);
254   GstShaperConnection *connection;
255
256   connection = gst_shaper_create_connection (shaper);
257
258   return connection->sinkpad;
259 }
260
261 static void 
262 gst_shaper_init (GstShaper *shaper) 
263 {
264   gst_element_set_loop_function (GST_ELEMENT (shaper), gst_shaper_loop);
265
266   shaper->policy = SHAPER_POLICY_TIMESTAMPS;
267   shaper->connections = NULL;
268   shaper->nconnections = 0;
269   shaper->silent = FALSE;
270   shaper->last_message = NULL;
271 }
272
273 static void 
274 gst_shaper_loop (GstElement *element) 
275 {
276   GstShaper *shaper;
277   GSList *connections;
278   gboolean eos = TRUE;
279   GstShaperConnection *min = NULL;
280
281   shaper = GST_SHAPER (element);
282
283   /* first make sure we have a buffer on all pads */
284   connections = shaper->connections;
285   while (connections) {
286     GstShaperConnection *connection = (GstShaperConnection *) connections->data;
287
288     /* try to fill a connection without a buffer on a pad that is
289      * active */
290     if (connection->buffer == NULL && GST_PAD_IS_USABLE (connection->sinkpad)) {
291       GstBuffer *buffer;
292
293       buffer = GST_BUFFER (gst_pad_pull (connection->sinkpad));
294
295       /* events are simply pushed ASAP */
296       if (GST_IS_EVENT (buffer)) {
297         /* save event type as it will be unreffed after the next push */
298         GstEventType type = GST_EVENT_TYPE (buffer);
299
300         gst_pad_push (connection->srcpad, GST_DATA (buffer));
301
302         switch (type) {
303           /* on EOS we disable the pad so that we don't pull on
304            * it again and never get more data */
305           case GST_EVENT_EOS:
306             gst_pad_set_active (connection->sinkpad, FALSE);
307             break;
308           default:
309             break;
310         }
311       }
312       else {
313         /* we store the buffer */
314         connection->buffer = buffer;
315       }
316     }
317     /* FIXME policy stuff goes here */
318     /* find connection with lowest timestamp */
319     if (min == NULL || (connection->buffer != NULL &&
320         (GST_BUFFER_TIMESTAMP (connection->buffer) < 
321          GST_BUFFER_TIMESTAMP (min->buffer)))) 
322     {
323       min = connection;
324     }
325     connections = g_slist_next (connections);
326   }
327   /* if we have a connection with a buffer, push it */
328   if (min != NULL && min->buffer) {
329     gst_pad_push (min->srcpad, GST_DATA (min->buffer));
330     min->buffer = NULL;
331     /* since we pushed a buffer, it's not EOS */
332     eos = FALSE;
333   }
334   
335   if (eos) {
336     gst_element_set_eos (element);
337   }
338 }
339
340 static void 
341 gst_shaper_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) 
342 {
343   GstShaper *shaper;
344
345   /* it's not null if we got it, but it might not be ours */
346   g_return_if_fail (GST_IS_SHAPER (object));
347   
348   shaper = GST_SHAPER (object);
349
350   switch (prop_id) {
351     case ARG_POLICY:
352       shaper->policy = g_value_get_enum (value);
353       break;
354     case ARG_SILENT:
355       shaper->silent = g_value_get_boolean (value);
356       break;
357     default:
358       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
359       break;
360   }
361 }
362
363 static void gst_shaper_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) {
364   GstShaper *shaper;
365
366   /* it's not null if we got it, but it might not be ours */
367   g_return_if_fail (GST_IS_SHAPER (object));
368   
369   shaper = GST_SHAPER (object);
370
371   switch (prop_id) {
372     case ARG_POLICY:
373       g_value_set_enum (value, shaper->policy);
374       break;
375     case ARG_SILENT:
376       g_value_set_boolean (value, shaper->silent);
377       break;
378     case ARG_LAST_MESSAGE:
379       g_value_set_string (value, shaper->last_message);
380       break;
381     default:
382       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
383       break;
384   }
385 }
386