Пример #1
0
static void
track_element_added_cb (GESClip * clip,
    GESTrackElement * track_element, GHashTable * props_table)
{
  GESPitiviFormatter *formatter;

  formatter = GES_PITIVI_FORMATTER (g_hash_table_lookup (props_table,
          "current-formatter"));
  if (formatter) {
    GESPitiviFormatterPrivate *priv = formatter->priv;

    /* Make sure the hack to get a ref to the formatter
     * doesn't break everything */
    g_hash_table_steal (props_table, "current-formatter");

    priv->sources_to_load = g_list_remove (priv->sources_to_load, clip);
    if (!priv->sources_to_load && GES_FORMATTER (formatter)->project)
      ges_project_set_loaded (GES_FORMATTER (formatter)->project,
          GES_FORMATTER (formatter));
  }

  /* Disconnect the signal */
  g_signal_handlers_disconnect_by_func (clip, track_element_added_cb,
      props_table);
}
Пример #2
0
static gboolean
save_to_uri (GESFormatter * formatter, GESTimeline * timeline,
    const gchar * uri)
{
  gchar *location;
  GError *e = NULL;
  gboolean ret = TRUE;
  GESFormatterPrivate *priv = GES_FORMATTER (formatter)->priv;

  if (!(location = g_filename_from_uri (uri, NULL, NULL))) {
    return FALSE;
  }

  if (!ges_formatter_save (formatter, timeline)) {
    GST_ERROR ("couldn't serialize formatter");
  } else {
    if (!g_file_set_contents (location, priv->data, priv->length, &e)) {
      GST_ERROR ("couldn't write file '%s': %s", location, e->message);
      ret = FALSE;
    }
  }

  if (e)
    g_error_free (e);
  g_free (location);

  return ret;
}
Пример #3
0
GESFormatter *
ges_formatter_new_for_uri (const gchar * uri)
{
  if (ges_formatter_can_load_uri (uri))
    return GES_FORMATTER (ges_keyfile_formatter_new ());
  return NULL;
}
Пример #4
0
static gboolean
load_from_uri (GESFormatter * formatter, GESTimeline * timeline,
    const gchar * uri)
{
  gchar *location;
  GError *e = NULL;
  gboolean ret = TRUE;
  GESFormatterPrivate *priv = GES_FORMATTER (formatter)->priv;


  if (priv->data) {
    GST_ERROR ("formatter already has data! please set data to NULL");
  }

  if (!(location = gst_uri_get_location (uri))) {
    return FALSE;
  }

  if (g_file_get_contents (location, &priv->data, &priv->length, &e)) {
    if (!ges_formatter_load (formatter, timeline)) {
      GST_ERROR ("couldn't deserialize formatter");
      ret = FALSE;
    }
  } else {
    GST_ERROR ("couldn't read file '%s': %s", location, e->message);
    ret = FALSE;
  }

  if (e)
    g_error_free (e);
  g_free (location);

  return ret;
}
Пример #5
0
void
ges_formatter_clear_data (GESFormatter * formatter)
{
  GESFormatterPrivate *priv = GES_FORMATTER (formatter)->priv;

  priv->data = NULL;
  priv->length = 0;
}
Пример #6
0
/**
 * ges_formatter_get_data:
 * @formatter: a #GESFormatter
 * @length: location into which to store the size of the data in bytes.
 *
 * Lets you get the data @formatter used for loading.
 *
 * Returns: (transfer none): a pointer to the data.
 */
void *
ges_formatter_get_data (GESFormatter * formatter, gsize * length)
{
  GESFormatterPrivate *priv = GES_FORMATTER (formatter)->priv;

  *length = priv->length;

  return priv->data;
}
Пример #7
0
void
ges_formatter_set_data (GESFormatter * formatter, void *data, gsize length)
{
  GESFormatterPrivate *priv = GES_FORMATTER (formatter)->priv;

  if (priv->data)
    g_free (priv->data);
  priv->data = data;
  priv->length = length;
}
Пример #8
0
static void
ges_formatter_dispose (GObject * object)
{
  GESFormatterPrivate *priv = GES_FORMATTER (object)->priv;

  if (priv->data) {
    g_free (priv->data);
  }
  g_hash_table_destroy (priv->uri_newuri_table);
  g_hash_table_destroy (priv->parent_newparent_table);
}
Пример #9
0
static gboolean
_load_project (GESProject * project, GESTimeline * timeline, GError ** error)
{
  GError *lerr = NULL;
  GESProjectPrivate *priv;
  GESFormatter *formatter;

  priv = GES_PROJECT (project)->priv;

  if (priv->uri == NULL) {
    EmitLoadedInIdle *data = g_slice_new (EmitLoadedInIdle);

    GST_LOG_OBJECT (project, "%s, Loading an empty timeline %s"
        " as no URI set yet", GST_OBJECT_NAME (timeline),
        ges_asset_get_id (GES_ASSET (project)));

    data->timeline = gst_object_ref (timeline);
    data->project = gst_object_ref (project);

    /* Make sure the signal is emitted after the functions ends */
    g_idle_add ((GSourceFunc) _emit_loaded_in_idle, data);
    return TRUE;
  }

  if (priv->formatter_asset == NULL)
    priv->formatter_asset = _find_formatter_asset_for_uri (priv->uri);

  if (priv->formatter_asset == NULL)
    goto failed;

  formatter = GES_FORMATTER (ges_asset_extract (priv->formatter_asset, &lerr));
  if (lerr) {
    GST_WARNING_OBJECT (project, "Could not create the formatter: %s",
        (*error)->message);

    goto failed;
  }

  ges_project_add_formatter (GES_PROJECT (project), formatter);
  ges_formatter_load_from_uri (formatter, timeline, priv->uri, &lerr);
  if (lerr) {
    GST_WARNING_OBJECT (project, "Could not load the timeline,"
        " returning: %s", lerr->message);
    goto failed;
  }

  return TRUE;

failed:
  if (lerr)
    g_propagate_error (error, lerr);
  return FALSE;
}
void
ges_base_xml_formatter_add_layer (GESBaseXmlFormatter * self,
    GType extractable_type, guint priority, GstStructure * properties,
    const gchar * metadatas, GError ** error)
{
  LayerEntry *entry;
  GESAsset *asset;
  GESLayer *layer;
  gboolean auto_transition = FALSE;
  GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self);

  if (priv->check_only)
    return;

  if (extractable_type == G_TYPE_NONE)
    layer = ges_layer_new ();
  else {
    asset = ges_asset_request (extractable_type, NULL, error);
    if (asset == NULL) {
      if (error && *error == NULL) {
        g_set_error (error, G_MARKUP_ERROR,
            G_MARKUP_ERROR_INVALID_CONTENT,
            "Layer type %s could not be created'",
            g_type_name (extractable_type));
        return;
      }
    }
    layer = GES_LAYER (ges_asset_extract (asset, error));
  }

  ges_layer_set_priority (layer, priority);
  ges_timeline_add_layer (GES_FORMATTER (self)->timeline, layer);
  if (properties) {
    if (gst_structure_get_boolean (properties, "auto-transition",
            &auto_transition))
      gst_structure_remove_field (properties, "auto-transition");

    gst_structure_foreach (properties,
        (GstStructureForeachFunc) set_property_foreach, layer);
  }

  if (metadatas)
    ges_meta_container_add_metas_from_string (GES_META_CONTAINER (layer),
        metadatas);

  entry = g_slice_new0 (LayerEntry);
  entry->layer = gst_object_ref (layer);
  entry->auto_trans = auto_transition;

  g_hash_table_insert (priv->layers, GINT_TO_POINTER (priority), entry);
}
void
ges_base_xml_formatter_add_track (GESBaseXmlFormatter * self,
    GESTrackType track_type, GstCaps * caps, const gchar * id,
    GstStructure * properties, const gchar * metadatas, GError ** error)
{
  GESTrack *track;
  GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self);

  if (priv->check_only) {
    if (caps)
      gst_caps_unref (caps);

    return;
  }

  track = ges_track_new (track_type, caps);
  ges_timeline_add_track (GES_FORMATTER (self)->timeline, track);

  if (properties) {
    gchar *restriction;
    GstCaps *caps;

    gst_structure_get (properties, "restriction-caps", G_TYPE_STRING,
        &restriction, NULL);
    gst_structure_remove_fields (properties, "restriction-caps", "caps",
        "message-forward", NULL);
    if (g_strcmp0 (restriction, "NULL")) {
      caps = gst_caps_from_string (restriction);
      ges_track_set_restriction_caps (track, caps);
    }
    gst_structure_foreach (properties,
        (GstStructureForeachFunc) set_property_foreach, track);
  }

  g_hash_table_insert (priv->tracks, g_strdup (id), gst_object_ref (track));
  if (metadatas)
    ges_meta_container_add_metas_from_string (GES_META_CONTAINER (track),
        metadatas);
}
void
ges_base_xml_formatter_add_asset (GESBaseXmlFormatter * self,
    const gchar * id, GType extractable_type, GstStructure * properties,
    const gchar * metadatas, GError ** error)
{
  PendingAsset *passet;
  GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self);

  if (priv->check_only)
    return;

  passet = g_slice_new0 (PendingAsset);
  passet->metadatas = g_strdup (metadatas);
  passet->formatter = gst_object_ref (self);
  if (properties)
    passet->properties = gst_structure_copy (properties);

  ges_asset_request_async (extractable_type, id, NULL,
      (GAsyncReadyCallback) new_asset_cb, passet);
  ges_project_add_loading_asset (GES_FORMATTER (self)->project,
      extractable_type, id);
  priv->pending_assets = g_list_prepend (priv->pending_assets, passet);
}
Пример #13
0
void
load_project (gchar * uri)
{
  GESFormatter *formatter;
  GESTimeline *timeline;
  GMainLoop *mainloop;
  GESTimelinePipeline *pipeline;
  GstBus *bus;

  formatter = GES_FORMATTER (ges_pitivi_formatter_new ());
  timeline = ges_timeline_new ();
  pipeline = ges_timeline_pipeline_new ();
  bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
  mainloop = g_main_loop_new (NULL, FALSE);

  ges_timeline_pipeline_add_timeline (pipeline, timeline);
  ges_formatter_load_from_uri (formatter, timeline, uri);
  ges_timeline_pipeline_set_mode (pipeline, TIMELINE_MODE_PREVIEW_VIDEO);

  gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING);
  gst_bus_add_signal_watch (bus);
  g_signal_connect (bus, "message", G_CALLBACK (bus_message_cb), mainloop);
  g_main_loop_run (mainloop);
}
void
ges_base_xml_formatter_add_track_element (GESBaseXmlFormatter * self,
    GType track_element_type, const gchar * asset_id, const gchar * track_id,
    const gchar * timeline_obj_id, GstStructure * children_properties,
    GstStructure * properties, const gchar * metadatas, GError ** error)
{
  GESTrackElement *trackelement;

  GError *err = NULL;
  GESAsset *asset = NULL;
  GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self);

  if (priv->check_only)
    return;

  if (g_type_is_a (track_element_type, GES_TYPE_TRACK_ELEMENT) == FALSE) {
    GST_DEBUG_OBJECT (self, "%s is not a TrackElement, can not create it",
        g_type_name (track_element_type));
    goto out;
  }

  if (g_type_is_a (track_element_type, GES_TYPE_BASE_EFFECT) == FALSE) {
    GST_FIXME_OBJECT (self, "%s currently not supported",
        g_type_name (track_element_type));
    goto out;
  }

  asset = ges_asset_request (track_element_type, asset_id, &err);
  if (asset == NULL) {
    GST_DEBUG_OBJECT (self, "Can not create trackelement %s", asset_id);
    GST_FIXME_OBJECT (self, "Check if missing plugins etc %s",
        err ? err->message : "");

    goto out;
  }

  trackelement = GES_TRACK_ELEMENT (ges_asset_extract (asset, NULL));
  if (trackelement) {
    GESClip *clip;
    if (metadatas)
      ges_meta_container_add_metas_from_string (GES_META_CONTAINER
          (trackelement), metadatas);

    clip = g_hash_table_lookup (priv->containers, timeline_obj_id);
    if (clip) {
      _add_track_element (GES_FORMATTER (self), clip, trackelement, track_id,
          children_properties, properties);
    } else {
      PendingEffects *peffect;
      PendingClip *pend = g_hash_table_lookup (priv->clipid_pendings,
          timeline_obj_id);
      if (pend == NULL) {
        GST_WARNING_OBJECT (self, "No Clip with id: %s can not "
            "add TrackElement", timeline_obj_id);
        goto out;
      }

      peffect = g_slice_new0 (PendingEffects);

      peffect->trackelement = trackelement;
      peffect->track_id = g_strdup (track_id);
      peffect->properties = properties ? gst_structure_copy (properties) : NULL;
      peffect->children_properties = children_properties ?
          gst_structure_copy (children_properties) : NULL;

      pend->effects = g_list_append (pend->effects, peffect);
    }
    priv->current_track_element = trackelement;
  }

  ges_project_add_asset (GES_FORMATTER (self)->project, asset);

out:
  if (asset)
    gst_object_unref (asset);
  if (err)
    g_error_free (err);

  return;
}
void
ges_base_xml_formatter_add_encoding_profile (GESBaseXmlFormatter * self,
    const gchar * type, const gchar * parent, const gchar * name,
    const gchar * description, GstCaps * format, const gchar * preset,
    const gchar * preset_name, guint id, guint presence, GstCaps * restriction,
    guint pass, gboolean variableframerate, GstStructure * properties,
    GError ** error)
{
  const GList *tmp;
  GstEncodingProfile *profile;
  GstEncodingContainerProfile *parent_profile = NULL;
  GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self);

  if (priv->check_only)
    goto done;

  if (parent == NULL) {
    profile =
        _create_profile (self, type, parent, name, description, format, preset,
        preset_name, id, presence, restriction, pass, variableframerate);
    ges_project_add_encoding_profile (GES_FORMATTER (self)->project, profile);
    gst_object_unref (profile);

    goto done;
  }

  for (tmp = ges_project_list_encoding_profiles (GES_FORMATTER (self)->project);
      tmp; tmp = tmp->next) {
    GstEncodingProfile *tmpprofile = GST_ENCODING_PROFILE (tmp->data);

    if (g_strcmp0 (gst_encoding_profile_get_name (tmpprofile),
            gst_encoding_profile_get_name (tmpprofile)) == 0) {

      if (!GST_IS_ENCODING_CONTAINER_PROFILE (tmpprofile)) {
        g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
            "Profile '%s' parent %s is not a container...'", name, parent);
        goto done;
      }

      parent_profile = GST_ENCODING_CONTAINER_PROFILE (tmpprofile);
      break;
    }
  }

  if (parent_profile == NULL) {
    g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
        "Profile '%s' parent %s does not exist'", name, parent);
    goto done;
  }

  profile =
      _create_profile (self, type, parent, name, description, format, preset,
      preset_name, id, presence, restriction, pass, variableframerate);

  if (profile == NULL)
    goto done;

  gst_encoding_container_profile_add_profile (parent_profile, profile);

done:
  if (format)
    gst_caps_unref (format);
  if (restriction)
    gst_caps_unref (restriction);
}
static void
new_asset_cb (GESAsset * source, GAsyncResult * res, PendingAsset * passet)
{
  GError *error = NULL;
  gchar *possible_id = NULL;
  GList *tmp, *pendings = NULL;
  GESFormatter *self = passet->formatter;
  const gchar *id = ges_asset_get_id (source);
  GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self);
  GESAsset *asset = ges_asset_request_finish (res, &error);

  if (error) {
    GST_LOG_OBJECT (self, "Error %s creating asset id: %s", error->message, id);

    /* We set the metas on the Asset to give hints to the user */
    if (passet->metadatas)
      ges_meta_container_add_metas_from_string (GES_META_CONTAINER (source),
          passet->metadatas);
    if (passet->properties)
      gst_structure_foreach (passet->properties,
          (GstStructureForeachFunc) set_property_foreach, source);

    possible_id = ges_project_try_updating_id (GES_FORMATTER (self)->project,
        source, error);

    if (possible_id == NULL) {
      GST_WARNING_OBJECT (self, "Abandoning creation of asset %s with ID %s"
          "- Error: %s", g_type_name (G_OBJECT_TYPE (source)), id,
          error->message);

      pendings = g_hash_table_lookup (priv->assetid_pendingclips, id);
      for (tmp = pendings; tmp; tmp = tmp->next)
        _free_pending_clip (priv, (PendingClip *) tmp->data);

      _free_pending_asset (priv, passet);
      goto done;
    }

    /* We got a possible ID replacement for that asset, create it, and
     * make sure the assetid_pendingclips will use it */
    ges_asset_request_async (ges_asset_get_extractable_type (source),
        possible_id, NULL, (GAsyncReadyCallback) new_asset_cb, passet);
    ges_project_add_loading_asset (GES_FORMATTER (self)->project,
        ges_asset_get_extractable_type (source), possible_id);

    pendings = g_hash_table_lookup (priv->assetid_pendingclips, id);
    if (pendings) {
      g_hash_table_remove (priv->assetid_pendingclips, id);
      g_hash_table_insert (priv->assetid_pendingclips,
          g_strdup (possible_id), pendings);

      /* pendings should no be freed */
      pendings = NULL;
    }
    goto done;
  }

  /* now that we have the GESAsset, we create the GESClips */
  pendings = g_hash_table_lookup (priv->assetid_pendingclips, id);
  GST_DEBUG_OBJECT (self, "Asset created with ID %s, now creating pending "
      " Clips, nb pendings: %i", id, g_list_length (pendings));
  for (tmp = pendings; tmp; tmp = tmp->next) {
    GList *tmpeffect;
    GESClip *clip;
    PendingClip *pend = (PendingClip *) tmp->data;

    clip =
        _add_object_to_layer (priv, pend->id, pend->layer, asset,
        pend->start, pend->inpoint, pend->duration, pend->track_types,
        pend->metadatas, pend->properties);

    if (clip == NULL)
      continue;

    _add_children_properties (priv, pend->children_props, clip);
    _add_pending_bindings (priv, pend->pending_bindings, clip);

    GST_DEBUG_OBJECT (self, "Adding %i effect to new object",
        g_list_length (pend->effects));
    for (tmpeffect = pend->effects; tmpeffect; tmpeffect = tmpeffect->next) {
      PendingEffects *peffect = (PendingEffects *) tmpeffect->data;

      /* We keep a ref as _free_pending_effect unrefs it */
      _add_track_element (self, clip, gst_object_ref (peffect->trackelement),
          peffect->track_id, peffect->children_properties, peffect->properties);
    }
    _free_pending_clip (priv, pend);
  }

  /* And now add to the project */
  ges_project_add_asset (self->project, asset);
  gst_object_unref (self);

  _free_pending_asset (priv, passet);

done:
  if (asset)
    gst_object_unref (asset);
  if (possible_id)
    g_free (possible_id);

  if (pendings) {
    g_hash_table_remove (priv->assetid_pendingclips, id);
    g_list_free (pendings);
  }

  if (g_hash_table_size (priv->assetid_pendingclips) == 0 &&
      priv->pending_assets == NULL)
    _loading_done (self);
}
Пример #17
0
GESFormatter *
ges_default_formatter_new (void)
{
  return GES_FORMATTER (ges_keyfile_formatter_new ());
}
Пример #18
0
static gboolean
load_pitivi_file_from_uri (GESFormatter * self,
    GESTimeline * timeline, const gchar * uri, GError ** error)
{
  xmlDocPtr doc;
  GESLayer *layer;
  GESPitiviFormatterPrivate *priv = GES_PITIVI_FORMATTER (self)->priv;

  gboolean ret = TRUE;
  gint *prio = malloc (sizeof (gint));

  *prio = 0;
  layer = ges_layer_new ();
  g_object_set (layer, "auto-transition", TRUE, NULL);

  g_hash_table_insert (priv->layers_table, prio, layer);
  g_object_set (layer, "priority", (gint32) 0, NULL);

  if (!ges_timeline_add_layer (timeline, layer)) {
    GST_ERROR ("Couldn't add layer");
    return FALSE;
  }

  if (!(doc = xmlParseFile (uri))) {
    GST_ERROR ("The xptv file for uri %s was badly formed or did not exist",
        uri);
    return FALSE;
  }

  priv->xpathCtx = xmlXPathNewContext (doc);

  if (self->project)
    parse_metadatas (self);

  if (!create_tracks (self)) {
    GST_ERROR ("Couldn't create tracks");
    return FALSE;
  }

  list_sources (self);

  if (!parse_clips (self)) {
    GST_ERROR ("Couldn't find clips markup in the xptv file");
    return FALSE;
  }

  if (!parse_track_elements (self)) {
    GST_ERROR ("Couldn't find track objects markup in the xptv file");
    return FALSE;
  }



  /* If there are no clips to load we should emit
   * 'project-loaded' signal.
   */
  if (!g_hash_table_size (priv->clips_table) && GES_FORMATTER (self)->project) {
    ges_project_set_loaded (GES_FORMATTER (self)->project,
        GES_FORMATTER (self));
  } else {
    if (!make_clips (self)) {
      GST_ERROR ("Couldn't deserialise the project properly");
      return FALSE;
    }
  }

  xmlXPathFreeContext (priv->xpathCtx);
  xmlFreeDoc (doc);
  return ret;
}
Пример #19
0
/**
 * ges_project_save:
 * @project: A #GESProject to save
 * @timeline: The #GESTimeline to save, it must have been extracted from @project
 * @uri: The uri where to save @project and @timeline
 * @formatter_asset: (allow-none): The formatter asset to use or %NULL. If %NULL,
 * will try to save in the same format as the one from which the timeline as been loaded
 * or default to the formatter with highest rank
 * @overwrite: %TRUE to overwrite file if it exists
 * @error: (out) (allow-none): An error to be set in case something wrong happens or %NULL
 *
 * Save the timeline of @project to @uri. You should make sure that @timeline
 * is one of the timelines that have been extracted from @project
 * (using ges_asset_extract (@project);)
 *
 * Returns: %TRUE if the project could be save, %FALSE otherwize
 */
gboolean
ges_project_save (GESProject * project, GESTimeline * timeline,
    const gchar * uri, GESAsset * formatter_asset, gboolean overwrite,
    GError ** error)
{
  GESAsset *tl_asset;
  gboolean ret = TRUE;
  GESFormatter *formatter = NULL;

  g_return_val_if_fail (GES_IS_PROJECT (project), FALSE);
  g_return_val_if_fail (formatter_asset == NULL ||
      g_type_is_a (ges_asset_get_extractable_type (formatter_asset),
          GES_TYPE_FORMATTER), FALSE);
  g_return_val_if_fail ((error == NULL || *error == NULL), FALSE);

  tl_asset = ges_extractable_get_asset (GES_EXTRACTABLE (timeline));
  if (tl_asset == NULL && project->priv->uri == NULL) {
    GESAsset *asset = ges_asset_cache_lookup (GES_TYPE_PROJECT, uri);

    if (asset) {
      GST_WARNING_OBJECT (project, "Trying to save project to %s but we already"
          "have %" GST_PTR_FORMAT " for that uri, can not save", uri, asset);
      goto out;
    }

    GST_DEBUG_OBJECT (project, "Timeline %" GST_PTR_FORMAT " has no asset"
        " we have no uri set, so setting ourself as asset", timeline);

    ges_extractable_set_asset (GES_EXTRACTABLE (timeline), GES_ASSET (project));
  } else if (tl_asset != GES_ASSET (project)) {
    GST_WARNING_OBJECT (project, "Timeline %" GST_PTR_FORMAT
        " not created by this project can not save", timeline);

    ret = FALSE;
    goto out;
  }

  if (formatter_asset == NULL)
    formatter_asset = gst_object_ref (ges_formatter_get_default ());

  formatter = GES_FORMATTER (ges_asset_extract (formatter_asset, error));
  if (formatter == NULL) {
    GST_WARNING_OBJECT (project, "Could not create the formatter %p %s: %s",
        formatter_asset, ges_asset_get_id (formatter_asset),
        (error && *error) ? (*error)->message : "Unknown Error");

    ret = FALSE;
    goto out;
  }

  ges_project_add_formatter (project, formatter);
  ret = ges_formatter_save_to_uri (formatter, timeline, uri, overwrite, error);
  if (ret && project->priv->uri == NULL)
    ges_project_set_uri (project, uri);

out:
  if (formatter_asset)
    gst_object_unref (formatter_asset);
  ges_project_remove_formatter (project, formatter);

  return ret;
}