ges-uri-asset.c 23.4 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/* GStreamer Editing Services
 *
 * Copyright (C) 2012 Thibault Saunier <thibault.saunier@collabora.com>
 * Copyright (C) 2012 Volodymyr Rudyi <vladimir.rudoy@gmail.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */
/**
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
28
 * the media file to use inside the GStreamer Editing Services. It has APIs that
 * 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
32
33
34
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

35
#include <errno.h>
36
37
38
#include <gst/pbutils/pbutils.h>
#include "ges.h"
#include "ges-internal.h"
39
#include "ges-track-element-asset.h"
40

41
42
#define DEFAULT_DISCOVERY_TIMEOUT (60 * GST_SECOND)

43
static GHashTable *parent_newparent_table = NULL;
44

Guillaume Desmottes's avatar
Guillaume Desmottes committed
45
46
static GstDiscoverer *discoverer = NULL;

47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
static void
initable_iface_init (GInitableIface * initable_iface)
{
  /*  We can not iniate synchronously */
  initable_iface->init = NULL;
}

/* TODO: We should monitor files here, and add some way of reporting changes
 * to user
 */
enum
{
  PROP_0,
  PROP_DURATION,
  PROP_LAST
};
static GParamSpec *properties[PROP_LAST];

static void discoverer_discovered_cb (GstDiscoverer * discoverer,
    GstDiscovererInfo * info, GError * err, gpointer user_data);

68
struct _GESUriClipAssetPrivate
69
70
71
72
{
  GstDiscovererInfo *info;
  GstClockTime duration;
  gboolean is_image;
73
74
75
76

  GList *asset_trackfilesources;
};

77
78
79
80
81
82
83
typedef struct
{
  GMainLoop *ml;
  GESAsset *asset;
  GError *error;
} RequestSyncData;

84
struct _GESUriSourceAssetPrivate
85
86
{
  GstDiscovererStreamInfo *sinfo;
87
  GESUriClipAsset *parent_asset;
88
89

  const gchar *uri;
90
91
};

92
93
94
G_DEFINE_TYPE_WITH_CODE (GESUriClipAsset, ges_uri_clip_asset,
    GES_TYPE_CLIP_ASSET, G_ADD_PRIVATE (GESUriClipAsset)
    G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init));
95
96

static void
97
ges_uri_clip_asset_get_property (GObject * object, guint property_id,
98
99
    GValue * value, GParamSpec * pspec)
{
100
  GESUriClipAssetPrivate *priv = GES_URI_CLIP_ASSET (object)->priv;
101
102
103
104
105
106
107
108
109
110
111

  switch (property_id) {
    case PROP_DURATION:
      g_value_set_uint64 (value, priv->duration);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
  }
}

static void
112
ges_uri_clip_asset_set_property (GObject * object, guint property_id,
113
114
    const GValue * value, GParamSpec * pspec)
{
115
  GESUriClipAssetPrivate *priv = GES_URI_CLIP_ASSET (object)->priv;
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130

  switch (property_id) {
    case PROP_DURATION:
      priv->duration = g_value_get_uint64 (value);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
  }
}

static GESAssetLoadingReturn
_start_loading (GESAsset * asset, GError ** error)
{
  gboolean ret;
  const gchar *uri;
131
  GESUriClipAssetClass *class = GES_URI_CLIP_ASSET_GET_CLASS (asset);
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147

  GST_DEBUG ("Started loading %p", asset);

  uri = ges_asset_get_id (asset);

  ret = gst_discoverer_discover_uri_async (class->discoverer, uri);
  if (ret)
    return GES_ASSET_LOADING_ASYNC;

  return GES_ASSET_LOADING_ERROR;
}

static gboolean
_request_id_update (GESAsset * self, gchar ** proposed_new_id, GError * error)
{
  if (error->domain == GST_RESOURCE_ERROR &&
148
149
      (error->code == GST_RESOURCE_ERROR_NOT_FOUND ||
          error->code == GST_RESOURCE_ERROR_OPEN_READ)) {
150
151
152
153
154
155
156
157
158
159
160
161
    const gchar *uri = ges_asset_get_id (self);
    GFile *parent, *file = g_file_new_for_uri (uri);

    /* Check if we have the new parent in cache */
    parent = g_file_get_parent (file);
    if (parent) {
      GFile *new_parent = g_hash_table_lookup (parent_newparent_table, parent);

      if (new_parent) {
        gchar *basename = g_file_get_basename (file);
        GFile *new_file = g_file_get_child (new_parent, basename);

162
        /* FIXME Handle the GCancellable */
163
        if (g_file_query_exists (new_file, NULL)) {
164
165
166
167
          *proposed_new_id = g_file_get_uri (new_file);
          GST_DEBUG_OBJECT (self, "Proposing path: %s as proxy",
              *proposed_new_id);
        }
168

169
        gst_object_unref (new_file);
170
171
        g_free (basename);
      }
172
      gst_object_unref (parent);
173
174
    }

175
    gst_object_unref (file);
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192

    return TRUE;
  }

  return FALSE;
}

static void
_asset_proxied (GESAsset * self, const gchar * new_uri)
{
  const gchar *uri = ges_asset_get_id (self);
  GFile *parent, *new_parent, *new_file = g_file_new_for_uri (new_uri),
      *file = g_file_new_for_uri (uri);

  parent = g_file_get_parent (file);
  new_parent = g_file_get_parent (new_file);
  g_hash_table_insert (parent_newparent_table, parent, new_parent);
193
194
  gst_object_unref (file);
  gst_object_unref (new_file);
195
196
}

Seungha Yang's avatar
Seungha Yang committed
197
198
199
200
201
202
203
204
205
206
207
208
static void
ges_uri_clip_asset_dispose (GObject * object)
{
  GESUriClipAsset *self = GES_URI_CLIP_ASSET (object);
  GESUriClipAssetPrivate *prif = self->priv;

  if (prif->asset_trackfilesources) {
    g_list_free_full (prif->asset_trackfilesources,
        (GDestroyNotify) gst_object_unref);
    prif->asset_trackfilesources = NULL;
  }

209
210
  gst_clear_object (&prif->info);

Seungha Yang's avatar
Seungha Yang committed
211
212
213
  G_OBJECT_CLASS (ges_uri_clip_asset_parent_class)->dispose (object);
}

214
static void
215
ges_uri_clip_asset_class_init (GESUriClipAssetClass * klass)
216
217
218
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);

219
220
  object_class->get_property = ges_uri_clip_asset_get_property;
  object_class->set_property = ges_uri_clip_asset_set_property;
Seungha Yang's avatar
Seungha Yang committed
221
  object_class->dispose = ges_uri_clip_asset_dispose;
222
223
224
225
226

  GES_ASSET_CLASS (klass)->start_loading = _start_loading;
  GES_ASSET_CLASS (klass)->request_id_update = _request_id_update;
  GES_ASSET_CLASS (klass)->inform_proxy = _asset_proxied;

227
228
  klass->discovered = discoverer_discovered_cb;

229
230

  /**
231
   * GESUriClipAsset:duration:
232
233
234
235
236
237
238
239
240
   *
   * The duration (in nanoseconds) of the media file
   */
  properties[PROP_DURATION] =
      g_param_spec_uint64 ("duration", "Duration", "The duration to use", 0,
      G_MAXUINT64, GST_CLOCK_TIME_NONE, G_PARAM_READWRITE);
  g_object_class_install_property (object_class, PROP_DURATION,
      properties[PROP_DURATION]);

241
  _ges_uri_asset_ensure_setup (klass);
242
243
244
}

static void
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
252
253
254
255

  priv->info = NULL;
  priv->duration = GST_CLOCK_TIME_NONE;
  priv->is_image = FALSE;
}

256
static void
257
_create_uri_source_asset (GESUriClipAsset * asset,
258
259
260
    GstDiscovererStreamInfo * sinfo, GESTrackType type)
{
  GESAsset *tck_filesource_asset;
261
  GESUriSourceAssetPrivate *priv_tckasset;
262
  GESUriClipAssetPrivate *priv = asset->priv;
263
264
265
266
267
268
269
270
271
  gchar *stream_id =
      g_strdup (gst_discoverer_stream_info_get_stream_id (sinfo));

  if (stream_id == NULL) {
    GST_WARNING ("No stream ID found, using the pointer instead");

    stream_id = g_strdup_printf ("%i", GPOINTER_TO_INT (sinfo));
  }

272
273
274
275
276
277
  if (type == GES_TRACK_TYPE_VIDEO)
    tck_filesource_asset = ges_asset_request (GES_TYPE_VIDEO_URI_SOURCE,
        stream_id, NULL);
  else
    tck_filesource_asset = ges_asset_request (GES_TYPE_AUDIO_URI_SOURCE,
        stream_id, NULL);
278
279
  g_free (stream_id);

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,
Seungha Yang's avatar
Seungha Yang committed
288
      tck_filesource_asset);
289
290
}

291
static void
292
ges_uri_clip_asset_set_info (GESUriClipAsset * self, GstDiscovererInfo * info)
293
294
295
296
{
  GList *tmp, *stream_list;

  GESTrackType supportedformats = GES_TRACK_TYPE_UNKNOWN;
297
  GESUriClipAssetPrivate *priv = GES_URI_CLIP_ASSET (self)->priv;
298
299
300
301

  /* Extract infos from the GstDiscovererInfo */
  stream_list = gst_discoverer_info_get_stream_list (info);
  for (tmp = stream_list; tmp; tmp = tmp->next) {
302
    GESTrackType type = GES_TRACK_TYPE_UNKNOWN;
303
304
305
306
307
308
309
310
    GstDiscovererStreamInfo *sinf = (GstDiscovererStreamInfo *) tmp->data;

    if (GST_IS_DISCOVERER_AUDIO_INFO (sinf)) {
      if (supportedformats == GES_TRACK_TYPE_UNKNOWN)
        supportedformats = GES_TRACK_TYPE_AUDIO;
      else
        supportedformats |= GES_TRACK_TYPE_AUDIO;

311
      type = GES_TRACK_TYPE_AUDIO;
312
313
314
315
316
317
    } else if (GST_IS_DISCOVERER_VIDEO_INFO (sinf)) {
      if (supportedformats == GES_TRACK_TYPE_UNKNOWN)
        supportedformats = GES_TRACK_TYPE_VIDEO;
      else
        supportedformats |= GES_TRACK_TYPE_VIDEO;
      if (gst_discoverer_video_info_is_image ((GstDiscovererVideoInfo *)
318
              sinf))
319
        priv->is_image = TRUE;
320
321
322
      type = GES_TRACK_TYPE_VIDEO;
    }

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
331
332
333
334
335
336

  if (stream_list)
    gst_discoverer_stream_info_list_free (stream_list);

  if (priv->is_image == FALSE)
    priv->duration = gst_discoverer_info_get_duration (info);
  /* else we keep #GST_CLOCK_TIME_NONE */

337
  priv->info = gst_object_ref (info);
338
339
}

340
341
342
343
static void
_set_meta_file_size (const gchar * uri, GESUriClipAsset * asset)
{
  GError *error = NULL;
Edward Hervey's avatar
Edward Hervey committed
344
  GFileInfo *file_info = NULL;
345
  guint64 file_size;
Edward Hervey's avatar
Edward Hervey committed
346
  GFile *gfile = NULL;
347
348
349

  GESMetaContainer *container = GES_META_CONTAINER (asset);

Edward Hervey's avatar
Edward Hervey committed
350
351
  gfile = g_file_new_for_uri (uri);
  file_info = g_file_query_info (gfile, "standard::size",
352
353
354
355
356
357
358
359
      G_FILE_QUERY_INFO_NONE, NULL, &error);
  if (!error) {
    file_size = g_file_info_get_attribute_uint64 (file_info, "standard::size");
    ges_meta_container_register_meta_uint64 (container, GES_META_READ_WRITE,
        "file-size", file_size);
  } else {
    g_error_free (error);
  }
Edward Hervey's avatar
Edward Hervey committed
360
361
362
363
  if (gfile)
    g_object_unref (gfile);
  if (file_info)
    g_object_unref (file_info);
364
365
}

366
367
368
369
370
371
static void
_set_meta_foreach (const GstTagList * tags, const gchar * tag,
    GESMetaContainer * container)
{
  GValue value = { 0 };

372
373
374
375
376
377
  if (gst_tag_list_copy_value (&value, tags, tag)) {
    ges_meta_container_set_meta (container, tag, &value);
    g_value_unset (&value);
  } else {
    GST_INFO ("Could not set metadata: %s", tag);
  }
378
379
380
381
382
383
}

static void
discoverer_discovered_cb (GstDiscoverer * discoverer,
    GstDiscovererInfo * info, GError * err, gpointer user_data)
{
384
  GError *error = NULL;
385
386
387
  const GstTagList *tags;

  const gchar *uri = gst_discoverer_info_get_uri (info);
388
389
  GESUriClipAsset *mfs =
      GES_URI_CLIP_ASSET (ges_asset_cache_lookup (GES_TYPE_URI_CLIP, uri));
390
391
392
393
394

  tags = gst_discoverer_info_get_tags (info);
  if (tags)
    gst_tag_list_foreach (tags, (GstTagForeachFunc) _set_meta_foreach, mfs);

395
396
  _set_meta_file_size (uri, mfs);

397
  if (gst_discoverer_info_get_result (info) == GST_DISCOVERER_OK) {
398
    ges_uri_clip_asset_set_info (mfs, info);
399
400
401
402
403
404
405
406
  } else {
    if (err) {
      error = g_error_copy (err);
    } else {
      error = g_error_new (GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_FAILED,
          "Stream %s discovering failed (error code: %d)",
          uri, gst_discoverer_info_get_result (info));
    }
407
408
  }

409
410
411
412
  ges_asset_cache_set_loaded (GES_TYPE_URI_CLIP, uri, error);

  if (error)
    g_error_free (error);
413
414
}

415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
static void
asset_ready_cb (GESAsset * source, GAsyncResult * res, RequestSyncData * data)
{
  data->asset = ges_asset_request_finish (res, &data->error);

  if (data->error) {
    gchar *possible_uri = ges_uri_asset_try_update_id (data->error, source);

    if (possible_uri) {
      g_clear_error (&data->error);
      ges_asset_request_async (GES_TYPE_URI_CLIP, possible_uri, NULL,
          (GAsyncReadyCallback) asset_ready_cb, data);
      g_free (possible_uri);

      return;
    }
  }
  g_main_loop_quit (data->ml);
}

435
436
/* API implementation */
/**
437
 * ges_uri_clip_asset_get_info:
438
439
440
441
442
443
444
 * @self: Target asset
 *
 * Gets #GstDiscovererInfo about the file
 *
 * Returns: (transfer none): #GstDiscovererInfo of specified asset
 */
GstDiscovererInfo *
445
ges_uri_clip_asset_get_info (const GESUriClipAsset * self)
446
{
447
448
  g_return_val_if_fail (GES_IS_URI_CLIP_ASSET (self), NULL);

449
450
451
452
  return self->priv->info;
}

/**
453
454
 * ges_uri_clip_asset_get_duration:
 * @self: a #GESUriClipAsset
455
456
457
458
459
460
 *
 * Gets duration of the file represented by @self
 *
 * Returns: The duration of @self
 */
GstClockTime
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
466
467
468

  return self->priv->duration;
}

/**
469
 * ges_uri_clip_asset_is_image:
Thibault Saunier's avatar
Thibault Saunier committed
470
 * @self: a #GESUriClipAsset
471
472
473
474
475
476
 *
 * Gets Whether the file represented by @self is an image or not
 *
 * Returns: Whether the file represented by @self is an image or not
 */
gboolean
477
ges_uri_clip_asset_is_image (GESUriClipAsset * self)
478
{
479
  g_return_val_if_fail (GES_IS_URI_CLIP_ASSET (self), FALSE);
480
481
482
483
484

  return self->priv->is_image;
}

/**
485
486
 * ges_uri_clip_asset_new:
 * @uri: The URI of the file for which to create a #GESUriClipAsset
487
488
489
490
 * @cancellable: optional %GCancellable object, %NULL to ignore.
 * @callback: (scope async): a #GAsyncReadyCallback to call when the initialization is finished
 * @user_data: The user data to pass when @callback is called
 *
491
 * Creates a #GESUriClipAsset for @uri
492
 *
493
 * Example of request of a GESUriClipAsset:
494
495
496
497
498
499
 * |[
 * // The request callback
 * static void
 * filesource_asset_loaded_cb (GESAsset * source, GAsyncResult * res, gpointer user_data)
 * {
 *   GError *error = NULL;
500
 *   GESUriClipAsset *filesource_asset;
501
 *
502
 *   filesource_asset = ges_uri_clip_asset_finish (res, &error);
503
504
505
 *   if (filesource_asset) {
 *    g_print ("The file: %s is usable as a FileSource, it is%s an image and lasts %" GST_TIME_FORMAT,
 *        ges_asset_get_id (GES_ASSET (filesource_asset))
506
507
 *        ges_uri_clip_asset_is_image (filesource_asset) ? "" : " not",
 *        GST_TIME_ARGS (ges_uri_clip_asset_get_duration (filesource_asset));
508
509
510
511
512
 *   } else {
 *    g_print ("The file: %s is *not* usable as a FileSource because: %s",
 *        ges_asset_get_id (source), error->message);
 *   }
 *
513
 *   gst_object_unref (mfs);
514
515
516
 * }
 *
 * // The request:
517
 * ges_uri_clip_asset_new (uri, (GAsyncReadyCallback) filesource_asset_loaded_cb, user_data);
518
519
520
 * ]|
 */
void
521
ges_uri_clip_asset_new (const gchar * uri, GCancellable * cancellable,
522
523
    GAsyncReadyCallback callback, gpointer user_data)
{
524
  ges_asset_request_async (GES_TYPE_URI_CLIP, uri, cancellable,
525
526
527
      callback, user_data);
}

528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
/**
 * ges_uri_clip_asset_finish:
 * @res: The #GAsyncResult from which to get the newly created #GESUriClipAsset
 * @error: An error to be set in case something wrong happens or %NULL
 *
 * Finalize the request of an async #GESUriClipAsset
 *
 * Returns: (transfer full): The #GESUriClipAsset previously requested
 */
GESUriClipAsset *
ges_uri_clip_asset_finish (GAsyncResult * res, GError ** error)
{
  GESAsset *asset;

  g_return_val_if_fail (G_IS_ASYNC_RESULT (res), NULL);

  asset = ges_asset_request_finish (res, error);
  if (asset != NULL) {
    return GES_URI_CLIP_ASSET (asset);
  }

  return NULL;
}

552
/**
553
 * ges_uri_clip_asset_request_sync:
Lubosz Sarnecki's avatar
Lubosz Sarnecki committed
554
555
 * @uri: The URI of the file for which to create a #GESUriClipAsset.
 * You can also use multi file uris for #GESMultiFileSource.
556
 * @error: An error to be set in case something wrong happens or %NULL
557
558
559
560
 *
 * Creates a #GESUriClipAsset for @uri syncronously. You should avoid
 * to use it in application, and rather create #GESUriClipAsset asynchronously
 *
561
562
 * Returns: (transfer full): A reference to the requested asset or %NULL if
 * an error happened
563
564
565
566
567
568
 */
GESUriClipAsset *
ges_uri_clip_asset_request_sync (const gchar * uri, GError ** error)
{
  GError *lerror = NULL;
  GESUriClipAsset *asset;
569
  RequestSyncData data = { 0, };
570
571
572
  GESUriClipAssetClass *klass = g_type_class_peek (GES_TYPE_URI_CLIP_ASSET);
  GstClockTime timeout;
  GstDiscoverer *previous_discoverer = klass->discoverer;
573
574
575
576
577
578
579

  asset = GES_URI_CLIP_ASSET (ges_asset_request (GES_TYPE_URI_CLIP, uri,
          &lerror));

  if (asset)
    return asset;

580
  data.ml = g_main_loop_new (NULL, TRUE);
581
582
583
584
585
586
587
588
589
590
591
  g_object_get (previous_discoverer, "timeout", &timeout, NULL);
  klass->discoverer = gst_discoverer_new (timeout, error);
  if (!klass->discoverer) {
    klass->discoverer = previous_discoverer;

    return NULL;
  }

  g_signal_connect (klass->discoverer, "discovered",
      G_CALLBACK (klass->discovered), NULL);
  gst_discoverer_start (klass->discoverer);
592
593
594
595
  ges_asset_request_async (GES_TYPE_URI_CLIP, uri, NULL,
      (GAsyncReadyCallback) asset_ready_cb, &data);
  g_main_loop_run (data.ml);
  g_main_loop_unref (data.ml);
596

597
598
599
  gst_object_unref (klass->discoverer);
  klass->discoverer = previous_discoverer;

600
601
602
603
  if (data.error) {
    GST_ERROR ("Got an error requesting asset: %s", data.error->message);
    if (error != NULL)
      g_propagate_error (error, data.error);
604
605
606
607

    return NULL;
  }

608
  return GES_URI_CLIP_ASSET (data.asset);
609
610
}

611
/**
612
 * ges_uri_clip_asset_class_set_timeout:
Lubosz Sarnecki's avatar
Lubosz Sarnecki committed
613
 * @klass: The #GESUriClipAssetClass on which to set the discoverer timeout
614
615
 * @timeout: The timeout to set
 *
616
 * Sets the timeout of #GESUriClipAsset loading
617
618
 */
void
Lubosz Sarnecki's avatar
Lubosz Sarnecki committed
619
ges_uri_clip_asset_class_set_timeout (GESUriClipAssetClass * klass,
620
621
    GstClockTime timeout)
{
Lubosz Sarnecki's avatar
Lubosz Sarnecki committed
622
  g_return_if_fail (GES_IS_URI_CLIP_ASSET_CLASS (klass));
623

Lubosz Sarnecki's avatar
Lubosz Sarnecki committed
624
  g_object_set (klass->discoverer, "timeout", timeout, NULL);
625
}
626
627

/**
628
629
 * ges_uri_clip_asset_get_stream_assets:
 * @self: A #GESUriClipAsset
630
 *
631
 * Get the GESUriSourceAsset @self containes
632
 *
633
634
 * Returns: (transfer none) (element-type GESUriSourceAsset): a
 * #GList of #GESUriSourceAsset
635
636
 */
const GList *
637
ges_uri_clip_asset_get_stream_assets (GESUriClipAsset * self)
638
{
639
  g_return_val_if_fail (GES_IS_URI_CLIP_ASSET (self), FALSE);
640
641
642
643
644

  return self->priv->asset_trackfilesources;
}

/*****************************************************************
645
 *            GESUriSourceAsset implementation             *
646
647
 *****************************************************************/
/**
648
 * SECTION: gesurisourceasset
649
 * @title: GESUriClipAsset
650
 * @short_description: A GESAsset subclass specialized in GESUriSource extraction
651
652
 *
 * NOTE: You should never request such a #GESAsset as they will be created automatically
653
 * by #GESUriClipAsset-s.
654
655
 */

656
G_DEFINE_TYPE_WITH_PRIVATE (GESUriSourceAsset, ges_uri_source_asset,
657
    GES_TYPE_TRACK_ELEMENT_ASSET);
658
659
660
661

static GESExtractable *
_extract (GESAsset * asset, GError ** error)
{
Justin Kim's avatar
Justin Kim committed
662
  gchar *uri = NULL;
663
  GESTrackElement *trackelement;
664
  GESUriSourceAssetPrivate *priv = GES_URI_SOURCE_ASSET (asset)->priv;
665
666
667
668
669
670
671
672
673
674
675
676
677

  if (GST_IS_DISCOVERER_STREAM_INFO (priv->sinfo) == FALSE) {
    GST_WARNING_OBJECT (asset, "Can not extract as no strean info set");

    return NULL;
  }

  if (priv->uri == NULL) {
    GST_WARNING_OBJECT (asset, "Can not extract as no uri set");

    return NULL;
  }

Justin Kim's avatar
Justin Kim committed
678
679
  uri = g_strdup (priv->uri);

Lubosz Sarnecki's avatar
Lubosz Sarnecki committed
680
  if (g_str_has_prefix (priv->uri, GES_MULTI_FILE_URI_PREFIX)) {
Justin Kim's avatar
Justin Kim committed
681
    trackelement = GES_TRACK_ELEMENT (ges_multi_file_source_new (uri));
Lubosz Sarnecki's avatar
Lubosz Sarnecki committed
682
683
  } else if (GST_IS_DISCOVERER_VIDEO_INFO (priv->sinfo)
      && gst_discoverer_video_info_is_image ((GstDiscovererVideoInfo *)
684
          priv->sinfo))
Justin Kim's avatar
Justin Kim committed
685
    trackelement = GES_TRACK_ELEMENT (ges_image_source_new (uri));
686
  else if (GST_IS_DISCOVERER_VIDEO_INFO (priv->sinfo))
Justin Kim's avatar
Justin Kim committed
687
    trackelement = GES_TRACK_ELEMENT (ges_video_uri_source_new (uri));
688
  else
Justin Kim's avatar
Justin Kim committed
689
    trackelement = GES_TRACK_ELEMENT (ges_audio_uri_source_new (uri));
690

691
692
  ges_track_element_set_track_type (trackelement,
      ges_track_element_asset_get_track_type (GES_TRACK_ELEMENT_ASSET (asset)));
693

Justin Kim's avatar
Justin Kim committed
694
695
  g_free (uri);

696
  return GES_EXTRACTABLE (trackelement);
697
698
}

699
700
701
702
703
704
705
706
707
708
709
static void
ges_uri_source_asset_dispose (GObject * object)
{
  GESUriSourceAsset *self = GES_URI_SOURCE_ASSET (object);
  GESUriSourceAssetPrivate *priv = self->priv;

  gst_clear_object (&priv->sinfo);

  G_OBJECT_CLASS (ges_uri_source_asset_parent_class)->dispose (object);
}

710
static void
711
ges_uri_source_asset_class_init (GESUriSourceAssetClass * klass)
712
{
713
714
715
716
  GObjectClass *object_class = G_OBJECT_CLASS (klass);

  object_class->dispose = ges_uri_source_asset_dispose;

717
718
719
720
  GES_ASSET_CLASS (klass)->extract = _extract;
}

static void
721
ges_uri_source_asset_init (GESUriSourceAsset * self)
722
{
723
  GESUriSourceAssetPrivate *priv;
724

725
  priv = self->priv = ges_uri_source_asset_get_instance_private (self);
726
727
728
729
730
731

  priv->sinfo = NULL;
  priv->parent_asset = NULL;
  priv->uri = NULL;
}

Thibault Saunier's avatar
Thibault Saunier committed
732
/**
733
 * ges_uri_source_asset_get_stream_info:
734
 * @asset: A #GESUriClipAsset
Thibault Saunier's avatar
Thibault Saunier committed
735
736
737
 *
 * Get the #GstDiscovererStreamInfo user by @asset
 *
738
 * Returns: (transfer none): a #GESUriClipAsset
Thibault Saunier's avatar
Thibault Saunier committed
739
 */
740
GstDiscovererStreamInfo *
741
ges_uri_source_asset_get_stream_info (GESUriSourceAsset * asset)
742
{
743
  g_return_val_if_fail (GES_IS_URI_SOURCE_ASSET (asset), NULL);
744
745
746
747
748

  return asset->priv->sinfo;
}

const gchar *
749
ges_uri_source_asset_get_stream_uri (GESUriSourceAsset * asset)
750
{
751
  g_return_val_if_fail (GES_IS_URI_SOURCE_ASSET (asset), NULL);
752
753
754
755
756

  return asset->priv->uri;
}

/**
757
 * ges_uri_source_asset_get_filesource_asset:
758
 * @asset: A #GESUriClipAsset
759
 *
760
 * Get the #GESUriClipAsset @self is contained in
761
 *
762
 * Returns: a #GESUriClipAsset
763
 */
764
const GESUriClipAsset *
765
ges_uri_source_asset_get_filesource_asset (GESUriSourceAsset * asset)
766
{
767
  g_return_val_if_fail (GES_IS_URI_SOURCE_ASSET (asset), NULL);
768
769
770

  return asset->priv->parent_asset;
}
Guillaume Desmottes's avatar
Guillaume Desmottes committed
771
772
773
774

void
_ges_uri_asset_cleanup (void)
{
775
776
  if (discoverer)
    gst_discoverer_stop (discoverer);
Guillaume Desmottes's avatar
Guillaume Desmottes committed
777
  g_clear_object (&discoverer);
778
779
780
781
  if (parent_newparent_table) {
    g_hash_table_destroy (parent_newparent_table);
    parent_newparent_table = NULL;
  }
Guillaume Desmottes's avatar
Guillaume Desmottes committed
782
}
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818

gboolean
_ges_uri_asset_ensure_setup (gpointer uriasset_class)
{
  GESUriClipAssetClass *klass;
  GError *err;
  GstClockTime timeout;
  const gchar *timeout_str;

  g_return_val_if_fail (GES_IS_URI_CLIP_ASSET_CLASS (uriasset_class), FALSE);

  klass = GES_URI_CLIP_ASSET_CLASS (uriasset_class);

  timeout = DEFAULT_DISCOVERY_TIMEOUT;
  errno = 0;
  timeout_str = g_getenv ("GES_DISCOVERY_TIMEOUT");
  if (timeout_str)
    timeout = g_ascii_strtod (timeout_str, NULL) * GST_SECOND;
  else
    errno = 10;

  if (errno)
    timeout = DEFAULT_DISCOVERY_TIMEOUT;

  if (!discoverer) {
    discoverer = gst_discoverer_new (timeout, &err);
    if (!discoverer) {
      GST_ERROR ("Could not create discoverer: %s", err->message);
      g_error_free (err);
      return FALSE;
    }
  }

  /* The class structure keeps weak pointers on the discoverers so they
   * can be properly cleaned up in _ges_uri_asset_cleanup(). */
  if (!klass->discoverer) {
819
    klass->discoverer = klass->sync_discoverer = discoverer;
820
821
    g_object_add_weak_pointer (G_OBJECT (discoverer),
        (gpointer *) & klass->discoverer);
822
823
    g_object_add_weak_pointer (G_OBJECT (discoverer),
        (gpointer *) & klass->sync_discoverer);
824
825

    g_signal_connect (klass->discoverer, "discovered",
826
        G_CALLBACK (klass->discovered), NULL);
827
828
829
830
831
832
833
834
835
836
837
  }

  /* We just start the discoverer and let it live */
  gst_discoverer_start (klass->discoverer);
  if (parent_newparent_table == NULL) {
    parent_newparent_table = g_hash_table_new_full (g_file_hash,
        (GEqualFunc) g_file_equal, g_object_unref, g_object_unref);
  }

  return TRUE;
}