ges: Implement the ges_timeline_layer_add_asset method

+ Remove GstDiscoverer related code in GESTimeline as we do not need it anymore
  + Refactor the ges_timeline_layer_add_object method to make sure it is still working as intended

API:
  ges_timeline_layer_add_asset
This commit is contained in:
Thibault Saunier 2012-12-17 17:05:56 -03:00
parent c7cefd0f37
commit a7280fb59d
3 changed files with 140 additions and 306 deletions

View file

@ -59,6 +59,12 @@ struct _GESTimelineLayerPrivate
gboolean auto_transition; gboolean auto_transition;
}; };
typedef struct
{
GESTimelineObject *object;
GESTimelineLayer *layer;
} NewAssetUData;
enum enum
{ {
PROP_0, PROP_0,
@ -695,6 +701,26 @@ start_calculating_transitions (GESTimelineLayer * layer)
/* FIXME calculate all the transitions at that time */ /* FIXME calculate all the transitions at that time */
} }
static void
new_asset_cb (GESAsset * source, GAsyncResult * res, NewAssetUData * udata)
{
GError *error = NULL;
GESAsset *asset = ges_asset_request_finish (res, &error);
GST_DEBUG_OBJECT (udata->layer, "%" GST_PTR_FORMAT " Asset loaded, "
"setting its asset", udata->object);
if (error) {
GST_ERROR ("Asset could not be created for uri");
} else {
ges_timeline_layer_add_object (udata->layer, udata->object);
}
g_object_unref (asset);
g_slice_free (NewAssetUData, udata);
}
/* Public methods */ /* Public methods */
/** /**
* ges_timeline_layer_remove_object: * ges_timeline_layer_remove_object:
@ -713,7 +739,7 @@ gboolean
ges_timeline_layer_remove_object (GESTimelineLayer * layer, ges_timeline_layer_remove_object (GESTimelineLayer * layer,
GESTimelineObject * object) GESTimelineObject * object)
{ {
GESTimelineLayer *tl_obj_layer; GESTimelineLayer *current_layer;
GList *trackobjects, *tmp; GList *trackobjects, *tmp;
g_return_val_if_fail (GES_IS_TIMELINE_LAYER (layer), FALSE); g_return_val_if_fail (GES_IS_TIMELINE_LAYER (layer), FALSE);
@ -721,16 +747,16 @@ ges_timeline_layer_remove_object (GESTimelineLayer * layer,
GST_DEBUG ("layer:%p, object:%p", layer, object); GST_DEBUG ("layer:%p, object:%p", layer, object);
tl_obj_layer = ges_timeline_object_get_layer (object); current_layer = ges_timeline_object_get_layer (object);
if (G_UNLIKELY (tl_obj_layer != layer)) { if (G_UNLIKELY (current_layer != layer)) {
GST_WARNING ("TimelineObject doesn't belong to this layer"); GST_WARNING ("TimelineObject doesn't belong to this layer");
if (tl_obj_layer != NULL) if (current_layer != NULL)
g_object_unref (tl_obj_layer); g_object_unref (current_layer);
return FALSE; return FALSE;
} }
g_object_unref (tl_obj_layer); g_object_unref (current_layer);
if (layer->priv->auto_transition && GES_IS_TIMELINE_SOURCE (object)) { if (layer->priv->auto_transition && GES_IS_TIMELINE_SOURCE (object)) {
trackobjects = ges_timeline_object_get_track_objects (object); trackobjects = ges_timeline_object_get_track_objects (object);
@ -912,24 +938,60 @@ gboolean
ges_timeline_layer_add_object (GESTimelineLayer * layer, ges_timeline_layer_add_object (GESTimelineLayer * layer,
GESTimelineObject * object) GESTimelineObject * object)
{ {
GESTimelineLayer *tl_obj_layer; GESAsset *asset;
GESTimelineLayer *current_layer;
guint32 maxprio, minprio, prio; guint32 maxprio, minprio, prio;
GST_DEBUG ("layer:%p, object:%p", layer, object); GESTimelineLayerPrivate *priv;
tl_obj_layer = ges_timeline_object_get_layer (object); g_return_val_if_fail (GES_IS_TIMELINE_LAYER (layer), FALSE);
g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
if (G_UNLIKELY (tl_obj_layer)) { GST_DEBUG_OBJECT (layer, "adding object:%p", object);
priv = layer->priv;
current_layer = ges_timeline_object_get_layer (object);
if (G_UNLIKELY (current_layer)) {
GST_WARNING ("TimelineObject %p already belongs to another layer", object); GST_WARNING ("TimelineObject %p already belongs to another layer", object);
g_object_unref (tl_obj_layer); g_object_unref (current_layer);
return FALSE; return FALSE;
} }
asset = ges_extractable_get_asset (GES_EXTRACTABLE (object));
if (asset == NULL) {
gchar *id;
NewAssetUData *mudata = g_slice_new (NewAssetUData);
mudata->object = object;
mudata->layer = layer;
GST_DEBUG_OBJECT (layer, "%" GST_PTR_FORMAT " as no reference to any "
"assets creating a asset... trying sync", object);
id = ges_extractable_get_id (GES_EXTRACTABLE (object));
asset = ges_asset_request (G_OBJECT_TYPE (object), id, NULL);
if (asset == NULL) {
ges_asset_request_async (G_OBJECT_TYPE (object),
id, NULL, (GAsyncReadyCallback) new_asset_cb, mudata);
g_free (id);
GST_LOG_OBJECT (layer, "Object added async");
return TRUE;
}
g_free (id);
ges_extractable_set_asset (GES_EXTRACTABLE (object), asset);
g_slice_free (NewAssetUData, mudata);
}
g_object_ref_sink (object); g_object_ref_sink (object);
/* Take a reference to the object and store it stored by start/priority */ /* Take a reference to the object and store it stored by start/priority */
layer->priv->objects_start = priv->objects_start = g_list_insert_sorted (priv->objects_start, object,
g_list_insert_sorted (layer->priv->objects_start, object,
(GCompareFunc) objects_start_compare); (GCompareFunc) objects_start_compare);
/* Inform the object it's now in this layer */ /* Inform the object it's now in this layer */
@ -943,15 +1005,17 @@ ges_timeline_layer_add_object (GESTimelineLayer * layer,
maxprio = layer->max_gnl_priority; maxprio = layer->max_gnl_priority;
minprio = layer->min_gnl_priority; minprio = layer->min_gnl_priority;
prio = GES_TIMELINE_OBJECT_PRIORITY (object); prio = GES_TIMELINE_OBJECT_PRIORITY (object);
if (minprio + prio > (maxprio)) { if (minprio + prio > (maxprio)) {
GST_WARNING ("%p is out of the layer %p space, setting its priority to " GST_WARNING_OBJECT (layer,
"setting its priority %d to failthe maximum priority of the layer %d", "%p is out of the layer space, setting its priority to "
object, layer, prio, maxprio - minprio); "%d, setting it to the maximum priority of the layer: %d", object, prio,
maxprio - minprio);
ges_timeline_object_set_priority (object, LAYER_HEIGHT - 1); ges_timeline_object_set_priority (object, LAYER_HEIGHT - 1);
} }
/* If the object has an acceptable priority, we just let it with its current /* If the object has an acceptable priority, we just let it with its current
* priority */ * priority */
ges_timeline_layer_resync_priorities (layer); ges_timeline_layer_resync_priorities (layer);
/* emit 'object-added' */ /* emit 'object-added' */
@ -960,6 +1024,54 @@ ges_timeline_layer_add_object (GESTimelineLayer * layer,
return TRUE; return TRUE;
} }
/**
* ges_timeline_layer_add_asset:
* @layer: a #GESTimelineLayer
* @asset: The asset to add to
* @start: The start value to set on the new #GESTimelineObject
* @inpoint: The inpoint value to set on the new #GESTimelineObject
* @duration: The duration value to set on the new #GESTimelineObject
* @rate: The rate value to set on the new #GESTimelineObject
* @track_types: The #GESTrackType to set on the the new #GESTimelineObject
*
* Creates TimelineObject from asset, adds it to layer and
* returns a reference to it.
*
* Returns: (transfer floating): Created #GESTimelineObject
*/
GESTimelineObject *
ges_timeline_layer_add_asset (GESTimelineLayer * layer,
GESAsset * asset, GstClockTime start, GstClockTime inpoint,
GstClockTime duration, gdouble rate, GESTrackType track_types)
{
GESTimelineObject *tlobj;
g_return_val_if_fail (GES_IS_TIMELINE_LAYER (layer), NULL);
g_return_val_if_fail (GES_IS_ASSET (asset), NULL);
g_return_val_if_fail (g_type_is_a (ges_asset_get_extractable_type
(asset), GES_TYPE_TIMELINE_OBJECT), NULL);
GST_DEBUG_OBJECT (layer, "Adding asset: %s", ges_asset_get_id (asset));
tlobj = GES_TIMELINE_OBJECT (ges_asset_extract (asset, NULL));
ges_timeline_object_set_start (tlobj, start);
ges_timeline_object_set_inpoint (tlobj, inpoint);
if (track_types != GES_TRACK_TYPE_UNKNOWN)
ges_timeline_object_set_supported_formats (tlobj, track_types);
if (GST_CLOCK_TIME_IS_VALID (duration)) {
ges_timeline_object_set_duration (tlobj, duration);
}
if (!ges_timeline_layer_add_object (layer, tlobj)) {
gst_object_unref (tlobj);
return NULL;
}
return tlobj;
}
/** /**
* ges_timeline_layer_new: * ges_timeline_layer_new:
* *

View file

@ -102,6 +102,14 @@ ges_timeline_layer_get_timeline (GESTimelineLayer * layer);
gboolean ges_timeline_layer_add_object (GESTimelineLayer * layer, gboolean ges_timeline_layer_add_object (GESTimelineLayer * layer,
GESTimelineObject * object); GESTimelineObject * object);
GESTimelineObject * ges_timeline_layer_add_asset (GESTimelineLayer *layer,
GESAsset *asset,
GstClockTime start,
GstClockTime inpoint,
GstClockTime duration,
gdouble rate,
GESTrackType track_types);
gboolean ges_timeline_layer_remove_object (GESTimelineLayer * layer, gboolean ges_timeline_layer_remove_object (GESTimelineLayer * layer,
GESTimelineObject * object); GESTimelineObject * object);

View file

@ -117,15 +117,6 @@ struct _GESTimelinePrivate
/* The duration of the timeline */ /* The duration of the timeline */
gint64 duration; gint64 duration;
/* discoverer used for virgin sources */
GstDiscoverer *discoverer;
GList *pendingobjects;
/* lock to avoid discovery of objects that will be removed */
GMutex pendingobjects_lock;
/* Whether we are changing state asynchronously or not */
gboolean async_pending;
/* Timeline edition modes and snapping management */ /* Timeline edition modes and snapping management */
guint64 snapping_distance; guint64 snapping_distance;
@ -183,13 +174,6 @@ static GstBinClass *parent_class;
static guint ges_timeline_signals[LAST_SIGNAL] = { 0 }; static guint ges_timeline_signals[LAST_SIGNAL] = { 0 };
static gint custom_find_track (TrackPrivate * tr_priv, GESTrack * track); static gint custom_find_track (TrackPrivate * tr_priv, GESTrack * track);
static GstStateChangeReturn
ges_timeline_change_state (GstElement * element, GstStateChange transition);
static void
discoverer_finished_cb (GstDiscoverer * discoverer, GESTimeline * timeline);
static void
discoverer_discovered_cb (GstDiscoverer * discoverer,
GstDiscovererInfo * info, GError * err, GESTimeline * timeline);
/* Internal methods */ /* Internal methods */
static gboolean static gboolean
@ -257,12 +241,6 @@ ges_timeline_dispose (GObject * object)
{ {
GESTimelinePrivate *priv = GES_TIMELINE (object)->priv; GESTimelinePrivate *priv = GES_TIMELINE (object)->priv;
if (priv->discoverer) {
gst_discoverer_stop (priv->discoverer);
g_object_unref (priv->discoverer);
priv->discoverer = NULL;
}
while (priv->layers) { while (priv->layers) {
GESTimelineLayer *layer = (GESTimelineLayer *) priv->layers->data; GESTimelineLayer *layer = (GESTimelineLayer *) priv->layers->data;
ges_timeline_remove_layer (GES_TIMELINE (object), layer); ges_timeline_remove_layer (GES_TIMELINE (object), layer);
@ -293,10 +271,6 @@ ges_timeline_dispose (GObject * object)
static void static void
ges_timeline_finalize (GObject * object) ges_timeline_finalize (GObject * object)
{ {
GESTimeline *timeline = GES_TIMELINE (object);
g_mutex_clear (&timeline->priv->pendingobjects_lock);
G_OBJECT_CLASS (ges_timeline_parent_class)->finalize (object); G_OBJECT_CLASS (ges_timeline_parent_class)->finalize (object);
} }
@ -304,14 +278,11 @@ static void
ges_timeline_class_init (GESTimelineClass * klass) ges_timeline_class_init (GESTimelineClass * klass)
{ {
GObjectClass *object_class = G_OBJECT_CLASS (klass); GObjectClass *object_class = G_OBJECT_CLASS (klass);
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
g_type_class_add_private (klass, sizeof (GESTimelinePrivate)); g_type_class_add_private (klass, sizeof (GESTimelinePrivate));
parent_class = g_type_class_peek_parent (klass); parent_class = g_type_class_peek_parent (klass);
element_class->change_state = ges_timeline_change_state;
object_class->get_property = ges_timeline_get_property; object_class->get_property = ges_timeline_get_property;
object_class->set_property = ges_timeline_set_property; object_class->set_property = ges_timeline_set_property;
object_class->dispose = ges_timeline_dispose; object_class->dispose = ges_timeline_dispose;
@ -489,15 +460,6 @@ ges_timeline_init (GESTimeline * self)
(GDestroyNotify) _destroy_obj_iters); (GDestroyNotify) _destroy_obj_iters);
priv->starts_ends = g_sequence_new (g_free); priv->starts_ends = g_sequence_new (g_free);
priv->tracksources = g_sequence_new (g_object_unref); priv->tracksources = g_sequence_new (g_object_unref);
g_mutex_init (&priv->pendingobjects_lock);
/* New discoverer with a 15s timeout */
priv->discoverer = gst_discoverer_new (15 * GST_SECOND, NULL);
g_signal_connect (priv->discoverer, "finished",
G_CALLBACK (discoverer_finished_cb), self);
g_signal_connect (priv->discoverer, "discovered",
G_CALLBACK (discoverer_discovered_cb), self);
gst_discoverer_start (priv->discoverer);
} }
/* Private methods */ /* Private methods */
@ -1441,169 +1403,6 @@ add_object_to_tracks (GESTimeline * timeline, GESTimelineObject * object)
} }
} }
static void
do_async_start (GESTimeline * timeline)
{
GstMessage *message;
GList *tmp;
timeline->priv->async_pending = TRUE;
/* Freeze state of tracks */
for (tmp = timeline->priv->tracks; tmp; tmp = tmp->next) {
TrackPrivate *tr_priv = (TrackPrivate *) tmp->data;
gst_element_set_locked_state ((GstElement *) tr_priv->track, TRUE);
}
message = gst_message_new_async_start (GST_OBJECT_CAST (timeline));
parent_class->handle_message (GST_BIN_CAST (timeline), message);
}
static void
do_async_done (GESTimeline * timeline)
{
GstMessage *message;
if (timeline->priv->async_pending) {
GList *tmp;
/* Unfreeze state of tracks */
for (tmp = timeline->priv->tracks; tmp; tmp = tmp->next) {
TrackPrivate *tr_priv = (TrackPrivate *) tmp->data;
gst_element_set_locked_state ((GstElement *) tr_priv->track, FALSE);
gst_element_sync_state_with_parent ((GstElement *) tr_priv->track);
}
GST_DEBUG_OBJECT (timeline, "Emitting async-done");
message = gst_message_new_async_done (GST_OBJECT_CAST (timeline), FALSE);
parent_class->handle_message (GST_BIN_CAST (timeline), message);
timeline->priv->async_pending = FALSE;
}
}
/* Callbacks */
static void
discoverer_finished_cb (GstDiscoverer * discoverer, GESTimeline * timeline)
{
do_async_done (timeline);
}
static void
discoverer_discovered_cb (GstDiscoverer * discoverer,
GstDiscovererInfo * info, GError * err, GESTimeline * timeline)
{
GList *tmp;
GList *stream_list;
GESTimelineObject *tlobj;
GESTrackType tfs_supportedformats;
gboolean found = FALSE;
gboolean is_image = FALSE;
GESTimelineFileSource *tfs = NULL;
GESTimelinePrivate *priv = timeline->priv;
const gchar *uri = gst_discoverer_info_get_uri (info);
GES_TIMELINE_PENDINGOBJS_LOCK (timeline);
/* Find corresponding TimelineFileSource in the sources */
for (tmp = priv->pendingobjects; tmp; tmp = tmp->next) {
tfs = (GESTimelineFileSource *) tmp->data;
if (!g_strcmp0 (ges_timeline_filesource_get_uri (tfs), uri)) {
found = TRUE;
break;
}
}
if (!found) {
GST_WARNING ("Discovered %s, that seems not to be in the list of sources"
"to discover", uri);
GES_TIMELINE_PENDINGOBJS_UNLOCK (timeline);
return;
}
if (err) {
GError *propagate_error = NULL;
priv->pendingobjects = g_list_delete_link (priv->pendingobjects, tmp);
GES_TIMELINE_PENDINGOBJS_UNLOCK (timeline);
GST_WARNING ("Error while discovering %s: %s", uri, err->message);
g_propagate_error (&propagate_error, err);
g_signal_emit (timeline, ges_timeline_signals[DISCOVERY_ERROR], 0, tfs,
propagate_error);
return;
}
/* Everything went fine... let's do our job! */
GST_DEBUG ("Discovered uri %s", uri);
/* The timeline file source will be updated with discovered information
* so it needs to not be finalized during this process */
g_object_ref (tfs);
/* Remove object from list */
priv->pendingobjects = g_list_delete_link (priv->pendingobjects, tmp);
GES_TIMELINE_PENDINGOBJS_UNLOCK (timeline);
/* FIXME : Handle errors in discovery */
stream_list = gst_discoverer_info_get_stream_list (info);
tfs_supportedformats = ges_timeline_filesource_get_supported_formats (tfs);
if (tfs_supportedformats != GES_TRACK_TYPE_UNKNOWN)
goto check_image;
/* Update timelinefilesource properties based on info */
for (tmp = stream_list; tmp; tmp = tmp->next) {
GstDiscovererStreamInfo *sinf = (GstDiscovererStreamInfo *) tmp->data;
if (GST_IS_DISCOVERER_AUDIO_INFO (sinf)) {
tfs_supportedformats |= GES_TRACK_TYPE_AUDIO;
ges_timeline_filesource_set_supported_formats (tfs, tfs_supportedformats);
} else if (GST_IS_DISCOVERER_VIDEO_INFO (sinf)) {
tfs_supportedformats |= GES_TRACK_TYPE_VIDEO;
ges_timeline_filesource_set_supported_formats (tfs, tfs_supportedformats);
if (gst_discoverer_video_info_is_image ((GstDiscovererVideoInfo *)
sinf)) {
tfs_supportedformats |= GES_TRACK_TYPE_AUDIO;
ges_timeline_filesource_set_supported_formats (tfs,
tfs_supportedformats);
is_image = TRUE;
}
}
}
if (stream_list)
gst_discoverer_stream_info_list_free (stream_list);
check_image:
tlobj = GES_TIMELINE_OBJECT (tfs);
if (is_image) {
/* don't set max-duration on still images */
g_object_set (tfs, "is_image", (gboolean) TRUE, NULL);
} else {
GstClockTime file_duration, tlobj_max_duration;
/* Properly set duration informations from the discovery */
file_duration = gst_discoverer_info_get_duration (info);
tlobj_max_duration = ges_timeline_object_get_max_duration (tlobj);
if (tlobj_max_duration == G_MAXUINT64)
ges_timeline_object_set_max_duration (tlobj, file_duration);
if (GST_CLOCK_TIME_IS_VALID (tlobj->duration) == FALSE)
ges_timeline_object_set_duration (tlobj, file_duration);
}
/* Continue the processing on tfs */
add_object_to_tracks (timeline, tlobj);
/* Remove the ref as the timeline file source is no longer needed here */
g_object_unref (tfs);
}
static void static void
layer_object_added_cb (GESTimelineLayer * layer, GESTimelineObject * object, layer_object_added_cb (GESTimelineLayer * layer, GESTimelineObject * object,
GESTimeline * timeline) GESTimeline * timeline)
@ -1616,37 +1415,8 @@ layer_object_added_cb (GESTimelineLayer * layer, GESTimelineObject * object,
} }
GST_DEBUG ("New TimelineObject %p added to layer %p", object, layer); GST_DEBUG ("New TimelineObject %p added to layer %p", object, layer);
if (GES_IS_TIMELINE_FILE_SOURCE (object)) {
GESTimelineFileSource *tfs = GES_TIMELINE_FILE_SOURCE (object);
GESTrackType tfs_supportedformats =
ges_timeline_filesource_get_supported_formats (tfs);
guint64 tfs_maxdur = ges_timeline_filesource_get_max_duration (tfs);
const gchar *tfs_uri;
/* Send the filesource to the discoverer if:
* * it doesn't have specified supported formats
* * OR it doesn't have a specified max-duration
* * OR it doesn't have a valid duration */
if (tfs_supportedformats == GES_TRACK_TYPE_UNKNOWN ||
tfs_maxdur == GST_CLOCK_TIME_NONE || object->duration == 0) {
GST_LOG ("Incomplete TimelineFileSource, discovering it");
tfs_uri = ges_timeline_filesource_get_uri (tfs);
GES_TIMELINE_PENDINGOBJS_LOCK (timeline);
timeline->priv->pendingobjects =
g_list_append (timeline->priv->pendingobjects, object);
GES_TIMELINE_PENDINGOBJS_UNLOCK (timeline);
gst_discoverer_discover_uri_async (timeline->priv->discoverer, tfs_uri);
} else
add_object_to_tracks (timeline, object); add_object_to_tracks (timeline, object);
} else { GST_DEBUG ("Done");
add_object_to_tracks (timeline, object);
}
GST_DEBUG ("done");
} }
static void static void
@ -1693,16 +1463,6 @@ layer_object_removed_cb (GESTimelineLayer * layer, GESTimelineObject * object,
g_list_free (trackobjects); g_list_free (trackobjects);
/* if the object is a timeline file source that has not yet been discovered,
* it no longer needs to be discovered so remove it from the pendingobjects
* list if it belongs to this layer */
if (GES_IS_TIMELINE_FILE_SOURCE (object)) {
GES_TIMELINE_PENDINGOBJS_LOCK (timeline);
timeline->priv->pendingobjects =
g_list_remove_all (timeline->priv->pendingobjects, object);
GES_TIMELINE_PENDINGOBJS_UNLOCK (timeline);
}
GST_DEBUG ("Done"); GST_DEBUG ("Done");
} }
@ -1835,51 +1595,6 @@ pad_removed_cb (GESTrack * track, GstPad * pad, TrackPrivate * tr_priv)
tr_priv->pad = NULL; tr_priv->pad = NULL;
} }
/* GstElement Virtual methods */
static GstStateChangeReturn
ges_timeline_change_state (GstElement * element, GstStateChange transition)
{
GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
GESTimeline *timeline = GES_TIMELINE (element);
switch (transition) {
case GST_STATE_CHANGE_READY_TO_PAUSED:
GES_TIMELINE_PENDINGOBJS_LOCK (timeline);
if (timeline->priv->pendingobjects) {
GES_TIMELINE_PENDINGOBJS_UNLOCK (timeline);
do_async_start (timeline);
ret = GST_STATE_CHANGE_ASYNC;
} else {
GES_TIMELINE_PENDINGOBJS_UNLOCK (timeline);
}
break;
default:
break;
}
{
GstStateChangeReturn bret;
bret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
if (G_UNLIKELY (bret == GST_STATE_CHANGE_NO_PREROLL)) {
do_async_done (timeline);
ret = bret;
}
}
switch (transition) {
case GST_STATE_CHANGE_PAUSED_TO_READY:
do_async_done (timeline);
break;
default:
break;
}
return ret;
}
/**** API *****/ /**** API *****/
/** /**
* ges_timeline_new: * ges_timeline_new:
@ -2247,7 +1962,6 @@ ges_timeline_remove_track (GESTimeline * timeline, GESTrack * track)
} }
/* set track state to NULL */ /* set track state to NULL */
gst_element_set_state (GST_ELEMENT (track), GST_STATE_NULL); gst_element_set_state (GST_ELEMENT (track), GST_STATE_NULL);
gst_object_unref (track); gst_object_unref (track);