1 /* GStreamer Editing Services
2  *
3  * Copyright (C) 2012 Thibault Saunier <thibault.saunier@collabora.com>
4  * Copyright (C) 2012 Volodymyr Rudyi <vladimir.rudoy@gmail.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21 /**
22  * SECTION: gesuriclipasset
23  * @title: GESUriClipAsset
24  * @short_description: A GESAsset subclass specialized in GESUriClip extraction
25  *
26  * The #GESUriClipAsset is a special #GESAsset that lets you handle
27  * the media file to use inside the GStreamer Editing Services. It has APIs that
28  * let you get information about the medias. Also, the tags found in the media file are
29  * set as Metadata of the Asset.
30  */
31 #ifdef HAVE_CONFIG_H
32 #include "config.h"
33 #endif
34 
35 #include <errno.h>
36 #include <gst/pbutils/pbutils.h>
37 #include "ges.h"
38 #include "ges-internal.h"
39 #include "ges-track-element-asset.h"
40 
41 #define DEFAULT_DISCOVERY_TIMEOUT (60 * GST_SECOND)
42 
43 static GHashTable *parent_newparent_table = NULL;
44 
45 static GstDiscoverer *discoverer = NULL;
46 
47 static void
initable_iface_init(GInitableIface * initable_iface)48 initable_iface_init (GInitableIface * initable_iface)
49 {
50   /*  We can not iniate synchronously */
51   initable_iface->init = NULL;
52 }
53 
54 /* TODO: We should monitor files here, and add some way of reporting changes
55  * to user
56  */
57 enum
58 {
59   PROP_0,
60   PROP_DURATION,
61   PROP_LAST
62 };
63 static GParamSpec *properties[PROP_LAST];
64 
65 static void discoverer_discovered_cb (GstDiscoverer * discoverer,
66     GstDiscovererInfo * info, GError * err, gpointer user_data);
67 
68 struct _GESUriClipAssetPrivate
69 {
70   GstDiscovererInfo *info;
71   GstClockTime duration;
72   gboolean is_image;
73 
74   GList *asset_trackfilesources;
75 };
76 
77 typedef struct
78 {
79   GMainLoop *ml;
80   GESAsset *asset;
81   GError *error;
82 } RequestSyncData;
83 
84 struct _GESUriSourceAssetPrivate
85 {
86   GstDiscovererStreamInfo *sinfo;
87   GESUriClipAsset *parent_asset;
88 
89   const gchar *uri;
90 };
91 
92 G_DEFINE_TYPE_WITH_CODE (GESUriClipAsset, ges_uri_clip_asset,
93     GES_TYPE_CLIP_ASSET, G_ADD_PRIVATE (GESUriClipAsset)
94     G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init));
95 
96 static void
ges_uri_clip_asset_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)97 ges_uri_clip_asset_get_property (GObject * object, guint property_id,
98     GValue * value, GParamSpec * pspec)
99 {
100   GESUriClipAssetPrivate *priv = GES_URI_CLIP_ASSET (object)->priv;
101 
102   switch (property_id) {
103     case PROP_DURATION:
104       g_value_set_uint64 (value, priv->duration);
105       break;
106     default:
107       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
108   }
109 }
110 
111 static void
ges_uri_clip_asset_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)112 ges_uri_clip_asset_set_property (GObject * object, guint property_id,
113     const GValue * value, GParamSpec * pspec)
114 {
115   GESUriClipAssetPrivate *priv = GES_URI_CLIP_ASSET (object)->priv;
116 
117   switch (property_id) {
118     case PROP_DURATION:
119       priv->duration = g_value_get_uint64 (value);
120       break;
121     default:
122       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
123   }
124 }
125 
126 static GESAssetLoadingReturn
_start_loading(GESAsset * asset,GError ** error)127 _start_loading (GESAsset * asset, GError ** error)
128 {
129   gboolean ret;
130   const gchar *uri;
131   GESUriClipAssetClass *class = GES_URI_CLIP_ASSET_GET_CLASS (asset);
132 
133   GST_DEBUG ("Started loading %p", asset);
134 
135   uri = ges_asset_get_id (asset);
136 
137   ret = gst_discoverer_discover_uri_async (class->discoverer, uri);
138   if (ret)
139     return GES_ASSET_LOADING_ASYNC;
140 
141   return GES_ASSET_LOADING_ERROR;
142 }
143 
144 static gboolean
_request_id_update(GESAsset * self,gchar ** proposed_new_id,GError * error)145 _request_id_update (GESAsset * self, gchar ** proposed_new_id, GError * error)
146 {
147   if (error->domain == GST_RESOURCE_ERROR &&
148       (error->code == GST_RESOURCE_ERROR_NOT_FOUND ||
149           error->code == GST_RESOURCE_ERROR_OPEN_READ)) {
150     const gchar *uri = ges_asset_get_id (self);
151     GFile *parent, *file = g_file_new_for_uri (uri);
152 
153     /* Check if we have the new parent in cache */
154     parent = g_file_get_parent (file);
155     if (parent) {
156       GFile *new_parent = g_hash_table_lookup (parent_newparent_table, parent);
157 
158       if (new_parent) {
159         gchar *basename = g_file_get_basename (file);
160         GFile *new_file = g_file_get_child (new_parent, basename);
161 
162         /* FIXME Handle the GCancellable */
163         if (g_file_query_exists (new_file, NULL)) {
164           *proposed_new_id = g_file_get_uri (new_file);
165           GST_DEBUG_OBJECT (self, "Proposing path: %s as proxy",
166               *proposed_new_id);
167         }
168 
169         gst_object_unref (new_file);
170         g_free (basename);
171       }
172       gst_object_unref (parent);
173     }
174 
175     gst_object_unref (file);
176 
177     return TRUE;
178   }
179 
180   return FALSE;
181 }
182 
183 static void
_asset_proxied(GESAsset * self,const gchar * new_uri)184 _asset_proxied (GESAsset * self, const gchar * new_uri)
185 {
186   const gchar *uri = ges_asset_get_id (self);
187   GFile *parent, *new_parent, *new_file = g_file_new_for_uri (new_uri),
188       *file = g_file_new_for_uri (uri);
189 
190   parent = g_file_get_parent (file);
191   new_parent = g_file_get_parent (new_file);
192   g_hash_table_insert (parent_newparent_table, parent, new_parent);
193   gst_object_unref (file);
194   gst_object_unref (new_file);
195 }
196 
197 static void
ges_uri_clip_asset_dispose(GObject * object)198 ges_uri_clip_asset_dispose (GObject * object)
199 {
200   GESUriClipAsset *self = GES_URI_CLIP_ASSET (object);
201   GESUriClipAssetPrivate *prif = self->priv;
202 
203   if (prif->asset_trackfilesources) {
204     g_list_free_full (prif->asset_trackfilesources,
205         (GDestroyNotify) gst_object_unref);
206     prif->asset_trackfilesources = NULL;
207   }
208 
209   gst_clear_object (&prif->info);
210 
211   G_OBJECT_CLASS (ges_uri_clip_asset_parent_class)->dispose (object);
212 }
213 
214 static void
ges_uri_clip_asset_class_init(GESUriClipAssetClass * klass)215 ges_uri_clip_asset_class_init (GESUriClipAssetClass * klass)
216 {
217   GObjectClass *object_class = G_OBJECT_CLASS (klass);
218 
219   object_class->get_property = ges_uri_clip_asset_get_property;
220   object_class->set_property = ges_uri_clip_asset_set_property;
221   object_class->dispose = ges_uri_clip_asset_dispose;
222 
223   GES_ASSET_CLASS (klass)->start_loading = _start_loading;
224   GES_ASSET_CLASS (klass)->request_id_update = _request_id_update;
225   GES_ASSET_CLASS (klass)->inform_proxy = _asset_proxied;
226 
227   klass->discovered = discoverer_discovered_cb;
228 
229 
230   /**
231    * GESUriClipAsset:duration:
232    *
233    * The duration (in nanoseconds) of the media file
234    */
235   properties[PROP_DURATION] =
236       g_param_spec_uint64 ("duration", "Duration", "The duration to use", 0,
237       G_MAXUINT64, GST_CLOCK_TIME_NONE, G_PARAM_READWRITE);
238   g_object_class_install_property (object_class, PROP_DURATION,
239       properties[PROP_DURATION]);
240 
241   _ges_uri_asset_ensure_setup (klass);
242 }
243 
244 static void
ges_uri_clip_asset_init(GESUriClipAsset * self)245 ges_uri_clip_asset_init (GESUriClipAsset * self)
246 {
247   GESUriClipAssetPrivate *priv;
248 
249   priv = self->priv = ges_uri_clip_asset_get_instance_private (self);
250 
251   priv->info = NULL;
252   priv->duration = GST_CLOCK_TIME_NONE;
253   priv->is_image = FALSE;
254 }
255 
256 static void
_create_uri_source_asset(GESUriClipAsset * asset,GstDiscovererStreamInfo * sinfo,GESTrackType type)257 _create_uri_source_asset (GESUriClipAsset * asset,
258     GstDiscovererStreamInfo * sinfo, GESTrackType type)
259 {
260   GESAsset *tck_filesource_asset;
261   GESUriSourceAssetPrivate *priv_tckasset;
262   GESUriClipAssetPrivate *priv = asset->priv;
263   gchar *stream_id =
264       g_strdup (gst_discoverer_stream_info_get_stream_id (sinfo));
265 
266   if (stream_id == NULL) {
267     GST_WARNING ("No stream ID found, using the pointer instead");
268 
269     stream_id = g_strdup_printf ("%i", GPOINTER_TO_INT (sinfo));
270   }
271 
272   if (type == GES_TRACK_TYPE_VIDEO)
273     tck_filesource_asset = ges_asset_request (GES_TYPE_VIDEO_URI_SOURCE,
274         stream_id, NULL);
275   else
276     tck_filesource_asset = ges_asset_request (GES_TYPE_AUDIO_URI_SOURCE,
277         stream_id, NULL);
278   g_free (stream_id);
279 
280   priv_tckasset = GES_URI_SOURCE_ASSET (tck_filesource_asset)->priv;
281   priv_tckasset->uri = ges_asset_get_id (GES_ASSET (asset));
282   priv_tckasset->sinfo = gst_object_ref (sinfo);
283   priv_tckasset->parent_asset = asset;
284   ges_track_element_asset_set_track_type (GES_TRACK_ELEMENT_ASSET
285       (tck_filesource_asset), type);
286 
287   priv->asset_trackfilesources = g_list_append (priv->asset_trackfilesources,
288       tck_filesource_asset);
289 }
290 
291 static void
ges_uri_clip_asset_set_info(GESUriClipAsset * self,GstDiscovererInfo * info)292 ges_uri_clip_asset_set_info (GESUriClipAsset * self, GstDiscovererInfo * info)
293 {
294   GList *tmp, *stream_list;
295 
296   GESTrackType supportedformats = GES_TRACK_TYPE_UNKNOWN;
297   GESUriClipAssetPrivate *priv = GES_URI_CLIP_ASSET (self)->priv;
298 
299   /* Extract infos from the GstDiscovererInfo */
300   stream_list = gst_discoverer_info_get_stream_list (info);
301   for (tmp = stream_list; tmp; tmp = tmp->next) {
302     GESTrackType type = GES_TRACK_TYPE_UNKNOWN;
303     GstDiscovererStreamInfo *sinf = (GstDiscovererStreamInfo *) tmp->data;
304 
305     if (GST_IS_DISCOVERER_AUDIO_INFO (sinf)) {
306       if (supportedformats == GES_TRACK_TYPE_UNKNOWN)
307         supportedformats = GES_TRACK_TYPE_AUDIO;
308       else
309         supportedformats |= GES_TRACK_TYPE_AUDIO;
310 
311       type = GES_TRACK_TYPE_AUDIO;
312     } else if (GST_IS_DISCOVERER_VIDEO_INFO (sinf)) {
313       if (supportedformats == GES_TRACK_TYPE_UNKNOWN)
314         supportedformats = GES_TRACK_TYPE_VIDEO;
315       else
316         supportedformats |= GES_TRACK_TYPE_VIDEO;
317       if (gst_discoverer_video_info_is_image ((GstDiscovererVideoInfo *)
318               sinf))
319         priv->is_image = TRUE;
320       type = GES_TRACK_TYPE_VIDEO;
321     }
322 
323     GST_DEBUG_OBJECT (self, "Creating GESUriSourceAsset for stream: %s",
324         gst_discoverer_stream_info_get_stream_id (sinf));
325     _create_uri_source_asset (self, sinf, type);
326   }
327   ges_clip_asset_set_supported_formats (GES_CLIP_ASSET
328       (self), supportedformats);
329 
330   if (stream_list)
331     gst_discoverer_stream_info_list_free (stream_list);
332 
333   if (priv->is_image == FALSE)
334     priv->duration = gst_discoverer_info_get_duration (info);
335   /* else we keep #GST_CLOCK_TIME_NONE */
336 
337   priv->info = gst_object_ref (info);
338 }
339 
340 static void
_set_meta_file_size(const gchar * uri,GESUriClipAsset * asset)341 _set_meta_file_size (const gchar * uri, GESUriClipAsset * asset)
342 {
343   GError *error = NULL;
344   GFileInfo *file_info = NULL;
345   guint64 file_size;
346   GFile *gfile = NULL;
347 
348   GESMetaContainer *container = GES_META_CONTAINER (asset);
349 
350   gfile = g_file_new_for_uri (uri);
351   file_info = g_file_query_info (gfile, "standard::size",
352       G_FILE_QUERY_INFO_NONE, NULL, &error);
353   if (!error) {
354     file_size = g_file_info_get_attribute_uint64 (file_info, "standard::size");
355     ges_meta_container_register_meta_uint64 (container, GES_META_READ_WRITE,
356         "file-size", file_size);
357   } else {
358     g_error_free (error);
359   }
360   if (gfile)
361     g_object_unref (gfile);
362   if (file_info)
363     g_object_unref (file_info);
364 }
365 
366 static void
_set_meta_foreach(const GstTagList * tags,const gchar * tag,GESMetaContainer * container)367 _set_meta_foreach (const GstTagList * tags, const gchar * tag,
368     GESMetaContainer * container)
369 {
370   GValue value = { 0 };
371 
372   if (gst_tag_list_copy_value (&value, tags, tag)) {
373     ges_meta_container_set_meta (container, tag, &value);
374     g_value_unset (&value);
375   } else {
376     GST_INFO ("Could not set metadata: %s", tag);
377   }
378 }
379 
380 static void
discoverer_discovered_cb(GstDiscoverer * discoverer,GstDiscovererInfo * info,GError * err,gpointer user_data)381 discoverer_discovered_cb (GstDiscoverer * discoverer,
382     GstDiscovererInfo * info, GError * err, gpointer user_data)
383 {
384   GError *error = NULL;
385   const GstTagList *tags;
386 
387   const gchar *uri = gst_discoverer_info_get_uri (info);
388   GESUriClipAsset *mfs =
389       GES_URI_CLIP_ASSET (ges_asset_cache_lookup (GES_TYPE_URI_CLIP, uri));
390 
391   tags = gst_discoverer_info_get_tags (info);
392   if (tags)
393     gst_tag_list_foreach (tags, (GstTagForeachFunc) _set_meta_foreach, mfs);
394 
395   _set_meta_file_size (uri, mfs);
396 
397   if (gst_discoverer_info_get_result (info) == GST_DISCOVERER_OK) {
398     ges_uri_clip_asset_set_info (mfs, info);
399   } else {
400     if (err) {
401       error = g_error_copy (err);
402     } else {
403       error = g_error_new (GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_FAILED,
404           "Stream %s discovering failed (error code: %d)",
405           uri, gst_discoverer_info_get_result (info));
406     }
407   }
408 
409   ges_asset_cache_set_loaded (GES_TYPE_URI_CLIP, uri, error);
410 
411   if (error)
412     g_error_free (error);
413 }
414 
415 static void
asset_ready_cb(GESAsset * source,GAsyncResult * res,RequestSyncData * data)416 asset_ready_cb (GESAsset * source, GAsyncResult * res, RequestSyncData * data)
417 {
418   data->asset = ges_asset_request_finish (res, &data->error);
419 
420   if (data->error) {
421     gchar *possible_uri = ges_uri_asset_try_update_id (data->error, source);
422 
423     if (possible_uri) {
424       g_clear_error (&data->error);
425       ges_asset_request_async (GES_TYPE_URI_CLIP, possible_uri, NULL,
426           (GAsyncReadyCallback) asset_ready_cb, data);
427       g_free (possible_uri);
428 
429       return;
430     }
431   }
432   g_main_loop_quit (data->ml);
433 }
434 
435 /* API implementation */
436 /**
437  * ges_uri_clip_asset_get_info:
438  * @self: Target asset
439  *
440  * Gets #GstDiscovererInfo about the file
441  *
442  * Returns: (transfer none): #GstDiscovererInfo of specified asset
443  */
444 GstDiscovererInfo *
ges_uri_clip_asset_get_info(const GESUriClipAsset * self)445 ges_uri_clip_asset_get_info (const GESUriClipAsset * self)
446 {
447   g_return_val_if_fail (GES_IS_URI_CLIP_ASSET (self), NULL);
448 
449   return self->priv->info;
450 }
451 
452 /**
453  * ges_uri_clip_asset_get_duration:
454  * @self: a #GESUriClipAsset
455  *
456  * Gets duration of the file represented by @self
457  *
458  * Returns: The duration of @self
459  */
460 GstClockTime
ges_uri_clip_asset_get_duration(GESUriClipAsset * self)461 ges_uri_clip_asset_get_duration (GESUriClipAsset * self)
462 {
463   g_return_val_if_fail (GES_IS_URI_CLIP_ASSET (self), GST_CLOCK_TIME_NONE);
464 
465   return self->priv->duration;
466 }
467 
468 /**
469  * ges_uri_clip_asset_is_image:
470  * @self: a #indent: Standard input:311: Error:Unexpected end of file
471 GESUriClipAsset
472  *
473  * Gets Whether the file represented by @self is an image or not
474  *
475  * Returns: Whether the file represented by @self is an image or not
476  */
477 gboolean
ges_uri_clip_asset_is_image(GESUriClipAsset * self)478 ges_uri_clip_asset_is_image (GESUriClipAsset * self)
479 {
480   g_return_val_if_fail (GES_IS_URI_CLIP_ASSET (self), FALSE);
481 
482   return self->priv->is_image;
483 }
484 
485 /**
486  * ges_uri_clip_asset_new:
487  * @uri: The URI of the file for which to create a #GESUriClipAsset
488  * @cancellable: optional %GCancellable object, %NULL to ignore.
489  * @callback: (scope async): a #GAsyncReadyCallback to call when the initialization is finished
490  * @user_data: The user data to pass when @callback is called
491  *
492  * Creates a #GESUriClipAsset for @uri
493  *
494  * Example of request of a GESUriClipAsset:
495  * |[
496  * // The request callback
497  * static void
498  * filesource_asset_loaded_cb (GESAsset * source, GAsyncResult * res, gpointer user_data)
499  * {
500  *   GError *error = NULL;
501  *   GESUriClipAsset *filesource_asset;
502  *
503  *   filesource_asset = ges_uri_clip_asset_finish (res, &error);
504  *   if (filesource_asset) {
505  *    g_print ("The file: %s is usable as a FileSource, it is%s an image and lasts %" GST_TIME_FORMAT,
506  *        ges_asset_get_id (GES_ASSET (filesource_asset))
507  *        ges_uri_clip_asset_is_image (filesource_asset) ? "" : " not",
508  *        GST_TIME_ARGS (ges_uri_clip_asset_get_duration (filesource_asset));
509  *   } else {
510  *    g_print ("The file: %s is *not* usable as a FileSource because: %s",
511  *        ges_asset_get_id (source), error->message);
512  *   }
513  *
514  *   gst_object_unref (mfs);
515  * }
516  *
517  * // The request:
518  * ges_uri_clip_asset_new (uri, (GAsyncReadyCallback) filesource_asset_loaded_cb, user_data);
519  * ]|
520  */
521 void
ges_uri_clip_asset_new(const gchar * uri,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)522 ges_uri_clip_asset_new (const gchar * uri, GCancellable * cancellable,
523     GAsyncReadyCallback callback, gpointer user_data)
524 {
525   ges_asset_request_async (GES_TYPE_URI_CLIP, uri, cancellable,
526       callback, user_data);
527 }
528 
529 /**
530  * ges_uri_clip_asset_finish:
531  * @res: The #GAsyncResult from which to get the newly created #GESUriClipAsset
532  * @error: An error to be set in case something wrong happens or %NULL
533  *
534  * Finalize the request of an async #GESUriClipAsset
535  *
536  * Returns: (transfer full): The #GESUriClipAsset previously requested
537  *
538  * Since: 1.16
539  */
540 GESUriClipAsset *
ges_uri_clip_asset_finish(GAsyncResult * res,GError ** error)541 ges_uri_clip_asset_finish (GAsyncResult * res, GError ** error)
542 {
543   GESAsset *asset;
544 
545   g_return_val_if_fail (G_IS_ASYNC_RESULT (res), NULL);
546 
547   asset = ges_asset_request_finish (res, error);
548   if (asset != NULL) {
549     return GES_URI_CLIP_ASSET (asset);
550   }
551 
552   return NULL;
553 }
554 
555 /**
556  * ges_uri_clip_asset_request_sync:
557  * @uri: The URI of the file for which to create a #GESUriClipAsset.
558  * You can also use multi file uris for #GESMultiFileSource.
559  * @error: An error to be set in case something wrong happens or %NULL
560  *
561  * Creates a #GESUriClipAsset for @uri syncronously. You should avoid
562  * to use it in application, and rather create #GESUriClipAsset asynchronously
563  *
564  * Returns: (transfer full): A reference to the requested asset or %NULL if
565  * an error happened
566  */
567 GESUriClipAsset *
ges_uri_clip_asset_request_sync(const gchar * uri,GError ** error)568 ges_uri_clip_asset_request_sync (const gchar * uri, GError ** error)
569 {
570   GError *lerror = NULL;
571   GESUriClipAsset *asset;
572   RequestSyncData data = { 0, };
573   GESUriClipAssetClass *klass = g_type_class_peek (GES_TYPE_URI_CLIP_ASSET);
574   GstClockTime timeout;
575   GstDiscoverer *previous_discoverer = klass->discoverer;
576 
577   asset = GES_URI_CLIP_ASSET (ges_asset_request (GES_TYPE_URI_CLIP, uri,
578           &lerror));
579 
580   if (asset)
581     return asset;
582 
583   data.ml = g_main_loop_new (NULL, TRUE);
584   g_object_get (previous_discoverer, "timeout", &timeout, NULL);
585   klass->discoverer = gst_discoverer_new (timeout, error);
586   if (!klass->discoverer) {
587     klass->discoverer = previous_discoverer;
588 
589     return NULL;
590   }
591 
592   g_signal_connect (klass->discoverer, "discovered",
593       G_CALLBACK (klass->discovered), NULL);
594   gst_discoverer_start (klass->discoverer);
595   ges_asset_request_async (GES_TYPE_URI_CLIP, uri, NULL,
596       (GAsyncReadyCallback) asset_ready_cb, &data);
597   g_main_loop_run (data.ml);
598   g_main_loop_unref (data.ml);
599 
600   gst_object_unref (klass->discoverer);
601   klass->discoverer = previous_discoverer;
602 
603   if (data.error) {
604     GST_ERROR ("Got an error requesting asset: %s", data.error->message);
605     if (error != NULL)
606       g_propagate_error (error, data.error);
607 
608     return NULL;
609   }
610 
611   return GES_URI_CLIP_ASSET (data.asset);
612 }
613 
614 /**
615  * ges_uri_clip_asset_class_set_timeout:
616  * @klass: The #GESUriClipAssetClass on which to set the discoverer timeout
617  * @timeout: The timeout to set
618  *
619  * Sets the timeout of #GESUriClipAsset loading
620  */
621 void
ges_uri_clip_asset_class_set_timeout(GESUriClipAssetClass * klass,GstClockTime timeout)622 ges_uri_clip_asset_class_set_timeout (GESUriClipAssetClass * klass,
623     GstClockTime timeout)
624 {
625   g_return_if_fail (GES_IS_URI_CLIP_ASSET_CLASS (klass));
626 
627   g_object_set (klass->discoverer, "timeout", timeout, NULL);
628 }
629 
630 /**
631  * ges_uri_clip_asset_get_stream_assets:
632  * @self: A #GESUriClipAsset
633  *
634  * Get the GESUriSourceAsset @self containes
635  *
636  * Returns: (transfer none) (element-type GESUriSourceAsset): a
637  * #GList of #GESUriSourceAsset
638  */
639 const GList *
ges_uri_clip_asset_get_stream_assets(GESUriClipAsset * self)640 ges_uri_clip_asset_get_stream_assets (GESUriClipAsset * self)
641 {
642   g_return_val_if_fail (GES_IS_URI_CLIP_ASSET (self), FALSE);
643 
644   return self->priv->asset_trackfilesources;
645 }
646 
647 /*****************************************************************
648  *            GESUriSourceAsset implementation             *
649  *****************************************************************/
650 /**
651  * SECTION: gesurisourceasset
652  * @title: GESUriClipAsset
653  * @short_description: A GESAsset subclass specialized in GESUriSource extraction
654  *
655  * NOTE: You should never request such a #GESAsset as they will be created automatically
656  * by #GESUriClipAsset-s.
657  */
658 
659 G_DEFINE_TYPE_WITH_PRIVATE (GESUriSourceAsset, ges_uri_source_asset,
660     GES_TYPE_TRACK_ELEMENT_ASSET);
661 
662 static GESExtractable *
_extract(GESAsset * asset,GError ** error)663 _extract (GESAsset * asset, GError ** error)
664 {
665   gchar *uri = NULL;
666   GESTrackElement *trackelement;
667   GESUriSourceAssetPrivate *priv = GES_URI_SOURCE_ASSET (asset)->priv;
668 
669   if (GST_IS_DISCOVERER_STREAM_INFO (priv->sinfo) == FALSE) {
670     GST_WARNING_OBJECT (asset, "Can not extract as no strean info set");
671 
672     return NULL;
673   }
674 
675   if (priv->uri == NULL) {
676     GST_WARNING_OBJECT (asset, "Can not extract as no uri set");
677 
678     return NULL;
679   }
680 
681   uri = g_strdup (priv->uri);
682 
683   if (g_str_has_prefix (priv->uri, GES_MULTI_FILE_URI_PREFIX)) {
684     trackelement = GES_TRACK_ELEMENT (ges_multi_file_source_new (uri));
685   } else if (GST_IS_DISCOVERER_VIDEO_INFO (priv->sinfo)
686       && gst_discoverer_video_info_is_image ((GstDiscovererVideoInfo *)
687           priv->sinfo))
688     trackelement = GES_TRACK_ELEMENT (ges_image_source_new (uri));
689   else if (GST_IS_DISCOVERER_VIDEO_INFO (priv->sinfo))
690     trackelement = GES_TRACK_ELEMENT (ges_video_uri_source_new (uri));
691   else
692     trackelement = GES_TRACK_ELEMENT (ges_audio_uri_source_new (uri));
693 
694   ges_track_element_set_track_type (trackelement,
695       ges_track_element_asset_get_track_type (GES_TRACK_ELEMENT_ASSET (asset)));
696 
697   g_free (uri);
698 
699   return GES_EXTRACTABLE (trackelement);
700 }
701 
702 static void
ges_uri_source_asset_dispose(GObject * object)703 ges_uri_source_asset_dispose (GObject * object)
704 {
705   GESUriSourceAsset *self = GES_URI_SOURCE_ASSET (object);
706   GESUriSourceAssetPrivate *priv = self->priv;
707 
708   gst_clear_object (&priv->sinfo);
709 
710   G_OBJECT_CLASS (ges_uri_source_asset_parent_class)->dispose (object);
711 }
712 
713 static void
ges_uri_source_asset_class_init(GESUriSourceAssetClass * klass)714 ges_uri_source_asset_class_init (GESUriSourceAssetClass * klass)
715 {
716   GObjectClass *object_class = G_OBJECT_CLASS (klass);
717 
718   object_class->dispose = ges_uri_source_asset_dispose;
719 
720   GES_ASSET_CLASS (klass)->extract = _extract;
721 }
722 
723 static void
ges_uri_source_asset_init(GESUriSourceAsset * self)724 ges_uri_source_asset_init (GESUriSourceAsset * self)
725 {
726   GESUriSourceAssetPrivate *priv;
727 
728   priv = self->priv = ges_uri_source_asset_get_instance_private (self);
729 
730   priv->sinfo = NULL;
731   priv->parent_asset = NULL;
732   priv->uri = NULL;
733 }
734 
735 /**
736  * ges_uri_source_asset_get_stream_info:
737  * @asset: A #GESUriClipAsset
738  *
739  * Get the #GstDiscovererStreamInfo user by @asset
740  *
741  * Returns: (transfer none): a #GESUriClipAsset
742  */
743 GstDiscovererStreamInfo *
ges_uri_source_asset_get_stream_info(GESUriSourceAsset * asset)744 ges_uri_source_asset_get_stream_info (GESUriSourceAsset * asset)
745 {
746   g_return_val_if_fail (GES_IS_URI_SOURCE_ASSET (asset), NULL);
747 
748   return asset->priv->sinfo;
749 }
750 
751 const gchar *
ges_uri_source_asset_get_stream_uri(GESUriSourceAsset * asset)752 ges_uri_source_asset_get_stream_uri (GESUriSourceAsset * asset)
753 {
754   g_return_val_if_fail (GES_IS_URI_SOURCE_ASSET (asset), NULL);
755 
756   return asset->priv->uri;
757 }
758 
759 /**
760  * ges_uri_source_asset_get_filesource_asset:
761  * @asset: A #GESUriClipAsset
762  *
763  * Get the #GESUriClipAsset @self is contained in
764  *
765  * Returns: a #GESUriClipAsset
766  */
767 const GESUriClipAsset *
ges_uri_source_asset_get_filesource_asset(GESUriSourceAsset * asset)768 ges_uri_source_asset_get_filesource_asset (GESUriSourceAsset * asset)
769 {
770   g_return_val_if_fail (GES_IS_URI_SOURCE_ASSET (asset), NULL);
771 
772   return asset->priv->parent_asset;
773 }
774 
775 void
_ges_uri_asset_cleanup(void)776 _ges_uri_asset_cleanup (void)
777 {
778   if (discoverer)
779     gst_discoverer_stop (discoverer);
780   g_clear_object (&discoverer);
781   if (parent_newparent_table) {
782     g_hash_table_destroy (parent_newparent_table);
783     parent_newparent_table = NULL;
784   }
785 }
786 
787 gboolean
_ges_uri_asset_ensure_setup(gpointer uriasset_class)788 _ges_uri_asset_ensure_setup (gpointer uriasset_class)
789 {
790   GESUriClipAssetClass *klass;
791   GError *err;
792   GstClockTime timeout;
793   const gchar *timeout_str;
794 
795   g_return_val_if_fail (GES_IS_URI_CLIP_ASSET_CLASS (uriasset_class), FALSE);
796 
797   klass = GES_URI_CLIP_ASSET_CLASS (uriasset_class);
798 
799   timeout = DEFAULT_DISCOVERY_TIMEOUT;
800   errno = 0;
801   timeout_str = g_getenv ("GES_DISCOVERY_TIMEOUT");
802   if (timeout_str)
803     timeout = g_ascii_strtod (timeout_str, NULL) * GST_SECOND;
804   else
805     errno = 10;
806 
807   if (errno)
808     timeout = DEFAULT_DISCOVERY_TIMEOUT;
809 
810   if (!discoverer) {
811     discoverer = gst_discoverer_new (timeout, &err);
812     if (!discoverer) {
813       GST_ERROR ("Could not create discoverer: %s", err->message);
814       g_error_free (err);
815       return FALSE;
816     }
817   }
818 
819   /* The class structure keeps weak pointers on the discoverers so they
820    * can be properly cleaned up in _ges_uri_asset_cleanup(). */
821   if (!klass->discoverer) {
822     klass->discoverer = klass->sync_discoverer = discoverer;
823     g_object_add_weak_pointer (G_OBJECT (discoverer),
824         (gpointer *) & klass->discoverer);
825     g_object_add_weak_pointer (G_OBJECT (discoverer),
826         (gpointer *) & klass->sync_discoverer);
827 
828     g_signal_connect (klass->discoverer, "discovered",
829         G_CALLBACK (klass->discovered), NULL);
830   }
831 
832   /* We just start the discoverer and let it live */
833   gst_discoverer_start (klass->discoverer);
834   if (parent_newparent_table == NULL) {
835     parent_newparent_table = g_hash_table_new_full (g_file_hash,
836         (GEqualFunc) g_file_equal, g_object_unref, g_object_unref);
837   }
838 
839   return TRUE;
840 }
841