text: /* GStreamer
   * Copyright (C) 2010 Collabora Multimedia
   *               2010 Nokia Corporation
   *
   * 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., 51 Franklin St, Fifth Floor,
   * Boston, MA 02110-1301, USA.
   */
  
  #ifdef HAVE_CONFIG_H
  #include "config.h"
  #endif
  
  #include "pbutils.h"
  #include "pbutils-private.h"
  
  static GstDiscovererStreamInfo
      * gst_discoverer_info_copy_int (GstDiscovererStreamInfo * info,
      GHashTable * stream_map);
  
  static GstDiscovererContainerInfo
      * gst_stream_container_info_copy_int (GstDiscovererContainerInfo * ptr,
      GHashTable * stream_map);
  
  static GstDiscovererAudioInfo
      * gst_discoverer_audio_info_copy_int (GstDiscovererAudioInfo * ptr);
  
  static GstDiscovererVideoInfo
      * gst_discoverer_video_info_copy_int (GstDiscovererVideoInfo * ptr);
  
  static GstDiscovererSubtitleInfo
      * gst_discoverer_subtitle_info_copy_int (GstDiscovererSubtitleInfo * ptr);
  
  /* Per-stream information */
  
  G_DEFINE_TYPE (GstDiscovererStreamInfo, gst_discoverer_stream_info,
      G_TYPE_OBJECT);
  
  static void
  gst_discoverer_stream_info_init (GstDiscovererStreamInfo * info)
  {
    /* Nothing needs initialization */
  }
  
  static void
  gst_discoverer_stream_info_finalize (GObject * object)
  {
    GstDiscovererStreamInfo *info = (GstDiscovererStreamInfo *) object;
  
    if (info->next)
      g_object_unref ((GObject *) info->next);
  
    if (info->caps)
      gst_caps_unref (info->caps);
  
    if (info->tags)
      gst_tag_list_unref (info->tags);
  
    if (info->toc)
      gst_toc_unref (info->toc);
  
    g_free (info->stream_id);
  
    if (info->misc)
      gst_structure_free (info->misc);
  }
  
  static void
  gst_discoverer_stream_info_class_init (GObjectClass * klass)
  {
    klass->finalize = gst_discoverer_stream_info_finalize;
  }
  
  static GstDiscovererStreamInfo *
  gst_discoverer_stream_info_new (void)
  {
    return (GstDiscovererStreamInfo *)
        g_object_new (GST_TYPE_DISCOVERER_STREAM_INFO, NULL);
  }
  
  static GstDiscovererStreamInfo *
  gst_discoverer_info_copy_int (GstDiscovererStreamInfo * info,
      GHashTable * stream_map)
  {
    GstDiscovererStreamInfo *ret;
    GType ltyp;
  
    g_return_val_if_fail (info != NULL, NULL);
  
    ltyp = G_TYPE_FROM_INSTANCE (info);
  
    if (ltyp == GST_TYPE_DISCOVERER_CONTAINER_INFO) {
      ret = (GstDiscovererStreamInfo *)
          gst_stream_container_info_copy_int (
          (GstDiscovererContainerInfo *) info, stream_map);
    } else if (ltyp == GST_TYPE_DISCOVERER_AUDIO_INFO) {
      ret = (GstDiscovererStreamInfo *)
          gst_discoverer_audio_info_copy_int ((GstDiscovererAudioInfo *) info);
  
    } else if (ltyp == GST_TYPE_DISCOVERER_VIDEO_INFO) {
      ret = (GstDiscovererStreamInfo *)
          gst_discoverer_video_info_copy_int ((GstDiscovererVideoInfo *) info);
  
    } else if (ltyp == GST_TYPE_DISCOVERER_SUBTITLE_INFO) {
      ret = (GstDiscovererStreamInfo *)
          gst_discoverer_subtitle_info_copy_int ((GstDiscovererSubtitleInfo *)
          info);
  
    } else
      ret = gst_discoverer_stream_info_new ();
  
    if (info->next) {
      ret->next = gst_discoverer_info_copy_int (info->next, stream_map);
      ret->next->previous = ret;
    }
  
    if (info->caps)
      ret->caps = gst_caps_copy (info->caps);
  
    if (info->tags)
      ret->tags = gst_tag_list_copy (info->tags);
  
    if (info->toc)
      ret->toc = gst_toc_ref (info->toc);
  
    if (info->stream_id)
      ret->stream_id = g_strdup (info->stream_id);
  
    if (info->misc)
      ret->misc = gst_structure_copy (info->misc);
  
    if (stream_map)
      g_hash_table_insert (stream_map, info, ret);
  
    return ret;
  }
  
  /* Container information */
  G_DEFINE_TYPE (GstDiscovererContainerInfo, gst_discoverer_container_info,
      GST_TYPE_DISCOVERER_STREAM_INFO);
  
  static void
  gst_discoverer_container_info_init (GstDiscovererContainerInfo * info)
  {
    /* Nothing to initialize */
  }
  
  static GstDiscovererContainerInfo *
  gst_discoverer_container_info_new (void)
  {
    return (GstDiscovererContainerInfo *)
        g_object_new (GST_TYPE_DISCOVERER_CONTAINER_INFO, NULL);
  }
  
  static void
  gst_discoverer_container_info_finalize (GObject * object)
  {
    GstDiscovererContainerInfo *info = (GstDiscovererContainerInfo *) object;
    GList *tmp;
  
    for (tmp = ((GstDiscovererContainerInfo *) info)->streams; tmp;
        tmp = tmp->next)
      g_object_unref ((GObject *) tmp->data);
  
    gst_discoverer_stream_info_list_free (info->streams);
  
    gst_discoverer_stream_info_finalize ((GObject *) info);
  }
  
  static void
  gst_discoverer_container_info_class_init (GObjectClass * klass)
  {
    klass->finalize = gst_discoverer_container_info_finalize;
  }
  
  static GstDiscovererContainerInfo *
  gst_stream_container_info_copy_int (GstDiscovererContainerInfo * ptr,
      GHashTable * stream_map)
  {
    GstDiscovererContainerInfo *ret;
    GList *tmp;
  
    g_return_val_if_fail (ptr != NULL, NULL);
  
    ret = gst_discoverer_container_info_new ();
  
    for (tmp = ((GstDiscovererContainerInfo *) ptr)->streams; tmp;
        tmp = tmp->next) {
      GstDiscovererStreamInfo *subtop = gst_discoverer_info_copy_int (tmp->data,
          stream_map);
      ret->streams = g_list_append (ret->streams, subtop);
      if (stream_map)
        g_hash_table_insert (stream_map, tmp->data, subtop);
    }
  
    return ret;
  }
  
  /* Audio information */
  G_DEFINE_TYPE (GstDiscovererAudioInfo, gst_discoverer_audio_info,
      GST_TYPE_DISCOVERER_STREAM_INFO);
  
  static void
  gst_discoverer_audio_info_finalize (GObject * object)
  {
    GstDiscovererAudioInfo *info = (GstDiscovererAudioInfo *) object;
  
    g_free (info->language);
  
    G_OBJECT_CLASS (gst_discoverer_audio_info_parent_class)->finalize (object);
  }
  
  static void
  gst_discoverer_audio_info_class_init (GObjectClass * klass)
  {
    klass->finalize = gst_discoverer_audio_info_finalize;
  }
  
  static void
  gst_discoverer_audio_info_init (GstDiscovererAudioInfo * info)
  {
    info->language = NULL;
  }
  
  static GstDiscovererAudioInfo *
  gst_discoverer_audio_info_new (void)
  {
    return (GstDiscovererAudioInfo *)
        g_object_new (GST_TYPE_DISCOVERER_AUDIO_INFO, NULL);
  }
  
  static GstDiscovererAudioInfo *
  gst_discoverer_audio_info_copy_int (GstDiscovererAudioInfo * ptr)
  {
    GstDiscovererAudioInfo *ret;
  
    ret = gst_discoverer_audio_info_new ();
  
    ret->channels = ptr->channels;
    ret->channel_mask = ptr->channel_mask;
    ret->sample_rate = ptr->sample_rate;
    ret->depth = ptr->depth;
    ret->bitrate = ptr->bitrate;
    ret->max_bitrate = ptr->max_bitrate;
    ret->language = g_strdup (ptr->language);
  
    return ret;
  }
  
  /* Subtitle information */
  G_DEFINE_TYPE (GstDiscovererSubtitleInfo, gst_discoverer_subtitle_info,
      GST_TYPE_DISCOVERER_STREAM_INFO);
  
  static void
  gst_discoverer_subtitle_info_init (GstDiscovererSubtitleInfo * info)
  {
    info->language = NULL;
  }
  
  static void
  gst_discoverer_subtitle_info_finalize (GObject * object)
  {
    GstDiscovererSubtitleInfo *info = (GstDiscovererSubtitleInfo *) object;
  
    g_free (info->language);
  
    G_OBJECT_CLASS (gst_discoverer_subtitle_info_parent_class)->finalize (object);
  }
  
  static void
  gst_discoverer_subtitle_info_class_init (GObjectClass * klass)
  {
    klass->finalize = gst_discoverer_subtitle_info_finalize;
  }
  
  static GstDiscovererSubtitleInfo *
  gst_discoverer_subtitle_info_new (void)
  {
    return (GstDiscovererSubtitleInfo *)
        g_object_new (GST_TYPE_DISCOVERER_SUBTITLE_INFO, NULL);
  }
  
  static GstDiscovererSubtitleInfo *
  gst_discoverer_subtitle_info_copy_int (GstDiscovererSubtitleInfo * ptr)
  {
    GstDiscovererSubtitleInfo *ret;
  
    ret = gst_discoverer_subtitle_info_new ();
  
    ret->language = g_strdup (ptr->language);
  
    return ret;
  }
  
  /* Video information */
  G_DEFINE_TYPE (GstDiscovererVideoInfo, gst_discoverer_video_info,
      GST_TYPE_DISCOVERER_STREAM_INFO);
  
  static void
  gst_discoverer_video_info_class_init (GObjectClass * klass)
  {
    /* Nothing to initialize */
  }
  
  static void
  gst_discoverer_video_info_init (GstDiscovererVideoInfo * info)
  {
    /* Nothing to initialize */
  }
  
  static GstDiscovererVideoInfo *
  gst_discoverer_video_info_new (void)
  {
    return (GstDiscovererVideoInfo *)
        g_object_new (GST_TYPE_DISCOVERER_VIDEO_INFO, NULL);
  }
  
  static GstDiscovererVideoInfo *
  gst_discoverer_video_info_copy_int (GstDiscovererVideoInfo * ptr)
  {
    GstDiscovererVideoInfo *ret;
  
    ret = gst_discoverer_video_info_new ();
  
    ret->width = ptr->width;
    ret->height = ptr->height;
    ret->depth = ptr->depth;
    ret->framerate_num = ptr->framerate_num;
    ret->framerate_denom = ptr->framerate_denom;
    ret->par_num = ptr->par_num;
    ret->par_denom = ptr->par_denom;
    ret->interlaced = ptr->interlaced;
    ret->bitrate = ptr->bitrate;
    ret->max_bitrate = ptr->max_bitrate;
    ret->is_image = ptr->is_image;
  
    return ret;
  }
  
  /* Global stream information */
  G_DEFINE_TYPE (GstDiscovererInfo, gst_discoverer_info, G_TYPE_OBJECT);
  
  static void
  gst_discoverer_info_init (GstDiscovererInfo * info)
  {
    info->missing_elements_details = g_ptr_array_new_with_free_func (g_free);
  }
  
  static void
  gst_discoverer_info_finalize (GObject * object)
  {
    GstDiscovererInfo *info = (GstDiscovererInfo *) object;
    g_free (info->uri);
  
    if (info->stream_info)
      g_object_unref ((GObject *) info->stream_info);
  
    if (info->misc)
      gst_structure_free (info->misc);
  
    g_list_free (info->stream_list);
  
    if (info->tags)
      gst_tag_list_unref (info->tags);
  
    if (info->toc)
      gst_toc_unref (info->toc);
  
    g_ptr_array_unref (info->missing_elements_details);
  }
  
  static GstDiscovererInfo *
  gst_discoverer_info_new (void)
  {
    return (GstDiscovererInfo *) g_object_new (GST_TYPE_DISCOVERER_INFO, NULL);
  }
  
  /**
   * gst_discoverer_info_copy:
   * @ptr: (transfer none): a #GstDiscovererInfo
   *
   * Returns: (transfer full): A copy of the #GstDiscovererInfo
   */
  GstDiscovererInfo *
  gst_discoverer_info_copy (GstDiscovererInfo * ptr)
  {
    GstDiscovererInfo *ret;
    GHashTable *stream_map;
    GList *tmp;
  
    g_return_val_if_fail (ptr != NULL, NULL);
  
    stream_map = g_hash_table_new (g_direct_hash, NULL);
  
    ret = gst_discoverer_info_new ();
  
    ret->uri = g_strdup (ptr->uri);
    if (ptr->stream_info) {
      ret->stream_info = gst_discoverer_info_copy_int (ptr->stream_info,
          stream_map);
    }
    ret->duration = ptr->duration;
    ret->result = ptr->result;
    ret->seekable = ptr->seekable;
    ret->live = ptr->live;
    if (ptr->misc)
      ret->misc = gst_structure_copy (ptr->misc);
  
    /* We need to set up the new list of streams to correspond to old one. The
     * list just contains a set of pointers to streams in the stream_info tree,
     * so we keep a map of old stream info objects to the corresponding new
     * ones and use that to figure out correspondence in stream_list. */
    for (tmp = ptr->stream_list; tmp; tmp = tmp->next) {
      GstDiscovererStreamInfo *old_stream = (GstDiscovererStreamInfo *) tmp->data;
      GstDiscovererStreamInfo *new_stream = g_hash_table_lookup (stream_map,
          old_stream);
      g_assert (new_stream != NULL);
      ret->stream_list = g_list_append (ret->stream_list, new_stream);
    }
  
    if (ptr->tags)
      ret->tags = gst_tag_list_copy (ptr->tags);
  
    if (ptr->toc)
      ret->toc = gst_toc_ref (ptr->toc);
  
    g_hash_table_destroy (stream_map);
    return ret;
  }
  
  static void
  gst_discoverer_info_class_init (GObjectClass * klass)
  {
    klass->finalize = gst_discoverer_info_finalize;
  }
  
  /**
   * gst_discoverer_stream_info_list_free:
   * @infos: (element-type GstPbutils.DiscovererStreamInfo): a #GList of #GstDiscovererStreamInfo
   *
   * Decrements the reference count of all contained #GstDiscovererStreamInfo
   * and fress the #GList.
   */
  void
  gst_discoverer_stream_info_list_free (GList * infos)
  {
    GList *tmp;
  
    for (tmp = infos; tmp; tmp = tmp->next)
      gst_discoverer_stream_info_unref ((GstDiscovererStreamInfo *) tmp->data);
    g_list_free (infos);
  }
  
  /**
   * gst_discoverer_info_get_streams:
   * @info: a #GstDiscovererInfo
   * @streamtype: a #GType derived from #GstDiscovererStreamInfo
   *
   * Finds the #GstDiscovererStreamInfo contained in @info that match the
   * given @streamtype.
   *
   * Returns: (transfer full) (element-type GstPbutils.DiscovererStreamInfo): A #GList of
   * matching #GstDiscovererStreamInfo. The caller should free it with
   * gst_discoverer_stream_info_list_free().
   */
  GList *
  gst_discoverer_info_get_streams (GstDiscovererInfo * info, GType streamtype)
  {
    GList *tmp, *res = NULL;
  
    for (tmp = info->stream_list; tmp; tmp = tmp->next) {
      GstDiscovererStreamInfo *stmp = (GstDiscovererStreamInfo *) tmp->data;
  
      if (G_TYPE_CHECK_INSTANCE_TYPE (stmp, streamtype))
        res = g_list_append (res, gst_discoverer_stream_info_ref (stmp));
    }
  
    return res;
  }
  
  /**
   * gst_discoverer_info_get_audio_streams:
   * @info: a #GstDiscovererInfo
   *
   * Finds all the #GstDiscovererAudioInfo contained in @info
   *
   * Returns: (transfer full) (element-type GstPbutils.DiscovererStreamInfo): A #GList of
   * matching #GstDiscovererStreamInfo. The caller should free it with
   * gst_discoverer_stream_info_list_free().
   */
  GList *
  gst_discoverer_info_get_audio_streams (GstDiscovererInfo * info)
  {
    return gst_discoverer_info_get_streams (info, GST_TYPE_DISCOVERER_AUDIO_INFO);
  }
  
  /**
   * gst_discoverer_info_get_video_streams:
   * @info: a #GstDiscovererInfo
   *
   * Finds all the #GstDiscovererVideoInfo contained in @info
   *
   * Returns: (transfer full) (element-type GstPbutils.DiscovererStreamInfo): A #GList of
   * matching #GstDiscovererStreamInfo. The caller should free it with
   * gst_discoverer_stream_info_list_free().
   */
  GList *
  gst_discoverer_info_get_video_streams (GstDiscovererInfo * info)
  {
    return gst_discoverer_info_get_streams (info, GST_TYPE_DISCOVERER_VIDEO_INFO);
  }
  
  /**
   * gst_discoverer_info_get_subtitle_streams:
   * @info: a #GstDiscovererInfo
   *
   * Finds all the #GstDiscovererSubtitleInfo contained in @info
   *
   * Returns: (transfer full) (element-type GstPbutils.DiscovererStreamInfo): A #GList of
   * matching #GstDiscovererStreamInfo. The caller should free it with
   * gst_discoverer_stream_info_list_free().
   */
  GList *
  gst_discoverer_info_get_subtitle_streams (GstDiscovererInfo * info)
  {
    return gst_discoverer_info_get_streams (info,
        GST_TYPE_DISCOVERER_SUBTITLE_INFO);
  }
  
  /**
   * gst_discoverer_info_get_container_streams:
   * @info: a #GstDiscovererInfo
   *
   * Finds all the #GstDiscovererContainerInfo contained in @info
   *
   * Returns: (transfer full) (element-type GstPbutils.DiscovererStreamInfo): A #GList of
   * matching #GstDiscovererStreamInfo. The caller should free it with
   * gst_discoverer_stream_info_list_free().
   */
  GList *
  gst_discoverer_info_get_container_streams (GstDiscovererInfo * info)
  {
    return gst_discoverer_info_get_streams (info,
        GST_TYPE_DISCOVERER_CONTAINER_INFO);
  }
  
  /**
   * gst_discoverer_stream_info_get_stream_type_nick:
   * @info: a #GstDiscovererStreamInfo
   *
   * Returns: a human readable name for the stream type of the given @info (ex : "audio",
   * "container",...).
   */
  const gchar *
  gst_discoverer_stream_info_get_stream_type_nick (GstDiscovererStreamInfo * info)
  {
    if (GST_IS_DISCOVERER_CONTAINER_INFO (info))
      return "container";
    if (GST_IS_DISCOVERER_AUDIO_INFO (info))
      return "audio";
    if (GST_IS_DISCOVERER_VIDEO_INFO (info)) {
      if (gst_discoverer_video_info_is_image ((GstDiscovererVideoInfo *)
              info))
        return "video(image)";
      else
        return "video";
    }
    if (GST_IS_DISCOVERER_SUBTITLE_INFO (info))
      return "subtitles";
    return "unknown";
  }
  
  /* ACCESSORS */
  
  
  #define GENERIC_ACCESSOR_CODE(parent, parenttype, parentgtype, fieldname, type, failval) \
    type parent##_get_##fieldname(const parenttype info) {			\
      g_return_val_if_fail(G_TYPE_CHECK_INSTANCE_TYPE((info), parentgtype), failval); \
      return (info)->fieldname;				\
    }
  
  /**
   * gst_discoverer_stream_info_get_previous:
   * @info: a #GstDiscovererStreamInfo
   *
   * Returns: (transfer full): the previous #GstDiscovererStreamInfo in a chain.
   * %NULL for starting points. Unref with #gst_discoverer_stream_info_unref
   * after usage.
   */
  GstDiscovererStreamInfo *
  gst_discoverer_stream_info_get_previous (GstDiscovererStreamInfo * info)
  {
    g_return_val_if_fail (GST_IS_DISCOVERER_STREAM_INFO (info), NULL);
  
    if (info->previous)
      return gst_discoverer_stream_info_ref (info->previous);
    return NULL;
  }
  
  /**
   * gst_discoverer_stream_info_get_next:
   * @info: a #GstDiscovererStreamInfo
   *
   * Returns: (transfer full): the next #GstDiscovererStreamInfo in a chain. %NULL
   * for final streams.
   * Unref with #gst_discoverer_stream_info_unref after usage.
   */
  GstDiscovererStreamInfo *
  gst_discoverer_stream_info_get_next (GstDiscovererStreamInfo * info)
  {
    g_return_val_if_fail (GST_IS_DISCOVERER_STREAM_INFO (info), NULL);
  
    if (info->next)
      return gst_discoverer_stream_info_ref (info->next);
    return NULL;
  }
  
  
  /**
   * gst_discoverer_stream_info_get_caps:
   * @info: a #GstDiscovererStreamInfo
   *
   * Returns: (transfer full): the #GstCaps of the stream. Unref with
   * #gst_caps_unref after usage.
   */
  GstCaps *
  gst_discoverer_stream_info_get_caps (GstDiscovererStreamInfo * info)
  {
    g_return_val_if_fail (GST_IS_DISCOVERER_STREAM_INFO (info), NULL);
  
    if (info->caps)
      return gst_caps_ref (info->caps);
    return NULL;
  }
  
  /**
   * gst_discoverer_stream_info_get_tags:
   * @info: a #GstDiscovererStreamInfo
   *
   * Returns: (transfer none): the tags contained in this stream. If you wish to
   * use the tags after the life-time of @info you will need to copy them.
   */
  const GstTagList *
  gst_discoverer_stream_info_get_tags (GstDiscovererStreamInfo * info)
  {
    g_return_val_if_fail (GST_IS_DISCOVERER_STREAM_INFO (info), NULL);
  
    return info->tags;
  }
  
  /**
   * gst_discoverer_stream_info_get_toc:
   * @info: a #GstDiscovererStreamInfo
   *
   * Returns: (transfer none): the TOC contained in this stream. If you wish to
   * use the TOC after the life-time of @info you will need to copy it.
   */
  const GstToc *
  gst_discoverer_stream_info_get_toc (GstDiscovererStreamInfo * info)
  {
    g_return_val_if_fail (GST_IS_DISCOVERER_STREAM_INFO (info), NULL);
  
    return info->toc;
  }
  
  /**
   * gst_discoverer_stream_info_get_stream_id:
   * @info: a #GstDiscovererStreamInfo
   *
   * Returns: (transfer none): the stream ID of this stream. If you wish to
   * use the stream ID after the life-time of @info you will need to copy it.
   */
  const gchar *
  gst_discoverer_stream_info_get_stream_id (GstDiscovererStreamInfo * info)
  {
    g_return_val_if_fail (GST_IS_DISCOVERER_STREAM_INFO (info), NULL);
  
    return info->stream_id;
  }
  
  #ifndef GST_REMOVE_DEPRECATED
  /**
   * gst_discoverer_stream_info_get_misc:
   * @info: a #GstDiscovererStreamInfo
   *
   * Deprecated: This functions is deprecated since version 1.4, use
   * #gst_discoverer_info_get_missing_elements_installer_details
   *
   * Returns: (transfer none): additional information regarding the stream (for
   * example codec version, profile, etc..). If you wish to use the #GstStructure
   * after the life-time of @info you will need to copy it.
   */
  const GstStructure *
  gst_discoverer_stream_info_get_misc (GstDiscovererStreamInfo * info)
  {
    g_return_val_if_fail (GST_IS_DISCOVERER_STREAM_INFO (info), NULL);
  
    return info->misc;
  }
  #endif
  
  /* GstDiscovererContainerInfo */
  
  /**
   * gst_discoverer_container_info_get_streams:
   * @info: a #GstDiscovererStreamInfo
   *
   * Returns: (transfer full) (element-type GstPbutils.DiscovererStreamInfo): the list of
   * #GstDiscovererStreamInfo this container stream offers.
   * Free with gst_discoverer_stream_info_list_free() after usage.
   */
  
  GList *
  gst_discoverer_container_info_get_streams (GstDiscovererContainerInfo * info)
  {
    GList *res = NULL, *tmp;
  
    g_return_val_if_fail (GST_IS_DISCOVERER_CONTAINER_INFO (info), NULL);
  
    for (tmp = info->streams; tmp; tmp = tmp->next)
      res =
          g_list_append (res,
          gst_discoverer_stream_info_ref ((GstDiscovererStreamInfo *) tmp->data));
  
    return res;
  }
  
  /* GstDiscovererAudioInfo */
  
  #define AUDIO_INFO_ACCESSOR_CODE(fieldname, type, failval)		\
    GENERIC_ACCESSOR_CODE(gst_discoverer_audio_info, GstDiscovererAudioInfo*, \
  			GST_TYPE_DISCOVERER_AUDIO_INFO,		\
  			fieldname, type, failval)
  
  /**
   * gst_discoverer_audio_info_get_channels:
   * @info: a #GstDiscovererAudioInfo
   *
   * Returns: the number of channels in the stream.
   */
  
  AUDIO_INFO_ACCESSOR_CODE (channels, guint, 0);
  
  /**
   * gst_discoverer_audio_info_get_channel_mask:
   * @info: a #GstDiscovererAudioInfo
   *
   * Returns: the channel-mask of the stream, refer to
   * gst_audio_channel_positions_from_mask() for more
   * information.
   *
   * Since: 1.14
   */
  
  AUDIO_INFO_ACCESSOR_CODE (channel_mask, guint64, G_MAXUINT64);
  
  /**
   * gst_discoverer_audio_info_get_sample_rate:
   * @info: a #GstDiscovererAudioInfo
   *
   * Returns: the sample rate of the stream in Hertz.
   */
  
  AUDIO_INFO_ACCESSOR_CODE (sample_rate, guint, 0);
  
  /**
   * gst_discoverer_audio_info_get_depth:
   * @info: a #GstDiscovererAudioInfo
   *
   * Returns: the number of bits used per sample in each channel.
   */
  
  AUDIO_INFO_ACCESSOR_CODE (depth, guint, 0);
  
  /**
   * gst_discoverer_audio_info_get_bitrate:
   * @info: a #GstDiscovererAudioInfo
   *
   * Returns: the average or nominal bitrate of the stream in bits/second.
   */
  
  AUDIO_INFO_ACCESSOR_CODE (bitrate, guint, 0);
  
  /**
   * gst_discoverer_audio_info_get_max_bitrate:
   * @info: a #GstDiscovererAudioInfo
   *
   * Returns: the maximum bitrate of the stream in bits/second.
   */
  
  AUDIO_INFO_ACCESSOR_CODE (max_bitrate, guint, 0);
  
  /**
   * gst_discoverer_audio_info_get_language:
   * @info: a #GstDiscovererAudioInfo
   *
   * Returns: the language of the stream, or NULL if unknown.
   */
  
  AUDIO_INFO_ACCESSOR_CODE (language, const gchar *, NULL);
  
  /* GstDiscovererVideoInfo */
  
  #define VIDEO_INFO_ACCESSOR_CODE(fieldname, type, failval)		\
    GENERIC_ACCESSOR_CODE(gst_discoverer_video_info, GstDiscovererVideoInfo*, \
  			GST_TYPE_DISCOVERER_VIDEO_INFO,			\
  			fieldname, type, failval)
  
  /**
   * gst_discoverer_video_info_get_width:
   * @info: a #GstDiscovererVideoInfo
   *
   * Returns: the width of the video stream in pixels.
   */
  
  VIDEO_INFO_ACCESSOR_CODE (width, guint, 0);
  
  /**
   * gst_discoverer_video_info_get_height:
   * @info: a #GstDiscovererVideoInfo
   *
   * Returns: the height of the video stream in pixels.
   */
  
  VIDEO_INFO_ACCESSOR_CODE (height, guint, 0);
  
  /**
   * gst_discoverer_video_info_get_depth:
   * @info: a #GstDiscovererVideoInfo
   *
   * Returns: the depth in bits of the video stream.
   */
  
  VIDEO_INFO_ACCESSOR_CODE (depth, guint, 0);
  
  /**
   * gst_discoverer_video_info_get_framerate_num:
   * @info: a #GstDiscovererVideoInfo
   *
   * Returns: the framerate of the video stream (numerator).
   */
  
  VIDEO_INFO_ACCESSOR_CODE (framerate_num, guint, 0);
  
  /**
   * gst_discoverer_video_info_get_framerate_denom:
   * @info: a #GstDiscovererVideoInfo
   *
   * Returns: the framerate of the video stream (denominator).
   */
  
  VIDEO_INFO_ACCESSOR_CODE (framerate_denom, guint, 0);
  
  /**
   * gst_discoverer_video_info_get_par_num:
   * @info: a #GstDiscovererVideoInfo
   *
   * Returns: the Pixel Aspect Ratio (PAR) of the video stream (numerator).
   */
  
  VIDEO_INFO_ACCESSOR_CODE (par_num, guint, 0);
  
  /**
   * gst_discoverer_video_info_get_par_denom:
   * @info: a #GstDiscovererVideoInfo
   *
   * Returns: the Pixel Aspect Ratio (PAR) of the video stream (denominator).
   */
  
  VIDEO_INFO_ACCESSOR_CODE (par_denom, guint, 0);
  
  /**
   * gst_discoverer_video_info_is_interlaced:
   * @info: a #GstDiscovererVideoInfo
   *
   * Returns: %TRUE if the stream is interlaced, else %FALSE.
   */
  gboolean
  gst_discoverer_video_info_is_interlaced (const GstDiscovererVideoInfo * info)
  {
    g_return_val_if_fail (GST_IS_DISCOVERER_VIDEO_INFO (info), FALSE);
  
    return info->interlaced;
  }
  
  /**
   * gst_discoverer_video_info_get_bitrate:
   * @info: a #GstDiscovererVideoInfo
   *
   * Returns: the average or nominal bitrate of the video stream in bits/second.
   */
  
  VIDEO_INFO_ACCESSOR_CODE (bitrate, guint, 0);
  
  /**
   * gst_discoverer_video_info_get_max_bitrate:
   * @info: a #GstDiscovererVideoInfo
   *
   * Returns: the maximum bitrate of the video stream in bits/second.
   */
  
  VIDEO_INFO_ACCESSOR_CODE (max_bitrate, guint, 0);
  
  /**
   * gst_discoverer_video_info_is_image:
   * @info: a #GstDiscovererVideoInfo
   *
   * Returns: %TRUE if the video stream corresponds to an image (i.e. only contains
   * one frame).
   */
  gboolean
  gst_discoverer_video_info_is_image (const GstDiscovererVideoInfo * info)
  {
    g_return_val_if_fail (GST_IS_DISCOVERER_VIDEO_INFO (info), FALSE);
  
    return info->is_image;
  }
  
  /* GstDiscovererSubtitleInfo */
  
  #define SUBTITLE_INFO_ACCESSOR_CODE(fieldname, type, failval)                     \
    GENERIC_ACCESSOR_CODE(gst_discoverer_subtitle_info, GstDiscovererSubtitleInfo*, \
  			GST_TYPE_DISCOVERER_SUBTITLE_INFO,                        \
  			fieldname, type, failval)
  
  /**
   * gst_discoverer_subtitle_info_get_language:
   * @info: a #GstDiscovererSubtitleInfo
   *
   * Returns: the language of the stream, or NULL if unknown.
   */
  
  SUBTITLE_INFO_ACCESSOR_CODE (language, const gchar *, NULL);
  
  /* GstDiscovererInfo */
  
  #define DISCOVERER_INFO_ACCESSOR_CODE(fieldname, type, failval)		\
    GENERIC_ACCESSOR_CODE(gst_discoverer_info, GstDiscovererInfo*,	\
  			GST_TYPE_DISCOVERER_INFO,			\
  			fieldname, type, failval)
  
  /**
   * gst_discoverer_info_get_uri:
   * @info: a #GstDiscovererInfo
   *
   * Returns: (transfer none): the URI to which this information corresponds to.
   * Copy it if you wish to use it after the life-time of @info.
   */
  
  DISCOVERER_INFO_ACCESSOR_CODE (uri, const gchar *, NULL);
  
  /**
   * gst_discoverer_info_get_result:
   * @info: a #GstDiscovererInfo
   *
   * Returns: the result of the discovery as a #GstDiscovererResult.
   */
  
  DISCOVERER_INFO_ACCESSOR_CODE (result, GstDiscovererResult, GST_DISCOVERER_OK);
  
  /**
   * gst_discoverer_info_get_stream_info:
   * @info: a #GstDiscovererInfo
   *
   * Returns: (transfer full): the structure (or topology) of the URI as a
   * #GstDiscovererStreamInfo.
   * This structure can be traversed to see the original hierarchy. Unref with
   * gst_discoverer_stream_info_unref() after usage.
   */
  
  GstDiscovererStreamInfo *
  gst_discoverer_info_get_stream_info (GstDiscovererInfo * info)
  {
    g_return_val_if_fail (GST_IS_DISCOVERER_INFO (info), NULL);
  
    if (info->stream_info)
      return gst_discoverer_stream_info_ref (info->stream_info);
    return NULL;
  }
  
  /**
   * gst_discoverer_info_get_stream_list:
   * @info: a #GstDiscovererInfo
   *
   * Returns: (transfer full) (element-type GstPbutils.DiscovererStreamInfo): the list of
   * all streams contained in the #info. Free after usage
   * with gst_discoverer_stream_info_list_free().
   */
  GList *
  gst_discoverer_info_get_stream_list (GstDiscovererInfo * info)
  {
    GList *res = NULL, *tmp;
  
    g_return_val_if_fail (GST_IS_DISCOVERER_INFO (info), NULL);
  
    for (tmp = info->stream_list; tmp; tmp = tmp->next)
      res =
          g_list_append (res,
          gst_discoverer_stream_info_ref ((GstDiscovererStreamInfo *) tmp->data));
  
    return res;
  }
  
  /**
   * gst_discoverer_info_get_duration:
   * @info: a #GstDiscovererInfo
   *
   * Returns: the duration of the URI in #GstClockTime (nanoseconds).
   */
  
  DISCOVERER_INFO_ACCESSOR_CODE (duration, GstClockTime, GST_CLOCK_TIME_NONE);
  
  /**
   * gst_discoverer_info_get_seekable:
   * @info: a #GstDiscovererInfo
   *
   * Returns: the whether the URI is seekable.
   */
  
  DISCOVERER_INFO_ACCESSOR_CODE (seekable, gboolean, FALSE);
  
  /**
   * gst_discoverer_info_get_live:
   * @info: a #GstDiscovererInfo
   *
   * Returns: whether the URI is live.
   *
   * Since: 1.14
   */
  
  DISCOVERER_INFO_ACCESSOR_CODE (live, gboolean, FALSE);
  
  #ifndef GST_REMOVE_DEPRECATED
  /**
   * gst_discoverer_info_get_misc:
   * @info: a #GstDiscovererInfo
   *
   * Deprecated: This functions is deprecated since version 1.4, use
   * #gst_discoverer_info_get_missing_elements_installer_details
   *
   * Returns: (transfer none): Miscellaneous information stored as a #GstStructure
   * (for example: information about missing plugins). If you wish to use the
   * #GstStructure after the life-time of @info, you will need to copy it.
   */
  
  DISCOVERER_INFO_ACCESSOR_CODE (misc, const GstStructure *, NULL);
  #endif
  
  /**
   * gst_discoverer_info_get_tags:
   * @info: a #GstDiscovererInfo
   *
   * Returns: (transfer none): all tags contained in the URI. If you wish to use
   * the tags after the life-time of @info, you will need to copy them.
   */
  
  DISCOVERER_INFO_ACCESSOR_CODE (tags, const GstTagList *, NULL);
  
  /**
   * gst_discoverer_info_get_toc:
   * @info: a #GstDiscovererInfo
   *
   * Returns: (transfer none): TOC contained in the URI. If you wish to use
   * the TOC after the life-time of @info, you will need to copy it.
   */
  
  DISCOVERER_INFO_ACCESSOR_CODE (toc, const GstToc *, NULL);
  
  /**
   * gst_discoverer_info_ref:
   * @info: a #GstDiscovererInfo
   *
   * Increments the reference count of @info.
   *
   * Returns: the same #GstDiscovererInfo object
   */
  
  /**
   * gst_discoverer_info_unref:
   * @info: a #GstDiscovererInfo
   *
   * Decrements the reference count of @info.
   */
  
  /**
   * gst_discoverer_stream_info_ref:
   * @info: a #GstDiscovererStreamInfo
   *
   * Increments the reference count of @info.
   *
   * Returns: the same #GstDiscovererStreamInfo object
   */
  
  /**
   * gst_discoverer_stream_info_unref:
   * @info: a #GstDiscovererStreamInfo
   *
   * Decrements the reference count of @info.
   */
  
  
  /**
   * gst_discoverer_info_get_missing_elements_installer_details:
   * @info: a #GstDiscovererStreamInfo to retrieve installer detail
   * for the missing element
   *
   * Get the installer details for missing elements
   *
   * Returns: (transfer none) (array zero-terminated=1): An array of strings
   * containing informations about how to install the various missing elements
   * for @info to be usable. If you wish to use the strings after the life-time
   * of @info, you will need to copy them.
   *
   * Since: 1.4
   */
  const gchar **
  gst_discoverer_info_get_missing_elements_installer_details (const
      GstDiscovererInfo * info)
  {
  
    if (info->result != GST_DISCOVERER_MISSING_PLUGINS) {
      GST_WARNING_OBJECT (info, "Trying to get missing element installed details "
          "but result is not 'MISSING_PLUGINS'");
  
      return NULL;
    }
  
    if (info->missing_elements_details->pdata[info->missing_elements_details->
            len]) {
      GST_DEBUG ("Adding NULL pointer to the end of missing_elements_details");
      g_ptr_array_add (info->missing_elements_details, NULL);
    }
  
    return (const gchar **) info->missing_elements_details->pdata;
  }
  