gstelement.c 102 KB
Newer Older
1 2 3 4 5
/* GStreamer
 * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
 *                    2000 Wim Taymans <wtay@chello.be>
 *
 * gstelement.c: The base element, all elements derive from this
Erik Walthinsen's avatar
Erik Walthinsen committed
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

23
#include <glib.h>
Wim Taymans's avatar
Wim Taymans committed
24
#include <stdarg.h>
25
#include <gobject/gvaluecollector.h>
26 27
#include "gst_private.h"

28
#include "gstelement.h"
29
#include "gstbin.h"
30
#include "gsterror.h"
31
#include "gstscheduler.h"
32
#include "gstevent.h"
Wim Taymans's avatar
Wim Taymans committed
33
#include "gstutils.h"
34
#include "gstinfo.h"
35
#include "gst-i18n-lib.h"
Erik Walthinsen's avatar
Erik Walthinsen committed
36 37 38 39 40

/* Element signals and args */
enum {
  STATE_CHANGE,
  NEW_PAD,
41
  PAD_REMOVED,
Erik Walthinsen's avatar
Erik Walthinsen committed
42
  ERROR,
43
  EOS,
Benjamin Otte's avatar
Benjamin Otte committed
44 45
  FOUND_TAG,
  /* add more above */
Erik Walthinsen's avatar
Erik Walthinsen committed
46 47 48 49 50 51 52 53
  LAST_SIGNAL
};

enum {
  ARG_0,
  /* FILL ME */
};

54 55 56 57
extern void			__gst_element_details_clear	(GstElementDetails *dp);
extern void			__gst_element_details_set	(GstElementDetails *dest, 
								 const GstElementDetails *src);

58 59
static void			gst_element_class_init		(GstElementClass *klass);
static void			gst_element_init		(GstElement *element);
60 61
static void			gst_element_base_class_init	(gpointer g_class);
static void			gst_element_base_class_finalize	(gpointer g_class);
Erik Walthinsen's avatar
Erik Walthinsen committed
62

63
static void			gst_element_real_set_property	(GObject *object, guint prop_id, 
64
								 const GValue *value, GParamSpec *pspec);
65
static void			gst_element_real_get_property	(GObject *object, guint prop_id, GValue *value, 
66
								 GParamSpec *pspec);
67

68
static void 			gst_element_dispose 		(GObject *object);
69

70
static GstElementStateReturn	gst_element_change_state	(GstElement *element);
71
static void			gst_element_error_func		(GstElement* element, GstElement *source, GError *error, gchar *debug);
Benjamin Otte's avatar
Benjamin Otte committed
72
static void			gst_element_found_tag_func	(GstElement* element, GstElement *source, GstTagList *tag_list);
73

74
#ifndef GST_DISABLE_LOADSAVE
75
static xmlNodePtr		gst_element_save_thyself	(GstObject *object, xmlNodePtr parent);
76
static void			gst_element_restore_thyself 	(GstObject *parent, xmlNodePtr self);
77
#endif
78

79 80
GType _gst_element_type = 0;

Erik Walthinsen's avatar
Erik Walthinsen committed
81 82 83
static GstObjectClass *parent_class = NULL;
static guint gst_element_signals[LAST_SIGNAL] = { 0 };

84 85 86
GType gst_element_get_type (void) 
{
  if (!_gst_element_type) {
87
    static const GTypeInfo element_info = {
Erik Walthinsen's avatar
Erik Walthinsen committed
88
      sizeof(GstElementClass),
89 90
      gst_element_base_class_init,
      gst_element_base_class_finalize,
91 92 93 94 95 96
      (GClassInitFunc)gst_element_class_init,
      NULL,
      NULL,
      sizeof(GstElement),
      0,
      (GInstanceInitFunc)gst_element_init,
97
      NULL
Erik Walthinsen's avatar
Erik Walthinsen committed
98
    };
Wim Taymans's avatar
Wim Taymans committed
99 100
    _gst_element_type = g_type_register_static(GST_TYPE_OBJECT, "GstElement", 
		          &element_info, G_TYPE_FLAG_ABSTRACT);
Erik Walthinsen's avatar
Erik Walthinsen committed
101
  }
102
  return _gst_element_type;
Erik Walthinsen's avatar
Erik Walthinsen committed
103 104
}

105 106
static void
gst_element_class_init (GstElementClass *klass)
Wim Taymans's avatar
Wim Taymans committed
107
{
108
  GObjectClass *gobject_class;
109
  GstObjectClass *gstobject_class;
Erik Walthinsen's avatar
Erik Walthinsen committed
110

111
  gobject_class = (GObjectClass*) klass;
112
  gstobject_class = (GstObjectClass*) klass;
Erik Walthinsen's avatar
Erik Walthinsen committed
113

114
  parent_class = g_type_class_ref(GST_TYPE_OBJECT);
Erik Walthinsen's avatar
Erik Walthinsen committed
115 116

  gst_element_signals[STATE_CHANGE] =
117
    g_signal_new ("state_change", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
118 119 120
                  G_STRUCT_OFFSET (GstElementClass, state_change), NULL, NULL,
		  gst_marshal_VOID__INT_INT, G_TYPE_NONE, 2,
                  G_TYPE_INT, G_TYPE_INT);
Erik Walthinsen's avatar
Erik Walthinsen committed
121
  gst_element_signals[NEW_PAD] =
122
    g_signal_new ("new_pad", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
123
                  G_STRUCT_OFFSET (GstElementClass, new_pad), NULL, NULL,
124 125
                  gst_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
                  G_TYPE_OBJECT);
126
  gst_element_signals[PAD_REMOVED] =
127
    g_signal_new ("pad_removed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
128
                  G_STRUCT_OFFSET (GstElementClass, pad_removed), NULL, NULL,
129 130
                  gst_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
                  G_TYPE_OBJECT);
Erik Walthinsen's avatar
Erik Walthinsen committed
131
  gst_element_signals[ERROR] =
132
    g_signal_new ("error", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
133
                  G_STRUCT_OFFSET (GstElementClass, error), NULL, NULL,
134 135 136
                  gst_marshal_VOID__OBJECT_POINTER_STRING, G_TYPE_NONE, 3,
                  GST_TYPE_ELEMENT, G_TYPE_POINTER, G_TYPE_STRING);
   gst_element_signals[EOS] =
137
    g_signal_new ("eos", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
Benjamin Otte's avatar
Benjamin Otte committed
138
                  G_STRUCT_OFFSET (GstElementClass, eos), NULL, NULL,
139
                  gst_marshal_VOID__VOID, G_TYPE_NONE, 0);
Benjamin Otte's avatar
Benjamin Otte committed
140 141 142 143 144
  gst_element_signals[FOUND_TAG] =
    g_signal_new ("found-tag", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (GstElementClass, found_tag), NULL, NULL,
                  gst_marshal_VOID__OBJECT_POINTER, G_TYPE_NONE, 2,
		  GST_TYPE_ELEMENT, G_TYPE_POINTER);
Erik Walthinsen's avatar
Erik Walthinsen committed
145

146 147
  gobject_class->set_property 		= GST_DEBUG_FUNCPTR (gst_element_real_set_property);
  gobject_class->get_property 		= GST_DEBUG_FUNCPTR (gst_element_real_get_property);
148

149
  gobject_class->dispose 		= GST_DEBUG_FUNCPTR (gst_element_dispose);
150

151
#ifndef GST_DISABLE_LOADSAVE
152 153
  gstobject_class->save_thyself 	= GST_DEBUG_FUNCPTR (gst_element_save_thyself);
  gstobject_class->restore_thyself 	= GST_DEBUG_FUNCPTR (gst_element_restore_thyself);
154
#endif
155

156
  klass->change_state 			= GST_DEBUG_FUNCPTR (gst_element_change_state);
157
  klass->error				= GST_DEBUG_FUNCPTR (gst_element_error_func);
Benjamin Otte's avatar
Benjamin Otte committed
158
  klass->found_tag	 		= GST_DEBUG_FUNCPTR (gst_element_found_tag_func);
159
  klass->numpadtemplates 		= 0;
160 161

  klass->elementfactory			= NULL;
Erik Walthinsen's avatar
Erik Walthinsen committed
162 163
}

164
static void
165
gst_element_base_class_init (gpointer g_class)
166
{
167
  GObjectClass *gobject_class = G_OBJECT_CLASS (g_class);
168 169
  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);

170

171 172
  gobject_class->set_property =		GST_DEBUG_FUNCPTR(gst_element_real_set_property);
  gobject_class->get_property =		GST_DEBUG_FUNCPTR(gst_element_real_get_property);
173 174

  element_class->padtemplates = NULL;
175 176
}

177 178 179 180 181 182 183 184 185 186
static void
gst_element_base_class_finalize (gpointer g_class)
{
  GstElementClass *klass = GST_ELEMENT_CLASS (klass);

  g_list_foreach (klass->padtemplates, (GFunc) g_object_unref, NULL);
  g_list_free (klass->padtemplates);
  __gst_element_details_clear (&klass->details);
}

187 188
static void
gst_element_init (GstElement *element)
Wim Taymans's avatar
Wim Taymans committed
189
{
190
  element->current_state = GST_STATE_NULL;
191
  element->pending_state = GST_STATE_VOID_PENDING;
Erik Walthinsen's avatar
Erik Walthinsen committed
192
  element->numpads = 0;
193 194
  element->numsrcpads = 0;
  element->numsinkpads = 0;
Erik Walthinsen's avatar
Erik Walthinsen committed
195 196
  element->pads = NULL;
  element->loopfunc = NULL;
197
  element->sched = NULL;
198
  element->clock = NULL;
199
  element->sched_private = NULL;
200 201
  element->state_mutex = g_mutex_new ();
  element->state_cond = g_cond_new ();
Erik Walthinsen's avatar
Erik Walthinsen committed
202 203
}

204
static void
205
gst_element_real_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
206
{
Wim Taymans's avatar
Wim Taymans committed
207
  GstElementClass *oclass = GST_ELEMENT_GET_CLASS (object);
208

209
  if (oclass->set_property)
210
    (oclass->set_property) (object, prop_id, value, pspec);
211 212 213
}

static void
214
gst_element_real_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
215
{
Wim Taymans's avatar
Wim Taymans committed
216
  GstElementClass *oclass = GST_ELEMENT_GET_CLASS (object);
217

218
  if (oclass->get_property)
219
    (oclass->get_property) (object, prop_id, value, pspec);
220 221
}

222 223
/** 
 * gst_element_default_error:
224 225
 * @object: a #GObject that signalled the error.
 * @orig: the #GstObject that initiated the error.
226 227
 * @error: the GError.
 * @debug: an additional debug information string, or NULL.
228
 *
229 230 231 232
 * A default error signal callback to attach to an element.
 * The user data passed to the g_signal_connect is ignored.
 *
 * The default handler will simply print the error string using g_print.
233 234
 */
void
235 236 237 238 239 240 241 242
gst_element_default_error (GObject *object, GstObject *source, GError *error, gchar *debug)
{
  gchar *name = gst_object_get_path_string (source);

  g_print (_("ERROR: from element %s: %s.\n"), name, error->message);
  if (debug)
    g_print (_("Additional debug info:\n%s\n"), debug);

243
  g_free (name);
244
}
245

246 247
typedef struct {
  const GParamSpec *pspec;
248
  GValue value;
249 250 251 252 253 254 255 256
} prop_value_t;

static void
element_set_property (GstElement *element, const GParamSpec *pspec, const GValue *value)
{
  prop_value_t *prop_value = g_new0 (prop_value_t, 1);

  prop_value->pspec = pspec;
257
  prop_value->value = *value;
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272

  g_async_queue_push (element->prop_value_queue, prop_value);
}

static void
element_get_property (GstElement *element, const GParamSpec *pspec, GValue *value)
{
  g_mutex_lock (element->property_mutex);
  g_object_get_property ((GObject*)element, pspec->name, value);
  g_mutex_unlock (element->property_mutex);
}

static void
gst_element_threadsafe_properties_pre_run (GstElement *element)
{
273
  GST_DEBUG ("locking element %s", GST_OBJECT_NAME (element));
274
  g_mutex_lock (element->property_mutex);
275 276 277 278 279 280
  gst_element_set_pending_properties (element);
}

static void
gst_element_threadsafe_properties_post_run (GstElement *element)
{
281
  GST_DEBUG ("unlocking element %s", GST_OBJECT_NAME (element));
282
  g_mutex_unlock (element->property_mutex);
283 284
}

Wim Taymans's avatar
Wim Taymans committed
285 286
/**
 * gst_element_enable_threadsafe_properties:
287
 * @element: a #GstElement to enable threadsafe properties on.
Wim Taymans's avatar
Wim Taymans committed
288
 *
289
 * Installs an asynchronous queue, a mutex and pre- and post-run functions on
Wim Taymans's avatar
Wim Taymans committed
290 291 292
 * this element so that properties on the element can be set in a 
 * threadsafe way.
 */
293 294 295 296 297 298 299 300 301 302 303 304 305 306
void
gst_element_enable_threadsafe_properties (GstElement *element)
{
  g_return_if_fail (GST_IS_ELEMENT (element));
  
  GST_FLAG_SET (element, GST_ELEMENT_USE_THREADSAFE_PROPERTIES);
  element->pre_run_func = gst_element_threadsafe_properties_pre_run;
  element->post_run_func = gst_element_threadsafe_properties_post_run;
  if (!element->prop_value_queue)
    element->prop_value_queue = g_async_queue_new ();
  if (!element->property_mutex)
    element->property_mutex = g_mutex_new ();
}

Wim Taymans's avatar
Wim Taymans committed
307 308
/**
 * gst_element_disable_threadsafe_properties:
309
 * @element: a #GstElement to disable threadsafe properties on.
Wim Taymans's avatar
Wim Taymans committed
310
 *
311
 * Removes the threadsafe properties, post- and pre-run locks from
Wim Taymans's avatar
Wim Taymans committed
312 313
 * this element.
 */
314 315 316 317 318 319 320 321 322 323 324
void
gst_element_disable_threadsafe_properties (GstElement *element)
{
  g_return_if_fail (GST_IS_ELEMENT (element));
  
  GST_FLAG_UNSET (element, GST_ELEMENT_USE_THREADSAFE_PROPERTIES);
  element->pre_run_func = NULL;
  element->post_run_func = NULL;
  /* let's keep around that async queue */
}

Wim Taymans's avatar
Wim Taymans committed
325 326
/**
 * gst_element_set_pending_properties:
327
 * @element: a #GstElement to set the pending properties on.
Wim Taymans's avatar
Wim Taymans committed
328
 *
329
 * Sets all pending properties on the threadsafe properties enabled
Wim Taymans's avatar
Wim Taymans committed
330 331
 * element.
 */
332 333 334 335 336 337
void
gst_element_set_pending_properties (GstElement *element) 
{
  prop_value_t *prop_value;

  while ((prop_value = g_async_queue_try_pop (element->prop_value_queue))) {
338 339
    g_object_set_property ((GObject*)element, prop_value->pspec->name, &prop_value->value);
    g_value_unset (&prop_value->value);
340 341 342 343 344 345
    g_free (prop_value);
  }
}

/* following 6 functions taken mostly from gobject.c */

Wim Taymans's avatar
Wim Taymans committed
346 347
/**
 * gst_element_set:
348 349 350 351
 * @element: a #GstElement to set properties on.
 * @first_property_name: the first property to set.
 * @...: value of the first property, and more properties to set, ending
 *       with NULL.
Wim Taymans's avatar
Wim Taymans committed
352
 *
353 354
 * Sets properties on an element. If the element uses threadsafe properties,
 * they will be queued and set on the object when it is scheduled again.
Wim Taymans's avatar
Wim Taymans committed
355
 */
356 357 358 359 360 361 362 363 364 365 366 367
void
gst_element_set (GstElement *element, const gchar *first_property_name, ...)
{
  va_list var_args;
  
  g_return_if_fail (GST_IS_ELEMENT (element));
  
  va_start (var_args, first_property_name);
  gst_element_set_valist (element, first_property_name, var_args);
  va_end (var_args);
}

Wim Taymans's avatar
Wim Taymans committed
368 369
/**
 * gst_element_get:
370 371 372 373
 * @element: a #GstElement to get properties of.
 * @first_property_name: the first property to get.
 * @...: pointer to a variable to store the first property in, as well as 
 * more properties to get, ending with NULL.
Wim Taymans's avatar
Wim Taymans committed
374
 *
375
 * Gets properties from an element. If the element uses threadsafe properties,
Wim Taymans's avatar
Wim Taymans committed
376 377
 * the element will be locked before getting the given properties.
 */
378 379 380 381 382 383 384 385 386 387 388 389
void
gst_element_get (GstElement *element, const gchar *first_property_name, ...)
{
  va_list var_args;
  
  g_return_if_fail (GST_IS_ELEMENT (element));
  
  va_start (var_args, first_property_name);
  gst_element_get_valist (element, first_property_name, var_args);
  va_end (var_args);
}

Wim Taymans's avatar
Wim Taymans committed
390 391
/**
 * gst_element_set_valist:
392 393 394
 * @element: a #GstElement to set properties on.
 * @first_property_name: the first property to set.
 * @var_args: the var_args list of other properties to get.
Wim Taymans's avatar
Wim Taymans committed
395
 *
396
 * Sets properties on an element. If the element uses threadsafe properties,
Wim Taymans's avatar
Wim Taymans committed
397 398
 * the property change will be put on the async queue.
 */
399 400 401 402 403 404 405 406
void
gst_element_set_valist (GstElement *element, const gchar *first_property_name, va_list var_args)
{
  const gchar *name;
  GObject *object;
  
  g_return_if_fail (GST_IS_ELEMENT (element));
  
407 408
  object = (GObject *) element;

409
  GST_CAT_DEBUG (GST_CAT_PROPERTIES, 
410 411
	     "setting valist of properties starting with %s on element %s",
	     first_property_name, gst_element_get_name (element));
412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469

  if (!GST_FLAG_IS_SET (element, GST_ELEMENT_USE_THREADSAFE_PROPERTIES)) {
    g_object_set_valist (object, first_property_name, var_args);
    return;
  }

  g_object_ref (object);
  
  name = first_property_name;

  while (name)
    {
      GValue value = { 0, };
      GParamSpec *pspec;
      gchar *error = NULL;
      
      pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (object), name);

      if (!pspec)
        {
	  g_warning ("%s: object class `%s' has no property named `%s'",
		     G_STRLOC,
		     G_OBJECT_TYPE_NAME (object),
		     name);
	  break;
	}
      if (!(pspec->flags & G_PARAM_WRITABLE))
	{
	  g_warning ("%s: property `%s' of object class `%s' is not writable",
		     G_STRLOC,
		     pspec->name,
		     G_OBJECT_TYPE_NAME (object));
	  break;
	}
      
      g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
      
      G_VALUE_COLLECT (&value, var_args, 0, &error);
      if (error)
	{
	  g_warning ("%s: %s", G_STRLOC, error);
	  g_free (error);
	  
	  /* we purposely leak the value here, it might not be
	   * in a sane state if an error condition occoured
	   */
	  break;
	}
      
      element_set_property (element, pspec, &value);
      g_value_unset (&value);
      
      name = va_arg (var_args, gchar*);
    }

  g_object_unref (object);
}

Wim Taymans's avatar
Wim Taymans committed
470 471
/**
 * gst_element_get_valist:
472 473 474
 * @element: a #GstElement to get properties of.
 * @first_property_name: the first property to get.
 * @var_args: the var_args list of other properties to get.
Wim Taymans's avatar
Wim Taymans committed
475
 *
476
 * Gets properties from an element. If the element uses threadsafe properties,
Wim Taymans's avatar
Wim Taymans committed
477 478
 * the element will be locked before getting the given properties.
 */
479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543
void
gst_element_get_valist (GstElement *element, const gchar *first_property_name, va_list var_args)
{
  const gchar *name;
  GObject *object;
  
  g_return_if_fail (GST_IS_ELEMENT (element));
  
  object = (GObject*)element;

  if (!GST_FLAG_IS_SET (element, GST_ELEMENT_USE_THREADSAFE_PROPERTIES)) {
    g_object_get_valist (object, first_property_name, var_args);
    return;
  }

  g_object_ref (object);
  
  name = first_property_name;
  
  while (name)
    {
      GValue value = { 0, };
      GParamSpec *pspec;
      gchar *error;
      
      pspec =  g_object_class_find_property (G_OBJECT_GET_CLASS (object), name);

      if (!pspec)
	{
	  g_warning ("%s: object class `%s' has no property named `%s'",
		     G_STRLOC,
		     G_OBJECT_TYPE_NAME (object),
		     name);
	  break;
	}
      if (!(pspec->flags & G_PARAM_READABLE))
	{
	  g_warning ("%s: property `%s' of object class `%s' is not readable",
		     G_STRLOC,
		     pspec->name,
		     G_OBJECT_TYPE_NAME (object));
	  break;
	}
      
      g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
      
      element_get_property (element, pspec, &value);
      
      G_VALUE_LCOPY (&value, var_args, 0, &error);
      if (error)
	{
	  g_warning ("%s: %s", G_STRLOC, error);
	  g_free (error);
	  g_value_unset (&value);
	  break;
	}
      
      g_value_unset (&value);
      
      name = va_arg (var_args, gchar*);
    }
  
  g_object_unref (object);
}

Wim Taymans's avatar
Wim Taymans committed
544 545
/**
 * gst_element_set_property:
546 547 548
 * @element: a #GstElement to set properties on.
 * @property_name: the first property to get.
 * @value: the #GValue that holds the value to set.
Wim Taymans's avatar
Wim Taymans committed
549
 *
550
 * Sets a property on an element. If the element uses threadsafe properties,
Wim Taymans's avatar
Wim Taymans committed
551 552
 * the property will be put on the async queue.
 */
553
void
554 555
gst_element_set_property (GstElement *element, const gchar *property_name, 
		          const GValue *value)
556 557 558 559 560 561 562 563
{
  GParamSpec *pspec;
  GObject *object;
  
  g_return_if_fail (GST_IS_ELEMENT (element));
  g_return_if_fail (property_name != NULL);
  g_return_if_fail (G_IS_VALUE (value));
  
564
  object = (GObject*) element;
565

566
  GST_CAT_DEBUG (GST_CAT_PROPERTIES, "setting property %s on element %s",
567
	     property_name, gst_element_get_name (element));
568 569 570 571 572 573 574
  if (!GST_FLAG_IS_SET (element, GST_ELEMENT_USE_THREADSAFE_PROPERTIES)) {
    g_object_set_property (object, property_name, value);
    return;
  }

  g_object_ref (object);
  
575 576
  pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (object), 
		                        property_name);
577 578 579 580 581 582 583 584 585 586 587 588
  
  if (!pspec)
    g_warning ("%s: object class `%s' has no property named `%s'",
	       G_STRLOC,
	       G_OBJECT_TYPE_NAME (object),
	       property_name);
  else
    element_set_property (element, pspec, value);
  
  g_object_unref (object);
}
  
Wim Taymans's avatar
Wim Taymans committed
589 590
/**
 * gst_element_get_property:
591 592 593
 * @element: a #GstElement to get properties of.
 * @property_name: the first property to get.
 * @value: the #GValue to store the property value in.
Wim Taymans's avatar
Wim Taymans committed
594
 *
595
 * Gets a property from an element. If the element uses threadsafe properties,
Wim Taymans's avatar
Wim Taymans committed
596 597
 * the element will be locked before getting the given property.
 */
598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659
void
gst_element_get_property (GstElement *element, const gchar *property_name, GValue *value)
{
  GParamSpec *pspec;
  GObject *object;
  
  g_return_if_fail (GST_IS_ELEMENT (element));
  g_return_if_fail (property_name != NULL);
  g_return_if_fail (G_IS_VALUE (value));
  
  object = (GObject*)element;

  if (!GST_FLAG_IS_SET (element, GST_ELEMENT_USE_THREADSAFE_PROPERTIES)) {
    g_object_get_property (object, property_name, value);
    return;
  }

  g_object_ref (object);
  
  pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (object), property_name);
  
  if (!pspec)
    g_warning ("%s: object class `%s' has no property named `%s'",
	       G_STRLOC,
	       G_OBJECT_TYPE_NAME (object),
	       property_name);
  else
    {
      GValue *prop_value, tmp_value = { 0, };
      
      /* auto-conversion of the callers value type
       */
      if (G_VALUE_TYPE (value) == G_PARAM_SPEC_VALUE_TYPE (pspec))
	{
	  g_value_reset (value);
	  prop_value = value;
	}
      else if (!g_value_type_transformable (G_PARAM_SPEC_VALUE_TYPE (pspec), G_VALUE_TYPE (value)))
	{
	  g_warning ("can't retrieve property `%s' of type `%s' as value of type `%s'",
		     pspec->name,
		     g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)),
		     G_VALUE_TYPE_NAME (value));
	  g_object_unref (object);
	  return;
	}
      else
	{
	  g_value_init (&tmp_value, G_PARAM_SPEC_VALUE_TYPE (pspec));
	  prop_value = &tmp_value;
	}
      element_get_property (element, pspec, prop_value);
      if (prop_value != value)
	{
	  g_value_transform (prop_value, value);
	  g_value_unset (&tmp_value);
	}
    }
  
  g_object_unref (object);
}

660 661 662 663 664 665
static GstPad*
gst_element_request_pad (GstElement *element, GstPadTemplate *templ, const gchar* name)
{
  GstPad *newpad = NULL;
  GstElementClass *oclass;

Wim Taymans's avatar
Wim Taymans committed
666 667
  oclass = GST_ELEMENT_GET_CLASS (element);

668 669 670 671 672 673
  if (oclass->request_new_pad)
    newpad = (oclass->request_new_pad)(element, templ, name);

  return newpad;
}

Wim Taymans's avatar
Wim Taymans committed
674 675
/**
 * gst_element_release_request_pad:
676 677
 * @element: a #GstElement to release the request pad of.
 * @pad: the #GstPad to release.
Wim Taymans's avatar
Wim Taymans committed
678
 *
679
 * Makes the element free the previously requested pad as obtained
Wim Taymans's avatar
Wim Taymans committed
680 681
 * with gst_element_get_request_pad().
 */
682 683 684 685 686 687 688 689
void
gst_element_release_request_pad (GstElement *element, GstPad *pad)
{
  GstElementClass *oclass;

  g_return_if_fail (GST_IS_ELEMENT (element));
  g_return_if_fail (GST_IS_PAD (pad));

Wim Taymans's avatar
Wim Taymans committed
690 691
  oclass = GST_ELEMENT_GET_CLASS (element);

692 693 694 695
  if (oclass->release_pad)
    (oclass->release_pad) (element, pad);
}

696 697 698 699 700 701 702 703 704 705 706 707 708
/**
 * gst_element_requires_clock:
 * @element: a #GstElement to query
 *
 * Query if the element requiresd a clock
 *
 * Returns: TRUE if the element requires a clock
 */
gboolean
gst_element_requires_clock (GstElement *element)
{
  g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);

Wim Taymans's avatar
Wim Taymans committed
709
  return (GST_ELEMENT_GET_CLASS (element)->set_clock != NULL);
710 711 712 713 714 715 716 717 718 719 720 721 722 723 724
}

/**
 * gst_element_provides_clock:
 * @element: a #GstElement to query
 *
 * Query if the element provides a clock
 *
 * Returns: TRUE if the element provides a clock
 */
gboolean
gst_element_provides_clock (GstElement *element)
{
  g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);

Wim Taymans's avatar
Wim Taymans committed
725
  return (GST_ELEMENT_GET_CLASS (element)->get_clock != NULL);
726 727
}

728 729
/**
 * gst_element_set_clock:
730 731
 * @element: a #GstElement to set the clock for.
 * @clock: the #GstClock to set for the element.
732
 *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
733
 * Sets the clock for the element.
734 735 736 737
 */
void
gst_element_set_clock (GstElement *element, GstClock *clock)
{
Wim Taymans's avatar
Wim Taymans committed
738 739
  GstElementClass *oclass;

740 741
  g_return_if_fail (GST_IS_ELEMENT (element));

Wim Taymans's avatar
Wim Taymans committed
742 743 744 745
  oclass = GST_ELEMENT_GET_CLASS (element);

  if (oclass->set_clock)
    oclass->set_clock (element, clock);
746

747
  gst_object_replace ((GstObject **)&element->clock, (GstObject *)clock);
748 749 750 751
}

/**
 * gst_element_get_clock:
752
 * @element: a #GstElement to get the clock of.
753
 *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
754
 * Gets the clock of the element.
Wim Taymans's avatar
Wim Taymans committed
755
 *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
756
 * Returns: the #GstClock of the element.
757 758 759 760
 */
GstClock*
gst_element_get_clock (GstElement *element)
{
Wim Taymans's avatar
Wim Taymans committed
761 762
  GstElementClass *oclass;

763
  g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
Wim Taymans's avatar
Wim Taymans committed
764 765

  oclass = GST_ELEMENT_GET_CLASS (element);
766
  
Wim Taymans's avatar
Wim Taymans committed
767 768
  if (oclass->get_clock)
    return oclass->get_clock (element);
769 770 771 772 773 774

  return NULL;
}

/**
 * gst_element_clock_wait:
775
 * @element: a #GstElement.
776
 * @id: the #GstClock to use.
777
 * @jitter: the difference between requested time and actual time.
778
 *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
779
 * Waits for a specific time on the clock.
Wim Taymans's avatar
Wim Taymans committed
780
 *
781
 * Returns: the #GstClockReturn result of the wait operation.
782 783
 */
GstClockReturn
784
gst_element_clock_wait (GstElement *element, GstClockID id, GstClockTimeDiff *jitter)
785
{
786 787
  GstClockReturn res;

788 789
  g_return_val_if_fail (GST_IS_ELEMENT (element), GST_CLOCK_ERROR);

790
  if (GST_ELEMENT_SCHED (element)) {
791
    GST_CAT_DEBUG (GST_CAT_CLOCK, "waiting on scheduler clock");
792
    res = gst_scheduler_clock_wait (GST_ELEMENT_SCHED (element), element, id, jitter);
793
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
794
  else {
795
    GST_CAT_DEBUG (GST_CAT_CLOCK, "no scheduler, returning GST_CLOCK_TIMEOUT");
796
    res = GST_CLOCK_TIMEOUT;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
797
  }
798 799 800

  return res;
}
801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886
#undef GST_CAT_DEFAULT
#define GST_CAT_DEFAULT GST_CAT_CLOCK
/**
 * gst_element_get_time:
 * @element: element to query
 *
 * Query the element's time. The element must use 
 * 
 * Returns: the current time of the element or #GST_CLOCK_TIME_NONE when there
 *          is no time available.
 */
GstClockTime
gst_element_get_time (GstElement *element)
{
  g_return_val_if_fail (GST_IS_ELEMENT (element), GST_CLOCK_TIME_NONE);

  if (element->clock == NULL) {
    GST_WARNING_OBJECT (element, "element queries time but has no clock");
    return GST_CLOCK_TIME_NONE;
  }
  switch (element->current_state) {
    case GST_STATE_NULL:
    case GST_STATE_READY:
      return GST_CLOCK_TIME_NONE;
    case GST_STATE_PAUSED:
      return element->base_time;
    case GST_STATE_PLAYING:
      return gst_clock_get_time (element->clock) - element->base_time;
    default:
      g_assert_not_reached ();
      return GST_CLOCK_TIME_NONE;
  }   
}

GstClockID		gst_clock_new_single_shot_id	(GstClock *clock, 
							 GstClockTime time); 
void			gst_clock_id_free		(GstClockID id);
/**
 * gst_element_wait:
 * @element: element that should wait
 * @timestamp: wait until this time has arrived
 *
 * Waits until the given time has arrived. When this function returns successfully, 
 * the time specified in the timestamp has passed.
 * <note>This function can only be called on elements in #GST_STATE_PLAYING</note>
 *
 * Returns: TRUE on success
 */
gboolean
gst_element_wait (GstElement *element, GstClockTime timestamp)
{
  GstClockID id;
  GstClockReturn ret;
  
  g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
  g_return_val_if_fail (GST_IS_CLOCK (element->clock), FALSE);
  g_return_val_if_fail (element->current_state == GST_STATE_PLAYING, FALSE);
  g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE);
  
  /* shortcut when we're already late... */
  if (gst_element_get_time (element) >= timestamp) {
    GST_INFO_OBJECT (element, "called gst_element_wait and was late");
    return TRUE;
  }
  
  id = gst_clock_new_single_shot_id (element->clock, element->base_time + timestamp);
  ret = gst_element_clock_wait (element, id, NULL);
  gst_clock_id_free (id);

  return ret == GST_CLOCK_STOPPED;
}

/**
 * gst_element_set_time:
 * @element: element to set time on
 * @time: time to set
 *
 * Sets the current time of the element. This function can be used when handling
 * discont events. You can only call this function on an element with a clock in
 * #GST_STATE_PAUSED or #GST_STATE_PLAYING. You might want to have a look at 
 * gst_element_adjust_time(), if you want to adjust by a difference as that is
 * more accurate.
 */
void
gst_element_set_time (GstElement *element, GstClockTime time)
{
887 888
  GstClockTime event_time;
  
889 890 891 892 893 894 895 896 897
  g_return_if_fail (GST_IS_ELEMENT (element));
  g_return_if_fail (GST_IS_CLOCK (element->clock));
  g_return_if_fail (element->current_state >= GST_STATE_PAUSED);

  switch (element->current_state) {
    case GST_STATE_PAUSED:
      element->base_time = time;
      break;
    case GST_STATE_PLAYING:
898 899 900
      event_time = gst_clock_get_event_time (element->clock);
      GST_LOG_OBJECT (element, "clock time %llu: setting element time to %llu", event_time, time);
      element->base_time = event_time - time;
901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953
      break;
    default:
      g_assert_not_reached ();
      break;
  }
}

/**
 * gst_element_adjust_time:
 * @element: element to adjust time on
 * @difference: difference to adjust
 *
 * Adjusts the current time of the element by the specified difference. This 
 * function can be used when handling discont events. You can only call this 
 * function on an element with a clock in #GST_STATE_PAUSED or 
 * #GST_STATE_PLAYING. It is more accurate than gst_element_set_time().
 */
void
gst_element_adjust_time (GstElement *element, GstClockTimeDiff diff)
{
  GstClockTime time;
  
  g_return_if_fail (GST_IS_ELEMENT (element));
  g_return_if_fail (GST_IS_CLOCK (element->clock));
  g_return_if_fail (element->current_state >= GST_STATE_PAUSED);

  switch (element->current_state) {
    case GST_STATE_PAUSED:
      if (diff < 0 && element->base_time < abs (diff)) {
	g_warning ("attempted to set the current time of element %s below 0",
	    GST_OBJECT_NAME (element));
	element->base_time = 0;
      } else {
	element->base_time += diff;
      }
      break;
    case GST_STATE_PLAYING:
      time = gst_clock_get_time (element->clock);
      if (time < element->base_time - diff) {
	g_warning ("attempted to set the current time of element %s below 0",
	    GST_OBJECT_NAME (element));
	element->base_time = time;
      } else {
	element->base_time -= diff;
      }
      break;
    default:
      g_assert_not_reached ();
      break;
  }
}

#undef GST_CAT_DEFAULT
954

Wim Taymans's avatar
Wim Taymans committed
955
#ifndef GST_DISABLE_INDEX
Wim Taymans's avatar
Wim Taymans committed
956 957 958 959
/**
 * gst_element_is_indexable:
 * @element: a #GstElement.
 *
960
 * Queries if the element can be indexed.
Wim Taymans's avatar
Wim Taymans committed
961 962 963
 *
 * Returns: TRUE if the element can be indexed.
 */
964
gboolean
Wim Taymans's avatar
Wim Taymans committed
965
gst_element_is_indexable (GstElement *element)
966 967 968
{
  g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);

Wim Taymans's avatar
Wim Taymans committed
969
  return (GST_ELEMENT_GET_CLASS (element)->set_index != NULL);
970 971
}

Wim Taymans's avatar
Wim Taymans committed
972 973 974 975 976 977 978
/**
 * gst_element_set_index:
 * @element: a #GstElement.
 * @index: a #GstIndex.
 *
 * Set the specified GstIndex on the element.
 */
979
void
Wim Taymans's avatar
Wim Taymans committed
980
gst_element_set_index (GstElement *element, GstIndex *index)
981
{
Wim Taymans's avatar
Wim Taymans committed
982 983
  GstElementClass *oclass;

984
  g_return_if_fail (GST_IS_ELEMENT (element));
Wim Taymans's avatar
Wim Taymans committed
985
  g_return_if_fail (GST_IS_INDEX (index));
986

Wim Taymans's avatar
Wim Taymans committed
987 988 989 990
  oclass = GST_ELEMENT_GET_CLASS (element);

  if (oclass->set_index)
    oclass->set_index (element, index);
991 992
}

Wim Taymans's avatar
Wim Taymans committed
993 994 995 996 997 998 999 1000 1001
/**
 * gst_element_get_index:
 * @element: a #GstElement.
 *
 * Gets the index from the element.
 *
 * Returns: a #GstIndex or NULL when no index was set on the
 * element.
 */
Wim Taymans's avatar
Wim Taymans committed
1002 1003
GstIndex*
gst_element_get_index (GstElement *element)
1004
{
Wim Taymans's avatar
Wim Taymans committed
1005 1006
  GstElementClass *oclass;

1007 1008
  g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);

Wim Taymans's avatar
Wim Taymans committed
1009 1010 1011 1012
  oclass = GST_ELEMENT_GET_CLASS (element);

  if (oclass->get_index)
    return oclass->get_index (element);
1013 1014 1015

  return NULL;
}
Wim Taymans's avatar
Wim Taymans committed
1016
#endif
1017

1018 1019
/**
 * gst_element_release_locks:
1020
 * @element: a #GstElement to release all locks on.
1021
 *
Wim Taymans's avatar
Wim Taymans committed
1022
 * Instruct the element to release all the locks it is holding, such as
1023 1024 1025 1026 1027 1028 1029
 * blocking reads, waiting for the clock, ...
 *
 * Returns: TRUE if the locks could be released.
 */
gboolean
gst_element_release_locks (GstElement *element)
{
Wim Taymans's avatar
Wim Taymans committed
1030 1031
  GstElementClass *oclass;

1032 1033
  g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);

Wim Taymans's avatar
Wim Taymans committed
1034 1035 1036 1037
  oclass = GST_ELEMENT_GET_CLASS (element);

  if (oclass->release_locks)
    return oclass->release_locks (element);
1038 1039
  
  return TRUE;
1040 1041
}

Erik Walthinsen's avatar
Erik Walthinsen committed
1042 1043
/**
 * gst_element_add_pad:
1044 1045
 * @element: a #GstElement to add the pad to.
 * @pad: the #GstPad to add to the element.
Erik Walthinsen's avatar
Erik Walthinsen committed
1046
 *
1047
 * Add a pad (link point) to the element, setting the parent of the
Erik Walthinsen's avatar
Erik Walthinsen committed
1048
 * pad to the element (and thus adding a reference).
Benjamin Otte's avatar
Benjamin Otte committed
1049
 * Pads are automatically activated when the element is in state PLAYING.
Erik Walthinsen's avatar
Erik Walthinsen committed
1050
 */
1051 1052
void
gst_element_add_pad (GstElement *element, GstPad *pad)
Wim Taymans's avatar
Wim Taymans committed
1053 1054 1055
{
  g_return_if_fail (GST_IS_ELEMENT (element));
  g_return_if_fail (GST_IS_PAD (pad));
Erik Walthinsen's avatar
Erik Walthinsen committed
1056

1057
  /* first check to make sure the pad's parent is already set */
1058 1059
  g_return_if_fail (GST_PAD_PARENT (pad) == NULL);

1060
  /* then check to see if there's already a pad by that name here */
1061 1062
  g_return_if_fail (gst_object_check_uniqueness (element->pads, GST_PAD_NAME(pad)) == TRUE);

Erik Walthinsen's avatar
Erik Walthinsen committed
1063
  /* set the pad's parent */
1064
  GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS,"setting parent of pad '%s' to '%s'",
1065
        GST_PAD_NAME (pad), GST_STR_NULL (GST_ELEMENT_NAME (element)));
1066
  gst_object_set_parent (GST_OBJECT (pad), GST_OBJECT (element));
Erik Walthinsen's avatar
Erik Walthinsen committed
1067 1068

  /* add it to the list */
Wim Taymans's avatar
Wim Taymans committed
1069
  element->pads = g_list_append (element->pads, pad);
Erik Walthinsen's avatar
Erik Walthinsen committed
1070
  element->numpads++;
1071 1072 1073 1074
  if (gst_pad_get_direction (pad) == GST_PAD_SRC)
    element->numsrcpads++;
  else
    element->numsinkpads++;
Erik Walthinsen's avatar
Erik Walthinsen committed
1075

Benjamin Otte's avatar
Benjamin Otte committed
1076 1077 1078 1079
  /* activate element when we are playing */
  if (GST_STATE (element) == GST_STATE_PLAYING)
    gst_pad_set_active (pad, TRUE);
  
Erik Walthinsen's avatar
Erik Walthinsen committed
1080
  /* emit the NEW_PAD signal */