ges-uri-asset.c 24.5 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

45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
G_LOCK_DEFINE_STATIC (discoverers_lock);
static GstClockTime discovering_timeout = DEFAULT_DISCOVERY_TIMEOUT;
static GHashTable *discoverers = NULL;  /* Thread ID -> GstDiscoverer */
static void discoverer_discovered_cb (GstDiscoverer * discoverer,
    GstDiscovererInfo * info, GError * err, gpointer user_data);

/* WITH discoverers_lock */
static GstDiscoverer *
create_discoverer ()
{
  GstDiscoverer *disco = gst_discoverer_new (discovering_timeout, NULL);

  g_signal_connect (disco, "discovered", G_CALLBACK (discoverer_discovered_cb),
      NULL);
  GST_INFO_OBJECT (disco, "Creating new discoverer");
  g_hash_table_insert (discoverers, g_thread_self (), disco);
  gst_discoverer_start (disco);

  return disco;
}

static GstDiscoverer *
get_discoverer ()
{
  GstDiscoverer *disco;

  G_LOCK (discoverers_lock);
  g_assert (discoverers);
  disco = g_hash_table_lookup (discoverers, g_thread_self ());
  if (!disco) {
    disco = create_discoverer ();
  }
  disco = gst_object_ref (disco);
  G_UNLOCK (discoverers_lock);

  return disco;
}
Guillaume Desmottes's avatar
Guillaume Desmottes committed
82

83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
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];

101
struct _GESUriClipAssetPrivate
102 103 104 105
{
  GstDiscovererInfo *info;
  GstClockTime duration;
  gboolean is_image;
106 107 108 109

  GList *asset_trackfilesources;
};

110 111 112 113 114 115 116
typedef struct
{
  GMainLoop *ml;
  GESAsset *asset;
  GError *error;
} RequestSyncData;

117
struct _GESUriSourceAssetPrivate
118 119
{
  GstDiscovererStreamInfo *sinfo;
120
  GESUriClipAsset *parent_asset;
121 122

  const gchar *uri;
123 124
};

125 126 127
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));
128 129

static void
130
ges_uri_clip_asset_get_property (GObject * object, guint property_id,
131 132
    GValue * value, GParamSpec * pspec)
{
133
  GESUriClipAssetPrivate *priv = GES_URI_CLIP_ASSET (object)->priv;
134 135 136 137 138 139 140 141 142 143 144

  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
145
ges_uri_clip_asset_set_property (GObject * object, guint property_id,
146 147
    const GValue * value, GParamSpec * pspec)
{
148
  GESUriClipAssetPrivate *priv = GES_URI_CLIP_ASSET (object)->priv;
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163

  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;
164
  GstDiscoverer *discoverer = get_discoverer ();
165 166

  uri = ges_asset_get_id (asset);
167 168 169 170
  GST_DEBUG_OBJECT (discoverer, "Started loading %s", uri);

  ret = gst_discoverer_discover_uri_async (discoverer, uri);
  gst_object_unref (discoverer);
171 172 173 174 175 176 177 178 179 180 181

  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 &&
182 183
      (error->code == GST_RESOURCE_ERROR_NOT_FOUND ||
          error->code == GST_RESOURCE_ERROR_OPEN_READ)) {
184 185 186 187 188 189 190 191 192 193 194 195
    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);

196
        /* FIXME Handle the GCancellable */
197
        if (g_file_query_exists (new_file, NULL)) {
198 199 200 201
          *proposed_new_id = g_file_get_uri (new_file);
          GST_DEBUG_OBJECT (self, "Proposing path: %s as proxy",
              *proposed_new_id);
        }
202

203
        gst_object_unref (new_file);
204 205
        g_free (basename);
      }
206
      gst_object_unref (parent);
207 208
    }

209
    gst_object_unref (file);
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226

    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);
227 228
  gst_object_unref (file);
  gst_object_unref (new_file);
229 230
}

Seungha Yang's avatar
Seungha Yang committed
231 232 233 234 235 236 237 238 239 240 241 242
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;
  }

243 244
  gst_clear_object (&prif->info);

Seungha Yang's avatar
Seungha Yang committed
245 246 247
  G_OBJECT_CLASS (ges_uri_clip_asset_parent_class)->dispose (object);
}

248
static void
249
ges_uri_clip_asset_class_init (GESUriClipAssetClass * klass)
250 251 252
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);

253 254
  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
255
  object_class->dispose = ges_uri_clip_asset_dispose;
256 257 258 259 260

  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;

261 262
  klass->discovered = discoverer_discovered_cb;

263 264

  /**
265
   * GESUriClipAsset:duration:
266 267 268 269 270 271 272 273 274
   *
   * 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]);

275
  _ges_uri_asset_ensure_setup (klass);
276 277 278
}

static void
279
ges_uri_clip_asset_init (GESUriClipAsset * self)
280
{
281
  GESUriClipAssetPrivate *priv;
282

283
  priv = self->priv = ges_uri_clip_asset_get_instance_private (self);
284 285 286 287 288 289

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

290
static void
291
_create_uri_source_asset (GESUriClipAsset * asset,
292 293 294
    GstDiscovererStreamInfo * sinfo, GESTrackType type)
{
  GESAsset *tck_filesource_asset;
295
  GESUriSourceAssetPrivate *priv_tckasset;
296
  GESUriClipAssetPrivate *priv = asset->priv;
297 298 299 300 301 302 303 304 305
  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));
  }

306 307 308 309 310 311
  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);
312 313
  g_free (stream_id);

314
  priv_tckasset = GES_URI_SOURCE_ASSET (tck_filesource_asset)->priv;
315
  priv_tckasset->uri = ges_asset_get_id (GES_ASSET (asset));
316
  priv_tckasset->sinfo = gst_object_ref (sinfo);
317
  priv_tckasset->parent_asset = asset;
318
  ges_track_element_asset_set_track_type (GES_TRACK_ELEMENT_ASSET
319
      (tck_filesource_asset), type);
320 321

  priv->asset_trackfilesources = g_list_append (priv->asset_trackfilesources,
Seungha Yang's avatar
Seungha Yang committed
322
      tck_filesource_asset);
323 324
}

325
static void
326
ges_uri_clip_asset_set_info (GESUriClipAsset * self, GstDiscovererInfo * info)
327 328 329 330
{
  GList *tmp, *stream_list;

  GESTrackType supportedformats = GES_TRACK_TYPE_UNKNOWN;
331
  GESUriClipAssetPrivate *priv = GES_URI_CLIP_ASSET (self)->priv;
332 333 334 335

  /* Extract infos from the GstDiscovererInfo */
  stream_list = gst_discoverer_info_get_stream_list (info);
  for (tmp = stream_list; tmp; tmp = tmp->next) {
336
    GESTrackType type = GES_TRACK_TYPE_UNKNOWN;
337 338 339 340 341 342 343 344
    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;

345
      type = GES_TRACK_TYPE_AUDIO;
346 347 348 349 350 351
    } 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 *)
352
              sinf))
353
        priv->is_image = TRUE;
354 355 356
      type = GES_TRACK_TYPE_VIDEO;
    }

357
    GST_DEBUG_OBJECT (self, "Creating GESUriSourceAsset for stream: %s",
358
        gst_discoverer_stream_info_get_stream_id (sinf));
359
    _create_uri_source_asset (self, sinf, type);
360
  }
361
  ges_clip_asset_set_supported_formats (GES_CLIP_ASSET
362
      (self), supportedformats);
363 364 365 366 367 368 369 370

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

371
  priv->info = gst_object_ref (info);
372 373
}

374 375 376 377
static void
_set_meta_file_size (const gchar * uri, GESUriClipAsset * asset)
{
  GError *error = NULL;
Edward Hervey's avatar
Edward Hervey committed
378
  GFileInfo *file_info = NULL;
379
  guint64 file_size;
Edward Hervey's avatar
Edward Hervey committed
380
  GFile *gfile = NULL;
381 382 383

  GESMetaContainer *container = GES_META_CONTAINER (asset);

Edward Hervey's avatar
Edward Hervey committed
384 385
  gfile = g_file_new_for_uri (uri);
  file_info = g_file_query_info (gfile, "standard::size",
386 387 388 389 390 391 392 393
      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
394 395 396 397
  if (gfile)
    g_object_unref (gfile);
  if (file_info)
    g_object_unref (file_info);
398 399
}

400 401 402 403 404 405
static void
_set_meta_foreach (const GstTagList * tags, const gchar * tag,
    GESMetaContainer * container)
{
  GValue value = { 0 };

406 407 408 409 410 411
  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);
  }
412 413 414 415 416 417
}

static void
discoverer_discovered_cb (GstDiscoverer * discoverer,
    GstDiscovererInfo * info, GError * err, gpointer user_data)
{
418
  GError *error = NULL;
419 420 421
  const GstTagList *tags;

  const gchar *uri = gst_discoverer_info_get_uri (info);
422 423
  GESUriClipAsset *mfs =
      GES_URI_CLIP_ASSET (ges_asset_cache_lookup (GES_TYPE_URI_CLIP, uri));
424 425 426 427 428

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

429 430
  _set_meta_file_size (uri, mfs);

431
  if (gst_discoverer_info_get_result (info) == GST_DISCOVERER_OK) {
432
    ges_uri_clip_asset_set_info (mfs, info);
433 434 435 436 437 438 439 440
  } 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));
    }
441 442
  }

443 444 445 446
  ges_asset_cache_set_loaded (GES_TYPE_URI_CLIP, uri, error);

  if (error)
    g_error_free (error);
447 448
}

449 450 451 452 453 454 455 456 457
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) {
458
      ges_asset_try_proxy (source, possible_uri);
459 460 461 462 463 464 465 466 467 468 469
      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);
}

470 471
/* API implementation */
/**
472
 * ges_uri_clip_asset_get_info:
473 474 475 476 477 478 479
 * @self: Target asset
 *
 * Gets #GstDiscovererInfo about the file
 *
 * Returns: (transfer none): #GstDiscovererInfo of specified asset
 */
GstDiscovererInfo *
480
ges_uri_clip_asset_get_info (const GESUriClipAsset * self)
481
{
482 483
  g_return_val_if_fail (GES_IS_URI_CLIP_ASSET (self), NULL);

484 485 486 487
  return self->priv->info;
}

/**
488 489
 * ges_uri_clip_asset_get_duration:
 * @self: a #GESUriClipAsset
490 491 492 493 494 495
 *
 * Gets duration of the file represented by @self
 *
 * Returns: The duration of @self
 */
GstClockTime
496
ges_uri_clip_asset_get_duration (GESUriClipAsset * self)
497
{
498
  g_return_val_if_fail (GES_IS_URI_CLIP_ASSET (self), GST_CLOCK_TIME_NONE);
499 500 501 502 503

  return self->priv->duration;
}

/**
504
 * ges_uri_clip_asset_is_image:
Thibault Saunier's avatar
Thibault Saunier committed
505
 * @self: a #GESUriClipAsset
506 507 508 509 510 511
 *
 * 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
512
ges_uri_clip_asset_is_image (GESUriClipAsset * self)
513
{
514
  g_return_val_if_fail (GES_IS_URI_CLIP_ASSET (self), FALSE);
515 516 517 518 519

  return self->priv->is_image;
}

/**
520 521
 * ges_uri_clip_asset_new:
 * @uri: The URI of the file for which to create a #GESUriClipAsset
522 523 524 525
 * @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
 *
526
 * Creates a #GESUriClipAsset for @uri
527
 *
528
 * Example of request of a GESUriClipAsset:
529 530 531 532 533 534
 * |[
 * // The request callback
 * static void
 * filesource_asset_loaded_cb (GESAsset * source, GAsyncResult * res, gpointer user_data)
 * {
 *   GError *error = NULL;
535
 *   GESUriClipAsset *filesource_asset;
536
 *
537
 *   filesource_asset = ges_uri_clip_asset_finish (res, &error);
538 539 540
 *   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))
541 542
 *        ges_uri_clip_asset_is_image (filesource_asset) ? "" : " not",
 *        GST_TIME_ARGS (ges_uri_clip_asset_get_duration (filesource_asset));
543 544 545 546 547
 *   } else {
 *    g_print ("The file: %s is *not* usable as a FileSource because: %s",
 *        ges_asset_get_id (source), error->message);
 *   }
 *
548
 *   gst_object_unref (mfs);
549 550 551
 * }
 *
 * // The request:
552
 * ges_uri_clip_asset_new (uri, (GAsyncReadyCallback) filesource_asset_loaded_cb, user_data);
553 554 555
 * ]|
 */
void
556
ges_uri_clip_asset_new (const gchar * uri, GCancellable * cancellable,
557 558
    GAsyncReadyCallback callback, gpointer user_data)
{
559
  ges_asset_request_async (GES_TYPE_URI_CLIP, uri, cancellable,
560 561 562
      callback, user_data);
}

563 564 565 566 567 568 569 570
/**
 * 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
571 572
 *
 * Since: 1.16
573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588
 */
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;
}

589
/**
590
 * ges_uri_clip_asset_request_sync:
Lubosz Sarnecki's avatar
Lubosz Sarnecki committed
591 592
 * @uri: The URI of the file for which to create a #GESUriClipAsset.
 * You can also use multi file uris for #GESMultiFileSource.
593
 * @error: An error to be set in case something wrong happens or %NULL
594 595 596 597
 *
 * Creates a #GESUriClipAsset for @uri syncronously. You should avoid
 * to use it in application, and rather create #GESUriClipAsset asynchronously
 *
598 599
 * Returns: (transfer full): A reference to the requested asset or %NULL if
 * an error happened
600 601 602 603 604 605
 */
GESUriClipAsset *
ges_uri_clip_asset_request_sync (const gchar * uri, GError ** error)
{
  GError *lerror = NULL;
  GESUriClipAsset *asset;
606
  RequestSyncData data = { 0, };
607
  GstDiscoverer *previous_discoverer;
608 609 610 611 612 613 614

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

  if (asset)
    return asset;

615
  data.ml = g_main_loop_new (NULL, TRUE);
616 617
  previous_discoverer = get_discoverer ();
  create_discoverer ();
618

619 620 621 622
  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);
623

624 625 626
  G_LOCK (discoverers_lock);
  g_hash_table_insert (discoverers, g_thread_self (), previous_discoverer);
  G_UNLOCK (discoverers_lock);
627

628 629 630 631
  if (data.error) {
    GST_ERROR ("Got an error requesting asset: %s", data.error->message);
    if (error != NULL)
      g_propagate_error (error, data.error);
632 633 634 635

    return NULL;
  }

636
  return GES_URI_CLIP_ASSET (data.asset);
637 638
}

639
/**
640
 * ges_uri_clip_asset_class_set_timeout:
Lubosz Sarnecki's avatar
Lubosz Sarnecki committed
641
 * @klass: The #GESUriClipAssetClass on which to set the discoverer timeout
642 643
 * @timeout: The timeout to set
 *
644
 * Sets the timeout of #GESUriClipAsset loading
645 646
 */
void
Lubosz Sarnecki's avatar
Lubosz Sarnecki committed
647
ges_uri_clip_asset_class_set_timeout (GESUriClipAssetClass * klass,
648 649
    GstClockTime timeout)
{
650 651 652
  GHashTableIter iter;
  gpointer value;

Lubosz Sarnecki's avatar
Lubosz Sarnecki committed
653
  g_return_if_fail (GES_IS_URI_CLIP_ASSET_CLASS (klass));
654

655 656 657 658 659 660 661
  discovering_timeout = timeout;

  G_LOCK (discoverers_lock);
  g_hash_table_iter_init (&iter, discoverers);
  while (g_hash_table_iter_next (&iter, NULL, &value))
    g_object_set (value, "timeout", timeout, NULL);
  G_UNLOCK (discoverers_lock);
662
}
663 664

/**
665 666
 * ges_uri_clip_asset_get_stream_assets:
 * @self: A #GESUriClipAsset
667
 *
668
 * Get the GESUriSourceAsset @self containes
669
 *
670 671
 * Returns: (transfer none) (element-type GESUriSourceAsset): a
 * #GList of #GESUriSourceAsset
672 673
 */
const GList *
674
ges_uri_clip_asset_get_stream_assets (GESUriClipAsset * self)
675
{
676
  g_return_val_if_fail (GES_IS_URI_CLIP_ASSET (self), FALSE);
677 678 679 680 681

  return self->priv->asset_trackfilesources;
}

/*****************************************************************
682
 *            GESUriSourceAsset implementation             *
683 684
 *****************************************************************/
/**
685
 * SECTION: gesurisourceasset
686
 * @title: GESUriClipAsset
687
 * @short_description: A GESAsset subclass specialized in GESUriSource extraction
688 689
 *
 * NOTE: You should never request such a #GESAsset as they will be created automatically
690
 * by #GESUriClipAsset-s.
691 692
 */

693
G_DEFINE_TYPE_WITH_PRIVATE (GESUriSourceAsset, ges_uri_source_asset,
694
    GES_TYPE_TRACK_ELEMENT_ASSET);
695 696 697 698

static GESExtractable *
_extract (GESAsset * asset, GError ** error)
{
Justin Kim's avatar
Justin Kim committed
699
  gchar *uri = NULL;
700
  GESTrackElement *trackelement;
701
  GESUriSourceAssetPrivate *priv = GES_URI_SOURCE_ASSET (asset)->priv;
702 703 704 705 706 707 708 709 710 711 712 713 714

  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
715 716
  uri = g_strdup (priv->uri);

Lubosz Sarnecki's avatar
Lubosz Sarnecki committed
717
  if (g_str_has_prefix (priv->uri, GES_MULTI_FILE_URI_PREFIX)) {
Justin Kim's avatar
Justin Kim committed
718
    trackelement = GES_TRACK_ELEMENT (ges_multi_file_source_new (uri));
Lubosz Sarnecki's avatar
Lubosz Sarnecki committed
719 720
  } else if (GST_IS_DISCOVERER_VIDEO_INFO (priv->sinfo)
      && gst_discoverer_video_info_is_image ((GstDiscovererVideoInfo *)
721
          priv->sinfo))
Justin Kim's avatar
Justin Kim committed
722
    trackelement = GES_TRACK_ELEMENT (ges_image_source_new (uri));
723
  else if (GST_IS_DISCOVERER_VIDEO_INFO (priv->sinfo))
Justin Kim's avatar
Justin Kim committed
724
    trackelement = GES_TRACK_ELEMENT (ges_video_uri_source_new (uri));
725
  else
Justin Kim's avatar
Justin Kim committed
726
    trackelement = GES_TRACK_ELEMENT (ges_audio_uri_source_new (uri));
727

728 729
  ges_track_element_set_track_type (trackelement,
      ges_track_element_asset_get_track_type (GES_TRACK_ELEMENT_ASSET (asset)));
730

Justin Kim's avatar
Justin Kim committed
731 732
  g_free (uri);

733
  return GES_EXTRACTABLE (trackelement);
734 735
}

736 737 738 739 740 741 742 743 744 745 746
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);
}

747
static void
748
ges_uri_source_asset_class_init (GESUriSourceAssetClass * klass)
749
{
750 751 752 753
  GObjectClass *object_class = G_OBJECT_CLASS (klass);

  object_class->dispose = ges_uri_source_asset_dispose;

754 755 756 757
  GES_ASSET_CLASS (klass)->extract = _extract;
}

static void
758
ges_uri_source_asset_init (GESUriSourceAsset * self)
759
{
760
  GESUriSourceAssetPrivate *priv;
761

762
  priv = self->priv = ges_uri_source_asset_get_instance_private (self);
763 764 765 766 767 768

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

Thibault Saunier's avatar
Thibault Saunier committed
769
/**
770
 * ges_uri_source_asset_get_stream_info:
771
 * @asset: A #GESUriClipAsset
Thibault Saunier's avatar
Thibault Saunier committed
772 773 774
 *
 * Get the #GstDiscovererStreamInfo user by @asset
 *
775
 * Returns: (transfer none): a #GESUriClipAsset
Thibault Saunier's avatar
Thibault Saunier committed
776
 */
777
GstDiscovererStreamInfo *
778
ges_uri_source_asset_get_stream_info (GESUriSourceAsset * asset)
779
{
780
  g_return_val_if_fail (GES_IS_URI_SOURCE_ASSET (asset), NULL);
781 782 783 784 785

  return asset->priv->sinfo;
}

const gchar *
786
ges_uri_source_asset_get_stream_uri (GESUriSourceAsset * asset)
787
{
788
  g_return_val_if_fail (GES_IS_URI_SOURCE_ASSET (asset), NULL);
789 790 791 792 793

  return asset->priv->uri;
}

/**
794
 * ges_uri_source_asset_get_filesource_asset:
795
 * @asset: A #GESUriClipAsset
796
 *
797
 * Get the #GESUriClipAsset @self is contained in
798
 *
799
 * Returns: a #GESUriClipAsset
800
 */
801
const GESUriClipAsset *
802
ges_uri_source_asset_get_filesource_asset (GESUriSourceAsset * asset)
803
{
804
  g_return_val_if_fail (GES_IS_URI_SOURCE_ASSET (asset), NULL);
805 806 807

  return asset->priv->parent_asset;
}
Guillaume Desmottes's avatar
Guillaume Desmottes committed
808 809 810 811

void
_ges_uri_asset_cleanup (void)
{
812 813 814 815
  if (parent_newparent_table) {
    g_hash_table_destroy (parent_newparent_table);
    parent_newparent_table = NULL;
  }
816 817 818 819 820 821 822

  G_LOCK (discoverers_lock);
  if (discoverers) {
    g_hash_table_destroy (discoverers);
    discoverers = NULL;
  }
  G_UNLOCK (discoverers_lock);
Guillaume Desmottes's avatar
Guillaume Desmottes committed
823
}
824 825 826 827 828 829 830 831

gboolean
_ges_uri_asset_ensure_setup (gpointer uriasset_class)
{
  GESUriClipAssetClass *klass;
  GError *err;
  GstClockTime timeout;
  const gchar *timeout_str;
832
  GstDiscoverer *discoverer = NULL;
833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860

  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) {
861
    klass->discoverer = klass->sync_discoverer = discoverer;
862 863
    g_object_add_weak_pointer (G_OBJECT (discoverer),
        (gpointer *) & klass->discoverer);
864 865
    g_object_add_weak_pointer (G_OBJECT (discoverer),
        (gpointer *) & klass->sync_discoverer);
866 867

    g_signal_connect (klass->discoverer, "discovered",
868
        G_CALLBACK (klass->discovered), NULL);
869 870 871 872 873 874 875
    gst_discoverer_start (klass->discoverer);
  }

  G_LOCK (discoverers_lock);
  if (discoverers == NULL) {
    discoverers = g_hash_table_new_full (g_direct_hash,
        (GEqualFunc) g_direct_equal, NULL, g_object_unref);
876
  }
877
  G_UNLOCK (discoverers_lock);
878 879 880 881 882 883 884 885 886

  /* We just start the discoverer and let it live */
  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;
}