ges-uri-asset.c 22.8 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:
470
 * @self: a #indent: Standard input:311: Error:Unexpected end of file
471
GESUriClipAsset
472
473
474
475
476
477
 *
 * 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
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
483
484
485

  return self->priv->is_image;
}

/**
486
487
 * ges_uri_clip_asset_new:
 * @uri: The URI of the file for which to create a #GESUriClipAsset
488
489
490
491
 * @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
 *
492
 * Creates a #GESUriClipAsset for @uri
493
 *
494
 * Example of request of a GESUriClipAsset:
495
496
497
498
499
500
 * |[
 * // The request callback
 * static void
 * filesource_asset_loaded_cb (GESAsset * source, GAsyncResult * res, gpointer user_data)
 * {
 *   GError *error = NULL;
501
 *   GESUriClipAsset *filesource_asset;
502
 *
503
 *   filesource_asset = ges_uri_clip_asset_finish (res, &error);
504
505
506
 *   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))
507
508
 *        ges_uri_clip_asset_is_image (filesource_asset) ? "" : " not",
 *        GST_TIME_ARGS (ges_uri_clip_asset_get_duration (filesource_asset));
509
510
511
512
513
 *   } else {
 *    g_print ("The file: %s is *not* usable as a FileSource because: %s",
 *        ges_asset_get_id (source), error->message);
 *   }
 *
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
522
ges_uri_clip_asset_new (const gchar * uri, GCancellable * cancellable,
523
524
    GAsyncReadyCallback callback, gpointer user_data)
{
525
  ges_asset_request_async (GES_TYPE_URI_CLIP, uri, cancellable,
526
527
528
      callback, user_data);
}

529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
/**
 * 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;
}

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

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

  if (asset)
    return asset;

578
579
580
581
582
  data.ml = g_main_loop_new (NULL, TRUE);
  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);
583

584
585
586
587
  if (data.error) {
    GST_ERROR ("Got an error requesting asset: %s", data.error->message);
    if (error != NULL)
      g_propagate_error (error, data.error);
588
589
590
591

    return NULL;
  }

592
  return GES_URI_CLIP_ASSET (data.asset);
593
594
}

595
/**
596
 * ges_uri_clip_asset_class_set_timeout:
Lubosz Sarnecki's avatar
Lubosz Sarnecki committed
597
 * @klass: The #GESUriClipAssetClass on which to set the discoverer timeout
598
599
 * @timeout: The timeout to set
 *
600
 * Sets the timeout of #GESUriClipAsset loading
601
602
 */
void
Lubosz Sarnecki's avatar
Lubosz Sarnecki committed
603
ges_uri_clip_asset_class_set_timeout (GESUriClipAssetClass * klass,
604
605
    GstClockTime timeout)
{
Lubosz Sarnecki's avatar
Lubosz Sarnecki committed
606
  g_return_if_fail (GES_IS_URI_CLIP_ASSET_CLASS (klass));
607

Lubosz Sarnecki's avatar
Lubosz Sarnecki committed
608
  g_object_set (klass->discoverer, "timeout", timeout, NULL);
609
}
610
611

/**
612
613
 * ges_uri_clip_asset_get_stream_assets:
 * @self: A #GESUriClipAsset
614
 *
615
 * Get the GESUriSourceAsset @self containes
616
 *
617
618
 * Returns: (transfer none) (element-type GESUriSourceAsset): a
 * #GList of #GESUriSourceAsset
619
620
 */
const GList *
621
ges_uri_clip_asset_get_stream_assets (GESUriClipAsset * self)
622
{
623
  g_return_val_if_fail (GES_IS_URI_CLIP_ASSET (self), FALSE);
624
625
626
627
628

  return self->priv->asset_trackfilesources;
}

/*****************************************************************
629
 *            GESUriSourceAsset implementation             *
630
631
 *****************************************************************/
/**
632
 * SECTION: gesurisourceasset
633
 * @title: GESUriClipAsset
634
 * @short_description: A GESAsset subclass specialized in GESUriSource extraction
635
636
 *
 * NOTE: You should never request such a #GESAsset as they will be created automatically
637
 * by #GESUriClipAsset-s.
638
639
 */

640
G_DEFINE_TYPE_WITH_PRIVATE (GESUriSourceAsset, ges_uri_source_asset,
641
    GES_TYPE_TRACK_ELEMENT_ASSET);
642
643
644
645

static GESExtractable *
_extract (GESAsset * asset, GError ** error)
{
Justin Kim's avatar
Justin Kim committed
646
  gchar *uri = NULL;
647
  GESTrackElement *trackelement;
648
  GESUriSourceAssetPrivate *priv = GES_URI_SOURCE_ASSET (asset)->priv;
649
650
651
652
653
654
655
656
657
658
659
660
661

  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
662
663
  uri = g_strdup (priv->uri);

Lubosz Sarnecki's avatar
Lubosz Sarnecki committed
664
  if (g_str_has_prefix (priv->uri, GES_MULTI_FILE_URI_PREFIX)) {
Justin Kim's avatar
Justin Kim committed
665
    trackelement = GES_TRACK_ELEMENT (ges_multi_file_source_new (uri));
Lubosz Sarnecki's avatar
Lubosz Sarnecki committed
666
667
  } else if (GST_IS_DISCOVERER_VIDEO_INFO (priv->sinfo)
      && gst_discoverer_video_info_is_image ((GstDiscovererVideoInfo *)
668
          priv->sinfo))
Justin Kim's avatar
Justin Kim committed
669
    trackelement = GES_TRACK_ELEMENT (ges_image_source_new (uri));
670
  else if (GST_IS_DISCOVERER_VIDEO_INFO (priv->sinfo))
Justin Kim's avatar
Justin Kim committed
671
    trackelement = GES_TRACK_ELEMENT (ges_video_uri_source_new (uri));
672
  else
Justin Kim's avatar
Justin Kim committed
673
    trackelement = GES_TRACK_ELEMENT (ges_audio_uri_source_new (uri));
674

675
676
  ges_track_element_set_track_type (trackelement,
      ges_track_element_asset_get_track_type (GES_TRACK_ELEMENT_ASSET (asset)));
677

Justin Kim's avatar
Justin Kim committed
678
679
  g_free (uri);

680
  return GES_EXTRACTABLE (trackelement);
681
682
}

683
684
685
686
687
688
689
690
691
692
693
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);
}

694
static void
695
ges_uri_source_asset_class_init (GESUriSourceAssetClass * klass)
696
{
697
698
699
700
  GObjectClass *object_class = G_OBJECT_CLASS (klass);

  object_class->dispose = ges_uri_source_asset_dispose;

701
702
703
704
  GES_ASSET_CLASS (klass)->extract = _extract;
}

static void
705
ges_uri_source_asset_init (GESUriSourceAsset * self)
706
{
707
  GESUriSourceAssetPrivate *priv;
708

709
  priv = self->priv = ges_uri_source_asset_get_instance_private (self);
710
711
712
713
714
715

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

Thibault Saunier's avatar
Thibault Saunier committed
716
/**
717
 * ges_uri_source_asset_get_stream_info:
718
 * @asset: A #GESUriClipAsset
Thibault Saunier's avatar
Thibault Saunier committed
719
720
721
 *
 * Get the #GstDiscovererStreamInfo user by @asset
 *
722
 * Returns: (transfer none): a #GESUriClipAsset
Thibault Saunier's avatar
Thibault Saunier committed
723
 */
724
GstDiscovererStreamInfo *
725
ges_uri_source_asset_get_stream_info (GESUriSourceAsset * asset)
726
{
727
  g_return_val_if_fail (GES_IS_URI_SOURCE_ASSET (asset), NULL);
728
729
730
731
732

  return asset->priv->sinfo;
}

const gchar *
733
ges_uri_source_asset_get_stream_uri (GESUriSourceAsset * asset)
734
{
735
  g_return_val_if_fail (GES_IS_URI_SOURCE_ASSET (asset), NULL);
736
737
738
739
740

  return asset->priv->uri;
}

/**
741
 * ges_uri_source_asset_get_filesource_asset:
742
 * @asset: A #GESUriClipAsset
743
 *
744
 * Get the #GESUriClipAsset @self is contained in
745
 *
746
 * Returns: a #GESUriClipAsset
747
 */
748
const GESUriClipAsset *
749
ges_uri_source_asset_get_filesource_asset (GESUriSourceAsset * asset)
750
{
751
  g_return_val_if_fail (GES_IS_URI_SOURCE_ASSET (asset), NULL);
752
753
754

  return asset->priv->parent_asset;
}
Guillaume Desmottes's avatar
Guillaume Desmottes committed
755
756
757
758
759

void
_ges_uri_asset_cleanup (void)
{
  g_clear_object (&discoverer);
760
761
762
763
  if (parent_newparent_table) {
    g_hash_table_destroy (parent_newparent_table);
    parent_newparent_table = NULL;
  }
Guillaume Desmottes's avatar
Guillaume Desmottes committed
764
}
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800

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) {
801
    klass->discoverer = klass->sync_discoverer = discoverer;
802
803
    g_object_add_weak_pointer (G_OBJECT (discoverer),
        (gpointer *) & klass->discoverer);
804
805
    g_object_add_weak_pointer (G_OBJECT (discoverer),
        (gpointer *) & klass->sync_discoverer);
806
807

    g_signal_connect (klass->discoverer, "discovered",
808
        G_CALLBACK (klass->discovered), NULL);
809
810
811
812
813
814
815
816
817
818
819
  }

  /* 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;
}