Beispiel #1
0
static GstClockTime
gst_hls_demux_get_duration (GstAdaptiveDemux * demux)
{
  GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (demux);

  return gst_m3u8_client_get_duration (hlsdemux->client);
}
Beispiel #2
0
static gboolean
gst_hls_demux_select_bitrate (GstAdaptiveDemuxStream * stream, guint64 bitrate)
{
  GstAdaptiveDemux *demux = GST_ADAPTIVE_DEMUX_CAST (stream->demux);
  GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (stream->demux);
  gboolean changed = FALSE;

  GST_M3U8_CLIENT_LOCK (hlsdemux->client);
  if (!hlsdemux->client->main->lists) {
    GST_M3U8_CLIENT_UNLOCK (hlsdemux->client);
    return FALSE;
  }
  GST_M3U8_CLIENT_UNLOCK (hlsdemux->client);

  /* FIXME: Currently several issues have be found when letting bitrate adaptation
   * happen using trick modes (such as 'All streams finished without buffers') and
   * the adaptive algorithm does not properly behave. */
  if (demux->segment.rate != 1.0)
    return FALSE;

  gst_hls_demux_change_playlist (hlsdemux, bitrate, &changed);
  if (changed)
    gst_hls_demux_setup_streams (GST_ADAPTIVE_DEMUX_CAST (hlsdemux));
  return changed;
}
Beispiel #3
0
/* Handles decrypted buffers only */
static GstFlowReturn
gst_hls_demux_handle_buffer (GstAdaptiveDemux * demux,
    GstAdaptiveDemuxStream * stream, GstBuffer * buffer, gboolean force)
{
  GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (demux);

  if (G_UNLIKELY (hlsdemux->do_typefind && buffer != NULL)) {
    GstCaps *caps = NULL;
    GstMapInfo info;
    guint buffer_size;
    GstTypeFindProbability prob = GST_TYPE_FIND_NONE;

    if (hlsdemux->pending_typefind_buffer)
      buffer = gst_buffer_append (hlsdemux->pending_typefind_buffer, buffer);
    hlsdemux->pending_typefind_buffer = NULL;

    gst_buffer_map (buffer, &info, GST_MAP_READ);
    buffer_size = info.size;

    /* Typefind could miss if buffer is too small. In this case we
     * will retry later */
    if (buffer_size >= (2 * 1024)) {
      caps =
          gst_type_find_helper_for_data (GST_OBJECT_CAST (hlsdemux), info.data,
          info.size, &prob);
    }
    gst_buffer_unmap (buffer, &info);

    if (G_UNLIKELY (!caps)) {
      /* 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) || force) {
        GST_ELEMENT_ERROR (hlsdemux, STREAM, TYPE_NOT_FOUND,
            ("Could not determine type of stream"), (NULL));
        gst_buffer_unref (buffer);
        return GST_FLOW_NOT_NEGOTIATED;
      } else {
        hlsdemux->pending_typefind_buffer = buffer;
        return GST_FLOW_OK;
      }
    }

    GST_DEBUG_OBJECT (hlsdemux, "Typefind result: %" GST_PTR_FORMAT " prob:%d",
        caps, prob);

    gst_adaptive_demux_stream_set_caps (stream, caps);
    hlsdemux->do_typefind = FALSE;
  }

  g_assert (hlsdemux->pending_typefind_buffer == NULL);

  if (buffer) {
    buffer = gst_buffer_make_writable (buffer);
    GST_BUFFER_OFFSET (buffer) = hlsdemux->current_offset;
    hlsdemux->current_offset += gst_buffer_get_size (buffer);
    GST_BUFFER_OFFSET_END (buffer) = hlsdemux->current_offset;
    return gst_adaptive_demux_stream_push_buffer (stream, buffer);
  }
  return GST_FLOW_OK;
}
Beispiel #4
0
static gboolean
gst_hls_demux_is_live (GstAdaptiveDemux * demux)
{
  GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (demux);

  return gst_m3u8_client_is_live (hlsdemux->client);
}
Beispiel #5
0
static gboolean
gst_hls_demux_start_fragment (GstAdaptiveDemux * demux,
    GstAdaptiveDemuxStream * stream)
{
  GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (demux);

  if (hlsdemux->current_key) {
    GError *err = NULL;
    GstFragment *key_fragment;
    GstBuffer *key_buffer;
    GstMapInfo key_info;

    /* new key? */
    if (hlsdemux->key_url
        && strcmp (hlsdemux->key_url, hlsdemux->current_key) == 0) {
      key_fragment = g_object_ref (hlsdemux->key_fragment);
    } else {
      g_free (hlsdemux->key_url);
      hlsdemux->key_url = NULL;

      if (hlsdemux->key_fragment)
        g_object_unref (hlsdemux->key_fragment);
      hlsdemux->key_fragment = NULL;

      GST_INFO_OBJECT (demux, "Fetching key %s", hlsdemux->current_key);
      key_fragment =
          gst_uri_downloader_fetch_uri (demux->downloader,
          hlsdemux->current_key, hlsdemux->client->main ?
          hlsdemux->client->main->uri : NULL, FALSE, FALSE,
          hlsdemux->client->current ? hlsdemux->client->current->
          allowcache : TRUE, &err);
      if (key_fragment == NULL)
        goto key_failed;
      hlsdemux->key_url = g_strdup (hlsdemux->current_key);
      hlsdemux->key_fragment = g_object_ref (key_fragment);
    }

    key_buffer = gst_fragment_get_buffer (key_fragment);
    gst_buffer_map (key_buffer, &key_info, GST_MAP_READ);

    gst_hls_demux_decrypt_start (hlsdemux, key_info.data, hlsdemux->current_iv);

    gst_buffer_unmap (key_buffer, &key_info);
    gst_buffer_unref (key_buffer);
    g_object_unref (key_fragment);
  }

  gst_hls_demux_clear_pending_data (hlsdemux);

  return TRUE;

key_failed:
  {
    GST_ELEMENT_ERROR (demux, STREAM, DEMUX,
        ("Couldn't retrieve key for decryption"), (NULL));
    GST_WARNING_OBJECT (demux, "Failed to decrypt data");
    return FALSE;
  }
}
Beispiel #6
0
static gboolean
gst_hls_demux_stream_has_next_fragment (GstAdaptiveDemuxStream * stream)
{
  GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (stream->demux);

  return gst_m3u8_client_has_next_fragment (hlsdemux->client,
      stream->demux->segment.rate > 0);
}
Beispiel #7
0
static gboolean
gst_hls_demux_get_live_seek_range (GstAdaptiveDemux * demux, gint64 * start,
    gint64 * stop)
{
  GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (demux);

  return gst_m3u8_client_get_seek_range (hlsdemux->client, start, stop);
}
Beispiel #8
0
static gint64
gst_hls_demux_get_manifest_update_interval (GstAdaptiveDemux * demux)
{
  GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (demux);

  return gst_util_uint64_scale (gst_m3u8_client_get_target_duration
      (hlsdemux->client), G_USEC_PER_SEC, GST_SECOND);
}
Beispiel #9
0
static gboolean
gst_hls_demux_process_manifest (GstAdaptiveDemux * demux, GstBuffer * buf)
{
  GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (demux);
  gchar *playlist = NULL;

  if (hlsdemux->client)
    gst_m3u8_client_free (hlsdemux->client);

  hlsdemux->client =
      gst_m3u8_client_new (demux->manifest_uri, demux->manifest_base_uri);

  GST_INFO_OBJECT (demux, "Changed location: %s (base uri: %s)",
      demux->manifest_uri, GST_STR_NULL (demux->manifest_base_uri));

  playlist = gst_hls_src_buf_to_utf8_playlist (buf);
  if (playlist == NULL) {
    GST_WARNING_OBJECT (demux, "Error validating first playlist.");
    return FALSE;
  } else if (!gst_m3u8_client_update (hlsdemux->client, playlist)) {
    /* In most cases, this will happen if we set a wrong url in the
     * source element and we have received the 404 HTML response instead of
     * the playlist */
    GST_ELEMENT_ERROR (demux, STREAM, DECODE, ("Invalid playlist."), (NULL));
    return FALSE;
  }

  /* If this playlist is a variant playlist, select the first one
   * and update it */
  if (gst_m3u8_client_has_variant_playlist (hlsdemux->client)) {
    GstM3U8 *child = NULL;
    GError *err = NULL;

    if (demux->connection_speed == 0) {
      GST_M3U8_CLIENT_LOCK (hlsdemux->client);
      child = hlsdemux->client->main->current_variant->data;
      GST_M3U8_CLIENT_UNLOCK (hlsdemux->client);
    } else {
      GList *tmp = gst_m3u8_client_get_playlist_for_bitrate (hlsdemux->client,
          demux->connection_speed);
      GST_M3U8_CLIENT_LOCK (hlsdemux->client);
      hlsdemux->client->main->current_variant = tmp;
      GST_M3U8_CLIENT_UNLOCK (hlsdemux->client);

      child = GST_M3U8 (tmp->data);
    }

    gst_m3u8_client_set_current (hlsdemux->client, child);
    if (!gst_hls_demux_update_playlist (hlsdemux, FALSE, &err)) {
      GST_ELEMENT_ERROR_FROM_ERROR (demux, "Could not fetch the child playlist",
          err);
      return FALSE;
    }
  }

  return gst_hls_demux_setup_streams (demux);
}
Beispiel #10
0
static GstFlowReturn
gst_hls_demux_update_manifest (GstAdaptiveDemux * demux)
{
  GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (demux);
  if (!gst_hls_demux_update_playlist (hlsdemux, TRUE, NULL))
    return GST_FLOW_ERROR;

  return GST_FLOW_OK;
}
Beispiel #11
0
static GstFlowReturn
gst_hls_demux_advance_fragment (GstAdaptiveDemuxStream * stream)
{
  GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (stream->demux);

  gst_m3u8_client_advance_fragment (hlsdemux->client,
      stream->demux->segment.rate > 0);
  hlsdemux->reset_pts = FALSE;
  return GST_FLOW_OK;
}
Beispiel #12
0
static gboolean
gst_hls_demux_setup_streams (GstAdaptiveDemux * demux)
{
  GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (demux);

  /* only 1 output supported */
  gst_adaptive_demux_stream_new (demux, gst_hls_demux_create_pad (hlsdemux));

  hlsdemux->reset_pts = TRUE;

  return TRUE;
}
Beispiel #13
0
static GstFlowReturn
gst_hls_demux_finish_fragment (GstAdaptiveDemux * demux,
    GstAdaptiveDemuxStream * stream)
{
  GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (demux);
  GstFlowReturn ret = GST_FLOW_OK;

  if (hlsdemux->current_key)
    gst_hls_demux_decrypt_end (hlsdemux);

  /* ideally this should be empty, but this eos might have been
   * caused by an error on the source element */
  GST_DEBUG_OBJECT (demux, "Data still on the adapter when EOS was received"
      ": %" G_GSIZE_FORMAT, gst_adapter_available (stream->adapter));
  gst_adapter_clear (stream->adapter);

  if (stream->last_ret == GST_FLOW_OK) {
    if (hlsdemux->pending_buffer) {
      if (hlsdemux->current_key) {
        GstMapInfo info;
        gssize unpadded_size;

        /* Handle pkcs7 unpadding here */
        gst_buffer_map (hlsdemux->pending_buffer, &info, GST_MAP_READ);
        unpadded_size = info.size - info.data[info.size - 1];
        gst_buffer_unmap (hlsdemux->pending_buffer, &info);

        gst_buffer_resize (hlsdemux->pending_buffer, 0, unpadded_size);
      }

      ret =
          gst_hls_demux_handle_buffer (demux, stream, hlsdemux->pending_buffer,
          TRUE);
      hlsdemux->pending_buffer = NULL;
    }
  } else {
    if (hlsdemux->pending_buffer)
      gst_buffer_unref (hlsdemux->pending_buffer);
    hlsdemux->pending_buffer = NULL;
  }

  if (ret == GST_FLOW_OK || ret == GST_FLOW_NOT_LINKED)
    return gst_adaptive_demux_stream_advance_fragment (demux, stream,
        stream->fragment.duration);
  return ret;
}
Beispiel #14
0
static GstFlowReturn
gst_hls_demux_data_received (GstAdaptiveDemux * demux,
    GstAdaptiveDemuxStream * stream)
{
  GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (demux);
  gsize available;
  GstBuffer *buffer = NULL;

  available = gst_adapter_available (stream->adapter);

  /* Is it encrypted? */
  if (hlsdemux->current_key) {
    GError *err = NULL;
    GstBuffer *tmp_buffer;

    /* must be a multiple of 16 */
    available = available & (~0xF);

    if (available == 0) {
      return GST_FLOW_OK;
    }

    buffer = gst_adapter_take_buffer (stream->adapter, available);
    buffer = gst_hls_demux_decrypt_fragment (hlsdemux, buffer, &err);
    if (buffer == NULL) {
      GST_ELEMENT_ERROR (demux, STREAM, DECODE, ("Failed to decrypt buffer"),
          ("decryption failed %s", err->message));
      g_error_free (err);
      return GST_FLOW_ERROR;
    }

    tmp_buffer = hlsdemux->pending_buffer;
    hlsdemux->pending_buffer = buffer;
    buffer = tmp_buffer;
  } else {
    buffer = gst_adapter_take_buffer (stream->adapter, available);
    if (hlsdemux->pending_buffer) {
      buffer = gst_buffer_append (hlsdemux->pending_buffer, buffer);
      hlsdemux->pending_buffer = NULL;
    }
  }

  return gst_hls_demux_handle_buffer (demux, stream, buffer, FALSE);
}
Beispiel #15
0
static GstFlowReturn
gst_hls_demux_update_fragment_info (GstAdaptiveDemuxStream * stream)
{
  GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (stream->demux);
  gchar *next_fragment_uri;
  GstClockTime duration;
  GstClockTime timestamp;
  gboolean discont;
  gint64 range_start, range_end;
  gchar *key = NULL;
  guint8 *iv = NULL;

  if (!gst_m3u8_client_get_next_fragment (hlsdemux->client, &discont,
          &next_fragment_uri, &duration, &timestamp, &range_start, &range_end,
          &key, &iv, stream->demux->segment.rate > 0)) {
    GST_INFO_OBJECT (hlsdemux, "This playlist doesn't contain more fragments");
    return GST_FLOW_EOS;
  }

  if (stream->discont)
    discont = TRUE;

  /* set up our source for download */
  if (hlsdemux->reset_pts || discont || stream->demux->segment.rate < 0.0) {
    stream->fragment.timestamp = timestamp;
  } else {
    stream->fragment.timestamp = GST_CLOCK_TIME_NONE;
  }

  g_free (hlsdemux->current_key);
  hlsdemux->current_key = key;
  g_free (hlsdemux->current_iv);
  hlsdemux->current_iv = iv;
  g_free (stream->fragment.uri);
  stream->fragment.uri = next_fragment_uri;
  stream->fragment.range_start = range_start;
  stream->fragment.range_end = range_end;
  stream->fragment.duration = duration;
  if (discont)
    stream->discont = discont;

  return GST_FLOW_OK;
}
Beispiel #16
0
static GstFlowReturn
gst_hls_demux_data_received (GstAdaptiveDemux * demux,
    GstAdaptiveDemuxStream * stream, GstBuffer * buffer)
{
  GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (demux);

  if (hlsdemux->current_offset == -1)
    hlsdemux->current_offset =
        GST_BUFFER_OFFSET_IS_VALID (buffer) ? GST_BUFFER_OFFSET (buffer) : 0;

  /* Is it encrypted? */
  if (hlsdemux->current_key) {
    GError *err = NULL;
    gsize size;
    GstBuffer *tmp_buffer;

    gst_adapter_push (hlsdemux->pending_encrypted_data, buffer);
    size = gst_adapter_available (hlsdemux->pending_encrypted_data);

    /* must be a multiple of 16 */
    size = size & (~0xF);

    if (size == 0) {
      return GST_FLOW_OK;
    }

    buffer = gst_adapter_take_buffer (hlsdemux->pending_encrypted_data, size);
    buffer = gst_hls_demux_decrypt_fragment (hlsdemux, buffer, &err);
    if (buffer == NULL) {
      GST_ELEMENT_ERROR (demux, STREAM, DECODE, ("Failed to decrypt buffer"),
          ("decryption failed %s", err->message));
      g_error_free (err);
      return GST_FLOW_ERROR;
    }

    tmp_buffer = hlsdemux->pending_decrypted_buffer;
    hlsdemux->pending_decrypted_buffer = buffer;
    buffer = tmp_buffer;
  }

  return gst_hls_demux_handle_buffer (demux, stream, buffer, FALSE);
}
Beispiel #17
0
static GstFlowReturn
gst_hls_demux_finish_fragment (GstAdaptiveDemux * demux,
    GstAdaptiveDemuxStream * stream)
{
  GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (demux);
  GstFlowReturn ret = GST_FLOW_OK;

  if (hlsdemux->current_key)
    gst_hls_demux_decrypt_end (hlsdemux);

  if (stream->last_ret == GST_FLOW_OK) {
    if (hlsdemux->pending_decrypted_buffer) {
      if (hlsdemux->current_key) {
        GstMapInfo info;
        gssize unpadded_size;

        /* Handle pkcs7 unpadding here */
        gst_buffer_map (hlsdemux->pending_decrypted_buffer, &info,
            GST_MAP_READ);
        unpadded_size = info.size - info.data[info.size - 1];
        gst_buffer_unmap (hlsdemux->pending_decrypted_buffer, &info);

        gst_buffer_resize (hlsdemux->pending_decrypted_buffer, 0,
            unpadded_size);
      }

      ret =
          gst_hls_demux_handle_buffer (demux, stream,
          hlsdemux->pending_decrypted_buffer, TRUE);
      hlsdemux->pending_decrypted_buffer = NULL;
    }
  }
  gst_hls_demux_clear_pending_data (hlsdemux);

  if (ret == GST_FLOW_OK || ret == GST_FLOW_NOT_LINKED)
    return gst_adaptive_demux_stream_advance_fragment (demux, stream,
        stream->fragment.duration);
  return ret;
}
Beispiel #18
0
static void
gst_hls_demux_reset (GstAdaptiveDemux * ademux)
{
  GstHLSDemux *demux = GST_HLS_DEMUX_CAST (ademux);

  demux->do_typefind = TRUE;
  demux->reset_pts = TRUE;

  g_free (demux->key_url);
  demux->key_url = NULL;

  if (demux->key_fragment)
    g_object_unref (demux->key_fragment);
  demux->key_fragment = NULL;

  if (demux->client) {
    gst_m3u8_client_free (demux->client);
    demux->client = NULL;
  }
  /* TODO recreated on hls only if reset was not for disposing */
  demux->client = gst_m3u8_client_new ("", NULL);

  demux->srcpad_counter = 0;
  if (demux->pending_buffer)
    gst_buffer_unref (demux->pending_buffer);
  demux->pending_buffer = NULL;
  if (demux->current_key) {
    g_free (demux->current_key);
    demux->current_key = NULL;
  }
  if (demux->current_iv) {
    g_free (demux->current_iv);
    demux->current_iv = NULL;
  }

  gst_hls_demux_decrypt_end (demux);
}
Beispiel #19
0
static gboolean
gst_hls_demux_seek (GstAdaptiveDemux * demux, GstEvent * seek)
{
  GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (demux);
  GstFormat format;
  GstSeekFlags flags;
  GstSeekType start_type, stop_type;
  gint64 start, stop;
  gdouble rate;
  GList *walk, *current_file = NULL;
  GstClockTime current_pos, target_pos;
  gint64 current_sequence;
  GstM3U8MediaFile *file;
  guint64 bitrate;

  gst_event_parse_seek (seek, &rate, &format, &flags, &start_type, &start,
      &stop_type, &stop);

  bitrate = gst_hls_demux_get_bitrate (hlsdemux);

  /* properly cleanup pending decryption status */
  if (flags & GST_SEEK_FLAG_FLUSH) {
    gst_hls_demux_decrypt_end (hlsdemux);
  }

  /* Use I-frame variants for trick modes */
  if (hlsdemux->client->main->iframe_lists && rate < -1.0
      && demux->segment.rate >= -1.0 && demux->segment.rate <= 1.0) {
    GError *err = NULL;

    GST_M3U8_CLIENT_LOCK (hlsdemux->client);
    /* Switch to I-frame variant */
    hlsdemux->client->main->current_variant =
        hlsdemux->client->main->iframe_lists;
    GST_M3U8_CLIENT_UNLOCK (hlsdemux->client);
    gst_m3u8_client_set_current (hlsdemux->client,
        hlsdemux->client->main->iframe_lists->data);
    gst_uri_downloader_reset (demux->downloader);
    if (!gst_hls_demux_update_playlist (hlsdemux, FALSE, &err)) {
      GST_ELEMENT_ERROR_FROM_ERROR (hlsdemux, "Could not switch playlist", err);
      return FALSE;
    }
    //hlsdemux->discont = TRUE;
    hlsdemux->do_typefind = TRUE;

    gst_hls_demux_change_playlist (hlsdemux, bitrate / ABS (rate), NULL);
  } else if (rate > -1.0 && rate <= 1.0 && (demux->segment.rate < -1.0
          || demux->segment.rate > 1.0)) {
    GError *err = NULL;
    GST_M3U8_CLIENT_LOCK (hlsdemux->client);
    /* Switch to normal variant */
    hlsdemux->client->main->current_variant = hlsdemux->client->main->lists;
    GST_M3U8_CLIENT_UNLOCK (hlsdemux->client);
    gst_m3u8_client_set_current (hlsdemux->client,
        hlsdemux->client->main->lists->data);
    gst_uri_downloader_reset (demux->downloader);
    if (!gst_hls_demux_update_playlist (hlsdemux, FALSE, &err)) {
      GST_ELEMENT_ERROR_FROM_ERROR (hlsdemux, "Could not switch playlist", err);
      return FALSE;
    }
    //hlsdemux->discont = TRUE;
    hlsdemux->do_typefind = TRUE;
    /* TODO why not continue using the same? that was being used up to now? */
    gst_hls_demux_change_playlist (hlsdemux, bitrate, NULL);
  }

  GST_M3U8_CLIENT_LOCK (hlsdemux->client);
  file = GST_M3U8_MEDIA_FILE (hlsdemux->client->current->files->data);
  current_sequence = file->sequence;
  current_pos = 0;
  target_pos = rate > 0 ? start : stop;
  /* FIXME: Here we need proper discont handling */
  for (walk = hlsdemux->client->current->files; walk; walk = walk->next) {
    file = walk->data;

    current_sequence = file->sequence;
    current_file = walk;
    if (current_pos <= target_pos && target_pos < current_pos + file->duration) {
      break;
    }
    current_pos += file->duration;
  }

  if (walk == NULL) {
    GST_DEBUG_OBJECT (demux, "seeking further than track duration");
    current_sequence++;
  }

  GST_DEBUG_OBJECT (demux, "seeking to sequence %u", (guint) current_sequence);
  hlsdemux->reset_pts = TRUE;
  hlsdemux->client->sequence = current_sequence;
  hlsdemux->client->current_file =
      current_file ? current_file : hlsdemux->client->current->files;
  hlsdemux->client->sequence_position = current_pos;
  GST_M3U8_CLIENT_UNLOCK (hlsdemux->client);

  return TRUE;
}
Beispiel #20
0
static gboolean
gst_hls_demux_seek (GstAdaptiveDemux * demux, GstEvent * seek)
{
  GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (demux);
  GstFormat format;
  GstSeekFlags flags;
  GstSeekType start_type, stop_type;
  gint64 start, stop;
  gdouble rate;
  GList *walk, *current_file = NULL;
  GstClockTime current_pos, target_pos;
  gint64 current_sequence;
  GstM3U8MediaFile *file;
  guint64 bitrate;
  gboolean snap_before, snap_after, snap_nearest, keyunit;
  gboolean reverse;

  gst_event_parse_seek (seek, &rate, &format, &flags, &start_type, &start,
      &stop_type, &stop);

  bitrate = gst_hls_demux_get_bitrate (hlsdemux);

  /* properly cleanup pending decryption status */
  if (flags & GST_SEEK_FLAG_FLUSH) {
    gst_hls_demux_clear_pending_data (hlsdemux);
  }

  /* Use I-frame variants for trick modes */
  if (hlsdemux->client->main->iframe_lists && rate < -1.0
      && demux->segment.rate >= -1.0 && demux->segment.rate <= 1.0) {
    GError *err = NULL;

    GST_M3U8_CLIENT_LOCK (hlsdemux->client);
    /* Switch to I-frame variant */
    hlsdemux->client->main->current_variant =
        hlsdemux->client->main->iframe_lists;
    GST_M3U8_CLIENT_UNLOCK (hlsdemux->client);
    gst_m3u8_client_set_current (hlsdemux->client,
        hlsdemux->client->main->iframe_lists->data);
    gst_uri_downloader_reset (demux->downloader);
    if (!gst_hls_demux_update_playlist (hlsdemux, FALSE, &err)) {
      GST_ELEMENT_ERROR_FROM_ERROR (hlsdemux, "Could not switch playlist", err);
      return FALSE;
    }
    //hlsdemux->discont = TRUE;
    hlsdemux->do_typefind = TRUE;

    gst_hls_demux_change_playlist (hlsdemux, bitrate / ABS (rate), NULL);
  } else if (rate > -1.0 && rate <= 1.0 && (demux->segment.rate < -1.0
          || demux->segment.rate > 1.0)) {
    GError *err = NULL;
    GST_M3U8_CLIENT_LOCK (hlsdemux->client);
    /* Switch to normal variant */
    hlsdemux->client->main->current_variant = hlsdemux->client->main->lists;
    GST_M3U8_CLIENT_UNLOCK (hlsdemux->client);
    gst_m3u8_client_set_current (hlsdemux->client,
        hlsdemux->client->main->lists->data);
    gst_uri_downloader_reset (demux->downloader);
    if (!gst_hls_demux_update_playlist (hlsdemux, FALSE, &err)) {
      GST_ELEMENT_ERROR_FROM_ERROR (hlsdemux, "Could not switch playlist", err);
      return FALSE;
    }
    //hlsdemux->discont = TRUE;
    hlsdemux->do_typefind = TRUE;
    /* TODO why not continue using the same? that was being used up to now? */
    gst_hls_demux_change_playlist (hlsdemux, bitrate, NULL);
  }

  GST_M3U8_CLIENT_LOCK (hlsdemux->client);
  file = GST_M3U8_MEDIA_FILE (hlsdemux->client->current->files->data);
  current_sequence = file->sequence;
  current_pos = 0;
  reverse = rate < 0;
  target_pos = reverse ? stop : start;

  /* Snap to segment boundary. Improves seek performance on slow machines. */
  keyunit = ! !(flags & GST_SEEK_FLAG_KEY_UNIT);
  snap_nearest =
      (flags & GST_SEEK_FLAG_SNAP_NEAREST) == GST_SEEK_FLAG_SNAP_NEAREST;
  snap_before = ! !(flags & GST_SEEK_FLAG_SNAP_BEFORE);
  snap_after = ! !(flags & GST_SEEK_FLAG_SNAP_AFTER);

  /* FIXME: Here we need proper discont handling */
  for (walk = hlsdemux->client->current->files; walk; walk = walk->next) {
    file = walk->data;

    current_sequence = file->sequence;
    current_file = walk;
    if ((!reverse && snap_after) || snap_nearest) {
      if (current_pos >= target_pos)
        break;
      if (snap_nearest && target_pos - current_pos < file->duration / 2)
        break;
    } else if (reverse && snap_after) {
      /* check if the next fragment is our target, in this case we want to
       * start from the previous fragment */
      GstClockTime next_pos = current_pos + file->duration;

      if (next_pos <= target_pos && target_pos < next_pos + file->duration) {
        break;
      }
    } else if (current_pos <= target_pos
        && target_pos < current_pos + file->duration) {
      break;
    }
    current_pos += file->duration;
  }

  if (walk == NULL) {
    GST_DEBUG_OBJECT (demux, "seeking further than track duration");
    current_sequence++;
  }

  GST_DEBUG_OBJECT (demux, "seeking to sequence %u", (guint) current_sequence);
  hlsdemux->reset_pts = TRUE;
  hlsdemux->client->sequence = current_sequence;
  hlsdemux->client->current_file =
      current_file ? current_file : hlsdemux->client->current->files;
  hlsdemux->client->sequence_position = current_pos;
  GST_M3U8_CLIENT_UNLOCK (hlsdemux->client);

  /* Play from the end of the current selected segment */
  if (reverse && (snap_before || snap_after || snap_nearest))
    current_pos += file->duration;

  if (keyunit || snap_before || snap_after || snap_nearest) {
    if (!reverse)
      gst_segment_do_seek (&demux->segment, rate, format, flags, start_type,
          current_pos, stop_type, stop, NULL);
    else
      gst_segment_do_seek (&demux->segment, rate, format, flags, start_type,
          start, stop_type, current_pos, NULL);
  }

  return TRUE;
}