diff --git a/ChangeLog b/ChangeLog index 040f1d1d93..df700e65f9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,218 @@ +2005-03-07 Wim Taymans + + * Makefile.am: + * configure.ac: + * docs/design/part-MT-refcounting.txt: + * docs/design/part-conventions.txt: + * docs/design/part-gstobject.txt: + * docs/design/part-relations.txt: + * examples/mixer/mixer.c: (main): + * examples/thread/thread.c: (eos), (main): + * gst/Makefile.am: + * gst/autoplug/gstsearchfuncs.c: (gst_autoplug_caps_intersect): + * gst/autoplug/gstspider.c: (gst_spider_identity_plug), + (gst_spider_plug_from_srcpad): + * gst/autoplug/gstspideridentity.c: (gst_spider_identity_getcaps), + (gst_spider_identity_change_state), + (gst_spider_identity_sink_loop_type_finding): + * gst/elements/gstfakesrc.c: (gst_fakesrc_loop): + * gst/elements/gstidentity.c: (gst_identity_init): + * gst/elements/gsttee.c: (gst_tee_init), (gst_tee_getcaps), + (gst_tee_link), (gst_tee_request_new_pad), (gst_tee_chain): + * gst/elements/gsttypefindelement.c: (free_entry): + * gst/gst.c: + * gst/gst.h: + * gst/gstbin.c: (gst_bin_init), (gst_bin_get_clock_func), + (gst_bin_set_clock_func), (gst_bin_auto_clock), + (gst_bin_set_index), (gst_bin_set_element_sched), + (gst_bin_unset_element_sched), (gst_bin_add_func), (gst_bin_add), + (gst_bin_remove_func), (gst_bin_remove), (iterate_child), + (gst_bin_iterate_elements), (iterate_child_recurse), + (gst_bin_iterate_recurse), (gst_bin_dispose), (compare_name), + (gst_bin_get_by_name), (gst_bin_get_by_name_recurse_up), + (compare_interface), (gst_bin_get_by_interface), + (gst_bin_iterate_all_by_interface), (gst_bin_iterate_func): + * gst/gstbin.h: + * gst/gstbuffer.c: (gst_buffer_get_type), (_gst_buffer_sub_free), + (gst_buffer_default_free), (gst_buffer_default_copy), + (gst_buffer_new), (gst_buffer_get_caps), (gst_buffer_set_caps), + (gst_buffer_create_sub): + * gst/gstbuffer.h: + * gst/gstcaps.c: (gst_caps_get_type), (gst_caps_new_empty), + (_gst_caps_free), (gst_caps_make_writable), (gst_caps_ref), + (gst_caps_unref), (gst_static_caps_get), + (gst_caps_remove_and_get_structure), (gst_caps_append), + (gst_caps_append_structure), (gst_caps_remove_structure), + (gst_caps_copy_nth), (gst_caps_set_simple), + (gst_caps_set_simple_valist), (gst_caps_is_fixed_foreach), + (gst_structure_is_equal_foreach), (gst_caps_is_subset), + (gst_caps_structure_intersect_field), (gst_caps_intersect), + (gst_caps_structure_subtract_field), (gst_caps_subtract), + (gst_caps_normalize_foreach), (gst_caps_compare_structures), + (gst_caps_structure_figure_out_union), + (gst_caps_switch_structures), (gst_caps_do_simplify), + (gst_caps_replace), (gst_caps_from_string), + (gst_caps_copy_conditional): + * gst/gstcaps.h: + * gst/gstclock.c: (gst_clock_entry_new), (gst_clock_id_ref), + (_gst_clock_id_free), (gst_clock_id_unref), + (gst_clock_id_compare_func), (gst_clock_id_wait), + (gst_clock_id_wait_async), (gst_clock_class_init), + (gst_clock_init), (gst_clock_dispose), (gst_clock_adjust_unlocked), + (gst_clock_get_time), (gst_clock_set_time_adjust), + (gst_clock_set_property), (gst_clock_get_property): + * gst/gstclock.h: + * gst/gstcompat.h: + * gst/gstcpu.c: (_gst_cpu_initialize_i386), (gst_cpu_get_flags): + * gst/gstdata.c: (gst_data_is_writable), (gst_data_copy_on_write): + * gst/gstdata.h: + * gst/gstelement.c: (gst_element_class_init), (gst_element_init), + (gst_element_requires_clock), (gst_element_provides_clock), + (gst_element_set_clock), (gst_element_clock_wait), + (gst_element_wait), (gst_element_set_time_delay), + (gst_element_is_indexable), (gst_element_add_pad), + (gst_element_add_ghost_pad), (gst_element_remove_pad), + (pad_compare_name), (gst_element_get_static_pad), + (gst_element_request_pad), (gst_element_get_request_pad), + (gst_element_get_pad), (iterate_pad), (gst_element_iterate_pads), + (gst_element_class_get_pad_template_list), + (gst_element_class_get_pad_template), (gst_element_error_func), + (gst_element_get_random_pad), (gst_element_get_event_masks), + (gst_element_send_event), (gst_element_seek), + (gst_element_get_query_types), (gst_element_query), + (gst_element_get_formats), (gst_element_convert), + (gst_element_is_locked_state), (gst_element_set_locked_state), + (gst_element_sync_state_with_parent), (gst_element_change_state), + (gst_element_finalize), (gst_element_yield), + (gst_element_interrupt), (gst_element_set_scheduler), + (gst_element_get_scheduler), (gst_element_set_loop_function): + * gst/gstelement.h: + * gst/gstevent.h: + * gst/gstformat.c: (_gst_format_initialize), (gst_format_register), + (gst_format_get_by_nick), (gst_format_get_details), + (gst_format_iterate_definitions): + * gst/gstformat.h: + * gst/gstindex.c: (gst_index_gtype_resolver): + * gst/gstinfo.c: + * gst/gstinfo.h: + * gst/gstmemchunk.c: (gst_mem_chunk_alloc), (gst_mem_chunk_alloc0), + (gst_mem_chunk_free): + * gst/gstobject.c: (gst_object_class_init), (gst_object_init), + (gst_object_ref), (gst_object_unref), (gst_object_sink), + (gst_object_replace), (gst_object_dispose), (gst_object_finalize), + (gst_object_dispatch_properties_changed), + (gst_object_set_name_default), (gst_object_set_name), + (gst_object_get_name), (gst_object_set_name_prefix), + (gst_object_get_name_prefix), (gst_object_set_parent), + (gst_object_get_parent), (gst_object_unparent), + (gst_object_check_uniqueness), (gst_object_save_thyself), + (gst_object_restore_thyself), (gst_object_real_restore_thyself), + (gst_object_set_property), (gst_object_get_property), + (gst_object_get_path_string): + * gst/gstobject.h: + * gst/gstpad.c: (gst_pad_dispose), (gst_real_pad_class_init), + (gst_real_pad_init), (gst_real_pad_get_property), + (gst_pad_custom_new), (gst_pad_get_direction), + (gst_pad_set_active), (gst_pad_is_active), + (gst_pad_set_event_function), (gst_pad_is_linked), + (gst_pad_link_free), (gst_pad_link_intersect), + (gst_pad_link_fixate), (gst_pad_set_caps), + (gst_pad_try_set_caps_nonfixed), (gst_pad_set_pad_template), + (gst_pad_get_real_parent), (gst_pad_add_ghost_pad), + (gst_pad_remove_ghost_pad), (_gst_pad_default_fixate_foreach), + (gst_pad_link_unnegotiate), (gst_pad_proxy_fixate), + (gst_pad_get_caps), (gst_pad_peer_get_caps), + (gst_pad_get_pad_template_caps), (gst_pad_get_peer), + (gst_pad_realize), (gst_pad_get_allowed_caps), + (gst_real_pad_dispose), (gst_real_pad_finalize), + (gst_pad_collectv), (gst_pad_collect_valist), + (gst_pad_template_dispose), (gst_pad_template_new), + (gst_pad_get_internal_links): + * gst/gstpad.h: + * gst/gstpipeline.c: (gst_pipeline_dispose), + (gst_pipeline_change_state): + * gst/gstpipeline.h: + * gst/gstplugin.c: + * gst/gstpluginfeature.c: (gst_plugin_feature_get_name), + (gst_plugin_feature_set_rank), (gst_plugin_feature_get_rank): + * gst/gstpluginfeature.h: + * gst/gstprobe.c: (gst_probe_dispatcher_dispatch): + * gst/gstquery.c: (_gst_query_type_initialize), + (gst_query_type_register), (gst_query_type_get_by_nick), + (gst_query_type_get_details), (gst_query_type_iterate_definitions): + * gst/gstquery.h: + * gst/gstqueue.c: (gst_queue_link_sink), (gst_queue_link_src): + * gst/gstscheduler.c: (gst_scheduler_add_element), + (gst_scheduler_factory_create): + * gst/gststructure.c: (gst_structure_set_parent_refcount), + (gst_structure_free), (gst_structure_set_name), + (gst_structure_id_set_value), (gst_structure_set_value), + (gst_structure_set_valist), (gst_structure_remove_field), + (gst_structure_remove_fields), + (gst_structure_remove_fields_valist), + (gst_structure_remove_all_fields), (gst_structure_foreach), + (gst_structure_map_in_place), + (gst_caps_structure_fixate_field_nearest_int), + (gst_caps_structure_fixate_field_nearest_double): + * gst/gststructure.h: + * gst/gstsystemclock.c: (gst_system_clock_class_init), + (gst_system_clock_init), (gst_system_clock_dispose), + (gst_system_clock_async_thread), + (gst_system_clock_id_wait_unlocked), (gst_system_clock_id_wait), + (gst_system_clock_id_wait_async), (gst_system_clock_id_unschedule): + * gst/gstsystemclock.h: + * gst/gsttag.c: (gst_tag_list_add_value_internal), + (gst_tag_list_copy_foreach), (structure_foreach_wrapper): + * gst/gsttaginterface.c: + * gst/gstthread.c: (gst_thread_dispose), + (gst_thread_release_children_locks), (gst_thread_change_state), + (gst_thread_main_loop): + * gst/gsttrashstack.h: + * gst/gsttypefind.c: (gst_type_find_factory_dispose): + * gst/gsttypes.h: + * gst/gstutils.c: (gst_element_get_compatible_pad_template), + (gst_element_request_pad), (gst_element_get_pad_from_template), + (gst_element_request_compatible_pad), + (gst_element_get_compatible_pad_filtered), + (gst_element_get_compatible_pad), (gst_element_state_get_name), + (gst_element_link_pads_filtered), (gst_element_link_filtered), + (gst_element_link_many), (gst_element_link), + (gst_element_link_pads), (gst_element_unlink_pads), + (gst_element_unlink_many), (gst_element_unlink), + (gst_pad_can_link_filtered), (gst_pad_can_link), + (gst_pad_use_fixed_caps), (gst_pad_get_fixed_caps_func), + (gst_object_default_error), (gst_bin_add_many), + (gst_bin_remove_many), (gst_element_populate_std_props), + (gst_element_class_install_std_props), (gst_buffer_merge), + (gst_buffer_stamp), (intersect_caps_func), (gst_pad_proxy_getcaps), + (link_fold_func), (gst_pad_proxy_setcaps): + * gst/gstutils.h: + * gst/gstvalue.c: (gst_value_deserialize_string): + * gst/parse/grammar.y: + * gst/schedulers/gstbasicscheduler.c: + (gst_basic_scheduler_cothreaded_chain), + (gst_basic_scheduler_chain_recursive_add), + (gst_basic_scheduler_pad_link): + * gst/schedulers/gstoptimalscheduler.c: + (get_group_schedule_function), + (gst_opt_scheduler_state_transition), + (gst_opt_scheduler_add_element), (element_get_reachables_func): + * libs/gst/bytestream/bytestream.c: + * libs/gst/dataprotocol/dataprotocol.c: + (gst_dp_header_from_buffer): + * po/nb.po: + * po/ru.po: + * tests/threadstate/threadstate2.c: (eos): + * tools/gst-compprep.c: (main): + * tools/gst-inspect.c: (print_field), (print_element_flag_info), + (print_pad_info), (print_children_info): + * tools/gst-launch.c: (idle_func), (main): + * tools/gst-md5sum.c: (idle_func), (main): + * tools/gst-xmlinspect.c: (print_element_info): + First THREADED backport attempt, focusing on adding locks and + making sure the API is threadsafe. Needs more work. More docs + follow this week. + 2005-02-24 Andy Wingo * tests/bench-complexity.scm: diff --git a/Makefile.am b/Makefile.am index aa4111ecb7..2e3bdb7869 100644 --- a/Makefile.am +++ b/Makefile.am @@ -8,13 +8,23 @@ SUBDIRS_DOCS = endif if BUILD_TESTS -SUBDIRS_TESTS = tests testsuite +## SUBDIRS_TESTS = tests testsuite +## FIXME: write tests from scratch +SUBDIRS_TESTS = +if HAVE_CHECK +SUBDIRS_CHECK = check +else +SUBDIRS_CHECK = +endif else SUBDIRS_TESTS = +SUBDIRS_CHECK = endif if BUILD_EXAMPLES -SUBDIRS_EXAMPLES = examples +## FIXME: write examples from scratch +# SUBDIRS_EXAMPLES = examples +SUBDIRS_EXAMPLES = else SUBDIRS_EXAMPLES = endif @@ -31,7 +41,9 @@ aclocal_DATA = gst-element-check-@GST_MAJORMINOR@.m4 SUBDIRS = \ include gst libs tools \ - $(SUBDIRS_TESTS) $(SUBDIRS_EXAMPLES) \ + $(SUBDIRS_CHECK) \ + $(SUBDIRS_TESTS) \ + $(SUBDIRS_EXAMPLES) \ pkgconfig po \ common \ $(SUBDIRS_DOCS) @@ -40,6 +52,7 @@ SUBDIRS = \ DIST_SUBDIRS = \ include libs gst \ tools \ + check \ tests testsuite \ examples \ pkgconfig \ diff --git a/configure.ac b/configure.ac index 43461c7a62..4e17be1adb 100644 --- a/configure.ac +++ b/configure.ac @@ -24,7 +24,7 @@ dnl - library source changed -> increment REVISION dnl - interfaces added/removed/changed -> increment CURRENT, REVISION = 0 dnl - interfaces added -> increment AGE dnl - interfaces removed -> AGE = 0 -AS_LIBTOOL(GST, 5, 0, 4) +AS_LIBTOOL(GST, 6, 0, 0) AM_PROG_LIBTOOL AC_CONFIG_SRCDIR([gst/gst.c]) @@ -314,6 +314,10 @@ AC_SUBST(POPT_LIBS) dnl Check for ucontext.h AC_CHECK_HEADER(ucontext.h, AC_DEFINE(HAVE_UCONTEXT_H, 1, [defined if we have ucontext.h])) +dnl check for "check", unit testing library/header +AM_PATH_CHECK(0.9.2, HAVE_CHECK=yes, HAVE_CHECK=no) +AM_CONDITIONAL(HAVE_CHECK, test "x$HAVE_CHECK" = "xyes") + dnl ###################################################################### dnl # Check command line parameters, and set shell variables accordingly # dnl ###################################################################### @@ -664,6 +668,7 @@ libs/gst/control/Makefile libs/gst/dataprotocol/Makefile libs/gst/getbits/Makefile po/Makefile.in +check/Makefile tests/Makefile tests/bufspeed/Makefile tests/instantiate/Makefile diff --git a/docs/design/part-MT-refcounting.txt b/docs/design/part-MT-refcounting.txt new file mode 100644 index 0000000000..e3b1e2ebd7 --- /dev/null +++ b/docs/design/part-MT-refcounting.txt @@ -0,0 +1,381 @@ +Conventions for thread a safe API +--------------------------------- + +The GStreamer API is designed to be thread safe. This means that API functions +can be called from multiple threads at the same time. GStreamer internally uses +threads to perform the data passing and various asynchronous services such as +the clock can also use threads. + +This design decision has implication for the usage of the API and the objects +which this document explains. + +MT safety techniques +-------------------- + +Several design patterns are used to guarantee object consistency in GStreamer. +This is an overview of the methods used in various GStreamer subsystems. + +Refcounting: + + All shared objects have a refcount associated with them. Each reference + obtained to the object should increase the refcount and each reference lost + should decrease the refcount. + + The refcounting is used to make sure that when another thread destroys the + object, the ones which still hold a reference to the object do not read from + invalid memory when accessing the object. + + Refcounting is also used to ensure that mutable data structures are only + modified when they are owned by the calling code. + + It is a requirement that when two threads have a handle on an object, the + refcount must be more than one. This means that when one thread passes an + object to another thread it must increase the refcount. This requirement makes + sure that one thread cannot suddenly dispose the object making the other + thread crash when it tries to access the pointer to invalid memory. + +Shared data structures and writability: + + All objects have a refcount associated with them. Each reference obtained to + the object should increase the refcount and each reference lost should + decrease the refcount. + + Each thread having a refcount to the object can safely read from the object. + but modifications made to the object should be preceeded with a + _get_writable() function call. This function will check the refcount of the + object and if the object is referenced by more than one instance, a copy is + made of the object that is then by definition only referenced from the calling + thread. This new copy is then modifyable without being visible to other + refcount holders. + + This technique is used for information objects that, once created, never + change their values. The lifetime of these objects is generally short, the + objects are usually simple and cheap to copy/create. + + The advantage of this method is that no reader/writers locks are needed. all + threads can concurrently read but writes happen locally on a new copy. In most + cases _get_writable() can avoid a real copy because the calling method is the + only one holding a reference, wich makes read/writes very cheap. + + The drawback is that sometimes 1 needless copy can be done. This would happen + when N threads call _get_writable() at the same time, all seeing that N + references are held on the object. In this case 1 copy too many will be done. + This is not a problem in any practical situation because the copy operation is + fast. + +Mutable substructures: + + Special techniques are necessary to ensure the consistency of compound shared + objects. As mentioned above, shared objects need to have a reference count of + 1 if they are to be modified. Implicit in this assumption is that all parts of + the shared object belong only to the object. For example, a GstStructure in + one GstCaps object should not belong to any other GstCaps object. This + condition suggests a parent-child relationship: structures can only be added + to parent object if they do not already have a parent object. + + In addition, these substructures must not be modified while more than one code + segment has a reference on the parent object. For example, if the user creates + a GstStructure, adds it to a GstCaps, and the GstCaps is then referenced by + other code segments, the GstStructure should then become immutable, so that + changes to that data structure do not affect other parts of the code. This + means that the child is only mutable when the parent's reference count is 1, + as well as when the child structure has no parent. + + The general solution to this problem is to include a field in child structures + pointing to the parent's atomic reference count. When set to NULL, this + indicates that the child has no parent. Otherwise, procedures that modify the + child structure must check if the parent's refcount is 1, and otherwise must + cause an error to be signaled. + + Note that this is an internal implementation detail; application or plugin + code that calls _get_writable() on an object is guaranteed to receive an + object of refcount 1, which must then be writable. The only trick is that a + pointer to a child structure of an object is only valid while the calling code + has a reference on the parent object, because the parent is the owner of the + child. + +Object locking: + + For objects that contain state information and generally have a longer + lifetime, object locking is used to update the information contained in the + object. + + All readers and writers acquire the lock before accessing the object. Only one + thread is allowed access the protected structures at a time. + + Object locking is used for all objects extending from GstObject such as + GstElement, GstPad. + +Atomic operations + + Atomic operations are operations that are performed as one consistent + operation even when executed by multiple threads. They do however not use the + conventional aproach of using mutexes to protect the critical section but rely + on CPU features and instructions. + + The advantages are mostly speed related since there are no heavyweight locks + involved. Most of this instructions also do not cause a context switch in case + of concurrent access but use a retry mechanism or spinlocking. + + Disadvantages are that each of these instructions usually cause a cache flush + on multi-CPU machines when two processors perform concurrent access. + + Atomic operations are generally used for refcounting and for the allocation of + small fixed size objects in a memchunk. They can also be used to implement a + lockfree list or stack. + + +Objects +------- + +* Locking involved: + + - atomic operations for refcounting + - object locking + + All objects should have a lock associated with them. This lock is used to keep + internal consistency when multiple threads call API function on the object. + + For objects that extend the GStreamer base object class this lock can be + obtained with the macros GST_LOCK() and GST_UNLOCK(). For other object that do + not extend from the base GstObject class these macros can be different. + +* refcounting + + All new objects created have the FLOATING flag set. This means that the object + is not owned or managed yet by anybody other than the one holding a reference + to the object. The object in this state has a reference count of 1. + + Various object methods can take ownership of another object, this means that + after calling a method on object A with an object B as an argument, the object + B is made sole property of object A. This means that after the method call you + are not allowed to access the object anymore unless you keep an extra + reference to the object. An example of such a method is the _bin_add() method. + As soon as this function is called in a Bin, the element passed as an argument + is owned by the bin and you are not allowed to access it anymore without + taking a _ref() before adding it to the bin. The reason being that after the + _bin_add() call disposing the bin also destroys the element. + + Taking ownership of an object happens trough the process of "sinking" the + object. the _sink() method on an object will decrease the refcount of the + object if the FLOATING flag is set. The act of taking ownership of an object + is then performed as a _ref() followed by a _sink() call on the object. + + The float/sink process is very useful when initializing elements that will + then be placed under control of a parent. The floating ref keeps the object + alive until it is parented, and once the object is parented you can forget + about it. + +* parent-child relations + + One can create parent-child relationships with the _object_set_parent() + method. This method refs and sinks the object and assigns its parent property + to that of the managing parent. + + The child is said to have a weak link to the parent since the refcount of the + parent is not increased in this process. This means that if the parent is + disposed it has to unset itself as the parent of the object before disposing + itself, else the child object holds a parent pointer to invalid memory. + + The responsibilites for an object that sinks other objects are summarised as: + + - taking ownership of the object + - call _object_set_parent() to set itself as the object parent, this call + will _ref() and _sink() the object. + - keep reference to object in a datastructure such as a list or array. + + - on dispose + - call _object_unparent() to reset the parent property and unref the + object. + - remove the object from the list. + + +* Properties + + Most objects also expose state information with public properties in the + object. Two types of properties might exist: accessible with or without + holding the object lock. All properties should only be accessed with their + corresponding macros. The public object properties are marked in the .h files + with /*< public >*/. The public properties that require a lock to be held are + marked with /*< public >*/ /* with */, where can be + "LOCK" or "STATE_LOCK" or any other lock to mark the type(s) of lock to be + held. + + Example: + + in GstPad there is a public property "direction". It can be found in the + section marked as public and requiring the LOCK to be held. There exists + also a macro to access the property. + + struct _GstRealPad { + ... + /*< public >*/ /* with LOCK */ + ... + GstPadDirection direction; + ... + }; + + #define GST_RPAD_DIRECTION(pad) (GST_REAL_PAD_CAST(pad)->direction) + + Accessing the property is therefore allowed with the following code example: + + GST_LOCK (pad); + direction = GST_RPAD_DIRECTION (pad); + GST_UNLOCK (pad); + +* Property lifetime + + All properties requiring a lock can change after releasing the associated + lock. This means that as long as you hold the lock, the state of the + object regarding the locked properties is consistent with the information + obtained. As soon as the lock is released, any values required from the + properties might not be valid anymore and can as best be described as a + snapshot of the state when the lock was held. + + This means that all properties that require access beyond the scope of the + critial section should be copied or refcounted before releasing the lock. + + Most object provide a _get_() method to get a copy or refcounted + instance of the property value. The caller should not wory about any locks + but should unref/free the object after usage. + + Example: + + the following example correctly gets the peer pad of an element. It is + required to increase the refcount of the peer pad because as soon as the + lock is released, the peer could be unreffed and disposed, making the + pointer obtained in the critical section point to invalid memory. + + GST_LOCK (pad); + peer = GST_RPAD_PEER (pad); + if (peer) + gst_object_ref (GST_OBJECT (peer)); + GST_UNLOCK (pad); + ... use peer ... + + if (peer) + gst_object_unref (GST_OBJECT (peer)); + + Note that after releasing the lock the peer might not actually be the peer + anymore of the pad. If you need to be sure it is, you need to extend the + critical section to include the operations on the peer. + + Example: + + Accessing the name of an object makes a copy of the name. The caller of the + function should g_free() the name after usage. + + GST_LOCK (object) + name = g_strdup (object->name); + GST_UNLOCK (object) + ... use name ... + + g_free (name); + + +* Accessor methods + + For aplications it is encouraged to use the public methods of the object. Most + useful operations can be performed with the methods so it is seldom required + to access the public fields manually. + + All accessor methods that return an object should increase the refcount of the + returned object. The caller should _unref() the object after usage. Each + method should state this refcounting policy in the documentation. + +* Accessing lists + + If the object property is a list, concurrent list iteration is needed to get + the contents of the list. GStreamer uses the cookie mechanism to mark the last + update of a list. The list and the cookie are protected by the same lock. Each + update to a list requires the following actions: + + - acquire lock + - update list + - update cookie + - release lock + + Updating the cookie is usually done by incrementing its value by one. Since + cookies use guint32 its wraparound is for all practical reasons is not a + problem. + + Iterating a list can safely be done by surrounding the list iteration with a + lock/unlock of the lock. + + In some cases it is not a good idea to hold the lock for a long time while + iterating the list. The state change code for a bin in GStreamer, for example, + has to iterate over each element and perform a blocking call on each of them + causing infinite bin locking. In this case the cookie can be used to iterate a + list. + + Example: + + The following algorithm iterates a list and reverses the updates in the + case a concurrent update was done to the list while iterating. The idea is + that whenever we reacquire the lock, we check for updates to the cookie to + decide if we are still iterating the right list. + + GST_LOCK (lock); + /* grab list and cookie */ + cookie = object->list_cookie; + list = object-list; + while (list) { + GstObject *item = GST_OBJECT (list->data); + /* need to ref the item before releasing the lock */ + gst_object_ref (item); + GST_UNLOCK (lock); + + ... use/change item here... + + /* release item here */ + gst_object_unref (item); + + GST_LOCK (lock); + if (cookie != object->list_cookie) { + /* handle rollback caused by concurrent modification + * of the list here */ + + ...rollback changes to items... + + /* grab new cookie and list */ + cookie = object->list_cookie; + list = object->list; + } + else { + list = g_list_next (list); + } + } + GST_UNLOCK (lock); + +* GstIterator + + GstIterator provides an easier way of retrieving elements in a concurrent + list. The following code example is equivalent to the previous example. + + Example: + + it = _get_iterator(object); + while (!done) { + switch (gst_iterator_next (it, &item)) { + case GST_ITERATOR_OK: + + ... use/change item here... + + /* release item here */ + gst_object_unref (item); + break; + case GST_ITERATOR_RESYNC: + /* handle rollback caused by concurrent modification + * of the list here */ + + ...rollback changes to items... + + /* resync iterator to start again */ + gst_iterator_resync (it); + break; + case GST_ITERATOR_DONE: + done = TRUE; + break; + } + } + gst_iterator_free (it); diff --git a/docs/design/part-conventions.txt b/docs/design/part-conventions.txt index da634b8f5a..f2610a7278 100644 --- a/docs/design/part-conventions.txt +++ b/docs/design/part-conventions.txt @@ -16,7 +16,7 @@ Function names -------------- Within the context of a given object, functions defined in that object's header and/or source file will have their -object-specific prefix stripped. For instance, gst_element_add_pad() would be referred to as simply _get_pad(). Note +object-specific prefix stripped. For instance, gst_element_add_pad() would be referred to as simply _add_pad(). Note that the trailing parentheses should always be present, but sometimes may not be. A prefixing underscore (_) will always tell you it's a function, however, regardless of the presence or absence of the trailing parentheses. @@ -25,7 +25,59 @@ always tell you it's a function, however, regardless of the presence or absence Values and macros defined as enums and preprocessor macros will be referred to in all capitals, as per their definition. This includes object flags and element states, as well as general enums. Examples are the states NULL, -READY, PLAYING, and PAUSED; the element flags DECOUPLED and USE_COTHREAD, and state return values SUCCESS, FAILURE, and +READY, PLAYING, and PAUSED; the element flags LOCKED_STATE , and state return values SUCCESS, FAILURE, and ASYNC. Where there is a prefix, as in the element flags, this is usually dropped, and implied. Not however that element flags should be cross-checked with the header, as there are currently two conventions in use: with and without _FLAGS_ in the middle. + +FIXME: check flags for consistency. + +Drawing conventions +=================== + +When drawing pictures the folowing conventions apply: + +objects +------- + +Objects are drawn with a box like + +------+ + | | + +------+ + + +pointers +-------- + +a pointer to an object. + +-----+ + *--->| | + +-----+ + +an invalid pointer, this is a pointer that should not be used. + + *-//-> + + +elements +-------- + + +----------+ + | name | + sink src + +----------+ + +pad links +--------- + + -----+ +--- + | | + src--sink + -----+ +--- + + + + + + + diff --git a/docs/design/part-gstobject.txt b/docs/design/part-gstobject.txt index 7445016c9d..a85480e21c 100644 --- a/docs/design/part-gstobject.txt +++ b/docs/design/part-gstobject.txt @@ -1,42 +1,72 @@ GstObject ========= -The base class for the entire GStreamer hierarchy is the GstObject. It is currently a bit of a hack caused by the -fact that GStreamer is built on top of GtkObject. GObject should help, if it's designed properly. Currently the -capabilities provided by GstObject are quite underutilized, this will be fixed with a refactoring of the -object system and a code cleanup at some point in the future. If nothing else, it serves as an easy check to see if a -given object belongs to GStreamer. +The base class for the entire GStreamer hierarchy is the GstObject. Parentage --------- -A pointer is available to store the current parent of the object. This one of the two fundamental requires for a -hierarchical system such as GStreamer (for the other, read up on GstBin). Three functions are provided: _set_parent(), -_get_parent(), and _unparent(). The third is required because there is an explicit check in _set_parent(): an object -must not already have a parent if you wish to set one. You must unparent the object first. This allows for new -additions later. +A pointer is available to store the current parent of the object. This is one +of the two fundamental requires for a hierarchical system such as GStreamer +(for the other, read up on GstBin). Three functions are provided: +_set_parent(), _get_parent(), and _unparent(). The third is required because +there is an explicit check in _set_parent(): an object must not already have a +parent if you wish to set one. You must unparent the object first. This +allows for new additions later. + +- GstObject's that can be parented: + GstElement (inside a bin) + GstPad (inside an element) Refcounting ----------- +- GObject refcount is not threadsafe. + GStreamer sets it to a constant value on each _ref/_unref() + and uses an atomic int "refcount" instead for threadsafe refcounting + This implies you should always use gst_object_ref() and gst_object_unref() ! -A reference count is kept for the object. When this is fully utilized, it will enable generic destruction of objects -by simply reducing the reference count to zero. GObject should provide these capabilities, however. +Naming +------ +- names of objects cannot be changed when they are parented +- names of objects should be unique across parent +- set_name() can fail because of this +- as can gst_element_add_pad()/gst_bin_add_element() +- gst_object_set_name() only changes the object's name + +- objects also have a name_prefix that is used to prefix the object name + during debugging and identification +- there are object-specific set_name's() which also set the name_prefix + on the object. This is useful for debugging purposes to give the object + a more identifiable name. Typically a parent will call _set_name_prefix + on children, taking a lock on them to do so. Locking ------- -The GstObject contains the necessary primitives to lock the object in a thread-safe manner. This will be used to -provide general thread-safety as needed. However, this lock is generic, i.e. it covers the whole object. Note that -this does *not* mean that no other thread can modify the object at the same time that the lock is held. It only means -that any two sections of code that obey the lock are guaranteed to not be running simultaneously. +The GstObject contains the necessary primitives to lock the object in a +thread-safe manner. This will be used to provide general thread-safety as +needed. However, this lock is generic, i.e. it covers the whole object. -This lock will ideally be used for parentage and refcounting, which is reasonable, since they are the only possible -things to protect in the GstObject. +All members of the GstObject structure marked as +/*< public >*/ /* with LOCK */ +are protected by this lock. These members can only be accessed for reading +or writing while the lock is held. + +Note that this does *not* mean that no other thread can modify the object at +the same time that the lock is held. It only means that any two sections of +code that obey the lock are guaranteed to not be running simultaneously. "The +lock is voluntary and cooperative". + +This lock will ideally be used for parentage and refcounting, which is +reasonable, since they are the only possible things to protect in the +GstObject. Path Generation --------------- +FIXME: rethink this ? -Due to the base nature of the GstObject, it becomes the only reasonable place to put this particular function -(_get_path_string). It will generate a string describing the parent hierarchy of a given GstObject. Currently it is -forced to use several child-class-specific functions, because we do not properly use the base capabilities (parentage, -etc.) of GstObject properly. +Due to the base nature of the GstObject, it becomes the only reasonable place +to put this particular function (_get_path_string). It will generate a string +describing the parent hierarchy of a given GstObject. Currently it is forced +to use several child-class-specific functions, because we do not properly use +the base capabilities (parentage, etc.) of GstObject properly. diff --git a/docs/design/part-relations.txt b/docs/design/part-relations.txt new file mode 100644 index 0000000000..f2deabbff4 --- /dev/null +++ b/docs/design/part-relations.txt @@ -0,0 +1,487 @@ +Object relation types +--------------------- + +1) parent-child relation + + +---------+ +-------+ + | parent | | child | + *--->| *----->| | + | F1|<-----* 1| + +---------+ +-------+ + + - properties + + - parent has references to multiple children + - child has reference to parent + - reference fields protected with LOCK + - the reference held by each child to the parent is + NOT reflected in the refcount of the parent. + - the parent removes the floating flag of the child when taking + ownership. + - the application has valid reference to parent + - creation/destruction requires two unnested locks and 1 refcount. + + - usage in GStreamer + + GstBin -> GstElement + GstElement -> GstRealPad + + - lifecycle + + a) object creation + + The application creates two object and holds a pointer + to them. The objects are initially FLOATING with a refcount + of 1. + + +---------+ +-------+ + *--->| parent | *--->| child | + | * | | | + | F1| | * F1| + +---------+ +-------+ + + b) establishing the parent-child relationship + + The application then calls a method on the parent object to take + ownership of the child object. The parent performs the following + actions: + + result = _set_parent (child, parent); + if (result) { + LOCK (parent); + ref_pointer = child; + + .. update other data structures .. + UNLOCK (parent); + } + else { + .. child had parent .. + } + + The _set_parent() method performs the following actions: + + LOCK (child); + if (child->parent != NULL) { + UNLOCK (child); + return FALSE; + } + if (IS_FLOATING (child)) { + UNSET (child, FLOATING); + } + else { + _ref (child); + } + child->parent = parent; + UNLOCK (child); + _signal (PARENT_SET, child, parent); + return TRUE; + + The function atomically checks if the child has no parent yet + and will set the parent if not. It will also sink the child, meaning + all floating references to the child are invalid now as it takes + over the refcount of the object. + + Visually: + + after _set_parent() returns TRUE: + + +---------+ +-------+ + *---->| parent | *-//->| child | + | * | | | + | F1|<-------------* 1| + +---------+ +-------+ + + after parent updates ref_pointer to child. + + +---------+ +-------+ + *---->| parent | *-//->| child | + | *--------->| | + | F1|<---------* 1| + +---------+ +-------+ + + - only one parent is able to _sink the same object because the + _set_parent() method is atomic. + - since only one parent is able to _set_parent() the object, only + one will add a reference to the object. + - since the parent can hold multiple references to children, we don't + need to lock the parent when locking the child. Many threads can + call _set_parent() on the children with the same parent, the parent + can then add all those to its lists. + + Note: that the signal is emited before the parent has added the + element to its internal data structures. This is not a problem + since the parent usually has his own signal to inform the app that + the child was reffed. One possible solution would be to update the + internal structure first and then perform a rollback if the _set_parent() + failed. This is not a good solution as iterators might grab the + 'half-added' child too soon. + + c) using the parent-child relationship + + - since the initial floating reference to the child object became + invalid after giving it to the parent, any reference to a child + has at least a refcount > 1. + + - this means that unreffing a child object cannot decrease the refcount + to 0. In fact, only the parent can destroy and dispose the child + object. + + - given a reference to the child object, the parent pointer is only + valid when holding the child LOCK. Indeed, after unlocking the child + LOCK, the parent can unparent the child or the parent could even become + disposed. To avoid the parent dispose problem, when obtaining the + parent pointer, if should be reffed before releasing the child LOCK. + + I) getting a reference to the parent. + + - a referece is held to the child, so it cannot be disposed. + + LOCK (child); + parent = _ref (child->parent); + UNLOCK (child); + + .. use parent .. + + _unref (parent); + + II) getting a reference to a child + + - a reference to a child can be obtained by reffing it before + adding it to the parent or by querying the parent. + + - when requesting a child from the parent, a reference is held to + the parent so it cannot be disposed. The parent will use its + internal data structures to locate the child element and will + return a reference to it with an incremented refcount. The + requester should _unref() the child after usage. + + + d) destroying the parent-child relationship + + - only the parent can actively destroy the parent-child relationship + this typically happens when a method is called on the parent to release + ownership of the child. + + - a child shall never remove itself from the parent. + + - since calling a method on the parent with the child as an argument + requires the caller to obtain a valid reference to the child, the child + refcount is at least > 1. + + - the parent will perform the folowing actions: + + LOCK (parent); + if (ref_pointer == child) { + ref_pointer = NULL; + + .. update other data structures .. + UNLOCK (parent); + + _unparent (child); + } + else { + UNLOCK (parent); + .. not our child .. + } + + The _unparent() method performs the following actions: + + LOCK (child); + if (child->parent != NULL) { + child->parent = NULL; + UNLOCK (child); + _signal (PARENT_UNSET, child, parent); + + _unref (child); + } + else { + UNLOCK (child); + } + + Since the _unparent() method unrefs the child object, it is possible that + the child pointer is invalid after this function. If the parent wants to + perform other actions on the child (such as signal emmision) it should + _ref() the child first. + + +2) single-reffed relation + + +---------+ +---------+ + *--->| object1 | *--->| object2 | + | *--------->| | + | 1| | 2| + +---------+ +---------+ + + - properties + + - one object has a reference to another + - reference field protected with LOCK + - the reference held by the object is reflected in the + refcount of the other object. + - typically the other object can be shared among multiple + other objects where each ref is counted for in the + refcount. + - no object has ownership of the other. + - either shared state or copy-on-write. + - creation/destruction requires one lock and one refcount. + + - usage + + GstRealPad -> GstCaps + GstBuffer -> GstCaps + GstEvent -> GstCaps + GstEvent -> GstObject + GstMessage -> GstCaps + GstMessage -> GstObject + + - lifecycle + + a) Two objects exist unlinked. + + +---------+ +---------+ + *--->| object1 | *--->| object2 | + | * | | | + | 1| | 1| + +---------+ +---------+ + + b) establishing the single-reffed relationship + + The second object is attached to the first one using a method + on the first object. The second object is reffed and a pointer + is updated in the first object using the following algorithm: + + LOCK (object1); + if (object1->pointer) + _unref (object1->pointer); + object1->pointer = _ref (object2); + UNLOCK (object1); + + After releasing the lock on the first object is is not sure that + object2 is still reffed from object1. + + +---------+ +---------+ + *--->| object1 | *--->| object2 | + | *--------->| | + | 1| | 2| + +---------+ +---------+ + + c) using the single-reffed relationship + + The only way to access object2 is by holding a ref to it or by + getting the reference from object1. + Reading the object pointed to by object1 can be done like this: + + LOCK (object1); + object2 = object1->pointer; + _ref (object2); + UNLOCK (object1); + + .. use object2 ... + _unref (object2); + + Depending on the type of the object, modifications can be done either + with copy-on-write or directly into the object. + + Copy on write can practically only be done like this: + + LOCK (object1); + object2 = object1->pointer; + object2 = _copy_on_write (object2); + ... make modifications to object2 ... + UNLOCK (object1); + + Releasing the lock has only a very small window where the copy_on_write + actually does not perform a copy: + + LOCK (object1); + object2 = object1->pointer; + _ref (object2); + UNLOCK (object1); + + .. object2 now has at least 2 refcounts making the next + copy-on-write make a real copy, unless some other thread + writes another object2 to object1 here ... + + object2 = _copy_on_write (object2); + + .. make modifications to object2 ... + + LOCK (object1); + if (object1->pointer != object2) { + if (object1->pointer) + _unref (object1->pointer); + object1->pointer = gst_object_ref (object2); + } + UNLOCK (object1); + + d) destroying the single-reffed relationship + + The folowing algorithm removes the single-reffed link between + object1 and object2. + + LOCK (object1); + _unref (object1->pointer); + object1->pointer = NULL; + UNLOCK (object1); + + Which yields the following initial state again: + + +---------+ +---------+ + *--->| object1 | *--->| object2 | + | * | | | + | 1| | 1| + +---------+ +---------+ + + +3) unreffed relation + + +---------+ +---------+ + *--->| object1 | *--->| object2 | + | *--------->| | + | 1|<---------* 1| + +---------+ +---------+ + + - properties + + - two objects have references to eachother + - both objects can only have 1 reference to another object. + - reference fields protected with LOCK + - the references held by each object are NOT reflected in the + refcount of the other object. + - no object has ownership of the other. + - typically each object is owned by a different parent. + - creation/destruction requires two nested locks and no refcounts. + + - usage + + - This type of link is used when the link is less important than + the existance of the objects, If one of the objects is disposed, so + is the link. + + GstRealPad <-> GstRealPad (srcpad lock taken first) + + - lifecycle + + a) Two objects exist unlinked. + + +---------+ +---------+ + *--->| object1 | *--->| object2 | + | * | | | + | 1| | * 1| + +---------+ +---------+ + + b) establishing the unreffed relationship + + Since we need to take two locks, the order in which these locks are + taken is very important or we might cause deadlocks. This lock order + must be defined for all unreffed relations. In these examples we always + lock object1 first and then object2. + + LOCK (object1); + LOCK (object2); + object2->refpointer = object1; + object1->refpointer = object2; + UNLOCK (object2); + UNLOCK (object1); + + c) using the unreffed relationship + + Reading requires taking one of the locks and reading the corresponing + object. Again we need to ref the object before releasing the lock. + + LOCK (object1); + object2 = _ref (object1->refpointer); + UNLOCK (object1); + + .. use object2 .. + _unref (object2); + + d) destroying the unreffed relationship + + Because of the lock order we need to be careful when destroying this + Relation. + + When only a reference to object1 is held: + + LOCK (object1); + LOCK (object2); + object1->refpointer->refpointer = NULL; + object1->refpointer = NULL; + UNLOCK (object2); + UNLOCK (object1); + + When only a reference to object2 is held we need to get a handle to the + other object fist so that we can lock it first. There is a window where + we need to release all locks and the relation could be invalid. To solve + this we check the relation after grabbing both locks and retry if the + relation changed. + + retry: + LOCK (object2); + object1 = _ref (object2->refpointer); + UNLOCK (object2); + .. things can change here .. + LOCK (object1); + LOCK (object2); + if (object1 == object2->refpointer) { + /* relation unchanged */ + object1->refpointer->refpointer = NULL; + object1->refpointer = NULL; + } + else { + /* relation changed.. retry */ + UNLOCK (object2); + UNLOCK (object1); + _unref (object1); + goto retry; + } + UNLOCK (object2); + UNLOCK (object1); + _unref (object1); + + When references are held to both objects. Note that it is not possible to + get references to both objects with the locks released since when the + references are taken and the locks are released, a concurrent update might + have changed the link, making the references not point to linked objects. + + LOCK (object1); + LOCK (object2); + if (object1->refpointer == object2) { + object2->refpointer = NULL; + object1->refpointer = NULL; + } + else { + .. objects are not linked .. + } + UNLOCK (object2); + UNLOCK (object1); + + +4) double-reffed relation + + +---------+ +---------+ + *--->| object1 | *--->| object2 | + | *--------->| | + | 2|<---------* 2| + +---------+ +---------+ + + - properties + + - two objects have references to eachother + - reference fields protected with LOCK + - the references held by each object are reflected in the + refcount of the other object. + - no object has ownership of the other. + - typically each object is owned by a different parent. + - creation/destruction requires two locks and two refcounts. + + - usage + + Not used in GStreamer. + + - lifecycle + + + + + diff --git a/examples/mixer/mixer.c b/examples/mixer/mixer.c index 6a3acc119f..3f31378d69 100644 --- a/examples/mixer/mixer.c +++ b/examples/mixer/mixer.c @@ -187,7 +187,7 @@ main (int argc, char *argv[]) gst_element_set_state (main_bin, GST_STATE_PLAYING); /* write out the schedule */ - gst_scheduler_show (GST_ELEMENT_SCHED (main_bin)); + gst_scheduler_show (GST_ELEMENT_SCHEDULER (main_bin)); playing = TRUE; j = 0; diff --git a/examples/thread/thread.c b/examples/thread/thread.c index 5478da84d7..964bec55d8 100644 --- a/examples/thread/thread.c +++ b/examples/thread/thread.c @@ -1,6 +1,8 @@ #include #include +static GMainLoop *loop; + /* eos will be called when the src element has an end of stream */ void eos (GstElement * element, gpointer data) @@ -12,7 +14,8 @@ eos (GstElement * element, gpointer data) /* stop the bin */ gst_element_set_state (GST_ELEMENT (thread), GST_STATE_NULL); - gst_main_quit (); + g_main_loop_quit (loop); + g_main_loop_unref (loop); } int @@ -68,7 +71,7 @@ main (int argc, char *argv[]) /* start playing */ gst_element_set_state (GST_ELEMENT (thread), GST_STATE_PLAYING); - gst_main (); + loop = g_main_loop_new (NULL, FALSE); gst_object_unref (GST_OBJECT (thread)); diff --git a/gst/Makefile.am b/gst/Makefile.am index 644b780f3d..2a5dfdb3e8 100644 --- a/gst/Makefile.am +++ b/gst/Makefile.am @@ -66,7 +66,7 @@ else GST_URI_SRC = gsturi.c endif -SUBDIRS = $(GST_PARSE_DIRS) $(GST_REGISTRY_DIRS) . autoplug elements schedulers $(GST_INDEX_DIRS) +SUBDIRS = $(GST_PARSE_DIRS) $(GST_REGISTRY_DIRS) . elements schedulers $(GST_INDEX_DIRS) DIST_SUBDIRS = autoplug elements parse registries schedulers indexers # make variables for all generated source and header files to make the @@ -98,6 +98,7 @@ libgstreamer_@GST_MAJORMINOR@_la_SOURCES = \ $(GST_INDEX_SRC) \ gstinfo.c \ gstinterface.c \ + gstiterator.c \ gstmemchunk.c \ gstpad.c \ gstpipeline.c \ @@ -171,6 +172,7 @@ gst_headers = \ gstindex.h \ gstinfo.h \ gstinterface.h \ + gstiterator.h \ gstmacros.h \ gstmemchunk.h \ gstpad.h \ diff --git a/gst/autoplug/gstsearchfuncs.c b/gst/autoplug/gstsearchfuncs.c index bfc251b74e..a0f7362ede 100644 --- a/gst/autoplug/gstsearchfuncs.c +++ b/gst/autoplug/gstsearchfuncs.c @@ -65,12 +65,12 @@ gst_autoplug_caps_intersect (const GstCaps * src, const GstCaps * sink) /* if the caps can't link, there is no intersection */ if (gst_caps_is_empty (caps)) { - gst_caps_free (caps); + gst_caps_unref (caps); return FALSE; } /* hurrah, we can link, now remove the intersection */ - gst_caps_free (caps); + gst_caps_unref (caps); return TRUE; } diff --git a/gst/autoplug/gstspider.c b/gst/autoplug/gstspider.c index 86a359e006..4bc001a655 100644 --- a/gst/autoplug/gstspider.c +++ b/gst/autoplug/gstspider.c @@ -464,11 +464,11 @@ gst_spider_identity_plug (GstSpiderIdentity * ident) GST_ELEMENT_ERROR (spider, STREAM, CODEC_NOT_FOUND, (_("There is no element present to handle the stream's mime type %s."), mime), (NULL)); - gst_caps_free (src_caps); + gst_caps_unref (src_caps); return; } } - gst_caps_free (src_caps); + gst_caps_unref (src_caps); } /* get the direction of our ident */ @@ -491,7 +491,7 @@ gst_spider_identity_plug (GstSpiderIdentity * ident) } /* now iterate all possible pads and link when needed */ - padlist = gst_element_get_pad_list (GST_ELEMENT (spider)); + padlist = GST_ELEMENT (spider)->pads; for (; padlist; padlist = padlist->next) { GstPad *otherpad; GstSpiderIdentity *peer; @@ -697,8 +697,8 @@ gst_spider_plug_from_srcpad (GstSpiderConnection * conn, GstPad * srcpad) caps1 = gst_pad_get_caps (srcpad); caps2 = gst_pad_get_caps (conn->src->sink); plugpath = gst_autoplug_sp (caps1, caps2, spider->factories); - gst_caps_free (caps1); - gst_caps_free (caps2); + gst_caps_unref (caps1); + gst_caps_unref (caps2); /* prints out the path that was found for plugging */ /* g_print ("found path from %s to %s:\n", GST_ELEMENT_NAME (conn->current), GST_ELEMENT_NAME (conn->src)); diff --git a/gst/autoplug/gstspideridentity.c b/gst/autoplug/gstspideridentity.c index f80ddc8041..832c617e01 100644 --- a/gst/autoplug/gstspideridentity.c +++ b/gst/autoplug/gstspideridentity.c @@ -289,7 +289,7 @@ gst_spider_identity_getcaps (GstPad * pad) if (ident->caps) { GstCaps *ret2 = gst_caps_intersect (ident->caps, ret); - gst_caps_free (ret); + gst_caps_unref (ret); ret = ret2; } return ret; @@ -388,12 +388,12 @@ gst_spider_identity_change_state (GstElement * element) gst_pad_get_caps ((GstPad *) GST_PAD_PEER (ident->sink)); if (gst_caps_is_any (caps) || gst_caps_is_empty (caps)) { gst_spider_identity_start_type_finding (ident); - gst_caps_free (caps); + gst_caps_unref (caps); break; } else { gst_spider_identity_plug (ident); } - gst_caps_free (caps); + gst_caps_unref (caps); } /* autoplug on src */ if ((GST_RPAD_PEER (ident->src) != NULL) @@ -528,7 +528,7 @@ gst_spider_identity_sink_loop_type_finding (GstSpiderIdentity * ident) if (!gst_caps_is_empty (find.caps) && !gst_caps_is_any (find.caps)) { goto plug; } else { - gst_caps_free (find.caps); + gst_caps_unref (find.caps); find.caps = NULL; } diff --git a/gst/elements/gstfakesrc.c b/gst/elements/gstfakesrc.c index 0b64860e90..780835e9a6 100644 --- a/gst/elements/gstfakesrc.c +++ b/gst/elements/gstfakesrc.c @@ -887,7 +887,7 @@ gst_fakesrc_loop (GstElement * element) src = GST_FAKESRC (element); - pads = gst_element_get_pad_list (element); + pads = element->pads; while (pads) { GstPad *pad = GST_PAD (pads->data); diff --git a/gst/elements/gstidentity.c b/gst/elements/gstidentity.c index 835697c209..2b4c460636 100644 --- a/gst/elements/gstidentity.c +++ b/gst/elements/gstidentity.c @@ -203,14 +203,14 @@ gst_identity_init (GstIdentity * identity) gst_element_add_pad (GST_ELEMENT (identity), identity->sinkpad); gst_pad_set_chain_function (identity->sinkpad, GST_DEBUG_FUNCPTR (gst_identity_chain)); - gst_pad_set_link_function (identity->sinkpad, gst_pad_proxy_pad_link); + //gst_pad_set_link_function (identity->sinkpad, gst_pad_proxy_pad_link); gst_pad_set_getcaps_function (identity->sinkpad, gst_pad_proxy_getcaps); identity->srcpad = gst_pad_new_from_template (gst_static_pad_template_get (&srctemplate), "src"); gst_element_add_pad (GST_ELEMENT (identity), identity->srcpad); - gst_pad_set_link_function (identity->srcpad, gst_pad_proxy_pad_link); + //gst_pad_set_link_function (identity->srcpad, gst_pad_proxy_pad_link); gst_pad_set_getcaps_function (identity->srcpad, gst_pad_proxy_getcaps); identity->loop_based = DEFAULT_LOOP_BASED; diff --git a/gst/elements/gsttee.c b/gst/elements/gsttee.c index a046ed3635..eec4586047 100644 --- a/gst/elements/gsttee.c +++ b/gst/elements/gsttee.c @@ -141,8 +141,7 @@ gst_tee_init (GstTee * tee) "sink"); gst_element_add_pad (GST_ELEMENT (tee), tee->sinkpad); gst_pad_set_chain_function (tee->sinkpad, GST_DEBUG_FUNCPTR (gst_tee_chain)); - gst_pad_set_link_function (tee->sinkpad, - GST_DEBUG_FUNCPTR (gst_pad_proxy_pad_link)); + //gst_pad_set_link_function (tee->sinkpad, GST_DEBUG_FUNCPTR (gst_pad_proxy_pad_link)); gst_pad_set_getcaps_function (tee->sinkpad, GST_DEBUG_FUNCPTR (gst_pad_proxy_getcaps)); @@ -169,16 +168,15 @@ gst_tee_getcaps (GstPad * _pad) GstPad *pad; const GList *pads; - for (pads = gst_element_get_pad_list (GST_ELEMENT (tee)); - pads != NULL; pads = pads->next) { + for (pads = GST_ELEMENT (tee)->pads; pads != NULL; pads = pads->next) { pad = GST_PAD (pads->data); if (pad == _pad) continue; tmp = gst_pad_get_allowed_caps (pad); res = gst_caps_intersect (caps, tmp); - gst_caps_free (tmp); - gst_caps_free (caps); + gst_caps_unref (tmp); + gst_caps_unref (caps); caps = res; } @@ -195,8 +193,7 @@ gst_tee_link (GstPad * _pad, const GstCaps * caps) GST_DEBUG_OBJECT (tee, "Forwarding link to all other pads"); - for (pads = gst_element_get_pad_list (GST_ELEMENT (tee)); - pads != NULL; pads = pads->next) { + for (pads = GST_ELEMENT (tee)->pads; pads != NULL; pads = pads->next) { pad = GST_PAD (pads->data); if (pad == _pad) continue; @@ -231,7 +228,7 @@ gst_tee_request_new_pad (GstElement * element, GstPadTemplate * templ, tee = GST_TEE (element); /* try names in order and find one that's not in use atm */ - pads = gst_element_get_pad_list (element); + pads = element->pads; name = NULL; while (!name) { @@ -335,7 +332,7 @@ gst_tee_chain (GstPad * pad, GstData * _data) gst_buffer_ref_by_count (buf, GST_ELEMENT (tee)->numsrcpads - 1); - pads = gst_element_get_pad_list (GST_ELEMENT (tee)); + pads = GST_ELEMENT (tee)->pads; while (pads) { GstPad *outpad = GST_PAD (pads->data); diff --git a/gst/elements/gsttypefindelement.c b/gst/elements/gsttypefindelement.c index a3b6fa206c..9c7b162444 100644 --- a/gst/elements/gsttypefindelement.c +++ b/gst/elements/gsttypefindelement.c @@ -350,7 +350,7 @@ free_entry (TypeFindEntry * entry) free_entry_buffers (entry); if (entry->caps) - gst_caps_free (entry->caps); + gst_caps_unref (entry->caps); g_free (entry); } static void diff --git a/gst/gst.c b/gst/gst.c index e4becd9986..5000685568 100644 --- a/gst/gst.c +++ b/gst/gst.c @@ -814,77 +814,6 @@ init_popt_callback (poptContext context, enum poptCallbackReason reason, } } -/** - * gst_use_threads: - * @use_threads: a #gboolean indicating whether threads should be used - * - * Does nothing anymore. GStreamer requires threads to be enabled at all times. - * - * Deprecated: This function is deprecated and should not be used in new code. - */ -void -gst_use_threads (gboolean use_threads) -{ -} - -/** - * gst_has_threads: - * - * Queries if GStreamer has threads enabled. - * - * Returns: %TRUE if threads are enabled. - * Deprecated: This function is deprecated and should not be used in new code. - */ -gboolean -gst_has_threads (void) -{ - return TRUE; -} - - -static GSList *mainloops = NULL; - -/** - * gst_main: - * - * Enters the main GStreamer processing loop. - * - * This function duplicates functionality in glib, and will be removed - * during the 0.9 development series. - */ -void -gst_main (void) -{ - GMainLoop *loop; - - loop = g_main_loop_new (NULL, FALSE); - mainloops = g_slist_prepend (mainloops, loop); - - g_main_loop_run (loop); -} - -/** - * gst_main_quit: - * - * Exits the main GStreamer processing loop. - * - * This function duplicates functionality in glib, and will be removed - * during the 0.9 development series. - */ -void -gst_main_quit (void) -{ - if (!mainloops) - g_error ("Quit more loops than there are"); - else { - GMainLoop *loop = mainloops->data; - - mainloops = g_slist_delete_link (mainloops, mainloops); - g_main_loop_quit (loop); - g_main_loop_unref (loop); - } -} - /** * gst_version: * @major: pointer to a guint to store the major version number diff --git a/gst/gst.h b/gst/gst.h index 2f25b2eb68..9bb8602013 100644 --- a/gst/gst.h +++ b/gst/gst.h @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -95,14 +96,6 @@ gboolean gst_init_check_with_popt_table (int *argc, char **argv[], const GstPoptOption * gst_init_get_popt_table (void); -#ifndef GST_DISABLE_DEPRECATED -void gst_use_threads (gboolean use_threads); -gboolean gst_has_threads (void); -#endif - -void gst_main (void); -void gst_main_quit (void); - G_END_DECLS #endif /* __GST_H__ */ diff --git a/gst/gstbin.c b/gst/gstbin.c index 438ccef5da..12d519dfb0 100644 --- a/gst/gstbin.c +++ b/gst/gstbin.c @@ -1,7 +1,7 @@ /* GStreamer * * Copyright (C) 1999,2000 Erik Walthinsen - * 2000 Wim Taymans + * 2004 Wim Taymans * * gstbin.c: GstBin container object and support code * @@ -19,6 +19,8 @@ * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. + * + * MT safe. */ #include "gst_private.h" @@ -46,7 +48,7 @@ GST_DEBUG_CATEGORY_STATIC (bin_debug); static GstElementDetails gst_bin_details = GST_ELEMENT_DETAILS ("Generic bin", "Generic/Bin", "Simple container object", - "Erik Walthinsen "); + "Erik Walthinsen ," "Wim Taymans "); GType _gst_bin_type = 0; @@ -62,8 +64,8 @@ static GstElementStateReturn gst_bin_change_state_norecurse (GstBin * bin); static void gst_bin_set_index (GstElement * element, GstIndex * index); #endif -static void gst_bin_add_func (GstBin * bin, GstElement * element); -static void gst_bin_remove_func (GstBin * bin, GstElement * element); +static gboolean gst_bin_add_func (GstBin * bin, GstElement * element); +static gboolean gst_bin_remove_func (GstBin * bin, GstElement * element); static void gst_bin_child_state_change_func (GstBin * bin, GstElementState oldstate, GstElementState newstate, GstElement * child); GstElementStateReturn gst_bin_set_state (GstElement * element, @@ -206,11 +208,9 @@ _gst_boolean_did_something_accumulator (GSignalInvocationHint * ihint, static void gst_bin_init (GstBin * bin) { - /* in general, we prefer to use cothreads for most things */ - GST_FLAG_SET (bin, GST_BIN_FLAG_PREFER_COTHREADS); - bin->numchildren = 0; bin->children = NULL; + bin->children_cookie = 0; } /** @@ -230,8 +230,8 @@ gst_bin_new (const gchar * name) static GstClock * gst_bin_get_clock_func (GstElement * element) { - if (GST_ELEMENT_SCHED (element)) - return gst_scheduler_get_clock (GST_ELEMENT_SCHED (element)); + if (GST_ELEMENT_SCHEDULER (element)) + return gst_scheduler_get_clock (GST_ELEMENT_SCHEDULER (element)); return NULL; } @@ -239,8 +239,8 @@ gst_bin_get_clock_func (GstElement * element) static void gst_bin_set_clock_func (GstElement * element, GstClock * clock) { - if (GST_ELEMENT_SCHED (element)) - gst_scheduler_use_clock (GST_ELEMENT_SCHED (element), clock); + if (GST_ELEMENT_SCHEDULER (element)) + gst_scheduler_use_clock (GST_ELEMENT_SCHEDULER (element), clock); } /** @@ -287,19 +287,26 @@ gst_bin_auto_clock (GstBin * bin) { g_return_if_fail (GST_IS_BIN (bin)); - if (GST_ELEMENT_SCHED (bin)) - gst_scheduler_auto_clock (GST_ELEMENT_SCHED (bin)); + if (GST_ELEMENT_SCHEDULER (bin)) + gst_scheduler_auto_clock (GST_ELEMENT_SCHEDULER (bin)); } #ifndef GST_DISABLE_INDEX static void gst_bin_set_index (GstElement * element, GstIndex * index) { - GstBin *bin = GST_BIN (element); + GstBin *bin; + GList *children; - g_return_if_fail (GST_IS_BIN (bin)); + bin = GST_BIN (element); - g_list_foreach (bin->children, (GFunc) gst_element_set_index, index); + GST_LOCK (bin); + for (children = bin->children; children; children = g_list_next (children)) { + GstElement *child = GST_ELEMENT (children->data); + + gst_element_set_index (child, index); + } + GST_UNLOCK (bin); } #endif @@ -314,8 +321,8 @@ gst_bin_set_element_sched (GstElement * element, GstScheduler * sched) if (GST_FLAG_IS_SET (element, GST_BIN_FLAG_MANAGER)) { GST_CAT_DEBUG_OBJECT (GST_CAT_PARENTAGE, element, "child is already a manager, not resetting sched"); - if (GST_ELEMENT_SCHED (element)) - gst_scheduler_add_scheduler (sched, GST_ELEMENT_SCHED (element)); + if (GST_ELEMENT_SCHEDULER (element)) + gst_scheduler_add_scheduler (sched, GST_ELEMENT_SCHEDULER (element)); return; } @@ -365,7 +372,7 @@ gst_bin_set_element_sched (GstElement * element, GstScheduler * sched) static void gst_bin_unset_element_sched (GstElement * element, GstScheduler * sched) { - if (GST_ELEMENT_SCHED (element) == NULL) { + if (GST_ELEMENT_SCHEDULER (element) == NULL) { GST_CAT_DEBUG (GST_CAT_SCHEDULING, "element \"%s\" has no scheduler", GST_ELEMENT_NAME (element)); return; @@ -373,7 +380,7 @@ gst_bin_unset_element_sched (GstElement * element, GstScheduler * sched) GST_CAT_DEBUG (GST_CAT_SCHEDULING, "removing element \"%s\" from its sched %p", GST_ELEMENT_NAME (element), - GST_ELEMENT_SCHED (element)); + GST_ELEMENT_SCHEDULER (element)); /* if it's actually a Bin */ if (GST_IS_BIN (element)) { @@ -382,7 +389,7 @@ gst_bin_unset_element_sched (GstElement * element, GstScheduler * sched) GST_CAT_DEBUG_OBJECT (GST_CAT_PARENTAGE, element, "child is already a manager, not unsetting sched"); if (sched) { - gst_scheduler_remove_scheduler (sched, GST_ELEMENT_SCHED (element)); + gst_scheduler_remove_scheduler (sched, GST_ELEMENT_SCHEDULER (element)); } return; } @@ -390,7 +397,7 @@ gst_bin_unset_element_sched (GstElement * element, GstScheduler * sched) g_list_foreach (GST_BIN (element)->children, (GFunc) gst_bin_unset_element_sched, sched); - gst_scheduler_remove_element (GST_ELEMENT_SCHED (element), element); + gst_scheduler_remove_element (GST_ELEMENT_SCHEDULER (element), element); } else { /* otherwise, if it's just a regular old element */ GList *pads; @@ -423,87 +430,77 @@ gst_bin_unset_element_sched (GstElement * element, GstScheduler * sched) } } - gst_scheduler_remove_element (GST_ELEMENT_SCHED (element), element); + gst_scheduler_remove_element (GST_ELEMENT_SCHEDULER (element), element); } } - -/** - * gst_bin_add_many: - * @bin: the bin to add the elements to - * @element_1: the first element to add to the bin - * @...: additional elements to add to the bin - * - * Adds a NULL-terminated list of elements to a bin. This function is - * equivalent to calling #gst_bin_add() for each member of the list. +/* add an element to this bin + * + * MT safe */ -void -gst_bin_add_many (GstBin * bin, GstElement * element_1, ...) -{ - va_list args; - - g_return_if_fail (GST_IS_BIN (bin)); - g_return_if_fail (GST_IS_ELEMENT (element_1)); - - va_start (args, element_1); - - while (element_1) { - gst_bin_add (bin, element_1); - - element_1 = va_arg (args, GstElement *); - } - - va_end (args); -} - -static void +static gboolean gst_bin_add_func (GstBin * bin, GstElement * element) { - gint state_idx = 0; - GstElementState state; - GstScheduler *sched; + gchar *elem_name; - /* the element must not already have a parent */ - g_return_if_fail (GST_ELEMENT_PARENT (element) == NULL); + /* we obviously can't add ourself to ourself */ + if (G_UNLIKELY (GST_ELEMENT_CAST (element) == GST_ELEMENT_CAST (bin))) + goto adding_itself; - /* then check to see if the element's name is already taken in the bin */ - if (gst_object_check_uniqueness (bin->children, - GST_ELEMENT_NAME (element)) == FALSE) { - g_warning ("Name %s is not unique in bin %s, not adding\n", - GST_ELEMENT_NAME (element), GST_ELEMENT_NAME (bin)); - return; - } + /* get the element name to make sure it is unique in this bin, FIXME, another + * thread can change the name after the unlock. */ + GST_LOCK (element); + elem_name = g_strdup (GST_ELEMENT_NAME (element)); + GST_UNLOCK (element); - if (GST_STATE (element) > GST_STATE (bin)) { - GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, bin, - "setting state to receive element \"%s\"", GST_OBJECT_NAME (element)); - gst_element_set_state ((GstElement *) bin, GST_STATE (element)); - } + GST_LOCK (bin); + /* then check to see if the element's name is already taken in the bin, + * we can safely take the lock here. This check is probably bogus because + * you can safely change the element name after adding it to the bin. */ + if (G_UNLIKELY (gst_object_check_uniqueness (bin->children, + elem_name) == FALSE)) + goto duplicate_name; /* set the element's parent and add the element to the bin's list of children */ - gst_object_set_parent (GST_OBJECT (element), GST_OBJECT (bin)); + if (G_UNLIKELY (!gst_object_set_parent (GST_OBJECT (element), + GST_OBJECT (bin)))) + goto had_parent; - bin->children = g_list_append (bin->children, element); + bin->children = g_list_prepend (bin->children, element); bin->numchildren++; + bin->children_cookie++; - /* bump our internal state counter */ - state = GST_STATE (element); - while ((state >>= 1) != 0) - state_idx++; - bin->child_states[state_idx]++; + gst_element_set_scheduler (element, GST_ELEMENT_SCHEDULER (bin)); - /* now we have to deal with manager stuff - * we can only do this if there's a scheduler: - * if we're not a manager, and aren't attached to anything, we have no sched (yet) */ - sched = GST_ELEMENT_SCHED (bin); - if (sched) { - gst_bin_set_element_sched (element, sched); - } + GST_UNLOCK (bin); GST_CAT_DEBUG_OBJECT (GST_CAT_PARENTAGE, bin, "added element \"%s\"", - GST_OBJECT_NAME (element)); + elem_name); + g_free (elem_name); g_signal_emit (G_OBJECT (bin), gst_bin_signals[ELEMENT_ADDED], 0, element); + + return TRUE; + + /* ERROR handling here */ +adding_itself: + GST_LOCK (bin); + g_warning ("Cannot add bin %s to itself", GST_ELEMENT_NAME (bin)); + GST_UNLOCK (bin); + return FALSE; + +duplicate_name: + g_warning ("Name %s is not unique in bin %s, not adding", + elem_name, GST_ELEMENT_NAME (bin)); + GST_UNLOCK (bin); + g_free (elem_name); + return FALSE; + +had_parent: + g_warning ("Element %s already has parent", elem_name); + GST_UNLOCK (bin); + g_free (elem_name); + return FALSE; } /** @@ -513,85 +510,86 @@ gst_bin_add_func (GstBin * bin, GstElement * element) * * Adds the given element to the bin. Sets the element's parent, and thus * takes ownership of the element. An element can only be added to one bin. + * + * Returns: TRUE if the element could be added, FALSE on wrong parameters or + * the bin does not want to accept the element. + * + * MT safe. */ -void +gboolean gst_bin_add (GstBin * bin, GstElement * element) { GstBinClass *bclass; + gboolean result; - g_return_if_fail (GST_IS_BIN (bin)); - g_return_if_fail (GST_IS_ELEMENT (element)); - - GST_CAT_INFO_OBJECT (GST_CAT_PARENTAGE, bin, "adding element \"%s\"", - GST_OBJECT_NAME (element)); + g_return_val_if_fail (GST_IS_BIN (bin), FALSE); + g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE); bclass = GST_BIN_GET_CLASS (bin); - if (bclass->add_element) { - bclass->add_element (bin, element); - } else { - GST_ELEMENT_ERROR (bin, CORE, FAILED, (NULL), - ("cannot add element %s to bin %s", - GST_ELEMENT_NAME (element), GST_ELEMENT_NAME (bin))); - } + if (G_UNLIKELY (bclass->add_element == NULL)) + goto no_function; + + GST_CAT_DEBUG (GST_CAT_PARENTAGE, "adding element %s to bin %s", + GST_ELEMENT_NAME (element), GST_ELEMENT_NAME (bin)); + + result = bclass->add_element (bin, element); + + return result; + +no_function: + g_warning ("adding elements to bin %s is not supported", + GST_ELEMENT_NAME (bin)); + return FALSE; } -static void +/* remove an element from the bin + * + * MT safe + */ +static gboolean gst_bin_remove_func (GstBin * bin, GstElement * element) { - gint state_idx = 0; - GstElementState state; + gchar *elem_name; - /* the element must have its parent set to the current bin */ - g_return_if_fail (GST_ELEMENT_PARENT (element) == (GstObject *) bin); + /* grab element name so we can print it */ + GST_LOCK (element); + elem_name = g_strdup (GST_ELEMENT_NAME (element)); + GST_UNLOCK (element); + GST_LOCK (bin); /* the element must be in the bin's list of children */ - if (g_list_find (bin->children, element) == NULL) { - g_warning ("no element \"%s\" in bin \"%s\"\n", GST_ELEMENT_NAME (element), - GST_ELEMENT_NAME (bin)); - return; - } - - /* remove this element from the list of managed elements */ - gst_bin_unset_element_sched (element, GST_ELEMENT_SCHED (bin)); - - /* if it is still iterating, make it stop */ - gst_element_release_locks (element); + if (G_UNLIKELY (g_list_find (bin->children, element) == NULL)) + goto not_in_bin; /* now remove the element from the list of elements */ bin->children = g_list_remove (bin->children, element); bin->numchildren--; - - /* bump our internal state counter */ - state = GST_STATE (element); - while ((state >>= 1) != 0) - state_idx++; - bin->child_states[state_idx]--; + bin->children_cookie++; + GST_UNLOCK (bin); GST_CAT_INFO_OBJECT (GST_CAT_PARENTAGE, bin, "removed child \"%s\"", - GST_OBJECT_NAME (element)); + elem_name); + g_free (elem_name); - /* ref as we're going to emit a signal */ + gst_element_set_scheduler (element, NULL); + + /* we ref here because after the _unparent() the element can be disposed + * and we still need it to fire a signal. */ gst_object_ref (GST_OBJECT (element)); gst_object_unparent (GST_OBJECT (element)); - /* if we're down to zero children, force state to NULL */ - while (bin->numchildren == 0 && GST_ELEMENT_SCHED (bin) != NULL && - GST_STATE (bin) > GST_STATE_NULL) { - GstElementState next = GST_STATE (bin) >> 1; - - GST_STATE_PENDING (bin) = next; - gst_bin_change_state_norecurse (bin); - if (!GST_STATE (bin) == next) { - g_warning ("bin %s failed state change to %d", GST_ELEMENT_NAME (bin), - next); - break; - } - } g_signal_emit (G_OBJECT (bin), gst_bin_signals[ELEMENT_REMOVED], 0, element); - /* element is really out of our control now */ gst_object_unref (GST_OBJECT (element)); + + return TRUE; + +not_in_bin: + g_warning ("Element %s is not in bin %s", elem_name, GST_ELEMENT_NAME (bin)); + GST_UNLOCK (bin); + g_free (elem_name); + return FALSE; } /** @@ -605,55 +603,132 @@ gst_bin_remove_func (GstBin * bin, GstElement * element) * will be freed in the process of removing it from the bin. If you * want the element to still exist after removing, you need to call * #gst_object_ref before removing it from the bin. + * + * Returns: TRUE if the element could be removed, FALSE on wrong parameters or + * the bin does not want to remove the element. + * + * MT safe. */ -void +gboolean gst_bin_remove (GstBin * bin, GstElement * element) { GstBinClass *bclass; + gboolean result; - GST_CAT_DEBUG (GST_CAT_PARENTAGE, "[%s]: trying to remove child %s", - GST_ELEMENT_NAME (bin), GST_ELEMENT_NAME (element)); - - g_return_if_fail (GST_IS_BIN (bin)); - g_return_if_fail (GST_IS_ELEMENT (element)); + g_return_val_if_fail (GST_IS_BIN (bin), FALSE); + g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE); bclass = GST_BIN_GET_CLASS (bin); - if (bclass->remove_element) { - bclass->remove_element (bin, element); - } else { - g_warning ("cannot remove elements from bin %s\n", GST_ELEMENT_NAME (bin)); - } + if (G_UNLIKELY (bclass->remove_element == NULL)) + goto no_function; + + GST_CAT_DEBUG (GST_CAT_PARENTAGE, "removing element %s from bin %s", + GST_ELEMENT_NAME (element), GST_ELEMENT_NAME (bin)); + + result = bclass->remove_element (bin, element); + + return result; + +no_function: + g_warning ("removing elements from bin %s is not supported", + GST_ELEMENT_NAME (bin)); + return FALSE; +} + +static GstIteratorItem +iterate_child (GstIterator * it, GstElement * child) +{ + gst_object_ref (GST_OBJECT (child)); + return GST_ITERATOR_ITEM_PASS; } /** - * gst_bin_remove_many: - * @bin: the bin to remove the elements from - * @element_1: the first element to remove from the bin - * @...: NULL-terminated list of elements to remove from the bin - * - * Remove a list of elements from a bin. This function is equivalent - * to calling #gst_bin_remove with each member of the list. + * gst_bin_iterate_elements: + * @bin: #Gstbin to iterate the elements of + * + * Get an iterator for the elements in this bin. + * Each element will have its refcount increased, so unref + * after usage. + * + * Returns: a #GstIterator of #GstElements. gst_iterator_free after + * use. returns NULL when passing bad parameters. + * + * MT safe. */ -void -gst_bin_remove_many (GstBin * bin, GstElement * element_1, ...) +GstIterator * +gst_bin_iterate_elements (GstBin * bin) { - va_list args; + GstIterator *result; - g_return_if_fail (GST_IS_BIN (bin)); - g_return_if_fail (GST_IS_ELEMENT (element_1)); + g_return_val_if_fail (GST_IS_BIN (bin), NULL); - va_start (args, element_1); + GST_LOCK (bin); + /* add ref because the iterator refs the bin. When the iterator + * is freed it will unref the bin again using the provided dispose + * function. */ + gst_object_ref (GST_OBJECT (bin)); + result = gst_iterator_new_list (GST_GET_LOCK (bin), + &bin->children_cookie, + &bin->children, + bin, + (GstIteratorItemFunction) iterate_child, + (GstIteratorDisposeFunction) gst_object_unref); + GST_UNLOCK (bin); - while (element_1) { - gst_bin_remove (bin, element_1); - - element_1 = va_arg (args, GstElement *); - } - - va_end (args); + return result; } +static GstIteratorItem +iterate_child_recurse (GstIterator * it, GstElement * child) +{ + gst_object_ref (GST_OBJECT (child)); + if (GST_IS_BIN (child)) { + GstIterator *other = gst_bin_iterate_recurse (GST_BIN (child)); + + gst_iterator_push (it, other); + } + return GST_ITERATOR_ITEM_PASS; +} + +/** + * gst_bin_iterate_recurse: + * @bin: #Gstbin to iterate the elements of + * + * Get an iterator for the elements in this bin. + * Each element will have its refcount increased, so unref + * after usage. This iterator recurses into GstBin children. + * + * Returns: a #GstIterator of #GstElements. gst_iterator_free after + * use. returns NULL when passing bad parameters. + * + * MT safe. + */ +GstIterator * +gst_bin_iterate_recurse (GstBin * bin) +{ + GstIterator *result; + + g_return_val_if_fail (GST_IS_BIN (bin), NULL); + + GST_LOCK (bin); + /* add ref because the iterator refs the bin. When the iterator + * is freed it will unref the bin again using the provided dispose + * function. */ + gst_object_ref (GST_OBJECT (bin)); + result = gst_iterator_new_list (GST_GET_LOCK (bin), + &bin->children_cookie, + &bin->children, + bin, + (GstIteratorItemFunction) iterate_child_recurse, + (GstIteratorDisposeFunction) gst_object_unref); + GST_UNLOCK (bin); + + return result; + return NULL; +} + + /** * gst_bin_child_state_change: * @bin: #GstBin with the child @@ -942,54 +1017,61 @@ gst_bin_dispose (GObject * object) GST_CAT_DEBUG_OBJECT (GST_CAT_REFCOUNTING, object, "dispose"); - gst_element_set_state (GST_ELEMENT (object), GST_STATE_NULL); + /* ref to not hit 0 again */ + gst_object_ref (GST_OBJECT (object)); while (bin->children) { gst_bin_remove (bin, GST_ELEMENT (bin->children->data)); } + GST_CAT_DEBUG_OBJECT (GST_CAT_REFCOUNTING, object, "dispose no children"); g_assert (bin->children == NULL); g_assert (bin->numchildren == 0); G_OBJECT_CLASS (parent_class)->dispose (object); } +static gint +compare_name (GstElement * element, const gchar * name) +{ + gint eq; + + GST_LOCK (element); + eq = strcmp (GST_ELEMENT_NAME (element), name) == 0; + GST_UNLOCK (element); + + if (eq != 0) { + gst_object_unref (GST_OBJECT (element)); + } + return eq; +} + /** * gst_bin_get_by_name: * @bin: #Gstbin to search * @name: the element name to search for * - * Get the element with the given name from this bin. + * Get the element with the given name from this bin. This + * function recurses into subbins. * - * Returns: the element with the given name + * Returns: the element with the given name. Returns NULL if the + * element is not found or when bad parameters were given. Unref after + * usage. + * + * MT safe. */ GstElement * gst_bin_get_by_name (GstBin * bin, const gchar * name) { - GList *children; - GstElement *child; + GstIterator *children; + GstIterator *result; - g_return_val_if_fail (bin != NULL, NULL); g_return_val_if_fail (GST_IS_BIN (bin), NULL); - g_return_val_if_fail (name != NULL, NULL); - GST_CAT_INFO (GST_CAT_PARENTAGE, "[%s]: looking up child element %s", - GST_ELEMENT_NAME (bin), name); + children = gst_bin_iterate_recurse (bin); + result = gst_iterator_find_custom (children, + (GCompareFunc) compare_name, (gpointer) name); - children = bin->children; - while (children) { - child = GST_ELEMENT (children->data); - if (!strcmp (GST_OBJECT_NAME (child), name)) - return child; - if (GST_IS_BIN (child)) { - GstElement *res = gst_bin_get_by_name (GST_BIN (child), name); - - if (res) - return res; - } - children = g_list_next (children); - } - - return NULL; + return GST_ELEMENT_CAST (result); } /** @@ -1000,45 +1082,49 @@ gst_bin_get_by_name (GstBin * bin, const gchar * name) * Get the element with the given name from this bin. If the * element is not found, a recursion is performed on the parent bin. * - * Returns: the element with the given name + * Returns: the element with the given name or NULL when the element + * was not found or bad parameters were given. Unref after usage. + * + * MT safe. */ GstElement * gst_bin_get_by_name_recurse_up (GstBin * bin, const gchar * name) { - GstElement *result = NULL; - GstObject *parent; + GstElement *result; - g_return_val_if_fail (bin != NULL, NULL); g_return_val_if_fail (GST_IS_BIN (bin), NULL); g_return_val_if_fail (name != NULL, NULL); result = gst_bin_get_by_name (bin, name); if (!result) { - parent = gst_object_get_parent (GST_OBJECT (bin)); + GstObject *parent; + + parent = gst_object_get_parent (GST_OBJECT_CAST (bin)); if (parent && GST_IS_BIN (parent)) { - result = gst_bin_get_by_name_recurse_up (GST_BIN (parent), name); + result = gst_bin_get_by_name_recurse_up (GST_BIN_CAST (parent), name); } + gst_object_unref (parent); } return result; } -/** - * gst_bin_get_list: - * @bin: #Gstbin to get the list from - * - * Get the list of elements in this bin. - * - * Returns: a GList of elements - */ -const GList * -gst_bin_get_list (GstBin * bin) +static gint +compare_interface (GstElement * element, gpointer interface) { - g_return_val_if_fail (GST_IS_BIN (bin), NULL); + gint ret; - return bin->children; + if (G_TYPE_CHECK_INSTANCE_TYPE (element, GPOINTER_TO_INT (interface))) { + ret = 0; + } else { + /* we did not find the element, need to release the ref + * added by the iterator */ + gst_object_unref (GST_OBJECT (element)); + ret = 1; + } + return ret; } /** @@ -1050,33 +1136,26 @@ gst_bin_get_list (GstBin * bin) * interface. If such an element is found, it returns the element. You can * cast this element to the given interface afterwards. * If you want all elements that implement the interface, use - * gst_bin_get_all_by_interface(). The function recurses bins inside bins. + * gst_bin_iterate_all_by_interface(). The function recurses bins inside bins. * - * Returns: An element inside the bin implementing the interface. + * Returns: An element inside the bin implementing the interface. Unref after + * usage. + * + * MT safe. */ GstElement * gst_bin_get_by_interface (GstBin * bin, GType interface) { - GList *walk; + GstIterator *children; + GstIterator *result; g_return_val_if_fail (GST_IS_BIN (bin), NULL); - g_return_val_if_fail (G_TYPE_IS_INTERFACE (interface), NULL); - walk = bin->children; - while (walk) { - if (G_TYPE_CHECK_INSTANCE_TYPE (walk->data, interface)) - return GST_ELEMENT (walk->data); - if (GST_IS_BIN (walk->data)) { - GstElement *ret; + children = gst_bin_iterate_recurse (bin); + result = gst_iterator_find_custom (children, (GCompareFunc) compare_interface, + GINT_TO_POINTER (interface)); - ret = gst_bin_get_by_interface (GST_BIN (walk->data), interface); - if (ret) - return ret; - } - walk = g_list_next (walk); - } - - return NULL; + return GST_ELEMENT_CAST (result); } /** @@ -1086,34 +1165,25 @@ gst_bin_get_by_interface (GstBin * bin, GType interface) * * Looks for all elements inside the bin that implements the given * interface. You can safely cast all returned elements to the given interface. - * The function recurses bins inside bins. You need to free the list using - * g_list_free() after use. + * The function recurses bins inside bins. The iterator will return a series + * of #GstElement that should be unreffed after usage. + * + * Returns: An iterator for the elements inside the bin implementing the interface. * - * Returns: An element inside the bin implementing the interface. */ -GList * -gst_bin_get_all_by_interface (GstBin * bin, GType interface) +GstIterator * +gst_bin_iterate_all_by_interface (GstBin * bin, GType interface) { - GList *walk, *ret = NULL; + GstIterator *children; + GstIterator *result; g_return_val_if_fail (GST_IS_BIN (bin), NULL); - g_return_val_if_fail (G_TYPE_IS_INTERFACE (interface), NULL); - walk = bin->children; - while (walk) { - if (G_TYPE_CHECK_INSTANCE_TYPE (walk->data, interface)) { - GST_DEBUG_OBJECT (bin, "element %s implements requested interface", - GST_ELEMENT_NAME (GST_ELEMENT (walk->data))); - ret = g_list_prepend (ret, walk->data); - } - if (GST_IS_BIN (walk->data)) { - ret = g_list_concat (ret, - gst_bin_get_all_by_interface (GST_BIN (walk->data), interface)); - } - walk = g_list_next (walk); - } + children = gst_bin_iterate_recurse (bin); + result = gst_iterator_filter (children, (GCompareFunc) compare_interface, + GINT_TO_POINTER (interface)); - return ret; + return result; } /** @@ -1235,7 +1305,7 @@ static GStaticRecMutex iterate_lock = G_STATIC_REC_MUTEX_INIT; static gboolean gst_bin_iterate_func (GstBin * bin) { - GstScheduler *sched = GST_ELEMENT_SCHED (bin); + GstScheduler *sched = GST_ELEMENT_SCHEDULER (bin); g_static_rec_mutex_unlock (&iterate_lock); diff --git a/gst/gstbin.h b/gst/gstbin.h index a7333e66e0..f8a4d01386 100644 --- a/gst/gstbin.h +++ b/gst/gstbin.h @@ -25,6 +25,7 @@ #define __GST_BIN_H__ #include +#include G_BEGIN_DECLS @@ -36,6 +37,7 @@ GST_EXPORT GType _gst_bin_type; #define GST_BIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_BIN, GstBinClass)) #define GST_BIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_BIN, GstBin)) #define GST_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_BIN, GstBinClass)) +#define GST_BIN_CAST(obj) ((GstBin*)(obj)) /** * GstBinFlags: @@ -58,45 +60,58 @@ GST_EXPORT GType _gst_bin_type; * and (un)set using GST_FLAG_SET () and GST_FLAG_UNSET (). */ typedef enum { - GST_BIN_FLAG_MANAGER = GST_ELEMENT_FLAG_LAST, + GST_BIN_FLAG_FIXED_CLOCK = GST_ELEMENT_FLAG_LAST, + GST_BIN_FLAG_MANAGER, GST_BIN_SELF_SCHEDULABLE, - GST_BIN_FLAG_PREFER_COTHREADS, - GST_BIN_FLAG_FIXED_CLOCK, GST_BIN_STATE_LOCKED, + /* padding */ GST_BIN_FLAG_LAST = GST_ELEMENT_FLAG_LAST + 5 } GstBinFlags; /*typedef struct _GstBin GstBin; */ /*typedef struct _GstBinClass GstBinClass; */ +#define GST_BIN_NUMCHILDREN(bin) (GST_BIN_CAST(bin)->numchildren); +#define GST_BIN_CHILDREN(bin) (GST_BIN_CAST(bin)->children); +#define GST_BIN_CHILDREN_COOKIE(bin) (GST_BIN_CAST(bin)->children_cookie); + struct _GstBin { GstElement element; - /* our children */ + /*< public >*/ /* with LOCK */ + /* our children, subclass are supposed to update these + * fields to reflect their state with _iterate_*() */ gint numchildren; GList *children; + guint32 children_cookie; GstElementState child_states[GST_NUM_STATES]; + /*< private >*/ gpointer _gst_reserved[GST_PADDING]; }; struct _GstBinClass { GstElementClass parent_class; - /* vtable */ - void (*add_element) (GstBin *bin, GstElement *element); - void (*remove_element) (GstBin *bin, GstElement *element); - void (*child_state_change) (GstBin *bin, GstElementState oldstate, - GstElementState newstate, GstElement *element); + /*< public >*/ /* run a full iteration of operation */ gboolean (*iterate) (GstBin *bin); + void (*child_state_change) (GstBin *bin, GstElementState oldstate, + GstElementState newstate, GstElement *element); + /* signals */ void (*element_added) (GstBin *bin, GstElement *child); void (*element_removed) (GstBin *bin, GstElement *child); + /*< protected >*/ + /* vtable */ + gboolean (*add_element) (GstBin *bin, GstElement *element); + gboolean (*remove_element) (GstBin *bin, GstElement *element); + + /*< private >*/ gpointer _gst_reserved[GST_PADDING]; }; @@ -104,25 +119,24 @@ GType gst_bin_get_type (void); GstElement* gst_bin_new (const gchar *name); /* add and remove elements from the bin */ -void gst_bin_add (GstBin *bin, GstElement *element); -void gst_bin_add_many (GstBin *bin, GstElement *element_1, ...); -void gst_bin_remove (GstBin *bin, GstElement *element); -void gst_bin_remove_many (GstBin *bin, GstElement *element_1, ...); +gboolean gst_bin_add (GstBin *bin, GstElement *element); +gboolean gst_bin_remove (GstBin *bin, GstElement *element); -/* retrieve a single element or the list of children */ -GstElement* gst_bin_get_by_name (GstBin *bin, const gchar *name); -GstElement* gst_bin_get_by_name_recurse_up (GstBin *bin, const gchar *name); -G_CONST_RETURN GList* - gst_bin_get_list (GstBin *bin); -GstElement* gst_bin_get_by_interface (GstBin *bin, GType interface); -GList * gst_bin_get_all_by_interface (GstBin *bin, GType interface); +/* retrieve a single child */ +GstElement* gst_bin_get_by_name (GstBin *bin, const gchar *name); +GstElement* gst_bin_get_by_name_recurse_up (GstBin *bin, const gchar *name); +GstElement* gst_bin_get_by_interface (GstBin *bin, GType interface); + +/* retrieve multiple children */ +GstIterator* gst_bin_iterate_elements (GstBin *bin); +GstIterator* gst_bin_iterate_recurse (GstBin *bin); +GstIterator* gst_bin_iterate_recurse_up (GstBin *bin); + +GstIterator* gst_bin_iterate_sinks (GstBin *bin); +GstIterator* gst_bin_iterate_all_by_interface (GstBin *bin, GType interface); gboolean gst_bin_iterate (GstBin *bin); -void gst_bin_use_clock (GstBin *bin, GstClock *clock); -GstClock* gst_bin_get_clock (GstBin *bin); -void gst_bin_auto_clock (GstBin *bin); - GstElementStateReturn gst_bin_sync_children_state (GstBin *bin); /* internal */ diff --git a/gst/gstbuffer.c b/gst/gstbuffer.c index 79015398f4..af4c87ed1f 100644 --- a/gst/gstbuffer.c +++ b/gst/gstbuffer.c @@ -28,7 +28,7 @@ #include "gstmemchunk.h" #include "gstinfo.h" -GType _gst_buffer_type; +GType _gst_buffer_type = 0; #ifndef GST_DISABLE_TRACE /* #define GST_WITH_ALLOC_TRACE */ @@ -60,7 +60,7 @@ _gst_buffer_initialize (void) GType gst_buffer_get_type (void) { - if (_gst_buffer_type == 0) { + if (G_UNLIKELY (_gst_buffer_type == 0)) { _gst_buffer_type = g_boxed_type_register_static ("GstBuffer", (GBoxedCopyFunc) gst_data_copy, (GBoxedFreeFunc) gst_data_unref); } @@ -74,6 +74,8 @@ _gst_buffer_sub_free (GstBuffer * buffer) GST_BUFFER_DATA (buffer) = NULL; GST_BUFFER_SIZE (buffer) = 0; + if (GST_BUFFER_CAPS (buffer)) + gst_caps_unref (GST_BUFFER_CAPS (buffer)); _GST_DATA_DISPOSE (GST_DATA (buffer)); @@ -86,6 +88,8 @@ _gst_buffer_sub_free (GstBuffer * buffer) * * Frees the memory associated with the buffer including the buffer data, * unless the GST_BUFFER_DONTFREE flags was set or the buffer data is NULL. + * + * MT safe. */ void gst_buffer_default_free (GstBuffer * buffer) @@ -102,39 +106,26 @@ gst_buffer_default_free (GstBuffer * buffer) /* set to safe values */ GST_BUFFER_DATA (buffer) = NULL; GST_BUFFER_SIZE (buffer) = 0; + if (GST_BUFFER_CAPS (buffer)) + gst_caps_unref (GST_BUFFER_CAPS (buffer)); _GST_DATA_DISPOSE (GST_DATA (buffer)); gst_buffer_free_chunk (buffer); } -/** - * gst_buffer_stamp: - * @dest: buffer to stamp - * @src: buffer to stamp from - * - * Copies additional information (timestamps and offsets) from one buffer to - * the other. - */ -void -gst_buffer_stamp (GstBuffer * dest, const GstBuffer * src) -{ - g_return_if_fail (dest != NULL); - g_return_if_fail (src != NULL); - - GST_BUFFER_TIMESTAMP (dest) = GST_BUFFER_TIMESTAMP (src); - GST_BUFFER_DURATION (dest) = GST_BUFFER_DURATION (src); - GST_BUFFER_OFFSET (dest) = GST_BUFFER_OFFSET (src); - GST_BUFFER_OFFSET_END (dest) = GST_BUFFER_OFFSET_END (src); -} /** * gst_buffer_default_copy: * @buffer: a #GstBuffer to make a copy of. * * Make a full newly allocated copy of the given buffer, data and all. + * Note that the caps on the buffer are not copied but their refcount + * is increased. * * Returns: the new #GstBuffer. + * + * MT safe. */ GstBuffer * gst_buffer_default_copy (GstBuffer * buffer) @@ -147,8 +138,10 @@ gst_buffer_default_copy (GstBuffer * buffer) /* create a fresh new buffer */ copy = gst_buffer_alloc_chunk (); + GST_CAT_LOG (GST_CAT_BUFFER, "copy %p to %p", buffer, copy); + /* copy relevant flags */ - flags = GST_DATA_FLAG_SHIFT (GST_BUFFER_KEY_UNIT) | + flags = GST_DATA_FLAG_SHIFT (GST_BUFFER_PREROLL) | GST_DATA_FLAG_SHIFT (GST_BUFFER_IN_CAPS) | GST_DATA_FLAG_SHIFT (GST_BUFFER_DELTA_UNIT); flags = GST_BUFFER_FLAGS (buffer) & flags; @@ -165,9 +158,15 @@ gst_buffer_default_copy (GstBuffer * buffer) GST_BUFFER_SIZE (copy) = GST_BUFFER_SIZE (buffer); GST_BUFFER_MAXSIZE (copy) = GST_BUFFER_SIZE (buffer); - gst_buffer_stamp (copy, buffer); + GST_BUFFER_TIMESTAMP (copy) = GST_BUFFER_TIMESTAMP (buffer); + GST_BUFFER_DURATION (copy) = GST_BUFFER_DURATION (buffer); + GST_BUFFER_OFFSET (copy) = GST_BUFFER_OFFSET (buffer); + GST_BUFFER_OFFSET_END (copy) = GST_BUFFER_OFFSET_END (buffer); + GST_BUFFER_FREE_DATA_FUNC (copy) = NULL; GST_BUFFER_PRIVATE (copy) = NULL; + if (GST_BUFFER_CAPS (buffer)) + GST_BUFFER_CAPS (copy) = gst_caps_ref (GST_BUFFER_CAPS (buffer)); return copy; } @@ -200,6 +199,8 @@ gst_buffer_free_chunk (GstBuffer * buffer) * Creates a newly allocated buffer without any data. * * Returns: the new #GstBuffer. + * + * MT safe. */ GstBuffer * gst_buffer_new (void) @@ -225,6 +226,7 @@ gst_buffer_new (void) GST_BUFFER_OFFSET_END (newbuf) = GST_BUFFER_OFFSET_NONE; GST_BUFFER_FREE_DATA_FUNC (newbuf) = NULL; GST_BUFFER_PRIVATE (newbuf) = NULL; + GST_BUFFER_CAPS (newbuf) = NULL; return newbuf; } @@ -236,6 +238,8 @@ gst_buffer_new (void) * Creates a newly allocated buffer with data of the given size. * * Returns: the new #GstBuffer. + * + * MT safe. */ GstBuffer * gst_buffer_new_and_alloc (guint size) @@ -251,6 +255,62 @@ gst_buffer_new_and_alloc (guint size) return newbuf; } + +/** + * gst_buffer_get_caps: + * @buffer: a #GstBuffer to get the caps of. + * + * Gets the media type of the buffer. This can be NULL if there + * is not media type attached to this buffer or when the media + * type is the same as the previous received buffer. + * + * This function does not increment the refcount of the caps. The + * caps pointer will therefore remain valid until the buffer is + * unreffed. + * + * Returns: the #GstCaps, or NULL if there was an error or there + * were no caps on this buffer. + */ +/* FIXME can we make this threadsafe without a lock on the buffer? */ +GstCaps * +gst_buffer_get_caps (GstBuffer * buffer) +{ + g_return_val_if_fail (buffer != NULL, NULL); + + return GST_BUFFER_CAPS (buffer); +} + +/** + * gst_buffer_set_caps: + * @buffer: a #GstBuffer to set the caps of. + * @caps: a #GstCaps to set. + * + * Sets the media type on the buffer. The caps' refcount will + * be increased and any previous caps on the buffer will be + * unreffed. + */ +/* FIXME can we make this threadsafe without a lock on the buffer? */ +void +gst_buffer_set_caps (GstBuffer * buffer, GstCaps * caps) +{ + GstCaps *oldcaps; + + g_return_if_fail (buffer != NULL); + + /* get old caps */ + oldcaps = GST_BUFFER_CAPS (buffer); + /* ref new caps if any */ + if (caps) + caps = gst_caps_ref (caps); + /* set caps */ + GST_BUFFER_CAPS (buffer) = caps; + + /* unref old caps if any */ + if (oldcaps) { + gst_caps_unref (oldcaps); + } +} + /** * gst_buffer_create_sub: * @parent: a parent #GstBuffer to create a subbuffer from. @@ -264,6 +324,8 @@ gst_buffer_new_and_alloc (guint size) * The duration field of the new buffer are set to GST_CLOCK_TIME_NONE. * * Returns: the new #GstBuffer, or NULL if there was an error. + * + * MT safe. */ GstBuffer * gst_buffer_create_sub (GstBuffer * parent, guint offset, guint size) @@ -318,72 +380,14 @@ gst_buffer_create_sub (GstBuffer * parent, guint offset, guint size) GST_BUFFER_DURATION (buffer) = GST_CLOCK_TIME_NONE; GST_BUFFER_OFFSET_END (buffer) = GST_BUFFER_OFFSET_NONE; - if (GST_BUFFER_FLAG_IS_SET (parent, GST_BUFFER_DONTKEEP)) { - GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_DONTKEEP); - } if (GST_BUFFER_FLAG_IS_SET (parent, GST_BUFFER_READONLY)) { GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_READONLY); } + GST_BUFFER_CAPS (buffer) = NULL; return buffer; } - -/** - * gst_buffer_merge: - * @buf1: a first source #GstBuffer to merge. - * @buf2: the second source #GstBuffer to merge. - * - * Create a new buffer that is the concatenation of the two source - * buffers. The original source buffers will not be modified or - * unref'd. - * - * WARNING: Incorrect use of this function can lead to memory leaks. - * It is recommended to use gst_buffer_join() instead of this function. - * - * If the buffers point to contiguous areas of memory, the buffer - * is created without copying the data. - * - * Returns: the new #GstBuffer that's the concatenation of the source buffers. - */ -GstBuffer * -gst_buffer_merge (GstBuffer * buf1, GstBuffer * buf2) -{ - GstBuffer *result; - - /* we're just a specific case of the more general gst_buffer_span() */ - result = gst_buffer_span (buf1, 0, buf2, buf1->size + buf2->size); - - return result; -} - -/** - * gst_buffer_join: - * @buf1: a first source #GstBuffer to merge. - * @buf2: the second source #GstBuffer to merge. - * - * Create a new buffer that is the concatenation of the two source - * buffers. The original buffers are unreferenced. - * - * If the buffers point to contiguous areas of memory, the buffer - * is created without copying the data. - * - * Returns: the new #GstBuffer that's the concatenation of the source buffers. - */ -GstBuffer * -gst_buffer_join (GstBuffer * buf1, GstBuffer * buf2) -{ - GstBuffer *result; - - /* we're just a specific case of the more general gst_buffer_span() */ - result = gst_buffer_span (buf1, 0, buf2, buf1->size + buf2->size); - - gst_buffer_unref (buf1); - gst_buffer_unref (buf2); - - return result; -} - /** * gst_buffer_is_span_fast: * @buf1: a first source #GstBuffer. @@ -394,6 +398,8 @@ gst_buffer_join (GstBuffer * buf1, GstBuffer * buf2) * * Returns: TRUE if the buffers are contiguous, * FALSE if a copy would be required. + * + * MT safe. */ gboolean gst_buffer_is_span_fast (GstBuffer * buf1, GstBuffer * buf2) @@ -428,6 +434,8 @@ gst_buffer_is_span_fast (GstBuffer * buf1, GstBuffer * buf2) * gst_buffer_is_span_fast() to determine if a memcpy will be needed. * * Returns: the new #GstBuffer that spans the two source buffers. + * + * MT safe. */ GstBuffer * gst_buffer_span (GstBuffer * buf1, guint32 offset, GstBuffer * buf2, diff --git a/gst/gstbuffer.h b/gst/gstbuffer.h index 911cabb8e1..ed2f932d45 100644 --- a/gst/gstbuffer.h +++ b/gst/gstbuffer.h @@ -26,6 +26,7 @@ #include #include +#include G_BEGIN_DECLS @@ -44,10 +45,8 @@ extern GType _gst_buffer_type; #define GST_BUFFER_REFCOUNT(buf) GST_DATA_REFCOUNT(buf) #define GST_BUFFER_REFCOUNT_VALUE(buf) GST_DATA_REFCOUNT_VALUE(buf) -#ifndef GST_DISABLE_DEPRECATED #define GST_BUFFER_COPY_FUNC(buf) GST_DATA_COPY_FUNC(buf) #define GST_BUFFER_FREE_FUNC(buf) GST_DATA_FREE_FUNC(buf) -#endif #define GST_BUFFER_FLAGS(buf) GST_DATA_FLAGS(buf) #define GST_BUFFER_FLAG_IS_SET(buf,flag) GST_DATA_FLAG_IS_SET (buf, flag) @@ -59,6 +58,7 @@ extern GType _gst_buffer_type; #define GST_BUFFER_MAXSIZE(buf) (GST_BUFFER(buf)->maxsize) #define GST_BUFFER_TIMESTAMP(buf) (GST_BUFFER(buf)->timestamp) #define GST_BUFFER_DURATION(buf) (GST_BUFFER(buf)->duration) +#define GST_BUFFER_CAPS(buf) (GST_BUFFER(buf)->caps) #define GST_BUFFER_OFFSET(buf) (GST_BUFFER(buf)->offset) #define GST_BUFFER_OFFSET_END(buf) (GST_BUFFER(buf)->offset_end) #define GST_BUFFER_FREE_DATA_FUNC(buf) (GST_BUFFER(buf)->free_data) @@ -87,6 +87,7 @@ extern GType _gst_buffer_type; * @GST_BUFFER_DONTKEEP: the buffer should not be ref()ed, but copied instead * before doing anything with it (for specially allocated hw buffers and such) * @GST_BUFFER_IN_CAPS: the buffer has been added as a field in a #GstCaps. + * @GST_BUFFER_GAP: the buffer has been created to fill a gap in the stream. * @GST_BUFFER_DELTA_UNIT: this unit cannot be decoded independently. * Since 0.8.5 * @GST_BUFFER_FLAG_LAST: additional flags can be added starting from this flag. @@ -96,18 +97,20 @@ extern GType _gst_buffer_type; typedef enum { GST_BUFFER_READONLY = GST_DATA_READONLY, GST_BUFFER_SUBBUFFER = GST_DATA_FLAG_LAST, - GST_BUFFER_ORIGINAL, - GST_BUFFER_DONTFREE, - GST_BUFFER_KEY_UNIT, /* deprecated, use reverse DELTA_UNIT */ - GST_BUFFER_DONTKEEP, /* FIXME: is this deprecated ? there is no reference in gstreamer, gst-plugins */ - GST_BUFFER_IN_CAPS, - GST_BUFFER_DELTA_UNIT, /* this unit depends on a previous unit */ - GST_BUFFER_FLAG_LAST = GST_DATA_FLAG_LAST + 8 + GST_BUFFER_ORIGINAL, /* original data, not copied, not currently used */ + GST_BUFFER_DONTFREE, /* buffer data is managed by somebody else and cannot be freeed */ + GST_BUFFER_PREROLL, /* sample should not be displayed */ + GST_BUFFER_DISCONT, /* buffer is first after discontinuity in the stream */ + GST_BUFFER_IN_CAPS, /* buffer is also part of caps */ + GST_BUFFER_GAP, /* buffer has been created to fill a gap in the stream */ + GST_BUFFER_DELTA_UNIT, /* can't be used as sync point in stream */ + GST_BUFFER_FLAG_LAST = GST_DATA_FLAG_LAST + 8 } GstBufferFlag; struct _GstBuffer { GstData data_type; + /*< public >*/ /* with COW */ /* pointer to data and its size */ guint8 *data; /* pointer to buffer data */ guint size; /* size of buffer data */ @@ -117,6 +120,9 @@ struct _GstBuffer { GstClockTime timestamp; GstClockTime duration; + /* the media type of this buffer */ + GstCaps *caps; + /* media specific offset * for video frames, this could be the number of frames, * for audio data, this could be the number of audio samples, @@ -127,9 +133,11 @@ struct _GstBuffer { guint64 offset; guint64 offset_end; + /*< protected >*/ GstBufferFreeDataFunc free_data; gpointer buffer_private; + /*< private >*/ gpointer _gst_reserved[GST_PADDING]; }; @@ -149,21 +157,21 @@ G_STMT_START { \ #define gst_buffer_ref_by_count(buf,c) GST_BUFFER (gst_data_ref_by_count (GST_DATA (buf), c)) #define gst_buffer_unref(buf) gst_data_unref (GST_DATA (buf)) /* copy buffer */ -void gst_buffer_stamp (GstBuffer *dest, const GstBuffer *src); #define gst_buffer_copy(buf) GST_BUFFER (gst_data_copy (GST_DATA (buf))) #define gst_buffer_is_writable(buf) gst_data_is_writable (GST_DATA (buf)) #define gst_buffer_copy_on_write(buf) GST_BUFFER (gst_data_copy_on_write (GST_DATA (buf))) +GstCaps* gst_buffer_get_caps (GstBuffer *buffer); +void gst_buffer_set_caps (GstBuffer *buffer, GstCaps *caps); + /* creating a subbuffer */ GstBuffer* gst_buffer_create_sub (GstBuffer *parent, guint offset, guint size); -/* merge, span, or append two buffers, intelligently */ -GstBuffer* gst_buffer_merge (GstBuffer *buf1, GstBuffer *buf2); -GstBuffer* gst_buffer_join (GstBuffer *buf1, GstBuffer *buf2); +/* span, two buffers, intelligently */ gboolean gst_buffer_is_span_fast (GstBuffer *buf1, GstBuffer *buf2); GstBuffer* gst_buffer_span (GstBuffer *buf1, guint32 offset, GstBuffer *buf2, guint32 len); -/* --- private --- */ +/* --- protected --- */ void _gst_buffer_initialize (void); void gst_buffer_default_free (GstBuffer *buffer); diff --git a/gst/gstcaps.c b/gst/gstcaps.c index 2bf07996cb..300003f53c 100644 --- a/gst/gstcaps.c +++ b/gst/gstcaps.c @@ -24,12 +24,15 @@ #include #include "gst_private.h" +#include "gstatomic_impl.h" #include +//#define DEBUG_REFCOUNT + #define CAPS_POISON(caps) G_STMT_START{ \ if (caps) { \ GstCaps *_newcaps = gst_caps_copy (caps); \ - gst_caps_free(caps); \ + gst_caps_unref(caps); \ caps = _newcaps; \ } \ } G_STMT_END @@ -40,13 +43,15 @@ structure = _newstruct; \ } \ } G_STMT_END +#define IS_WRITABLE(caps) \ + (gst_atomic_int_read(&(caps)->refcount) == 1) static void gst_caps_transform_to_string (const GValue * src_value, GValue * dest_value); static gboolean gst_caps_from_string_inplace (GstCaps * caps, const gchar * string); -static GstCaps *gst_caps_copy_conditional (const GstCaps * src); +static GstCaps *gst_caps_copy_conditional (GstCaps * src); GType gst_caps_get_type (void) @@ -56,7 +61,7 @@ gst_caps_get_type (void) if (!gst_caps_type) { gst_caps_type = g_boxed_type_register_static ("GstCaps", (GBoxedCopyFunc) gst_caps_copy_conditional, - (GBoxedFreeFunc) gst_caps_free); + (GBoxedFreeFunc) gst_caps_unref); g_value_register_transform_func (gst_caps_type, G_TYPE_STRING, gst_caps_transform_to_string); @@ -80,9 +85,14 @@ gst_caps_new_empty (void) { GstCaps *caps = g_new0 (GstCaps, 1); + gst_atomic_int_init (&(caps)->refcount, 1); caps->type = GST_TYPE_CAPS; caps->structs = g_ptr_array_new (); +#ifdef DEBUG_REFCOUNT + GST_CAT_LOG (GST_CAT_CAPS, "created caps %p", caps); +#endif + return caps; } @@ -188,8 +198,14 @@ gst_caps_new_full_valist (GstStructure * structure, va_list var_args) * gst_caps_copy: * @caps: the #GstCaps to copy * - * Deeply copies a #GstCaps, including all structures and all the - * structures' values. + * Creates a new #GstCaps as a copy of the old @caps. The new caps will have a + * refcount of 1, owned by the caller. The structures are copied as well. + * + * Note that this function is the semantic equivalent of a gst_caps_ref() + * followed by a gst_caps_make_writable(). If you only want to hold on to a + * reference to the data, you should use gst_caps_ref(). + * + * When you are finished with the caps, call gst_caps_unref() on it. * * Returns: the new #GstCaps */ @@ -213,32 +229,118 @@ gst_caps_copy (const GstCaps * caps) return newcaps; } -/** - * gst_caps_free: - * @caps: the #GstCaps to free - * - * Frees a #GstCaps and all its structures and the structures' - * values. - */ -void -gst_caps_free (GstCaps * caps) +static void +_gst_caps_free (GstCaps * caps) { GstStructure *structure; int i; - g_return_if_fail (GST_IS_CAPS (caps)); + /* The refcount must be 0, but since we're only called by gst_caps_unref, + * don't bother testing. */ for (i = 0; i < caps->structs->len; i++) { - structure = gst_caps_get_structure (caps, i); + structure = (GstStructure *) gst_caps_get_structure (caps, i); + gst_structure_set_parent_refcount (structure, NULL); gst_structure_free (structure); } g_ptr_array_free (caps->structs, TRUE); #ifdef USE_POISONING memset (caps, 0xff, sizeof (GstCaps)); #endif + gst_atomic_int_destroy (&(caps)->refcount); g_free (caps); } +/** + * gst_caps_make_writable: + * @caps: the #GstCaps to make writable + * + * Returns a writable copy of @caps. + * + * If there is only one reference count on @caps, the caller must be the owner, + * and so this function will return the caps object unchanged. If on the other + * hand there is more than one reference on the object, a new caps object will + * be returned. The caller's reference on @caps will be removed, and instead the + * caller will own a reference to the returned object. + * + * In short, this function unrefs the caps in the argument and refs the caps + * that it returns. Don't access the argument after calling this function. See + * also: gst_caps_ref(). + * + * Returns: the same #GstCaps object. + */ +GstCaps * +gst_caps_make_writable (GstCaps * caps) +{ + GstCaps *copy; + + g_return_val_if_fail (caps != NULL, NULL); + + /* we are the only instance reffing this caps */ + if (gst_atomic_int_read (&caps->refcount) == 1) + return caps; + + /* else copy */ + copy = gst_caps_copy (caps); + gst_caps_unref (caps); + + return copy; +} + +/** + * gst_caps_ref: + * @caps: the #GstCaps to reference + * + * Add a reference to a #GstCaps object. + * + * From this point on, until the caller calls gst_caps_unref() or + * gst_caps_make_writable(), it is guaranteed that the caps object will not + * change. This means its structures won't change, etc. To use a #GstCaps + * object, you must always have a refcount on it -- either the one made + * implicitly by gst_caps_new(), or via taking one explicitly with this + * function. + * + * Returns: the same #GstCaps object. + */ +GstCaps * +gst_caps_ref (GstCaps * caps) +{ + g_return_val_if_fail (caps != NULL, NULL); + +#ifdef DEBUG_REFCOUNT + GST_CAT_LOG (GST_CAT_CAPS, "%p %d->%d", caps, + GST_CAPS_REFCOUNT_VALUE (caps), GST_CAPS_REFCOUNT_VALUE (caps) + 1); +#endif + + gst_atomic_int_inc (&caps->refcount); + + return caps; +} + +/** + * gst_caps_unref: + * @caps: the #GstCaps to unref + * + * Unref a #GstCaps and and free all its structures and the + * structures' values when the refcount reaches 0. + */ +void +gst_caps_unref (GstCaps * caps) +{ + g_return_if_fail (caps != NULL); + g_return_if_fail (GST_CAPS_REFCOUNT_VALUE (caps) > 0); + +#ifdef DEBUG_REFCOUNT + GST_CAT_LOG (GST_CAT_CAPS, "%p %d->%d", caps, + GST_CAPS_REFCOUNT_VALUE (caps), GST_CAPS_REFCOUNT_VALUE (caps) - 1); +#endif + + /* if we ended up with the refcount at zero, free the caps */ + if (gst_atomic_int_dec_and_test (&caps->refcount)) { + _gst_caps_free (caps); + } +} + /** * gst_static_caps_get: * @static_caps: the #GstStaticCaps to convert @@ -255,12 +357,18 @@ gst_static_caps_get (GstStaticCaps * static_caps) if (caps->type == 0) { caps->type = GST_TYPE_CAPS; + /* initialize the caps to a refcount of 1 so the caps can be writable... */ + gst_atomic_int_init (&(caps)->refcount, 1); caps->structs = g_ptr_array_new (); ret = gst_caps_from_string_inplace (caps, static_caps->string); if (!ret) { g_critical ("Could not convert static caps \"%s\"", static_caps->string); } + /* and now that we return it to the user, keep a ref for ourselves. This + * makes the caps immutable... AND INVINCIBLE! WOULD YOU LIKE TO TRY MY + * IMMUTABLE CAPS STYLE? I AM A CAPS WARRIOR!!! */ + gst_caps_ref (caps); } return caps; @@ -268,14 +376,24 @@ gst_static_caps_get (GstStaticCaps * static_caps) /* manipulation */ +static GstStructure * +gst_caps_remove_and_get_structure (GstCaps * caps, guint idx) +{ + /* don't use index_fast, gst_caps_simplify relies on the order */ + GstStructure *s = g_ptr_array_remove_index (caps->structs, idx); + + gst_structure_set_parent_refcount (s, NULL); + return s; +} + /** * gst_caps_append: * @caps1: the #GstCaps that will be appended to * @caps2: the #GstCaps to append * - * Appends the structures contained in @caps2 to @caps1. The structures - * in @caps2 are not copied -- they are transferred to @caps1, and then - * @caps2 is freed. + * Appends the structures contained in @caps2 to @caps1. The structures in + * @caps2 are not copied -- they are transferred to @caps1, and then @caps2 is + * freed. If either caps is ANY, the resulting caps will be ANY. */ void gst_caps_append (GstCaps * caps1, GstCaps * caps2) @@ -285,6 +403,8 @@ gst_caps_append (GstCaps * caps1, GstCaps * caps2) g_return_if_fail (GST_IS_CAPS (caps1)); g_return_if_fail (GST_IS_CAPS (caps2)); + g_return_if_fail (IS_WRITABLE (caps1)); + g_return_if_fail (IS_WRITABLE (caps2)); #ifdef USE_POISONING CAPS_POISON (caps2); @@ -292,21 +412,19 @@ gst_caps_append (GstCaps * caps1, GstCaps * caps2) if (gst_caps_is_any (caps1) || gst_caps_is_any (caps2)) { /* FIXME: this leaks */ caps1->flags |= GST_CAPS_FLAGS_ANY; - for (i = 0; i < caps2->structs->len; i++) { - structure = gst_caps_get_structure (caps2, i); - gst_structure_remove_all_fields (structure); + for (i = caps2->structs->len - 1; i >= 0; i--) { + structure = gst_caps_remove_and_get_structure (caps2, i); + gst_structure_free (structure); } } else { - for (i = 0; i < caps2->structs->len; i++) { - structure = gst_caps_get_structure (caps2, i); + int len = caps2->structs->len; + + for (i = 0; i < len; i++) { + structure = gst_caps_remove_and_get_structure (caps2, 0); gst_caps_append_structure (caps1, structure); } } - g_ptr_array_free (caps2->structs, TRUE); -#ifdef USE_POISONING - memset (caps2, 0xff, sizeof (GstCaps)); -#endif - g_free (caps2); + gst_caps_unref (caps2); /* guaranteed to free it */ } /** @@ -321,24 +439,20 @@ void gst_caps_append_structure (GstCaps * caps, GstStructure * structure) { g_return_if_fail (GST_IS_CAPS (caps)); + g_return_if_fail (IS_WRITABLE (caps)); if (structure) { + g_return_if_fail (structure->parent_refcount == NULL); #if 0 #ifdef USE_POISONING STRUCTURE_POISON (structure); #endif #endif + gst_structure_set_parent_refcount (structure, &caps->refcount); g_ptr_array_add (caps->structs, structure); } } -static GstStructure * -gst_caps_remove_and_get_structure (GstCaps * caps, guint idx) -{ - /* don't use index_fast, gst_caps_simplify relies on the order */ - return g_ptr_array_remove_index (caps->structs, idx); -} - /* * gst_caps_remove_structure: * @caps: the #GstCaps to remove from @@ -354,6 +468,7 @@ gst_caps_remove_structure (GstCaps * caps, guint idx) g_return_if_fail (caps != NULL); g_return_if_fail (idx <= gst_caps_get_size (caps)); + g_return_if_fail (IS_WRITABLE (caps)); structure = gst_caps_remove_and_get_structure (caps, idx); gst_structure_free (structure); @@ -417,17 +532,18 @@ gst_caps_get_structure (const GstCaps * caps, int index) return g_ptr_array_index (caps->structs, index); } -/** - * gst_caps_copy_1: - * @caps: the @GstCaps to copy - * - * Creates a new @GstCaps and appends a copy of the first structure - * contained in @caps. - * - * Returns: the new @GstCaps +/** + * gst_caps_copy_nth: + * @caps: the @GstCaps to copy + * @nth: the nth structure to copy + * + * Creates a new @GstCaps and appends a copy of the nth structure + * contained in @caps. + * + * Returns: the new @GstCaps */ GstCaps * -gst_caps_copy_1 (const GstCaps * caps) +gst_caps_copy_nth (const GstCaps * caps, gint nth) { GstCaps *newcaps; GstStructure *structure; @@ -437,8 +553,8 @@ gst_caps_copy_1 (const GstCaps * caps) newcaps = gst_caps_new_empty (); newcaps->flags = caps->flags; - if (caps->structs->len > 0) { - structure = gst_caps_get_structure (caps, 0); + if (caps->structs->len > nth) { + structure = gst_caps_get_structure (caps, nth); gst_caps_append_structure (newcaps, gst_structure_copy (structure)); } @@ -463,6 +579,7 @@ gst_caps_set_simple (GstCaps * caps, char *field, ...) g_return_if_fail (GST_IS_CAPS (caps)); g_return_if_fail (caps->structs->len == 1); + g_return_if_fail (IS_WRITABLE (caps)); structure = gst_caps_get_structure (caps, 0); @@ -488,6 +605,7 @@ gst_caps_set_simple_valist (GstCaps * caps, char *field, va_list varargs) g_return_if_fail (GST_IS_CAPS (caps)); g_return_if_fail (caps->structs->len != 1); + g_return_if_fail (IS_WRITABLE (caps)); structure = gst_caps_get_structure (caps, 0); @@ -531,27 +649,9 @@ gst_caps_is_empty (const GstCaps * caps) return (caps->structs == NULL) || (caps->structs->len == 0); } -/** - * gst_caps_is_chained: - * @caps: the @GstCaps to test - * - * Determines if @caps contains multiple #GstStructures. - * - * This function is deprecated, and should not be used in new code. - * Use #gst_caps_is_simple() instead. - * - * Returns: TRUE if @caps contains more than one structure - */ -gboolean -gst_caps_is_chained (const GstCaps * caps) -{ - g_return_val_if_fail (GST_IS_CAPS (caps), FALSE); - - return (caps->structs->len > 1); -} - static gboolean -gst_caps_is_fixed_foreach (GQuark field_id, GValue * value, gpointer unused) +gst_caps_is_fixed_foreach (GQuark field_id, const GValue * value, + gpointer unused) { return gst_value_is_fixed (value); } @@ -582,7 +682,8 @@ gst_caps_is_fixed (const GstCaps * caps) } static gboolean -gst_structure_is_equal_foreach (GQuark field_id, GValue * val2, gpointer data) +gst_structure_is_equal_foreach (GQuark field_id, const GValue * val2, + gpointer data) { GstStructure *struct1 = (GstStructure *) data; const GValue *val1 = gst_structure_id_get_value (struct1, field_id); @@ -675,7 +776,7 @@ gst_caps_is_subset (const GstCaps * subset, const GstCaps * superset) caps = gst_caps_subtract (subset, superset); ret = gst_caps_is_empty (caps); - gst_caps_free (caps); + gst_caps_unref (caps); return ret; } @@ -711,7 +812,8 @@ typedef struct IntersectData; static gboolean -gst_caps_structure_intersect_field (GQuark id, GValue * val1, gpointer data) +gst_caps_structure_intersect_field (GQuark id, const GValue * val1, + gpointer data) { IntersectData *idata = (IntersectData *) data; GValue dest_value = { 0 }; @@ -818,10 +920,11 @@ gst_caps_structure_union (const GstStructure * struct1, GstCaps * gst_caps_intersect (const GstCaps * caps1, const GstCaps * caps2) { - int i, j; + int i, j, k; GstStructure *struct1; GstStructure *struct2; GstCaps *dest; + GstStructure *istruct; g_return_val_if_fail (GST_IS_CAPS (caps1), NULL); g_return_val_if_fail (GST_IS_CAPS (caps2), NULL); @@ -835,18 +938,44 @@ gst_caps_intersect (const GstCaps * caps1, const GstCaps * caps2) return gst_caps_copy (caps1); dest = gst_caps_new_empty (); - for (i = 0; i < caps1->structs->len; i++) { - struct1 = gst_caps_get_structure (caps1, i); - for (j = 0; j < caps2->structs->len; j++) { - GstStructure *istruct; - struct2 = gst_caps_get_structure (caps2, j); + /* run zigzag on top line then right line, this preserves the caps order + * much better than a simple loop. + * + * This algorithm zigzags over the caps structures as demonstrated in + * the folowing matrix: + * + * caps1 + * +------------- + * | 1 2 4 7 + * caps2 | 3 5 8 10 + * | 6 9 11 12 + * + * First we iterate over the caps1 structures (top line) intersecting + * the structures diagonally down, then we iterate over the caps2 + * structures. + */ + for (i = 0; i < caps1->structs->len + caps2->structs->len - 1; i++) { + /* caps1 index goes from 0 to caps1->structs->len-1 */ + j = MIN (i, caps1->structs->len - 1); + /* caps2 index stays 0 until i reaches caps1->structs->len, then it counts + * up from 1 to caps2->structs->len - 1 */ + k = MAX (0, i - j); + + /* now run the diagonal line, end condition is the left or bottom + * border */ + while (k < caps2->structs->len && j >= 0) { + struct1 = gst_caps_get_structure (caps1, j); + struct2 = gst_caps_get_structure (caps2, k); + istruct = gst_caps_structure_intersect (struct1, struct2); gst_caps_append_structure (dest, istruct); + /* move down left */ + k++; + j--; } } - gst_caps_do_simplify (dest); return dest; } @@ -859,8 +988,8 @@ typedef struct SubtractionEntry; -gboolean -gst_caps_structure_subtract_field (GQuark field_id, GValue * value, +static gboolean +gst_caps_structure_subtract_field (GQuark field_id, const GValue * value, gpointer user_data) { SubtractionEntry *e = user_data; @@ -951,7 +1080,7 @@ gst_caps_subtract (const GstCaps * minuend, const GstCaps * subtrahend) for (i = 0; i < subtrahend->structs->len; i++) { sub = gst_caps_get_structure (subtrahend, i); if (dest) { - gst_caps_free (src); + gst_caps_unref (src); src = dest; } dest = gst_caps_new_empty (); @@ -975,12 +1104,12 @@ gst_caps_subtract (const GstCaps * minuend, const GstCaps * subtrahend) } } if (gst_caps_is_empty (dest)) { - gst_caps_free (src); + gst_caps_unref (src); return dest; } } - gst_caps_free (src); + gst_caps_unref (src); gst_caps_do_simplify (dest); return dest; } @@ -1020,7 +1149,7 @@ typedef struct _NormalizeForeach NormalizeForeach; static gboolean -gst_caps_normalize_foreach (GQuark field_id, GValue * value, gpointer ptr) +gst_caps_normalize_foreach (GQuark field_id, const GValue * value, gpointer ptr) { NormalizeForeach *nf = (NormalizeForeach *) ptr; GValue val = { 0 }; @@ -1090,7 +1219,7 @@ gst_caps_compare_structures (gconstpointer one, gconstpointer two) if (ret) return ret; - return gst_structure_n_fields (struct1) - gst_structure_n_fields (struct2); + return gst_structure_n_fields (struct2) - gst_structure_n_fields (struct1); } /** @@ -1126,7 +1255,7 @@ typedef struct UnionField; static gboolean -gst_caps_structure_figure_out_union (GQuark field_id, GValue * value, +gst_caps_structure_figure_out_union (GQuark field_id, const GValue * value, gpointer user_data) { UnionField *u = user_data; @@ -1214,6 +1343,16 @@ gst_caps_structure_simplify (GstStructure ** result, return FALSE; } +static void +gst_caps_switch_structures (GstCaps * caps, GstStructure * old, + GstStructure * new, gint i) +{ + gst_structure_set_parent_refcount (old, NULL); + gst_structure_free (old); + gst_structure_set_parent_refcount (new, &caps->refcount); + g_ptr_array_index (caps->structs, i) = new; +} + /** * gst_caps_do_simplify: * @caps: a #GstCaps to simplify @@ -1233,6 +1372,7 @@ gst_caps_do_simplify (GstCaps * caps) gboolean changed = FALSE; g_return_val_if_fail (caps != NULL, FALSE); + g_return_val_if_fail (IS_WRITABLE (caps), FALSE); if (gst_caps_get_size (caps) < 2) return FALSE; @@ -1261,9 +1401,7 @@ gst_caps_do_simplify (GstCaps * caps) result ? gst_structure_to_string (result) : "---"); #endif if (result) { - gst_structure_free (simplify); - g_ptr_array_index (caps->structs, i) = result; - simplify = result; + gst_caps_switch_structures (caps, simplify, result, i); } else { gst_caps_remove_structure (caps, i); start--; @@ -1340,7 +1478,7 @@ gst_caps_replace (GstCaps ** caps, GstCaps * newcaps) #endif #endif if (*caps) - gst_caps_free (*caps); + gst_caps_unref (*caps); *caps = newcaps; } @@ -1454,7 +1592,7 @@ gst_caps_from_string (const gchar * string) if (gst_caps_from_string_inplace (caps, string)) { return caps; } else { - gst_caps_free (caps); + gst_caps_unref (caps); return NULL; } } @@ -1473,140 +1611,11 @@ gst_caps_transform_to_string (const GValue * src_value, GValue * dest_value) } static GstCaps * -gst_caps_copy_conditional (const GstCaps * src) +gst_caps_copy_conditional (GstCaps * src) { if (src) { - return gst_caps_copy (src); + return gst_caps_ref (src); } else { return NULL; } } - -/* fixate utility functions */ - -/** - * gst_caps_structure_fixate_field_nearest_int: - * @structure: a #GstStructure - * @field_name: a field in @structure - * @target: the target value of the fixation - * - * Fixates a #GstStructure by changing the given field to the nearest - * integer to @target that is a subset of the existing field. - * - * Returns: TRUE if the structure could be fixated - */ -gboolean -gst_caps_structure_fixate_field_nearest_int (GstStructure * structure, - const char *field_name, int target) -{ - const GValue *value; - - g_return_val_if_fail (gst_structure_has_field (structure, field_name), FALSE); - - value = gst_structure_get_value (structure, field_name); - - if (G_VALUE_TYPE (value) == G_TYPE_INT) { - /* already fixed */ - return FALSE; - } else if (G_VALUE_TYPE (value) == GST_TYPE_INT_RANGE) { - int x; - - x = gst_value_get_int_range_min (value); - if (target < x) - target = x; - x = gst_value_get_int_range_max (value); - if (target > x) - target = x; - gst_structure_set (structure, field_name, G_TYPE_INT, target, NULL); - return TRUE; - } else if (G_VALUE_TYPE (value) == GST_TYPE_LIST) { - const GValue *list_value; - int i, n; - int best = 0; - int best_index = -1; - - n = gst_value_list_get_size (value); - for (i = 0; i < n; i++) { - list_value = gst_value_list_get_value (value, i); - if (G_VALUE_TYPE (list_value) == G_TYPE_INT) { - int x = g_value_get_int (list_value); - - if (best_index == -1 || (ABS (target - x) < ABS (target - best))) { - best_index = i; - best = x; - } - } - } - if (best_index != -1) { - gst_structure_set (structure, field_name, G_TYPE_INT, best, NULL); - return TRUE; - } - return FALSE; - } - - return FALSE; -} - -/** - * gst_caps_structure_fixate_field_nearest_double: - * @structure: a #GstStructure - * @field_name: a field in @structure - * @target: the target value of the fixation - * - * Fixates a #GstStructure by changing the given field to the nearest - * double to @target that is a subset of the existing field. - * - * Returns: TRUE if the structure could be fixated - */ -gboolean -gst_caps_structure_fixate_field_nearest_double (GstStructure * structure, - const char *field_name, double target) -{ - const GValue *value; - - g_return_val_if_fail (gst_structure_has_field (structure, field_name), FALSE); - - value = gst_structure_get_value (structure, field_name); - - if (G_VALUE_TYPE (value) == G_TYPE_DOUBLE) { - /* already fixed */ - return FALSE; - } else if (G_VALUE_TYPE (value) == GST_TYPE_DOUBLE_RANGE) { - double x; - - x = gst_value_get_double_range_min (value); - if (target < x) - target = x; - x = gst_value_get_double_range_max (value); - if (target > x) - target = x; - gst_structure_set (structure, field_name, G_TYPE_DOUBLE, target, NULL); - return TRUE; - } else if (G_VALUE_TYPE (value) == GST_TYPE_LIST) { - const GValue *list_value; - int i, n; - double best = 0; - int best_index = -1; - - n = gst_value_list_get_size (value); - for (i = 0; i < n; i++) { - list_value = gst_value_list_get_value (value, i); - if (G_VALUE_TYPE (list_value) == G_TYPE_DOUBLE) { - double x = g_value_get_double (list_value); - - if (best_index == -1 || (ABS (target - x) < ABS (target - best))) { - best_index = i; - best = x; - } - } - } - if (best_index != -1) { - gst_structure_set (structure, field_name, G_TYPE_DOUBLE, best, NULL); - return TRUE; - } - return FALSE; - } - - return FALSE; - -} diff --git a/gst/gstcaps.h b/gst/gstcaps.h index 359175cce9..84eb9204d8 100644 --- a/gst/gstcaps.h +++ b/gst/gstcaps.h @@ -54,18 +54,30 @@ G_BEGIN_DECLS typedef struct _GstCaps GstCaps; typedef struct _GstStaticCaps GstStaticCaps; +/* refcount */ +#define GST_CAPS_REFCOUNT(caps) ((GST_CAPS(caps))->refcount) +#define GST_CAPS_REFCOUNT_VALUE(caps) (gst_atomic_int_read (&(GST_CAPS(caps))->refcount)) + struct _GstCaps { GType type; + /*< public >*/ /* with COW */ + /* refcounting */ + GstAtomicInt refcount; + guint16 flags; GPtrArray *structs; + /*< private >*/ gpointer _gst_reserved[GST_PADDING]; }; struct _GstStaticCaps { + /*< public >*/ GstCaps caps; const char *string; + + /*< private >*/ gpointer _gst_reserved[GST_PADDING]; }; @@ -79,8 +91,13 @@ GstCaps * gst_caps_new_full (GstStru ...); GstCaps * gst_caps_new_full_valist (GstStructure *structure, va_list var_args); -GstCaps * gst_caps_copy (const GstCaps *caps); -void gst_caps_free (GstCaps *caps); + +/* reference counting */ +GstCaps * gst_caps_ref (GstCaps* caps); +GstCaps * gst_caps_copy (const GstCaps * caps); +GstCaps * gst_caps_make_writable (GstCaps *caps); +void gst_caps_unref (GstCaps* caps); + G_CONST_RETURN GstCaps * gst_static_caps_get (GstStaticCaps *static_caps); /* manipulation */ @@ -91,10 +108,7 @@ void gst_caps_append_structure (GstCaps int gst_caps_get_size (const GstCaps *caps); GstStructure * gst_caps_get_structure (const GstCaps *caps, int index); -#ifndef GST_DISABLE_DEPRECATED -GstCaps * gst_caps_split_one (GstCaps *caps); -GstCaps * gst_caps_copy_1 (const GstCaps *caps); -#endif +GstCaps * gst_caps_copy_nth (const GstCaps * caps, gint nth); void gst_caps_set_simple (GstCaps *caps, char *field, ...); void gst_caps_set_simple_valist (GstCaps *caps, @@ -104,11 +118,6 @@ void gst_caps_set_simple_valist (GstCaps /* tests */ gboolean gst_caps_is_any (const GstCaps *caps); gboolean gst_caps_is_empty (const GstCaps *caps); -#ifndef GST_DISABLE_DEPRECATED -gboolean gst_caps_is_chained (const GstCaps *caps); -gboolean gst_caps_is_equal_fixed (const GstCaps *caps1, - const GstCaps *caps2); -#endif gboolean gst_caps_is_fixed (const GstCaps *caps); gboolean gst_caps_is_always_compatible (const GstCaps *caps1, const GstCaps *caps2); @@ -125,9 +134,6 @@ GstCaps * gst_caps_subtract (const GstCaps *minuend, GstCaps * gst_caps_union (const GstCaps *caps1, const GstCaps *caps2); GstCaps * gst_caps_normalize (const GstCaps *caps); -#ifndef GST_DISABLE_DEPRECATED -GstCaps * gst_caps_simplify (const GstCaps *caps); -#endif gboolean gst_caps_do_simplify (GstCaps *caps); #ifndef GST_DISABLE_LOADSAVE diff --git a/gst/gstclock.c b/gst/gstclock.c index 1552d6617d..494fd8e4ee 100644 --- a/gst/gstclock.c +++ b/gst/gstclock.c @@ -1,6 +1,7 @@ /* GStreamer * Copyright (C) 1999,2000 Erik Walthinsen * 2000 Wim Taymans + * 2004 Wim Taymans * * gstclock.c: Clock subsystem for maintaining time sync * @@ -27,6 +28,7 @@ #include "gstclock.h" #include "gstinfo.h" #include "gstmemchunk.h" +#include "gstatomic_impl.h" #ifndef GST_DISABLE_TRACE /* #define GST_WITH_ALLOC_TRACE */ @@ -47,9 +49,6 @@ enum static GstMemChunk *_gst_clock_entries_chunk; -void gst_clock_id_unlock (GstClockID id); - - static void gst_clock_class_init (GstClockClass * klass); static void gst_clock_init (GstClock * clock); static void gst_clock_dispose (GObject * object); @@ -75,25 +74,86 @@ gst_clock_entry_new (GstClock * clock, GstClockTime time, #ifndef GST_DISABLE_TRACE gst_alloc_trace_new (_gst_clock_entry_trace, entry); #endif + GST_CAT_DEBUG (GST_CAT_CLOCK, "created entry %p", entry); + gst_atomic_int_init (&entry->refcount, 1); entry->clock = clock; entry->time = time; entry->interval = interval; entry->type = type; - entry->status = GST_CLOCK_ENTRY_OK; + entry->status = GST_CLOCK_BUSY; return (GstClockID) entry; } +/** + * gst_clock_id_ref: + * @id: The clockid to ref + * + * Increase the refcount of the given clockid. + * + * Returns: The same #GstClockID with increased refcount. + * + * MT safe. + */ +GstClockID +gst_clock_id_ref (GstClockID id) +{ + g_return_val_if_fail (id != NULL, NULL); + + gst_atomic_int_inc (&((GstClockEntry *) id)->refcount); + + return id; +} + +static void +_gst_clock_id_free (GstClockID id) +{ + g_return_if_fail (id != NULL); + + GST_CAT_DEBUG (GST_CAT_CLOCK, "freed entry %p", id); + +#ifndef GST_DISABLE_TRACE + gst_alloc_trace_free (_gst_clock_entry_trace, id); +#endif + gst_mem_chunk_free (_gst_clock_entries_chunk, id); +} + +/** + * gst_clock_id_unref: + * @id: The clockid to unref + * + * Unref the given clockid. When the refcount reaches 0 the + * #GstClockID will be freed. + * + * MT safe. + */ +void +gst_clock_id_unref (GstClockID id) +{ + gint zero; + + g_return_if_fail (id != NULL); + + zero = gst_atomic_int_dec_and_test (&((GstClockEntry *) id)->refcount); + /* if we ended up with the refcount at zero, free the id */ + if (zero) { + _gst_clock_id_free (id); + } +} + /** * gst_clock_new_single_shot_id * @clock: The clockid to get a single shot notification from * @time: the requested time * * Get an ID from the given clock to trigger a single shot - * notification at the requested time. + * notification at the requested time. The single shot id should be + * unreffed after usage. * * Returns: An id that can be used to request the time notification. + * + * MT safe. */ GstClockID gst_clock_new_single_shot_id (GstClock * clock, GstClockTime time) @@ -112,9 +172,12 @@ gst_clock_new_single_shot_id (GstClock * clock, GstClockTime time) * * Get an ID from the given clock to trigger a periodic notification. * The periodeic notifications will be start at time start_time and - * will then be fired with the given interval. + * will then be fired with the given interval. The id should be unreffed + * after usage. * * Returns: An id that can be used to request the time notification. + * + * MT safe. */ GstClockID gst_clock_new_periodic_id (GstClock * clock, GstClockTime start_time, @@ -128,13 +191,45 @@ gst_clock_new_periodic_id (GstClock * clock, GstClockTime start_time, start_time, interval, GST_CLOCK_ENTRY_PERIODIC); } +/** + * gst_clock_id_compare_func + * @id1: A clockid + * @id2: A clockid to compare with + * + * Compares the two GstClockID instances. This function can be used + * as a GCompareFunc when sorting ids. + * + * Returns: negative value if a < b; zero if a = b; positive value if a > b + * + * MT safe. + */ +gint +gst_clock_id_compare_func (gconstpointer id1, gconstpointer id2) +{ + GstClockEntry *entry1, *entry2; + + entry1 = (GstClockEntry *) id1; + entry2 = (GstClockEntry *) id2; + + if (GST_CLOCK_ENTRY_TIME (entry1) > GST_CLOCK_ENTRY_TIME (entry2)) { + return 1; + } + if (GST_CLOCK_ENTRY_TIME (entry1) < GST_CLOCK_ENTRY_TIME (entry2)) { + return -1; + } + + return entry1 - entry2; +} + /** * gst_clock_id_get_time * @id: The clockid to query * * Get the time of the clock ID * - * Returns: the time of the given clock id + * Returns: the time of the given clock id. + * + * MT safe. */ GstClockTime gst_clock_id_get_time (GstClockID id) @@ -151,16 +246,18 @@ gst_clock_id_get_time (GstClockID id) * @jitter: A pointer that will contain the jitter * * Perform a blocking wait on the given ID. The jitter arg can be - * NULL + * NULL. * * Returns: the result of the blocking wait. + * + * MT safe. */ GstClockReturn gst_clock_id_wait (GstClockID id, GstClockTimeDiff * jitter) { GstClockEntry *entry; GstClock *clock; - GstClockReturn res = GST_CLOCK_UNSUPPORTED; + GstClockReturn res; GstClockTime requested; GstClockClass *cclass; @@ -169,43 +266,49 @@ gst_clock_id_wait (GstClockID id, GstClockTimeDiff * jitter) entry = (GstClockEntry *) id; requested = GST_CLOCK_ENTRY_TIME (entry); - if (!GST_CLOCK_TIME_IS_VALID (requested)) { - GST_CAT_DEBUG (GST_CAT_CLOCK, "invalid time requested, returning _TIMEOUT"); - return GST_CLOCK_TIMEOUT; - } + if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (requested))) + goto invalid_time; + + if (G_UNLIKELY (entry->status == GST_CLOCK_UNSCHEDULED)) + goto unscheduled; clock = GST_CLOCK_ENTRY_CLOCK (entry); cclass = GST_CLOCK_GET_CLASS (clock); - if (cclass->wait) { - GstClockTime now; + if (G_LIKELY (cclass->wait)) { - GST_LOCK (clock); - clock->entries = g_list_prepend (clock->entries, entry); - GST_UNLOCK (clock); - - GST_CAT_DEBUG (GST_CAT_CLOCK, "waiting on clock"); - do { - res = cclass->wait (clock, entry); - } - while (res == GST_CLOCK_ENTRY_RESTART); - GST_CAT_DEBUG (GST_CAT_CLOCK, "done waiting"); - - GST_LOCK (clock); - clock->entries = g_list_remove (clock->entries, entry); - GST_UNLOCK (clock); + GST_CAT_DEBUG (GST_CAT_CLOCK, "waiting on clock entry %p", id); + res = cclass->wait (clock, entry); + GST_CAT_DEBUG (GST_CAT_CLOCK, "done waiting entry %p", id); if (jitter) { - now = gst_clock_get_time (clock); + GstClockTime now = gst_clock_get_time (clock); + *jitter = now - requested; } + if (entry->type == GST_CLOCK_ENTRY_PERIODIC) { + entry->time += entry->interval; + } if (clock->stats) { gst_clock_update_stats (clock); } + } else { + res = GST_CLOCK_UNSUPPORTED; } - return res; + + /* ERRORS */ +invalid_time: + { + GST_CAT_DEBUG (GST_CAT_CLOCK, "invalid time requested, returning _BADTIME"); + return GST_CLOCK_BADTIME; + } +unscheduled: + { + GST_CAT_DEBUG (GST_CAT_CLOCK, "entry was unscheduled return _UNSCHEDULED"); + return GST_CLOCK_UNSCHEDULED; + } } /** @@ -215,9 +318,14 @@ gst_clock_id_wait (GstClockID id, GstClockTimeDiff * jitter) * @user_data: User data passed in the calback * * Register a callback on the given clockid with the given - * function and user_data. + * function and user_data. When passing an id with an invalid + * time to this function, the callback will be called immediatly + * with a time set to GST_CLOCK_TIME_NONE. The callback will + * be called when the time of the id has been reached. * * Returns: the result of the non blocking wait. + * + * MT safe. */ GstClockReturn gst_clock_id_wait_async (GstClockID id, @@ -225,19 +333,22 @@ gst_clock_id_wait_async (GstClockID id, { GstClockEntry *entry; GstClock *clock; - GstClockReturn res = GST_CLOCK_UNSUPPORTED; + GstClockReturn res; GstClockClass *cclass; + GstClockTime requested; g_return_val_if_fail (id != NULL, GST_CLOCK_ERROR); g_return_val_if_fail (func != NULL, GST_CLOCK_ERROR); entry = (GstClockEntry *) id; - clock = entry->clock; + requested = GST_CLOCK_ENTRY_TIME (entry); + clock = GST_CLOCK_ENTRY_CLOCK (entry); - if (!GST_CLOCK_TIME_IS_VALID (GST_CLOCK_ENTRY_TIME (entry))) { - (func) (clock, GST_CLOCK_TIME_NONE, id, user_data); - return GST_CLOCK_TIMEOUT; - } + if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (requested))) + goto invalid_time; + + if (G_UNLIKELY (entry->status == GST_CLOCK_UNSCHEDULED)) + goto unscheduled; cclass = GST_CLOCK_GET_CLASS (clock); @@ -246,24 +357,35 @@ gst_clock_id_wait_async (GstClockID id, entry->user_data = user_data; res = cclass->wait_async (clock, entry); + } else { + res = GST_CLOCK_UNSUPPORTED; } - return res; -} -static void -gst_clock_reschedule_func (GstClockEntry * entry) -{ - entry->status = GST_CLOCK_ENTRY_OK; - - gst_clock_id_unlock ((GstClockID) entry); + /* ERRORS */ +invalid_time: + { + (func) (clock, GST_CLOCK_TIME_NONE, id, user_data); + GST_CAT_DEBUG (GST_CAT_CLOCK, "invalid time requested, returning _BADTIME"); + return GST_CLOCK_BADTIME; + } +unscheduled: + { + GST_CAT_DEBUG (GST_CAT_CLOCK, "entry was unscheduled return _UNSCHEDULED"); + return GST_CLOCK_UNSCHEDULED; + } } /** * gst_clock_id_unschedule: * @id: The id to unschedule * - * Cancel an outstanding async notification request with the given ID. + * Cancel an outstanding request with the given ID. This can either + * be an outstanding async notification or a pending sync notification. + * After this call, the @id cannot be used anymore to receive sync or + * async notifications, you need to create a new GstClockID. + * + * MT safe. */ void gst_clock_id_unschedule (GstClockID id) @@ -283,47 +405,6 @@ gst_clock_id_unschedule (GstClockID id) cclass->unschedule (clock, entry); } -/** - * gst_clock_id_free: - * @id: The clockid to free - * - * Free the resources held by the given id - */ -void -gst_clock_id_free (GstClockID id) -{ - g_return_if_fail (id != NULL); - -#ifndef GST_DISABLE_TRACE - gst_alloc_trace_free (_gst_clock_entry_trace, id); -#endif - gst_mem_chunk_free (_gst_clock_entries_chunk, id); -} - -/** - * gst_clock_id_unlock: - * @id: The clockid to unlock - * - * Unlock the givan ClockID. - */ -void -gst_clock_id_unlock (GstClockID id) -{ - GstClockEntry *entry; - GstClock *clock; - GstClockClass *cclass; - - g_return_if_fail (id != NULL); - - entry = (GstClockEntry *) id; - clock = entry->clock; - - cclass = GST_CLOCK_GET_CLASS (clock); - - if (cclass->unlock) - cclass->unlock (clock, entry); -} - /** * GstClock abstract base class implementation @@ -382,30 +463,17 @@ gst_clock_class_init (GstClockClass * klass) g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_STATS, g_param_spec_boolean ("stats", "Stats", "Enable clock stats", FALSE, G_PARAM_READWRITE)); - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_MAX_DIFF, - g_param_spec_int64 ("max-diff", "Max diff", - "The maximum amount of time to wait in nanoseconds", 0, G_MAXINT64, - DEFAULT_MAX_DIFF, G_PARAM_READWRITE)); - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_EVENT_DIFF, - g_param_spec_uint64 ("event-diff", "event diff", - "The amount of time that may elapse until 2 events are treated as happening at different times", - 0, G_MAXUINT64, DEFAULT_EVENT_DIFF, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); } static void gst_clock_init (GstClock * clock) { - clock->max_diff = DEFAULT_MAX_DIFF; - - clock->start_time = 0; + clock->adjust = 0; clock->last_time = 0; clock->entries = NULL; + clock->entries_changed = g_cond_new (); clock->flags = 0; clock->stats = FALSE; - - clock->active_mutex = g_mutex_new (); - clock->active_cond = g_cond_new (); } static void @@ -413,48 +481,11 @@ gst_clock_dispose (GObject * object) { GstClock *clock = GST_CLOCK (object); - g_mutex_free (clock->active_mutex); - g_cond_free (clock->active_cond); + g_cond_free (clock->entries_changed); G_OBJECT_CLASS (parent_class)->dispose (object); } -/** - * gst_clock_set_speed - * @clock: a #GstClock to modify - * @speed: the speed to set on the clock - * - * Sets the speed on the given clock. 1.0 is the default - * speed. - * - * Returns: the new speed of the clock. - */ -gdouble -gst_clock_set_speed (GstClock * clock, gdouble speed) -{ - g_return_val_if_fail (GST_IS_CLOCK (clock), 0.0); - - GST_WARNING_OBJECT (clock, "called deprecated function"); - return 1.0; -} - -/** - * gst_clock_get_speed - * @clock: a #GstClock to query - * - * Gets the speed of the given clock. - * - * Returns: the speed of the clock. - */ -gdouble -gst_clock_get_speed (GstClock * clock) -{ - g_return_val_if_fail (GST_IS_CLOCK (clock), 0.0); - - GST_WARNING_OBJECT (clock, "called deprecated function"); - return 1.0; -} - /** * gst_clock_set_resolution * @clock: The clock set the resolution on @@ -488,6 +519,8 @@ gst_clock_set_resolution (GstClock * clock, guint64 resolution) * Get the accuracy of the clock. * * Returns: the resolution of the clock in microseconds. + * + * MT safe. */ guint64 gst_clock_get_resolution (GstClock * clock) @@ -505,88 +538,31 @@ gst_clock_get_resolution (GstClock * clock) } /** - * gst_clock_set_active - * @clock: a #GstClock to set state of - * @active: flag indicating if the clock should be activated (TRUE) or deactivated + * gst_clock_adjust_unlocked + * @clock: a #GstClock to use + * @internal: a clock time * - * Activates or deactivates the clock based on the active parameter. - * As soon as the clock is activated, the time will start ticking. - */ -void -gst_clock_set_active (GstClock * clock, gboolean active) -{ - g_return_if_fail (GST_IS_CLOCK (clock)); - - GST_ERROR_OBJECT (clock, "called deprecated function that does nothing now."); - - return; -} - -/** - * gst_clock_is_active - * @clock: a #GstClock to query + * Converts the given @internal clock time to the real time, adjusting + * and making sure that the returned time is increasing. + * This function should be called with the clock lock held. * - * Checks if the given clock is active. - * - * Returns: TRUE if the clock is active. - */ -gboolean -gst_clock_is_active (GstClock * clock) -{ - g_return_val_if_fail (GST_IS_CLOCK (clock), FALSE); - - GST_WARNING_OBJECT (clock, "called deprecated function."); - - return TRUE; -} - -/** - * gst_clock_reset - * @clock: a #GstClock to reset + * Returns: the converted time of the clock. * - * Reset the clock to time 0. + * MT safe. */ -void -gst_clock_reset (GstClock * clock) +GstClockTime +gst_clock_adjust_unlocked (GstClock * clock, GstClockTime internal) { - GstClockTime time = G_GINT64_CONSTANT (0); - GstClockClass *cclass; + GstClockTime ret; - g_return_if_fail (GST_IS_CLOCK (clock)); - - GST_ERROR_OBJECT (clock, "called deprecated function."); - - cclass = GST_CLOCK_GET_CLASS (clock); - - if (cclass->get_internal_time) { - time = cclass->get_internal_time (clock); + ret = internal + clock->adjust; + /* make sure the time is increasing, else return last_time */ + if ((gint64) ret < (gint64) clock->last_time) { + ret = clock->last_time; + } else { + clock->last_time = ret; } - - GST_LOCK (clock); - //clock->active = FALSE; - clock->start_time = time; - clock->last_time = G_GINT64_CONSTANT (0); - g_list_foreach (clock->entries, (GFunc) gst_clock_reschedule_func, NULL); - GST_UNLOCK (clock); -} - -/** - * gst_clock_handle_discont - * @clock: a #GstClock to notify of the discontinuity - * @time: The new time - * - * Notifies the clock of a discontinuity in time. - * - * Returns: TRUE if the clock was updated. It is possible that - * the clock was not updated by this call because only the first - * discontinuitity in the pipeline is honoured. - */ -gboolean -gst_clock_handle_discont (GstClock * clock, guint64 time) -{ - GST_ERROR_OBJECT (clock, "called deprecated function."); - - return FALSE; + return ret; } /** @@ -596,107 +572,60 @@ gst_clock_handle_discont (GstClock * clock, guint64 time) * Gets the current time of the given clock. The time is always * monotonically increasing. * - * Returns: the time of the clock. + * Returns: the time of the clock. Or GST_CLOCK_TIME_NONE when + * giving wrong input. + * + * MT safe. */ GstClockTime gst_clock_get_time (GstClock * clock) { - GstClockTime ret = G_GINT64_CONSTANT (0); + GstClockTime ret; GstClockClass *cclass; - g_return_val_if_fail (GST_IS_CLOCK (clock), G_GINT64_CONSTANT (0)); + g_return_val_if_fail (GST_IS_CLOCK (clock), GST_CLOCK_TIME_NONE); cclass = GST_CLOCK_GET_CLASS (clock); if (cclass->get_internal_time) { - ret = cclass->get_internal_time (clock) - clock->start_time; - } - /* make sure the time is increasing, else return last_time */ - if ((gint64) ret < (gint64) clock->last_time) { - ret = clock->last_time; + ret = cclass->get_internal_time (clock); } else { - clock->last_time = ret; + ret = G_GINT64_CONSTANT (0); } + GST_CAT_DEBUG (GST_CAT_CLOCK, "internal time %" GST_TIME_FORMAT, + GST_TIME_ARGS (ret)); + + GST_LOCK (clock); + ret = gst_clock_adjust_unlocked (clock, ret); + GST_UNLOCK (clock); + + GST_CAT_DEBUG (GST_CAT_CLOCK, "adjusted time %" GST_TIME_FORMAT, + GST_TIME_ARGS (ret)); return ret; } /** - * gst_clock_get_event_time: - * @clock: clock to query - * - * Gets the "event time" of a given clock. An event on the clock happens - * whenever this function is called. This ensures that multiple events that - * happen shortly after each other are treated as if they happened at the same - * time. GStreamer uses to keep state changes of multiple elements in sync. + * gst_clock_set_time_adjust + * @clock: a #GstClock to adjust + * @adjust: the adjust value * - * Returns: the time of the event + * Adjusts the current time of the clock with the adjust value. + * A positive value moves the clock forwards and a backwards value + * moves it backwards. Note that _get_time() always returns + * increasing values so when you move the clock backwards, _get_time() + * will report the previous value until the clock catches up. + * + * MT safe. */ -GstClockTime -gst_clock_get_event_time (GstClock * clock) +void +gst_clock_set_time_adjust (GstClock * clock, GstClockTime adjust) { - return gst_clock_get_event_time_delay (clock, 0); -} - -/** - * gst_clock_get_event_time_delay: - * @clock: clock to query - * @delay: time before the event actually occurs - * - * Gets the "event time" of a given clock. An event on the clock happens - * whenever this function is called. This ensures that multiple events that - * happen shortly after each other are treated as if they happened at the same - * time. GStreamer uses to keep state changes of multiple elements in sync. - * - * When calling this function, the specified delay will be added to the current - * time to produce the event time. This can be used for events that are - * scheduled to happen at some point in the future. - * - * Returns: the time of the event - */ -GstClockTime -gst_clock_get_event_time_delay (GstClock * clock, GstClockTime delay) -{ - GstClockTime time; - - g_return_val_if_fail (GST_IS_CLOCK (clock), GST_CLOCK_TIME_NONE); - - time = gst_clock_get_time (clock); - - if (ABS (GST_CLOCK_DIFF (clock->last_event, time + delay)) < - clock->max_event_diff) { - GST_LOG_OBJECT (clock, "reporting last event time %" G_GUINT64_FORMAT, - clock->last_event); - } else { - clock->last_event = time + delay; - GST_LOG_OBJECT (clock, "reporting new event time %" G_GUINT64_FORMAT, - clock->last_event); - } - - return clock->last_event; -} - -/** - * gst_clock_get_next_id - * @clock: The clock to query - * - * Get the clockid of the next event. - * - * Returns: a clockid or NULL is no event is pending. - */ -GstClockID -gst_clock_get_next_id (GstClock * clock) -{ - GstClockEntry *entry = NULL; - - g_return_val_if_fail (GST_IS_CLOCK (clock), NULL); + g_return_if_fail (GST_IS_CLOCK (clock)); GST_LOCK (clock); - if (clock->entries) - entry = GST_CLOCK_ENTRY (clock->entries->data); + clock->adjust = adjust; GST_UNLOCK (clock); - - return (GstClockID *) entry; } static void @@ -717,14 +646,6 @@ gst_clock_set_property (GObject * object, guint prop_id, clock->stats = g_value_get_boolean (value); g_object_notify (object, "stats"); break; - case ARG_MAX_DIFF: - clock->max_diff = g_value_get_int64 (value); - g_object_notify (object, "max-diff"); - break; - case ARG_EVENT_DIFF: - clock->max_event_diff = g_value_get_uint64 (value); - g_object_notify (object, "event-diff"); - break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -743,12 +664,6 @@ gst_clock_get_property (GObject * object, guint prop_id, case ARG_STATS: g_value_set_boolean (value, clock->stats); break; - case ARG_MAX_DIFF: - g_value_set_int64 (value, clock->max_diff); - break; - case ARG_EVENT_DIFF: - g_value_set_uint64 (value, clock->max_event_diff); - break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; diff --git a/gst/gstclock.h b/gst/gstclock.h index dabb624bd7..41a6b001c0 100644 --- a/gst/gstclock.h +++ b/gst/gstclock.h @@ -34,6 +34,7 @@ G_BEGIN_DECLS #define GST_CLOCK_CLASS(cclass) (G_TYPE_CHECK_CLASS_CAST ((cclass), GST_TYPE_CLOCK, GstClockClass)) #define GST_IS_CLOCK_CLASS(cclass) (G_TYPE_CHECK_CLASS_TYPE ((cclass), GST_TYPE_CLOCK)) #define GST_CLOCK_GET_CLASS(clock) (G_TYPE_INSTANCE_GET_CLASS ((clock), GST_TYPE_CLOCK, GstClockClass)) +#define GST_CLOCK_CAST(clock) ((GstClock*)(clock)) typedef guint64 GstClockTime; typedef gint64 GstClockTimeDiff; @@ -55,6 +56,21 @@ G_STMT_START { \ (tv).tv_usec = ((t) - (tv).tv_sec * GST_SECOND) / GST_USECOND; \ } G_STMT_END +#define GST_TIMESPEC_TO_TIME(ts) ((ts).tv_sec * GST_SECOND + (ts).tv_nsec * GST_NSECOND) +#define GST_TIME_TO_TIMESPEC(t,ts) \ +G_STMT_START { \ + (ts).tv_sec = (t) / GST_SECOND; \ + (ts).tv_usec = ((t) - (ts).tv_sec * GST_SECOND) / GST_NSECOND; \ +} G_STMT_END + +/* timestamp debugging macros */ +#define GST_TIME_FORMAT "u:%02u:%02u.%09u" +#define GST_TIME_ARGS(t) \ + (guint) ((t) / (GST_SECOND * 60 * 60)), \ + (guint) (((t) / (GST_SECOND * 60)) % 60), \ + (guint) (((t) / GST_SECOND) % 60), \ + (guint) ((t) % GST_SECOND) + #define GST_CLOCK_ENTRY_TRACE_NAME "GstClockEntry" typedef struct _GstClockEntry GstClockEntry; @@ -65,15 +81,18 @@ typedef struct _GstClockClass GstClockClass; typedef gboolean (*GstClockCallback) (GstClock *clock, GstClockTime time, GstClockID id, gpointer user_data); -typedef enum { - /* --- protected --- */ - GST_CLOCK_ENTRY_OK, - GST_CLOCK_ENTRY_EARLY, - GST_CLOCK_ENTRY_RESTART -} GstClockEntryStatus; +typedef enum +{ + GST_CLOCK_OK = 0, + GST_CLOCK_EARLY = 1, + GST_CLOCK_UNSCHEDULED = 2, + GST_CLOCK_BUSY = 3, + GST_CLOCK_BADTIME = 4, + GST_CLOCK_ERROR = 5, + GST_CLOCK_UNSUPPORTED = 6, +} GstClockReturn; typedef enum { - /* --- protected --- */ GST_CLOCK_ENTRY_SINGLE, GST_CLOCK_ENTRY_PERIODIC } GstClockEntryType; @@ -86,25 +105,17 @@ typedef enum { #define GST_CLOCK_ENTRY_STATUS(entry) ((entry)->status) struct _GstClockEntry { - /* --- protected --- */ + GstAtomicInt refcount; + /*< protected >*/ GstClock *clock; GstClockEntryType type; GstClockTime time; GstClockTime interval; - GstClockEntryStatus status; + GstClockReturn status; GstClockCallback func; gpointer user_data; }; -typedef enum -{ - GST_CLOCK_STOPPED = 0, - GST_CLOCK_TIMEOUT = 1, - GST_CLOCK_EARLY = 2, - GST_CLOCK_ERROR = 3, - GST_CLOCK_UNSUPPORTED = 4 -} GstClockReturn; - typedef enum { GST_CLOCK_FLAG_CAN_DO_SINGLE_SYNC = (1 << 1), @@ -112,41 +123,39 @@ typedef enum GST_CLOCK_FLAG_CAN_DO_PERIODIC_SYNC = (1 << 3), GST_CLOCK_FLAG_CAN_DO_PERIODIC_ASYNC = (1 << 4), GST_CLOCK_FLAG_CAN_SET_RESOLUTION = (1 << 5), - GST_CLOCK_FLAG_CAN_SET_SPEED = (1 << 6) } GstClockFlags; #define GST_CLOCK_FLAGS(clock) (GST_CLOCK(clock)->flags) +#define GST_CLOCK_COND(clock) (GST_CLOCK_CAST(clock)->entries_changed) +#define GST_CLOCK_WAIT(clock) g_cond_wait(GST_CLOCK_COND(clock),GST_GET_LOCK(clock)) +#define GST_CLOCK_TIMED_WAIT(clock,tv) g_cond_timed_wait(GST_CLOCK_COND(clock),GST_GET_LOCK(clock),tv) +#define GST_CLOCK_SIGNAL(clock) g_cond_broadcast(GST_CLOCK_COND(clock)) + struct _GstClock { GstObject object; + /*< public >*/ GstClockFlags flags; - /* --- protected --- */ - GstClockTime start_time; + /*< protected >*/ /* with LOCK */ + GstClockTime adjust; GstClockTime last_time; - gint64 max_diff; - - /* --- private --- */ - guint64 resolution; GList *entries; - GMutex *active_mutex; - GCond *active_cond; + GCond *entries_changed; + + /*< private >*/ + guint64 resolution; gboolean stats; - GstClockTime last_event; - GstClockTime max_event_diff; - gpointer _gst_reserved[GST_PADDING]; }; struct _GstClockClass { GstObjectClass parent_class; + /*< protected >*/ /* vtable */ - gdouble (*change_speed) (GstClock *clock, - gdouble oldspeed, gdouble newspeed); - gdouble (*get_speed) (GstClock *clock); guint64 (*change_resolution) (GstClock *clock, guint64 old_resolution, guint64 new_resolution); guint64 (*get_resolution) (GstClock *clock); @@ -154,32 +163,24 @@ struct _GstClockClass { GstClockTime (*get_internal_time) (GstClock *clock); /* waiting on an ID */ - GstClockEntryStatus (*wait) (GstClock *clock, GstClockEntry *entry); - GstClockEntryStatus (*wait_async) (GstClock *clock, GstClockEntry *entry); + GstClockReturn (*wait) (GstClock *clock, GstClockEntry *entry); + GstClockReturn (*wait_async) (GstClock *clock, GstClockEntry *entry); void (*unschedule) (GstClock *clock, GstClockEntry *entry); - void (*unlock) (GstClock *clock, GstClockEntry *entry); + + /*< private >*/ gpointer _gst_reserved[GST_PADDING]; }; GType gst_clock_get_type (void); -gdouble gst_clock_set_speed (GstClock *clock, gdouble speed); -gdouble gst_clock_get_speed (GstClock *clock); - guint64 gst_clock_set_resolution (GstClock *clock, guint64 resolution); guint64 gst_clock_get_resolution (GstClock *clock); -void gst_clock_set_active (GstClock *clock, gboolean active); -gboolean gst_clock_is_active (GstClock *clock); -void gst_clock_reset (GstClock *clock); -gboolean gst_clock_handle_discont (GstClock *clock, guint64 time); - GstClockTime gst_clock_get_time (GstClock *clock); -GstClockTime gst_clock_get_event_time (GstClock *clock); -GstClockTime gst_clock_get_event_time_delay (GstClock *clock, GstClockTime delay); +void gst_clock_set_time_adjust (GstClock *clock, GstClockTime adjust); +GstClockTime gst_clock_adjust_unlocked (GstClock *clock, GstClockTime internal); -GstClockID gst_clock_get_next_id (GstClock *clock); /* creating IDs that can be used to get notifications */ GstClockID gst_clock_new_single_shot_id (GstClock *clock, @@ -188,7 +189,13 @@ GstClockID gst_clock_new_periodic_id (GstClock *clock, GstClockTime start_time, GstClockTime interval); +/* reference counting */ +GstClockID gst_clock_id_ref (GstClockID id); +void gst_clock_id_unref (GstClockID id); + /* operations on IDs */ +gint gst_clock_id_compare_func (gconstpointer id1, gconstpointer id2); + GstClockTime gst_clock_id_get_time (GstClockID id); GstClockReturn gst_clock_id_wait (GstClockID id, GstClockTimeDiff *jitter); @@ -196,9 +203,6 @@ GstClockReturn gst_clock_id_wait_async (GstClockID id, GstClockCallback func, gpointer user_data); void gst_clock_id_unschedule (GstClockID id); -void gst_clock_id_unlock (GstClockID id); -void gst_clock_id_free (GstClockID id); - G_END_DECLS diff --git a/gst/gstcompat.h b/gst/gstcompat.h index dd47c17a51..513f4398f8 100644 --- a/gst/gstcompat.h +++ b/gst/gstcompat.h @@ -1,6 +1,6 @@ /* GStreamer * Copyright (C) 1999,2000 Erik Walthinsen - * 2000 Wim Taymans + * 2004 Wim Taymans * * gstcompat.h: backwards compatibility stuff * @@ -28,57 +28,6 @@ G_BEGIN_DECLS #ifndef GST_DISABLE_DEPRECATED -/* 0.5.2 changes; remove these ASAP */ - -/* element functions */ -#define gst_element_connect(a,b) gst_element_link(a,b) -#define gst_element_connect_pads(a,b,c,d) \ - gst_element_link_pads(a,b,c,d) -#ifdef G_HAVE_ISO_VARARGS -#define gst_element_connect_many(a,...) gst_element_link_many(a,__VA_ARGS__) -#elif defined(G_HAVE_GNUC_VARARGS) -#define gst_element_connect_many(a,args...) \ - gst_element_link_many(a, ## args) -#else -/* FIXME: need an inline function */ -#endif -#define gst_element_connect_filtered(a,b,c) \ - gst_element_link_filtered(a,b,c) -#define gst_element_disconnect(a,b) gst_element_unlink(a,b) - -/* pad functions */ -#define gst_pad_connect(a,b) gst_pad_link(a,b) -#define gst_pad_connect_filtered(a,b,c) gst_pad_link_filtered(a,b,c) -#define gst_pad_disconnect(a,b) gst_pad_unlink(a,b) -#define gst_pad_proxy_connect(a,b) gst_pad_proxy_link(a,b) -#define gst_pad_set_connect_function(a,b) \ - gst_pad_set_link_function(a,b) - -/* pad macros */ -#define GST_PAD_IS_CONNECTED(a) GST_PAD_IS_LINKED(a) - -/* pad enums */ -#define GST_PAD_CONNECT_REFUSED GST_PAD_LINK_REFUSED -#define GST_PAD_CONNECT_DELAYED GST_PAD_LINK_DELAYED -#define GST_PAD_CONNECT_OK GST_PAD_LINK_OK -#define GST_PAD_CONNECT_DONE GST_PAD_LINK_DONE -typedef GstPadLinkReturn GstPadConnectReturn; - -/* pad function types */ -typedef GstPadLinkFunction GstPadConnectFunction; - -/* probably not used */ -/* - * GST_RPAD_LINKFUNC - */ - -/* 0.8.1.1 removal; remove completely in 0.9 */ -/* information messages */ -# ifdef G_HAVE_ISO_VARARGS -#define gst_info(...) GST_INFO(__VA_ARGS__) -# elif defined(G_HAVE_GNUC_VARARGS) -#define gst_info(format,args...) GST_INFO(format,##args) -# endif #endif /* not GST_DISABLE_DEPRECATED */ diff --git a/gst/gstcpu.c b/gst/gstcpu.c index c8e00750d9..88f186354b 100644 --- a/gst/gstcpu.c +++ b/gst/gstcpu.c @@ -28,6 +28,7 @@ #include "gstcpu.h" #include "gstinfo.h" +static GStaticMutex _cpu_mutex = G_STATIC_MUTEX_INIT; static guint32 _gst_cpu_flags = 0; #if defined(HAVE_CPU_I386) && defined(__GNUC__) @@ -89,7 +90,9 @@ _gst_cpu_initialize_i386 (gulong * flags, GString * featurelist) { gboolean AMD; gulong eax = 0, ebx = 0, ecx = 0, edx = 0; + gboolean res = FALSE; + g_static_mutex_lock (&_cpu_mutex); gst_cpuid_i386 (0, &eax, &ebx, &ecx, &edx); AMD = (ebx == 0x68747541) && (ecx == 0x444d4163) && (edx == 0x69746e65); @@ -124,13 +127,21 @@ _gst_cpu_initialize_i386 (gulong * flags, GString * featurelist) } *flags = eax; if (_gst_cpu_flags) - return TRUE; - return FALSE; + res = TRUE; + g_static_mutex_unlock (&_cpu_mutex); + + return res; } #endif GstCPUFlags gst_cpu_get_flags (void) { - return _gst_cpu_flags; + GstCPUFlags res; + + g_static_mutex_lock (&_cpu_mutex); + res = _gst_cpu_flags; + g_static_mutex_unlock (&_cpu_mutex); + + return res; } diff --git a/gst/gstdata.c b/gst/gstdata.c index 52b8ce766f..abd7cd43c7 100644 --- a/gst/gstdata.c +++ b/gst/gstdata.c @@ -1,6 +1,6 @@ /* GStreamer * Copyright (C) 1999,2000 Erik Walthinsen - * 2000 Wim Taymans + * 2000 Wim Taymans * * gstdata.c: Data operations * @@ -43,11 +43,12 @@ gst_data_get_type (void) * @data: a #GstData to initialize * @type: the type of this data * @flags: flags for this data - * @free: a free function - * @copy: a copy function + * @free: a free function + * @copy: a copy function * - * Initialize the given data structure with the given parameters. The free and copy - * function will be called when this data is freed or copied respectively. + * Initialize the given data structure with the given parameters. + * The free and copy function will be called when this data is freed + * or copied, respectively. */ void gst_data_init (GstData * data, GType type, guint16 flags, @@ -64,7 +65,7 @@ gst_data_init (GstData * data, GType type, guint16 flags, * @target: the target #GstData to copy into * * Copy the GstData into the specified target GstData structure. - * Thos method is mainly used by subclasses when they want to copy + * This method is mainly used by subclasses when they want to copy * the relevant GstData info. */ void @@ -77,7 +78,7 @@ gst_data_copy_into (const GstData * data, GstData * target) * gst_data_dispose: * @data: a #GstData to dispose * - * Free all the resources allocated in the gst_data_init() function, + * Free all the resources allocated in the gst_data_init() function, * mainly used by subclass implementors. */ void @@ -95,9 +96,11 @@ gst_data_dispose (GstData * data) * Copies the given #GstData. This function will call the custom subclass * copy function or return NULL if no function was provided by the subclass. * - * Returns: a copy of the data or NULL if the data cannot be copied. The refcount - * of the original buffer is not changed so you should unref it when you don't - * need it anymore. + * Returns: a copy of the data or NULL if the data cannot be copied. + * The refcount of the original buffer is not changed so you should unref it + * when you don't need it anymore. + * + * MT safe. */ GstData * gst_data_copy (const GstData * data) @@ -112,12 +115,14 @@ gst_data_copy (const GstData * data) /** * gst_data_is_writable: - * @data: a #GstData to copy + * @data: a #GstData to check * - * Query if the gstdata needs to be copied before it can safely be modified. + * Query if the data needs to be copied before it can safely be modified. * * Returns: FALSE if the given #GstData is potentially shared and needs to * be copied before it can be modified safely. + * + * MT safe. */ gboolean gst_data_is_writable (GstData * data) @@ -128,12 +133,12 @@ gst_data_is_writable (GstData * data) refcount = gst_atomic_int_read (&data->refcount); - if (refcount > 1) - return FALSE; - if (GST_DATA_FLAG_IS_SET (data, GST_DATA_READONLY)) - return FALSE; + /* if we have the only ref and the data is not readonly, we can + * safely write */ + if (refcount == 1 && !GST_DATA_FLAG_IS_SET (data, GST_DATA_READONLY)) + return TRUE; - return TRUE; + return FALSE; } /** @@ -143,11 +148,14 @@ gst_data_is_writable (GstData * data) * Copies the given #GstData if the refcount is greater than 1 so that the * #GstData object can be written to safely. * - * Returns: a copy of the data if the refcount is > 1 or the buffer is + * Returns: a copy of the data if the refcount is > 1 or the buffer is * marked READONLY, data if the refcount == 1, - * or NULL if the data could not be copied. The refcount of the original buffer - * is decreased when a copy is made, so you are not supposed to use it after a - * call to this function. + * or NULL if the data could not be copied. + * + * The refcount of the passed @data is decreased when a copy is made, so + * you are not supposed to use it anymore after a call to this function. + * + * MT safe. */ GstData * gst_data_copy_on_write (GstData * data) @@ -158,6 +166,8 @@ gst_data_copy_on_write (GstData * data) refcount = gst_atomic_int_read (&data->refcount); + /* if we have the only ref and the data is not readonly, we can + * safely write, so we return the input data */ if (refcount == 1 && !GST_DATA_FLAG_IS_SET (data, GST_DATA_READONLY)) return GST_DATA (data); @@ -178,6 +188,8 @@ gst_data_copy_on_write (GstData * data) * Increments the reference count of this data. * * Returns: the data + * + * MT safe. */ GstData * gst_data_ref (GstData * data) @@ -201,6 +213,8 @@ gst_data_ref (GstData * data) * Increments the reference count of this data by the given number. * * Returns: the data + * + * MT safe. */ GstData * gst_data_ref_by_count (GstData * data, gint count) @@ -224,9 +238,12 @@ gst_data_ref_by_count (GstData * data, gint count) * Decrements the refcount of this data. If the refcount is * zero, the data will be freed. * - * When you add data to a pipeline, the pipeline takes ownership of the - * data. When the data has been used by some plugin, it must unref()s it. - * Applications usually don't need to unref() anything. + * When you move data out of your element into the pipeline, + * the pipeline takes ownership of the + * data. When the data has been consumed by some element, it must unref() it. + * Applications usually don't need to unref() @data. + * + * MT safe. */ void gst_data_unref (GstData * data) diff --git a/gst/gstdata.h b/gst/gstdata.h index 1c3da1f0da..9cd447b2b4 100644 --- a/gst/gstdata.h +++ b/gst/gstdata.h @@ -39,22 +39,22 @@ G_BEGIN_DECLS #define GST_DATA_FLAG_SHIFT(flag) (1<<(flag)) #define GST_DATA_FLAG_IS_SET(data,flag) (GST_DATA_FLAGS(data) & (1<<(flag))) #define GST_DATA_FLAG_SET(data,flag) G_STMT_START{ (GST_DATA_FLAGS(data) |= (1<<(flag))); }G_STMT_END -#define GST_DATA_FLAG_UNSET(data,flag) G_STMT_START{ (GST_DATA_FLAGS(data) &= ~(1<<(flag))); }G_STMT_END +#define GST_DATA_FLAG_UNSET(data,flag) G_STMT_START{ (GST_DATA_FLAGS(data) &= ~(1<<(flag))); }G_STMT_END /* Macros for the GType */ #define GST_TYPE_DATA (gst_data_get_type ()) typedef struct _GstData GstData; -typedef void (*GstDataFreeFunction) (GstData *data); +typedef void (*GstDataFreeFunction) (GstData *data); typedef GstData* (*GstDataCopyFunction) (const GstData *data); typedef enum { - GST_DATA_READONLY = 1, + GST_DATA_READONLY = 1, /* insert more */ - GST_DATA_FLAG_LAST = 8 + GST_DATA_FLAG_LAST = 8 } GstDataFlags; /* refcount */ @@ -62,22 +62,25 @@ typedef enum #define GST_DATA_REFCOUNT_VALUE(data) (gst_atomic_int_read (&(GST_DATA(data))->refcount)) /* copy/free functions */ -#define GST_DATA_COPY_FUNC(data) (GST_DATA(data)->copy) -#define GST_DATA_FREE_FUNC(data) (GST_DATA(data)->free) +#define GST_DATA_COPY_FUNC(data) (GST_DATA(data)->copy) +#define GST_DATA_FREE_FUNC(data) (GST_DATA(data)->free) struct _GstData { - GType type; + GType type; + /*< public >*/ /* with COW */ /* refcounting */ GstAtomicInt refcount; guint16 flags; + /*< protected >*/ /* utility function pointers, can override default */ - GstDataFreeFunction free; /* free the data */ - GstDataCopyFunction copy; /* copy the data */ + GstDataFreeFunction free; /* free the data */ + GstDataCopyFunction copy; /* copy the data */ + /*< private >*/ gpointer _gst_reserved[GST_PADDING]; }; @@ -89,9 +92,9 @@ void gst_data_dispose (GstData *data); void gst_data_copy_into (const GstData *data, GstData *target); /* basic operations on data */ -GstData* gst_data_copy (const GstData *data); -gboolean gst_data_is_writable (GstData *data); -GstData* gst_data_copy_on_write (GstData *data); +GstData* gst_data_copy (const GstData *data); +gboolean gst_data_is_writable (GstData *data); +GstData* gst_data_copy_on_write (GstData *data); /* reference counting */ GstData* gst_data_ref (GstData* data); diff --git a/gst/gstelement.c b/gst/gstelement.c index f9b1e2b5f7..dd6a6d89e7 100644 --- a/gst/gstelement.c +++ b/gst/gstelement.c @@ -1,6 +1,6 @@ /* GStreamer * Copyright (C) 1999,2000 Erik Walthinsen - * 2000 Wim Taymans + * 2004 Wim Taymans * * gstelement.c: The base element, all elements derive from this * @@ -70,6 +70,7 @@ static void gst_element_real_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); static void gst_element_dispose (GObject * object); +static void gst_element_finalize (GObject * object); static GstElementStateReturn gst_element_change_state (GstElement * element); static void gst_element_error_func (GstElement * element, GstElement * source, @@ -212,6 +213,7 @@ gst_element_class_init (GstElementClass * klass) GST_DEBUG_FUNCPTR (gst_element_real_get_property); gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_element_dispose); + gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_element_finalize); #ifndef GST_DISABLE_LOADSAVE gstobject_class->save_thyself = GST_DEBUG_FUNCPTR (gst_element_save_thyself); @@ -262,7 +264,10 @@ gst_element_init (GstElement * element) element->numpads = 0; element->numsrcpads = 0; element->numsinkpads = 0; + element->pads_cookie = 0; element->pads = NULL; + element->srcpads = NULL; + element->sinkpads = NULL; element->loopfunc = NULL; element->sched = NULL; element->clock = NULL; @@ -302,6 +307,8 @@ gst_element_real_get_property (GObject * object, guint prop_id, GValue * value, * The user data passed to the g_signal_connect is ignored. * * The default handler will simply print the error string using g_print. + * + * MT safe. */ void gst_element_default_error (GObject * object, GstObject * source, GError * error, @@ -718,21 +725,6 @@ gst_element_get_property (GstElement * element, const gchar * property_name, g_object_unref (object); } -static GstPad * -gst_element_request_pad (GstElement * element, GstPadTemplate * templ, - const gchar * name) -{ - GstPad *newpad = NULL; - GstElementClass *oclass; - - oclass = GST_ELEMENT_GET_CLASS (element); - - if (oclass->request_new_pad) - newpad = (oclass->request_new_pad) (element, templ, name); - - return newpad; -} - /** * gst_element_release_request_pad: * @element: a #GstElement to release the request pad of. @@ -740,6 +732,8 @@ gst_element_request_pad (GstElement * element, GstPadTemplate * templ, * * Makes the element free the previously requested pad as obtained * with gst_element_get_request_pad(). + * + * MT safe. */ void gst_element_release_request_pad (GstElement * element, GstPad * pad) @@ -761,32 +755,44 @@ gst_element_release_request_pad (GstElement * element, GstPad * pad) * gst_element_requires_clock: * @element: a #GstElement to query * - * Query if the element requiresd a clock + * Query if the element requires a clock. * * Returns: TRUE if the element requires a clock + * + * MT safe. */ gboolean gst_element_requires_clock (GstElement * element) { - g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE); + gboolean result = FALSE; - return (GST_ELEMENT_GET_CLASS (element)->set_clock != NULL); + g_return_val_if_fail (GST_IS_ELEMENT (element), result); + + result = (GST_ELEMENT_GET_CLASS (element)->set_clock != NULL); + + return result; } /** * gst_element_provides_clock: * @element: a #GstElement to query * - * Query if the element provides a clock + * Query if the element provides a clock. * * Returns: TRUE if the element provides a clock + * + * MT safe. */ gboolean gst_element_provides_clock (GstElement * element) { + gboolean result = FALSE; + g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE); - return (GST_ELEMENT_GET_CLASS (element)->get_clock != NULL); + result = (GST_ELEMENT_GET_CLASS (element)->get_clock != NULL); + + return result; } /** @@ -794,7 +800,11 @@ gst_element_provides_clock (GstElement * element) * @element: a #GstElement to set the clock for. * @clock: the #GstClock to set for the element. * - * Sets the clock for the element. + * Sets the clock for the element. This function increases the + * refcount on the clock. Any previously set clock on the object + * is unreffed. + * + * MT safe. */ void gst_element_set_clock (GstElement * element, GstClock * clock) @@ -808,16 +818,22 @@ gst_element_set_clock (GstElement * element, GstClock * clock) if (oclass->set_clock) oclass->set_clock (element, clock); + GST_LOCK (element); gst_object_replace ((GstObject **) & element->clock, (GstObject *) clock); + GST_UNLOCK (element); } /** * gst_element_get_clock: * @element: a #GstElement to get the clock of. * - * Gets the clock of the element. + * Gets the clock of the element. If the element provides a clock, + * this function will return this clock. For elements that do not + * provide a clock, this function returns NULL. * - * Returns: the #GstClock of the element. + * Returns: the #GstClock of the element. unref after usage. + * + * MT safe. */ GstClock * gst_element_get_clock (GstElement * element) @@ -852,14 +868,14 @@ gst_element_clock_wait (GstElement * element, GstClockID id, g_return_val_if_fail (GST_IS_ELEMENT (element), GST_CLOCK_ERROR); - if (GST_ELEMENT_SCHED (element)) { + if (GST_ELEMENT_SCHEDULER (element)) { GST_CAT_DEBUG (GST_CAT_CLOCK, "waiting on scheduler clock with id %d"); res = - gst_scheduler_clock_wait (GST_ELEMENT_SCHED (element), element, id, + gst_scheduler_clock_wait (GST_ELEMENT_SCHEDULER (element), element, id, jitter); } else { - GST_CAT_DEBUG (GST_CAT_CLOCK, "no scheduler, returning GST_CLOCK_TIMEOUT"); - res = GST_CLOCK_TIMEOUT; + GST_CAT_DEBUG (GST_CAT_CLOCK, "no scheduler, returning GST_CLOCK_OK"); + res = GST_CLOCK_OK; } return res; @@ -947,9 +963,9 @@ gst_element_wait (GstElement * element, GstClockTime timestamp) 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); + gst_clock_id_unref (id); - return ret == GST_CLOCK_STOPPED; + return ret == GST_CLOCK_OK; } /** @@ -1001,7 +1017,8 @@ gst_element_set_time_delay (GstElement * element, GstClockTime time, element->base_time = time - delay; break; case GST_STATE_PLAYING: - event_time = gst_clock_get_event_time_delay (element->clock, delay); + event_time = GST_CLOCK_TIME_NONE; + //gst_clock_get_event_time_delay (element->clock, delay); GST_CAT_LOG_OBJECT (GST_CAT_CLOCK, element, "clock time %" GST_TIME_FORMAT ": setting element time to %" GST_TIME_FORMAT, GST_TIME_ARGS (event_time), GST_TIME_ARGS (time)); @@ -1068,13 +1085,19 @@ gst_element_adjust_time (GstElement * element, GstClockTimeDiff diff) * Queries if the element can be indexed. * * Returns: TRUE if the element can be indexed. + * + * MT safe. */ gboolean gst_element_is_indexable (GstElement * element) { - g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE); + gboolean result = FALSE; - return (GST_ELEMENT_GET_CLASS (element)->set_index != NULL); + g_return_val_if_fail (GST_IS_ELEMENT (element), result); + + result = (GST_ELEMENT_GET_CLASS (element)->set_index != NULL); + + return result; } /** @@ -1083,6 +1106,8 @@ gst_element_is_indexable (GstElement * element) * @index: a #GstIndex. * * Set the specified GstIndex on the element. + * + * MT safe. */ void gst_element_set_index (GstElement * element, GstIndex * index) @@ -1105,7 +1130,9 @@ gst_element_set_index (GstElement * element, GstIndex * index) * Gets the index from the element. * * Returns: a #GstIndex or NULL when no index was set on the - * element. + * element. unref after usage. + * + * MT safe. */ GstIndex * gst_element_get_index (GstElement * element) @@ -1156,49 +1183,83 @@ gst_element_release_locks (GstElement * element) * see gst_object_set_parent() for refcounting information. * * Pads are automatically activated when the element is in state PLAYING. + * + * The pad and the element should be unlocked when calling this function. + * + * Returns: TRUE if the pad could be added. This function can fail when + * passing bad arguments or when a pad with the same name already existed. + * + * MT safe. */ -void +gboolean gst_element_add_pad (GstElement * element, GstPad * pad) { - g_return_if_fail (GST_IS_ELEMENT (element)); - g_return_if_fail (GST_IS_PAD (pad)); + gchar *pad_name; - /* first check to make sure the pad hasn't already been added to another - * element */ - g_return_if_fail (GST_PAD_PARENT (pad) == NULL); + g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE); + g_return_val_if_fail (GST_IS_PAD (pad), FALSE); + + /* locking pad to look at the name */ + GST_LOCK (pad); + pad_name = g_strdup (GST_PAD_NAME (pad)); + GST_CAT_INFO_OBJECT (GST_CAT_ELEMENT_PADS, element, "adding pad '%s'", + GST_STR_NULL (pad_name)); + GST_UNLOCK (pad); /* then check to see if there's already a pad by that name here */ - g_return_if_fail (gst_object_check_uniqueness (element->pads, - GST_PAD_NAME (pad)) == TRUE); + GST_LOCK (element); + if (G_UNLIKELY (!gst_object_check_uniqueness (element->pads, pad_name))) + goto name_exists; - GST_CAT_INFO_OBJECT (GST_CAT_ELEMENT_PADS, element, "adding pad '%s'", - GST_STR_NULL (GST_OBJECT_NAME (pad))); + /* try to set the pad's parent */ + if (G_UNLIKELY (!gst_object_set_parent (GST_OBJECT_CAST (pad), + GST_OBJECT_CAST (element)))) + goto had_parent; - /* set the pad's parent */ - gst_object_set_parent (GST_OBJECT (pad), GST_OBJECT (element)); + g_free (pad_name); /* add it to the list */ - element->pads = g_list_append (element->pads, pad); - element->numpads++; - switch (gst_pad_get_direction (pad)) { case GST_PAD_SRC: + element->srcpads = g_list_prepend (element->srcpads, pad); element->numsrcpads++; break; case GST_PAD_SINK: + element->sinkpads = g_list_prepend (element->sinkpads, pad); element->numsinkpads++; break; default: /* can happen for ghost pads */ break; } - - /* activate element when we are playing */ - if (GST_STATE (element) == GST_STATE_PLAYING) - gst_pad_set_active (pad, TRUE); + element->pads = g_list_prepend (element->pads, pad); + element->numpads++; + element->pads_cookie++; + GST_UNLOCK (element); /* emit the NEW_PAD signal */ g_signal_emit (G_OBJECT (element), gst_element_signals[NEW_PAD], 0, pad); + + return TRUE; + + /* ERROR cases */ +name_exists: + { + g_critical ("Padname %s is not unique in element %s, not adding", + pad_name, GST_ELEMENT_NAME (element)); + GST_UNLOCK (element); + g_free (pad_name); + return FALSE; + } +had_parent: + { + g_critical + ("Pad %s already has parent when trying to add to element %s", + pad_name, GST_ELEMENT_NAME (element)); + GST_UNLOCK (element); + g_free (pad_name); + return FALSE; + } } /** @@ -1212,6 +1273,8 @@ gst_element_add_pad (GstElement * element, GstPad * pad) * gst_element_add_pad(). * * Returns: the added ghost #GstPad, or NULL on error. + * + * MT safe. */ GstPad * gst_element_add_ghost_pad (GstElement * element, GstPad * pad, @@ -1222,13 +1285,12 @@ gst_element_add_ghost_pad (GstElement * element, GstPad * pad, g_return_val_if_fail (GST_IS_ELEMENT (element), NULL); g_return_val_if_fail (GST_IS_PAD (pad), NULL); - /* then check to see if there's already a pad by that name here */ - g_return_val_if_fail (gst_object_check_uniqueness (element->pads, - name) == TRUE, NULL); - ghostpad = gst_ghost_pad_new (name, pad); - gst_element_add_pad (element, ghostpad); + if (!gst_element_add_pad (element, ghostpad)) { + gst_object_unref (GST_OBJECT (ghostpad)); + ghostpad = NULL; + } return ghostpad; } @@ -1240,65 +1302,92 @@ gst_element_add_ghost_pad (GstElement * element, GstPad * pad, * * Removes @pad from @element. @pad will be destroyed if it has not been * referenced elsewhere. + * + * Returns: TRUE if the pad could be removed. Can return FALSE if the + * pad is not belonging to the provided element or when wrong parameters + * are passed to this function. + * + * MT safe. */ -void +gboolean gst_element_remove_pad (GstElement * element, GstPad * pad) { - g_return_if_fail (element != NULL); - g_return_if_fail (GST_IS_ELEMENT (element)); - g_return_if_fail (pad != NULL); - g_return_if_fail (GST_IS_PAD (pad)); + gchar *pad_name; - g_return_if_fail (GST_PAD_PARENT (pad) == element); + g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE); + g_return_val_if_fail (GST_IS_PAD (pad), FALSE); + /* locking pad to look at the name and parent */ + GST_LOCK (pad); + pad_name = g_strdup (GST_PAD_NAME (pad)); + + GST_CAT_INFO_OBJECT (GST_CAT_ELEMENT_PADS, element, "removing pad '%s'", + GST_STR_NULL (pad_name)); + + if (G_UNLIKELY (GST_PAD_PARENT (pad) != element)) + goto not_our_pad; + GST_UNLOCK (pad); + + g_free (pad_name); + + /* FIXME, is this redundant with pad disposal? */ if (GST_IS_REAL_PAD (pad)) { - /* unlink if necessary */ - if (GST_RPAD_PEER (pad) != NULL) { - gst_pad_unlink (pad, GST_PAD (GST_RPAD_PEER (pad))); + GstPad *peer = gst_pad_get_peer (pad); + + /* unlink */ + if (peer != NULL) { + /* window for MT unsafeness, someone else could unlink here + * and then we call unlink with wrong pads. The unlink + * function would catch this and safely return failed. */ + if (GST_PAD_IS_SRC (pad)) + gst_pad_unlink (pad, GST_PAD_CAST (peer)); + else + gst_pad_unlink (GST_PAD_CAST (peer), pad); + + gst_object_unref (GST_OBJECT (peer)); } - gst_caps_replace (&GST_RPAD_EXPLICIT_CAPS (pad), NULL); } else if (GST_IS_GHOST_PAD (pad)) { g_object_set (pad, "real-pad", NULL, NULL); } + GST_LOCK (element); /* remove it from the list */ - element->pads = g_list_remove (element->pads, pad); - element->numpads--; switch (gst_pad_get_direction (pad)) { case GST_PAD_SRC: + element->srcpads = g_list_remove (element->srcpads, pad); element->numsrcpads--; break; case GST_PAD_SINK: + element->sinkpads = g_list_remove (element->sinkpads, pad); element->numsinkpads--; break; default: /* can happen for ghost pads */ break; } + element->pads = g_list_remove (element->pads, pad); + element->numpads--; + element->pads_cookie++; + GST_UNLOCK (element); g_signal_emit (G_OBJECT (element), gst_element_signals[PAD_REMOVED], 0, pad); gst_object_unparent (GST_OBJECT (pad)); -} -/** - * gst_element_remove_ghost_pad: - * @element: a #GstElement to remove the ghost pad from. - * @pad: ghost #GstPad to remove. - * - * Removes a ghost pad from an element. Deprecated, use gst_element_remove_pad() - * instead. - */ -void -gst_element_remove_ghost_pad (GstElement * element, GstPad * pad) -{ - g_return_if_fail (GST_IS_ELEMENT (element)); - g_return_if_fail (GST_IS_GHOST_PAD (pad)); + return TRUE; - g_warning ("gst_element_remove_ghost_pad is deprecated.\n" - "Use gst_element_remove_pad instead."); - - gst_element_remove_pad (element, pad); +not_our_pad: + { + /* FIXME, locking order? */ + GST_LOCK (element); + g_critical ("Padname %s:%s does not belong to element %s when removing", + GST_ELEMENT_NAME (GST_PAD_PARENT (pad)), GST_PAD_NAME (pad), + GST_ELEMENT_NAME (element)); + GST_UNLOCK (element); + GST_UNLOCK (pad); + g_free (pad_name); + return FALSE; + } } /** @@ -1310,6 +1399,8 @@ gst_element_remove_ghost_pad (GstElement * element, GstPad * pad) * pads have been added by the element itself. Elements with GST_PAD_SOMETIMES * pad templates use this in combination with autopluggers to figure out that * the element is done initializing its pads. + * + * MT safe. */ void gst_element_no_more_pads (GstElement * element) @@ -1319,30 +1410,16 @@ gst_element_no_more_pads (GstElement * element) g_signal_emit (element, gst_element_signals[NO_MORE_PADS], 0); } -/** - * gst_element_get_pad: - * @element: a #GstElement. - * @name: the name of the pad to retrieve. - * - * Retrieves a pad from @element by name. Tries gst_element_get_static_pad() - * first, then gst_element_get_request_pad(). - * - * Returns: the #GstPad if found, otherwise %NULL. - */ -GstPad * -gst_element_get_pad (GstElement * element, const gchar * name) +static gint +pad_compare_name (GstPad * pad1, const gchar * name) { - GstPad *pad; + gint result; - g_return_val_if_fail (element != NULL, NULL); - g_return_val_if_fail (GST_IS_ELEMENT (element), NULL); - g_return_val_if_fail (name != NULL, NULL); + GST_LOCK (pad1); + result = strcmp (GST_PAD_NAME (pad1), name); + GST_UNLOCK (pad1); - pad = gst_element_get_static_pad (element, name); - if (!pad) - pad = gst_element_get_request_pad (element, name); - - return pad; + return result; } /** @@ -1353,33 +1430,53 @@ gst_element_get_pad (GstElement * element, const gchar * name) * Retrieves a pad from @element by name. This version only retrieves * already-existing (i.e. 'static') pads. * - * Returns: the requested #GstPad if found, otherwise NULL. + * Returns: the requested #GstPad if found, otherwise NULL. unref after + * usage. + * + * MT safe. */ GstPad * gst_element_get_static_pad (GstElement * element, const gchar * name) { - GList *walk; + GList *find; + GstPad *result = NULL; - g_return_val_if_fail (element != NULL, NULL); g_return_val_if_fail (GST_IS_ELEMENT (element), NULL); g_return_val_if_fail (name != NULL, NULL); - walk = element->pads; - while (walk) { - GstPad *pad; - - pad = GST_PAD (walk->data); - if (strcmp (GST_PAD_NAME (pad), name) == 0) { - GST_CAT_INFO (GST_CAT_ELEMENT_PADS, "found pad %s:%s", - GST_DEBUG_PAD_NAME (pad)); - return pad; - } - walk = g_list_next (walk); + GST_LOCK (element); + find = + g_list_find_custom (element->pads, name, (GCompareFunc) pad_compare_name); + if (find) { + result = GST_PAD_CAST (find->data); + gst_object_ref (GST_OBJECT_CAST (result)); } - GST_CAT_INFO (GST_CAT_ELEMENT_PADS, "no such pad '%s' in element \"%s\"", - name, GST_OBJECT_NAME (element)); - return NULL; + if (result == NULL) { + GST_CAT_INFO (GST_CAT_ELEMENT_PADS, "no such pad '%s' in element \"%s\"", + name, GST_ELEMENT_NAME (element)); + } else { + GST_CAT_INFO (GST_CAT_ELEMENT_PADS, "found pad %s:%s", + GST_ELEMENT_NAME (element), name); + } + GST_UNLOCK (element); + + return result; +} + +static GstPad * +gst_element_request_pad (GstElement * element, GstPadTemplate * templ, + const gchar * name) +{ + GstPad *newpad = NULL; + GstElementClass *oclass; + + oclass = GST_ELEMENT_GET_CLASS (element); + + if (oclass->request_new_pad) + newpad = (oclass->request_new_pad) (element, templ, name); + + return newpad; } /** @@ -1403,18 +1500,20 @@ gst_element_get_request_pad (GstElement * element, const gchar * name) gint n; const gchar *data; gchar *str, *endptr = NULL; + GstElementClass *class; - g_return_val_if_fail (element != NULL, NULL); g_return_val_if_fail (GST_IS_ELEMENT (element), NULL); g_return_val_if_fail (name != NULL, NULL); + class = GST_ELEMENT_GET_CLASS (element); + if (strstr (name, "%")) { - templ = gst_element_get_pad_template (element, name); + templ = gst_element_class_get_pad_template (class, name); req_name = NULL; if (templ) templ_found = TRUE; } else { - list = gst_element_get_pad_template_list (element); + list = gst_element_class_get_pad_template_list (class); while (!templ_found && list) { templ = (GstPadTemplate *) list->data; if (templ->presence == GST_PAD_REQUEST) { @@ -1457,24 +1556,68 @@ gst_element_get_request_pad (GstElement * element, const gchar * name) } /** - * gst_element_get_pad_list: - * @element: a #GstElement to get pads of. + * gst_element_get_pad: + * @element: a #GstElement. + * @name: the name of the pad to retrieve. * - * Retrieves a list of @element's pads. The list must not be modified by the - * calling code. + * Retrieves a pad from @element by name. Tries gst_element_get_static_pad() + * first, then gst_element_get_request_pad(). * - * Returns: the #GList of pads. + * Returns: the #GstPad if found, otherwise %NULL. Unref after usage. */ -const GList * -gst_element_get_pad_list (GstElement * element) +GstPad * +gst_element_get_pad (GstElement * element, const gchar * name) { - g_return_val_if_fail (element != NULL, NULL); + GstPad *pad; + + g_return_val_if_fail (GST_IS_ELEMENT (element), NULL); + g_return_val_if_fail (name != NULL, NULL); + + pad = gst_element_get_static_pad (element, name); + if (!pad) + pad = gst_element_get_request_pad (element, name); + + return pad; +} + +GstIteratorItem +iterate_pad (GstIterator * it, GstPad * pad) +{ + gst_object_ref (GST_OBJECT_CAST (pad)); + return GST_ITERATOR_ITEM_PASS; +} + +/** + * gst_element_iterate_pads: + * @element: a #GstElement to iterate pads of. + * + * Retrieves an iterattor of @element's pads. + * + * Returns: the #GstIterator of pads. + * + * MT safe. + */ +GstIterator * +gst_element_iterate_pads (GstElement * element) +{ + GstIterator *result; + g_return_val_if_fail (GST_IS_ELEMENT (element), NULL); - /* return the list of pads */ - return element->pads; + GST_LOCK (element); + gst_object_ref (GST_OBJECT (element)); + result = gst_iterator_new_list (GST_GET_LOCK (element), + &element->pads_cookie, + &element->pads, + element, + (GstIteratorItemFunction) iterate_pad, + (GstIteratorDisposeFunction) gst_object_unref); + GST_UNLOCK (element); + + return result; } + /** * gst_element_class_add_pad_template: * @klass: the #GstElementClass to add the pad template to. @@ -1532,7 +1675,6 @@ gst_element_class_set_details (GstElementClass * klass, GList * gst_element_class_get_pad_template_list (GstElementClass * element_class) { - g_return_val_if_fail (element_class != NULL, NULL); g_return_val_if_fail (GST_IS_ELEMENT_CLASS (element_class), NULL); return element_class->padtemplates; @@ -1557,7 +1699,6 @@ gst_element_class_get_pad_template (GstElementClass * element_class, { GList *padlist; - g_return_val_if_fail (element_class != NULL, NULL); g_return_val_if_fail (GST_IS_ELEMENT_CLASS (element_class), NULL); g_return_val_if_fail (name != NULL, NULL); @@ -1617,634 +1758,6 @@ gst_element_get_pad_template (GstElement * element, const gchar * name) name); } -/** - * gst_element_get_compatible_pad_template: - * @element: a #GstElement to get a compatible pad template for. - * @compattempl: the #GstPadTemplate to find a compatible template for. - * - * Retrieves a pad template from @element that is compatible with @compattempl. - * Pads from compatible templates can be linked together. - * - * Returns: a compatible #GstPadTemplate, or NULL if none was found. No - * unreferencing is necessary. - */ -GstPadTemplate * -gst_element_get_compatible_pad_template (GstElement * element, - GstPadTemplate * compattempl) -{ - GstPadTemplate *newtempl = NULL; - GList *padlist; - - g_return_val_if_fail (element != NULL, NULL); - g_return_val_if_fail (GST_IS_ELEMENT (element), NULL); - g_return_val_if_fail (compattempl != NULL, NULL); - - padlist = gst_element_get_pad_template_list (element); - - GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, - "Looking for a suitable pad template in %s out of %d templates...", - GST_ELEMENT_NAME (element), g_list_length (padlist)); - - while (padlist) { - GstPadTemplate *padtempl = (GstPadTemplate *) padlist->data; - GstCaps *intersection; - - /* Ignore name - * Ignore presence - * Check direction (must be opposite) - * Check caps - */ - GST_CAT_LOG (GST_CAT_CAPS, - "checking pad template %s", padtempl->name_template); - if (padtempl->direction != compattempl->direction) { - gboolean is_empty; - - GST_CAT_DEBUG (GST_CAT_CAPS, - "compatible direction: found %s pad template \"%s\"", - padtempl->direction == GST_PAD_SRC ? "src" : "sink", - padtempl->name_template); - - intersection = gst_caps_intersect (GST_PAD_TEMPLATE_CAPS (compattempl), - GST_PAD_TEMPLATE_CAPS (padtempl)); - - is_empty = gst_caps_is_empty (intersection); - - GST_CAT_DEBUG (GST_CAT_CAPS, "caps are %scompatible", - is_empty ? "not " : ""); - - if (!is_empty) - newtempl = padtempl; - gst_caps_free (intersection); - if (newtempl) - break; - } - - padlist = g_list_next (padlist); - } - if (newtempl) - GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, - "Returning new pad template %p", newtempl); - else - GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "No compatible pad template found"); - - return newtempl; -} - -/** - * gst_element_get_pad_from_template: - * @element: a #GstElement. - * @templ: a #GstPadTemplate belonging to @element. - * - * Gets a pad from @element described by @templ. If the presence of @templ is - * #GST_PAD_REQUEST, requests a new pad. Can return %NULL for #GST_PAD_SOMETIMES - * templates. - * - * Returns: the #GstPad, or NULL if one could not be found or created. - */ -static GstPad * -gst_element_get_pad_from_template (GstElement * element, GstPadTemplate * templ) -{ - GstPad *ret = NULL; - GstPadPresence presence; - - /* If this function is ever exported, we need check the validity of `element' - * and `templ', and to make sure the template actually belongs to the - * element. */ - - presence = GST_PAD_TEMPLATE_PRESENCE (templ); - - switch (presence) { - case GST_PAD_ALWAYS: - case GST_PAD_SOMETIMES: - ret = gst_element_get_static_pad (element, templ->name_template); - if (!ret && presence == GST_PAD_ALWAYS) - g_warning - ("Element %s has an ALWAYS template %s, but no pad of the same name", - GST_OBJECT_NAME (element), templ->name_template); - break; - - case GST_PAD_REQUEST: - ret = gst_element_request_pad (element, templ, NULL); - break; - } - - return ret; -} - -/** - * gst_element_request_compatible_pad: - * @element: a #GstElement. - * @templ: the #GstPadTemplate to which the new pad should be able to link. - * - * Requests a pad from @element. The returned pad should be unlinked and - * compatible with @templ. Might return an existing pad, or request a new one. - * - * Returns: a #GstPad, or %NULL if one could not be found or created. - */ -GstPad * -gst_element_request_compatible_pad (GstElement * element, - GstPadTemplate * templ) -{ - GstPadTemplate *templ_new; - GstPad *pad = NULL; - - g_return_val_if_fail (GST_IS_ELEMENT (element), NULL); - g_return_val_if_fail (GST_IS_PAD_TEMPLATE (templ), NULL); - - /* FIXME: should really loop through the templates, testing each for - compatibility and pad availability. */ - templ_new = gst_element_get_compatible_pad_template (element, templ); - if (templ_new) - pad = gst_element_get_pad_from_template (element, templ_new); - - /* This can happen for non-request pads. No need to unref. */ - if (pad && GST_PAD_PEER (pad)) - pad = NULL; - - return pad; -} - -/** - * gst_element_get_compatible_pad_filtered: - * @element: a #GstElement in which the pad should be found. - * @pad: the #GstPad to find a compatible one for. - * @filtercaps: the #GstCaps to use as a filter. - * - * Looks for an unlinked pad to which the given pad can link. It is not - * guaranteed that linking the pads will work, though it should work in most - * cases. - * - * Returns: the #GstPad to which a link can be made, or %NULL if one cannot be - * found. - */ -GstPad * -gst_element_get_compatible_pad_filtered (GstElement * element, GstPad * pad, - const GstCaps * filtercaps) -{ - const GList *pads; - GstPadTemplate *templ; - GstCaps *templcaps; - GstPad *foundpad = NULL; - - g_return_val_if_fail (GST_IS_ELEMENT (element), NULL); - g_return_val_if_fail (GST_IS_PAD (pad), NULL); - - GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, - "finding pad in %s compatible with %s:%s and filter %" GST_PTR_FORMAT, - GST_ELEMENT_NAME (element), GST_DEBUG_PAD_NAME (pad), filtercaps); - - /* let's use the real pad */ - pad = (GstPad *) GST_PAD_REALIZE (pad); - g_return_val_if_fail (pad != NULL, NULL); - g_return_val_if_fail (GST_RPAD_PEER (pad) == NULL, NULL); - - /* try to get an existing unlinked pad */ - pads = gst_element_get_pad_list (element); - while (pads) { - GstPad *current = GST_PAD (pads->data); - - GST_CAT_LOG (GST_CAT_ELEMENT_PADS, "examing pad %s:%s", - GST_DEBUG_PAD_NAME (current)); - if (GST_PAD_PEER (current) == NULL && - gst_pad_can_link_filtered (pad, current, filtercaps)) { - GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, - "found existing unlinked pad %s:%s", GST_DEBUG_PAD_NAME (current)); - return current; - } - pads = g_list_next (pads); - } - - /* try to create a new one */ - /* requesting is a little crazy, we need a template. Let's create one */ - templcaps = gst_pad_get_caps (pad); - if (filtercaps != NULL) { - GstCaps *temp; - - temp = gst_caps_intersect (filtercaps, templcaps); - gst_caps_free (templcaps); - templcaps = temp; - } - - templ = gst_pad_template_new ((gchar *) GST_PAD_NAME (pad), - GST_PAD_DIRECTION (pad), GST_PAD_ALWAYS, templcaps); - foundpad = gst_element_request_compatible_pad (element, templ); - gst_object_unref (GST_OBJECT (templ)); - - if (foundpad) { - GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, - "found existing request pad %s:%s", GST_DEBUG_PAD_NAME (foundpad)); - return foundpad; - } - - GST_CAT_INFO_OBJECT (GST_CAT_ELEMENT_PADS, element, - "Could not find a compatible pad to link to %s:%s", - GST_DEBUG_PAD_NAME (pad)); - return NULL; -} - -/** - * gst_element_get_compatible_pad: - * @element: a #GstElement in which the pad should be found. - * @pad: the #GstPad to find a compatible one for. - * - * Looks for an unlinked pad to which the given pad can link to. - * It is not guaranteed that linking the pads will work, though - * it should work in most cases. - * - * Returns: the #GstPad to which a link can be made, or %NULL if one - * could not be found. - */ -GstPad * -gst_element_get_compatible_pad (GstElement * element, GstPad * pad) -{ - return gst_element_get_compatible_pad_filtered (element, pad, NULL); -} - -/** - * gst_element_link_pads_filtered: - * @src: a #GstElement containing the source pad. - * @srcpadname: the name of the #GstPad in source element or NULL for any pad. - * @dest: the #GstElement containing the destination pad. - * @destpadname: the name of the #GstPad in destination element or NULL for any pad. - * @filtercaps: the #GstCaps to use as a filter. - * - * Links the two named pads of the source and destination elements. - * Side effect is that if one of the pads has no parent, it becomes a - * child of the parent of the other element. If they have different - * parents, the link fails. - * - * Returns: TRUE if the pads could be linked, FALSE otherwise. - */ -gboolean -gst_element_link_pads_filtered (GstElement * src, const gchar * srcpadname, - GstElement * dest, const gchar * destpadname, const GstCaps * filtercaps) -{ - const GList *srcpads, *destpads, *srctempls, *desttempls, *l; - GstPad *srcpad, *destpad; - GstPadTemplate *srctempl, *desttempl; - - /* checks */ - g_return_val_if_fail (GST_IS_ELEMENT (src), FALSE); - g_return_val_if_fail (GST_IS_ELEMENT (dest), FALSE); - - GST_CAT_INFO (GST_CAT_ELEMENT_PADS, - "trying to link element %s:%s to element %s:%s", GST_ELEMENT_NAME (src), - srcpadname ? srcpadname : "(any)", GST_ELEMENT_NAME (dest), - destpadname ? destpadname : "(any)"); - - /* now get the pads we're trying to link and a list of all remaining pads */ - if (srcpadname) { - srcpad = gst_element_get_pad (src, srcpadname); - if (!srcpad) { - GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "no pad %s:%s", - GST_ELEMENT_NAME (src), srcpadname); - return FALSE; - } else { - if (!(GST_PAD_DIRECTION (srcpad) == GST_PAD_SRC)) { - GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "pad %s:%s is no src pad", - GST_DEBUG_PAD_NAME (srcpad)); - return FALSE; - } - if (GST_PAD_PEER (srcpad) != NULL) { - GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "pad %s:%s is already linked", - GST_DEBUG_PAD_NAME (srcpad)); - return FALSE; - } - } - srcpads = NULL; - } else { - srcpads = gst_element_get_pad_list (src); - srcpad = srcpads ? (GstPad *) GST_PAD_REALIZE (srcpads->data) : NULL; - } - if (destpadname) { - destpad = gst_element_get_pad (dest, destpadname); - if (!destpad) { - GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "no pad %s:%s", - GST_ELEMENT_NAME (dest), destpadname); - return FALSE; - } else { - if (!(GST_PAD_DIRECTION (destpad) == GST_PAD_SINK)) { - GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "pad %s:%s is no sink pad", - GST_DEBUG_PAD_NAME (destpad)); - return FALSE; - } - if (GST_PAD_PEER (destpad) != NULL) { - GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "pad %s:%s is already linked", - GST_DEBUG_PAD_NAME (destpad)); - return FALSE; - } - } - destpads = NULL; - } else { - destpads = gst_element_get_pad_list (dest); - destpad = destpads ? (GstPad *) GST_PAD_REALIZE (destpads->data) : NULL; - } - - if (srcpadname && destpadname) { - /* two explicitly specified pads */ - return gst_pad_link_filtered (srcpad, destpad, filtercaps); - } - if (srcpad) { - /* loop through the allowed pads in the source, trying to find a - * compatible destination pad */ - GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, - "looping through allowed src and dest pads"); - do { - GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "trying src pad %s:%s", - GST_DEBUG_PAD_NAME (srcpad)); - if ((GST_PAD_DIRECTION (srcpad) == GST_PAD_SRC) && - (GST_PAD_PEER (srcpad) == NULL)) { - GstPad *temp = destpadname ? destpad : - gst_element_get_compatible_pad_filtered (dest, srcpad, - filtercaps); - - if (temp && gst_pad_link_filtered (srcpad, temp, filtercaps)) { - GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "linked pad %s:%s to pad %s:%s", - GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (temp)); - return TRUE; - } - } - /* find a better way for this mess */ - if (srcpads) { - srcpads = g_list_next (srcpads); - if (srcpads) - srcpad = (GstPad *) GST_PAD_REALIZE (srcpads->data); - } - } while (srcpads); - } - if (srcpadname) { - GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "no link possible from %s:%s to %s", - GST_DEBUG_PAD_NAME (srcpad), GST_ELEMENT_NAME (dest)); - return FALSE; - } - if (destpad) { - /* loop through the existing pads in the destination */ - do { - GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "trying dest pad %s:%s", - GST_DEBUG_PAD_NAME (destpad)); - if ((GST_PAD_DIRECTION (destpad) == GST_PAD_SINK) && - (GST_PAD_PEER (destpad) == NULL)) { - GstPad *temp = gst_element_get_compatible_pad_filtered (src, destpad, - filtercaps); - - if (temp && gst_pad_link_filtered (temp, destpad, filtercaps)) { - GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "linked pad %s:%s to pad %s:%s", - GST_DEBUG_PAD_NAME (temp), GST_DEBUG_PAD_NAME (destpad)); - return TRUE; - } - } - if (destpads) { - destpads = g_list_next (destpads); - if (destpads) - destpad = (GstPad *) GST_PAD_REALIZE (destpads->data); - } - } while (destpads); - } - if (destpadname) { - GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "no link possible from %s to %s:%s", - GST_ELEMENT_NAME (src), GST_DEBUG_PAD_NAME (destpad)); - return FALSE; - } - - GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, - "we might have request pads on both sides, checking..."); - srctempls = gst_element_get_pad_template_list (src); - desttempls = gst_element_get_pad_template_list (dest); - - if (srctempls && desttempls) { - while (srctempls) { - srctempl = (GstPadTemplate *) srctempls->data; - if (srctempl->presence == GST_PAD_REQUEST) { - for (l = desttempls; l; l = l->next) { - desttempl = (GstPadTemplate *) l->data; - if (desttempl->presence == GST_PAD_REQUEST && - desttempl->direction != srctempl->direction) { - if (gst_caps_is_always_compatible (gst_pad_template_get_caps - (srctempl), gst_pad_template_get_caps (desttempl))) { - srcpad = - gst_element_get_request_pad (src, srctempl->name_template); - destpad = - gst_element_get_request_pad (dest, desttempl->name_template); - if (gst_pad_link_filtered (srcpad, destpad, filtercaps)) { - GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, - "linked pad %s:%s to pad %s:%s", - GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (destpad)); - return TRUE; - } - /* it failed, so we release the request pads */ - gst_element_release_request_pad (src, srcpad); - gst_element_release_request_pad (dest, destpad); - } - } - } - } - srctempls = srctempls->next; - } - } - - GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "no link possible from %s to %s", - GST_ELEMENT_NAME (src), GST_ELEMENT_NAME (dest)); - return FALSE; -} - -/** - * gst_element_link_filtered: - * @src: a #GstElement containing the source pad. - * @dest: the #GstElement containing the destination pad. - * @filtercaps: the #GstCaps to use as a filter. - * - * Links @src to @dest, filtered by @filtercaps. The link must be from source to - * destination; the other direction will not be tried. The function looks for - * existing pads that aren't linked yet. It will request new pads if necessary. - * If multiple links are possible, only one is established. - * - * Returns: TRUE if the elements could be linked, FALSE otherwise. - */ -gboolean -gst_element_link_filtered (GstElement * src, GstElement * dest, - const GstCaps * filtercaps) -{ - return gst_element_link_pads_filtered (src, NULL, dest, NULL, filtercaps); -} - -/** - * gst_element_link_many: - * @element_1: the first #GstElement in the link chain. - * @element_2: the second #GstElement in the link chain. - * @...: the NULL-terminated list of elements to link in order. - * - * Chain together a series of elements. Uses gst_element_link(). - * - * Returns: TRUE on success, FALSE otherwise. - */ -gboolean -gst_element_link_many (GstElement * element_1, GstElement * element_2, ...) -{ - va_list args; - - g_return_val_if_fail (GST_IS_ELEMENT (element_1), FALSE); - g_return_val_if_fail (GST_IS_ELEMENT (element_2), FALSE); - - va_start (args, element_2); - - while (element_2) { - if (!gst_element_link (element_1, element_2)) - return FALSE; - - element_1 = element_2; - element_2 = va_arg (args, GstElement *); - } - - va_end (args); - - return TRUE; -} - -/** - * gst_element_link: - * @src: a #GstElement containing the source pad. - * @dest: the #GstElement containing the destination pad. - * - * Links @src to @dest with no filter caps. See gst_element_link_filtered() for - * more information. - * - * Returns: TRUE if the elements could be linked, FALSE otherwise. - */ -gboolean -gst_element_link (GstElement * src, GstElement * dest) -{ - return gst_element_link_pads_filtered (src, NULL, dest, NULL, NULL); -} - -/** - * gst_element_link_pads: - * @src: a #GstElement containing the source pad. - * @srcpadname: the name of the #GstPad in the source element. - * @dest: the #GstElement containing the destination pad. - * @destpadname: the name of the #GstPad in destination element. - * - * Links the two named pads of the source and destination elements. - * Side effect is that if one of the pads has no parent, it becomes a - * child of the parent of the other element. If they have different - * parents, the link fails. - * - * Returns: TRUE if the pads could be linked, FALSE otherwise. - */ -gboolean -gst_element_link_pads (GstElement * src, const gchar * srcpadname, - GstElement * dest, const gchar * destpadname) -{ - return gst_element_link_pads_filtered (src, srcpadname, dest, destpadname, - NULL); -} - -/** - * gst_element_unlink_pads: - * @src: a #GstElement containing the source pad. - * @srcpadname: the name of the #GstPad in source element. - * @dest: a #GstElement containing the destination pad. - * @destpadname: the name of the #GstPad in destination element. - * - * Unlinks the two named pads of the source and destination elements. - */ -void -gst_element_unlink_pads (GstElement * src, const gchar * srcpadname, - GstElement * dest, const gchar * destpadname) -{ - GstPad *srcpad, *destpad; - - g_return_if_fail (src != NULL); - g_return_if_fail (GST_IS_ELEMENT (src)); - g_return_if_fail (srcpadname != NULL); - g_return_if_fail (dest != NULL); - g_return_if_fail (GST_IS_ELEMENT (dest)); - g_return_if_fail (destpadname != NULL); - - /* obtain the pads requested */ - srcpad = gst_element_get_pad (src, srcpadname); - if (srcpad == NULL) { - GST_WARNING_OBJECT (src, "source element has no pad \"%s\"", srcpadname); - return; - } - destpad = gst_element_get_pad (dest, destpadname); - if (srcpad == NULL) { - GST_WARNING_OBJECT (dest, "destination element has no pad \"%s\"", - destpadname); - return; - } - - /* we're satisified they can be unlinked, let's do it */ - gst_pad_unlink (srcpad, destpad); -} - -/** - * gst_element_unlink_many: - * @element_1: the first #GstElement in the link chain. - * @element_2: the second #GstElement in the link chain. - * @...: the NULL-terminated list of elements to unlink in order. - * - * Unlinks a series of elements. Uses gst_element_unlink(). - */ -void -gst_element_unlink_many (GstElement * element_1, GstElement * element_2, ...) -{ - va_list args; - - g_return_if_fail (element_1 != NULL && element_2 != NULL); - g_return_if_fail (GST_IS_ELEMENT (element_1) && GST_IS_ELEMENT (element_2)); - - va_start (args, element_2); - - while (element_2) { - gst_element_unlink (element_1, element_2); - - element_1 = element_2; - element_2 = va_arg (args, GstElement *); - } - - va_end (args); -} - -/** - * gst_element_unlink: - * @src: the source #GstElement to unlink. - * @dest: the sink #GstElement to unlink. - * - * Unlinks all source pads of the source element with all sink pads - * of the sink element to which they are linked. - */ -void -gst_element_unlink (GstElement * src, GstElement * dest) -{ - const GList *srcpads; - GstPad *pad; - - g_return_if_fail (GST_IS_ELEMENT (src)); - g_return_if_fail (GST_IS_ELEMENT (dest)); - - GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "unlinking \"%s\" and \"%s\"", - GST_ELEMENT_NAME (src), GST_ELEMENT_NAME (dest)); - - srcpads = gst_element_get_pad_list (src); - - while (srcpads) { - pad = GST_PAD (srcpads->data); - - /* we only care about real src pads */ - if (GST_IS_REAL_PAD (pad) && GST_PAD_IS_SRC (pad)) { - GstPad *peerpad = GST_PAD_PEER (pad); - - /* see if the pad is connected and is really a pad - * of dest */ - if (peerpad && (GST_OBJECT_PARENT (peerpad) == (GstObject *) dest)) { - gst_pad_unlink (pad, peerpad); - } - } - - srcpads = g_list_next (srcpads); - } -} - static void gst_element_error_func (GstElement * element, GstElement * source, GError * error, gchar * debug) @@ -2271,30 +1784,46 @@ gst_element_error_func (GstElement * element, GstElement * source, static GstPad * gst_element_get_random_pad (GstElement * element, GstPadDirection dir) { - GList *pads = element->pads; + GstPad *result = NULL; + GList *pads; GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "getting a random pad"); - while (pads) { + + GST_LOCK (element); + switch (dir) { + case GST_PAD_SRC: + pads = element->srcpads; + break; + case GST_PAD_SINK: + pads = element->sinkpads; + break; + default: + g_warning ("unknown pad direction"); + return NULL; + } + for (; pads; pads = g_list_next (pads)) { GstPad *pad = GST_PAD (pads->data); + GST_LOCK (pad); GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "checking pad %s:%s", GST_DEBUG_PAD_NAME (pad)); - if (GST_PAD_DIRECTION (pad) == dir) { - if (GST_PAD_IS_LINKED (pad)) { - return pad; - } else { - GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "pad %s:%s is not linked", - GST_DEBUG_PAD_NAME (pad)); - } + if (GST_PAD_IS_LINKED (pad)) { + GST_UNLOCK (pad); + result = pad; + break; } else { - GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "pad %s:%s is in wrong direction", + GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "pad %s:%s is not linked", GST_DEBUG_PAD_NAME (pad)); } - - pads = g_list_next (pads); + GST_UNLOCK (pad); } - return NULL; + if (result) + gst_object_ref (GST_OBJECT (result)); + + GST_UNLOCK (element); + + return result; } /** @@ -2305,27 +1834,38 @@ gst_element_get_random_pad (GstElement * element, GstPadDirection dir) * If the element doesn't implement an event masks function, * the query will be forwarded to a random linked sink pad. * - * Returns: An array of #GstEventMask elements. + * Returns: An array of #GstEventMask elements. The array + * cannot be modified or freed. + * + * MT safe. */ const GstEventMask * gst_element_get_event_masks (GstElement * element) { GstElementClass *oclass; + const GstEventMask *result = NULL; g_return_val_if_fail (GST_IS_ELEMENT (element), NULL); oclass = GST_ELEMENT_GET_CLASS (element); - if (oclass->get_event_masks) - return oclass->get_event_masks (element); - else { + if (oclass->get_event_masks) { + result = oclass->get_event_masks (element); + } else { GstPad *pad = gst_element_get_random_pad (element, GST_PAD_SINK); - if (pad) - return gst_pad_get_event_masks (GST_PAD_PEER (pad)); + if (pad) { + GstPad *peer = gst_pad_get_peer (pad); + + if (peer) { + result = gst_pad_get_event_masks (peer); + gst_object_unref (GST_OBJECT (peer)); + } + gst_object_unref (GST_OBJECT (pad)); + } } - return NULL; + return result; } /** @@ -2335,34 +1875,47 @@ gst_element_get_event_masks (GstElement * element) * * Sends an event to an element. If the element doesn't * implement an event handler, the event will be forwarded - * to a random sink pad. + * to a random sink pad. This function takes owership of the + * provided event so you should _ref it if you want to reuse + * the event after this call. * * Returns: TRUE if the event was handled. + * + * MT safe. */ gboolean gst_element_send_event (GstElement * element, GstEvent * event) { GstElementClass *oclass; + gboolean result = FALSE; g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE); g_return_val_if_fail (event != NULL, FALSE); oclass = GST_ELEMENT_GET_CLASS (element); - if (oclass->send_event) - return oclass->send_event (element, event); - else { + if (oclass->send_event) { + result = oclass->send_event (element, event); + } else { GstPad *pad = gst_element_get_random_pad (element, GST_PAD_SINK); if (pad) { - GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "sending event to random pad %s:%s", - GST_DEBUG_PAD_NAME (pad)); - return gst_pad_send_event (GST_PAD_PEER (pad), event); + GstPad *peer = gst_pad_get_peer (pad); + + if (peer) { + GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, + "sending event to random pad %s:%s", GST_DEBUG_PAD_NAME (pad)); + + result = gst_pad_send_event (peer, event); + gst_object_unref (GST_OBJECT (peer)); + } + gst_object_unref (GST_OBJECT (pad)); } } GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "can't send event on element %s", GST_ELEMENT_NAME (element)); - return FALSE; + + return result; } /** @@ -2374,13 +1927,18 @@ gst_element_send_event (GstElement * element, GstEvent * event) * Sends a seek event to an element. * * Returns: TRUE if the event was handled. + * + * MT safe. */ gboolean gst_element_seek (GstElement * element, GstSeekType seek_type, guint64 offset) { GstEvent *event = gst_event_new_seek (seek_type, offset); + gboolean result; - return gst_element_send_event (element, event); + result = gst_element_send_event (element, event); + + return result; } /** @@ -2391,27 +1949,38 @@ gst_element_seek (GstElement * element, GstSeekType seek_type, guint64 offset) * If the element doesn't implement a query types function, * the query will be forwarded to a random sink pad. * - * Returns: An array of #GstQueryType elements. + * Returns: An array of #GstQueryType elements that should not + * be freed or modified. + * + * MT safe. */ const GstQueryType * gst_element_get_query_types (GstElement * element) { GstElementClass *oclass; + const GstQueryType *result = NULL; g_return_val_if_fail (GST_IS_ELEMENT (element), NULL); oclass = GST_ELEMENT_GET_CLASS (element); - if (oclass->get_query_types) - return oclass->get_query_types (element); - else { + if (oclass->get_query_types) { + result = oclass->get_query_types (element); + } else { GstPad *pad = gst_element_get_random_pad (element, GST_PAD_SINK); - if (pad) - return gst_pad_get_query_types (GST_PAD_PEER (pad)); - } + if (pad) { + GstPad *peer = gst_pad_get_peer (pad); - return NULL; + if (peer) { + result = gst_pad_get_query_types (peer); + + gst_object_unref (GST_OBJECT (peer)); + } + gst_object_unref (GST_OBJECT (pad)); + } + } + return result; } /** @@ -2428,12 +1997,15 @@ gst_element_get_query_types (GstElement * element) * forwards the query to a random usable sinkpad of this element. * * Returns: TRUE if the query could be performed. + * + * MT safe. */ gboolean gst_element_query (GstElement * element, GstQueryType type, GstFormat * format, gint64 * value) { GstElementClass *oclass; + gboolean result = FALSE; g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE); g_return_val_if_fail (format != NULL, FALSE); @@ -2441,19 +2013,30 @@ gst_element_query (GstElement * element, GstQueryType type, oclass = GST_ELEMENT_GET_CLASS (element); - if (oclass->query) - return oclass->query (element, type, format, value); - else { + if (oclass->query) { + result = oclass->query (element, type, format, value); + } else { GstPad *pad = gst_element_get_random_pad (element, GST_PAD_SRC); - if (pad) - return gst_pad_query (pad, type, format, value); - pad = gst_element_get_random_pad (element, GST_PAD_SINK); - if (pad) - return gst_pad_query (GST_PAD_PEER (pad), type, format, value); - } + if (pad) { + result = gst_pad_query (pad, type, format, value); - return FALSE; + gst_object_unref (GST_OBJECT (pad)); + } else { + pad = gst_element_get_random_pad (element, GST_PAD_SINK); + if (pad) { + GstPad *peer = gst_pad_get_peer (pad); + + if (peer) { + result = gst_pad_query (peer, type, format, value); + + gst_object_unref (GST_OBJECT (peer)); + } + gst_object_unref (GST_OBJECT (pad)); + } + } + } + return result; } /** @@ -2465,26 +2048,37 @@ gst_element_query (GstElement * element, GstQueryType type, * the query will be forwarded to a random sink pad. * * Returns: An array of #GstFormat elements. + * + * MT safe. */ const GstFormat * gst_element_get_formats (GstElement * element) { GstElementClass *oclass; + const GstFormat *result = NULL; g_return_val_if_fail (GST_IS_ELEMENT (element), NULL); oclass = GST_ELEMENT_GET_CLASS (element); - if (oclass->get_formats) - return oclass->get_formats (element); - else { + if (oclass->get_formats) { + result = oclass->get_formats (element); + } else { GstPad *pad = gst_element_get_random_pad (element, GST_PAD_SINK); - if (pad) - return gst_pad_get_formats (GST_PAD_PEER (pad)); + if (pad) { + GstPad *peer = gst_pad_get_peer (pad); + + if (peer) { + result = gst_pad_get_formats (peer); + + gst_object_unref (GST_OBJECT (peer)); + } + gst_object_unref (GST_OBJECT (pad)); + } } - return NULL; + return result; } /** @@ -2500,6 +2094,8 @@ gst_element_get_formats (GstElement * element) * the query will be forwarded to a random sink pad. * * Returns: TRUE if the conversion could be performed. + * + * MT safe. */ gboolean gst_element_convert (GstElement * element, @@ -2507,6 +2103,7 @@ gst_element_convert (GstElement * element, GstFormat * dest_format, gint64 * dest_value) { GstElementClass *oclass; + gboolean result = FALSE; g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE); g_return_val_if_fail (dest_format != NULL, FALSE); @@ -2519,18 +2116,25 @@ gst_element_convert (GstElement * element, oclass = GST_ELEMENT_GET_CLASS (element); - if (oclass->convert) - return oclass->convert (element, + if (oclass->convert) { + result = oclass->convert (element, src_format, src_value, dest_format, dest_value); - else { + } else { GstPad *pad = gst_element_get_random_pad (element, GST_PAD_SINK); - if (pad) - return gst_pad_convert (GST_PAD_PEER (pad), - src_format, src_value, dest_format, dest_value); - } + if (pad) { + GstPad *peer = gst_pad_get_peer (pad); - return FALSE; + if (peer) { + result = gst_pad_convert (peer, + src_format, src_value, dest_format, dest_value); + + gst_object_unref (GST_OBJECT (peer)); + } + gst_object_unref (GST_OBJECT (pad)); + } + } + return result; } /** @@ -2540,6 +2144,8 @@ gst_element_convert (GstElement * element, * This function is only used internally by the #gst_element_error macro. * * Returns: a newly allocated string, or NULL if the format was NULL or "" + * + * MT safe. */ gchar * _gst_element_error_printf (const gchar * format, ...) @@ -2668,13 +2274,21 @@ void gst_element_error_full * state before changing the state from #GST_STATE_NULL. * * Returns: TRUE, if the element's state is locked. + * + * MT safe. */ gboolean gst_element_is_locked_state (GstElement * element) { + gboolean result = FALSE; + g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE); - return GST_FLAG_IS_SET (element, GST_ELEMENT_LOCKED_STATE) ? TRUE : FALSE; + GST_LOCK (element); + result = GST_FLAG_IS_SET (element, GST_ELEMENT_LOCKED_STATE); + GST_UNLOCK (element); + + return result; } /** @@ -2684,18 +2298,24 @@ gst_element_is_locked_state (GstElement * element) * * Locks the state of an element, so state changes of the parent don't affect * this element anymore. + * + * Returns: TRUE if the state was changed, FALSE if bad params were given or + * the element was already in the correct state. + * + * MT safe. */ -void +gboolean gst_element_set_locked_state (GstElement * element, gboolean locked_state) { gboolean old; - g_return_if_fail (GST_IS_ELEMENT (element)); + g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE); + GST_LOCK (element); old = GST_FLAG_IS_SET (element, GST_ELEMENT_LOCKED_STATE); - if (old == locked_state) - return; + if (G_UNLIKELY (old == locked_state)) + goto was_ok; if (locked_state) { GST_CAT_DEBUG (GST_CAT_STATES, "locking state of element %s", @@ -2706,6 +2326,14 @@ gst_element_set_locked_state (GstElement * element, gboolean locked_state) GST_ELEMENT_NAME (element)); GST_FLAG_UNSET (element, GST_ELEMENT_LOCKED_STATE); } + GST_UNLOCK (element); + + return TRUE; + +was_ok: + GST_UNLOCK (element); + + return FALSE; } /** @@ -2731,6 +2359,7 @@ gst_element_sync_state_with_parent (GstElement * element) gst_element_state_get_name (GST_STATE (element)), GST_ELEMENT_NAME (parent), gst_element_state_get_name (GST_STATE (parent))); + if (gst_element_set_state (element, GST_STATE (parent)) == GST_STATE_FAILURE) { return FALSE; } @@ -2988,7 +2617,7 @@ gst_element_change_state (GstElement * element) switch (old_transition) { case GST_STATE_PLAYING_TO_PAUSED: if (element->clock) { - GstClockTimeDiff time = gst_clock_get_event_time (element->clock); + GstClockTimeDiff time = GST_CLOCK_TIME_NONE; //gst_clock_get_event_time (element->clock); g_assert (time >= element->base_time); element->base_time = time - element->base_time; @@ -3000,7 +2629,7 @@ gst_element_change_state (GstElement * element) case GST_STATE_PAUSED_TO_PLAYING: gst_element_pads_activate (element, TRUE); if (element->clock) { - GstClockTime time = gst_clock_get_event_time (element->clock); + GstClockTime time = GST_CLOCK_TIME_NONE; //gst_clock_get_event_time (element->clock); element->base_time = time - element->base_time; GST_CAT_LOG_OBJECT (GST_CAT_CLOCK, element, "setting base time to %" @@ -3132,6 +2761,14 @@ gst_element_dispose (GObject * object) G_OBJECT_CLASS (parent_class)->dispose (object); } +static void +gst_element_finalize (GObject * object) +{ + //GstElement *element = GST_ELEMENT (object); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + #ifndef GST_DISABLE_LOADSAVE /** * gst_element_save_thyself: @@ -3277,8 +2914,8 @@ gst_element_restore_thyself (GstObject * object, xmlNodePtr self) void gst_element_yield (GstElement * element) { - if (GST_ELEMENT_SCHED (element)) { - gst_scheduler_yield (GST_ELEMENT_SCHED (element), element); + if (GST_ELEMENT_SCHEDULER (element)) { + gst_scheduler_yield (GST_ELEMENT_SCHEDULER (element), element); } } @@ -3295,8 +2932,8 @@ gst_element_yield (GstElement * element) gboolean gst_element_interrupt (GstElement * element) { - if (GST_ELEMENT_SCHED (element)) { - return gst_scheduler_interrupt (GST_ELEMENT_SCHED (element), element); + if (GST_ELEMENT_SCHEDULER (element)) { + return gst_scheduler_interrupt (GST_ELEMENT_SCHEDULER (element), element); } else return TRUE; } @@ -3304,21 +2941,24 @@ gst_element_interrupt (GstElement * element) /** * gst_element_set_scheduler: * @element: a #GstElement to set the scheduler of. - * @sched: the #GstScheduler to set. + * @scheduler: the #GstScheduler to set. * * Sets the scheduler of the element. For internal use only, unless you're - * writing a new bin subclass. + * testing elements. + * + * MT safe. */ void -gst_element_set_scheduler (GstElement * element, GstScheduler * sched) +gst_element_set_scheduler (GstElement * element, GstScheduler * scheduler) { + GstElementClass *oclass; + g_return_if_fail (GST_IS_ELEMENT (element)); - GST_CAT_DEBUG_OBJECT (GST_CAT_PARENTAGE, element, "setting scheduler to %p", - sched); + oclass = GST_ELEMENT_GET_CLASS (element); - gst_object_replace ((GstObject **) & GST_ELEMENT_SCHED (element), - GST_OBJECT (sched)); + if (oclass->set_scheduler) + oclass->set_scheduler (element, scheduler); } /** @@ -3328,13 +2968,21 @@ gst_element_set_scheduler (GstElement * element, GstScheduler * sched) * Returns the scheduler of the element. * * Returns: the element's #GstScheduler. + * + * MT safe. */ GstScheduler * gst_element_get_scheduler (GstElement * element) { - g_return_val_if_fail (GST_IS_ELEMENT (element), NULL); + GstScheduler *result = NULL; - return GST_ELEMENT_SCHED (element); + g_return_val_if_fail (GST_IS_ELEMENT (element), result); + + GST_LOCK (element); + result = GST_ELEMENT_SCHEDULER (element); + GST_UNLOCK (element); + + return result; } /** @@ -3372,8 +3020,9 @@ gst_element_set_loop_function (GstElement * element, /* set the NEW_LOOPFUNC flag so everyone knows to go try again */ GST_FLAG_SET (element, GST_ELEMENT_NEW_LOOPFUNC); - if (GST_ELEMENT_SCHED (element)) { - gst_scheduler_scheduling_change (GST_ELEMENT_SCHED (element), element); + if (GST_ELEMENT_SCHEDULER (element)) { + gst_scheduler_scheduling_change (GST_ELEMENT_SCHEDULER (element), + element); } } } @@ -3493,180 +3142,6 @@ gst_element_set_eos (GstElement * element) } } - -/** - * gst_element_state_get_name: - * @state: a #GstElementState to get the name of. - * - * Gets a string representing the given state. - * - * Returns: a string with the name of the state. - */ -const gchar * -gst_element_state_get_name (GstElementState state) -{ - switch (state) { -#ifdef GST_DEBUG_COLOR - case GST_STATE_VOID_PENDING: - return "NONE_PENDING"; - break; - case GST_STATE_NULL: - return "\033[01;34mNULL\033[00m"; - break; - case GST_STATE_READY: - return "\033[01;31mREADY\033[00m"; - break; - case GST_STATE_PLAYING: - return "\033[01;32mPLAYING\033[00m"; - break; - case GST_STATE_PAUSED: - return "\033[01;33mPAUSED\033[00m"; - break; - default: - /* This is a memory leak */ - return g_strdup_printf ("\033[01;35;41mUNKNOWN!\033[00m(%d)", state); -#else - case GST_STATE_VOID_PENDING: - return "NONE_PENDING"; - break; - case GST_STATE_NULL: - return "NULL"; - break; - case GST_STATE_READY: - return "READY"; - break; - case GST_STATE_PLAYING: - return "PLAYING"; - break; - case GST_STATE_PAUSED: - return "PAUSED"; - break; - default: - return "UNKNOWN!"; -#endif - } - return ""; -} - -static void -gst_element_populate_std_props (GObjectClass * klass, const gchar * prop_name, - guint arg_id, GParamFlags flags) -{ - GQuark prop_id = g_quark_from_string (prop_name); - GParamSpec *pspec; - - static GQuark fd_id = 0; - static GQuark blocksize_id; - static GQuark bytesperread_id; - static GQuark dump_id; - static GQuark filesize_id; - static GQuark mmapsize_id; - static GQuark location_id; - static GQuark offset_id; - static GQuark silent_id; - static GQuark touch_id; - - if (!fd_id) { - fd_id = g_quark_from_static_string ("fd"); - blocksize_id = g_quark_from_static_string ("blocksize"); - bytesperread_id = g_quark_from_static_string ("bytesperread"); - dump_id = g_quark_from_static_string ("dump"); - filesize_id = g_quark_from_static_string ("filesize"); - mmapsize_id = g_quark_from_static_string ("mmapsize"); - location_id = g_quark_from_static_string ("location"); - offset_id = g_quark_from_static_string ("offset"); - silent_id = g_quark_from_static_string ("silent"); - touch_id = g_quark_from_static_string ("touch"); - } - - if (prop_id == fd_id) { - pspec = g_param_spec_int ("fd", "File-descriptor", - "File-descriptor for the file being read", 0, G_MAXINT, 0, flags); - } else if (prop_id == blocksize_id) { - pspec = g_param_spec_ulong ("blocksize", "Block Size", - "Block size to read per buffer", 0, G_MAXULONG, 4096, flags); - - } else if (prop_id == bytesperread_id) { - pspec = g_param_spec_int ("bytesperread", "Bytes per read", - "Number of bytes to read per buffer", G_MININT, G_MAXINT, 0, flags); - - } else if (prop_id == dump_id) { - pspec = g_param_spec_boolean ("dump", "Dump", - "Dump bytes to stdout", FALSE, flags); - - } else if (prop_id == filesize_id) { - pspec = g_param_spec_int64 ("filesize", "File Size", - "Size of the file being read", 0, G_MAXINT64, 0, flags); - - } else if (prop_id == mmapsize_id) { - pspec = g_param_spec_ulong ("mmapsize", "mmap() Block Size", - "Size in bytes of mmap()d regions", 0, G_MAXULONG, 4 * 1048576, flags); - - } else if (prop_id == location_id) { - pspec = g_param_spec_string ("location", "File Location", - "Location of the file to read", NULL, flags); - - } else if (prop_id == offset_id) { - pspec = g_param_spec_int64 ("offset", "File Offset", - "Byte offset of current read pointer", 0, G_MAXINT64, 0, flags); - - } else if (prop_id == silent_id) { - pspec = g_param_spec_boolean ("silent", "Silent", "Don't produce events", - FALSE, flags); - - } else if (prop_id == touch_id) { - pspec = g_param_spec_boolean ("touch", "Touch read data", - "Touch data to force disk read before " "push ()", TRUE, flags); - } else { - g_warning ("Unknown - 'standard' property '%s' id %d from klass %s", - prop_name, arg_id, g_type_name (G_OBJECT_CLASS_TYPE (klass))); - pspec = NULL; - } - - if (pspec) { - g_object_class_install_property (klass, arg_id, pspec); - } -} - -/** - * gst_element_class_install_std_props: - * @klass: the #GstElementClass to add the properties to. - * @first_name: the name of the first property. - * in a NULL terminated - * @...: the id and flags of the first property, followed by - * further 'name', 'id', 'flags' triplets and terminated by NULL. - * - * Adds a list of standardized properties with types to the @klass. - * the id is for the property switch in your get_prop method, and - * the flags determine readability / writeability. - **/ -void -gst_element_class_install_std_props (GstElementClass * klass, - const gchar * first_name, ...) -{ - const char *name; - - va_list args; - - g_return_if_fail (GST_IS_ELEMENT_CLASS (klass)); - - va_start (args, first_name); - - name = first_name; - - while (name) { - int arg_id = va_arg (args, int); - int flags = va_arg (args, int); - - gst_element_populate_std_props ((GObjectClass *) klass, name, arg_id, - flags); - - name = va_arg (args, char *); - } - - va_end (args); -} - /** * gst_element_get_managing_bin: * @element: a #GstElement to get the managing bin of. diff --git a/gst/gstelement.h b/gst/gstelement.h index 5f834aec84..f6621edf0f 100644 --- a/gst/gstelement.h +++ b/gst/gstelement.h @@ -1,6 +1,7 @@ /* GStreamer * Copyright (C) 1999,2000 Erik Walthinsen * 2000 Wim Taymans + * 2004 Wim Taymans * * gstelement.h: Header for GstElement * @@ -32,6 +33,7 @@ #include #include #include +#include #include G_BEGIN_DECLS @@ -39,14 +41,18 @@ G_BEGIN_DECLS typedef struct _GstElementDetails GstElementDetails; /* FIXME: need translatable stuff in here (how handle in registry)? */ -struct _GstElementDetails { +struct _GstElementDetails +{ + /*< public > */ gchar *longname; /* long, english name */ gchar *klass; /* type of element, as hierarchy */ gchar *description; /* insights of one form or another */ gchar *author; /* who wrote this thing? */ + /*< private > */ gpointer _gst_reserved[GST_PADDING]; }; + #define GST_ELEMENT_DETAILS(longname,klass,description,author) \ { longname, klass, description, author, GST_PADDING_INIT } #define GST_IS_ELEMENT_DETAILS(details) ( \ @@ -60,6 +66,7 @@ struct _GstElementDetails { */ #define GST_STATE(obj) (GST_ELEMENT(obj)->current_state) #define GST_STATE_PENDING(obj) (GST_ELEMENT(obj)->pending_state) +#define GST_STATE_ERROR(obj) (GST_ELEMENT(obj)->state_error) /* Note: using 8 bit shift mostly "just because", it leaves us enough room to grow */ #define GST_STATE_TRANSITION(obj) ((GST_STATE(obj)<<8) | GST_STATE_PENDING(obj)) @@ -78,6 +85,7 @@ GST_EXPORT GType _gst_element_type; #define GST_ELEMENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_ELEMENT, GstElementClass)) #define GST_ELEMENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_ELEMENT, GstElement)) #define GST_ELEMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_ELEMENT, GstElementClass)) +#define GST_ELEMENT_CAST(obj) ((GstElement*)(obj)) /* convenience functions */ #ifndef GST_DISABLE_DEPRECATED @@ -98,14 +106,11 @@ GST_EXPORT GType _gst_element_type; #endif #endif -typedef enum { - /* element is complex (for some def.) and generally require a cothread */ - GST_ELEMENT_COMPLEX = GST_OBJECT_FLAG_LAST, +typedef enum +{ /* input and output pads aren't directly coupled to each other examples: queues, multi-output async readers, etc. */ GST_ELEMENT_DECOUPLED, - /* this element should be placed in a thread if at all possible */ - GST_ELEMENT_THREAD_SUGGESTED, /* this element, for some reason, has a loop function that performs * an infinite loop without calls to gst_element_yield () */ GST_ELEMENT_INFINITE_LOOP, @@ -116,6 +121,7 @@ typedef enum { /* use threadsafe property get/set implementation */ GST_ELEMENT_USE_THREADSAFE_PROPERTIES, + /* private flags that can be used by the scheduler */ GST_ELEMENT_SCHEDULER_PRIVATE1, GST_ELEMENT_SCHEDULER_PRIVATE2, @@ -136,7 +142,7 @@ typedef enum { #define GST_ELEMENT_NAME(obj) (GST_OBJECT_NAME(obj)) #define GST_ELEMENT_PARENT(obj) (GST_OBJECT_PARENT(obj)) -#define GST_ELEMENT_SCHED(obj) (((GstElement*)(obj))->sched) +#define GST_ELEMENT_SCHEDULER(obj) (((GstElement*)(obj))->sched) #define GST_ELEMENT_CLOCK(obj) (((GstElement*)(obj))->clock) #define GST_ELEMENT_PADS(obj) ((obj)->pads) @@ -186,11 +192,15 @@ struct _GstElement { GstClock *clock; GstClockTimeDiff base_time; /* NULL/READY: 0 - PAUSED: current time - PLAYING: difference to clock */ - /* element pads */ - guint16 numpads; - guint16 numsrcpads; - guint16 numsinkpads; - GList *pads; + /* element pads, these lists can only be iterated while holding + * the LOCK or checking the cookie after each LOCK. */ + guint16 numpads; + GList *pads; + guint16 numsrcpads; + GList *srcpads; + guint16 numsinkpads; + GList *sinkpads; + guint32 pads_cookie; GMutex *state_mutex; GCond *state_cond; @@ -203,9 +213,11 @@ struct _GstElement { gpointer _gst_reserved[GST_PADDING]; }; -struct _GstElementClass { - GstObjectClass parent_class; +struct _GstElementClass +{ + GstObjectClass parent_class; + /*< public > */ /* the element details */ GstElementDetails details; @@ -213,9 +225,10 @@ struct _GstElementClass { GstElementFactory *elementfactory; /* templates for our pads */ - GList *padtemplates; - gint numpadtemplates; - + GList *padtemplates; + gint numpadtemplates; + guint32 pad_templ_cookie; + /* signal callbacks */ void (*state_change) (GstElement *element, GstElementState old, GstElementState state); void (*new_pad) (GstElement *element, GstPad *pad); @@ -257,6 +270,9 @@ struct _GstElementClass { GstIndex* (*get_index) (GstElement *element); void (*set_index) (GstElement *element, GstIndex *index); + /* scheduler */ + void (*set_scheduler) (GstElement *element, GstScheduler *scheduler); + GstElementStateReturn (*set_state) (GstElement *element, GstElementState state); /* FIXME 0.9: move up to signals */ @@ -327,12 +343,9 @@ gboolean gst_element_interrupt (GstElement *element); void gst_element_set_scheduler (GstElement *element, GstScheduler *sched); GstScheduler* gst_element_get_scheduler (GstElement *element); -void gst_element_add_pad (GstElement *element, GstPad *pad); -void gst_element_remove_pad (GstElement *element, GstPad *pad); +gboolean gst_element_add_pad (GstElement *element, GstPad *pad); +gboolean gst_element_remove_pad (GstElement *element, GstPad *pad); GstPad * gst_element_add_ghost_pad (GstElement *element, GstPad *pad, const gchar *name); -#ifndef GST_DISABLE_DEPRECATED -void gst_element_remove_ghost_pad (GstElement *element, GstPad *pad); -#endif void gst_element_no_more_pads (GstElement *element); GstPad* gst_element_get_pad (GstElement *element, const gchar *name); @@ -340,8 +353,8 @@ GstPad* gst_element_get_static_pad (GstElement *element, const gchar *name); GstPad* gst_element_get_request_pad (GstElement *element, const gchar *name); void gst_element_release_request_pad (GstElement *element, GstPad *pad); -G_CONST_RETURN GList* - gst_element_get_pad_list (GstElement *element); +GstIterator *gst_element_iterate_pads (GstElement * element); + GstPad* gst_element_get_compatible_pad (GstElement *element, GstPad *pad); GstPad* gst_element_get_compatible_pad_filtered (GstElement *element, GstPad *pad, const GstCaps *filtercaps); @@ -396,7 +409,7 @@ void gst_element_error_full (GstElement *element, GQuark domain, gint code, const gchar *file, const gchar *function, gint line); gboolean gst_element_is_locked_state (GstElement *element); -void gst_element_set_locked_state (GstElement *element, gboolean locked_state); +gboolean gst_element_set_locked_state (GstElement *element, gboolean locked_state); gboolean gst_element_sync_state_with_parent (GstElement *element); GstElementState gst_element_get_state (GstElement *element); diff --git a/gst/gstevent.h b/gst/gstevent.h index d10b7f13a3..b476ba972f 100644 --- a/gst/gstevent.h +++ b/gst/gstevent.h @@ -161,6 +161,7 @@ typedef struct struct _GstEvent { GstData data; + /*< public >*/ /* with COW */ GstEventType type; guint64 timestamp; GstObject *src; @@ -189,6 +190,7 @@ struct _GstEvent { } structure; } event_data; + /*< private >*/ gpointer _gst_reserved[GST_PADDING]; }; diff --git a/gst/gstformat.c b/gst/gstformat.c index 559cf07608..975be25238 100644 --- a/gst/gstformat.c +++ b/gst/gstformat.c @@ -1,6 +1,7 @@ /* GStreamer * Copyright (C) 1999,2000 Erik Walthinsen * 2000 Wim Taymans + * 2005 Wim Taymans * * gstformat.c: GstFormat registration * @@ -25,10 +26,11 @@ #include "gst_private.h" #include "gstformat.h" +static GStaticMutex mutex = G_STATIC_MUTEX_INIT; static GList *_gst_formats = NULL; static GHashTable *_nick_to_format = NULL; static GHashTable *_format_to_nick = NULL; -static gint _n_values = 1; /* we start from 1 because 0 reserved for UNDEFINED */ +static guint32 _n_values = 1; /* we start from 1 because 0 reserved for UNDEFINED */ static GstFormatDefinition standard_definitions[] = { {GST_FORMAT_DEFAULT, "default", "Default format for the media type"}, @@ -44,6 +46,7 @@ _gst_format_initialize (void) { GstFormatDefinition *standards = standard_definitions; + g_static_mutex_lock (&mutex); if (_nick_to_format == NULL) { _nick_to_format = g_hash_table_new (g_str_hash, g_str_equal); _format_to_nick = g_hash_table_new (NULL, NULL); @@ -58,6 +61,7 @@ _gst_format_initialize (void) standards++; _n_values++; } + g_static_mutex_unlock (&mutex); } /** @@ -66,10 +70,12 @@ _gst_format_initialize (void) * @description: The description of the new format * * Create a new GstFormat based on the nick or return an - * allrady registered format with that nick + * already registered format with that nick. * * Returns: A new GstFormat or an already registered format * with the same nick. + * + * MT safe. */ GstFormat gst_format_register (const gchar * nick, const gchar * description) @@ -89,11 +95,13 @@ gst_format_register (const gchar * nick, const gchar * description) format->nick = g_strdup (nick); format->description = g_strdup (description); + g_static_mutex_lock (&mutex); g_hash_table_insert (_nick_to_format, format->nick, format); g_hash_table_insert (_format_to_nick, GINT_TO_POINTER (format->value), format); _gst_formats = g_list_append (_gst_formats, format); _n_values++; + g_static_mutex_unlock (&mutex); return format->value; } @@ -114,7 +122,9 @@ gst_format_get_by_nick (const gchar * nick) g_return_val_if_fail (nick != NULL, 0); + g_static_mutex_lock (&mutex); format = g_hash_table_lookup (_nick_to_format, nick); + g_static_mutex_unlock (&mutex); if (format != NULL) return format->value; @@ -154,22 +164,38 @@ gst_formats_contains (const GstFormat * formats, GstFormat format) * Get details about the given format. * * Returns: The #GstFormatDefinition for @format or NULL on failure. + * + * MT safe. */ const GstFormatDefinition * gst_format_get_details (GstFormat format) { - return g_hash_table_lookup (_format_to_nick, GINT_TO_POINTER (format)); + const GstFormatDefinition *result; + + g_static_mutex_lock (&mutex); + result = g_hash_table_lookup (_format_to_nick, GINT_TO_POINTER (format)); + g_static_mutex_unlock (&mutex); + + return result; } /** - * gst_format_get_definitions: + * gst_format_iterate_definitions: * - * Get a list of all the registered formats. + * Iterate all the registered formats. The format definition is read + * only. * - * Returns: A GList of #GstFormatDefinition. + * Returns: A GstIterator of #GstFormatDefinition. */ -const GList * -gst_format_get_definitions (void) +GstIterator * +gst_format_iterate_definitions (void) { - return _gst_formats; + GstIterator *result; + + g_static_mutex_lock (&mutex); + result = gst_iterator_new_list (g_static_mutex_get_mutex (&mutex), + &_n_values, &_gst_formats, NULL, NULL, NULL); + g_static_mutex_unlock (&mutex); + + return result; } diff --git a/gst/gstformat.h b/gst/gstformat.h index eaaed50286..fb91fa9f3e 100644 --- a/gst/gstformat.h +++ b/gst/gstformat.h @@ -27,6 +27,8 @@ #include +#include + G_BEGIN_DECLS typedef enum { @@ -88,8 +90,7 @@ gboolean gst_formats_contains (const GstFormat *formats, GstFormat format); /* query for format details */ G_CONST_RETURN GstFormatDefinition* gst_format_get_details (GstFormat format); -G_CONST_RETURN GList* - gst_format_get_definitions (void); +GstIterator* gst_format_iterate_definitions (void); G_END_DECLS diff --git a/gst/gstindex.c b/gst/gstindex.c index ec28790b23..b5164d28ab 100644 --- a/gst/gstindex.c +++ b/gst/gstindex.c @@ -515,7 +515,8 @@ gst_index_gtype_resolver (GstIndex * index, GstObject * writer, gchar ** writer_string, gpointer data) { if (GST_IS_PAD (writer)) { - GstElement *element = gst_pad_get_parent (GST_PAD (writer)); + GstElement *element = + (GstElement *) gst_object_get_parent (GST_OBJECT (writer)); *writer_string = g_strdup_printf ("%s.%s", g_type_name (G_OBJECT_TYPE (element)), gst_object_get_name (writer)); diff --git a/gst/gstinfo.c b/gst/gstinfo.c index d9ee104d70..4d95390f92 100644 --- a/gst/gstinfo.c +++ b/gst/gstinfo.c @@ -41,6 +41,7 @@ #include "gstpad.h" #include "gstscheduler.h" #include "gst_private.h" +#include "gstatomic_impl.h" #ifdef HAVE_VALGRIND #include #endif diff --git a/gst/gstinfo.h b/gst/gstinfo.h index 3e36994b30..bd5ade2b2c 100644 --- a/gst/gstinfo.h +++ b/gst/gstinfo.h @@ -109,6 +109,7 @@ struct _GstDebugCategory { #define GST_STR_NULL(str) ((str) ? (str) : "(NULL)") /* easier debugging for pad names */ +/* FIXME, not MT safe */ #define GST_DEBUG_PAD_NAME(pad) \ (GST_OBJECT_PARENT(pad) != NULL) ? \ GST_STR_NULL (GST_OBJECT_NAME (GST_OBJECT_PARENT(pad))) : \ @@ -824,15 +825,6 @@ GST_LOG (const char *format, ...) void gst_debug_print_stack_trace (void); -/* timestamp debugging macros */ -/* FIXME 0.9: move into the correct header (gstclock.h) */ -#define GST_TIME_FORMAT "u:%02u:%02u.%09u" -#define GST_TIME_ARGS(t) \ - (guint) ((t) / (GST_SECOND * 60 * 60)), \ - (guint) (((t) / (GST_SECOND * 60)) % 60), \ - (guint) (((t) / GST_SECOND) % 60), \ - (guint) ((t) % GST_SECOND) - G_END_DECLS #endif /* __GSTINFO_H__ */ diff --git a/gst/gstmemchunk.c b/gst/gstmemchunk.c index 1f82fb2592..d9cf01763f 100644 --- a/gst/gstmemchunk.c +++ b/gst/gstmemchunk.c @@ -1,5 +1,9 @@ /* GStreamer * Copyright (C) <1999> Erik Walthinsen + * <2005> Wim Taymans + * + * gstmemchunk.c: implementation of lockfree allocation of fixed + * size memory chunks. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -28,7 +32,6 @@ #include #endif - #define GST_MEM_CHUNK_AREA(chunk) (((GstMemChunkElement*)(chunk))->area) #define GST_MEM_CHUNK_DATA(chunk) ((gpointer)(((GstMemChunkElement*)(chunk)) + 1)) #define GST_MEM_CHUNK_LINK(mem) ((GstMemChunkElement*)((guint8*)(mem) - sizeof (GstMemChunkElement))) @@ -105,6 +108,8 @@ populate (GstMemChunk * mem_chunk) * when it is too small (with a small overhead when that happens) * * Returns: a new #GstMemChunk + * + * MT safe. */ GstMemChunk * gst_mem_chunk_new (gchar * name, gint atom_size, gulong area_size, gint type) @@ -152,7 +157,9 @@ free_area (gpointer key, gpointer value, gpointer user_data) * gst_mem_chunk_destroy: * @mem_chunk: the GstMemChunk to destroy * - * Free the memory allocated by the memchunk + * Free the memory allocated by the memchunk. This function + * is not Threadsafe as it does not wait for all outstanding + * allocations to be freed. */ void gst_mem_chunk_destroy (GstMemChunk * mem_chunk) @@ -186,6 +193,8 @@ gst_mem_chunk_destroy (GstMemChunk * mem_chunk) * was created. * * Returns: a pointer to the allocated memory region. + * + * MT safe. */ gpointer gst_mem_chunk_alloc (GstMemChunk * mem_chunk) @@ -197,15 +206,20 @@ gst_mem_chunk_alloc (GstMemChunk * mem_chunk) again: chunk = gst_trash_stack_pop (&mem_chunk->stack); /* chunk is empty, try to refill */ - if (!chunk) { - if (populate (mem_chunk)) + if (G_UNLIKELY (!chunk)) { + if (G_LIKELY (populate (mem_chunk))) { goto again; - else + } else { + /* this happens when we are in cleanup mode and we + * allocate all remaining chunks for cleanup */ return NULL; + } } #ifdef HAVE_VALGRIND - VALGRIND_MALLOCLIKE_BLOCK (GST_MEM_CHUNK_DATA (chunk), mem_chunk->atom_size, - 0, 0); + if (G_UNLIKELY (__gst_in_valgrind ())) { + VALGRIND_MALLOCLIKE_BLOCK (GST_MEM_CHUNK_DATA (chunk), mem_chunk->atom_size, + 0, 0); + } #endif return GST_MEM_CHUNK_DATA (chunk); } @@ -219,13 +233,15 @@ again: * was created. The memory will be set to all zeroes. * * Returns: a pointer to the allocated memory region. + * + * MT safe. */ gpointer gst_mem_chunk_alloc0 (GstMemChunk * mem_chunk) { gpointer mem = gst_mem_chunk_alloc (mem_chunk); - if (mem) + if (G_LIKELY (mem)) memset (mem, 0, mem_chunk->atom_size); return mem; @@ -237,6 +253,8 @@ gst_mem_chunk_alloc0 (GstMemChunk * mem_chunk) * @mem: the memory region to hand back to the chunk * * Free the memeory region allocated from the chunk. + * + * MT safe. */ void gst_mem_chunk_free (GstMemChunk * mem_chunk, gpointer mem) @@ -249,7 +267,9 @@ gst_mem_chunk_free (GstMemChunk * mem_chunk, gpointer mem) chunk = GST_MEM_CHUNK_LINK (mem); #ifdef HAVE_VALGRIND - VALGRIND_FREELIKE_BLOCK (mem, 0); + if (G_UNLIKELY (__gst_in_valgrind ())) { + VALGRIND_FREELIKE_BLOCK (mem, 0); + } #endif gst_trash_stack_push (&mem_chunk->stack, chunk); } diff --git a/gst/gstobject.c b/gst/gstobject.c index 46fc246489..d2d5b964fc 100644 --- a/gst/gstobject.c +++ b/gst/gstobject.c @@ -1,6 +1,7 @@ /* GStreamer * Copyright (C) 1999,2000 Erik Walthinsen * 2000 Wim Taymans + * 2005 Wim Taymans * * gstobject.c: Fundamental class used for all of GStreamer * @@ -25,11 +26,32 @@ #include "gstobject.h" #include "gstmarshal.h" #include "gstinfo.h" +#include "gstatomic_impl.h" #ifndef GST_DISABLE_TRACE #include "gsttrace.h" #endif +#define DEBUG_REFCOUNT +#define REFCOUNT_HACK + +/* Refcount hack: since glib is not threadsafe, the glib refcounter can be + * screwed up and the object can be freed unexpectedly. We use an evil hack + * to work around this problem. We set the glib refcount to a high value so + * that glib will never unref the object under realistic circumstances. Then + * we use our own atomic refcounting to do proper MT safe refcounting. + * + * A proper fix is of course to make the glib refcounting threadsafe which is + * planned. + */ +#ifdef REFCOUNT_HACK +#define PATCH_REFCOUNT(obj) ((GObject*)(obj))->ref_count = 100000; +#define PATCH_REFCOUNT1(obj) ((GObject*)(obj))->ref_count = 1; +#else +#define PATCH_REFCOUNT(obj) +#define PATCH_REFCOUNT1(obj) +#endif + /* Object signals and args */ enum { @@ -89,7 +111,7 @@ static void gst_object_dispatch_properties_changed (GObject * object, static void gst_object_dispose (GObject * object); static void gst_object_finalize (GObject * object); -static void gst_object_set_name_default (GstObject * object, +static gboolean gst_object_set_name_default (GstObject * object, const gchar * type_name); #ifndef GST_DISABLE_LOADSAVE_REGISTRY @@ -165,6 +187,7 @@ gst_object_class_init (GstObjectClass * klass) G_TYPE_PARAM); klass->path_string_separator = "/"; + klass->lock = g_mutex_new (); klass->signal_object = g_object_new (gst_signal_object_get_type (), NULL); @@ -187,10 +210,12 @@ gst_object_init (GTypeInstance * instance, gpointer g_class) object->lock = g_mutex_new (); object->parent = NULL; object->name = NULL; + gst_atomic_int_init (&(object)->refcount, 1); + PATCH_REFCOUNT (object); gst_object_set_name_default (object, G_OBJECT_CLASS_NAME (g_class)); object->flags = 0; - GST_FLAG_SET (object, GST_FLOATING); + GST_FLAG_SET (object, GST_OBJECT_FLOATING); } #ifndef GST_DISABLE_TRACE @@ -219,7 +244,13 @@ gst_object_constructor (GType type, guint n_construct_properties, * gst_object_ref: * @object: GstObject to reference * - * Increments the refence count on the object. + * Increments the refence count on the object. This function + * does not take the lock on the object because it relies on + * atomic refcounting. + * + * This object returns the input parameter to ease writing + * constructs like : + * result = gst_object_ref (object->parent); * * Returns: A pointer to the object */ @@ -228,10 +259,25 @@ gst_object_ref (GstObject * object) { g_return_val_if_fail (GST_IS_OBJECT (object), NULL); +#ifdef DEBUG_REFCOUNT +#ifdef REFCOUNT_HACK GST_CAT_LOG_OBJECT (GST_CAT_REFCOUNTING, object, "ref %d->%d", - G_OBJECT (object)->ref_count, G_OBJECT (object)->ref_count + 1); + GST_OBJECT_REFCOUNT_VALUE (object), + GST_OBJECT_REFCOUNT_VALUE (object) + 1); +#else + GST_CAT_LOG_OBJECT (GST_CAT_REFCOUNTING, object, "ref %d->%d", + ((GObject *) object)->ref_count, ((GObject *) object)->ref_count + 1); +#endif +#endif + +#ifdef REFCOUNT_HACK + gst_atomic_int_inc (&object->refcount); + PATCH_REFCOUNT (object); +#else + /* FIXME, not MT safe because glib is not MT safe */ + g_object_ref (object); +#endif - g_object_ref (G_OBJECT (object)); return object; } @@ -240,18 +286,53 @@ gst_object_ref (GstObject * object) * @object: GstObject to unreference * * Decrements the refence count on the object. If reference count hits - * zero, destroy the object. + * zero, destroy the object. This function does not take the lock + * on the object as it relies on atomic refcounting. + * + * The unref method should never be called with the LOCK held since + * this might deadlock the dispose function. + * + * Returns: NULL, so that constructs like + * + * object = gst_object_unref (object); + * + * automatically clear the input object pointer. */ -void +GstObject * gst_object_unref (GstObject * object) { - g_return_if_fail (GST_IS_OBJECT (object)); - g_return_if_fail (G_OBJECT (object)->ref_count > 0); + g_return_val_if_fail (GST_IS_OBJECT (object), NULL); +#ifdef REFCOUNT_HACK + g_return_val_if_fail (GST_OBJECT_REFCOUNT_VALUE (object) > 0, NULL); +#else + g_return_val_if_fail (((GObject *) object)->ref_count > 0, NULL); +#endif + +#ifdef DEBUG_REFCOUNT +#ifdef REFCOUNT_HACK GST_CAT_LOG_OBJECT (GST_CAT_REFCOUNTING, object, "unref %d->%d", - G_OBJECT (object)->ref_count, G_OBJECT (object)->ref_count - 1); + GST_OBJECT_REFCOUNT_VALUE (object), + GST_OBJECT_REFCOUNT_VALUE (object) - 1); +#else + GST_CAT_LOG_OBJECT (GST_CAT_REFCOUNTING, object, "unref %d->%d", + ((GObject *) object)->ref_count, ((GObject *) object)->ref_count - 1); +#endif +#endif - g_object_unref (G_OBJECT (object)); +#ifdef REFCOUNT_HACK + if (G_UNLIKELY (gst_atomic_int_dec_and_test (&object->refcount))) { + PATCH_REFCOUNT1 (object); + g_object_unref (object); + } else { + PATCH_REFCOUNT (object); + } +#else + /* FIXME, not MT safe because glib is not MT safe */ + g_object_unref (object); +#endif + + return NULL; } /** @@ -262,18 +343,25 @@ gst_object_unref (GstObject * object) * a refcount of 1 and is FLOATING. This function should be used when * creating a new object to symbolically 'take ownership' of the object. * Use #gst_object_set_parent to have this done for you. + * + * This function takes the object lock. + * + * MT safe. */ void gst_object_sink (GstObject * object) { - g_return_if_fail (object != NULL); g_return_if_fail (GST_IS_OBJECT (object)); GST_CAT_LOG_OBJECT (GST_CAT_REFCOUNTING, object, "sink"); - if (GST_OBJECT_FLOATING (object)) { - GST_FLAG_UNSET (object, GST_FLOATING); + GST_LOCK (object); + if (G_LIKELY (GST_OBJECT_IS_FLOATING (object))) { + GST_FLAG_UNSET (object, GST_OBJECT_FLOATING); + GST_UNLOCK (object); gst_object_unref (object); + } else { + GST_UNLOCK (object); } } @@ -283,7 +371,10 @@ gst_object_sink (GstObject * object) * @newobj: new GstObject * * Unrefs the object pointer to by oldobj, refs the newobj and - * puts the newobj in *oldobj. + * puts the newobj in *oldobj. Be carefull when calling this + * function, it does not take any locks. You might want to lock + * the object owning the oldobj pointer before calling this + * function. */ void gst_object_replace (GstObject ** oldobj, GstObject * newobj) @@ -292,13 +383,23 @@ gst_object_replace (GstObject ** oldobj, GstObject * newobj) g_return_if_fail (*oldobj == NULL || GST_IS_OBJECT (*oldobj)); g_return_if_fail (newobj == NULL || GST_IS_OBJECT (newobj)); +#ifdef DEBUG_REFCOUNT +#ifdef REFCOUNT_HACK + GST_CAT_LOG (GST_CAT_REFCOUNTING, "replace %s (%d) with %s (%d)", + *oldobj ? GST_STR_NULL (GST_OBJECT_NAME (*oldobj)) : "(NONE)", + *oldobj ? GST_OBJECT_REFCOUNT_VALUE (*oldobj) : 0, + newobj ? GST_STR_NULL (GST_OBJECT_NAME (newobj)) : "(NONE)", + newobj ? GST_OBJECT_REFCOUNT_VALUE (newobj) : 0); +#else GST_CAT_LOG (GST_CAT_REFCOUNTING, "replace %s (%d) with %s (%d)", *oldobj ? GST_STR_NULL (GST_OBJECT_NAME (*oldobj)) : "(NONE)", *oldobj ? G_OBJECT (*oldobj)->ref_count : 0, newobj ? GST_STR_NULL (GST_OBJECT_NAME (newobj)) : "(NONE)", newobj ? G_OBJECT (newobj)->ref_count : 0); +#endif +#endif - if (*oldobj != newobj) { + if (G_LIKELY (*oldobj != newobj)) { if (newobj) gst_object_ref (newobj); if (*oldobj) @@ -308,15 +409,22 @@ gst_object_replace (GstObject ** oldobj, GstObject * newobj) } } +/* dispose is called when the object has to release all links + * to other objects */ static void gst_object_dispose (GObject * object) { GST_CAT_LOG_OBJECT (GST_CAT_REFCOUNTING, object, "dispose"); - GST_FLAG_SET (GST_OBJECT (object), GST_DESTROYED); + GST_LOCK (object); + GST_FLAG_SET (GST_OBJECT (object), GST_OBJECT_DESTROYED); GST_OBJECT_PARENT (object) = NULL; + GST_UNLOCK (object); - parent_class->dispose (object); + /* need to patch refcount so it is finalized */ + PATCH_REFCOUNT1 (object) + + parent_class->dispose (object); } /* finalize is called when the object has to free its resources */ @@ -330,7 +438,6 @@ gst_object_finalize (GObject * object) g_signal_handlers_destroy (object); g_free (gstobject->name); - g_mutex_free (gstobject->lock); #ifndef GST_DISABLE_TRACE @@ -352,34 +459,67 @@ gst_object_finalize (GObject * object) * signals being emitted by the object itself, as well as in each parent * object. This is so that an application can connect a listener to the * top-level bin to catch property-change notifications for all contained - * elements. */ + * elements. + * + * This function is not MT safe in glib so we need to lock it with a + * classwide mutex. + * + * MT safe. + */ static void gst_object_dispatch_properties_changed (GObject * object, guint n_pspecs, GParamSpec ** pspecs) { - GstObject *gst_object; + GstObject *gst_object, *parent, *old_parent; guint i; + gchar *name, *debug_name; + GstObjectClass *klass; + /* we fail when this is not a GstObject */ + g_return_if_fail (GST_IS_OBJECT (object)); + + klass = GST_OBJECT_GET_CLASS (object); + + GST_CLASS_LOCK (klass); /* do the standard dispatching */ + PATCH_REFCOUNT (object); G_OBJECT_CLASS (parent_class)->dispatch_properties_changed (object, n_pspecs, pspecs); + PATCH_REFCOUNT (object); + + gst_object = GST_OBJECT_CAST (object); + name = gst_object_get_name (gst_object); + debug_name = GST_STR_NULL (name); /* now let the parent dispatch those, too */ - gst_object = GST_OBJECT_PARENT (object); - while (gst_object) { + parent = gst_object_get_parent (gst_object); + while (parent) { + /* for debugging ... */ + gchar *parent_name = gst_object_get_name (parent); + gchar *debug_parent_name = GST_STR_NULL (parent_name); + /* need own category? */ for (i = 0; i < n_pspecs; i++) { - GST_CAT_LOG (GST_CAT_SIGNAL, "deep notification from %s to %s (%s)", - GST_OBJECT_NAME (object) ? GST_OBJECT_NAME (object) : "(null)", - GST_OBJECT_NAME (gst_object) ? GST_OBJECT_NAME (gst_object) : - "(null)", pspecs[i]->name); - g_signal_emit (gst_object, gst_object_signals[DEEP_NOTIFY], - g_quark_from_string (pspecs[i]->name), (GstObject *) object, - pspecs[i]); - } + GST_CAT_LOG (GST_CAT_EVENT, "deep notification from %s to %s (%s)", + debug_name, debug_parent_name, pspecs[i]->name); - gst_object = GST_OBJECT_PARENT (gst_object); + /* not MT safe because of glib, fixed by taking class lock higher up */ + PATCH_REFCOUNT (parent); + PATCH_REFCOUNT (object); + g_signal_emit (parent, gst_object_signals[DEEP_NOTIFY], + g_quark_from_string (pspecs[i]->name), GST_OBJECT_CAST (object), + pspecs[i]); + PATCH_REFCOUNT (parent); + PATCH_REFCOUNT (object); + } + g_free (parent_name); + + old_parent = parent; + parent = gst_object_get_parent (old_parent); + gst_object_unref (old_parent); } + g_free (name); + GST_CLASS_UNLOCK (klass); } /** @@ -395,6 +535,8 @@ gst_object_dispatch_properties_changed (GObject * object, * strings that should be excluded from the notify. * The default handler will print the new value of the property * using g_print. + * + * MT safe. */ void gst_object_default_deep_notify (GObject * object, GstObject * orig, @@ -438,11 +580,12 @@ gst_object_default_deep_notify (GObject * object, GstObject * orig, } } -static void +static gboolean gst_object_set_name_default (GstObject * object, const gchar * type_name) { gint count; gchar *name, *tmp; + gboolean result; /* to ensure guaranteed uniqueness across threads, only one thread * may ever assign a name */ @@ -466,47 +609,133 @@ gst_object_set_name_default (GstObject * object, const gchar * type_name) name = g_ascii_strdown (tmp, strlen (tmp)); g_free (tmp); - gst_object_set_name (object, name); + result = gst_object_set_name (object, name); g_free (name); + + return result; } /** * gst_object_set_name: - * @object: GstObject to set the name of - * @name: new name of object + * @object: a #GstObject to set the name of + * @name: new name of object * - * Sets the name of the object, or gives the element a guaranteed unique + * Sets the name of the object, or gives the object a guaranteed unique * name (if @name is NULL). + * This function makes a copy of the provided name, so the caller + * retains ownership of the name it sent. + * + * Returns: TRUE if the name could be set. Objects that have + * a parent cannot be renamed. + * + * MT safe. This function grabs and releases the object's LOCK. */ -void +gboolean gst_object_set_name (GstObject * object, const gchar * name) { - g_return_if_fail (object != NULL); - g_return_if_fail (GST_IS_OBJECT (object)); + gboolean result; - if (object->name != NULL) + g_return_val_if_fail (GST_IS_OBJECT (object), FALSE); + + GST_LOCK (object); + + /* parented objects cannot be renamed */ + if (G_UNLIKELY (object->parent != NULL)) + goto had_parent; + + if (name != NULL) { g_free (object->name); - - if (name != NULL) object->name = g_strdup (name); - else - gst_object_set_name_default (object, G_OBJECT_TYPE_NAME (object)); + GST_UNLOCK (object); + result = TRUE; + } else { + GST_UNLOCK (object); + result = gst_object_set_name_default (object, G_OBJECT_TYPE_NAME (object)); + } + return result; + + /* error */ +had_parent: + { + GST_UNLOCK (object); + return FALSE; + } } /** * gst_object_get_name: - * @object: GstObject to get the name of + * @object: a #GstObject to get the name of * - * Get the name of the object. + * Returns a copy of the name of the object. + * Caller should g_free() the return value after usage. + * For a nameless object, this returns NULL, which you can safely g_free() + * as well. * - * Returns: name of the object + * Returns: the name of the object. g_free() after usage. + * + * MT safe. */ -const gchar * +gchar * gst_object_get_name (GstObject * object) { + gchar *result = NULL; + g_return_val_if_fail (GST_IS_OBJECT (object), NULL); - return object->name; + GST_LOCK (object); + result = g_strdup (object->name); + GST_UNLOCK (object); + + return result; +} + +/** + * gst_object_set_name_prefix: + * @object: a #GstObject to set the name prefix of + * @name_prefix: new name prefix of object + * + * Sets the name prefix of the object. + * This function makes a copy of the provided name prefix, so the caller + * retains ownership of the name prefix it sent. + * + * MT safe. This function grabs and releases the object's LOCK. + */ +void +gst_object_set_name_prefix (GstObject * object, const gchar * name_prefix) +{ + g_return_if_fail (GST_IS_OBJECT (object)); + + GST_LOCK (object); + g_free (object->name_prefix); + object->name_prefix = g_strdup (name_prefix); /* NULL gives NULL */ + GST_UNLOCK (object); +} + +/** + * gst_object_get_name_prefix: + * @object: a #GstObject to get the name prefix of + * + * Returns a copy of the name prefix of the object. + * Caller should g_free() the return value after usage. + * For a prefixless object, this returns NULL, which you can safely g_free() + * as well. + * + * Returns: the name prefix of the object. g_free() after usage. + * + * MT safe. + */ +gchar * +gst_object_get_name_prefix (GstObject * object) +{ + gchar *result = NULL; + + g_return_val_if_fail (GST_IS_OBJECT (object), NULL); + + GST_LOCK (object); + result = g_strdup (object->name_prefix); + GST_UNLOCK (object); + + return result; } /** @@ -517,41 +746,78 @@ gst_object_get_name (GstObject * object) * Sets the parent of @object. The object's reference count will be incremented, * and any floating reference will be removed (see gst_object_sink()). * + * This function takes the object lock. + * * Causes the parent-set signal to be emitted. + * + * Returns: TRUE if the parent could be set or FALSE when the object + * already had a parent, the object and parent are the same or wrong + * parameters were provided. + * + * MT safe. */ -void +gboolean gst_object_set_parent (GstObject * object, GstObject * parent) { - g_return_if_fail (object != NULL); - g_return_if_fail (GST_IS_OBJECT (object)); - g_return_if_fail (parent != NULL); - g_return_if_fail (GST_IS_OBJECT (parent)); - g_return_if_fail (object != parent); - g_return_if_fail (object->parent == NULL); + g_return_val_if_fail (GST_IS_OBJECT (object), FALSE); + g_return_val_if_fail (GST_IS_OBJECT (parent), FALSE); + g_return_val_if_fail (object != parent, FALSE); GST_CAT_LOG_OBJECT (GST_CAT_REFCOUNTING, object, "set parent (ref and sink)"); - gst_object_ref (object); - gst_object_sink (object); + + GST_LOCK (object); + if (G_UNLIKELY (object->parent != NULL)) + goto had_parent; + + /* sink object, we don't call our own function because we don't + * need to release/acquire the lock needlessly or touch the refcount + * in the floating case. */ object->parent = parent; + if (G_LIKELY (GST_OBJECT_IS_FLOATING (object))) { + GST_FLAG_UNSET (object, GST_OBJECT_FLOATING); + GST_UNLOCK (object); + } else { + GST_UNLOCK (object); + gst_object_ref (object); + } g_signal_emit (G_OBJECT (object), gst_object_signals[PARENT_SET], 0, parent); + return TRUE; + + /* ERROR */ +had_parent: + { + GST_UNLOCK (object); + return FALSE; + } } /** * gst_object_get_parent: * @object: GstObject to get parent of * - * Returns the parent of @object. + * Returns the parent of @object. This function increases the refcount + * of the parent object so you should gst_object_unref() it after usage. * - * Returns: parent of the object + * Returns: parent of the object, this can be NULL if the object has no + * parent. unref after usage. + * + * MT safe. */ GstObject * gst_object_get_parent (GstObject * object) { - g_return_val_if_fail (object != NULL, NULL); + GstObject *result = NULL; + g_return_val_if_fail (GST_IS_OBJECT (object), NULL); - return object->parent; + GST_LOCK (object); + result = object->parent; + if (G_LIKELY (result)) + gst_object_ref (result); + GST_UNLOCK (object); + + return result; } /** @@ -559,22 +825,33 @@ gst_object_get_parent (GstObject * object) * @object: GstObject to unparent * * Clear the parent of @object, removing the associated reference. + * This function decreases the refcount of the object so the object + * might not point to valid memory anymore after calling this function. + * + * MT safe. */ void gst_object_unparent (GstObject * object) { - g_return_if_fail (object != NULL); + GstObject *parent; + g_return_if_fail (GST_IS_OBJECT (object)); - if (object->parent == NULL) - return; - GST_CAT_LOG_OBJECT (GST_CAT_REFCOUNTING, object, "unparent"); + GST_LOCK (object); + parent = object->parent; - g_signal_emit (G_OBJECT (object), gst_object_signals[PARENT_UNSET], 0, - object->parent); + if (G_LIKELY (parent != NULL)) { + GST_CAT_LOG_OBJECT (GST_CAT_REFCOUNTING, object, "unparent"); + object->parent = NULL; + GST_UNLOCK (object); - object->parent = NULL; - gst_object_unref (object); + g_signal_emit (G_OBJECT (object), gst_object_signals[PARENT_UNSET], 0, + parent); + + gst_object_unref (object); + } else { + GST_UNLOCK (object); + } } /** @@ -582,25 +859,39 @@ gst_object_unparent (GstObject * object) * @list: a list of #GstObject to check through * @name: the name to search for * - * Checks to see if there is any object named @name in @list. + * Checks to see if there is any object named @name in @list. This function + * does not do any locking of any kind. You might want to protect the + * provided list with the lock of the owner of the list. This function + * will lock each GstObject in the list to compare the name, so be + * carefull when passing a list with a locked object. * * Returns: TRUE if the name does not appear in the list, FALSE if it does. + * + * MT safe. */ gboolean gst_object_check_uniqueness (GList * list, const gchar * name) { + gboolean result = TRUE; + g_return_val_if_fail (name != NULL, FALSE); - while (list) { - GstObject *child = GST_OBJECT (list->data); + for (; list; list = g_list_next (list)) { + GstObject *child; + gboolean eq; - list = g_list_next (list); + child = GST_OBJECT (list->data); - if (strcmp (GST_OBJECT_NAME (child), name) == 0) - return FALSE; + GST_LOCK (child); + eq = strcmp (GST_OBJECT_NAME (child), name) == 0; + GST_UNLOCK (child); + + if (G_UNLIKELY (eq)) { + result = FALSE; + break; + } } - - return TRUE; + return result; } @@ -619,7 +910,6 @@ gst_object_save_thyself (GstObject * object, xmlNodePtr parent) { GstObjectClass *oclass; - g_return_val_if_fail (object != NULL, parent); g_return_val_if_fail (GST_IS_OBJECT (object), parent); g_return_val_if_fail (parent != NULL, parent); @@ -646,7 +936,6 @@ gst_object_restore_thyself (GstObject * object, xmlNodePtr self) { GstObjectClass *oclass; - g_return_if_fail (object != NULL); g_return_if_fail (GST_IS_OBJECT (object)); g_return_if_fail (self != NULL); @@ -659,7 +948,6 @@ gst_object_restore_thyself (GstObject * object, xmlNodePtr self) static void gst_object_real_restore_thyself (GstObject * object, xmlNodePtr self) { - g_return_if_fail (object != NULL); g_return_if_fail (GST_IS_OBJECT (object)); g_return_if_fail (self != NULL); @@ -673,9 +961,6 @@ gst_object_set_property (GObject * object, guint prop_id, { GstObject *gstobject; - /* it's not null if we got it, but it might not be ours */ - g_return_if_fail (GST_IS_OBJECT (object)); - gstobject = GST_OBJECT (object); switch (prop_id) { @@ -694,14 +979,11 @@ gst_object_get_property (GObject * object, guint prop_id, { GstObject *gstobject; - /* it's not null if we got it, but it might not be ours */ - g_return_if_fail (GST_IS_OBJECT (object)); - gstobject = GST_OBJECT (object); switch (prop_id) { case ARG_NAME: - g_value_set_string (value, (gchar *) GST_OBJECT_NAME (gstobject)); + g_value_take_string (value, gst_object_get_name (gstobject)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -716,61 +998,63 @@ gst_object_get_property (GObject * object, guint prop_id, * Generates a string describing the path of the object in * the object hierarchy. Only useful (or used) for debugging. * - * Returns: a string describing the path of the object + * Returns: a string describing the path of the object. You must + * g_free() the string after usage. + * + * MT safe. */ gchar * gst_object_get_path_string (GstObject * object) { - GSList *parentage = NULL; + GSList *parentage; GSList *parents; void *parent; gchar *prevpath, *path; - const char *component; - gchar *separator = ""; - gboolean free_component; + gchar *component; + gchar *separator; + /* ref object before adding to list */ + gst_object_ref (object); parentage = g_slist_prepend (NULL, object); path = g_strdup (""); - /* first walk the object hierarchy to build a list of the parents */ + /* first walk the object hierarchy to build a list of the parents, + * be carefull here with refcounting. */ do { if (GST_IS_OBJECT (object)) { parent = gst_object_get_parent (object); + /* add parents to list, refcount remains increased while + * we handle the object */ + if (parent) + parentage = g_slist_prepend (parentage, parent); } else { - parentage = g_slist_prepend (parentage, NULL); - parent = NULL; + break; } - - if (parent != NULL) { - parentage = g_slist_prepend (parentage, parent); - } - object = parent; } while (object != NULL); - /* then walk the parent list and print them out */ - parents = parentage; - while (parents) { + /* then walk the parent list and print them out. we need to + * decrease the refcounting on each element after we handled + * it. */ + for (parents = parentage; parents; parents = g_slist_next (parents)) { if (GST_IS_OBJECT (parents->data)) { - GstObjectClass *oclass = GST_OBJECT_GET_CLASS (parents->data); + GstObject *item = GST_OBJECT_CAST (parents->data); + GstObjectClass *oclass = GST_OBJECT_GET_CLASS (item); - component = gst_object_get_name (parents->data); + component = gst_object_get_name (item); separator = oclass->path_string_separator; - free_component = FALSE; + /* and unref now */ + gst_object_unref (item); } else { component = g_strdup_printf ("%p", parents->data); separator = "/"; - free_component = TRUE; } prevpath = path; path = g_strjoin (separator, prevpath, component, NULL); g_free (prevpath); - if (free_component) - g_free ((gchar *) component); - - parents = g_slist_next (parents); + g_free (component); } g_slist_free (parentage); @@ -778,8 +1062,6 @@ gst_object_get_path_string (GstObject * object) return path; } - - struct _GstSignalObject { GObject object; diff --git a/gst/gstobject.h b/gst/gstobject.h index bbb9dc8181..d3be094d83 100644 --- a/gst/gstobject.h +++ b/gst/gstobject.h @@ -1,6 +1,7 @@ /* GStreamer * Copyright (C) 1999,2000 Erik Walthinsen * 2000 Wim Taymans + * 2005 Wim Taymans * * gstobject.h: Header for base GstObject * @@ -28,6 +29,7 @@ #include /* note that this gets wrapped in __GST_OBJECT_H__ */ +#include #include G_BEGIN_DECLS @@ -40,6 +42,8 @@ GST_EXPORT GType _gst_object_type; #define GST_OBJECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_OBJECT, GstObjectClass)) #define GST_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_OBJECT, GstObject)) #define GST_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_OBJECT, GstObjectClass)) +#define GST_OBJECT_CAST(obj) ((GstObject*)(obj)) +#define GST_OBJECT_CLASS_CAST(klass) ((GstObjectClass*)(klass)) /* make sure we don't change the object size but stil make it compile * without libxml */ @@ -49,27 +53,57 @@ GST_EXPORT GType _gst_object_type; typedef enum { - GST_DESTROYED = 0, - GST_FLOATING, + GST_OBJECT_DISPOSING = 0, + GST_OBJECT_DESTROYED = 1, + GST_OBJECT_FLOATING, GST_OBJECT_FLAG_LAST = 4 } GstObjectFlags; +#define GST_OBJECT_REFCOUNT(caps) ((GST_OBJECT_CAST(caps))->refcount) +#define GST_OBJECT_REFCOUNT_VALUE(caps) (gst_atomic_int_read (&(GST_OBJECT_CAST(caps))->refcount)) + +/* we do a GST_OBJECT_CAST to avoid type checking, better call these + * function with a valid object! */ +#define GST_LOCK(obj) (g_mutex_lock(GST_OBJECT_CAST(obj)->lock)) +#define GST_TRYLOCK(obj) (g_mutex_trylock(GST_OBJECT_CAST(obj)->lock)) +#define GST_UNLOCK(obj) (g_mutex_unlock(GST_OBJECT_CAST(obj)->lock)) +#define GST_GET_LOCK(obj) (GST_OBJECT_CAST(obj)->lock) + +#define GST_OBJECT_NAME(obj) (GST_OBJECT_CAST(obj)->name) +#define GST_OBJECT_PARENT(obj) (GST_OBJECT_CAST(obj)->parent) + +/* for the flags we double-not to make them comparable to TRUE and FALSE */ +#define GST_FLAGS(obj) (GST_OBJECT_CAST (obj)->flags) +#define GST_FLAG_IS_SET(obj,flag) (!!(GST_FLAGS (obj) & (1<<(flag)))) +#define GST_FLAG_SET(obj,flag) G_STMT_START{ (GST_FLAGS (obj) |= (1<<(flag))); }G_STMT_END +#define GST_FLAG_UNSET(obj,flag) G_STMT_START{ (GST_FLAGS (obj) &= ~(1<<(flag))); }G_STMT_END + +#define GST_OBJECT_IS_DESTROYED(obj) (GST_FLAG_IS_SET (obj, GST_OBJECT_DESTROYED)) +#define GST_OBJECT_IS_FLOATING(obj) (GST_FLAG_IS_SET (obj, GST_OBJECT_FLOATING)) + struct _GstObject { - GObject object; + GObject object; - gchar *name; + /*< public >*/ + GstAtomicInt refcount; - /* locking for all sorts of things */ - GMutex *lock; - /* this object's parent */ - GstObject *parent; - - guint32 flags; + /*< public >*/ /* with LOCK */ + GMutex *lock; /* object LOCK */ + gchar *name; /* object name */ + GstObject *parent; /* this object's parent, weak ref */ + guint32 flags; + gchar *name_prefix; /* used for debugging */ + /*< private >*/ gpointer _gst_reserved[GST_PADDING]; }; +#define GST_CLASS_LOCK(obj) (g_mutex_lock(GST_OBJECT_CLASS_CAST(obj)->lock)) +#define GST_CLASS_TRYLOCK(obj) (g_mutex_trylock(GST_OBJECT_CLASS_CAST(obj)->lock)) +#define GST_CLASS_UNLOCK(obj) (g_mutex_unlock(GST_OBJECT_CLASS_CAST(obj)->lock)) +#define GST_CLASS_GET_LOCK(obj) (GST_OBJECT_CLASS_CAST(obj)->lock) + /* signal_object is used to signal to the whole class */ struct _GstObjectClass { GObjectClass parent_class; @@ -77,57 +111,54 @@ struct _GstObjectClass { gchar *path_string_separator; GObject *signal_object; + GMutex *lock; + /* signals */ void (*parent_set) (GstObject *object, GstObject *parent); void (*parent_unset) (GstObject *object, GstObject *parent); void (*object_saved) (GstObject *object, xmlNodePtr parent); void (*deep_notify) (GstObject *object, GstObject *orig, GParamSpec *pspec); - /* functions go here */ - void (*destroy) (GstObject *object); - + /* vtable */ xmlNodePtr (*save_thyself) (GstObject *object, xmlNodePtr parent); void (*restore_thyself) (GstObject *object, xmlNodePtr self); + /*< private >*/ gpointer _gst_reserved[GST_PADDING]; }; -#define GST_FLAGS(obj) (GST_OBJECT (obj)->flags) -#define GST_FLAG_IS_SET(obj,flag) (GST_FLAGS (obj) & (1<<(flag))) -#define GST_FLAG_SET(obj,flag) G_STMT_START{ (GST_FLAGS (obj) |= (1<<(flag))); }G_STMT_END -#define GST_FLAG_UNSET(obj,flag) G_STMT_START{ (GST_FLAGS (obj) &= ~(1<<(flag))); }G_STMT_END - -#define GST_OBJECT_NAME(obj) (const gchar*)(((GstObject *)(obj))->name) -#define GST_OBJECT_PARENT(obj) (((GstObject *)(obj))->parent) - -#define GST_OBJECT_DESTROYED(obj) (GST_FLAG_IS_SET (obj, GST_DESTROYED)) -#define GST_OBJECT_FLOATING(obj) (GST_FLAG_IS_SET (obj, GST_FLOATING)) - -/* CR1: object locking - GObject 2.0 doesn't have threadsafe locking */ -#define GST_LOCK(obj) (g_mutex_lock(GST_OBJECT(obj)->lock)) -#define GST_TRYLOCK(obj) (g_mutex_trylock(GST_OBJECT(obj)->lock)) -#define GST_UNLOCK(obj) (g_mutex_unlock(GST_OBJECT(obj)->lock)) -#define GST_GET_LOCK(obj) (GST_OBJECT(obj)->lock) - - /* normal GObject stuff */ GType gst_object_get_type (void); /* name routines */ -void gst_object_set_name (GstObject *object, const gchar *name); -G_CONST_RETURN gchar* - gst_object_get_name (GstObject *object); +gboolean gst_object_set_name (GstObject *object, const gchar *name); +gchar* gst_object_get_name (GstObject *object); +void gst_object_set_name_prefix (GstObject *object, const gchar *name_prefix); +gchar* gst_object_get_name_prefix (GstObject *object); /* parentage routines */ -void gst_object_set_parent (GstObject *object, GstObject *parent); +gboolean gst_object_set_parent (GstObject *object, GstObject *parent); GstObject* gst_object_get_parent (GstObject *object); void gst_object_unparent (GstObject *object); void gst_object_default_deep_notify (GObject *object, GstObject *orig, GParamSpec *pspec, gchar **excluded_props); +/* refcounting + life cycle */ +GstObject * gst_object_ref (GstObject *object); +GstObject * gst_object_unref (GstObject *object); +void gst_object_sink (GstObject *object); + +/* replace object pointer */ +void gst_object_replace (GstObject **oldobj, GstObject *newobj); + +/* printing out the 'path' of the object */ +gchar * gst_object_get_path_string (GstObject *object); + +/* misc utils */ gboolean gst_object_check_uniqueness (GList *list, const gchar *name); +/* load/save */ #ifndef GST_DISABLE_LOADSAVE_REGISTRY xmlNodePtr gst_object_save_thyself (GstObject *object, xmlNodePtr parent); void gst_object_restore_thyself (GstObject *object, xmlNodePtr self); @@ -138,17 +169,7 @@ void gst_object_restore_thyself (GstObject *object, xmlNodePtr self); #endif #endif -/* refcounting + life cycle */ -GstObject * gst_object_ref (GstObject *object); -void gst_object_unref (GstObject *object); -void gst_object_sink (GstObject *object); - -/* replace object pointer */ -void gst_object_replace (GstObject **oldobj, GstObject *newobj); - -/* printing out the 'path' of the object */ -gchar * gst_object_get_path_string (GstObject *object); - +/* class signal stuff */ guint gst_class_signal_connect (GstObjectClass *klass, const gchar *name, gpointer func, diff --git a/gst/gstpad.c b/gst/gstpad.c index c7ce568b10..c247a9b639 100644 --- a/gst/gstpad.c +++ b/gst/gstpad.c @@ -47,6 +47,19 @@ GST_DEBUG_CATEGORY_STATIC (debug_dataflow); }G_STMT_END #define GST_CAT_DEFAULT GST_CAT_PADS +/* realize and pad and grab the lock of the realized pad. */ +#define GST_PAD_REALIZE_AND_LOCK(pad, realpad, lost_ghostpad) \ + GST_LOCK (pad); \ + realpad = GST_PAD_REALIZE (pad); \ + if (G_UNLIKELY (realpad == NULL)) { \ + GST_UNLOCK (pad); \ + goto lost_ghostpad; \ + } \ + if (G_UNLIKELY (pad != GST_PAD_CAST (realpad))) { \ + GST_LOCK (realpad); \ + GST_UNLOCK (pad); \ + } + struct _GstPadLink { GType type; @@ -143,6 +156,8 @@ gst_pad_dispose (GObject * object) GstPad *pad = GST_PAD (object); gst_pad_set_pad_template (pad, NULL); + /* FIXME, we have links to many other things like caps + * and the peer pad... */ G_OBJECT_CLASS (pad_parent_class)->dispose (object); } @@ -171,6 +186,8 @@ enum static void gst_real_pad_class_init (GstRealPadClass * klass); static void gst_real_pad_init (GstRealPad * pad); static void gst_real_pad_dispose (GObject * object); +static void gst_real_pad_finalize (GObject * object); + static gboolean _gst_real_pad_fixate_accumulator (GSignalInvocationHint * ihint, GValue * return_accu, const GValue * handler_return, gpointer dummy); @@ -214,6 +231,7 @@ gst_real_pad_class_init (GstRealPadClass * klass) real_pad_parent_class = g_type_class_ref (GST_TYPE_PAD); gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_real_pad_dispose); + gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_real_pad_finalize); gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_real_pad_set_property); gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_real_pad_get_property); @@ -284,7 +302,7 @@ gst_real_pad_init (GstRealPad * pad) pad->formatsfunc = gst_pad_get_formats_default; pad->querytypefunc = gst_pad_get_query_types_default; - GST_FLAG_SET (pad, GST_PAD_DISABLED); + GST_FLAG_UNSET (pad, GST_PAD_ACTIVE); GST_FLAG_UNSET (pad, GST_PAD_NEGOTIATING); gst_probe_dispatcher_init (&pad->probedisp); @@ -314,7 +332,7 @@ gst_real_pad_get_property (GObject * object, guint prop_id, switch (prop_id) { case REAL_ARG_ACTIVE: - g_value_set_boolean (value, !GST_FLAG_IS_SET (object, GST_PAD_DISABLED)); + g_value_set_boolean (value, GST_FLAG_IS_SET (object, GST_PAD_ACTIVE)); break; case REAL_ARG_CAPS: g_value_set_boxed (value, GST_PAD_CAPS (GST_REAL_PAD (object))); @@ -326,7 +344,7 @@ gst_real_pad_get_property (GObject * object, guint prop_id, } /* FIXME-0.9: Replace these custom functions with proper inheritance via _init - functions and object properties */ + functions and object properties. update: probably later in the cycle. */ /** * gst_pad_custom_new: * @type: the #Gtype of the pad. @@ -335,22 +353,23 @@ gst_real_pad_get_property (GObject * object, guint prop_id, * * Creates a new pad with the given name and type in the given direction. * If name is NULL, a guaranteed unique name (across all pads) - * will be assigned. + * will be assigned. + * This function makes a copy of the name so you can safely free the name. * * Returns: a new #GstPad, or NULL in case of an error. + * + * MT safe. */ GstPad * gst_pad_custom_new (GType type, const gchar * name, GstPadDirection direction) { GstRealPad *pad; - g_return_val_if_fail (direction != GST_PAD_UNKNOWN, NULL); - pad = g_object_new (type, NULL); gst_object_set_name (GST_OBJECT (pad), name); GST_RPAD_DIRECTION (pad) = direction; - return GST_PAD (pad); + return GST_PAD_CAST (pad); } /** @@ -361,8 +380,11 @@ gst_pad_custom_new (GType type, const gchar * name, GstPadDirection direction) * Creates a new real pad with the given name in the given direction. * If name is NULL, a guaranteed unique name (across all pads) * will be assigned. + * This function makes a copy of the name so you can safely free the name. * * Returns: a new #GstPad, or NULL in case of an error. + * + * MT safe. */ GstPad * gst_pad_new (const gchar * name, GstPadDirection direction) @@ -379,6 +401,7 @@ gst_pad_new (const gchar * name, GstPadDirection direction) * Creates a new custom pad with the given name from the given template. * If name is NULL, a guaranteed unique name (across all pads) * will be assigned. + * This function makes a copy of the name so you can safely free the name. * * Returns: a new #GstPad, or NULL in case of an error. */ @@ -404,6 +427,7 @@ gst_pad_custom_new_from_template (GType type, GstPadTemplate * templ, * Creates a new real pad with the given name from the given template. * If name is NULL, a guaranteed unique name (across all pads) * will be assigned. + * This function makes a copy of the name so you can safely free the name. * * Returns: a new #GstPad, or NULL in case of an error. */ @@ -414,24 +438,32 @@ gst_pad_new_from_template (GstPadTemplate * templ, const gchar * name) templ, name); } -/* FIXME 0.9: GST_PAD_UNKNOWN needs to die! */ /** * gst_pad_get_direction: * @pad: a #GstPad to get the direction of. * - * Gets the direction of the pad. + * Gets the direction of the pad. The direction of the pad is + * decided at construction time so this function does not take + * the LOCK. * * Returns: the #GstPadDirection of the pad. + * + * MT safe. */ GstPadDirection gst_pad_get_direction (GstPad * pad) { + GstPadDirection result; + + /* pad unkown is a little silly but we need some sort of + * error return value */ g_return_val_if_fail (GST_IS_PAD (pad), GST_PAD_UNKNOWN); - if (GST_IS_REAL_PAD (pad)) - return GST_PAD_DIRECTION (pad); - else - return GST_PAD_UNKNOWN; + /* since the direction cannot change at runtime we can + * safely read without locking. */ + result = GST_PAD_DIRECTION (pad); + + return result; } /** @@ -440,31 +472,33 @@ gst_pad_get_direction (GstPad * pad) * @active: TRUE to activate the pad. * * Activates or deactivates the given pad. + * + * Returns: TRUE if the operation was successfull. */ -void +gboolean gst_pad_set_active (GstPad * pad, gboolean active) { GstRealPad *realpad; gboolean old; GstPadLink *link; - g_return_if_fail (GST_IS_PAD (pad)); + g_return_val_if_fail (GST_IS_PAD (pad), FALSE); old = GST_PAD_IS_ACTIVE (pad); if (old == active) - return; + return TRUE; realpad = GST_PAD_REALIZE (pad); if (active) { GST_CAT_DEBUG (GST_CAT_PADS, "activating pad %s:%s", GST_DEBUG_PAD_NAME (realpad)); - GST_FLAG_UNSET (realpad, GST_PAD_DISABLED); + GST_FLAG_SET (realpad, GST_PAD_ACTIVE); } else { GST_CAT_DEBUG (GST_CAT_PADS, "de-activating pad %s:%s", GST_DEBUG_PAD_NAME (realpad)); - GST_FLAG_SET (realpad, GST_PAD_DISABLED); + GST_FLAG_UNSET (realpad, GST_PAD_ACTIVE); } link = GST_RPAD_LINK (realpad); if (link) { @@ -478,6 +512,8 @@ gst_pad_set_active (GstPad * pad, gboolean active) } g_object_notify (G_OBJECT (realpad), "active"); + + return TRUE; } /** @@ -533,47 +569,27 @@ gst_pad_set_active_recursive (GstPad * pad, gboolean active) * Query if a pad is active * * Returns: TRUE if the pad is active. + * + * MT safe. */ gboolean gst_pad_is_active (GstPad * pad) { + gboolean result = FALSE; + GstRealPad *realpad; + g_return_val_if_fail (GST_IS_PAD (pad), FALSE); - return !GST_FLAG_IS_SET (pad, GST_PAD_DISABLED); -} + GST_PAD_REALIZE_AND_LOCK (pad, realpad, lost_ghostpad); + result = GST_FLAG_IS_SET (realpad, GST_PAD_ACTIVE); + GST_UNLOCK (realpad); -/** - * gst_pad_set_name: - * @pad: a #GstPad to set the name of. - * @name: the name of the pad. - * - * Sets the name of a pad. If name is NULL, then a guaranteed unique - * name will be assigned. - */ -void -gst_pad_set_name (GstPad * pad, const gchar * name) -{ - g_return_if_fail (GST_IS_PAD (pad)); + return result; - gst_object_set_name (GST_OBJECT (pad), name); -} - -/* FIXME 0.9: This function must die */ -/** - * gst_pad_get_name: - * @pad: a #GstPad to get the name of. - * - * Gets the name of a pad. - * - * Returns: the name of the pad. This is not a newly allocated pointer - * so you must not free it. - */ -const gchar * -gst_pad_get_name (GstPad * pad) -{ - g_return_val_if_fail (GST_IS_PAD (pad), NULL); - - return GST_OBJECT_NAME (pad); +lost_ghostpad: + { + return FALSE; + } } /** @@ -582,7 +598,7 @@ gst_pad_get_name (GstPad * pad) * @chain: the #GstPadChainFunction to set. * * Sets the given chain function for the pad. The chain function is called to - * process a #GstData input buffer. + * process a #GstBuffer input buffer. */ void gst_pad_set_chain_function (GstPad * pad, GstPadChainFunction chain) @@ -627,7 +643,6 @@ void gst_pad_set_event_function (GstPad * pad, GstPadEventFunction event) { g_return_if_fail (GST_IS_REAL_PAD (pad)); - g_return_if_fail (GST_RPAD_DIRECTION (pad) == GST_PAD_SRC); GST_RPAD_EVENTFUNC (pad) = event; @@ -1081,13 +1096,26 @@ gst_pad_unlink (GstPad * srcpad, GstPad * sinkpad) * Checks if a @pad is linked to another pad or not. * * Returns: TRUE if the pad is linked, FALSE otherwise. + * + * MT safe. */ gboolean gst_pad_is_linked (GstPad * pad) { + gboolean result; + GstRealPad *realpad; + g_return_val_if_fail (GST_IS_PAD (pad), FALSE); - return GST_PAD_PEER (pad) != NULL; + GST_PAD_REALIZE_AND_LOCK (pad, realpad, lost_ghostpad); + result = (GST_PAD_PEER (realpad) != NULL); + GST_UNLOCK (realpad); + return result; + +lost_ghostpad: + { + return FALSE; + } } static gboolean @@ -1132,13 +1160,13 @@ static void gst_pad_link_free (GstPadLink * link) { if (link->srccaps) - gst_caps_free (link->srccaps); + gst_caps_unref (link->srccaps); if (link->sinkcaps) - gst_caps_free (link->sinkcaps); + gst_caps_unref (link->sinkcaps); if (link->filtercaps) - gst_caps_free (link->filtercaps); + gst_caps_unref (link->filtercaps); if (link->caps) - gst_caps_free (link->caps); + gst_caps_unref (link->caps); if (link->temp_store) gst_data_unref (link->temp_store); #ifdef USE_POISONING @@ -1153,7 +1181,7 @@ gst_pad_link_intersect (GstPadLink * link) GstCaps *pad_intersection; if (link->caps) - gst_caps_free (link->caps); + gst_caps_unref (link->caps); GST_DEBUG ("intersecting link from %s:%s to %s:%s", GST_DEBUG_PAD_NAME (link->srcpad), GST_DEBUG_PAD_NAME (link->sinkpad)); @@ -1166,7 +1194,7 @@ gst_pad_link_intersect (GstPadLink * link) if (link->filtercaps) { GST_DEBUG ("unfiltered intersection %" GST_PTR_FORMAT, pad_intersection); link->caps = gst_caps_intersect (pad_intersection, link->filtercaps); - gst_caps_free (pad_intersection); + gst_caps_unref (pad_intersection); } else { link->caps = pad_intersection; } @@ -1266,7 +1294,7 @@ gst_pad_link_fixate (GstPadLink * link) } else if (gst_caps_is_empty (newcaps)) { g_warning ("a fixation function did not fixate correctly, it returned empty caps"); - gst_caps_free (newcaps); + gst_caps_unref (newcaps); continue; } else if (gst_caps_is_any (caps)) { bad = gst_caps_is_any (newcaps); @@ -1274,7 +1302,7 @@ gst_pad_link_fixate (GstPadLink * link) GstCaps *test = gst_caps_subtract (caps, newcaps); bad = gst_caps_is_empty (test); - gst_caps_free (test); + gst_caps_unref (test); /* simplifying is ok, too */ if (bad) bad = (gst_caps_get_size (newcaps) >= gst_caps_get_size (caps)); @@ -1288,11 +1316,11 @@ gst_pad_link_fixate (GstPadLink * link) newcaps_str, caps_str); g_free (newcaps_str); g_free (caps_str); - gst_caps_free (newcaps); + gst_caps_unref (newcaps); } else #endif { - gst_caps_free (caps); + gst_caps_unref (caps); caps = newcaps; break; } @@ -1590,6 +1618,13 @@ gst_pad_try_set_caps (GstPad * pad, const GstCaps * caps) return ret; } +/* FIXME, temporarily put here until real code gets backported */ +gboolean +gst_pad_set_caps (GstPad * pad, GstCaps * caps) +{ + return TRUE; +} + /** * gst_pad_try_set_caps_nonfixed: * @pad: a real #GstPad @@ -1625,10 +1660,10 @@ gst_pad_try_set_caps_nonfixed (GstPad * pad, const GstCaps * caps) intersection = gst_caps_intersect (caps, GST_PAD_CAPS (pad)); if (!gst_caps_is_empty (intersection)) { - gst_caps_free (intersection); + gst_caps_unref (intersection); return GST_PAD_LINK_OK; } - gst_caps_free (intersection); + gst_caps_unref (intersection); } link = gst_pad_link_new (); @@ -1660,131 +1695,6 @@ gst_pad_try_set_caps_nonfixed (GstPad * pad, const GstCaps * caps) return ret; } -/** - * gst_pad_can_link_filtered: - * @srcpad: the source #GstPad to link. - * @sinkpad: the sink #GstPad to link. - * @filtercaps: the filter #GstCaps. - * - * Checks if the source pad and the sink pad can be linked when constrained - * by the given filter caps. Both @srcpad and @sinkpad must be unlinked. - * - * Returns: TRUE if the pads can be linked, FALSE otherwise. - */ -gboolean -gst_pad_can_link_filtered (GstPad * srcpad, GstPad * sinkpad, - const GstCaps * filtercaps) -{ - GstRealPad *realsrc, *realsink; - GstPadLink *link; - - /* FIXME This function is gross. It's almost a direct copy of - * gst_pad_link_filtered(). Any decent programmer would attempt - * to merge the two functions, which I will do some day. --ds - */ - - /* generic checks */ - g_return_val_if_fail (srcpad != NULL, FALSE); - g_return_val_if_fail (GST_IS_PAD (srcpad), FALSE); - g_return_val_if_fail (sinkpad != NULL, FALSE); - g_return_val_if_fail (GST_IS_PAD (sinkpad), FALSE); - - GST_CAT_INFO (GST_CAT_PADS, "trying to link %s:%s and %s:%s", - GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (sinkpad)); - - /* now we need to deal with the real/ghost stuff */ - realsrc = GST_PAD_REALIZE (srcpad); - realsink = GST_PAD_REALIZE (sinkpad); - - if ((GST_PAD (realsrc) != srcpad) || (GST_PAD (realsink) != sinkpad)) { - GST_CAT_INFO (GST_CAT_PADS, "*actually* linking %s:%s and %s:%s", - GST_DEBUG_PAD_NAME (realsrc), GST_DEBUG_PAD_NAME (realsink)); - } - /* FIXME: shouldn't we convert this to g_return_val_if_fail? */ - if (GST_RPAD_PEER (realsrc) != NULL) { - GST_CAT_INFO (GST_CAT_PADS, "Real source pad %s:%s has a peer, failed", - GST_DEBUG_PAD_NAME (realsrc)); - return FALSE; - } - if (GST_RPAD_PEER (realsink) != NULL) { - GST_CAT_INFO (GST_CAT_PADS, "Real sink pad %s:%s has a peer, failed", - GST_DEBUG_PAD_NAME (realsink)); - return FALSE; - } - if (GST_PAD_PARENT (realsrc) == NULL) { - GST_CAT_INFO (GST_CAT_PADS, "Real src pad %s:%s has no parent, failed", - GST_DEBUG_PAD_NAME (realsrc)); - return FALSE; - } - if (GST_PAD_PARENT (realsink) == NULL) { - GST_CAT_INFO (GST_CAT_PADS, "Real sink pad %s:%s has no parent, failed", - GST_DEBUG_PAD_NAME (realsrc)); - return FALSE; - } - - if (!gst_pad_check_schedulers (realsrc, realsink)) { - g_warning ("linking pads with different scheds requires " - "exactly one decoupled element (such as queue)"); - return FALSE; - } - - g_return_val_if_fail (realsrc != NULL, GST_PAD_LINK_REFUSED); - g_return_val_if_fail (realsink != NULL, GST_PAD_LINK_REFUSED); - - link = gst_pad_link_new (); - - if (GST_RPAD_DIRECTION (realsrc) == GST_PAD_SRC) { - link->srcpad = GST_PAD (realsrc); - link->sinkpad = GST_PAD (realsink); - } else { - link->srcpad = GST_PAD (realsink); - link->sinkpad = GST_PAD (realsrc); - } - - if (GST_RPAD_DIRECTION (link->srcpad) != GST_PAD_SRC) { - GST_CAT_INFO (GST_CAT_PADS, - "Real src pad %s:%s is not a source pad, failed", - GST_DEBUG_PAD_NAME (link->srcpad)); - gst_pad_link_free (link); - return FALSE; - } - if (GST_RPAD_DIRECTION (link->sinkpad) != GST_PAD_SINK) { - GST_CAT_INFO (GST_CAT_PADS, "Real sink pad %s:%s is not a sink pad, failed", - GST_DEBUG_PAD_NAME (link->sinkpad)); - gst_pad_link_free (link); - return FALSE; - } - - link->srccaps = gst_pad_get_caps (link->srcpad); - link->sinkcaps = gst_pad_get_caps (link->sinkpad); - if (filtercaps) - link->filtercaps = gst_caps_copy (filtercaps); - - gst_pad_link_intersect (link); - if (gst_caps_is_empty (link->caps)) { - gst_pad_link_free (link); - return FALSE; - } - - gst_pad_link_free (link); - return TRUE; -} - -/** - * gst_pad_can_link: - * @srcpad: the source #GstPad to link. - * @sinkpad: the sink #GstPad to link. - * - * Checks if the source pad and the sink pad can be linked. - * - * Returns: TRUE if the pads can be linked, FALSE otherwise. - */ -gboolean -gst_pad_can_link (GstPad * srcpad, GstPad * sinkpad) -{ - return gst_pad_can_link_filtered (srcpad, sinkpad, NULL); -} - /** * gst_pad_link_filtered: * @srcpad: the source #GstPad to link. @@ -1925,50 +1835,14 @@ gst_pad_link (GstPad * srcpad, GstPad * sinkpad) return gst_pad_link_filtered (srcpad, sinkpad, NULL); } -/* FIXME 0.9: Remove this */ -/** - * gst_pad_set_parent: - * @pad: a #GstPad to set the parent of. - * @parent: the new parent #GstElement. - * - * Sets the parent object of a pad. Deprecated, use gst_object_set_parent() - * instead. - */ -void -gst_pad_set_parent (GstPad * pad, GstElement * parent) -{ - g_return_if_fail (GST_IS_PAD (pad)); - g_return_if_fail (GST_PAD_PARENT (pad) == NULL); - g_return_if_fail (GST_IS_ELEMENT (parent)); - - gst_object_set_parent (GST_OBJECT (pad), GST_OBJECT (parent)); -} - -/* FIXME 0.9: Remove this */ -/** - * gst_pad_get_parent: - * @pad: the #GstPad to get the parent of. - * - * Gets the parent object of this pad. Deprecated, use gst_object_get_parent() - * instead. - * - * Returns: the parent #GstElement. - */ -GstElement * -gst_pad_get_parent (GstPad * pad) -{ - g_return_val_if_fail (pad != NULL, NULL); - g_return_val_if_fail (GST_IS_PAD (pad), NULL); - - return GST_PAD_PARENT (pad); -} - static void gst_pad_set_pad_template (GstPad * pad, GstPadTemplate * templ) { /* this function would need checks if it weren't static */ + GST_LOCK (pad); gst_object_replace ((GstObject **) & pad->padtemplate, (GstObject *) templ); + GST_UNLOCK (pad); if (templ) { gst_object_sink (GST_OBJECT (templ)); @@ -2039,34 +1913,40 @@ gst_pad_get_scheduler (GstPad * pad) * Gets the real parent object of this pad. If the pad * is a ghost pad, the actual owner of the real pad is * returned, as opposed to #gst_pad_get_parent(). + * Unref the object after use. * * Returns: the parent #GstElement. + * + * MT safe. */ GstElement * gst_pad_get_real_parent (GstPad * pad) { + GstRealPad *realpad; + GstElement *element; + g_return_val_if_fail (GST_IS_PAD (pad), NULL); - return GST_PAD_PARENT (GST_PAD (GST_PAD_REALIZE (pad))); + GST_PAD_REALIZE_AND_LOCK (pad, realpad, lost_ghostpad); + element = GST_PAD_PARENT (realpad); + if (element) + gst_object_ref (GST_OBJECT (element)); + GST_UNLOCK (realpad); + + return element; + +lost_ghostpad: + { + return NULL; + } } -/* FIXME 0.9: Make static. */ -/** - * gst_pad_add_ghost_pad: - * @pad: a #GstPad to attach the ghost pad to. - * @ghostpad: the ghost #GstPad to to the pad. - * - * Adds a ghost pad to a pad. Private function, will be removed from the API in - * 0.9. - */ -void +/* FIXME not MT safe */ +static void gst_pad_add_ghost_pad (GstPad * pad, GstPad * ghostpad) { GstRealPad *realpad; - g_return_if_fail (GST_IS_PAD (pad)); - g_return_if_fail (GST_IS_GHOST_PAD (ghostpad)); - /* if we're ghosting a ghost pad, drill down to find the real pad */ realpad = (GstRealPad *) pad; while (GST_IS_GHOST_PAD (realpad)) @@ -2079,21 +1959,11 @@ gst_pad_add_ghost_pad (GstPad * pad, GstPad * ghostpad) gst_pad_set_pad_template (GST_PAD (ghostpad), GST_PAD_PAD_TEMPLATE (pad)); } -/* FIXME 0.9: Make static. */ -/** - * gst_pad_remove_ghost_pad: - * @pad: a #GstPad to remove the ghost pad from. - * @ghostpad: the ghost #GstPad to remove from the pad. - * - * Removes a ghost pad from a pad. Private, will be removed from the API in 0.9. - */ -void +static void gst_pad_remove_ghost_pad (GstPad * pad, GstPad * ghostpad) { GstRealPad *realpad; - g_return_if_fail (GST_IS_PAD (pad)); - g_return_if_fail (GST_IS_GHOST_PAD (ghostpad)); realpad = GST_PAD_REALIZE (pad); g_return_if_fail (GST_GPAD_REALPAD (ghostpad) == realpad); @@ -2159,7 +2029,8 @@ _gst_pad_default_fixate_value (const GValue * value, GValue * dest) } static gboolean -_gst_pad_default_fixate_foreach (GQuark field_id, GValue * value, gpointer s) +_gst_pad_default_fixate_foreach (GQuark field_id, const GValue * value, + gpointer s) { GstStructure *structure = (GstStructure *) s; GValue dest = { 0 }; @@ -2221,7 +2092,7 @@ gst_pad_link_unnegotiate (GstPadLink * link) g_return_if_fail (link != NULL); if (link->caps) { - gst_caps_free (link->caps); + gst_caps_unref (link->caps); link->caps = NULL; link->engaged = FALSE; if (GST_RPAD_LINK (link->srcpad) != link) { @@ -2371,98 +2242,6 @@ gst_pad_relink_filtered (GstPad * srcpad, GstPad * sinkpad, return FALSE; } -/** - * gst_pad_proxy_getcaps: - * @pad: a #GstPad to proxy. - * - * Calls gst_pad_get_allowed_caps() for every other pad belonging to the - * same element as @pad, and returns the intersection of the results. - * - * This function is useful as a default getcaps function for an element - * that can handle any stream format, but requires all its pads to have - * the same caps. Two such elements are tee and aggregator. - * - * Returns: the intersection of the other pads' allowed caps. - */ -GstCaps * -gst_pad_proxy_getcaps (GstPad * pad) -{ - GstElement *element; - const GList *pads; - GstCaps *caps, *intersected; - - g_return_val_if_fail (GST_IS_PAD (pad), NULL); - - GST_DEBUG ("proxying getcaps for %s:%s", GST_DEBUG_PAD_NAME (pad)); - - element = gst_pad_get_parent (pad); - - pads = gst_element_get_pad_list (element); - - caps = gst_caps_new_any (); - while (pads) { - GstPad *otherpad = GST_PAD (pads->data); - GstCaps *temp; - - if (otherpad != pad) { - GstCaps *allowed = gst_pad_get_allowed_caps (otherpad); - - temp = gst_caps_intersect (caps, allowed); - gst_caps_free (caps); - gst_caps_free (allowed); - caps = temp; - } - - pads = g_list_next (pads); - } - - intersected = gst_caps_intersect (caps, gst_pad_get_pad_template_caps (pad)); - gst_caps_free (caps); - return intersected; -} - -/** - * gst_pad_proxy_pad_link: - * @pad: a #GstPad to proxy from - * @caps: the #GstCaps to link with - * - * Calls gst_pad_try_set_caps() for every other pad belonging to the - * same element as @pad. If gst_pad_try_set_caps() fails on any pad, - * the proxy link fails. May be used only during negotiation. - * - * Returns: GST_PAD_LINK_OK if sucessful - */ -GstPadLinkReturn -gst_pad_proxy_pad_link (GstPad * pad, const GstCaps * caps) -{ - GstElement *element; - const GList *pads; - GstPadLinkReturn ret; - - g_return_val_if_fail (GST_IS_PAD (pad), GST_PAD_LINK_REFUSED); - g_return_val_if_fail (caps != NULL, GST_PAD_LINK_REFUSED); - - GST_DEBUG ("proxying pad link for %s:%s", GST_DEBUG_PAD_NAME (pad)); - - element = gst_pad_get_parent (pad); - - pads = gst_element_get_pad_list (element); - - while (pads) { - GstPad *otherpad = GST_PAD (pads->data); - - if (otherpad != pad) { - ret = gst_pad_try_set_caps (otherpad, caps); - if (GST_PAD_LINK_FAILED (ret)) { - return ret; - } - } - pads = g_list_next (pads); - } - - return GST_PAD_LINK_OK; -} - /** * gst_pad_proxy_fixate: * @pad: a #GstPad to proxy. @@ -2488,7 +2267,8 @@ gst_pad_proxy_fixate (GstPad * pad, const GstCaps * caps) element = gst_pad_get_parent (pad); - pads = gst_element_get_pad_list (element); + /* FIXME, not MT safe but this function will be removed */ + pads = element->pads; while (pads) { GstPad *otherpad = GST_PAD (pads->data); @@ -2505,7 +2285,7 @@ gst_pad_proxy_fixate (GstPad * pad, const GstCaps * caps) if (!gst_caps_is_empty (icaps)) { return icaps; } else { - gst_caps_free (icaps); + gst_caps_unref (icaps); } } } @@ -2680,7 +2460,7 @@ gst_pad_get_negotiated_caps (GstPad * pad) * Gets the capabilities of this pad. * * Returns: the #GstCaps of this pad. This function returns a new caps, so use - * gst_caps_free to get rid of it. + * gst_caps_unref to get rid of it. */ GstCaps * gst_pad_get_caps (GstPad * pad) @@ -2728,7 +2508,7 @@ gst_pad_get_caps (GstPad * pad) ("pad %s:%s returned caps that are not a real subset of its template caps", GST_DEBUG_PAD_NAME (realpad)); temp = gst_caps_intersect (templ_caps, caps); - gst_caps_free (caps); + gst_caps_unref (caps); caps = temp; } } @@ -2767,6 +2547,53 @@ gst_pad_get_caps (GstPad * pad) return gst_caps_new_any (); } +/** + * gst_pad_peer_get_caps: + * @pad: a #GstPad to get the peer capabilities of. + * + * Gets the capabilities of the peer connected to this pad. + * + * Returns: the #GstCaps of the peer pad. This function returns a new caps, so use + * gst_caps_unref to get rid of it. + */ +GstCaps * +gst_pad_peer_get_caps (GstPad * pad) +{ + GstRealPad *realpad, *peerpad; + GstCaps *result = NULL; + + g_return_val_if_fail (GST_IS_PAD (pad), NULL); + + /* now we need to deal with the real/ghost stuff */ + GST_PAD_REALIZE_AND_LOCK (pad, realpad, lost_ghostpad); + + GST_CAT_DEBUG (GST_CAT_CAPS, "get peer caps of %s:%s (%p)", + GST_DEBUG_PAD_NAME (realpad), realpad); + + peerpad = GST_RPAD_PEER (realpad); + if (G_UNLIKELY (peerpad == NULL)) + goto no_peer; + + gst_object_ref (GST_OBJECT (peerpad)); + GST_UNLOCK (realpad); + + result = gst_pad_get_caps (GST_PAD_CAST (peerpad)); + + gst_object_unref (GST_OBJECT (peerpad)); + + return result; + +lost_ghostpad: + { + return NULL; + } +no_peer: + { + GST_UNLOCK (realpad); + return gst_caps_new_any (); + } +} + /** * gst_pad_get_pad_template_caps: * @pad: a #GstPad to get the template capabilities from. @@ -2786,141 +2613,146 @@ gst_pad_get_pad_template_caps (GstPad * pad) if (GST_PAD_PAD_TEMPLATE (pad)) return GST_PAD_TEMPLATE_CAPS (GST_PAD_PAD_TEMPLATE (pad)); -#if 0 - /* FIXME this should be enabled some day */ - /* wingo: why? mail the list during 0.9 when you find this :) */ - g_warning ("pad %s:%s (%p) has no pad template\n", - GST_DEBUG_PAD_NAME (realpad), realpad); -#endif - return gst_static_caps_get (&anycaps); } -/* FIXME 0.9: This function should probably die, or at least be renamed to - * get_caps_by_format. */ -/** - * gst_pad_template_get_caps_by_name: - * @templ: a #GstPadTemplate to get the capabilities of. - * @name: the name of the capability to get. - * - * Gets the capability with the given name from @templ. - * - * Returns: the #GstCaps of this pad template, or NULL if not found. If you - * intend to keep a reference on the caps, make a copy (see gst_caps_copy ()). - */ -const GstCaps * -gst_pad_template_get_caps_by_name (GstPadTemplate * templ, const gchar * name) -{ - GstCaps *caps; - - g_return_val_if_fail (templ != NULL, NULL); - - caps = GST_PAD_TEMPLATE_CAPS (templ); - if (!caps) - return NULL; - - /* FIXME */ - return NULL; -} - -/* FIXME 0.9: What good is this if it only works for already-negotiated pads? */ -/** - * gst_pad_check_compatibility: - * @srcpad: the source #GstPad to check. - * @sinkpad: the sink #GstPad to check against. - * - * Checks if two pads have compatible capabilities. If neither one has yet been - * negotiated, returns TRUE for no good reason. - * - * Returns: TRUE if they are compatible or if the capabilities could not be - * checked, FALSE if the capabilities are not compatible. - */ -gboolean -gst_pad_check_compatibility (GstPad * srcpad, GstPad * sinkpad) -{ - g_return_val_if_fail (GST_IS_PAD (srcpad), FALSE); - g_return_val_if_fail (GST_IS_PAD (sinkpad), FALSE); - - if (GST_PAD_CAPS (srcpad) && GST_PAD_CAPS (sinkpad)) { - if (!gst_caps_is_always_compatible (GST_PAD_CAPS (srcpad), - GST_PAD_CAPS (sinkpad))) { - return FALSE; - } else { - return TRUE; - } - } else { - GST_CAT_DEBUG (GST_CAT_PADS, - "could not check capabilities of pads (%s:%s) and (%s:%s) %p %p", - GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (sinkpad), - GST_PAD_CAPS (srcpad), GST_PAD_CAPS (sinkpad)); - return TRUE; - } -} /** * gst_pad_get_peer: * @pad: a #GstPad to get the peer of. * - * Gets the peer of @pad. + * Gets the peer of @pad. This function refs the peer pad so + * you need to unref it after use. * - * Returns: the peer #GstPad. + * Returns: the peer #GstPad. Unref after usage. + * + * MT safe. */ GstPad * gst_pad_get_peer (GstPad * pad) { + GstRealPad *realpad; + GstRealPad *result; + g_return_val_if_fail (GST_IS_PAD (pad), NULL); - return GST_PAD (GST_PAD_PEER (pad)); + GST_PAD_REALIZE_AND_LOCK (pad, realpad, lost_ghostpad); + result = GST_RPAD_PEER (realpad); + if (result) + gst_object_ref (GST_OBJECT (result)); + GST_UNLOCK (realpad); + + return GST_PAD_CAST (result); + +lost_ghostpad: + { + return NULL; + } +} + +/** + * gst_pad_realize: + * @pad: a #GstPad to realize + * + * If the pad is a #GstRealPad, it is simply returned, else + * the #GstGhostPad will be dereffed to the real pad. + * + * After this function you always receive the real pad of + * the provided pad. + * + * This function unrefs the input pad and refs the result so + * that you can write constructs like: + * + * pad = gst_pad_realize(pad) + * + * without having to unref the old pad. + * + * Returns: the real #GstPad or NULL when an old reference to a + * ghostpad is used. + * + * MT safe. + */ +GstPad * +gst_pad_realize (GstPad * pad) +{ + GstRealPad *result; + + g_return_val_if_fail (GST_IS_PAD (pad), NULL); + + GST_LOCK (pad); + result = GST_PAD_REALIZE (pad); + if (result && pad != GST_PAD_CAST (result)) { + gst_object_ref (GST_OBJECT (result)); + GST_UNLOCK (pad); + /* no other thread could dispose this since we + * hold at least one ref */ + gst_object_unref (GST_OBJECT (pad)); + } else { + GST_UNLOCK (pad); + } + + return GST_PAD_CAST (result); } /** * gst_pad_get_allowed_caps: - * @pad: a real #GstPad. + * @srcpad: a #GstPad, it must a a source pad. * - * Gets the capabilities of the allowed media types that can flow through @pad. + * Gets the capabilities of the allowed media types that can flow through @pad + * and its peer. The pad must be a source pad. * The caller must free the resulting caps. * * Returns: the allowed #GstCaps of the pad link. Free the caps when - * you no longer need it. + * you no longer need it. This function returns NULL when the @pad has no + * peer. + * + * MT safe. */ GstCaps * -gst_pad_get_allowed_caps (GstPad * pad) +gst_pad_get_allowed_caps (GstPad * srcpad) { - const GstCaps *mycaps; + GstCaps *mycaps; GstCaps *caps; GstCaps *peercaps; - GstCaps *icaps; - GstPadLink *link; + GstRealPad *realpad, *peer; - g_return_val_if_fail (GST_IS_REAL_PAD (pad), NULL); + g_return_val_if_fail (GST_IS_PAD (srcpad), NULL); + + GST_PAD_REALIZE_AND_LOCK (srcpad, realpad, lost_ghostpad); + + if (G_UNLIKELY ((peer = GST_RPAD_PEER (realpad)) == NULL)) + goto no_peer; GST_CAT_DEBUG (GST_CAT_PROPERTIES, "%s:%s: getting allowed caps", - GST_DEBUG_PAD_NAME (pad)); + GST_DEBUG_PAD_NAME (realpad)); - mycaps = gst_pad_get_pad_template_caps (pad); - if (GST_RPAD_PEER (pad) == NULL) { - GST_CAT_DEBUG (GST_CAT_PROPERTIES, "%s:%s: no peer, returning template", - GST_DEBUG_PAD_NAME (pad)); - return gst_caps_copy (mycaps); - } + gst_object_ref (GST_OBJECT_CAST (peer)); + GST_UNLOCK (realpad); + mycaps = gst_pad_get_caps (GST_PAD_CAST (realpad)); + + peercaps = gst_pad_get_caps (GST_PAD_CAST (peer)); + gst_object_unref (GST_OBJECT_CAST (peer)); - peercaps = gst_pad_get_caps (GST_PAD_PEER (pad)); caps = gst_caps_intersect (mycaps, peercaps); - gst_caps_free (peercaps); + gst_caps_unref (peercaps); + gst_caps_unref (mycaps); - link = GST_RPAD_LINK (pad); - if (link->filtercaps) { - icaps = gst_caps_intersect (caps, link->filtercaps); - gst_caps_free (caps); - GST_CAT_DEBUG (GST_CAT_PROPERTIES, - "%s:%s: returning filtered intersection with peer", - GST_DEBUG_PAD_NAME (pad)); - return icaps; - } else { - GST_CAT_DEBUG (GST_CAT_PROPERTIES, - "%s:%s: returning unfiltered intersection with peer", - GST_DEBUG_PAD_NAME (pad)); - return caps; + GST_CAT_DEBUG (GST_CAT_CAPS, "allowed caps %" GST_PTR_FORMAT, caps); + + return caps; + +lost_ghostpad: + { + GST_UNLOCK (srcpad); + return NULL; + } +no_peer: + { + GST_CAT_DEBUG (GST_CAT_PROPERTIES, "%s:%s: no peer", + GST_DEBUG_PAD_NAME (realpad)); + GST_UNLOCK (realpad); + + return NULL; } } @@ -2998,7 +2830,11 @@ gst_pad_alloc_buffer (GstPad * pad, guint64 offset, gint size) static void gst_real_pad_dispose (GObject * object) { - GstPad *pad = GST_PAD (object); + GstPad *pad; + GstRealPad *rpad; + + pad = GST_PAD (object); + rpad = GST_REAL_PAD (object); /* No linked pad can ever be disposed. * It has to have a parent to be linked @@ -3012,10 +2848,10 @@ gst_real_pad_dispose (GObject * object) GST_DEBUG_PAD_NAME (pad)); /* we destroy the ghostpads, because they are nothing without the real pad */ - if (GST_REAL_PAD (pad)->ghostpads) { + if (rpad->ghostpads) { GList *orig, *ghostpads; - orig = ghostpads = g_list_copy (GST_REAL_PAD (pad)->ghostpads); + orig = ghostpads = g_list_copy (rpad->ghostpads); while (ghostpads) { GstPad *ghostpad = GST_PAD (ghostpads->data); @@ -3036,7 +2872,7 @@ gst_real_pad_dispose (GObject * object) g_list_free (orig); /* as the ghost pads are removed, they remove themselves from ->ghostpads. So it should be empty now. Let's assert that. */ - g_assert (GST_REAL_PAD (pad)->ghostpads == NULL); + g_assert (rpad->ghostpads == NULL); } if (GST_IS_ELEMENT (GST_OBJECT_PARENT (pad))) { @@ -3055,6 +2891,16 @@ gst_real_pad_dispose (GObject * object) G_OBJECT_CLASS (real_pad_parent_class)->dispose (object); } +static void +gst_real_pad_finalize (GObject * object) +{ + //GstRealPad *rpad; + + //rpad = GST_REAL_PAD (object); + + G_OBJECT_CLASS (real_pad_parent_class)->finalize (object); +} + #ifndef GST_DISABLE_LOADSAVE /* FIXME: why isn't this on a GstElement ? */ @@ -3164,9 +3010,6 @@ gst_pad_save_thyself (GstObject * object, xmlNodePtr parent) return parent; } -/* FIXME: shouldn't it be gst_pad_ghost_* ? - * dunno -- wingo 7 feb 2004 - */ /** * gst_ghost_pad_save_thyself: * @pad: a ghost #GstPad to save. @@ -3455,7 +3298,8 @@ gst_pad_collectv (GstPad ** selected, const GList * padlist) } pads[i] = NULL; - return gst_pad_collect_array (GST_ELEMENT_SCHED (element), selected, pads); + return gst_pad_collect_array (GST_ELEMENT_SCHEDULER (element), selected, + pads); } /** @@ -3515,7 +3359,8 @@ gst_pad_collect_valist (GstPad ** selected, GstPad * pad, va_list var_args) pad = va_arg (var_args, GstPad *); } padlist[i] = NULL; - return gst_pad_collect_array (GST_ELEMENT_SCHED (element), selected, padlist); + return gst_pad_collect_array (GST_ELEMENT_SCHEDULER (element), selected, + padlist); } /** @@ -3654,7 +3499,7 @@ gst_pad_template_dispose (GObject * object) g_free (GST_PAD_TEMPLATE_NAME_TEMPLATE (templ)); if (GST_PAD_TEMPLATE_CAPS (templ)) { - gst_caps_free (GST_PAD_TEMPLATE_CAPS (templ)); + gst_caps_unref (GST_PAD_TEMPLATE_CAPS (templ)); } G_OBJECT_CLASS (padtemplate_parent_class)->dispose (object); @@ -3763,7 +3608,7 @@ gst_pad_template_new (const gchar * name_template, if (caps) { GstCaps *newcaps = gst_caps_copy (caps); - gst_caps_free (caps); + gst_caps_unref (caps); caps = newcaps; } #endif @@ -4029,7 +3874,7 @@ gst_pad_get_internal_links (GstPad * pad) rpad = GST_PAD_REALIZE (pad); if (GST_RPAD_INTLINKFUNC (rpad)) - res = GST_RPAD_INTLINKFUNC (rpad) (GST_PAD (rpad)); + res = GST_RPAD_INTLINKFUNC (rpad) (GST_PAD_CAST (rpad)); return res; } diff --git a/gst/gstpad.h b/gst/gstpad.h index 74787688fa..edd273f180 100644 --- a/gst/gstpad.h +++ b/gst/gstpad.h @@ -50,8 +50,9 @@ GST_EXPORT GType _gst_ghost_pad_type; #define GST_IS_PAD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_PAD)) #define GST_PAD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_PAD, GstPad)) #define GST_PAD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_PAD, GstPadClass)) +#define GST_PAD_CAST(obj) ((GstPad*)(obj)) -/* +/* * Real Pads */ #define GST_TYPE_REAL_PAD (_gst_real_pad_type) @@ -60,6 +61,7 @@ GST_EXPORT GType _gst_ghost_pad_type; #define GST_IS_REAL_PAD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_REAL_PAD)) #define GST_REAL_PAD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_REAL_PAD, GstRealPad)) #define GST_REAL_PAD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_REAL_PAD, GstRealPadClass)) +#define GST_REAL_PAD_CAST(obj) ((GstRealPad*)(obj)) /* * Ghost Pads @@ -70,6 +72,7 @@ GST_EXPORT GType _gst_ghost_pad_type; #define GST_IS_GHOST_PAD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_GHOST_PAD)) #define GST_GHOST_PAD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_GHOST_PAD, GstGhostPad)) #define GST_GHOST_PAD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_GHOST_PAD, GstGhostPadClass)) +#define GST_GHOST_PAD_CAST(obj) ((GstGhostPad*)(obj)) /*typedef struct _GstPad GstPad; */ @@ -93,8 +96,24 @@ typedef enum { #define GST_PAD_LINK_FAILED(ret) (ret < GST_PAD_LINK_OK) #define GST_PAD_LINK_SUCCESSFUL(ret) (ret >= GST_PAD_LINK_OK) +typedef enum { + GST_FLOW_OK = 0, /* data passing was ok */ + GST_FLOW_RESEND = 1, /* resend buffer, possibly with new caps */ + GST_FLOW_ERROR = -1, /* some (fatal) error occured */ + GST_FLOW_NOT_CONNECTED = -2, /* pad is not connected */ + GST_FLOW_NOT_NEGOTIATED = -3, /* pad is not negotiated */ + GST_FLOW_WRONG_STATE = -4, /* pad is in wrong state */ + GST_FLOW_UNEXPECTED = -5, /* did not expect anything, this is not fatal */ + GST_FLOW_NOT_SUPPORTED = -6 /* function not supported */ +} GstFlowReturn; + +typedef enum { + GST_ACTIVATE_NONE, + GST_ACTIVATE_PUSH, + GST_ACTIVATE_PULL, +} GstActivateMode; + /* convenience functions */ -#ifndef GST_DISABLE_DEPRECATED #ifdef G_HAVE_ISO_VARARGS #define GST_PAD_QUERY_TYPE_FUNCTION(functionname, ...) GST_QUERY_TYPE_FUNCTION (GstPad *, functionname, __VA_ARGS__); #define GST_PAD_FORMATS_FUNCTION(functionname, ...) GST_FORMATS_FUNCTION (GstPad *, functionname, __VA_ARGS__); @@ -104,8 +123,10 @@ typedef enum { #define GST_PAD_FORMATS_FUNCTION(functionname, a...) GST_FORMATS_FUNCTION (GstPad *, functionname, a); #define GST_PAD_EVENT_MASK_FUNCTION(functionname, a...) GST_EVENT_MASK_FUNCTION (GstPad *, functionname, a); #endif -#endif + +/* pad states */ +typedef gboolean (*GstPadActivateFunction) (GstPad *pad, GstActivateMode mode); /* this defines the functions used to chain buffers * pad is the sink pad (so the same chain function can be used for N pads) @@ -138,7 +159,7 @@ typedef enum { } GstPadDirection; typedef enum { - GST_PAD_DISABLED = GST_OBJECT_FLAG_LAST, + GST_PAD_ACTIVE = GST_OBJECT_FLAG_LAST, GST_PAD_NEGOTIATING, GST_PAD_DISPATCHING, @@ -194,7 +215,9 @@ struct _GstRealPad { GstPadEventFunction eventhandler; GstPadEventMaskFunction eventmaskfunc; - GList *ghostpads; + /* ghostpads */ + GList *ghostpads; + guint32 ghostpads_cookie; /* query/convert/formats functions */ GstPadConvertFunction convertfunc; @@ -285,7 +308,7 @@ struct _GstGhostPadClass { /* Some check functions (unused?) */ #define GST_PAD_IS_LINKED(pad) (GST_PAD_PEER(pad) != NULL) -#define GST_PAD_IS_ACTIVE(pad) (!GST_FLAG_IS_SET(GST_PAD_REALIZE(pad), GST_PAD_DISABLED)) +#define GST_PAD_IS_ACTIVE(pad) (GST_FLAG_IS_SET(GST_PAD_REALIZE(pad), GST_PAD_ACTIVE)) #define GST_PAD_IS_NEGOTIATING(pad) (GST_FLAG_IS_SET (pad, GST_PAD_NEGOTIATING)) #define GST_PAD_IS_DISPATCHING(pad) (GST_FLAG_IS_SET (pad, GST_PAD_DISPATCHING)) #define GST_PAD_IS_USABLE(pad) (GST_PAD_IS_LINKED (pad) && \ @@ -366,26 +389,23 @@ GstPad* gst_pad_new_from_template (GstPadTemplate *templ, const gchar *name); GstPad* gst_pad_custom_new (GType type, const gchar *name, GstPadDirection direction); GstPad* gst_pad_custom_new_from_template (GType type, GstPadTemplate *templ, const gchar *name); -void gst_pad_set_name (GstPad *pad, const gchar *name); -G_CONST_RETURN gchar* gst_pad_get_name (GstPad *pad); +#define gst_pad_get_name(pad) gst_object_get_name(GST_OBJECT(pad)) +#define gst_pad_set_name(pad,name) gst_object_set_name(GST_OBJECT(pad),name) +#define gst_pad_get_parent(pad) GST_ELEMENT(gst_object_get_parent(GST_OBJECT(pad))) +#define gst_pad_set_parent(pad,parent) gst_object_set_parent(GST_OBJECT(pad),parent) +GstElement* gst_pad_get_real_parent (GstPad *pad); + GstPadDirection gst_pad_get_direction (GstPad *pad); -void gst_pad_set_active (GstPad *pad, gboolean active); -void gst_pad_set_active_recursive (GstPad *pad, gboolean active); +gboolean gst_pad_set_active (GstPad *pad, gboolean active); gboolean gst_pad_is_active (GstPad *pad); void gst_pad_set_element_private (GstPad *pad, gpointer priv); gpointer gst_pad_get_element_private (GstPad *pad); -void gst_pad_set_parent (GstPad *pad, GstElement *parent); -GstElement* gst_pad_get_parent (GstPad *pad); -GstElement* gst_pad_get_real_parent (GstPad *pad); - GstScheduler* gst_pad_get_scheduler (GstPad *pad); -void gst_pad_add_ghost_pad (GstPad *pad, GstPad *ghostpad); -void gst_pad_remove_ghost_pad (GstPad *pad, GstPad *ghostpad); GList* gst_pad_get_ghost_pad_list (GstPad *pad); GstPadTemplate* gst_pad_get_pad_template (GstPad *pad); @@ -415,11 +435,13 @@ void gst_pad_unlink (GstPad *srcpad, GstPad *sinkpad); gboolean gst_pad_is_linked (GstPad *pad); GstPad* gst_pad_get_peer (GstPad *pad); +GstPad* gst_pad_realize (GstPad *pad); /* capsnego functions */ G_CONST_RETURN GstCaps* gst_pad_get_negotiated_caps (GstPad *pad); gboolean gst_pad_is_negotiated (GstPad *pad); GstCaps* gst_pad_get_caps (GstPad *pad); +gboolean gst_pad_set_caps (GstPad *pad, GstCaps *caps); G_CONST_RETURN GstCaps* gst_pad_get_pad_template_caps (GstPad *pad); GstPadLinkReturn gst_pad_try_set_caps (GstPad *pad, const GstCaps *caps); GstPadLinkReturn gst_pad_try_set_caps_nonfixed (GstPad *pad, const GstCaps *caps); @@ -428,17 +450,10 @@ gboolean gst_pad_check_compatibility (GstPad *srcpad, GstPad *sinkpad); void gst_pad_set_getcaps_function (GstPad *pad, GstPadGetCapsFunction getcaps); void gst_pad_set_fixate_function (GstPad *pad, GstPadFixateFunction fixate); GstCaps * gst_pad_proxy_getcaps (GstPad *pad); -GstPadLinkReturn gst_pad_proxy_pad_link (GstPad *pad, const GstCaps *caps); GstCaps * gst_pad_proxy_fixate (GstPad *pad, const GstCaps *caps); -#ifndef GST_DISABLE_DEPRECATED -GstPadLinkReturn gst_pad_proxy_link (GstPad *pad, const GstCaps *caps); -#endif gboolean gst_pad_set_explicit_caps (GstPad *pad, const GstCaps *caps); void gst_pad_use_explicit_caps (GstPad *pad); gboolean gst_pad_relink_filtered (GstPad *srcpad, GstPad *sinkpad, const GstCaps *filtercaps); -#ifndef GST_DISABLE_DEPRECATED -gboolean gst_pad_perform_negotiate (GstPad *srcpad, GstPad *sinkpad); -#endif GstPadLinkReturn gst_pad_renegotiate (GstPad *pad); void gst_pad_unnegotiate (GstPad *pad); gboolean gst_pad_try_relink_filtered (GstPad *srcpad, GstPad *sinkpad, const GstCaps *filtercaps); @@ -447,16 +462,14 @@ void gst_pad_caps_change_notify (GstPad *pad); gboolean gst_pad_recover_caps_error (GstPad *pad, const GstCaps *allowed); +GstCaps * gst_pad_peer_get_caps (GstPad * pad); + + /* data passing functions */ void gst_pad_push (GstPad *pad, GstData *data); GstData* gst_pad_pull (GstPad *pad); gboolean gst_pad_send_event (GstPad *pad, GstEvent *event); gboolean gst_pad_event_default (GstPad *pad, GstEvent *event); -#ifndef GST_DISABLE_DEPRECATED -GstPad* gst_pad_selectv (GList *padlist); -GstPad* gst_pad_select (GstPad *pad, ...); -GstPad* gst_pad_select_valist (GstPad *pad, va_list varargs); -#endif /* FIXME 0.9: rename to _select? Otherwise rename SchedulerClass pointer */ GstData * gst_pad_collectv (GstPad **selected, const GList *padlist); GstData * gst_pad_collect (GstPad **selected, GstPad *pad, ...); @@ -497,6 +510,7 @@ GList* gst_pad_get_internal_links_default (GstPad *pad); gboolean gst_pad_dispatcher (GstPad *pad, GstPadDispatcherFunction dispatch, gpointer data); +/* probes */ #define gst_pad_add_probe(pad, probe) \ (gst_probe_dispatcher_add_probe (&(GST_PAD_REALIZE (pad)->probedisp), probe)) #define gst_pad_remove_probe(pad, probe) \ @@ -520,9 +534,6 @@ GstPadTemplate* gst_pad_template_new (const gchar *name_template, GstPadTemplate * gst_static_pad_template_get (GstStaticPadTemplate *pad_template); const GstCaps* gst_pad_template_get_caps (GstPadTemplate *templ); -#ifndef GST_DISABLE_DEPRECATED -const GstCaps* gst_pad_template_get_caps_by_name (GstPadTemplate *templ, const gchar *name); -#endif #ifndef GST_DISABLE_LOADSAVE xmlNodePtr gst_ghost_pad_save_thyself (GstPad *pad, diff --git a/gst/gstpipeline.c b/gst/gstpipeline.c index acfb8f3991..f7457a7244 100644 --- a/gst/gstpipeline.c +++ b/gst/gstpipeline.c @@ -1,6 +1,6 @@ /* GStreamer * Copyright (C) 1999,2000 Erik Walthinsen - * 2000 Wim Taymans + * 2004,2005 Wim Taymans * * gstpipeline.c: Overall pipeline management element * @@ -30,7 +30,7 @@ static GstElementDetails gst_pipeline_details = GST_ELEMENT_DETAILS ("Pipeline object", "Generic/Bin", "Complete pipeline object", - "Erik Walthinsen "); + "Erik Walthinsen , Wim Taymans "); /* Pipeline signals and args */ enum @@ -39,6 +39,8 @@ enum LAST_SIGNAL }; +#define DEFAULT_DELAY 0 +#define DEFAULT_PLAY_TIMEOUT (2*GST_SECOND) enum { ARG_0 @@ -134,8 +136,8 @@ gst_pipeline_dispose (GObject * object) GstPipeline *pipeline = GST_PIPELINE (object); GstScheduler *sched; - g_assert (GST_IS_SCHEDULER (GST_ELEMENT_SCHED (pipeline))); - sched = GST_ELEMENT_SCHED (pipeline); + g_assert (GST_IS_SCHEDULER (GST_ELEMENT_SCHEDULER (pipeline))); + sched = GST_ELEMENT_SCHEDULER (pipeline); gst_scheduler_reset (sched); G_OBJECT_CLASS (parent_class)->dispose (object); @@ -148,6 +150,8 @@ gst_pipeline_dispose (GObject * object) * Create a new pipeline with the given name. * * Returns: newly created GstPipeline + * + * MT safe. */ GstElement * gst_pipeline_new (const gchar * name) @@ -160,7 +164,7 @@ gst_pipeline_change_state (GstElement * element) { switch (GST_STATE_TRANSITION (element)) { case GST_STATE_NULL_TO_READY: - gst_scheduler_setup (GST_ELEMENT_SCHED (element)); + gst_scheduler_setup (GST_ELEMENT_SCHEDULER (element)); break; case GST_STATE_READY_TO_PAUSED: case GST_STATE_PAUSED_TO_PLAYING: diff --git a/gst/gstpipeline.h b/gst/gstpipeline.h index 5dd5b11c06..9163f89014 100644 --- a/gst/gstpipeline.h +++ b/gst/gstpipeline.h @@ -24,6 +24,7 @@ #ifndef __GST_PIPELINE_H__ #define __GST_PIPELINE_H__ +#include #include G_BEGIN_DECLS @@ -35,18 +36,33 @@ G_BEGIN_DECLS #define GST_IS_PIPELINE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_PIPELINE)) #define GST_PIPELINE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_PIPELINE, GstPipelineClass)) -typedef struct _GstPipeline GstPipeline; -typedef struct _GstPipelineClass GstPipelineClass; +typedef enum { + /* this pipeline works with a fixed clock */ + GST_PIPELINE_FLAG_FIXED_CLOCK = GST_BIN_FLAG_LAST, + + /* padding */ + GST_PIPELINE_FLAG_LAST = GST_BIN_FLAG_LAST + 4 +} GstPipelineFlags; struct _GstPipeline { GstBin bin; + /*< public >*/ /* with LOCK */ + GstClock *fixed_clock; /* fixed clock if any */ + GstClockTime stream_time; + GstClockTime delay; + GstClockTime play_timeout; + + GList *eosed; /* list of elements that posted EOS */ + + /*< private >*/ gpointer _gst_reserved[GST_PADDING]; }; struct _GstPipelineClass { GstBinClass parent_class; + /*< private >*/ gpointer _gst_reserved[GST_PADDING]; }; @@ -54,6 +70,11 @@ GType gst_pipeline_get_type (void); GstElement* gst_pipeline_new (const gchar *name); +void gst_pipeline_use_clock (GstPipeline *pipeline, GstClock *clock); +void gst_pipeline_set_clock (GstPipeline *pipeline, GstClock *clock); +GstClock* gst_pipeline_get_clock (GstPipeline *pipeline); +void gst_pipeline_auto_clock (GstPipeline *pipeline); + G_END_DECLS #endif /* __GST_PIPELINE_H__ */ diff --git a/gst/gstplugin.c b/gst/gstplugin.c index 231d7dd308..cb5e635db9 100644 --- a/gst/gstplugin.c +++ b/gst/gstplugin.c @@ -114,7 +114,7 @@ gst_plugin_error_quark (void) * plugin description in a list to initialize it when we open the main * module later on. * When the main module is known, we can register the plugin right away. - * */ + */ void _gst_plugin_register_static (GstPluginDesc * desc) { diff --git a/gst/gstpluginfeature.c b/gst/gstpluginfeature.c index b16c10ca52..9fa2b234a7 100644 --- a/gst/gstpluginfeature.c +++ b/gst/gstpluginfeature.c @@ -154,23 +154,6 @@ gst_plugin_feature_type_name_filter (GstPluginFeature * feature, || !strcmp (data->name, GST_PLUGIN_FEATURE_NAME (feature)))); } -/** - * gst_plugin_feature_set_rank: - * @feature: feature to rank - * @rank: rank value - higher number means more priority rank - * - * Specifies a rank for a plugin feature, so that autoplugging uses - * the most appropriate feature. - */ -void -gst_plugin_feature_set_rank (GstPluginFeature * feature, guint rank) -{ - g_return_if_fail (feature != NULL); - g_return_if_fail (GST_IS_PLUGIN_FEATURE (feature)); - - feature->rank = rank; -} - /** * gst_plugin_feature_set_name: * @feature: a feature @@ -178,7 +161,8 @@ gst_plugin_feature_set_rank (GstPluginFeature * feature, guint rank) * * Sets the name of a plugin feature. The name uniquely identifies a feature * within all features of the same type. Renaming a plugin feature is not - * allowed. + * allowed. A copy is made of the name so you should free the supplied @name + * after calling this function. */ void gst_plugin_feature_set_name (GstPluginFeature * feature, const gchar * name) @@ -193,22 +177,6 @@ gst_plugin_feature_set_name (GstPluginFeature * feature, const gchar * name) } } -/** - * gst_plugin_feature_get rank: - * @feature: a feature - * - * Gets the rank of a plugin feature. - * - * Returns: The rank of the feature - */ -guint -gst_plugin_feature_get_rank (GstPluginFeature * feature) -{ - g_return_val_if_fail (GST_IS_PLUGIN_FEATURE (feature), GST_RANK_NONE); - - return feature->rank; -} - /** * gst_plugin_feature_get_name: * @feature: a feature @@ -224,3 +192,36 @@ gst_plugin_feature_get_name (GstPluginFeature * feature) return feature->name; } + +/** + * gst_plugin_feature_set_rank: + * @feature: feature to rank + * @rank: rank value - higher number means more priority rank + * + * Specifies a rank for a plugin feature, so that autoplugging uses + * the most appropriate feature. + */ +void +gst_plugin_feature_set_rank (GstPluginFeature * feature, guint rank) +{ + g_return_if_fail (feature != NULL); + g_return_if_fail (GST_IS_PLUGIN_FEATURE (feature)); + + feature->rank = rank; +} + +/** + * gst_plugin_feature_get rank: + * @feature: a feature + * + * Gets the rank of a plugin feature. + * + * Returns: The rank of the feature + */ +guint +gst_plugin_feature_get_rank (GstPluginFeature * feature) +{ + g_return_val_if_fail (GST_IS_PLUGIN_FEATURE (feature), GST_RANK_NONE); + + return feature->rank; +} diff --git a/gst/gstpluginfeature.h b/gst/gstpluginfeature.h index 66994070a0..c2c88a8bf8 100644 --- a/gst/gstpluginfeature.h +++ b/gst/gstpluginfeature.h @@ -58,6 +58,7 @@ struct _GstPluginFeatureClass { void (*unload_thyself) (GstPluginFeature *feature); + /*< private >*/ gpointer _gst_reserved[GST_PADDING]; }; diff --git a/gst/gstprobe.c b/gst/gstprobe.c index 1a43c8387a..eb9fd576c2 100644 --- a/gst/gstprobe.c +++ b/gst/gstprobe.c @@ -49,7 +49,9 @@ gst_probe_get_type (void) * @callback: the function to call when the probe is triggered * @user_data: data passed to the callback function * - * Create a new probe with the specified parameters + * Create a new probe with the specified parameters. The single shot + * probe will be fired only once. It is the responsability of the + * application to free the single probe after it has been fired. * * Returns: a new #GstProbe. */ @@ -255,7 +257,8 @@ gst_probe_dispatcher_dispatch (GstProbeDispatcher * disp, GstData ** data) g_slist_find (disp->probes, probe) && probe->single_shot) { disp->probes = g_slist_remove (disp->probes, probe); - gst_probe_destroy (probe); + /* do not free the probe here as it cannot be made threadsafe */ + //gst_probe_destroy (probe); } } diff --git a/gst/gstquery.c b/gst/gstquery.c index f37cc77b1f..ffb64498bf 100644 --- a/gst/gstquery.c +++ b/gst/gstquery.c @@ -1,6 +1,7 @@ /* GStreamer * Copyright (C) 1999,2000 Erik Walthinsen * 2000 Wim Taymans + * 2005 Wim Taymans * * gstquery.c: GstQueryType registration * @@ -25,10 +26,11 @@ #include "gst_private.h" #include "gstquery.h" +static GStaticMutex mutex = G_STATIC_MUTEX_INIT; static GList *_gst_queries = NULL; static GHashTable *_nick_to_query = NULL; static GHashTable *_query_type_to_nick = NULL; -static gint _n_values = 1; /* we start from 1 because 0 reserved for NONE */ +static guint32 _n_values = 1; /* we start from 1 because 0 reserved for NONE */ static GstQueryTypeDefinition standard_definitions[] = { {GST_QUERY_TOTAL, "total", "Total length"}, @@ -46,6 +48,7 @@ _gst_query_type_initialize (void) { GstQueryTypeDefinition *standards = standard_definitions; + g_static_mutex_lock (&mutex); if (_nick_to_query == NULL) { _nick_to_query = g_hash_table_new (g_str_hash, g_str_equal); _query_type_to_nick = g_hash_table_new (NULL, NULL); @@ -60,6 +63,7 @@ _gst_query_type_initialize (void) standards++; _n_values++; } + g_static_mutex_unlock (&mutex); } /** @@ -91,11 +95,13 @@ gst_query_type_register (const gchar * nick, const gchar * description) query->nick = g_strdup (nick); query->description = g_strdup (description); + g_static_mutex_lock (&mutex); g_hash_table_insert (_nick_to_query, query->nick, query); g_hash_table_insert (_query_type_to_nick, GINT_TO_POINTER (query->value), query); _gst_queries = g_list_append (_gst_queries, query); _n_values++; + g_static_mutex_unlock (&mutex); return query->value; } @@ -116,7 +122,9 @@ gst_query_type_get_by_nick (const gchar * nick) g_return_val_if_fail (nick != NULL, 0); + g_static_mutex_lock (&mutex); query = g_hash_table_lookup (_nick_to_query, nick); + g_static_mutex_unlock (&mutex); if (query != NULL) return query->value; @@ -160,18 +168,32 @@ gst_query_types_contains (const GstQueryType * types, GstQueryType type) const GstQueryTypeDefinition * gst_query_type_get_details (GstQueryType type) { - return g_hash_table_lookup (_query_type_to_nick, GINT_TO_POINTER (type)); + const GstQueryTypeDefinition *result; + + g_static_mutex_lock (&mutex); + result = g_hash_table_lookup (_query_type_to_nick, GINT_TO_POINTER (type)); + g_static_mutex_unlock (&mutex); + + return result; } /** - * gst_query_type_get_definitions: + * gst_query_type_iterate_definitions: * - * Get a list of all the registered query types. + * Get an Iterator of all the registered query types. The querytype + * definition is read only. * - * Returns: A GList of #GstQueryTypeDefinition. + * Returns: A #GstIterator of #GstQueryTypeDefinition. */ -const GList * -gst_query_type_get_definitions (void) +GstIterator * +gst_query_type_iterate_definitions (void) { - return _gst_queries; + GstIterator *result; + + g_static_mutex_lock (&mutex); + result = gst_iterator_new_list (g_static_mutex_get_mutex (&mutex), + &_n_values, &_gst_queries, NULL, NULL, NULL); + g_static_mutex_unlock (&mutex); + + return result; } diff --git a/gst/gstquery.h b/gst/gstquery.h index 32053e2f84..3b6ae641aa 100644 --- a/gst/gstquery.h +++ b/gst/gstquery.h @@ -1,6 +1,7 @@ /* GStreamer * Copyright (C) 1999,2000 Erik Walthinsen * 2000 Wim Taymans + * 2005 Wim Taymans * * gstquery.h: GstQuery API declaration * @@ -26,6 +27,8 @@ #include +#include + G_BEGIN_DECLS typedef enum { @@ -87,8 +90,8 @@ gboolean gst_query_types_contains (const GstQueryType *types, GstQ /* query for query details */ G_CONST_RETURN GstQueryTypeDefinition* - gst_query_type_get_details (GstQueryType type); -G_CONST_RETURN GList* gst_query_type_get_definitions (void); + gst_query_type_get_details (GstQueryType type); +GstIterator* gst_query_type_iterate_definitions (void); G_END_DECLS diff --git a/gst/gstqueue.c b/gst/gstqueue.c index b9ade24055..0ed213dd70 100644 --- a/gst/gstqueue.c +++ b/gst/gstqueue.c @@ -434,6 +434,8 @@ gst_queue_link_sink (GstPad * pad, const GstCaps * caps) GST_QUEUE_MUTEX_UNLOCK; } + link_ret = GST_PAD_LINK_OK; +#if 0 link_ret = gst_pad_proxy_pad_link (pad, caps); if (GST_PAD_LINK_SUCCESSFUL (link_ret)) { @@ -441,6 +443,7 @@ gst_queue_link_sink (GstPad * pad, const GstCaps * caps) * the pads become unnegotiated while we have buffers */ gst_caps_replace (&queue->negotiated_caps, gst_caps_copy (caps)); } +#endif return link_ret; } @@ -459,8 +462,10 @@ gst_queue_link_src (GstPad * pad, const GstCaps * caps) } return GST_PAD_LINK_REFUSED; } - +#if 0 link_ret = gst_pad_proxy_pad_link (pad, caps); +#endif + link_ret = GST_PAD_LINK_OK; if (GST_PAD_LINK_SUCCESSFUL (link_ret)) { /* we store an extra copy of the negotiated caps, just in case diff --git a/gst/gstscheduler.c b/gst/gstscheduler.c index 1bc7c15b61..fe0470e2f3 100644 --- a/gst/gstscheduler.c +++ b/gst/gstscheduler.c @@ -227,14 +227,14 @@ gst_scheduler_add_element (GstScheduler * sched, GstElement * element) g_return_if_fail (GST_IS_ELEMENT (element)); /* if it's already in this scheduler, don't bother doing anything */ - if (GST_ELEMENT_SCHED (element) == sched) { + if (GST_ELEMENT_SCHEDULER (element) == sched) { GST_CAT_DEBUG (GST_CAT_SCHEDULING, "element %s already in scheduler %p", GST_ELEMENT_NAME (element), sched); return; } /* if it's not inside this scheduler, it has to be NULL */ - g_assert (GST_ELEMENT_SCHED (element) == NULL); + g_assert (GST_ELEMENT_SCHEDULER (element) == NULL); if (gst_element_provides_clock (element)) { sched->clock_providers = g_list_prepend (sched->clock_providers, element); @@ -970,7 +970,7 @@ gst_scheduler_factory_create (GstSchedulerFactory * factory, sched = GST_SCHEDULER (g_object_new (factory->type, NULL)); sched->parent = parent; - GST_ELEMENT_SCHED (parent) = sched; + GST_ELEMENT_SCHEDULER (parent) = sched; /* let's refcount the scheduler */ gst_object_ref (GST_OBJECT (sched)); diff --git a/gst/gststructure.c b/gst/gststructure.c index b456c56c61..abe1f4382f 100644 --- a/gst/gststructure.c +++ b/gst/gststructure.c @@ -40,6 +40,10 @@ struct _GstStructureField #define GST_STRUCTURE_FIELD(structure, index) \ &g_array_index((structure)->fields, GstStructureField, (index)) +#define IS_MUTABLE(structure) \ + (!(structure)->parent_refcount || \ + gst_atomic_int_read ((structure)->parent_refcount) == 1) + static void gst_structure_set_field (GstStructure * structure, GstStructureField * field); static GstStructureField *gst_structure_get_field (const GstStructure * @@ -173,6 +177,30 @@ gst_structure_new_valist (const gchar * name, return structure; } +/** + * gst_structure_set_parent_refcount: + * @structure: a #GstStructure + * @refcount: a pointer to the parent's #GstAtomicInt refcount + * + * Sets the parent_refcount field of #GstStructure. This field is used to + * determine whether a structure is mutable or not. This function should only be + * called by code implementing parent objects of GstStructure, as described in + * the MT Refcounting section of the design documents. + * + * Returns: a new #GstStructure. + */ +void +gst_structure_set_parent_refcount (GstStructure * structure, + GstAtomicInt * refcount) +{ + if (structure->parent_refcount) + g_return_if_fail (refcount == NULL); + else + g_return_if_fail (refcount != NULL); + + structure->parent_refcount = refcount; +} + /** * gst_structure_copy: * @structure: a #GstStructure to duplicate @@ -211,7 +239,8 @@ gst_structure_copy (const GstStructure * structure) * gst_structure_free: * @structure: the #GstStructure to free * - * Frees a #GstStructure and all its fields and values. + * Frees a #GstStructure and all its fields and values. The structure must not + * parent when this function is called. */ void gst_structure_free (GstStructure * structure) @@ -220,6 +249,7 @@ gst_structure_free (GstStructure * structure) int i; g_return_if_fail (structure != NULL); + g_return_if_fail (structure->parent_refcount == NULL); for (i = 0; i < structure->fields->len; i++) { field = GST_STRUCTURE_FIELD (structure, i); @@ -280,6 +310,7 @@ gst_structure_set_name (GstStructure * structure, const gchar * name) { g_return_if_fail (structure != NULL); g_return_if_fail (name != NULL); + g_return_if_fail (IS_MUTABLE (structure)); structure->name = g_quark_from_string (name); } @@ -302,6 +333,7 @@ gst_structure_id_set_value (GstStructure * structure, g_return_if_fail (structure != NULL); g_return_if_fail (G_IS_VALUE (value)); + g_return_if_fail (IS_MUTABLE (structure)); gsfield.name = field; gst_value_init_and_copy (&gsfield.value, value); @@ -326,6 +358,7 @@ gst_structure_set_value (GstStructure * structure, g_return_if_fail (structure != NULL); g_return_if_fail (fieldname != NULL); g_return_if_fail (G_IS_VALUE (value)); + g_return_if_fail (IS_MUTABLE (structure)); gst_structure_id_set_value (structure, g_quark_from_string (fieldname), value); @@ -373,6 +406,7 @@ gst_structure_set_valist (GstStructure * structure, char *s; g_return_if_fail (structure != NULL); + g_return_if_fail (IS_MUTABLE (structure)); while (fieldname) { GstStructureField field = { 0 }; @@ -451,18 +485,9 @@ gst_structure_set_valist (GstStructure * structure, } } -/** - * gst_structure_set_field: - * @structure: a #GstStructure - * @field: the #GstStructureField to set - * - * Sets a field in the structure. If the structure currently contains - * a field with the same name, it is replaced with the provided field. - * Otherwise, the field is added to the structure. The field's value - * is not deeply copied. - * - * This function is intended mainly for internal use. The function - * #gst_structure_set() is recommended instead of this one. +/* If the structure currently contains a field with the same name, it is + * replaced with the provided field. Otherwise, the field is added to the + * structure. The field's value is not deeply copied. */ static void gst_structure_set_field (GstStructure * structure, GstStructureField * field) @@ -483,15 +508,7 @@ gst_structure_set_field (GstStructure * structure, GstStructureField * field) g_array_append_val (structure->fields, *field); } -/* FIXME: is this private ? if so remove gtk-doc - * gst_structure_id_get_field: - * @structure: a #GstStructure - * @field_id: the GQuark of the field to get - * - * Gets the specified field from the structure. If there is no - * field with the given ID, NULL is returned. - * - * Returns: the #GstStructureField with the given ID +/* If there is no field with the given ID, NULL is returned. */ static GstStructureField * gst_structure_id_get_field (const GstStructure * structure, GQuark field_id) @@ -511,15 +528,7 @@ gst_structure_id_get_field (const GstStructure * structure, GQuark field_id) return NULL; } -/** - * gst_structure_get_field: - * @structure: a #GstStructure - * @fieldname: the name of the field to get - * - * Gets the specified field from the structure. If there is no - * field with the given ID, NULL is returned. - * - * Returns: the #GstStructureField with the given name +/* If there is no field with the given ID, NULL is returned. */ static GstStructureField * gst_structure_get_field (const GstStructure * structure, @@ -598,6 +607,7 @@ gst_structure_remove_field (GstStructure * structure, const gchar * fieldname) g_return_if_fail (structure != NULL); g_return_if_fail (fieldname != NULL); + g_return_if_fail (IS_MUTABLE (structure)); id = g_quark_from_string (fieldname); @@ -631,6 +641,7 @@ gst_structure_remove_fields (GstStructure * structure, g_return_if_fail (structure != NULL); g_return_if_fail (fieldname != NULL); + /* mutability checked in remove_field */ va_start (varargs, fieldname); @@ -656,6 +667,7 @@ gst_structure_remove_fields_valist (GstStructure * structure, g_return_if_fail (structure != NULL); g_return_if_fail (fieldname != NULL); + /* mutability checked in remove_field */ while (field) { gst_structure_remove_field (structure, field); @@ -676,6 +688,7 @@ gst_structure_remove_all_fields (GstStructure * structure) int i; g_return_if_fail (structure != NULL); + g_return_if_fail (IS_MUTABLE (structure)); for (i = structure->fields->len - 1; i >= 0; i--) { field = GST_STRUCTURE_FIELD (structure, i); @@ -736,19 +749,57 @@ gst_structure_n_fields (const GstStructure * structure) * @func: a function to call for each field * @user_data: private data * - * Calls the provided function once for each field in the #GstStructure. + * Calls the provided function once for each field in the #GstStructure. The + * function must not modify the fields. Also see gst_structure_map_in_place(). * * Returns: TRUE if the supplied function returns TRUE For each of the fields, * FALSE otherwise. */ gboolean -gst_structure_foreach (GstStructure * structure, +gst_structure_foreach (const GstStructure * structure, GstStructureForeachFunc func, gpointer user_data) { int i; GstStructureField *field; gboolean ret; + g_return_val_if_fail (structure != NULL, FALSE); + + for (i = 0; i < structure->fields->len; i++) { + field = GST_STRUCTURE_FIELD (structure, i); + + ret = func (field->name, &field->value, user_data); + if (!ret) + return FALSE; + } + + return TRUE; +} + +/** + * gst_structure_map_in_place: + * @structure: a #GstStructure + * @func: a function to call for each field + * @user_data: private data + * + * Calls the provided function once for each field in the #GstStructure. In + * contrast to gst_structure_foreach(), the function may modify the fields. The + * structure must be mutable. + * + * Returns: TRUE if the supplied function returns TRUE For each of the fields, + * FALSE otherwise. + */ +gboolean +gst_structure_map_in_place (GstStructure * structure, + GstStructureMapFunc func, gpointer user_data) +{ + int i; + GstStructureField *field; + gboolean ret; + + g_return_val_if_fail (structure != NULL, FALSE); + g_return_val_if_fail (IS_MUTABLE (structure), FALSE); + for (i = 0; i < structure->fields->len; i++) { field = GST_STRUCTURE_FIELD (structure, i); @@ -1548,3 +1599,134 @@ gst_structure_copy_conditional (const GstStructure * structure) return gst_structure_copy (structure); return NULL; } + +/* fixate utility functions */ + +/** + * gst_caps_structure_fixate_field_nearest_int: + * @structure: a #GstStructure + * @field_name: a field in @structure + * @target: the target value of the fixation + * + * Fixates a #GstStructure by changing the given field to the nearest + * integer to @target that is a subset of the existing field. + * + * Returns: TRUE if the structure could be fixated + */ +gboolean +gst_caps_structure_fixate_field_nearest_int (GstStructure * structure, + const char *field_name, int target) +{ + const GValue *value; + + g_return_val_if_fail (gst_structure_has_field (structure, field_name), FALSE); + g_return_val_if_fail (IS_MUTABLE (structure), FALSE); + + value = gst_structure_get_value (structure, field_name); + + if (G_VALUE_TYPE (value) == G_TYPE_INT) { + /* already fixed */ + return FALSE; + } else if (G_VALUE_TYPE (value) == GST_TYPE_INT_RANGE) { + int x; + + x = gst_value_get_int_range_min (value); + if (target < x) + target = x; + x = gst_value_get_int_range_max (value); + if (target > x) + target = x; + gst_structure_set (structure, field_name, G_TYPE_INT, target, NULL); + return TRUE; + } else if (G_VALUE_TYPE (value) == GST_TYPE_LIST) { + const GValue *list_value; + int i, n; + int best = 0; + int best_index = -1; + + n = gst_value_list_get_size (value); + for (i = 0; i < n; i++) { + list_value = gst_value_list_get_value (value, i); + if (G_VALUE_TYPE (list_value) == G_TYPE_INT) { + int x = g_value_get_int (list_value); + + if (best_index == -1 || (ABS (target - x) < ABS (target - best))) { + best_index = i; + best = x; + } + } + } + if (best_index != -1) { + gst_structure_set (structure, field_name, G_TYPE_INT, best, NULL); + return TRUE; + } + return FALSE; + } + + return FALSE; +} + +/** + * gst_caps_structure_fixate_field_nearest_double: + * @structure: a #GstStructure + * @field_name: a field in @structure + * @target: the target value of the fixation + * + * Fixates a #GstStructure by changing the given field to the nearest + * double to @target that is a subset of the existing field. + * + * Returns: TRUE if the structure could be fixated + */ +gboolean +gst_caps_structure_fixate_field_nearest_double (GstStructure * structure, + const char *field_name, double target) +{ + const GValue *value; + + g_return_val_if_fail (gst_structure_has_field (structure, field_name), FALSE); + g_return_val_if_fail (IS_MUTABLE (structure), FALSE); + + value = gst_structure_get_value (structure, field_name); + + if (G_VALUE_TYPE (value) == G_TYPE_DOUBLE) { + /* already fixed */ + return FALSE; + } else if (G_VALUE_TYPE (value) == GST_TYPE_DOUBLE_RANGE) { + double x; + + x = gst_value_get_double_range_min (value); + if (target < x) + target = x; + x = gst_value_get_double_range_max (value); + if (target > x) + target = x; + gst_structure_set (structure, field_name, G_TYPE_DOUBLE, target, NULL); + return TRUE; + } else if (G_VALUE_TYPE (value) == GST_TYPE_LIST) { + const GValue *list_value; + int i, n; + double best = 0; + int best_index = -1; + + n = gst_value_list_get_size (value); + for (i = 0; i < n; i++) { + list_value = gst_value_list_get_value (value, i); + if (G_VALUE_TYPE (list_value) == G_TYPE_DOUBLE) { + double x = g_value_get_double (list_value); + + if (best_index == -1 || (ABS (target - x) < ABS (target - best))) { + best_index = i; + best = x; + } + } + } + if (best_index != -1) { + gst_structure_set (structure, field_name, G_TYPE_DOUBLE, best, NULL); + return TRUE; + } + return FALSE; + } + + return FALSE; + +} diff --git a/gst/gststructure.h b/gst/gststructure.h index 2a98d6ad6c..1fa05531e0 100644 --- a/gst/gststructure.h +++ b/gst/gststructure.h @@ -33,14 +33,22 @@ G_BEGIN_DECLS typedef struct _GstStructure GstStructure; typedef gboolean (*GstStructureForeachFunc) (GQuark field_id, + const GValue * value, + gpointer user_data); + +typedef gboolean (*GstStructureMapFunc) (GQuark field_id, GValue * value, gpointer user_data); struct _GstStructure { GType type; + /*< private >*/ GQuark name; + /* owned by parent structure, NULL if no parent */ + GstAtomicInt *parent_refcount; + GArray *fields; gpointer _gst_reserved[GST_PADDING]; @@ -57,6 +65,8 @@ GstStructure * gst_structure_new_valist (const gchar * const gchar * firstfield, va_list varargs); GstStructure * gst_structure_copy (const GstStructure *structure); +void gst_structure_set_parent_refcount (GstStructure *structure, + GstAtomicInt *refcount); void gst_structure_free (GstStructure *structure); G_CONST_RETURN gchar * gst_structure_get_name (const GstStructure *structure); @@ -92,9 +102,12 @@ void gst_structure_remove_all_fields (GstStructure GType gst_structure_get_field_type (const GstStructure *structure, const gchar *fieldname); -gboolean gst_structure_foreach (GstStructure *structure, +gboolean gst_structure_foreach (const GstStructure *structure, GstStructureForeachFunc func, gpointer user_data); +gboolean gst_structure_map_in_place (GstStructure *structure, + GstStructureMapFunc func, + gpointer user_data); gint gst_structure_n_fields (const GstStructure *structure); gboolean gst_structure_has_field (const GstStructure *structure, const gchar *fieldname); @@ -122,6 +135,14 @@ gchar * gst_structure_to_string (const GstStructure GstStructure * gst_structure_from_string (const gchar *string, gchar **end); +gboolean gst_caps_structure_fixate_field_nearest_int (GstStructure *structure, + const char *field_name, + int target); +gboolean gst_caps_structure_fixate_field_nearest_double (GstStructure *structure, + const char *field_name, + double target); + + G_END_DECLS #endif diff --git a/gst/gstsystemclock.c b/gst/gstsystemclock.c index 83a0eeb706..8c45060877 100644 --- a/gst/gstsystemclock.c +++ b/gst/gstsystemclock.c @@ -1,6 +1,6 @@ /* GStreamer * Copyright (C) 1999,2000 Erik Walthinsen - * 2000 Wim Taymans + * 2004 Wim Taymans * * gstsystemclock.c: Default clock, uses the system clock * @@ -20,13 +20,12 @@ * Boston, MA 02111-1307, USA. */ -#include - #include "gst_private.h" #include "gstinfo.h" #include "gstsystemclock.h" +/* the one instance of the systemclock */ static GstClock *_the_system_clock = NULL; static void gst_system_clock_class_init (GstSystemClockClass * klass); @@ -35,9 +34,15 @@ static void gst_system_clock_dispose (GObject * object); static GstClockTime gst_system_clock_get_internal_time (GstClock * clock); static guint64 gst_system_clock_get_resolution (GstClock * clock); -static GstClockEntryStatus gst_system_clock_wait (GstClock * clock, +static GstClockReturn gst_system_clock_id_wait (GstClock * clock, GstClockEntry * entry); -static void gst_system_clock_unlock (GstClock * clock, GstClockEntry * entry); +static GstClockReturn gst_system_clock_id_wait_unlocked + (GstClock * clock, GstClockEntry * entry); +static GstClockReturn gst_system_clock_id_wait_async (GstClock * clock, + GstClockEntry * entry); +static void gst_system_clock_id_unschedule (GstClock * clock, + GstClockEntry * entry); +static void gst_system_clock_async_thread (GstClock * clock); static GStaticMutex _gst_sysclock_mutex = G_STATIC_MUTEX_INIT; @@ -87,22 +92,43 @@ gst_system_clock_class_init (GstSystemClockClass * klass) gstclock_class->get_internal_time = gst_system_clock_get_internal_time; gstclock_class->get_resolution = gst_system_clock_get_resolution; - gstclock_class->wait = gst_system_clock_wait; - gstclock_class->unlock = gst_system_clock_unlock; + gstclock_class->wait = gst_system_clock_id_wait; + gstclock_class->wait_async = gst_system_clock_id_wait_async; + gstclock_class->unschedule = gst_system_clock_id_unschedule; } static void gst_system_clock_init (GstSystemClock * clock) { - clock->mutex = g_mutex_new (); - clock->cond = g_cond_new (); + GError *error = NULL; + + GST_CLOCK_FLAGS (clock) = + GST_CLOCK_FLAG_CAN_DO_SINGLE_SYNC | + GST_CLOCK_FLAG_CAN_DO_SINGLE_ASYNC | + GST_CLOCK_FLAG_CAN_DO_PERIODIC_SYNC | + GST_CLOCK_FLAG_CAN_DO_PERIODIC_ASYNC; + + GST_LOCK (clock); + clock->thread = g_thread_create ((GThreadFunc) gst_system_clock_async_thread, + clock, TRUE, &error); + if (error) + goto no_thread; + + /* wait for it to spin up */ + GST_CLOCK_WAIT (clock); + GST_UNLOCK (clock); + return; + +no_thread: + { + g_warning ("could not create async clock thread: %s", error->message); + } } static void gst_system_clock_dispose (GObject * object) { GstClock *clock = (GstClock *) object; - GstSystemClock *sysclock = (GstSystemClock *) object; /* there are subclasses of GstSystemClock running around... */ if (_the_system_clock == clock) { @@ -111,19 +137,19 @@ gst_system_clock_dispose (GObject * object) /* no parent dispose here, this is bad enough already */ } else { G_OBJECT_CLASS (parent_class)->dispose (object); - - /* FIXME: Notifying before freeing? */ - g_cond_free (sysclock->cond); - g_mutex_free (sysclock->mutex); } } /** * gst_system_clock_obtain * - * Get a handle to the default system clock. + * Get a handle to the default system clock. The refcount of the + * clock will be increased so you need to unref the clock after + * usage. * * Returns: the default clock. + * + * MT safe. */ GstClock * gst_system_clock_obtain (void) @@ -164,6 +190,105 @@ have_clock: return clock; } +/* this thread reads the sorted clock entries from the queue. + * + * It waits on each of them and fires the callback when the timeout occurs. + * + * When an entry in the queue was canceled, it is simply skipped. + * + * When waiting for an entry, it can become canceled, in that case we don't + * call the callback but move to the next item in the queue. + * + * MT safe. + */ +static void +gst_system_clock_async_thread (GstClock * clock) +{ + GstSystemClock *sysclock = GST_SYSTEM_CLOCK (clock); + + GST_CAT_DEBUG (GST_CAT_CLOCK, "enter system clock thread"); + GST_LOCK (clock); + /* signal spinup */ + GST_CLOCK_SIGNAL (clock); + /* now enter our infinite loop */ + while (!sysclock->stopping) { + GstClockEntry *entry; + GstClockReturn res; + + /* check if something to be done */ + while (clock->entries == NULL) { + GST_CAT_DEBUG (GST_CAT_CLOCK, "nothing to wait for"); + /* wait for work to do */ + GST_CLOCK_WAIT (clock); + GST_CAT_DEBUG (GST_CAT_CLOCK, "got signal"); + /* clock was stopping, exit */ + if (sysclock->stopping) + goto exit; + } + + /* pick the next entry */ + entry = clock->entries->data; + /* if it was unscheduled, just move on to the next entry */ + if (entry->status == GST_CLOCK_UNSCHEDULED) { + GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p was unscheduled", entry); + goto next_entry; + } + + /* now wait for the entry, we already hold the lock */ + res = gst_system_clock_id_wait_unlocked (clock, (GstClockID) entry); + + switch (res) { + case GST_CLOCK_UNSCHEDULED: + /* entry was unscheduled, move to the next */ + GST_CAT_DEBUG (GST_CAT_CLOCK, "async entry %p unscheduled", entry); + goto next_entry; + case GST_CLOCK_OK: + case GST_CLOCK_EARLY: + { + /* entry timed out normally, fire the callback and move to the next + * entry */ + GST_CAT_DEBUG (GST_CAT_CLOCK, "async entry %p unlocked", entry); + if (entry->func) { + entry->func (clock, entry->time, (GstClockID) entry, + entry->user_data); + } + if (entry->type == GST_CLOCK_ENTRY_PERIODIC) { + /* adjust time now */ + entry->time += entry->interval; + /* and resort the list now */ + clock->entries = + g_list_sort (clock->entries, gst_clock_id_compare_func); + /* and restart */ + continue; + } else { + goto next_entry; + } + } + case GST_CLOCK_BUSY: + /* somebody unlocked the entry but is was not canceled, This means that + * either a new entry was added in front of the queue or some other entry + * was canceled. Whatever it is, pick the head entry of the list and + * continue waiting. */ + GST_CAT_DEBUG (GST_CAT_CLOCK, "async entry %p needs restart", entry); + continue; + default: + GST_CAT_DEBUG (GST_CAT_CLOCK, + "strange result %d waiting for %p, skipping", res, entry); + goto next_entry; + } + next_entry: + /* we remove the current entry and unref it */ + clock->entries = g_list_remove (clock->entries, entry); + gst_clock_id_unref ((GstClockID) entry); + } +exit: + /* signal exit */ + GST_CLOCK_SIGNAL (clock); + GST_UNLOCK (clock); + GST_CAT_DEBUG (GST_CAT_CLOCK, "exit system clock thread"); +} + +/* MT safe */ static GstClockTime gst_system_clock_get_internal_time (GstClock * clock) { @@ -180,49 +305,128 @@ gst_system_clock_get_resolution (GstClock * clock) return 1 * GST_USECOND; } -static GstClockEntryStatus -gst_system_clock_wait (GstClock * clock, GstClockEntry * entry) +/* synchronously wait on the given GstClockEntry. + * + * We do this by blocking on the global clock GCond variable with + * the requested time as a timeout. This allows us to unblock the + * entry by signaling the GCond variable. + * + * Note that signaling the global GCond unlocks all waiting entries. So + * we need to check if an unlocked entry has changed when it unlocks. + * + * Entries that arrive too late are simply not waited on and a + * GST_CLOCK_EARLY result is returned. + * + * MT safe. + */ +static GstClockReturn +gst_system_clock_id_wait_unlocked (GstClock * clock, GstClockEntry * entry) { - GstClockEntryStatus res; - GstClockTime current, target; - gint64 diff; - GstSystemClock *sysclock = GST_SYSTEM_CLOCK (clock); + GstClockTime real, current, target; + GstClockTimeDiff diff; - current = gst_clock_get_time (clock); - diff = GST_CLOCK_ENTRY_TIME (entry) - current; - - if (diff + clock->max_diff < 0) { - GST_WARNING_OBJECT (clock, "clock is way behind: %" G_GINT64_FORMAT - "s (max allowed is %" G_GINT64_FORMAT "s", -diff, clock->max_diff); - return GST_CLOCK_ENTRY_EARLY; - } + /* need to call the overridden method */ + real = GST_CLOCK_GET_CLASS (clock)->get_internal_time (clock); + target = GST_CLOCK_ENTRY_TIME (entry); + current = gst_clock_adjust_unlocked (clock, real); + diff = target - current; target = gst_system_clock_get_internal_time (clock) + diff; - GST_CAT_DEBUG (GST_CAT_CLOCK, "real_target %" G_GUINT64_FORMAT - " target %" G_GUINT64_FORMAT - " now %" G_GUINT64_FORMAT, target, GST_CLOCK_ENTRY_TIME (entry), current); + GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p" + " target %" GST_TIME_FORMAT + " now %" GST_TIME_FORMAT + " real %" GST_TIME_FORMAT + " diff %" G_GINT64_FORMAT, + entry, + GST_TIME_ARGS (target), + GST_TIME_ARGS (current), GST_TIME_ARGS (real), diff); - if (((gint64) target) > 0) { + if (diff > 0) { GTimeVal tv; GST_TIME_TO_TIMEVAL (target, tv); - g_mutex_lock (sysclock->mutex); - g_cond_timed_wait (sysclock->cond, sysclock->mutex, &tv); - g_mutex_unlock (sysclock->mutex); - res = entry->status; + + while (TRUE) { + /* now wait on the entry, it either times out or the cond is signaled. */ + if (!GST_CLOCK_TIMED_WAIT (clock, &tv)) { + /* timeout, this is fine, we can report success now */ + GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p unlocked after timeout", entry); + entry->status = GST_CLOCK_OK; + break; + } else { + /* the waiting is interrupted because the GCond was signaled. This can + * be because this or some other entry was unscheduled. */ + GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p unlocked with signal", entry); + /* if the entry is unscheduled, we can stop waiting for it */ + if (entry->status == GST_CLOCK_UNSCHEDULED) + break; + } + } } else { - res = GST_CLOCK_ENTRY_EARLY; + entry->status = GST_CLOCK_EARLY; } - return res; + return entry->status; } -static void -gst_system_clock_unlock (GstClock * clock, GstClockEntry * entry) +static GstClockReturn +gst_system_clock_id_wait (GstClock * clock, GstClockEntry * entry) { - GstSystemClock *sysclock = GST_SYSTEM_CLOCK (clock); + GstClockReturn ret; - g_mutex_lock (sysclock->mutex); - g_cond_broadcast (sysclock->cond); - g_mutex_unlock (sysclock->mutex); + GST_LOCK (clock); + ret = gst_system_clock_id_wait_unlocked (clock, entry); + GST_UNLOCK (clock); + + return ret; +} + +/* Add an entry to the list of pending async waits. The entry is inserted + * in sorted order. If we inserted the entry at the head of the list, we + * need to signal the thread as it might either be waiting on it or waiting + * for a new entry. + * + * MT safe. + */ +static GstClockReturn +gst_system_clock_id_wait_async (GstClock * clock, GstClockEntry * entry) +{ + GST_CAT_DEBUG (GST_CAT_CLOCK, "adding entry %p", entry); + + GST_LOCK (clock); + /* need to take a ref */ + gst_clock_id_ref ((GstClockID) entry); + /* insert the entry in sorted order */ + clock->entries = g_list_insert_sorted (clock->entries, entry, + gst_clock_id_compare_func); + + /* only need to send the signal if the entry was added to the + * front, else the thread is just waiting for another entry and + * will get to this entry automatically. */ + if (clock->entries->data == entry) { + GST_CAT_DEBUG (GST_CAT_CLOCK, "send signal"); + GST_CLOCK_SIGNAL (clock); + } + GST_UNLOCK (clock); + + return GST_CLOCK_OK; +} + +/* unschedule an entry. This will set the state of the entry to GST_CLOCK_UNSCHEDULED + * and will signal any thread waiting for entries to recheck their entry. + * We cannot really decide if the signal is needed or not because the entry + * could be waited on in async or sync mode. + * + * MT safe. + */ +static void +gst_system_clock_id_unschedule (GstClock * clock, GstClockEntry * entry) +{ + GST_CAT_DEBUG (GST_CAT_CLOCK, "unscheduling entry %p", entry); + + GST_LOCK (clock); + entry->status = GST_CLOCK_UNSCHEDULED; + GST_CAT_DEBUG (GST_CAT_CLOCK, "send signal"); + GST_CLOCK_SIGNAL (clock); + GST_UNLOCK (clock); } diff --git a/gst/gstsystemclock.h b/gst/gstsystemclock.h index 33c03533bd..a09dd0f82f 100644 --- a/gst/gstsystemclock.h +++ b/gst/gstsystemclock.h @@ -42,8 +42,9 @@ typedef struct _GstSystemClockClass GstSystemClockClass; struct _GstSystemClock { GstClock clock; - GMutex * mutex; - GCond * cond; + /*< private >*/ + GThread *thread; /* thread for async notify */ + gboolean stopping; gpointer _gst_reserved[GST_PADDING]; }; @@ -51,6 +52,7 @@ struct _GstSystemClock { struct _GstSystemClockClass { GstClockClass parent_class; + /*< private >*/ gpointer _gst_reserved[GST_PADDING]; }; diff --git a/gst/gsttag.c b/gst/gsttag.c index 127a7d2218..c222b14eec 100644 --- a/gst/gsttag.c +++ b/gst/gsttag.c @@ -446,7 +446,7 @@ typedef struct GstTagCopyData; static void gst_tag_list_add_value_internal (GstStructure * list, GstTagMergeMode mode, - GQuark tag, GValue * value) + GQuark tag, const GValue * value) { GstTagInfo *info = gst_tag_lookup (tag); const GValue *value2; @@ -500,7 +500,7 @@ gst_tag_list_add_value_internal (GstStructure * list, GstTagMergeMode mode, } } static gboolean -gst_tag_list_copy_foreach (GQuark tag, GValue * value, gpointer user_data) +gst_tag_list_copy_foreach (GQuark tag, const GValue * value, gpointer user_data) { GstTagCopyData *copy = (GstTagCopyData *) user_data; @@ -770,7 +770,8 @@ typedef struct } TagForeachData; static int -structure_foreach_wrapper (GQuark field_id, GValue * value, gpointer user_data) +structure_foreach_wrapper (GQuark field_id, const GValue * value, + gpointer user_data) { TagForeachData *data = (TagForeachData *) user_data; diff --git a/gst/gsttaginterface.c b/gst/gsttaginterface.c index f6b792b6fe..339b6ba9c3 100644 --- a/gst/gsttaginterface.c +++ b/gst/gsttaginterface.c @@ -132,7 +132,7 @@ gst_tag_setter_merge (GstTagSetter * setter, const GstTagList * list, * @...: more tag / value pairs to set * * Adds the given tag / value pairs on the setter using the given merge mode. - * The list must be terminated with %NULL. + * The list must be terminated with GST_TAG_INVALID. */ void gst_tag_setter_add (GstTagSetter * setter, GstTagMergeMode mode, @@ -156,7 +156,7 @@ gst_tag_setter_add (GstTagSetter * setter, GstTagMergeMode mode, * @...: more tag / GValue pairs to set * * Adds the given tag / GValue pairs on the setter using the given merge mode. - * The list must be terminated with %NULL. + * The list must be terminated with GST_TAG_INVALID. */ void gst_tag_setter_add_values (GstTagSetter * setter, GstTagMergeMode mode, @@ -180,7 +180,7 @@ gst_tag_setter_add_values (GstTagSetter * setter, GstTagMergeMode mode, * @var_args: tag / value pairs to set * * Adds the given tag / value pairs on the setter using the given merge mode. - * The list must be terminated with %NULL. + * The list must be terminated with GST_TAG_INVALID. */ void gst_tag_setter_add_valist (GstTagSetter * setter, GstTagMergeMode mode, @@ -206,7 +206,7 @@ gst_tag_setter_add_valist (GstTagSetter * setter, GstTagMergeMode mode, * @var_args: tag / GValue pairs to set * * Adds the given tag / GValue pairs on the setter using the given merge mode. - * The list must be terminated with %NULL. + * The list must be terminated with GST_TAG_INVALID. */ void gst_tag_setter_add_valist_values (GstTagSetter * setter, GstTagMergeMode mode, diff --git a/gst/gsttaglist.c b/gst/gsttaglist.c index 127a7d2218..c222b14eec 100644 --- a/gst/gsttaglist.c +++ b/gst/gsttaglist.c @@ -446,7 +446,7 @@ typedef struct GstTagCopyData; static void gst_tag_list_add_value_internal (GstStructure * list, GstTagMergeMode mode, - GQuark tag, GValue * value) + GQuark tag, const GValue * value) { GstTagInfo *info = gst_tag_lookup (tag); const GValue *value2; @@ -500,7 +500,7 @@ gst_tag_list_add_value_internal (GstStructure * list, GstTagMergeMode mode, } } static gboolean -gst_tag_list_copy_foreach (GQuark tag, GValue * value, gpointer user_data) +gst_tag_list_copy_foreach (GQuark tag, const GValue * value, gpointer user_data) { GstTagCopyData *copy = (GstTagCopyData *) user_data; @@ -770,7 +770,8 @@ typedef struct } TagForeachData; static int -structure_foreach_wrapper (GQuark field_id, GValue * value, gpointer user_data) +structure_foreach_wrapper (GQuark field_id, const GValue * value, + gpointer user_data) { TagForeachData *data = (TagForeachData *) user_data; diff --git a/gst/gsttagsetter.c b/gst/gsttagsetter.c index f6b792b6fe..339b6ba9c3 100644 --- a/gst/gsttagsetter.c +++ b/gst/gsttagsetter.c @@ -132,7 +132,7 @@ gst_tag_setter_merge (GstTagSetter * setter, const GstTagList * list, * @...: more tag / value pairs to set * * Adds the given tag / value pairs on the setter using the given merge mode. - * The list must be terminated with %NULL. + * The list must be terminated with GST_TAG_INVALID. */ void gst_tag_setter_add (GstTagSetter * setter, GstTagMergeMode mode, @@ -156,7 +156,7 @@ gst_tag_setter_add (GstTagSetter * setter, GstTagMergeMode mode, * @...: more tag / GValue pairs to set * * Adds the given tag / GValue pairs on the setter using the given merge mode. - * The list must be terminated with %NULL. + * The list must be terminated with GST_TAG_INVALID. */ void gst_tag_setter_add_values (GstTagSetter * setter, GstTagMergeMode mode, @@ -180,7 +180,7 @@ gst_tag_setter_add_values (GstTagSetter * setter, GstTagMergeMode mode, * @var_args: tag / value pairs to set * * Adds the given tag / value pairs on the setter using the given merge mode. - * The list must be terminated with %NULL. + * The list must be terminated with GST_TAG_INVALID. */ void gst_tag_setter_add_valist (GstTagSetter * setter, GstTagMergeMode mode, @@ -206,7 +206,7 @@ gst_tag_setter_add_valist (GstTagSetter * setter, GstTagMergeMode mode, * @var_args: tag / GValue pairs to set * * Adds the given tag / GValue pairs on the setter using the given merge mode. - * The list must be terminated with %NULL. + * The list must be terminated with GST_TAG_INVALID. */ void gst_tag_setter_add_valist_values (GstTagSetter * setter, GstTagMergeMode mode, diff --git a/gst/gstthread.c b/gst/gstthread.c index 3e87ef919f..04f910a0b0 100644 --- a/gst/gstthread.c +++ b/gst/gstthread.c @@ -233,7 +233,7 @@ gst_thread_dispose (GObject * object) g_cond_free (thread->cond); g_mutex_free (thread->iterate_lock); - gst_object_replace ((GstObject **) & GST_ELEMENT_SCHED (thread), NULL); + gst_object_replace ((GstObject **) & GST_ELEMENT_SCHEDULER (thread), NULL); } /** @@ -321,7 +321,9 @@ gst_thread_release_children_locks (GstThread * thread) { GstRealPad *peer = NULL; GstElement *peerelement; - GList *elements = (GList *) gst_bin_get_list (GST_BIN (thread)); + + /* not MT safe but gstthread is going away soon */ + GList *elements = (GList *) GST_BIN (thread)->children; while (elements) { GstElement *element = GST_ELEMENT (elements->data); @@ -354,7 +356,7 @@ gst_thread_release_children_locks (GstThread * thread) if (!peerelement) continue; /* FIXME: deal with case where there's no peer */ - if (GST_ELEMENT_SCHED (peerelement) != GST_ELEMENT_SCHED (thread)) { + if (GST_ELEMENT_SCHEDULER (peerelement) != GST_ELEMENT_SCHEDULER (thread)) { GST_LOG_OBJECT (thread, "element \"%s\" has pad cross sched boundary", GST_ELEMENT_NAME (element)); GST_LOG_OBJECT (thread, "waking element \"%s\"", @@ -473,8 +475,9 @@ revert: break; case GST_STATE_PAUSED_TO_PLAYING: { - /* FIXME: recurse into sub-bins */ - GList *elements = (GList *) gst_bin_get_list (GST_BIN (thread)); + /* FIXME: recurse into sub-bins. not MT safe but gstthread is going + * away soon. */ + GList *elements = (GList *) GST_BIN (thread)->children; while (elements) { gst_element_enable_threadsafe_properties ((GstElement *) elements-> @@ -491,7 +494,7 @@ revert: GST_FLAG_UNSET (thread, GST_THREAD_STATE_SPINNING); - elements = (GList *) gst_bin_get_list (GST_BIN (thread)); + elements = (GList *) GST_BIN (thread)->children; while (elements) { gst_element_disable_threadsafe_properties ((GstElement *) elements-> @@ -664,7 +667,7 @@ gst_thread_main_loop (void *arg) g_private_set (gst_thread_current, thread); /* set up the element's scheduler */ - gst_scheduler_setup (GST_ELEMENT_SCHED (thread)); + gst_scheduler_setup (GST_ELEMENT_SCHEDULER (thread)); GST_FLAG_UNSET (thread, GST_THREAD_STATE_REAPING); GST_FLAG_UNSET (thread, GST_THREAD_STATE_WAITING); @@ -726,7 +729,7 @@ gst_thread_main_loop (void *arg) /* we need to destroy the scheduler here because it might have * mapped it's stack into the threads stack space */ - sched = GST_ELEMENT_SCHED (thread); + sched = GST_ELEMENT_SCHEDULER (thread); if (sched) gst_scheduler_reset (sched); diff --git a/gst/gsttrashstack.h b/gst/gsttrashstack.h index 92fcf373a0..7f1c2262b5 100644 --- a/gst/gsttrashstack.h +++ b/gst/gsttrashstack.h @@ -98,7 +98,7 @@ gst_trash_stack_pop (GstTrashStack *stack) * problem that arises when a pop and push of the same element happens * right between when we read head->next and try to swing the new pointer * into place. This is usually solved using a counter which makes it highly - * inlikely that we manage to grab the wrong head->next value. + * unlikely that we manage to grab the wrong head->next value. */ __asm__ __volatile__ ( " pushl %%ebx; \n\t" @@ -124,6 +124,7 @@ gst_trash_stack_pop (GstTrashStack *stack) } #else +#warning "using fallback trashstack implementation, performance may suffer" /* * generic implementation diff --git a/gst/gsttypefind.c b/gst/gsttypefind.c index e6ae25f7af..fcb9058b1f 100644 --- a/gst/gsttypefind.c +++ b/gst/gsttypefind.c @@ -94,7 +94,7 @@ gst_type_find_factory_dispose (GObject * object) GstTypeFindFactory *factory = GST_TYPE_FIND_FACTORY (object); if (factory->caps) { - gst_caps_free (factory->caps); + gst_caps_unref (factory->caps); factory->caps = NULL; } if (factory->extensions) { diff --git a/gst/gsttypes.h b/gst/gsttypes.h index 0c091cba1b..62bafc713b 100644 --- a/gst/gsttypes.h +++ b/gst/gsttypes.h @@ -22,7 +22,7 @@ #include -G_BEGIN_DECLS +G_BEGIN_DECLS typedef struct _GstObject GstObject; typedef struct _GstObjectClass GstObjectClass; @@ -34,6 +34,10 @@ typedef struct _GstElement GstElement; typedef struct _GstElementClass GstElementClass; typedef struct _GstBin GstBin; typedef struct _GstBinClass GstBinClass; +typedef struct _GstPipeline GstPipeline; +typedef struct _GstPipelineClass GstPipelineClass; +typedef struct _GstBus GstBus; +typedef struct _GstBusClass GstBusClass; typedef struct _GstScheduler GstScheduler; typedef struct _GstSchedulerClass GstSchedulerClass; typedef struct _GstEvent GstEvent; @@ -68,7 +72,6 @@ typedef enum { #define GST_PADDING 4 #define GST_PADDING_INIT { 0 } - G_END_DECLS #endif /* __GST_TYPES_H__ */ diff --git a/gst/gstutils.c b/gst/gstutils.c index e00e23e612..10ac425c44 100644 --- a/gst/gstutils.c +++ b/gst/gstutils.c @@ -27,6 +27,8 @@ #include "gstutils.h" #include "gsturitype.h" #include "gstinfo.h" +#include "gst-i18n-lib.h" + /** * gst_util_dump_mem: @@ -367,3 +369,1354 @@ gst_print_element_args (GString * buf, gint indent, GstElement * element) g_free (specs); } + +/** + * gst_element_get_compatible_pad_template: + * @element: a #GstElement to get a compatible pad template for. + * @compattempl: the #GstPadTemplate to find a compatible template for. + * + * Retrieves a pad template from @element that is compatible with @compattempl. + * Pads from compatible templates can be linked together. + * + * Returns: a compatible #GstPadTemplate, or NULL if none was found. No + * unreferencing is necessary. + */ +GstPadTemplate * +gst_element_get_compatible_pad_template (GstElement * element, + GstPadTemplate * compattempl) +{ + GstPadTemplate *newtempl = NULL; + GList *padlist; + GstElementClass *class; + + g_return_val_if_fail (element != NULL, NULL); + g_return_val_if_fail (GST_IS_ELEMENT (element), NULL); + g_return_val_if_fail (compattempl != NULL, NULL); + + class = GST_ELEMENT_GET_CLASS (element); + + padlist = gst_element_class_get_pad_template_list (class); + + GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, + "Looking for a suitable pad template in %s out of %d templates...", + GST_ELEMENT_NAME (element), g_list_length (padlist)); + + while (padlist) { + GstPadTemplate *padtempl = (GstPadTemplate *) padlist->data; + GstCaps *intersection; + + /* Ignore name + * Ignore presence + * Check direction (must be opposite) + * Check caps + */ + GST_CAT_LOG (GST_CAT_CAPS, + "checking pad template %s", padtempl->name_template); + if (padtempl->direction != compattempl->direction) { + GST_CAT_DEBUG (GST_CAT_CAPS, + "compatible direction: found %s pad template \"%s\"", + padtempl->direction == GST_PAD_SRC ? "src" : "sink", + padtempl->name_template); + + intersection = gst_caps_intersect (GST_PAD_TEMPLATE_CAPS (compattempl), + GST_PAD_TEMPLATE_CAPS (padtempl)); + + GST_CAT_DEBUG (GST_CAT_CAPS, "caps are %scompatible", + (intersection ? "" : "not ")); + + if (!gst_caps_is_empty (intersection)) + newtempl = padtempl; + gst_caps_unref (intersection); + if (newtempl) + break; + } + + padlist = g_list_next (padlist); + } + if (newtempl) + GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, + "Returning new pad template %p", newtempl); + else + GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "No compatible pad template found"); + + return newtempl; +} + +static GstPad * +gst_element_request_pad (GstElement * element, GstPadTemplate * templ, + const gchar * name) +{ + GstPad *newpad = NULL; + GstElementClass *oclass; + + oclass = GST_ELEMENT_GET_CLASS (element); + + if (oclass->request_new_pad) + newpad = (oclass->request_new_pad) (element, templ, name); + + return newpad; +} + + + +/** + * gst_element_get_pad_from_template: + * @element: a #GstElement. + * @templ: a #GstPadTemplate belonging to @element. + * + * Gets a pad from @element described by @templ. If the presence of @templ is + * #GST_PAD_REQUEST, requests a new pad. Can return %NULL for #GST_PAD_SOMETIMES + * templates. + * + * Returns: the #GstPad, or NULL if one could not be found or created. + */ +static GstPad * +gst_element_get_pad_from_template (GstElement * element, GstPadTemplate * templ) +{ + GstPad *ret = NULL; + GstPadPresence presence; + + /* If this function is ever exported, we need check the validity of `element' + * and `templ', and to make sure the template actually belongs to the + * element. */ + + presence = GST_PAD_TEMPLATE_PRESENCE (templ); + + switch (presence) { + case GST_PAD_ALWAYS: + case GST_PAD_SOMETIMES: + ret = gst_element_get_static_pad (element, templ->name_template); + if (!ret && presence == GST_PAD_ALWAYS) + g_warning + ("Element %s has an ALWAYS template %s, but no pad of the same name", + GST_OBJECT_NAME (element), templ->name_template); + break; + + case GST_PAD_REQUEST: + ret = gst_element_request_pad (element, templ, NULL); + break; + } + + return ret; +} + +/** + * gst_element_request_compatible_pad: + * @element: a #GstElement. + * @templ: the #GstPadTemplate to which the new pad should be able to link. + * + * Requests a pad from @element. The returned pad should be unlinked and + * compatible with @templ. Might return an existing pad, or request a new one. + * + * Returns: a #GstPad, or %NULL if one could not be found or created. + */ +GstPad * +gst_element_request_compatible_pad (GstElement * element, + GstPadTemplate * templ) +{ + GstPadTemplate *templ_new; + GstPad *pad = NULL; + + g_return_val_if_fail (GST_IS_ELEMENT (element), NULL); + g_return_val_if_fail (GST_IS_PAD_TEMPLATE (templ), NULL); + + /* FIXME: should really loop through the templates, testing each for + * compatibility and pad availability. */ + templ_new = gst_element_get_compatible_pad_template (element, templ); + if (templ_new) + pad = gst_element_get_pad_from_template (element, templ_new); + + /* This can happen for non-request pads. No need to unref. */ + if (pad && GST_PAD_PEER (pad)) + pad = NULL; + + return pad; +} + +/** + * gst_element_get_compatible_pad_filtered: + * @element: a #GstElement in which the pad should be found. + * @pad: the #GstPad to find a compatible one for. + * @filtercaps: the #GstCaps to use as a filter. + * + * Looks for an unlinked pad to which the given pad can link. It is not + * guaranteed that linking the pads will work, though it should work in most + * cases. + * + * Returns: the #GstPad to which a link can be made, or %NULL if one cannot be + * found. + */ +GstPad * +gst_element_get_compatible_pad_filtered (GstElement * element, GstPad * pad, + const GstCaps * filtercaps) +{ + GstIterator *pads; + GstPadTemplate *templ; + GstCaps *templcaps; + GstPad *foundpad = NULL; + gboolean done; + + g_return_val_if_fail (GST_IS_ELEMENT (element), NULL); + g_return_val_if_fail (GST_IS_PAD (pad), NULL); + + GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, + "finding pad in %s compatible with %s:%s and filter %" GST_PTR_FORMAT, + GST_ELEMENT_NAME (element), GST_DEBUG_PAD_NAME (pad), filtercaps); + + /* let's use the real pad */ + pad = (GstPad *) GST_PAD_REALIZE (pad); + g_return_val_if_fail (pad != NULL, NULL); + g_return_val_if_fail (GST_RPAD_PEER (pad) == NULL, NULL); + + done = FALSE; + /* try to get an existing unlinked pad */ + pads = gst_element_iterate_pads (element); + while (!done) { + gpointer padptr; + + switch (gst_iterator_next (pads, &padptr)) { + case GST_ITERATOR_OK: + { + GstPad *peer; + GstPad *current; + + current = GST_PAD (padptr); + + GST_CAT_LOG (GST_CAT_ELEMENT_PADS, "examing pad %s:%s", + GST_DEBUG_PAD_NAME (current)); + + peer = gst_pad_get_peer (current); + + if (peer == NULL && + gst_pad_can_link_filtered (pad, current, filtercaps)) { + + GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, + "found existing unlinked pad %s:%s", + GST_DEBUG_PAD_NAME (current)); + + gst_iterator_free (pads); + + return current; + } else { + GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "unreffing pads"); + + gst_object_unref (GST_OBJECT (current)); + if (peer) + gst_object_unref (GST_OBJECT (peer)); + } + break; + } + case GST_ITERATOR_DONE: + done = TRUE; + break; + case GST_ITERATOR_RESYNC: + gst_iterator_resync (pads); + break; + case GST_ITERATOR_ERROR: + g_assert_not_reached (); + break; + } + } + gst_iterator_free (pads); + + /* try to create a new one */ + /* requesting is a little crazy, we need a template. Let's create one */ + templcaps = gst_pad_get_caps (pad); + if (filtercaps != NULL) { + GstCaps *temp; + + temp = gst_caps_intersect (filtercaps, templcaps); + gst_caps_unref (templcaps); + templcaps = temp; + } + + templ = gst_pad_template_new ((gchar *) GST_PAD_NAME (pad), + GST_PAD_DIRECTION (pad), GST_PAD_ALWAYS, templcaps); + foundpad = gst_element_request_compatible_pad (element, templ); + gst_object_unref (GST_OBJECT (templ)); + + if (foundpad) { + GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, + "found existing request pad %s:%s", GST_DEBUG_PAD_NAME (foundpad)); + return foundpad; + } + + GST_CAT_INFO_OBJECT (GST_CAT_ELEMENT_PADS, element, + "Could not find a compatible pad to link to %s:%s", + GST_DEBUG_PAD_NAME (pad)); + return NULL; +} + + + +/** + * gst_element_get_compatible_pad: + * @element: a #GstElement in which the pad should be found. + * @pad: the #GstPad to find a compatible one for. + * + * Looks for an unlinked pad to which the given pad can link to. + * It is not guaranteed that linking the pads will work, though + * it should work in most cases. + * + * Returns: the #GstPad to which a link can be made, or %NULL if one + * could not be found. + */ +GstPad * +gst_element_get_compatible_pad (GstElement * element, GstPad * pad) +{ + return gst_element_get_compatible_pad_filtered (element, pad, NULL); +} + +/** + * gst_element_state_get_name: + * @state: a #GstElementState to get the name of. + * + * Gets a string representing the given state. + * + * Returns: a string with the name of the state. + */ +const gchar * +gst_element_state_get_name (GstElementState state) +{ + switch (state) { +#ifdef GST_DEBUG_COLOR + case GST_STATE_VOID_PENDING: + return "NONE_PENDING"; + break; + case GST_STATE_NULL: + return "\033[01;34mNULL\033[00m"; + break; + case GST_STATE_READY: + return "\033[01;31mREADY\033[00m"; + break; + case GST_STATE_PLAYING: + return "\033[01;32mPLAYING\033[00m"; + break; + case GST_STATE_PAUSED: + return "\033[01;33mPAUSED\033[00m"; + break; + default: + /* This is a memory leak */ + return g_strdup_printf ("\033[01;35;41mUNKNOWN!\033[00m(%d)", state); +#else + case GST_STATE_VOID_PENDING: + return "NONE_PENDING"; + break; + case GST_STATE_NULL: + return "NULL"; + break; + case GST_STATE_READY: + return "READY"; + break; + case GST_STATE_PLAYING: + return "PLAYING"; + break; + case GST_STATE_PAUSED: + return "PAUSED"; + break; + default: + return g_strdup_printf ("UNKNOWN!(%d)", state); +#endif + } + return ""; +} + +/** + * gst_element_link_pads_filtered: + * @src: a #GstElement containing the source pad. + * @srcpadname: the name of the #GstPad in source element or NULL for any pad. + * @dest: the #GstElement containing the destination pad. + * @destpadname: the name of the #GstPad in destination element or NULL for any pad. + * @filtercaps: the #GstCaps to use as a filter. + * + * Links the two named pads of the source and destination elements. + * Side effect is that if one of the pads has no parent, it becomes a + * child of the parent of the other element. If they have different + * parents, the link fails. + * + * Returns: TRUE if the pads could be linked, FALSE otherwise. + */ +gboolean +gst_element_link_pads_filtered (GstElement * src, const gchar * srcpadname, + GstElement * dest, const gchar * destpadname, const GstCaps * filtercaps) +{ + const GList *srcpads, *destpads, *srctempls, *desttempls, *l; + GstPad *srcpad, *destpad; + GstPadTemplate *srctempl, *desttempl; + GstElementClass *srcclass, *destclass; + + /* checks */ + g_return_val_if_fail (GST_IS_ELEMENT (src), FALSE); + g_return_val_if_fail (GST_IS_ELEMENT (dest), FALSE); + + srcclass = GST_ELEMENT_GET_CLASS (src); + destclass = GST_ELEMENT_GET_CLASS (dest); + + GST_CAT_INFO (GST_CAT_ELEMENT_PADS, + "trying to link element %s:%s to element %s:%s", GST_ELEMENT_NAME (src), + srcpadname ? srcpadname : "(any)", GST_ELEMENT_NAME (dest), + destpadname ? destpadname : "(any)"); + + /* now get the pads we're trying to link and a list of all remaining pads */ + if (srcpadname) { + srcpad = gst_element_get_pad (src, srcpadname); + if (!srcpad) { + GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "no pad %s:%s", + GST_ELEMENT_NAME (src), srcpadname); + return FALSE; + } else { + if (!(GST_PAD_DIRECTION (srcpad) == GST_PAD_SRC)) { + GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "pad %s:%s is no src pad", + GST_DEBUG_PAD_NAME (srcpad)); + gst_object_unref (GST_OBJECT (srcpad)); + return FALSE; + } + if (GST_PAD_PEER (srcpad) != NULL) { + GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "pad %s:%s is already linked", + GST_DEBUG_PAD_NAME (srcpad)); + gst_object_unref (GST_OBJECT (srcpad)); + return FALSE; + } + } + srcpads = NULL; + } else { + GST_LOCK (src); + srcpads = GST_ELEMENT_PADS (src); + srcpad = srcpads ? (GstPad *) GST_PAD_REALIZE (srcpads->data) : NULL; + if (srcpad) + gst_object_ref (GST_OBJECT (srcpad)); + GST_UNLOCK (src); + } + if (destpadname) { + destpad = gst_element_get_pad (dest, destpadname); + if (!destpad) { + GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "no pad %s:%s", + GST_ELEMENT_NAME (dest), destpadname); + return FALSE; + } else { + if (!(GST_PAD_DIRECTION (destpad) == GST_PAD_SINK)) { + GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "pad %s:%s is no sink pad", + GST_DEBUG_PAD_NAME (destpad)); + gst_object_unref (GST_OBJECT (destpad)); + return FALSE; + } + if (GST_PAD_PEER (destpad) != NULL) { + GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "pad %s:%s is already linked", + GST_DEBUG_PAD_NAME (destpad)); + gst_object_unref (GST_OBJECT (destpad)); + return FALSE; + } + } + destpads = NULL; + } else { + GST_LOCK (dest); + destpads = GST_ELEMENT_PADS (dest); + destpad = destpads ? (GstPad *) GST_PAD_REALIZE (destpads->data) : NULL; + if (destpad) + gst_object_ref (GST_OBJECT (destpad)); + GST_UNLOCK (dest); + } + + if (srcpadname && destpadname) { + gboolean result; + + /* two explicitly specified pads */ + result = gst_pad_link_filtered (srcpad, destpad, filtercaps); + + gst_object_unref (GST_OBJECT (srcpad)); + gst_object_unref (GST_OBJECT (destpad)); + + return result; + } + if (srcpad) { + /* loop through the allowed pads in the source, trying to find a + * compatible destination pad */ + GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, + "looping through allowed src and dest pads"); + do { + GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "trying src pad %s:%s", + GST_DEBUG_PAD_NAME (srcpad)); + if ((GST_PAD_DIRECTION (srcpad) == GST_PAD_SRC) && + (GST_PAD_PEER (srcpad) == NULL)) { + GstPad *temp = destpadname ? destpad : + gst_element_get_compatible_pad_filtered (dest, srcpad, + filtercaps); + + if (temp + && gst_pad_link_filtered (srcpad, temp, + filtercaps) == GST_PAD_LINK_OK) { + GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "linked pad %s:%s to pad %s:%s", + GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (temp)); + if (destpad) + gst_object_unref (GST_OBJECT (destpad)); + gst_object_unref (GST_OBJECT (srcpad)); + gst_object_unref (GST_OBJECT (temp)); + return TRUE; + } + } + /* find a better way for this mess */ + if (srcpads) { + srcpads = g_list_next (srcpads); + if (srcpads) { + gst_object_unref (GST_OBJECT (srcpad)); + srcpad = (GstPad *) GST_PAD_REALIZE (srcpads->data); + gst_object_ref (GST_OBJECT (srcpad)); + } + } + } while (srcpads); + } + if (srcpadname) { + GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "no link possible from %s:%s to %s", + GST_DEBUG_PAD_NAME (srcpad), GST_ELEMENT_NAME (dest)); + gst_object_unref (GST_OBJECT (srcpad)); + if (destpad) + gst_object_unref (GST_OBJECT (destpad)); + return FALSE; + } else { + gst_object_unref (GST_OBJECT (srcpad)); + srcpad = NULL; + } + if (destpad) { + /* loop through the existing pads in the destination */ + do { + GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "trying dest pad %s:%s", + GST_DEBUG_PAD_NAME (destpad)); + if ((GST_PAD_DIRECTION (destpad) == GST_PAD_SINK) && + (GST_PAD_PEER (destpad) == NULL)) { + GstPad *temp = gst_element_get_compatible_pad_filtered (src, destpad, + filtercaps); + + if (temp + && gst_pad_link_filtered (temp, destpad, + filtercaps) == GST_PAD_LINK_OK) { + GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "linked pad %s:%s to pad %s:%s", + GST_DEBUG_PAD_NAME (temp), GST_DEBUG_PAD_NAME (destpad)); + gst_object_unref (GST_OBJECT (temp)); + gst_object_unref (GST_OBJECT (destpad)); + if (srcpad) + gst_object_unref (GST_OBJECT (srcpad)); + return TRUE; + } + } + if (destpads) { + destpads = g_list_next (destpads); + if (destpads) { + gst_object_unref (GST_OBJECT (destpad)); + destpad = (GstPad *) GST_PAD_REALIZE (destpads->data); + gst_object_ref (GST_OBJECT (destpad)); + } + } + } while (destpads); + } + if (destpadname) { + GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "no link possible from %s to %s:%s", + GST_ELEMENT_NAME (src), GST_DEBUG_PAD_NAME (destpad)); + gst_object_unref (GST_OBJECT (destpad)); + if (srcpad) + gst_object_unref (GST_OBJECT (srcpad)); + return FALSE; + } else { + gst_object_unref (GST_OBJECT (destpad)); + if (srcpad) + gst_object_unref (GST_OBJECT (srcpad)); + srcpad = NULL; + destpad = NULL; + } + + GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, + "we might have request pads on both sides, checking..."); + srctempls = gst_element_class_get_pad_template_list (srcclass); + desttempls = gst_element_class_get_pad_template_list (destclass); + + if (srctempls && desttempls) { + while (srctempls) { + srctempl = (GstPadTemplate *) srctempls->data; + if (srctempl->presence == GST_PAD_REQUEST) { + for (l = desttempls; l; l = l->next) { + desttempl = (GstPadTemplate *) l->data; + if (desttempl->presence == GST_PAD_REQUEST && + desttempl->direction != srctempl->direction) { + if (gst_caps_is_always_compatible (gst_pad_template_get_caps + (srctempl), gst_pad_template_get_caps (desttempl))) { + srcpad = + gst_element_get_request_pad (src, srctempl->name_template); + destpad = + gst_element_get_request_pad (dest, desttempl->name_template); + if (gst_pad_link_filtered (srcpad, destpad, + filtercaps) == GST_PAD_LINK_OK) { + GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, + "linked pad %s:%s to pad %s:%s", + GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (destpad)); + gst_object_unref (GST_OBJECT (srcpad)); + gst_object_unref (GST_OBJECT (destpad)); + return TRUE; + } + /* it failed, so we release the request pads */ + gst_element_release_request_pad (src, srcpad); + gst_element_release_request_pad (dest, destpad); + } + } + } + } + srctempls = srctempls->next; + } + } + + GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "no link possible from %s to %s", + GST_ELEMENT_NAME (src), GST_ELEMENT_NAME (dest)); + return FALSE; +} + +/** + * gst_element_link_filtered: + * @src: a #GstElement containing the source pad. + * @dest: the #GstElement containing the destination pad. + * @filtercaps: the #GstCaps to use as a filter. + * + * Links @src to @dest, filtered by @filtercaps. The link must be from source to + * destination; the other direction will not be tried. The function looks for + * existing pads that aren't linked yet. It will request new pads if necessary. + * If multiple links are possible, only one is established. + * + * Returns: TRUE if the elements could be linked, FALSE otherwise. + */ +gboolean +gst_element_link_filtered (GstElement * src, GstElement * dest, + const GstCaps * filtercaps) +{ + return gst_element_link_pads_filtered (src, NULL, dest, NULL, filtercaps); +} + +/** + * gst_element_link_many: + * @element_1: the first #GstElement in the link chain. + * @element_2: the second #GstElement in the link chain. + * @...: the NULL-terminated list of elements to link in order. + * + * Chain together a series of elements. Uses gst_element_link(). + * + * Returns: TRUE on success, FALSE otherwise. + */ +gboolean +gst_element_link_many (GstElement * element_1, GstElement * element_2, ...) +{ + va_list args; + + g_return_val_if_fail (GST_IS_ELEMENT (element_1), FALSE); + g_return_val_if_fail (GST_IS_ELEMENT (element_2), FALSE); + + va_start (args, element_2); + + while (element_2) { + if (!gst_element_link (element_1, element_2)) + return FALSE; + + element_1 = element_2; + element_2 = va_arg (args, GstElement *); + } + + va_end (args); + + return TRUE; +} + +/** + * gst_element_link: + * @src: a #GstElement containing the source pad. + * @dest: the #GstElement containing the destination pad. + * + * Links @src to @dest with no filter caps. See gst_element_link_filtered() for + * more information. + * + * Returns: TRUE if the elements could be linked, FALSE otherwise. + */ +gboolean +gst_element_link (GstElement * src, GstElement * dest) +{ + return gst_element_link_pads_filtered (src, NULL, dest, NULL, NULL); +} + +/** + * gst_element_link_pads: + * @src: a #GstElement containing the source pad. + * @srcpadname: the name of the #GstPad in the source element. + * @dest: the #GstElement containing the destination pad. + * @destpadname: the name of the #GstPad in destination element. + * + * Links the two named pads of the source and destination elements. + * Side effect is that if one of the pads has no parent, it becomes a + * child of the parent of the other element. If they have different + * parents, the link fails. + * + * Returns: TRUE if the pads could be linked, FALSE otherwise. + */ +gboolean +gst_element_link_pads (GstElement * src, const gchar * srcpadname, + GstElement * dest, const gchar * destpadname) +{ + return gst_element_link_pads_filtered (src, srcpadname, dest, destpadname, + NULL); +} + +/** + * gst_element_unlink_pads: + * @src: a #GstElement containing the source pad. + * @srcpadname: the name of the #GstPad in source element. + * @dest: a #GstElement containing the destination pad. + * @destpadname: the name of the #GstPad in destination element. + * + * Unlinks the two named pads of the source and destination elements. + */ +void +gst_element_unlink_pads (GstElement * src, const gchar * srcpadname, + GstElement * dest, const gchar * destpadname) +{ + GstPad *srcpad, *destpad; + + g_return_if_fail (src != NULL); + g_return_if_fail (GST_IS_ELEMENT (src)); + g_return_if_fail (srcpadname != NULL); + g_return_if_fail (dest != NULL); + g_return_if_fail (GST_IS_ELEMENT (dest)); + g_return_if_fail (destpadname != NULL); + + /* obtain the pads requested */ + srcpad = gst_element_get_pad (src, srcpadname); + if (srcpad == NULL) { + GST_WARNING_OBJECT (src, "source element has no pad \"%s\"", srcpadname); + return; + } + destpad = gst_element_get_pad (dest, destpadname); + if (srcpad == NULL) { + GST_WARNING_OBJECT (dest, "destination element has no pad \"%s\"", + destpadname); + return; + } + + /* we're satisified they can be unlinked, let's do it */ + gst_pad_unlink (srcpad, destpad); +} + +/** + * gst_element_unlink_many: + * @element_1: the first #GstElement in the link chain. + * @element_2: the second #GstElement in the link chain. + * @...: the NULL-terminated list of elements to unlink in order. + * + * Unlinks a series of elements. Uses gst_element_unlink(). + */ +void +gst_element_unlink_many (GstElement * element_1, GstElement * element_2, ...) +{ + va_list args; + + g_return_if_fail (element_1 != NULL && element_2 != NULL); + g_return_if_fail (GST_IS_ELEMENT (element_1) && GST_IS_ELEMENT (element_2)); + + va_start (args, element_2); + + while (element_2) { + gst_element_unlink (element_1, element_2); + + element_1 = element_2; + element_2 = va_arg (args, GstElement *); + } + + va_end (args); +} + +/** + * gst_element_unlink: + * @src: the source #GstElement to unlink. + * @dest: the sink #GstElement to unlink. + * + * Unlinks all source pads of the source element with all sink pads + * of the sink element to which they are linked. + */ +void +gst_element_unlink (GstElement * src, GstElement * dest) +{ + GstIterator *pads; + gboolean done = FALSE; + + g_return_if_fail (GST_IS_ELEMENT (src)); + g_return_if_fail (GST_IS_ELEMENT (dest)); + + GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "unlinking \"%s\" and \"%s\"", + GST_ELEMENT_NAME (src), GST_ELEMENT_NAME (dest)); + + pads = gst_element_iterate_pads (src); + while (!done) { + gpointer data; + + switch (gst_iterator_next (pads, &data)) { + case GST_ITERATOR_OK: + { + GstPad *pad = GST_PAD_CAST (data); + + /* we only care about real src pads */ + if (GST_IS_REAL_PAD (pad) && GST_PAD_IS_SRC (pad)) { + GstPad *peerpad = gst_pad_get_peer (pad); + + /* see if the pad is connected and is really a pad + * of dest */ + if (peerpad) { + GstElement *peerelem = gst_pad_get_parent (peerpad); + + if (peerelem == dest) { + gst_pad_unlink (pad, peerpad); + } + if (peerelem) + gst_object_unref (GST_OBJECT (peerelem)); + + gst_object_unref (GST_OBJECT (peerpad)); + } + } + gst_object_unref (GST_OBJECT (pad)); + break; + } + case GST_ITERATOR_RESYNC: + gst_iterator_resync (pads); + break; + case GST_ITERATOR_DONE: + done = TRUE; + break; + default: + g_assert_not_reached (); + break; + } + } +} + +/** + * gst_pad_can_link_filtered: + * @srcpad: the source #GstPad to link. + * @sinkpad: the sink #GstPad to link. + * @filtercaps: the filter #GstCaps. + * + * Checks if the source pad and the sink pad can be linked when constrained + * by the given filter caps. Both @srcpad and @sinkpad must be unlinked. + * + * Returns: TRUE if the pads can be linked, FALSE otherwise. + */ +gboolean +gst_pad_can_link_filtered (GstPad * srcpad, GstPad * sinkpad, + const GstCaps * filtercaps) +{ + GstRealPad *realsrc, *realsink; + + /* FIXME This function is gross. It's almost a direct copy of + * gst_pad_link_filtered(). Any decent programmer would attempt + * to merge the two functions, which I will do some day. --ds + */ + + /* generic checks */ + g_return_val_if_fail (srcpad != NULL, FALSE); + g_return_val_if_fail (GST_IS_PAD (srcpad), FALSE); + g_return_val_if_fail (sinkpad != NULL, FALSE); + g_return_val_if_fail (GST_IS_PAD (sinkpad), FALSE); + + GST_CAT_INFO (GST_CAT_PADS, "trying to link %s:%s and %s:%s", + GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (sinkpad)); + + /* now we need to deal with the real/ghost stuff */ + realsrc = GST_PAD_REALIZE (srcpad); + realsink = GST_PAD_REALIZE (sinkpad); + + if ((GST_PAD (realsrc) != srcpad) || (GST_PAD (realsink) != sinkpad)) { + GST_CAT_INFO (GST_CAT_PADS, "*actually* linking %s:%s and %s:%s", + GST_DEBUG_PAD_NAME (realsrc), GST_DEBUG_PAD_NAME (realsink)); + } + /* FIXME: shouldn't we convert this to g_return_val_if_fail? */ + if (GST_RPAD_PEER (realsrc) != NULL) { + GST_CAT_INFO (GST_CAT_PADS, "Real source pad %s:%s has a peer, failed", + GST_DEBUG_PAD_NAME (realsrc)); + return FALSE; + } + if (GST_RPAD_PEER (realsink) != NULL) { + GST_CAT_INFO (GST_CAT_PADS, "Real sink pad %s:%s has a peer, failed", + GST_DEBUG_PAD_NAME (realsink)); + return FALSE; + } + if (!GST_PAD_IS_SRC (realsrc)) { + GST_CAT_INFO (GST_CAT_PADS, "Real src pad %s:%s is not source pad, failed", + GST_DEBUG_PAD_NAME (realsrc)); + return FALSE; + } + if (!GST_PAD_IS_SINK (realsink)) { + GST_CAT_INFO (GST_CAT_PADS, "Real sink pad %s:%s is not sink pad, failed", + GST_DEBUG_PAD_NAME (realsink)); + return FALSE; + } + if (GST_PAD_PARENT (realsrc) == NULL) { + GST_CAT_INFO (GST_CAT_PADS, "Real src pad %s:%s has no parent, failed", + GST_DEBUG_PAD_NAME (realsrc)); + return FALSE; + } + if (GST_PAD_PARENT (realsink) == NULL) { + GST_CAT_INFO (GST_CAT_PADS, "Real sink pad %s:%s has no parent, failed", + GST_DEBUG_PAD_NAME (realsrc)); + return FALSE; + } + + g_return_val_if_fail (realsrc != NULL, GST_PAD_LINK_REFUSED); + g_return_val_if_fail (realsink != NULL, GST_PAD_LINK_REFUSED); + + return TRUE; +} + +/** + * gst_pad_can_link: + * @srcpad: the source #GstPad to link. + * @sinkpad: the sink #GstPad to link. + * + * Checks if the source pad and the sink pad can be linked. + * + * Returns: TRUE if the pads can be linked, FALSE otherwise. + */ +gboolean +gst_pad_can_link (GstPad * srcpad, GstPad * sinkpad) +{ + return gst_pad_can_link_filtered (srcpad, sinkpad, NULL); +} + +/** + * gst_pad_use_fixed_caps: + * @pad: the pad to use + * + * A helper function you can use that sets the + * @gst_pad_get_fixed_caps_func as the gstcaps function for the + * pad. This way the function will always return the negotiated caps + * or in case the pad is not negotiated, the padtemplate caps. + */ +void +gst_pad_use_fixed_caps (GstPad * pad) +{ + gst_pad_set_getcaps_function (pad, gst_pad_get_fixed_caps_func); +} + +/** + * gst_pad_get_fixed_caps_func: + * @pad: the pad to use + * + * A helper function you can use as a GetCaps function that + * will return the currently negotiated caps or the padtemplate + * when NULL. + * + * Returns: The currently negotiated caps or the padtemplate. + */ +GstCaps * +gst_pad_get_fixed_caps_func (GstPad * pad) +{ + GstCaps *result; + GstRealPad *realpad; + + g_return_val_if_fail (GST_IS_REAL_PAD (pad), NULL); + + realpad = GST_REAL_PAD_CAST (pad); + + if (GST_RPAD_CAPS (realpad)) { + result = GST_RPAD_CAPS (realpad); + + GST_CAT_DEBUG (GST_CAT_CAPS, + "using pad caps %p %" GST_PTR_FORMAT, result, result); + + result = gst_caps_ref (result); + goto done; + } + if (GST_PAD_PAD_TEMPLATE (realpad)) { + GstPadTemplate *templ = GST_PAD_PAD_TEMPLATE (realpad); + + result = GST_PAD_TEMPLATE_CAPS (templ); + GST_CAT_DEBUG (GST_CAT_CAPS, + "using pad template %p with caps %p %" GST_PTR_FORMAT, templ, result, + result); + + result = gst_caps_ref (result); + goto done; + } + GST_CAT_DEBUG (GST_CAT_CAPS, "pad has no caps"); + result = gst_caps_new_empty (); + +done: + return result; +} + +/** + * gst_object_default_error: + * @object: a #GObject that signalled the error. + * @orig: the #GstObject that initiated the error. + * @error: the GError. + * @debug: an additional debug information string, or NULL. + * + * A default error function. + * + * The default handler will simply print the error string using g_print. + */ +void +gst_object_default_error (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); + + g_free (name); +} + +/** + * gst_bin_add_many: + * @bin: the bin to add the elements to + * @element_1: the first element to add to the bin + * @...: additional elements to add to the bin + * + * Adds a NULL-terminated list of elements to a bin. This function is + * equivalent to calling #gst_bin_add() for each member of the list. + */ +void +gst_bin_add_many (GstBin * bin, GstElement * element_1, ...) +{ + va_list args; + + g_return_if_fail (GST_IS_BIN (bin)); + g_return_if_fail (GST_IS_ELEMENT (element_1)); + + va_start (args, element_1); + + while (element_1) { + gst_bin_add (bin, element_1); + + element_1 = va_arg (args, GstElement *); + } + + va_end (args); +} + +/** + * gst_bin_remove_many: + * @bin: the bin to remove the elements from + * @element_1: the first element to remove from the bin + * @...: NULL-terminated list of elements to remove from the bin + * + * Remove a list of elements from a bin. This function is equivalent + * to calling #gst_bin_remove with each member of the list. + */ +void +gst_bin_remove_many (GstBin * bin, GstElement * element_1, ...) +{ + va_list args; + + g_return_if_fail (GST_IS_BIN (bin)); + g_return_if_fail (GST_IS_ELEMENT (element_1)); + + va_start (args, element_1); + + while (element_1) { + gst_bin_remove (bin, element_1); + + element_1 = va_arg (args, GstElement *); + } + + va_end (args); +} + +static void +gst_element_populate_std_props (GObjectClass * klass, const gchar * prop_name, + guint arg_id, GParamFlags flags) +{ + GQuark prop_id = g_quark_from_string (prop_name); + GParamSpec *pspec; + + static GQuark fd_id = 0; + static GQuark blocksize_id; + static GQuark bytesperread_id; + static GQuark dump_id; + static GQuark filesize_id; + static GQuark mmapsize_id; + static GQuark location_id; + static GQuark offset_id; + static GQuark silent_id; + static GQuark touch_id; + + if (!fd_id) { + fd_id = g_quark_from_static_string ("fd"); + blocksize_id = g_quark_from_static_string ("blocksize"); + bytesperread_id = g_quark_from_static_string ("bytesperread"); + dump_id = g_quark_from_static_string ("dump"); + filesize_id = g_quark_from_static_string ("filesize"); + mmapsize_id = g_quark_from_static_string ("mmapsize"); + location_id = g_quark_from_static_string ("location"); + offset_id = g_quark_from_static_string ("offset"); + silent_id = g_quark_from_static_string ("silent"); + touch_id = g_quark_from_static_string ("touch"); + } + + if (prop_id == fd_id) { + pspec = g_param_spec_int ("fd", "File-descriptor", + "File-descriptor for the file being read", 0, G_MAXINT, 0, flags); + } else if (prop_id == blocksize_id) { + pspec = g_param_spec_ulong ("blocksize", "Block Size", + "Block size to read per buffer", 0, G_MAXULONG, 4096, flags); + + } else if (prop_id == bytesperread_id) { + pspec = g_param_spec_int ("bytesperread", "Bytes per read", + "Number of bytes to read per buffer", G_MININT, G_MAXINT, 0, flags); + + } else if (prop_id == dump_id) { + pspec = g_param_spec_boolean ("dump", "Dump", + "Dump bytes to stdout", FALSE, flags); + + } else if (prop_id == filesize_id) { + pspec = g_param_spec_int64 ("filesize", "File Size", + "Size of the file being read", 0, G_MAXINT64, 0, flags); + + } else if (prop_id == mmapsize_id) { + pspec = g_param_spec_ulong ("mmapsize", "mmap() Block Size", + "Size in bytes of mmap()d regions", 0, G_MAXULONG, 4 * 1048576, flags); + + } else if (prop_id == location_id) { + pspec = g_param_spec_string ("location", "File Location", + "Location of the file to read", NULL, flags); + + } else if (prop_id == offset_id) { + pspec = g_param_spec_int64 ("offset", "File Offset", + "Byte offset of current read pointer", 0, G_MAXINT64, 0, flags); + + } else if (prop_id == silent_id) { + pspec = g_param_spec_boolean ("silent", "Silent", "Don't produce events", + FALSE, flags); + + } else if (prop_id == touch_id) { + pspec = g_param_spec_boolean ("touch", "Touch read data", + "Touch data to force disk read before " "push ()", TRUE, flags); + } else { + g_warning ("Unknown - 'standard' property '%s' id %d from klass %s", + prop_name, arg_id, g_type_name (G_OBJECT_CLASS_TYPE (klass))); + pspec = NULL; + } + + if (pspec) { + g_object_class_install_property (klass, arg_id, pspec); + } +} + +/** + * gst_element_class_install_std_props: + * @klass: the #GstElementClass to add the properties to. + * @first_name: the name of the first property. + * in a NULL terminated + * @...: the id and flags of the first property, followed by + * further 'name', 'id', 'flags' triplets and terminated by NULL. + * + * Adds a list of standardized properties with types to the @klass. + * the id is for the property switch in your get_prop method, and + * the flags determine readability / writeability. + **/ +void +gst_element_class_install_std_props (GstElementClass * klass, + const gchar * first_name, ...) +{ + const char *name; + + va_list args; + + g_return_if_fail (GST_IS_ELEMENT_CLASS (klass)); + + va_start (args, first_name); + + name = first_name; + + while (name) { + int arg_id = va_arg (args, int); + int flags = va_arg (args, int); + + gst_element_populate_std_props ((GObjectClass *) klass, name, arg_id, + flags); + + name = va_arg (args, char *); + } + + va_end (args); +} + + +/** + * gst_buffer_merge: + * @buf1: a first source #GstBuffer to merge. + * @buf2: the second source #GstBuffer to merge. + * + * Create a new buffer that is the concatenation of the two source + * buffers. The original source buffers will not be modified or + * unref'd. Make sure you unref the source buffers if they are not used + * anymore afterwards. + * + * If the buffers point to contiguous areas of memory, the buffer + * is created without copying the data. + * + * Returns: the new #GstBuffer that's the concatenation of the source buffers. + */ +GstBuffer * +gst_buffer_merge (GstBuffer * buf1, GstBuffer * buf2) +{ + GstBuffer *result; + + /* we're just a specific case of the more general gst_buffer_span() */ + result = gst_buffer_span (buf1, 0, buf2, buf1->size + buf2->size); + + return result; +} + +/** + * gst_buffer_stamp: + * @dest: buffer to stamp + * @src: buffer to stamp from + * + * Copies additional information (timestamps and offsets) from one buffer to + * the other. + */ +void +gst_buffer_stamp (GstBuffer * dest, const GstBuffer * src) +{ + g_return_if_fail (dest != NULL); + g_return_if_fail (src != NULL); + + GST_BUFFER_TIMESTAMP (dest) = GST_BUFFER_TIMESTAMP (src); + GST_BUFFER_DURATION (dest) = GST_BUFFER_DURATION (src); + GST_BUFFER_OFFSET (dest) = GST_BUFFER_OFFSET (src); + GST_BUFFER_OFFSET_END (dest) = GST_BUFFER_OFFSET_END (src); +} + +static gboolean +intersect_caps_func (GstPad * pad, GValue * ret, GstPad * orig) +{ + if (pad != orig) { + GstCaps *peercaps, *existing; + + existing = g_value_get_pointer (ret); + peercaps = gst_pad_peer_get_caps (pad); + g_value_set_pointer (ret, gst_caps_intersect (existing, peercaps)); + gst_caps_unref (existing); + gst_caps_unref (peercaps); + } + return TRUE; +} + +/** + * gst_pad_proxy_getcaps: + * @pad: a #GstPad to proxy. + * + * Calls gst_pad_get_allowed_caps() for every other pad belonging to the + * same element as @pad, and returns the intersection of the results. + * + * This function is useful as a default getcaps function for an element + * that can handle any stream format, but requires all its pads to have + * the same caps. Two such elements are tee and aggregator. + * + * Returns: the intersection of the other pads' allowed caps. + */ +GstCaps * +gst_pad_proxy_getcaps (GstPad * pad) +{ + GstElement *element; + GstCaps *caps, *intersected; + GstIterator *iter; + GstIteratorResult res; + GValue ret = { 0, }; + + g_return_val_if_fail (GST_IS_PAD (pad), NULL); + + GST_DEBUG ("proxying getcaps for %s:%s", GST_DEBUG_PAD_NAME (pad)); + + element = gst_pad_get_parent (pad); + + iter = gst_element_iterate_pads (element); + + g_value_init (&ret, G_TYPE_POINTER); + g_value_set_pointer (&ret, gst_caps_new_any ()); + + res = gst_iterator_fold (iter, (GstIteratorFoldFunction) intersect_caps_func, + &ret, pad); + gst_iterator_free (iter); + + if (res != GST_ITERATOR_DONE) { + g_warning ("Pad list changed during capsnego for element %s", + GST_ELEMENT_NAME (element)); + return NULL; + } + + caps = g_value_get_pointer (&ret); + g_value_unset (&ret); + + intersected = gst_caps_intersect (caps, gst_pad_get_pad_template_caps (pad)); + gst_caps_unref (caps); + + return intersected; +} + +typedef struct +{ + GstPad *orig; + GstCaps *caps; +} LinkData; + +static gboolean +link_fold_func (GstPad * pad, GValue * ret, LinkData * data) +{ + gboolean success = TRUE; + + if (pad != data->orig) { + success = gst_pad_set_caps (pad, data->caps); + g_value_set_boolean (ret, success); + } + + return success; +} + +/** + * gst_pad_proxy_setcaps + * @pad: a #GstPad to proxy from + * @caps: the #GstCaps to link with + * + * Calls gst_pad_set_caps() for every other pad belonging to the + * same element as @pad. If gst_pad_set_caps() fails on any pad, + * the proxy setcaps fails. May be used only during negotiation. + * + * Returns: TRUE if sucessful + */ +gboolean +gst_pad_proxy_setcaps (GstPad * pad, GstCaps * caps) +{ + GstElement *element; + GstIterator *iter; + GstIteratorResult res; + GValue ret = { 0, }; + LinkData data; + + g_return_val_if_fail (GST_IS_PAD (pad), FALSE); + g_return_val_if_fail (caps != NULL, FALSE); + + GST_DEBUG ("proxying pad link for %s:%s", GST_DEBUG_PAD_NAME (pad)); + + element = gst_pad_get_parent (pad); + + iter = gst_element_iterate_pads (element); + + g_value_init (&ret, G_TYPE_BOOLEAN); + g_value_set_boolean (&ret, TRUE); + data.orig = pad; + data.caps = caps; + + res = gst_iterator_fold (iter, (GstIteratorFoldFunction) link_fold_func, + &ret, &data); + gst_iterator_free (iter); + + if (res != GST_ITERATOR_DONE) { + g_warning ("Pad list changed during proxy_pad_link for element %s", + GST_ELEMENT_NAME (element)); + return FALSE; + } + + /* ok not to unset the gvalue */ + return g_value_get_boolean (&ret); +} diff --git a/gst/gstutils.h b/gst/gstutils.h index 86b7d9b883..62c9620786 100644 --- a/gst/gstutils.h +++ b/gst/gstutils.h @@ -25,7 +25,7 @@ #define __GST_UTILS_H__ #include -#include +#include G_BEGIN_DECLS @@ -224,6 +224,62 @@ type_as_function ## _get_type (void) \ #endif /* GST_HAVE_UNALIGNED_ACCESS */ +void gst_object_default_error (GstObject * source, + GError * error, gchar * debug); + + +/* element functions */ +GstFlowReturn gst_element_abort_preroll (GstElement *element); +GstFlowReturn gst_element_finish_preroll (GstElement *element, GstPad *pad); + +GstPad* gst_element_get_compatible_pad (GstElement *element, GstPad *pad); +GstPad* gst_element_get_compatible_pad_filtered (GstElement *element, GstPad *pad, + const GstCaps *filtercaps); + +GstPadTemplate* gst_element_get_compatible_pad_template (GstElement *element, GstPadTemplate *compattempl); + +G_CONST_RETURN gchar* gst_element_state_get_name (GstElementState state); + +gboolean gst_element_link (GstElement *src, GstElement *dest); +gboolean gst_element_link_many (GstElement *element_1, + GstElement *element_2, ...); +gboolean gst_element_link_filtered (GstElement *src, GstElement *dest, + const GstCaps *filtercaps); +void gst_element_unlink (GstElement *src, GstElement *dest); +void gst_element_unlink_many (GstElement *element_1, + GstElement *element_2, ...); + +gboolean gst_element_link_pads (GstElement *src, const gchar *srcpadname, + GstElement *dest, const gchar *destpadname); +gboolean gst_element_link_pads_filtered (GstElement *src, const gchar *srcpadname, + GstElement *dest, const gchar *destpadname, + const GstCaps *filtercaps); +void gst_element_unlink_pads (GstElement *src, const gchar *srcpadname, + GstElement *dest, const gchar *destpadname); + +/* element class functions */ +void gst_element_class_install_std_props (GstElementClass * klass, + const gchar * first_name, ...); + +/* pad functions */ +gboolean gst_pad_can_link (GstPad *srcpad, GstPad *sinkpad); +gboolean gst_pad_can_link_filtered (GstPad *srcpad, GstPad *sinkpad, const GstCaps *filtercaps); + +void gst_pad_use_fixed_caps (GstPad *pad); +GstCaps* gst_pad_get_fixed_caps_func (GstPad *pad); +GstCaps* gst_pad_proxy_getcaps (GstPad * pad); +gboolean gst_pad_proxy_setcaps (GstPad * pad, GstCaps * caps); + +/* bin functions */ +void gst_bin_add_many (GstBin *bin, GstElement *element_1, ...); +void gst_bin_remove_many (GstBin *bin, GstElement *element_1, ...); + +/* buffer functions */ +GstBuffer * gst_buffer_merge (GstBuffer * buf1, GstBuffer * buf2); +void gst_buffer_stamp (GstBuffer * dest, const GstBuffer * src); +void gst_buffer_stamp (GstBuffer * dest, const GstBuffer * src); + + G_END_DECLS #endif /* __GST_UTILS_H__ */ diff --git a/gst/gstvalue.c b/gst/gstvalue.c index 03e80beeb7..2a24eb7837 100644 --- a/gst/gstvalue.c +++ b/gst/gstvalue.c @@ -1343,7 +1343,7 @@ gst_value_deserialize_string (GValue * dest, const char *s) if (!str) return FALSE; - g_value_set_string_take_ownership (dest, str); + g_value_take_string (dest, str); } return TRUE; diff --git a/gst/parse/grammar.y b/gst/parse/grammar.y index 9c8a6b5661..792bf5b9f7 100644 --- a/gst/parse/grammar.y +++ b/gst/parse/grammar.y @@ -13,6 +13,7 @@ #include "../gstinfo.h" #include "../gsterror.h" #include "../gsturi.h" +#include "../gstutils.h" #include "../gstvalue.h" #include "types.h" @@ -285,28 +286,56 @@ gst_parse_free_link (link_t *link) g_slist_foreach (link->sink_pads, (GFunc) gst_parse_strfree, NULL); g_slist_free (link->src_pads); g_slist_free (link->sink_pads); - if (link->caps) gst_caps_free (link->caps); + if (link->caps) gst_caps_unref (link->caps); gst_parse_link_free (link); } + static void gst_parse_element_lock (GstElement *element, gboolean lock) { GstPad *pad; - GList *walk = (GList *) gst_element_get_pad_list (element); + GstIterator *pads; gboolean unlocked_peer = FALSE; + gboolean done = FALSE; + GList *walk; if (gst_element_is_locked_state (element) == lock) return; + + return; + /* check if we have an unlocked peer */ - for (; walk; walk = walk->next) { - pad = (GstPad *) GST_PAD_REALIZE (walk->data); - if (GST_PAD_IS_SINK (pad) && GST_PAD_PEER (pad) && - !gst_element_is_locked_state (GST_PAD_PARENT (GST_PAD_PEER (pad)))) { - unlocked_peer = TRUE; - break; + pads = gst_element_iterate_pads (element); + while (!done) { + gpointer data; + switch (gst_iterator_next (pads, &data)) { + case GST_ITERATOR_OK: + { + GstPad *pad = GST_PAD_CAST (data); + + pad = gst_pad_realize (pad); + if (GST_PAD_IS_SINK (pad) && GST_PAD_PEER (pad) && + !gst_element_is_locked_state (GST_PAD_PARENT (GST_PAD_PEER (pad)))) { + unlocked_peer = TRUE; + done = TRUE; + } + gst_object_unref (GST_OBJECT (pad)); + break; + } + case GST_ITERATOR_RESYNC: + unlocked_peer = FALSE; + gst_iterator_resync (pads); + break; + case GST_ITERATOR_DONE: + done = TRUE; + break; + default: + g_assert_not_reached (); + break; } - } - + } + gst_iterator_free (pads); + if (!(lock && unlocked_peer)) { GST_CAT_DEBUG (GST_CAT_PIPELINE, "setting locked state on element"); gst_element_set_locked_state (element, lock); @@ -324,7 +353,7 @@ gst_parse_element_lock (GstElement *element, gboolean lock) } /* check if there are other pads to (un)lock */ - walk = (GList *) gst_element_get_pad_list (element); + walk = (GList *) element->pads; for (; walk; walk = walk->next) { pad = (GstPad *) GST_PAD_REALIZE (walk->data); if (GST_PAD_IS_SRC (pad) && GST_PAD_PEER (pad)) { @@ -351,7 +380,7 @@ gst_parse_found_pad (GstElement *src, GstPad *pad, gpointer data) g_signal_handler_disconnect (src, link->signal_id); g_free (link->src_pad); g_free (link->sink_pad); - if (link->caps) gst_caps_free (link->caps); + if (link->caps) gst_caps_unref (link->caps); if (!gst_element_is_locked_state (src)) gst_parse_element_lock (link->sink, FALSE); g_free (link); @@ -362,7 +391,7 @@ static gboolean gst_parse_perform_delayed_link (GstElement *src, const gchar *src_pad, GstElement *sink, const gchar *sink_pad, GstCaps *caps) { - GList *templs = gst_element_get_pad_template_list (src); + GList *templs = gst_element_class_get_pad_template_list (GST_ELEMENT_GET_CLASS (src)); for (; templs; templs = templs->next) { GstPadTemplate *templ = (GstPadTemplate *) templs->data; diff --git a/gst/schedulers/gstbasicscheduler.c b/gst/schedulers/gstbasicscheduler.c index d82be10ced..c8dd004913 100644 --- a/gst/schedulers/gstbasicscheduler.c +++ b/gst/schedulers/gstbasicscheduler.c @@ -75,7 +75,7 @@ struct _GstSchedulerChain #define GST_IS_BASIC_SCHEDULER_CLASS(obj) \ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_BASIC_SCHEDULER)) -#define SCHED(element) GST_BASIC_SCHEDULER (GST_ELEMENT_SCHED (element)) +#define SCHED(element) GST_BASIC_SCHEDULER (GST_ELEMENT_SCHEDULER (element)) typedef enum { @@ -651,7 +651,7 @@ gst_basic_scheduler_cothreaded_chain (GstBin * bin, GstSchedulerChain * chain) } /* now we have to walk through the pads to set up their state */ - pads = gst_element_get_pad_list (element); + pads = element->pads; while (pads) { GstPad *peerpad; @@ -1046,8 +1046,8 @@ gst_basic_scheduler_chain_recursive_add (GstSchedulerChain * chain, if (GST_PAD_PEER (pad)) { GST_DEBUG ("has peer %s:%s", GST_DEBUG_PAD_NAME (GST_PAD_PEER (pad))); peerelement = GST_PAD_PARENT (GST_PAD_PEER (pad)); - if (GST_ELEMENT_SCHED (GST_PAD_PARENT (pad)) == - GST_ELEMENT_SCHED (peerelement)) { + if (GST_ELEMENT_SCHEDULER (GST_PAD_PARENT (pad)) == + GST_ELEMENT_SCHEDULER (peerelement)) { GST_DEBUG ("peer \"%s\" is valid for same chain", GST_ELEMENT_NAME (peerelement)); gst_basic_scheduler_chain_recursive_add (chain, peerelement, remove); @@ -1247,7 +1247,7 @@ gst_basic_scheduler_pad_link (GstScheduler * sched, GstPad * srcpad, GST_INFO ("have pad linked callback on %s:%s to %s:%s", GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (sinkpad)); GST_DEBUG ("srcpad sched is %p, sinkpad sched is %p", - GST_ELEMENT_SCHED (srcelement), GST_ELEMENT_SCHED (sinkelement)); + GST_ELEMENT_SCHEDULER (srcelement), GST_ELEMENT_SCHEDULER (sinkelement)); gst_basic_scheduler_chain_elements (bsched, srcelement, sinkelement); } diff --git a/gst/schedulers/gstoptimalscheduler.c b/gst/schedulers/gstoptimalscheduler.c index c49ca33483..99218e9c8b 100644 --- a/gst/schedulers/gstoptimalscheduler.c +++ b/gst/schedulers/gstoptimalscheduler.c @@ -1386,7 +1386,7 @@ get_group_schedule_function (int argc, char *argv[]) osched = group->chain->sched; - pads = gst_element_get_pad_list (entry); + pads = entry->pads; GST_LOG ("executing get-based group %p", group); @@ -1725,7 +1725,7 @@ gst_opt_scheduler_state_transition (GstScheduler * sched, GstElement * element, break; case GST_STATE_PAUSED_TO_READY: { - GList *pads = (GList *) gst_element_get_pad_list (element); + GList *pads = (GList *) element->pads; g_list_foreach (pads, (GFunc) pad_clear_queued, NULL); break; @@ -1994,7 +1994,7 @@ gst_opt_scheduler_add_element (GstScheduler * sched, GstElement * element) * in _link, it can be overruled if need be */ /* FIXME: we should also do this when new pads on the element are created; but there are no hooks, so we do it again in _link */ - pads = gst_element_get_pad_list (element); + pads = element->pads; while (pads) { GstPad *pad = GST_PAD (pads->data); @@ -2327,7 +2327,7 @@ element_get_reachables_func (GstElement * element, GstOptSchedulerGroup * group, result = g_list_prepend (result, element); - pads = gst_element_get_pad_list (element); + pads = element->pads; while (pads) { GstPad *pad = GST_PAD (pads->data); GstPad *peer; diff --git a/libs/gst/bytestream/bytestream.c b/libs/gst/bytestream/bytestream.c index 2a1ef69cbc..1954de5c18 100644 --- a/libs/gst/bytestream/bytestream.c +++ b/libs/gst/bytestream/bytestream.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "bytestream.h" GST_DEBUG_CATEGORY_STATIC (debug_bs); diff --git a/libs/gst/dataprotocol/dataprotocol.c b/libs/gst/dataprotocol/dataprotocol.c index 645d7574a7..b271883c37 100644 --- a/libs/gst/dataprotocol/dataprotocol.c +++ b/libs/gst/dataprotocol/dataprotocol.c @@ -188,7 +188,7 @@ gst_dp_header_from_buffer (const GstBuffer * buffer, GstDPHeaderFlag flags, /* data flags */ /* we only copy KEY_UNIT,DELTA_UNIT and IN_CAPS flags */ - flags_mask = GST_DATA_FLAG_SHIFT (GST_BUFFER_KEY_UNIT) | + flags_mask = GST_DATA_FLAG_SHIFT (GST_BUFFER_PREROLL) | GST_DATA_FLAG_SHIFT (GST_BUFFER_IN_CAPS) | GST_DATA_FLAG_SHIFT (GST_BUFFER_DELTA_UNIT); diff --git a/plugins/elements/gstfakesrc.c b/plugins/elements/gstfakesrc.c index 0b64860e90..780835e9a6 100644 --- a/plugins/elements/gstfakesrc.c +++ b/plugins/elements/gstfakesrc.c @@ -887,7 +887,7 @@ gst_fakesrc_loop (GstElement * element) src = GST_FAKESRC (element); - pads = gst_element_get_pad_list (element); + pads = element->pads; while (pads) { GstPad *pad = GST_PAD (pads->data); diff --git a/plugins/elements/gstidentity.c b/plugins/elements/gstidentity.c index 835697c209..2b4c460636 100644 --- a/plugins/elements/gstidentity.c +++ b/plugins/elements/gstidentity.c @@ -203,14 +203,14 @@ gst_identity_init (GstIdentity * identity) gst_element_add_pad (GST_ELEMENT (identity), identity->sinkpad); gst_pad_set_chain_function (identity->sinkpad, GST_DEBUG_FUNCPTR (gst_identity_chain)); - gst_pad_set_link_function (identity->sinkpad, gst_pad_proxy_pad_link); + //gst_pad_set_link_function (identity->sinkpad, gst_pad_proxy_pad_link); gst_pad_set_getcaps_function (identity->sinkpad, gst_pad_proxy_getcaps); identity->srcpad = gst_pad_new_from_template (gst_static_pad_template_get (&srctemplate), "src"); gst_element_add_pad (GST_ELEMENT (identity), identity->srcpad); - gst_pad_set_link_function (identity->srcpad, gst_pad_proxy_pad_link); + //gst_pad_set_link_function (identity->srcpad, gst_pad_proxy_pad_link); gst_pad_set_getcaps_function (identity->srcpad, gst_pad_proxy_getcaps); identity->loop_based = DEFAULT_LOOP_BASED; diff --git a/plugins/elements/gstqueue.c b/plugins/elements/gstqueue.c index b9ade24055..0ed213dd70 100644 --- a/plugins/elements/gstqueue.c +++ b/plugins/elements/gstqueue.c @@ -434,6 +434,8 @@ gst_queue_link_sink (GstPad * pad, const GstCaps * caps) GST_QUEUE_MUTEX_UNLOCK; } + link_ret = GST_PAD_LINK_OK; +#if 0 link_ret = gst_pad_proxy_pad_link (pad, caps); if (GST_PAD_LINK_SUCCESSFUL (link_ret)) { @@ -441,6 +443,7 @@ gst_queue_link_sink (GstPad * pad, const GstCaps * caps) * the pads become unnegotiated while we have buffers */ gst_caps_replace (&queue->negotiated_caps, gst_caps_copy (caps)); } +#endif return link_ret; } @@ -459,8 +462,10 @@ gst_queue_link_src (GstPad * pad, const GstCaps * caps) } return GST_PAD_LINK_REFUSED; } - +#if 0 link_ret = gst_pad_proxy_pad_link (pad, caps); +#endif + link_ret = GST_PAD_LINK_OK; if (GST_PAD_LINK_SUCCESSFUL (link_ret)) { /* we store an extra copy of the negotiated caps, just in case diff --git a/plugins/elements/gsttee.c b/plugins/elements/gsttee.c index a046ed3635..eec4586047 100644 --- a/plugins/elements/gsttee.c +++ b/plugins/elements/gsttee.c @@ -141,8 +141,7 @@ gst_tee_init (GstTee * tee) "sink"); gst_element_add_pad (GST_ELEMENT (tee), tee->sinkpad); gst_pad_set_chain_function (tee->sinkpad, GST_DEBUG_FUNCPTR (gst_tee_chain)); - gst_pad_set_link_function (tee->sinkpad, - GST_DEBUG_FUNCPTR (gst_pad_proxy_pad_link)); + //gst_pad_set_link_function (tee->sinkpad, GST_DEBUG_FUNCPTR (gst_pad_proxy_pad_link)); gst_pad_set_getcaps_function (tee->sinkpad, GST_DEBUG_FUNCPTR (gst_pad_proxy_getcaps)); @@ -169,16 +168,15 @@ gst_tee_getcaps (GstPad * _pad) GstPad *pad; const GList *pads; - for (pads = gst_element_get_pad_list (GST_ELEMENT (tee)); - pads != NULL; pads = pads->next) { + for (pads = GST_ELEMENT (tee)->pads; pads != NULL; pads = pads->next) { pad = GST_PAD (pads->data); if (pad == _pad) continue; tmp = gst_pad_get_allowed_caps (pad); res = gst_caps_intersect (caps, tmp); - gst_caps_free (tmp); - gst_caps_free (caps); + gst_caps_unref (tmp); + gst_caps_unref (caps); caps = res; } @@ -195,8 +193,7 @@ gst_tee_link (GstPad * _pad, const GstCaps * caps) GST_DEBUG_OBJECT (tee, "Forwarding link to all other pads"); - for (pads = gst_element_get_pad_list (GST_ELEMENT (tee)); - pads != NULL; pads = pads->next) { + for (pads = GST_ELEMENT (tee)->pads; pads != NULL; pads = pads->next) { pad = GST_PAD (pads->data); if (pad == _pad) continue; @@ -231,7 +228,7 @@ gst_tee_request_new_pad (GstElement * element, GstPadTemplate * templ, tee = GST_TEE (element); /* try names in order and find one that's not in use atm */ - pads = gst_element_get_pad_list (element); + pads = element->pads; name = NULL; while (!name) { @@ -335,7 +332,7 @@ gst_tee_chain (GstPad * pad, GstData * _data) gst_buffer_ref_by_count (buf, GST_ELEMENT (tee)->numsrcpads - 1); - pads = gst_element_get_pad_list (GST_ELEMENT (tee)); + pads = GST_ELEMENT (tee)->pads; while (pads) { GstPad *outpad = GST_PAD (pads->data); diff --git a/plugins/elements/gsttypefindelement.c b/plugins/elements/gsttypefindelement.c index a3b6fa206c..9c7b162444 100644 --- a/plugins/elements/gsttypefindelement.c +++ b/plugins/elements/gsttypefindelement.c @@ -350,7 +350,7 @@ free_entry (TypeFindEntry * entry) free_entry_buffers (entry); if (entry->caps) - gst_caps_free (entry->caps); + gst_caps_unref (entry->caps); g_free (entry); } static void diff --git a/po/nb.po b/po/nb.po index b73729c393..26d532a62a 100644 --- a/po/nb.po +++ b/po/nb.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: gstreamer 0.8.8\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2004-12-17 16:11+0100\n" +"POT-Creation-Date: 2005-03-03 13:45+0100\n" "PO-Revision-Date: 2005-02-17 12:00+0100\n" "Last-Translator: Kjartan Maraas \n" "Language-Team: Norwegian Bokmaal \n" @@ -27,7 +27,8 @@ msgid "Print available debug categories and exit" msgstr "Skriv ut tilgjengelige feilsøkingskategorier og avslutt" #: gst/gst.c:169 -msgid "Default debug level from 1 (only error) to 5 (anything) or 0 for no output" +msgid "" +"Default debug level from 1 (only error) to 5 (anything) or 0 for no output" msgstr "" #: gst/gst.c:171 @@ -35,7 +36,9 @@ msgid "LEVEL" msgstr "NIVÅ" #: gst/gst.c:173 -msgid "Comma-separated list of category_name:level pairs to set specific levels for the individual categories. Example: GST_AUTOPLUG:5,GST_ELEMENT_*:3" +msgid "" +"Comma-separated list of category_name:level pairs to set specific levels for " +"the individual categories. Example: GST_AUTOPLUG:5,GST_ELEMENT_*:3" msgstr "" #: gst/gst.c:176 @@ -63,7 +66,9 @@ msgid "PATHS" msgstr "STIER" #: gst/gst.c:191 -msgid "Comma-separated list of plugins to preload in addition to the list stored in envronment variable GST_PLUGIN_PATH" +msgid "" +"Comma-separated list of plugins to preload in addition to the list stored in " +"environment variable GST_PLUGIN_PATH" msgstr "" #: gst/gst.c:193 @@ -96,12 +101,12 @@ msgstr "" msgid "Scheduler to use (default is '%s')" msgstr "Planlegger som skal brukes («%s» er forvalgt)" -#: gst/gstelement.c:261 +#: gst/gstelement.c:312 #, c-format msgid "ERROR: from element %s: %s\n" msgstr "FEIL: fra element %s: %s\n" -#: gst/gstelement.c:263 +#: gst/gstelement.c:314 #, c-format msgid "" "Additional debug info:\n" @@ -115,7 +120,9 @@ msgid "GStreamer encountered a general core library error." msgstr "" #: gst/gsterror.c:58 gst/gsterror.c:95 gst/gsterror.c:115 gst/gsterror.c:145 -msgid "GStreamer developers were too lazy to assign an error code to this error. Please file a bug." +msgid "" +"GStreamer developers were too lazy to assign an error code to this error. " +"Please file a bug." msgstr "" #: gst/gsterror.c:61 @@ -158,7 +165,7 @@ msgstr "" msgid "Internal GStreamer error: tag problem. File a bug." msgstr "" -#: gst/gsterror.c:93 gst/gsterror.c:113 gst/gsterror.c:143 +#: gst/gsterror.c:93 msgid "GStreamer encountered a general supporting library error." msgstr "" @@ -170,6 +177,10 @@ msgstr "Kunne ikke initiere støttebibliotek." msgid "Could not close supporting library." msgstr "Kunne ikke lukke støttebibliotek." +#: gst/gsterror.c:113 +msgid "GStreamer encountered a general resource error." +msgstr "" + #: gst/gsterror.c:117 msgid "Resource not found." msgstr "Ressursen ble ikke funnet." @@ -214,6 +225,10 @@ msgstr "Kunne ikke synkronisere på ressurs." msgid "Could not get/set settings from/on resource." msgstr "Kunne ikke hente/sette innstillinger fra/på ressurs." +#: gst/gsterror.c:143 +msgid "GStreamer encountered a general stream error." +msgstr "" + #: gst/gsterror.c:148 msgid "Element doesn't implement handling of this stream. Please file a bug." msgstr "" @@ -290,7 +305,8 @@ msgstr "dato" #: gst/gsttag.c:95 msgid "date the data was created (in Julian calendar days)" -msgstr "dato for oppretting av dataene (kalenderdager i den Julianske kalenderen)" +msgstr "" +"dato for oppretting av dataene (kalenderdager i den Julianske kalenderen)" #: gst/gsttag.c:98 msgid "genre" @@ -551,8 +567,8 @@ msgstr "Kunne ikke åpne fil «%s» for skriving." msgid "Error closing file \"%s\"." msgstr "Feil ved lukking av fil «%s»." -#: gst/elements/gstfilesink.c:364 gst/elements/gstfilesink.c:399 -#: gst/elements/gstfilesink.c:452 +#: gst/elements/gstfilesink.c:364 gst/elements/gstfilesink.c:400 +#: gst/elements/gstfilesink.c:453 #, c-format msgid "Error while writing to file \"%s\"." msgstr "Feil ved skriving til fil «%s»." @@ -668,112 +684,114 @@ msgstr "" msgid "Print all elements" msgstr "" -#: tools/gst-launch.c:107 +#: tools/gst-launch.c:114 #, c-format -msgid "Execution ended after %s iterations (sum %s ns, average %s ns, min %s ns, max %s ns).\n" +msgid "" +"Execution ended after %s iterations (sum %s ns, average %s ns, min %s ns, " +"max %s ns).\n" msgstr "" -#: tools/gst-launch.c:134 +#: tools/gst-launch.c:141 msgid "Usage: gst-xmllaunch [ element.property=value ... ]\n" msgstr "" -#: tools/gst-launch.c:142 +#: tools/gst-launch.c:149 #, c-format msgid "ERROR: parse of xml file '%s' failed.\n" msgstr "" -#: tools/gst-launch.c:148 +#: tools/gst-launch.c:155 #, c-format msgid "ERROR: no toplevel pipeline element in file '%s'.\n" msgstr "" -#: tools/gst-launch.c:155 +#: tools/gst-launch.c:162 #, c-format msgid "WARNING: only one toplevel element is supported at this time." msgstr "" -#: tools/gst-launch.c:166 +#: tools/gst-launch.c:173 #, c-format msgid "ERROR: could not parse command line argument %d: %s.\n" msgstr "" -#: tools/gst-launch.c:177 +#: tools/gst-launch.c:184 #, c-format msgid "WARNING: element named '%s' not found.\n" msgstr "" -#: tools/gst-launch.c:316 +#: tools/gst-launch.c:327 #, c-format msgid "FOUND TAG : found by element \"%s\".\n" msgstr "" -#: tools/gst-launch.c:403 +#: tools/gst-launch.c:412 msgid "Output tags (also known as metadata)" msgstr "" -#: tools/gst-launch.c:405 +#: tools/gst-launch.c:414 msgid "Output status information and property notifications" msgstr "" -#: tools/gst-launch.c:407 +#: tools/gst-launch.c:416 msgid "Do not output status information of TYPE" msgstr "" -#: tools/gst-launch.c:407 +#: tools/gst-launch.c:416 msgid "TYPE1,TYPE2,..." msgstr "TYPE1,TYPE2,..." -#: tools/gst-launch.c:410 +#: tools/gst-launch.c:419 msgid "Save xml representation of pipeline to FILE and exit" msgstr "" -#: tools/gst-launch.c:410 +#: tools/gst-launch.c:419 msgid "FILE" msgstr "FIL" -#: tools/gst-launch.c:413 +#: tools/gst-launch.c:422 msgid "Do not install a fault handler" msgstr "" -#: tools/gst-launch.c:415 +#: tools/gst-launch.c:424 msgid "Print alloc trace (if enabled at compile time)" msgstr "" -#: tools/gst-launch.c:417 +#: tools/gst-launch.c:426 msgid "Number of times to iterate pipeline" msgstr "" -#: tools/gst-launch.c:487 -#, c-format -msgid "ERROR: pipeline could not be constructed: %s.\n" -msgstr "" - -#: tools/gst-launch.c:491 -#, c-format -msgid "ERROR: pipeline could not be constructed.\n" -msgstr "" - -#: tools/gst-launch.c:495 -#, c-format -msgid "WARNING: erroneous pipeline: %s\n" -msgstr "" - #: tools/gst-launch.c:496 #, c-format +msgid "ERROR: pipeline could not be constructed: %s.\n" +msgstr "" + +#: tools/gst-launch.c:500 +#, c-format +msgid "ERROR: pipeline could not be constructed.\n" +msgstr "" + +#: tools/gst-launch.c:504 +#, c-format +msgid "WARNING: erroneous pipeline: %s\n" +msgstr "" + +#: tools/gst-launch.c:505 +#, c-format msgid " Trying to run anyway.\n" msgstr "" -#: tools/gst-launch.c:523 +#: tools/gst-launch.c:532 #, c-format msgid "ERROR: the 'pipeline' element wasn't found.\n" msgstr "" -#: tools/gst-launch.c:530 +#: tools/gst-launch.c:539 #, c-format msgid "RUNNING pipeline ...\n" msgstr "" -#: tools/gst-launch.c:533 +#: tools/gst-launch.c:542 #, c-format msgid "ERROR: pipeline doesn't want to play.\n" msgstr "" diff --git a/po/ru.po b/po/ru.po index 1c7e1c41f7..117f023f4f 100644 --- a/po/ru.po +++ b/po/ru.po @@ -7,14 +7,15 @@ msgid "" msgstr "" "Project-Id-Version: gstreamer 0.8.8\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2004-12-17 16:11+0100\n" +"POT-Creation-Date: 2005-03-03 13:45+0100\n" "PO-Revision-Date: 2005-02-12 23:37+0300\n" "Last-Translator: Peter Astakhov \n" "Language-Team: Russian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=3; plural=(n%10==1&&n%100!=11?0:n%10>=2&&n%10<=4&&(n%100<10||n%100>=20)?1:2);\n" +"Plural-Forms: nplurals=3; plural=(n%10==1&&n%100!=11?0:n%10>=2&&n%10<=4&&(n%" +"100<10||n%100>=20)?1:2);\n" "X-Generator: KBabel 1.9.1\n" #: gst/gst.c:160 @@ -30,15 +31,20 @@ msgid "Print available debug categories and exit" msgstr "Печатает доступные категории отладки и выходит" #: gst/gst.c:169 -msgid "Default debug level from 1 (only error) to 5 (anything) or 0 for no output" -msgstr "Уровень отладки по умолчанию от 1 (только ошибки) до 5(все) или 0 - ничего не печатать" +msgid "" +"Default debug level from 1 (only error) to 5 (anything) or 0 for no output" +msgstr "" +"Уровень отладки по умолчанию от 1 (только ошибки) до 5(все) или 0 - ничего " +"не печатать" #: gst/gst.c:171 msgid "LEVEL" msgstr "УРОВЕНЬ" #: gst/gst.c:173 -msgid "Comma-separated list of category_name:level pairs to set specific levels for the individual categories. Example: GST_AUTOPLUG:5,GST_ELEMENT_*:3" +msgid "" +"Comma-separated list of category_name:level pairs to set specific levels for " +"the individual categories. Example: GST_AUTOPLUG:5,GST_ELEMENT_*:3" msgstr "" #: gst/gst.c:176 @@ -66,7 +72,9 @@ msgid "PATHS" msgstr "ПУТИ" #: gst/gst.c:191 -msgid "Comma-separated list of plugins to preload in addition to the list stored in envronment variable GST_PLUGIN_PATH" +msgid "" +"Comma-separated list of plugins to preload in addition to the list stored in " +"environment variable GST_PLUGIN_PATH" msgstr "" #: gst/gst.c:193 @@ -99,12 +107,12 @@ msgstr "список путей для загрузки расширений (р msgid "Scheduler to use (default is '%s')" msgstr "" -#: gst/gstelement.c:261 +#: gst/gstelement.c:312 #, c-format msgid "ERROR: from element %s: %s\n" msgstr "ОШИБКА: из элемента %s: %s\n" -#: gst/gstelement.c:263 +#: gst/gstelement.c:314 #, c-format msgid "" "Additional debug info:\n" @@ -118,7 +126,9 @@ msgid "GStreamer encountered a general core library error." msgstr "" #: gst/gsterror.c:58 gst/gsterror.c:95 gst/gsterror.c:115 gst/gsterror.c:145 -msgid "GStreamer developers were too lazy to assign an error code to this error. Please file a bug." +msgid "" +"GStreamer developers were too lazy to assign an error code to this error. " +"Please file a bug." msgstr "" #: gst/gsterror.c:61 @@ -161,7 +171,7 @@ msgstr "" msgid "Internal GStreamer error: tag problem. File a bug." msgstr "" -#: gst/gsterror.c:93 gst/gsterror.c:113 gst/gsterror.c:143 +#: gst/gsterror.c:93 msgid "GStreamer encountered a general supporting library error." msgstr "" @@ -173,6 +183,10 @@ msgstr "Не могу проинициализировать дополните msgid "Could not close supporting library." msgstr "Не могу закрыть дополнительную библиотеку." +#: gst/gsterror.c:113 +msgid "GStreamer encountered a general resource error." +msgstr "" + #: gst/gsterror.c:117 msgid "Resource not found." msgstr "Ресурс не найден." @@ -217,6 +231,10 @@ msgstr "" msgid "Could not get/set settings from/on resource." msgstr "" +#: gst/gsterror.c:143 +msgid "GStreamer encountered a general stream error." +msgstr "" + #: gst/gsterror.c:148 msgid "Element doesn't implement handling of this stream. Please file a bug." msgstr "" @@ -554,8 +572,8 @@ msgstr "" msgid "Error closing file \"%s\"." msgstr "" -#: gst/elements/gstfilesink.c:364 gst/elements/gstfilesink.c:399 -#: gst/elements/gstfilesink.c:452 +#: gst/elements/gstfilesink.c:364 gst/elements/gstfilesink.c:400 +#: gst/elements/gstfilesink.c:453 #, c-format msgid "Error while writing to file \"%s\"." msgstr "" @@ -671,112 +689,115 @@ msgstr "" msgid "Print all elements" msgstr "Печатать все элементы" -#: tools/gst-launch.c:107 +#: tools/gst-launch.c:114 #, c-format -msgid "Execution ended after %s iterations (sum %s ns, average %s ns, min %s ns, max %s ns).\n" +msgid "" +"Execution ended after %s iterations (sum %s ns, average %s ns, min %s ns, " +"max %s ns).\n" msgstr "" -#: tools/gst-launch.c:134 +#: tools/gst-launch.c:141 msgid "Usage: gst-xmllaunch [ element.property=value ... ]\n" -msgstr "Использование: gst-xmllaunch <файл.xml> [ элемент.поле=значение ... ]\n" +msgstr "" +"Использование: gst-xmllaunch <файл.xml> [ элемент.поле=значение ... ]\n" -#: tools/gst-launch.c:142 +#: tools/gst-launch.c:149 #, c-format msgid "ERROR: parse of xml file '%s' failed.\n" msgstr "ОШИБКА: не могу разобрать xml файл '%s'.\n" -#: tools/gst-launch.c:148 +#: tools/gst-launch.c:155 #, c-format msgid "ERROR: no toplevel pipeline element in file '%s'.\n" msgstr "" -#: tools/gst-launch.c:155 +#: tools/gst-launch.c:162 #, c-format msgid "WARNING: only one toplevel element is supported at this time." msgstr "" -#: tools/gst-launch.c:166 +#: tools/gst-launch.c:173 #, c-format msgid "ERROR: could not parse command line argument %d: %s.\n" msgstr "ОШИБКА: не могу разобрать аргумент командной строки %d: %s.\n" -#: tools/gst-launch.c:177 +#: tools/gst-launch.c:184 #, c-format msgid "WARNING: element named '%s' not found.\n" msgstr "ПРЕДУПРЕЖДЕНИЕ: элемент '%s' не найден.\n" -#: tools/gst-launch.c:316 +#: tools/gst-launch.c:327 #, c-format msgid "FOUND TAG : found by element \"%s\".\n" msgstr "" -#: tools/gst-launch.c:403 +#: tools/gst-launch.c:412 msgid "Output tags (also known as metadata)" msgstr "Выводить тэги (такжи называемые 'метаданные')" -#: tools/gst-launch.c:405 +#: tools/gst-launch.c:414 msgid "Output status information and property notifications" msgstr "" -#: tools/gst-launch.c:407 +#: tools/gst-launch.c:416 msgid "Do not output status information of TYPE" msgstr "" -#: tools/gst-launch.c:407 +#: tools/gst-launch.c:416 msgid "TYPE1,TYPE2,..." msgstr "ТИП1, ТИП2,..." -#: tools/gst-launch.c:410 +#: tools/gst-launch.c:419 msgid "Save xml representation of pipeline to FILE and exit" msgstr "" -#: tools/gst-launch.c:410 +#: tools/gst-launch.c:419 msgid "FILE" msgstr "ФАЙЛ" -#: tools/gst-launch.c:413 +#: tools/gst-launch.c:422 msgid "Do not install a fault handler" msgstr "Не устанавливать ошибочный обработчик" -#: tools/gst-launch.c:415 +#: tools/gst-launch.c:424 msgid "Print alloc trace (if enabled at compile time)" msgstr "" -#: tools/gst-launch.c:417 +#: tools/gst-launch.c:426 msgid "Number of times to iterate pipeline" msgstr "" -#: tools/gst-launch.c:487 -#, c-format -msgid "ERROR: pipeline could not be constructed: %s.\n" -msgstr "" - -#: tools/gst-launch.c:491 -#, c-format -msgid "ERROR: pipeline could not be constructed.\n" -msgstr "" - -#: tools/gst-launch.c:495 -#, c-format -msgid "WARNING: erroneous pipeline: %s\n" -msgstr "" - #: tools/gst-launch.c:496 #, c-format +msgid "ERROR: pipeline could not be constructed: %s.\n" +msgstr "" + +#: tools/gst-launch.c:500 +#, c-format +msgid "ERROR: pipeline could not be constructed.\n" +msgstr "" + +#: tools/gst-launch.c:504 +#, c-format +msgid "WARNING: erroneous pipeline: %s\n" +msgstr "" + +#: tools/gst-launch.c:505 +#, c-format msgid " Trying to run anyway.\n" msgstr " Все равно пытаюсь запустить.\n" -#: tools/gst-launch.c:523 +#: tools/gst-launch.c:532 #, c-format msgid "ERROR: the 'pipeline' element wasn't found.\n" msgstr "" -#: tools/gst-launch.c:530 +#: tools/gst-launch.c:539 #, c-format msgid "RUNNING pipeline ...\n" msgstr "" -#: tools/gst-launch.c:533 +#: tools/gst-launch.c:542 #, c-format msgid "ERROR: pipeline doesn't want to play.\n" msgstr "" diff --git a/tests/old/examples/mixer/mixer.c b/tests/old/examples/mixer/mixer.c index 6a3acc119f..3f31378d69 100644 --- a/tests/old/examples/mixer/mixer.c +++ b/tests/old/examples/mixer/mixer.c @@ -187,7 +187,7 @@ main (int argc, char *argv[]) gst_element_set_state (main_bin, GST_STATE_PLAYING); /* write out the schedule */ - gst_scheduler_show (GST_ELEMENT_SCHED (main_bin)); + gst_scheduler_show (GST_ELEMENT_SCHEDULER (main_bin)); playing = TRUE; j = 0; diff --git a/tests/old/examples/thread/thread.c b/tests/old/examples/thread/thread.c index 5478da84d7..964bec55d8 100644 --- a/tests/old/examples/thread/thread.c +++ b/tests/old/examples/thread/thread.c @@ -1,6 +1,8 @@ #include #include +static GMainLoop *loop; + /* eos will be called when the src element has an end of stream */ void eos (GstElement * element, gpointer data) @@ -12,7 +14,8 @@ eos (GstElement * element, gpointer data) /* stop the bin */ gst_element_set_state (GST_ELEMENT (thread), GST_STATE_NULL); - gst_main_quit (); + g_main_loop_quit (loop); + g_main_loop_unref (loop); } int @@ -68,7 +71,7 @@ main (int argc, char *argv[]) /* start playing */ gst_element_set_state (GST_ELEMENT (thread), GST_STATE_PLAYING); - gst_main (); + loop = g_main_loop_new (NULL, FALSE); gst_object_unref (GST_OBJECT (thread)); diff --git a/tests/threadstate/threadstate2.c b/tests/threadstate/threadstate2.c index 15827dbdb1..9123a58a2d 100644 --- a/tests/threadstate/threadstate2.c +++ b/tests/threadstate/threadstate2.c @@ -15,8 +15,6 @@ eos (GstElement * element, gpointer data) /* stop the bin */ gst_element_set_state (GST_ELEMENT (thread), GST_STATE_NULL); - - gst_main_quit (); } int diff --git a/tools/gst-compprep.c b/tools/gst-compprep.c index c9073f2ae7..0d5ea5a87f 100644 --- a/tools/gst-compprep.c +++ b/tools/gst-compprep.c @@ -91,7 +91,7 @@ main (int argc, char *argv[]) padtemplate->name_template); } - pads = gst_element_get_pad_list (element); + pads = element->pads; while (pads) { pad = (GstPad *) (pads->data); pads = g_list_next (pads); diff --git a/tools/gst-inspect.c b/tools/gst-inspect.c index 266c9a5f94..ab1403f174 100644 --- a/tools/gst-inspect.c +++ b/tools/gst-inspect.c @@ -54,7 +54,7 @@ n_print (const char *format, ...) } static gboolean -print_field (GQuark field, GValue * value, gpointer pfx) +print_field (GQuark field, const GValue * value, gpointer pfx) { gchar *str = gst_value_serialize (value); @@ -505,18 +505,10 @@ print_element_flag_info (GstElement * element) n_print ("\n"); n_print ("Element Flags:\n"); - if (GST_FLAG_IS_SET (element, GST_ELEMENT_COMPLEX)) { - n_print (" GST_ELEMENT_COMPLEX\n"); - have_flags = TRUE; - } if (GST_FLAG_IS_SET (element, GST_ELEMENT_DECOUPLED)) { n_print (" GST_ELEMENT_DECOUPLED\n"); have_flags = TRUE; } - if (GST_FLAG_IS_SET (element, GST_ELEMENT_THREAD_SUGGESTED)) { - n_print (" GST_ELEMENT_THREADSUGGESTED\n"); - have_flags = TRUE; - } if (GST_FLAG_IS_SET (element, GST_ELEMENT_EVENT_AWARE)) { n_print (" GST_ELEMENT_EVENT_AWARE\n"); have_flags = TRUE; @@ -535,10 +527,6 @@ print_element_flag_info (GstElement * element) n_print (" GST_BIN_SELF_SCHEDULABLE\n"); have_flags = TRUE; } - if (GST_FLAG_IS_SET (element, GST_BIN_FLAG_PREFER_COTHREADS)) { - n_print (" GST_BIN_FLAG_PREFER_COTHREADS\n"); - have_flags = TRUE; - } if (!have_flags) n_print (" no flags set\n"); } @@ -635,7 +623,7 @@ print_pad_info (GstElement * element) return; } - pads = gst_element_get_pad_list (element); + pads = element->pads; while (pads) { pad = GST_PAD (pads->data); pads = g_list_next (pads); @@ -854,7 +842,7 @@ print_children_info (GstElement * element) if (!GST_IS_BIN (element)) return; - children = (GList *) gst_bin_get_list (GST_BIN (element)); + children = (GList *) GST_BIN (element)->children; if (children) { n_print ("\n"); g_print ("Children:\n"); diff --git a/tools/gst-launch.c b/tools/gst-launch.c index 5075f255bb..af0cb9b2f4 100644 --- a/tools/gst-launch.c +++ b/tools/gst-launch.c @@ -69,6 +69,7 @@ static gboolean caught_intr = FALSE; static gboolean caught_error = FALSE; static gboolean need_new_state = FALSE; static GstElementState new_state; +static GMainLoop *loop; gboolean idle_func (gpointer data) @@ -101,7 +102,8 @@ idle_func (gpointer data) char *s_min; char *s_max; - gst_main_quit (); + g_main_loop_quit (loop); + g_main_loop_unref (loop); /* We write these all to strings first because * G_GUINT64_FORMAT and gettext mix very poorly */ @@ -544,11 +546,12 @@ main (int argc, char *argv[]) goto end; } - s_clock = gst_bin_get_clock (GST_BIN (pipeline)); + s_clock = gst_element_get_clock (GST_ELEMENT (pipeline)); if (!GST_FLAG_IS_SET (GST_OBJECT (pipeline), GST_BIN_SELF_SCHEDULABLE)) { g_idle_add (idle_func, pipeline); - gst_main (); + loop = g_main_loop_new (NULL, FALSE); + g_main_loop_run (loop); } else { g_print ("Waiting for the state change... "); gst_element_wait_state_change (pipeline); diff --git a/tools/gst-md5sum.c b/tools/gst-md5sum.c index 3101c85679..c5052f6a6b 100644 --- a/tools/gst-md5sum.c +++ b/tools/gst-md5sum.c @@ -12,6 +12,7 @@ static guint64 sum = 0; static guint64 min = G_MAXINT64; static guint64 max = 0; static GstClock *s_clock; +static GMainLoop *loop; gboolean idle_func (gpointer data) @@ -36,7 +37,8 @@ idle_func (gpointer data) max = MAX (max, diff); if (!busy) { - gst_main_quit (); + g_main_loop_quit (loop); + g_main_loop_unref (loop); /* g_print ("execution ended after %llu iterations (sum %llu ns, average %llu ns, min %llu ns, max %llu ns)\n", iterations, sum, sum/iterations, min, max); @@ -127,7 +129,8 @@ main (int argc, char *argv[]) if (!GST_FLAG_IS_SET (GST_OBJECT (pipeline), GST_BIN_SELF_SCHEDULABLE)) { g_idle_add (idle_func, pipeline); - gst_main (); + loop = g_main_loop_new (NULL, FALSE); + g_main_loop_run (loop); } else { gst_element_wait_state_change (pipeline); } diff --git a/tools/gst-xmlinspect.c b/tools/gst-xmlinspect.c index 16aacafa00..847257fb0e 100644 --- a/tools/gst-xmlinspect.c +++ b/tools/gst-xmlinspect.c @@ -533,15 +533,9 @@ print_element_info (GstElementFactory * factory) PUT_END_TAG (1, "pad-templates"); PUT_START_TAG (1, "element-flags"); - if (GST_FLAG_IS_SET (element, GST_ELEMENT_COMPLEX)) { - PUT_ESCAPED (2, "flag", "GST_ELEMENT_COMPLEX"); - } if (GST_FLAG_IS_SET (element, GST_ELEMENT_DECOUPLED)) { PUT_ESCAPED (2, "flag", "GST_ELEMENT_DECOUPLED"); } - if (GST_FLAG_IS_SET (element, GST_ELEMENT_THREAD_SUGGESTED)) { - PUT_ESCAPED (2, "flag", "GST_ELEMENT_THREADSUGGESTED"); - } if (GST_FLAG_IS_SET (element, GST_ELEMENT_EVENT_AWARE)) { PUT_ESCAPED (2, "flag", "GST_ELEMENT_EVENT_AWARE"); } @@ -556,9 +550,6 @@ print_element_info (GstElementFactory * factory) if (GST_FLAG_IS_SET (element, GST_BIN_SELF_SCHEDULABLE)) { PUT_ESCAPED (2, "flag", "GST_BIN_SELF_SCHEDULABLE"); } - if (GST_FLAG_IS_SET (element, GST_BIN_FLAG_PREFER_COTHREADS)) { - PUT_ESCAPED (2, "flag", "GST_BIN_FLAG_PREFER_COTHREADS"); - } PUT_END_TAG (1, "bin-flags"); } @@ -602,7 +593,7 @@ print_element_info (GstElementFactory * factory) if (element->numpads) { const GList *pads; - pads = gst_element_get_pad_list (element); + pads = element->pads; while (pads) { pad = GST_PAD (pads->data); pads = g_list_next (pads); @@ -683,7 +674,7 @@ print_element_info (GstElementFactory * factory) /* for compound elements */ if (GST_IS_BIN (element)) { PUT_START_TAG (1, "children"); - children = (GList *) gst_bin_get_list (GST_BIN (element)); + children = (GList *) GST_BIN (element)->children; while (children) { child = GST_ELEMENT (children->data); children = g_list_next (children);