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