Merge branch 'tizen' into tizen_gst_1.19.2
authorGilbok Lee <gilbok.lee@samsung.com>
Wed, 19 Jan 2022 01:50:15 +0000 (10:50 +0900)
committerGilbok Lee <gilbok.lee@samsung.com>
Wed, 19 Jan 2022 05:03:30 +0000 (14:03 +0900)
Change-Id: I4cebef33eadb3e868c122c1d09a70fb4afbfecb9

53 files changed:
1  2 
ext/aes/meson.build
ext/bz2/gstbz2enc.c
ext/dash/gstdashdemux.c
ext/dash/gstmpdclient.c
ext/dash/gstmpdparser.h
ext/dtls/meson.build
ext/hls/gsthlsdemux.c
ext/hls/gsthlsdemux.h
ext/hls/m3u8.c
ext/hls/m3u8.h
ext/hls/meson.build
ext/kate/gstkatedec.c
ext/musepack/gstmusepackdec.c
ext/openal/gstopenalsink.c
ext/openal/meson.build
ext/rsvg/gstrsvgdec.c
ext/soundtouch/gstpitch.cc
ext/wayland/wldisplay.c
ext/webrtc/gstwebrtcbin.c
ext/webrtc/gstwebrtcbin.h
ext/webrtc/gstwebrtcice.c
ext/webrtc/webrtcdatachannel.c
gst-libs/gst/adaptivedemux/gstadaptivedemux.c
gst-libs/gst/adaptivedemux/gstadaptivedemux.h
gst-libs/gst/uridownloader/gsturidownloader.c
gst-libs/gst/vulkan/wayland/gstvkdisplay_wayland.c
gst/autoconvert/gstautoconvert.c
gst/bayer/gstbayer2rgb.c
gst/id3tag/id3tag.c
gst/mpegdemux/gstmpegdemux.c
gst/mpegtsdemux/mpegtsbase.c
gst/mpegtsdemux/mpegtspacketizer.c
gst/mpegtsdemux/mpegtspacketizer.h
gst/mpegtsdemux/tsdemux.c
gst/mpegtsdemux/tsdemux.h
gst/mxf/mxfdemux.c
gst/mxf/mxfvc3.c
gst/sdp/gstsdpdemux.c
gst/timecode/gstavwait.c
gst/videofilters/gstscenechange.c
gst/videofilters/gstvideodiff.c
gst/videoparsers/gsth264parse.c
gst/videosignal/gstsimplevideomark.c
gst/videosignal/gstvideoanalyse.c
meson_options.txt
packaging/gst-plugins-bad.manifest
packaging/gst-plugins-bad.spec
po/gst-plugins-bad-1.0.pot
sys/applemedia/vtenc.c
sys/bluez/gstavdtpsrc.c
sys/dshowdecwrapper/gstdshowvideodec.cpp
sys/dvb/dvbbasebin.c
sys/uvch264/gstuvch264_src.c

index a200b8d,0000000..a5d3f94
mode 100644,000000..100644
--- /dev/null
@@@ -1,28 -1,0 +1,28 @@@
- aes_dep = dependency('openssl', required : get_option('aes'))
 +aes_sources = [
 +  'gstaes.c',
 +  'gstaeshelper.c',
 +  'gstaesenc.c',
 +  'gstaesdec.c',
 +]
 +
 +aes_cargs = []
++aes_dep = dependency('openssl1.1', required : get_option('aes'))
 +if aes_dep.found()
 +  aes_cargs += ['-DHAVE_OPENSSL']
 +else
 +  subdir_done()
 +endif
 +
 +gstaes = library('gstaes',
 +  aes_sources,
 +  c_args : gst_plugins_bad_args + aes_cargs,
 +  link_args : noseh_link_args,
 +  include_directories : [configinc],
 +  dependencies : [gstpbutils_dep, gstvideo_dep,
 +                  aes_dep, gio_dep, libm],
 +  install : true,
 +  install_dir : plugins_install_dir,
 +)
 +pkgconfig.generate(gstaes, install_dir : plugins_pkgconfig_install_dir)
 +plugins += [gstaes]
 +aes_dep = declare_dependency(include_directories : include_directories('.'))
Simple merge
Simple merge
index fdaaa65,0000000..fbbea06
mode 100644,000000..100644
--- /dev/null
@@@ -1,3393 -1,0 +1,3413 @@@
 +/* GStreamer
 + *
 + * Copyright (C) 2019 Collabora Ltd.
 + *   Author: Stéphane Cerveau <scerveau@collabora.com>
 + *
 + * This library is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public
 + * License as published by the Free Software Foundation; either
 + * version 2.1 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
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with this library; if not, write to the Free Software
 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
 + *
 + */
 +
 +#include "gstmpdclient.h"
 +#include "gstmpdparser.h"
 +
 +GST_DEBUG_CATEGORY_STATIC (gst_dash_mpd_client_debug);
 +#undef GST_CAT_DEFAULT
 +#define GST_CAT_DEFAULT gst_dash_mpd_client_debug
 +
 +G_DEFINE_TYPE (GstMPDClient, gst_mpd_client, GST_TYPE_OBJECT);
 +
 +static GstMPDSegmentBaseNode *gst_mpd_client_get_segment_base (GstMPDPeriodNode
 +    * Period, GstMPDAdaptationSetNode * AdaptationSet,
 +    GstMPDRepresentationNode * Representation);
 +static GstMPDSegmentListNode *gst_mpd_client_get_segment_list (GstMPDClient *
 +    client, GstMPDPeriodNode * Period, GstMPDAdaptationSetNode * AdaptationSet,
 +    GstMPDRepresentationNode * Representation);
 +/* Segments */
 +static guint gst_mpd_client_get_segments_counts (GstMPDClient * client,
 +    GstActiveStream * stream);
 +
 +static GList *gst_mpd_client_fetch_external_periods (GstMPDClient * client,
 +    GstMPDPeriodNode * period_node);
 +static GList *gst_mpd_client_fetch_external_adaptation_set (GstMPDClient *
 +    client, GstMPDPeriodNode * period, GstMPDAdaptationSetNode * adapt_set);
 +
 +static GstMPDRepresentationNode *gst_mpd_client_get_lowest_representation (GList
 +    * Representations);
 +static GstStreamPeriod *gst_mpd_client_get_stream_period (GstMPDClient *
 +    client);
 +
 +typedef GstMPDNode *(*MpdClientStringIDFilter) (GList * list, gchar * data);
 +typedef GstMPDNode *(*MpdClientIDFilter) (GList * list, guint data);
 +
 +static GstMPDNode *
 +gst_mpd_client_get_period_with_id (GList * periods, gchar * period_id)
 +{
 +  GstMPDPeriodNode *period;
 +  GList *list = NULL;
 +
 +  for (list = g_list_first (periods); list; list = g_list_next (list)) {
 +    period = (GstMPDPeriodNode *) list->data;
 +    if (!g_strcmp0 (period->id, period_id))
 +      return GST_MPD_NODE (period);
 +  }
 +  return NULL;
 +}
 +
 +static GstMPDNode *
 +gst_mpd_client_get_adaptation_set_with_id (GList * adaptation_sets, guint id)
 +{
 +  GstMPDAdaptationSetNode *adaptation_set;
 +  GList *list = NULL;
 +
 +  for (list = g_list_first (adaptation_sets); list; list = g_list_next (list)) {
 +    adaptation_set = (GstMPDAdaptationSetNode *) list->data;
 +    if (adaptation_set->id == id)
 +      return GST_MPD_NODE (adaptation_set);
 +  }
 +  return NULL;
 +}
 +
 +static GstMPDNode *
 +gst_mpd_client_get_representation_with_id (GList * representations,
 +    gchar * rep_id)
 +{
 +  GstMPDRepresentationNode *representation;
 +  GList *list = NULL;
 +
 +  for (list = g_list_first (representations); list; list = g_list_next (list)) {
 +    representation = (GstMPDRepresentationNode *) list->data;
 +    if (!g_strcmp0 (representation->id, rep_id))
 +      return GST_MPD_NODE (representation);
 +  }
 +  return NULL;
 +}
 +
 +static gchar *
 +_generate_new_string_id (GList * list, const gchar * tuple,
 +    MpdClientStringIDFilter filter)
 +{
 +  guint i = 0;
 +  gchar *id = NULL;
 +  GstMPDNode *node;
 +  do {
 +    g_free (id);
 +    id = g_strdup_printf (tuple, i);
 +    node = filter (list, id);
 +    i++;
 +  } while (node);
 +
 +  return id;
 +}
 +
 +static guint
 +_generate_new_id (GList * list, MpdClientIDFilter filter)
 +{
 +  guint id = 0;
 +  GstMPDNode *node;
 +  do {
 +    node = filter (list, id);
 +    id++;
 +  } while (node);
 +
 +  return id;
 +}
 +
 +static GstMPDRepresentationNode *
 +gst_mpd_client_get_lowest_representation (GList * Representations)
 +{
 +  GList *list = NULL;
 +  GstMPDRepresentationNode *rep = NULL;
 +  GstMPDRepresentationNode *lowest = NULL;
 +
 +  if (Representations == NULL)
 +    return NULL;
 +
 +  for (list = g_list_first (Representations); list; list = g_list_next (list)) {
 +    rep = (GstMPDRepresentationNode *) list->data;
 +    if (rep && (!lowest || rep->bandwidth < lowest->bandwidth)) {
 +      lowest = rep;
 +    }
 +  }
 +
 +  return lowest;
 +}
 +
 +#if 0
 +static GstMPDRepresentationNode *
 +gst_mpdparser_get_highest_representation (GList * Representations)
 +{
 +  GList *list = NULL;
 +
 +  if (Representations == NULL)
 +    return NULL;
 +
 +  list = g_list_last (Representations);
 +
 +  return list ? (GstMPDRepresentationNode *) list->data : NULL;
 +}
 +
 +static GstMPDRepresentationNode *
 +gst_mpdparser_get_representation_with_max_bandwidth (GList * Representations,
 +    gint max_bandwidth)
 +{
 +  GList *list = NULL;
 +  GstMPDRepresentationNode *representation, *best_rep = NULL;
 +
 +  if (Representations == NULL)
 +    return NULL;
 +
 +  if (max_bandwidth <= 0)       /* 0 => get highest representation available */
 +    return gst_mpdparser_get_highest_representation (Representations);
 +
 +  for (list = g_list_first (Representations); list; list = g_list_next (list)) {
 +    representation = (GstMPDRepresentationNode *) list->data;
 +    if (representation && representation->bandwidth <= max_bandwidth) {
 +      best_rep = representation;
 +    }
 +  }
 +
 +  return best_rep;
 +}
 +#endif
 +
 +static GstMPDSegmentListNode *
 +gst_mpd_client_fetch_external_segment_list (GstMPDClient * client,
 +    GstMPDPeriodNode * Period,
 +    GstMPDAdaptationSetNode * AdaptationSet,
 +    GstMPDRepresentationNode * Representation,
 +    GstMPDSegmentListNode * parent, GstMPDSegmentListNode * segment_list)
 +{
 +  GstFragment *download;
 +  GstBuffer *segment_list_buffer = NULL;
 +  GstMapInfo map;
 +  GError *err = NULL;
 +
 +  GstUri *base_uri, *uri;
 +  gchar *query = NULL;
 +  gchar *uri_string;
 +  GstMPDSegmentListNode *new_segment_list = NULL;
 +
 +  /* ISO/IEC 23009-1:2014 5.5.3 4)
 +   * Remove nodes that resolve to nothing when resolving
 +   */
 +  if (strcmp (segment_list->xlink_href,
 +          "urn:mpeg:dash:resolve-to-zero:2013") == 0) {
 +    return NULL;
 +  }
 +
 +  if (!client->downloader) {
 +    return NULL;
 +  }
 +
 +  /* Build absolute URI */
 +
 +  /* Get base URI at the MPD level */
 +  base_uri =
 +      gst_uri_from_string (client->mpd_base_uri ? client->
 +      mpd_base_uri : client->mpd_uri);
 +
 +  /* combine a BaseURL at the MPD level with the current base url */
 +  base_uri =
 +      gst_mpd_helper_combine_urls (base_uri, client->mpd_root_node->BaseURLs,
 +      &query, 0);
 +
 +  /* combine a BaseURL at the Period level with the current base url */
 +  base_uri =
 +      gst_mpd_helper_combine_urls (base_uri, Period->BaseURLs, &query, 0);
 +
 +  if (AdaptationSet) {
 +    /* combine a BaseURL at the AdaptationSet level with the current base url */
 +    base_uri =
 +        gst_mpd_helper_combine_urls (base_uri, AdaptationSet->BaseURLs, &query,
 +        0);
 +
 +    if (Representation) {
 +      /* combine a BaseURL at the Representation level with the current base url */
 +      base_uri =
 +          gst_mpd_helper_combine_urls (base_uri, Representation->BaseURLs,
 +          &query, 0);
 +    }
 +  }
 +
 +  uri = gst_uri_from_string_with_base (base_uri, segment_list->xlink_href);
 +  if (query)
 +    gst_uri_set_query_string (uri, query);
 +  g_free (query);
 +  uri_string = gst_uri_to_string (uri);
 +  gst_uri_unref (base_uri);
 +  gst_uri_unref (uri);
++#ifdef TIZEN_FEATURE_ADAPTIVE_MODIFICATION
++  download =
++      gst_uri_downloader_fetch_uri (client->downloader,
++      uri_string, client->mpd_uri, NULL, NULL, DEFAULT_ADAPTIVE_RETRY,
++      DEFAULT_ADAPTIVE_TIMEOUT, TRUE, FALSE, TRUE, &err);
++#else
 +  download =
 +      gst_uri_downloader_fetch_uri (client->downloader,
 +      uri_string, client->mpd_uri, TRUE, FALSE, TRUE, &err);
++#endif
 +  g_free (uri_string);
 +
 +  if (!download) {
 +    GST_ERROR ("Failed to download external SegmentList node at '%s': %s",
 +        segment_list->xlink_href, err->message);
 +    g_clear_error (&err);
 +    return NULL;
 +  }
 +
 +  segment_list_buffer = gst_fragment_get_buffer (download);
 +  g_object_unref (download);
 +
 +  gst_buffer_map (segment_list_buffer, &map, GST_MAP_READ);
 +
 +  new_segment_list =
 +      gst_mpdparser_get_external_segment_list ((const gchar *) map.data,
 +      map.size, parent);
 +
 +  if (segment_list_buffer) {
 +    gst_buffer_unmap (segment_list_buffer, &map);
 +    gst_buffer_unref (segment_list_buffer);
 +  }
 +
 +  return new_segment_list;
 +}
 +
 +static GstMPDSegmentBaseNode *
 +gst_mpd_client_get_segment_base (GstMPDPeriodNode * Period,
 +    GstMPDAdaptationSetNode * AdaptationSet,
 +    GstMPDRepresentationNode * Representation)
 +{
 +  GstMPDSegmentBaseNode *SegmentBase = NULL;
 +
 +  if (Representation && Representation->SegmentBase) {
 +    SegmentBase = Representation->SegmentBase;
 +  } else if (AdaptationSet && AdaptationSet->SegmentBase) {
 +    SegmentBase = AdaptationSet->SegmentBase;
 +  } else if (Period && Period->SegmentBase) {
 +    SegmentBase = Period->SegmentBase;
 +  }
 +  /* the SegmentBase element could be encoded also inside a SegmentList element */
 +  if (SegmentBase == NULL) {
 +    if (Representation && Representation->SegmentList
 +        && GST_MPD_MULT_SEGMENT_BASE_NODE (Representation->SegmentList)
 +        && GST_MPD_MULT_SEGMENT_BASE_NODE (Representation->
 +            SegmentList)->SegmentBase) {
 +      SegmentBase =
 +          GST_MPD_MULT_SEGMENT_BASE_NODE (Representation->
 +          SegmentList)->SegmentBase;
 +    } else if (AdaptationSet && AdaptationSet->SegmentList
 +        && GST_MPD_MULT_SEGMENT_BASE_NODE (AdaptationSet->SegmentList)
 +        && GST_MPD_MULT_SEGMENT_BASE_NODE (AdaptationSet->
 +            SegmentList)->SegmentBase) {
 +      SegmentBase =
 +          GST_MPD_MULT_SEGMENT_BASE_NODE (AdaptationSet->
 +          SegmentList)->SegmentBase;
 +    } else if (Period && Period->SegmentList
 +        && GST_MPD_MULT_SEGMENT_BASE_NODE (Period->SegmentList)
 +        && GST_MPD_MULT_SEGMENT_BASE_NODE (Period->SegmentList)->SegmentBase) {
 +      SegmentBase =
 +          GST_MPD_MULT_SEGMENT_BASE_NODE (Period->SegmentList)->SegmentBase;
 +    }
 +  }
 +
 +  return SegmentBase;
 +}
 +
 +static GstMPDSegmentListNode *
 +gst_mpd_client_get_segment_list (GstMPDClient * client,
 +    GstMPDPeriodNode * Period, GstMPDAdaptationSetNode * AdaptationSet,
 +    GstMPDRepresentationNode * Representation)
 +{
 +  GstMPDSegmentListNode **SegmentList;
 +  GstMPDSegmentListNode *ParentSegmentList = NULL;
 +
 +  if (Representation && Representation->SegmentList) {
 +    SegmentList = &Representation->SegmentList;
 +    ParentSegmentList = AdaptationSet->SegmentList;
 +  } else if (AdaptationSet && AdaptationSet->SegmentList) {
 +    SegmentList = &AdaptationSet->SegmentList;
 +    ParentSegmentList = Period->SegmentList;
 +    Representation = NULL;
 +  } else {
 +    Representation = NULL;
 +    AdaptationSet = NULL;
 +    SegmentList = &Period->SegmentList;
 +  }
 +
 +  /* Resolve external segment list here. */
 +  if (*SegmentList && (*SegmentList)->xlink_href) {
 +    GstMPDSegmentListNode *new_segment_list;
 +
 +    /* TODO: Use SegmentList of parent if
 +     * - Parent has its own SegmentList
 +     * - Fail to get SegmentList from external xml
 +     */
 +    new_segment_list =
 +        gst_mpd_client_fetch_external_segment_list (client, Period,
 +        AdaptationSet, Representation, ParentSegmentList, *SegmentList);
 +
 +    gst_mpd_segment_list_node_free (*SegmentList);
 +    *SegmentList = new_segment_list;
 +  }
 +
 +  return *SegmentList;
 +}
 +
 +static GstClockTime
 +gst_mpd_client_get_segment_duration (GstMPDClient * client,
 +    GstActiveStream * stream, guint64 * scale_dur)
 +{
 +  GstStreamPeriod *stream_period;
 +  GstMPDMultSegmentBaseNode *base = NULL;
 +  GstClockTime duration = 0;
 +
 +  g_return_val_if_fail (stream != NULL, GST_CLOCK_TIME_NONE);
 +  stream_period = gst_mpd_client_get_stream_period (client);
 +  g_return_val_if_fail (stream_period != NULL, GST_CLOCK_TIME_NONE);
 +
 +  if (stream->cur_segment_list) {
 +    base = GST_MPD_MULT_SEGMENT_BASE_NODE (stream->cur_segment_list);
 +  } else if (stream->cur_seg_template) {
 +    base = GST_MPD_MULT_SEGMENT_BASE_NODE (stream->cur_seg_template);
 +  }
 +
 +  if (base == NULL || base->SegmentBase == NULL) {
 +    /* this may happen when we have a single segment */
 +    duration = stream_period->duration;
 +    if (scale_dur)
 +      *scale_dur = duration;
 +  } else {
 +    /* duration is guint so this cannot overflow */
 +    duration = base->duration * GST_SECOND;
 +    if (scale_dur)
 +      *scale_dur = duration;
 +    duration /= base->SegmentBase->timescale;
 +  }
 +
 +  return duration;
 +}
 +
 +void
 +gst_mpd_client_active_streams_free (GstMPDClient * client)
 +{
 +  if (client->active_streams) {
 +    g_list_foreach (client->active_streams,
 +        (GFunc) gst_mpdparser_free_active_stream, NULL);
 +    g_list_free (client->active_streams);
 +    client->active_streams = NULL;
 +  }
 +}
 +
 +static void
 +gst_mpd_client_finalize (GObject * object)
 +{
 +  GstMPDClient *client = GST_MPD_CLIENT (object);
 +
 +  if (client->mpd_root_node)
 +    gst_mpd_root_node_free (client->mpd_root_node);
 +
 +  if (client->periods) {
 +    g_list_free_full (client->periods,
 +        (GDestroyNotify) gst_mpdparser_free_stream_period);
 +  }
 +
 +  gst_mpd_client_active_streams_free (client);
 +
 +  g_free (client->mpd_uri);
 +  client->mpd_uri = NULL;
 +  g_free (client->mpd_base_uri);
 +  client->mpd_base_uri = NULL;
 +
 +  if (client->downloader)
 +    gst_object_unref (client->downloader);
 +  client->downloader = NULL;
 +
 +  G_OBJECT_CLASS (gst_mpd_client_parent_class)->finalize (object);
 +}
 +
 +static void
 +gst_mpd_client_class_init (GstMPDClientClass * klass)
 +{
 +  GObjectClass *object_class = G_OBJECT_CLASS (klass);
 +  object_class->finalize = gst_mpd_client_finalize;
 +}
 +
 +static void
 +gst_mpd_client_init (GstMPDClient * client)
 +{
 +}
 +
 +GstMPDClient *
 +gst_mpd_client_new (void)
 +{
 +  GST_DEBUG_CATEGORY_INIT (gst_dash_mpd_client_debug, "dashmpdclient", 0,
 +      "DashmMpdClient");
 +  return g_object_new (GST_TYPE_MPD_CLIENT, NULL);
 +}
 +
 +GstMPDClient *
 +gst_mpd_client_new_static (void)
 +{
 +  GstMPDClient *client = gst_mpd_client_new ();
 +
 +  client->mpd_root_node = gst_mpd_root_node_new ();
 +  client->mpd_root_node->default_namespace =
 +      g_strdup ("urn:mpeg:dash:schema:mpd:2011");
 +  client->mpd_root_node->profiles =
 +      g_strdup ("urn:mpeg:dash:profile:isoff-main:2011");
 +  client->mpd_root_node->type = GST_MPD_FILE_TYPE_STATIC;
 +  client->mpd_root_node->minBufferTime = 1500;
 +
 +  return client;
 +}
 +
 +void
 +gst_mpd_client_free (GstMPDClient * client)
 +{
 +  if (client)
 +    gst_object_unref (client);
 +}
 +
 +gboolean
 +gst_mpd_client_parse (GstMPDClient * client, const gchar * data, gint size)
 +{
 +  gboolean ret = FALSE;
 +
 +
 +  ret = gst_mpdparser_get_mpd_root_node (&client->mpd_root_node, data, size);
 +
 +  if (ret) {
 +    gst_mpd_client_check_profiles (client);
 +    gst_mpd_client_fetch_on_load_external_resources (client);
 +  }
 +
 +  return ret;
 +}
 +
 +
 +gboolean
 +gst_mpd_client_get_xml_content (GstMPDClient * client, gchar ** data,
 +    gint * size)
 +{
 +  gboolean ret = FALSE;
 +
 +  g_return_val_if_fail (client != NULL, ret);
 +  g_return_val_if_fail (client->mpd_root_node != NULL, ret);
 +
 +  ret = gst_mpd_node_get_xml_buffer (GST_MPD_NODE (client->mpd_root_node),
 +      data, (int *) size);
 +
 +  return ret;
 +}
 +
 +GstDateTime *
 +gst_mpd_client_get_availability_start_time (GstMPDClient * client)
 +{
 +  GstDateTime *start_time;
 +
 +  if (client == NULL)
 +    return (GstDateTime *) NULL;
 +
 +  start_time = client->mpd_root_node->availabilityStartTime;
 +  if (start_time)
 +    gst_date_time_ref (start_time);
 +  return start_time;
 +}
 +
 +void
 +gst_mpd_client_set_uri_downloader (GstMPDClient * client,
 +    GstUriDownloader * downloader)
 +{
 +  if (client->downloader)
 +    gst_object_unref (client->downloader);
 +  client->downloader = gst_object_ref (downloader);
 +}
 +
 +void
 +gst_mpd_client_check_profiles (GstMPDClient * client)
 +{
 +  GST_DEBUG ("Profiles: %s",
 +      client->mpd_root_node->profiles ? client->mpd_root_node->
 +      profiles : "<none>");
 +
 +  if (!client->mpd_root_node->profiles)
 +    return;
 +
 +  if (g_strstr_len (client->mpd_root_node->profiles, -1,
 +          "urn:mpeg:dash:profile:isoff-on-demand:2011")) {
 +    client->profile_isoff_ondemand = TRUE;
 +    GST_DEBUG ("Found ISOFF on demand profile (2011)");
 +  }
 +}
 +
 +void
 +gst_mpd_client_fetch_on_load_external_resources (GstMPDClient * client)
 +{
 +  GList *l;
 +
 +  for (l = client->mpd_root_node->Periods; l; /* explicitly advanced below */ ) {
 +    GstMPDPeriodNode *period = l->data;
 +    GList *m;
 +
 +    if (period->xlink_href && period->actuate == GST_MPD_XLINK_ACTUATE_ON_LOAD) {
 +      GList *new_periods, *prev, *next;
 +
 +      new_periods = gst_mpd_client_fetch_external_periods (client, period);
 +
 +      prev = l->prev;
 +      client->mpd_root_node->Periods =
 +          g_list_delete_link (client->mpd_root_node->Periods, l);
 +      gst_mpd_period_node_free (period);
 +      period = NULL;
 +
 +      /* Get new next node, we will insert before this */
 +      if (prev)
 +        next = prev->next;
 +      else
 +        next = client->mpd_root_node->Periods;
 +
 +      while (new_periods) {
 +        client->mpd_root_node->Periods =
 +            g_list_insert_before (client->mpd_root_node->Periods, next,
 +            new_periods->data);
 +        new_periods = g_list_delete_link (new_periods, new_periods);
 +      }
 +      next = NULL;
 +
 +      /* Update our iterator to the first new period if any, or the next */
 +      if (prev)
 +        l = prev->next;
 +      else
 +        l = client->mpd_root_node->Periods;
 +
 +      continue;
 +    }
 +
 +    if (period->SegmentList && period->SegmentList->xlink_href
 +        && period->SegmentList->actuate == GST_MPD_XLINK_ACTUATE_ON_LOAD) {
 +      GstMPDSegmentListNode *new_segment_list;
 +
 +      new_segment_list =
 +          gst_mpd_client_fetch_external_segment_list (client, period, NULL,
 +          NULL, NULL, period->SegmentList);
 +
 +      gst_mpd_segment_list_node_free (period->SegmentList);
 +      period->SegmentList = new_segment_list;
 +    }
 +
 +    for (m = period->AdaptationSets; m; /* explicitly advanced below */ ) {
 +      GstMPDAdaptationSetNode *adapt_set = m->data;
 +      GList *n;
 +
 +      if (adapt_set->xlink_href
 +          && adapt_set->actuate == GST_MPD_XLINK_ACTUATE_ON_LOAD) {
 +        GList *new_adapt_sets, *prev, *next;
 +
 +        new_adapt_sets =
 +            gst_mpd_client_fetch_external_adaptation_set (client, period,
 +            adapt_set);
 +
 +        prev = m->prev;
 +        period->AdaptationSets = g_list_delete_link (period->AdaptationSets, m);
 +        gst_mpd_adaptation_set_node_free (adapt_set);
 +        adapt_set = NULL;
 +
 +        /* Get new next node, we will insert before this */
 +        if (prev)
 +          next = prev->next;
 +        else
 +          next = period->AdaptationSets;
 +
 +        while (new_adapt_sets) {
 +          period->AdaptationSets =
 +              g_list_insert_before (period->AdaptationSets, next,
 +              new_adapt_sets->data);
 +          new_adapt_sets = g_list_delete_link (new_adapt_sets, new_adapt_sets);
 +        }
 +        next = NULL;
 +
 +        /* Update our iterator to the first new adapt_set if any, or the next */
 +        if (prev)
 +          m = prev->next;
 +        else
 +          m = period->AdaptationSets;
 +
 +        continue;
 +      }
 +
 +      if (adapt_set->SegmentList && adapt_set->SegmentList->xlink_href
 +          && adapt_set->SegmentList->actuate == GST_MPD_XLINK_ACTUATE_ON_LOAD) {
 +        GstMPDSegmentListNode *new_segment_list;
 +
 +        new_segment_list =
 +            gst_mpd_client_fetch_external_segment_list (client, period,
 +            adapt_set, NULL, period->SegmentList, adapt_set->SegmentList);
 +
 +        gst_mpd_segment_list_node_free (adapt_set->SegmentList);
 +        adapt_set->SegmentList = new_segment_list;
 +      }
 +
 +      for (n = adapt_set->Representations; n; n = n->next) {
 +        GstMPDRepresentationNode *representation = n->data;
 +
 +        if (representation->SegmentList
 +            && representation->SegmentList->xlink_href
 +            && representation->SegmentList->actuate ==
 +            GST_MPD_XLINK_ACTUATE_ON_LOAD) {
 +
 +          GstMPDSegmentListNode *new_segment_list;
 +
 +          new_segment_list =
 +              gst_mpd_client_fetch_external_segment_list (client, period,
 +              adapt_set, representation, adapt_set->SegmentList,
 +              representation->SegmentList);
 +
 +          gst_mpd_segment_list_node_free (representation->SegmentList);
 +          representation->SegmentList = new_segment_list;
 +
 +        }
 +      }
 +
 +      m = m->next;
 +    }
 +
 +    l = l->next;
 +  }
 +}
 +
 +
 +static GstStreamPeriod *
 +gst_mpd_client_get_stream_period (GstMPDClient * client)
 +{
 +  g_return_val_if_fail (client != NULL, NULL);
 +  g_return_val_if_fail (client->periods != NULL, NULL);
 +
 +  return g_list_nth_data (client->periods, client->period_idx);
 +}
 +
 +const gchar *
 +gst_mpd_client_get_baseURL (GstMPDClient * client, guint indexStream)
 +{
 +  GstActiveStream *stream;
 +
 +  g_return_val_if_fail (client != NULL, NULL);
 +  g_return_val_if_fail (client->active_streams != NULL, NULL);
 +  stream = g_list_nth_data (client->active_streams, indexStream);
 +  g_return_val_if_fail (stream != NULL, NULL);
 +
 +  return stream->baseURL;
 +}
 +
 +/* select a stream and extract the baseURL (if present) */
 +gchar *
 +gst_mpd_client_parse_baseURL (GstMPDClient * client, GstActiveStream * stream,
 +    gchar ** query)
 +{
 +  GstStreamPeriod *stream_period;
 +  static const gchar empty[] = "";
 +  gchar *ret = NULL;
 +  GstUri *abs_url;
 +
 +  g_return_val_if_fail (stream != NULL, g_strdup (empty));
 +  stream_period = gst_mpd_client_get_stream_period (client);
 +  g_return_val_if_fail (stream_period != NULL, g_strdup (empty));
 +  g_return_val_if_fail (stream_period->period != NULL, g_strdup (empty));
 +
 +  /* NULLify query return before we start */
 +  if (query)
 +    *query = NULL;
 +
 +  /* initialise base url */
 +  abs_url =
 +      gst_uri_from_string (client->mpd_base_uri ? client->
 +      mpd_base_uri : client->mpd_uri);
 +
 +  /* combine a BaseURL at the MPD level with the current base url */
 +  abs_url =
 +      gst_mpd_helper_combine_urls (abs_url, client->mpd_root_node->BaseURLs,
 +      query, stream->baseURL_idx);
 +
 +  /* combine a BaseURL at the Period level with the current base url */
 +  abs_url =
 +      gst_mpd_helper_combine_urls (abs_url, stream_period->period->BaseURLs,
 +      query, stream->baseURL_idx);
 +
 +  GST_DEBUG ("Current adaptation set id %i (%s)", stream->cur_adapt_set->id,
 +      stream->cur_adapt_set->contentType);
 +  /* combine a BaseURL at the AdaptationSet level with the current base url */
 +  abs_url =
 +      gst_mpd_helper_combine_urls (abs_url, stream->cur_adapt_set->BaseURLs,
 +      query, stream->baseURL_idx);
 +
 +  /* combine a BaseURL at the Representation level with the current base url */
 +  abs_url =
 +      gst_mpd_helper_combine_urls (abs_url,
 +      stream->cur_representation->BaseURLs, query, stream->baseURL_idx);
 +
 +  ret = gst_uri_to_string (abs_url);
 +  gst_uri_unref (abs_url);
 +
 +  return ret;
 +}
 +
 +static GstClockTime
 +gst_mpd_client_get_segment_end_time (GstMPDClient * client,
 +    GPtrArray * segments, const GstMediaSegment * segment, gint index)
 +{
 +  const GstStreamPeriod *stream_period;
 +  GstClockTime end;
 +
 +  if (segment->repeat >= 0)
 +    return segment->start + (segment->repeat + 1) * segment->duration;
 +
 +  if (index < segments->len - 1) {
 +    const GstMediaSegment *next_segment =
 +        g_ptr_array_index (segments, index + 1);
 +    end = next_segment->start;
 +  } else {
 +    stream_period = gst_mpd_client_get_stream_period (client);
 +    end = stream_period->start + stream_period->duration;
 +  }
 +  return end;
 +}
 +
 +static gboolean
 +gst_mpd_client_add_media_segment (GstActiveStream * stream,
 +    GstMPDSegmentURLNode * url_node, guint number, gint repeat,
 +    guint64 scale_start, guint64 scale_duration,
 +    GstClockTime start, GstClockTime duration)
 +{
 +  GstMediaSegment *media_segment;
 +
 +  g_return_val_if_fail (stream->segments != NULL, FALSE);
 +
 +  media_segment = g_slice_new0 (GstMediaSegment);
 +
 +  media_segment->SegmentURL = url_node;
 +  media_segment->number = number;
 +  media_segment->scale_start = scale_start;
 +  media_segment->scale_duration = scale_duration;
 +  media_segment->start = start;
 +  media_segment->duration = duration;
 +  media_segment->repeat = repeat;
 +
 +  g_ptr_array_add (stream->segments, media_segment);
 +  GST_LOG ("Added new segment: number %d, repeat %d, "
 +      "ts: %" GST_TIME_FORMAT ", dur: %"
 +      GST_TIME_FORMAT, number, repeat,
 +      GST_TIME_ARGS (start), GST_TIME_ARGS (duration));
 +
 +  return TRUE;
 +}
 +
 +static void
 +gst_mpd_client_stream_update_presentation_time_offset (GstMPDClient * client,
 +    GstActiveStream * stream)
 +{
 +  GstMPDSegmentBaseNode *segbase = NULL;
 +
 +  /* Find the used segbase */
 +  if (stream->cur_segment_list) {
 +    segbase =
 +        GST_MPD_MULT_SEGMENT_BASE_NODE (stream->cur_segment_list)->SegmentBase;
 +  } else if (stream->cur_seg_template) {
 +    segbase =
 +        GST_MPD_MULT_SEGMENT_BASE_NODE (stream->cur_seg_template)->SegmentBase;
 +  } else if (stream->cur_segment_base) {
 +    segbase = stream->cur_segment_base;
 +  }
 +
 +  if (segbase) {
 +    /* Avoid overflows */
 +    stream->presentationTimeOffset =
 +        gst_util_uint64_scale (segbase->presentationTimeOffset, GST_SECOND,
 +        segbase->timescale);
 +  } else {
 +    stream->presentationTimeOffset = 0;
 +  }
 +
 +  GST_LOG ("Setting stream's presentation time offset to %" GST_TIME_FORMAT,
 +      GST_TIME_ARGS (stream->presentationTimeOffset));
 +}
 +
 +gboolean
 +gst_mpd_client_setup_representation (GstMPDClient * client,
 +    GstActiveStream * stream, GstMPDRepresentationNode * representation)
 +{
 +  GstStreamPeriod *stream_period;
 +  GList *rep_list;
 +  GstClockTime PeriodStart, PeriodEnd, start_time, duration;
 +  guint i;
 +  guint64 start;
 +
 +  if (stream->cur_adapt_set == NULL) {
 +    GST_WARNING ("No valid AdaptationSet node in the MPD file, aborting...");
 +    return FALSE;
 +  }
 +
 +  rep_list = stream->cur_adapt_set->Representations;
 +  stream->cur_representation = representation;
 +  stream->representation_idx = g_list_index (rep_list, representation);
 +
 +  /* clean the old segment list, if any */
 +  if (stream->segments) {
 +    g_ptr_array_unref (stream->segments);
 +    stream->segments = NULL;
 +  }
 +
 +  stream_period = gst_mpd_client_get_stream_period (client);
 +  g_return_val_if_fail (stream_period != NULL, FALSE);
 +  g_return_val_if_fail (stream_period->period != NULL, FALSE);
 +
 +  PeriodStart = stream_period->start;
 +  if (GST_CLOCK_TIME_IS_VALID (stream_period->duration))
 +    PeriodEnd = stream_period->start + stream_period->duration;
 +  else
 +    PeriodEnd = GST_CLOCK_TIME_NONE;
 +
 +  GST_LOG ("Building segment list for Period from %" GST_TIME_FORMAT " to %"
 +      GST_TIME_FORMAT, GST_TIME_ARGS (PeriodStart), GST_TIME_ARGS (PeriodEnd));
 +
 +  if (representation->SegmentBase != NULL
 +      || representation->SegmentList != NULL) {
 +    GList *SegmentURL;
 +
 +    /* We have a fixed list of segments for any of the cases here,
 +     * init the segments list */
 +    gst_mpdparser_init_active_stream_segments (stream);
 +
 +    /* get the first segment_base of the selected representation */
 +    if ((stream->cur_segment_base =
 +            gst_mpd_client_get_segment_base (stream_period->period,
 +                stream->cur_adapt_set, representation)) == NULL) {
 +      GST_DEBUG ("No useful SegmentBase node for the current Representation");
 +    }
 +
 +    /* get the first segment_list of the selected representation */
 +    if ((stream->cur_segment_list =
 +            gst_mpd_client_get_segment_list (client, stream_period->period,
 +                stream->cur_adapt_set, representation)) == NULL) {
 +      GST_DEBUG ("No useful SegmentList node for the current Representation");
 +      /* here we should have a single segment for each representation, whose URL is encoded in the baseURL element */
 +      if (!gst_mpd_client_add_media_segment (stream, NULL, 1, 0, 0,
 +              PeriodEnd - PeriodStart, PeriodStart, PeriodEnd - PeriodStart)) {
 +        return FALSE;
 +      }
 +    } else {
 +      /* build the list of GstMediaSegment nodes from the SegmentList node */
 +      SegmentURL = stream->cur_segment_list->SegmentURL;
 +      if (SegmentURL == NULL) {
 +        GST_WARNING
 +            ("No valid list of SegmentURL nodes in the MPD file, aborting...");
 +        return FALSE;
 +      }
 +
 +      /* build segment list */
 +      i = GST_MPD_MULT_SEGMENT_BASE_NODE (stream->
 +          cur_segment_list)->startNumber;
 +      start = 0;
 +      start_time = PeriodStart;
 +
 +      GST_LOG ("Building media segment list using a SegmentList node");
 +      if (GST_MPD_MULT_SEGMENT_BASE_NODE (stream->
 +              cur_segment_list)->SegmentTimeline) {
 +        GstMPDSegmentTimelineNode *timeline;
 +        GstMPDSNode *S;
 +        GList *list;
 +        GstClockTime presentationTimeOffset;
 +        GstMPDSegmentBaseNode *segbase;
 +
 +        segbase =
 +            GST_MPD_MULT_SEGMENT_BASE_NODE (stream->
 +            cur_segment_list)->SegmentBase;
 +        presentationTimeOffset =
 +            gst_util_uint64_scale (segbase->presentationTimeOffset, GST_SECOND,
 +            segbase->timescale);
 +        GST_LOG ("presentationTimeOffset = %" G_GUINT64_FORMAT,
 +            presentationTimeOffset);
 +
 +        timeline =
 +            GST_MPD_MULT_SEGMENT_BASE_NODE (stream->
 +            cur_segment_list)->SegmentTimeline;
 +        for (list = g_queue_peek_head_link (&timeline->S); list;
 +            list = g_list_next (list)) {
 +          guint timescale;
 +
 +          S = (GstMPDSNode *) list->data;
 +          GST_LOG ("Processing S node: d=%" G_GUINT64_FORMAT " r=%d t=%"
 +              G_GUINT64_FORMAT, S->d, S->r, S->t);
 +          timescale =
 +              GST_MPD_MULT_SEGMENT_BASE_NODE (stream->
 +              cur_segment_list)->SegmentBase->timescale;
 +          duration = gst_util_uint64_scale (S->d, GST_SECOND, timescale);
 +
 +          if (S->t > 0) {
 +            start = S->t;
 +            start_time = gst_util_uint64_scale (S->t, GST_SECOND, timescale)
 +                + PeriodStart - presentationTimeOffset;
 +          }
 +
 +          if (!SegmentURL) {
 +            GST_WARNING
 +                ("SegmentTimeline does not have a matching SegmentURL, aborting...");
 +            return FALSE;
 +          }
 +
 +          if (!gst_mpd_client_add_media_segment (stream, SegmentURL->data, i,
 +                  S->r, start, S->d, start_time, duration)) {
 +            return FALSE;
 +          }
 +          i += S->r + 1;
 +          start_time += duration * (S->r + 1);
 +          start += S->d * (S->r + 1);
 +          SegmentURL = g_list_next (SegmentURL);
 +        }
 +      } else {
 +        guint64 scale_dur;
 +
 +        duration =
 +            gst_mpd_client_get_segment_duration (client, stream, &scale_dur);
 +        if (!GST_CLOCK_TIME_IS_VALID (duration))
 +          return FALSE;
 +
 +        while (SegmentURL) {
 +          if (!gst_mpd_client_add_media_segment (stream, SegmentURL->data, i,
 +                  0, start, scale_dur, start_time, duration)) {
 +            return FALSE;
 +          }
 +          i++;
 +          start += scale_dur;
 +          start_time += duration;
 +          SegmentURL = g_list_next (SegmentURL);
 +        }
 +      }
 +    }
 +  } else {
 +    if (representation->SegmentTemplate != NULL) {
 +      stream->cur_seg_template = representation->SegmentTemplate;
 +    } else if (stream->cur_adapt_set->SegmentTemplate != NULL) {
 +      stream->cur_seg_template = stream->cur_adapt_set->SegmentTemplate;
 +    } else if (stream_period->period->SegmentTemplate != NULL) {
 +      stream->cur_seg_template = stream_period->period->SegmentTemplate;
 +    }
 +
 +    if (stream->cur_seg_template == NULL) {
 +
 +      gst_mpdparser_init_active_stream_segments (stream);
 +      /* here we should have a single segment for each representation, whose URL is encoded in the baseURL element */
 +      if (!gst_mpd_client_add_media_segment (stream, NULL, 1, 0, 0,
 +              PeriodEnd - PeriodStart, 0, PeriodEnd - PeriodStart)) {
 +        return FALSE;
 +      }
 +    } else {
 +      GstClockTime presentationTimeOffset;
 +      GstMPDMultSegmentBaseNode *mult_seg =
 +          GST_MPD_MULT_SEGMENT_BASE_NODE (stream->cur_seg_template);
 +      presentationTimeOffset =
 +          gst_util_uint64_scale (mult_seg->SegmentBase->presentationTimeOffset,
 +          GST_SECOND, mult_seg->SegmentBase->timescale);
 +      GST_LOG ("presentationTimeOffset = %" GST_TIME_FORMAT,
 +          GST_TIME_ARGS (presentationTimeOffset));
 +      /* build segment list */
 +      i = mult_seg->startNumber;
 +      start = 0;
 +      start_time = 0;
 +
 +      GST_LOG ("Building media segment list using this template: %s",
 +          stream->cur_seg_template->media);
 +
 +      if (mult_seg->SegmentTimeline) {
 +        GstMPDSegmentTimelineNode *timeline;
 +        GstMPDSNode *S;
 +        GList *list;
 +
 +        timeline = mult_seg->SegmentTimeline;
 +        gst_mpdparser_init_active_stream_segments (stream);
 +        for (list = g_queue_peek_head_link (&timeline->S); list;
 +            list = g_list_next (list)) {
 +          guint timescale;
 +
 +          S = (GstMPDSNode *) list->data;
 +          GST_LOG ("Processing S node: d=%" G_GUINT64_FORMAT " r=%u t=%"
 +              G_GUINT64_FORMAT, S->d, S->r, S->t);
 +          timescale = mult_seg->SegmentBase->timescale;
 +          duration = gst_util_uint64_scale (S->d, GST_SECOND, timescale);
 +          if (S->t > 0) {
 +            start = S->t;
 +            start_time = gst_util_uint64_scale (S->t, GST_SECOND, timescale)
 +                + PeriodStart - presentationTimeOffset;
 +          }
 +
 +          if (!gst_mpd_client_add_media_segment (stream, NULL, i, S->r, start,
 +                  S->d, start_time, duration)) {
 +            return FALSE;
 +          }
 +          i += S->r + 1;
 +          start += S->d * (S->r + 1);
 +          start_time += duration * (S->r + 1);
 +        }
 +      } else {
 +        /* NOP - The segment is created on demand with the template, no need
 +         * to build a list */
 +      }
 +    }
 +  }
 +
 +  /* clip duration of segments to stop at period end */
 +  if (stream->segments && stream->segments->len) {
 +    if (GST_CLOCK_TIME_IS_VALID (PeriodEnd)) {
 +      guint n;
 +
 +      for (n = 0; n < stream->segments->len; ++n) {
 +        GstMediaSegment *media_segment =
 +            g_ptr_array_index (stream->segments, n);
 +        if (media_segment) {
 +          if (media_segment->start + media_segment->duration > PeriodEnd) {
 +            GstClockTime stop = PeriodEnd;
 +            if (n < stream->segments->len - 1) {
 +              GstMediaSegment *next_segment =
 +                  g_ptr_array_index (stream->segments, n + 1);
 +              if (next_segment && next_segment->start < PeriodEnd)
 +                stop = next_segment->start;
 +            }
 +            media_segment->duration =
 +                media_segment->start > stop ? 0 : stop - media_segment->start;
 +            GST_LOG ("Fixed duration of segment %u: %" GST_TIME_FORMAT, n,
 +                GST_TIME_ARGS (media_segment->duration));
 +
 +            /* If the segment was clipped entirely, we discard it and all
 +             * subsequent ones */
 +            if (media_segment->duration == 0) {
 +              GST_WARNING ("Discarding %u segments outside period",
 +                  stream->segments->len - n);
 +              /* _set_size should properly unref elements */
 +              g_ptr_array_set_size (stream->segments, n);
 +              break;
 +            }
 +          }
 +        }
 +      }
 +    }
 +#ifndef GST_DISABLE_GST_DEBUG
 +    if (stream->segments->len > 0) {
 +      GstMediaSegment *last_media_segment =
 +          g_ptr_array_index (stream->segments, stream->segments->len - 1);
 +      GST_LOG ("Built a list of %d segments", last_media_segment->number);
 +    } else {
 +      GST_LOG ("All media segments were clipped");
 +    }
 +#endif
 +  }
 +
 +  g_free (stream->baseURL);
 +  g_free (stream->queryURL);
 +  stream->baseURL =
 +      gst_mpd_client_parse_baseURL (client, stream, &stream->queryURL);
 +
 +  gst_mpd_client_stream_update_presentation_time_offset (client, stream);
 +
 +  return TRUE;
 +}
 +
 +#define CUSTOM_WRAPPER_START "<custom_wrapper>"
 +#define CUSTOM_WRAPPER_END "</custom_wrapper>"
 +
 +static GList *
 +gst_mpd_client_fetch_external_periods (GstMPDClient * client,
 +    GstMPDPeriodNode * period_node)
 +{
 +  GstFragment *download;
 +  GstAdapter *adapter;
 +  GstBuffer *period_buffer;
 +  GError *err = NULL;
 +
 +  GstUri *base_uri, *uri;
 +  gchar *query = NULL;
 +  gchar *uri_string, *wrapper;
 +  GList *new_periods = NULL;
 +  const gchar *data;
 +
 +  /* ISO/IEC 23009-1:2014 5.5.3 4)
 +   * Remove nodes that resolve to nothing when resolving
 +   */
 +  if (strcmp (period_node->xlink_href,
 +          "urn:mpeg:dash:resolve-to-zero:2013") == 0) {
 +    return NULL;
 +  }
 +
 +  if (!client->downloader) {
 +    return NULL;
 +  }
 +
 +  /* Build absolute URI */
 +
 +  /* Get base URI at the MPD level */
 +  base_uri =
 +      gst_uri_from_string (client->mpd_base_uri ? client->
 +      mpd_base_uri : client->mpd_uri);
 +
 +  /* combine a BaseURL at the MPD level with the current base url */
 +  base_uri =
 +      gst_mpd_helper_combine_urls (base_uri, client->mpd_root_node->BaseURLs,
 +      &query, 0);
 +  uri = gst_uri_from_string_with_base (base_uri, period_node->xlink_href);
 +  if (query)
 +    gst_uri_set_query_string (uri, query);
 +  g_free (query);
 +  uri_string = gst_uri_to_string (uri);
 +  gst_uri_unref (base_uri);
 +  gst_uri_unref (uri);
 +
++#ifdef TIZEN_FEATURE_ADAPTIVE_MODIFICATION
++  download =
++      gst_uri_downloader_fetch_uri (client->downloader,
++      uri_string, client->mpd_uri, NULL, NULL, DEFAULT_ADAPTIVE_RETRY,
++      DEFAULT_ADAPTIVE_TIMEOUT, TRUE, FALSE, TRUE, &err);
++#else
 +  download =
 +      gst_uri_downloader_fetch_uri (client->downloader,
 +      uri_string, client->mpd_uri, TRUE, FALSE, TRUE, &err);
++#endif
 +  g_free (uri_string);
 +
 +  if (!download) {
 +    GST_ERROR ("Failed to download external Period node at '%s': %s",
 +        period_node->xlink_href, err->message);
 +    g_clear_error (&err);
 +    return NULL;
 +  }
 +
 +  period_buffer = gst_fragment_get_buffer (download);
 +  g_object_unref (download);
 +
 +  /* external xml could have multiple period without root xmlNode.
 +   * To avoid xml parsing error caused by no root node, wrapping it with
 +   * custom root node */
 +  adapter = gst_adapter_new ();
 +
 +  wrapper = g_new (gchar, strlen (CUSTOM_WRAPPER_START));
 +  memcpy (wrapper, CUSTOM_WRAPPER_START, strlen (CUSTOM_WRAPPER_START));
 +  gst_adapter_push (adapter,
 +      gst_buffer_new_wrapped (wrapper, strlen (CUSTOM_WRAPPER_START)));
 +
 +  gst_adapter_push (adapter, period_buffer);
 +
 +  wrapper = g_strdup (CUSTOM_WRAPPER_END);
 +  gst_adapter_push (adapter,
 +      gst_buffer_new_wrapped (wrapper, strlen (CUSTOM_WRAPPER_END) + 1));
 +
 +  data = gst_adapter_map (adapter, gst_adapter_available (adapter));
 +
 +  new_periods =
 +      gst_mpdparser_get_external_periods (data,
 +      gst_adapter_available (adapter));
 +
 +  gst_adapter_unmap (adapter);
 +  gst_adapter_clear (adapter);
 +  gst_object_unref (adapter);
 +
 +  return new_periods;
 +}
 +
 +gboolean
 +gst_mpd_client_setup_media_presentation (GstMPDClient * client,
 +    GstClockTime time, gint period_idx, const gchar * period_id)
 +{
 +  GstStreamPeriod *stream_period;
 +  GstClockTime start, duration;
 +  GList *list, *next;
 +  guint idx;
 +  gboolean ret = FALSE;
 +
 +  g_return_val_if_fail (client != NULL, FALSE);
 +  g_return_val_if_fail (client->mpd_root_node != NULL, FALSE);
 +
 +  /* Check if we set up the media presentation far enough already */
 +  for (list = client->periods; list; list = list->next) {
 +    GstStreamPeriod *stream_period = list->data;
 +
 +    if ((time != GST_CLOCK_TIME_NONE
 +            && stream_period->duration != GST_CLOCK_TIME_NONE
 +            && stream_period->start + stream_period->duration >= time)
 +        || (time != GST_CLOCK_TIME_NONE && stream_period->start >= time))
 +      return TRUE;
 +
 +    if (period_idx != -1 && stream_period->number >= period_idx)
 +      return TRUE;
 +
 +    if (period_id != NULL && stream_period->period->id != NULL
 +        && strcmp (stream_period->period->id, period_id) == 0)
 +      return TRUE;
 +
 +  }
 +
 +  GST_DEBUG ("Building the list of Periods in the Media Presentation");
 +  /* clean the old period list, if any */
 +  /* TODO: In theory we could reuse the ones we have so far but that
 +   * seems more complicated than the overhead caused here
 +   */
 +  if (client->periods) {
 +    g_list_foreach (client->periods,
 +        (GFunc) gst_mpdparser_free_stream_period, NULL);
 +    g_list_free (client->periods);
 +    client->periods = NULL;
 +  }
 +
 +  idx = 0;
 +  start = 0;
 +  duration = GST_CLOCK_TIME_NONE;
 +
 +  if (client->mpd_root_node->mediaPresentationDuration <= 0 &&
 +      client->mpd_root_node->mediaPresentationDuration != -1) {
 +    /* Invalid MPD file: MPD duration is negative or zero */
 +    goto syntax_error;
 +  }
 +
 +  for (list = client->mpd_root_node->Periods; list;
 +      /* explicitly advanced below */ ) {
 +    GstMPDPeriodNode *period_node = list->data;
 +    GstMPDPeriodNode *next_period_node = NULL;
 +
 +    /* Download external period */
 +    if (period_node->xlink_href) {
 +      GList *new_periods;
 +      GList *prev;
 +
 +      new_periods = gst_mpd_client_fetch_external_periods (client, period_node);
 +
 +      prev = list->prev;
 +      client->mpd_root_node->Periods =
 +          g_list_delete_link (client->mpd_root_node->Periods, list);
 +      gst_mpd_period_node_free (period_node);
 +      period_node = NULL;
 +
 +      /* Get new next node, we will insert before this */
 +      if (prev)
 +        next = prev->next;
 +      else
 +        next = client->mpd_root_node->Periods;
 +
 +      while (new_periods) {
 +        client->mpd_root_node->Periods =
 +            g_list_insert_before (client->mpd_root_node->Periods, next,
 +            new_periods->data);
 +        new_periods = g_list_delete_link (new_periods, new_periods);
 +      }
 +      next = NULL;
 +
 +      /* Update our iterator to the first new period if any, or the next */
 +      if (prev)
 +        list = prev->next;
 +      else
 +        list = client->mpd_root_node->Periods;
 +
 +      /* And try again */
 +      continue;
 +    }
 +
 +    if (period_node->start != -1) {
 +      /* we have a regular period */
 +      /* start cannot be smaller than previous start */
 +      if (list != g_list_first (client->mpd_root_node->Periods)
 +          && start >= period_node->start * GST_MSECOND) {
 +        /* Invalid MPD file: duration would be negative or zero */
 +        goto syntax_error;
 +      }
 +      start = period_node->start * GST_MSECOND;
 +    } else if (duration != GST_CLOCK_TIME_NONE) {
 +      /* start time inferred from previous period, this is still a regular period */
 +      start += duration;
 +    } else if (idx == 0
 +        && client->mpd_root_node->type == GST_MPD_FILE_TYPE_STATIC) {
 +      /* first period of a static MPD file, start time is 0 */
 +      start = 0;
 +    } else if (client->mpd_root_node->type == GST_MPD_FILE_TYPE_DYNAMIC) {
 +      /* this should be a live stream, let this pass */
 +    } else {
 +      /* this is an 'Early Available Period' */
 +      goto early;
 +    }
 +
 +    /* compute duration.
 +       If there is a start time for the next period, or this is the last period
 +       and mediaPresentationDuration was set, those values will take precedence
 +       over a configured period duration in computing this period's duration
 +
 +       ISO/IEC 23009-1:2014(E), chapter 5.3.2.1
 +       "The Period extends until the PeriodStart of the next Period, or until
 +       the end of the Media Presentation in the case of the last Period."
 +     */
 +
 +    while ((next = g_list_next (list)) != NULL) {
 +      /* try to infer this period duration from the start time of the next period */
 +      next_period_node = next->data;
 +
 +      if (next_period_node->xlink_href) {
 +        GList *new_periods;
 +
 +        new_periods =
 +            gst_mpd_client_fetch_external_periods (client, next_period_node);
 +
 +        client->mpd_root_node->Periods =
 +            g_list_delete_link (client->mpd_root_node->Periods, next);
 +        gst_mpd_period_node_free (next_period_node);
 +        next_period_node = NULL;
 +        /* Get new next node, we will insert before this */
 +        next = g_list_next (list);
 +        while (new_periods) {
 +          client->mpd_root_node->Periods =
 +              g_list_insert_before (client->mpd_root_node->Periods, next,
 +              new_periods->data);
 +          new_periods = g_list_delete_link (new_periods, new_periods);
 +        }
 +
 +        /* And try again, getting the next list element which is now our newly
 +         * inserted nodes. If any */
 +      } else {
 +        /* Got the next period and it doesn't have to be downloaded first */
 +        break;
 +      }
 +    }
 +
 +    if (next_period_node) {
 +      if (next_period_node->start != -1) {
 +        if (start >= next_period_node->start * GST_MSECOND) {
 +          /* Invalid MPD file: duration would be negative or zero */
 +          goto syntax_error;
 +        }
 +        duration = next_period_node->start * GST_MSECOND - start;
 +      } else if (period_node->duration != -1) {
 +        if (period_node->duration <= 0) {
 +          /* Invalid MPD file: duration would be negative or zero */
 +          goto syntax_error;
 +        }
 +        duration = period_node->duration * GST_MSECOND;
 +      } else if (client->mpd_root_node->type == GST_MPD_FILE_TYPE_DYNAMIC) {
 +        /* might be a live file, ignore unspecified duration */
 +      } else {
 +        /* Invalid MPD file! */
 +        goto syntax_error;
 +      }
 +    } else if (client->mpd_root_node->mediaPresentationDuration != -1) {
 +      /* last Period of the Media Presentation */
 +      if (client->mpd_root_node->mediaPresentationDuration * GST_MSECOND <=
 +          start) {
 +        /* Invalid MPD file: duration would be negative or zero */
 +        goto syntax_error;
 +      }
 +      duration =
 +          client->mpd_root_node->mediaPresentationDuration * GST_MSECOND -
 +          start;
 +    } else if (period_node->duration != -1) {
 +      duration = period_node->duration * GST_MSECOND;
 +    } else if (client->mpd_root_node->type == GST_MPD_FILE_TYPE_DYNAMIC) {
 +      /* might be a live file, ignore unspecified duration */
 +    } else {
 +      /* Invalid MPD file! */
 +      GST_ERROR
 +          ("Invalid MPD file. The MPD is static without a valid duration");
 +      goto syntax_error;
 +    }
 +
 +    stream_period = g_slice_new0 (GstStreamPeriod);
 +    client->periods = g_list_append (client->periods, stream_period);
 +    stream_period->period = period_node;
 +    stream_period->number = idx++;
 +    stream_period->start = start;
 +    stream_period->duration = duration;
 +    ret = TRUE;
 +    GST_LOG (" - added Period %d start=%" GST_TIME_FORMAT " duration=%"
 +        GST_TIME_FORMAT, idx, GST_TIME_ARGS (start), GST_TIME_ARGS (duration));
 +
 +    if ((time != GST_CLOCK_TIME_NONE
 +            && stream_period->duration != GST_CLOCK_TIME_NONE
 +            && stream_period->start + stream_period->duration >= time)
 +        || (time != GST_CLOCK_TIME_NONE && stream_period->start >= time))
 +      break;
 +
 +    if (period_idx != -1 && stream_period->number >= period_idx)
 +      break;
 +
 +    if (period_id != NULL && stream_period->period->id != NULL
 +        && strcmp (stream_period->period->id, period_id) == 0)
 +      break;
 +
 +    list = list->next;
 +  }
 +
 +  GST_DEBUG
 +      ("Found a total of %d valid Periods in the Media Presentation up to this point",
 +      idx);
 +  return ret;
 +
 +early:
 +  GST_WARNING
 +      ("Found an Early Available Period, skipping the rest of the Media Presentation");
 +  return ret;
 +
 +syntax_error:
 +  GST_WARNING
 +      ("Cannot get the duration of the Period %d, skipping the rest of the Media Presentation",
 +      idx);
 +  return ret;
 +}
 +
 +static GList *
 +gst_mpd_client_fetch_external_adaptation_set (GstMPDClient * client,
 +    GstMPDPeriodNode * period, GstMPDAdaptationSetNode * adapt_set)
 +{
 +  GstFragment *download;
 +  GstBuffer *adapt_set_buffer;
 +  GstMapInfo map;
 +  GError *err = NULL;
 +  GstUri *base_uri, *uri;
 +  gchar *query = NULL;
 +  gchar *uri_string;
 +  GList *new_adapt_sets = NULL;
 +
 +  /* ISO/IEC 23009-1:2014 5.5.3 4)
 +   * Remove nodes that resolve to nothing when resolving
 +   */
 +  if (strcmp (adapt_set->xlink_href, "urn:mpeg:dash:resolve-to-zero:2013") == 0) {
 +    return NULL;
 +  }
 +
 +  if (!client->downloader) {
 +    return NULL;
 +  }
 +
 +  /* Build absolute URI */
 +
 +  /* Get base URI at the MPD level */
 +  base_uri =
 +      gst_uri_from_string (client->mpd_base_uri ? client->
 +      mpd_base_uri : client->mpd_uri);
 +
 +  /* combine a BaseURL at the MPD level with the current base url */
 +  base_uri =
 +      gst_mpd_helper_combine_urls (base_uri, client->mpd_root_node->BaseURLs,
 +      &query, 0);
 +
 +  /* combine a BaseURL at the Period level with the current base url */
 +  base_uri =
 +      gst_mpd_helper_combine_urls (base_uri, period->BaseURLs, &query, 0);
 +
 +  uri = gst_uri_from_string_with_base (base_uri, adapt_set->xlink_href);
 +  if (query)
 +    gst_uri_set_query_string (uri, query);
 +  g_free (query);
 +  uri_string = gst_uri_to_string (uri);
 +  gst_uri_unref (base_uri);
 +  gst_uri_unref (uri);
 +
++#ifdef TIZEN_FEATURE_ADAPTIVE_MODIFICATION
++  download =
++      gst_uri_downloader_fetch_uri (client->downloader,
++      uri_string, client->mpd_uri, NULL, NULL, DEFAULT_ADAPTIVE_RETRY,
++      DEFAULT_ADAPTIVE_TIMEOUT, TRUE, FALSE, TRUE, &err);
++#else
 +  download =
 +      gst_uri_downloader_fetch_uri (client->downloader,
 +      uri_string, client->mpd_uri, TRUE, FALSE, TRUE, &err);
++#endif
 +  g_free (uri_string);
 +
 +  if (!download) {
 +    GST_ERROR ("Failed to download external AdaptationSet node at '%s': %s",
 +        adapt_set->xlink_href, err->message);
 +    g_clear_error (&err);
 +    return NULL;
 +  }
 +
 +  adapt_set_buffer = gst_fragment_get_buffer (download);
 +  g_object_unref (download);
 +
 +  gst_buffer_map (adapt_set_buffer, &map, GST_MAP_READ);
 +
 +  new_adapt_sets =
 +      gst_mpdparser_get_external_adaptation_sets ((const gchar *) map.data,
 +      map.size, period);
 +
 +  gst_buffer_unmap (adapt_set_buffer, &map);
 +  gst_buffer_unref (adapt_set_buffer);
 +
 +  return new_adapt_sets;
 +}
 +
 +static GList *
 +gst_mpd_client_get_adaptation_sets_for_period (GstMPDClient * client,
 +    GstStreamPeriod * period)
 +{
 +  GList *list;
 +
 +  g_return_val_if_fail (period != NULL, NULL);
 +
 +  /* Resolve all external adaptation sets of this period. Every user of
 +   * the adaptation sets would need to know the content of all adaptation sets
 +   * to decide which one to use, so we have to resolve them all here
 +   */
 +  for (list = period->period->AdaptationSets; list;
 +      /* advanced explicitly below */ ) {
 +    GstMPDAdaptationSetNode *adapt_set = (GstMPDAdaptationSetNode *) list->data;
 +    GList *new_adapt_sets = NULL, *prev, *next;
 +
 +    if (!adapt_set->xlink_href) {
 +      list = list->next;
 +      continue;
 +    }
 +
 +    new_adapt_sets =
 +        gst_mpd_client_fetch_external_adaptation_set (client, period->period,
 +        adapt_set);
 +
 +    prev = list->prev;
 +    period->period->AdaptationSets =
 +        g_list_delete_link (period->period->AdaptationSets, list);
 +    gst_mpd_adaptation_set_node_free (adapt_set);
 +    adapt_set = NULL;
 +
 +    /* Get new next node, we will insert before this */
 +    if (prev)
 +      next = prev->next;
 +    else
 +      next = period->period->AdaptationSets;
 +
 +    while (new_adapt_sets) {
 +      period->period->AdaptationSets =
 +          g_list_insert_before (period->period->AdaptationSets, next,
 +          new_adapt_sets->data);
 +      new_adapt_sets = g_list_delete_link (new_adapt_sets, new_adapt_sets);
 +    }
 +
 +    /* Update our iterator to the first new adaptation set if any, or the next */
 +    if (prev)
 +      list = prev->next;
 +    else
 +      list = period->period->AdaptationSets;
 +  }
 +
 +  return period->period->AdaptationSets;
 +}
 +
 +GList *
 +gst_mpd_client_get_adaptation_sets (GstMPDClient * client)
 +{
 +  GstStreamPeriod *stream_period;
 +
 +  stream_period = gst_mpd_client_get_stream_period (client);
 +  if (stream_period == NULL || stream_period->period == NULL) {
 +    GST_DEBUG ("No more Period nodes in the MPD file, terminating...");
 +    return NULL;
 +  }
 +
 +  return gst_mpd_client_get_adaptation_sets_for_period (client, stream_period);
 +}
 +
 +gboolean
 +gst_mpd_client_setup_streaming (GstMPDClient * client,
 +    GstMPDAdaptationSetNode * adapt_set)
 +{
 +  GstMPDRepresentationNode *representation;
 +  GList *rep_list = NULL;
 +  GstActiveStream *stream;
 +
 +  rep_list = adapt_set->Representations;
 +  if (!rep_list) {
 +    GST_WARNING ("Can not retrieve any representation, aborting...");
 +    return FALSE;
 +  }
 +
 +  stream = g_slice_new0 (GstActiveStream);
 +  gst_mpdparser_init_active_stream_segments (stream);
 +
 +  stream->baseURL_idx = 0;
 +  stream->cur_adapt_set = adapt_set;
 +
 +  GST_DEBUG ("0. Current stream %p", stream);
 +
 +#if 0
 +  /* fast start */
 +  representation =
 +      gst_mpdparser_get_representation_with_max_bandwidth (rep_list,
 +      stream->max_bandwidth);
 +
 +  if (!representation) {
 +    GST_WARNING
 +        ("Can not retrieve a representation with the requested bandwidth");
 +    representation = gst_mpd_client_get_lowest_representation (rep_list);
 +  }
 +#else
 +  /* slow start */
 +  representation = gst_mpd_client_get_lowest_representation (rep_list);
 +#endif
 +
 +  if (!representation) {
 +    GST_WARNING ("No valid representation in the MPD file, aborting...");
 +    gst_mpdparser_free_active_stream (stream);
 +    return FALSE;
 +  }
 +  stream->mimeType =
 +      gst_mpdparser_representation_get_mimetype (adapt_set, representation);
 +  if (stream->mimeType == GST_STREAM_UNKNOWN) {
 +    GST_WARNING ("Unknown mime type in the representation, aborting...");
 +    gst_mpdparser_free_active_stream (stream);
 +    return FALSE;
 +  }
 +
 +  client->active_streams = g_list_append (client->active_streams, stream);
 +  if (!gst_mpd_client_setup_representation (client, stream, representation)) {
 +    GST_WARNING ("Failed to setup the representation, aborting...");
 +    return FALSE;
 +  }
 +
 +  GST_INFO ("Successfully setup the download pipeline for mimeType %d",
 +      stream->mimeType);
 +
 +  return TRUE;
 +}
 +
 +gboolean
 +gst_mpd_client_stream_seek (GstMPDClient * client, GstActiveStream * stream,
 +    gboolean forward, GstSeekFlags flags, GstClockTime ts,
 +    GstClockTime * final_ts)
 +{
 +  gint index = 0;
 +  gint repeat_index = 0;
 +  GstMediaSegment *selectedChunk = NULL;
 +
 +  g_return_val_if_fail (stream != NULL, 0);
 +
 +  if (stream->segments) {
 +    for (index = 0; index < stream->segments->len; index++) {
 +      gboolean in_segment = FALSE;
 +      GstMediaSegment *segment = g_ptr_array_index (stream->segments, index);
 +      GstClockTime end_time;
 +
 +      GST_DEBUG ("Looking at fragment sequence chunk %d / %d", index,
 +          stream->segments->len);
 +
 +      end_time =
 +          gst_mpd_client_get_segment_end_time (client, stream->segments,
 +          segment, index);
 +
 +      /* avoid downloading another fragment just for 1ns in reverse mode */
 +      if (forward)
 +        in_segment = ts < end_time;
 +      else
 +        in_segment = ts <= end_time;
 +
 +      if (in_segment) {
 +        GstClockTime chunk_time;
 +
 +        selectedChunk = segment;
 +        repeat_index = (ts - segment->start) / segment->duration;
 +
 +        chunk_time = segment->start + segment->duration * repeat_index;
 +
 +        /* At the end of a segment in reverse mode, start from the previous fragment */
 +        if (!forward && repeat_index > 0
 +            && ((ts - segment->start) % segment->duration == 0))
 +          repeat_index--;
 +
 +        if ((flags & GST_SEEK_FLAG_SNAP_NEAREST) == GST_SEEK_FLAG_SNAP_NEAREST) {
 +          if (repeat_index + 1 < segment->repeat) {
 +            if (ts - chunk_time > chunk_time + segment->duration - ts)
 +              repeat_index++;
 +          } else if (index + 1 < stream->segments->len) {
 +            GstMediaSegment *next_segment =
 +                g_ptr_array_index (stream->segments, index + 1);
 +
 +            if (ts - chunk_time > next_segment->start - ts) {
 +              repeat_index = 0;
 +              selectedChunk = next_segment;
 +              index++;
 +            }
 +          }
 +        } else if (((forward && flags & GST_SEEK_FLAG_SNAP_AFTER) ||
 +                (!forward && flags & GST_SEEK_FLAG_SNAP_BEFORE)) &&
 +            ts != chunk_time) {
 +
 +          if (repeat_index + 1 < segment->repeat) {
 +            repeat_index++;
 +          } else {
 +            repeat_index = 0;
 +            if (index + 1 >= stream->segments->len) {
 +              selectedChunk = NULL;
 +            } else {
 +              selectedChunk = g_ptr_array_index (stream->segments, ++index);
 +            }
 +          }
 +        }
 +        break;
 +      }
 +    }
 +
 +    if (selectedChunk == NULL) {
 +      stream->segment_index = stream->segments->len;
 +      stream->segment_repeat_index = 0;
 +      GST_DEBUG ("Seek to after last segment");
 +      return FALSE;
 +    }
 +
 +    if (final_ts)
 +      *final_ts = selectedChunk->start + selectedChunk->duration * repeat_index;
 +  } else {
 +    GstClockTime duration =
 +        gst_mpd_client_get_segment_duration (client, stream, NULL);
 +    GstStreamPeriod *stream_period = gst_mpd_client_get_stream_period (client);
 +    guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
 +    GstClockTime index_time;
 +
 +    g_return_val_if_fail (GST_MPD_MULT_SEGMENT_BASE_NODE
 +        (stream->cur_seg_template)->SegmentTimeline == NULL, FALSE);
 +    if (!GST_CLOCK_TIME_IS_VALID (duration)) {
 +      return FALSE;
 +    }
 +
 +    if (ts > stream_period->start)
 +      ts -= stream_period->start;
 +    else
 +      ts = 0;
 +
 +    index = ts / duration;
 +
 +    /* At the end of a segment in reverse mode, start from the previous fragment */
 +    if (!forward && index > 0 && ts % duration == 0)
 +      index--;
 +
 +    index_time = index * duration;
 +
 +    if ((flags & GST_SEEK_FLAG_SNAP_NEAREST) == GST_SEEK_FLAG_SNAP_NEAREST) {
 +      if (ts - index_time > index_time + duration - ts)
 +        index++;
 +    } else if (((forward && flags & GST_SEEK_FLAG_SNAP_AFTER) ||
 +            (!forward && flags & GST_SEEK_FLAG_SNAP_BEFORE))
 +        && ts != index_time) {
 +      index++;
 +    }
 +
 +    if (segments_count > 0 && index >= segments_count) {
 +      stream->segment_index = segments_count;
 +      stream->segment_repeat_index = 0;
 +      GST_DEBUG ("Seek to after last segment");
 +      return FALSE;
 +    }
 +    if (final_ts)
 +      *final_ts = index * duration;
 +  }
 +
 +  stream->segment_repeat_index = repeat_index;
 +  stream->segment_index = index;
 +
 +  return TRUE;
 +}
 +
 +gint64
 +gst_mpd_client_calculate_time_difference (const GstDateTime * t1,
 +    const GstDateTime * t2)
 +{
 +  GDateTime *gdt1, *gdt2;
 +  GTimeSpan diff;
 +
 +  g_assert (t1 != NULL && t2 != NULL);
 +  gdt1 = gst_date_time_to_g_date_time ((GstDateTime *) t1);
 +  gdt2 = gst_date_time_to_g_date_time ((GstDateTime *) t2);
 +  diff = g_date_time_difference (gdt2, gdt1);
 +  g_date_time_unref (gdt1);
 +  g_date_time_unref (gdt2);
 +  return diff * GST_USECOND;
 +}
 +
 +GstDateTime *
 +gst_mpd_client_add_time_difference (GstDateTime * t1, gint64 usecs)
 +{
 +  GDateTime *gdt;
 +  GDateTime *gdt2;
 +  GstDateTime *rv;
 +
 +  g_assert (t1 != NULL);
 +  gdt = gst_date_time_to_g_date_time (t1);
 +  g_assert (gdt != NULL);
 +  gdt2 = g_date_time_add (gdt, usecs);
 +  g_assert (gdt2 != NULL);
 +  g_date_time_unref (gdt);
 +  rv = gst_date_time_new_from_g_date_time (gdt2);
 +
 +  /* Don't g_date_time_unref(gdt2) because gst_date_time_new_from_g_date_time takes
 +   * ownership of the GDateTime pointer.
 +   */
 +
 +  return rv;
 +}
 +
 +gboolean
 +gst_mpd_client_get_last_fragment_timestamp_end (GstMPDClient * client,
 +    guint stream_idx, GstClockTime * ts)
 +{
 +  GstActiveStream *stream;
 +  gint segment_idx;
 +  GstMediaSegment *currentChunk;
 +  GstStreamPeriod *stream_period;
 +
 +  GST_DEBUG ("Stream index: %i", stream_idx);
 +  stream = g_list_nth_data (client->active_streams, stream_idx);
 +  g_return_val_if_fail (stream != NULL, 0);
 +
 +  if (!stream->segments) {
 +    stream_period = gst_mpd_client_get_stream_period (client);
 +    *ts = stream_period->start + stream_period->duration;
 +  } else {
 +    segment_idx = gst_mpd_client_get_segments_counts (client, stream) - 1;
 +    if (segment_idx >= stream->segments->len) {
 +      GST_WARNING ("Segment index %d is outside of segment list of length %d",
 +          segment_idx, stream->segments->len);
 +      return FALSE;
 +    }
 +    currentChunk = g_ptr_array_index (stream->segments, segment_idx);
 +
 +    if (currentChunk->repeat >= 0) {
 +      *ts =
 +          currentChunk->start + (currentChunk->duration * (1 +
 +              currentChunk->repeat));
 +    } else {
 +      /* 5.3.9.6.1: negative repeat means repeat till the end of the
 +       * period, or the next update of the MPD (which I think is
 +       * implicit, as this will all get deleted/recreated), or the
 +       * start of the next segment, if any. */
 +      stream_period = gst_mpd_client_get_stream_period (client);
 +      *ts = stream_period->start + stream_period->duration;
 +    }
 +  }
 +
 +  return TRUE;
 +}
 +
 +gboolean
 +gst_mpd_client_get_next_fragment_timestamp (GstMPDClient * client,
 +    guint stream_idx, GstClockTime * ts)
 +{
 +  GstActiveStream *stream;
 +  GstMediaSegment *currentChunk;
 +
 +  GST_DEBUG ("Stream index: %i", stream_idx);
 +  stream = g_list_nth_data (client->active_streams, stream_idx);
 +  g_return_val_if_fail (stream != NULL, 0);
 +
 +  if (stream->segments) {
 +    GST_DEBUG ("Looking for fragment sequence chunk %d / %d",
 +        stream->segment_index, stream->segments->len);
 +    if (stream->segment_index >= stream->segments->len)
 +      return FALSE;
 +    currentChunk = g_ptr_array_index (stream->segments, stream->segment_index);
 +
 +    *ts =
 +        currentChunk->start +
 +        (currentChunk->duration * stream->segment_repeat_index);
 +  } else {
 +    GstClockTime duration =
 +        gst_mpd_client_get_segment_duration (client, stream, NULL);
 +    guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
 +
 +    g_return_val_if_fail (GST_MPD_MULT_SEGMENT_BASE_NODE
 +        (stream->cur_seg_template)->SegmentTimeline == NULL, FALSE);
 +    if (!GST_CLOCK_TIME_IS_VALID (duration) || (segments_count > 0
 +            && stream->segment_index >= segments_count)) {
 +      return FALSE;
 +    }
 +    *ts = stream->segment_index * duration;
 +  }
 +
 +  return TRUE;
 +}
 +
 +GstClockTime
 +gst_mpd_client_get_stream_presentation_offset (GstMPDClient * client,
 +    guint stream_idx)
 +{
 +  GstActiveStream *stream = NULL;
 +
 +  g_return_val_if_fail (client != NULL, 0);
 +  g_return_val_if_fail (client->active_streams != NULL, 0);
 +  stream = g_list_nth_data (client->active_streams, stream_idx);
 +  g_return_val_if_fail (stream != NULL, 0);
 +
 +  return stream->presentationTimeOffset;
 +}
 +
 +GstClockTime
 +gst_mpd_client_get_period_start_time (GstMPDClient * client)
 +{
 +  GstStreamPeriod *stream_period = NULL;
 +
 +  g_return_val_if_fail (client != NULL, 0);
 +  stream_period = gst_mpd_client_get_stream_period (client);
 +  g_return_val_if_fail (stream_period != NULL, 0);
 +
 +  return stream_period->start;
 +}
 +
 +/**
 + * gst_mpd_client_get_utc_timing_sources:
 + * @client: #GstMPDClient to check for UTCTiming elements
 + * @methods: A bit mask of #GstMPDUTCTimingType that specifies the methods
 + *     to search for.
 + * @selected_method: (nullable): The selected method
 + * Returns: (transfer none): A NULL terminated array of URLs of servers
 + *     that use @selected_method to provide a realtime clock.
 + *
 + * Searches the UTCTiming elements found in the manifest for an element
 + * that uses one of the UTC timing methods specified in @selected_method.
 + * If multiple UTCTiming elements are present that support one of the
 + * methods specified in @selected_method, the first one is returned.
 + *
 + * Since: 1.6
 + */
 +gchar **
 +gst_mpd_client_get_utc_timing_sources (GstMPDClient * client,
 +    guint methods, GstMPDUTCTimingType * selected_method)
 +{
 +  GList *list;
 +
 +  g_return_val_if_fail (client != NULL, NULL);
 +  g_return_val_if_fail (client->mpd_root_node != NULL, NULL);
 +  for (list = g_list_first (client->mpd_root_node->UTCTimings); list;
 +      list = g_list_next (list)) {
 +    const GstMPDUTCTimingNode *node = (const GstMPDUTCTimingNode *) list->data;
 +    if (node->method & methods) {
 +      if (selected_method) {
 +        *selected_method = node->method;
 +      }
 +      return node->urls;
 +    }
 +  }
 +  return NULL;
 +}
 +
 +
 +gboolean
 +gst_mpd_client_get_next_fragment (GstMPDClient * client,
 +    guint indexStream, GstMediaFragmentInfo * fragment)
 +{
 +  GstActiveStream *stream = NULL;
 +  GstMediaSegment *currentChunk;
 +  gchar *mediaURL = NULL;
 +  gchar *indexURL = NULL;
 +  GstUri *base_url, *frag_url;
 +
 +  /* select stream */
 +  g_return_val_if_fail (client != NULL, FALSE);
 +  g_return_val_if_fail (client->active_streams != NULL, FALSE);
 +  stream = g_list_nth_data (client->active_streams, indexStream);
 +  g_return_val_if_fail (stream != NULL, FALSE);
 +  g_return_val_if_fail (stream->cur_representation != NULL, FALSE);
 +
 +  if (stream->segments) {
 +    GST_DEBUG ("Looking for fragment sequence chunk %d / %d",
 +        stream->segment_index, stream->segments->len);
 +    if (stream->segment_index >= stream->segments->len)
 +      return FALSE;
 +  } else {
 +    GstClockTime duration = gst_mpd_client_get_segment_duration (client,
 +        stream, NULL);
 +    guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
 +
 +    g_return_val_if_fail (GST_MPD_MULT_SEGMENT_BASE_NODE
 +        (stream->cur_seg_template)->SegmentTimeline == NULL, FALSE);
 +    if (!GST_CLOCK_TIME_IS_VALID (duration) || (segments_count > 0
 +            && stream->segment_index >= segments_count)) {
 +      return FALSE;
 +    }
 +    fragment->duration = duration;
 +  }
 +
 +  /* FIXME rework discont checking */
 +  /* fragment->discontinuity = segment_idx != currentChunk.number; */
 +  fragment->range_start = 0;
 +  fragment->range_end = -1;
 +  fragment->index_uri = NULL;
 +  fragment->index_range_start = 0;
 +  fragment->index_range_end = -1;
 +
 +  if (stream->segments) {
 +    currentChunk = g_ptr_array_index (stream->segments, stream->segment_index);
 +
 +    GST_DEBUG ("currentChunk->SegmentURL = %p", currentChunk->SegmentURL);
 +    if (currentChunk->SegmentURL != NULL) {
 +      mediaURL =
 +          g_strdup (gst_mpdparser_get_mediaURL (stream,
 +              currentChunk->SegmentURL));
 +      indexURL = g_strdup (currentChunk->SegmentURL->index);
 +    } else if (stream->cur_seg_template != NULL) {
 +      mediaURL =
 +          gst_mpdparser_build_URL_from_template (stream->cur_seg_template->
 +          media, stream->cur_representation->id,
 +          currentChunk->number + stream->segment_repeat_index,
 +          stream->cur_representation->bandwidth,
 +          currentChunk->scale_start +
 +          stream->segment_repeat_index * currentChunk->scale_duration);
 +      if (stream->cur_seg_template->index) {
 +        indexURL =
 +            gst_mpdparser_build_URL_from_template (stream->cur_seg_template->
 +            index, stream->cur_representation->id,
 +            currentChunk->number + stream->segment_repeat_index,
 +            stream->cur_representation->bandwidth,
 +            currentChunk->scale_start +
 +            stream->segment_repeat_index * currentChunk->scale_duration);
 +      }
 +    }
 +    GST_DEBUG ("mediaURL = %s", mediaURL);
 +    GST_DEBUG ("indexURL = %s", indexURL);
 +
 +    fragment->timestamp =
 +        currentChunk->start +
 +        stream->segment_repeat_index * currentChunk->duration;
 +    fragment->duration = currentChunk->duration;
 +    if (currentChunk->SegmentURL) {
 +      if (currentChunk->SegmentURL->mediaRange) {
 +        fragment->range_start =
 +            currentChunk->SegmentURL->mediaRange->first_byte_pos;
 +        fragment->range_end =
 +            currentChunk->SegmentURL->mediaRange->last_byte_pos;
 +      }
 +      if (currentChunk->SegmentURL->indexRange) {
 +        fragment->index_range_start =
 +            currentChunk->SegmentURL->indexRange->first_byte_pos;
 +        fragment->index_range_end =
 +            currentChunk->SegmentURL->indexRange->last_byte_pos;
 +      }
 +    }
 +  } else {
 +    if (stream->cur_seg_template != NULL) {
 +      mediaURL =
 +          gst_mpdparser_build_URL_from_template (stream->cur_seg_template->
 +          media, stream->cur_representation->id,
 +          stream->segment_index +
 +          GST_MPD_MULT_SEGMENT_BASE_NODE (stream->
 +              cur_seg_template)->startNumber,
 +          stream->cur_representation->bandwidth,
 +          stream->segment_index * fragment->duration);
 +      if (stream->cur_seg_template->index) {
 +        indexURL =
 +            gst_mpdparser_build_URL_from_template (stream->cur_seg_template->
 +            index, stream->cur_representation->id,
 +            stream->segment_index +
 +            GST_MPD_MULT_SEGMENT_BASE_NODE (stream->
 +                cur_seg_template)->startNumber,
 +            stream->cur_representation->bandwidth,
 +            stream->segment_index * fragment->duration);
 +      }
 +    } else {
 +      return FALSE;
 +    }
 +
 +    GST_DEBUG ("mediaURL = %s", mediaURL);
 +    GST_DEBUG ("indexURL = %s", indexURL);
 +
 +    fragment->timestamp = stream->segment_index * fragment->duration;
 +  }
 +
 +  base_url = gst_uri_from_string (stream->baseURL);
 +  frag_url = gst_uri_from_string_with_base (base_url, mediaURL);
 +  g_free (mediaURL);
 +  if (stream->queryURL) {
 +    frag_url = gst_uri_make_writable (frag_url);
 +    gst_uri_set_query_string (frag_url, stream->queryURL);
 +  }
 +  fragment->uri = gst_uri_to_string (frag_url);
 +  gst_uri_unref (frag_url);
 +
 +  if (indexURL != NULL) {
 +    frag_url = gst_uri_make_writable (gst_uri_from_string_with_base (base_url,
 +            indexURL));
 +    gst_uri_set_query_string (frag_url, stream->queryURL);
 +    fragment->index_uri = gst_uri_to_string (frag_url);
 +    gst_uri_unref (frag_url);
 +    g_free (indexURL);
 +  } else if (indexURL == NULL && (fragment->index_range_start
 +          || fragment->index_range_end != -1)) {
 +    /* index has no specific URL but has a range, we should only use this if
 +     * the media also has a range, otherwise we are serving some data twice
 +     * (in the media fragment and again in the index) */
 +    if (!(fragment->range_start || fragment->range_end != -1)) {
 +      GST_WARNING ("Ignoring index ranges because there isn't a media range "
 +          "and URIs would be the same");
 +      /* removing index information */
 +      fragment->index_range_start = 0;
 +      fragment->index_range_end = -1;
 +    }
 +  }
 +
 +  gst_uri_unref (base_url);
 +
 +  GST_DEBUG ("Loading chunk with URL %s", fragment->uri);
 +
 +  return TRUE;
 +}
 +
 +gboolean
 +gst_mpd_client_has_next_segment (GstMPDClient * client,
 +    GstActiveStream * stream, gboolean forward)
 +{
 +  if (forward) {
 +    guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
 +
 +    if (segments_count > 0 && stream->segments
 +        && stream->segment_index + 1 == segments_count) {
 +      GstMediaSegment *segment;
 +
 +      segment = g_ptr_array_index (stream->segments, stream->segment_index);
 +      if (segment->repeat >= 0
 +          && stream->segment_repeat_index >= segment->repeat)
 +        return FALSE;
 +    } else if (segments_count > 0
 +        && stream->segment_index + 1 >= segments_count) {
 +      return FALSE;
 +    }
 +  } else {
 +    if (stream->segment_index < 0)
 +      return FALSE;
 +  }
 +
 +  return TRUE;
 +}
 +
 +GstFlowReturn
 +gst_mpd_client_advance_segment (GstMPDClient * client, GstActiveStream * stream,
 +    gboolean forward)
 +{
 +  GstMediaSegment *segment;
 +  GstFlowReturn ret = GST_FLOW_OK;
 +  guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
 +
 +  GST_DEBUG ("Advancing segment. Current: %d / %d r:%d", stream->segment_index,
 +      segments_count, stream->segment_repeat_index);
 +
 +  /* handle special cases first */
 +  if (forward) {
 +    if (segments_count > 0 && stream->segment_index >= segments_count) {
 +      ret = GST_FLOW_EOS;
 +      goto done;
 +    }
 +
 +    if (stream->segments == NULL) {
 +      if (stream->segment_index < 0) {
 +        stream->segment_index = 0;
 +      } else {
 +        stream->segment_index++;
 +        if (segments_count > 0 && stream->segment_index >= segments_count) {
 +          ret = GST_FLOW_EOS;
 +        }
 +      }
 +      goto done;
 +    }
 +
 +    /* special case for when playback direction is reverted right at *
 +     * the end of the segment list */
 +    if (stream->segment_index < 0) {
 +      stream->segment_index = 0;
 +      goto done;
 +    }
 +  } else {
 +    if (stream->segments == NULL)
 +      stream->segment_index--;
 +    if (stream->segment_index < 0) {
 +      stream->segment_index = -1;
 +      ret = GST_FLOW_EOS;
 +      goto done;
 +    }
 +    if (stream->segments == NULL)
 +      goto done;
 +
 +    /* special case for when playback direction is reverted right at *
 +     * the end of the segment list */
 +    if (stream->segment_index >= segments_count) {
 +      stream->segment_index = segments_count - 1;
 +      segment = g_ptr_array_index (stream->segments, stream->segment_index);
 +      if (segment->repeat >= 0) {
 +        stream->segment_repeat_index = segment->repeat;
 +      } else {
 +        GstClockTime start = segment->start;
 +        GstClockTime end =
 +            gst_mpd_client_get_segment_end_time (client, stream->segments,
 +            segment,
 +            stream->segment_index);
 +        stream->segment_repeat_index =
 +            (guint) (end - start) / segment->duration;
 +      }
 +      goto done;
 +    }
 +  }
 +
 +  /* for the normal cases we can get the segment safely here */
 +  segment = g_ptr_array_index (stream->segments, stream->segment_index);
 +  if (forward) {
 +    if (segment->repeat >= 0 && stream->segment_repeat_index >= segment->repeat) {
 +      stream->segment_repeat_index = 0;
 +      stream->segment_index++;
 +      if (segments_count > 0 && stream->segment_index >= segments_count) {
 +        ret = GST_FLOW_EOS;
 +        goto done;
 +      }
 +    } else {
 +      stream->segment_repeat_index++;
 +    }
 +  } else {
 +    if (stream->segment_repeat_index == 0) {
 +      stream->segment_index--;
 +      if (stream->segment_index < 0) {
 +        ret = GST_FLOW_EOS;
 +        goto done;
 +      }
 +
 +      segment = g_ptr_array_index (stream->segments, stream->segment_index);
 +      /* negative repeats only seem to make sense at the end of a list,
 +       * so this one will probably not be. Needs some sanity checking
 +       * when loading the XML data. */
 +      if (segment->repeat >= 0) {
 +        stream->segment_repeat_index = segment->repeat;
 +      } else {
 +        GstClockTime start = segment->start;
 +        GstClockTime end =
 +            gst_mpd_client_get_segment_end_time (client, stream->segments,
 +            segment,
 +            stream->segment_index);
 +        stream->segment_repeat_index =
 +            (guint) (end - start) / segment->duration;
 +      }
 +    } else {
 +      stream->segment_repeat_index--;
 +    }
 +  }
 +
 +done:
 +  GST_DEBUG ("Advanced to segment: %d / %d r:%d (ret: %s)",
 +      stream->segment_index, segments_count,
 +      stream->segment_repeat_index, gst_flow_get_name (ret));
 +  return ret;
 +}
 +
 +gboolean
 +gst_mpd_client_get_next_header (GstMPDClient * client, gchar ** uri,
 +    guint stream_idx, gint64 * range_start, gint64 * range_end)
 +{
 +  GstActiveStream *stream;
 +  GstStreamPeriod *stream_period;
 +
 +  stream = gst_mpd_client_get_active_stream_by_index (client, stream_idx);
 +  g_return_val_if_fail (stream != NULL, FALSE);
 +  g_return_val_if_fail (stream->cur_representation != NULL, FALSE);
 +  stream_period = gst_mpd_client_get_stream_period (client);
 +  g_return_val_if_fail (stream_period != NULL, FALSE);
 +  g_return_val_if_fail (stream_period->period != NULL, FALSE);
 +
 +  *range_start = 0;
 +  *range_end = -1;
 +
 +  GST_DEBUG ("Looking for current representation header");
 +  *uri = NULL;
 +  if (stream->cur_segment_base) {
 +    if (stream->cur_segment_base->Initialization) {
 +      *uri =
 +          g_strdup (gst_mpdparser_get_initializationURL (stream,
 +              stream->cur_segment_base->Initialization));
 +      if (stream->cur_segment_base->Initialization->range) {
 +        *range_start =
 +            stream->cur_segment_base->Initialization->range->first_byte_pos;
 +        *range_end =
 +            stream->cur_segment_base->Initialization->range->last_byte_pos;
 +      }
 +    } else if (stream->cur_segment_base->indexRange) {
 +      *uri =
 +          g_strdup (gst_mpdparser_get_initializationURL (stream,
 +              stream->cur_segment_base->Initialization));
 +      *range_start = 0;
 +      *range_end = stream->cur_segment_base->indexRange->first_byte_pos - 1;
 +    }
 +  } else if (stream->cur_seg_template
 +      && stream->cur_seg_template->initialization) {
 +    *uri =
 +        gst_mpdparser_build_URL_from_template (stream->cur_seg_template->
 +        initialization, stream->cur_representation->id, 0,
 +        stream->cur_representation->bandwidth, 0);
 +  }
 +
 +  return *uri == NULL ? FALSE : TRUE;
 +}
 +
 +gboolean
 +gst_mpd_client_get_next_header_index (GstMPDClient * client, gchar ** uri,
 +    guint stream_idx, gint64 * range_start, gint64 * range_end)
 +{
 +  GstActiveStream *stream;
 +  GstStreamPeriod *stream_period;
 +
 +  stream = gst_mpd_client_get_active_stream_by_index (client, stream_idx);
 +  g_return_val_if_fail (stream != NULL, FALSE);
 +  g_return_val_if_fail (stream->cur_representation != NULL, FALSE);
 +  stream_period = gst_mpd_client_get_stream_period (client);
 +  g_return_val_if_fail (stream_period != NULL, FALSE);
 +  g_return_val_if_fail (stream_period->period != NULL, FALSE);
 +
 +  *range_start = 0;
 +  *range_end = -1;
 +
 +  GST_DEBUG ("Looking for current representation index");
 +  *uri = NULL;
 +  if (stream->cur_segment_base && stream->cur_segment_base->indexRange) {
 +    *uri =
 +        g_strdup (gst_mpdparser_get_initializationURL (stream,
 +            stream->cur_segment_base->RepresentationIndex));
 +    *range_start = stream->cur_segment_base->indexRange->first_byte_pos;
 +    *range_end = stream->cur_segment_base->indexRange->last_byte_pos;
 +  } else if (stream->cur_seg_template && stream->cur_seg_template->index) {
 +    *uri =
 +        gst_mpdparser_build_URL_from_template (stream->cur_seg_template->index,
 +        stream->cur_representation->id, 0,
 +        stream->cur_representation->bandwidth, 0);
 +  }
 +
 +  return *uri == NULL ? FALSE : TRUE;
 +}
 +
 +GstClockTime
 +gst_mpd_client_get_next_fragment_duration (GstMPDClient * client,
 +    GstActiveStream * stream)
 +{
 +  GstMediaSegment *media_segment = NULL;
 +  gint seg_idx;
 +
 +  g_return_val_if_fail (stream != NULL, 0);
 +
 +  seg_idx = stream->segment_index;
 +
 +  if (stream->segments) {
 +    if (seg_idx < stream->segments->len && seg_idx >= 0)
 +      media_segment = g_ptr_array_index (stream->segments, seg_idx);
 +
 +    return media_segment == NULL ? 0 : media_segment->duration;
 +  } else {
 +    GstClockTime duration =
 +        gst_mpd_client_get_segment_duration (client, stream, NULL);
 +    guint segments_count = gst_mpd_client_get_segments_counts (client, stream);
 +
 +    g_return_val_if_fail (GST_MPD_MULT_SEGMENT_BASE_NODE
 +        (stream->cur_seg_template)->SegmentTimeline == NULL, 0);
 +
 +    if (!GST_CLOCK_TIME_IS_VALID (duration) || (segments_count > 0
 +            && seg_idx >= segments_count)) {
 +      return 0;
 +    }
 +    return duration;
 +  }
 +}
 +
 +GstClockTime
 +gst_mpd_client_get_media_presentation_duration (GstMPDClient * client)
 +{
 +  GstClockTime duration;
 +
 +  g_return_val_if_fail (client != NULL, GST_CLOCK_TIME_NONE);
 +
 +  if (client->mpd_root_node->mediaPresentationDuration != -1) {
 +    duration = client->mpd_root_node->mediaPresentationDuration * GST_MSECOND;
 +  } else {
 +    /* We can only get the duration for on-demand streams */
 +    duration = GST_CLOCK_TIME_NONE;
 +  }
 +
 +  return duration;
 +}
 +
 +gboolean
 +gst_mpd_client_set_period_id (GstMPDClient * client, const gchar * period_id)
 +{
 +  GstStreamPeriod *next_stream_period;
 +  gboolean ret = FALSE;
 +  GList *iter;
 +  guint period_idx;
 +
 +  g_return_val_if_fail (client != NULL, FALSE);
 +  g_return_val_if_fail (client->periods != NULL, FALSE);
 +  g_return_val_if_fail (period_id != NULL, FALSE);
 +
 +  if (!gst_mpd_client_setup_media_presentation (client, GST_CLOCK_TIME_NONE, -1,
 +          period_id))
 +    return FALSE;
 +
 +  for (period_idx = 0, iter = client->periods; iter;
 +      period_idx++, iter = g_list_next (iter)) {
 +    next_stream_period = iter->data;
 +
 +    if (next_stream_period->period->id
 +        && strcmp (next_stream_period->period->id, period_id) == 0) {
 +      ret = TRUE;
 +      client->period_idx = period_idx;
 +      break;
 +    }
 +  }
 +
 +  return ret;
 +}
 +
 +gboolean
 +gst_mpd_client_set_period_index (GstMPDClient * client, guint period_idx)
 +{
 +  GstStreamPeriod *next_stream_period;
 +  gboolean ret = FALSE;
 +
 +  g_return_val_if_fail (client != NULL, FALSE);
 +  g_return_val_if_fail (client->periods != NULL, FALSE);
 +
 +  if (!gst_mpd_client_setup_media_presentation (client, -1, period_idx, NULL))
 +    return FALSE;
 +
 +  next_stream_period = g_list_nth_data (client->periods, period_idx);
 +  if (next_stream_period != NULL) {
 +    client->period_idx = period_idx;
 +    ret = TRUE;
 +  }
 +
 +  return ret;
 +}
 +
 +guint
 +gst_mpd_client_get_period_index (GstMPDClient * client)
 +{
 +  guint period_idx;
 +
 +  g_return_val_if_fail (client != NULL, 0);
 +  period_idx = client->period_idx;
 +
 +  return period_idx;
 +}
 +
 +const gchar *
 +gst_mpd_client_get_period_id (GstMPDClient * client)
 +{
 +  GstStreamPeriod *period;
 +  gchar *period_id = NULL;
 +
 +  g_return_val_if_fail (client != NULL, 0);
 +  period = g_list_nth_data (client->periods, client->period_idx);
 +  if (period && period->period)
 +    period_id = period->period->id;
 +
 +  return period_id;
 +}
 +
 +gboolean
 +gst_mpd_client_has_next_period (GstMPDClient * client)
 +{
 +  GList *next_stream_period;
 +  g_return_val_if_fail (client != NULL, FALSE);
 +  g_return_val_if_fail (client->periods != NULL, FALSE);
 +
 +  if (!gst_mpd_client_setup_media_presentation (client, GST_CLOCK_TIME_NONE,
 +          client->period_idx + 1, NULL))
 +    return FALSE;
 +
 +  next_stream_period =
 +      g_list_nth_data (client->periods, client->period_idx + 1);
 +  return next_stream_period != NULL;
 +}
 +
 +gboolean
 +gst_mpd_client_has_previous_period (GstMPDClient * client)
 +{
 +  GList *next_stream_period;
 +  g_return_val_if_fail (client != NULL, FALSE);
 +  g_return_val_if_fail (client->periods != NULL, FALSE);
 +
 +  if (!gst_mpd_client_setup_media_presentation (client, GST_CLOCK_TIME_NONE,
 +          client->period_idx - 1, NULL))
 +    return FALSE;
 +
 +  next_stream_period =
 +      g_list_nth_data (client->periods, client->period_idx - 1);
 +
 +  return next_stream_period != NULL;
 +}
 +
 +gint
 +gst_mpd_client_get_rep_idx_with_min_bandwidth (GList * Representations)
 +{
 +  GList *list = NULL, *lowest = NULL;
 +  GstMPDRepresentationNode *rep = NULL;
 +  gint lowest_bandwidth = -1;
 +
 +  if (Representations == NULL)
 +    return -1;
 +
 +  for (list = g_list_first (Representations); list; list = g_list_next (list)) {
 +    rep = (GstMPDRepresentationNode *) list->data;
 +    if (rep && (!lowest || rep->bandwidth < lowest_bandwidth)) {
 +      lowest = list;
 +      lowest_bandwidth = rep->bandwidth;
 +    }
 +  }
 +
 +  return lowest ? g_list_position (Representations, lowest) : -1;
 +}
 +
 +gint
 +gst_mpd_client_get_rep_idx_with_max_bandwidth (GList * Representations,
 +    gint64 max_bandwidth, gint max_video_width, gint max_video_height, gint
 +    max_video_framerate_n, gint max_video_framerate_d)
 +{
 +  GList *list = NULL, *best = NULL;
 +  GstMPDRepresentationNode *representation;
 +  gint best_bandwidth = 0;
 +
 +  GST_DEBUG ("max_bandwidth = %" G_GINT64_FORMAT, max_bandwidth);
 +
 +  if (Representations == NULL)
 +    return -1;
 +
 +  if (max_bandwidth <= 0)       /* 0 => get lowest representation available */
 +    return gst_mpd_client_get_rep_idx_with_min_bandwidth (Representations);
 +
 +  for (list = g_list_first (Representations); list; list = g_list_next (list)) {
 +    GstXMLFrameRate *framerate = NULL;
 +
 +    representation = (GstMPDRepresentationNode *) list->data;
 +
 +    /* FIXME: Really? */
 +    if (!representation)
 +      continue;
 +
 +    framerate = GST_MPD_REPRESENTATION_BASE_NODE (representation)->frameRate;
 +    if (!framerate)
 +      framerate =
 +          GST_MPD_REPRESENTATION_BASE_NODE (representation)->maxFrameRate;
 +
 +    if (framerate && max_video_framerate_n > 0) {
 +      if (gst_util_fraction_compare (framerate->num, framerate->den,
 +              max_video_framerate_n, max_video_framerate_d) > 0)
 +        continue;
 +    }
 +
 +    if (max_video_width > 0
 +        && GST_MPD_REPRESENTATION_BASE_NODE (representation)->width >
 +        max_video_width)
 +      continue;
 +    if (max_video_height > 0
 +        && GST_MPD_REPRESENTATION_BASE_NODE (representation)->height >
 +        max_video_height)
 +      continue;
 +
 +    if (representation->bandwidth <= max_bandwidth &&
 +        representation->bandwidth > best_bandwidth) {
 +      best = list;
 +      best_bandwidth = representation->bandwidth;
 +    }
 +  }
 +
 +  return best ? g_list_position (Representations, best) : -1;
 +}
 +
 +void
 +gst_mpd_client_seek_to_first_segment (GstMPDClient * client)
 +{
 +  GList *list;
 +
 +  g_return_if_fail (client != NULL);
 +  g_return_if_fail (client->active_streams != NULL);
 +
 +  for (list = g_list_first (client->active_streams); list;
 +      list = g_list_next (list)) {
 +    GstActiveStream *stream = (GstActiveStream *) list->data;
 +    if (stream) {
 +      stream->segment_index = 0;
 +      stream->segment_repeat_index = 0;
 +    }
 +  }
 +}
 +
 +static guint
 +gst_mpd_client_get_segments_counts (GstMPDClient * client,
 +    GstActiveStream * stream)
 +{
 +  GstStreamPeriod *stream_period;
 +
 +  g_return_val_if_fail (stream != NULL, 0);
 +
 +  if (stream->segments)
 +    return stream->segments->len;
 +  g_return_val_if_fail (GST_MPD_MULT_SEGMENT_BASE_NODE
 +      (stream->cur_seg_template)->SegmentTimeline == NULL, 0);
 +
 +  stream_period = gst_mpd_client_get_stream_period (client);
 +  if (stream_period->duration != -1)
 +    return gst_util_uint64_scale_ceil (stream_period->duration, 1,
 +        gst_mpd_client_get_segment_duration (client, stream, NULL));
 +
 +  return 0;
 +}
 +
 +gboolean
 +gst_mpd_client_is_live (GstMPDClient * client)
 +{
 +  g_return_val_if_fail (client != NULL, FALSE);
 +  g_return_val_if_fail (client->mpd_root_node != NULL, FALSE);
 +
 +  return client->mpd_root_node->type == GST_MPD_FILE_TYPE_DYNAMIC;
 +}
 +
 +guint
 +gst_mpd_client_get_nb_active_stream (GstMPDClient * client)
 +{
 +  g_return_val_if_fail (client != NULL, 0);
 +
 +  return g_list_length (client->active_streams);
 +}
 +
 +guint
 +gst_mpd_client_get_nb_adaptationSet (GstMPDClient * client)
 +{
 +  GstStreamPeriod *stream_period;
 +
 +  stream_period = gst_mpd_client_get_stream_period (client);
 +  g_return_val_if_fail (stream_period != NULL, 0);
 +  g_return_val_if_fail (stream_period->period != NULL, 0);
 +
 +  return g_list_length (stream_period->period->AdaptationSets);
 +}
 +
 +GstActiveStream *
 +gst_mpd_client_get_active_stream_by_index (GstMPDClient * client,
 +    guint stream_idx)
 +{
 +  g_return_val_if_fail (client != NULL, NULL);
 +  g_return_val_if_fail (client->active_streams != NULL, NULL);
 +
 +  return g_list_nth_data (client->active_streams, stream_idx);
 +}
 +
 +gboolean
 +gst_mpd_client_active_stream_contains_subtitles (GstActiveStream * stream)
 +{
 +  const gchar *mimeType;
 +  const gchar *adapt_set_codecs;
 +  const gchar *rep_codecs;
 +
 +  mimeType =
 +      GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_representation)->mimeType;
 +  if (!mimeType)
 +    mimeType =
 +        GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->mimeType;
 +
 +  if (g_strcmp0 (mimeType, "application/ttml+xml") == 0 ||
 +      g_strcmp0 (mimeType, "text/vtt") == 0)
 +    return TRUE;
 +
 +  adapt_set_codecs =
 +      GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->codecs;
 +  rep_codecs =
 +      GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_representation)->codecs;
 +
 +  return (adapt_set_codecs && g_str_has_prefix (adapt_set_codecs, "stpp"))
 +      || (rep_codecs && g_str_has_prefix (rep_codecs, "stpp"));
 +}
 +
 +GstCaps *
 +gst_mpd_client_get_stream_caps (GstActiveStream * stream)
 +{
 +  const gchar *mimeType, *caps_string;
 +  GstCaps *ret = NULL;
 +
 +  if (stream == NULL || stream->cur_adapt_set == NULL
 +      || stream->cur_representation == NULL)
 +    return NULL;
 +
 +  mimeType =
 +      GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_representation)->mimeType;
 +  if (mimeType == NULL) {
 +    mimeType =
 +        GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->mimeType;
 +  }
 +
 +  caps_string = gst_mpd_helper_mimetype_to_caps (mimeType);
 +
 +  if ((g_strcmp0 (caps_string, "application/mp4") == 0)
 +      && gst_mpd_client_active_stream_contains_subtitles (stream))
 +    caps_string = "video/quicktime";
 +
 +  if (caps_string)
 +    ret = gst_caps_from_string (caps_string);
 +
 +  return ret;
 +}
 +
 +gboolean
 +gst_mpd_client_get_bitstream_switching_flag (GstActiveStream * stream)
 +{
 +  if (stream == NULL || stream->cur_adapt_set == NULL)
 +    return FALSE;
 +
 +  return stream->cur_adapt_set->bitstreamSwitching;
 +}
 +
 +guint
 +gst_mpd_client_get_video_stream_width (GstActiveStream * stream)
 +{
 +  guint width;
 +
 +  if (stream == NULL || stream->cur_adapt_set == NULL
 +      || stream->cur_representation == NULL)
 +    return 0;
 +
 +  width = GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_representation)->width;
 +  if (width == 0) {
 +    width = GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->width;
 +  }
 +
 +  return width;
 +}
 +
 +guint
 +gst_mpd_client_get_video_stream_height (GstActiveStream * stream)
 +{
 +  guint height;
 +
 +  if (stream == NULL || stream->cur_adapt_set == NULL
 +      || stream->cur_representation == NULL)
 +    return 0;
 +
 +  height =
 +      GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_representation)->height;
 +  if (height == 0) {
 +    height = GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->height;
 +  }
 +
 +  return height;
 +}
 +
 +gboolean
 +gst_mpd_client_get_video_stream_framerate (GstActiveStream * stream,
 +    gint * fps_num, gint * fps_den)
 +{
 +  if (stream == NULL)
 +    return FALSE;
 +
 +  if (stream->cur_adapt_set &&
 +      GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->frameRate !=
 +      NULL) {
 +    *fps_num =
 +        GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->
 +        frameRate->num;
 +    *fps_den =
 +        GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->
 +        frameRate->den;
 +    return TRUE;
 +  }
 +
 +  if (stream->cur_adapt_set &&
 +      GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->maxFrameRate !=
 +      NULL) {
 +    *fps_num =
 +        GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->
 +        maxFrameRate->num;
 +    *fps_den =
 +        GST_MPD_REPRESENTATION_BASE_NODE (stream->cur_adapt_set)->
 +        maxFrameRate->den;
 +    return TRUE;
 +  }
 +
 +  if (stream->cur_representation &&
 +      GST_MPD_REPRESENTATION_BASE_NODE (stream->
 +          cur_representation)->frameRate != NULL) {
 +    *fps_num =
 +        GST_MPD_REPRESENTATION_BASE_NODE (stream->
 +        cur_representation)->frameRate->num;
 +    *fps_den =
 +        GST_MPD_REPRESENTATION_BASE_NODE (stream->
 +        cur_representation)->frameRate->den;
 +    return TRUE;
 +  }
 +
 +  if (stream->cur_representation &&
 +      GST_MPD_REPRESENTATION_BASE_NODE (stream->
 +          cur_representation)->maxFrameRate != NULL) {
 +    *fps_num =
 +        GST_MPD_REPRESENTATION_BASE_NODE (stream->
 +        cur_representation)->maxFrameRate->num;
 +    *fps_den =
 +        GST_MPD_REPRESENTATION_BASE_NODE (stream->
 +        cur_representation)->maxFrameRate->den;
 +    return TRUE;
 +  }
 +
 +  return FALSE;
 +}
 +
 +guint
 +gst_mpd_client_get_audio_stream_rate (GstActiveStream * stream)
 +{
 +  const gchar *rate;
 +
 +  if (stream == NULL || stream->cur_adapt_set == NULL
 +      || stream->cur_representation == NULL)
 +    return 0;
 +
 +  rate =
 +      GST_MPD_REPRESENTATION_BASE_NODE (stream->
 +      cur_representation)->audioSamplingRate;
 +  if (rate == NULL) {
 +    rate =
 +        GST_MPD_REPRESENTATION_BASE_NODE (stream->
 +        cur_adapt_set)->audioSamplingRate;
 +  }
 +
 +  return rate ? atoi (rate) : 0;
 +}
 +
 +guint
 +gst_mpd_client_get_audio_stream_num_channels (GstActiveStream * stream)
 +{
 +  if (stream == NULL || stream->cur_adapt_set == NULL
 +      || stream->cur_representation == NULL)
 +    return 0;
 +  /* TODO: here we have to parse the AudioChannelConfiguration descriptors */
 +  return 0;
 +}
 +
 +guint
 +gst_mpd_client_get_list_and_nb_of_audio_language (GstMPDClient * client,
 +    GList ** lang)
 +{
 +  GstStreamPeriod *stream_period;
 +  GstMPDAdaptationSetNode *adapt_set;
 +  GList *adaptation_sets, *list;
 +  const gchar *this_mimeType = "audio";
 +  gchar *mimeType = NULL;
 +  guint nb_adaptation_set = 0;
 +
 +  stream_period = gst_mpd_client_get_stream_period (client);
 +  g_return_val_if_fail (stream_period != NULL, 0);
 +  g_return_val_if_fail (stream_period->period != NULL, 0);
 +
 +  adaptation_sets =
 +      gst_mpd_client_get_adaptation_sets_for_period (client, stream_period);
 +  for (list = adaptation_sets; list; list = g_list_next (list)) {
 +    adapt_set = (GstMPDAdaptationSetNode *) list->data;
 +    if (adapt_set && adapt_set->lang) {
 +      gchar *this_lang = adapt_set->lang;
 +      GstMPDRepresentationNode *rep;
 +      rep =
 +          gst_mpd_client_get_lowest_representation (adapt_set->Representations);
 +      mimeType = NULL;
 +      if (GST_MPD_REPRESENTATION_BASE_NODE (rep))
 +        mimeType = GST_MPD_REPRESENTATION_BASE_NODE (rep)->mimeType;
 +      if (!mimeType && GST_MPD_REPRESENTATION_BASE_NODE (adapt_set)) {
 +        mimeType = GST_MPD_REPRESENTATION_BASE_NODE (adapt_set)->mimeType;
 +      }
 +
 +      if (gst_mpd_helper_strncmp_ext (mimeType, this_mimeType) == 0) {
 +        nb_adaptation_set++;
 +        *lang = g_list_append (*lang, this_lang);
 +      }
 +    }
 +  }
 +
 +  return nb_adaptation_set;
 +}
 +
 +
 +GstDateTime *
 +gst_mpd_client_get_next_segment_availability_start_time (GstMPDClient * client,
 +    GstActiveStream * stream)
 +{
 +  GstDateTime *availability_start_time, *rv;
 +  gint seg_idx;
 +  GstMediaSegment *segment;
 +  GstClockTime segmentEndTime;
 +  const GstStreamPeriod *stream_period;
 +  GstClockTime period_start = 0;
 +
 +  g_return_val_if_fail (client != NULL, NULL);
 +  g_return_val_if_fail (stream != NULL, NULL);
 +
 +  stream_period = gst_mpd_client_get_stream_period (client);
 +  if (stream_period && stream_period->period) {
 +    period_start = stream_period->start;
 +  }
 +
 +  seg_idx = stream->segment_index;
 +
 +  if (stream->segments) {
 +    segment = g_ptr_array_index (stream->segments, seg_idx);
 +
 +    if (segment->repeat >= 0) {
 +      segmentEndTime = segment->start + (stream->segment_repeat_index + 1) *
 +          segment->duration;
 +    } else if (seg_idx < stream->segments->len - 1) {
 +      const GstMediaSegment *next_segment =
 +          g_ptr_array_index (stream->segments, seg_idx + 1);
 +      segmentEndTime = next_segment->start;
 +    } else {
 +      g_return_val_if_fail (stream_period != NULL, NULL);
 +      segmentEndTime = period_start + stream_period->duration;
 +    }
 +  } else {
 +    GstClockTime seg_duration;
 +    seg_duration = gst_mpd_client_get_segment_duration (client, stream, NULL);
 +    if (seg_duration == 0)
 +      return NULL;
 +    segmentEndTime = period_start + (1 + seg_idx) * seg_duration;
 +  }
 +
 +  availability_start_time = gst_mpd_client_get_availability_start_time (client);
 +  if (availability_start_time == NULL) {
 +    GST_WARNING_OBJECT (client, "Failed to get availability_start_time");
 +    return NULL;
 +  }
 +
 +  rv = gst_mpd_client_add_time_difference (availability_start_time,
 +      segmentEndTime / GST_USECOND);
 +  gst_date_time_unref (availability_start_time);
 +  if (rv == NULL) {
 +    GST_WARNING_OBJECT (client, "Failed to offset availability_start_time");
 +    return NULL;
 +  }
 +
 +  return rv;
 +}
 +
 +gboolean
 +gst_mpd_client_seek_to_time (GstMPDClient * client, GDateTime * time)
 +{
 +  GDateTime *start;
 +  GTimeSpan ts_microseconds;
 +  GstClockTime ts;
 +  gboolean ret = TRUE;
 +  GList *stream;
 +
 +  g_return_val_if_fail (gst_mpd_client_is_live (client), FALSE);
 +  g_return_val_if_fail (client->mpd_root_node->availabilityStartTime != NULL,
 +      FALSE);
 +
 +  start =
 +      gst_date_time_to_g_date_time (client->mpd_root_node->
 +      availabilityStartTime);
 +
 +  ts_microseconds = g_date_time_difference (time, start);
 +  g_date_time_unref (start);
 +
 +  /* Clamp to availability start time, otherwise calculations wrap around */
 +  if (ts_microseconds < 0)
 +    ts_microseconds = 0;
 +
 +  ts = ts_microseconds * GST_USECOND;
 +  for (stream = client->active_streams; stream; stream = g_list_next (stream)) {
 +    ret =
 +        ret & gst_mpd_client_stream_seek (client, stream->data, TRUE, 0, ts,
 +        NULL);
 +  }
 +  return ret;
 +}
 +
 +gboolean
 +gst_mpd_client_has_isoff_ondemand_profile (GstMPDClient * client)
 +{
 +  return client->profile_isoff_ondemand;
 +}
 +
 +/**
 + * gst_mpd_client_parse_default_presentation_delay:
 + * @client: #GstMPDClient that has a parsed manifest
 + * @default_presentation_delay: A string that specifies a time period
 + * in fragments (e.g. "5 f"), seconds ("12 s") or milliseconds
 + * ("12000 ms")
 + * Returns: the parsed string in milliseconds
 + *
 + * Since: 1.6
 + */
 +gint64
 +gst_mpd_client_parse_default_presentation_delay (GstMPDClient * client,
 +    const gchar * default_presentation_delay)
 +{
 +  gint64 value;
 +  char *endptr = NULL;
 +
 +  g_return_val_if_fail (client != NULL, 0);
 +  g_return_val_if_fail (default_presentation_delay != NULL, 0);
 +  value = strtol (default_presentation_delay, &endptr, 10);
 +  if (endptr == default_presentation_delay || value == 0) {
 +    return 0;
 +  }
 +  while (*endptr == ' ')
 +    endptr++;
 +  if (*endptr == 's' || *endptr == 'S') {
 +    value *= 1000;              /* convert to ms */
 +  } else if (*endptr == 'f' || *endptr == 'F') {
 +    gint64 segment_duration;
 +    g_assert (client->mpd_root_node != NULL);
 +    segment_duration = client->mpd_root_node->maxSegmentDuration;
 +    value *= segment_duration;
 +  } else if (*endptr != 'm' && *endptr != 'M') {
 +    GST_ERROR ("Unable to parse default presentation delay: %s",
 +        default_presentation_delay);
 +    value = 0;
 +  }
 +  return value;
 +}
 +
 +GstClockTime
 +gst_mpd_client_get_maximum_segment_duration (GstMPDClient * client)
 +{
 +  GstClockTime ret = GST_CLOCK_TIME_NONE, dur;
 +  GList *stream;
 +
 +  g_return_val_if_fail (client != NULL, GST_CLOCK_TIME_NONE);
 +  g_return_val_if_fail (client->mpd_root_node != NULL, GST_CLOCK_TIME_NONE);
 +
 +  if (client->mpd_root_node->maxSegmentDuration != GST_MPD_DURATION_NONE) {
 +    return client->mpd_root_node->maxSegmentDuration * GST_MSECOND;
 +  }
 +
 +  /* According to the DASH specification, if maxSegmentDuration is not present:
 +     "If not present, then the maximum Segment duration shall be the maximum
 +     duration of any Segment documented in this MPD"
 +   */
 +  for (stream = client->active_streams; stream; stream = g_list_next (stream)) {
 +    dur = gst_mpd_client_get_segment_duration (client, stream->data, NULL);
 +    if (dur != GST_CLOCK_TIME_NONE && (dur > ret || ret == GST_CLOCK_TIME_NONE)) {
 +      ret = dur;
 +    }
 +  }
 +  return ret;
 +}
 +
 +guint
 +gst_mpd_client_get_period_index_at_time (GstMPDClient * client,
 +    GstDateTime * time)
 +{
 +  GList *iter;
 +  guint period_idx = G_MAXUINT;
 +  guint idx;
 +  gint64 time_offset;
 +  GstDateTime *avail_start =
 +      gst_mpd_client_get_availability_start_time (client);
 +  GstStreamPeriod *stream_period;
 +
 +  if (avail_start == NULL)
 +    return 0;
 +
 +  time_offset = gst_mpd_client_calculate_time_difference (avail_start, time);
 +  gst_date_time_unref (avail_start);
 +
 +  if (time_offset < 0)
 +    return 0;
 +
 +  if (!gst_mpd_client_setup_media_presentation (client, time_offset, -1, NULL))
 +    return 0;
 +
 +  for (idx = 0, iter = client->periods; iter; idx++, iter = g_list_next (iter)) {
 +    stream_period = iter->data;
 +    if (stream_period->start <= time_offset
 +        && (!GST_CLOCK_TIME_IS_VALID (stream_period->duration)
 +            || stream_period->start + stream_period->duration > time_offset)) {
 +      period_idx = idx;
 +      break;
 +    }
 +  }
 +
 +  return period_idx;
 +}
 +
 +/* add or set node methods */
 +
 +gboolean
 +gst_mpd_client_set_root_node (GstMPDClient * client,
 +    const gchar * property_name, ...)
 +{
 +  va_list myargs;
 +  g_return_val_if_fail (client != NULL, FALSE);
 +
 +  if (!client->mpd_root_node)
 +    client->mpd_root_node = gst_mpd_root_node_new ();
 +
 +  va_start (myargs, property_name);
 +  g_object_set_valist (G_OBJECT (client->mpd_root_node), property_name, myargs);
 +  va_end (myargs);
 +
 +  return TRUE;
 +}
 +
 +gboolean
 +gst_mpd_client_add_baseurl_node (GstMPDClient * client,
 +    const gchar * property_name, ...)
 +{
 +  GstMPDBaseURLNode *baseurl_node = NULL;
 +  va_list myargs;
 +
 +  g_return_val_if_fail (client != NULL, FALSE);
 +  g_return_val_if_fail (client->mpd_root_node != NULL, FALSE);
 +
 +  va_start (myargs, property_name);
 +
 +  baseurl_node = gst_mpd_baseurl_node_new ();
 +  g_object_set_valist (G_OBJECT (baseurl_node), property_name, myargs);
 +  client->mpd_root_node->BaseURLs =
 +      g_list_append (client->mpd_root_node->BaseURLs, baseurl_node);
 +
 +  va_end (myargs);
 +  return TRUE;
 +}
 +
 +/* returns a period id */
 +gchar *
 +gst_mpd_client_set_period_node (GstMPDClient * client,
 +    gchar * period_id, const gchar * property_name, ...)
 +{
 +  GstMPDPeriodNode *period_node = NULL;
 +  va_list myargs;
 +
 +  g_return_val_if_fail (client != NULL, NULL);
 +  g_return_val_if_fail (client->mpd_root_node != NULL, NULL);
 +
 +  period_node =
 +      GST_MPD_PERIOD_NODE (gst_mpd_client_get_period_with_id
 +      (client->mpd_root_node->Periods, period_id));
 +  if (!period_node) {
 +    period_node = gst_mpd_period_node_new ();
 +    if (period_id)
 +      period_node->id = g_strdup (period_id);
 +    else
 +      period_node->id =
 +          _generate_new_string_id (client->mpd_root_node->Periods,
 +          "period_%.2d", gst_mpd_client_get_period_with_id);
 +    client->mpd_root_node->Periods =
 +        g_list_append (client->mpd_root_node->Periods, period_node);
 +  }
 +
 +  va_start (myargs, property_name);
 +  g_object_set_valist (G_OBJECT (period_node), property_name, myargs);
 +  va_end (myargs);
 +
 +  return period_node->id;
 +}
 +
 +/* returns an adaptation set id */
 +guint
 +gst_mpd_client_set_adaptation_set_node (GstMPDClient * client,
 +    gchar * period_id, guint adaptation_set_id, const gchar * property_name,
 +    ...)
 +{
 +  GstMPDAdaptationSetNode *adap_node = NULL;
 +  GstMPDPeriodNode *period_node = NULL;
 +  va_list myargs;
 +
 +  g_return_val_if_fail (client != NULL, 0);
 +  g_return_val_if_fail (client->mpd_root_node != NULL, 0);
 +
 +  period_node =
 +      GST_MPD_PERIOD_NODE (gst_mpd_client_get_period_with_id
 +      (client->mpd_root_node->Periods, period_id));
 +  g_return_val_if_fail (period_node != NULL, 0);
 +  adap_node =
 +      GST_MPD_ADAPTATION_SET_NODE (gst_mpd_client_get_adaptation_set_with_id
 +      (period_node->AdaptationSets, adaptation_set_id));
 +  if (!adap_node) {
 +    adap_node = gst_mpd_adaptation_set_node_new ();
 +    if (adaptation_set_id)
 +      adap_node->id = adaptation_set_id;
 +    else
 +      adap_node->id =
 +          _generate_new_id (period_node->AdaptationSets,
 +          gst_mpd_client_get_adaptation_set_with_id);
 +    GST_DEBUG_OBJECT (client, "Add a new adaptation set with id %d",
 +        adap_node->id);
 +    period_node->AdaptationSets =
 +        g_list_append (period_node->AdaptationSets, adap_node);
 +  }
 +
 +  va_start (myargs, property_name);
 +  g_object_set_valist (G_OBJECT (adap_node), property_name, myargs);
 +  va_end (myargs);
 +
 +  return adap_node->id;
 +}
 +
 +/* returns a representation id */
 +gchar *
 +gst_mpd_client_set_representation_node (GstMPDClient * client,
 +    gchar * period_id, guint adaptation_set_id, gchar * representation_id,
 +    const gchar * property_name, ...)
 +{
 +  GstMPDRepresentationNode *rep_node = NULL;
 +  GstMPDAdaptationSetNode *adap_set_node = NULL;
 +  GstMPDPeriodNode *period_node = NULL;
 +  va_list myargs;
 +
 +  g_return_val_if_fail (client != NULL, NULL);
 +  g_return_val_if_fail (client->mpd_root_node != NULL, NULL);
 +
 +  period_node =
 +      GST_MPD_PERIOD_NODE (gst_mpd_client_get_period_with_id
 +      (client->mpd_root_node->Periods, period_id));
 +  adap_set_node =
 +      GST_MPD_ADAPTATION_SET_NODE (gst_mpd_client_get_adaptation_set_with_id
 +      (period_node->AdaptationSets, adaptation_set_id));
 +  g_return_val_if_fail (adap_set_node != NULL, NULL);
 +  rep_node =
 +      GST_MPD_REPRESENTATION_NODE (gst_mpd_client_get_representation_with_id
 +      (adap_set_node->Representations, representation_id));
 +  if (!rep_node) {
 +    rep_node = gst_mpd_representation_node_new ();
 +    if (representation_id)
 +      rep_node->id = g_strdup (representation_id);
 +    else
 +      rep_node->id =
 +          _generate_new_string_id (adap_set_node->Representations,
 +          "representation_%.2d", gst_mpd_client_get_representation_with_id);
 +    GST_DEBUG_OBJECT (client, "Add a new representation with id %s",
 +        rep_node->id);
 +    adap_set_node->Representations =
 +        g_list_append (adap_set_node->Representations, rep_node);
 +  }
 +
 +  va_start (myargs, property_name);
 +  g_object_set_valist (G_OBJECT (rep_node), property_name, myargs);
 +  va_end (myargs);
 +
 +  return rep_node->id;
 +}
 +
 +/* add/set a segment list node */
 +gboolean
 +gst_mpd_client_set_segment_list (GstMPDClient * client,
 +    gchar * period_id, guint adap_set_id, gchar * rep_id,
 +    const gchar * property_name, ...)
 +{
 +  GstMPDRepresentationNode *representation = NULL;
 +  GstMPDAdaptationSetNode *adaptation_set = NULL;
 +  GstMPDPeriodNode *period = NULL;
 +  va_list myargs;
 +
 +  g_return_val_if_fail (client != NULL, FALSE);
 +  g_return_val_if_fail (client->mpd_root_node != NULL, FALSE);
 +
 +  period =
 +      GST_MPD_PERIOD_NODE (gst_mpd_client_get_period_with_id
 +      (client->mpd_root_node->Periods, period_id));
 +  adaptation_set =
 +      GST_MPD_ADAPTATION_SET_NODE (gst_mpd_client_get_adaptation_set_with_id
 +      (period->AdaptationSets, adap_set_id));
 +  g_return_val_if_fail (adaptation_set != NULL, FALSE);
 +
 +  representation =
 +      GST_MPD_REPRESENTATION_NODE (gst_mpd_client_get_representation_with_id
 +      (adaptation_set->Representations, rep_id));
 +  if (!representation->SegmentList) {
 +    representation->SegmentList = gst_mpd_segment_list_node_new ();
 +  }
 +
 +  va_start (myargs, property_name);
 +  g_object_set_valist (G_OBJECT (representation->SegmentList), property_name,
 +      myargs);
 +  va_end (myargs);
 +
 +  return TRUE;
 +}
 +
 +/* add/set a segment template node */
 +gboolean
 +gst_mpd_client_set_segment_template (GstMPDClient * client,
 +    gchar * period_id, guint adap_set_id, gchar * rep_id,
 +    const gchar * property_name, ...)
 +{
 +  GstMPDRepresentationNode *representation = NULL;
 +  GstMPDAdaptationSetNode *adaptation_set = NULL;
 +  GstMPDPeriodNode *period = NULL;
 +  va_list myargs;
 +
 +  g_return_val_if_fail (client != NULL, FALSE);
 +  g_return_val_if_fail (client->mpd_root_node != NULL, FALSE);
 +
 +  period =
 +      GST_MPD_PERIOD_NODE (gst_mpd_client_get_period_with_id
 +      (client->mpd_root_node->Periods, period_id));
 +  adaptation_set =
 +      GST_MPD_ADAPTATION_SET_NODE (gst_mpd_client_get_adaptation_set_with_id
 +      (period->AdaptationSets, adap_set_id));
 +  g_return_val_if_fail (adaptation_set != NULL, FALSE);
 +
 +  representation =
 +      GST_MPD_REPRESENTATION_NODE (gst_mpd_client_get_representation_with_id
 +      (adaptation_set->Representations, rep_id));
 +  if (!representation->SegmentTemplate) {
 +    representation->SegmentTemplate = gst_mpd_segment_template_node_new ();
 +  }
 +
 +  va_start (myargs, property_name);
 +  g_object_set_valist (G_OBJECT (representation->SegmentTemplate),
 +      property_name, myargs);
 +  va_end (myargs);
 +
 +  return TRUE;
 +}
 +
 +/* add a segmentURL node with to a SegmentList node */
 +gboolean
 +gst_mpd_client_add_segment_url (GstMPDClient * client,
 +    gchar * period_id, guint adap_set_id, gchar * rep_id,
 +    const gchar * property_name, ...)
 +{
 +  GstMPDRepresentationNode *representation = NULL;
 +  GstMPDAdaptationSetNode *adaptation_set = NULL;
 +  GstMPDPeriodNode *period = NULL;
 +  GstMPDSegmentURLNode *segment_url = NULL;
 +  guint64 media_presentation_duration = 0;
 +  va_list myargs;
 +
 +  g_return_val_if_fail (client != NULL, FALSE);
 +  g_return_val_if_fail (client->mpd_root_node != NULL, FALSE);
 +
 +  period =
 +      GST_MPD_PERIOD_NODE (gst_mpd_client_get_period_with_id
 +      (client->mpd_root_node->Periods, period_id));
 +  adaptation_set =
 +      GST_MPD_ADAPTATION_SET_NODE (gst_mpd_client_get_adaptation_set_with_id
 +      (period->AdaptationSets, adap_set_id));
 +  g_return_val_if_fail (adaptation_set != NULL, FALSE);
 +
 +  representation =
 +      GST_MPD_REPRESENTATION_NODE (gst_mpd_client_get_representation_with_id
 +      (adaptation_set->Representations, rep_id));
 +
 +  if (!representation->SegmentList) {
 +    representation->SegmentList = gst_mpd_segment_list_node_new ();
 +  }
 +
 +  segment_url = gst_mpd_segment_url_node_new ();
 +
 +  va_start (myargs, property_name);
 +  g_object_set_valist (G_OBJECT (segment_url), property_name, myargs);
 +  va_end (myargs);
 +
 +  gst_mpd_segment_list_node_add_segment (representation->SegmentList,
 +      segment_url);
 +
 +  /* Set the media presentation time according to the new segment duration added */
 +  g_object_get (client->mpd_root_node, "media-presentation-duration",
 +      &media_presentation_duration, NULL);
 +  media_presentation_duration +=
 +      GST_MPD_MULT_SEGMENT_BASE_NODE (representation->SegmentList)->duration;
 +  g_object_set (client->mpd_root_node, "media-presentation-duration",
 +      media_presentation_duration, NULL);
 +
 +  return TRUE;
 +}
@@@ -63,10 -38,43 +63,15 @@@ typedef struct _GstActiveStrea
  typedef struct _GstStreamPeriod           GstStreamPeriod;
  typedef struct _GstMediaFragmentInfo      GstMediaFragmentInfo;
  typedef struct _GstMediaSegment           GstMediaSegment;
 -typedef struct _GstMPDNode                GstMPDNode;
 -typedef struct _GstPeriodNode             GstPeriodNode;
 -typedef struct _GstRepresentationBaseType GstRepresentationBaseType;
 -typedef struct _GstDescriptorType         GstDescriptorType;
 -typedef struct _GstContentComponentNode   GstContentComponentNode;
 -typedef struct _GstAdaptationSetNode      GstAdaptationSetNode;
 -typedef struct _GstRepresentationNode     GstRepresentationNode;
 -typedef struct _GstSubRepresentationNode  GstSubRepresentationNode;
 -typedef struct _GstSegmentListNode        GstSegmentListNode;
 -typedef struct _GstSegmentTemplateNode    GstSegmentTemplateNode;
 -typedef struct _GstSegmentURLNode         GstSegmentURLNode;
 -typedef struct _GstBaseURL                GstBaseURL;
 -typedef struct _GstRange                  GstRange;
 -typedef struct _GstRatio                  GstRatio;
 -typedef struct _GstFrameRate              GstFrameRate;
 -typedef struct _GstConditionalUintType    GstConditionalUintType;
 -typedef struct _GstSubsetNode             GstSubsetNode;
 -typedef struct _GstProgramInformationNode GstProgramInformationNode;
 -typedef struct _GstMetricsRangeNode       GstMetricsRangeNode;
 -typedef struct _GstMetricsNode            GstMetricsNode;
 -typedef struct _GstUTCTimingNode          GstUTCTimingNode;
 -typedef struct _GstSNode                  GstSNode;
 -typedef struct _GstSegmentTimelineNode    GstSegmentTimelineNode;
 -typedef struct _GstSegmentBaseType        GstSegmentBaseType;
 -typedef struct _GstURLType                GstURLType;
 -typedef struct _GstMultSegmentBaseType    GstMultSegmentBaseType;
  
 -#define GST_MPD_CLIENT_LOCK(c) g_mutex_lock (&c->lock);
 -#define GST_MPD_CLIENT_UNLOCK(c) g_mutex_unlock (&c->lock);
 +
 +#define GST_MPD_DURATION_NONE ((guint64)-1)
  
 -#define GST_MPD_DURATION_NONE ((guint64)-1)
 -
+ #ifdef TIZEN_FEATURE_ADAPTIVE_MODIFICATION
+ #define DEFAULT_ADAPTIVE_RETRY -1
+ #define DEFAULT_ADAPTIVE_TIMEOUT -1
+ #endif
  typedef enum
  {
    GST_STREAM_UNKNOWN,
@@@ -9,11 -9,10 +9,11 @@@ dtls_sources = 
    'gstdtlssrtpdemux.c',
    'gstdtlssrtpenc.c',
    'plugin.c',
 +  'gstdtlselement.c',
  ]
  
--openssl_dep = dependency('openssl', version : '>= 1.0.1', required : get_option('dtls'))
--libcrypto_dep = dependency('libcrypto', required : get_option('dtls'))
++openssl_dep = dependency('openssl1.1', version : '>= 1.0.1', required : get_option('dtls'))
++libcrypto_dep = dependency('libcrypto1.1', required : get_option('dtls'))
  
  if openssl_dep.found() and libcrypto_dep.found()
    gstdtls = library('gstdtls',
  
  #include <string.h>
  #include <gst/base/gsttypefindhelper.h>
 +#include "gsthlselements.h"
++#ifdef TIZEN_FEATURE_HLSDEMUX_LANG_TAG
+ #include <gst/tag/tag.h>
++#endif
  #include "gsthlsdemux.h"
  
  static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src_%u",
@@@ -487,9 -588,95 +584,14 @@@ gst_hls_demux_update_manifest (GstAdapt
    return GST_FLOW_OK;
  }
  
 -#ifdef TIZEN_FEATURE_AVOID_PAD_SWITCHING
 -static GstAdaptiveDemuxStream *
 -gst_adaptive_demux_find_stream (GstAdaptiveDemux * demux,
 -    gboolean is_primary_playlist, GstHLSMediaType type)
 -{
 -  GstAdaptiveDemuxStream *stream;
 -  GstHLSDemuxStream *hls_stream;
 -  GList *iter;
 -  GstCaps *caps;
 -  gchar *caps_str;
 -  gboolean find_stream = FALSE;
 -
 -  for (iter = demux->streams; iter; iter = g_list_next (iter)) {
 -    stream = iter->data;
 -    if (!stream) {
 -      GST_ERROR_OBJECT (demux, "no stream data");
 -      continue;
 -    }
 -    hls_stream = GST_HLS_DEMUX_STREAM_CAST (stream);
 -    if (is_primary_playlist && hls_stream->is_primary_playlist)
 -      return stream;
 -
 -    caps = gst_pad_get_current_caps (stream->pad);
 -    caps_str = gst_caps_to_string (caps);
 -    GST_DEBUG_OBJECT (demux, "pad %s:%s, caps %s",
 -        GST_DEBUG_PAD_NAME (stream->pad), caps_str);
 -
 -    if (((type == GST_HLS_MEDIA_TYPE_AUDIO) && (g_strrstr (caps_str, "audio")))
 -        || ((type == GST_HLS_MEDIA_TYPE_VIDEO)
 -            && (g_strrstr (caps_str, "video"))))
 -      find_stream = TRUE;
 -
 -    g_free (caps_str);
 -    gst_caps_unref (caps);
 -
 -    if (find_stream)
 -      return stream;
 -  }
 -
 -  GST_WARNING_OBJECT (demux, "failed to find stream %d %d", is_primary_playlist,
 -      type);
 -  return NULL;
 -}
 -
 -static void
 -create_stream_for_playlist (GstAdaptiveDemux * demux, GstM3U8 * playlist,
 -    gboolean is_primary_playlist, GstHLSMediaType type)
 -{
 -  GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (demux);
 -  GstHLSDemuxStream *hlsdemux_stream = NULL;
 -  GstAdaptiveDemuxStream *stream = NULL;
 -
 -  if (!is_primary_playlist &&
 -      type != GST_HLS_MEDIA_TYPE_AUDIO && type != GST_HLS_MEDIA_TYPE_VIDEO) {
 -    /* FIXME: Later, create the stream but mark not-selected */
 -    GST_LOG_OBJECT (demux, "Ignoring not-selected stream");
 -    return;
 -  }
 -
 -  GST_DEBUG_OBJECT (demux, "streams list %d", g_list_length (demux->streams));
 -
 -  if (demux->streams)
 -    stream = gst_adaptive_demux_find_stream (demux, is_primary_playlist, type);
 -
 -  if (!stream) {
 -    GST_LOG_OBJECT (demux, "new pad will be created");
 -    stream = gst_adaptive_demux_stream_new (demux,
 -        gst_hls_demux_create_pad (hlsdemux));
 -  }
 -
 -  hlsdemux_stream = GST_HLS_DEMUX_STREAM_CAST (stream);
 -
 -  hlsdemux_stream->stream_type = GST_HLS_TSREADER_NONE;
 -
 -  hlsdemux_stream->playlist = gst_m3u8_ref (playlist);
 -  hlsdemux_stream->is_primary_playlist = is_primary_playlist;
 -
 -  hlsdemux_stream->do_typefind = TRUE;
 -  hlsdemux_stream->reset_pts = TRUE;
 -}
 -#else
  static void
+ #ifdef TIZEN_FEATURE_HLSDEMUX_LANG_TAG
+ create_stream_for_playlist (GstAdaptiveDemux * demux, GstM3U8 * playlist,
+     gboolean is_primary_playlist, gboolean selected, GstHLSMedia * media)
+ #else
  create_stream_for_playlist (GstAdaptiveDemux * demux, GstM3U8 * playlist,
      gboolean is_primary_playlist, gboolean selected)
+ #endif
  {
    GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (demux);
    GstHLSDemuxStream *hlsdemux_stream;
  
    hlsdemux_stream->do_typefind = TRUE;
    hlsdemux_stream->reset_pts = TRUE;
+ #ifdef TIZEN_FEATURE_HLSDEMUX_DISCONT_SEQUENCE
+   hlsdemux_stream->failed_count = 0;
+ #endif
+ #ifdef TIZEN_FEATURE_HLSDEMUX_DISCONT
+   hlsdemux_stream->sequence_pos = GST_CLOCK_TIME_NONE;
+   hlsdemux_stream->last_pcr = GST_CLOCK_TIME_NONE;
+ #endif
+ #ifdef TIZEN_FEATURE_HLSDEMUX_LANG_TAG
+   if (media) {
+     gst_hlsdemux_set_stream_event (stream, media);
+     return;
+   }
+   for (GList * mlist =
+       hlsdemux->current_variant->media[GST_HLS_MEDIA_TYPE_AUDIO]; mlist;
+       mlist = g_list_next (mlist)) {
+     GstHLSMedia *media = mlist->data;
+     if (!media->uri && gst_hlsdemux_set_stream_event (stream, media))
+       return;
+   }
+ #endif
  }
 -#endif
  
 -#ifdef TIZEN_FEATURE_UPSTREAM
  static GstHLSDemuxStream *
  find_adaptive_stream_for_playlist (GstAdaptiveDemux * demux, GstM3U8 * playlist)
  {
@@@ -660,8 -873,17 +785,11 @@@ gst_hls_demux_setup_streams (GstAdaptiv
    gst_hls_demux_clear_all_pending_data (hlsdemux);
  
    /* 1 output for the main playlist */
 -#ifdef TIZEN_FEATURE_AVOID_PAD_SWITCHING
 -  create_stream_for_playlist (demux, playlist->m3u8, TRUE,
 -      GST_HLS_MEDIA_TYPE_INVALID);
 -#else
+ #ifdef TIZEN_FEATURE_HLSDEMUX_LANG_TAG
+   create_stream_for_playlist (demux, playlist->m3u8, TRUE, TRUE, NULL);
+ #else
    create_stream_for_playlist (demux, playlist->m3u8, TRUE, TRUE);
+ #endif
 -#endif
    for (i = 0; i < GST_HLS_N_MEDIA_TYPES; ++i) {
      GList *mlist = playlist->media[i];
      while (mlist != NULL) {
          mlist = mlist->next;
          continue;
        }
 -#ifdef TIZEN_FEATURE_UPSTREAM
        GST_LOG_OBJECT (demux, "media of type %s - %s, uri: %s",
            gst_hls_media_type_get_name (i), media->name, media->uri);
 -#else
 -      GST_LOG_OBJECT (demux, "media of type %d - %s, uri: %s", i,
 -          media->name, media->uri);
 -#endif
 -
 -#ifdef TIZEN_FEATURE_AVOID_PAD_SWITCHING
 -      create_stream_for_playlist (demux, media->playlist, FALSE, media->mtype);
 -#else
        create_stream_for_playlist (demux, media->playlist, FALSE,
            (media->mtype == GST_HLS_MEDIA_TYPE_VIDEO
-               || media->mtype == GST_HLS_MEDIA_TYPE_AUDIO));
+               || media->mtype == GST_HLS_MEDIA_TYPE_AUDIO
+ #ifdef TIZEN_FEATURE_HLSDEMUX_LANG_TAG
+               || media->mtype == GST_HLS_MEDIA_TYPE_SUBTITLES), media);
+ #else
+               || media->mtype == GST_HLS_MEDIA_TYPE_SUBTITLES));
+ #endif
 -#endif
 -
        mlist = mlist->next;
      }
    }
@@@ -802,8 -1070,26 +951,26 @@@ gst_hls_demux_process_manifest (GstAdap
      GST_INFO_OBJECT (hlsdemux, "selected %s", variant->name);
      gst_hls_demux_set_current_variant (hlsdemux, variant);      // FIXME: inline?
    }
+ #endif
+ #ifdef TIZEN_FEATURE_AD
+   if (variant) {
+     GST_DEBUG_OBJECT (hlsdemux, "post AD info");
+     gst_element_post_message (GST_ELEMENT_CAST (hlsdemux),
+         gst_message_new_element (GST_OBJECT_CAST (hlsdemux),
+             gst_structure_new ("adaptive-ad-info",
+                 "ad-info", G_TYPE_POINTER, variant->m3u8->ad_info, NULL)));
+     GST_DEBUG_OBJECT (hlsdemux, "post current bandwidth info : %d",
+         variant->bandwidth);
+     gst_element_post_message (GST_ELEMENT_CAST (hlsdemux),
+         gst_message_new_element (GST_OBJECT_CAST (hlsdemux),
+             gst_structure_new (GST_ADAPTIVE_DEMUX_STATISTICS_MESSAGE_NAME,
+                 "bitrate", G_TYPE_INT, variant->bandwidth, NULL)));
+   }
+ #endif
  
 -  /* get the selected media playlist (unless the inital list was one already) */
 +  /* get the selected media playlist (unless the initial list was one already) */
    if (!hlsdemux->master->is_simple) {
      GError *err = NULL;
  
@@@ -994,25 -1280,51 +1168,48 @@@ gst_hls_demux_handle_buffer (GstAdaptiv
      }
  
      if (G_UNLIKELY (!caps)) {
-       /* Won't need this mapping any more all paths return inside this if() */
-       gst_buffer_unmap (buffer, &info);
-       /* Only fail typefinding if we already a good amount of data
-        * and we still don't know the type */
-       if (buffer_size > (2 * 1024 * 1024) || at_eos) {
-         GST_ELEMENT_ERROR (hlsdemux, STREAM, TYPE_NOT_FOUND,
-             ("Could not determine type of stream"), (NULL));
+ #ifdef TIZEN_FEATURE_HLSDEMUX_EMPTY_VTT
+       if (at_eos && info.data
+           && g_strrstr ((const gchar *) info.data, "WEBVTT")) {
+         gchar *dummy =
+             g_strdup ("WEBVTT\nX-TIMESTAMP-MAP=LOCAL:00:00:00.000,MPEGTS:0");
+         GstBuffer *dummy_buffer =
+             gst_buffer_new_wrapped (dummy, strlen (dummy));
+         gst_buffer_unmap (buffer, &info);
          gst_buffer_unref (buffer);
-         return GST_FLOW_NOT_NEGOTIATED;
-       }
  
-       hls_stream->pending_typefind_buffer = buffer;
+         GST_WARNING_OBJECT (stream->pad,
+             "replace the empty VTT buffer with dummy");
  
-       return GST_FLOW_OK;
+         buffer = dummy_buffer;
+         gst_buffer_map (buffer, &info, GST_MAP_READ);
+         caps = gst_caps_new_simple ("application/x-subtitle-vtt",
+             "parsed", G_TYPE_BOOLEAN, FALSE, NULL);
+       } else
+ #endif
+       {
+         /* Won't need this mapping any more all paths return inside this if() */
+         gst_buffer_unmap (buffer, &info);
+         /* Only fail typefinding if we already a good amount of data
+          * and we still don't know the type */
+         if (buffer_size > (2 * 1024 * 1024) || at_eos) {
+           GST_ELEMENT_ERROR (hlsdemux, STREAM, TYPE_NOT_FOUND,
+               ("Could not determine type of stream"), (NULL));
+           gst_buffer_unref (buffer);
+           return GST_FLOW_NOT_NEGOTIATED;
+         }
+         hls_stream->pending_typefind_buffer = buffer;
++
+         return GST_FLOW_OK;
 -      }
++        }
      }
 -#ifdef TIZEN_FEATURE_UPSTREAM
 +
      GST_DEBUG_OBJECT (stream->pad,
          "Typefind result: %" GST_PTR_FORMAT " prob:%d", caps, prob);
 -#else
 -    GST_DEBUG_OBJECT (hlsdemux, "Typefind result: %" GST_PTR_FORMAT " prob:%d",
 -        caps, prob);
 -#endif
  
      hls_stream->stream_type = caps_to_reader (caps);
      gst_hlsdemux_tsreader_set_type (&hls_stream->tsreader,
@@@ -1251,25 -1592,30 +1470,39 @@@ gst_hls_demux_update_fragment_info (Gst
  
    if (file == NULL) {
      GST_INFO_OBJECT (hlsdemux, "This playlist doesn't contain more fragments");
+ #ifdef TIZEN_FEATURE_HLSDEMUX_DISCONT_SEQUENCE
+     if (++hlsdemux_stream->failed_count > DEFAULT_FAILED_COUNT) {
+       GST_WARNING_OBJECT (hlsdemux,
+           "Reset media sequence(fail %d times to gst_m3u8_get_next_fragment)",
+           hlsdemux_stream->failed_count);
+       m3u8->sequence = 0;
+     }
+ #endif
      return GST_FLOW_EOS;
    }
+ #ifdef TIZEN_FEATURE_HLSDEMUX_DISCONT_SEQUENCE
+   hlsdemux_stream->failed_count = 0;
+ #endif
  
 +  if (GST_ADAPTIVE_DEMUX_STREAM_NEED_HEADER (stream) && file->init_file) {
 +    GstM3U8InitFile *header_file = file->init_file;
 +    stream->fragment.header_uri = g_strdup (header_file->uri);
 +    stream->fragment.header_range_start = header_file->offset;
 +    if (header_file->size != -1) {
 +      stream->fragment.header_range_end =
 +          header_file->offset + header_file->size - 1;
 +    } else {
 +      stream->fragment.header_range_end = -1;
 +    }
 +  }
 +
    if (stream->discont)
      discont = TRUE;
  
+ #ifdef TIZEN_FEATURE_HLSDEMUX_DISCONT
+   hlsdemux_stream->sequence_pos = sequence_pos;
+ #endif
    /* set up our source for download */
 -#ifdef TIZEN_FEATURE_AVOID_PAD_SWITCHING
 -  stream->fragment.timestamp = hlsdemux_stream->current_pts = sequence_pos;
 -#else
    if (hlsdemux_stream->reset_pts || discont
        || stream->demux->segment.rate < 0.0) {
      stream->fragment.timestamp = sequence_pos;
@@@ -1762,8 -2142,22 +2024,18 @@@ gst_hls_demux_change_playlist (GstHLSDe
  
    stream = adaptive_demux->streams->data;
  
 -#ifdef TIZEN_FEATURE_UPSTREAM
    /* Make sure we keep a reference in case we need to switch back */
    previous_variant = gst_hls_variant_stream_ref (demux->current_variant);
 -#else
 -  previous_variant = demux->current_variant;
 -#endif
+ #ifdef TIZEN_FEATURE_ADAPTIVE_MODIFICATION
+   new_variant =
+       gst_hls_master_playlist_get_variant_for_bandwitdh_limit (demux->master,
+       demux->current_variant, max_bitrate, NULL, adaptive_demux->min_bandwidth,
+       adaptive_demux->max_bandwidth, adaptive_demux->max_width,
+       adaptive_demux->max_height);
+   GST_INFO_OBJECT (demux, "new_variant : %d, %d x %d",
+       new_variant->bandwidth, new_variant->width, new_variant->height);
+ #else
    new_variant =
        gst_hls_master_playlist_get_variant_for_bitrate (demux->master,
        demux->current_variant, max_bitrate);
@@@ -97,6 -98,13 +97,10 @@@ struct _GstHLSDemuxStrea
                                            We only know that it is the last at EOS */
    guint64 current_offset;              /* offset we're currently at */
    gboolean reset_pts;
 -#ifdef TIZEN_FEATURE_AVOID_PAD_SWITCHING
 -  GstClockTime current_pts;
 -#endif
+ #ifdef TIZEN_FEATURE_HLSDEMUX_DISCONT
+   GstClockTime sequence_pos;
+   GstClockTime last_pcr;
+ #endif
  
    /* decryption tooling */
  #if defined(HAVE_OPENSSL)
diff --cc ext/hls/m3u8.c
  #include <glib.h>
  #include <string.h>
  
 -#include "gsthls.h"
  #include "m3u8.h"
 +#include "gsthlselements.h"
  
  #define GST_CAT_DEFAULT hls_debug
+ #ifdef TIZEN_FEATURE_ADAPTIVE_MODIFICATION
+ #define DEFAULT_RESOLUTION_LIMIT -1
+ #define DEFAULT_BANDWIDTH_LIMIT -1
+ #endif
  
  static GstM3U8MediaFile *gst_m3u8_media_file_new (gchar * uri,
      gchar * title, GstClockTime duration, guint sequence);
@@@ -493,7 -495,9 +530,10 @@@ gst_m3u8_update (GstM3U8 * self, gchar 
    gint64 mediasequence;
    GList *previous_files = NULL;
    gboolean have_mediasequence = FALSE;
 +  GstM3U8InitFile *last_init_file = NULL;
+ #ifdef TIZEN_FEATURE_AD
+   GstClockTime timestamp = 0;
+ #endif
  
    g_return_val_if_fail (self != NULL, FALSE);
    g_return_val_if_fail (data != NULL, FALSE);
          }
  
          file->discont = discontinuity;
 +        if (last_init_file)
 +          file->init_file = gst_m3u8_init_file_ref (last_init_file);
 +
+ #ifdef TIZEN_FEATURE_AD
+         timestamp += duration;
+ #endif
          duration = 0;
          title = NULL;
          discontinuity = FALSE;
          } else {
            goto next_line;
          }
-       } else {
 +      } else if (g_str_has_prefix (data_ext_x, "MAP:")) {
 +        gchar *v, *a, *header_uri = NULL;
 +
 +        data = data + 11;
 +
 +        while (data != NULL && parse_attributes (&data, &a, &v)) {
 +          if (strcmp (a, "URI") == 0) {
 +            header_uri =
 +                uri_join (self->base_uri ? self->base_uri : self->uri, v);
 +          } else if (strcmp (a, "BYTERANGE") == 0) {
 +            if (int64_from_string (v, &v, &size)) {
 +              if (*v == '@' && !int64_from_string (v + 1, &v, &offset)) {
 +                g_free (header_uri);
 +                goto next_line;
 +              }
 +            } else {
 +              g_free (header_uri);
 +              goto next_line;
 +            }
 +          }
 +        }
 +
 +        if (header_uri) {
 +          GstM3U8InitFile *init_file;
 +          init_file = gst_m3u8_init_file_new (header_uri);
 +
 +          if (size != -1) {
 +            init_file->size = size;
 +            if (offset != -1)
 +              init_file->offset = offset;
 +            else
 +              init_file->offset = 0;
 +          } else {
 +            init_file->size = -1;
 +            init_file->offset = 0;
 +          }
 +          if (last_init_file)
 +            gst_m3u8_init_file_unref (last_init_file);
 +
 +          last_init_file = init_file;
 +        }
+       }
+ #ifdef TIZEN_FEATURE_AD
+       else if (g_str_has_prefix (data_ext_x, "CUE-OUT:")) {
+         GstM3U8Cue *cue;
+         gdouble fval;
+         GST_LOG ("cue out: %" GST_TIME_FORMAT ", %s", GST_TIME_ARGS (timestamp), data);
+         data = data + strlen ("#EXT-X-CUE-OUT:");
+         if (g_str_has_prefix (data, "DURATION="))
+           data = data + strlen ("DURATION=");
+         if (!double_from_string (data, &data, &fval)) {
+           GST_WARNING ("Can't read CUE-OUT duration");
+           goto next_line;
+         }
+         duration = fval * (gdouble) GST_SECOND;
+         cue = gst_m3u8_cue_info_new (timestamp, duration);
+         GST_LOG ("cue out start %" GST_TIME_FORMAT ", dur %" GST_TIME_FORMAT
+                       , GST_TIME_ARGS (cue->start_time), GST_TIME_ARGS (cue->duration));
+         self->ad_info->cue = g_list_append (self->ad_info->cue, cue);
+         duration = 0;
+       } else if (g_str_has_prefix (data_ext_x, "CUE-IN")) {
+         GList *cue;
+         GstM3U8Cue *cue_data;
+         GST_LOG ("cue in: %" GST_TIME_FORMAT ", %s", GST_TIME_ARGS (timestamp), data);
+         cue = g_list_last (self->ad_info->cue);
+         if (!cue || !(cue->data)) {
+           GST_WARNING ("there is no valid data");
+           goto next_line;
+         }
+         cue_data = cue->data;
+         GST_LOG ("start %" GST_TIME_FORMAT ", dur %" GST_TIME_FORMAT,
+                 GST_TIME_ARGS (cue_data->start_time), GST_TIME_ARGS (cue_data->duration));
+         if (cue_data->end_time != 0) {
+           GST_WARNING ("cue syntax err, skip this tag.");
+           goto next_line;
+         }
+         cue_data->end_time = timestamp;
+         GST_LOG ("cue start %" GST_TIME_FORMAT ", end %" GST_TIME_FORMAT " dur %" GST_TIME_FORMAT,
+                  GST_TIME_ARGS (cue_data->start_time), GST_TIME_ARGS (cue_data->end_time),
+                  GST_TIME_ARGS (cue_data->duration));
+       } else if (g_str_has_prefix (data_ext_x, "CUE-OUT-CONT:")) {
+         GstM3U8CueOutCont *cont = g_new0 (GstM3U8CueOutCont, 1);
+         GST_LOG ("cue cont: %" GST_TIME_FORMAT ", %s", GST_TIME_ARGS (timestamp), data);
+         data = data + strlen ("#EXT-X-CUE-OUT-CONT:");
+         cont->timestamp = timestamp;
+         cont->cont_data = g_strdup (data);
+         self->ad_info->cue_cont = g_list_append (self->ad_info->cue_cont, cont);
+       }
+ #endif
+       else {
          GST_LOG ("Ignored line: %s", data);
        }
      } else {
diff --cc ext/hls/m3u8.h
@@@ -100,16 -107,31 +108,39 @@@ struct _GstM3U8MediaFil
    guint8 iv[16];
    gint64 offset, size;
    gint ref_count;               /* ATOMIC */
 +  GstM3U8InitFile *init_file;   /* Media Initialization (hold ref) */
 +};
 +
 +struct _GstM3U8InitFile
 +{
 +  gchar *uri;
 +  gint64 offset, size;
 +  guint ref_count;      /* ATOMIC */
  };
  
+ #ifdef TIZEN_FEATURE_AD
+ struct _GstM3U8Cue
+ {
+   GstClockTime start_time;      /* EXT-X-CUE-OUT */
+   GstClockTime end_time;        /* EXT-X-CUE-IN */
+   GstClockTime duration;        /* from EXT-X-CUE-OUT */
+ };
+ struct _GstM3U8CueOutCont
+ {
+   GstClockTime timestamp;
+   gchar *cont_data;             /* EXT-X-CUE-OUT-CONT */
+ };
+ struct _GstM3U8AdInfo
+ {
+   GList *cue;                   /* GstM3U8Cue */
+   GList *cue_cont;              /* GstM3U8CueOutCont */
+ };
+ #endif
  GstM3U8MediaFile * gst_m3u8_media_file_ref   (GstM3U8MediaFile * mfile);
  
  void               gst_m3u8_media_file_unref (GstM3U8MediaFile * mfile);
@@@ -35,8 -33,8 +35,8 @@@ if not hls_crypto_dep.found() and ['aut
    endif
  endif
  
--if not hls_crypto_dep.found() and ['auto', 'openssl'].contains(hls_crypto)
--  hls_crypto_dep = dependency('openssl', required : false)
++if not hls_crypto_dep.found() and ['auto', 'openssl1.1'].contains(hls_crypto)
++  hls_crypto_dep = dependency('openssl1.1', required : false)
    if hls_crypto_dep.found()
      hls_cargs += ['-DHAVE_OPENSSL']
    endif
Simple merge
Simple merge
  GST_DEBUG_CATEGORY_EXTERN (openal_debug);
  #define GST_CAT_DEFAULT openal_debug
  
 +#include "gstopenalelements.h"
  #include "gstopenalsink.h"
  
++#ifdef TIZEN_FEATURE_OALSINK_MODIFICATION
++#include <math.h>
++#endif /* TIZEN_FEATURE_OALSINK_MODIFICATION */
++
  static void gst_openal_sink_dispose (GObject * object);
  static void gst_openal_sink_finalize (GObject * object);
  
@@@ -975,6 -1174,26 +1177,26 @@@ gst_openal_sink_write (GstAudioSink * a
  
    old = pushContext (sink->default_context);
  
 -      sourceOri[0] = (-1) * sin (sink->source_rotation_y);
 -      sourceOri[2] = (-1) * cos (sink->source_rotation_y);
+ #ifdef TIZEN_FEATURE_OALSINK_MODIFICATION
+   if (sink->ambisonic_type == 1) {
+     sink->format = AL_FORMAT_BFORMAT3D_16;
+     if (sink->source_rotation_y != source_rotation_y_old) {
+       /* FIXME (m.alieskieie): Implement more appropriate
+          Euler angles -> AL Source orientation transform algorithm */
++      // sourceOri[0] = (-1) * sin (sink->source_rotation_y);
++      // sourceOri[2] = (-1) * cos (sink->source_rotation_y);
+       GST_DEBUG_OBJECT (sink,
+           "Source_rotation_y = %g    atx = %g    atz = %g\n",
+           sink->source_rotation_y, sourceOri[0], sourceOri[2]);
+       alSourcefv (sink->default_source, AL_ORIENTATION, sourceOri);
+       if (alGetError () != AL_NO_ERROR)
+         GST_ELEMENT_ERROR (sink, RESOURCE, WRITE, (NULL),
+             ("Failed to set Source's orientation"));
+       source_rotation_y_old = sink->source_rotation_y;
+     }
+   }
+ #endif /* TIZEN_FEATURE_OALSINK_MODIFICATION */
    rest_us =
        (guint64) (sink->buffer_length / sink->bytes_per_sample) *
        G_USEC_PER_SEC / sink->rate / sink->channels;
@@@ -2,10 -2,10 +2,10 @@@ openal_dep = dependency('openal', metho
  
  if openal_dep.found()
    gstopenal = library('gstopenal',
 -    'gstopenal.c', 'gstopenalsink.c', 'gstopenalsrc.c',
 +    'gstopenal.c', 'gstopenalelement.c', 'gstopenalsink.c', 'gstopenalsrc.c',
      c_args: gst_plugins_bad_args,
      include_directories: [configinc, libsinc],
--    dependencies: [gstaudio_dep, openal_dep],
++    dependencies: [gstaudio_dep, gstvideo_dep, openal_dep],
      install: true,
      install_dir: plugins_install_dir,
    )
Simple merge
Simple merge
@@@ -244,7 -238,7 +244,8 @@@ static voi
  registry_handle_global_remove (void *data, struct wl_registry *registry,
      uint32_t name)
  {
 +  /* temporarily do nothing */
+   GST_LOG ("Removed global object: name(%d)", name);
  }
  
  static const struct wl_registry_listener registry_listener = {
@@@ -467,8 -391,14 +467,13 @@@ enu
    PROP_BUNDLE_POLICY,
    PROP_ICE_TRANSPORT_POLICY,
    PROP_ICE_AGENT,
 -#ifndef TIZEN_FEATURE_IMPORT_NETSIM
 -  PROP_LATENCY
 -#else
    PROP_LATENCY,
 +  PROP_SCTP_TRANSPORT,
++#ifdef TIZEN_FEATURE_IMPORT_NETSIM
+   PROP_NETSIM,
+   PROP_DROP_PROBABILITY_SENDER,
+   PROP_DROP_PROBABILITY_RECEIVER
+ #endif
  };
  
  static guint gst_webrtc_bin_signals[LAST_SIGNAL] = { 0 };
@@@ -1217,7 -1150,33 +1227,33 @@@ _collate_peer_connection_states (GstWeb
  #undef STATE
  }
  
 -static void
+ #ifdef __TIZEN__
+ static void
+ _update_and_notify_ice_gathering_state (GstWebRTCBin * webrtc, GstWebRTCICEGatheringState state)
+ {
+   GstWebRTCICEGatheringState old_state = webrtc->ice_gathering_state;
+   if (state != webrtc->ice_gathering_state) {
+     gchar *old_s, *new_s;
+     old_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_GATHERING_STATE,
+         old_state);
+     new_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_GATHERING_STATE,
+         state);
+     GST_INFO_OBJECT (webrtc, "ICE gathering state change from %s(%u) to %s(%u)",
+         old_s, old_state, new_s, state);
+     g_free (old_s);
+     g_free (new_s);
+     webrtc->ice_gathering_state = state;
+     PC_UNLOCK (webrtc);
+     g_object_notify (G_OBJECT (webrtc), "ice-gathering-state");
+     PC_LOCK (webrtc);
+   }
+ }
+ #endif
 +static GstStructure *
  _update_ice_gathering_state_task (GstWebRTCBin * webrtc, gpointer data)
  {
    GstWebRTCICEGatheringState old_state = webrtc->ice_gathering_state;
      ICE_LOCK (webrtc);
      if (webrtc->priv->pending_local_ice_candidates->len != 0) {
        /* ICE candidates queued for emissiong -> we're gathering, not complete */
 -      return;
+ #ifdef __TIZEN__
+       webrtc->pending_ice_gathering_state = GST_WEBRTC_ICE_GATHERING_STATE_COMPLETE;
+       GST_INFO_OBJECT (webrtc, "set pending_ice_gathering_state to (%u)",
+           webrtc->pending_ice_gathering_state);
+       ICE_UNLOCK (webrtc);
++      return  NULL;
+     }
+ #else
        new_state = GST_WEBRTC_ICE_GATHERING_STATE_GATHERING;
      }
+ #endif
      ICE_UNLOCK (webrtc);
    }
  
      g_object_notify (G_OBJECT (webrtc), "ice-gathering-state");
      PC_LOCK (webrtc);
    }
 +
+ #endif
 +  return NULL;
  }
  
  static void
    return ret;
  }
  
 -    uint32_t idx)
+ #ifdef TIZEN_FEATURE_IMPORT_NETSIM
+ static void
+ _insert_netsim_element_between (GstWebRTCBin * webrtc, GstElement * srcbin,
+     const gchar * srcpadname, GstElement * sinkbin, const gchar * sinkpadname,
++    guint idx)
+ {
+   gboolean send = !g_strcmp0 (sinkpadname, "rtp_sink");
+   gchar *netsim_name = g_strdup_printf ("netsim_%s_%u",
+       send ? "send" : "recv", idx);
+   GstElement *netsim = gst_element_factory_make ("netsim", netsim_name);
+   g_free (netsim_name);
+   gst_bin_add (GST_BIN (webrtc), netsim);
+   g_object_set (netsim, "drop-probability",
+       send ? webrtc->priv->drop_probability_sender :
+       webrtc->priv->drop_probability_receiver, NULL);
+   gst_element_sync_state_with_parent (netsim);
+   if (!gst_element_link_pads (srcbin, srcpadname, netsim, "sink"))
+     g_warn_if_reached ();
+   if (!gst_element_link_pads (netsim, "src", sinkbin, sinkpadname))
+     g_warn_if_reached ();
+ }
+ #endif
  static GstPad *
  _connect_input_stream (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
  {
      rtp_sink =
          gst_element_request_pad (webrtc->rtpbin, rtp_templ, pad_name, NULL);
      g_free (pad_name);
 -    gst_ghost_pad_set_target (GST_GHOST_PAD (pad), rtp_sink);
 +    gst_pad_link (srcpad, rtp_sink);
      gst_object_unref (rtp_sink);
  
 -    pad_name = g_strdup_printf ("send_rtp_src_%u", pad->mlineindex);
 +    gst_ghost_pad_set_target (GST_GHOST_PAD (pad), sinkpad);
 +
 +    pad_name = g_strdup_printf ("send_rtp_src_%u", pad->trans->mline);
+ #ifdef TIZEN_FEATURE_IMPORT_NETSIM
+     if (webrtc->priv->netsim) {
+       _insert_netsim_element_between (webrtc, GST_ELEMENT (webrtc->rtpbin), pad_name,
 -          GST_ELEMENT (trans->stream->send_bin), "rtp_sink", pad->mlineindex);
++          GST_ELEMENT (trans->stream->send_bin), "rtp_sink", pad->trans->mline);
+     } else {
+ #endif
      if (!gst_element_link_pads (GST_ELEMENT (webrtc->rtpbin), pad_name,
              GST_ELEMENT (trans->stream->send_bin), "rtp_sink"))
        g_warn_if_reached ();
+ #ifdef TIZEN_FEATURE_IMPORT_NETSIM
+     }
+ #endif
      g_free (pad_name);
    } else {
 -    gchar *pad_name = g_strdup_printf ("sink_%u", pad->mlineindex);
 +    gchar *pad_name = g_strdup_printf ("sink_%u", pad->trans->mline);
      GstPad *funnel_sinkpad =
 -        gst_element_get_request_pad (webrtc->rtpfunnel, pad_name);
 +        gst_element_request_pad_simple (webrtc->rtpfunnel, pad_name);
  
 -    gst_ghost_pad_set_target (GST_GHOST_PAD (pad), funnel_sinkpad);
 +    gst_pad_link (srcpad, funnel_sinkpad);
 +    gst_ghost_pad_set_target (GST_GHOST_PAD (pad), sinkpad);
  
      g_free (pad_name);
      gst_object_unref (funnel_sinkpad);
@@@ -5621,8 -5387,12 +5736,15 @@@ _on_local_ice_candidate_task (GstWebRTC
  
    }
    g_array_free (items, TRUE);
 +
+ #ifdef __TIZEN__
+   if (webrtc->pending_ice_gathering_state == GST_WEBRTC_ICE_GATHERING_STATE_COMPLETE) {
+     _update_and_notify_ice_gathering_state (webrtc, GST_WEBRTC_ICE_GATHERING_STATE_COMPLETE);
+     webrtc->pending_ice_gathering_state = GST_WEBRTC_ICE_GATHERING_STATE_NEW;
+   }
+ #endif
++
 +  return NULL;
  }
  
  static void
@@@ -6773,9 -6399,12 +6908,13 @@@ gst_webrtc_bin_release_pad (GstElement 
     * a possibly dead transceiver */
    PC_LOCK (webrtc);
    if (webrtc_pad->trans)
+ #ifdef __TIZEN__
+     _remove_webrtc_transceiver (webrtc, webrtc_pad->trans);
+ #else
      gst_object_unref (webrtc_pad->trans);
+ #endif
    webrtc_pad->trans = NULL;
 +  gst_caps_replace (&webrtc_pad->received_caps, NULL);
    PC_UNLOCK (webrtc);
  
    _remove_pad (webrtc, webrtc_pad);
@@@ -6808,6 -6437,27 +6947,27 @@@ _update_rtpstorage_latency (GstWebRTCBi
    }
  }
  
 -
+ #ifdef TIZEN_FEATURE_IMPORT_NETSIM
+ static void
+ _update_drop_probability (GstWebRTCBin * webrtc, gfloat probability, gboolean sender)
+ {
+   GValue value = G_VALUE_INIT;
+   GstElement *element;
+   GstIterator *bin_iterator = gst_bin_iterate_sorted (GST_BIN (webrtc));
+   g_assert (bin_iterator);
+   while (gst_iterator_next (bin_iterator, &value) == GST_ITERATOR_OK) {
+     element = GST_ELEMENT (g_value_get_object (&value));
+     if (g_strrstr (GST_ELEMENT_NAME (element), sender ? "netsim_send" : "netsim_recv"))
+       g_object_set (element, "drop-probability", probability, NULL);
+     g_value_reset (&value);
+   }
+   g_value_unset (&value);
+   gst_iterator_free (bin_iterator);
+ }
+ #endif
++
  static void
  gst_webrtc_bin_set_property (GObject * object, guint prop_id,
      const GValue * value, GParamSpec * pspec)
@@@ -6915,9 -6582,17 +7092,20 @@@ gst_webrtc_bin_get_property (GObject * 
      case PROP_LATENCY:
        g_value_set_uint (value, webrtc->priv->jb_latency);
        break;
 +    case PROP_SCTP_TRANSPORT:
 +      g_value_set_object (value, webrtc->priv->sctp_transport);
 +      break;
+ #ifdef TIZEN_FEATURE_IMPORT_NETSIM
+     case PROP_NETSIM:
+       g_value_set_boolean (value, webrtc->priv->netsim);
+       break;
+     case PROP_DROP_PROBABILITY_SENDER:
+       g_value_set_float (value, webrtc->priv->drop_probability_sender);
+       break;
+     case PROP_DROP_PROBABILITY_RECEIVER:
+       g_value_set_float (value, webrtc->priv->drop_probability_receiver);
+       break;
+ #endif
      default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
        break;
@@@ -7192,23 -6870,30 +7380,46 @@@ gst_webrtc_bin_class_init (GstWebRTCBin
        PROP_LATENCY,
        g_param_spec_uint ("latency", "Latency",
            "Default duration to buffer in the jitterbuffers (in ms)",
 -          0, G_MAXUINT, 200, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 +          0, G_MAXUINT, DEFAULT_JB_LATENCY,
 +          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 +
 +  /**
 +   * GstWebRTCBin:sctp-transport:
 +   *
 +   * The WebRTC SCTP Transport
 +   *
 +   * Since: 1.20
 +   */
 +  g_object_class_install_property (gobject_class,
 +      PROP_SCTP_TRANSPORT,
 +      g_param_spec_object ("sctp-transport", "WebRTC SCTP Transport",
 +          "The WebRTC SCTP Transport",
 +          GST_TYPE_WEBRTC_SCTP_TRANSPORT,
 +          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
  
+ #ifdef TIZEN_FEATURE_IMPORT_NETSIM
+   g_object_class_install_property (gobject_class,
+       PROP_NETSIM,
+       g_param_spec_boolean ("netsim", "Use network simulator",
+           "Use network simulator for packet loss",
+           FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+   g_object_class_install_property (gobject_class,
+       PROP_DROP_PROBABILITY_SENDER,
+       g_param_spec_float ("drop-probability-sender", "Drop Probability for sender",
+           "The Probability a sending RTP buffer is dropped",
+           0.0, 1.0, 0.0,
+           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+   g_object_class_install_property (gobject_class,
+       PROP_DROP_PROBABILITY_RECEIVER,
+       g_param_spec_float ("drop-probability-receiver", "Drop Probability for receiver",
+           "The Probability a received RTP buffer is dropped",
+           0.0, 1.0, 0.0,
+           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+ #endif
++
    /**
     * GstWebRTCBin::create-offer:
     * @object: the #webrtcbin
@@@ -142,9 -144,14 +145,14 @@@ struct _GstWebRTCBinPrivat
    GstWebRTCSessionDescription *last_generated_answer;
  
    gboolean tos_attached;
+ #ifdef TIZEN_FEATURE_IMPORT_NETSIM
+   gboolean netsim;
+   gfloat drop_probability_sender;
+   gfloat drop_probability_receiver;
+ #endif
  };
  
 -typedef void (*GstWebRTCBinFunc) (GstWebRTCBin * webrtc, gpointer data);
 +typedef GstStructure *(*GstWebRTCBinFunc) (GstWebRTCBin * webrtc, gpointer data);
  
  typedef struct
  {
@@@ -681,9 -694,14 +694,15 @@@ gst_webrtc_ice_add_candidate (GstWebRTC
      g_free (prefix);
      g_free (new_addr);
      g_free (postfix);
+ #endif
 +
      if (0) {
      fail:
+ #ifdef __TIZEN__
+       g_free (new_addr);
+       g_free (new_candidate);
+ #endif
        g_free (prefix);
        g_free (address);
        g_free (postfix);
Simple merge
index ae9c172,0000000..2da484e
mode 100644,000000..100644
--- /dev/null
@@@ -1,196 -1,0 +1,204 @@@
-   registry_handle_global
 +/*
 + * GStreamer
 + * Copyright (C) 2016 Matthew Waters <matthew@centricular.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., 51 Franklin St, Fifth Floor,
 + * Boston, MA 02110-1301, USA.
 + */
 +
 +#ifdef HAVE_CONFIG_H
 +#include "config.h"
 +#endif
 +
 +#include <gst/vulkan/wayland/gstvkdisplay_wayland.h>
 +
 +#include "wayland_event_source.h"
 +
 +GST_DEBUG_CATEGORY_STATIC (gst_vulkan_display_wayland_debug);
 +#define GST_CAT_DEFAULT gst_vulkan_display_wayland_debug
 +
 +G_DEFINE_TYPE_WITH_CODE (GstVulkanDisplayWayland, gst_vulkan_display_wayland,
 +    GST_TYPE_VULKAN_DISPLAY, GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT,
 +        "vulkandisplaywayland", 0, "Vulkan Wayland Display");
 +    );
 +
 +static void gst_vulkan_display_wayland_finalize (GObject * object);
 +static gpointer gst_vulkan_display_wayland_get_handle (GstVulkanDisplay *
 +    display);
 +
 +static void
 +registry_handle_global (void *data, struct wl_registry *registry,
 +    uint32_t name, const char *interface, uint32_t version)
 +{
 +  GstVulkanDisplayWayland *display = data;
 +
 +  GST_TRACE_OBJECT (display, "registry_handle_global with registry %p, "
 +      "interface %s, version %u", registry, interface, version);
 +
 +  if (g_strcmp0 (interface, "wl_compositor") == 0) {
 +    display->compositor =
 +        wl_registry_bind (registry, name, &wl_compositor_interface, 1);
 +  } else if (g_strcmp0 (interface, "wl_subcompositor") == 0) {
 +    display->subcompositor =
 +        wl_registry_bind (registry, name, &wl_subcompositor_interface, 1);
 +  } else if (g_strcmp0 (interface, "wl_shell") == 0) {
 +    display->shell = wl_registry_bind (registry, name, &wl_shell_interface, 1);
 +  }
 +}
 +
++static void
++registry_handle_global_remove (void *data, struct wl_registry *registry,
++    uint32_t name)
++{
++  GST_LOG ("Removed global object: name(%d)", name);
++}
++
 +static const struct wl_registry_listener registry_listener = {
++  registry_handle_global,
++  registry_handle_global_remove
 +};
 +
 +static void
 +_connect_listeners (GstVulkanDisplayWayland * display)
 +{
 +  display->registry = wl_display_get_registry (display->display);
 +  wl_registry_add_listener (display->registry, &registry_listener, display);
 +
 +  wl_display_roundtrip (display->display);
 +}
 +
 +static void
 +gst_vulkan_display_wayland_class_init (GstVulkanDisplayWaylandClass * klass)
 +{
 +  GST_VULKAN_DISPLAY_CLASS (klass)->get_handle =
 +      GST_DEBUG_FUNCPTR (gst_vulkan_display_wayland_get_handle);
 +
 +  G_OBJECT_CLASS (klass)->finalize = gst_vulkan_display_wayland_finalize;
 +}
 +
 +static void
 +gst_vulkan_display_wayland_init (GstVulkanDisplayWayland * display_wayland)
 +{
 +  GstVulkanDisplay *display = (GstVulkanDisplay *) display_wayland;
 +
 +  display->type = GST_VULKAN_DISPLAY_TYPE_WAYLAND;
 +  display_wayland->foreign_display = FALSE;
 +}
 +
 +static void
 +gst_vulkan_display_wayland_finalize (GObject * object)
 +{
 +  GstVulkanDisplayWayland *display_wayland =
 +      GST_VULKAN_DISPLAY_WAYLAND (object);
 +
 +  if (!display_wayland->foreign_display && display_wayland->display) {
 +    wl_display_flush (display_wayland->display);
 +    wl_display_disconnect (display_wayland->display);
 +  }
 +
 +  G_OBJECT_CLASS (gst_vulkan_display_wayland_parent_class)->finalize (object);
 +}
 +
 +/**
 + * gst_vulkan_display_wayland_new:
 + * @name: (allow-none): a display name
 + *
 + * Create a new #GstVulkanDisplayWayland from the wayland display name.  See `wl_display_connect`()
 + * for details on what is a valid name.
 + *
 + * Returns: (transfer full): a new #GstVulkanDisplayWayland or %NULL
 + *
 + * Since: 1.18
 + */
 +GstVulkanDisplayWayland *
 +gst_vulkan_display_wayland_new (const gchar * name)
 +{
 +  GstVulkanDisplayWayland *ret;
 +
 +  ret = g_object_new (GST_TYPE_VULKAN_DISPLAY_WAYLAND, NULL);
 +  gst_object_ref_sink (ret);
 +  ret->display = wl_display_connect (name);
 +
 +  if (!ret->display) {
 +    GST_ERROR ("Failed to open Wayland display connection with name, \'%s\'",
 +        name);
 +    return NULL;
 +  }
 +
 +  /* connecting the listeners after attaching the event source will race with
 +   * the source and the source may eat an event that we're waiting for and
 +   * deadlock */
 +  _connect_listeners (ret);
 +
 +  GST_VULKAN_DISPLAY (ret)->event_source =
 +      wayland_event_source_new (ret->display, NULL);
 +  g_source_attach (GST_VULKAN_DISPLAY (ret)->event_source,
 +      GST_VULKAN_DISPLAY (ret)->main_context);
 +
 +  return ret;
 +}
 +
 +/**
 + * gst_vulkan_display_wayland_new_with_display:
 + * @display: an existing, wayland display
 + *
 + * Creates a new display connection from a wl_display Display.
 + *
 + * Returns: (transfer full): a new #GstVulkanDisplayWayland
 + *
 + * Since: 1.18
 + */
 +GstVulkanDisplayWayland *
 +gst_vulkan_display_wayland_new_with_display (struct wl_display * display)
 +{
 +  GstVulkanDisplayWayland *ret;
 +
 +  g_return_val_if_fail (display != NULL, NULL);
 +
 +  ret = g_object_new (GST_TYPE_VULKAN_DISPLAY_WAYLAND, NULL);
 +  gst_object_ref_sink (ret);
 +
 +  ret->display = display;
 +  ret->foreign_display = TRUE;
 +
 +  _connect_listeners (ret);
 +
 +  return ret;
 +}
 +
 +static gpointer
 +gst_vulkan_display_wayland_get_handle (GstVulkanDisplay * display)
 +{
 +  return GST_VULKAN_DISPLAY_WAYLAND (display)->display;
 +}
 +
 +static gboolean
 +_roundtrip_async (gpointer data)
 +{
 +  GstVulkanDisplayWayland *display = data;
 +
 +  wl_display_roundtrip (display->display);
 +
 +  return G_SOURCE_REMOVE;
 +}
 +
 +void
 +gst_vulkan_display_wayland_roundtrip_async (GstVulkanDisplayWayland * display)
 +{
 +  g_return_if_fail (GST_IS_VULKAN_DISPLAY_WAYLAND (display));
 +
 +  g_main_context_invoke (GST_VULKAN_DISPLAY (display)->main_context,
 +      (GSourceFunc) _roundtrip_async, display);
 +}
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
@@@ -282,9 -269,11 +282,13 @@@ mpegts_packetizer_init (MpegTSPacketize
    packetizer->nb_seen_offsets = 0;
    packetizer->refoffset = -1;
    packetizer->last_in_time = GST_CLOCK_TIME_NONE;
+ #ifdef TIZEN_FEATURE_TSDEMUX_MODIFICATION
+   packetizer->is_live_stream = FALSE;
+   packetizer->need_pmt_update = FALSE;
+ #endif
    packetizer->pcr_discont_threshold = GST_SECOND;
 +  packetizer->last_pts = GST_CLOCK_TIME_NONE;
 +  packetizer->last_dts = GST_CLOCK_TIME_NONE;
  }
  
  static void
@@@ -605,8 -583,9 +609,11 @@@ mpegts_packetizer_clear (MpegTSPacketiz
    packetizer->map_size = 0;
    packetizer->map_offset = 0;
    packetizer->last_in_time = GST_CLOCK_TIME_NONE;
 +  packetizer->last_pts = GST_CLOCK_TIME_NONE;
 +  packetizer->last_dts = GST_CLOCK_TIME_NONE;
+ #ifdef TIZEN_FEATURE_TSDEMUX_MODIFICATION
+   packetizer->is_live_stream = FALSE;
+ #endif
  
    pcrtable = packetizer->observations[packetizer->pcrtablelut[0x1fff]];
    if (pcrtable)
Simple merge
@@@ -2261,7 -2483,9 +2306,10 @@@ check_pending_buffers (GstTSDemux * dem
    guint64 offset = 0;
    GList *tmp;
    gboolean have_only_sparse = TRUE;
 +  gboolean exceeded_threshold = FALSE;
+ #ifdef TIZEN_FEATURE_TSDEMUX_INVALID_PCR_PID
+   gboolean wrong_pcr_pid = FALSE;
+ #endif
  
    /* 0. Do we only have sparse stream */
    for (tmp = demux->program->stream_list; tmp; tmp = tmp->next) {
      }
    }
  
 -  /* 2. If we don't have a valid value yet, break out */
 -#ifdef TIZEN_FEATURE_TSDEMUX_INVALID_PCR_PID
    if (have_observation == FALSE) {
 -  }
 -#else
 -  if (have_observation == FALSE)
 -    return FALSE;
 +    /* 2. If we don't have a valid value yet, break out */
 +    if (!exceeded_threshold)
 +      return FALSE;
 +
++#ifdef TIZEN_FEATURE_TSDEMUX_INVALID_PCR_PID
+     /* Checking PCR other pid */
+     for (int i = 0; i < demux->program->pmt->streams->len; ++i) {
+       GstMpegtsPMTStream *pmt_stream =
+           g_ptr_array_index (demux->program->pmt->streams, i);
+       if (pmt_stream->pid == demux->program->pcr_pid)
+         continue;
+       if (GST_CLOCK_TIME_IS_VALID (mpegts_packetizer_get_pcr_base_time
+               (MPEG_TS_BASE_PACKETIZER (demux), pmt_stream->pid))) {
+         GST_WARNING ("PCR_PID will update %x -> %x", demux->program->pcr_pid,
+             pmt_stream->pid);
+         demux->program->pcr_pid = pmt_stream->pid;
+         wrong_pcr_pid = TRUE;
+         break;
+       }
+     }
+     if (!wrong_pcr_pid)
+       return FALSE;
+ #endif
 +    /* Except if we've exceed the maximum amount of pending buffers, in which
 +     * case we ignore PCR from now on */
 +    GST_DEBUG_OBJECT (demux,
 +        "Saw more than 500ms of data without PCR. Ignoring PCR from now on");
 +    GST_MPEGTS_BASE (demux)->ignore_pcr = TRUE;
 +    demux->program->pcr_pid = 0x1fff;
 +    g_object_notify (G_OBJECT (demux), "ignore-pcr");
 +  }
  
    /* 3. Go over all streams that have current/pending data */
    for (tmp = demux->program->stream_list; tmp; tmp = tmp->next) {
Simple merge
Simple merge
Simple merge
Simple merge
@@@ -1355,18 -1158,16 +1355,19 @@@ gst_avwait_asink_chain (GstPad * pad, G
      if ((self->must_send_end_message & END_MESSAGE_VIDEO_PUSHED) ||
          self->video_eos_flag) {
        self->must_send_end_message = END_MESSAGE_NORMAL;
 -      g_mutex_unlock (&self->mutex);
 -      gst_avwait_send_element_message (self, TRUE,
 -          self->audio_running_time_to_end_at);
 +      send_message = TRUE;
 +      audio_running_time_to_end_at = self->audio_running_time_to_end_at;
      } else if (self->must_send_end_message & END_MESSAGE_STREAM_ENDED) {
        self->must_send_end_message |= END_MESSAGE_AUDIO_PUSHED;
 -      g_mutex_unlock (&self->mutex);
      } else {
        g_assert_not_reached ();
+       g_mutex_unlock (&self->mutex);
      }
 +    g_mutex_unlock (&self->mutex);
 +
 +    if (send_message)
 +      gst_avwait_send_element_message (self, TRUE,
 +          audio_running_time_to_end_at);
    }
    send_element_message = FALSE;
    return ret;
Simple merge
Simple merge
@@@ -1319,6 -1183,11 +1319,9 @@@ gst_h264_parse_handle_frame (GstBasePar
    GstH264NalUnit nalu;
    GstH264ParserResult pres;
    gint framesize;
 -  GstFlowReturn ret;
 -  gboolean au_complete;
+ #ifdef TIZEN_FEATURE_H264PARSE_MODIFICATION
+   GstH264NalUnitType last_nal_type = GST_H264_NAL_UNKNOWN;
+ #endif
  
    if (G_UNLIKELY (GST_BUFFER_FLAG_IS_SET (frame->buffer,
                GST_BUFFER_FLAG_DISCONT))) {
        case GST_H264_PARSER_OK:
          GST_DEBUG_OBJECT (h264parse, "complete nal (offset, size): (%u, %u) ",
              nalu.offset, nalu.size);
+ #ifdef TIZEN_FEATURE_H264PARSE_MODIFICATION
+         last_nal_type = nalu.type;
+ #endif
          break;
        case GST_H264_PARSER_NO_NAL_END:
 +        /* In NAL alignment, assume the NAL is complete */
 +        if (h264parse->in_align == GST_H264_PARSE_ALIGN_NAL ||
 +            h264parse->in_align == GST_H264_PARSE_ALIGN_AU) {
 +          nonext = TRUE;
 +          nalu.size = size - nalu.offset;
 +          break;
 +        }
          GST_DEBUG_OBJECT (h264parse, "not a complete nal found at offset %u",
              nalu.offset);
          /* if draining, accept it as complete nal */
Simple merge
Simple merge
@@@ -212,5 -180,3 +212,9 @@@ option('package-name', type : 'string'
         description : 'package name to use in plugins')
  option('package-origin', type : 'string', value : 'Unknown package origin', yield : true,
         description : 'package origin URL to use in plugins')
 +option('doc', type : 'feature', value : 'auto', yield: true,
 +       description: 'Enable documentation.')
++
++# Tizen Options
++option('tv-profile', type : 'boolean', value : false,
++       description : 'tv-profile')
index 0000000,97e8c31..97e8c31
mode 000000,100644..100644
--- /dev/null
index 0000000,c583762..918d84e
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,284 +1,295 @@@
 -#%bcond_with x
+ %bcond_with wayland
 -Version:        1.16.2
 -Release:        29
+ %define gst_branch 1.0
+ Name:           gst-plugins-bad
 -Source100:      common.tar.gz
++Version:        1.19.2
++Release:        0
+ Summary:        GStreamer Streaming-Media Framework Plug-Ins
+ License:        LGPL-2.0+
+ Group:          Multimedia/Framework
+ Url:            http://gstreamer.freedesktop.org/
+ Source:         http://gstreamer.freedesktop.org/src/gst-plugins-bad/%{name}-%{version}.tar.xz
 -BuildRequires:  pkgconfig(gstreamer-1.0) >= 1.12.0
 -BuildRequires:  pkgconfig(gstreamer-plugins-base-1.0) >= 1.12.0
++Source1001:     gst-plugins-bad.manifest
+ BuildRequires:  gettext-tools
+ BuildRequires:  autoconf
+ BuildRequires:  gcc-c++
+ BuildRequires:  pkgconfig(glib-2.0) >= 2.31.14
 -%if 0%{?webrtctest:1}
 -BuildRequires:  pkgconfig(json-glib-1.0)
 -BuildRequires:  pkgconfig(libsoup-2.4)
 -%endif
++BuildRequires:  gstreamer-devel >= %{version}
++BuildRequires:  gst-plugins-base-devel >= %{version}
+ BuildRequires:  pkgconfig(orc-0.4) >= 0.4.11
+ BuildRequires:  python
+ BuildRequires:  xsltproc
+ BuildRequires:  pkgconfig(libusb-1.0)
+ BuildRequires:  pkgconfig(gio-2.0) >= 2.25.0
+ BuildRequires:  pkgconfig(libcurl) >= 7.21.0
+ BuildRequires:  pkgconfig(libexif) >= 0.6.16
+ BuildRequires:  pkgconfig(openssl1.1)
++#BuildRequires:  pkgconfig(libcryto1.1)
+ BuildRequires:  pkgconfig(sndfile) >= 1.0.16
+ BuildRequires:  pkgconfig(openal)
+ BuildRequires:  pkgconfig(soundtouch) > 1.4
+ BuildRequires:  pkgconfig(nice)
+ BuildRequires:  pkgconfig(usrsctp)
+ BuildRequires:  pkgconfig(libsrtp2) >= 2.1.0
+ BuildRequires:  pkgconfig(opus)
 -Requires:       gstreamer >= 1.12.0
++BuildRequires:  meson >= 0.48.0
+ %if %{with wayland}
+ %if 0%{?enable_gl:1}
+ BuildRequires:  pkgconfig(gles20)
+ BuildRequires:  pkgconfig(wayland-egl) >= 9.0
+ %endif
+ BuildRequires:  pkgconfig(wayland-client) >= 1.0.0
+ BuildRequires:  pkgconfig(wayland-cursor) >= 1.0.0
++BuildRequires:  pkgconfig(wayland-protocols)
+ BuildRequires:  pkgconfig(libxml-2.0)
++BuildRequires:  pkgconfig(libdrm)
+ %endif
+ %if %{with x}
+ BuildRequires:  pkgconfig(x11)
+ %endif
 -%setup -q -T -D -a 100
++Requires:       gstreamer >= %{version}
+ %description
+ GStreamer is a streaming media framework based on graphs of filters
+ that operate on media data. Applications using this library can do
+ anything media-related,from real-time sound processing to playing
+ videos. Its plug-in-based architecture means that new data types or
+ processing capabilities can be added simply by installing new plug-ins.
+ %package devel
+ Summary:        GStreamer Streaming-Media Framework Plug-Ins
+ Requires: %{name} = %{version}-%{release}
+ Requires: gst-plugins-base-devel
+ %description devel
+ GStreamer is a streaming media framework based on graphs of filters
+ that operate on media data. Applications using this library can do
+ anything media-related,from real-time sound processing to playing
+ videos. Its plug-in-based architecture means that new data types or
+ processing capabilities can be added simply by installing new plug-ins.
+ %prep
+ %setup -q -n %{name}-%{version}
 -export V=1
 -NOCONFIGURE=1 ./autogen.sh
++cp %{SOURCE1001} .
+ %build
 -  -DTIZEN_FEATURE_UPSTREAM\
 -  -DTIZEN_FEATURE_GST_UPSTREAM_AVOID_BUILD_BREAK\
++mkdir -p build
++
+ export CFLAGS+=" -Wall -g -fPIC\
+   -DTIZEN_FEATURE_ADAPTIVE_MODIFICATION\
+   -DTIZEN_FEATURE_HLSDEMUX_PROPERTY\
+   -DTIZEN_FEATURE_HLSDEMUX_EMPTY_VTT\
+   -DTIZEN_FEATURE_HLSDEMUX_UPDATE_SEGMENT\
+   -DTIZEN_FEATURE_HLSDEMUX_DISCONT_SEQUENCE\
+   -DTIZEN_FEATURE_TSDEMUX_MODIFICATION\
+   -DTIZEN_FEATURE_TSDEMUX_INVALID_PCR_PID\
+   -DTIZEN_FEATURE_TSDEMUX_LANG_TAG\
+   -DTIZEN_FEATURE_TSDEMUX_UPDATE_PMT\
+   -DTIZEN_FEATURE_TSDEMUX_UPDATE_STREAM\
+   -DTIZEN_FEATURE_HLS_WEBVTT\
+   -DTIZEN_FEATURE_OALSINK_MODIFICATION\
+   -DTIZEN_FEATURE_MPEGDEMUX_MODIFICATION\
+   -DTIZEN_FEATURE_H264PARSE_MODIFICATION\
+   -DTIZEN_FEATURE_AD\
+   -DTIZEN_FEATURE_HLSDEMUX_LANG_TAG\
+   -DTIZEN_FEATURE_HLSDEMUX_DISCONT\
 -%configure\
 -      --disable-static\
 -      --disable-examples\
 -      --enable-experimental\
+   -DTIZEN_FEATURE_IMPORT_NETSIM\
+   -D__TIZEN__\
+   -fstack-protector-strong\
+   -Wl,-z,relro\
+   -D_FORTIFY_SOURCE=2"
+ export CXXFLAGS+=" -DTIZEN_FEATURE_PITCH_AUDIO_META"
+ # export pthread for checking usrsctp_init at sctp configure
+ export LDFLAGS+=" -pthread "
 -      --enable-tv\
 -      --disable-autoconvert\
 -      --disable-camerabin2\
 -      --disable-dash\
 -      --disable-hls\
 -      --disable-id3tag\
 -      --disable-jpegformat\
 -      --disable-mpegdemux\
 -      --disable-smoothstreaming\
 -      --disable-rtp\
 -      --disable-gl\
++meson --auto-feature=auto --prefix=/usr --libdir=%{_libdir} --datadir=%{_datadir} \
++      -D gst_play_tests=false\
+ %if "%{tizen_profile_name}" == "tv"
 -      --disable-accurip\
 -      --disable-adpcmdec\
 -      --disable-audiofxbad\
 -      --disable-decklink\
 -      --disable-dvb\
 -      --disable-fieldanalysis\
 -      --disable-ivtc\
 -      --disable-rawparse\
 -      --disable-videofilters\
 -      --disable-yadif\
 -      --disable-curl\
 -      --enable-dtls\
 -      --disable-fbdev\
 -      --disable-uvch264\
 -      --disable-y4m\
 -      --disable-adpcmenc\
 -      --disable-aiff\
 -      --disable-asfmux\
 -      --disable-audiovisualizers\
 -      --disable-bayer\
 -      --disable-dvbsuboverlay\
 -      --disable-dvdspu\
 -      --disable-faceoverlay\
 -      --disable-festival\
 -      --disable-freeverb\
 -      --disable-frei0r\
 -      --disable-geometrictransform\
 -      --disable-inter\
 -      --disable-interlace\
 -      --disable-ivfparse\
 -      --disable-jp2kdecimator\
 -      --disable-librfb\
 -      --disable-mxf\
 -      --disable-onvif\
 -      --disable-pcapparse\
 -      --disable-pnm\
 -      --disable-removesilence\
 -      --disable-segmentclip\
 -      --disable-siren\
 -      --disable-smooth\
 -      --disable-speed\
 -      --disable-subenc\
 -      --disable-videosignal\
 -      --disable-vmnc\
++      -D tv-profile=false \
++      -D autoconvert=disabled \
++      -D camerabin2=disabled \
++      -D dash=disabled \
++      -D hls=disabled \
++      -D id3tag=disabled \
++      -D jpegformat=disabled \
++      -D mpegdemux=disabled \
++      -D smoothstreaming=disabled \
++      -D rtp=disabled \
++      -D gl=disabled \
+ %endif
 -      --enable-egl=yes\
 -      --enable-gles2=yes\
 -%endif
 -%if 0%{?webrtctest:1}
 -      --enable-webrtctest\
++      -D accurip=disabled \
++      -D adpcmdec=disabled \
++      -D adpcmenc=disabled \
++      -D aiff=disabled \
++      -D asfmux=disabled \
++      -D audiofxbad=disabled \
++      -D audiovisualizers=disabled \
++      -D bayer=disabled \
++      -D codecalpha=disabled \
++      -D dvbsubenc=disabled \
++      -D dvbsuboverlay=disabled \
++      -D dvdspu=disabled \
++      -D faceoverlay=disabled \
++      -D festival=disabled \
++      -D fieldanalysis=disabled \
++      -D freeverb=disabled \
++      -D frei0r=disabled \
++      -D geometrictransform=disabled \
++      -D inter=disabled \
++      -D interlace=disabled \
++      -D ivfparse=disabled \
++      -D ivtc=disabled \
++      -D jp2kdecimator=disabled \
++      -D librfb=disabled \
++      -D mpegpsmux=disabled \
++      -D mxf=disabled \
++      -D onvif=disabled \
++      -D pcapparse=disabled \
++      -D pnm=disabled \
++      -D rawparse=disabled \
++      -D removesilence=disabled \
++      -D rist=disabled \
++      -D rtmp2=disabled \
++      -D segmentclip=disabled \
++      -D siren=disabled \
++      -D smooth=disabled \
++      -D speed=disabled \
++      -D subenc=disabled \
++      -D switchbin=disabled \
++      -D videofilters=disabled \
++      -D videosignal=disabled \
++      -D vmnc=disabled \
++      -D y4m=disabled \
++      -D wayland=disabled \
++      -D curl=disabled \
++      -D decklink=disabled \
++      -D dtls=enabled \
++      -D dvb=disabled \
++      -D fbdev=disabled \
+ %if 0%{?enable_gl:1}
 -      --enable-wayland=yes\
 -      --enable-openal=yes\
 -      --disable-sndfile\
 -      --disable-gtk-doc\
 -      --disable-mpegpsmux
 -%__make %{?_smp_mflags} V=1
++      -D gl=enabled\
+ %endif
 -%make_install
++      -D kms=disabled \
++      -D nvcodec=disabled \
++      -D openal=enabled \
++      -D sndfile=disabled \
++      -D transcode=disabled \
++      -D uvch264=disabled \
++      -D examples=disabled \
++      -D tests=disabled \
++      -D doc=disabled build
++
++ninja -C build all %{?_smp_mflags}
+ %install
 -%license COPYING.LIB
++export DESTDIR=%{buildroot}
++ninja -C build install
++rm -rf %{buildroot}%{_datadir}/gstreamer-%{gst_branch}/encoding-profiles
++
+ %find_lang %{name}-%{gst_branch}
+ mv %{name}-%{gst_branch}.lang %{name}.lang
+ %lang_package
+ %clean
+ rm -rf $RPM_BUILD_ROOT
+ %post -p /sbin/ldconfig
+ %postun -p /sbin/ldconfig
+ %files
+ %manifest %{name}.manifest
+ %defattr(-, root, root)
 -%{_libdir}/gstreamer-%{gst_branch}/libgstdashdemux.so
++%license COPYING
+ %if "%{tizen_profile_name}" != "tv"
+ %{_libdir}/gstreamer-%{gst_branch}/libgstautoconvert.so
+ %{_libdir}/gstreamer-%{gst_branch}/libgstcamerabin.so
+ %{_libdir}/gstreamer-%{gst_branch}/libgstid3tag.so
+ %{_libdir}/gstreamer-%{gst_branch}/libgstjpegformat.so
+ %{_libdir}/gstreamer-%{gst_branch}/libgstmpegpsdemux.so
+ %{_libdir}/gstreamer-%{gst_branch}/libgstsmoothstreaming.so
 -%{_libdir}/libgstmpegts-%{gst_branch}.so.0*
++%{_libdir}/gstreamer-%{gst_branch}/libgstdash.so
+ %if 0%{?enable_gl:1}
+ %{_libdir}/gstreamer-%{gst_branch}/libgstopengl.so
+ %endif
+ %{_libdir}/gstreamer-%{gst_branch}/libgstsoundtouch.so
+ %{_libdir}/libgstinsertbin-%{gst_branch}.so.0*
+ %{_libdir}/libgstphotography-%{gst_branch}.so.0*
+ %{_libdir}/libgstbasecamerabinsrc-%{gst_branch}.so.0*
+ %{_libdir}/libgsturidownloader-%{gst_branch}.so.0*
+ %{_libdir}/libgstadaptivedemux-%{gst_branch}.so.0*
+ %if 0%{?enable_gl:1}
+ %{_libdir}/libgstgl-%{gst_branch}.so.0*
+ %endif
+ %{_libdir}/libgstisoff-%{gst_branch}.so.0*
+ %endif
++%{_libdir}/gstreamer-%{gst_branch}/libgstaes.so
+ %{_libdir}/gstreamer-%{gst_branch}/libgstgdp.so
+ %{_libdir}/gstreamer-%{gst_branch}/libgstdebugutilsbad.so
+ %{_libdir}/gstreamer-%{gst_branch}/libgstmpegtsdemux.so
+ %{_libdir}/gstreamer-%{gst_branch}/libgstmpegtsmux.so
+ %{_libdir}/gstreamer-%{gst_branch}/libgstsdpelem.so
+ %{_libdir}/gstreamer-%{gst_branch}/libgstshm.so
+ %{_libdir}/gstreamer-%{gst_branch}/libgstvideoparsersbad.so
+ %{_libdir}/gstreamer-%{gst_branch}/libgstmidi.so
+ %{_libdir}/gstreamer-%{gst_branch}/libgstopenal.so
+ %{_libdir}/gstreamer-%{gst_branch}/libgsthls.so
+ %{_libdir}/gstreamer-%{gst_branch}/libgstnetsim.so
+ %{_libdir}/gstreamer-%{gst_branch}/libgsttimecode.so
+ %{_libdir}/gstreamer-%{gst_branch}/libgstvideoframe_audiolevel.so
+ %{_libdir}/gstreamer-%{gst_branch}/libgstaudiomixmatrix.so
+ %{_libdir}/gstreamer-%{gst_branch}/libgstaudiobuffersplit.so
+ %{_libdir}/gstreamer-%{gst_branch}/libgstaudiolatency.so
+ %{_libdir}/gstreamer-%{gst_branch}/libgstipcpipeline.so
+ %{_libdir}/gstreamer-%{gst_branch}/libgstproxy.so
+ %{_libdir}/gstreamer-%{gst_branch}/libgstwebrtc.so
+ %{_libdir}/gstreamer-%{gst_branch}/libgstdtls.so
++%{_libdir}/gstreamer-%{gst_branch}/libgstrtpmanagerbad.so
+ %{_libdir}/gstreamer-%{gst_branch}/libgstsctp.so
+ %{_libdir}/gstreamer-%{gst_branch}/libgstsrtp.so
+ %{_libdir}/gstreamer-%{gst_branch}/libgstopusparse.so
+ %{_libdir}/gstreamer-%{gst_branch}/libgstgaudieffects.so
+ %{_libdir}/gstreamer-%{gst_branch}/libgstcoloreffects.so
+ %{_libdir}/libgstsctp-%{gst_branch}.so.0*
+ %{_libdir}/libgstwebrtc-%{gst_branch}.so.0*
++%{_libdir}/libgstcodecs-%{gst_branch}.so.0*
+ %{_libdir}/libgstcodecparsers-%{gst_branch}.so.0*
 -%if 0%{?webrtctest:1}
 -%{_bindir}/webrtc*
 -%endif
+ %{_libdir}/libgstbadaudio-%{gst_branch}.so.0*
++%{_libdir}/libgstmpegts-%{gst_branch}.so.0*
++%{_libdir}/libgstplay-%{gst_branch}.so.0*
+ %{_libdir}/libgstplayer-%{gst_branch}.so.0*
++%{_libdir}/libgsttranscoder-%{gst_branch}.so.0*
++
++%exclude %{_bindir}/gst-transcoder-1.0
+ %files devel
+ %manifest %{name}.manifest
+ %defattr(-, root, root)
+ %{_includedir}/gstreamer-%{gst_branch}
+ %if "%{tizen_profile_name}" != "tv"
+ %if 0%{?enable_gl:1}
+ %{_libdir}/gstreamer-%{gst_branch}/include/gst/gl/gstglconfig.h
+ %endif
+ %endif
+ %{_libdir}/*.so
+ %{_libdir}/pkgconfig/gstreamer-codecparsers-%{gst_branch}.pc
+ %{_libdir}/pkgconfig/gstreamer-plugins-bad-%{gst_branch}.pc
+ %{_libdir}/pkgconfig/gstreamer-insertbin-%{gst_branch}.pc
+ %{_libdir}/pkgconfig/gstreamer-mpegts-%{gst_branch}.pc
+ %if 0%{?enable_gl:1}
+ %{_libdir}/pkgconfig/gstreamer-gl-1.0.pc
+ %endif
+ %{_libdir}/pkgconfig/gstreamer-bad-audio-1.0.pc
++%{_libdir}/pkgconfig/gstreamer-photography-1.0.pc
++%{_libdir}/pkgconfig/gstreamer-play-1.0.pc
+ %{_libdir}/pkgconfig/gstreamer-player-1.0.pc
+ %{_libdir}/pkgconfig/gstreamer-sctp-1.0.pc
++%{_libdir}/pkgconfig/gstreamer-transcoder-1.0.pc
+ %{_libdir}/pkgconfig/gstreamer-webrtc-1.0.pc
index 0000000,0000000..1b6f434
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,154 @@@
++# SOME DESCRIPTIVE TITLE.
++# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
++# This file is distributed under the same license as the gst-plugins-bad-1.0 package.
++# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
++#
++#, fuzzy
++msgid ""
++msgstr ""
++"Project-Id-Version: gst-plugins-bad-1.0\n"
++"Report-Msgid-Bugs-To: \n"
++"POT-Creation-Date: 2021-09-23 01:34+0100\n"
++"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
++"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
++"Language-Team: LANGUAGE <LL@li.org>\n"
++"Language: \n"
++"MIME-Version: 1.0\n"
++"Content-Type: text/plain; charset=CHARSET\n"
++"Content-Transfer-Encoding: 8bit\n"
++
++#: ext/curl/gstcurlhttpsrc.c:1439
++msgid "No URL set."
++msgstr ""
++
++#: ext/opencv/gsttemplatematch.cpp:189
++msgid "OpenCV failed to load template image"
++msgstr ""
++
++#: ext/resindvd/resindvdsrc.c:361
++msgid "Could not read title information for DVD."
++msgstr ""
++
++#: ext/resindvd/resindvdsrc.c:367
++#, c-format
++msgid "Failed to open DVD device '%s'."
++msgstr ""
++
++#: ext/resindvd/resindvdsrc.c:373
++msgid "Failed to set PGC based seeking."
++msgstr ""
++
++#: ext/resindvd/resindvdsrc.c:1164
++msgid ""
++"Could not read DVD. This may be because the DVD is encrypted and a DVD "
++"decryption library is not installed."
++msgstr ""
++
++#: ext/resindvd/resindvdsrc.c:1169 ext/resindvd/resindvdsrc.c:1178
++msgid "Could not read DVD."
++msgstr ""
++
++#: ext/smoothstreaming/gstmssdemux.c:430
++#: gst-libs/gst/adaptivedemux/gstadaptivedemux.c:735
++msgid "This file contains no playable streams."
++msgstr ""
++
++#: ext/sndfile/gstsfdec.c:771
++msgid "Could not open sndfile stream for reading."
++msgstr ""
++
++#: gst/asfmux/gstasfmux.c:1834
++msgid "Generated file has a larger preroll time than its streams duration"
++msgstr ""
++
++#: gst/camerabin2/camerabingeneral.c:167 gst/camerabin2/gstcamerabin2.c:1866
++#: gst/camerabin2/gstdigitalzoom.c:283 gst/camerabin2/gstviewfinderbin.c:275
++#, c-format
++msgid "Missing element '%s' - check your GStreamer installation."
++msgstr ""
++
++#: gst/camerabin2/gstcamerabin2.c:352
++msgid "File location is set to NULL, please set it to a valid filename"
++msgstr ""
++
++#: gst/camerabin2/gstwrappercamerabinsrc.c:587
++msgid "Digitalzoom element couldn't be created"
++msgstr ""
++
++#: gst/dvdspu/gstdvdspu.c:1044
++msgid "Subpicture format was not configured before data flow"
++msgstr ""
++
++#: gst-libs/gst/adaptivedemux/gstadaptivedemux.c:3626
++msgid "Failed to get fragment URL."
++msgstr ""
++
++#: gst-libs/gst/adaptivedemux/gstadaptivedemux.c:4013
++#, c-format
++msgid "Couldn't download fragments"
++msgstr ""
++
++#: gst-libs/gst/adaptivedemux/gstadaptivedemux.c:4102
++#: gst/mpegtsdemux/mpegtsbase.c:1693
++msgid "Internal data stream error."
++msgstr ""
++
++#: sys/dvb/gstdvbsrc.c:1597 sys/dvb/gstdvbsrc.c:1811
++#, c-format
++msgid "Device \"%s\" does not exist."
++msgstr ""
++
++#: sys/dvb/gstdvbsrc.c:1601
++#, c-format
++msgid "Could not open frontend device \"%s\"."
++msgstr ""
++
++#: sys/dvb/gstdvbsrc.c:1620
++#, c-format
++msgid "Could not get settings from frontend device \"%s\"."
++msgstr ""
++
++#: sys/dvb/gstdvbsrc.c:1637
++#, c-format
++msgid "Cannot enumerate delivery systems from frontend device \"%s\"."
++msgstr ""
++
++#: sys/dvb/gstdvbsrc.c:1815
++#, c-format
++msgid "Could not open file \"%s\" for reading."
++msgstr ""
++
++#: sys/dvb/parsechannels.c:410
++#, c-format
++msgid "Couldn't find channel configuration file"
++msgstr ""
++
++#: sys/dvb/parsechannels.c:413 sys/dvb/parsechannels.c:563
++#, c-format
++msgid "Couldn't load channel configuration file: '%s'"
++msgstr ""
++
++#: sys/dvb/parsechannels.c:421 sys/dvb/parsechannels.c:846
++#, c-format
++msgid "Couldn't find details for channel '%s'"
++msgstr ""
++
++#: sys/dvb/parsechannels.c:430
++#, c-format
++msgid "No properties for channel '%s'"
++msgstr ""
++
++#: sys/dvb/parsechannels.c:439
++#, c-format
++msgid "Failed to set properties for channel '%s'"
++msgstr ""
++
++#: sys/dvb/parsechannels.c:560
++#, c-format
++msgid "Couldn't find channel configuration file: '%s'"
++msgstr ""
++
++#: sys/dvb/parsechannels.c:570
++#, c-format
++msgid "Channel configuration file doesn't contain any channels"
++msgstr ""
Simple merge
Simple merge
Simple merge
Simple merge