static u32 dash_main_thread_proc()

in src/media_tools/dash_client.c [3528:4116]


static u32 dash_main_thread_proc(void *par)
{
	GF_Err e;
	GF_DashClient *dash = (GF_DashClient*) par;
	GF_MPD_Representation *rep;
	u32 i, group_count, ret = 0;
	Bool go_on = GF_TRUE;
	u32 min_wait = 0;
	Bool first_period_in_mpd = GF_TRUE;
	char *new_base_seg_url;
	char *key_url=NULL;
	bin128 key_iv;

	assert(dash);
	if (!dash->mpd) {
		GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] Incorrect state, no dash->mpd for URL=%s, already stopped ?\n", dash->base_url));
		return 1;
	}

restart_period:

	/* Setting the download status in exclusive code */
	gf_mx_p(dash->dl_mutex);
	dash->dash_state = GF_DASH_STATE_SETUP;
	gf_mx_v(dash->dl_mutex);

	dash->in_period_setup = 1;

	/*setup period*/
	e = gf_dash_setup_period(dash);
	if (e) {
		dash->dash_io->on_dash_event(dash->dash_io, GF_DASH_EVENT_PERIOD_SETUP_ERROR, -1, e);
		ret = 1;
		goto exit;
	}
	dash->dash_io->on_dash_event(dash->dash_io, GF_DASH_EVENT_SELECT_GROUPS, -1, GF_OK);

	e = GF_OK;
	group_count = gf_list_count(dash->groups);
	for (i=0; i<group_count; i++) {
		GF_DASH_Group *group = gf_list_get(dash->groups, i);
		if (group->selection==GF_DASH_GROUP_NOT_SELECTABLE)
			continue;

		//by default all groups are started (init seg download and buffering). They will be (de)selected by the user
		if (first_period_in_mpd) {
			gf_dash_buffer_on(group);
		}
		e = gf_dash_download_init_segment(dash, group);
		if (e) break;
	}
	first_period_in_mpd = 0;

	/*if error signal to the user*/
	if (e != GF_OK) {
		dash->dash_io->on_dash_event(dash->dash_io, GF_DASH_EVENT_PERIOD_SETUP_ERROR, -1, e);
		ret = 1;
		goto exit;
	}


	dash->last_update_time = gf_sys_clock();

	gf_mx_p(dash->dl_mutex);
	dash->dash_state = GF_DASH_STATE_CONNECTING;
	gf_mx_v(dash->dl_mutex);


	/*ask the user to connect to desired groups*/
	e = dash->dash_io->on_dash_event(dash->dash_io, GF_DASH_EVENT_CREATE_PLAYBACK, -1, GF_OK);
	if (e) {
		ret = 1;
		goto exit;
	}
	if (dash->mpd_stop_request) {
		ret = 1;
		goto exit;
	}

	gf_mx_p(dash->dl_mutex);
	dash->in_period_setup = 0;
	dash->dash_state = GF_DASH_STATE_RUNNING;
	gf_mx_v(dash->dl_mutex);

	min_wait = 0;
	while (go_on) {
		const char *local_file_name = NULL;
		const char *resource_name = NULL;

		/*wait until next segment is needed*/
		while (!dash->mpd_stop_request) {
			u32 timer = gf_sys_clock() - dash->last_update_time;

			/*refresh MPD*/
			if (dash->force_mpd_update || (dash->mpd->minimum_update_period && (timer > dash->mpd->minimum_update_period))) {
				u32 diff = gf_sys_clock();
				dash->force_mpd_update = 0;
				GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] At %d Time to update the playlist (%u ms elapsed since last refresh and min reload rate is %u)\n", gf_sys_clock() , timer, dash->mpd->minimum_update_period));
				gf_mx_p(dash->dl_mutex);
				e = gf_dash_update_manifest(dash);
				gf_mx_v(dash->dl_mutex);
				group_count = gf_list_count(dash->groups);
				diff = gf_sys_clock() - diff;
				if (e) {
					if (!dash->in_error) {
						GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Error updating MPD %s\n", gf_error_to_string(e)));
					}
				} else {
					GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Updated MPD in %d ms\n", diff));
				}
			} else {
				Bool all_groups_done = GF_TRUE;
				Bool cache_full = GF_TRUE;

				/*wait if nothing is ready to be downloaded*/
				if (min_wait>1) {
					u32 sleep_for = MIN(min_wait/2, 1000);
					GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] No segments available on the server until %d ms - going to sleep for %d ms\n", min_wait, sleep_for));
					gf_sleep(sleep_for);
				}

				/*check if cache is not full*/
				gf_mx_p(dash->dl_mutex);
				dash->tsb_exceeded = 0;
				dash->time_in_tsb = 0;
				for (i=0; i<group_count; i++) {
					GF_DASH_Group *group = gf_list_get(dash->groups, i);
					if ((group->selection != GF_DASH_GROUP_SELECTED) || group->done) continue;
					all_groups_done = 0;
					if (dash->mpd->type==GF_MPD_TYPE_DYNAMIC) {
						gf_dash_group_check_time(group);
					}
					if (group->nb_cached_segments<group->max_cached_segments) {
						cache_full = 0;
						break;
					}
				}
				gf_mx_v(dash->dl_mutex);

				if (dash->tsb_exceeded) {
					dash->dash_io->on_dash_event(dash->dash_io, GF_DASH_EVENT_TIMESHIFT_OVERFLOW, (s32) dash->tsb_exceeded, GF_OK);
					dash->tsb_exceeded = 0;
				} else if (dash->time_in_tsb != dash->prev_time_in_tsb) {
					dash->prev_time_in_tsb = dash->time_in_tsb;
					dash->dash_io->on_dash_event(dash->dash_io, GF_DASH_EVENT_TIMESHIFT_UPDATE, 0, GF_OK);
				}


				if (!cache_full) break;

				//seek request
				if (dash->request_period_switch==2) all_groups_done = 1;

				if (all_groups_done && dash->next_period_checked) {
					dash->next_period_checked = 1;
					//check if we can continue next period with the same groups
					if (gf_dash_is_seamless_period_switch(dash)) {
						all_groups_done = 0;
					}
				}
				if (all_groups_done && dash->request_period_switch) {
					gf_dash_reset_groups(dash);
					if (dash->request_period_switch == 1) {
						if (dash->speed<0) {
							if (dash->active_period_index) {
								dash->active_period_index--;
							}
						} else {
							dash->active_period_index++;
						}
					}

					dash->request_period_switch = 0;
					GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] Switching to period #%d\n", dash->active_period_index+1));
					goto restart_period;
				}

				gf_sleep(30);
			}
		}

		/* stop the thread if requested */
		if (dash->mpd_stop_request) {
			go_on = 0;
			break;
		}

		min_wait = 0;

		/*for each selected groups*/
		for (i=0; i<group_count; i++) {
			//commented out as we end up doing too many requets
#if 0
			Bool in_segment_avail_time = GF_FALSE;
#endif
			u64 start_range, end_range;
			Bool use_byterange;
			u32 representation_index;
			u32 clock_time;
			Bool empty_file = GF_FALSE;
			GF_DASH_Group *group = gf_list_get(dash->groups, i);

			if (group->selection != GF_DASH_GROUP_SELECTED) {
				if (group->nb_cached_segments) {
					gf_dash_group_reset(dash, group);
				}
				continue;
			}
			if (group->done) continue;

			if (group->nb_cached_segments>=group->max_cached_segments) {
				GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] At %d Cache is full for this group - skipping\n", gf_sys_clock() ));
				continue;
			}

			/*remember the active rep index, since group->active_rep_index may change because of bandwidth control algorithm*/
			representation_index = group->active_rep_index;
			rep = gf_list_get(group->adaptation_set->representations, group->active_rep_index);

			/* if the index of the segment to be downloaded is greater or equal to the last segment (as seen in the playlist),
			we need to check if a new playlist is ready */
			if (group->nb_segments_in_rep && (group->download_segment_index >= (s32) group->nb_segments_in_rep)) {
				u32 timer = gf_sys_clock() - dash->last_update_time;
				Bool update_playlist = 0;

				/* this period is done*/
				if ((dash->mpd->type==GF_MPD_TYPE_DYNAMIC) && group->period->duration) {
				}
				/* update of the playlist, only if indicated */
				else if (dash->mpd->minimum_update_period && (timer > dash->mpd->minimum_update_period)) {
					update_playlist = 1;
				}
				/* if media_presentation_duration is 0 and we are in live, force a refresh (not in the spec but safety check*/
				else if ((dash->mpd->type==GF_MPD_TYPE_DYNAMIC) && !dash->mpd->media_presentation_duration) {
					if (group->segment_duration && (timer > group->segment_duration*1000))
						update_playlist = 1;
				}
				if (update_playlist) {
					GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Last segment in current playlist downloaded, checking updates after %u ms\n", timer));
					e = gf_dash_update_manifest(dash);
					if (e) {
						GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Error updating MPD %s\n", gf_error_to_string(e)));
					}
					group_count = gf_list_count(dash->groups);
					rep = gf_list_get(group->adaptation_set->representations, group->active_rep_index);
				} else {
					gf_sleep(1);
				}
				/* Now that the playlist is up to date, we can check again */
				if (group->download_segment_index >= (s32) group->nb_segments_in_rep) {
					/* if there is a specified update period, we redo the whole process */
					if (dash->mpd->minimum_update_period || dash->mpd->type==GF_MPD_TYPE_DYNAMIC) {

						if ((dash->mpd->type==GF_MPD_TYPE_DYNAMIC) && group->period->duration) {
							GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Last segment in period (dynamic mode) - group is done\n"));
							group->done = 1;
							break;
						}
						else if (! group->maybe_end_of_stream) {
							u32 now = gf_sys_clock();
							GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] End of segment list reached (%d segments but idx is %d), waiting for next MPD update\n", group->nb_segments_in_rep, group->download_segment_index));
							if (group->nb_cached_segments)
								continue;

							if (!group->time_at_first_reload_required) {
								group->time_at_first_reload_required = now;
								continue;
							}
							if (now - group->time_at_first_reload_required < group->cache_duration)
								continue;
							if (dash->mpd->minimum_update_period && (now - group->time_at_first_reload_required < dash->mpd->minimum_update_period))
								continue;

							GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] Segment list has not been updated for more than %d ms - assuming end of period\n", now - group->time_at_first_reload_required));
							group->done = 1;
							break;
						}
					} else {
						/* if not, we are really at the end of the playlist, we can quit */
						GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] End of period reached for group\n"));
						group->done = 1;
						break;
					}
				}
			}
			group->time_at_first_reload_required = 0;
			gf_mx_p(dash->dl_mutex);

			if (group->force_switch_bandwidth && !dash->auto_switch_count) {
				GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Forcing representation switch, retesting group"));
				gf_dash_switch_group_representation(dash, group);
				/*restart*/
				i--;
				gf_mx_v(dash->dl_mutex);
				continue;
			}

			/*check availablity start time of segment in Live !!*/
			if (!group->broken_timing && (dash->mpd->type==GF_MPD_TYPE_DYNAMIC) && !dash->is_m3u8) {
				s32 to_wait = 0;
				u32 seg_dur_ms=0;
#ifndef GPAC_DISABLE_LOG
				u32 start_number = gf_dash_get_start_number(group, rep);
#endif
				s64 segment_ast = (s64) gf_dash_get_segment_availability_start_time(dash->mpd, group, group->download_segment_index, &seg_dur_ms);
				s64 now = (s64) gf_net_get_utc();


				if (group->retry_after_utc > (u64) now) {
					min_wait = (u32) (group->retry_after_utc - (u64) now);
					gf_mx_v(dash->dl_mutex);
					continue;
				}

				clock_time = gf_sys_clock();
				to_wait = (s32) (segment_ast - now);

				/*if segment AST is greater than now, it is not yet available - we would need an estimate on how long the request takes to be sent to the server in order to be more reactive ...*/
				if (to_wait > 1) {

					GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Set #%d At %d Next segment %d (AST "LLD" - sec in period %g) is not yet available on server - requesting later in %d ms\n", i+1, gf_sys_clock(), group->download_segment_index + start_number, segment_ast, (segment_ast - group->period->start - group->ast_at_init)/1000.0, to_wait));
					if (group->last_segment_time) {
						GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] %d ms elapsed since previous segment download\n", clock_time - group->last_segment_time));
					}
					gf_mx_v(dash->dl_mutex);
					if (!min_wait || ((u32) to_wait < min_wait))
						min_wait = to_wait;

					continue;
				} else {
					GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Set #%d At %d Next segment %d (AST "LLD" - sec in period %g) should now be available on server since %d ms - requesting it\n", i+1, gf_sys_clock(), group->download_segment_index + start_number, segment_ast, (segment_ast - group->period->start - group->ast_at_init)/1000.0, -to_wait));

					if (group->last_segment_time) {
						GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] %d ms elapsed since previous segment download\n", clock_time - group->last_segment_time));
					}
#if 0
					/*check if we are in the segment availability end time*/
					if (now < segment_ast + seg_dur_ms + group->time_shift_buffer_depth )
						in_segment_avail_time = 1;
#endif
				}
			}
			min_wait = 0;


			/* At this stage, there are some segments left to be downloaded */
			e = gf_dash_resolve_url(dash->mpd, rep, group, dash->base_url, GF_MPD_RESOLVE_URL_MEDIA, group->download_segment_index, &new_base_seg_url, &start_range, &end_range, &group->current_downloaded_segment_duration, NULL, &key_url, &key_iv);
			gf_mx_v(dash->dl_mutex);
			if (e) {
				/*do something!!*/
				break;
			}
			use_byterange = (start_range || end_range) ? 1 : 0;

			if (use_byterange) {
				GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Downloading new segment: %s (range: "LLD"-"LLD")\n", new_base_seg_url, start_range, end_range));
			}

			/*local file*/
			if (!strstr(new_base_seg_url, "://") || (!strnicmp(new_base_seg_url, "file://", 7) || !strnicmp(new_base_seg_url, "gmem://", 7)) ) {
				resource_name = local_file_name = (char *) new_base_seg_url;
				e = GF_OK;
				/*do not erase local files*/
				group->local_files = 1;
				gf_dash_buffer_off(group);
				if (group->force_switch_bandwidth && !dash->auto_switch_count) {
					gf_dash_switch_group_representation(dash, group);
					/*restart*/
					i--;
					continue;
				}
			} else {
				group->max_bitrate = 0;
				group->min_bitrate = (u32)-1;

				/*use persistent connection for segment downloads*/
				gf_mx_p(dash->dl_mutex);
				if (use_byterange) {
					e = gf_dash_download_resource(dash, &(group->segment_download), new_base_seg_url, start_range, end_range, 1, group);
				} else {
					e = gf_dash_download_resource(dash, &(group->segment_download), new_base_seg_url, 0, 0, 1, group);
				}
				gf_mx_v(dash->dl_mutex);

				if ((e==GF_IP_CONNECTION_CLOSED) && group->download_abort_type) {
					group->download_abort_type = 0;
					GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] Aborted while downloading segment (seek ?)%s \n", new_base_seg_url));
					gf_free(new_base_seg_url);
					gf_free(key_url);
					new_base_seg_url = NULL;
					key_url = NULL;
					continue;
				}


				/*TODO decide what is the best, fetch from another representation or ignore ...*/
				if (e != GF_OK) {
					clock_time = gf_sys_clock();
					min_wait = dash->min_timeout_between_404;
					group->retry_after_utc = min_wait + gf_net_get_utc();

					if (group->maybe_end_of_stream) {
						if (group->maybe_end_of_stream==2) {
							GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] Couldn't get segment %s (error %s) and end of period was guessed during last update - stopping playback\n", new_base_seg_url, gf_error_to_string(e)));
							group->maybe_end_of_stream = 0;
							group->done = 1;
						}
						group->maybe_end_of_stream++;
					} else if (group->segment_in_valid_range) {
						GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Error in downloading new segment: %s %s - segment was lost at server/proxy side\n", new_base_seg_url, gf_error_to_string(e)));
						if (dash->speed >= 0) {
							group->download_segment_index++;
						} else if (group->download_segment_index) {
							group->download_segment_index--;
						} else {
							GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Playing in backward - start of playlist reached - assuming end of stream\n"));
							group->done = 1;
						}
						group->segment_in_valid_range=0;
					} else if (group->prev_segment_ok && !group->time_at_first_failure) {
						if (!group->loop_detected) {
							group->time_at_first_failure = clock_time;
							GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Error in downloading new segment: %s %s - starting countdown for %d ms\n", new_base_seg_url, gf_error_to_string(e), group->current_downloaded_segment_duration));
						}
					}
					//if previous segment download was OK, we are likely asking too early - retry for the complete duration in case one segment was lost - we add 100ms safety
					else if (group->prev_segment_ok && (clock_time - group->time_at_first_failure <= group->current_downloaded_segment_duration + dash->segment_lost_after_ms )) {
					} else {
						if (group->prev_segment_ok) {
							GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Error in downloading new segment: %s %s - waited %d ms but segment still not available, checking next one ...\n", new_base_seg_url, gf_error_to_string(e), clock_time - group->time_at_first_failure));
							group->time_at_first_failure = 0;
							group->prev_segment_ok = GF_FALSE;
						}
#if 1
						group->nb_consecutive_fail ++;
						//we are lost ....
						if (group->nb_consecutive_fail == 20) {
							GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Too many consecutive segments not found, sync or signal has been lost - entering end of stream detection mode\n"));
							min_wait = 1000;
							group->maybe_end_of_stream = 1;
						} else
#endif
						{
							GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Error in downloading new segment: %s %s\n", new_base_seg_url, gf_error_to_string(e)));
							if (dash->speed >= 0) {
								group->download_segment_index++;
							} else if (group->download_segment_index) {
								group->download_segment_index--;
							} else {
								GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Playing in backward - start of playlist reached - assuming end of stream\n"));
								group->done = 1;
							}
						}
					}
					gf_free(new_base_seg_url);
					gf_free(key_url);
					new_base_seg_url = NULL;
					key_url = NULL;
					continue;
				}

				group->prev_segment_ok = GF_TRUE;
				if (group->time_at_first_failure) {
					GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Recovered segment %s after 404 - was our download schedule too early ?\n", new_base_seg_url));
					group->time_at_first_failure = 0;
					group->nb_consecutive_fail = 0;
				}

				if ((e==GF_OK) && group->force_switch_bandwidth) {
					if (!dash->auto_switch_count) {
						gf_dash_switch_group_representation(dash, group);
						/*restart*/
						i--;
						gf_free(new_base_seg_url);
						new_base_seg_url = NULL;
						gf_free(key_url);
						key_url = NULL;
						continue;
					}
					if (rep->playback.disabled) {
						gf_dash_skip_disabled_representation(group, rep, GF_FALSE);
						/*restart*/
						i--;
						gf_free(new_base_seg_url);
						new_base_seg_url = NULL;
						gf_free(key_url);
						key_url = NULL;
						continue;
					}
				}

				if (group->segment_must_be_streamed) local_file_name = dash->dash_io->get_url(dash->dash_io, group->segment_download);
				else local_file_name = dash->dash_io->get_cache_name(dash->dash_io, group->segment_download);

				if (dash->dash_io->get_total_size(dash->dash_io, group->segment_download)==0) {
					empty_file = GF_TRUE;
				}
				resource_name = dash->dash_io->get_url(dash->dash_io, group->segment_download);

				dash_do_rate_adaptation(dash, group, rep);
			}

			if (local_file_name && (e == GF_OK || group->segment_must_be_streamed )) {
				gf_mx_p(dash->dl_mutex);

				assert(group->nb_cached_segments<group->max_cached_segments);
				assert(local_file_name);

				if (!empty_file) {

					group->cached[group->nb_cached_segments].cache = gf_strdup(local_file_name);
					group->cached[group->nb_cached_segments].url = gf_strdup( resource_name );
					group->cached[group->nb_cached_segments].start_range = 0;
					group->cached[group->nb_cached_segments].end_range = 0;
					group->cached[group->nb_cached_segments].representation_index = representation_index;
					group->cached[group->nb_cached_segments].duration = (u32) group->current_downloaded_segment_duration;
					group->cached[group->nb_cached_segments].loop_detected = group->loop_detected;
					if (key_url) {
						group->cached[group->nb_cached_segments].key_url = key_url;
						memcpy(group->cached[group->nb_cached_segments].key_IV, key_iv, sizeof(bin128));
						key_url = NULL;
					}
					
					group->loop_detected = GF_FALSE;

					if (group->local_files && use_byterange) {
						group->cached[group->nb_cached_segments].start_range = start_range;
						group->cached[group->nb_cached_segments].end_range = end_range;
					}
					GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Added file to cache (%u/%u in cache): %s\n", group->nb_cached_segments+1, group->max_cached_segments, group->cached[group->nb_cached_segments].url));
					group->nb_cached_segments++;
					gf_dash_update_buffering(group, dash);
				}

				/* download enhancement representation of this segment*/
				if ((representation_index != group->force_max_rep_index) && rep->enhancement_rep_index_plus_one) {
					group->active_rep_index = rep->enhancement_rep_index_plus_one - 1;
					group->has_pending_enhancement = GF_TRUE;
				}
				/* if we have downloaded all enhancement representations of this segment, restart from base representation and increase dowloaded segment index by 1*/
				else {
					if (group->base_rep_index_plus_one) group->active_rep_index = group->base_rep_index_plus_one - 1;
					if (dash->speed >= 0) {
						group->download_segment_index++;
					} else if (group->download_segment_index) {
						group->download_segment_index--;
					} else {
						GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Playing in backward - start of playlist reached - assuming end of stream\n"));
						group->done = 1;
					}
					group->has_pending_enhancement = GF_FALSE;
				}
				if (dash->auto_switch_count) {
					group->nb_segments_done++;
					if (group->nb_segments_done==dash->auto_switch_count) {
						group->nb_segments_done=0;
						gf_dash_skip_disabled_representation(group, rep, GF_TRUE);
					}
				}

				//do not notify segments if there is a pending period switch - since these are decided by the user, we don't
				//want to notify old segments
				if (!dash->request_period_switch && !group->has_pending_enhancement)
					dash->dash_io->on_dash_event(dash->dash_io, GF_DASH_EVENT_SEGMENT_AVAILABLE, gf_list_find(dash->groups, group), GF_OK);

				gf_mx_v(dash->dl_mutex);

			}
			gf_free(new_base_seg_url);
			new_base_seg_url = NULL;
			if (key_url) {
				gf_free(key_url);
				key_url = NULL;
			}
		}
	}

exit:
	/* Signal that the download thread has ended */
	gf_mx_p(dash->dl_mutex);

	/*an error occured during playback chain creation and we couldn't release our plyayback chain in time, do it now*/
	if (dash->dash_state == GF_DASH_STATE_CONNECTING)
		gf_dash_reset_groups(dash);

	dash->dash_state = GF_DASH_STATE_STOPPED;
	gf_mx_v(dash->dl_mutex);
	return ret;
}