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; } }
static gboolean gst_hls_demux_update_playlist (GstHLSDemux * demux, gboolean update) { GstFragment *download; GstBuffer *buf; gchar *playlist; gboolean updated = FALSE; const gchar *uri = gst_m3u8_client_get_current_uri (demux->client); download = gst_uri_downloader_fetch_uri (demux->downloader, uri); if (download == NULL) return FALSE; buf = gst_fragment_get_buffer (download); playlist = gst_hls_src_buf_to_utf8_playlist (buf); g_object_unref (download); if (playlist == NULL) { GST_WARNING_OBJECT (demux, "Couldn't not validate playlist encoding"); return FALSE; } updated = gst_m3u8_client_update (demux->client, playlist); /* If it's a live source, do not let the sequence number go beyond * three fragments before the end of the list */ if (updated && update == FALSE && demux->client->current && gst_m3u8_client_is_live (demux->client)) { guint last_sequence; GST_M3U8_CLIENT_LOCK (demux->client); last_sequence = GST_M3U8_MEDIA_FILE (g_list_last (demux->client->current-> files)->data)->sequence; if (demux->client->sequence >= last_sequence - 3) { GST_DEBUG_OBJECT (demux, "Sequence is beyond playlist. Moving back to %d", last_sequence - 3); demux->need_segment = TRUE; demux->client->sequence = last_sequence - 3; } GST_M3U8_CLIENT_UNLOCK (demux->client); } return updated; }
static gboolean gst_hls_demux_update_playlist (GstHLSDemux * demux, gboolean update, GError ** err) { GstAdaptiveDemux *adaptive_demux = GST_ADAPTIVE_DEMUX (demux); GstFragment *download; GstBuffer *buf; gchar *playlist; gboolean main_checked = FALSE, updated = FALSE; gchar *uri, *main_uri; retry: uri = gst_m3u8_client_get_current_uri (demux->client); main_uri = gst_m3u8_client_get_uri (demux->client); download = gst_uri_downloader_fetch_uri (adaptive_demux->downloader, uri, main_uri, TRUE, TRUE, TRUE, err); g_free (main_uri); if (download == NULL) { gchar *base_uri; if (!update || main_checked || !gst_m3u8_client_has_variant_playlist (demux->client)) { g_free (uri); return FALSE; } g_clear_error (err); main_uri = gst_m3u8_client_get_uri (demux->client); GST_INFO_OBJECT (demux, "Updating playlist %s failed, attempt to refresh variant playlist %s", uri, main_uri); download = gst_uri_downloader_fetch_uri (adaptive_demux->downloader, main_uri, NULL, TRUE, TRUE, TRUE, err); g_free (main_uri); if (download == NULL) { g_free (uri); return FALSE; } buf = gst_fragment_get_buffer (download); playlist = gst_hls_src_buf_to_utf8_playlist (buf); gst_buffer_unref (buf); if (playlist == NULL) { GST_WARNING_OBJECT (demux, "Failed to validate variant playlist encoding"); g_free (uri); g_object_unref (download); g_set_error (err, GST_STREAM_ERROR, GST_STREAM_ERROR_WRONG_TYPE, "Couldn't validate playlist encoding"); return FALSE; } g_free (uri); if (download->redirect_permanent && download->redirect_uri) { uri = download->redirect_uri; base_uri = NULL; } else { uri = download->uri; base_uri = download->redirect_uri; } if (!gst_m3u8_client_update_variant_playlist (demux->client, playlist, uri, base_uri)) { GST_WARNING_OBJECT (demux, "Failed to update the variant playlist"); g_object_unref (download); g_set_error (err, GST_STREAM_ERROR, GST_STREAM_ERROR_FAILED, "Couldn't update playlist"); return FALSE; } g_object_unref (download); main_checked = TRUE; goto retry; } g_free (uri); /* Set the base URI of the playlist to the redirect target if any */ GST_M3U8_CLIENT_LOCK (demux->client); g_free (demux->client->current->uri); g_free (demux->client->current->base_uri); if (download->redirect_permanent && download->redirect_uri) { demux->client->current->uri = g_strdup (download->redirect_uri); demux->client->current->base_uri = NULL; } else { demux->client->current->uri = g_strdup (download->uri); demux->client->current->base_uri = g_strdup (download->redirect_uri); } GST_M3U8_CLIENT_UNLOCK (demux->client); buf = gst_fragment_get_buffer (download); playlist = gst_hls_src_buf_to_utf8_playlist (buf); gst_buffer_unref (buf); g_object_unref (download); if (playlist == NULL) { GST_WARNING_OBJECT (demux, "Couldn't validate playlist encoding"); g_set_error (err, GST_STREAM_ERROR, GST_STREAM_ERROR_WRONG_TYPE, "Couldn't validate playlist encoding"); return FALSE; } updated = gst_m3u8_client_update (demux->client, playlist); if (!updated) { GST_WARNING_OBJECT (demux, "Couldn't update playlist"); g_set_error (err, GST_STREAM_ERROR, GST_STREAM_ERROR_FAILED, "Couldn't update playlist"); return FALSE; } /* If it's a live source, do not let the sequence number go beyond * three fragments before the end of the list */ if (update == FALSE && demux->client->current && gst_m3u8_client_is_live (demux->client)) { gint64 last_sequence, first_sequence; GST_M3U8_CLIENT_LOCK (demux->client); last_sequence = GST_M3U8_MEDIA_FILE (g_list_last (demux->client->current-> files)->data)->sequence; first_sequence = GST_M3U8_MEDIA_FILE (demux->client->current->files->data)->sequence; GST_DEBUG_OBJECT (demux, "sequence:%" G_GINT64_FORMAT " , first_sequence:%" G_GINT64_FORMAT " , last_sequence:%" G_GINT64_FORMAT, demux->client->sequence, first_sequence, last_sequence); if (demux->client->sequence >= last_sequence - 3) { //demux->need_segment = TRUE; /* Make sure we never go below the minimum sequence number */ demux->client->sequence = MAX (first_sequence, last_sequence - 3); GST_DEBUG_OBJECT (demux, "Sequence is beyond playlist. Moving back to %" G_GINT64_FORMAT, demux->client->sequence); } GST_M3U8_CLIENT_UNLOCK (demux->client); } else if (demux->client->current && !gst_m3u8_client_is_live (demux->client)) { GstClockTime current_pos, target_pos; guint sequence = 0; GList *walk; /* Sequence numbers are not guaranteed to be the same in different * playlists, so get the correct fragment here based on the current * position */ GST_M3U8_CLIENT_LOCK (demux->client); /* Valid because hlsdemux only has a single output */ if (GST_ADAPTIVE_DEMUX_CAST (demux)->streams) { GstAdaptiveDemuxStream *stream = GST_ADAPTIVE_DEMUX_CAST (demux)->streams->data; target_pos = stream->segment.position; } else { target_pos = 0; } if (GST_CLOCK_TIME_IS_VALID (demux->client->sequence_position)) { target_pos = MAX (target_pos, demux->client->sequence_position); } GST_LOG_OBJECT (demux, "Looking for sequence position %" GST_TIME_FORMAT " in updated playlist", GST_TIME_ARGS (target_pos)); current_pos = 0; for (walk = demux->client->current->files; walk; walk = walk->next) { GstM3U8MediaFile *file = walk->data; sequence = file->sequence; if (current_pos <= target_pos && target_pos < current_pos + file->duration) { break; } current_pos += file->duration; } /* End of playlist */ if (!walk) sequence++; demux->client->sequence = sequence; demux->client->sequence_position = current_pos; GST_M3U8_CLIENT_UNLOCK (demux->client); } return updated; }
static gboolean gst_hls_demux_get_next_fragment (GstHLSDemux * demux, gboolean caching) { GstFragment *download; const gchar *next_fragment_uri; GstClockTime duration; GstClockTime timestamp; GstBuffer *buf; gboolean discont; if (!gst_m3u8_client_get_next_fragment (demux->client, &discont, &next_fragment_uri, &duration, ×tamp)) { GST_INFO_OBJECT (demux, "This playlist doesn't contain more fragments"); demux->end_of_playlist = TRUE; gst_task_start (demux->stream_task); return FALSE; } GST_INFO_OBJECT (demux, "Fetching next fragment %s", next_fragment_uri); download = gst_uri_downloader_fetch_uri (demux->downloader, next_fragment_uri); if (download == NULL) goto error; buf = gst_fragment_get_buffer (download); GST_BUFFER_DURATION (buf) = duration; GST_BUFFER_PTS (buf) = timestamp; /* We actually need to do this every time we switch bitrate */ if (G_UNLIKELY (demux->do_typefind)) { GstCaps *caps = gst_fragment_get_caps (download); if (!demux->input_caps || !gst_caps_is_equal (caps, demux->input_caps)) { gst_caps_replace (&demux->input_caps, caps); /* gst_pad_set_caps (demux->srcpad, demux->input_caps); */ GST_INFO_OBJECT (demux, "Input source caps: %" GST_PTR_FORMAT, demux->input_caps); demux->do_typefind = FALSE; } gst_caps_unref (caps); } else { gst_fragment_set_caps (download, demux->input_caps); } if (discont) { GST_DEBUG_OBJECT (demux, "Marking fragment as discontinuous"); GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT); } g_queue_push_tail (demux->queue, download); if (!caching) { GST_TASK_SIGNAL (demux->updates_task); gst_task_start (demux->stream_task); } return TRUE; error: { gst_hls_demux_stop (demux); return FALSE; } }