1 /**
2  *			GPAC - Multimedia Framework C SDK
3  *
4  *			Authors: Jean Le Feuvre, Cyril Concolato
5  *			Copyright (c) Telecom ParisTech 2010-2020
6  *					All rights reserved
7  *
8  *  This file is part of GPAC / Adaptive HTTP Streaming
9  *
10  *  GPAC is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU Lesser General Public License as published by
12  *  the Free Software Foundation; either version 2, or (at your option)
13  *  any later version.
14  *
15  *  GPAC is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU Lesser General Public License for more details.
19  *
20  *  You should have received a copy of the GNU Lesser General Public
21  *  License along with this library; see the file COPYING.  If not, write to
22  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23  *
24  */
25 
26 #include <gpac/thread.h>
27 #include <gpac/network.h>
28 #include <gpac/dash.h>
29 #include <gpac/mpd.h>
30 #include <gpac/internal/m3u8.h>
31 #include <gpac/internal/isomedia_dev.h>
32 #include <gpac/base_coding.h>
33 #include <string.h>
34 #include <sys/stat.h>
35 
36 #include <math.h>
37 
38 
39 #ifndef GPAC_DISABLE_DASH_CLIENT
40 
41 /*ISO 639 languages*/
42 #include <gpac/iso639.h>
43 
44 /*set to 1 if you want MPD to use SegmentTemplate if possible instead of SegmentList*/
45 #define M3U8_TO_MPD_USE_TEMPLATE	0
46 /*set to 1 if you want MPD to use SegmentTimeline*/
47 #define M3U8_TO_MPD_USE_SEGTIMELINE	0
48 
49 typedef enum {
50 	GF_DASH_STATE_STOPPED = 0,
51 	/*period setup and playback chain creation*/
52 	GF_DASH_STATE_SETUP,
53 	/*request to start playback chain*/
54 	GF_DASH_STATE_CONNECTING,
55 	GF_DASH_STATE_RUNNING,
56 } GF_DASH_STATE;
57 
58 
59 //shifts AST in the past(>0) or future (<0)  so that client starts request in the future or in the past
60 //#define FORCE_DESYNC	4000
61 
62 /* WARNING: GF_DASH_Group does not represent a Group in DASH
63    It corresponds to an AdaptationSet with additional live information not present in the MPD
64    (e.g. current active representation)
65 */
66 typedef struct __dash_group GF_DASH_Group;
67 
68 struct __dash_client
69 {
70 	GF_DASHFileIO *dash_io;
71 
72 	/*interface to mpd parser - get rid of this and use the DASHIO instead ?*/
73 	GF_FileDownload getter;
74 
75 	char *base_url;
76 
77 	u32 max_cache_duration, max_width, max_height;
78 	u8 max_bit_per_pixel;
79 	u32 auto_switch_count;
80 	Bool keep_files, disable_switching, allow_local_mpd_update, estimate_utc_drift, ntp_forced;
81 	Bool is_m3u8, is_smooth;
82 	Bool split_adaptation_set;
83 	GF_DASHLowLatencyMode low_latency_mode;
84 	//set when MPD downloading fails. Will resetup DASH live once MPD is sync again
85 	Bool in_error;
86 
87 	u64 mpd_fetch_time;
88 	GF_DASHInitialSelectionMode first_select_mode;
89 
90 	/* MPD downloader*/
91 	GF_DASHFileIOSession mpd_dnload;
92 	/* MPD */
93 	GF_MPD *mpd;
94 	/* number of time the MPD has been reloaded and last update time*/
95 	u32 reload_count, last_update_time;
96 	/*signature of last MPD*/
97 	u8 lastMPDSignature[GF_SHA1_DIGEST_SIZE];
98 	/*mime type of media segments (m3u8)*/
99 	char *mimeTypeForM3U8Segments;
100 
101 	/* active period in MPD */
102 	u32 active_period_index;
103 	u32 reinit_period_index;
104 	u32 request_period_switch;
105 
106 	Bool next_period_checked;
107 
108 	u64 start_time_in_active_period;
109 
110 	Bool ignore_mpd_duration;
111 	u32 initial_time_shift_value;
112 
113 	const char *query_string;
114 
115 	/*list of groups in the active period*/
116 	GF_List *groups;
117 
118 	/*Main Thread handling segment downloads and MPD/M3U8 update*/
119 	GF_Thread *dash_thread;
120 	/*mutex for MPD updates and group access*/
121 	GF_Mutex *dash_mutex;
122 
123 	/* one of the above state*/
124 	GF_DASH_STATE dash_state;
125 	Bool mpd_stop_request;
126 	Bool in_period_setup;
127 	Bool all_groups_done_notified;
128 
129 	s64 utc_drift_estimate;
130 	s32 utc_shift;
131 
132 	Double start_range_period;
133 
134 	Double speed;
135 	Bool is_rt_speed;
136 	u32 probe_times_before_switch;
137 	Bool agressive_switching;
138 	u32 min_wait_ms_before_next_request;
139 	u32 min_wait_sys_clock;
140 
141 	Bool force_mpd_update;
142 
143 	u32 user_buffer_ms;
144 
145 	u32 min_timeout_between_404, segment_lost_after_ms;
146 
147 	GF_DASHThreadMode thread_mode;
148 
149 	Bool ignore_xlink;
150 
151 	//0: not atsc - 1: atsc but clock not init 2- atsc clock init
152 	u32 atsc_clock_state;
153 	//atsc AST shift in ms
154 	u32 atsc_ast_shift;
155 
156 	Bool initial_period_tunein;
157 
158 	//in ms
159 	u32 time_in_tsb, prev_time_in_tsb;
160 	u32 tsb_exceeded;
161 	s32 debug_group_index;
162 	Bool disable_speed_adaptation;
163 
164 	Bool period_groups_setup;
165 	u32 tile_rate_decrease;
166 	GF_DASHTileAdaptationMode tile_adapt_mode;
167 
168 	GF_List *SRDs;
169 
170 	GF_DASHAdaptationAlgorithm adaptation_algorithm;
171 
172 	s32 (*rate_adaptation_algo)(GF_DashClient *dash, GF_DASH_Group *group, GF_DASH_Group *base_group,
173 												  u32 dl_rate, Double speed, Double max_available_speed, Bool force_lower_complexity,
174 												  GF_MPD_Representation *rep, Bool go_up_bitrate);
175 
176 	GF_Err (*rate_adaptation_download_monitor)(GF_DashClient *dash, GF_DASH_Group *group);
177 };
178 
179 static void gf_dash_seek_group(GF_DashClient *dash, GF_DASH_Group *group, Double seek_to, Bool is_dynamic);
180 
181 
182 typedef struct
183 {
184 	char *cache;
185 	char *url;
186 	u64 start_range, end_range;
187 	/*representation index in adaptation_set->representations*/
188 	u32 representation_index;
189 	Bool loop_detected;
190 	u32 duration;
191 	char *key_url;
192 	bin128 key_IV;
193 	Bool has_dep_following;
194 } segment_cache_entry;
195 
196 typedef enum
197 {
198 	/*set if group cannot be selected (wrong MPD)*/
199 	GF_DASH_GROUP_NOT_SELECTABLE = 0,
200 	GF_DASH_GROUP_NOT_SELECTED,
201 	GF_DASH_GROUP_SELECTED,
202 } GF_DASHGroupSelection;
203 
204 /*this structure Group is the implementation of the adaptationSet element of the MPD.*/
205 struct __dash_group
206 {
207 	GF_DashClient *dash;
208 
209 	/*pointer to adaptation set*/
210 	GF_MPD_AdaptationSet *adaptation_set;
211 	/*pointer to active period*/
212 	GF_MPD_Period *period;
213 
214 	/*active representation index in adaptation_set->representations*/
215 	u32 active_rep_index;
216 
217 	u32 prev_active_rep_index;
218 
219 	Bool timeline_setup;
220 
221 	GF_DASHGroupSelection selection;
222 
223 	/*may be mpd@time_shift_buffer_depth or rep@time_shift_buffer_depth*/
224 	u32 time_shift_buffer_depth;
225 
226 	Bool bitstream_switching, dont_delete_first_segment;
227 	GF_DASH_Group *depend_on_group;
228 	Bool done;
229 	//if set, will redownload the last segment partially downloaded
230 	Bool force_switch_bandwidth;
231 	Bool min_bandwidth_selected;
232 	u32 download_start_time;
233 	u32 active_bitrate, max_bitrate, min_bitrate;
234 	u32 min_representation_bitrate;
235 
236 	u32 nb_segments_in_rep;
237 
238 	/* Segment duration as advertised in the MPD
239 	   for the real duration of the segment being downloaded see current_downloaded_segment_duration */
240 	Double segment_duration;
241 
242 	Double start_playback_range;
243 
244 	Bool group_setup;
245 
246 	Bool was_segment_base;
247 	/*local file playback, do not delete them*/
248 	Bool local_files;
249 	/*next segment to download for this group - negative number to take into account SN wrapping*/
250 	s32 download_segment_index;
251 	/*number of segments pruged since the start of the period*/
252 	u32 nb_segments_purged;
253 
254 	u32 nb_retry_on_last_segment;
255 	s32 start_number_at_last_ast;
256 	u64 ast_at_init;
257 
258 	/*next file (cached) to delete at next GF_NET_SERVICE_QUERY_NEXT for this group*/
259 	char * urlToDeleteNext;
260 	volatile u32 max_cached_segments, nb_cached_segments, max_buffer_segments;
261 	segment_cache_entry *cached;
262 
263 	GF_DASHFileIOSession segment_download;
264 	//0: not set, 1: abort because group has been stopped - 2: abort because bandwidth was too low
265 	u32 download_abort_type;
266 	/*usually 0-0 (no range) but can be non-zero when playing local MPD/DASH sessions*/
267 	u64 bs_switching_init_segment_url_start_range, bs_switching_init_segment_url_end_range;
268 	char *bs_switching_init_segment_url;
269 
270 	u32 nb_segments_done;
271 	u32 last_segment_time;
272 	u32 nb_segments_since_switch;
273 
274 	//stats of last downloaded segment
275 	u32 total_size, bytes_per_sec, bytes_done, backup_Bps;
276 
277 
278 	Bool segment_must_be_streamed;
279 	Bool broken_timing;
280 	Bool buffering;
281 	u32 maybe_end_of_stream;
282 	u32 cache_duration;
283 	u32 time_at_first_reload_required;
284 	u32 force_representation_idx_plus_one;
285 
286 	Bool force_segment_switch;
287 	Bool is_downloading;
288 	Bool loop_detected;
289 
290 	u32 time_at_first_failure;
291 	Bool prev_segment_ok, segment_in_valid_range;
292 	//this is the number of 404
293 	u32 nb_consecutive_segments_lost;
294 	u64 retry_after_utc;
295 	/*set when switching segment, indicates the current downloaded segment duration*/
296 	u64 current_downloaded_segment_duration;
297 
298 	char *service_mime;
299 
300 	/* base representation index of this group plus one, or 0 if all representations in this group are independent*/
301 	u32 base_rep_index_plus_one;
302 
303 	/* maximum representation index we want to download*/
304 	u32 max_complementary_rep_index;
305 	//start time and timescales of currently downloaded segment
306 	u64 current_start_time;
307 	u32 current_timescale;
308 
309 	void *udta;
310 
311 	Bool has_pending_enhancement;
312 
313 	/*Codec statistics*/
314 	u32 avg_dec_time, max_dec_time, irap_avg_dec_time, irap_max_dec_time;
315 	Bool codec_reset;
316 	Bool decode_only_rap;
317 	/*display statistics*/
318 	u32 display_width, display_height;
319 	/*sets by user, indicates when the client will decide to play/resume after a buffering period (this is a static value for the entire session)*/
320 	u32 max_buffer_playout_ms;
321 	/*buffer status*/
322 	u32 buffer_min_ms, buffer_max_ms, buffer_occupancy_ms;
323 	u32 buffer_occupancy_at_last_seg;
324 
325 	u32 m3u8_start_media_seq;
326 	u64 hls_next_start_time;
327 
328 	GF_List *groups_depending_on;
329 	u32 current_dep_idx;
330 
331 	u32 target_new_rep;
332 
333 	u32 srd_x, srd_y, srd_w, srd_h, srd_row_idx, srd_col_idx;
334 	struct _dash_srd_desc *srd_desc;
335 
336 	/*mutex for group->cache access (read and write in download)*/
337 	GF_Mutex *cache_mutex;
338 
339 	GF_Thread *download_th;
340 	Bool download_th_done;
341 
342 	/*current index of the base URL used*/
343 	u32 current_base_url_idx;
344 
345 	u32 quality_degradation_hint;
346 
347 	Bool rate_adaptation_postponed;
348 
349 	/* current segment index in BBA and BOLA algorithm */
350 	u32 current_index;
351 
352 	//in non-threaded mode, indicates that the demux for this group has nothing to do...
353 	Bool force_early_fetch;
354 	Bool is_low_latency;
355 };
356 
357 static void gf_dash_solve_period_xlink(GF_DashClient *dash, GF_List *period_list, u32 period_idx);
358 
359 struct _dash_srd_desc
360 {
361 	u32 srd_nb_rows, srd_nb_cols;
362 	u32 id, width, height, srd_fw, srd_fh;
363 };
364 
365 void drm_decrypt(unsigned char * data, unsigned long dataSize, const char * decryptMethod, const char * keyfileURL, const unsigned char * keyIV);
366 
367 
368 
gf_dash_get_mime_type(GF_MPD_SubRepresentation * subrep,GF_MPD_Representation * rep,GF_MPD_AdaptationSet * set)369 static const char *gf_dash_get_mime_type(GF_MPD_SubRepresentation *subrep, GF_MPD_Representation *rep, GF_MPD_AdaptationSet *set)
370 {
371 	if (subrep && subrep->mime_type) return subrep->mime_type;
372 	if (rep && rep->mime_type) return rep->mime_type;
373 	if (set && set->mime_type) return set->mime_type;
374 	return NULL;
375 }
376 
377 
dash_get_fetch_time(GF_DashClient * dash)378 static u64 dash_get_fetch_time(GF_DashClient *dash)
379 {
380 	u64 utc = 0;
381 
382 	if (dash->mpd_dnload && dash->dash_io->get_utc_start_time)
383 		utc = dash->dash_io->get_utc_start_time(dash->dash_io, dash->mpd_dnload);
384 	if (!utc)
385 		utc = gf_net_get_utc();
386 	return utc;
387 }
388 
389 
gf_dash_group_count_rep_needed(GF_DASH_Group * group)390 static u32 gf_dash_group_count_rep_needed(GF_DASH_Group *group)
391 {
392 	u32 count, nb_rep_need, next_rep_index_plus_one;
393 	GF_MPD_Representation *rep;
394 	count  = gf_list_count(group->adaptation_set->representations);
395 	nb_rep_need = 1;
396 	if (!group->base_rep_index_plus_one || (group->base_rep_index_plus_one == group->max_complementary_rep_index+1))
397 		return nb_rep_need; // we need to download only one representation
398 	rep = gf_list_get(group->adaptation_set->representations, group->base_rep_index_plus_one-1);
399 	next_rep_index_plus_one = rep->playback.enhancement_rep_index_plus_one;
400 	while ((nb_rep_need < count) && rep->playback.enhancement_rep_index_plus_one) {
401 		nb_rep_need++;
402 		if (next_rep_index_plus_one == group->max_complementary_rep_index+1)
403 			break;
404 		rep = gf_list_get(group->adaptation_set->representations, next_rep_index_plus_one-1);
405 		next_rep_index_plus_one = rep->playback.enhancement_rep_index_plus_one;
406 	}
407 
408 	assert(nb_rep_need <= count);
409 
410 	return nb_rep_need;
411 }
412 
413 static
gf_dash_check_mpd_root_type(const char * local_url)414 u32 gf_dash_check_mpd_root_type(const char *local_url)
415 {
416 	if (local_url) {
417 		char *rtype = gf_xml_get_root_type(local_url, NULL);
418 		if (rtype) {
419 			u32 handled = 0;
420 			if (!strcmp(rtype, "MPD")) {
421 				handled = 1;
422 			}
423 			else if (!strcmp(rtype, "SmoothStreamingMedia")) {
424 				handled = 2;
425 			}
426 			gf_free(rtype);
427 			return handled;
428 		}
429 	}
430 	return GF_FALSE;
431 }
432 
433 
gf_dash_group_timeline_setup(GF_MPD * mpd,GF_DASH_Group * group,u64 fetch_time)434 static void gf_dash_group_timeline_setup(GF_MPD *mpd, GF_DASH_Group *group, u64 fetch_time)
435 {
436 	GF_MPD_SegmentTimeline *timeline = NULL;
437 	GF_MPD_Representation *rep = NULL;
438 	const char *val;
439 	u32 shift, timescale;
440 	u64 current_time, current_time_no_timeshift, availabilityStartTime;
441 	u32 ast_diff, start_number;
442 	Double ast_offset = 0;
443 
444 	if (mpd->type==GF_MPD_TYPE_STATIC)
445 		return;
446 
447 	//always init clock even if active period is a remote one
448 #if 0
449 	if (group->period->origin_base_url && (group->period->type != GF_MPD_TYPE_DYNAMIC))
450 		return;
451 #endif
452 
453 	/*M3U8 does not use NTP sync */
454 	if (group->dash->is_m3u8)
455 		return;
456 
457 	if (group->broken_timing )
458 		return;
459 
460 
461 	/*if no AST, do not use NTP sync */
462 	if (! group->dash->mpd->availabilityStartTime) {
463 		group->broken_timing = GF_TRUE;
464 		return;
465 	}
466 
467 	if (!fetch_time) {
468 		//when we initialize the timeline without an explicit fetch time, use our local clock - this allows for better precision
469 		//when trying to locate the live edge
470 		fetch_time = gf_net_get_utc();
471 	}
472 	//if ATSC and clock not setup, do it
473 	val = group->dash->dash_io->get_header_value(group->dash->dash_io, group->dash->mpd_dnload, "x-dash-atsc");
474 	if (val && !group->dash->utc_drift_estimate) {
475 		u32 i;
476 		GF_MPD_Period *dyn_period=NULL;
477 		Bool found = GF_FALSE;
478 		u64 timeline_offset_ms=0;
479 		if (!group->dash->atsc_clock_state) {
480 			GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] Detected ATSC DASH service ID %s\n", val));
481 			group->dash->atsc_clock_state = 1;
482 		}
483 
484 		for (i=0; i<gf_list_count(group->dash->mpd->periods); i++) {
485 			dyn_period = gf_list_get(group->dash->mpd->periods, i);
486 			if (!dyn_period->xlink_href && !dyn_period->origin_base_url) break;
487 			if (dyn_period->xlink_href && !dyn_period->origin_base_url && gf_list_count(dyn_period->adaptation_sets) ) break;
488 			dyn_period = NULL;
489 		}
490 		if (!dyn_period) {
491 			GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] ATSC with no dynamic period, cannot init clock yet\n"));
492 			return;
493 		}
494 
495 		for (i=0; i<gf_list_count(dyn_period->adaptation_sets); i++) {
496 			u64 sr, seg_dur;
497 			u32 j, len, nb_space=0;
498 			GF_MPD_AdaptationSet *set;
499 			char *sep, *start, *end, *seg_url = NULL;
500 			val = group->dash->dash_io->get_header_value(group->dash->dash_io, group->dash->mpd_dnload, "x-dash-first-seg");
501 			if (!val) {
502 				GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] Waiting for ATSC clock ...\n"));
503 				return;
504 			}
505 
506 			set = gf_list_get(dyn_period->adaptation_sets, i);
507 			for (j=0; j<gf_list_count(set->representations); j++) {
508 				u64 dur = dyn_period->duration;
509 				rep = gf_list_get(set->representations, j);
510 
511 				dyn_period->duration = 0;
512 
513 				gf_mpd_resolve_url(group->dash->mpd, rep, set, dyn_period, "./", 0, GF_MPD_RESOLVE_URL_MEDIA_NOSTART, 9876, 0, &seg_url, &sr, &sr, &seg_dur, NULL, NULL, NULL);
514 
515 				dyn_period->duration = dur;
516 
517 				sep = seg_url ? strstr(seg_url, "987") : NULL;
518 				if (!sep) {
519 					GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Failed to resolve template for segment #9876 on rep #%d\n", j+1));
520 					if (seg_url) gf_free(seg_url);
521 					continue;
522 				}
523 				start = sep;
524 				end = sep+4;
525 				while (start>seg_url && (*(start-1)=='0')) { start--; nb_space++;}
526 				start[0]=0;
527 				len = (u32) strlen(seg_url)-2;
528 				if (!strncmp(val, seg_url+2, len)) {
529 					u32 number=0;
530 					char szTemplate[100];
531 
532 					GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Resolve ATSC clock on bootstrap segment URL %s template %s\n", val, seg_url+2));
533 
534 					strcpy(szTemplate, seg_url+2);
535 					strcat(szTemplate, "%");
536 					if (nb_space) {
537 						char szFmt[20];
538 						sprintf(szFmt, "0%d", nb_space+4);
539 						strcat(szTemplate, szFmt);
540 					}
541 					strcat(szTemplate, "d");
542 					strcat(szTemplate, end);
543 					if (sscanf(val, szTemplate, &number) == 1) {
544 						u32 startNum = 1;
545 						//safety check for now, in case one of the segment is send too early compared to the rest
546 						if (number) number--;
547 						if (dyn_period->segment_template) startNum = dyn_period->segment_template->start_number;
548 						if (set->segment_template) startNum = set->segment_template->start_number;
549 						if (rep->segment_template) startNum = rep->segment_template->start_number;
550 						if (number>=startNum) {
551 							timeline_offset_ms = seg_dur*(number-startNum);
552 						}
553 						found = GF_TRUE;
554 					}
555 				} else {
556 					GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] ATSC bootstrap segment URL %s does not match template %s for rep #%d\n", val, seg_url+2, j+1));
557 				}
558 				gf_free(seg_url);
559 				if (found) break;
560 			}
561 			if (found) break;
562 		}
563 		if (found) {
564 			//adjust so that nb_seg = current_time/segdur = (fetch-ast)/seg_dur;
565 			// = (fetch- ( mpd->availabilityStartTime + group->dash->utc_shift + group->dash->utc_drift_estimate) / segdur;
566 			//hence nb_seg*seg_dur = fetch - mpd->availabilityStartTime - group->dash->utc_shift - group->dash->utc_drift_estimate
567 			//so group->dash->utc_drift_estimate = fetch - (mpd->availabilityStartTime + nb_seg*seg_dur)
568 
569 
570 			u64 utc = mpd->availabilityStartTime + dyn_period->start + timeline_offset_ms;
571 			group->dash->utc_drift_estimate = ((s64) fetch_time - (s64) utc);
572 			GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] Estimated UTC diff of ATSC broadcast "LLD" ms (UTC fetch "LLU" - server UTC "LLU" - MPD AST "LLU" - MPD PublishTime "LLU" - bootstraping on segment %s\n", group->dash->utc_drift_estimate, fetch_time, utc, group->dash->mpd->availabilityStartTime, group->dash->mpd->publishTime, val));
573 
574 			group->dash->atsc_clock_state = 2;
575 		} else {
576 			GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] Failed to setup ATSC clock from segment template with bootstrap URL %s, using NTP\n", val));
577 			group->dash->atsc_clock_state = 3;
578 		}
579 	}
580 	else if (val) {
581 		GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] ATSC clock already setup - UTC diff of ATSC broadcast "LLD" ms\n", group->dash->utc_drift_estimate));
582 	} else {
583 		GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] No ATSC entity on HTPP request\n"));
584 	}
585 
586 	if ((!group->dash->atsc_clock_state || (group->dash->atsc_clock_state>2)) && !group->dash->ntp_forced && group->dash->estimate_utc_drift && !group->dash->utc_drift_estimate && group->dash->mpd_dnload && group->dash->dash_io->get_header_value) {
587 		val = group->dash->dash_io->get_header_value(group->dash->dash_io, group->dash->mpd_dnload, "Server-UTC");
588 		if (val) {
589 			u64 utc;
590 			sscanf(val, LLU, &utc);
591 			group->dash->utc_drift_estimate = ((s64) fetch_time - (s64) utc);
592 			GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] Estimated UTC diff between client and server "LLD" ms (UTC fetch "LLU" - server UTC "LLU" - MPD AST "LLU" - MPD PublishTime "LLU"\n", group->dash->utc_drift_estimate, fetch_time, utc, group->dash->mpd->availabilityStartTime, group->dash->mpd->publishTime));
593 		} else {
594 			s64 drift_estimate = 0;
595 			u64 utc = 0;
596 			val = group->dash->dash_io->get_header_value(group->dash->dash_io, group->dash->mpd_dnload, "Date");
597 			if (val)
598 				utc = gf_net_parse_date(val);
599 			if (utc)
600 				drift_estimate = ((s64) fetch_time - (s64) utc);
601 
602 			//HTTP date is in second - if the clock diff is less than 1 sec, we cannot infer anything
603 			if (ABS(drift_estimate) > 1000) {
604 				group->dash->utc_drift_estimate = drift_estimate;
605 				GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] Estimated UTC diff between client and server "LLD" ms (UTC fetch "LLU" - server UTC "LLU" - MPD AST "LLU" - MPD PublishTime "LLU"\n", group->dash->utc_drift_estimate, fetch_time, utc, group->dash->mpd->availabilityStartTime, group->dash->mpd->publishTime));
606 			} else {
607 				GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] No UTC diff between client and server (UTC fetch "LLU" - server UTC "LLU" - MPD AST "LLU" - MPD PublishTime "LLU"\n", fetch_time, utc, group->dash->mpd->availabilityStartTime, group->dash->mpd->publishTime));
608 			}
609 		}
610 	}
611 
612 	availabilityStartTime = 0;
613 	if ((s64) mpd->availabilityStartTime + group->dash->utc_shift > (s64) - group->dash->utc_drift_estimate) {
614 		availabilityStartTime = mpd->availabilityStartTime + group->dash->utc_shift + group->dash->utc_drift_estimate;
615 	}
616 
617 
618 #ifdef FORCE_DESYNC
619 	availabilityStartTime -= FORCE_DESYNC;
620 #endif
621 
622 	ast_diff = (u32) (availabilityStartTime - group->dash->mpd->availabilityStartTime);
623 	current_time = fetch_time;
624 
625 	if (current_time < availabilityStartTime) {
626 		//if more than 1 sec consider we have a pb
627 		if (availabilityStartTime - current_time >= 1000) {
628 			Bool broken_timing = GF_TRUE;
629 #ifndef _WIN32_WCE
630 			time_t gtime1, gtime2;
631 			struct tm *t1, *t2;
632 			gtime1 = current_time / 1000;
633 			t1 = gf_gmtime(&gtime1);
634 			gtime2 = availabilityStartTime / 1000;
635 			t2 = gf_gmtime(&gtime2);
636 			if (t1 == t2) {
637 				GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Slight drift in UTC clock at time %d-%02d-%02dT%02d:%02d:%02dZ: diff AST - now %d ms\n", 1900+t1->tm_year, t1->tm_mon+1, t1->tm_mday, t1->tm_hour, t1->tm_min, t1->tm_sec, (s32) (availabilityStartTime - current_time) ));
638 				current_time = 0;
639 				broken_timing = GF_FALSE;
640 			}
641 			else if (t1 && t2) {
642 				t1->tm_year = t2->tm_year;
643 				GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Error in UTC clock: current time %d-%02d-%02dT%02d:%02d:%02dZ is less than AST %d-%02d-%02dT%02d:%02d:%02dZ - diff AST-now %d ms\n",
644 				                                   1900+t1->tm_year, t1->tm_mon+1, t1->tm_mday, t1->tm_hour, t1->tm_min, t1->tm_sec,
645 				                                   1900+t2->tm_year, t2->tm_mon+1, t2->tm_mday, t2->tm_hour, t2->tm_min, t2->tm_sec,
646 				                                   (u32) (availabilityStartTime - current_time)
647 				                                  ));
648 			} else {
649 				GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Error in UTC clock: could not retrieve time!\n"));
650 			}
651 
652 #endif
653 			if (broken_timing) {
654 				if (group->dash->utc_shift + group->dash->utc_drift_estimate > 0) {
655 					availabilityStartTime = current_time;
656 				} else {
657 					group->broken_timing = GF_TRUE;
658 					return;
659 				}
660 			}
661 		} else {
662 			availabilityStartTime = current_time;
663 			current_time = 0;
664 		}
665 	}
666 	else current_time -= availabilityStartTime;
667 
668 	if (gf_list_count(group->dash->mpd->periods)) {
669 		u64 seg_start_ms = current_time;
670 		u64 seg_end_ms = (u64) (seg_start_ms + group->segment_duration*1000);
671 		u32 i;
672 		u64 start = 0;
673 		for (i=0; i<gf_list_count(group->dash->mpd->periods); i++) {
674 			GF_MPD_Period *ap = gf_list_get(group->dash->mpd->periods, i);
675 			if (ap->start) start = ap->start;
676 
677 			if (group->dash->initial_period_tunein
678 				&& (seg_start_ms>=ap->start)
679 				&& (!ap->duration || (seg_end_ms<=start + ap->duration))
680 			) {
681 				if (i != group->dash->active_period_index) {
682 					group->dash->reinit_period_index = 1+i;
683 					group->dash->start_range_period = (Double) seg_start_ms;
684 					group->dash->start_range_period -= ap->start;
685 					group->dash->start_range_period /= 1000;
686 					return;
687 				}
688 			}
689 
690 			if (!ap->duration) break;
691 			start += ap->duration;
692 		}
693 	}
694 
695 	//compute current time in period
696 	if (current_time < group->period->start)
697 		current_time = 0;
698 	else {
699 		if (group->dash->initial_period_tunein) {
700 			current_time -= group->period->start;
701 		} else {
702 			//initial period was setup, consider we are moving to a new period, so time in this period is 0
703 			current_time = 0;
704 			if (group->start_playback_range) current_time = (u64) (group->start_playback_range*1000);
705 		}
706 	}
707 
708 	current_time_no_timeshift = current_time;
709 	if ( ((s32) mpd->time_shift_buffer_depth>=0)) {
710 
711 		if (group->dash->initial_time_shift_value) {
712 			if (group->dash->initial_time_shift_value<=100) {
713 				shift = mpd->time_shift_buffer_depth;
714 				shift *= group->dash->initial_time_shift_value;
715 				shift /= 100;
716 			} else {
717 				shift = (u32) group->dash->initial_time_shift_value;
718 				if (shift > mpd->time_shift_buffer_depth) shift = mpd->time_shift_buffer_depth;
719 			}
720 
721 			if (current_time < shift) current_time = 0;
722 			else current_time -= shift;
723 		}
724 	}
725 	group->dash->time_in_tsb = group->dash->prev_time_in_tsb = 0;
726 
727 	timeline = NULL;
728 	timescale=1;
729 	start_number=0;
730 	rep = gf_list_get(group->adaptation_set->representations, group->active_rep_index);
731 
732 	if (group->period->segment_list) {
733 		if (group->period->segment_list->segment_timeline) timeline = group->period->segment_list->segment_timeline;
734 		if (group->period->segment_list->timescale) timescale = group->period->segment_list->timescale;
735 		if (group->period->segment_list->start_number) start_number = group->period->segment_list->start_number;
736 		if (group->period->segment_list->availability_time_offset) ast_offset = group->period->segment_list->availability_time_offset;
737 	}
738 	if (group->adaptation_set->segment_list) {
739 		if (group->adaptation_set->segment_list->segment_timeline) timeline = group->adaptation_set->segment_list->segment_timeline;
740 		if (group->adaptation_set->segment_list->timescale) timescale = group->adaptation_set->segment_list->timescale;
741 		if (group->adaptation_set->segment_list->start_number) start_number = group->adaptation_set->segment_list->start_number;
742 		if (group->adaptation_set->segment_list->availability_time_offset) ast_offset = group->adaptation_set->segment_list->availability_time_offset;
743 	}
744 	if (rep->segment_list) {
745 		if (rep->segment_list->segment_timeline) timeline = rep->segment_list->segment_timeline;
746 		if (rep->segment_list->timescale) timescale = rep->segment_list->timescale;
747 		if (rep->segment_list->start_number) start_number = rep->segment_list->start_number;
748 		if (rep->segment_list->availability_time_offset) ast_offset = rep->segment_list->availability_time_offset;
749 	}
750 
751 	if (group->period->segment_template) {
752 		if (group->period->segment_template->segment_timeline) timeline = group->period->segment_template->segment_timeline;
753 		if (group->period->segment_template->timescale) timescale = group->period->segment_template->timescale;
754 		if (group->period->segment_template->start_number) start_number = group->period->segment_template->start_number;
755 		if (group->period->segment_template->availability_time_offset) ast_offset = group->period->segment_template->availability_time_offset;
756 	}
757 	if (group->adaptation_set->segment_template) {
758 		if (group->adaptation_set->segment_template->segment_timeline) timeline = group->adaptation_set->segment_template->segment_timeline;
759 		if (group->adaptation_set->segment_template->timescale) timescale = group->adaptation_set->segment_template->timescale;
760 		if (group->adaptation_set->segment_template->start_number) start_number = group->adaptation_set->segment_template->start_number;
761 		if (group->adaptation_set->segment_template->availability_time_offset) ast_offset = group->adaptation_set->segment_template->availability_time_offset;
762 	}
763 	if (rep->segment_template) {
764 		if (rep->segment_template->segment_timeline) timeline = rep->segment_template->segment_timeline;
765 		if (rep->segment_template->timescale) timescale = rep->segment_template->timescale;
766 		if (rep->segment_template->start_number) start_number = rep->segment_template->start_number;
767 		if (rep->segment_template->availability_time_offset) ast_offset = rep->segment_template->availability_time_offset;
768 	}
769 
770 	group->is_low_latency = GF_FALSE;
771 	if (group->dash->low_latency_mode==GF_DASH_LL_DISABLE) {
772 		ast_offset = 0;
773 	} else if (ast_offset>0) {
774 		group->is_low_latency = GF_TRUE;
775 	}
776 	if (timeline) {
777 		u64 start_segtime = 0;
778 		u64 segtime = 0;
779 		u64 current_time_rescale;
780 		u64 timeline_duration = 0;
781 		u32 count;
782 		u64 last_s_dur=0;
783 		u32 i, seg_idx = 0;
784 
785 		current_time_rescale = current_time;
786 		current_time_rescale *= timescale;
787 		current_time_rescale /= 1000;
788 
789 		count = gf_list_count(timeline->entries);
790 		for (i=0; i<count; i++) {
791 			GF_MPD_SegmentTimelineEntry *ent = gf_list_get(timeline->entries, i);
792 
793 			if (!i && (current_time_rescale + ent->duration < ent->start_time)) {
794 				current_time_rescale = current_time_no_timeshift * timescale / 1000;
795 			}
796 			timeline_duration += (1+ent->repeat_count)*ent->duration;
797 
798 			if (i+1 == count) timeline_duration -= ent->duration;
799 			last_s_dur=ent->duration;
800 		}
801 
802 
803 		if (!group->dash->mpd->minimum_update_period) {
804 			last_s_dur *= 1000;
805 			last_s_dur /= timescale;
806 			GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] dynamic MPD but no update period specified and SegmentTimeline used - will use segment duration %d ms as default update rate\n", last_s_dur));
807 
808 			group->dash->mpd->minimum_update_period = (u32) last_s_dur;
809 		}
810 
811 		for (i=0; i<count; i++) {
812 			u32 repeat;
813 			GF_MPD_SegmentTimelineEntry *ent = gf_list_get(timeline->entries, i);
814 			if (!segtime) {
815 				start_segtime = segtime = ent->start_time;
816 
817 				//if current time is before the start of the previous segment, consider our timing is broken
818 				if (current_time_rescale + ent->duration < segtime) {
819 					GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] current time "LLU" is before start time "LLU" of first segment in timeline (timescale %d) by %g sec - using first segment as starting point\n", current_time_rescale, segtime, timescale, (segtime-current_time_rescale)*1.0/timescale));
820 					group->download_segment_index = seg_idx;
821 					group->nb_segments_in_rep = count;
822 					group->start_playback_range = (segtime)*1.0/timescale;
823 					group->ast_at_init = availabilityStartTime - (u32) (ast_offset*1000);
824 					group->broken_timing = GF_TRUE;
825 					return;
826 				}
827 			}
828 
829 			repeat = 1+ent->repeat_count;
830 			while (repeat) {
831 				if ((current_time_rescale >= segtime) && (current_time_rescale < segtime + ent->duration)) {
832 					GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] Found segment %d for current time "LLU" is in SegmentTimeline ["LLU"-"LLU"] (timecale %d - current index %d - startNumber %d)\n", seg_idx, current_time_rescale, start_segtime, segtime + ent->duration, timescale, group->download_segment_index, start_number));
833 
834 					group->download_segment_index = seg_idx;
835 					group->nb_segments_in_rep = seg_idx + count - i;
836 					group->start_playback_range = (current_time)/1000.0;
837 					group->ast_at_init = availabilityStartTime - (u32) (ast_offset*1000);
838 
839 					//to remove - this is a hack to speedup starting for some strange MPDs which announce the live point as the first segment but have already produced the complete timeline
840 					if (group->dash->utc_drift_estimate<0) {
841 						group->ast_at_init -= (timeline_duration - (segtime-start_segtime)) *1000/timescale;
842 					}
843 					return;
844 				}
845 				segtime += ent->duration;
846 				repeat--;
847 				seg_idx++;
848 				last_s_dur=ent->duration;
849 			}
850 		}
851 		//check if we're ahead of time but "reasonnably" ahead (max 1 min) - otherwise consider the timing is broken
852 		if ((current_time_rescale + last_s_dur >= segtime) && (current_time_rescale <= segtime + 60*timescale)) {
853 			GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] current time "LLU" is greater than last SegmentTimeline end "LLU" - defaulting to last entry in SegmentTimeline\n", current_time_rescale, segtime));
854 			group->download_segment_index = seg_idx-1;
855 			group->nb_segments_in_rep = seg_idx;
856 			group->start_playback_range = current_time/1000.0;
857 			group->ast_at_init = availabilityStartTime - (u32) (ast_offset*1000);
858 			//force an update in half the target period
859 			group->dash->last_update_time = gf_sys_clock() + group->dash->mpd->minimum_update_period/2;
860 		} else {
861 			//NOT FOUND !!
862 			GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] current time "LLU" is NOT in SegmentTimeline ["LLU"-"LLU"] - cannot estimate current startNumber, default to 0 ...\n", current_time_rescale, start_segtime, segtime));
863 			group->download_segment_index = 0;
864 			group->nb_segments_in_rep = 10;
865 			group->broken_timing = GF_TRUE;
866 		}
867 
868 		return;
869 	}
870 
871 	if (group->segment_duration) {
872 		u32 nb_segs_in_update = (u32) (mpd->minimum_update_period / (1000*group->segment_duration) );
873 		Double nb_seg = (Double) current_time;
874 		nb_seg /= 1000;
875 		nb_seg /= group->segment_duration;
876 		shift = (u32) nb_seg;
877 
878 		if ((group->dash->atsc_clock_state == 2) && shift) {
879 			//shift currently points to the next segment after the one used for clock bootstrap, use the right one
880 			shift--;
881 			//avoid querying too early the cache since segments do not usually arrive exactly on time ...
882 			availabilityStartTime += group->dash->atsc_ast_shift;
883 		}
884 
885 		if (group->dash->initial_period_tunein) {
886 			u64 seg_start_ms, seg_end_ms;
887 
888 			seg_start_ms = (u64) (group->segment_duration * (shift+start_number) * 1000);
889 			seg_end_ms = (u64) (seg_start_ms + group->segment_duration*1000);
890 			//we are in the right period
891 			if (seg_start_ms>=group->period->start && (!group->period->duration || (seg_end_ms<=group->period->start+group->period->duration)) ) {
892 			} else {
893 				u32 i;
894 				u64 start = 0;
895 				for (i=0; i<gf_list_count(group->dash->mpd->periods); i++) {
896 					GF_MPD_Period *ap = gf_list_get(group->dash->mpd->periods, i);
897 					if (ap->start) start = ap->start;
898 
899 					if ((seg_start_ms>=ap->start) && (!ap->duration || (seg_end_ms<=start + ap->duration))) {
900 						group->dash->reinit_period_index = 1+i;
901 						group->dash->start_range_period = (Double) seg_start_ms;
902 						group->dash->start_range_period -= ap->start;
903 						group->dash->start_range_period /= 1000;
904 						return;
905 					}
906 
907 					if (!ap->duration) break;
908 					start += ap->duration;
909 				}
910 			}
911 		}
912 
913 		//not time shifting, we are at the live edge, we must stick to start of segment otherwise we won't have enough data to play until next segment is ready
914 
915 		if (!group->dash->initial_time_shift_value) {
916 			Double time_in_seg;
917 			//by default playback starts at begining of segment
918 			group->start_playback_range = shift * group->segment_duration;
919 
920 			time_in_seg = (Double) current_time/1000.0;
921 			time_in_seg -= group->start_playback_range;
922 
923 			//if low latency, try to adjust
924 			if (ast_offset) {
925 				Double ast_diff_d;
926 				if (ast_offset>group->segment_duration) ast_offset = group->segment_duration;
927 				ast_diff_d = group->segment_duration - ast_offset;
928 
929 				//we assume that in low latency mode, chunks are made available every (group->segment_duration - ast_offset)
930 				//we need to seek such that the remaining time R satisfies now + R = NextSegAST
931 				//hence S(n) + ms_in_seg + R = S(n+1) + Aoffset
932 				//which gives us R = S(n+1) + Aoffset - S(n) - ms_in_seg = D + Aoffset - ms_in_seg
933 				//seek = D - R = D - (D + Aoffset - ms_in_seg) = ms_in_seg - Ao
934 				if (time_in_seg > ast_diff_d) {
935 					group->start_playback_range += time_in_seg - ast_diff_d;
936 				}
937 			}
938 		} else {
939 			group->start_playback_range = (Double) current_time / 1000.0;
940 		}
941 
942 		if (!group->start_number_at_last_ast) {
943 			group->download_segment_index = shift;
944 			group->start_number_at_last_ast = start_number;
945 
946 			group->ast_at_init = availabilityStartTime - (u32) (ast_offset*1000);
947 			GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] AST at init "LLD"\n", group->ast_at_init));
948 
949 			GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] At current time "LLD" ms: Initializing Timeline: startNumber=%d segmentNumber=%d segmentDuration=%f - %.03f seconds in segment (start range %g)\n", current_time, start_number, shift, group->segment_duration, group->start_playback_range ? group->start_playback_range - shift*group->segment_duration : 0, group->start_playback_range));
950 		} else {
951 			group->download_segment_index += start_number;
952 			if (group->download_segment_index > group->start_number_at_last_ast) {
953 				group->download_segment_index -= group->start_number_at_last_ast;
954 				GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] At current time %d ms: Updating Timeline: startNumber=%d segmentNumber=%d downloadSegmentIndex=%d segmentDuration=%g AST_diff=%d\n", current_time, start_number, shift, group->download_segment_index, group->segment_duration, ast_diff));
955 			} else {
956 				group->download_segment_index = shift;
957 				group->ast_at_init = availabilityStartTime -  (u32) (ast_offset*1000);
958 				GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] At current time "LLU" ms: Re-Initializing Timeline: startNumber=%d segmentNumber=%d segmentDuration=%g AST_diff=%d\n", current_time, start_number, shift, group->segment_duration, ast_diff));
959 			}
960 			group->start_number_at_last_ast = start_number;
961 		}
962 		if (group->nb_segments_in_rep) {
963 			GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] UTC time indicates first segment in period is %d, MPD indicates %d segments are available\n", group->download_segment_index , group->nb_segments_in_rep));
964 		} else {
965 			GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] UTC time indicates first segment in period is %d\n", group->download_segment_index));
966 		}
967 
968 		if (group->nb_segments_in_rep && (group->download_segment_index + nb_segs_in_update > group->nb_segments_in_rep)) {
969 			if (group->download_segment_index < (s32)group->nb_segments_in_rep) {
970 
971 			} else {
972 				GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] Not enough segments (%d needed vs %d indicated) to reach period endTime indicated in MPD - ignoring MPD duration\n", nb_segs_in_update, group->nb_segments_in_rep - group->download_segment_index ));
973 				group->nb_segments_in_rep = shift + nb_segs_in_update;
974 				group->dash->ignore_mpd_duration = GF_TRUE;
975 			}
976 		}
977 		group->prev_segment_ok = GF_TRUE;
978 	} else {
979 		GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Segment duration unknown - cannot estimate current startNumber\n"));
980 	}
981 }
982 
983 
984 
985 
986 
987 /*!
988 * Returns true if given mime type is a MPD file
989 \param mime the mime-type to check
990 \return true if mime-type if MPD-OK
991 */
gf_dash_is_dash_mime(const char * mime)992 static Bool gf_dash_is_dash_mime(const char *mime) {
993 	u32 i;
994 	if (!mime)
995 		return GF_FALSE;
996 	for (i = 0 ; GF_DASH_MPD_MIME_TYPES[i] ; i++) {
997 		if ( !stricmp(mime, GF_DASH_MPD_MIME_TYPES[i]))
998 			return GF_TRUE;
999 	}
1000 	return GF_FALSE;
1001 }
1002 
1003 /*!
1004 * Returns true if mime type of a given URL is an M3U8 mime-type
1005 \param url The url to check
1006 \param mime The mime-type to check
1007 \return true if mime-type is OK for M3U8
1008 */
gf_dash_is_m3u8_mime(const char * url,const char * mime)1009 static Bool gf_dash_is_m3u8_mime(const char *url, const char * mime) {
1010 	u32 i;
1011 	if (!url || !mime)
1012 		return GF_FALSE;
1013 	if (strstr(url, ".mpd") || strstr(url, ".MPD"))
1014 		return GF_FALSE;
1015 
1016 	for (i = 0 ; GF_DASH_M3U8_MIME_TYPES[i] ; i++) {
1017 		if ( !stricmp(mime, GF_DASH_M3U8_MIME_TYPES[i]))
1018 			return GF_TRUE;
1019 	}
1020 	return GF_FALSE;
1021 }
1022 
gf_dash_is_smooth_mime(const char * url,const char * mime)1023 static Bool gf_dash_is_smooth_mime(const char *url, const char * mime)
1024 {
1025 	u32 i;
1026 	if (!url || !mime)
1027 		return GF_FALSE;
1028 	if (strstr(url, ".mpd") || strstr(url, ".MPD"))
1029 		return GF_FALSE;
1030 
1031 	for (i = 0 ; GF_DASH_SMOOTH_MIME_TYPES[i] ; i++) {
1032 		if ( !stricmp(mime, GF_DASH_SMOOTH_MIME_TYPES[i]))
1033 			return GF_TRUE;
1034 	}
1035 	return GF_FALSE;
1036 }
1037 
1038 
1039 GF_EXPORT
gf_dash_group_check_bandwidth(GF_DashClient * dash,u32 idx)1040 GF_Err gf_dash_group_check_bandwidth(GF_DashClient *dash, u32 idx)
1041 {
1042 	GF_DASH_Group *group = gf_list_get(dash->groups, idx);
1043 	if (!group) return GF_BAD_PARAM;
1044 
1045 	if (dash->rate_adaptation_download_monitor)
1046 		return dash->rate_adaptation_download_monitor(dash, group);
1047 
1048 	return GF_OK;
1049 }
1050 
1051 /*!
1052 * Download a file with possible retry if GF_IP_CONNECTION_FAILURE|GF_IP_NETWORK_FAILURE
1053 * (I discovered that with my WIFI connection, I had many issues with BFM-TV downloads)
1054 * Similar to gf_service_download_new() and gf_dm_sess_process().
1055 * Parameters are identical to the ones of gf_service_download_new.
1056 * \see gf_service_download_new()
1057 */
gf_dash_download_resource(GF_DashClient * dash,GF_DASHFileIOSession * sess,const char * url,u64 start_range,u64 end_range,u32 persistent_mode,GF_DASH_Group * group)1058 GF_Err gf_dash_download_resource(GF_DashClient *dash, GF_DASHFileIOSession *sess, const char *url, u64 start_range, u64 end_range, u32 persistent_mode, GF_DASH_Group *group)
1059 {
1060 	s32 group_idx = -1;
1061 	Bool had_sess = GF_FALSE;
1062 	Bool retry = GF_TRUE;
1063 	GF_Err e;
1064 	GF_DASHFileIO *dash_io = dash->dash_io;
1065 
1066 	if (!dash_io) return GF_BAD_PARAM;
1067 	GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Downloading %s starting at UTC "LLU" ms\n", url, gf_net_get_utc() ));
1068 
1069 	if (group) {
1070 		group_idx = gf_list_find(group->dash->groups, group);
1071 	}
1072 
1073 	if (! *sess) {
1074 		*sess = dash_io->create(dash_io, persistent_mode ? 1 : 0, url, group_idx);
1075 		if (!(*sess)) {
1076 			if (dash->atsc_clock_state)
1077 				return GF_IP_NETWORK_EMPTY;
1078 			GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Cannot try to download %s... out of memory ?\n", url));
1079 			return GF_OUT_OF_MEM;
1080 		}
1081 	} else {
1082 		had_sess = GF_TRUE;
1083 		if (persistent_mode!=2) {
1084 			e = dash_io->setup_from_url(dash_io, *sess, url, group_idx);
1085 			if (e) {
1086 				//with ATSC we may have 404 right away if nothing in cache yet, not an error
1087 				GF_LOG(dash->atsc_clock_state ? GF_LOG_DEBUG : GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Cannot resetup downloader for url %s: %s\n", url, gf_error_to_string(e) ));
1088 				return e;
1089 			}
1090 		}
1091 	}
1092 	if (group) {
1093 		group->is_downloading = GF_TRUE;
1094 		group->download_start_time  = gf_sys_clock();
1095 	}
1096 
1097 retry:
1098 
1099 	if (end_range) {
1100 		e = dash_io->set_range(dash_io, *sess, start_range, end_range, (persistent_mode==2) ? GF_FALSE : GF_TRUE);
1101 		if (e) {
1102 			if (had_sess) {
1103 				dash_io->del(dash_io, *sess);
1104 				*sess = NULL;
1105 				return gf_dash_download_resource(dash, sess, url, start_range, end_range, persistent_mode ? 1 : 0, group);
1106 			}
1107 			GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Cannot setup byte-range download for %s: %s\n", url, gf_error_to_string(e) ));
1108 			if (group)
1109 				group->is_downloading = GF_FALSE;
1110 
1111 			return e;
1112 		}
1113 	}
1114 	assert(*sess);
1115 
1116 	/*issue HTTP GET for headers only*/
1117 	e = dash_io->init(dash_io, *sess);
1118 
1119 	if (e>=GF_OK) {
1120 		/*check mime type of the adaptation set if not provided*/
1121 		if (group) {
1122 			const char *mime = *sess ? dash_io->get_mime(dash_io, *sess) : NULL;
1123 			if (mime && !group->service_mime) {
1124 				group->service_mime = gf_strdup(mime);
1125 			}
1126 			/*we allow servers to give us broken mim types for the representation served ...*/
1127 #if 0
1128 			else if (mime && stricmp(group->service_mime, mime)) {
1129 				GF_MPD_Representation *rep = gf_list_get(group->adaptation_set->representations, group->active_rep_index);
1130 				if (! gf_dash_get_mime_type(NULL, rep, group->adaptation_set) )
1131 					rep->mime_type = gf_strdup(mime);
1132 				rep->disabled = 1;
1133 				GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] Disabling representation since mime does not match: expected %s, but had %s for %s!\n", group->service_mime, mime, url));
1134 				group->force_switch_bandwidth = 1;
1135 				if (group->segment_download) dash_io->abort(dash_io, group->segment_download);
1136 				group->is_downloading = 0;
1137 				return GF_OK;
1138 			}
1139 #endif
1140 
1141 			/*file cannot be cached on disk !*/
1142 			if (dash_io->get_cache_name(dash_io, *sess ) == NULL) {
1143 				GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Segment %s cannot be cached on disk, will use direct streaming\n", url));
1144 				group->segment_must_be_streamed = GF_TRUE;
1145 				if (group->segment_download) dash_io->abort(dash_io, group->segment_download);
1146 				group->is_downloading = GF_TRUE;
1147 				return GF_OK;
1148 			}
1149 			group->segment_must_be_streamed = GF_FALSE;
1150 		}
1151 
1152 		//release dl_mutex while downloading segment
1153 		/*we can download the file*/
1154 		e = dash_io->run(dash_io, *sess);
1155 	} else {
1156 		GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] At "LLU" error %s - released dl_mutex\n", gf_net_get_utc(), gf_error_to_string(e)));
1157 	}
1158 	if (group && group->download_abort_type) {
1159 		group->is_downloading = GF_FALSE;
1160 		return GF_IP_CONNECTION_CLOSED;
1161 	}
1162 	switch (e) {
1163 	case GF_IP_CONNECTION_FAILURE:
1164 	case GF_IP_NETWORK_FAILURE:
1165 		if (!dash->in_error || group) {
1166 			dash_io->del(dash_io, *sess);
1167 			GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] failed to download, retrying once with %s...\n", url));
1168 			*sess = dash_io->create(dash_io, 0, url, group_idx);
1169 			if (! (*sess)) {
1170 				GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Cannot retry to download %s... out of memory ?\n", url));
1171 				if (group)
1172 					group->is_downloading = GF_FALSE;
1173 				return GF_OUT_OF_MEM;
1174 			}
1175 
1176 			if (retry) {
1177 				retry = GF_FALSE;
1178 				goto retry;
1179 			}
1180 			GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] two consecutive failures, aborting the download %s.\n", url));
1181 		} else if (dash->in_error) {
1182 			GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Download still in error for %s.\n", url));
1183 		}
1184 		break;
1185 	case GF_OK:
1186 		GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Download %s complete at UTC "LLU" ms\n", url, gf_net_get_utc() ));
1187 		break;
1188 	default:
1189 		//log as warning, maybe the dash client can recover from this error
1190 		GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] Failed to download %s = %s...\n", url, gf_error_to_string(e)));
1191 		break;
1192 	}
1193 	if (group)
1194 		group->is_downloading = GF_FALSE;
1195 
1196 	return e;
1197 }
1198 
gf_dash_get_timeline_duration(GF_MPD * mpd,GF_MPD_Period * period,GF_MPD_SegmentTimeline * timeline,u32 timescale,u32 * nb_segments,Double * max_seg_duration)1199 static void gf_dash_get_timeline_duration(GF_MPD *mpd, GF_MPD_Period *period, GF_MPD_SegmentTimeline *timeline, u32 timescale, u32 *nb_segments, Double *max_seg_duration)
1200 {
1201 	u32 i, count;
1202 	u64 period_duration, start_time, dur;
1203 	if (period->duration) {
1204 		period_duration = period->duration;
1205 	} else {
1206 		period_duration = mpd->media_presentation_duration - period->start;
1207 	}
1208 	period_duration *= timescale;
1209 	period_duration /= 1000;
1210 
1211 	*nb_segments = 0;
1212 	if (max_seg_duration) *max_seg_duration = 0;
1213 	start_time = 0;
1214 	dur = 0;
1215 	count = gf_list_count(timeline->entries);
1216 	for (i=0; i<count; i++) {
1217 		GF_MPD_SegmentTimelineEntry *ent = gf_list_get(timeline->entries, i);
1218 		if ((s32)ent->repeat_count >=0) {
1219 			*nb_segments += 1 + ent->repeat_count;
1220 			if (ent->start_time) {
1221 				start_time = ent->start_time;
1222 				dur = (1 + ent->repeat_count);
1223 			} else {
1224 				dur += (1 + ent->repeat_count);
1225 			}
1226 			dur	*= ent->duration;
1227 		} else {
1228 			u32 nb_seg = 0;
1229 			if (i+1<count) {
1230 				GF_MPD_SegmentTimelineEntry *ent2 = gf_list_get(timeline->entries, i+1);
1231 				if (ent2->start_time>0) {
1232 					nb_seg = (u32) ( (ent2->start_time - start_time - dur) / ent->duration);
1233 					dur += ((u64)nb_seg) * ent->duration;
1234 				}
1235 			}
1236 			if (!nb_seg) {
1237 				nb_seg = (u32) ( (period_duration - start_time) / ent->duration );
1238 				dur += ((u64)nb_seg) * ent->duration;
1239 			}
1240 			*nb_segments += nb_seg;
1241 		}
1242 		if (max_seg_duration && (*max_seg_duration < ent->duration)) *max_seg_duration = ent->duration;
1243 	}
1244 }
1245 
gf_dash_get_segment_duration(GF_MPD_Representation * rep,GF_MPD_AdaptationSet * set,GF_MPD_Period * period,GF_MPD * mpd,u32 * nb_segments,Double * max_seg_duration)1246 static void gf_dash_get_segment_duration(GF_MPD_Representation *rep, GF_MPD_AdaptationSet *set, GF_MPD_Period *period, GF_MPD *mpd, u32 *nb_segments, Double *max_seg_duration)
1247 {
1248 	Double mediaDuration;
1249 	Bool single_segment = GF_FALSE;
1250 	u32 timescale;
1251 	u64 duration;
1252 	GF_MPD_SegmentTimeline *timeline = NULL;
1253 	*nb_segments = timescale = 0;
1254 	duration = 0;
1255 
1256 	if (rep->segment_list || set->segment_list || period->segment_list) {
1257 		GF_List *segments = NULL;
1258 		if (period->segment_list) {
1259 			if (period->segment_list->duration) duration = period->segment_list->duration;
1260 			if (period->segment_list->timescale) timescale = period->segment_list->timescale;
1261 			if (period->segment_list->segment_URLs) segments = period->segment_list->segment_URLs;
1262 			if (period->segment_list->segment_timeline) timeline = period->segment_list->segment_timeline;
1263 		}
1264 		if (set->segment_list) {
1265 			if (set->segment_list->duration) duration = set->segment_list->duration;
1266 			if (set->segment_list->timescale) timescale = set->segment_list->timescale;
1267 			if (set->segment_list->segment_URLs) segments = set->segment_list->segment_URLs;
1268 			if (set->segment_list->segment_timeline) timeline = set->segment_list->segment_timeline;
1269 		}
1270 		if (rep->segment_list) {
1271 			if (rep->segment_list->duration) duration = rep->segment_list->duration;
1272 			if (rep->segment_list->timescale) timescale = rep->segment_list->timescale;
1273 			if (rep->segment_list->segment_URLs) segments = rep->segment_list->segment_URLs;
1274 			if (rep->segment_list->segment_timeline) timeline = rep->segment_list->segment_timeline;
1275 		}
1276 		if (!timescale) timescale=1;
1277 
1278 		if (timeline) {
1279 			gf_dash_get_timeline_duration(mpd, period, timeline, timescale, nb_segments, max_seg_duration);
1280 			if (max_seg_duration) *max_seg_duration /= timescale;
1281 		} else {
1282 			if (segments)
1283 				*nb_segments = gf_list_count(segments);
1284 			if (max_seg_duration) {
1285 				*max_seg_duration = (Double) duration;
1286 				*max_seg_duration /= timescale;
1287 			}
1288 		}
1289 		return;
1290 	}
1291 
1292 	/*single segment*/
1293 	if (rep->segment_base || set->segment_base || period->segment_base) {
1294 		*max_seg_duration = (Double)mpd->media_presentation_duration;
1295 		*max_seg_duration /= 1000;
1296 		*nb_segments = 1;
1297 		return;
1298 	}
1299 
1300 	single_segment = GF_TRUE;
1301 	if (period->segment_template) {
1302 		single_segment = GF_FALSE;
1303 		if (period->segment_template->duration) duration = period->segment_template->duration;
1304 		if (period->segment_template->timescale) timescale = period->segment_template->timescale;
1305 		if (period->segment_template->segment_timeline) timeline = period->segment_template->segment_timeline;
1306 	}
1307 	if (set->segment_template) {
1308 		single_segment = GF_FALSE;
1309 		if (set->segment_template->duration) duration = set->segment_template->duration;
1310 		if (set->segment_template->timescale) timescale = set->segment_template->timescale;
1311 		if (set->segment_template->segment_timeline) timeline = set->segment_template->segment_timeline;
1312 	}
1313 	if (rep->segment_template) {
1314 		single_segment = GF_FALSE;
1315 		if (rep->segment_template->duration) duration = rep->segment_template->duration;
1316 		if (rep->segment_template->timescale) timescale = rep->segment_template->timescale;
1317 		if (rep->segment_template->segment_timeline) timeline = rep->segment_template->segment_timeline;
1318 	}
1319 	if (!timescale) timescale=1;
1320 
1321 	/*if no SegmentXXX is found, this is a single segment representation (onDemand profile)*/
1322 	if (single_segment) {
1323 		*max_seg_duration = (Double)mpd->media_presentation_duration;
1324 		*max_seg_duration /= 1000;
1325 		*nb_segments = 1;
1326 		return;
1327 	}
1328 
1329 	if (timeline) {
1330 		gf_dash_get_timeline_duration(mpd, period, timeline, timescale, nb_segments, max_seg_duration);
1331 		if (max_seg_duration) *max_seg_duration /= timescale;
1332 	} else {
1333 		if (max_seg_duration) {
1334 			*max_seg_duration = (Double) duration;
1335 			*max_seg_duration /= timescale;
1336 		}
1337 		mediaDuration = (Double)period->duration;
1338 		if (!mediaDuration && mpd->media_presentation_duration) {
1339 			u32 i, count = gf_list_count(mpd->periods);
1340 			Double start = 0;
1341 			for (i=0; i<count; i++) {
1342 				GF_MPD_Period *ap = gf_list_get(mpd->periods, i);
1343 				if (ap==period) break;
1344 				if (ap->start) start = (Double)ap->start;
1345 				start += ap->duration;
1346 			}
1347 			mediaDuration = mpd->media_presentation_duration - start;
1348 		}
1349 		if (mediaDuration && duration) {
1350 			Double nb_seg = (Double) mediaDuration;
1351 			/*duration is given in ms*/
1352 			nb_seg /= 1000;
1353 			nb_seg *= timescale;
1354 			nb_seg /= duration;
1355 			*nb_segments = (u32) nb_seg;
1356 			if (*nb_segments < nb_seg) (*nb_segments)++;
1357 		}
1358 	}
1359 }
1360 
1361 
gf_dash_get_segment_start_time_with_timescale(GF_DASH_Group * group,u64 * segment_duration,u32 * scale)1362 static u64 gf_dash_get_segment_start_time_with_timescale(GF_DASH_Group *group, u64 *segment_duration, u32 *scale)
1363 {
1364 	GF_MPD_Representation *rep = gf_list_get(group->adaptation_set->representations, group->active_rep_index);
1365 	GF_MPD_AdaptationSet *set = group->adaptation_set;
1366 	GF_MPD_Period *period = group->period;
1367 	s32 segment_index = group->download_segment_index;
1368 	u64 start_time = 0;
1369 
1370 	gf_mpd_get_segment_start_time_with_timescale(segment_index,
1371 		period, set, rep,
1372 		&start_time, segment_duration, scale);
1373 
1374 	return start_time;
1375 }
1376 
gf_dash_get_segment_start_time(GF_DASH_Group * group,Double * segment_duration)1377 static Double gf_dash_get_segment_start_time(GF_DASH_Group *group, Double *segment_duration)
1378 {
1379 	u64 start = 0;
1380 	u64 dur = 0;
1381 	u32 scale = 1000;
1382 
1383 	start = gf_dash_get_segment_start_time_with_timescale(group, &dur, &scale);
1384 	if (segment_duration) {
1385 		*segment_duration = (Double) dur;
1386 		*segment_duration /= scale;
1387 	}
1388 	return ((Double)start)/scale;
1389 }
1390 
gf_dash_get_segment_availability_start_time(GF_MPD * mpd,GF_DASH_Group * group,u32 segment_index,u32 * seg_dur_ms)1391 static u64 gf_dash_get_segment_availability_start_time(GF_MPD *mpd, GF_DASH_Group *group, u32 segment_index, u32 *seg_dur_ms)
1392 {
1393 	Double seg_ast, seg_dur=0.0;
1394 	seg_ast = gf_dash_get_segment_start_time(group, &seg_dur);
1395 	if (seg_dur_ms) *seg_dur_ms = (u32) (seg_dur * 1000);
1396 
1397 	seg_ast += seg_dur;
1398 	seg_ast *= 1000;
1399 	seg_ast += group->period->start + group->ast_at_init;
1400  	return (u64) seg_ast;
1401 }
1402 
gf_dash_get_index_in_timeline(GF_MPD_SegmentTimeline * timeline,u64 segment_start,u64 start_timescale,u64 timescale)1403 static u32 gf_dash_get_index_in_timeline(GF_MPD_SegmentTimeline *timeline, u64 segment_start, u64 start_timescale, u64 timescale)
1404 {
1405 	u64 start_time = 0;
1406 	u32 idx = 0;
1407 	u32 i, count, repeat;
1408 	count = gf_list_count(timeline->entries);
1409 	for (i=0; i<count; i++) {
1410 		GF_MPD_SegmentTimelineEntry *ent = gf_list_get(timeline->entries, i);
1411 
1412 		if (!i || ent->start_time) start_time = ent->start_time;
1413 
1414 		repeat = ent->repeat_count+1;
1415 		while (repeat) {
1416 			if (start_timescale==timescale) {
1417 				if (start_time == segment_start ) return idx;
1418 				if (start_time > segment_start) {
1419 					GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] Warning: segment timeline entry start "LLU" greater than segment start "LLU", using current entry\n", start_time, segment_start));
1420 					return idx;
1421 				}
1422 			} else {
1423 				if (start_time*start_timescale == segment_start * timescale) return idx;
1424 				if (start_time*start_timescale > segment_start * timescale) {
1425 					GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] Warning: segment timeline entry start "LLU" greater than segment start "LLU", using current entry\n", start_time, segment_start));
1426 					return idx;
1427 				}
1428 			}
1429 			start_time+=ent->duration;
1430 			repeat--;
1431 			idx++;
1432 		}
1433 	}
1434 	//end of list in regular case: segment was the last one of the previous list and no changes happend
1435 	if (start_timescale==timescale) {
1436 		if (start_time == segment_start ) return idx;
1437 	} else {
1438 		if (start_time*start_timescale == segment_start * timescale) return idx;
1439 	}
1440 
1441 	GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Error: could not find previous segment start in current timeline ! seeking to end of timeline\n"));
1442 	return idx;
1443 }
1444 
1445 
gf_dash_merge_segment_timeline(GF_DASH_Group * group,GF_DashClient * dash,GF_MPD_SegmentList * old_list,GF_MPD_SegmentTemplate * old_template,GF_MPD_SegmentList * new_list,GF_MPD_SegmentTemplate * new_template,Double min_start_time)1446 static GF_Err gf_dash_merge_segment_timeline(GF_DASH_Group *group, GF_DashClient *dash, GF_MPD_SegmentList *old_list, GF_MPD_SegmentTemplate *old_template, GF_MPD_SegmentList *new_list, GF_MPD_SegmentTemplate *new_template, Double min_start_time)
1447 {
1448 	GF_MPD_SegmentTimeline *old_timeline, *new_timeline;
1449 	u32 i, idx, timescale, nb_new_segs;
1450 	GF_MPD_SegmentTimelineEntry *ent;
1451 
1452 	old_timeline = new_timeline = NULL;
1453 	if (old_list && old_list->segment_timeline) {
1454 		if (!new_list || !new_list->segment_timeline) {
1455 			GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Error - cannot update playlist: segment timeline not present in new MPD segmentList\n"));
1456 			return GF_NON_COMPLIANT_BITSTREAM;
1457 		}
1458 		old_timeline = old_list->segment_timeline;
1459 		new_timeline = new_list->segment_timeline;
1460 		timescale = new_list->timescale;
1461 	} else if (old_template && old_template->segment_timeline) {
1462 		if (!new_template || !new_template->segment_timeline) {
1463 			GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Error - cannot update playlist: segment timeline not present in new MPD segmentTemplate\n"));
1464 			return GF_NON_COMPLIANT_BITSTREAM;
1465 		}
1466 		old_timeline = old_template->segment_timeline;
1467 		new_timeline = new_template->segment_timeline;
1468 		timescale = new_template->timescale;
1469 	}
1470 	if (!old_timeline && !new_timeline) return GF_OK;
1471 
1472 	if (group) {
1473 		group->current_start_time = gf_dash_get_segment_start_time_with_timescale(group, NULL, &group->current_timescale);
1474 	} else {
1475 		for (i=0; i<gf_list_count(dash->groups); i++) {
1476 			GF_DASH_Group *a_group = gf_list_get(dash->groups, i);
1477 			a_group->current_start_time = gf_dash_get_segment_start_time_with_timescale(a_group, NULL, &a_group->current_timescale);
1478 		}
1479 	}
1480 
1481 	nb_new_segs = 0;
1482 	idx=0;
1483 	while ((ent = gf_list_enum(new_timeline->entries, &idx))) {
1484 		nb_new_segs += 1 + ent->repeat_count;
1485 	}
1486 
1487 	if (group) {
1488 #ifndef GPAC_DISABLE_LOG
1489 		u32 prev_idx = group->download_segment_index;
1490 #endif
1491 		group->nb_segments_in_rep = nb_new_segs;
1492 		group->download_segment_index = gf_dash_get_index_in_timeline(new_timeline, group->current_start_time, group->current_timescale, timescale ? timescale : group->current_timescale);
1493 
1494 		GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] Updated SegmentTimeline: New segment number %d - old %d - start time "LLD"\n", group->download_segment_index , prev_idx, group->current_start_time));
1495 	} else {
1496 		for (i=0; i<gf_list_count(dash->groups); i++) {
1497 			GF_DASH_Group *a_group = gf_list_get(dash->groups, i);
1498 #ifndef GPAC_DISABLE_LOG
1499 			u32 prev_idx = a_group->download_segment_index;
1500 #endif
1501 			a_group->nb_segments_in_rep = nb_new_segs;
1502 			a_group->download_segment_index = gf_dash_get_index_in_timeline(new_timeline, a_group->current_start_time, a_group->current_timescale, timescale ? timescale : a_group->current_timescale);
1503 
1504 			GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] Updated SegmentTimeline: New segment number %d - old %d - start time "LLD"\n", a_group->download_segment_index , prev_idx, a_group->current_start_time));
1505 		}
1506 	}
1507 
1508 
1509 #ifndef GPAC_DISABLE_LOG
1510 	if (gf_log_tool_level_on(GF_LOG_DASH, GF_LOG_INFO) ) {
1511 		GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] New SegmentTimeline: \n"));
1512 		for (idx=0; idx<gf_list_count(new_timeline->entries); idx++) {
1513 			ent = gf_list_get(new_timeline->entries, idx);
1514 			GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("\tt="LLU" d=%d r=%d\n", ent->start_time, ent->duration, ent->repeat_count));
1515 		}
1516 	}
1517 #endif
1518 
1519 	return GF_OK;
1520 }
1521 
gf_dash_purge_segment_timeline(GF_DASH_Group * group,Double min_start_time)1522 static u32 gf_dash_purge_segment_timeline(GF_DASH_Group *group, Double min_start_time)
1523 {
1524 	u32 nb_removed, time_scale;
1525 	u64 start_time, min_start, duration;
1526 	GF_MPD_SegmentTimeline *timeline=NULL;
1527 	GF_MPD_Representation *rep = gf_list_get(group->adaptation_set->representations, group->active_rep_index);
1528 
1529 	if (!min_start_time) return 0;
1530 	gf_mpd_resolve_segment_duration(rep, group->adaptation_set, group->period, &duration, &time_scale, NULL, &timeline);
1531 	if (!timeline) return 0;
1532 
1533 	min_start = (u64) (min_start_time*time_scale);
1534 	start_time = 0;
1535 	nb_removed=0;
1536 	while (1) {
1537 		GF_MPD_SegmentTimelineEntry *ent = gf_list_get(timeline->entries, 0);
1538 		if (!ent) break;
1539 		if (ent->start_time) start_time = ent->start_time;
1540 
1541 		while (start_time + ent->duration < min_start) {
1542 			if (! ent->repeat_count) break;
1543 			ent->repeat_count--;
1544 			nb_removed++;
1545 			start_time += ent->duration;
1546 		}
1547 		/*this entry is in our range, keep it and make sure it has a start time*/
1548 		if (start_time + ent->duration >= min_start) {
1549 			if (!ent->start_time) ent->start_time = start_time;
1550 			break;
1551 		}
1552 		start_time += ent->duration;
1553 		gf_list_rem(timeline->entries, 0);
1554 		gf_free(ent);
1555 		nb_removed++;
1556 	}
1557 	if (nb_removed) {
1558 		GF_MPD_SegmentList *segment_list;
1559 		/*update next download index*/
1560 		group->download_segment_index -= nb_removed;
1561 		assert(group->nb_segments_in_rep >= nb_removed);
1562 		group->nb_segments_in_rep -= nb_removed;
1563 		/*clean segmentList*/
1564 		segment_list = NULL;
1565 		if (group->period && group->period->segment_list) segment_list = group->period->segment_list;
1566 		if (group->adaptation_set && group->adaptation_set->segment_list) segment_list = group->adaptation_set->segment_list;
1567 		if (rep && rep->segment_list) segment_list = rep->segment_list;
1568 
1569 		if (segment_list) {
1570 			u32 i = nb_removed;
1571 			while (i) {
1572 				GF_MPD_SegmentURL *seg_url = gf_list_get(segment_list->segment_URLs, 0);
1573 				gf_list_rem(segment_list->segment_URLs, 0);
1574 				gf_mpd_segment_url_free(seg_url);
1575 				i--;
1576 			}
1577 		}
1578 		group->nb_segments_purged += nb_removed;
1579 	}
1580 	return nb_removed;
1581 }
1582 
gf_dash_solve_representation_xlink(GF_DashClient * dash,GF_MPD_Representation * rep)1583 static GF_Err gf_dash_solve_representation_xlink(GF_DashClient *dash, GF_MPD_Representation *rep)
1584 {
1585 	u32 count, i;
1586 	GF_Err e;
1587 	Bool is_local=GF_FALSE;
1588 	const char *local_url;
1589 	char *url;
1590 	GF_DOMParser *parser;
1591 	if (!rep->segment_list->xlink_href) return GF_BAD_PARAM;
1592 
1593 	GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] Resolving Representation SegmentList XLINK %s\n", rep->segment_list->xlink_href));
1594 
1595 	//SPEC: If this value is present, the element containing the xlink:href attribute and all @xlink attributes included in the element containing @xlink:href shall be removed at the time when the resolution is due.
1596 	if (!strcmp(rep->segment_list->xlink_href, "urn:mpeg:dash:resolve-to-zero:2013")) {
1597 		gf_mpd_delete_segment_list(rep->segment_list);
1598 		rep->segment_list = NULL;
1599 		return GF_OK;
1600 	}
1601 
1602 	//xlink relative to our MPD base URL
1603 	url = gf_url_concatenate(dash->base_url, rep->segment_list->xlink_href);
1604 
1605 	if (!strstr(url, "://") || !strnicmp(url, "file://", 7) ) {
1606 		local_url = url;
1607 		is_local=GF_TRUE;
1608 		e = GF_OK;
1609 	} else {
1610 		/*use non-persistent connection for MPD*/
1611 		e = gf_dash_download_resource(dash, &(dash->mpd_dnload), url ? url : rep->segment_list->xlink_href, 0, 0, 0, NULL);
1612 		gf_free(url);
1613 	}
1614 
1615 	if (e) {
1616 		GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Error - cannot download Representation SegmentList XLINK %s: error %s\n", rep->segment_list->xlink_href, gf_error_to_string(e)));
1617 		gf_free(rep->segment_list->xlink_href);
1618 		rep->segment_list->xlink_href = NULL;
1619 		return e;
1620 	}
1621 
1622 	if (!is_local) {
1623 		/*in case the session has been restarted, local_url may have been destroyed - get it back*/
1624 		local_url = dash->dash_io->get_cache_name(dash->dash_io, dash->mpd_dnload);
1625 	}
1626 
1627 	parser = gf_xml_dom_new();
1628 	e = gf_xml_dom_parse(parser, local_url, NULL, NULL);
1629 	if (is_local) gf_free(url);
1630 
1631 	if (e != GF_OK) {
1632 		gf_xml_dom_del(parser);
1633 		GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Error - cannot parse Representation SegmentList XLINK: error in XML parsing %s\n", gf_error_to_string(e)));
1634 		gf_free(rep->segment_list->xlink_href);
1635 		rep->segment_list->xlink_href = NULL;
1636 		return GF_NON_COMPLIANT_BITSTREAM;
1637 	}
1638 
1639 	count = gf_xml_dom_get_root_nodes_count(parser);
1640 	if (count > 1) {
1641 		GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] XLINK %s has more than one segment list - ignoring it\n", rep->segment_list->xlink_href));
1642 		gf_mpd_delete_segment_list(rep->segment_list);
1643 		rep->segment_list = NULL;
1644 		return GF_NON_COMPLIANT_BITSTREAM;
1645 	}
1646 	for (i=0; i<count; i++) {
1647 		GF_XMLNode *root = gf_xml_dom_get_root_idx(parser, i);
1648 		if (!strcmp(root->name, "SegmentList")) {
1649 			GF_MPD_SegmentList *new_seg_list = gf_mpd_solve_segment_list_xlink(dash->mpd, root);
1650 			//forbiden
1651 			if (new_seg_list && new_seg_list->xlink_href) {
1652 				if (new_seg_list->xlink_actuate_on_load) {
1653 					GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] XLINK %s references to remote element entities that contain another @xlink:href attribute with xlink:actuate set to onLoad - forbiden\n", rep->segment_list->xlink_href));
1654 					gf_mpd_delete_segment_list(new_seg_list);
1655 					new_seg_list = NULL;
1656 				} else {
1657 					new_seg_list->consecutive_xlink_count = rep->segment_list->consecutive_xlink_count + 1;
1658 				}
1659 			}
1660 			//replace current segment list by the one from remote element entity (located by xlink:href)
1661 			gf_mpd_delete_segment_list(rep->segment_list);
1662 			rep->segment_list = new_seg_list;
1663 		}
1664 		else
1665 			GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] XML node %s is not a representation segmentlist - ignoring it\n", root->name));
1666 	}
1667 	return GF_OK;
1668 }
1669 
dash_purge_xlink(GF_MPD * new_mpd)1670 static void dash_purge_xlink(GF_MPD *new_mpd)
1671 {
1672 	u32 i, count = gf_list_count(new_mpd->periods);
1673 	for (i=0; i<count; i++) {
1674 		GF_MPD_Period *period = gf_list_get(new_mpd->periods, i);
1675 		if (!gf_list_count(period->adaptation_sets)) continue;
1676 		if (!period->xlink_href) continue;
1677 		gf_free(period->xlink_href);
1678 		period->xlink_href = NULL;
1679 	}
1680 }
1681 
gf_dash_mark_group_done(GF_DASH_Group * group)1682 static void gf_dash_mark_group_done(GF_DASH_Group *group)
1683 {
1684 	GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] AS#%d group is done\n", 1+gf_list_find(group->period->adaptation_sets, group->adaptation_set) ));
1685 	group->done = GF_TRUE;
1686 }
1687 
gf_dash_update_manifest(GF_DashClient * dash)1688 static GF_Err gf_dash_update_manifest(GF_DashClient *dash)
1689 {
1690 	GF_Err e;
1691 	Bool force_timeline_setup = GF_FALSE;
1692 	u32 group_idx, rep_idx, i, j;
1693 	u64 fetch_time=0;
1694 	GF_DOMParser *mpd_parser;
1695 	u8 signature[GF_SHA1_DIGEST_SIZE];
1696 	GF_MPD_Period *period, *new_period;
1697 	const char *local_url;
1698 	char mime[128];
1699 	char * purl;
1700 	Double timeline_start_time;
1701 	GF_MPD *new_mpd=NULL;
1702 	Bool fetch_only = GF_FALSE;
1703 
1704 	if (!dash->mpd_dnload) {
1705 		local_url = purl = NULL;
1706 		if (!gf_list_count(dash->mpd->locations)) {
1707 			FILE *t = gf_fopen(dash->base_url, "rt");
1708 			if (t) {
1709 				local_url = dash->base_url;
1710 				gf_fclose(t);
1711 			}
1712 			if (!local_url) {
1713 				/*we will no longer attempt to update the MPD ...*/
1714 				dash->mpd->minimum_update_period = 0;
1715 				GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Error - cannot update playlist: no HTTP source for MPD could be found\n"));
1716 				return GF_BAD_PARAM;
1717 			}
1718 		}
1719 		if (!local_url) {
1720 			purl = gf_strdup(gf_list_get(dash->mpd->locations, 0));
1721 
1722 			/*if no absolute URL, use <Location> to get MPD in case baseURL is relative...*/
1723 			if (!strstr(dash->base_url, "://")) {
1724 				gf_free(dash->base_url);
1725 				dash->base_url = gf_strdup(purl);
1726 			}
1727 			fetch_only = 1;
1728 		}
1729 	} else {
1730 		local_url = dash->dash_io->get_cache_name(dash->dash_io, dash->mpd_dnload);
1731 		if (local_url) {
1732 			gf_file_delete(local_url);
1733 		}
1734 		//use the redirected url stored in base URL - DO NOT USE the redirected URL of the session since
1735 		//the session may have been reused for period XLINK dowload.
1736 		purl = gf_strdup( dash->base_url );
1737 	}
1738 
1739 	/*if update location is specified, update - spec does not say whether location is a relative or absoute URL*/
1740 	if (gf_list_count(dash->mpd->locations)) {
1741 		char *update_loc = gf_list_get(dash->mpd->locations, 0);
1742 		char *update_url = gf_url_concatenate(purl, update_loc);
1743 		if (update_url) {
1744 			gf_free(purl);
1745 			purl = update_url;
1746 		}
1747 	}
1748 
1749 	GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Updating Playlist %s...\n", purl ? purl : local_url));
1750 	if (purl) {
1751 		const char *mime_type;
1752 		/*use non-persistent connection for MPD*/
1753 		e = gf_dash_download_resource(dash, &(dash->mpd_dnload), purl, 0, 0, 0, NULL);
1754 		if (e!=GF_OK) {
1755 			if (!dash->in_error) {
1756 				GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Error - cannot update playlist: download problem %s for MPD file\n", gf_error_to_string(e)));
1757 				dash->in_error = GF_TRUE;
1758 			}
1759 			gf_free(purl);
1760 			//try to refetch MPD every second
1761 			dash->last_update_time+=1000;
1762 			return e;
1763 		} else {
1764 			GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Playlist %s updated with success\n", purl));
1765 		}
1766 
1767 		mime_type = dash->dash_io->get_mime(dash->dash_io, dash->mpd_dnload) ;
1768 		strcpy(mime, mime_type ? mime_type : "");
1769 		strlwr(mime);
1770 
1771 		/*in case the session has been restarted, local_url may have been destroyed - get it back*/
1772 		local_url = dash->dash_io->get_cache_name(dash->dash_io, dash->mpd_dnload);
1773 
1774 		/* Some servers, for instance http://tv.freebox.fr, serve m3u8 as text/plain */
1775 		if (gf_dash_is_m3u8_mime(purl, mime) || strstr(purl, ".m3u8")) {
1776 			new_mpd = gf_mpd_new();
1777 			e = gf_m3u8_to_mpd(local_url, purl, NULL, dash->reload_count, dash->mimeTypeForM3U8Segments, 0, M3U8_TO_MPD_USE_TEMPLATE, M3U8_TO_MPD_USE_SEGTIMELINE, &dash->getter, new_mpd, GF_FALSE, dash->keep_files);
1778 			if (e) {
1779 				GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Error - cannot update playlist: error in MPD creation %s\n", gf_error_to_string(e)));
1780 				gf_mpd_del(new_mpd);
1781 				return GF_NON_COMPLIANT_BITSTREAM;
1782 			}
1783 		} else if (strlen(mime) && !gf_dash_is_dash_mime(mime)) {
1784 			GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] mime '%s' should be m3u8 or mpd\n", mime));
1785 		}
1786 
1787 		gf_free(purl);
1788 
1789 		purl = (char *) dash->dash_io->get_url(dash->dash_io, dash->mpd_dnload) ;
1790 
1791 		/*if relocated, reassign MPD base URL*/
1792 		if (strcmp(purl, dash->base_url)) {
1793 			gf_free(dash->base_url);
1794 			dash->base_url = gf_strdup(purl);
1795 
1796 		}
1797 
1798 		purl = NULL;
1799 	}
1800 
1801 	fetch_time = dash_get_fetch_time(dash);
1802 
1803 	// parse the mpd file for filling the GF_MPD structure. Note: for m3u8, MPD has been fetched above
1804 	if (!new_mpd) {
1805 		u32 res = gf_dash_check_mpd_root_type(local_url);
1806 		if ((res==1) && dash->is_smooth) res = 0;
1807 		else if ((res==2) && !dash->is_smooth) res = 0;
1808 
1809 		if (!res) {
1810 			GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Error - cannot update playlist: MPD file type is not correct %s\n", local_url));
1811 			return GF_NON_COMPLIANT_BITSTREAM;
1812 		}
1813 
1814 		if (gf_sha1_file( local_url, signature) != GF_OK) {
1815 			GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] : cannot SHA1 file %s\n", local_url));
1816 			return GF_IO_ERR;
1817 		}
1818 
1819 		if (!dash->in_error && ! memcmp( signature, dash->lastMPDSignature, GF_SHA1_DIGEST_SIZE)) {
1820 
1821 			dash->reload_count++;
1822 			GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] MPD file did not change for %d consecutive reloads\n", dash->reload_count));
1823 			/*if the MPD did not change, we should refresh "soon" but cannot wait a full refresh cycle in case of
1824 			low latencies as we could miss a segment*/
1825 
1826 			if (dash->is_m3u8) {
1827 				dash->last_update_time += dash->mpd->minimum_update_period/2;
1828 			} else {
1829 				dash->last_update_time = gf_sys_clock();
1830 			}
1831 
1832 			dash->mpd_fetch_time = fetch_time;
1833 			return GF_OK;
1834 		}
1835 
1836 		force_timeline_setup = dash->in_error;
1837 		dash->in_error = GF_FALSE;
1838 		dash->reload_count = 0;
1839 		memcpy(dash->lastMPDSignature, signature, GF_SHA1_DIGEST_SIZE);
1840 
1841 		/* It means we have to reparse the file ... */
1842 		/* parse the MPD */
1843 		mpd_parser = gf_xml_dom_new();
1844 		e = gf_xml_dom_parse(mpd_parser, local_url, NULL, NULL);
1845 		if (e != GF_OK) {
1846 			gf_xml_dom_del(mpd_parser);
1847 			GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Error - cannot update playlist: error in XML parsing %s\n", gf_error_to_string(e)));
1848 			return GF_NON_COMPLIANT_BITSTREAM;
1849 		}
1850 		new_mpd = gf_mpd_new();
1851 		e = gf_mpd_init_from_dom(gf_xml_dom_get_root(mpd_parser), new_mpd, purl);
1852 		gf_xml_dom_del(mpd_parser);
1853 		if (e) {
1854 			GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Error - cannot update playlist: error in MPD creation %s\n", gf_error_to_string(e)));
1855 			gf_mpd_del(new_mpd);
1856 			return GF_NON_COMPLIANT_BITSTREAM;
1857 		}
1858 		if (dash->ignore_xlink)
1859 			dash_purge_xlink(new_mpd);
1860 
1861 		if (!e && dash->split_adaptation_set)
1862 			gf_mpd_split_adaptation_sets(new_mpd);
1863 
1864 	}
1865 
1866 	assert(new_mpd);
1867 
1868 	period = gf_list_get(dash->mpd->periods, dash->active_period_index);
1869 	if (fetch_only  && !period) goto exit;
1870 
1871 #ifndef GPAC_DISABLE_LOG
1872 	GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] Updated manifest:\n"));
1873 	for (i=0; i<gf_list_count(new_mpd->periods); i++) {
1874 		GF_MPD_Period *ap = gf_list_get(new_mpd->periods, i);
1875 		GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("\tP#%d: start "LLU" - duration " LLU" - xlink %s\n", i+1, ap->start, ap->duration, ap->xlink_href ? ap->xlink_href : "none"));
1876 	}
1877 #endif
1878 
1879 	//if current period was a remote period, do a pass on the new manifest periods, check for xlink
1880 	if (period->origin_base_url) {
1881 restart_period_check:
1882 		for (i=0; i<gf_list_count(new_mpd->periods); i++) {
1883 			new_period = gf_list_get(new_mpd->periods, i);
1884 			if (!new_period->xlink_href) continue;
1885 
1886 			if ((new_period->start<=period->start) &&
1887 				( (new_period->start+new_period->duration >= period->start + period->duration) || (!new_period->duration))
1888 			) {
1889 				const char *base_url;
1890 				u32 insert_idx;
1891 				if (period->type == GF_MPD_TYPE_DYNAMIC) {
1892 					gf_dash_solve_period_xlink(dash, new_mpd->periods, i);
1893 					goto restart_period_check;
1894 				}
1895 				//this is a static period xlink, no need to further update the mpd, jsut swap the old periods in the new MPD
1896 
1897 				base_url = period->origin_base_url;
1898 				if (!base_url) {
1899 					GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Error - found new Xlink period overlapping a non-xlink period in original manifest\n"));
1900 					gf_mpd_del(new_mpd);
1901 					return GF_NON_COMPLIANT_BITSTREAM;
1902 				}
1903 				insert_idx = i;
1904 				gf_list_rem(new_mpd->periods, i);
1905 				for (j=0; j<gf_list_count(dash->mpd->periods); j++) {
1906 					GF_MPD_Period *ap = gf_list_get(dash->mpd->periods, j);
1907 					if (!ap->origin_base_url) continue;
1908 					if (strcmp(ap->origin_base_url, base_url)) continue;
1909 					gf_list_rem(dash->mpd->periods, j);
1910 					j--;
1911 					gf_list_insert(new_mpd->periods, ap, insert_idx);
1912 					insert_idx++;
1913 				}
1914 				//update active period index in new list
1915 				dash->active_period_index = gf_list_find(new_mpd->periods, period);
1916 				assert((s32)dash->active_period_index >= 0);
1917 				//this will do the garbage collection
1918 				gf_list_add(dash->mpd->periods, new_period);
1919 				goto exit;
1920 			}
1921 			new_period=NULL;
1922 		}
1923 	}
1924 
1925 	new_period = NULL;
1926 	for (i=0; i<gf_list_count(new_mpd->periods); i++) {
1927 		new_period = gf_list_get(new_mpd->periods, i);
1928 		if (new_period->start==period->start) break;
1929 		new_period=NULL;
1930 	}
1931 
1932 	if (!new_period) {
1933 		if (dash->mpd->type == GF_MPD_TYPE_DYNAMIC) {
1934 			GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] current active period not found in MPD update - assuming end of active period and switching to first period in new MPD\n"));
1935 
1936 			//assume the old period is no longer valid
1937 			dash->active_period_index = 0;
1938 			dash->request_period_switch = GF_TRUE;
1939 			for (i=0; i<gf_list_count(dash->groups); i++) {
1940 				GF_DASH_Group *group = gf_list_get(dash->groups, i);
1941 				gf_dash_mark_group_done(group);
1942 				group->adaptation_set = NULL;
1943 			}
1944 			goto exit;
1945 		}
1946 		GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Error - cannot update playlist: missing period\n"));
1947 		gf_mpd_del(new_mpd);
1948 		return GF_NON_COMPLIANT_BITSTREAM;
1949 	}
1950 
1951 	dash->active_period_index = gf_list_find(new_mpd->periods, new_period);
1952 	assert((s32)dash->active_period_index >= 0);
1953 
1954 	if (gf_list_count(period->adaptation_sets) != gf_list_count(new_period->adaptation_sets)) {
1955 		GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Error - cannot update playlist: missing AdaptationSet\n"));
1956 		gf_mpd_del(new_mpd);
1957 		return GF_NON_COMPLIANT_BITSTREAM;
1958 	}
1959 
1960 	GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Updating playlist at UTC time "LLU" - availabilityStartTime "LLU"\n", fetch_time, new_mpd->availabilityStartTime));
1961 
1962 	timeline_start_time = 0;
1963 	/*if not infinity for timeShift, compute min media time before merge and adjust it*/
1964 	if (dash->mpd->time_shift_buffer_depth != (u32) -1) {
1965 		Double timeshift = dash->mpd->time_shift_buffer_depth;
1966 		timeshift /= 1000;
1967 
1968 		for (group_idx=0; group_idx<gf_list_count(dash->groups); group_idx++) {
1969 			GF_DASH_Group *group = gf_list_get(dash->groups, group_idx);
1970 			if (group->selection!=GF_DASH_GROUP_NOT_SELECTABLE) {
1971 				Double group_start = gf_dash_get_segment_start_time(group, NULL);
1972 				if (!group_idx || (timeline_start_time > group_start) ) timeline_start_time = group_start;
1973 			}
1974 		}
1975 		/*we can rewind our segments from timeshift*/
1976 		if (timeline_start_time > timeshift) timeline_start_time -= timeshift;
1977 		/*we can rewind all segments*/
1978 		else timeline_start_time = 0;
1979 	}
1980 
1981 	/*update segmentTimeline at Period level*/
1982 	e = gf_dash_merge_segment_timeline(NULL, dash, period->segment_list, period->segment_template, new_period->segment_list, new_period->segment_template, timeline_start_time);
1983 	if (e) {
1984 		gf_mpd_del(new_mpd);
1985 		return e;
1986 	}
1987 
1988 	for (group_idx=0; group_idx<gf_list_count(dash->groups); group_idx++) {
1989 		GF_MPD_AdaptationSet *set, *new_set;
1990 		u32 rep_i;
1991 		GF_DASH_Group *group = gf_list_get(dash->groups, group_idx);
1992 
1993 		/*update info even if the group is not selected !*/
1994 		if (group->selection==GF_DASH_GROUP_NOT_SELECTABLE)
1995 			continue;
1996 
1997 		set = group->adaptation_set;
1998 		new_set = gf_list_get(new_period->adaptation_sets, group_idx);
1999 
2000 		//sort by bandwidth and quality
2001 		for (rep_i = 1; rep_i < gf_list_count(new_set->representations); rep_i++) {
2002 			Bool swap=GF_FALSE;
2003 			GF_MPD_Representation *r2 = gf_list_get(new_set->representations, rep_i);
2004 			GF_MPD_Representation *r1 = gf_list_get(new_set->representations, rep_i-1);
2005 			if (r1->bandwidth > r2->bandwidth) {
2006 				swap=GF_TRUE;
2007 			} else if ((r1->bandwidth == r2->bandwidth) && (r1->quality_ranking<r2->quality_ranking)) {
2008 				swap=GF_TRUE;
2009 			}
2010 			if (swap) {
2011 				gf_list_rem(new_set->representations, rep_i);
2012 				gf_list_insert(new_set->representations, r2, rep_i-1);
2013 				rep_i=0;
2014 			}
2015 		}
2016 
2017 		if (gf_list_count(new_set->representations) != gf_list_count(group->adaptation_set->representations)) {
2018 			GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Error - cannot update playlist: missing representation in adaptation set\n"));
2019 			gf_mpd_del(new_mpd);
2020 			return GF_NON_COMPLIANT_BITSTREAM;
2021 		}
2022 
2023 		/*get all representations in both periods*/
2024 		for (rep_idx = 0; rep_idx <gf_list_count(group->adaptation_set->representations); rep_idx++) {
2025 			GF_List *segments, *new_segments;
2026 			GF_MPD_Representation *rep = gf_list_get(group->adaptation_set->representations, rep_idx);
2027 			GF_MPD_Representation *new_rep = gf_list_get(new_set->representations, rep_idx);
2028 
2029 			if (rep->segment_base || group->adaptation_set->segment_base || period->segment_base) {
2030 				if (!new_rep->segment_base && !new_set->segment_base && !new_period->segment_base) {
2031 					GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Error - cannot update playlist: representation does not use segment base as previous version\n"));
2032 					gf_mpd_del(new_mpd);
2033 					return GF_NON_COMPLIANT_BITSTREAM;
2034 				}
2035 				/*what else should we check ??*/
2036 
2037 				/*OK, this rep is fine*/
2038 			}
2039 
2040 			else if (rep->segment_template || group->adaptation_set->segment_template || period->segment_template) {
2041 				if (!new_rep->segment_template && !new_set->segment_template && !new_period->segment_template) {
2042 					GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Error - cannot update playlist: representation does not use segment template as previous version\n"));
2043 					gf_mpd_del(new_mpd);
2044 					return GF_NON_COMPLIANT_BITSTREAM;
2045 				}
2046 				//if no segment timeline, adjust current idx of the group if start number changes (not needed if SegmentTimeline, otherwise, we will look for the index with the same starttime in the timeline)
2047 				if ((period->segment_template && period->segment_template->segment_timeline)
2048 				        || (set->segment_template && set->segment_template->segment_timeline)
2049 				        || (rep->segment_template && rep->segment_template->segment_timeline)
2050 				   ) {
2051 				} else {
2052 					s32 sn_diff = 0;
2053 
2054 					if (period->segment_template && (period->segment_template->start_number != (u32) -1) ) sn_diff = period->segment_template->start_number;
2055 					else if (set->segment_template && (set->segment_template->start_number != (u32) -1) ) sn_diff = set->segment_template->start_number;
2056 					else if (rep->segment_template && (rep->segment_template->start_number != (u32) -1) ) sn_diff = rep->segment_template->start_number;
2057 
2058 					if (new_period->segment_template && (new_period->segment_template->start_number != (u32) -1) ) sn_diff -= (s32) new_period->segment_template->start_number;
2059 					else if (new_set->segment_template && (new_set->segment_template->start_number != (u32) -1) ) sn_diff -= (s32) new_set->segment_template->start_number;
2060 					else if (new_rep->segment_template && (new_rep->segment_template->start_number != (u32) -1) ) sn_diff -= (s32) new_rep->segment_template->start_number;
2061 
2062 					if (sn_diff != 0) {
2063 						GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] startNumber change for SegmentTemplate without SegmentTimeline - adjusting current segment index by %d\n", sn_diff));
2064 						group->download_segment_index += sn_diff;
2065 					}
2066 				}
2067 				/*OK, this rep is fine*/
2068 			}
2069 			else {
2070 				/*we're using segment list*/
2071 				assert(rep->segment_list || group->adaptation_set->segment_list || period->segment_list);
2072 
2073 				//if we have a xlink_href in segment_list, solve it
2074 				while (new_rep->segment_list->xlink_href && (group->active_rep_index==rep_idx)) {
2075 					u32 retry=10;
2076 					Bool is_static = GF_FALSE;
2077 					u64 dur = 0;
2078 
2079 					if (new_rep->segment_list->consecutive_xlink_count) {
2080 						GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] Resolving a XLINK pointed from another XLINK (%d consecutive XLINK in segment list)\n", new_rep->segment_list->consecutive_xlink_count));
2081 					}
2082 
2083 					while (retry) {
2084 						if (dash->is_m3u8) {
2085 							e = gf_m3u8_solve_representation_xlink(new_rep, &group->dash->getter, &is_static, &dur);
2086 						} else {
2087 							e = gf_dash_solve_representation_xlink(group->dash, new_rep);
2088 						}
2089 						if (e==GF_OK) break;
2090 						if (e==GF_NON_COMPLIANT_BITSTREAM) break;
2091 						if (e==GF_OUT_OF_MEM) break;
2092 						if (group->dash->dash_state != GF_DASH_STATE_RUNNING)
2093 							break;
2094 
2095 						gf_sleep(100);
2096 						retry --;
2097 					}
2098 
2099 					if (e==GF_OK) {
2100 						if (dash->is_m3u8 && is_static) {
2101 							GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[m3u8] MPD type changed from dynamic to static\n"));
2102 							group->dash->mpd->type = GF_MPD_TYPE_STATIC;
2103 							group->dash->mpd->media_presentation_duration = dur;
2104 							group->dash->mpd->minimum_update_period = 0;
2105 							group->period->duration = dur;
2106 						}
2107 					}
2108 				}
2109 
2110 
2111 				if (!new_rep->segment_list && !new_set->segment_list && !new_period->segment_list) {
2112 					GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Error - cannot update playlist: representation does not use segment list as previous version\n"));
2113 					gf_mpd_del(new_mpd);
2114 					return GF_NON_COMPLIANT_BITSTREAM;
2115 				}
2116 				/*get the segment list*/
2117 				segments = new_segments = NULL;
2118 				if (period->segment_list && period->segment_list->segment_URLs) segments = period->segment_list->segment_URLs;
2119 				if (set->segment_list && set->segment_list->segment_URLs) segments = set->segment_list->segment_URLs;
2120 				if (rep->segment_list && rep->segment_list->segment_URLs) segments = rep->segment_list->segment_URLs;
2121 
2122 				if (new_period->segment_list && new_period->segment_list->segment_URLs) new_segments = new_period->segment_list->segment_URLs;
2123 				if (new_set->segment_list && new_set->segment_list->segment_URLs) new_segments = new_set->segment_list->segment_URLs;
2124 				if (new_rep->segment_list && new_rep->segment_list->segment_URLs) new_segments = new_rep->segment_list->segment_URLs;
2125 
2126 
2127 				for (i=0; i<gf_list_count(new_segments); i++) {
2128 					GF_MPD_SegmentURL *new_seg = gf_list_get(new_segments, i);
2129 					Bool found = GF_FALSE;
2130 					for (j=0; j<gf_list_count(segments); j++) {
2131 						GF_MPD_SegmentURL *seg = gf_list_get(segments, j);
2132 						if (seg->media && new_seg->media && !strcmp(seg->media, new_seg->media)) {
2133 							found=1;
2134 							break;
2135 						}
2136 						if (seg->media_range && new_seg->media_range && (seg->media_range->start_range==new_seg->media_range->start_range) && (seg->media_range->end_range==new_seg->media_range->end_range) ) {
2137 							found=1;
2138 							break;
2139 						}
2140 					}
2141 					/*this is a new segment, merge it: we remove from new list and push to old one, before doing a final swap
2142 					this ensures that indexing in the segment_list is still correct after merging*/
2143 					if (!found) {
2144 						gf_list_rem(new_segments, i);
2145 						i--;
2146 						gf_list_add(segments, new_seg);
2147 						GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Representation #%d: Adding new segment %s\n", rep_idx+1, new_seg->media));
2148 					}
2149 				}
2150 				/*what else should we check ?*/
2151 
2152 				/*swap segment list content*/
2153 				gf_list_swap(new_segments, segments);
2154 
2155 				//HLS live: if a new time is set (active group only), we just switched betwwe qualities
2156 				//locate the segment with the same start time in the manifest, and purge previous ones
2157 				//it may happen that the manifest does still not contain the segment we are looking for, force an MPD update
2158 				if (group->hls_next_start_time && (group->active_rep_index==rep_idx)) {
2159 					u32 k;
2160 
2161 					for (k=0; k<gf_list_count(new_segments); k++) {
2162 						s64 diff;
2163 						GF_MPD_SegmentURL *segu = (GF_MPD_SegmentURL *) gf_list_get(new_segments, k);
2164 						diff = (s64) group->hls_next_start_time;
2165 						diff -= (s64) segu->hls_utc_start_time;
2166 						if (abs( (s32) diff)<200) {
2167 							group->download_segment_index = k;
2168 							group->hls_next_start_time=0;
2169 							break;
2170 						}
2171 						//purge old segments
2172 						if (segu->hls_utc_start_time < group->hls_next_start_time) {
2173 							gf_mpd_segment_url_free(segu);
2174 							gf_list_rem(new_segments, k);
2175 							k--;
2176 						}
2177 						if (segu->hls_utc_start_time > group->hls_next_start_time) {
2178 							group->download_segment_index = k;
2179 							GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] Waiting for HLS segment start "LLU" but found segment at "LLU" - missing segment ?\n", group->hls_next_start_time, segu->hls_utc_start_time));
2180 							group->hls_next_start_time=0;
2181 							break;
2182 						}
2183 					}
2184 					//not yet available
2185 					if (group->hls_next_start_time) {
2186 						GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Cannot find segment for given HLS start time "LLU" - forcing manifest update\n", group->hls_next_start_time));
2187 						dash->force_mpd_update=GF_TRUE;
2188 						//force sleep of half sec to avoid updating manifest too often - this will need refinement for low latency !!
2189 						gf_sleep(500);
2190 					}
2191 				}
2192 
2193 				/*current representation is the active one in the group - update the number of segments*/
2194 				if (group->active_rep_index==rep_idx) {
2195 					group->nb_segments_in_rep = gf_list_count(new_segments);
2196 				}
2197 			}
2198 
2199 			e = gf_dash_merge_segment_timeline(group, NULL, rep->segment_list, rep->segment_template, new_rep->segment_list, new_rep->segment_template, timeline_start_time);
2200 			if (e) {
2201 				gf_mpd_del(new_mpd);
2202 				return e;
2203 			}
2204 
2205 			//move redirections in representations base URLs (we could do that on AS as well )
2206 			if (rep->base_URLs && new_rep->base_URLs) {
2207 				u32 k;
2208 				for (i=0; i<gf_list_count(new_rep->base_URLs); i++) {
2209 					GF_MPD_BaseURL *n_url = gf_list_get(new_rep->base_URLs, i);
2210 					if (!n_url->URL) continue;
2211 
2212 					for (k=0; k<gf_list_count(rep->base_URLs); k++) {
2213 						GF_MPD_BaseURL *o_url = gf_list_get(rep->base_URLs, k);
2214 						if (o_url->URL && !strcmp(o_url->URL, n_url->URL)) {
2215 							n_url->redirection = o_url->redirection;
2216 							o_url->redirection = NULL;
2217 						}
2218 					}
2219 				}
2220 			}
2221 
2222 			/*what else should we check ??*/
2223 
2224 
2225 			/*switch all internal GPAC stuff*/
2226 			memcpy(&new_rep->playback, &rep->playback, sizeof(GF_DASH_RepresentationPlayback));
2227 			if (rep->playback.cached_init_segment_url) rep->playback.cached_init_segment_url = NULL;
2228 
2229 			if (!new_rep->mime_type) {
2230 				new_rep->mime_type = rep->mime_type;
2231 				rep->mime_type = NULL;
2232 			}
2233 		}
2234 
2235 		/*update segmentTimeline at AdaptationSet level before switching the set (old setup needed to compute current timing of each group) */
2236 		e = gf_dash_merge_segment_timeline(group, NULL, set->segment_list, set->segment_template, new_set->segment_list, new_set->segment_template, timeline_start_time);
2237 		if (e) {
2238 			gf_mpd_del(new_mpd);
2239 			return e;
2240 		}
2241 	}
2242 	//good to go, switch pointers
2243 	for (group_idx=0; group_idx<gf_list_count(dash->groups); group_idx++) {
2244 		Double seg_dur;
2245 		Bool reset_segment_count;
2246 		GF_DASH_Group *group = gf_list_get(dash->groups, group_idx);
2247 		/*update group/period to new period*/
2248 		j = gf_list_find(group->period->adaptation_sets, group->adaptation_set);
2249 		group->adaptation_set = gf_list_get(new_period->adaptation_sets, j);
2250 		group->period = new_period;
2251 		assert(group->adaptation_set);
2252 
2253 		j = gf_list_count(group->adaptation_set->representations);
2254 		assert(j);
2255 
2256 		/*now that all possible SegmentXXX have been updated, purge them if needed: all segments ending before timeline_start_time
2257 		will be removed from MPD*/
2258 		if (timeline_start_time) {
2259 			u32 nb_segments_removed = gf_dash_purge_segment_timeline(group, timeline_start_time);
2260 			if (nb_segments_removed) {
2261 				GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] AdaptationSet %d - removed %d segments from timeline (%d since start of the period)\n", group_idx+1, nb_segments_removed, group->nb_segments_purged));
2262 			}
2263 		}
2264 
2265 		if (force_timeline_setup) {
2266 			group->timeline_setup = 0;
2267 			group->start_number_at_last_ast = 0;
2268 			gf_dash_group_timeline_setup(new_mpd, group, fetch_time);
2269 		}
2270 		else if (new_mpd->availabilityStartTime != dash->mpd->availabilityStartTime) {
2271 			s64 diff = new_mpd->availabilityStartTime;
2272 			diff -= dash->mpd->availabilityStartTime;
2273 			if (diff < 0) diff = -diff;
2274 			if (diff>3000)
2275 				gf_dash_group_timeline_setup(new_mpd, group, fetch_time);
2276 		}
2277 
2278 		group->maybe_end_of_stream = 0;
2279 		reset_segment_count = GF_FALSE;
2280 		/*compute fetchTime + minUpdatePeriod and check period end time*/
2281 		if (new_mpd->minimum_update_period && new_mpd->media_presentation_duration) {
2282 			u64 endTime = fetch_time - new_mpd->availabilityStartTime - period->start;
2283 			if (endTime > new_mpd->media_presentation_duration) {
2284 				GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Period EndTime is signaled to "LLU", less than fetch time "LLU" ! Ignoring mediaPresentationDuration\n", new_mpd->media_presentation_duration, endTime));
2285 				new_mpd->media_presentation_duration = 0;
2286 				reset_segment_count = GF_TRUE;
2287 			} else {
2288 				endTime += new_mpd->minimum_update_period;
2289 				if (endTime > new_mpd->media_presentation_duration) {
2290 					GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Period EndTime is signaled to "LLU", less than fetch time + next update "LLU" - maybe end of stream ?\n", new_mpd->availabilityStartTime, endTime));
2291 					group->maybe_end_of_stream = 1;
2292 				}
2293 			}
2294 		}
2295 
2296 		/*update number of segments in active rep*/
2297 		gf_dash_get_segment_duration(gf_list_get(group->adaptation_set->representations, group->active_rep_index), group->adaptation_set, group->period, new_mpd, &group->nb_segments_in_rep, &seg_dur);
2298 
2299 		if (reset_segment_count) {
2300 			u32 nb_segs_in_mpd_period = (u32) (dash->mpd->minimum_update_period / (1000*seg_dur) );
2301 			group->nb_segments_in_rep = group->download_segment_index + nb_segs_in_mpd_period;
2302 		}
2303 		/*check if number of segments are coherent ...*/
2304 		else if (!group->maybe_end_of_stream && new_mpd->minimum_update_period && new_mpd->media_presentation_duration) {
2305 			u32 nb_segs_in_mpd_period = (u32) (dash->mpd->minimum_update_period / (1000*seg_dur) );
2306 
2307 			if (group->download_segment_index + nb_segs_in_mpd_period >= group->nb_segments_in_rep) {
2308 				GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Period has %d segments but %d are needed until next refresh. Maybe end of stream is near ?\n", group->nb_segments_in_rep, group->download_segment_index + nb_segs_in_mpd_period));
2309 				group->maybe_end_of_stream = 1;
2310 			}
2311 		}
2312 
2313 		GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Updated AdaptationSet %d - %d segments\n", group_idx+1, group->nb_segments_in_rep));
2314 
2315 		if (!period->duration && new_period->duration) {
2316 			GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("End of period upcoming, current segment index for group #%d: %d\n", group_idx+1, group->download_segment_index));
2317 			if (group->download_segment_index > (s32) group->nb_segments_in_rep)
2318 				gf_dash_mark_group_done(group);
2319 		}
2320 
2321 	}
2322 
2323 exit:
2324 	/*swap MPDs*/
2325 	if (dash->mpd) {
2326 		if (!new_mpd->minimum_update_period && (new_mpd->type==GF_MPD_TYPE_DYNAMIC))
2327 			new_mpd->minimum_update_period = dash->mpd->minimum_update_period;
2328 		gf_mpd_del(dash->mpd);
2329 	}
2330 	dash->mpd = new_mpd;
2331 	dash->last_update_time = gf_sys_clock();
2332 	dash->mpd_fetch_time = fetch_time;
2333 
2334 #ifndef GPAC_DISABLE_LOG
2335 	GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] Manifest after update:\n"));
2336 	for (i=0; i<gf_list_count(new_mpd->periods); i++) {
2337 		GF_MPD_Period *ap = gf_list_get(new_mpd->periods, i);
2338 		GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("\tP#%d: start "LLU" - duration " LLU" - xlink %s\n", i+1, ap->start, ap->duration, ap->xlink_href ? ap->xlink_href : ap->origin_base_url ? ap->origin_base_url : "none"));
2339 	}
2340 #endif
2341 
2342 	return GF_OK;
2343 }
2344 
2345 
gf_dash_set_group_representation(GF_DASH_Group * group,GF_MPD_Representation * rep)2346 static void gf_dash_set_group_representation(GF_DASH_Group *group, GF_MPD_Representation *rep)
2347 {
2348 #ifndef GPAC_DISABLE_LOG
2349 	u32 width=0, height=0, samplerate=0;
2350 	GF_MPD_Fractional *framerate=NULL;
2351 #endif
2352 	u32 k;
2353 	s32 timeshift;
2354 	GF_MPD_AdaptationSet *set;
2355 	GF_MPD_Period *period;
2356 	u32 nb_segs;
2357 	u32 i = gf_list_find(group->adaptation_set->representations, rep);
2358 	u32 prev_active_rep_index = group->active_rep_index;
2359 	u32 nb_cached_seg_per_rep = group->max_cached_segments / gf_dash_group_count_rep_needed(group);
2360 	assert((s32) i >= 0);
2361 
2362 	/* in case of dependent representations: we set max_complementary_rep_index than active_rep_index*/
2363 	if (group->base_rep_index_plus_one)
2364 		group->max_complementary_rep_index = i;
2365 	else
2366 		group->active_rep_index = i;
2367 	group->active_bitrate = rep->bandwidth;
2368 	group->max_cached_segments = nb_cached_seg_per_rep * gf_dash_group_count_rep_needed(group);
2369 	nb_segs = group->nb_segments_in_rep;
2370 
2371 	group->min_bandwidth_selected = GF_TRUE;
2372 	for (k=0; k<gf_list_count(group->adaptation_set->representations); k++) {
2373 		GF_MPD_Representation *arep = gf_list_get(group->adaptation_set->representations, k);
2374 		if (group->active_bitrate > arep->bandwidth) {
2375 			group->min_bandwidth_selected = GF_FALSE;
2376 			break;
2377 		}
2378 	}
2379 
2380 	while (rep->segment_list && rep->segment_list->xlink_href) {
2381 		Bool is_static = GF_FALSE;
2382 		u64 dur = 0;
2383 		u32 retry=10;
2384 		GF_Err e=GF_OK;
2385 
2386 		if (rep->segment_list->consecutive_xlink_count) {
2387 			GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] Resolving a XLINK pointed from another XLINK (%d consecutive XLINK in segment list)\n", rep->segment_list->consecutive_xlink_count));
2388 		}
2389 
2390 		while (retry) {
2391 			if (group->dash->is_m3u8) {
2392 				e = gf_m3u8_solve_representation_xlink(rep, &group->dash->getter, &is_static, &dur);
2393 			} else {
2394 				e = gf_dash_solve_representation_xlink(group->dash, rep);
2395 			}
2396 			if (e==GF_OK) break;
2397 			if (e==GF_NON_COMPLIANT_BITSTREAM) break;
2398 			if (group->dash->dash_state != GF_DASH_STATE_RUNNING)
2399 				break;
2400 
2401 			retry--;
2402 			gf_sleep(100);
2403 		}
2404 
2405 		//after resolving xlink: if this represenstation is marked as disabled, we have nothing to do
2406 		if (rep->playback.disabled)
2407 			return;
2408 
2409 		if (e) {
2410 			GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Could not reslove XLINK %s in time - using old representation\n", (rep->segment_list && rep->segment_list->xlink_href) ? rep->segment_list->xlink_href : ""));
2411 			group->active_rep_index = prev_active_rep_index;
2412 			return;
2413 		}
2414 
2415 		//we only know this is a static or dynamic MPD after parsing the first subplaylist
2416 		//if this is static, we need to update infos in mpd and period
2417 		if (group->dash->is_m3u8 && is_static) {
2418 			group->dash->mpd->type = GF_MPD_TYPE_STATIC;
2419 			group->dash->mpd->media_presentation_duration = dur;
2420 			group->dash->mpd->minimum_update_period = 0;
2421 			group->period->duration = dur;
2422 		}
2423 	}
2424 
2425 	if (group->dash->is_m3u8) {
2426 		//here we change to another representation: we need to remove all URLs from segment list and adjust the download segment index for this group
2427 		if (group->dash->dash_state == GF_DASH_STATE_RUNNING) {
2428 			u32 next_media_seq = group->m3u8_start_media_seq + group->download_segment_index;
2429 			GF_MPD_Representation *prev_active_rep = (GF_MPD_Representation *)gf_list_get(group->adaptation_set->representations, prev_active_rep_index);
2430 
2431 			if (group->dash->mpd->type == GF_MPD_TYPE_DYNAMIC) {
2432 				u64 current_start_time = 0;
2433 				Bool next_found=GF_FALSE;
2434 
2435 				//find the start time of the next segment on the old representation
2436 				GF_MPD_SegmentURL *seg_url = gf_list_get(prev_active_rep->segment_list->segment_URLs, group->download_segment_index);
2437 				if (!seg_url) {
2438 					//end of segment list, assume next was last one plus duration
2439 					seg_url = gf_list_last(prev_active_rep->segment_list->segment_URLs);
2440 					if (seg_url) current_start_time = seg_url->hls_utc_start_time + (seg_url->duration ? seg_url->duration : prev_active_rep->segment_list->duration);
2441 				} else {
2442 					current_start_time = seg_url->hls_utc_start_time;
2443 				}
2444 				group->hls_next_start_time = 0;
2445 
2446 				//check in new list where the start is
2447 				for (k=0; rep->segment_list && k<gf_list_count(rep->segment_list->segment_URLs); k++) {
2448 					s64 start_diff;
2449 					seg_url = (GF_MPD_SegmentURL *) gf_list_get(rep->segment_list->segment_URLs, k);
2450 
2451 					start_diff = (s64) current_start_time;
2452 					start_diff -= (s64) seg_url->hls_utc_start_time;
2453 					//Warning, we may have precision issues in start times, add 200 ms for safety
2454 					if (ABS(start_diff) <= 200) {
2455 						group->download_segment_index = k;
2456 						next_media_seq = rep->m3u8_media_seq_min + group->download_segment_index;
2457 						next_found = GF_TRUE;
2458 						break;
2459 					}
2460 					if (current_start_time < seg_url->hls_utc_start_time) {
2461 						GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] Switching to HLS start time "LLU" but found earlier segment with start time "LLU" - probably lost one segment\n", current_start_time, seg_url->hls_utc_start_time));
2462 						group->download_segment_index = k;
2463 						next_media_seq = rep->m3u8_media_seq_min + group->download_segment_index;
2464 						next_found = GF_TRUE;
2465 						break;
2466 					}
2467 				}
2468 				//no segment in the new playlist is found for this start time, force an MPD update
2469 				if (!next_found) {
2470 					GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] No segment in new rep for current HLS time "LLU", updating manifest\n", current_start_time));
2471 					group->hls_next_start_time = current_start_time;
2472 					//this will force the MPD update below
2473 					next_media_seq = 1+rep->m3u8_media_seq_max;
2474 				}
2475 			}
2476 
2477 			if (rep->m3u8_media_seq_min > next_media_seq) {
2478 				GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] Something wrong here: next media segment %d vs min media segment in segment list %d - some segments missing\n", next_media_seq, rep->m3u8_media_seq_min));
2479 				group->download_segment_index = rep->m3u8_media_seq_min;
2480 			} else if (rep->m3u8_media_seq_max < next_media_seq) {
2481 				GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] Too late: next media segment %d vs max media segment in segment list %d - force updating mpd\n", next_media_seq, rep->m3u8_media_seq_max));
2482 				group->dash->force_mpd_update = GF_TRUE;
2483 			} else {
2484 				GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] next  media segment %d found in  segment list (min %d - max %d) - adjusting download segment index\n", next_media_seq, rep->m3u8_media_seq_min, rep->m3u8_media_seq_max));
2485 				group->download_segment_index =  next_media_seq - rep->m3u8_media_seq_min;
2486 			}
2487 			GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] after switching download segment index should be %d\n", group->download_segment_index));
2488 		}
2489 		group->m3u8_start_media_seq = rep->m3u8_media_seq_min;
2490 	}
2491 
2492 	set = group->adaptation_set;
2493 	period = group->period;
2494 
2495 #ifndef GPAC_DISABLE_LOG
2496 
2497 #define GET_REP_ATTR(_a)	_a = rep->_a; if (!_a) _a = set->_a;
2498 
2499 	GET_REP_ATTR(width);
2500 	GET_REP_ATTR(height);
2501 	GET_REP_ATTR(samplerate);
2502 	GET_REP_ATTR(framerate);
2503 
2504 	if (width || height) {
2505 		u32 num=25, den=1;
2506 		if (framerate) {
2507 			num = framerate->num;
2508 			if (framerate->den) den = framerate->den;
2509 		}
2510 
2511 		GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] AS#%d changed quality to bitrate %d kbps - Width %d Height %d FPS %d/%d (playback speed %g)\n", 1+gf_list_find(group->period->adaptation_sets, group->adaptation_set), rep->bandwidth/1024, width, height, num, den, group->dash->speed));
2512 	}
2513 	else if (samplerate) {
2514 		GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] AS#%d changed quality to bitrate %d kbps - sample rate %u (playback speed %g)\n", 1+gf_list_find(group->period->adaptation_sets, group->adaptation_set), rep->bandwidth/1024, samplerate, group->dash->speed));
2515 	}
2516 	else {
2517 		GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] AS#%d changed quality to bitrate %d kbps (playback speed %g)\n", 1+gf_list_find(group->period->adaptation_sets, group->adaptation_set), rep->bandwidth/1024, group->dash->speed));
2518 	}
2519 #endif
2520 
2521 	gf_dash_get_segment_duration(rep, set, period, group->dash->mpd, &group->nb_segments_in_rep, &group->segment_duration);
2522 
2523 	/*if broken indication in duration restore previous seg count*/
2524 	if (group->dash->ignore_mpd_duration)
2525 		group->nb_segments_in_rep = nb_segs;
2526 
2527 	timeshift = (s32) (rep->segment_base ? rep->segment_base->time_shift_buffer_depth : (rep->segment_list ? rep->segment_list->time_shift_buffer_depth : (rep->segment_template ? rep->segment_template->time_shift_buffer_depth : -1) ) );
2528 	if (timeshift == -1) timeshift = (s32) (set->segment_base ? set->segment_base->time_shift_buffer_depth : (set->segment_list ? set->segment_list->time_shift_buffer_depth : (set->segment_template ? set->segment_template->time_shift_buffer_depth : -1) ) );
2529 	if (timeshift == -1) timeshift = (s32) (period->segment_base ? period->segment_base->time_shift_buffer_depth : (period->segment_list ? period->segment_list->time_shift_buffer_depth : (period->segment_template ? period->segment_template->time_shift_buffer_depth : -1) ) );
2530 
2531 	if (timeshift == -1) timeshift = (s32) group->dash->mpd->time_shift_buffer_depth;
2532 	group->time_shift_buffer_depth = (u32) timeshift;
2533 
2534 	group->dash->dash_io->on_dash_event(group->dash->dash_io, GF_DASH_EVENT_QUALITY_SWITCH, gf_list_find(group->dash->groups, group), GF_OK);
2535 }
2536 
gf_dash_switch_group_representation(GF_DashClient * mpd,GF_DASH_Group * group)2537 static void gf_dash_switch_group_representation(GF_DashClient *mpd, GF_DASH_Group *group)
2538 {
2539 	u32 i, bandwidth, min_bandwidth;
2540 	GF_MPD_Representation *rep_sel = NULL;
2541 	GF_MPD_Representation *min_rep_sel = NULL;
2542 	Bool min_bandwidth_selected = GF_FALSE;
2543 	bandwidth = 0;
2544 	min_bandwidth = (u32) -1;
2545 
2546 	GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Checking representations between %d and %d kbps\n", group->min_bitrate/1024, group->max_bitrate/1024));
2547 
2548 	if (group->force_representation_idx_plus_one) {
2549 		rep_sel = gf_list_get(group->adaptation_set->representations, group->force_representation_idx_plus_one - 1);
2550 		group->force_representation_idx_plus_one = 0;
2551 	}
2552 
2553 	if (!rep_sel) {
2554 		for (i=0; i<gf_list_count(group->adaptation_set->representations); i++) {
2555 			GF_MPD_Representation *rep = gf_list_get(group->adaptation_set->representations, i);
2556 			if (rep->playback.disabled) continue;
2557 			if ((rep->bandwidth > bandwidth) && (rep->bandwidth < group->max_bitrate )) {
2558 				rep_sel = rep;
2559 				bandwidth = rep->bandwidth;
2560 			}
2561 			if (rep->bandwidth < min_bandwidth) {
2562 				min_rep_sel = rep;
2563 				min_bandwidth = rep->bandwidth;
2564 			}
2565 		}
2566 	}
2567 
2568 	if (!rep_sel) {
2569 		if (!min_rep_sel) {
2570 			min_rep_sel = gf_list_get(group->adaptation_set->representations, 0);
2571 		}
2572 		rep_sel = min_rep_sel;
2573 		min_bandwidth_selected = 1;
2574 	}
2575 	assert(rep_sel);
2576 	i = gf_list_find(group->adaptation_set->representations, rep_sel);
2577 
2578 	assert((s32) i >= 0);
2579 
2580 	group->force_switch_bandwidth = 0;
2581 	group->max_bitrate = 0;
2582 	group->min_bitrate = (u32) -1;
2583 
2584 	if (i != group->active_rep_index) {
2585 		if (min_bandwidth_selected) {
2586 			GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] No representation found with bandwidth below %d kbps - using representation @ %d kbps\n", group->max_bitrate/1024, rep_sel->bandwidth/1024));
2587 		}
2588 		gf_dash_set_group_representation(group, rep_sel);
2589 	}
2590 }
2591 
2592 
gf_dash_resolve_url(GF_MPD * mpd,GF_MPD_Representation * rep,GF_DASH_Group * group,const char * mpd_url,GF_MPD_URLResolveType resolve_type,u32 item_index,char ** out_url,u64 * out_range_start,u64 * out_range_end,u64 * segment_duration,Bool * is_in_base_url,char ** out_key_url,bin128 * out_key_iv,Bool * data_url_process)2593 static GF_Err gf_dash_resolve_url(GF_MPD *mpd, GF_MPD_Representation *rep, GF_DASH_Group *group, const char *mpd_url, GF_MPD_URLResolveType resolve_type, u32 item_index, char **out_url, u64 *out_range_start, u64 *out_range_end, u64 *segment_duration, Bool *is_in_base_url, char **out_key_url, bin128 *out_key_iv, Bool *data_url_process)
2594 {
2595 	GF_Err e;
2596 	GF_MPD_AdaptationSet *set = group->adaptation_set;
2597 	GF_MPD_Period *period = group->period;
2598 	u32 timescale;
2599 
2600 	if (!mpd_url) return GF_BAD_PARAM;
2601 
2602 	if (!strncmp(mpd_url, "gfio://", 7))
2603 		mpd_url = gf_file_basename(gf_fileio_translate_url(mpd_url));
2604 
2605 	if (!group->timeline_setup) {
2606 		gf_dash_group_timeline_setup(mpd, group, 0);
2607 		//we must wait for ATSC 3.0 clock to initialize, even if first period is static remote (we need to know when to tune)
2608 		if (group->dash->atsc_clock_state==1)
2609 			return GF_IP_NETWORK_EMPTY;
2610 
2611 		if (group->dash->reinit_period_index)
2612 			return GF_IP_NETWORK_EMPTY;
2613 		group->timeline_setup = 1;
2614 		item_index = group->download_segment_index;
2615 	}
2616 
2617 	gf_mpd_resolve_segment_duration(rep, set, period, segment_duration, &timescale, NULL, NULL);
2618 	*segment_duration = (resolve_type==GF_MPD_RESOLVE_URL_MEDIA) ? (u32) ((Double) ((*segment_duration) * 1000.0) / timescale) : 0;
2619 	e = gf_mpd_resolve_url(mpd, rep, set, period, mpd_url, group->current_base_url_idx, resolve_type, item_index, group->nb_segments_purged, out_url, out_range_start, out_range_end, segment_duration, is_in_base_url, out_key_url, out_key_iv);
2620 
2621 
2622 	if (e == GF_NON_COMPLIANT_BITSTREAM) {
2623 //		group->selection = GF_DASH_GROUP_NOT_SELECTABLE;
2624 	}
2625 	if (!*out_url) {
2626 		return e;
2627 	}
2628 
2629 	if (*out_url && data_url_process && !strncmp(*out_url, "data:", 5)) {
2630 		char *sep;
2631 		sep = strstr(*out_url, ";base64,");
2632 		if (sep) {
2633 			GF_Blob *blob;
2634 			u32 len;
2635 			sep+=8;
2636 			len = (u32)strlen(sep) + 1;
2637 			GF_SAFEALLOC(blob, GF_Blob);
2638 			if (!blob) return GF_OUT_OF_MEM;
2639 
2640 			blob->data = (char *)gf_malloc(len);
2641 			blob->size = gf_base64_decode(sep, len, blob->data, len);
2642 			sprintf(*out_url, "gmem://%p", blob);
2643 			*data_url_process = GF_TRUE;
2644 		} else {
2645 			GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("data scheme with encoding different from base64 not supported\n"));
2646 		}
2647 	}
2648 
2649 	return e;
2650 }
2651 
2652 /* Estimate the maximum speed that we can play, using our statistic. If it is below the max_playout_rate in MPD, return max_playout_rate*/
gf_dash_get_max_available_speed(GF_DashClient * dash,GF_DASH_Group * group,GF_MPD_Representation * rep)2653 static Double gf_dash_get_max_available_speed(GF_DashClient *dash, GF_DASH_Group *group, GF_MPD_Representation *rep)
2654 {
2655 	Double max_available_speed = 0;
2656 	Double max_dl_speed, max_decoding_speed;
2657 	u32 framerate;
2658 	u32 bytes_per_sec;
2659 
2660 	if (!group->irap_max_dec_time && !group->avg_dec_time) {
2661 		return 0;
2662 	}
2663 	bytes_per_sec = group->backup_Bps;
2664 	max_dl_speed = 8.0*bytes_per_sec / rep->bandwidth;
2665 
2666 	//if framerate is not in MPD, suppose that it is 25 fps
2667 	framerate = 25;
2668 	if (rep->framerate) {
2669 		framerate = rep->framerate->num;
2670 		if (rep->framerate->den) {
2671 			framerate /= rep->framerate->den;
2672 		}
2673 	}
2674 
2675 	if (group->decode_only_rap)
2676 		max_decoding_speed = group->irap_max_dec_time ? 1000000.0 / group->irap_max_dec_time : 0;
2677 	else
2678 		max_decoding_speed = group->avg_dec_time ? 1000000.0 / (group->max_dec_time + group->avg_dec_time*(framerate - 1)) : 0;
2679 	max_available_speed = max_decoding_speed > max_dl_speed ? max_dl_speed : max_decoding_speed;
2680 	GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Representation %s max playout rate: in MPD %f - calculated by stat: %f\n", rep->id, rep->max_playout_rate, max_available_speed));
2681 
2682 	return max_available_speed;
2683 }
2684 
dash_store_stats(GF_DashClient * dash,GF_DASH_Group * group,u32 bytes_per_sec,u32 file_size,Bool is_broadcast)2685 static void dash_store_stats(GF_DashClient *dash, GF_DASH_Group *group, u32 bytes_per_sec, u32 file_size, Bool is_broadcast)
2686 {
2687 	const char *url;
2688 	GF_MPD_Representation *rep;
2689 
2690 	if (!group->nb_cached_segments)
2691 		return;
2692 	url = strrchr( group->cached[group->nb_cached_segments-1].url, '/');
2693 	if (!url) url = strrchr( group->cached[group->nb_cached_segments-1].url, '\\');
2694 	if (url) url+=1;
2695 	else url = group->cached[group->nb_cached_segments-1].url;
2696 
2697 	group->total_size = file_size;
2698 	//in broadcast mode, just store the rate
2699 	if (is_broadcast) group->bytes_per_sec = bytes_per_sec;
2700 	//otherwise store the min rate we got (to deal with complementary representations)
2701 	else if (!group->bytes_per_sec || group->bytes_per_sec > bytes_per_sec) group->bytes_per_sec = bytes_per_sec;
2702 
2703 	group->last_segment_time = gf_sys_clock();
2704 	group->nb_segments_since_switch ++;
2705 
2706 	if (!dash->thread_mode) {
2707 		group->prev_segment_ok = GF_TRUE;
2708 		if (group->time_at_first_failure) {
2709 			if (group->current_base_url_idx) {
2710 				GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] Recovered segment %s after 404 by switching baseURL\n", url));
2711 			} else {
2712 				GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] Recovered segment %s after 404 - was our download schedule %d too early ?\n", url, gf_sys_clock() - group->time_at_first_failure));
2713 			}
2714 			group->time_at_first_failure = 0;
2715 		}
2716 		group->nb_consecutive_segments_lost = 0;
2717 		group->current_base_url_idx = 0;
2718 	}
2719 
2720 	rep = gf_list_get(group->adaptation_set->representations, group->active_rep_index);
2721 	rep->playback.broadcast_flag = is_broadcast;
2722 
2723 #ifndef GPAC_DISABLE_LOG
2724 	if (gf_log_tool_level_on(GF_LOG_DASH, GF_LOG_INFO)) {
2725 		u32 i, buffer_ms = 0;
2726 		Double bitrate, time;
2727 		//force a call go query buffer
2728 		dash->dash_io->on_dash_event(dash->dash_io, GF_DASH_EVENT_CODEC_STAT_QUERY, gf_list_find(dash->groups, group), GF_OK);
2729 		buffer_ms = group->buffer_occupancy_ms;
2730 		for (i=0; i < group->nb_cached_segments; i++) {
2731 			buffer_ms += group->cached[i].duration;
2732 		}
2733 
2734 		bitrate=0;
2735 		time=0;
2736 		if (group->current_downloaded_segment_duration) {
2737 			bitrate = 8*group->total_size;
2738 			bitrate /= group->current_downloaded_segment_duration;
2739 		}
2740 
2741 		if (bytes_per_sec) {
2742 			time = group->total_size;
2743 			time /= bytes_per_sec;
2744 		}
2745 		GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] AS#%d got %s stats: %d bytes in %g sec (%d kbps) - duration %g sec - Media Rate: indicated %d - computed %d kbps - buffer %d ms\n", 1+gf_list_find(group->period->adaptation_sets, group->adaptation_set), url, group->total_size, time, 8*bytes_per_sec/1000, group->current_downloaded_segment_duration/1000.0, rep->bandwidth/1000, (u32) bitrate, buffer_ms));
2746 	}
2747 #endif
2748 }
2749 
dash_do_rate_monitor_default(GF_DashClient * dash,GF_DASH_Group * group)2750 static GF_Err dash_do_rate_monitor_default(GF_DashClient *dash, GF_DASH_Group *group)
2751 {
2752 	Bool default_switch_mode = GF_FALSE;
2753 	u32 download_rate, set_idx, time_since_start, done, tot_size, time_until_end;
2754 	if (group->depend_on_group) return GF_BAD_PARAM;
2755 	if (group->dash->disable_switching) return GF_OK;
2756 
2757 	if (group->buffering)
2758 		return GF_OK;
2759 
2760 	if (group->segment_download) {
2761 		download_rate = group->dash->dash_io->get_bytes_per_sec(group->dash->dash_io, group->segment_download);
2762 		done = group->dash->dash_io->get_bytes_done(group->dash->dash_io, group->segment_download);
2763 		tot_size = group->dash->dash_io->get_total_size(group->dash->dash_io, group->segment_download);
2764 	} else {
2765 		download_rate = group->bytes_per_sec;
2766 		done = group->bytes_done;
2767 		tot_size = group->total_size;
2768 
2769 	}
2770 	if (!download_rate) return GF_OK;
2771 
2772 	time_until_end = 0;
2773 	if (tot_size) {
2774 		time_until_end = 1000*(tot_size-done) / download_rate;
2775 	}
2776 
2777 	download_rate *= 8;
2778 	if (download_rate<group->min_bitrate) group->min_bitrate = download_rate;
2779 	if (download_rate>group->max_bitrate) group->max_bitrate = download_rate;
2780 
2781 	if (download_rate > group->active_bitrate) {
2782 		return GF_OK;
2783 	}
2784 
2785 	set_idx = gf_list_find(group->period->adaptation_sets, group->adaptation_set)+1;
2786 	time_since_start = gf_sys_clock() - group->download_start_time;
2787 
2788 	if (group->min_bandwidth_selected) {
2789 		GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Downloading from set #%d at rate %d kbps but media bitrate is"
2790 				" %d kbps - no lower bitrate available ...\n", set_idx, download_rate/1024, group->active_bitrate/1024 ));
2791 		return GF_OK;
2792 	}
2793 
2794 	//TODO - when do we start checking ?
2795 	if (time_since_start < 200) {
2796 		GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Downloading from set #%ds at rate %d kbps but "
2797 				"media bitrate is %d kbps\n", set_idx, download_rate/1024, group->active_bitrate/1024 ));
2798 		return GF_OK;
2799 	}
2800 
2801 	if (time_until_end) {
2802 		u32 i, cache_dur=0;
2803 		for (i=1; i<group->nb_cached_segments; i++) {
2804 			cache_dur += group->cached[i].duration;
2805 		}
2806 		//we have enough cache data to go until end of this download, perform rate switching at next segment
2807 		if (time_until_end<cache_dur) {
2808 			GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Downloading from set #%ds at rate %d kbps but "
2809 					"media bitrate is %d kbps - %d till end of download and %d in cache - "
2810 					"going on with download\n", set_idx, download_rate/1024, group->active_bitrate/1024,time_until_end, cache_dur ));
2811 			return GF_OK;
2812 		}
2813 	}
2814 
2815 	GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] Downloading from set #%d at rate %d kbps but "
2816 			"media bitrate is %d kbps - %d/%d in cache - killing connection and "
2817 			"switching\n", set_idx, download_rate/1024, group->active_bitrate/1024, group->nb_cached_segments, group->max_cached_segments ));
2818 
2819 	if (dash->thread_mode) {
2820 		group->download_abort_type = 2;
2821 		group->dash->dash_io->abort(group->dash->dash_io, group->segment_download);
2822 	} else {
2823 		dash->dash_io->on_dash_event(dash->dash_io, GF_DASH_EVENT_ABORT_DOWNLOAD, gf_list_find(dash->groups, group), GF_OK);
2824 	}
2825 	//in live we just abort current download and go to next. In onDemand, we may want to rebuffer
2826 	default_switch_mode = (group->dash->mpd->type==GF_MPD_TYPE_DYNAMIC) ? GF_FALSE : GF_TRUE;
2827 
2828 	//if we have time to download from another rep ?
2829 	if (group->current_downloaded_segment_duration <= time_since_start) {
2830 		//don't force bandwidth switch (it's too late anyway, consider we lost the segment), let the rate adaptation decide
2831 		group->force_switch_bandwidth = default_switch_mode;
2832 
2833 		GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] Download time longer than segment duration - trying to resync on next "
2834 				"segment\n"));
2835 	} else {
2836 		u32 target_rate;
2837 		//compute min bitrate needed to fetch the segment in another rep, with the time remaining
2838 		Double ratio = ((u32)group->current_downloaded_segment_duration - time_since_start);
2839 		ratio /= (u32)group->current_downloaded_segment_duration;
2840 
2841 		target_rate = (u32) (download_rate * ratio);
2842 
2843 		if (target_rate < group->min_representation_bitrate) {
2844 			GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] Download rate lower than min available rate ...\n"));
2845 			target_rate = group->min_representation_bitrate;
2846 			//don't force bandwidth switch, we won't have time to redownload the segment.
2847 			group->force_switch_bandwidth = default_switch_mode;
2848 		} else {
2849 			group->force_switch_bandwidth = GF_TRUE;
2850 			GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] Attempting to re-download at target rate %d\n", target_rate));
2851 		}
2852 		//cap max bitrate for next rate adaptation pass
2853 		group->max_bitrate = target_rate;
2854 	}
2855 	return GF_OK;
2856 }
2857 
dash_do_rate_adaptation_legacy_rate(GF_DashClient * dash,GF_DASH_Group * group,GF_DASH_Group * base_group,u32 dl_rate,Double speed,Double max_available_speed,Bool force_lower_complexity,GF_MPD_Representation * rep,Bool go_up_bitrate)2858 static s32 dash_do_rate_adaptation_legacy_rate(GF_DashClient *dash, GF_DASH_Group *group, GF_DASH_Group *base_group,
2859 												u32 dl_rate, Double speed, Double max_available_speed, Bool force_lower_complexity,
2860 												GF_MPD_Representation *rep, Bool go_up_bitrate)
2861 {
2862 	u32 k;
2863 	Bool do_switch;
2864 	GF_MPD_Representation *new_rep;
2865 	s32 new_index = group->active_rep_index;
2866 
2867 	/* records the number of representations between the current one and the next chosen one */
2868 	u32 nb_inter_rep = 0;
2869 
2870 	/* We assume that there will be a change in quality */
2871 	do_switch = GF_TRUE;
2872 
2873 	/*find best bandwidth that fits our bitrate and playing speed*/
2874 	new_rep = NULL;
2875 
2876 	/* for each playable representation, if we still need to switch, evaluate it */
2877 	for (k = 0; k<gf_list_count(group->adaptation_set->representations) && do_switch; k++) {
2878 		GF_MPD_Representation *arep = gf_list_get(group->adaptation_set->representations, k);
2879 		if (!arep->playback.prev_max_available_speed) {
2880 			arep->playback.prev_max_available_speed = 1.0;
2881 		}
2882 		if (arep->playback.disabled) {
2883 			continue;
2884 		}
2885 		if (arep->playback.prev_max_available_speed && (speed > arep->playback.prev_max_available_speed)) {
2886 			continue;
2887 		}
2888 		/* Only try to switch to a representation, if download rate is greater than its bitrate */
2889 		if (dl_rate >= arep->bandwidth) {
2890 
2891 			/* First check if we are adapting to CPU */
2892 			if (!dash->disable_speed_adaptation && force_lower_complexity) {
2893 
2894 				/*try to switch to highest quality below the current one*/
2895 				if ((arep->quality_ranking < rep->quality_ranking) ||
2896 					(arep->width < rep->width) || (arep->height < rep->height)) {
2897 
2898 					/* If we hadn't found a new representation, use it
2899 					   otherwise use it only if current representation is better*/
2900 					if (!new_rep) {
2901 						new_rep = arep;
2902 						new_index = k;
2903 					}
2904 					else if ((arep->quality_ranking > new_rep->quality_ranking) ||
2905 						(arep->width > new_rep->width) || (arep->height > new_rep->height)) {
2906 						new_rep = arep;
2907 						new_index = k;
2908 					}
2909 				}
2910 				rep->playback.prev_max_available_speed = max_available_speed;
2911 				go_up_bitrate = GF_FALSE;
2912 			}
2913 			else {
2914 				/* if speed adaptation is not used or used, but the new representation can be played at the right speed */
2915 
2916 				if (!new_rep) {
2917 					new_rep = arep;
2918 					new_index = k;
2919 				}
2920 				else if (go_up_bitrate) {
2921 
2922 					/* agressive switching is configured in the GPAC configuration */
2923 					if (dash->agressive_switching) {
2924 						/*be agressive, try to switch to highest bitrate below available download rate*/
2925 						if (arep->bandwidth > new_rep->bandwidth) {
2926 							if (new_rep->bandwidth > rep->bandwidth) {
2927 								nb_inter_rep++;
2928 							}
2929 							new_rep = arep;
2930 							new_index = k;
2931 						}
2932 						else if (arep->bandwidth > rep->bandwidth) {
2933 							nb_inter_rep++;
2934 						}
2935 					}
2936 					else {
2937 						/*don't be agressive, try to switch to lowest bitrate above our current rep*/
2938 						if (new_rep->bandwidth <= rep->bandwidth) {
2939 							new_rep = arep;
2940 							new_index = k;
2941 						}
2942 						else if ((arep->bandwidth < new_rep->bandwidth) && (arep->bandwidth > rep->bandwidth)) {
2943 							new_rep = arep;
2944 							new_index = k;
2945 						}
2946 					}
2947 				}
2948 				else {
2949 					/* go_up_bitrate is GF_FALSE */
2950 					/*try to switch to highest bitrate below available download rate*/
2951 					if (arep->bandwidth > new_rep->bandwidth) {
2952 						new_rep = arep;
2953 						new_index = k;
2954 					}
2955 				}
2956 			}
2957 		}
2958 	}
2959 
2960 	if (!new_rep || (new_rep == rep)) {
2961 		GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] AS#%d no better match for requested bandwidth %d - not switching (AS bitrate %d)!\n", 1 + gf_list_find(group->period->adaptation_sets, group->adaptation_set), dl_rate, rep->bandwidth));
2962 		do_switch = GF_FALSE;
2963 	}
2964 
2965 	if (do_switch) {
2966 		//if we're switching to the next upper bitrate (no intermediate bitrates), do not immediately switch
2967 		//but for a given number of segments - this avoids fluctuation in the quality
2968 		if (go_up_bitrate && !nb_inter_rep) {
2969 			new_rep->playback.probe_switch_count++;
2970 			if (new_rep->playback.probe_switch_count > dash->probe_times_before_switch) {
2971 				new_rep->playback.probe_switch_count = 0;
2972 			} else {
2973 				new_index = group->active_rep_index;
2974 			}
2975 		}
2976 	}
2977 	return new_index;
2978 }
2979 
dash_do_rate_adaptation_legacy_buffer(GF_DashClient * dash,GF_DASH_Group * group,GF_DASH_Group * base_group,u32 dl_rate,Double speed,Double max_available_speed,Bool force_lower_complexity,GF_MPD_Representation * rep,Bool go_up_bitrate)2980 static s32 dash_do_rate_adaptation_legacy_buffer(GF_DashClient *dash, GF_DASH_Group *group, GF_DASH_Group *base_group,
2981 												  u32 dl_rate, Double speed, Double max_available_speed, Bool force_lower_complexity,
2982 												  GF_MPD_Representation *rep, Bool go_up_bitrate)
2983 {
2984 	Bool do_switch;
2985 	s32 new_index = group->active_rep_index;
2986 
2987 	/* We assume that there will be a change in quality
2988 	   and then set it to no change or increase if this is not the case */
2989 	do_switch = GF_TRUE;
2990 
2991 	/* if the current representation bitrate is smaller than the measured bandwidth,
2992 	   tentatively start to increase bitrate */
2993 	if (rep->bandwidth < dl_rate) {
2994 		go_up_bitrate = GF_TRUE;
2995 	}
2996 
2997 	/* clamp download bitrate to the lowest representation rate, to allow choosing it */
2998 	if (dl_rate < group->min_representation_bitrate) {
2999 		dl_rate = group->min_representation_bitrate;
3000 	}
3001 
3002 	/* buffer_max_ms is non-null when the adaptation algorithm requires buffer information (e.g. GF_DASH_ALGO_GPAC_LEGACY_BUFFER ) */
3003 	/* if the cache is full (i.e. player did not fetch downloaded data yet)
3004 	   if we are below half of the buffer don't try to go up and limit rate to less than our current rep bandwidth*/
3005 	if (group->buffer_max_ms && (group->nb_cached_segments<group->max_cached_segments)) {
3006 		u32 buf_high_threshold, buf_low_threshold;
3007 		s32 occ;
3008 
3009 		if (group->current_downloaded_segment_duration && (group->buffer_max_ms > group->current_downloaded_segment_duration)) {
3010 			buf_high_threshold = group->buffer_max_ms - (u32)group->current_downloaded_segment_duration;
3011 		}
3012 		else {
3013 			buf_high_threshold = 2 * group->buffer_max_ms / 3;
3014 		}
3015 		buf_low_threshold = (group->current_downloaded_segment_duration && (group->buffer_min_ms>10)) ? group->buffer_min_ms : (u32)group->current_downloaded_segment_duration;
3016 		if (buf_low_threshold > group->buffer_max_ms) buf_low_threshold = 1 * group->buffer_max_ms / 3;
3017 
3018 		//compute how much we managed to refill (current state minus previous state)
3019 		occ = (s32)group->buffer_occupancy_ms;
3020 		occ -= (s32)group->buffer_occupancy_at_last_seg;
3021 		//if above max buffer force occ>0 since a segment may still be pending and not dispatched (buffer regulation)
3022 		if (group->buffer_occupancy_ms>group->buffer_max_ms) occ = 1;
3023 
3024 		//switch down if current buffer falls below min threshold
3025 		if ((s32)group->buffer_occupancy_ms < (s32)buf_low_threshold) {
3026 			if (!group->buffer_occupancy_ms) {
3027 				dl_rate = group->min_representation_bitrate;
3028 			}
3029 			else {
3030 				dl_rate = (rep->bandwidth > 10) ? rep->bandwidth - 10 : 1;
3031 			}
3032 			go_up_bitrate = GF_FALSE;
3033 			GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] AS#%d bitrate %d bps buffer max %d current %d refill since last %d - running low, switching down, target rate %d\n", 1 + gf_list_find(group->period->adaptation_sets, group->adaptation_set), rep->bandwidth, group->buffer_max_ms, group->buffer_occupancy_ms, occ, dl_rate));
3034 		}
3035 		//switch up if above max threshold and buffer refill is fast enough
3036 		else if ((occ>0) && (group->buffer_occupancy_ms > buf_high_threshold)) {
3037 			GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] AS#%d bitrate %d bps buffer max %d current %d refill since last %d - running high, will try to switch up, target rate %d\n", 1 + gf_list_find(group->period->adaptation_sets, group->adaptation_set), rep->bandwidth, group->buffer_max_ms, group->buffer_occupancy_ms, occ, dl_rate));
3038 			go_up_bitrate = GF_TRUE;
3039 		}
3040 		//don't do anything in the middle range of the buffer or if refill not fast enough
3041 		else {
3042 			do_switch = GF_FALSE;
3043 			GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] AS#%d bitrate %d bps buffer max %d current %d refill since last %d - steady\n", 1 + gf_list_find(group->period->adaptation_sets, group->adaptation_set), rep->bandwidth, group->buffer_max_ms, group->buffer_occupancy_ms, occ));
3044 		}
3045 	}
3046 
3047 	/* Unless the switching has been turned off (e.g. middle buffer range),
3048 	   we apply rate-based adaptation */
3049 	if (do_switch) {
3050 		new_index = dash_do_rate_adaptation_legacy_rate(dash, group, base_group, dl_rate, speed, max_available_speed, force_lower_complexity, rep, go_up_bitrate);
3051 	}
3052 
3053 	return new_index;
3054 }
3055 
3056 // returns the bitrate and index of the representation having the minimum bitrate above the given rate
get_min_rate_above(GF_List * representations,double rate,s32 * index)3057 static u32 get_min_rate_above(GF_List *representations, double rate, s32 *index) {
3058 	u32 k;
3059 	u32 min_rate = GF_INT_MAX;
3060 	GF_MPD_Representation *rep;
3061 
3062 	u32 nb_reps = gf_list_count(representations);
3063 	for (k = 0; k < nb_reps; k++) {
3064 		rep = gf_list_get(representations, k);
3065 		if ((rep->bandwidth < min_rate) && (rep->bandwidth > rate)) {
3066 			min_rate = rep->bandwidth;
3067 			if (index) {
3068 				*index = k;
3069 			}
3070 			return min_rate; // representations are sorted by bandwidth
3071 		}
3072 	}
3073 	return min_rate;
3074 }
3075 
3076 // returns the bitrate and index of the representation having the maximum bitrate below the given rate
get_max_rate_below(GF_List * representations,double rate,s32 * index)3077 static u32 get_max_rate_below(GF_List *representations, double rate, s32 *index) {
3078 	s32 k;
3079 	u32 max_rate = 0;
3080 	GF_MPD_Representation *rep;
3081 
3082 	u32 nb_reps = gf_list_count(representations);
3083 	for (k = (s32) nb_reps-1; k >=0 ; k--) {
3084 		rep = gf_list_get(representations, k);
3085 		if ((rep->bandwidth > max_rate) && (rep->bandwidth < rate)) {
3086 			max_rate = rep->bandwidth;
3087 			if (index) {
3088 				*index = k;
3089 			}
3090 			return max_rate; // representations are sorted by bandwidth
3091 		}
3092 	}
3093 	return max_rate;
3094 }
3095 
3096 /**
3097 Adaptation Algorithm as described in
3098 	T.-Y. Huang et al. 2014. A buffer-based approach to rate adaptation: evidence from a large video streaming service.
3099 	In Proceedings of the 2014 ACM conference on SIGCOMM (SIGCOMM '14).
3100 */
dash_do_rate_adaptation_bba0(GF_DashClient * dash,GF_DASH_Group * group,GF_DASH_Group * base_group,u32 dl_rate,Double speed,Double max_available_speed,Bool force_lower_complexity,GF_MPD_Representation * rep,Bool go_up_bitrate)3101 static s32 dash_do_rate_adaptation_bba0(GF_DashClient *dash, GF_DASH_Group *group, GF_DASH_Group *base_group,
3102 												  u32 dl_rate, Double speed, Double max_available_speed, Bool force_lower_complexity,
3103 												  GF_MPD_Representation *rep, Bool go_up_bitrate)
3104 {
3105 	u32 rate_plus;
3106 	u32 rate_minus;
3107 	u32 rate_prev = group->active_bitrate;
3108 	u32 rate_max;
3109 	u32 rate_min;
3110 	s32 new_index;
3111 	u32 r; // reservoir
3112 	u32 cu; // cushion
3113 	u32 buf_now = group->buffer_occupancy_ms;
3114 	u32 buf_max = group->buffer_max_ms;
3115 	double f_buf_now;
3116 
3117 	/* We don't use the segment duration as advertised in the MPD because it may not be there due to segment timeline*/
3118 	u32 segment_duration_ms = (u32)group->current_downloaded_segment_duration;
3119 
3120 	rate_min = ((GF_MPD_Representation *)gf_list_get(group->adaptation_set->representations, 0))->bandwidth;
3121 	rate_max = ((GF_MPD_Representation *)gf_list_get(group->adaptation_set->representations, gf_list_count(group->adaptation_set->representations) - 1))->bandwidth;
3122 
3123 	if (!buf_max) buf_max = 3*segment_duration_ms;
3124 	/* if the current buffer cannot hold an entire new segment, we indicate that we don't want to download it
3125 	   NOTE: This is not described in the paper
3126 	*/
3127 	if (group->buffer_occupancy_ms + segment_duration_ms > buf_max) {
3128 		GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] BBA-0: not enough space to download new segment: %d\n", group->buffer_occupancy_ms));
3129 		return -1;
3130 	}
3131 
3132     if (rate_prev == rate_max) {
3133     	rate_plus = rate_max;
3134     } else {
3135 		rate_plus = get_min_rate_above(group->adaptation_set->representations, rate_prev, NULL);
3136     }
3137 
3138     if (rate_prev == rate_min) {
3139     	rate_minus = rate_min;
3140     } else {
3141 		rate_minus = get_max_rate_below(group->adaptation_set->representations, rate_prev, NULL);
3142     }
3143 
3144     /*
3145      * the size of the reservoir is 37.5% of the buffer size, but at least = 1 chunk duration)
3146 	 * the size of the upper reservoir is 10% of the buffer size
3147      * the size of cushion is between 37.5% and 90% of the buffer size
3148 	 * the rate map is piece-wise
3149      */
3150 	if (buf_max <= segment_duration_ms) {
3151 		GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] BBA-0: cannot initialize BBA-0 given the buffer size (%d) and segment duration (%d)\n", group->buffer_max_ms, group->segment_duration*1000));
3152 		return -1;
3153 	}
3154 	r = (u32)(37.5*buf_max / 100);
3155 	if (r < segment_duration_ms) {
3156 		r = segment_duration_ms;
3157 	}
3158 	cu = (u32)((90-37.5)*buf_max / 100);
3159 
3160 	if (buf_now <= r) {
3161 		f_buf_now = rate_min;
3162 	}
3163 	else if (buf_now >= (cu + r)) {
3164 		f_buf_now = rate_max;
3165 	}
3166 	else {
3167 		f_buf_now = rate_min + (rate_max - rate_min)*((buf_now - r) * 1.0 / cu);
3168 	}
3169 
3170 	if (f_buf_now == rate_max) {
3171 		// rate_next = rate_max;
3172 		new_index = gf_list_count(group->adaptation_set->representations) - 1;
3173 	}
3174 	else if (f_buf_now == rate_min) {
3175 		// rate_next = rate_min;
3176 		new_index = 0;
3177 	}
3178 	else if (f_buf_now >= rate_plus) {
3179 		// rate_next = max of Ri st. Ri < f_buf_now
3180 		new_index = 0;
3181 		get_max_rate_below(group->adaptation_set->representations, f_buf_now, &new_index);
3182 	}
3183 	else if (f_buf_now <= rate_minus) {
3184 		// rate_next = min of Ri st. Ri > f_buf_now
3185 		new_index = gf_list_count(group->adaptation_set->representations) - 1;
3186 		get_min_rate_above(group->adaptation_set->representations, f_buf_now, &new_index);
3187 	}
3188 	else {
3189 		// no change
3190 		new_index = group->active_rep_index;
3191 	}
3192 
3193 	if (new_index != -1) {
3194 #ifndef GPAC_DISABLE_LOG
3195 		GF_MPD_Representation *result = gf_list_get(group->adaptation_set->representations, (u32)new_index);
3196 #endif
3197 		// increment the segment number for debug purposes
3198 		group->current_index++;
3199 		GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] BBA-0: buffer %d ms, segment number %d, new quality %d with rate %d\n", group->buffer_occupancy_ms, group->current_index, new_index, result->bandwidth));
3200 	}
3201 	return new_index;
3202 }
3203 
3204 /* returns the index of the representation which maximises BOLA utility function
3205    based on the relative log-based utility of each representation compared to the one with the lowest bitrate
3206    NOTE: V can represent V (in BOLA BASIC) or V_D (in other modes of BOLA) */
bola_find_max_utility_index(GF_List * representations,Double V,Double gamma,Double p,Double Q)3207 static s32 bola_find_max_utility_index(GF_List *representations, Double V, Double gamma, Double p, Double Q) {
3208 	u32 k;
3209 	Double max_utility = GF_MIN_DOUBLE;
3210 	u32 nb_reps = gf_list_count(representations);
3211 	s32 new_index = -1;
3212 
3213 	for (k = 0; k < nb_reps; k++) {
3214 		GF_MPD_Representation *rep = gf_list_get(representations, k);
3215 		Double utility = (V * rep->playback.bola_v + V*gamma*p - Q) / (rep->bandwidth*p);
3216 		if (utility >= max_utility) {
3217 			max_utility = utility;
3218 			new_index = k;
3219 		}
3220 	}
3221 	return new_index;
3222 }
3223 
3224 /**
3225 Adaptation Algorithm as described in
3226 K. Spiteri et al. 2016. BOLA: Near-Optimal Bitrate Adaptation for Online Videos
3227 Arxiv.org
3228 */
dash_do_rate_adaptation_bola(GF_DashClient * dash,GF_DASH_Group * group,GF_DASH_Group * base_group,u32 dl_rate,Double speed,Double max_available_speed,Bool force_lower_complexity,GF_MPD_Representation * rep,Bool go_up_bitrate)3229 static s32 dash_do_rate_adaptation_bola(GF_DashClient *dash, GF_DASH_Group *group, GF_DASH_Group *base_group,
3230 		  	  	  	  	  	  	  	  	  u32 dl_rate, Double speed, Double max_available_speed, Bool force_lower_complexity,
3231 										  	  GF_MPD_Representation *rep, Bool go_up_bitrate)
3232 {
3233 	s32 new_index = -1;
3234 	u32 k;
3235 	Double p = group->current_downloaded_segment_duration / 1000.0;	// segment duration
3236 	Double gamma = (double)5/(double)p;
3237 	Double Qmax = group->buffer_max_ms / 1000.0 / p;		// max nb of segments in the buffer
3238 	Double Q = group->buffer_occupancy_ms / 1000.0 / p;		// current buffer occupancy in number of segments
3239 
3240 	GF_MPD_Representation *min_rep;
3241 	GF_MPD_Representation *max_rep;
3242 	u32 nb_reps;
3243 
3244 	if (dash->mpd->type != GF_MPD_TYPE_STATIC) {
3245 		GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] BOLA: Cannot be used for live MPD\n"));
3246 		return -1;
3247 	}
3248 
3249 	nb_reps = gf_list_count(group->adaptation_set->representations);
3250 	min_rep = gf_list_get(group->adaptation_set->representations, 0);
3251 	max_rep = gf_list_get(group->adaptation_set->representations, nb_reps - 1);
3252 
3253 	// Computing the log-based utility of each segment (recomputing each time for period changes)
3254 	for (k = 0; k < nb_reps; k++) {
3255 		GF_MPD_Representation *a_rep = gf_list_get(group->adaptation_set->representations, k);
3256 		a_rep->playback.bola_v = log(((Double)a_rep->bandwidth) / min_rep->bandwidth);
3257 	}
3258 
3259 	if (dash->adaptation_algorithm == GF_DASH_ALGO_BOLA_BASIC) {
3260 		/* BOLA Basic is the variant of BOLA that assumes infinite duration streams (no wind-up/down, no rate use, no oscillation control)
3261 		   It simply consists in finding the maximum utility */
3262 		// NOTE in BOLA, representation indices decrease when the quality increases [1 = best quality]
3263 		Double V = (Qmax - 1) / (gamma * p + max_rep->playback.bola_v);
3264 		new_index = bola_find_max_utility_index(group->adaptation_set->representations, V, gamma, p, Q);
3265 	}
3266 	else if (dash->adaptation_algorithm == GF_DASH_ALGO_BOLA_FINITE ||
3267 		dash->adaptation_algorithm == GF_DASH_ALGO_BOLA_O ||
3268 		dash->adaptation_algorithm == GF_DASH_ALGO_BOLA_U) {
3269 		/* BOLA FINITE is the same as BOLA Basic with the wind-up and down phases */
3270 		/* BOLA O and U add extra steps to BOLA FINITE */
3271 		Double t_bgn; //play time from begin
3272 		Double t_end; //play time to the end
3273 		Double t;
3274 		Double t_prime;
3275 		Double Q_Dmax;
3276 		Double V_D;
3277 		Double N = dash->mpd->media_presentation_duration / p;
3278 
3279 		t_bgn = p*group->current_index;
3280 		t_end = (N - group->current_index)*p;
3281 		t = MIN(t_bgn, t_end);
3282 		t_prime = MAX(t / 2, 3 * p);
3283 		Q_Dmax = MIN(Qmax, t_prime / p);
3284 		V_D = (Q_Dmax - 1) / (gamma * p + max_rep->playback.bola_v);
3285 
3286 		new_index = bola_find_max_utility_index(group->adaptation_set->representations, V_D, gamma, p, Q);
3287 
3288 		if (dash->adaptation_algorithm == GF_DASH_ALGO_BOLA_U || dash->adaptation_algorithm == GF_DASH_ALGO_BOLA_O) {
3289 			//Bola U algorithm
3290 			if ((new_index != -1) && ((u32)new_index > group->active_rep_index)) {
3291 				u32 r = group->bytes_per_sec*8;
3292 
3293 				// index_prime the min m such that (Sm[m]/p)<= max(bandwidth_previous,S_M/p))
3294 				// NOTE in BOLA, representation indices decrease when the quality increases [1 = best quality]
3295 				u32 m_prime = 0;
3296 				get_max_rate_below(group->adaptation_set->representations, MAX(r, min_rep->bandwidth), &m_prime);
3297 				if (m_prime >= (u32)new_index) {
3298 					m_prime = new_index;
3299 				}
3300 				else if (m_prime < group->active_rep_index) {
3301 					m_prime = group->active_rep_index;
3302 				}
3303 				else {
3304 					if (dash->adaptation_algorithm == GF_DASH_ALGO_BOLA_U) {
3305 						m_prime++;
3306 					}
3307 					else { //GF_DASH_ALGO_BOLA_O
3308 #if 0
3309 						GF_MPD_Representation *rep_m_prime, *rep_m_prime_plus_one;
3310 						Double Sm_prime, Sm_prime_plus_one, f_m_prime, f_m_prime_1, bola_o_pause;
3311 
3312 						assert(m_prime >= 0 && m_prime < nb_reps - 2);
3313 						rep_m_prime = (GF_MPD_Representation *)gf_list_get(group->adaptation_set->representations, m_prime);
3314 						rep_m_prime_plus_one = (GF_MPD_Representation *)gf_list_get(group->adaptation_set->representations, m_prime+1);
3315 						Sm_prime = rep_m_prime->bandwidth*p;
3316 						Sm_prime_plus_one = rep_m_prime_plus_one->bandwidth*p;
3317 						f_m_prime = V_D*(rep_m_prime->playback.bola_v + gamma*p) / Sm_prime;
3318 						f_m_prime_1 = V_D*(rep_m_prime_plus_one->playback.bola_v + gamma*p) / Sm_prime_plus_one;
3319 						// TODO wait for bola_o_pause before making the download
3320 						bola_o_pause = Q - (f_m_prime - f_m_prime_1) / (1 / Sm_prime - 1 / Sm_prime_plus_one);
3321 #endif
3322 					}
3323 				}
3324 				new_index = m_prime;
3325 			}
3326 		}
3327 		// TODO trigger pause for max(p*(Q-Q_Dmax+1), 0)
3328 	}
3329 
3330 	if (new_index != -1) {
3331 #ifndef GPAC_DISABLE_LOG
3332 		GF_MPD_Representation *result = gf_list_get(group->adaptation_set->representations, (u32)new_index);
3333 #endif
3334 		// increment the segment number for debug purposes
3335 		group->current_index++;
3336 		GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] BOLA: buffer %d ms, segment number %d, new quality %d with rate %d\n", group->buffer_occupancy_ms, group->current_index, new_index, result->bandwidth));
3337 	}
3338 	return new_index;
3339 }
3340 
3341 /* This function is called each time a new segment has been downloaded */
dash_do_rate_adaptation(GF_DashClient * dash,GF_DASH_Group * group)3342 static void dash_do_rate_adaptation(GF_DashClient *dash, GF_DASH_Group *group)
3343 {
3344 	Double speed;
3345 	Double max_available_speed;
3346 	u32 dl_rate;
3347 	u32 k;
3348 	s32 new_index, old_index;
3349 	GF_DASH_Group *base_group;
3350 	GF_MPD_Representation *rep;
3351 	Bool force_lower_complexity;
3352 
3353 	/* Don't do adaptation if configured switching to happen systematically (debug) */
3354 	if (dash->auto_switch_count) {
3355 		return;
3356 	}
3357 	/* Don't do adaptation if GPAC config disabled switching */
3358 	if (group->dash->disable_switching) {
3359 		return;
3360 	}
3361 
3362 	/* The bytes_per_sec field is set each time a segment is downloaded,
3363 	   (this may need to be adjusted in the future to accomodate algorithms
3364 	   that smooth download rate over several segments)
3365 	   if set to 0, this means that no segment was downloaded since the last call
3366 	   because this AdaptationSet is not selected
3367 	   So no rate adaptation should be done*/
3368 	if (!group->bytes_per_sec) {
3369 		return;
3370 	}
3371 
3372 	/* Find the AdaptationSet on which this AdaptationSet depends, if any
3373 	   (e.g. used for specific coding schemes: scalable streams, tiled streams, ...)*/
3374 	base_group = group;
3375 	while (base_group->depend_on_group) {
3376 		base_group = base_group->depend_on_group;
3377 	}
3378 
3379 	/* adjust the download rate according to the playback speed
3380 	   All adaptation algorithms should use this value */
3381 	speed = dash->speed;
3382 	if (speed<0) speed = -speed;
3383 	dl_rate = (u32)  (8*group->bytes_per_sec / speed);
3384 
3385 	/* Get the active representation in the AdaptationSet */
3386 	rep = gf_list_get(group->adaptation_set->representations, group->active_rep_index);
3387 
3388 	/*check whether we can play with this speed (i.e. achieve target frame rate);
3389 	if not force, let algorithm know that they should switch to a lower resolution*/
3390 	max_available_speed = gf_dash_get_max_available_speed(dash, base_group, rep);
3391 	if (!dash->disable_speed_adaptation && !rep->playback.waiting_codec_reset) {
3392 		if (max_available_speed && (speed > max_available_speed)) {
3393 			GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] Forcing a lower complexity to achieve desired playback speed\n"));
3394 			force_lower_complexity = GF_TRUE;
3395 		}
3396 		else {
3397 			force_lower_complexity = GF_FALSE;
3398 		}
3399 	}
3400 	else {
3401 		force_lower_complexity = GF_FALSE;
3402 	}
3403 
3404 	/*query codec and buffer statistics for buffer-based algorithms */
3405 	group->buffer_max_ms = 0;
3406 	group->buffer_occupancy_ms = 0;
3407 	group->codec_reset = 0;
3408 	/* the DASH Client asks the player for its buffer level
3409 	  (uses a function pointer to avoid depenencies on the player code, to reuse the DASH client in different situations)*/
3410 	dash->dash_io->on_dash_event(dash->dash_io, GF_DASH_EVENT_CODEC_STAT_QUERY, gf_list_find(group->dash->groups, group), GF_OK);
3411 
3412 	//adjust buffer with current segments not yet consumed by player
3413 	for (k=0; k<group->nb_cached_segments; k++) {
3414 		group->buffer_occupancy_ms += group->cached[k].duration;
3415 	}
3416 
3417 
3418 	/* If the playback for the current representation was waiting for a codec reset and it happened,
3419 	   indicate that this representation does not need a reset anymore */
3420 	if (rep->playback.waiting_codec_reset && group->codec_reset) {
3421 		rep->playback.waiting_codec_reset = GF_FALSE;
3422 	}
3423 
3424 	old_index = group->active_rep_index;
3425 	//scalable case, force the rate algo to consider the active rep is the max rep
3426 	if (group->base_rep_index_plus_one) {
3427 		group->active_rep_index = group->max_complementary_rep_index;
3428 	}
3429 	if (group->dash->atsc_clock_state) {
3430 		rep = gf_list_get(group->adaptation_set->representations, group->active_rep_index);
3431 		if (rep->playback.broadcast_flag && (dl_rate < rep->bandwidth)) {
3432 			dl_rate = rep->bandwidth+1;
3433 			GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] AS#%d representation %d segment sent over broadcast, forcing bandwidth to %d\n", 1 + gf_list_find(group->period->adaptation_sets, group->adaptation_set), group->active_rep_index, dl_rate));
3434 		}
3435 	}
3436 
3437 	/* Call a specific adaptation algorithm (see GPAC configuration)
3438 	Each algorithm should:
3439 	- return the new_index value to the desired quality
3440 
3441 	It can use:
3442 	- the information about each available representation (group->adaptation_set->representations, e.g. bandwidth required for that representation)
3443 	- the information of the current representation (rep)
3444 	- the download_rate dl_rate (computed on the previously downloaded segment, and adjusted to the playback speed),
3445 	- the buffer levels:
3446 	    - current: group->buffer_occupancy_ms,
3447 		- previous: group->buffer_occupancy_at_last_seg
3448 		- max: group->buffer_max_ms,
3449 	- the playback speed,
3450 	- the maximum achievable speed at the current resolution,
3451 	- the indicator that the current representation is too demanding CPU-wise (force_lower_complexity)
3452 
3453 	Private algorithm information should be stored in the dash object if global to all AdaptationSets,
3454 	or in the group if local to an AdaptationSet.
3455 
3456 	TODO: document how to access other possible parameters (e.g. segment sizes if available, ...)
3457 	*/
3458 	new_index = group->active_rep_index;
3459 	if (dash->rate_adaptation_algo) {
3460 		new_index = dash->rate_adaptation_algo(dash, group, base_group,
3461 														  dl_rate, speed, max_available_speed, force_lower_complexity,
3462 														  rep, GF_FALSE);
3463 	}
3464 
3465 	if (new_index==-1) {
3466 		group->active_rep_index = old_index;
3467 		group->rate_adaptation_postponed = GF_TRUE;
3468 		return;
3469 	}
3470 	group->rate_adaptation_postponed = GF_FALSE;
3471 
3472 	if (new_index != group->active_rep_index) {
3473 		GF_MPD_Representation *new_rep = gf_list_get(group->adaptation_set->representations, (u32)new_index);
3474 		if (!new_rep) {
3475 			GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Error: Cannot find new representation index %d\n", new_index));
3476 			return;
3477 		}
3478 
3479 		GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] AS#%d switching after playing %d segments, from rep %d to rep %d\n", 1 + gf_list_find(group->period->adaptation_sets, group->adaptation_set),
3480 				group->nb_segments_since_switch, group->active_rep_index, new_index));
3481 		group->nb_segments_since_switch = 0;
3482 
3483 		if (force_lower_complexity) {
3484 			GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] Requesting codec reset\n"));
3485 			new_rep->playback.waiting_codec_reset = GF_TRUE;
3486 		}
3487 		/* request downloads for the new representation */
3488 		gf_dash_set_group_representation(group, new_rep);
3489 
3490 		/* Reset smoothing of switches
3491 		(note: should really apply only to algorithms using the switch_probe_count (smoothing the aggressiveness of the change)
3492 		for now: only GF_DASH_ALGO_GPAC_LEGACY_RATE and  GF_DASH_ALGO_GPAC_LEGACY_BUFFER */
3493 		for (k = 0; k < gf_list_count(group->adaptation_set->representations); k++) {
3494 			GF_MPD_Representation *arep = gf_list_get(group->adaptation_set->representations, k);
3495 			if (new_rep == arep) continue;
3496 			arep->playback.probe_switch_count = 0;
3497 		}
3498 
3499 	} else {
3500 		group->active_rep_index = old_index;
3501 		if (force_lower_complexity) {
3502 			GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] Speed %f is too fast to play - speed down\n", dash->speed));
3503 			/*FIXME: should do something here*/
3504 		}
3505 	}
3506 
3507 	/* Remembering the buffer level for the processing of the next segment */
3508 	group->buffer_occupancy_at_last_seg = group->buffer_occupancy_ms;
3509 }
3510 
gf_dash_get_fileio_url(const char * base_url,char * res_url)3511 static char *gf_dash_get_fileio_url(const char *base_url, char *res_url)
3512 {
3513 	const char *new_res;
3514 	GF_FileIO *gfio;
3515 	if (!base_url)
3516 		return NULL;
3517 	if (strncmp(base_url, "gfio://", 7))
3518 		return res_url;
3519 
3520 	gfio = gf_fileio_from_url(base_url);
3521 
3522 	new_res = gf_fileio_factory(gfio, res_url);
3523 	if (!new_res) return res_url;
3524 	gf_free(res_url);
3525 	return gf_strdup(new_res);
3526 }
3527 
gf_dash_download_init_segment(GF_DashClient * dash,GF_DASH_Group * group)3528 static GF_Err gf_dash_download_init_segment(GF_DashClient *dash, GF_DASH_Group *group)
3529 {
3530 	GF_Err e;
3531 	char *base_init_url;
3532 	char *init_segment_local_url=NULL;
3533 	GF_MPD_Representation *rep;
3534 	u64 start_range, end_range;
3535 	char mime[128];
3536 	const char *mime_type;
3537 	Bool data_url_processed = GF_FALSE;
3538 	/* This variable is 0 if there is a initURL, the index of first segment downloaded otherwise */
3539 	u32 nb_segment_read = 0;
3540 	u32 file_size=0, Bps= 0;
3541 	char *base_url=NULL;
3542 	char *base_url_orig=NULL;
3543 	char *key_url=NULL;
3544 	bin128 key_iv;
3545 
3546 	if (!dash || !group)
3547 		return GF_BAD_PARAM;
3548 
3549 	assert(group->adaptation_set && group->adaptation_set->representations);
3550 	rep = gf_list_get(group->adaptation_set->representations, group->active_rep_index);
3551 	if (!rep) {
3552 		GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Unable to find any representation, aborting.\n"));
3553 		return GF_IO_ERR;
3554 	}
3555 	start_range = end_range = 0;
3556 	base_url = dash->base_url;
3557 	if (group->period->origin_base_url) base_url = group->period->origin_base_url;
3558 
3559 	base_url_orig = base_url;
3560 	if (base_url && !strncmp(base_url, "gfio://", 7)) {
3561 		GF_FileIO *gfio = gf_fileio_from_url(base_url);
3562 		base_url = (char *) gf_file_basename(gf_fileio_resource_url(gfio));
3563 	}
3564 
3565 	e = gf_dash_resolve_url(dash->mpd, rep, group, base_url, GF_MPD_RESOLVE_URL_INIT, 0, &base_init_url, &start_range, &end_range, &group->current_downloaded_segment_duration, NULL, &key_url, &key_iv, &data_url_processed);
3566 	if (e) {
3567 		if (e != GF_IP_NETWORK_EMPTY) {
3568 			GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Unable to resolve initialization URL: %s\n", gf_error_to_string(e) ));
3569 		}
3570 		return e;
3571 	}
3572 
3573 	if (!base_init_url && rep->dependency_id) {
3574 		return GF_OK;
3575 	}
3576 
3577 	/*no error and no init segment, go for media segment - this is needed for TS so that the set of media streams can be
3578 	declared to the player */
3579 	if (!base_init_url) {
3580 		e = gf_dash_resolve_url(dash->mpd, rep, group, dash->base_url, GF_MPD_RESOLVE_URL_MEDIA, group->download_segment_index, &base_init_url, &start_range, &end_range, &group->current_downloaded_segment_duration, NULL, &key_url, &key_iv, NULL);
3581 		if (e) {
3582 			GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Unable to resolve media URL: %s\n", gf_error_to_string(e) ));
3583 			return e;
3584 		}
3585 		nb_segment_read = 1;
3586 	} else if (!group->bitstream_switching) {
3587 		group->dont_delete_first_segment = 1;
3588 	}
3589 
3590 	base_url = base_url_orig;
3591 	base_init_url = gf_dash_get_fileio_url(base_url, base_init_url);
3592 
3593 	if (!strstr(base_init_url, "://") || !strnicmp(base_init_url, "file://", 7) || !strnicmp(base_init_url, "gmem://", 7)
3594 		|| !strnicmp(base_init_url, "views://", 8) || !strnicmp(base_init_url, "mosaic://", 9)
3595 		|| !strnicmp(base_init_url, "isobmff://", 10) || !strnicmp(base_init_url, "gfio://", 7)
3596 	) {
3597 		//if file-based, check if file exists, if not switch base URL
3598 		if ( strnicmp(base_init_url, "gmem://", 7) && strnicmp(base_init_url, "gfio://", 7)) {
3599 			FILE *ftest = gf_fopen(base_init_url, "rb");
3600 			if (!ftest) {
3601 				if (group->current_base_url_idx + 1 < gf_mpd_get_base_url_count(dash->mpd, group->period, group->adaptation_set, rep) ){
3602 					group->current_base_url_idx++;
3603 					gf_free(base_init_url);
3604 					return gf_dash_download_init_segment(dash, group);
3605 				}
3606 			} else {
3607 				file_size = (u32) gf_fsize(ftest);
3608 				gf_fclose(ftest);
3609 			}
3610 		}
3611 		//we don't reset the baseURL index until we are done fetching all init segments
3612 
3613 		assert(!group->nb_cached_segments);
3614 		group->cached[0].cache = gf_strdup(base_init_url);
3615 		group->cached[0].url = gf_strdup(base_init_url);
3616 		group->cached[0].representation_index = group->active_rep_index;
3617 		group->prev_active_rep_index = group->active_rep_index;
3618 		if (key_url) {
3619 			group->cached[0].key_url = key_url;
3620 			memcpy(group->cached[0].key_IV, key_iv, sizeof(bin128));
3621 		}
3622 
3623 		group->nb_cached_segments = 1;
3624 		/*do not erase local files*/
3625 		group->local_files = group->was_segment_base ? 0 : 1;
3626 
3627 		group->download_segment_index += nb_segment_read;
3628 		init_segment_local_url = group->cached[0].cache;
3629 		if (group->bitstream_switching) {
3630 			group->bs_switching_init_segment_url = gf_strdup(init_segment_local_url);
3631 			group->bs_switching_init_segment_url_start_range = start_range;
3632 			group->bs_switching_init_segment_url_end_range = end_range;
3633 			if (data_url_processed) {
3634 				GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("URL with data scheme not handled for Bistream Switching Segments, probable memory leak"));
3635 			}
3636 		} else {
3637 			rep->playback.cached_init_segment_url = gf_strdup(init_segment_local_url);
3638 			rep->playback.owned_gmem = data_url_processed;
3639 			rep->playback.init_start_range = start_range;
3640 			rep->playback.init_end_range = end_range;
3641 		}
3642 
3643 
3644 		/*finally download all init segments if any*/
3645 		if (!group->bitstream_switching) {
3646 			u32 k;
3647 			for (k=0; k<gf_list_count(group->adaptation_set->representations); k++) {
3648 				char *a_base_init_url = NULL;
3649 				u64 a_start = 0, a_end = 0, a_dur = 0;
3650 				GF_MPD_Representation *a_rep = gf_list_get(group->adaptation_set->representations, k);
3651 				if (a_rep==rep) continue;
3652 				if (a_rep->playback.disabled) continue;
3653 
3654 				e = gf_dash_resolve_url(dash->mpd, a_rep, group, dash->base_url, GF_MPD_RESOLVE_URL_INIT, 0, &a_base_init_url, &a_start, &a_end, &a_dur, NULL, &a_rep->playback.key_url, &a_rep->playback.key_IV, &a_rep->playback.owned_gmem);
3655 				if (!e && a_base_init_url) {
3656 					a_rep->playback.cached_init_segment_url = a_base_init_url;
3657 					a_rep->playback.init_start_range = a_start;
3658 					a_rep->playback.init_end_range =a_end ;
3659 				} else if (e) {
3660 					GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Cannot solve initialization segment for representation: %s - discarding representation\n", gf_error_to_string(e) ));
3661 					a_rep->playback.disabled = 1;
3662 				}
3663 			}
3664 		}
3665 		GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] First segment is %s \n", init_segment_local_url));
3666 		gf_free(base_init_url);
3667 		group->current_base_url_idx=0;
3668 		dash_store_stats(dash, group, 0, file_size, GF_FALSE);
3669 		return GF_OK;
3670 	}
3671 
3672 	group->max_bitrate = 0;
3673 	group->min_bitrate = (u32)-1;
3674 
3675 
3676 	if (!dash->thread_mode) {
3677 
3678 		if (dash->atsc_clock_state && !group->period->origin_base_url) {
3679 			GF_DASHFileIOSession sess = NULL;
3680 			/*check the init segment has been received*/
3681 			e = gf_dash_download_resource(dash, &sess, base_init_url, start_range, end_range, 1, NULL);
3682 			dash->dash_io->del(dash->dash_io, sess);
3683 
3684 			if (e==GF_OK) {
3685 
3686 			} else {
3687 				return e;
3688 			}
3689 		}
3690 
3691 		assert(!group->nb_cached_segments);
3692 		group->cached[0].url = base_init_url;
3693 		group->cached[0].cache = gf_strdup(base_init_url);
3694 		group->cached[0].representation_index = group->active_rep_index;
3695 		group->cached[0].duration = (u32) group->current_downloaded_segment_duration;
3696 
3697 		if (group->bitstream_switching) {
3698 			group->bs_switching_init_segment_url = gf_strdup(base_init_url);
3699 			group->bs_switching_init_segment_url_start_range = start_range;
3700 			group->bs_switching_init_segment_url_end_range = end_range;
3701 			if (data_url_processed) {
3702 				GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("URL with data scheme not handled for Bistream Switching Segments, probable memory leak"));
3703 			}
3704 		} else {
3705 			rep->playback.cached_init_segment_url = gf_strdup(base_init_url);
3706 			rep->playback.init_start_range = start_range;
3707 			rep->playback.init_end_range = end_range;
3708 			rep->playback.owned_gmem = data_url_processed;
3709 		}
3710 		group->nb_cached_segments = 1;
3711 		group->download_segment_index += nb_segment_read;
3712 
3713 		/*download all init segments if any*/
3714 		if (!group->bitstream_switching) {
3715 			u32 k;
3716 			for (k=0; k<gf_list_count(group->adaptation_set->representations); k++) {
3717 				char *a_base_init_url = NULL;
3718 				u64 a_start, a_end, a_dur;
3719 				GF_MPD_Representation *a_rep = gf_list_get(group->adaptation_set->representations, k);
3720 				if (a_rep==rep) continue;
3721 				if (a_rep->playback.disabled) continue;
3722 
3723 				e = gf_dash_resolve_url(dash->mpd, a_rep, group, dash->base_url, GF_MPD_RESOLVE_URL_INIT, 0, &a_base_init_url, &a_start, &a_end, &a_dur, NULL, &a_rep->playback.key_url, &a_rep->playback.key_IV, &a_rep->playback.owned_gmem);
3724 				if (!e && a_base_init_url) {
3725 
3726 					a_rep->playback.cached_init_segment_url = a_base_init_url;
3727 					a_rep->playback.init_start_range = a_start;
3728 					a_rep->playback.init_end_range = a_end;
3729 				} else if (e) {
3730 					GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Cannot solve initialization segment for representation: %s - disabling representation\n", gf_error_to_string(e) ));
3731 					a_rep->playback.disabled = 1;
3732 				}
3733 			}
3734 		}
3735 		return GF_OK;
3736 	}
3737 
3738 	/*use persistent connection for segment downloads*/
3739 	e = gf_dash_download_resource(dash, &(group->segment_download), base_init_url, start_range, end_range, 1, group);
3740 
3741 	if ((e==GF_OK) && group->force_switch_bandwidth && !dash->auto_switch_count) {
3742 		gf_free(base_init_url);
3743 		if (key_url) gf_free(key_url);
3744 		gf_dash_switch_group_representation(dash, group);
3745 		return gf_dash_download_init_segment(dash, group);
3746 	}
3747 
3748 	if ((e==GF_URL_ERROR) && base_init_url) {
3749 		if (group->current_base_url_idx + 1 < gf_mpd_get_base_url_count(dash->mpd, group->period, group->adaptation_set, rep) ){
3750 			group->current_base_url_idx++;
3751 			gf_free(base_init_url);
3752 			if (key_url) gf_free(key_url);
3753 			return gf_dash_download_init_segment(dash, group);
3754 		}
3755 	}
3756 
3757 
3758 	if ((e==GF_URL_ERROR) && base_init_url && !group->download_abort_type) { /* We have a 404 and started with segments */
3759 		/* It is possible that the first segment has been deleted while we made the first request...
3760 		 * so we try with the next segment on some M3U8 servers */
3761 		gf_free(base_init_url);
3762 		if (key_url) gf_free(key_url);
3763 		e = gf_dash_resolve_url(dash->mpd, rep, group, dash->base_url, GF_MPD_RESOLVE_URL_MEDIA, group->download_segment_index + 1, &base_init_url, &start_range, &end_range, &group->current_downloaded_segment_duration, NULL, &key_url, &key_iv, NULL);
3764 		if (e != GF_OK) {
3765 			return e;
3766 		}
3767 		GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("Download of first segment failed... retrying with second one : %s\n", base_init_url));
3768 		nb_segment_read = 2;
3769 		/*use persistent connection for segment downloads*/
3770 		e = gf_dash_download_resource(dash, &(group->segment_download), base_init_url, 0, 0, 1, group);
3771 	} /* end of 404 */
3772 
3773 
3774 	if ((e==GF_IP_CONNECTION_CLOSED) && group->download_abort_type) {
3775 		group->download_abort_type = 0;
3776 		GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] Aborted while downloading init segment (seek ?)%s \n", base_init_url));
3777 		gf_free(base_init_url);
3778 		if (key_url) gf_free(key_url);
3779 		return GF_OK;
3780 	}
3781 
3782 	if (e!= GF_OK && !group->segment_must_be_streamed) {
3783 		gf_free(base_init_url);
3784 		if (key_url) gf_free(key_url);
3785 		if (!group->dash->atsc_clock_state || (group->dash->atsc_clock_state==3)) {
3786 			dash->mpd_stop_request = 1;
3787 		}
3788 		return e;
3789 	}
3790 
3791 
3792 	if (!group->nb_segments_in_rep) {
3793 		if (dash->mpd->type==GF_MPD_TYPE_STATIC) {
3794 			GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] 0 segments in static representation (MPD duration "LLU", will probably have 404\n", group->dash->mpd->media_presentation_duration));
3795 		}
3796 	} else if (!group->groups_depending_on &&  (group->nb_segments_in_rep < group->max_cached_segments)) {
3797 		GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] Resizing to %u max_cached_segments elements instead of %u.\n", group->nb_segments_in_rep, group->max_cached_segments));
3798 		/* OK, we have a problem, it may ends download */
3799 		group->max_cached_segments = group->nb_segments_in_rep;
3800 	}
3801 
3802 	/* Mime-Type check */
3803 	mime_type = dash->dash_io->get_mime(dash->dash_io, group->segment_download) ;
3804 	strcpy(mime, mime_type ? mime_type : "");
3805 	strlwr(mime);
3806 
3807 	if (dash->mimeTypeForM3U8Segments)
3808 		gf_free(dash->mimeTypeForM3U8Segments);
3809 	dash->mimeTypeForM3U8Segments = gf_strdup( mime );
3810 	mime_type = gf_dash_get_mime_type(NULL, rep, group->adaptation_set);
3811 	if (!rep->mime_type) {
3812 		rep->mime_type = gf_strdup( mime_type ? mime_type : mime );
3813 
3814 	//disable mime type check
3815 #if 0
3816 		mime_type = gf_dash_get_mime_type(NULL, rep, group->adaptation_set);
3817 	}
3818 	if (stricmp(mime, mime_type)) {
3819 		Bool valid = GF_FALSE;
3820 		char *stype1, *stype2;
3821 		stype1 = strchr(mime_type, '/');
3822 		stype2 = strchr(mime, '/');
3823 		if (stype1 && stype2 && !strcmp(stype1, stype2)) valid = 1;
3824 
3825 		if (!valid) {
3826 			GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Mime '%s' is not correct for '%s', it should be '%s'\n", mime, base_init_url, mime_type));
3827 			dash->mpd_stop_request = 0;
3828 			gf_free(base_init_url);
3829 			if (key_url) gf_free(key_url);
3830 			return GF_BAD_PARAM;
3831 		}
3832 	}
3833 	if (!rep->mime_type) {
3834 		rep->mime_type = gf_strdup( mime_type ? mime_type : mime );
3835 #endif
3836 	}
3837 
3838 	if (group->segment_must_be_streamed ) {
3839 		init_segment_local_url = (char *) dash->dash_io->get_url(dash->dash_io, group->segment_download);
3840 		e = GF_OK;
3841 	} else {
3842 		init_segment_local_url = (char *) dash->dash_io->get_cache_name(dash->dash_io, group->segment_download);
3843 	}
3844 
3845 	if ((e!=GF_OK) || !init_segment_local_url) {
3846 		GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Error with initialization segment: download result:%s, cache file: %s\n", gf_error_to_string(e), init_segment_local_url ? init_segment_local_url : "UNKNOWN"));
3847 		dash->mpd_stop_request = 1;
3848 		gf_free(base_init_url);
3849 		if (key_url) gf_free(key_url);
3850 		return GF_BAD_PARAM;
3851 	}
3852 
3853 	file_size = dash->dash_io->get_total_size(dash->dash_io, group->segment_download) ;
3854 	Bps = dash->dash_io->get_bytes_per_sec(dash->dash_io, group->segment_download) ;
3855 
3856 	assert(!group->nb_cached_segments);
3857 	group->cached[0].cache = gf_strdup(init_segment_local_url);
3858 	group->cached[0].url = gf_strdup( dash->dash_io->get_url(dash->dash_io, group->segment_download) );
3859 	group->cached[0].representation_index = group->active_rep_index;
3860 	group->cached[0].duration = (u32) group->current_downloaded_segment_duration;
3861 
3862 	if (group->bitstream_switching) {
3863 		group->bs_switching_init_segment_url = gf_strdup(init_segment_local_url);
3864 		group->bs_switching_init_segment_url_start_range = 0;
3865 		group->bs_switching_init_segment_url_end_range = 0;
3866 		if (data_url_processed) {
3867 			GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("URL with data scheme not handled for Bistream Switching Segments, probable memory leak"));
3868 		}
3869 	} else {
3870 		rep->playback.cached_init_segment_url = gf_strdup(init_segment_local_url);
3871 		rep->playback.init_start_range = 0;
3872 		rep->playback.init_end_range = 0;
3873 		rep->playback.owned_gmem = data_url_processed;
3874 	}
3875 
3876 	group->nb_cached_segments = 1;
3877 	group->download_segment_index += nb_segment_read;
3878 
3879 	GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Adding initialization segment %s to cache: %s\n", init_segment_local_url, group->cached[0].url ));
3880 
3881 	gf_free(base_init_url);
3882 
3883 	/*download all init segments if any*/
3884 	if (!group->bitstream_switching) {
3885 		u32 k;
3886 		for (k=0; k<gf_list_count(group->adaptation_set->representations); k++) {
3887 			char *a_base_init_url = NULL;
3888 			u64 a_start, a_end, a_dur;
3889 			GF_MPD_Representation *a_rep = gf_list_get(group->adaptation_set->representations, k);
3890 			if (a_rep==rep) continue;
3891 			if (a_rep->playback.disabled) continue;
3892 
3893 			e = gf_dash_resolve_url(dash->mpd, a_rep, group, dash->base_url, GF_MPD_RESOLVE_URL_INIT, 0, &a_base_init_url, &a_start, &a_end, &a_dur, NULL, &a_rep->playback.key_url, &a_rep->playback.key_IV, &a_rep->playback.owned_gmem);
3894 			if (!e && a_base_init_url) {
3895 				e = gf_dash_download_resource(dash, &(group->segment_download), a_base_init_url, a_start, a_end, 1, group);
3896 
3897 				if ((e==GF_IP_CONNECTION_CLOSED) && group->download_abort_type) {
3898 					group->download_abort_type = 0;
3899 					GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] Aborted while downloading init segment (seek ?)%s \n", a_base_init_url));
3900 
3901 					gf_free(a_base_init_url);
3902 					return GF_OK;
3903 				}
3904 
3905 				if (e) {
3906 					GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Cannot retrieve initialization segment %s for representation: %s - discarding representation\n", a_base_init_url, gf_error_to_string(e) ));
3907 					a_rep->playback.disabled = 1;
3908 				} else {
3909 					a_rep->playback.cached_init_segment_url = gf_strdup( dash->dash_io->get_cache_name(dash->dash_io, group->segment_download) );
3910 					a_rep->playback.init_start_range = 0;
3911 					a_rep->playback.init_end_range = 0;
3912 				}
3913 				gf_free(a_base_init_url);
3914 			} else if (e) {
3915 				GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Cannot solve initialization segment for representation: %s - discarding representation\n", gf_error_to_string(e) ));
3916 				a_rep->playback.disabled = 1;
3917 			}
3918 
3919 		}
3920 	}
3921 	//reset baseURL idx to use first base URL
3922 	group->current_base_url_idx = 0;
3923 	/*if this was not an init segment, perform rate adaptation*/
3924 	if (nb_segment_read) {
3925 		dash_store_stats(dash, group, Bps, file_size, GF_FALSE);
3926 		dash_do_rate_adaptation(dash, group);
3927 	}
3928 
3929 	if (dash->atsc_clock_state) {
3930 		u32 i, j;
3931 		for (i=0; i<gf_list_count(group->adaptation_set->representations); i++) {
3932 			GF_MPD_Representation *a_rep = gf_list_get(group->adaptation_set->representations, i);
3933 			for (j=0; j<gf_list_count(a_rep->base_URLs); j++) {
3934 				GF_MPD_BaseURL *b_url = gf_list_get(a_rep->base_URLs, j);
3935 				char *nURL = gf_url_concatenate(dash->base_url, b_url->URL);
3936 				if (nURL) {
3937 					u32 len = (u32) strlen(nURL);
3938 					if (nURL[len] != '/') {
3939 						e = gf_dash_download_resource(dash, &(group->segment_download), nURL, 0, 0, 1, group);
3940 						if (!e) {
3941 							const char *redirected_url = dash->dash_io->get_url(dash->dash_io, group->segment_download);
3942 							if (redirected_url && strcmp(redirected_url, nURL)) {
3943 								b_url->redirection = gf_strdup(redirected_url);
3944 							}
3945 						}
3946 					}
3947 					gf_free(nURL);
3948 				}
3949 			}
3950 		}
3951 	}
3952 
3953 	return GF_OK;
3954 }
3955 
gf_dash_skip_disabled_representation(GF_DASH_Group * group,GF_MPD_Representation * rep,Bool for_autoswitch)3956 static void gf_dash_skip_disabled_representation(GF_DASH_Group *group, GF_MPD_Representation *rep, Bool for_autoswitch)
3957 {
3958 	s32 rep_idx, orig_idx;
3959 	u32 bandwidth = 0xFFFFFFFF;
3960 	if (for_autoswitch && group->segment_download) {
3961 		bandwidth = 8*group->dash->dash_io->get_bytes_per_sec(group->dash->dash_io, group->segment_download);
3962 	}
3963 
3964 	rep_idx = orig_idx = gf_list_find(group->adaptation_set->representations, rep);
3965 	while (1) {
3966 		rep_idx++;
3967 		if (rep_idx==gf_list_count(group->adaptation_set->representations)) rep_idx = 0;
3968 		//none other than current one
3969 		if (orig_idx==rep_idx) return;
3970 
3971 		rep = gf_list_get(group->adaptation_set->representations, rep_idx);
3972 		if (rep->playback.disabled) continue;
3973 
3974 		if (rep->bandwidth<=bandwidth) break;
3975 		assert(for_autoswitch);
3976 		//go to next rep
3977 	}
3978 	assert(rep && !rep->playback.disabled);
3979 	gf_dash_set_group_representation(group, rep);
3980 }
3981 
3982 
gf_dash_group_reset_cache_entry(segment_cache_entry * cached)3983 static void gf_dash_group_reset_cache_entry(segment_cache_entry *cached)
3984 {
3985 	gf_free(cached->cache);
3986 	gf_free(cached->url);
3987 	if (cached->key_url) gf_free(cached->key_url);
3988 	memset(cached, 0, sizeof(segment_cache_entry));
3989 }
3990 
gf_dash_group_reset(GF_DashClient * dash,GF_DASH_Group * group)3991 static void gf_dash_group_reset(GF_DashClient *dash, GF_DASH_Group *group)
3992 {
3993 	if (group->urlToDeleteNext) {
3994 		if (!dash->keep_files && !group->local_files)
3995 			if (dash->dash_io) dash->dash_io->delete_cache_file(dash->dash_io, group->segment_download, group->urlToDeleteNext);
3996 
3997 		gf_free(group->urlToDeleteNext);
3998 		group->urlToDeleteNext = NULL;
3999 	}
4000 	if (group->segment_download) {
4001 		if (dash->dash_io) dash->dash_io->del(dash->dash_io, group->segment_download);
4002 		group->segment_download = NULL;
4003 	}
4004 	while (group->nb_cached_segments) {
4005 		group->nb_cached_segments--;
4006 		if (!dash->keep_files && !group->local_files)
4007 			gf_file_delete(group->cached[group->nb_cached_segments].cache);
4008 
4009 		gf_dash_group_reset_cache_entry(&group->cached[group->nb_cached_segments]);
4010 	}
4011 
4012 	group->timeline_setup = 0;
4013 }
4014 
gf_dash_reset_groups(GF_DashClient * dash)4015 static void gf_dash_reset_groups(GF_DashClient *dash)
4016 {
4017 	/*send playback destroy event*/
4018 	if (dash->dash_io) dash->dash_io->on_dash_event(dash->dash_io, GF_DASH_EVENT_DESTROY_PLAYBACK, -1, GF_OK);
4019 
4020 	while (gf_list_count(dash->groups)) {
4021 		GF_DASH_Group *group = gf_list_last(dash->groups);
4022 		gf_list_rem_last(dash->groups);
4023 
4024 		gf_dash_group_reset(dash, group);
4025 
4026 		gf_list_del(group->groups_depending_on);
4027 		gf_free(group->cached);
4028 		if (group->service_mime)
4029 			gf_free(group->service_mime);
4030 
4031 		if (group->download_th)
4032 			gf_th_del(group->download_th);
4033 
4034 		if (group->cache_mutex)
4035 			gf_mx_del(group->cache_mutex);
4036 		if (group->bs_switching_init_segment_url)
4037 			gf_free(group->bs_switching_init_segment_url);
4038 
4039 		gf_free(group);
4040 	}
4041 	gf_list_del(dash->groups);
4042 	dash->groups = NULL;
4043 
4044 	while (gf_list_count(dash->SRDs)) {
4045 		struct _dash_srd_desc *srd = gf_list_last(dash->SRDs);
4046 		gf_list_rem_last(dash->SRDs);
4047 		gf_free(srd);
4048 	}
4049 	gf_list_del(dash->SRDs);
4050 	dash->SRDs = NULL;
4051 }
4052 
4053 #ifndef GPAC_DISABLE_LOG
gf_dash_get_start_number(GF_DASH_Group * group,GF_MPD_Representation * rep)4054 static u32 gf_dash_get_start_number(GF_DASH_Group *group, GF_MPD_Representation *rep)
4055 {
4056 	if (rep->segment_list && rep->segment_list->start_number) return rep->segment_list->start_number;
4057 	if (group->adaptation_set->segment_list && group->adaptation_set->segment_list->start_number) return group->adaptation_set->segment_list->start_number;
4058 	if (group->period->segment_list && group->period->segment_list->start_number) return group->period->segment_list->start_number;
4059 
4060 	if (rep->segment_template && rep->segment_template->start_number) return rep->segment_template->start_number;
4061 	if (group->adaptation_set->segment_template && group->adaptation_set->segment_template->start_number) return group->adaptation_set->segment_template->start_number;
4062 	if (group->period->segment_template && group->period->segment_template->start_number) return group->period->segment_template->start_number;
4063 
4064 	return 0;
4065 }
4066 #endif
4067 
gf_dash_find_rep(GF_DashClient * dash,const char * dependency_id,GF_DASH_Group ** rep_group)4068 static GF_MPD_Representation *gf_dash_find_rep(GF_DashClient *dash, const char *dependency_id, GF_DASH_Group **rep_group)
4069 {
4070 	u32 i, j, nb_groups, nb_reps;
4071 	GF_MPD_Representation *rep;
4072 
4073 	if (rep_group) *rep_group = NULL;
4074 
4075 	if (!dependency_id) return NULL;
4076 
4077 	nb_groups = gf_list_count(dash->groups);
4078 	for (i=0; i<nb_groups; i++) {
4079 		GF_DASH_Group *group = gf_list_get(dash->groups, i);
4080 		nb_reps = gf_list_count(group->adaptation_set->representations);
4081 		for (j=0; j<nb_reps; j++) {
4082 			rep = gf_list_get(group->adaptation_set->representations, j);
4083 			if (rep->id && !strcmp(rep->id, dependency_id)) {
4084 				if (rep_group) *rep_group = group;
4085 				return rep;
4086 			}
4087 		}
4088 	}
4089 	return NULL;
4090 }
4091 
4092 static
gf_dash_group_get_dependency_group(GF_DashClient * dash,u32 idx)4093 s32 gf_dash_group_get_dependency_group(GF_DashClient *dash, u32 idx)
4094 {
4095 	GF_MPD_Representation *rep;
4096 	GF_DASH_Group *group = gf_list_get(dash->groups, idx);
4097 	if (!group) return idx;
4098 
4099 	rep = gf_list_get(group->adaptation_set->representations, 0);
4100 
4101 	while (rep && rep->dependency_id) {
4102 		char *sep = strchr(rep->dependency_id, ' ');
4103 		if (sep) sep[0] = 0;
4104 		rep = gf_dash_find_rep(dash, rep->dependency_id, &group);
4105 		if (sep) sep[0] = ' ';
4106 	}
4107 	return gf_list_find(dash->groups, group);
4108 }
4109 
4110 GF_EXPORT
gf_dash_group_has_dependent_group(GF_DashClient * dash,u32 idx)4111 s32 gf_dash_group_has_dependent_group(GF_DashClient *dash, u32 idx)
4112 {
4113 	GF_DASH_Group *group = gf_list_get(dash->groups, idx);
4114 	if (!group) return GF_FALSE;
4115 	return group->depend_on_group ? gf_list_find(dash->groups, group->depend_on_group) : -1;
4116 }
4117 
4118 GF_EXPORT
gf_dash_group_get_num_groups_depending_on(GF_DashClient * dash,u32 idx)4119 u32 gf_dash_group_get_num_groups_depending_on(GF_DashClient *dash, u32 idx)
4120 {
4121 	GF_DASH_Group *group = gf_list_get(dash->groups, idx);
4122 	if (!group) return 0;
4123 	return group->groups_depending_on ? gf_list_count(group->groups_depending_on) : 0;
4124 }
4125 
4126 GF_EXPORT
gf_dash_get_dependent_group_index(GF_DashClient * dash,u32 idx,u32 group_depending_on_dep_idx)4127 s32 gf_dash_get_dependent_group_index(GF_DashClient *dash, u32 idx, u32 group_depending_on_dep_idx)
4128 {
4129 	GF_DASH_Group *group_depending_on;
4130 	GF_DASH_Group *group = gf_list_get(dash->groups, idx);
4131 	if (!group || !group->groups_depending_on) return -1;
4132 	group_depending_on = gf_list_get(group->groups_depending_on, group_depending_on_dep_idx);
4133 	if (!group_depending_on) return -1;
4134 	return gf_list_find(dash->groups, group_depending_on);
4135 }
4136 
4137 /* create groups (implementation of adaptations set) */
gf_dash_setup_groups(GF_DashClient * dash)4138 GF_Err gf_dash_setup_groups(GF_DashClient *dash)
4139 {
4140 	GF_Err e;
4141 	u32 i, j, count, nb_dependent_rep;
4142 	GF_MPD_Period *period;
4143 
4144 	if (!dash->groups) {
4145 		dash->groups = gf_list_new();
4146 		if (!dash->groups) return GF_OUT_OF_MEM;
4147 	}
4148 
4149 	period = gf_list_get(dash->mpd->periods, dash->active_period_index);
4150 	if (!period) return GF_BAD_PARAM;
4151 
4152 	count = gf_list_count(period->adaptation_sets);
4153 	for (i=0; i<count; i++) {
4154 		Double seg_dur;
4155 		GF_DASH_Group *group;
4156 		Bool found = GF_FALSE;
4157 		Bool has_dependent_representations = GF_FALSE;
4158 		GF_MPD_AdaptationSet *set = gf_list_get(period->adaptation_sets, i);
4159 		for (j=0; j<gf_list_count(dash->groups); j++) {
4160 			group = gf_list_get(dash->groups, j);
4161 			if (group->adaptation_set==set) {
4162 				found = 1;
4163 				break;
4164 			}
4165 		}
4166 
4167 		if (found) continue;
4168 
4169 		if (! gf_list_count(set->representations)) {
4170 			GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] Empty adaptation set found (ID %s) - ignoring\n", set->id));
4171 			continue;
4172 		}
4173 
4174 
4175 		GF_SAFEALLOC(group, GF_DASH_Group);
4176 		if (!group) return GF_OUT_OF_MEM;
4177 		group->dash = dash;
4178 		group->adaptation_set = set;
4179 		group->period = period;
4180 		if (dash->thread_mode) {
4181 			if (dash->thread_mode == GF_DASH_THREAD_ALL)
4182 				group->download_th = gf_th_new("DashGroupDownload");
4183 
4184 			group->cache_mutex = gf_mx_new("DashGroupMutex");
4185 		}
4186 
4187 		group->bitstream_switching = (set->bitstream_switching || period->bitstream_switching) ? GF_TRUE : GF_FALSE;
4188 
4189 		seg_dur = 0;
4190 		nb_dependent_rep = 0;
4191 		for (j=0; j<gf_list_count(set->representations); j++) {
4192 			Double dur;
4193 			u32 nb_seg, k;
4194 			Bool cp_supported;
4195 			GF_MPD_Representation *rep = gf_list_get(set->representations, j);
4196 			gf_dash_get_segment_duration(rep, set, period, dash->mpd, &nb_seg, &dur);
4197 			if (dur>seg_dur) seg_dur = dur;
4198 
4199 			if (group->bitstream_switching && (set->segment_base || period->segment_base || rep->segment_base) ) {
4200 				group->bitstream_switching = GF_FALSE;
4201 				GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] bitstreamSwitching set for onDemand content - ignoring flag\n"));
4202 			}
4203 
4204 			if (dash->dash_io->dash_codec_supported) {
4205 				Bool res = dash->dash_io->dash_codec_supported(dash->dash_io, rep->codecs, rep->width, rep->height, (rep->scan_type==GF_MPD_SCANTYPE_INTERLACED) ? 1 : 0, rep->framerate ? rep->framerate->num : 0, rep->framerate ? rep->framerate->den : 0, rep->samplerate);
4206 				if (!res) {
4207 					GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] Representation not supported by playback engine - ignoring\n"));
4208 					rep->playback.disabled = 1;
4209 					continue;
4210 				}
4211 			}
4212 			//filter out everything above HD
4213 			if ((dash->max_width>2000) && (dash->max_height>2000)) {
4214 				if ((rep->width>dash->max_width) || (rep->height>dash->max_height)) {
4215 					GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] Representation size %dx%d exceeds max display size allowed %dx%d - ignoring\n", rep->width, rep->height, dash->max_width, dash->max_height));
4216 					rep->playback.disabled = 1;
4217 					continue;
4218 				}
4219 			}
4220 			if (rep->codecs && (dash->max_bit_per_pixel > 8) ) {
4221 				char *vid_type = strstr(rep->codecs, "hvc");
4222 				if (!vid_type) vid_type = strstr(rep->codecs, "hev");
4223 				if (!vid_type) vid_type = strstr(rep->codecs, "avc");
4224 				if (!vid_type) vid_type = strstr(rep->codecs, "svc");
4225 				if (!vid_type) vid_type = strstr(rep->codecs, "mvc");
4226 				//HEVC
4227 				if (vid_type && (!strnicmp(rep->codecs, "hvc", 3) || !strnicmp(rep->codecs, "hev", 3))) {
4228 					char *pidc = rep->codecs+5;
4229 					if ((pidc[0]=='A') || (pidc[0]=='B') || (pidc[0]=='C')) pidc++;
4230 					//Main 10 !!
4231 					if (!strncmp(pidc, "2.", 2)) {
4232 						rep->playback.disabled = 1;
4233 						GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] Representation bit depth higher than max display bit depth - ignoring\n"));
4234 						continue;
4235 					}
4236 				}
4237 				//AVC
4238 				if (vid_type && (!strnicmp(rep->codecs, "avc", 3) || !strnicmp(rep->codecs, "svc", 3) || !strnicmp(rep->codecs, "mvc", 3))) {
4239 					char prof_string[3];
4240 					u8 prof;
4241 					strncpy(prof_string, vid_type+5, 2);
4242 					prof_string[2]=0;
4243 					prof = atoi(prof_string);
4244 					//Main 10
4245 					if (prof==0x6E) {
4246 						rep->playback.disabled = 1;
4247 						GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] Representation bit depth higher than max display bit depth - ignoring\n"));
4248 						continue;
4249 					}
4250 				}
4251 			}
4252 
4253 			for (k=0; k<gf_list_count(rep->essential_properties); k++) {
4254 				GF_MPD_Descriptor *mpd_desc = gf_list_get(rep->essential_properties, k);
4255 
4256 				//we don't know any defined scheme for now
4257 				if (! strstr(mpd_desc->scheme_id_uri, "gpac") ) {
4258 					GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] Representation with unrecognized EssentialProperty %s - ignoring because not supported\n", mpd_desc->scheme_id_uri));
4259 					rep->playback.disabled = 1;
4260 					break;
4261 				}
4262 			}
4263 			if (rep->playback.disabled) {
4264 				continue;
4265 			}
4266 
4267 			cp_supported = 1;
4268 			for (k=0; k<gf_list_count(rep->content_protection); k++) {
4269 				GF_MPD_Descriptor *mpd_desc = gf_list_get(rep->content_protection, k);
4270 				//we don't know any defined scheme for now
4271 				if (strcmp(mpd_desc->scheme_id_uri, "urn:mpeg:dash:mp4protection:2011")) {
4272 					GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] Representation with unrecognized ContentProtection %s\n", mpd_desc->scheme_id_uri));
4273 					cp_supported = 0;
4274 				} else {
4275 					cp_supported = 1;
4276 					break;
4277 				}
4278 			}
4279 			if (!cp_supported) {
4280 				GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] Representation with no supported ContentProtection\n"));
4281 				rep->playback.disabled = 1;
4282 				continue;
4283 			}
4284 
4285 			rep->playback.disabled = 0;
4286 			if (rep->width>set->max_width) {
4287 				set->max_width = rep->width;
4288 				set->max_height = rep->height;
4289 			}
4290 			if (rep->dependency_id && strlen(rep->dependency_id))
4291 				has_dependent_representations = GF_TRUE;
4292 			else
4293 				group->base_rep_index_plus_one = j+1;
4294 			rep->playback.enhancement_rep_index_plus_one = 0;
4295 			for (k = 0; k < gf_list_count(set->representations); k++) {
4296 				GF_MPD_Representation *a_rep = gf_list_get(set->representations, k);
4297 				if (a_rep->dependency_id) {
4298 					char * tmp = strrchr(a_rep->dependency_id, ' ');
4299 					if (tmp)
4300 						tmp = tmp + 1;
4301 					else
4302 						tmp = a_rep->dependency_id;
4303 					if (!strcmp(tmp, rep->id))
4304 						rep->playback.enhancement_rep_index_plus_one = k + 1;
4305 				}
4306 			}
4307 			if (!rep->playback.enhancement_rep_index_plus_one)
4308 				group->max_complementary_rep_index = j;
4309 			if (!rep->playback.disabled && rep->dependency_id)
4310 				nb_dependent_rep++;
4311 		}
4312 
4313 		if (!seg_dur && !dash->is_m3u8) {
4314 			GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] Cannot compute default segment duration\n"));
4315 		}
4316 
4317 		group->cache_duration = dash->max_cache_duration;
4318 		if (group->cache_duration < dash->mpd->min_buffer_time)
4319 			group->cache_duration = dash->mpd->min_buffer_time;
4320 
4321 		//we want at least 2 segments available in the cache, in order to perform rate adaptation with one cache ahead
4322 		group->max_cached_segments = 2;
4323 		if (seg_dur) {
4324 			while (group->max_cached_segments * seg_dur * 1000 < group->cache_duration)
4325 				group->max_cached_segments ++;
4326 
4327 			group->max_buffer_segments = group->max_cached_segments;
4328 
4329 #if 0
4330 			/*unless we are in low latency modes*/
4331 			if (dash->max_cache_duration>1000) {
4332 				/*we need one more entry in cache for segment being currently played*/
4333 				if (group->max_cached_segments<3)
4334 					group->max_cached_segments ++;
4335 			}
4336 #endif
4337 			group->max_cached_segments *= (nb_dependent_rep+1);
4338 			group->max_buffer_segments *= (nb_dependent_rep+1);
4339 		}
4340 
4341 		if (!dash->thread_mode) {
4342 			group->max_cached_segments = (nb_dependent_rep+1);
4343 			group->max_buffer_segments = (nb_dependent_rep+1);
4344 		}
4345 
4346 		if (!has_dependent_representations)
4347 			group->base_rep_index_plus_one = 0; // all representations in this group are independent
4348 
4349 		if (group->max_cached_segments>50) {
4350 			GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] Too many cached segments (%d), segment duration %g - using 50 max\n", group->max_cached_segments, seg_dur));
4351 			group->max_cached_segments = 50;
4352 		}
4353 		group->cached = gf_malloc(sizeof(segment_cache_entry)*group->max_cached_segments);
4354 		memset(group->cached, 0, sizeof(segment_cache_entry)*group->max_cached_segments);
4355 		if (!group->cached) {
4356 			gf_free(group);
4357 			return GF_OUT_OF_MEM;
4358 		}
4359 		e = gf_list_add(dash->groups, group);
4360 		if (e) {
4361 			gf_free(group->cached);
4362 			gf_free(group);
4363 			return e;
4364 		}
4365 	}
4366 
4367 
4368 	count = gf_list_count(dash->groups);
4369 	for (i=0; i<count; i++) {
4370 		GF_DASH_Group *group = gf_list_get(dash->groups, i);
4371 		j = gf_dash_group_get_dependency_group(dash, i);
4372 		if (i != j) {
4373 			GF_DASH_Group *base_group = gf_list_get(dash->groups, j);
4374 			assert(base_group);
4375 			group->depend_on_group = base_group;
4376 			if (!base_group->groups_depending_on) {
4377 				base_group->groups_depending_on = gf_list_new();
4378 			}
4379 			gf_list_add(base_group->groups_depending_on, group);
4380 		}
4381 	}
4382 
4383 	for (i=0; i<count; i++) {
4384 		GF_DASH_Group *group = gf_list_get(dash->groups, i);
4385 		if (group->groups_depending_on) {
4386 			u32 nb_dep_groups = gf_list_count(group->groups_depending_on);
4387 			//all dependent groups will be stored in the base group
4388 			group->max_cached_segments *= (1+nb_dep_groups);
4389 			group->max_buffer_segments *= (1+nb_dep_groups);
4390 			group->cached = gf_realloc(group->cached, sizeof(segment_cache_entry)*group->max_cached_segments);
4391 			memset(group->cached, 0, sizeof(segment_cache_entry)*group->max_cached_segments);
4392 
4393 			for (j=0; j<nb_dep_groups; j++) {
4394 				GF_DASH_Group *dep_group = gf_list_get(group->groups_depending_on, j);
4395 
4396 				dep_group->max_cached_segments = 0;
4397 
4398 				/* the rest of the code assumes that at least group->cached[0] is allocated */
4399 				dep_group->cached = gf_realloc(dep_group->cached, sizeof(segment_cache_entry));
4400 				memset(dep_group->cached, 0, sizeof(segment_cache_entry));
4401 
4402 			}
4403 		}
4404 	}
4405 
4406 	return GF_OK;
4407 }
4408 
gf_dash_load_sidx(GF_BitStream * bs,GF_MPD_Representation * rep,Bool separate_index,u64 sidx_offset)4409 static GF_Err gf_dash_load_sidx(GF_BitStream *bs, GF_MPD_Representation *rep, Bool separate_index, u64 sidx_offset)
4410 {
4411 #ifdef GPAC_DISABLE_ISOM
4412 	return GF_NOT_SUPPORTED;
4413 #else
4414 	u64 anchor_position, prev_pos;
4415 	GF_SegmentIndexBox *sidx = NULL;
4416 	u32 i, size, type;
4417 	GF_Err e;
4418 	u64 offset;
4419 
4420 	prev_pos = gf_bs_get_position(bs);
4421 	gf_bs_seek(bs, sidx_offset);
4422 	size = gf_bs_read_u32(bs);
4423 	type = gf_bs_read_u32(bs);
4424 	if (type != GF_ISOM_BOX_TYPE_SIDX) {
4425 		GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Error parsing SIDX: type is %s (box start offset "LLD")\n", gf_4cc_to_str(type), gf_bs_get_position(bs)-8 ));
4426 		return GF_ISOM_INVALID_FILE;
4427 	}
4428 
4429 	gf_bs_seek(bs, sidx_offset);
4430 
4431 	anchor_position = sidx_offset + size;
4432 	if (separate_index)
4433 		anchor_position = 0;
4434 
4435 	e = gf_isom_box_parse((GF_Box **) &sidx, bs);
4436 	if (e) return e;
4437 
4438 	GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Loading SIDX - %d entries - Earliest Presentation Time "LLD"\n", sidx->nb_refs, sidx->earliest_presentation_time));
4439 
4440 	offset = sidx->first_offset + anchor_position;
4441 	rep->segment_list->timescale = sidx->timescale;
4442 	for (i=0; i<sidx->nb_refs; i++) {
4443 		if (sidx->refs[i].reference_type) {
4444 			e = gf_dash_load_sidx(bs, rep, separate_index, offset);
4445 			if (e) {
4446 				break;
4447 			}
4448 		} else {
4449 			GF_MPD_SegmentURL *seg;
4450 			GF_SAFEALLOC(seg, GF_MPD_SegmentURL);
4451 			if (!seg) return GF_OUT_OF_MEM;
4452 			GF_SAFEALLOC(seg->media_range, GF_MPD_ByteRange);
4453 			if (!seg->media_range) return GF_OUT_OF_MEM;
4454 			GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Found media segment size %d - duration %d - start with SAP: %d - SAP type %d - SAP Deltat Time %d\n",
4455 			                                   sidx->refs[i].reference_size, sidx->refs[i].subsegment_duration, sidx->refs[i].starts_with_SAP, sidx->refs[i].SAP_type, sidx->refs[i].SAP_delta_time));
4456 
4457 			seg->media_range->start_range = offset;
4458 			offset += sidx->refs[i].reference_size;
4459 			seg->media_range->end_range = offset - 1;
4460 			seg->duration = sidx->refs[i].subsegment_duration;
4461 			gf_list_add(rep->segment_list->segment_URLs, seg);
4462 		}
4463 	}
4464 	gf_isom_box_del((GF_Box*)sidx);
4465 	gf_bs_seek(bs, prev_pos);
4466 	return e;
4467 #endif
4468 }
4469 
gf_dash_load_representation_sidx(GF_DASH_Group * group,GF_MPD_Representation * rep,const char * cache_name,Bool separate_index,Bool needs_mov_range)4470 static GF_Err gf_dash_load_representation_sidx(GF_DASH_Group *group, GF_MPD_Representation *rep, const char *cache_name, Bool separate_index, Bool needs_mov_range)
4471 {
4472 	GF_Err e;
4473 	GF_BitStream *bs;
4474 	FILE *f=NULL;
4475 	if (!cache_name) return GF_BAD_PARAM;
4476 
4477 	if (!strncmp(cache_name, "gmem://", 7)) {
4478 		u32 size;
4479 		u8 *mem_address;
4480 		e = gf_blob_get_data(cache_name, &mem_address, &size);
4481 		if (e) return e;
4482 
4483 		bs = gf_bs_new(mem_address, size, GF_BITSTREAM_READ);
4484 	} else {
4485 		f = gf_fopen(cache_name, "rb");
4486 		if (!f) return GF_IO_ERR;
4487 		bs = gf_bs_from_file(f, GF_BITSTREAM_READ);
4488 	}
4489 	e = GF_OK;
4490 	while (gf_bs_available(bs)) {
4491 		u32 size = gf_bs_read_u32(bs);
4492 		u32 type = gf_bs_read_u32(bs);
4493 		if (type != GF_ISOM_BOX_TYPE_SIDX) {
4494 			gf_bs_skip_bytes(bs, size-8);
4495 
4496 			if (needs_mov_range && (type==GF_ISOM_BOX_TYPE_MOOV )) {
4497 				GF_SAFEALLOC(rep->segment_list->initialization_segment->byte_range, GF_MPD_ByteRange);
4498 				if (rep->segment_list->initialization_segment->byte_range)
4499 					rep->segment_list->initialization_segment->byte_range->end_range = gf_bs_get_position(bs);
4500 			}
4501 			continue;
4502 		}
4503 		gf_bs_seek(bs, gf_bs_get_position(bs)-8);
4504 		e = gf_dash_load_sidx(bs, rep, separate_index, gf_bs_get_position(bs) );
4505 
4506 		/*we could also parse the sub sidx*/
4507 		break;
4508 	}
4509 	gf_bs_del(bs);
4510 	if (f) gf_fclose(f);
4511 	return e;
4512 }
4513 
dash_load_box_type(const char * cache_name,u32 offset,u32 * box_type,u32 * box_size)4514 static GF_Err dash_load_box_type(const char *cache_name, u32 offset, u32 *box_type, u32 *box_size)
4515 {
4516 	*box_type = *box_size = 0;
4517 	if (!strncmp(cache_name, "gmem://", 7)) {
4518 		GF_Err e;
4519 		u32 size;
4520 		u8 *mem_address;
4521 		e = gf_blob_get_data(cache_name, &mem_address, &size);
4522 		if (e) return e;
4523 
4524 		if (offset+8 > size)
4525 			return GF_IO_ERR;
4526 		mem_address += offset;
4527 		*box_size = GF_4CC(mem_address[0], mem_address[1], mem_address[2], mem_address[3]);
4528 		*box_type = GF_4CC(mem_address[4], mem_address[5], mem_address[6], mem_address[7]);
4529 	} else {
4530 		unsigned char data[4];
4531 		FILE *f = gf_fopen(cache_name, "rb");
4532 		if (!f) return GF_IO_ERR;
4533 		if (gf_fseek(f, offset, SEEK_SET))
4534 			return GF_IO_ERR;
4535 		if (gf_fread(data, 4, f) == 4) {
4536 			*box_size = GF_4CC(data[0], data[1], data[2], data[3]);
4537 			if (gf_fread(data, 4, f) == 4) {
4538 				*box_type = GF_4CC(data[0], data[1], data[2], data[3]);
4539 			}
4540 		}
4541 		gf_fclose(f);
4542 	}
4543 	return GF_OK;
4544 }
4545 
4546 extern void gf_mpd_segment_template_free(void *_item);
gf_dash_setup_single_index_mode(GF_DASH_Group * group)4547 static GF_Err gf_dash_setup_single_index_mode(GF_DASH_Group *group)
4548 {
4549 	u32 i;
4550 	GF_Err e = GF_OK;
4551 	char *init_url = NULL;
4552 	char *index_url = NULL;
4553 	GF_DASHFileIOSession *download_sess = &group->segment_download;
4554 	GF_MPD_Representation *rep = gf_list_get(group->adaptation_set->representations, 0);
4555 
4556 	if (!group->dash->thread_mode) download_sess = &group->dash->mpd_dnload;
4557 
4558 	if (!rep->segment_base && !group->adaptation_set->segment_base && !group->period->segment_base) {
4559 		if (rep->segment_template || group->adaptation_set->segment_template || group->period->segment_template) return GF_OK;
4560 		if (rep->segment_list || group->adaptation_set->segment_list || group->period->segment_list) return GF_OK;
4561 	} else {
4562 		char *profile = rep->profiles;
4563 		if (!profile) profile = group->adaptation_set->profiles;
4564 		if (!profile) profile = group->dash->mpd->profiles;
4565 
4566 		//if on-demand cleanup all segment templates and segment list if we have base URLs
4567 		if (profile && strstr(profile, "on-demand")) {
4568 			u32 nb_rem=0;
4569 			if (rep->segment_template) {
4570 				nb_rem++;
4571 				gf_mpd_segment_template_free(rep->segment_template);
4572 				rep->segment_template = NULL;
4573 			}
4574 			if (group->adaptation_set->segment_template) {
4575 				nb_rem++;
4576 				gf_mpd_segment_template_free(group->adaptation_set->segment_template);
4577 				group->adaptation_set->segment_template = NULL;
4578 			}
4579 
4580 			if (group->period->segment_template) {
4581 				nb_rem++;
4582 				gf_mpd_segment_template_free(group->period->segment_template);
4583 				group->period->segment_template = NULL;
4584 			}
4585 			if (nb_rem) {
4586 				GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] SegmentTemplate present for on-demand with SegmentBase present - skipping SegmentTemplate\n"));
4587 			}
4588 			nb_rem=0;
4589 			if (rep->segment_list) {
4590 				nb_rem++;
4591 				gf_mpd_segment_template_free(rep->segment_list);
4592 				rep->segment_list = NULL;
4593 			}
4594 			if (group->adaptation_set->segment_list) {
4595 				nb_rem++;
4596 				gf_mpd_segment_template_free(group->adaptation_set->segment_list);
4597 				group->adaptation_set->segment_list = NULL;
4598 			}
4599 			if (group->period->segment_list) {
4600 				nb_rem++;
4601 				gf_mpd_segment_template_free(group->period->segment_list);
4602 				group->period->segment_list = NULL;
4603 			}
4604 			if (nb_rem) {
4605 				GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] SegmentList present for on-demand with SegmentBase present - skipping SegmentList\n"));
4606 			}
4607 		}
4608 	}
4609 
4610 	/*OK we are in single-file mode, download all required indexes & co*/
4611 	for (i=0; i<gf_list_count(group->adaptation_set->representations); i++) {
4612 		char *sidx_file = NULL;
4613 		u64 duration, index_start_range = 0, index_end_range = 0, init_start_range, init_end_range;
4614 		Bool index_in_base, init_in_base;
4615 		Bool init_needs_byte_range = GF_FALSE;
4616 		Bool has_seen_sidx = GF_FALSE;
4617 		Bool is_isom = GF_TRUE;
4618 		rep = gf_list_get(group->adaptation_set->representations, i);
4619 
4620 		index_in_base = init_in_base = GF_FALSE;
4621 		e = gf_dash_resolve_url(group->dash->mpd, rep, group, group->dash->base_url, GF_MPD_RESOLVE_URL_INIT, 0, &init_url, &init_start_range, &init_end_range, &duration, &init_in_base, NULL, NULL, NULL);
4622 		if (e) goto exit;
4623 
4624 		e = gf_dash_resolve_url(group->dash->mpd, rep, group, group->dash->base_url, GF_MPD_RESOLVE_URL_INDEX, 0, &index_url, &index_start_range, &index_end_range, &duration, &index_in_base, NULL, NULL, NULL);
4625 		if (e) goto exit;
4626 
4627 
4628 		if (is_isom && (init_in_base || index_in_base)) {
4629 			if (!strstr(init_url, "://") || (!strnicmp(init_url, "file://", 7) ) ) {
4630 				GF_SAFEALLOC(rep->segment_list, GF_MPD_SegmentList);
4631 				if (!rep->segment_list) {
4632 					e = GF_OUT_OF_MEM;
4633 					goto exit;
4634 				}
4635 				rep->segment_list->segment_URLs = gf_list_new();
4636 
4637 				if (init_in_base) {
4638 					GF_SAFEALLOC(rep->segment_list->initialization_segment, GF_MPD_URL);
4639 					if (!rep->segment_list->initialization_segment) {
4640 						e = GF_OUT_OF_MEM;
4641 						goto exit;
4642 					}
4643 					rep->segment_list->initialization_segment->sourceURL = gf_strdup(init_url);
4644 					rep->segment_list->initialization_segment->is_resolved = GF_TRUE;
4645 					/*we don't want to load the entire movie */
4646 					init_needs_byte_range = 1;
4647 				}
4648 				if (index_in_base) {
4649 					sidx_file = (char *)init_url;
4650 				}
4651 			}
4652 			/*we need to download the init segment, at least partially*/
4653 			else {
4654 				u32 offset = 0;
4655 				u32 box_type = 0;
4656 				u32 box_size = 0;
4657 				u32 sidx_start = 0;
4658 				const char *cache_name;
4659 
4660 				GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] Downloading init segment and SIDX for representation %s\n", init_url));
4661 
4662 				/*download first 8 bytes and check if we do have a box starting there*/
4663 				e = gf_dash_download_resource(group->dash, download_sess, init_url, offset, 7, 1, group);
4664 				if (e) goto exit;
4665 				cache_name = group->dash->dash_io->get_cache_name(group->dash->dash_io, *download_sess);
4666 				e = dash_load_box_type(cache_name, offset, &box_type, &box_size);
4667 				offset = 8;
4668 				while (box_type) {
4669 					/*we got the moov, stop here */
4670 					if (!index_in_base && (box_type==GF_ISOM_BOX_TYPE_MOOV)) {
4671 						e = gf_dash_download_resource(group->dash, download_sess, init_url, offset, offset+box_size-9, 2, group);
4672 						break;
4673 					} else {
4674 						const u32 offset_ori = offset;
4675 						e = gf_dash_download_resource(group->dash, download_sess, init_url, offset, offset+box_size-1, 2, group);
4676 						if (e < 0) goto exit;
4677 						offset += box_size;
4678 						/*we need to refresh the cache name because of our memory astorage thing ...*/
4679 						cache_name = group->dash->dash_io->get_cache_name(group->dash->dash_io, *download_sess);
4680 						e = dash_load_box_type(cache_name, offset-8, &box_type, &box_size);
4681 						if (e == GF_IO_ERR) {
4682 							/*if the socket was closed then gf_dash_download_resource() with gmem:// was reset - retry*/
4683 							e = dash_load_box_type(cache_name, offset-offset_ori-8, &box_type, &box_size);
4684 							if (box_type == GF_ISOM_BOX_TYPE_SIDX) {
4685 								offset -= 8;
4686 								/*FIXME sidx found, reload the full resource*/
4687 								GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] have to re-downloading init and SIDX for rep %s\n", init_url));
4688 								e = gf_dash_download_resource(group->dash, download_sess, init_url, 0, offset+box_size-1, 2, group);
4689 								break;
4690 							}
4691 						}
4692 
4693 						if (box_type == GF_ISOM_BOX_TYPE_SIDX) {
4694 							if (!sidx_start) sidx_start = offset;
4695 							has_seen_sidx = 1;
4696 						} else if (has_seen_sidx)
4697 							break;
4698 					}
4699 				}
4700 				if (e < 0) goto exit;
4701 
4702 				if (box_type == 0) {
4703 					e = GF_ISOM_INVALID_FILE;
4704 					goto exit;
4705 				}
4706 				GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] Done downloading init segment and SIDX\n"));
4707 
4708 				GF_SAFEALLOC(rep->segment_list, GF_MPD_SegmentList);
4709 				if (!rep->segment_list) {
4710 					e = GF_OUT_OF_MEM;
4711 					goto exit;
4712 				}
4713 				rep->segment_list->segment_URLs = gf_list_new();
4714 
4715 				cache_name = group->dash->dash_io->get_cache_name(group->dash->dash_io, *download_sess);
4716 				if (init_in_base) {
4717 					char szName[100];
4718 					GF_SAFEALLOC(rep->segment_list->initialization_segment, GF_MPD_URL);
4719 					if (!rep->segment_list->initialization_segment) {
4720 						e = GF_OUT_OF_MEM;
4721 						goto exit;
4722 					}
4723 
4724 					if (!group->dash->thread_mode) {
4725 						rep->segment_list->initialization_segment->sourceURL = gf_strdup(init_url);
4726 						GF_SAFEALLOC(rep->segment_list->initialization_segment->byte_range, GF_MPD_ByteRange);
4727 						if (rep->segment_list->initialization_segment->byte_range) {
4728 							rep->segment_list->initialization_segment->byte_range->start_range = init_start_range;
4729 							rep->segment_list->initialization_segment->byte_range->end_range = init_end_range ? init_end_range : (sidx_start-1);
4730 						}
4731 					}
4732 					//we need to store the init segment since it has the same name as the rest of the segments and will be destroyed when cleaning up the cache ..
4733 					else if (!strnicmp(cache_name, "gmem://", 7)) {
4734 						u8 *mem_address;
4735 						e = gf_blob_get_data(cache_name, &mem_address, &rep->playback.init_segment.size);
4736 						if (e) {
4737 							goto exit;
4738 						}
4739 						rep->playback.init_segment.data = gf_malloc(sizeof(char) * rep->playback.init_segment.size);
4740 						memcpy(rep->playback.init_segment.data, mem_address, sizeof(char) * rep->playback.init_segment.size);
4741 
4742 						sprintf(szName, "gmem://%p", &rep->playback.init_segment);
4743 						rep->segment_list->initialization_segment->sourceURL = gf_strdup(szName);
4744 						rep->segment_list->initialization_segment->is_resolved = GF_TRUE;
4745 					} else {
4746 						FILE *t = gf_fopen(cache_name, "rb");
4747 						if (t) {
4748 							s32 res;
4749 							rep->playback.init_segment.size = (u32) gf_fsize(t);
4750 
4751 							rep->playback.init_segment.data = gf_malloc(sizeof(char) * rep->playback.init_segment.size);
4752 							res = (s32) gf_fread(rep->playback.init_segment.data, rep->playback.init_segment.size, t);
4753 							if (res != rep->playback.init_segment.size) {
4754 								GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Failed to store init segment\n"));
4755 							} else if (rep->segment_list && rep->segment_list->initialization_segment) {
4756 								sprintf(szName, "gmem://%p", &rep->playback.init_segment);
4757 								rep->segment_list->initialization_segment->sourceURL = gf_strdup(szName);
4758 								rep->segment_list->initialization_segment->is_resolved = GF_TRUE;
4759 							}
4760 						}
4761 					}
4762 					if (group->dash->thread_mode) {
4763 						cache_name = rep->segment_list->initialization_segment->sourceURL;
4764 						//cleanup cache right away
4765 						group->dash->dash_io->delete_cache_file(group->dash->dash_io, *download_sess, init_url);
4766 					}
4767 
4768 				}
4769 				if (index_in_base) {
4770 					sidx_file = (char *)cache_name;
4771 				}
4772 			}
4773 		}
4774 		/*we have index url, download it*/
4775 		if (! index_in_base) {
4776 			e = gf_dash_download_resource(group->dash, download_sess, index_url, index_start_range, index_end_range, 1, group);
4777 			if (e) goto exit;
4778 			sidx_file = (char *)group->dash->dash_io->get_cache_name(group->dash->dash_io, *download_sess);
4779 		}
4780 
4781 		/*load sidx*/
4782 		e = gf_dash_load_representation_sidx(group, rep, sidx_file, !index_in_base, init_needs_byte_range);
4783 		if (e) {
4784 			rep->playback.disabled = 1;
4785 			GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Failed to load segment index for this representation - disabling\n"));
4786 		}
4787 
4788 		if (!group->dash->thread_mode) {
4789 			//cleanup cache right away
4790 			group->dash->dash_io->delete_cache_file(group->dash->dash_io, *download_sess, init_url);
4791 		}
4792 
4793 		/*reset all seg based stuff*/
4794 		if (rep->segment_base) {
4795 			gf_mpd_segment_base_free(rep->segment_base);
4796 			rep->segment_base = NULL;
4797 		}
4798 
4799 		gf_free(index_url);
4800 		index_url = NULL;
4801 		gf_free(init_url);
4802 		init_url = NULL;
4803 	}
4804 	if (group->adaptation_set->segment_base) {
4805 		gf_mpd_segment_base_free(group->adaptation_set->segment_base);
4806 		group->adaptation_set->segment_base = NULL;
4807 	}
4808 	group->was_segment_base = 1;
4809 
4810 exit:
4811 	if (init_url) gf_free(init_url);
4812 	if (index_url) gf_free(index_url);
4813 	return e;
4814 }
4815 
gf_dash_solve_period_xlink(GF_DashClient * dash,GF_List * period_list,u32 period_idx)4816 static void gf_dash_solve_period_xlink(GF_DashClient *dash, GF_List *period_list, u32 period_idx)
4817 {
4818 	u32 count, i;
4819 	GF_Err e;
4820 	u64 start = 0;
4821 	u64 src_duration = 0;
4822 	Bool is_local=GF_FALSE;
4823 	const char *local_url;
4824 	char *url, *period_xlink;
4825 	GF_DOMParser *parser;
4826 	GF_MPD *new_mpd;
4827 	GF_MPD_Period *period;
4828 	GF_DASHFileIOSession xlink_sess=NULL;
4829 
4830 	if (dash->dash_mutex) gf_mx_p(dash->dash_mutex);
4831 
4832 	period = gf_list_get(period_list, period_idx);
4833 	if (!period->xlink_href || (dash->atsc_clock_state==1)) {
4834 		if (dash->dash_mutex) gf_mx_v(dash->dash_mutex);
4835 		return;
4836 	}
4837 	start = period->start;
4838 	GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] Resolving period XLINK %s\n", period->xlink_href));
4839 
4840 	if (!strcmp(period->xlink_href, "urn:mpeg:dash:resolve-to-zero:2013")) {
4841 		//spec is not very clear here, I suppose it means "remove the element"
4842 		gf_list_rem(period_list, period_idx);
4843 		gf_mpd_period_free(period);
4844 		if (dash->dash_mutex) gf_mx_v(dash->dash_mutex);
4845 		return;
4846 	}
4847 
4848 	//ATSC puts a tag in front of the url ("tag:atsc.org,2016:xlink") - in case others decide to follow this crazy example, whe search for http:// or https://
4849 	period_xlink = strstr(period->xlink_href, "http://");
4850 	if (!period_xlink) period_xlink = strstr(period->xlink_href, "HTTP://");
4851 	if (!period_xlink) period_xlink = strstr(period->xlink_href, "https://");
4852 	if (!period_xlink) period_xlink = strstr(period->xlink_href, "HTTPS://");
4853 	if (!period_xlink) period_xlink = period->xlink_href;
4854 
4855 	//xlink relative to our MPD base URL
4856 	url = gf_url_concatenate(dash->base_url, period_xlink);
4857 
4858 	if (!strstr(url, "://") || !strnicmp(url, "file://", 7) ) {
4859 		local_url = url;
4860 		is_local=GF_TRUE;
4861 		e = GF_OK;
4862 	} else {
4863 		if (dash->query_string) {
4864 			char *full_url;
4865 			char *purl, *sep;
4866 			u32 len;
4867 			purl = url ? url : period_xlink;
4868 			len = (u32) (2 + strlen(purl) + strlen(dash->query_string) + (period->ID ? strlen(period->ID) : 0 ) );
4869 			full_url = gf_malloc(sizeof(char)*len);
4870 
4871 			strcpy(full_url, purl);
4872 			if (strchr(purl, '?')) strcat(full_url, "&");
4873 			else strcat(full_url, "?");
4874 
4875 			//append the query string
4876 			strcat(full_url, dash->query_string);
4877 			//if =PID is given, replace by period ID
4878 			sep = strstr(dash->query_string, "=PID");
4879 			if (sep && period->ID) {
4880 				char *sep2 = strstr(full_url, "=PID");
4881 				assert(sep2);
4882 				sep2[1] = 0;
4883 				strcat(full_url, period->ID);
4884 				strcat(full_url, sep+4);
4885 			}
4886 
4887 			/*use non-persistent connection for MPD*/
4888 			e = gf_dash_download_resource(dash, &xlink_sess, full_url, 0, 0, 0, NULL);
4889 			gf_free(full_url);
4890 
4891 		} else {
4892 			/*use non-persistent connection for MPD*/
4893 			e = gf_dash_download_resource(dash, &xlink_sess, url ? url : period_xlink, 0, 0, 0, NULL);
4894 		}
4895 	}
4896 
4897 	if (e) {
4898 		GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Error - cannot download xlink from periods %s: error %s\n", period->xlink_href, gf_error_to_string(e)));
4899 		gf_free(period->xlink_href);
4900 		period->xlink_href = NULL;
4901 		if (dash->dash_mutex) gf_mx_v(dash->dash_mutex);
4902 		if (xlink_sess) dash->dash_io->del(dash->dash_io, xlink_sess);
4903 		if (url) gf_free(url);
4904 		return;
4905 	}
4906 
4907 	if (!is_local) {
4908 		/*in case the session has been restarted, local_url may have been destroyed - get it back*/
4909 		local_url = dash->dash_io->get_cache_name(dash->dash_io, xlink_sess);
4910 	}
4911 
4912 	/* parse the MPD */
4913 	parser = gf_xml_dom_new();
4914 	e = gf_xml_dom_parse(parser, local_url, NULL, NULL);
4915 	if (url) gf_free(url);
4916 	url = NULL;
4917 
4918 	if (xlink_sess) {
4919 		//get redirected URL
4920 		url = (char *) dash->dash_io->get_url(dash->dash_io, xlink_sess);
4921 		if (url) url = gf_strdup(url);
4922 		dash->dash_io->del(dash->dash_io, xlink_sess);
4923 	}
4924 	if (e != GF_OK) {
4925 		gf_xml_dom_del(parser);
4926 		GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Error - cannot parse xlink periods: error in XML parsing %s\n", gf_error_to_string(e)));
4927 		gf_free(period->xlink_href);
4928 		period->xlink_href = NULL;
4929 		if (dash->dash_mutex) gf_mx_v(dash->dash_mutex);
4930 		if (url) gf_free(url);
4931 		return;
4932 	}
4933 	new_mpd = gf_mpd_new();
4934 
4935 	count = gf_xml_dom_get_root_nodes_count(parser);
4936 	for (i=0; i<count; i++) {
4937 		GF_XMLNode *root = gf_xml_dom_get_root_idx(parser, i);
4938 		if (i) {
4939 			e = gf_mpd_complete_from_dom(root, new_mpd, period->xlink_href);
4940 		} else {
4941 			e = gf_mpd_init_from_dom(root, new_mpd, period->xlink_href);
4942 		}
4943 		if (e) break;
4944 	}
4945 	gf_xml_dom_del(parser);
4946 	if (e) {
4947 		gf_free(period->xlink_href);
4948 		period->xlink_href = NULL;
4949 		gf_mpd_del(new_mpd);
4950 		if (dash->dash_mutex) gf_mx_v(dash->dash_mutex);
4951 		if (url) gf_free(url);
4952 		return;
4953 	}
4954 
4955 	gf_list_rem(period_list, period_idx);
4956 
4957 	if (dash->split_adaptation_set)
4958 		gf_mpd_split_adaptation_sets(new_mpd);
4959 
4960 	if (!period->duration) {
4961 		GF_MPD_Period *next_period = gf_list_get(period_list, period_idx);
4962 		if (next_period && next_period->start)
4963 			period->duration = next_period->start - period->start;
4964 	}
4965 	src_duration = period->duration;
4966 	//insert all periods
4967 	while (gf_list_count(new_mpd->periods)) {
4968 		GF_MPD_Period *inserted_period = gf_list_get(new_mpd->periods, 0);
4969 		gf_list_rem(new_mpd->periods, 0);
4970 		//forbiden
4971 		if (inserted_period->xlink_href && inserted_period->xlink_actuate_on_load) {
4972 			gf_mpd_period_free(inserted_period);
4973 			continue;
4974 		}
4975 		inserted_period->origin_base_url = url ? gf_strdup(url) : NULL;
4976 		inserted_period->start = start;
4977 		inserted_period->type = GF_MPD_TYPE_STATIC;
4978 
4979 		gf_list_insert(period_list, inserted_period, period_idx);
4980 		period_idx++;
4981 
4982 		if (period->duration) {
4983 			//truncate duration
4984 			if (inserted_period->duration > src_duration) {
4985 				inserted_period->duration = src_duration;
4986 				break;
4987 			} else {
4988 				src_duration -= inserted_period->duration;
4989 			}
4990 		}
4991 		start += inserted_period->duration;
4992 	}
4993 	if (url) gf_free(url);
4994 
4995 	//this will do the garbage collection
4996 	gf_list_add(new_mpd->periods, period);
4997 
4998 	gf_mpd_del(new_mpd);
4999 
5000 	if (dash->dash_mutex) gf_mx_v(dash->dash_mutex);
5001 }
5002 
gf_dash_get_tiles_quality_rank(GF_DashClient * dash,GF_DASH_Group * tile_group)5003 static u32 gf_dash_get_tiles_quality_rank(GF_DashClient *dash, GF_DASH_Group *tile_group)
5004 {
5005 	s32 res, res2;
5006 	struct _dash_srd_desc *srd = tile_group->srd_desc;
5007 
5008 	//no SRD is max quality for now
5009 	if (!srd) return 0;
5010 	if (!tile_group->srd_w || !tile_group->srd_h) return 0;
5011 
5012 	if (tile_group->quality_degradation_hint) {
5013 		u32 v = tile_group->quality_degradation_hint * MAX(srd->srd_nb_rows, srd->srd_nb_cols);
5014 		v/=100;
5015 		return v;
5016 	}
5017 
5018 
5019 	switch (dash->tile_adapt_mode) {
5020 	case GF_DASH_ADAPT_TILE_NONE:
5021 		return 0;
5022 	case GF_DASH_ADAPT_TILE_ROWS:
5023 		return tile_group->srd_row_idx;
5024 	case GF_DASH_ADAPT_TILE_ROWS_REVERSE:
5025 		return srd->srd_nb_rows - 1 - tile_group->srd_row_idx;
5026 	case GF_DASH_ADAPT_TILE_ROWS_MIDDLE:
5027 		res = srd->srd_nb_rows/2;
5028 		res -= tile_group->srd_row_idx;
5029 		return ABS(res);
5030 	case GF_DASH_ADAPT_TILE_COLUMNS:
5031 		return tile_group->srd_col_idx;
5032 	case GF_DASH_ADAPT_TILE_COLUMNS_REVERSE:
5033 		return srd->srd_nb_cols - 1 - tile_group->srd_col_idx;
5034 	case GF_DASH_ADAPT_TILE_COLUMNS_MIDDLE:
5035 		res = srd->srd_nb_cols/2;
5036 		res -= tile_group->srd_col_idx;
5037 		return ABS(res);
5038 	case GF_DASH_ADAPT_TILE_CENTER:
5039 		res = srd->srd_nb_rows/2 - tile_group->srd_row_idx;
5040 		res2 = srd->srd_nb_cols/2 - tile_group->srd_col_idx;
5041 		return MAX( ABS(res), ABS(res2) );
5042 	case GF_DASH_ADAPT_TILE_EDGES:
5043 		res = srd->srd_nb_rows/2 - tile_group->srd_row_idx;
5044 		res = srd->srd_nb_rows/2 - ABS(res);
5045 		res2 = srd->srd_nb_cols/2 - tile_group->srd_col_idx;
5046 		res2 = srd->srd_nb_cols/2 - ABS(res2);
5047 		return MIN( res, res2 );
5048 	}
5049 	return 0;
5050 }
5051 
5052 //used upon startup of the session only
gf_dash_set_tiles_quality(GF_DashClient * dash,struct _dash_srd_desc * srd)5053 static void gf_dash_set_tiles_quality(GF_DashClient *dash, struct _dash_srd_desc *srd)
5054 {
5055 	u32 i, count;
5056 	Bool tiles_use_lowest = (dash->first_select_mode==GF_DASH_SELECT_BANDWIDTH_HIGHEST_TILES) ? GF_TRUE : GF_FALSE;
5057 
5058 	count = gf_list_count(dash->groups);
5059 	for (i=0; i<count; i++) {
5060 		GF_DASH_Group *group = gf_list_get(dash->groups, i);
5061 		u32 lower_quality;
5062 		if (group->srd_desc != srd) continue;
5063 
5064 		lower_quality = gf_dash_get_tiles_quality_rank(dash, group);
5065 		if (!lower_quality) continue;
5066 
5067 		if (tiles_use_lowest && (group->active_rep_index >= lower_quality)) {
5068 			lower_quality = group->active_rep_index - lower_quality;
5069 		} else {
5070 			lower_quality = 0;
5071 		}
5072 		gf_dash_set_group_representation(group,
5073 		                                 gf_list_get(group->adaptation_set->representations, lower_quality) );
5074 	}
5075 }
5076 
gf_dash_get_srd_desc(GF_DashClient * dash,u32 srd_id,Bool do_create)5077 static struct _dash_srd_desc *gf_dash_get_srd_desc(GF_DashClient *dash, u32 srd_id, Bool do_create)
5078 {
5079 	u32 i, count;
5080 	struct _dash_srd_desc *srd;
5081 	count = dash->SRDs ? gf_list_count(dash->SRDs) : 0;
5082 	for (i=0; i<count; i++) {
5083 		srd = gf_list_get(dash->SRDs, i);
5084 		if (srd->id==srd_id) return srd;
5085 	}
5086 	if (!do_create) return NULL;
5087 	GF_SAFEALLOC(srd, struct _dash_srd_desc);
5088 	if (!srd) return NULL;
5089 	srd->id = srd_id;
5090 	if (!dash->SRDs) dash->SRDs = gf_list_new();
5091 	gf_list_add(dash->SRDs, srd);
5092 	return srd;
5093 }
5094 
gf_dash_setup_period(GF_DashClient * dash)5095 static GF_Err gf_dash_setup_period(GF_DashClient *dash)
5096 {
5097 	GF_MPD_Period *period;
5098 	u32 rep_i, as_i, group_i, j, nb_groups_ok;
5099 	u32 retry = 10;
5100 
5101 	//solve xlink - if
5102 	while (retry) {
5103 		period = gf_list_get(dash->mpd->periods, dash->active_period_index);
5104 		if (!period) return GF_EOS;
5105 		if (!period->xlink_href) break;
5106 		gf_dash_solve_period_xlink(dash, dash->mpd->periods, dash->active_period_index);
5107 		retry --;
5108 	}
5109 	period = gf_list_get(dash->mpd->periods, dash->active_period_index);
5110 	if (period->xlink_href && (dash->atsc_clock_state!=1) ) {
5111 		GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Too many xlink indirections on the same period - not supported\n"));
5112 		return GF_NOT_SUPPORTED;
5113 	}
5114 
5115 	if (!period->duration) {
5116 		GF_MPD_Period *next_period = gf_list_get(dash->mpd->periods, dash->active_period_index+1);
5117 		if (next_period && next_period->start)
5118 			period->duration = next_period->start - period->start;
5119 	}
5120 
5121 	/*we are not able to process webm dash (youtube)*/
5122 	j = gf_list_count(period->adaptation_sets);
5123 	for (as_i=0; as_i<j; as_i++) {
5124 		GF_MPD_AdaptationSet *set = (GF_MPD_AdaptationSet*)gf_list_get(period->adaptation_sets, as_i);
5125 		if (set->mime_type && strstr(set->mime_type, "webm")) {
5126 			u32 k;
5127 			for (k=0; k<gf_list_count(set->representations); ++k) {
5128 				GF_MPD_Representation *rep = (GF_MPD_Representation*)gf_list_get(set->representations, k);
5129 				rep->playback.disabled = GF_TRUE;
5130 			}
5131 		}
5132 	}
5133 
5134 	GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] Setting up period start "LLU" duration "LLU" xlink %s ID %s\n", period->start, period->duration, period->origin_base_url ? period->origin_base_url : "none", period->ID ? period->ID : "none"));
5135 
5136 	/*setup all groups*/
5137 	gf_dash_setup_groups(dash);
5138 
5139 	if (dash->debug_group_index>=0) {
5140 		GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] Debuging adaptation set #%d in period, ignoring other ones!\n\n", dash->debug_group_index + 1));
5141 	}
5142 
5143 	nb_groups_ok = 0;
5144 	for (group_i=0; group_i<gf_list_count(dash->groups); group_i++) {
5145 		GF_MPD_Representation *rep_sel;
5146 		u32 active_rep, nb_rep;
5147 		const char *mime_type;
5148 		u32 nb_rep_ok = 0;
5149 		Bool group_has_video = GF_FALSE;
5150 		Bool disabled = GF_FALSE;
5151 		Bool cp_supported = GF_FALSE;
5152 		GF_DASH_Group *group = gf_list_get(dash->groups, group_i);
5153 		Bool active_rep_found;
5154 
5155 		active_rep = 0;
5156 
5157 		if ((dash->debug_group_index>=0) && (group_i != (u32) dash->debug_group_index)) {
5158 			group->selection = GF_DASH_GROUP_NOT_SELECTABLE;
5159 			continue;
5160 		}
5161 
5162 		nb_rep = gf_list_count(group->adaptation_set->representations);
5163 
5164 		//on HLS get rid of audio only adaptation set if not in fMP4 mode
5165 		if (dash->is_m3u8
5166 			&& !group->adaptation_set->max_width
5167 			&& !group->adaptation_set->max_height
5168 			&& (gf_list_count(dash->groups)>1)
5169 		) {
5170 			GF_MPD_Representation *rep = gf_list_get(group->adaptation_set->representations, 0);
5171 			if ((!rep->segment_template || !rep->segment_template->initialization)
5172 				&& (!rep->segment_list || (!rep->segment_list->initialization_segment && !rep->segment_list->xlink_href))
5173 			) {
5174 				group->selection = GF_DASH_GROUP_NOT_SELECTABLE;
5175 				continue;
5176 			}
5177 		}
5178 
5179 		if ((nb_rep>1) && !group->adaptation_set->segment_alignment && !group->adaptation_set->subsegment_alignment) {
5180 			GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] AdaptationSet without segmentAlignment flag set - may result in broken adaptation\n"));
5181 		}
5182 		if (group->adaptation_set->xlink_href) {
5183 			GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] AdaptationSet with xlink:href to %s - ignoring because not supported\n", group->adaptation_set->xlink_href));
5184 			continue;
5185 		}
5186 
5187 		for (j=0; j<gf_list_count(group->adaptation_set->essential_properties); j++) {
5188 			GF_MPD_Descriptor *mpd_desc = gf_list_get(group->adaptation_set->essential_properties, j);
5189 			if (!strcmp(mpd_desc->scheme_id_uri, "urn:mpeg:dash:srd:2014")) {
5190 				u32 id, w, h, res;
5191 				w = h = 0;
5192 				res = sscanf(mpd_desc->value, "%d,%d,%d,%d,%d,%d,%d", &id, &group->srd_x, &group->srd_y, &group->srd_w, &group->srd_h, &w, &h);
5193 				if (res != 7) {
5194 					res = sscanf(mpd_desc->value, "%d,%d,%d,%d,%d", &id, &group->srd_x, &group->srd_y, &group->srd_w, &group->srd_h);
5195 					if (res!=5) res=0;
5196 				}
5197 				if (res) {
5198 					group->srd_desc = gf_dash_get_srd_desc(dash, id, GF_TRUE);
5199 					if (!w) w = group->srd_x + group->srd_w;
5200 					if (!h) h = group->srd_y + group->srd_h;
5201 
5202 					if (w>group->srd_desc->srd_fw)
5203 						group->srd_desc->srd_fw = w;
5204 					if (h>group->srd_desc->srd_fh)
5205 						group->srd_desc->srd_fh = h;
5206 				}
5207 
5208 			} else {
5209 				//we don't know any defined scheme for now
5210 				GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] AdaptationSet with unrecognized EssentialProperty %s - ignoring because not supported\n", mpd_desc->scheme_id_uri));
5211 				disabled = 1;
5212 				break;
5213 			}
5214 		}
5215 		if (disabled) {
5216 			continue;
5217 		}
5218 
5219 		cp_supported = 1;
5220 		for (j=0; j<gf_list_count(group->adaptation_set->content_protection); j++) {
5221 			GF_MPD_Descriptor *mpd_desc = gf_list_get(group->adaptation_set->content_protection, j);
5222 			//we don't know any defined scheme for now
5223 			if (strcmp(mpd_desc->scheme_id_uri, "urn:mpeg:dash:mp4protection:2011")) {
5224 				GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] AdaptationSet with unrecognized ContentProtection %s\n", mpd_desc->scheme_id_uri));
5225 				cp_supported = 0;
5226 			} else {
5227 				cp_supported = 1;
5228 				break;
5229 			}
5230 		}
5231 		if (!cp_supported) {
5232 			GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] AdaptationSet with no supported ContentProtection - ignoring\n"));
5233 			continue;
5234 		}
5235 
5236 		for (j=0; j<gf_list_count(group->adaptation_set->supplemental_properties); j++) {
5237 			GF_MPD_Descriptor *mpd_desc = gf_list_get(group->adaptation_set->supplemental_properties, j);
5238 			if (!strcmp(mpd_desc->scheme_id_uri, "urn:mpeg:dash:srd:2014")) {
5239 				u32 id, w, h, res;
5240 				w = h = 0;
5241 				res = sscanf(mpd_desc->value, "%d,%d,%d,%d,%d,%d,%d", &id, &group->srd_x, &group->srd_y, &group->srd_w, &group->srd_h, &w, &h);
5242 				if (res != 7) {
5243 					res = sscanf(mpd_desc->value, "%d,%d,%d,%d,%d", &id, &group->srd_x, &group->srd_y, &group->srd_w, &group->srd_h);
5244 					if (res != 5) res=0;
5245 				}
5246 				if (res) {
5247 					group->srd_desc = gf_dash_get_srd_desc(dash, id, GF_TRUE);
5248 					if (!w) w = group->srd_x + group->srd_w;
5249 					if (!h) h = group->srd_y + group->srd_h;
5250 
5251 					if (w>group->srd_desc->srd_fw)
5252 						group->srd_desc->srd_fw = w;
5253 					if (h>group->srd_desc->srd_fh)
5254 						group->srd_desc->srd_fh = h;
5255 				}
5256 			}
5257 		}
5258 
5259 		/*translate from single-indexed file to SegmentList*/
5260 		gf_dash_setup_single_index_mode(group);
5261 
5262 		/* Select the appropriate representation in the given period */
5263 		for (rep_i = 0; rep_i < nb_rep; rep_i++) {
5264 			GF_MPD_Representation *rep = gf_list_get(group->adaptation_set->representations, rep_i);
5265 			if (rep->width && rep->height) group_has_video = GF_TRUE;
5266 		}
5267 
5268 		//sort by ascending bandwidth and quality
5269 		for (rep_i = 1; rep_i < nb_rep; rep_i++) {
5270 			Bool swap=GF_FALSE;
5271 			GF_MPD_Representation *r2 = gf_list_get(group->adaptation_set->representations, rep_i);
5272 			GF_MPD_Representation *r1 = gf_list_get(group->adaptation_set->representations, rep_i-1);
5273 			if (r1->bandwidth > r2->bandwidth) {
5274 				swap=GF_TRUE;
5275 			} else if ((r1->bandwidth == r2->bandwidth) && (r1->quality_ranking<r2->quality_ranking)) {
5276 				swap=GF_TRUE;
5277 			}
5278 			if (swap) {
5279 				gf_list_rem(group->adaptation_set->representations, rep_i);
5280 				gf_list_insert(group->adaptation_set->representations, r2, rep_i-1);
5281 				rep_i=0;
5282 			}
5283 		}
5284 
5285 select_active_rep:
5286 		group->min_representation_bitrate = (u32) -1;
5287 		active_rep_found = GF_FALSE;
5288 		for (rep_i = 0; rep_i < nb_rep; rep_i++) {
5289 			u32 first_select_mode = dash->first_select_mode;
5290 			GF_MPD_Representation *rep = gf_list_get(group->adaptation_set->representations, rep_i);
5291 			if (rep->playback.disabled)
5292 				continue;
5293 			if (!active_rep_found) {
5294 				active_rep = rep_i;
5295 				active_rep_found = GF_TRUE;
5296 			}
5297 
5298 			rep_sel = gf_list_get(group->adaptation_set->representations, active_rep);
5299 
5300 			if (group_has_video && !rep->width && !rep->height) {
5301 				GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] Adaptation %s: non-video in a video group - disabling it\n", rep->id));
5302 				rep->playback.disabled = 1;
5303 				continue;
5304 			}
5305 
5306 			if (group_has_video && !rep_sel->width && !rep_sel->height && rep->width && rep->height) {
5307 				rep_sel = rep;
5308 			}
5309 
5310 			if (rep->bandwidth < group->min_representation_bitrate) {
5311 				group->min_representation_bitrate = rep->bandwidth;
5312 			}
5313 
5314 			if (rep_i) {
5315 				Bool ok;
5316 				if (rep->codecs && rep_sel->codecs) {
5317 					char *sep = strchr(rep_sel->codecs, '.');
5318 					if (sep) sep[0] = 0;
5319 					ok = !strnicmp(rep->codecs, rep_sel->codecs, strlen(rep_sel->codecs) );
5320 					//check for scalable coding
5321 					if (!ok && rep->dependency_id) {
5322 						if (!strncmp(rep_sel->codecs, "avc", 3)) {
5323 							//we accept LHVC with different configs as enhancement for AVC
5324 							if (!strncmp(rep->codecs, "lhv", 3) || !strncmp(rep->codecs, "lhe", 3) ) ok = 1;
5325 							//we accept SVC and MVC as enhancement for AVC
5326 							else if (!strncmp(rep->codecs, "svc", 3) || !strncmp(rep->codecs, "mvc", 3) ) ok = 1;
5327 						}
5328 						else if (!strncmp(rep_sel->codecs, "hvc", 3) || !strncmp(rep_sel->codecs, "hev", 3)) {
5329 							//we accept HEVC and HEVC+LHVC with different configs
5330 							if (!strncmp(rep->codecs, "hvc", 3) || !strncmp(rep->codecs, "hev", 3) ) ok = 1;
5331 							//we accept LHVC with different configs
5332 							else if (!strncmp(rep->codecs, "lhv", 3) || !strncmp(rep->codecs, "lhe", 3) ) ok = 1;
5333 						}
5334 					}
5335 
5336 					if (sep) sep[0] = '.';
5337 					if (!ok) {
5338 						GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] Different codec types (%s vs %s) in same AdaptationSet - disabling rep %s\n", rep_sel->codecs, rep->codecs, rep->codecs));
5339 						//we don(t support mixes
5340 						rep->playback.disabled = 1;
5341 						continue;
5342 					}
5343 				}
5344 			}
5345 			//move to highest rate if ATSC session and rep is not a remote one (baseURL set)
5346 			if (dash->atsc_clock_state && (first_select_mode==GF_DASH_SELECT_BANDWIDTH_LOWEST) && !gf_list_count(rep->base_URLs))
5347 				first_select_mode = GF_DASH_SELECT_BANDWIDTH_HIGHEST;
5348 
5349 			switch (first_select_mode) {
5350 			case GF_DASH_SELECT_QUALITY_LOWEST:
5351 				if (rep->quality_ranking && (rep->quality_ranking < rep_sel->quality_ranking)) {
5352 					active_rep = rep_i;
5353 					break;
5354 				}/*fallthrough if quality is not indicated*/
5355 			case GF_DASH_SELECT_BANDWIDTH_LOWEST:
5356 				if ((rep->width&&rep->height) || !group_has_video) {
5357 					if (rep->bandwidth < rep_sel->bandwidth) {
5358 						active_rep = rep_i;
5359 					}
5360 				}
5361 				break;
5362 			case GF_DASH_SELECT_QUALITY_HIGHEST:
5363 				if (rep->quality_ranking > rep_sel->quality_ranking) {
5364 					active_rep = rep_i;
5365 					break;
5366 				}
5367 				/*fallthrough if quality is not indicated*/
5368 			case GF_DASH_SELECT_BANDWIDTH_HIGHEST:
5369 				if (rep->bandwidth > rep_sel->bandwidth) {
5370 					active_rep = rep_i;
5371 				}
5372 				break;
5373 			default:
5374 				break;
5375 			}
5376 		}
5377 		for (rep_i = 0; rep_i < nb_rep; rep_i++) {
5378 			GF_MPD_Representation *rep = gf_list_get(group->adaptation_set->representations, rep_i);
5379 			if (!rep->playback.disabled)
5380 				nb_rep_ok++;
5381 		}
5382 
5383 		if (! nb_rep_ok) {
5384 			GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] No valid representation in this group - disabling\n"));
5385 			group->selection = GF_DASH_GROUP_NOT_SELECTABLE;
5386 			continue;
5387 		}
5388 
5389 		rep_sel = gf_list_get(group->adaptation_set->representations, active_rep);
5390 
5391 		gf_dash_set_group_representation(group, rep_sel);
5392 		// active representation is marked as disabled, we need to redo the selection
5393 		if (rep_sel->playback.disabled)
5394 			goto select_active_rep;
5395 
5396 		//adjust seek
5397 		if (dash->start_range_period) {
5398 			gf_dash_seek_group(dash, group, dash->start_range_period, 0);
5399 		}
5400 
5401 		mime_type = gf_dash_get_mime_type(NULL, rep_sel, group->adaptation_set);
5402 
5403 		if (!mime_type) {
5404 			GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Missing MIME type for AdaptationSet - skipping\n"));
5405 			continue;
5406 		}
5407 
5408 		/* TODO: Generate segment names if urltemplates are used */
5409 		if (!rep_sel->segment_base && !rep_sel->segment_list && !rep_sel->segment_template
5410 		        && !group->adaptation_set->segment_base && !group->adaptation_set->segment_list && !group->adaptation_set->segment_template
5411 		        && !group->period->segment_base && !group->period->segment_list && !group->period->segment_template
5412 		        && !gf_list_count(rep_sel->base_URLs)
5413 		   ) {
5414 			GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Segment URLs are not present for AdaptationSet - skipping\n"));
5415 			continue;
5416 		}
5417 
5418 		group->selection = GF_DASH_GROUP_NOT_SELECTED;
5419 		nb_groups_ok++;
5420 	}
5421 	dash->start_range_period = 0;
5422 
5423 	//setup SRDs
5424 	for (as_i = 0; as_i<gf_list_count(dash->SRDs); as_i++) {
5425 		u32 cols[10], rows[10];
5426 		struct _dash_srd_desc *srd = gf_list_get(dash->SRDs, as_i);
5427 
5428 		srd->srd_nb_rows = srd->srd_nb_cols = 0;
5429 
5430 		//sort SRDs
5431 		for (j=1; j < gf_list_count(dash->groups); j++) {
5432 			GF_DASH_Group *dg2 = gf_list_get(dash->groups, j);
5433 			GF_DASH_Group *dg1 = gf_list_get(dash->groups, j-1);
5434 			u32 dg1_weight = dg1->srd_y << 16 | dg1->srd_x;
5435 			u32 dg2_weight = dg2->srd_y << 16 | dg2->srd_x;
5436 
5437 			if (dg1->srd_desc != srd) continue;
5438 			if (dg2->srd_desc != srd) continue;
5439 
5440 			if (dg1_weight > dg2_weight) {
5441 				gf_list_rem(dash->groups, j);
5442 				gf_list_insert(dash->groups, dg2, j-1);
5443 				j=0;
5444 			}
5445 		}
5446 
5447 		//groups are now sorted for this srd, locate col/row positions
5448 		for (group_i=0; group_i<gf_list_count(dash->groups); group_i++) {
5449 			u32 k;
5450 			Bool found = GF_FALSE;
5451 			GF_DASH_Group *group = gf_list_get(dash->groups, group_i);
5452 			if (group->srd_desc != srd) continue;
5453 
5454 			if (!group->srd_w || !group->srd_h) continue;
5455 
5456 			for (k=0; k<srd->srd_nb_cols; k++) {
5457 				if (cols[k]==group->srd_x) {
5458 					found=GF_TRUE;
5459 					break;
5460 				}
5461 			}
5462 			if (!found) {
5463 				cols[srd->srd_nb_cols] = group->srd_x;
5464 				group->srd_col_idx = srd->srd_nb_cols;
5465 				srd->srd_nb_cols++;
5466 
5467 				srd->width += group->adaptation_set->max_width;
5468 
5469 			} else {
5470 				group->srd_col_idx = k;
5471 			}
5472 
5473 			found = GF_FALSE;
5474 			for (k=0; k<srd->srd_nb_rows; k++) {
5475 				if (rows[k]==group->srd_y) {
5476 					found=GF_TRUE;
5477 					break;
5478 				}
5479 			}
5480 			if (!found) {
5481 				rows[srd->srd_nb_rows] = group->srd_y;
5482 				group->srd_row_idx = srd->srd_nb_rows;
5483 				srd->srd_nb_rows++;
5484 				srd->height += group->adaptation_set->max_height;
5485 			} else {
5486 				group->srd_row_idx = k;
5487 			}
5488 
5489 		}
5490 		gf_dash_set_tiles_quality(dash, srd);
5491 	}
5492 
5493 	period = gf_list_get(dash->mpd->periods, dash->active_period_index);
5494 
5495 	if (period->segment_base) {
5496 		gf_mpd_segment_base_free(period->segment_base);
5497 		period->segment_base = NULL;
5498 	}
5499 
5500 	if (!nb_groups_ok) {
5501 		GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] No AdaptationSet could be selected in the MPD - Cannot play\n"));
5502 		return GF_NON_COMPLIANT_BITSTREAM;
5503 	}
5504 
5505 	/*and seek if needed*/
5506 	return GF_OK;
5507 }
5508 
5509 
gf_dash_group_check_time(GF_DASH_Group * group)5510 static void gf_dash_group_check_time(GF_DASH_Group *group)
5511 {
5512 	s64 check_time;
5513 	u32 nb_dropped;
5514 
5515 	if (group->dash->is_m3u8) return;
5516 	if (! group->timeline_setup) return;
5517 	if (group->broken_timing) return;
5518 
5519 	check_time = (s64) gf_net_get_utc();
5520 	nb_dropped = 0;
5521 
5522 	while (1) {
5523 		u32 seg_dur_ms;
5524 		u64 seg_ast = gf_dash_get_segment_availability_start_time(group->dash->mpd, group, group->download_segment_index, &seg_dur_ms);
5525 
5526 		s64 now = check_time + (s64) seg_dur_ms;
5527 		if (now <= (s64) seg_ast) {
5528 			group->dash->tsb_exceeded = (u32) -1;
5529 			return;
5530 		}
5531 
5532 		now -= (s64) seg_ast;
5533 		if (now <= (s64) seg_dur_ms) {
5534 			group->dash->tsb_exceeded = (u32) -1;
5535 			return;
5536 		}
5537 		if (((s32) group->time_shift_buffer_depth > 0) && (now > group->time_shift_buffer_depth)) {
5538 			group->download_segment_index ++;
5539 			nb_dropped ++;
5540 			group->dash->time_in_tsb = 0;
5541 			continue;
5542 		}
5543 
5544 		if (nb_dropped > group->dash->tsb_exceeded) {
5545 			group->dash->tsb_exceeded = nb_dropped;
5546 		}
5547 
5548 		now -= group->dash->user_buffer_ms;
5549 		if (now<0) return;
5550 
5551 		if (now>group->dash->time_in_tsb)
5552 			group->dash->time_in_tsb = (u32) now;
5553 		return;
5554 	}
5555 }
5556 
5557 typedef enum
5558 {
5559 	GF_DASH_DownloadCancel,
5560 	GF_DASH_DownloadRestart,
5561 	GF_DASH_DownloadSuccess,
5562 } DownloadGroupStatus;
5563 
5564 
5565 static DownloadGroupStatus dash_download_group_download(GF_DashClient *dash, GF_DASH_Group *group, GF_DASH_Group *base_group, Bool has_dep_following);
5566 
dash_set_min_wait(GF_DashClient * dash,u32 min_wait)5567 static GFINLINE void dash_set_min_wait(GF_DashClient *dash, u32 min_wait)
5568 {
5569 	if (!dash->min_wait_ms_before_next_request || (min_wait < dash->min_wait_ms_before_next_request)) {
5570 		dash->min_wait_ms_before_next_request = min_wait;
5571 		dash->min_wait_sys_clock = gf_sys_clock();
5572 	}
5573 }
5574 
5575 /*TODO decide what is the best, fetch from another representation or ignore ...*/
on_group_download_error(GF_DashClient * dash,GF_DASH_Group * group,GF_DASH_Group * base_group,GF_Err e,GF_MPD_Representation * rep,char * new_base_seg_url,char * key_url,Bool has_dep_following)5576 static DownloadGroupStatus on_group_download_error(GF_DashClient *dash, GF_DASH_Group *group, GF_DASH_Group *base_group, GF_Err e, GF_MPD_Representation *rep, char *new_base_seg_url, char *key_url, Bool has_dep_following)
5577 {
5578 	u32 clock_time;
5579 	Bool will_retry = GF_FALSE;
5580 	Bool is_live = GF_FALSE;
5581 	if (!dash || !group)
5582 		return GF_DASH_DownloadCancel;
5583 
5584 	clock_time = gf_sys_clock();
5585 
5586 	dash_set_min_wait(dash, dash->min_timeout_between_404);
5587 
5588 	group->retry_after_utc = dash->min_timeout_between_404 + gf_net_get_utc();
5589 	if (!group->period->origin_base_url && (dash->mpd->type==GF_MPD_TYPE_DYNAMIC))
5590 		is_live = GF_TRUE;
5591 
5592 	if (e==GF_REMOTE_SERVICE_ERROR) {
5593 		gf_dash_mark_group_done(group);
5594 	}
5595 	//failure on last segment in non dynamic mode: likely due to rounding in dash segment duration, assume no error
5596 	//in dynamic mode, we need to check if this is a download schedule issue
5597 	else if (!is_live && group->period->duration && (group->download_segment_index + 1 >= (s32) group->nb_segments_in_rep) ) {
5598 		gf_dash_mark_group_done(group);
5599 	}
5600 	else if (group->maybe_end_of_stream) {
5601 		if (group->maybe_end_of_stream==2) {
5602 			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)));
5603 			group->maybe_end_of_stream = 0;
5604 			gf_dash_mark_group_done(group);
5605 		}
5606 		group->maybe_end_of_stream++;
5607 	} else if (group->segment_in_valid_range) {
5608 		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)));
5609 		if (dash->speed >= 0) {
5610 			group->download_segment_index++;
5611 		} else if (group->download_segment_index) {
5612 			group->download_segment_index--;
5613 		} else {
5614 			GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Playing in backward - start of playlist reached - assuming end of stream\n"));
5615 			gf_dash_mark_group_done(group);
5616 		}
5617 		group->segment_in_valid_range=0;
5618 	} else if (group->prev_segment_ok && !group->time_at_first_failure) {
5619 		if (!group->loop_detected) {
5620 			group->time_at_first_failure = clock_time;
5621 			GF_LOG(GF_LOG_INFO, 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));
5622 
5623 			will_retry = GF_TRUE;
5624 		}
5625 	}
5626 	//if multiple baseURL, try switching the base
5627 	else if ((e==GF_URL_ERROR) && (group->current_base_url_idx + 1 < gf_mpd_get_base_url_count(dash->mpd, group->period, group->adaptation_set, rep) )) {
5628 		group->current_base_url_idx++;
5629 		if (new_base_seg_url) gf_free(new_base_seg_url);
5630 		if (key_url) gf_free(key_url);
5631 		return dash_download_group_download(dash, group, base_group, has_dep_following);
5632 	}
5633 	//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 some default safety safety
5634 	else if (group->prev_segment_ok && (clock_time - group->time_at_first_failure <= group->current_downloaded_segment_duration + dash->segment_lost_after_ms )) {
5635 		will_retry = GF_TRUE;
5636 	} else {
5637 		if ((group->dash->atsc_clock_state==2) && (e==GF_URL_ERROR)) {
5638 			const char *val = group->dash->dash_io->get_header_value(group->dash->dash_io, group->dash->mpd_dnload, "x-atsc-loop");
5639 			Bool is_loop = (val && !strcmp(val, "yes")) ? GF_TRUE : GF_FALSE;
5640 			//if explicit loop or more than 5 consecutive seg lost restart synchro
5641 			if ((group->nb_consecutive_segments_lost >= 5) || is_loop) {
5642 				u32 i=0;
5643 				if (is_loop) {
5644 					GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] ATSC loop detected, reseting timeline\n"));
5645 				} else {
5646 					GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] ATSC lost %d consecutive segments, resetup tune-in\n", group->nb_consecutive_segments_lost));
5647 				}
5648 				dash->utc_drift_estimate = 0;
5649 				dash->initial_period_tunein = GF_TRUE;
5650 				dash->atsc_clock_state = 1;
5651 				while ((group = gf_list_enum(dash->groups, &i))) {
5652 					group->start_number_at_last_ast = 0;
5653 					gf_dash_group_timeline_setup(dash->mpd, group, 0);
5654 					group->loop_detected = is_loop;
5655 					group->time_at_first_failure = 0;
5656 					group->prev_segment_ok = GF_TRUE;
5657 				}
5658 				if (new_base_seg_url) gf_free(new_base_seg_url);
5659 				if (key_url) gf_free(key_url);
5660 				return GF_DASH_DownloadCancel;
5661 			}
5662 		}
5663 		if (group->prev_segment_ok) {
5664 			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));
5665 			group->time_at_first_failure = 0;
5666 			group->prev_segment_ok = GF_FALSE;
5667 		}
5668 		group->nb_consecutive_segments_lost ++;
5669 
5670 		//we are lost ....
5671 		if (group->nb_consecutive_segments_lost == 20) {
5672 			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"));
5673 			dash_set_min_wait(dash, 1000);
5674 			group->maybe_end_of_stream = 1;
5675 		} else {
5676 			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)));
5677 			if (dash->speed >= 0) {
5678 				group->download_segment_index++;
5679 			} else if (group->download_segment_index) {
5680 				group->download_segment_index--;
5681 			} else {
5682 				GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Playing in backward - start of playlist reached - assuming end of stream\n"));
5683 				gf_dash_mark_group_done(group);
5684 			}
5685 		}
5686 	}
5687 	//if retry, do not reset dependency status
5688 	if (!will_retry) {
5689 		if (rep->dependency_id) {
5690 			segment_cache_entry *cache_entry = &base_group->cached[base_group->nb_cached_segments];
5691 			cache_entry->has_dep_following = 0;
5692 		}
5693 
5694 		if (group->base_rep_index_plus_one) {
5695 			group->active_rep_index = group->base_rep_index_plus_one - 1;
5696 			group->has_pending_enhancement = GF_FALSE;
5697 		}
5698 	}
5699 
5700 	if (new_base_seg_url) gf_free(new_base_seg_url);
5701 	if (key_url) gf_free(key_url);
5702 	return GF_DASH_DownloadCancel;
5703 }
5704 
dash_download_group_download(GF_DashClient * dash,GF_DASH_Group * group,GF_DASH_Group * base_group,Bool has_dep_following)5705 static DownloadGroupStatus dash_download_group_download(GF_DashClient *dash, GF_DASH_Group *group, GF_DASH_Group *base_group, Bool has_dep_following)
5706 {
5707 	//commented out as we end up doing too many requets
5708 	GF_Err e;
5709 	GF_MPD_Representation *rep;
5710 	char *new_base_seg_url=NULL;
5711 	char *key_url=NULL;
5712 	bin128 key_iv;
5713 	u64 start_range, end_range;
5714 	Bool use_byterange;
5715 	u32 representation_index;
5716 	u32 clock_time, file_size=0, Bps=0;
5717 	Bool empty_file = GF_FALSE;
5718 	Bool remote_file = GF_FALSE;
5719 	const char *local_file_name = NULL;
5720 	const char *resource_name = NULL;
5721 	const char *hdr = NULL;
5722 	const char *base_url = NULL;
5723 	GF_MPD_Type dyn_type = dash->mpd->type;
5724 	if (group->period->origin_base_url)
5725 		dyn_type = group->period->type;
5726 
5727 	if (group->done) return GF_DASH_DownloadSuccess;
5728 	if (!base_group) return GF_DASH_DownloadSuccess;
5729 
5730 	if (group->selection != GF_DASH_GROUP_SELECTED) return GF_DASH_DownloadSuccess;
5731 
5732 	if (base_group->nb_cached_segments>=base_group->max_cached_segments) {
5733 		return GF_DASH_DownloadCancel;
5734 	}
5735 
5736 	/*remember the active rep index, since group->active_rep_index may change because of bandwidth control algorithm*/
5737 	representation_index = group->active_rep_index;
5738 	rep = gf_list_get(group->adaptation_set->representations, group->active_rep_index);
5739 	rep->playback.broadcast_flag = GF_FALSE;
5740 
5741 	/* if the index of the segment to be downloaded is greater or equal to the last segment (as seen in the playlist),
5742 	 we need to check if a new playlist is ready */
5743 	if (group->nb_segments_in_rep && (group->download_segment_index >= (s32) group->nb_segments_in_rep)) {
5744 		u32 timer = gf_sys_clock() - dash->last_update_time;
5745 		Bool update_playlist = 0;
5746 
5747 		/* this period is done*/
5748 		if ((dyn_type==GF_MPD_TYPE_DYNAMIC) && group->period->duration) {
5749 		}
5750 		/* update of the playlist, only if indicated */
5751 		else if (dash->mpd->minimum_update_period && (timer > dash->mpd->minimum_update_period)) {
5752 			update_playlist = 1;
5753 		}
5754 		/* if media_presentation_duration is 0 and we are in live, force a refresh (not in the spec but safety check*/
5755 		else if ((dyn_type==GF_MPD_TYPE_DYNAMIC) && !dash->mpd->media_presentation_duration) {
5756 			if (group->segment_duration && (timer > group->segment_duration*1000))
5757 				update_playlist = 1;
5758 		}
5759 		if (update_playlist) {
5760 			GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Playlist should be updated, postponing group download until playlist is updated\n"));
5761 			dash->force_mpd_update = 1;
5762 			return GF_DASH_DownloadCancel;
5763 		}
5764 		/* Now that the playlist is up to date, we can check again */
5765 		if (group->download_segment_index  >= (s32) group->nb_segments_in_rep) {
5766 			/* if there is a specified update period, we redo the whole process */
5767 			if (dash->mpd->minimum_update_period || dyn_type==GF_MPD_TYPE_DYNAMIC) {
5768 
5769 				if (dyn_type==GF_MPD_TYPE_STATIC) {
5770 					GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Last segment in static period (dynamic MPD) - group is done\n"));
5771 					gf_dash_mark_group_done(group);
5772 					return GF_DASH_DownloadCancel;
5773 				}
5774 				else if ((dyn_type==GF_MPD_TYPE_DYNAMIC) && group->period->duration) {
5775 					GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Last segment in period (dynamic mode) - group is done\n"));
5776 					gf_dash_mark_group_done(group);
5777 					return GF_DASH_DownloadCancel;
5778 				}
5779 				else if (! group->maybe_end_of_stream) {
5780 					u32 now = gf_sys_clock();
5781 					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));
5782 					if (group->nb_cached_segments) {
5783 						if (dash->is_m3u8 && (group->nb_cached_segments <= 1)) {
5784 							GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[M3U8] There is only %d segment in cache, force MPD update\n", group->nb_cached_segments));
5785 							dash->force_mpd_update = GF_TRUE;
5786 						}
5787 						return GF_DASH_DownloadCancel;
5788 					}
5789 
5790 					if (!group->time_at_first_reload_required) {
5791 						group->time_at_first_reload_required = now;
5792 						return GF_DASH_DownloadCancel;
5793 					}
5794 					if (now - group->time_at_first_reload_required < group->cache_duration)
5795 						return GF_DASH_DownloadCancel;
5796 					if (dash->mpd->minimum_update_period) {
5797 						if (now - group->time_at_first_reload_required < dash->mpd->minimum_update_period)
5798 							return GF_DASH_DownloadCancel;
5799 					} else if (dyn_type==GF_MPD_TYPE_DYNAMIC) {
5800 						if (timer < group->nb_segments_in_rep * group->segment_duration * 1000)
5801 							return GF_DASH_DownloadCancel;
5802 					}
5803 
5804 					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));
5805 					gf_dash_mark_group_done(group);
5806 					return GF_DASH_DownloadCancel;
5807 				}
5808 			} else {
5809 				/* if not, we are really at the end of the playlist, we can quit */
5810 				GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] End of period reached for group\n"));
5811 				gf_dash_mark_group_done(group);
5812 				if (!dash->request_period_switch && !group->has_pending_enhancement && !has_dep_following)
5813 					dash->dash_io->on_dash_event(dash->dash_io, GF_DASH_EVENT_SEGMENT_AVAILABLE, gf_list_find(dash->groups, base_group), GF_OK);
5814 				return GF_DASH_DownloadCancel;
5815 			}
5816 		}
5817 	}
5818 	group->time_at_first_reload_required = 0;
5819 
5820 	if (group->force_switch_bandwidth && !dash->auto_switch_count) {
5821 		GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Forcing representation switch, retesting group"));
5822 		gf_dash_switch_group_representation(dash, group);
5823 		/*restart*/
5824 		return GF_DASH_DownloadRestart;
5825 	}
5826 
5827 	/*check availablity start time of segment in Live !!*/
5828 	if (!group->broken_timing && (dyn_type==GF_MPD_TYPE_DYNAMIC) && !dash->is_m3u8) {
5829 		s32 to_wait = 0;
5830 		u32 seg_dur_ms=0;
5831 #ifndef GPAC_DISABLE_LOG
5832 		u32 start_number = gf_dash_get_start_number(group, rep);
5833 #endif
5834 		s64 segment_ast = (s64) gf_dash_get_segment_availability_start_time(dash->mpd, group, group->download_segment_index, &seg_dur_ms);
5835 		s64 now = (s64) gf_net_get_utc();
5836 
5837 
5838 		if (group->retry_after_utc > (u64) now) {
5839 			to_wait = (u32) (group->retry_after_utc - (u64) now);
5840 			dash_set_min_wait(dash, (u32) to_wait);
5841 
5842 			return GF_DASH_DownloadCancel;
5843 		}
5844 
5845 		clock_time = gf_sys_clock();
5846 		to_wait = (s32) (segment_ast - now);
5847 
5848 		if (group->force_early_fetch) {
5849 			if (to_wait>1) {
5850 				GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] Set #%d demux empty but wait time for segment %d is still %d ms, forcing scheduling\n", 1+gf_list_find(dash->groups, group), group->download_segment_index + start_number, to_wait));
5851 				to_wait = 0;
5852 			} else {
5853 				//we officially reached segment AST
5854 				group->force_early_fetch = GF_FALSE;
5855 			}
5856 		}
5857 
5858 		/*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 ...*/
5859 		if (to_wait > 1) {
5860 			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", 1+gf_list_find(dash->groups, group), gf_sys_clock(), group->download_segment_index + start_number, segment_ast, (segment_ast - group->period->start - group->ast_at_init)/1000.0, to_wait));
5861 			if (group->last_segment_time) {
5862 				GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] %d ms elapsed since previous segment download\n", clock_time - group->last_segment_time));
5863 			}
5864 
5865 			dash_set_min_wait(dash, (u32) to_wait);
5866 
5867 			return GF_DASH_DownloadCancel;
5868 		} else {
5869 			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", 1+gf_list_find(dash->groups, group), gf_sys_clock(), group->download_segment_index + start_number, segment_ast, (segment_ast - group->period->start - group->ast_at_init)/1000.0, -to_wait));
5870 
5871 			if (group->last_segment_time) {
5872 				GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] %d ms elapsed since previous segment download\n", clock_time - group->last_segment_time));
5873 			}
5874 #if 0
5875 			/*check if we are in the segment availability end time*/
5876 			if (now < segment_ast + seg_dur_ms + group->time_shift_buffer_depth )
5877 				in_segment_avail_time = 1;
5878 #endif
5879 		}
5880 	}
5881 
5882 	base_url = dash->base_url;
5883 	if (group->period->origin_base_url) base_url = group->period->origin_base_url;
5884 	/* At this stage, there are some segments left to be downloaded */
5885 	e = gf_dash_resolve_url(dash->mpd, rep, group, 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, NULL);
5886 
5887 	if (e || !new_base_seg_url) {
5888 		if (e==GF_EOS) {
5889 			gf_dash_mark_group_done(group);
5890 		} else {
5891 			/*do something!!*/
5892 			GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Error resolving URL of next segment: %s\n", gf_error_to_string(e) ));
5893 		}
5894 		if (new_base_seg_url) gf_free(new_base_seg_url);
5895 		return GF_DASH_DownloadCancel;
5896 	}
5897 	use_byterange = (start_range || end_range) ? 1 : 0;
5898 
5899 	if (use_byterange) {
5900 		GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Queuing new segment: %s (range: "LLD"-"LLD")\n", new_base_seg_url, start_range, end_range));
5901 	}
5902 
5903 	/*local file*/
5904 	if (strnicmp(base_url, "gfio://", 7)
5905 		&& (!strstr(new_base_seg_url, "://") || (!strnicmp(new_base_seg_url, "file://", 7) || !strnicmp(new_base_seg_url, "gmem://", 7) ) )
5906 	) {
5907 		FILE *ftest;
5908 		resource_name = local_file_name = (char *) new_base_seg_url;
5909 		e = GF_OK;
5910 		/*do not erase local files*/
5911 		group->local_files = 1;
5912 		if (group->force_switch_bandwidth && !dash->auto_switch_count) {
5913 			if (new_base_seg_url) gf_free(new_base_seg_url);
5914 			gf_dash_switch_group_representation(dash, group);
5915 			/*restart*/
5916 			return GF_DASH_DownloadRestart;
5917 		}
5918 		ftest = gf_fopen(local_file_name, "rb");
5919 		if (!ftest) {
5920 			if (group->current_base_url_idx + 1 < gf_mpd_get_base_url_count(dash->mpd, group->period, group->adaptation_set, rep) ){
5921 				group->current_base_url_idx++;
5922 				if (new_base_seg_url) gf_free(new_base_seg_url);
5923 				if (key_url) gf_free(key_url);
5924 				return dash_download_group_download(dash, group, base_group, has_dep_following);
5925 			} else if (group->period->duration && (group->download_segment_index + 1 == group->nb_segments_in_rep) ) {
5926 				if (new_base_seg_url) gf_free(new_base_seg_url);
5927 				gf_dash_mark_group_done(group);
5928 				return GF_DASH_DownloadCancel;
5929 			} else {
5930 				GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] File %s not found on disk\n", local_file_name));
5931 				group->current_base_url_idx = 0;
5932 				return on_group_download_error(dash, group, base_group, GF_NOT_FOUND, rep, new_base_seg_url, key_url, has_dep_following);
5933 			}
5934 		} else {
5935 			file_size = (u32) gf_fsize(ftest);
5936 			gf_fclose(ftest);
5937 		}
5938 		group->current_base_url_idx = 0;
5939 	} else if (dash->thread_mode) {
5940 		base_group->max_bitrate = 0;
5941 		base_group->min_bitrate = (u32)-1;
5942 
5943 		/*use persistent connection for segment downloads*/
5944 		if (use_byterange) {
5945 			e = gf_dash_download_resource(dash, &(base_group->segment_download), new_base_seg_url, start_range, end_range, 1, base_group);
5946 		} else {
5947 			e = gf_dash_download_resource(dash, &(base_group->segment_download), new_base_seg_url, 0, 0, 1, base_group);
5948 		}
5949 
5950 		if ((e==GF_IP_CONNECTION_CLOSED) && group->download_abort_type) {
5951 			base_group->download_abort_type = 0;
5952 			GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] Aborted while downloading segment (seek ?)%s \n", new_base_seg_url));
5953 			if (new_base_seg_url) gf_free(new_base_seg_url);
5954 			if (key_url) gf_free(key_url);
5955 			return GF_DASH_DownloadSuccess;
5956 		}
5957 
5958 		if (e != GF_OK) {
5959 			return on_group_download_error(dash, group, base_group, e, rep, new_base_seg_url, key_url, has_dep_following);
5960 		}
5961 
5962 		group->prev_segment_ok = GF_TRUE;
5963 		if (group->time_at_first_failure) {
5964 			if (group->current_base_url_idx) {
5965 				GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] Recovered segment %s after 404 by switching baseURL\n", new_base_seg_url));
5966 			} else {
5967 				GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] Recovered segment %s after 404 - was our download schedule %d ms too early ?\n", new_base_seg_url, gf_sys_clock() - group->time_at_first_failure));
5968 			}
5969 			group->time_at_first_failure = 0;
5970 		}
5971 		group->nb_consecutive_segments_lost = 0;
5972 		group->current_base_url_idx = 0;
5973 
5974 		if ((e==GF_OK) && group->force_switch_bandwidth) {
5975 			if (!dash->auto_switch_count) {
5976 				gf_dash_switch_group_representation(dash, group);
5977 				if (new_base_seg_url) gf_free(new_base_seg_url);
5978 				if (key_url) gf_free(key_url);
5979 				/*restart*/
5980 				return GF_DASH_DownloadRestart;
5981 			}
5982 			if (rep->playback.disabled) {
5983 				gf_dash_skip_disabled_representation(group, rep, GF_FALSE);
5984 				if (new_base_seg_url) gf_free(new_base_seg_url);
5985 				if (key_url) gf_free(key_url);
5986 				/*restart*/
5987 				return GF_DASH_DownloadRestart;
5988 			}
5989 		}
5990 		group->segment_must_be_streamed = base_group->segment_must_be_streamed;
5991 
5992 		if (group->segment_must_be_streamed)
5993 			local_file_name = dash->dash_io->get_url(dash->dash_io, base_group->segment_download);
5994 		else
5995 			local_file_name = dash->dash_io->get_cache_name(dash->dash_io, base_group->segment_download);
5996 
5997 		file_size = dash->dash_io->get_total_size(dash->dash_io, base_group->segment_download);
5998 		if (file_size==0) {
5999 			empty_file = GF_TRUE;
6000 		}
6001 		resource_name = dash->dash_io->get_url(dash->dash_io, base_group->segment_download);
6002 
6003 		Bps = dash->dash_io->get_bytes_per_sec(dash->dash_io, base_group->segment_download);
6004 	}//unthreaded mode
6005 	else {
6006 		resource_name = local_file_name = new_base_seg_url;
6007 		remote_file = GF_TRUE;
6008 
6009 		hdr = dash->dash_io->get_header_value(dash->dash_io, base_group->segment_download, "x-atsc");
6010 		if (hdr && !strcmp(hdr, "yes"))
6011 			rep->playback.broadcast_flag = GF_TRUE;
6012 	}
6013 
6014 	if (local_file_name && (e == GF_OK || group->segment_must_be_streamed || !dash->thread_mode ) ) {
6015 		if (group->cache_mutex) gf_mx_p(group->cache_mutex);
6016 
6017 		assert(base_group->nb_cached_segments<base_group->max_cached_segments);
6018 		assert(local_file_name);
6019 
6020 		if (!empty_file) {
6021 			segment_cache_entry *cache_entry = &base_group->cached[base_group->nb_cached_segments];
6022 
6023 			cache_entry->cache = gf_strdup(local_file_name);
6024 			cache_entry->url = gf_strdup( resource_name );
6025 			if (use_byterange && remote_file) {
6026 				cache_entry->start_range = start_range;
6027 				cache_entry->end_range = end_range;
6028 			} else {
6029 				cache_entry->start_range = 0;
6030 				cache_entry->end_range = 0;
6031 			}
6032 			cache_entry->representation_index = representation_index;
6033 			cache_entry->duration = (u32) group->current_downloaded_segment_duration;
6034 			cache_entry->loop_detected = group->loop_detected;
6035 			cache_entry->has_dep_following = has_dep_following;
6036 			if (key_url) {
6037 				cache_entry->key_url = key_url;
6038 				memcpy(cache_entry->key_IV, key_iv, sizeof(bin128));
6039 				key_url = NULL;
6040 			}
6041 
6042 			group->loop_detected = GF_FALSE;
6043 
6044 			if (group->local_files && use_byterange) {
6045 				cache_entry->start_range = start_range;
6046 				cache_entry->end_range = end_range;
6047 			}
6048 			GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Added file to cache (%u/%u in cache): %s\n", base_group->nb_cached_segments+1, base_group->max_cached_segments, cache_entry->url));
6049 
6050 			base_group->nb_cached_segments++;
6051 		}
6052 		if (file_size)
6053 			dash_store_stats(dash, group, Bps, file_size, rep->playback.broadcast_flag);
6054 
6055 		/* download enhancement representation of this segment*/
6056 		if ((representation_index != group->max_complementary_rep_index) && rep->playback.enhancement_rep_index_plus_one) {
6057 			group->active_rep_index = rep->playback.enhancement_rep_index_plus_one - 1;
6058 			group->has_pending_enhancement = GF_TRUE;
6059 		}
6060 		/* if we have downloaded all enhancement representations of this segment, restart from base representation and increase dowloaded segment index by 1*/
6061 		else {
6062 			if (group->base_rep_index_plus_one) group->active_rep_index = group->base_rep_index_plus_one - 1;
6063 			if (dash->speed >= 0) {
6064 				group->download_segment_index++;
6065 			} else if (group->download_segment_index) {
6066 				group->download_segment_index--;
6067 			} else {
6068 				GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Playing in backward - start of playlist reached - assuming end of stream\n"));
6069 				gf_dash_mark_group_done(group);
6070 			}
6071 			group->has_pending_enhancement = GF_FALSE;
6072 		}
6073 		if (dash->auto_switch_count) {
6074 			group->nb_segments_done++;
6075 			if (group->nb_segments_done==dash->auto_switch_count) {
6076 				group->nb_segments_done=0;
6077 				gf_dash_skip_disabled_representation(group, rep, GF_TRUE);
6078 			}
6079 		}
6080 
6081 		if (group->cache_mutex) gf_mx_v(group->cache_mutex);
6082 
6083 		//do not notify segments if there is a pending period switch - since these are decided by the user, we don't
6084 		//want to notify old segments
6085 		if (!dash->request_period_switch && !group->has_pending_enhancement && !has_dep_following)
6086 			dash->dash_io->on_dash_event(dash->dash_io, GF_DASH_EVENT_SEGMENT_AVAILABLE, gf_list_find(dash->groups, base_group), GF_OK);
6087 
6088 	}
6089 	if (new_base_seg_url) gf_free(new_base_seg_url);
6090 	if (key_url) gf_free(key_url);
6091 	if (e) return GF_DASH_DownloadCancel;
6092 	return GF_DASH_DownloadSuccess;
6093 }
6094 
6095 
dash_download_group(GF_DashClient * dash,GF_DASH_Group * group,GF_DASH_Group * base_group,Bool has_dep_following)6096 static DownloadGroupStatus dash_download_group(GF_DashClient *dash, GF_DASH_Group *group, GF_DASH_Group *base_group, Bool has_dep_following)
6097 {
6098 	DownloadGroupStatus res;
6099 
6100 	if (!group->current_dep_idx) {
6101 		res = dash_download_group_download(dash, group, base_group, has_dep_following);
6102 		if (res==GF_DASH_DownloadRestart) return res;
6103 		if (res==GF_DASH_DownloadCancel) return res;
6104 		group->current_dep_idx = 1;
6105 	}
6106 
6107 	if (group->groups_depending_on) {
6108 		u32 i, count = gf_list_count(group->groups_depending_on);
6109 		i = group->current_dep_idx - 1;
6110 		for (; i<count; i++) {
6111 
6112 			GF_DASH_Group *dep_group = gf_list_get(group->groups_depending_on, i);
6113 			if ((i+1==count) && !dep_group->groups_depending_on)
6114 				has_dep_following = GF_FALSE;
6115 
6116 			res = dash_download_group(dash, dep_group, base_group, has_dep_following);
6117 			if (res==GF_DASH_DownloadRestart) {
6118 				i--;
6119 				continue;
6120 			}
6121 			group->current_dep_idx = i + 1;
6122 			if (res==GF_DASH_DownloadCancel)
6123 				return GF_DASH_DownloadCancel;
6124 		}
6125 	}
6126 	group->current_dep_idx = 0;
6127 	return GF_DASH_DownloadSuccess;
6128 }
6129 
6130 //tile based adaptation
dash_global_rate_adaptation(GF_DashClient * dash,Bool for_postponed_only)6131 static void dash_global_rate_adaptation(GF_DashClient *dash, Bool for_postponed_only)
6132 {
6133 	u32 quality_rank;
6134 	u32 min_bandwidth = 0;
6135 	Bool force_rep_idx = GF_FALSE;
6136 	Bool local_file_mode = GF_FALSE;
6137 	GF_MPD_Representation *rep, *rep_new;
6138 	u32 total_rate, max_fsize, bandwidths[20], groups_per_quality[20], max_level;
6139 	u32 q_idx, nb_qualities = 0;
6140 	u32 i, count = gf_list_count(dash->groups), local_files = 0;
6141 
6142 	//initialize min/max bandwidth
6143 	min_bandwidth = 0;
6144 	max_level = 0;
6145 	total_rate = (u32) -1;
6146 	nb_qualities = 1;
6147 	max_fsize = 0;
6148 
6149 	//get max qualities due to SRD descriptions
6150 	//for now, consider all non-SRDs group to run in max quality
6151 	for (i=0; i<gf_list_count(dash->SRDs); i++) {
6152 		struct _dash_srd_desc *srd = gf_list_get(dash->SRDs, i);
6153 		u32 nb_q = MAX(srd->srd_nb_cols, srd->srd_nb_rows);
6154 		if (nb_q > nb_qualities) nb_qualities = nb_q;
6155 	}
6156 
6157 	//estimate bitrate
6158 	for (i=0; i<count; i++) {
6159 		GF_DASH_Group *group = gf_list_get(dash->groups, i);
6160 		if (group->selection != GF_DASH_GROUP_SELECTED) continue;
6161 		if (group->local_files) local_files ++;
6162 		if (!group->bytes_per_sec) {
6163 			if (!for_postponed_only && !dash->thread_mode)
6164 				return;
6165 			continue;
6166 		}
6167 		if (group->done) continue;
6168 
6169 		group->backup_Bps = group->bytes_per_sec;
6170 		//only count broadband ones
6171 		if (dash->atsc_clock_state && !gf_list_count(group->period->base_URLs) && !gf_list_count(group->adaptation_set->base_URLs) && !group->period->origin_base_url) {
6172 			u32 j;
6173 			//get all active rep, count bandwidth for broadband ones
6174 			for (j=0; j<=group->max_complementary_rep_index; j++) {
6175 				rep = gf_list_get(group->adaptation_set->representations, j);
6176 				//this rep is not in broadcast, add bandwidth
6177 				if (gf_list_count(rep->base_URLs)) {
6178 					total_rate = group->bytes_per_sec;
6179 				}
6180 			}
6181 		} else {
6182 			//use rate of largest downloaded file to perform rate adaptation
6183 			//TODO: we should split rate adaptation per baseURL
6184 			if (!max_fsize || (max_fsize<group->total_size)) {
6185 				if (total_rate > group->bytes_per_sec) {
6186 					total_rate = group->bytes_per_sec;
6187 					max_fsize = group->total_size;
6188 				}
6189 			}
6190 		}
6191 	}
6192 	if (total_rate == (u32) -1) {
6193 		total_rate = 0;
6194 	}
6195 	if (local_files==count) {
6196 		total_rate = dash->dash_io->get_bytes_per_sec ? dash->dash_io->get_bytes_per_sec(dash->dash_io, NULL) : 0;
6197 		if (!total_rate) local_file_mode = GF_TRUE;
6198 	} else if (!total_rate) {
6199 		return;
6200 	}
6201 
6202   for (q_idx=0; q_idx<nb_qualities; q_idx++) {
6203 		bandwidths[q_idx] = 0;
6204 		groups_per_quality[q_idx] = 0;
6205 
6206 		for (i=0; i<count; i++) {
6207 			GF_DASH_Group *group = gf_list_get(dash->groups, i);
6208 			if (group->selection != GF_DASH_GROUP_SELECTED) continue;
6209 			if (group->done) continue;
6210 
6211 			quality_rank = gf_dash_get_tiles_quality_rank(dash, group);
6212 			if (quality_rank >= nb_qualities) quality_rank = nb_qualities-1;
6213 			if (quality_rank != q_idx) continue;
6214 
6215 			group->target_new_rep = 0;
6216 			rep = gf_list_get(group->adaptation_set->representations, group->target_new_rep);
6217 			bandwidths[q_idx] += rep->bandwidth;
6218 			groups_per_quality[q_idx] ++;
6219 			if (max_level < 1 + quality_rank) max_level = 1+quality_rank;
6220 
6221 			//quick trick here: if no download cap in local playback, compute the total rate based on
6222 			//quality rank
6223 			if (local_file_mode) {
6224 				u32 nb_reps = gf_list_count(group->adaptation_set->representations);
6225 				//get rep matching the given quality rank - quality 0 is the highest and our
6226 				//reps are sorted from lowest !
6227 				u32 rep_target;
6228 				if (!quality_rank)
6229 					rep_target = nb_reps-1;
6230 				else
6231 					rep_target = (nb_qualities - quality_rank) * nb_reps / nb_qualities;
6232 
6233 				rep = gf_list_get(group->adaptation_set->representations, rep_target);
6234 				total_rate += rep->bandwidth;
6235 			}
6236 		}
6237 		min_bandwidth += bandwidths[q_idx];
6238 	}
6239 	if (local_file_mode) {
6240 		//total rate is in bytes per second, and add a safety of 10 bytes to ensure selection
6241 		total_rate = 10 + total_rate / 8;
6242 	}
6243 
6244 	/*no per-quality adaptation, we may have oscillations*/
6245 	if (!dash->tile_rate_decrease) {
6246 	}
6247 	/*automatic rate alloc*/
6248 	else if (dash->tile_rate_decrease==100) {
6249 		//for each quality level (starting from highest priority), increase the bitrate if possible
6250 		for (q_idx=0; q_idx < max_level; q_idx++) {
6251 			Bool test_pass = GF_TRUE;
6252 			while (1) {
6253 				u32 nb_rep_increased = 0;
6254 				u32 nb_rep_in_qidx = 0;
6255 				u32 cumulated_bw_in_pass = 0;
6256 
6257 				for (i=0; i<count; i++) {
6258 					u32 diff;
6259 					GF_DASH_Group *group = gf_list_get(dash->groups, i);
6260 					if (group->selection != GF_DASH_GROUP_SELECTED) continue;
6261 					if (group->done) continue;
6262 
6263 					quality_rank = gf_dash_get_tiles_quality_rank(dash, group);
6264 					if (quality_rank >= nb_qualities) quality_rank = nb_qualities-1;
6265 					if (quality_rank != q_idx) continue;
6266 
6267 					if (group->target_new_rep + 1 == gf_list_count(group->adaptation_set->representations))
6268 						continue;
6269 
6270 					nb_rep_in_qidx++;
6271 
6272 					rep = gf_list_get(group->adaptation_set->representations, group->target_new_rep);
6273 					diff = rep->bandwidth;
6274 					rep_new = gf_list_get(group->adaptation_set->representations, group->target_new_rep+1);
6275 					diff = rep_new->bandwidth - diff;
6276 
6277 					if (dash->atsc_clock_state) {
6278 						//if baseURL in period or adaptation set, we assume we are in broadband mode, otherwise we re in broadcast, don't count bitrate
6279 						if (!gf_list_count(group->period->base_URLs) && !gf_list_count(group->adaptation_set->base_URLs)) {
6280 							//new rep is in broadcast, force diff to 0 to select the rep
6281 							if (!gf_list_count(rep_new->base_URLs)) {
6282 								diff = 0;
6283 							}
6284 							//new rep is in broadband, prev rep is in broadcast, diff is the new rep bandwidth
6285 							else if (!gf_list_count(rep->base_URLs)) {
6286 								diff = rep_new->bandwidth;
6287 							}
6288 						}
6289 					}
6290 
6291 					if (test_pass) {
6292 						cumulated_bw_in_pass+= diff;
6293 						nb_rep_increased ++;
6294 					} else if (min_bandwidth + diff < 8*total_rate) {
6295 						min_bandwidth += diff;
6296 						nb_rep_increased ++;
6297 						bandwidths[q_idx] += diff;
6298 						group->target_new_rep++;
6299 					}
6300 				}
6301 				if (test_pass) {
6302 					//all reps cannot be switched up in this quality level, do it
6303 					if ( min_bandwidth + cumulated_bw_in_pass > 8*total_rate) {
6304 						break;
6305 					}
6306 				}
6307 				//no more adjustement possible for this quality level
6308 				if (! nb_rep_increased)
6309 					break;
6310 
6311 				test_pass = !test_pass;
6312 			}
6313 		}
6314 
6315 		if (! for_postponed_only) {
6316 			GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Rate Adaptation - download rate %d kbps - %d quality levels (cumulated representations rate %d kbps)\n", 8*total_rate/1000, max_level, min_bandwidth/1000));
6317 
6318 			for (q_idx=0; q_idx<max_level; q_idx++) {
6319 				GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH]\tLevel #%d - %d Adaptation Sets for a total %d kbps allocated\n", q_idx+1, groups_per_quality[q_idx], bandwidths[q_idx]/1000 ));
6320 			}
6321 		}
6322 
6323 		force_rep_idx = GF_TRUE;
6324 	}
6325 	/*for each quality level get tile_rate_decrease% of the available bandwidth*/
6326 	else {
6327 		u32 rate = bandwidths[0] = total_rate * dash->tile_rate_decrease / 100;
6328 		for (i=1; i<max_level; i++) {
6329 			u32 remain = total_rate - rate;
6330 			if (i+1==max_level) {
6331 				bandwidths[i] = remain;
6332 			} else {
6333 				bandwidths[i] = remain * dash->tile_rate_decrease/100;
6334 				rate += bandwidths[i];
6335 			}
6336 		}
6337 
6338 		if (! for_postponed_only) {
6339 			GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] Rate Adaptation - download rate %d kbps - %d quality levels (cumulated rate %d kbps)\n", 8*total_rate/1000, max_level, 8*min_bandwidth/1000));
6340 			for (q_idx=0; q_idx<max_level; q_idx++) {
6341 				GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH]\tLevel #%d - %d Adaptation Sets for a total %d kbps allocated\n", q_idx+1, groups_per_quality[q_idx], 8*bandwidths[q_idx]/1000 ));
6342 			}
6343 		}
6344 	}
6345 
6346 	//GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("DEBUG. 2. dowload at %d \n", 8*bandwidths[q_idx]/1000));
6347 	//bandwitdh sharing done, perform rate adaptation with theses new numbers
6348 	for (i=0; i<count; i++) {
6349 		GF_DASH_Group *group = gf_list_get(dash->groups, i);
6350 		if (group->selection != GF_DASH_GROUP_SELECTED) continue;
6351 		if (group->done) continue;
6352 
6353 		if (force_rep_idx) {
6354 			rep = gf_list_get(group->adaptation_set->representations, group->target_new_rep);
6355 			//add 100 bytes/sec to make sure we select the target one
6356 			group->bytes_per_sec = 100 + rep->bandwidth / 8;
6357 		}
6358 		//decrease by quality level
6359 		else if (dash->tile_rate_decrease) {
6360 			quality_rank = gf_dash_get_tiles_quality_rank(dash, group);
6361 			if (quality_rank >= nb_qualities) quality_rank = nb_qualities-1;
6362 			assert(groups_per_quality[quality_rank]);
6363 			group->bytes_per_sec = bandwidths[quality_rank] / groups_per_quality[quality_rank];
6364 		}
6365 
6366 		if (for_postponed_only) {
6367 			if (group->rate_adaptation_postponed)
6368 				dash_do_rate_adaptation(dash, group);
6369 		} else {
6370 			dash_do_rate_adaptation(dash, group);
6371 		}
6372 		group->bytes_per_sec = group->backup_Bps;
6373 	}
6374 }
6375 
6376 
dash_download_threaded(void * par)6377 static u32 dash_download_threaded(void *par)
6378 {
6379 	GF_DASH_Group *group = (GF_DASH_Group *) par;
6380 	if (!group) return 0;
6381 	group->download_th_done = GF_FALSE;
6382 
6383 	while (1) {
6384 		DownloadGroupStatus res = dash_download_group(group->dash, group, group, group->groups_depending_on ? GF_TRUE : GF_FALSE);
6385 		if (res==GF_DASH_DownloadRestart) {
6386 			continue;
6387 		}
6388 		break;
6389 	}
6390 	group->download_th_done = GF_TRUE;
6391 	return 0;
6392 }
6393 
6394 
dash_setup_period_and_groups(GF_DashClient * dash)6395 static GF_Err dash_setup_period_and_groups(GF_DashClient *dash)
6396 {
6397 	u32 i, group_count;
6398 	GF_Err e;
6399 
6400 	//don't resetup the entire period, only the broken group(s) ...
6401 	if (!dash->period_groups_setup) {
6402 		/*setup period*/
6403 		e = gf_dash_setup_period(dash);
6404 		if (e) {
6405 			//move to stop state before sending the error event otherwise we might deadlock when disconnecting the dash client
6406 			dash->dash_state = GF_DASH_STATE_STOPPED;
6407 			dash->dash_io->on_dash_event(dash->dash_io, GF_DASH_EVENT_PERIOD_SETUP_ERROR, -1, e);
6408 			return e;
6409 		}
6410 		dash->dash_io->on_dash_event(dash->dash_io, GF_DASH_EVENT_SELECT_GROUPS, -1, GF_OK);
6411 
6412 		dash->period_groups_setup = GF_TRUE;
6413 		dash->all_groups_done_notified = GF_FALSE;
6414 	}
6415 
6416 	e = GF_OK;
6417 	group_count = gf_list_count(dash->groups);
6418 	for (i=0; i<group_count; i++) {
6419 		GF_DASH_Group *group = gf_list_get(dash->groups, i);
6420 		if (group->selection==GF_DASH_GROUP_NOT_SELECTABLE)
6421 			continue;
6422 
6423 		if (group->group_setup) continue;
6424 
6425 		if (group->cache_mutex) gf_mx_p(group->cache_mutex);
6426 		e = gf_dash_download_init_segment(dash, group);
6427 
6428 		if (group->cache_mutex) gf_mx_v(group->cache_mutex);
6429 		//might happen with broadcast DASH (eg ATSC3)
6430 		if (e == GF_IP_NETWORK_EMPTY) {
6431 			if (dash->mpd_stop_request)
6432 				return GF_OK;
6433 
6434 			if (dash->reinit_period_index) {
6435 				gf_dash_reset_groups(dash);
6436 				dash->active_period_index = dash->reinit_period_index-1;
6437 				dash->reinit_period_index = 0;
6438 				dash->period_groups_setup = GF_FALSE;
6439 				return dash_setup_period_and_groups(dash);
6440 			}
6441 
6442 			return e;
6443 		}
6444 		group->group_setup = GF_TRUE;
6445 		if (e) break;
6446 	}
6447 	dash->initial_period_tunein = GF_FALSE;
6448 
6449 	/*if error signal to the user*/
6450 	if (e != GF_OK) {
6451 		//move to stop state before sending the error event otherwise we might deadlock when disconnecting the dash client
6452 		dash->dash_state = GF_DASH_STATE_STOPPED;
6453 		dash->dash_io->on_dash_event(dash->dash_io, GF_DASH_EVENT_PERIOD_SETUP_ERROR, -1, e);
6454 		return e;
6455 	}
6456 
6457 	return GF_OK;
6458 }
6459 
dash_do_groups(GF_DashClient * dash)6460 static void dash_do_groups(GF_DashClient *dash)
6461 {
6462 	GF_Err e;
6463 	u32 i, group_count = gf_list_count(dash->groups);
6464 
6465 	dash->min_wait_ms_before_next_request = 0;
6466 
6467 	/*for each selected groups*/
6468 	for (i=0; i<group_count; i++) {
6469 		GF_DASH_Group *group = gf_list_get(dash->groups, i);
6470 		if (group->selection != GF_DASH_GROUP_SELECTED) {
6471 			if (group->nb_cached_segments && !group->dont_delete_first_segment) {
6472 				gf_dash_group_reset(dash, group);
6473 			}
6474 			group->download_th_done = GF_TRUE;
6475 			continue;
6476 		}
6477 
6478 		if (group->depend_on_group) continue;
6479 		//not yet scheduled for download
6480 		if (group->rate_adaptation_postponed) {
6481 			group->download_th_done = GF_TRUE;
6482 			continue;
6483 		}
6484 
6485 		if (dash->thread_mode == GF_DASH_THREAD_ALL) {
6486 			group->download_th_done = GF_FALSE;
6487 			e = gf_th_run(group->download_th, dash_download_threaded, group);
6488 			if (e!=GF_OK) {
6489 				GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Cannot launch download thread for AdaptationSet #%d - error %s\n", i+1, gf_error_to_string(e)));
6490 				group->download_th_done = GF_TRUE;
6491 			}
6492 		} else {
6493 			DownloadGroupStatus res;
6494 			group->download_th_done = GF_FALSE;
6495 			res = dash_download_group(dash, group, group, group->groups_depending_on ? GF_TRUE : GF_FALSE);
6496 			if (res==GF_DASH_DownloadRestart) {
6497 				i--;
6498 				continue;
6499 			}
6500 			group->download_th_done = GF_TRUE;
6501 		}
6502 	}
6503 
6504 	while (dash->thread_mode == GF_DASH_THREAD_ALL) {
6505 		Bool all_done = GF_TRUE;
6506 		for (i=0; i<group_count; i++) {
6507 			GF_DASH_Group *group = gf_list_get(dash->groups, i);
6508 			if (group->selection != GF_DASH_GROUP_SELECTED) {
6509 				continue;
6510 			}
6511 			if (group->depend_on_group) continue;
6512 			//not yet scheduled for download
6513 			if (group->rate_adaptation_postponed) continue;
6514 
6515 			if (!group->download_th_done) {
6516 				all_done = GF_FALSE;
6517 				break;
6518 			}
6519 		}
6520 		if (all_done)
6521 			break;
6522 
6523 		if (dash->thread_mode)
6524 			gf_sleep(1);
6525 	}
6526 	//in non threaded mode we need to wait for the stats
6527 	if (dash->thread_mode)
6528 		dash_global_rate_adaptation(dash, GF_FALSE);
6529 }
6530 
dash_check_mpd_update_and_cache(GF_DashClient * dash,Bool * cache_is_full)6531 static GF_Err dash_check_mpd_update_and_cache(GF_DashClient *dash, Bool *cache_is_full)
6532 {
6533 	GF_Err e = GF_OK;
6534 	u32 i, group_count;
6535 	u32 timer = gf_sys_clock() - dash->last_update_time;
6536 	Bool has_postponed_rate_adaptation;
6537 
6538 	(*cache_is_full) = GF_TRUE;
6539 	has_postponed_rate_adaptation = GF_FALSE;
6540 
6541 	group_count = gf_list_count(dash->groups);
6542 	/*refresh MPD*/
6543 	if (dash->force_mpd_update || (dash->mpd->minimum_update_period && (timer > dash->mpd->minimum_update_period))) {
6544 		u32 diff = gf_sys_clock();
6545 		if (dash->force_mpd_update || dash->mpd->minimum_update_period) {
6546 			GF_LOG(GF_LOG_INFO, 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));
6547 		}
6548 		dash->force_mpd_update = 0;
6549 
6550 		if (dash->dash_mutex) gf_mx_p(dash->dash_mutex);
6551 		e = gf_dash_update_manifest(dash);
6552 		if (dash->dash_mutex) gf_mx_v(dash->dash_mutex);
6553 
6554 		diff = gf_sys_clock() - diff;
6555 		if (e) {
6556 			if (!dash->in_error) {
6557 				GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Error updating MPD %s\n", gf_error_to_string(e)));
6558 			}
6559 		} else {
6560 			GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Updated MPD in %d ms\n", diff));
6561 		}
6562 	} else {
6563 		Bool all_groups_done = GF_TRUE;
6564 		Bool cache_full = GF_TRUE;
6565 
6566 		has_postponed_rate_adaptation = GF_FALSE;
6567 
6568 		/*wait if nothing is ready to be downloaded*/
6569 		if (dash->min_wait_ms_before_next_request > 1) {
6570 			if (dash->thread_mode) {
6571 				u32 sleep_for = MIN(dash->min_wait_ms_before_next_request/2, 1000);
6572 				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", dash->min_wait_ms_before_next_request, sleep_for));
6573 				gf_sleep(sleep_for);
6574 			} else {
6575 				if (gf_sys_clock() < dash->min_wait_sys_clock + dash->min_wait_ms_before_next_request) {
6576 					return GF_EOS;
6577 				}
6578 				dash->min_wait_ms_before_next_request = 0;
6579 			}
6580 		}
6581 
6582 		/*check if cache is not full*/
6583 		dash->tsb_exceeded = 0;
6584 		dash->time_in_tsb = 0;
6585 		for (i=0; i<group_count; i++) {
6586 			GF_MPD_Type type = dash->mpd->type;
6587 			GF_DASH_Group *group = gf_list_get(dash->groups, i);
6588 
6589 			if (group->period->origin_base_url)
6590 				type = group->period->type;
6591 
6592 			if (group->cache_mutex) gf_mx_p(group->cache_mutex);
6593 
6594 			if ((group->selection != GF_DASH_GROUP_SELECTED)
6595 				|| group->depend_on_group
6596 				|| (group->done && !group->nb_cached_segments)
6597 			) {
6598 				if (group->cache_mutex) gf_mx_v(group->cache_mutex);
6599 				continue;
6600 			}
6601 			all_groups_done = 0;
6602 			if (type==GF_MPD_TYPE_DYNAMIC) {
6603 				gf_dash_group_check_time(group);
6604 			}
6605 			//cache is full, notify client some segments are pending
6606 			if ((group->nb_cached_segments == group->max_cached_segments)
6607 				&& !dash->request_period_switch
6608 				&& !group->has_pending_enhancement
6609 			) {
6610 				dash->dash_io->on_dash_event(dash->dash_io, GF_DASH_EVENT_SEGMENT_AVAILABLE, i, GF_OK);
6611 			}
6612 			if (group->done && group->nb_cached_segments) {
6613 				dash->dash_io->on_dash_event(dash->dash_io, GF_DASH_EVENT_SEGMENT_AVAILABLE, i, GF_OK);
6614 			}
6615 
6616 			if (group->nb_cached_segments<group->max_cached_segments) {
6617 				cache_full = 0;
6618 			}
6619 			if (group->rate_adaptation_postponed)
6620 				has_postponed_rate_adaptation = GF_TRUE;
6621 
6622 			if (group->cache_mutex) gf_mx_v(group->cache_mutex);
6623 			if (!cache_full)
6624 				break;
6625 		}
6626 
6627 		if (dash->tsb_exceeded) {
6628 			dash->dash_io->on_dash_event(dash->dash_io, GF_DASH_EVENT_TIMESHIFT_OVERFLOW, (s32) dash->tsb_exceeded, GF_OK);
6629 			dash->tsb_exceeded = 0;
6630 		} else if (dash->time_in_tsb != dash->prev_time_in_tsb) {
6631 			dash->prev_time_in_tsb = dash->time_in_tsb;
6632 			dash->dash_io->on_dash_event(dash->dash_io, GF_DASH_EVENT_TIMESHIFT_UPDATE, 0, GF_OK);
6633 		}
6634 
6635 
6636 		if (!cache_full) {
6637 			(*cache_is_full) = GF_FALSE;
6638 		} else {
6639 			//seek request
6640 			if (dash->request_period_switch==2) all_groups_done = 1;
6641 
6642 			if (all_groups_done && dash->next_period_checked) {
6643 				dash->next_period_checked = 1;
6644 			}
6645 			if (all_groups_done && dash->request_period_switch) {
6646 				gf_dash_reset_groups(dash);
6647 				if (dash->request_period_switch == 1) {
6648 					if (dash->speed<0) {
6649 						if (dash->active_period_index) {
6650 							dash->active_period_index--;
6651 						}
6652 					} else {
6653 						dash->active_period_index++;
6654 					}
6655 				}
6656 
6657 				(*cache_is_full) = GF_FALSE;
6658 				dash->request_period_switch = 0;
6659 				dash->period_groups_setup = GF_FALSE;
6660 				GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] Switching to period #%d\n", dash->active_period_index+1));
6661 				dash->dash_state = GF_DASH_STATE_SETUP;
6662 
6663 				if (!dash->all_groups_done_notified) {
6664 					dash->all_groups_done_notified = GF_TRUE;
6665 					dash->dash_io->on_dash_event(dash->dash_io, GF_DASH_EVENT_END_OF_PERIOD, 0, GF_OK);
6666 				}
6667 
6668 			} else {
6669 				(*cache_is_full) = GF_TRUE;
6670 				return GF_OK;
6671 			}
6672 		}
6673 	}
6674 
6675 	if (!dash->mpd_stop_request && has_postponed_rate_adaptation) {
6676 		dash_global_rate_adaptation(dash, GF_TRUE);
6677 	}
6678 	return GF_OK;
6679 }
6680 
gf_dash_process_internal(GF_DashClient * dash)6681 static GF_Err gf_dash_process_internal(GF_DashClient *dash)
6682 {
6683 	GF_Err e;
6684 	Bool cache_is_full;
6685 	if (dash->mpd_stop_request) return GF_EOS;
6686 
6687 	switch (dash->dash_state) {
6688 	case GF_DASH_STATE_SETUP:
6689 		dash->in_period_setup = 1;
6690 		e = dash_setup_period_and_groups(dash);
6691 		if (e) return e;
6692 
6693 		dash->last_update_time = gf_sys_clock();
6694 		if (dash->dash_mutex) gf_mx_p(dash->dash_mutex);
6695 		dash->dash_state = GF_DASH_STATE_CONNECTING;
6696 		if (dash->dash_mutex) gf_mx_v(dash->dash_mutex);
6697 		return GF_OK;
6698 
6699 	case GF_DASH_STATE_CONNECTING:
6700 		/*ask the user to connect to desired groups*/
6701 		e = dash->dash_io->on_dash_event(dash->dash_io, GF_DASH_EVENT_CREATE_PLAYBACK, -1, GF_OK);
6702 		if (e) return e;
6703 		if (dash->dash_mutex) gf_mx_p(dash->dash_mutex);
6704 		dash->in_period_setup = 0;
6705 		dash->dash_state = GF_DASH_STATE_RUNNING;
6706 		if (dash->dash_mutex) gf_mx_v(dash->dash_mutex);
6707 		dash->min_wait_ms_before_next_request = 0;
6708 		return GF_OK;
6709 	case GF_DASH_STATE_RUNNING:
6710 		e = dash_check_mpd_update_and_cache(dash, &cache_is_full);
6711 		if (e || cache_is_full) {
6712 			return GF_OK;
6713 		}
6714 		if (dash->dash_state == GF_DASH_STATE_SETUP)
6715 			return GF_OK;
6716 
6717 		dash->initial_period_tunein = GF_FALSE;
6718 		dash_do_groups(dash);
6719 		return GF_OK;
6720 	case GF_DASH_STATE_STOPPED:
6721 		return GF_EOS;
6722 	}
6723 	return GF_OK;
6724 }
6725 
gf_dash_process(GF_DashClient * dash)6726 GF_Err gf_dash_process(GF_DashClient *dash)
6727 {
6728 	if (dash->thread_mode) return GF_BAD_PARAM;
6729 	return gf_dash_process_internal(dash);
6730 }
6731 
dash_main_thread_proc(void * par)6732 static u32 dash_main_thread_proc(void *par)
6733 {
6734 	GF_Err e;
6735 	u32 ret = 0;
6736 	Bool go_on = GF_TRUE;
6737 	GF_DashClient *dash = (GF_DashClient*) par;
6738 
6739 	if (!dash)
6740 		return 0;
6741 
6742 	if (!dash->mpd) {
6743 		GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] Incorrect state, no dash->mpd for URL=%s, already stopped ?\n", dash->base_url));
6744 		return 1;
6745 	}
6746 
6747 restart_period:
6748 
6749 	/* Setting the download status in exclusive code */
6750 	if (dash->dash_mutex) gf_mx_p(dash->dash_mutex);
6751 	dash->dash_state = GF_DASH_STATE_SETUP;
6752 	dash->period_groups_setup = GF_FALSE;
6753 	if (dash->dash_mutex) gf_mx_v(dash->dash_mutex);
6754 
6755 	//period setup state
6756 	e = gf_dash_process_internal(dash);
6757 	if (e) {
6758 		ret = 1;
6759 		goto exit;
6760 	}
6761 
6762 	//group creation
6763 	e = gf_dash_process_internal(dash);
6764 	if (e || dash->mpd_stop_request) {
6765 		ret = 1;
6766 		goto exit;
6767 	}
6768 
6769 	while (go_on) {
6770 		Bool cache_is_full = GF_FALSE;
6771 
6772 
6773 		/*wait until next segment is needed*/
6774 		while (!dash->mpd_stop_request) {
6775 			e = dash_check_mpd_update_and_cache(dash, &cache_is_full);
6776 			if (e || ! cache_is_full) break;
6777 
6778 			if (dash->dash_state == GF_DASH_STATE_SETUP)
6779 				goto restart_period;
6780 
6781 			gf_sleep(30);
6782 		}
6783 
6784 		/* stop the thread if requested */
6785 		if (dash->mpd_stop_request) {
6786 			break;
6787 		}
6788 		dash_do_groups(dash);
6789 	}
6790 
6791 exit:
6792 	/* Signal that the download thread has ended */
6793 	if (dash->dash_mutex) gf_mx_p(dash->dash_mutex);
6794 
6795 	/*an error occured during playback chain creation and we couldn't release our plyayback chain in time, do it now*/
6796 	if (dash->dash_state == GF_DASH_STATE_CONNECTING)
6797 		gf_dash_reset_groups(dash);
6798 
6799 	dash->dash_state = GF_DASH_STATE_STOPPED;
6800 	if (dash->dash_mutex) gf_mx_v(dash->dash_mutex);
6801 	return ret;
6802 }
6803 
gf_dash_period_index_from_time(GF_DashClient * dash,u64 time)6804 static u32 gf_dash_period_index_from_time(GF_DashClient *dash, u64 time)
6805 {
6806 	u32 i, count, active_period_plus_one;
6807 	u64 cumul_start = 0;
6808 	Bool is_dyn = GF_FALSE;
6809 	GF_MPD_Period *period;
6810 
6811 	if (dash->mpd->type==GF_MPD_TYPE_DYNAMIC) {
6812 		u64 now, availabilityStartTime;
6813 		availabilityStartTime = dash->mpd->availabilityStartTime + dash->utc_shift;
6814 		availabilityStartTime += dash->utc_drift_estimate;
6815 
6816 		now = dash->mpd_fetch_time + (gf_sys_clock() - dash->last_update_time) - availabilityStartTime;
6817 		if (dash->initial_time_shift_value<=100) {
6818 			now -= dash->mpd->time_shift_buffer_depth * dash->initial_time_shift_value / 100;
6819 		} else {
6820 			now -= dash->initial_time_shift_value;
6821 		}
6822 		if (!time) is_dyn = GF_TRUE;
6823 		time += now;
6824 	}
6825 
6826 
6827 restart:
6828 	count = gf_list_count(dash->mpd->periods);
6829 	cumul_start = 0;
6830 	active_period_plus_one = 0;
6831 	for (i = 0; i<count; i++) {
6832 		period = gf_list_get(dash->mpd->periods, i);
6833 
6834 		if (period->xlink_href && !period->duration) {
6835 			if (!active_period_plus_one || period->xlink_actuate_on_load) {
6836 				gf_dash_solve_period_xlink(dash, dash->mpd->periods, i);
6837 				goto restart;
6838 			}
6839 		}
6840 
6841 		if ((period->start > time) || (cumul_start > time)) {
6842 		} else {
6843 			cumul_start += period->duration;
6844 
6845 			if (!active_period_plus_one && (time < cumul_start)) {
6846 				active_period_plus_one = i+1;
6847 			}
6848 		}
6849 	}
6850 	if (is_dyn) {
6851 		for (i = 0; i<count; i++) {
6852 			period = gf_list_get(dash->mpd->periods, i);
6853 			if (!period->xlink_href && !period->origin_base_url) return i;
6854 		}
6855 	}
6856 	return active_period_plus_one ? active_period_plus_one-1 : 0;
6857 }
6858 
gf_dash_download_stop(GF_DashClient * dash)6859 static void gf_dash_download_stop(GF_DashClient *dash)
6860 {
6861 	u32 i;
6862 	assert(dash);
6863 	if (dash->dash_mutex) gf_mx_p(dash->dash_mutex);
6864 	if (dash->groups) {
6865 		for (i=0; i<gf_list_count(dash->groups); i++) {
6866 			GF_DASH_Group *group = gf_list_get(dash->groups, i);
6867 			assert(group);
6868 			if ((group->selection == GF_DASH_GROUP_SELECTED) && group->segment_download) {
6869 				dash->dash_io->abort(dash->dash_io, group->segment_download);
6870 				gf_dash_mark_group_done(group);
6871 			}
6872 		}
6873 	}
6874 	if (!dash->thread_mode) {
6875 		dash->mpd_stop_request = GF_TRUE;
6876 		return;
6877 	}
6878 
6879 	/* stop the download thread */
6880 	dash->mpd_stop_request = GF_TRUE;
6881 	if (dash->dash_state != GF_DASH_STATE_STOPPED) {
6882 		dash->mpd_stop_request = 1;
6883 		if (dash->dash_mutex) gf_mx_v(dash->dash_mutex);
6884 		while (1) {
6885 			/* waiting for the download thread to stop */
6886 			if (dash->dash_mutex) gf_mx_p(dash->dash_mutex);
6887 			if (dash->dash_state == GF_DASH_STATE_STOPPED) {
6888 				/* it's stopped we can continue */
6889 				if (dash->dash_mutex) gf_mx_v(dash->dash_mutex);
6890 				break;
6891 			}
6892 			if (dash->dash_mutex) gf_mx_v(dash->dash_mutex);
6893 		}
6894 	} else {
6895 		if (dash->dash_mutex) gf_mx_v(dash->dash_mutex);
6896 	}
6897 	dash->mpd_stop_request = GF_TRUE;
6898 }
6899 
6900 
6901 
gf_dash_seek_periods(GF_DashClient * dash,Double seek_time)6902 static Bool gf_dash_seek_periods(GF_DashClient *dash, Double seek_time)
6903 {
6904 	Double start_time;
6905 	/*we have an arch issue here: we may get a seek request in normal multiperiod playback
6906 	 with a seek time close but before to the active period, typically if the stream is shorter
6907 	 than the advertised period duration. We will use a safety check of 0.5 seconds, any seek request
6908 	 at Start(Pn) - 0.5 will resolve in seek at Start(Pn)
6909 	 * */
6910 	Bool at_period_boundary=GF_FALSE;
6911 	u32 i, period_idx;
6912 	u32 nb_retry = 10;
6913 	if (dash->dash_mutex) gf_mx_p(dash->dash_mutex);
6914 
6915 	dash->start_range_period = 0;
6916 	start_time = 0;
6917 	period_idx = 0;
6918 	for (i=0; i<gf_list_count(dash->mpd->periods); i++) {
6919 		GF_MPD_Period *period = gf_list_get(dash->mpd->periods, i);
6920 		Double dur;
6921 
6922 		if (period->xlink_href) {
6923 			gf_dash_solve_period_xlink(dash, dash->mpd->periods, i);
6924 			if (nb_retry) {
6925 				nb_retry --;
6926 				i--;
6927 				continue;
6928 			}
6929 			GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Period still resolves to XLINK %s for more than 10 consecutive retry, ignoring it ...\n", period->xlink_href));
6930 			gf_free(period->xlink_href);
6931 			period->xlink_href = NULL;
6932 		} else {
6933 			nb_retry = 10;
6934 		}
6935 		dur = (Double)period->duration;
6936 		dur /= 1000;
6937 		if (seek_time + 0.5 >= start_time) {
6938 			if ((i+1==gf_list_count(dash->mpd->periods)) || (seek_time + 0.5 < start_time + dur) ) {
6939 				if (seek_time > start_time + dur) {
6940 					at_period_boundary = GF_TRUE;
6941 				}
6942 				period_idx = i;
6943 				break;
6944 			}
6945 		}
6946 		start_time += dur;
6947 	}
6948 	if (period_idx != dash->active_period_index) {
6949 		seek_time -= start_time;
6950 		dash->active_period_index = period_idx;
6951 		dash->request_period_switch = 2;
6952 
6953 		dash->start_range_period = seek_time;
6954 	} else if (seek_time < start_time) {
6955 		at_period_boundary = GF_TRUE;
6956 	}
6957 
6958 	if (dash->dash_mutex) gf_mx_v(dash->dash_mutex);
6959 	if (at_period_boundary) return GF_TRUE;
6960 	return dash->request_period_switch ? 1 : 0;
6961 }
6962 
gf_dash_seek_group(GF_DashClient * dash,GF_DASH_Group * group,Double seek_to,Bool is_dynamic)6963 static void gf_dash_seek_group(GF_DashClient *dash, GF_DASH_Group *group, Double seek_to, Bool is_dynamic)
6964 {
6965 	GF_Err e;
6966 	u32 first_downloaded, last_downloaded, segment_idx, orig_idx;
6967 
6968 	if (group->selection==GF_DASH_GROUP_NOT_SELECTABLE) return;
6969 
6970 	if (group->cache_mutex) gf_mx_p(group->cache_mutex);
6971 
6972 	group->force_segment_switch = 0;
6973 	if (!is_dynamic) {
6974 		/*figure out where to seek*/
6975 		orig_idx = group->download_segment_index;
6976 		e = gf_mpd_seek_in_period(seek_to, MPD_SEEK_PREV, group->period, group->adaptation_set, gf_list_get(group->adaptation_set->representations, group->active_rep_index), &segment_idx, NULL);
6977 		if (e<0)
6978 			GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] An error occured while seeking to time %lf: %s\n", seek_to, gf_error_to_string(e)));
6979 
6980 		group->download_segment_index = orig_idx;
6981 
6982 		/*remember to seek to given duration*/
6983 		group->start_playback_range = seek_to;
6984 
6985 		first_downloaded = last_downloaded = group->download_segment_index;
6986 		if (group->download_segment_index + 1 >= (s32) group->nb_cached_segments) {
6987 			first_downloaded = group->download_segment_index + 1 - group->nb_cached_segments;
6988 		}
6989 		/*we are seeking in our download range, just go on*/
6990 		if ((segment_idx>=first_downloaded) && (segment_idx<=last_downloaded)) {
6991 			if (group->cache_mutex) gf_mx_v(group->cache_mutex);
6992 			return;
6993 		}
6994 
6995 		group->force_segment_switch = 1;
6996 		group->download_segment_index = segment_idx;
6997 	} else {
6998 		group->start_number_at_last_ast = 0;
6999 		/*remember to adjust time in timeline steup*/
7000 		group->start_playback_range = seek_to;
7001 		group->timeline_setup = 0;
7002 	}
7003 
7004 
7005 	if (group->segment_download)
7006 		dash->dash_io->abort(dash->dash_io, group->segment_download);
7007 
7008 	if (group->urlToDeleteNext) {
7009 		if (!dash->keep_files && !group->local_files)
7010 			dash->dash_io->delete_cache_file(dash->dash_io, group->segment_download, group->urlToDeleteNext);
7011 
7012 		gf_free(group->urlToDeleteNext);
7013 		group->urlToDeleteNext = NULL;
7014 	}
7015 
7016 	if (group->segment_download) {
7017 		dash->dash_io->abort(dash->dash_io, group->segment_download);
7018 		dash->dash_io->del(dash->dash_io, group->segment_download);
7019 		group->segment_download = NULL;
7020 	}
7021 	while (group->nb_cached_segments) {
7022 		group->nb_cached_segments--;
7023 		if (!dash->keep_files && !group->local_files && !group->segment_must_be_streamed)
7024 			gf_file_delete(group->cached[group->nb_cached_segments].cache);
7025 
7026 		gf_dash_group_reset_cache_entry(&group->cached[group->nb_cached_segments]);
7027 	}
7028 	group->done = 0;
7029 
7030 	if (group->cache_mutex) gf_mx_v(group->cache_mutex);
7031 }
7032 
7033 GF_EXPORT
gf_dash_group_seek(GF_DashClient * dash,u32 group_idx,Double seek_to)7034 void gf_dash_group_seek(GF_DashClient *dash, u32 group_idx, Double seek_to)
7035 {
7036 	GF_DASH_Group *group = gf_list_get(dash->groups, group_idx);
7037 	if (!group) return;
7038 	if (dash->dash_mutex) gf_mx_p(dash->dash_mutex);
7039 	gf_dash_seek_group(dash, group, seek_to, (dash->mpd->type==GF_MPD_TYPE_DYNAMIC) ? GF_TRUE : GF_FALSE);
7040 	if (dash->dash_mutex) gf_mx_v(dash->dash_mutex);
7041 }
7042 
gf_dash_seek_groups(GF_DashClient * dash,Double seek_time,Bool is_dynamic)7043 static void gf_dash_seek_groups(GF_DashClient *dash, Double seek_time, Bool is_dynamic)
7044 {
7045 	u32 i;
7046 
7047 	if (dash->dash_mutex) gf_mx_p(dash->dash_mutex);
7048 
7049 	if (dash->active_period_index) {
7050 		Double dur = 0;
7051 		for (i=0; i<dash->active_period_index; i++) {
7052 			GF_MPD_Period *period = gf_list_get(dash->mpd->periods, dash->active_period_index-1);
7053 			dur += period->duration/1000.0;
7054 		}
7055 		seek_time -= dur;
7056 	}
7057 	for (i=0; i<gf_list_count(dash->groups); i++) {
7058 		GF_DASH_Group *group = gf_list_get(dash->groups, i);
7059 		gf_dash_seek_group(dash, group, seek_time, is_dynamic);
7060 	}
7061 
7062 	if (dash->dash_mutex) gf_mx_v(dash->dash_mutex);
7063 }
7064 
7065 
http_ifce_get(GF_FileDownload * getter,char * url)7066 static GF_Err http_ifce_get(GF_FileDownload *getter, char *url)
7067 {
7068 	GF_Err e;
7069 	GF_DASHFileIOSession *sess;
7070 	GF_DashClient *dash = (GF_DashClient*) getter->udta;
7071 	if (!getter->session) {
7072 		if (!dash->mpd_dnload || (dash->thread_mode!=GF_DASH_THREAD_NONE)) {
7073 			sess = dash->dash_io->create(dash->dash_io, 1, url, -1);
7074 			if (!sess) return GF_IO_ERR;
7075 			getter->session = sess;
7076 			e = GF_OK;
7077 		} else {
7078 			sess = getter->session = dash->mpd_dnload;
7079 			e = dash->dash_io->setup_from_url(dash->dash_io, getter->session, url, -1);
7080 		}
7081 	}
7082 	else {
7083 		u32 group_idx = -1, i;
7084 		for (i = 0; i < gf_list_count(dash->groups); i++) {
7085 			GF_DASH_Group *group = gf_list_get(dash->groups, i);
7086 			if (group->selection != GF_DASH_GROUP_SELECTED) continue;
7087 			group_idx = i;
7088 			break;
7089 		}
7090 		e = dash->dash_io->setup_from_url(dash->dash_io, getter->session, url, group_idx);
7091 		if (e) {
7092 			//with ATSC we may have 404 right away if nothing in cache yet, not an error
7093 			GF_LOG(dash->atsc_clock_state ? GF_LOG_DEBUG : GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Cannot resetup downloader for url %s: %s\n", url, gf_error_to_string(e) ));
7094 			return e;
7095 		}
7096 		sess = (GF_DASHFileIOSession *)getter->session;
7097 	}
7098 	if (!e)
7099 		e = dash->dash_io->init(dash->dash_io, sess);
7100 
7101 	if (e) {
7102 		dash->dash_io->del(dash->dash_io, sess);
7103 		if (getter->session == sess)
7104 			getter->session = NULL;
7105 		return e;
7106 	}
7107 	return dash->dash_io->run(dash->dash_io, sess);
7108 }
7109 
http_ifce_clean(GF_FileDownload * getter)7110 static void http_ifce_clean(GF_FileDownload *getter)
7111 {
7112 	GF_DashClient *dash = (GF_DashClient*) getter->udta;
7113 	if (getter->session) dash->dash_io->del(dash->dash_io, getter->session);
7114 	getter->session = NULL;
7115 }
7116 
http_ifce_cache_name(GF_FileDownload * getter)7117 static const char *http_ifce_cache_name(GF_FileDownload *getter)
7118 {
7119 	GF_DashClient *dash = (GF_DashClient*) getter->udta;
7120 	if (getter->session) return dash->dash_io->get_cache_name(dash->dash_io, getter->session);
7121 	return NULL;
7122 }
7123 
7124 GF_EXPORT
gf_dash_open(GF_DashClient * dash,const char * manifest_url)7125 GF_Err gf_dash_open(GF_DashClient *dash, const char *manifest_url)
7126 {
7127 	char local_path[GF_MAX_PATH];
7128 	const char *local_url;
7129 	char *sep_cgi = NULL;
7130 	char *sep_frag = NULL;
7131 	GF_Err e;
7132 	GF_MPD_Period *period;
7133 	GF_DOMParser *mpd_parser=NULL;
7134 	Bool is_local = GF_FALSE;
7135 
7136 	if (!dash || !manifest_url) return GF_BAD_PARAM;
7137 
7138 	memset( dash->lastMPDSignature, 0, GF_SHA1_DIGEST_SIZE);
7139 	dash->reload_count = 0;
7140 
7141 	if (dash->base_url) gf_free(dash->base_url);
7142 	sep_cgi = strrchr(manifest_url, '?');
7143 	if (sep_cgi) sep_cgi[0] = 0;
7144 	dash->base_url = gf_strdup(manifest_url);
7145 	if (sep_cgi) sep_cgi[0] = '?';
7146 
7147 	dash->getter.udta = dash;
7148 	dash->getter.new_session = http_ifce_get;
7149 	dash->getter.del_session = http_ifce_clean;
7150 	dash->getter.get_cache_name = http_ifce_cache_name;
7151 	dash->getter.session = NULL;
7152 
7153 
7154 
7155 	if (dash->mpd_dnload) dash->dash_io->del(dash->dash_io, dash->mpd_dnload);
7156 	dash->mpd_dnload = NULL;
7157 	local_url = NULL;
7158 
7159 	if (!strnicmp(manifest_url, "file://", 7)) {
7160 		local_url = manifest_url + 7;
7161 		is_local = 1;
7162 		if (strstr(manifest_url, ".m3u8")) {
7163 			dash->is_m3u8 = GF_TRUE;
7164 		}
7165 	} else if (strstr(manifest_url, "://") && strncmp(manifest_url, "gfio://", 7)) {
7166 		const char *reloc_url, *mtype;
7167 		char mime[128];
7168 		e = gf_dash_download_resource(dash, &(dash->mpd_dnload), manifest_url, 0, 0, 1, NULL);
7169 		if (e!=GF_OK) {
7170 			GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Error - cannot connect service: MPD downloading problem %s for %s\n", gf_error_to_string(e), manifest_url));
7171 			dash->dash_io->del(dash->dash_io, dash->mpd_dnload);
7172 			dash->mpd_dnload = NULL;
7173 			return e;
7174 		}
7175 
7176 		mtype = dash->dash_io->get_mime(dash->dash_io, dash->mpd_dnload);
7177 		strcpy(mime, mtype ? mtype : "");
7178 		strlwr(mime);
7179 
7180 		reloc_url = dash->dash_io->get_url(dash->dash_io, dash->mpd_dnload);
7181 		/* Some servers, for instance http://tv.freebox.fr, serve m3u8 as text/plain */
7182 		if (gf_dash_is_m3u8_mime(reloc_url, mime) || strstr(reloc_url, ".m3u8") || strstr(reloc_url, ".M3U8")) {
7183 			dash->is_m3u8 = 1;
7184 		} else if (gf_dash_is_smooth_mime(reloc_url, mime)) {
7185 			dash->is_smooth = 1;
7186 		} else if (!gf_dash_is_dash_mime(mime) && !strstr(reloc_url, ".mpd") && !strstr(reloc_url, ".MPD")) {
7187 			GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] mime '%s' for '%s' should be m3u8 or mpd\n", mime, reloc_url));
7188 			dash->dash_io->del(dash->dash_io, dash->mpd_dnload);
7189 			dash->mpd_dnload = NULL;
7190 			return GF_REMOTE_SERVICE_ERROR;
7191 		}
7192 
7193 		/*if relocated use new URL as base URL for all requests*/
7194 		if (strcmp(reloc_url, manifest_url)) {
7195 			gf_free(dash->base_url);
7196 			dash->base_url = gf_strdup(reloc_url);
7197 		}
7198 
7199 		local_url = dash->dash_io->get_cache_name(dash->dash_io, dash->mpd_dnload);
7200 		if (!local_url) {
7201 			GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Error - cannot connect service: cache problem %s\n", local_url));
7202 			dash->dash_io->del(dash->dash_io, dash->mpd_dnload);
7203 			dash->mpd_dnload = NULL;
7204 			return GF_IO_ERR;
7205 		}
7206 	} else {
7207 		local_url = manifest_url;
7208 		is_local = 1;
7209 		if (strstr(manifest_url, ".m3u8"))
7210 			dash->is_m3u8 = 1;
7211 	}
7212 
7213 	if (is_local && strncmp(manifest_url, "gfio://", 7)) {
7214 		FILE *f = gf_fopen(local_url, "rt");
7215 		if (!f) {
7216 			sep_cgi = strrchr(local_url, '?');
7217 			if (sep_cgi) sep_cgi[0] = 0;
7218 			sep_frag = strrchr(local_url, '#');
7219 			if (sep_frag) sep_frag[0] = 0;
7220 
7221 			f = gf_fopen(local_url, "rt");
7222 			if (!f) {
7223 				if (sep_cgi) sep_cgi[0] = '?';
7224 				if (sep_frag) sep_frag[0] = '#';
7225 				return GF_URL_ERROR;
7226 			}
7227 		}
7228 		gf_fclose(f);
7229 	}
7230 	dash->mpd_fetch_time = dash_get_fetch_time(dash);
7231 
7232 	gf_dash_reset_groups(dash);
7233 
7234 	if (dash->mpd)
7235 		gf_mpd_del(dash->mpd);
7236 
7237 	dash->mpd = gf_mpd_new();
7238 	if (!dash->mpd) {
7239 		e = GF_OUT_OF_MEM;
7240 		GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Error - cannot connect service: MPD creation problem %s\n", gf_error_to_string(e)));
7241 		goto exit;
7242 	}
7243 
7244 	if (dash->is_m3u8) {
7245 		if (is_local) {
7246 			char *sep;
7247 			strcpy(local_path, local_url);
7248 			sep = gf_file_ext_start(local_path);
7249 			if (sep) sep[0]=0;
7250 			strcat(local_path, ".mpd");
7251 
7252 			e = gf_m3u8_to_mpd(local_url, manifest_url, local_path, dash->reload_count, dash->mimeTypeForM3U8Segments, 0, M3U8_TO_MPD_USE_TEMPLATE, M3U8_TO_MPD_USE_SEGTIMELINE, &dash->getter, dash->mpd, GF_FALSE, dash->keep_files);
7253 		} else {
7254 			const char *redirected_url = dash->dash_io->get_url(dash->dash_io, dash->mpd_dnload);
7255 			if (!redirected_url) redirected_url=manifest_url;
7256 
7257 			e = gf_m3u8_to_mpd(local_url, redirected_url, NULL, dash->reload_count, dash->mimeTypeForM3U8Segments, 0, M3U8_TO_MPD_USE_TEMPLATE, M3U8_TO_MPD_USE_SEGTIMELINE, &dash->getter, dash->mpd, GF_FALSE, dash->keep_files);
7258 		}
7259 	} else {
7260 		u32 res = gf_dash_check_mpd_root_type(local_url);
7261 		if (res==2) {
7262 			dash->is_smooth = 1;
7263 		}
7264 		if (!dash->is_smooth && !res) {
7265 			GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Error - cannot connect service: wrong file type %s\n", local_url));
7266 			dash->dash_io->del(dash->dash_io, dash->mpd_dnload);
7267 			dash->mpd_dnload = NULL;
7268 			return GF_URL_ERROR;
7269 		}
7270 
7271 		GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] parsing MPD %s\n", local_url));
7272 
7273 		/* parse the MPD */
7274 		mpd_parser = gf_xml_dom_new();
7275 		e = gf_xml_dom_parse(mpd_parser, local_url, NULL, NULL);
7276 
7277 		if (sep_cgi) sep_cgi[0] = '?';
7278 		if (sep_frag) sep_frag[0] = '#';
7279 
7280 		if (e != GF_OK) {
7281 			GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Error - cannot connect service: MPD parsing problem %s\n", gf_xml_dom_get_error(mpd_parser) ));
7282 			gf_xml_dom_del(mpd_parser);
7283 			dash->dash_io->del(dash->dash_io, dash->mpd_dnload);
7284 			dash->mpd_dnload = NULL;
7285 			return GF_URL_ERROR;
7286 		}
7287 
7288 		if (dash->is_smooth) {
7289 			e = gf_mpd_init_smooth_from_dom(gf_xml_dom_get_root(mpd_parser), dash->mpd, manifest_url);
7290 		} else {
7291 			e = gf_mpd_init_from_dom(gf_xml_dom_get_root(mpd_parser), dash->mpd, manifest_url);
7292 		}
7293 		gf_xml_dom_del(mpd_parser);
7294 
7295 		if (!e && dash->split_adaptation_set)
7296 			gf_mpd_split_adaptation_sets(dash->mpd);
7297 
7298 		if (dash->ignore_xlink)
7299 			dash_purge_xlink(dash->mpd);
7300 
7301 		if (!is_local) {
7302 			const char *hdr = dash->dash_io->get_header_value(dash->dash_io, dash->mpd_dnload, "x-dash-atsc");
7303 			if (hdr) {
7304 				if (!dash->atsc_clock_state) {
7305 					GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] Detected ATSC DASH service ID %s\n", hdr));
7306 					dash->atsc_clock_state = 1;
7307 				}
7308 			}
7309 		}
7310 	}
7311 
7312 	if (e != GF_OK) {
7313 		GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Error - cannot connect service: MPD creation problem %s\n", gf_error_to_string(e)));
7314 		goto exit;
7315 	}
7316 	GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] DASH client initialized from MPD at UTC time "LLU" - availabilityStartTime "LLU"\n", dash->mpd_fetch_time , dash->mpd->availabilityStartTime));
7317 
7318 	if (is_local && dash->mpd->minimum_update_period) {
7319 		e = gf_dash_update_manifest(dash);
7320 		if (e != GF_OK) {
7321 			GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Error - cannot update MPD: %s\n", gf_error_to_string(e)));
7322 			goto exit;
7323 		}
7324 	}
7325 
7326 	/* Get the right period from the given time */
7327 	dash->active_period_index = gf_dash_period_index_from_time(dash, 0);
7328 	period = gf_list_get(dash->mpd->periods, dash->active_period_index);
7329 	if (period->xlink_href) {
7330 		gf_dash_solve_period_xlink(dash, dash->mpd->periods, dash->active_period_index);
7331 		period = gf_list_get(dash->mpd->periods, dash->active_period_index);
7332 	}
7333 	if (!period || !gf_list_count(period->adaptation_sets) ) {
7334 		GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Error - cannot start: not enough periods or representations in MPD\n"));
7335 		e = GF_URL_ERROR;
7336 		goto exit;
7337 	}
7338 
7339 	dash->dash_state = GF_DASH_STATE_SETUP;
7340 	dash->mpd_stop_request = 0;
7341 	if (dash->thread_mode) {
7342 		return gf_th_run(dash->dash_thread, dash_main_thread_proc, dash);
7343 	} else {
7344 		return GF_OK;
7345 	}
7346 
7347 exit:
7348 	if (dash->dash_io) {
7349 		dash->dash_io->del(dash->dash_io, dash->mpd_dnload);
7350 		dash->mpd_dnload = NULL;
7351 	}
7352 
7353 	if (dash->mpd)
7354 		gf_mpd_del(dash->mpd);
7355 	dash->mpd = NULL;
7356 	return e;
7357 }
7358 
7359 GF_EXPORT
gf_dash_close(GF_DashClient * dash)7360 void gf_dash_close(GF_DashClient *dash)
7361 {
7362 	assert(dash);
7363 
7364 	if (dash->dash_io) {
7365 		gf_dash_download_stop(dash);
7366 		if (dash->dash_mutex) gf_mx_p(dash->dash_mutex);
7367 		if (dash->mpd_dnload) {
7368 			dash->dash_io->del(dash->dash_io, dash->mpd_dnload);
7369 			dash->mpd_dnload = NULL;
7370 		}
7371 
7372 		if (dash->getter.del_session)
7373 			dash->getter.del_session(&dash->getter);
7374 
7375 		if (dash->dash_mutex) gf_mx_v(dash->dash_mutex);
7376 	}
7377 	if (dash->mpd)
7378 		gf_mpd_del(dash->mpd);
7379 	dash->mpd = NULL;
7380 
7381 	if (dash->dash_state != GF_DASH_STATE_CONNECTING)
7382 		gf_dash_reset_groups(dash);
7383 }
7384 
7385 GF_EXPORT
gf_dash_set_algo(GF_DashClient * dash,GF_DASHAdaptationAlgorithm algo)7386 void gf_dash_set_algo(GF_DashClient *dash, GF_DASHAdaptationAlgorithm algo)
7387 {
7388 	dash->adaptation_algorithm = algo;
7389 	switch (dash->adaptation_algorithm) {
7390 	case GF_DASH_ALGO_GPAC_LEGACY_BUFFER:
7391 		dash->rate_adaptation_algo = dash_do_rate_adaptation_legacy_buffer;
7392 		dash->rate_adaptation_download_monitor = dash_do_rate_monitor_default;
7393 		break;
7394 	case GF_DASH_ALGO_GPAC_LEGACY_RATE:
7395 		dash->rate_adaptation_algo = dash_do_rate_adaptation_legacy_rate;
7396 		dash->rate_adaptation_download_monitor = dash_do_rate_monitor_default;
7397 		break;
7398 	case GF_DASH_ALGO_BBA0:
7399 		dash->rate_adaptation_algo = dash_do_rate_adaptation_bba0;
7400 		dash->rate_adaptation_download_monitor = dash_do_rate_monitor_default;
7401 		break;
7402 	case GF_DASH_ALGO_BOLA_FINITE:
7403 	case GF_DASH_ALGO_BOLA_BASIC:
7404 	case GF_DASH_ALGO_BOLA_U:
7405 	case GF_DASH_ALGO_BOLA_O:
7406 		dash->rate_adaptation_algo = dash_do_rate_adaptation_bola;
7407 		dash->rate_adaptation_download_monitor = dash_do_rate_monitor_default;
7408 		break;
7409 	case GF_DASH_ALGO_NONE:
7410 	default:
7411 		dash->rate_adaptation_algo = NULL;
7412 		break;
7413 	}
7414 }
7415 
7416 GF_EXPORT
gf_dash_new(GF_DASHFileIO * dash_io,GF_DASHThreadMode thread_mode,u32 max_cache_duration,u32 auto_switch_count,Bool keep_files,Bool disable_switching,GF_DASHInitialSelectionMode first_select_mode,u32 initial_time_shift_percent)7417 GF_DashClient *gf_dash_new(GF_DASHFileIO *dash_io, GF_DASHThreadMode thread_mode, u32 max_cache_duration, u32 auto_switch_count, Bool keep_files, Bool disable_switching, GF_DASHInitialSelectionMode first_select_mode, u32 initial_time_shift_percent)
7418 {
7419 	GF_DashClient *dash;
7420 	if (!dash_io) {
7421 		GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Cannot create client withou sync IO for HTTP\n"));
7422 		return NULL;
7423 	}
7424 
7425 	GF_SAFEALLOC(dash, GF_DashClient);
7426 	if (!dash) return NULL;
7427 	dash->dash_io = dash_io;
7428 	dash->speed = 1.0;
7429 	dash->is_rt_speed = GF_TRUE;
7430 	dash->thread_mode = thread_mode;
7431 	dash->low_latency_mode = GF_DASH_LL_STRICT;
7432 
7433 	//wait one segment to validate we have enough bandwidth
7434 	dash->probe_times_before_switch = 1;
7435 	if (dash->thread_mode) {
7436 		dash->dash_thread = gf_th_new("DashClientMainThread");
7437 		dash->dash_mutex = gf_mx_new("DashClientMainMutex");
7438 	}
7439 	//FIXME: mime type for segments MUST be mp2t, webvtt or a Packed Audio file (like AAC)
7440 	dash->mimeTypeForM3U8Segments = gf_strdup( "video/mp2t" );
7441 
7442 	dash->max_cache_duration = max_cache_duration;
7443 	dash->initial_time_shift_value = initial_time_shift_percent;
7444 
7445 	dash->auto_switch_count = auto_switch_count;
7446 	dash->keep_files = keep_files;
7447 	dash->disable_switching = disable_switching;
7448 	dash->first_select_mode = first_select_mode;
7449 	dash->min_timeout_between_404 = 500;
7450 	dash->segment_lost_after_ms = 100;
7451 	dash->debug_group_index = -1;
7452 	dash->tile_rate_decrease = 100;
7453 	dash->atsc_ast_shift = 1000;
7454 	dash->initial_period_tunein = GF_TRUE;
7455 	GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Client created\n"));
7456 
7457 #ifdef GPAC_ENABLE_COVERAGE
7458 	if (gf_sys_is_cov_mode()) {
7459 		on_group_download_error(NULL, NULL, NULL, GF_OK, NULL, NULL, NULL, GF_FALSE);
7460 		dash_download_threaded(NULL);
7461 		dash_main_thread_proc(NULL);
7462 		gf_dash_is_running(dash);
7463 	}
7464 #endif
7465 	return dash;
7466 }
7467 
7468 GF_EXPORT
gf_dash_del(GF_DashClient * dash)7469 void gf_dash_del(GF_DashClient *dash)
7470 {
7471 	//force group cleanup
7472 	dash->dash_state = GF_DASH_STATE_STOPPED;
7473 	gf_dash_close(dash);
7474 	if (dash->dash_thread)
7475 		gf_th_del(dash->dash_thread);
7476 	if (dash->dash_mutex) gf_mx_del(dash->dash_mutex);
7477 
7478 	if (dash->mimeTypeForM3U8Segments) gf_free(dash->mimeTypeForM3U8Segments);
7479 	if (dash->base_url) gf_free(dash->base_url);
7480 
7481 	gf_free(dash);
7482 }
7483 
7484 GF_EXPORT
gf_dash_enable_utc_drift_compensation(GF_DashClient * dash,Bool estimate_utc_drift)7485 void gf_dash_enable_utc_drift_compensation(GF_DashClient *dash, Bool estimate_utc_drift)
7486 {
7487 	dash->estimate_utc_drift = estimate_utc_drift;
7488 }
7489 
7490 GF_EXPORT
gf_dash_set_switching_probe_count(GF_DashClient * dash,u32 switch_probe_count)7491 void gf_dash_set_switching_probe_count(GF_DashClient *dash, u32 switch_probe_count)
7492 {
7493 	dash->probe_times_before_switch = switch_probe_count;
7494 }
7495 
7496 GF_EXPORT
gf_dash_set_agressive_adaptation(GF_DashClient * dash,Bool agressive_switch)7497 void gf_dash_set_agressive_adaptation(GF_DashClient *dash, Bool agressive_switch)
7498 {
7499 	dash->agressive_switching = agressive_switch;
7500 }
7501 
7502 
7503 GF_EXPORT
gf_dash_get_group_count(GF_DashClient * dash)7504 u32 gf_dash_get_group_count(GF_DashClient *dash)
7505 {
7506 	return gf_list_count(dash->groups);
7507 }
7508 
7509 
7510 GF_EXPORT
gf_dash_get_group_udta(GF_DashClient * dash,u32 idx)7511 void *gf_dash_get_group_udta(GF_DashClient *dash, u32 idx)
7512 {
7513 	GF_DASH_Group *group = gf_list_get(dash->groups, idx);
7514 	if (!group) return NULL;
7515 	return group->udta;
7516 }
7517 
7518 GF_EXPORT
gf_dash_set_group_udta(GF_DashClient * dash,u32 idx,void * udta)7519 GF_Err gf_dash_set_group_udta(GF_DashClient *dash, u32 idx, void *udta)
7520 {
7521 	GF_DASH_Group *group = gf_list_get(dash->groups, idx);
7522 	if (!group) return GF_BAD_PARAM;
7523 	group->udta = udta;
7524 	return GF_OK;
7525 }
7526 
7527 GF_EXPORT
gf_dash_is_group_selected(GF_DashClient * dash,u32 idx)7528 Bool gf_dash_is_group_selected(GF_DashClient *dash, u32 idx)
7529 {
7530 	GF_DASH_Group *group = gf_list_get(dash->groups, idx);
7531 	if (!group) return 0;
7532 	return (group->selection == GF_DASH_GROUP_SELECTED) ? 1 : 0;
7533 }
7534 
7535 GF_EXPORT
gf_dash_is_group_selectable(GF_DashClient * dash,u32 idx)7536 Bool gf_dash_is_group_selectable(GF_DashClient *dash, u32 idx)
7537 {
7538 	GF_DASH_Group *group = gf_list_get(dash->groups, idx);
7539 	if (!group) return 0;
7540 	return (group->selection == GF_DASH_GROUP_NOT_SELECTABLE) ? 0 : 1;
7541 }
7542 
7543 GF_EXPORT
gf_dash_get_info(GF_DashClient * dash,const char ** title,const char ** source)7544 void gf_dash_get_info(GF_DashClient *dash, const char **title, const char **source)
7545 {
7546 	GF_MPD_ProgramInfo *info = dash ? gf_list_get(dash->mpd->program_infos, 0) : NULL;
7547 	if (title) *title = info ? info->title : NULL;
7548 	if (source) *source = info ? info->source : NULL;
7549 }
7550 
7551 
7552 GF_EXPORT
gf_dash_switch_quality(GF_DashClient * dash,Bool switch_up,Bool immediate_switch)7553 void gf_dash_switch_quality(GF_DashClient *dash, Bool switch_up, Bool immediate_switch)
7554 {
7555 	u32 i;
7556 	for (i=0; i<gf_list_count(dash->groups); i++) {
7557 		u32 switch_to_rep_idx = 0;
7558 		u32 bandwidth, quality, k;
7559 		GF_MPD_Representation *rep, *active_rep;
7560 		GF_DASH_Group *group = gf_list_get(dash->groups, i);
7561 		u32 current_idx = group->active_rep_index;
7562 		if (group->selection != GF_DASH_GROUP_SELECTED) continue;
7563 
7564 		if (group->base_rep_index_plus_one) current_idx = group->max_complementary_rep_index;
7565 		if (group->force_representation_idx_plus_one) current_idx = group->force_representation_idx_plus_one - 1;
7566 
7567 		active_rep = gf_list_get(group->adaptation_set->representations, current_idx);
7568 		if (!active_rep) continue;
7569 		bandwidth = switch_up ? (u32) -1 : 0;
7570 		quality = switch_up ? (u32) -1 : 0;
7571 
7572 		for (k=0; k<gf_list_count(group->adaptation_set->representations); k++) {
7573 			rep = gf_list_get(group->adaptation_set->representations, k);
7574 			if (switch_up) {
7575 				if ((rep->quality_ranking>active_rep->quality_ranking) || (rep->bandwidth>active_rep->bandwidth)) {
7576 					if ((rep->quality_ranking < quality) || (rep->bandwidth < bandwidth)) {
7577 						bandwidth = rep->bandwidth;
7578 						quality = rep->quality_ranking;
7579 						switch_to_rep_idx = k+1;
7580 					}
7581 				}
7582 			} else {
7583 				if ((rep->quality_ranking < active_rep->quality_ranking) || (rep->bandwidth < active_rep->bandwidth)) {
7584 					if ((rep->quality_ranking > quality) || (rep->bandwidth > bandwidth)) {
7585 						bandwidth = rep->bandwidth;
7586 						quality = rep->quality_ranking;
7587 						switch_to_rep_idx = k+1;
7588 					}
7589 				}
7590 			}
7591 		}
7592 		if (switch_to_rep_idx && (switch_to_rep_idx-1 != current_idx) ) {
7593 			u32 nb_cached_seg_per_rep = group->max_cached_segments / gf_dash_group_count_rep_needed(group);
7594 
7595 			if (group->cache_mutex) gf_mx_p(group->cache_mutex);
7596 
7597 			group->force_switch_bandwidth = 1;
7598 			if (!group->base_rep_index_plus_one)
7599 				group->force_representation_idx_plus_one = switch_to_rep_idx;
7600 			else
7601 				group->max_complementary_rep_index = switch_to_rep_idx-1;
7602 
7603 
7604 			if (group->local_files || immediate_switch) {
7605 				u32 keep_seg_index = 0;
7606 				//keep all scalable enhancements of the first segment
7607 				rep = gf_list_get(group->adaptation_set->representations, group->cached[0].representation_index);
7608 				if (rep->playback.enhancement_rep_index_plus_one) {
7609 					u32 rep_idx = rep->playback.enhancement_rep_index_plus_one;
7610 					while (keep_seg_index + 1 < group->nb_cached_segments) {
7611 						rep = gf_list_get(group->adaptation_set->representations, group->cached[keep_seg_index+1].representation_index);
7612 						if (rep_idx == group->cached[keep_seg_index+1].representation_index+1) {
7613 							keep_seg_index ++;
7614 							rep_idx = rep->playback.enhancement_rep_index_plus_one;
7615 						}
7616 						else
7617 							break;
7618 					}
7619 				}
7620 
7621 				if (!group->base_rep_index_plus_one) {
7622 					/*in local playback just switch at the end of the current segment
7623 					for remote, we should let the user decide*/
7624 					while (group->nb_cached_segments > keep_seg_index + 1) {
7625 						group->nb_cached_segments--;
7626 						GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Group %d switching quality - delete cached segment: %s\n", i, group->cached[group->nb_cached_segments].url));
7627 
7628 						if (!group->local_files && group->cached[group->nb_cached_segments].cache) {
7629 							gf_file_delete( group->cached[group->nb_cached_segments].cache );
7630 						}
7631 						gf_dash_group_reset_cache_entry(&group->cached[group->nb_cached_segments]);
7632 
7633 						group->cached[group->nb_cached_segments].duration = (u32) group->current_downloaded_segment_duration;
7634 						if (group->download_segment_index>1)
7635 							group->download_segment_index--;
7636 					}
7637 				} else {
7638 					if (switch_up) {
7639 						//first, we keep the second segment and remove all segments from the third one
7640 						keep_seg_index++;
7641 						rep = gf_list_get(group->adaptation_set->representations, group->cached[keep_seg_index].representation_index);
7642 						if (rep->playback.enhancement_rep_index_plus_one) {
7643 							u32 rep_idx = rep->playback.enhancement_rep_index_plus_one;
7644 							while (keep_seg_index + 1 < group->nb_cached_segments) {
7645 								rep = gf_list_get(group->adaptation_set->representations, group->cached[keep_seg_index+1].representation_index);
7646 								if (rep_idx == group->cached[keep_seg_index+1].representation_index+1) {
7647 									keep_seg_index ++;
7648 									rep_idx = rep->playback.enhancement_rep_index_plus_one;
7649 								}
7650 								else
7651 									break;
7652 							}
7653 						}
7654 						while (group->nb_cached_segments > keep_seg_index + 1) {
7655 							Bool decrease_download_segment_index = (group->cached[group->nb_cached_segments-1].representation_index == current_idx) ? GF_TRUE : GF_FALSE;
7656 							group->nb_cached_segments--;
7657 							GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Group %d switching quality - delete cached segment: %s\n", i, group->cached[group->nb_cached_segments].url));
7658 
7659 							if (!group->local_files && group->cached[group->nb_cached_segments].cache) {
7660 								gf_file_delete( group->cached[group->nb_cached_segments].cache );
7661 							}
7662 
7663 							gf_dash_group_reset_cache_entry(&group->cached[group->nb_cached_segments]);
7664 
7665 							group->cached[group->nb_cached_segments].duration = (u32) group->current_downloaded_segment_duration;
7666 
7667 							if (decrease_download_segment_index && group->download_segment_index>1)
7668 								group->download_segment_index--;
7669 						}
7670 						/*force to download scalable enhancement of the second segment*/
7671 						group->force_representation_idx_plus_one = switch_to_rep_idx;
7672 						group->active_rep_index = switch_to_rep_idx - 1;
7673 						group->download_segment_index--;
7674 					}
7675 					else if (group->nb_cached_segments) {
7676 						/* we remove highest scalable enhancements of the dowloaded segments, and keep another segments*/
7677 						for (k = group->nb_cached_segments - 1; k > keep_seg_index; k--) {
7678 							if (group->cached[k].representation_index != current_idx)
7679 								continue;
7680 							group->nb_cached_segments--;
7681 							GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Group %d switching quality - delete cached segment: %s\n", i, group->cached[k].url));
7682 							if (k != group->nb_cached_segments) {
7683 								memmove(&group->cached[k], &group->cached[k+1], (group->nb_cached_segments-k)*sizeof(segment_cache_entry));
7684 							}
7685 							memset(&group->cached[group->nb_cached_segments], 0, sizeof(segment_cache_entry));
7686 						}
7687 					}
7688 				}
7689 			}
7690 			/*resize max cached segment*/
7691 			group->max_cached_segments = nb_cached_seg_per_rep * gf_dash_group_count_rep_needed(group);
7692 
7693 			if (group->srd_desc)
7694 				gf_dash_set_tiles_quality(dash, group->srd_desc);
7695 
7696 			if (group->cache_mutex) gf_mx_v(group->cache_mutex);
7697 		}
7698 	}
7699 }
7700 
7701 GF_EXPORT
gf_dash_get_duration(GF_DashClient * dash)7702 Double gf_dash_get_duration(GF_DashClient *dash)
7703 {
7704 	return gf_mpd_get_duration(dash->mpd);
7705 }
7706 
7707 GF_EXPORT
gf_dash_group_get_time_shift_buffer_depth(GF_DashClient * dash,u32 idx)7708 u32 gf_dash_group_get_time_shift_buffer_depth(GF_DashClient *dash, u32 idx)
7709 {
7710 	GF_DASH_Group *group = gf_list_get(dash->groups, idx);
7711 	if (!group) return 0;
7712 	return group->time_shift_buffer_depth;
7713 }
7714 
7715 GF_EXPORT
gf_dash_get_url(GF_DashClient * dash)7716 const char *gf_dash_get_url(GF_DashClient *dash)
7717 {
7718 	return dash->base_url;
7719 }
7720 
7721 GF_EXPORT
gf_dash_is_m3u8(GF_DashClient * dash)7722 Bool gf_dash_is_m3u8(GF_DashClient *dash) {
7723 	return dash->is_m3u8;
7724 }
7725 
7726 GF_EXPORT
gf_dash_group_get_segment_mime(GF_DashClient * dash,u32 idx)7727 const char *gf_dash_group_get_segment_mime(GF_DashClient *dash, u32 idx)
7728 {
7729 	GF_MPD_Representation *rep;
7730 	GF_DASH_Group *group = gf_list_get(dash->groups, idx);
7731 	if (!group) return NULL;
7732 
7733 	rep = gf_list_get(group->adaptation_set->representations, group->active_rep_index);
7734 	return gf_dash_get_mime_type(NULL, rep, group->adaptation_set);
7735 }
7736 
7737 
7738 
7739 GF_EXPORT
gf_dash_group_get_segment_init_url(GF_DashClient * dash,u32 idx,u64 * start_range,u64 * end_range)7740 const char *gf_dash_group_get_segment_init_url(GF_DashClient *dash, u32 idx, u64 *start_range, u64 *end_range)
7741 {
7742 	GF_MPD_Representation *rep;
7743 	GF_DASH_Group *group = gf_list_get(dash->groups, idx);
7744 	if (!group) return NULL;
7745 
7746 	/*solve dependencies if any - we first test highest: if this is a complementary rep, keep the highest for init
7747 	otherwise use the selected one
7748 	this is need for scalable because we only init once the demuxer*/
7749 
7750 	rep = gf_list_last(group->adaptation_set->representations);
7751 	if (!rep->dependency_id)
7752 		rep = gf_list_get(group->adaptation_set->representations, group->active_rep_index);
7753 
7754 	if (group->bs_switching_init_segment_url) {
7755 		if (start_range) *start_range = group->bs_switching_init_segment_url_start_range;
7756 		if (end_range) *end_range = group->bs_switching_init_segment_url_end_range;
7757 		return group->bs_switching_init_segment_url;
7758 	}
7759 
7760 	//no rep found or no init for the rep, check all reps
7761 	//this may happen when the adaptation rate algo changed the rep between the init (TS) and the next segment
7762 	if (!rep || !rep->playback.cached_init_segment_url) {
7763 		u32 i, count;
7764 		count = gf_list_count(group->adaptation_set->representations);
7765 		for (i=0; i<count; i++) {
7766 			rep = gf_list_get(group->adaptation_set->representations, i);
7767 			if (rep->playback.cached_init_segment_url) break;
7768 			rep = NULL;
7769 		}
7770 	}
7771 	if (!rep) return NULL;
7772 	if (start_range) *start_range = rep->playback.init_start_range;
7773 	if (end_range) *end_range = rep->playback.init_end_range;
7774 	return rep->playback.cached_init_segment_url;
7775 }
7776 
7777 GF_EXPORT
gf_dash_group_get_segment_init_keys(GF_DashClient * dash,u32 idx,bin128 * key_IV)7778 const char *gf_dash_group_get_segment_init_keys(GF_DashClient *dash, u32 idx, bin128 *key_IV)
7779 {
7780 	GF_MPD_Representation *rep;
7781 	GF_DASH_Group *group = gf_list_get(dash->groups, idx);
7782 	if (!group) return NULL;
7783 
7784 	rep = gf_list_get(group->adaptation_set->representations, group->active_rep_index);
7785 	if (!rep) return NULL;
7786 
7787 	if (key_IV) memcpy(*key_IV, rep->playback.key_IV, sizeof(bin128));
7788 	return rep->playback.key_url;
7789 }
7790 
7791 GF_EXPORT
gf_dash_group_select(GF_DashClient * dash,u32 idx,Bool select)7792 void gf_dash_group_select(GF_DashClient *dash, u32 idx, Bool select)
7793 {
7794 	Bool needs_resetup = GF_FALSE;
7795 	GF_DASH_Group *group = gf_list_get(dash->groups, idx);
7796 	if (!group) return;
7797 	if (group->selection == GF_DASH_GROUP_NOT_SELECTABLE)
7798 		return;
7799 
7800 	if ((group->selection==GF_DASH_GROUP_NOT_SELECTED) && select) needs_resetup = 1;
7801 
7802 	group->selection = select ? GF_DASH_GROUP_SELECTED : GF_DASH_GROUP_NOT_SELECTED;
7803 	/*this set is part of a group, make sure no all other sets from the indicated group are unselected*/
7804 	if (select && (group->adaptation_set->group>=0)) {
7805 		u32 i;
7806 		for (i=0; i<gf_dash_get_group_count(dash); i++) {
7807 			GF_DASH_Group *agroup = gf_list_get(dash->groups, i);
7808 			if (agroup==group) continue;
7809 
7810 			/*either one Representation from group 0, if present,*/
7811 			if ((group->adaptation_set->group==0)
7812 			        /*or the combination of at most one Representation from each non-zero group*/
7813 			        || (group->adaptation_set->group==agroup->adaptation_set->group)
7814 			   ) {
7815 				agroup->selection = GF_DASH_GROUP_NOT_SELECTED;
7816 			}
7817 		}
7818 	}
7819 	//TODO: recompute grop download index based on current playback ...
7820 	if (needs_resetup) {
7821 
7822 	}
7823 }
7824 
7825 GF_EXPORT
gf_dash_groups_set_language(GF_DashClient * dash,const char * lang_code_rfc_5646)7826 void gf_dash_groups_set_language(GF_DashClient *dash, const char *lang_code_rfc_5646)
7827 {
7828 	u32 i, len;
7829 	s32 lang_idx;
7830 	GF_List *groups_selected;
7831 	if (!lang_code_rfc_5646) return;
7832 
7833 	groups_selected = gf_list_new();
7834 
7835 	if (dash->dash_mutex) gf_mx_p(dash->dash_mutex);
7836 
7837 	//first pass, check exact match
7838 	for (i=0; i<gf_list_count(dash->groups); i++) {
7839 		GF_DASH_Group *group = gf_list_get(dash->groups, i);
7840 		if (group->selection==GF_DASH_GROUP_NOT_SELECTABLE) continue;
7841 		if (!group->adaptation_set->lang) continue;
7842 
7843 		if (!stricmp(group->adaptation_set->lang, lang_code_rfc_5646)) {
7844 			gf_dash_group_select(dash, i, 1);
7845 			gf_list_add(groups_selected, group);
7846 		}
7847 	}
7848 
7849 	lang_idx = gf_lang_find(lang_code_rfc_5646);
7850 	if (lang_idx>=0) {
7851 		const char *n2cc = gf_lang_get_2cc(lang_idx);
7852 		const char *n3cc = gf_lang_get_3cc(lang_idx);
7853 
7854 		for (i=0; i<gf_list_count(dash->groups); i++) {
7855 			char *sep;
7856 			GF_DASH_Group *group = gf_list_get(dash->groups, i);
7857 			if (group->selection==GF_DASH_GROUP_NOT_SELECTABLE) continue;
7858 			if (!group->adaptation_set->lang) continue;
7859 			if (gf_list_find(groups_selected, group) >= 0) continue;
7860 
7861 			//check we didn't select one AS in this group in the previous pass or in this pass
7862 			if (group->adaptation_set->group>=0) {
7863 				u32 k;
7864 				Bool found = GF_FALSE;
7865 				for (k=0; k<gf_list_count(groups_selected); k++) {
7866 					GF_DASH_Group *ag = gf_list_get(groups_selected, k);
7867 
7868 					if (ag->adaptation_set->group == group->adaptation_set->group) {
7869 						found = 1;
7870 						break;
7871 					}
7872 				}
7873 				if (found) continue;
7874 			}
7875 			//get the 2 or 3 land code
7876 			sep = strchr(group->adaptation_set->lang, '-');
7877 			if (sep) {
7878 				sep[0] = 0;
7879 			}
7880 			len = (u32) strlen(group->adaptation_set->lang);
7881 			//compare with what we found
7882 			if ( ((len==3) && !stricmp(group->adaptation_set->lang, n3cc))
7883 			        || ((len==2) && !stricmp(group->adaptation_set->lang, n2cc))
7884 			   ) {
7885 				gf_dash_group_select(dash, i, 1);
7886 				gf_list_add(groups_selected, group);
7887 			}
7888 
7889 			if (sep) sep[0] = '-';
7890 		}
7891 	}
7892 
7893 	if (dash->dash_mutex) gf_mx_v(dash->dash_mutex);
7894 
7895 	gf_list_del(groups_selected);
7896 }
7897 
7898 GF_EXPORT
gf_dash_is_running(GF_DashClient * dash)7899 Bool gf_dash_is_running(GF_DashClient *dash)
7900 {
7901 	return (dash->dash_state==GF_DASH_STATE_STOPPED) ? GF_FALSE : GF_TRUE;
7902 }
7903 
7904 GF_EXPORT
gf_dash_is_in_setup(GF_DashClient * dash)7905 Bool gf_dash_is_in_setup(GF_DashClient *dash)
7906 {
7907 	if (dash->dash_state==GF_DASH_STATE_SETUP) return GF_TRUE;
7908 	if (dash->dash_state==GF_DASH_STATE_CONNECTING) return GF_TRUE;
7909 	if (dash->request_period_switch) return GF_TRUE;
7910 	return GF_FALSE;
7911 }
7912 
7913 GF_EXPORT
gf_dash_get_period_switch_status(GF_DashClient * dash)7914 u32 gf_dash_get_period_switch_status(GF_DashClient *dash)
7915 {
7916 	return dash->request_period_switch;
7917 }
7918 GF_EXPORT
gf_dash_request_period_switch(GF_DashClient * dash)7919 void gf_dash_request_period_switch(GF_DashClient *dash)
7920 {
7921 	GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] Period switch has been requested\n"));
7922 	dash->request_period_switch = 1;
7923 }
7924 GF_EXPORT
gf_dash_in_last_period(GF_DashClient * dash,Bool check_eos)7925 Bool gf_dash_in_last_period(GF_DashClient *dash, Bool check_eos)
7926 {
7927 	Bool res;
7928 
7929 	switch (dash->dash_state) {
7930 	case GF_DASH_STATE_SETUP:
7931 	case GF_DASH_STATE_CONNECTING:
7932 		return GF_FALSE;
7933 	default:
7934 		break;
7935 	}
7936 
7937 	res = (dash->active_period_index+1 < gf_list_count(dash->mpd->periods)) ? 0 : 1;
7938 	//this code seems buggy, commented for now
7939 #if 0
7940 	if (res && dash->mpd->type==GF_MPD_TYPE_DYNAMIC) {
7941 		GF_MPD_Period*period = gf_list_last(dash->mpd->periods);
7942 		//consider we are
7943 		if (!period->duration || dash->mpd->media_presentation_duration) res = GF_FALSE;
7944 	}
7945 #endif
7946 	return res;
7947 }
7948 GF_EXPORT
gf_dash_in_period_setup(GF_DashClient * dash)7949 Bool gf_dash_in_period_setup(GF_DashClient *dash)
7950 {
7951 	return dash->in_period_setup;
7952 }
7953 
7954 GF_EXPORT
gf_dash_set_speed(GF_DashClient * dash,Double speed)7955 void gf_dash_set_speed(GF_DashClient *dash, Double speed)
7956 {
7957 	u32 i;
7958 	if (!dash) return;
7959 	if (!speed) speed = 1.0;
7960 	if (dash->speed == speed) return;
7961 
7962 	for (i=0; i<gf_list_count(dash->groups); i++) {
7963 		GF_DASH_Group *group = (GF_DASH_Group *)gf_list_get(dash->groups, i);
7964 		GF_MPD_Representation *active_rep;
7965 		Double max_available_speed;
7966 		if (!group || (group->selection != GF_DASH_GROUP_SELECTED)) continue;
7967 		active_rep = (GF_MPD_Representation *)gf_list_get(group->adaptation_set->representations, group->active_rep_index);
7968 		if (speed < 0)
7969 			group->decode_only_rap = GF_TRUE;
7970 
7971 		max_available_speed = gf_dash_get_max_available_speed(dash, group, active_rep);
7972 
7973 		/*verify if this representation support this speed*/
7974 		if (!max_available_speed || (ABS(speed) <= max_available_speed)) {
7975 			//nothing to do
7976 		} else {
7977 			/*if the representation does not support this speed, search for another which support it*/
7978 			u32 switch_to_rep_idx = 0;
7979 			u32 bandwidth = 0, quality = 0, k;
7980 			GF_MPD_Representation *rep;
7981 			for (k=0; k<gf_list_count(group->adaptation_set->representations); k++) {
7982 				rep = gf_list_get(group->adaptation_set->representations, k);
7983 				if ((ABS(speed) <= rep->max_playout_rate) && ((rep->quality_ranking > quality) || (rep->bandwidth > bandwidth))) {
7984 					bandwidth = rep->bandwidth;
7985 					quality = rep->quality_ranking;
7986 					switch_to_rep_idx = k+1;
7987 				}
7988 			}
7989 			if (switch_to_rep_idx) {
7990 				GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] Switching representation for adapting playing speed\n"));
7991 				group->force_switch_bandwidth = 1;
7992 				group->force_representation_idx_plus_one = switch_to_rep_idx;
7993 			}
7994 		}
7995 		GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Playing at %f speed \n", speed));
7996 		dash->speed = speed;
7997 		dash->is_rt_speed = (ABS(speed - 1.0)<0.1) ? GF_TRUE : GF_FALSE;
7998 
7999 	}
8000 }
8001 
8002 
8003 GF_EXPORT
gf_dash_group_get_max_segments_in_cache(GF_DashClient * dash,u32 idx)8004 u32 gf_dash_group_get_max_segments_in_cache(GF_DashClient *dash, u32 idx)
8005 {
8006 	GF_DASH_Group *group = gf_list_get(dash->groups, idx);
8007 	if (group) return group->max_cached_segments;
8008 	return 0;
8009 }
8010 
8011 
8012 GF_EXPORT
gf_dash_group_get_num_segments_ready(GF_DashClient * dash,u32 idx,Bool * group_is_done)8013 u32 gf_dash_group_get_num_segments_ready(GF_DashClient *dash, u32 idx, Bool *group_is_done)
8014 {
8015 	u32 res = 0;
8016 	GF_DASH_Group *group;
8017 
8018 	if (dash->dash_mutex) gf_mx_p(dash->dash_mutex);
8019 	group = gf_list_get(dash->groups, idx);
8020 	if (!group) {
8021 		*group_is_done = 1;
8022 		if (dash->dash_mutex) gf_mx_v(dash->dash_mutex);
8023 		return 0;
8024 	}
8025 	if (group->cache_mutex) gf_mx_p(group->cache_mutex);
8026 
8027 	*group_is_done = group->done;
8028 	res = group->nb_cached_segments;
8029 
8030 	if (group->buffering) {
8031 		res = 0;
8032 	}
8033 
8034 	if (group->cache_mutex) gf_mx_v(group->cache_mutex);
8035 	if (dash->dash_mutex) gf_mx_v(dash->dash_mutex);
8036 	return res;
8037 }
8038 
8039 GF_EXPORT
gf_dash_group_discard_segment(GF_DashClient * dash,u32 idx)8040 void gf_dash_group_discard_segment(GF_DashClient *dash, u32 idx)
8041 {
8042 	GF_DASH_Group *group;
8043 	Bool delete_next;
8044 
8045 	if (dash->dash_mutex) gf_mx_p(dash->dash_mutex);
8046 	group = gf_list_get(dash->groups, idx);
8047 	if (group->cache_mutex) gf_mx_p(group->cache_mutex);
8048 
8049 discard_segment:
8050 	if (!group->nb_cached_segments) {
8051 		if (group->cache_mutex) gf_mx_v(group->cache_mutex);
8052 		if (dash->dash_mutex) gf_mx_v(dash->dash_mutex);
8053 		return;
8054 	}
8055 	delete_next = group->cached[0].has_dep_following ? GF_TRUE : GF_FALSE;
8056 
8057 	if (group->cached[0].cache) {
8058 		if (group->urlToDeleteNext) {
8059 			if (!group->local_files && !dash->keep_files && strncmp(group->urlToDeleteNext, "gmem://", 7) )
8060 				dash->dash_io->delete_cache_file(dash->dash_io, group->segment_download, group->urlToDeleteNext);
8061 
8062 			gf_free(group->urlToDeleteNext);
8063 			group->urlToDeleteNext = NULL;
8064 		}
8065 		assert(group->cached[0].url);
8066 
8067 		if (group->dont_delete_first_segment) {
8068 			group->dont_delete_first_segment = 0;
8069 			GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] deleting cache file %s : %s (kept in HTTP cache)\n", group->cached[0].url, group->cached[0].cache));
8070 		} else {
8071 			GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] deleting cache file %s : %s\n", group->cached[0].url, group->cached[0].cache));
8072 			group->urlToDeleteNext = gf_strdup( group->cached[0].url );
8073 		}
8074 
8075 		//remember the representation index of the last segment
8076 		group->prev_active_rep_index = group->cached[0].representation_index;
8077 
8078 		gf_dash_group_reset_cache_entry(&group->cached[0]);
8079 	}
8080 
8081 	memmove(&group->cached[0], &group->cached[1], sizeof(segment_cache_entry)*(group->nb_cached_segments-1));
8082 	memset(&(group->cached[group->nb_cached_segments-1]), 0, sizeof(segment_cache_entry));
8083 	group->nb_cached_segments--;
8084 
8085 	if (delete_next) {
8086 		goto discard_segment;
8087 	}
8088 
8089 	/*if we have dependency representations, we need also discard them*/
8090 	if (group->base_rep_index_plus_one) {
8091 		if (group->cached[0].cache && (group->cached[0].representation_index != group->base_rep_index_plus_one-1))
8092 			goto discard_segment;
8093 	}
8094 
8095 	if (group->cache_mutex) gf_mx_v(group->cache_mutex);
8096 	if (dash->dash_mutex) gf_mx_v(dash->dash_mutex);
8097 }
8098 
8099 GF_EXPORT
gf_dash_set_group_done(GF_DashClient * dash,u32 idx,Bool done)8100 void gf_dash_set_group_done(GF_DashClient *dash, u32 idx, Bool done)
8101 {
8102 	GF_DASH_Group *group = gf_list_get(dash->groups, idx);
8103 	if (group) {
8104 		if (dash->dash_mutex) gf_mx_p(dash->dash_mutex);
8105 		if (group->cache_mutex) gf_mx_p(group->cache_mutex);
8106 		group->done = done;
8107 		if (done && group->segment_download) {
8108 			group->download_abort_type = 1;
8109 			dash->dash_io->abort(dash->dash_io, group->segment_download);
8110 		}
8111 		if (group->cache_mutex) gf_mx_v(group->cache_mutex);
8112 		if (dash->dash_mutex) gf_mx_v(dash->dash_mutex);
8113 	}
8114 }
8115 
8116 GF_EXPORT
gf_dash_group_get_presentation_time_offset(GF_DashClient * dash,u32 idx,u64 * presentation_time_offset,u32 * timescale)8117 GF_Err gf_dash_group_get_presentation_time_offset(GF_DashClient *dash, u32 idx, u64 *presentation_time_offset, u32 *timescale)
8118 {
8119 	GF_DASH_Group *group = gf_list_get(dash->groups, idx);
8120 	if (group) {
8121 		u64 duration;
8122 		GF_MPD_Representation *rep = gf_list_get(group->adaptation_set->representations, group->active_rep_index);
8123 		gf_mpd_resolve_segment_duration(rep, group->adaptation_set, group->period, &duration, timescale, presentation_time_offset, NULL);
8124 		return GF_OK;
8125 	}
8126 	return GF_BAD_PARAM;
8127 }
8128 
8129 GF_EXPORT
gf_dash_group_get_next_segment_location(GF_DashClient * dash,u32 idx,u32 dependent_representation_index,const char ** url,u64 * start_range,u64 * end_range,s32 * switching_index,const char ** switching_url,u64 * switching_start_range,u64 * switching_end_range,const char ** original_url,Bool * has_next_segment,const char ** key_url,bin128 * key_IV)8130 GF_Err gf_dash_group_get_next_segment_location(GF_DashClient *dash, u32 idx, u32 dependent_representation_index, const char **url, u64 *start_range, u64 *end_range, s32 *switching_index, const char **switching_url, u64 *switching_start_range, u64 *switching_end_range, const char **original_url, Bool *has_next_segment, const char **key_url, bin128 *key_IV)
8131 {
8132 	GF_DASH_Group *group;
8133 	u32 index;
8134 	Bool has_dep_following;
8135 	*url = NULL;
8136 	if (switching_url) *switching_url = NULL;
8137 	if (start_range) *start_range = 0;
8138 	if (end_range) *end_range = 0;
8139 	if (switching_start_range) *switching_start_range = 0;
8140 	if (switching_end_range) *switching_end_range = 0;
8141 	if (original_url) *original_url = NULL;
8142 	if (switching_index) *switching_index = -1;
8143 	if (has_next_segment) *has_next_segment = GF_FALSE;
8144 
8145 	if (dash->dash_mutex) gf_mx_p(dash->dash_mutex);
8146 	group = gf_list_get(dash->groups, idx);
8147 
8148 	if (!group) {
8149 		if (dash->dash_mutex) gf_mx_v(dash->dash_mutex);
8150 		return GF_BAD_PARAM;
8151 	}
8152 
8153 	if (group->cache_mutex) gf_mx_p(group->cache_mutex);
8154 
8155 	if (!group->nb_cached_segments) {
8156 		if (group->cache_mutex) gf_mx_v(group->cache_mutex);
8157 		if (dash->dash_mutex) gf_mx_v(dash->dash_mutex);
8158 		if (group->done) return GF_EOS;
8159 		if ((dash->low_latency_mode==GF_DASH_LL_EARLY_FETCH)
8160 			&& group->is_low_latency
8161 			&& dash->is_rt_speed
8162 			&& !dash->time_in_tsb
8163 		) {
8164 			group->force_early_fetch = GF_TRUE;
8165 			dash->min_wait_ms_before_next_request = 1;
8166 		}
8167 		return GF_BUFFER_TOO_SMALL;
8168 	}
8169 
8170 	/*check the dependent rep is in the cache and does not target next segment (next in time)*/
8171 	has_dep_following = group->cached[0].has_dep_following;
8172 	index = 0;
8173 	while (dependent_representation_index) {
8174 		GF_Err err = GF_OK;
8175 
8176 		if (has_dep_following) {
8177 			if (index+1 >= group->nb_cached_segments)
8178 				err = GF_BUFFER_TOO_SMALL;
8179 			else if (! group->cached[index].has_dep_following)
8180 				err = GF_BAD_PARAM;
8181 		} else {
8182 			GF_MPD_Representation *rep = gf_list_get(group->adaptation_set->representations, group->cached[index].representation_index);
8183 
8184 			if (index+1 >= group->nb_cached_segments) err = GF_BUFFER_TOO_SMALL;
8185 			else if (!rep->playback.enhancement_rep_index_plus_one) err = GF_BAD_PARAM;
8186 			else if (rep->playback.enhancement_rep_index_plus_one != group->cached[index+1].representation_index + 1) err = GF_BAD_PARAM;
8187 		}
8188 
8189 		if (err) {
8190 			if (dash->dash_mutex) gf_mx_v(dash->dash_mutex);
8191 			return err;
8192 		}
8193 		index ++;
8194 		dependent_representation_index--;
8195 	}
8196 
8197 	*url = group->cached[index].cache;
8198 	if (start_range)
8199 		*start_range = group->cached[index].start_range;
8200 	if (end_range)
8201 		*end_range = group->cached[index].end_range;
8202 	if (original_url) *original_url = group->cached[index].url;
8203 	if (key_url) *key_url = group->cached[index].key_url;
8204 	if (key_IV) memcpy((*key_IV), group->cached[index].key_IV, sizeof(bin128));
8205 
8206 	if (!group->base_rep_index_plus_one && (group->cached[index].representation_index != group->prev_active_rep_index)) {
8207 		GF_MPD_Representation *rep = gf_list_get(group->adaptation_set->representations, group->cached[0].representation_index);
8208 		if (switching_index)
8209 			*switching_index = group->cached[0].representation_index;
8210 		if (switching_start_range)
8211 			*switching_start_range = rep->playback.init_start_range;
8212 		if (switching_end_range)
8213 			*switching_end_range = rep->playback.init_end_range;
8214 		if (switching_url)
8215 			*switching_url = rep->playback.cached_init_segment_url;
8216 	}
8217 	group->force_segment_switch = 0;
8218 
8219 	if (group->cached[index].has_dep_following) {
8220 		if (has_next_segment) *has_next_segment = GF_TRUE;
8221 	} else if ((index+1<group->max_cached_segments) && group->cached[index+1].cache  && group->adaptation_set) {
8222 		GF_MPD_Representation *rep;
8223 
8224 		rep = gf_list_get(group->adaptation_set->representations, group->cached[index].representation_index);
8225 		if (rep && (rep->playback.enhancement_rep_index_plus_one == group->cached[index+1].representation_index+1) ) {
8226 			if (has_next_segment)
8227 				*has_next_segment = GF_TRUE;
8228 		}
8229 	}
8230 	if (group->cache_mutex) gf_mx_v(group->cache_mutex);
8231 	if (dash->dash_mutex) gf_mx_v(dash->dash_mutex);
8232 	return GF_OK;
8233 }
8234 
8235 GF_EXPORT
gf_dash_group_probe_current_download_segment_location(GF_DashClient * dash,u32 idx,const char ** url,s32 * switching_index,const char ** switching_url,const char ** original_url,Bool * switched)8236 GF_Err gf_dash_group_probe_current_download_segment_location(GF_DashClient *dash, u32 idx, const char **url, s32 *switching_index, const char **switching_url, const char **original_url, Bool *switched)
8237 {
8238 	GF_DASH_Group *group;
8239 
8240 	if (url) *url = NULL;
8241 	if (switching_url) *switching_url = NULL;
8242 	if (original_url) *original_url = NULL;
8243 	if (switching_index) *switching_index = -1;
8244 
8245 	if (dash->dash_mutex) gf_mx_p(dash->dash_mutex);
8246 	group = gf_list_get(dash->groups, idx);
8247 	if (!group) {
8248 		if (dash->dash_mutex) gf_mx_v(dash->dash_mutex);
8249 		return GF_BAD_PARAM;
8250 	}
8251 
8252 	if (!group->is_downloading) {
8253 		if (dash->dash_mutex) gf_mx_v(dash->dash_mutex);
8254 		return GF_OK;
8255 	}
8256 
8257 	*switched = GF_FALSE;
8258 	if (group->download_abort_type==2) {
8259 		group->download_abort_type = 0;
8260 		*switched = GF_TRUE;
8261 	}
8262 
8263 	//no download yet
8264 	if ( ! dash->dash_io->get_bytes_done(dash->dash_io, group->segment_download)) {
8265 		if (dash->dash_mutex) gf_mx_v(dash->dash_mutex);
8266 		return GF_OK;
8267 	}
8268 
8269 	if (url) *url = dash->dash_io->get_cache_name(dash->dash_io, group->segment_download);
8270 	if (original_url) *original_url = dash->dash_io->get_url(dash->dash_io, group->segment_download);
8271 
8272 	if (group->active_rep_index != group->prev_active_rep_index) {
8273 		GF_MPD_Representation *rep = gf_list_get(group->adaptation_set->representations, group->active_rep_index);
8274 		if (switching_index)
8275 			*switching_index = group->active_rep_index;
8276 		if (switching_url)
8277 			*switching_url = rep->playback.cached_init_segment_url;
8278 	}
8279 	if (dash->dash_mutex) gf_mx_v(dash->dash_mutex);
8280 	return GF_OK;
8281 }
8282 
8283 GF_EXPORT
gf_dash_seek(GF_DashClient * dash,Double start_range)8284 void gf_dash_seek(GF_DashClient *dash, Double start_range)
8285 {
8286 	Bool is_dynamic = GF_FALSE;
8287 	if (dash->dash_mutex) gf_mx_p(dash->dash_mutex);
8288 
8289 	GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH] Seek request - playing from %g\n", start_range));
8290 
8291 	//are we live ? if so adjust start range
8292 	if (dash->mpd->type==GF_MPD_TYPE_DYNAMIC) {
8293 		u64 now, availabilityStartTime;
8294 		availabilityStartTime = dash->mpd->availabilityStartTime + dash->utc_shift;
8295 		availabilityStartTime += dash->utc_drift_estimate;
8296 
8297 		now = dash->mpd_fetch_time + (gf_sys_clock() - dash->last_update_time) - availabilityStartTime;
8298 
8299 		if (dash->initial_time_shift_value<=100) {
8300 			now -= dash->mpd->time_shift_buffer_depth * dash->initial_time_shift_value / 100;
8301 		} else {
8302 			now -= dash->initial_time_shift_value;
8303 		}
8304 //		now += (u64) (start_range*1000);
8305 		start_range = (Double) now;
8306 		start_range /= 1000;
8307 
8308 		is_dynamic = 1;
8309 		dash->initial_period_tunein = GF_TRUE;
8310 	}
8311 
8312 	/*first check if we seek to another period*/
8313 	if (! gf_dash_seek_periods(dash, start_range)) {
8314 		/*if no, seek in group*/
8315 		gf_dash_seek_groups(dash, start_range, is_dynamic);
8316 	}
8317 	if (dash->dash_mutex) gf_mx_v(dash->dash_mutex);
8318 }
8319 
8320 GF_EXPORT
gf_dash_group_segment_switch_forced(GF_DashClient * dash,u32 idx)8321 Bool gf_dash_group_segment_switch_forced(GF_DashClient *dash, u32 idx)
8322 {
8323 	GF_DASH_Group *group = gf_list_get(dash->groups, idx);
8324 	return group->force_segment_switch;
8325 }
8326 
8327 GF_EXPORT
gf_dash_set_utc_shift(GF_DashClient * dash,s32 shift_utc_sec)8328 void gf_dash_set_utc_shift(GF_DashClient *dash, s32 shift_utc_sec)
8329 {
8330 	if (dash) dash->utc_shift = shift_utc_sec;
8331 }
8332 
8333 GF_EXPORT
gf_dash_group_get_video_info(GF_DashClient * dash,u32 idx,u32 * max_width,u32 * max_height)8334 GF_Err gf_dash_group_get_video_info(GF_DashClient *dash, u32 idx, u32 *max_width, u32 *max_height)
8335 {
8336 	GF_DASH_Group *group = gf_list_get(dash->groups, idx);
8337 	if (!group || !max_width || !max_height) return GF_BAD_PARAM;
8338 
8339 	*max_width = group->adaptation_set->max_width;
8340 	*max_height = group->adaptation_set->max_height;
8341 	return GF_OK;
8342 }
8343 
8344 GF_EXPORT
gf_dash_group_get_srd_max_size_info(GF_DashClient * dash,u32 idx,u32 * max_width,u32 * max_height)8345 Bool gf_dash_group_get_srd_max_size_info(GF_DashClient *dash, u32 idx, u32 *max_width, u32 *max_height)
8346 {
8347 	GF_DASH_Group *group = gf_list_get(dash->groups, idx);
8348 	if (!group || !group->srd_desc || !max_width || !max_height) return GF_FALSE;
8349 
8350 	*max_width = group->srd_desc->width;
8351 	*max_height = group->srd_desc->height;
8352 	return GF_TRUE;
8353 }
8354 
8355 GF_EXPORT
gf_dash_set_min_timeout_between_404(GF_DashClient * dash,u32 min_timeout_between_404)8356 GF_Err gf_dash_set_min_timeout_between_404(GF_DashClient *dash, u32 min_timeout_between_404)
8357 {
8358 	if (!dash) return GF_BAD_PARAM;
8359 	dash->min_timeout_between_404 = min_timeout_between_404;
8360 	return GF_OK;
8361 }
8362 
8363 GF_EXPORT
gf_dash_set_segment_expiration_threshold(GF_DashClient * dash,u32 expire_after_ms)8364 GF_Err gf_dash_set_segment_expiration_threshold(GF_DashClient *dash, u32 expire_after_ms)
8365 {
8366 	if (!dash) return GF_BAD_PARAM;
8367 	dash->segment_lost_after_ms = expire_after_ms;
8368 	return GF_OK;
8369 }
8370 
8371 GF_EXPORT
gf_dash_group_loop_detected(GF_DashClient * dash,u32 idx)8372 Bool gf_dash_group_loop_detected(GF_DashClient *dash, u32 idx)
8373 {
8374 	GF_DASH_Group *group = gf_list_get(dash->groups, idx);
8375 	return (group && group->nb_cached_segments) ? group->cached[0].loop_detected : GF_FALSE;
8376 }
8377 
8378 GF_EXPORT
gf_dash_group_get_start_range(GF_DashClient * dash,u32 idx)8379 Double gf_dash_group_get_start_range(GF_DashClient *dash, u32 idx)
8380 {
8381 	GF_DASH_Group *group = gf_list_get(dash->groups, idx);
8382 	if (!group) return 0.0;
8383 	return group->start_playback_range;
8384 }
8385 
8386 
8387 GF_EXPORT
gf_dash_is_dynamic_mpd(GF_DashClient * dash)8388 Bool gf_dash_is_dynamic_mpd(GF_DashClient *dash)
8389 {
8390 	return (dash && dash->mpd->type==GF_MPD_TYPE_DYNAMIC) ? 1 : 0;
8391 }
8392 
8393 GF_EXPORT
gf_dash_get_min_buffer_time(GF_DashClient * dash)8394 u32 gf_dash_get_min_buffer_time(GF_DashClient *dash)
8395 {
8396 	return dash ? dash->mpd->min_buffer_time : 0;
8397 }
8398 
8399 #if 0 //unused
8400 GF_Err gf_dash_resync_to_segment(GF_DashClient *dash, const char *latest_segment_name, const char *earliest_segment_name)
8401 {
8402 	Bool found = GF_FALSE;
8403 	u32 i, j, latest_segment_number, earliest_segment_number, start_number;
8404 	/*Double latest_segment_time, earliest_segment_time;*/ //FIX : set but not used
8405 	u64 start_range, end_range, current_dur;
8406 	char *seg_url, *seg_name, *seg_sep;
8407 	GF_MPD_Representation *rep;
8408 	GF_DASH_Group *group = NULL;
8409 	if (!latest_segment_name) return GF_BAD_PARAM;
8410 
8411 	seg_url = NULL;
8412 	for (i=0; i<gf_list_count(dash->groups); i++) {
8413 		group = gf_list_get(dash->groups, i);
8414 		for (j=0; j<gf_list_count(group->adaptation_set->representations); j++) {
8415 			GF_Err e;
8416 			rep = gf_list_get(group->adaptation_set->representations, j);
8417 			e = gf_dash_resolve_url(dash->mpd, rep, group, dash->base_url, GF_MPD_RESOLVE_URL_MEDIA_TEMPLATE, i, &seg_url, &start_range, &end_range, &current_dur, NULL, NULL, NULL, NULL);
8418 			if (e)
8419 				GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Unable to resolve media template URL: %s\n", gf_error_to_string(e)));
8420 
8421 			if (!seg_url) continue;
8422 
8423 			seg_sep = NULL;
8424 			seg_name = strstr(seg_url, "://");
8425 			if (seg_name) seg_name = strchr(seg_name+4, '/');
8426 			if (!seg_name) {
8427 				gf_free(seg_url);
8428 				continue;
8429 			}
8430 			seg_sep = strchr(seg_name+1, '$');
8431 			if (seg_sep) seg_sep[0] = 0;
8432 
8433 			if (!strncmp(seg_name, latest_segment_name, strlen(seg_name)))
8434 				found = GF_TRUE;
8435 
8436 			if (found) break;
8437 
8438 			if (seg_sep) seg_sep[0] = '$';
8439 			gf_free(seg_url);
8440 			continue;
8441 		}
8442 		if (found) break;
8443 	}
8444 
8445 	if (!found) {
8446 		if (seg_url) gf_free(seg_url);
8447 		GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] No representation found matching the resync segment name %s\n", latest_segment_name));
8448 		return GF_BAD_PARAM;
8449 	}
8450 
8451 	start_number = gf_dash_get_start_number(group, rep);
8452 
8453 	if (seg_sep) {
8454 		char *sep_template, *sep_name, c;
8455 		char *latest_template = (char *) (latest_segment_name + strlen(seg_name));
8456 		char *earliest_template = earliest_segment_name ? (char *) (earliest_segment_name + strlen(seg_name)) : NULL;
8457 
8458 		latest_segment_number = earliest_segment_number = 0;
8459 		/*latest_segment_time = earliest_segment_time = 0;*/
8460 
8461 		seg_sep[0] = '$';
8462 		while (seg_sep) {
8463 			sep_template = strchr(seg_sep+1, '$');
8464 			if (!sep_template) break;
8465 			c = sep_template[1];
8466 			sep_template[1] = 0;
8467 			//solve template for latest
8468 			sep_name = strchr(latest_template, c);
8469 			if (!sep_name) break;
8470 
8471 			sep_name[0] = 0;
8472 			if (!strcmp(seg_sep, "$Number$")) {
8473 				latest_segment_number = atoi(latest_template);
8474 			}
8475 			/*else if (!strcmp(seg_sep, "$Time$")) {
8476 				latest_segment_time = atof(latest_template);
8477 			}*/
8478 			sep_name[0] = c;
8479 			latest_template = sep_name;
8480 
8481 			//solve template for earliest
8482 			if (earliest_template) {
8483 				sep_name = strchr(earliest_template, c);
8484 				if (!sep_name) break;
8485 
8486 				sep_name[0] = 0;
8487 				if (!strcmp(seg_sep, "$Number$")) {
8488 					earliest_segment_number = atoi(earliest_template);
8489 				}
8490 				/*else if (!strcmp(seg_sep, "$Time$")) {
8491 					earliest_segment_time = atof(earliest_template);
8492 				}*/
8493 				sep_name[0] = c;
8494 				earliest_template = sep_name;
8495 			}
8496 
8497 			sep_template[1] = c;
8498 			seg_sep = sep_template+1;
8499 			//find next $ - if any, move the segment name of the same amount of chars that what found in the template
8500 			sep_template = strchr(seg_sep, '$');
8501 			if (!sep_template) break;
8502 			sep_template[0]=0;
8503 			latest_template += strlen(sep_template);
8504 			sep_template[0]='$';
8505 		}
8506 		if (earliest_segment_number && !latest_segment_number) {
8507 			latest_segment_number = earliest_segment_number;
8508 		}
8509 
8510 		gf_free(seg_url);
8511 
8512 		//todo - recompute an AST offset so that the AST of the new segment equals UTC(now) + offset
8513 		if (latest_segment_number) {
8514 			Bool loop_detected = GF_FALSE;
8515 			s32 nb_seg_diff = 0;
8516 			s32 range_in = 0;
8517 			//how many segment ahead are we ?
8518 			nb_seg_diff = start_number + group->download_segment_index;
8519 			nb_seg_diff -= latest_segment_number;
8520 
8521 			//we are just too early for this request, do request later
8522 			if (nb_seg_diff == 1 ) {
8523 				//set to false, eg don't increment seg index
8524 				group->segment_in_valid_range = GF_FALSE;
8525 				return GF_OK;
8526 			}
8527 
8528 			//if earliest is not given, allow 5 segments
8529 			if (!earliest_segment_number) range_in = 4;
8530 			else range_in = latest_segment_number - earliest_segment_number;
8531 
8532 			//loop
8533 			if (latest_segment_number <= start_number ) {
8534 				GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Loop in segment start numbers detected - old start %d new seg %d\n", start_number , latest_segment_number));
8535 				loop_detected = GF_TRUE;
8536 			}
8537 			//we are behind live
8538 			else if (nb_seg_diff<0) {
8539 				//we fall in the buffer of the sender, we liklely have a loss
8540 				if (nb_seg_diff + range_in >= 0) {
8541 					group->segment_in_valid_range = GF_TRUE;
8542 					return GF_OK;
8543 				}
8544 				//we are late (something wrong happen locally maybe) - If not too late (5 segs) jump to newest
8545 				else if (earliest_segment_number && (start_number + group->download_segment_index + 5 >= earliest_segment_number)) {
8546 					group->download_segment_index = latest_segment_number - start_number;
8547 					group->segment_in_valid_range = GF_FALSE;
8548 					return GF_OK;
8549 				}
8550 				//we are too late resync...
8551 			}
8552 
8553 			GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Sync to live was lost - reloading MPD (loop detected %d)\n", loop_detected));
8554 			for (i=0; i< gf_list_count(dash->groups); i++) {
8555 				group = gf_list_get(dash->groups, i);
8556 				//force reinit of timeline for this group
8557 				group->start_number_at_last_ast = 0;
8558 				if (loop_detected)
8559 					group->loop_detected = GF_TRUE;
8560 			}
8561 			dash->force_mpd_update = GF_TRUE;
8562 		}
8563 		return GF_OK;
8564 	}
8565 
8566 	//TODO segment list addressing:
8567 	return GF_OK;
8568 }
8569 #endif
8570 
8571 
8572 GF_EXPORT
gf_dash_set_max_resolution(GF_DashClient * dash,u32 width,u32 height,u8 max_display_bpp)8573 GF_Err gf_dash_set_max_resolution(GF_DashClient *dash, u32 width, u32 height, u8 max_display_bpp)
8574 {
8575 	if (dash) {
8576 		dash->max_width = width;
8577 		dash->max_height = height;
8578 		dash->max_bit_per_pixel = max_display_bpp;
8579 		return GF_OK;
8580 	}
8581 	return GF_BAD_PARAM;
8582 }
8583 
8584 GF_EXPORT
gf_dash_debug_group(GF_DashClient * dash,s32 group_index)8585 void gf_dash_debug_group(GF_DashClient *dash, s32 group_index)
8586 {
8587 	dash->debug_group_index = group_index;
8588 }
8589 
8590 GF_EXPORT
gf_dash_split_adaptation_sets(GF_DashClient * dash)8591 void gf_dash_split_adaptation_sets(GF_DashClient *dash)
8592 {
8593 	dash->split_adaptation_set = GF_TRUE;
8594 }
8595 
8596 GF_EXPORT
gf_dash_set_low_latency_mode(GF_DashClient * dash,GF_DASHLowLatencyMode low_lat_mode)8597 void gf_dash_set_low_latency_mode(GF_DashClient *dash, GF_DASHLowLatencyMode low_lat_mode)
8598 {
8599 	dash->low_latency_mode = low_lat_mode;
8600 }
8601 
8602 
8603 GF_EXPORT
gf_dash_set_user_buffer(GF_DashClient * dash,u32 buffer_time_ms)8604 void gf_dash_set_user_buffer(GF_DashClient *dash, u32 buffer_time_ms)
8605 {
8606 	if (dash) dash->user_buffer_ms = buffer_time_ms;
8607 }
8608 
8609 /*returns active period start in ms*/
8610 GF_EXPORT
gf_dash_get_period_start(GF_DashClient * dash)8611 u64 gf_dash_get_period_start(GF_DashClient *dash)
8612 {
8613 	u64 start;
8614 	u32 i;
8615 	GF_MPD_Period *period;
8616 	if (!dash || !dash->mpd) return 0;
8617 
8618 	start = 0;
8619 	for (i=0; i<=dash->active_period_index; i++) {
8620 		period = gf_list_get(dash->mpd->periods, i);
8621 		if (period->start) start = period->start;
8622 
8623 		if (i<dash->active_period_index) start += period->duration;
8624 	}
8625 	return start;
8626 }
8627 
8628 
8629 /*returns active period duration in ms*/
8630 GF_EXPORT
gf_dash_get_period_duration(GF_DashClient * dash)8631 u64 gf_dash_get_period_duration(GF_DashClient *dash)
8632 {
8633 	u64 start;
8634 	u32 i;
8635 	GF_MPD_Period *period = NULL;
8636 	if (!dash || !dash->mpd) return 0;
8637 
8638 	start = 0;
8639 	for (i=0; i<=dash->active_period_index; i++) {
8640 		period = gf_list_get(dash->mpd->periods, i);
8641 		if (period->start) start = period->start;
8642 		if (i<dash->active_period_index) start += period->duration;
8643 	}
8644 	if (!period) return 0;
8645 	if (period->duration) return period->duration;
8646 	period = gf_list_get(dash->mpd->periods, dash->active_period_index+1);
8647 
8648 	if (!period) {
8649 		//infered from MPD duration
8650 		if (dash->mpd->media_presentation_duration) return dash->mpd->media_presentation_duration - start;
8651 		//duration is not known (live)
8652 		if (dash->mpd->type==GF_MPD_TYPE_STATIC) {
8653 			GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Period duration is not computable: last period without duration and no MPD duration !\n"));
8654 		}
8655 		return 0;
8656 	}
8657 	if (!period->start) {
8658 		GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH] Period duration is not computable, paeriod has no duration and next period has no start !\n"));
8659 		return 0;
8660 	}
8661 	return period->start - start;
8662 }
8663 
8664 GF_EXPORT
gf_dash_group_get_language(GF_DashClient * dash,u32 idx)8665 const char *gf_dash_group_get_language(GF_DashClient *dash, u32 idx)
8666 {
8667 	GF_DASH_Group *group = gf_list_get(dash->groups, idx);
8668 	if (!group) return NULL;
8669 	return group->adaptation_set->lang;
8670 }
8671 
8672 GF_EXPORT
gf_dash_group_get_audio_channels(GF_DashClient * dash,u32 idx)8673 u32 gf_dash_group_get_audio_channels(GF_DashClient *dash, u32 idx)
8674 {
8675 	GF_MPD_Descriptor *mpd_desc;
8676 	u32 i=0;
8677 	GF_DASH_Group *group = gf_list_get(dash->groups, idx);
8678 	if (!group) return 0;
8679 
8680 	while ((mpd_desc=gf_list_enum(group->adaptation_set->audio_channels, &i))) {
8681 		if (!strcmp(mpd_desc->scheme_id_uri, "urn:mpeg:dash:23003:3:audio_channel_configuration:2011")) {
8682 			return atoi(mpd_desc->value);
8683 		}
8684 	}
8685 	return 0;
8686 }
8687 
8688 GF_EXPORT
gf_dash_group_get_num_qualities(GF_DashClient * dash,u32 idx)8689 u32 gf_dash_group_get_num_qualities(GF_DashClient *dash, u32 idx)
8690 {
8691 	GF_DASH_Group *group = gf_list_get(dash->groups, idx);
8692 	if (!group) return 0;
8693 	return gf_list_count(group->adaptation_set->representations);
8694 }
8695 
8696 GF_EXPORT
gf_dash_group_get_num_components(GF_DashClient * dash,u32 idx)8697 u32 gf_dash_group_get_num_components(GF_DashClient *dash, u32 idx)
8698 {
8699 	GF_DASH_Group *group = gf_list_get(dash->groups, idx);
8700 	if (!group) return 0;
8701 	return gf_list_count(group->adaptation_set->content_component);
8702 }
8703 
8704 
8705 GF_EXPORT
gf_dash_group_get_quality_info(GF_DashClient * dash,u32 idx,u32 quality_idx,GF_DASHQualityInfo * quality)8706 GF_Err gf_dash_group_get_quality_info(GF_DashClient *dash, u32 idx, u32 quality_idx, GF_DASHQualityInfo *quality)
8707 {
8708 	GF_MPD_Fractional *sar;
8709 	GF_DASH_Group *group = gf_list_get(dash->groups, idx);
8710 	GF_MPD_Representation *rep;
8711 	if (!group || !quality) return GF_BAD_PARAM;
8712 	rep = gf_list_get(group->adaptation_set->representations, quality_idx);
8713 	if (!rep) return GF_BAD_PARAM;
8714 
8715 	memset(quality, 0, sizeof(GF_DASHQualityInfo));
8716 	quality->mime = rep->mime_type ? rep->mime_type : group->adaptation_set->mime_type;
8717 	quality->codec = rep->codecs ? rep->codecs : group->adaptation_set->codecs;
8718 	quality->disabled = rep->playback.disabled;
8719 	sar = rep->framerate ? rep->framerate : group->adaptation_set->framerate;
8720 	if (sar) {
8721 		quality->fps_den = sar->den;
8722 		quality->fps_num = sar->num;
8723 	}
8724 	quality->height = rep->height ? rep->height : group->adaptation_set->height;
8725 	quality->width = rep->width ? rep->width : group->adaptation_set->width;
8726 	quality->nb_channels = gf_dash_group_get_audio_channels(dash, idx);
8727 	sar = rep->sar ? rep->sar : group->adaptation_set->sar;
8728 	if (sar) {
8729 		quality->par_num = sar->num;
8730 		quality->par_den = sar->den;
8731 	}
8732 	quality->sample_rate = rep->samplerate ? rep->samplerate : group->adaptation_set->samplerate;
8733 	quality->bandwidth = rep->bandwidth;
8734 	quality->ID = rep->id;
8735 	quality->interlaced = (rep->scan_type == GF_MPD_SCANTYPE_INTERLACED) ? 1 : ( (group->adaptation_set->scan_type == GF_MPD_SCANTYPE_INTERLACED) ? 1 : 0);
8736 
8737 	//scalable rep, selected quality is max_complementary_rep_index
8738 	if (group->base_rep_index_plus_one) {
8739 		quality->is_selected = (quality_idx==group->max_complementary_rep_index) ? 1 : 0;
8740 	} else {
8741 		quality->is_selected = (quality_idx==group->active_rep_index) ? 1 : 0;
8742 	}
8743 	return GF_OK;
8744 }
8745 
8746 
gf_dash_group_enum_descriptor_list(GF_DashClient * dash,u32 idx,GF_List * descs,const char ** desc_id,const char ** desc_scheme,const char ** desc_value)8747 static Bool gf_dash_group_enum_descriptor_list(GF_DashClient *dash, u32 idx, GF_List *descs, const char **desc_id, const char **desc_scheme, const char **desc_value)
8748 {
8749 	GF_MPD_Descriptor *mpd_desc;
8750 	if (idx>=gf_list_count(descs)) return 0;
8751 	mpd_desc = gf_list_get(descs, idx);
8752 	if (desc_value) *desc_value = mpd_desc->value;
8753 	if (desc_scheme) *desc_scheme = mpd_desc->scheme_id_uri;
8754 	if (desc_id) *desc_id = mpd_desc->id;
8755 	return 1;
8756 }
8757 
8758 GF_EXPORT
gf_dash_group_enum_descriptor(GF_DashClient * dash,u32 group_idx,GF_DashDescriptorType desc_type,u32 desc_idx,const char ** desc_id,const char ** desc_scheme,const char ** desc_value)8759 Bool gf_dash_group_enum_descriptor(GF_DashClient *dash, u32 group_idx, GF_DashDescriptorType  desc_type, u32 desc_idx, const char **desc_id, const char **desc_scheme, const char **desc_value)
8760 {
8761 	GF_List *descs = NULL;
8762 	GF_DASH_Group *group = gf_list_get(dash->groups, group_idx);
8763 	if (!group) return 0;
8764 	switch (desc_type) {
8765 	case GF_MPD_DESC_ACCESSIBILITY:
8766 		descs = group->adaptation_set->accessibility;
8767 		break;
8768 	case GF_MPD_DESC_AUDIOCONFIG:
8769 		descs = group->adaptation_set->audio_channels;
8770 		break;
8771 	case GF_MPD_DESC_CONTENT_PROTECTION:
8772 		descs = group->adaptation_set->content_protection;
8773 		break;
8774 	case GF_MPD_DESC_ESSENTIAL_PROPERTIES:
8775 		descs = group->adaptation_set->essential_properties;
8776 		break;
8777 	case GF_MPD_DESC_SUPPLEMENTAL_PROPERTIES:
8778 		descs = group->adaptation_set->supplemental_properties;
8779 		break;
8780 	case GF_MPD_DESC_FRAME_PACKING:
8781 		descs = group->adaptation_set->frame_packing;
8782 		break;
8783 	case GF_MPD_DESC_ROLE:
8784 		descs = group->adaptation_set->role;
8785 		break;
8786 	case GF_MPD_DESC_RATING:
8787 		descs = group->adaptation_set->rating;
8788 		break;
8789 	case GF_MPD_DESC_VIEWPOINT:
8790 		descs = group->adaptation_set->viewpoint;
8791 		break;
8792 	default:
8793 		return 0;
8794 	}
8795 	return gf_dash_group_enum_descriptor_list(dash, desc_idx, descs, desc_id, desc_scheme, desc_value);
8796 }
8797 
8798 GF_EXPORT
gf_dash_get_automatic_switching(GF_DashClient * dash)8799 Bool gf_dash_get_automatic_switching(GF_DashClient *dash)
8800 {
8801 	return (dash && dash->disable_switching) ? GF_FALSE : GF_TRUE;
8802 }
8803 
8804 GF_EXPORT
gf_dash_set_automatic_switching(GF_DashClient * dash,Bool enable_switching)8805 GF_Err gf_dash_set_automatic_switching(GF_DashClient *dash, Bool enable_switching)
8806 {
8807 	if (!dash) return GF_BAD_PARAM;
8808 	dash->disable_switching = !enable_switching;
8809 	return GF_OK;
8810 }
8811 
8812 GF_EXPORT
gf_dash_group_select_quality(GF_DashClient * dash,u32 idx,const char * ID,u32 q_idx)8813 GF_Err gf_dash_group_select_quality(GF_DashClient *dash, u32 idx, const char *ID, u32 q_idx)
8814 {
8815 	u32 i, count;
8816 	GF_DASH_Group *group = gf_list_get(dash->groups, idx);
8817 	if (!group) return GF_BAD_PARAM;
8818 
8819 	if (!ID) {
8820 		GF_MPD_Representation *rep = gf_list_get(group->adaptation_set->representations, q_idx);
8821 		if (!rep) return GF_BAD_PARAM;
8822 		group->force_representation_idx_plus_one = q_idx+1;
8823 		group->force_switch_bandwidth = 1;
8824 		return GF_OK;
8825 	}
8826 
8827 	count = gf_list_count(group->adaptation_set->representations);
8828 	for (i=0; i<count; i++) {
8829 		GF_MPD_Representation *rep = gf_list_get(group->adaptation_set->representations, i);
8830 		if (rep->id && !strcmp(rep->id, ID)) {
8831 			group->force_representation_idx_plus_one = i+1;
8832 			group->force_switch_bandwidth = 1;
8833 			return GF_OK;
8834 		}
8835 	}
8836 	return GF_BAD_PARAM;
8837 }
8838 
8839 GF_EXPORT
gf_dash_group_get_active_quality(GF_DashClient * dash,u32 idx)8840 s32 gf_dash_group_get_active_quality(GF_DashClient *dash, u32 idx)
8841 {
8842 	GF_DASH_Group *group = gf_list_get(dash->groups, idx);
8843 	if (!group) return -1;
8844 	return group->active_rep_index;
8845 }
8846 
8847 
8848 GF_EXPORT
gf_dash_group_get_download_rate(GF_DashClient * dash,u32 idx)8849 u32 gf_dash_group_get_download_rate(GF_DashClient *dash, u32 idx)
8850 {
8851 	GF_DASH_Group *group = gf_list_get(dash->groups, idx);
8852 	if (!group || !group->segment_download) return 0;
8853 
8854 	return dash->dash_io->get_bytes_per_sec(dash->dash_io, group->segment_download);
8855 }
8856 
8857 
8858 GF_EXPORT
gf_dash_set_timeshift(GF_DashClient * dash,u32 ms_in_timeshift)8859 GF_Err gf_dash_set_timeshift(GF_DashClient *dash, u32 ms_in_timeshift)
8860 {
8861 	if (!dash) return GF_BAD_PARAM;
8862 	dash->initial_time_shift_value = ms_in_timeshift;
8863 	return GF_OK;
8864 }
8865 
8866 GF_EXPORT
gf_dash_get_timeshift_buffer_pos(GF_DashClient * dash)8867 Double gf_dash_get_timeshift_buffer_pos(GF_DashClient *dash)
8868 {
8869 	return dash ? dash->prev_time_in_tsb / 1000.0 : 0.0;
8870 }
8871 
8872 GF_EXPORT
gf_dash_group_set_codec_stat(GF_DashClient * dash,u32 idx,u32 avg_dec_time,u32 max_dec_time,u32 irap_avg_dec_time,u32 irap_max_dec_time,Bool codec_reset,Bool decode_only_rap)8873 void gf_dash_group_set_codec_stat(GF_DashClient *dash, u32 idx, u32 avg_dec_time, u32 max_dec_time, u32 irap_avg_dec_time, u32 irap_max_dec_time, Bool codec_reset, Bool decode_only_rap)
8874 {
8875 	GF_DASH_Group *group = (GF_DASH_Group *)gf_list_get(dash->groups, idx);
8876 	if (!group) return;
8877 	group->avg_dec_time = avg_dec_time;
8878 	group->max_dec_time = max_dec_time;
8879 	group->irap_avg_dec_time = irap_avg_dec_time;
8880 	group->irap_max_dec_time = irap_max_dec_time;
8881 	group->codec_reset = codec_reset;
8882 	group->decode_only_rap = decode_only_rap;
8883 }
8884 
8885 GF_EXPORT
gf_dash_group_set_buffer_levels(GF_DashClient * dash,u32 idx,u32 buffer_min_ms,u32 buffer_max_ms,u32 buffer_occupancy_ms)8886 void gf_dash_group_set_buffer_levels(GF_DashClient *dash, u32 idx, u32 buffer_min_ms, u32 buffer_max_ms, u32 buffer_occupancy_ms)
8887 {
8888 	GF_DASH_Group *group = (GF_DASH_Group *)gf_list_get(dash->groups, idx);
8889 	if (!group) return;
8890 	group->buffer_min_ms = buffer_min_ms;
8891 	group->buffer_max_ms = buffer_max_ms;
8892 	if (group->max_buffer_playout_ms > buffer_max_ms) {
8893 		GF_LOG(GF_LOG_WARNING, GF_LOG_DASH, ("[DASH] Max buffer %d less than max playout buffer %d, overwriting max playout buffer\n", buffer_max_ms, group->max_buffer_playout_ms));
8894 		group->max_buffer_playout_ms = buffer_max_ms;
8895 	}
8896 	group->buffer_occupancy_ms = buffer_occupancy_ms;
8897 }
8898 
8899 
8900 GF_EXPORT
gf_dash_disable_speed_adaptation(GF_DashClient * dash,Bool disable)8901 void gf_dash_disable_speed_adaptation(GF_DashClient *dash, Bool disable)
8902 {
8903 	dash->disable_speed_adaptation = disable;
8904 }
8905 
8906 GF_EXPORT
gf_dash_override_ntp(GF_DashClient * dash,u64 server_ntp)8907 void gf_dash_override_ntp(GF_DashClient *dash, u64 server_ntp)
8908 {
8909 	if (server_ntp) {
8910 		dash->utc_drift_estimate = gf_net_get_ntp_diff_ms(server_ntp);
8911 		dash->ntp_forced = 1;
8912 		GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Overwriting local NTP "LLU" to given one "LLU"\n", gf_net_get_ntp_ts(), server_ntp));
8913 	} else {
8914 		dash->utc_drift_estimate = 0;
8915 		dash->ntp_forced = 0;
8916 	}
8917 }
8918 
8919 GF_EXPORT
gf_dash_get_utc_drift_estimate(GF_DashClient * dash)8920 s32 gf_dash_get_utc_drift_estimate(GF_DashClient *dash) {
8921 	return (s32) dash->utc_drift_estimate;
8922 }
8923 
8924 GF_EXPORT
gf_dash_get_tile_adaptation_mode(GF_DashClient * dash)8925 GF_DASHTileAdaptationMode gf_dash_get_tile_adaptation_mode(GF_DashClient *dash)
8926 {
8927 	return dash->tile_adapt_mode;
8928 }
8929 
8930 GF_EXPORT
gf_dash_set_tile_adaptation_mode(GF_DashClient * dash,GF_DASHTileAdaptationMode mode,u32 tile_rate_decrease)8931 void gf_dash_set_tile_adaptation_mode(GF_DashClient *dash, GF_DASHTileAdaptationMode mode, u32 tile_rate_decrease)
8932 {
8933 	u32 i;
8934 	dash->tile_adapt_mode = mode;
8935 	dash->tile_rate_decrease = (tile_rate_decrease<100) ? tile_rate_decrease : 100;
8936 	for (i=0; i<gf_list_count(dash->groups); i++) {
8937 		GF_DASH_Group *group = (GF_DASH_Group *)gf_list_get(dash->groups, i);
8938 		if (group->srd_desc) gf_dash_set_tiles_quality(dash, group->srd_desc);
8939 	}
8940 }
8941 
8942 GF_EXPORT
gf_dash_group_get_srd_info(GF_DashClient * dash,u32 idx,u32 * srd_id,u32 * srd_x,u32 * srd_y,u32 * srd_w,u32 * srd_h,u32 * srd_width,u32 * srd_height)8943 Bool gf_dash_group_get_srd_info(GF_DashClient *dash, u32 idx, u32 *srd_id, u32 *srd_x, u32 *srd_y, u32 *srd_w, u32 *srd_h, u32 *srd_width, u32 *srd_height)
8944 {
8945 	GF_DASH_Group *group = gf_list_get(dash->groups, idx);
8946 	if (!group || !group->srd_desc) return GF_FALSE;
8947 
8948 	if (group->srd_desc) {
8949 		if (srd_id) (*srd_id) = group->srd_desc->id;
8950 		if (srd_width) (*srd_width) = group->srd_desc->srd_fw;
8951 		if (srd_height) (*srd_height) = group->srd_desc->srd_fh;
8952 	}
8953 
8954 	if (srd_x) (*srd_x) = group->srd_x;
8955 	if (srd_y) (*srd_y) = group->srd_y;
8956 	if (srd_w) (*srd_w) = group->srd_w;
8957 	if (srd_h) (*srd_h) = group->srd_h;
8958 
8959 
8960 	return GF_TRUE;
8961 }
8962 
8963 GF_EXPORT
gf_dash_ignore_xlink(GF_DashClient * dash,Bool ignore_xlink)8964 void gf_dash_ignore_xlink(GF_DashClient *dash, Bool ignore_xlink)
8965 {
8966 	dash->ignore_xlink = ignore_xlink;
8967 }
8968 
8969 GF_EXPORT
gf_dash_set_atsc_ast_shift(GF_DashClient * dash,u32 ast_shift)8970 void gf_dash_set_atsc_ast_shift(GF_DashClient *dash, u32 ast_shift)
8971 {
8972 	dash->atsc_ast_shift = ast_shift;
8973 }
8974 
8975 GF_EXPORT
gf_dash_group_set_max_buffer_playout(GF_DashClient * dash,u32 idx,u32 max_buffer_playout_ms)8976 GF_Err gf_dash_group_set_max_buffer_playout(GF_DashClient *dash, u32 idx, u32 max_buffer_playout_ms)
8977 {
8978 	GF_DASH_Group *group = gf_list_get(dash->groups, idx);
8979 	if (!group) return GF_BAD_PARAM;
8980 	group->max_buffer_playout_ms = max_buffer_playout_ms;
8981 	return GF_OK;
8982 }
8983 
8984 GF_EXPORT
gf_dash_group_set_quality_degradation_hint(GF_DashClient * dash,u32 idx,u32 quality_degradation_hint)8985 GF_Err gf_dash_group_set_quality_degradation_hint(GF_DashClient *dash, u32 idx, u32 quality_degradation_hint)
8986 {
8987 	GF_DASH_Group *group = gf_list_get(dash->groups, idx);
8988 	if (!group) return GF_BAD_PARAM;
8989 
8990 	group->quality_degradation_hint = quality_degradation_hint;
8991 	if (group->quality_degradation_hint > 100) group->quality_degradation_hint=100;
8992 	return GF_OK;
8993 }
8994 
8995 
8996 GF_EXPORT
gf_dash_group_set_visible_rect(GF_DashClient * dash,u32 idx,u32 min_x,u32 max_x,u32 min_y,u32 max_y,Bool is_gaze)8997 GF_Err gf_dash_group_set_visible_rect(GF_DashClient *dash, u32 idx, u32 min_x, u32 max_x, u32 min_y, u32 max_y, Bool is_gaze)
8998 {
8999 	u32 i, count;
9000 	GF_DASH_Group *group = gf_list_get(dash->groups, idx);
9001 	if (!group) return GF_BAD_PARAM;
9002 
9003 	if (!min_x && !max_x && !min_y && !max_y) {
9004 		group->quality_degradation_hint = 0;
9005 	}
9006 
9007 
9008 	//TODO - single video, we may want to switch down quality if not a lot of the video is visible
9009 	//we will need the zoom factor as well
9010 	if (!group->groups_depending_on) return GF_OK;
9011 
9012 	GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Group Visible rect %d,%d,%d,%d \n", min_x, max_x, min_y, max_y));
9013 	count = gf_list_count(group->groups_depending_on);
9014 	for (i=0; i<count; i++) {
9015 		Bool is_visible = GF_TRUE;
9016 		GF_DASH_Group *a_group = gf_list_get(group->groups_depending_on, i);
9017 		if (!a_group->srd_w || !a_group->srd_h) continue;
9018 
9019 		if (is_gaze) {
9020 
9021 			if (min_x < a_group->srd_x)
9022 				is_visible = GF_FALSE;
9023 			else if (min_x > a_group->srd_x + a_group->srd_w)
9024 				is_visible = GF_FALSE;
9025 			else if (min_y < a_group->srd_y)
9026 				is_visible = GF_FALSE;
9027 			else if (min_y > a_group->srd_y + a_group->srd_h)
9028 				is_visible = GF_FALSE;
9029 
9030 		} else {
9031 
9032 			//single rectangle case
9033 			if (min_x<max_x) {
9034 				if (a_group->srd_x+a_group->srd_w <min_x) is_visible = GF_FALSE;
9035 				else if (a_group->srd_x>max_x) is_visible = GF_FALSE;
9036 			} else {
9037 				if ( (a_group->srd_x>max_x) && (a_group->srd_x+a_group->srd_w<min_x)) is_visible = GF_FALSE;
9038 			}
9039 
9040 			if (a_group->srd_y>max_y) is_visible = GF_FALSE;
9041 			else if (a_group->srd_y+a_group->srd_h < min_y) is_visible = GF_FALSE;
9042 
9043 		}
9044 
9045 		a_group->quality_degradation_hint = is_visible ? 0 : 100;
9046 
9047 		GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH] Group SRD %d,%d,%d,%d is %s\n", a_group->srd_x, a_group->srd_w, a_group->srd_y, a_group->srd_h, is_visible ? "visible" : "hidden"));
9048 	}
9049 	return GF_OK;
9050 }
9051 
gf_dash_set_group_download_state(GF_DashClient * dash,u32 idx,GF_Err err)9052 void gf_dash_set_group_download_state(GF_DashClient *dash, u32 idx, GF_Err err)
9053 {
9054 	GF_MPD_Representation *rep;
9055 	Bool has_dep_following;
9056 	char *key_url, *url;
9057 	GF_DASH_Group *base_group;
9058 	GF_DASH_Group *group = gf_list_get(dash->groups, idx);
9059 	if (dash->thread_mode) return;
9060 	if (!group) return;
9061 
9062 	//we forced early fetch because demux was empty, consider all errors as 404
9063 	if (group->force_early_fetch && err) {
9064 		err = GF_URL_ERROR;
9065 	}
9066 
9067 	if (!err) {
9068 		group->force_early_fetch = GF_FALSE;
9069 		return;
9070 	}
9071 	if (!group->nb_cached_segments) return;
9072 	rep = gf_list_get(group->adaptation_set->representations, group->cached[0].representation_index);
9073 	//FILTER_FIXME: find base group and current cache idx for scalability
9074 	has_dep_following = group->cached[0].has_dep_following;
9075 	key_url = group->cached[0].key_url;
9076 	url = group->cached[0].url;
9077 	gf_free(group->cached[0].cache);
9078 	group->nb_cached_segments--;
9079 	assert(!group->nb_cached_segments);
9080 
9081 	base_group = group;
9082 	while (base_group->depend_on_group) {
9083 		base_group = base_group->depend_on_group;
9084 	}
9085 	on_group_download_error(dash, group, base_group, err, rep, url, key_url, has_dep_following);
9086 
9087 
9088 	if (dash->speed>=0) {
9089 		group->download_segment_index--;
9090 	} else {
9091 		group->download_segment_index++;
9092 	}
9093 }
9094 
gf_dash_group_store_stats(GF_DashClient * dash,u32 idx,u32 bytes_per_sec,u32 file_size,u32 bytes_done,Bool is_broadcast)9095 void gf_dash_group_store_stats(GF_DashClient *dash, u32 idx, u32 bytes_per_sec, u32 file_size, u32 bytes_done, Bool is_broadcast)
9096 {
9097 	GF_DASH_Group *group = gf_list_get(dash->groups, idx);
9098 	if (dash->thread_mode) return;
9099 	if (!group) return;
9100 	if (!group->nb_cached_segments) return;
9101 
9102 	dash_store_stats(dash, group, bytes_per_sec, file_size, is_broadcast);
9103 
9104 	if (file_size==bytes_done) {
9105 		dash_global_rate_adaptation(dash, GF_FALSE);
9106 	}
9107 }
9108 
gf_dash_get_min_wait_ms(GF_DashClient * dash)9109 u32 gf_dash_get_min_wait_ms(GF_DashClient *dash)
9110 {
9111 	if (dash && dash->min_wait_ms_before_next_request) {
9112 		u32 ellapsed = gf_sys_clock() - dash->min_wait_sys_clock;
9113 		if (ellapsed < dash->min_wait_ms_before_next_request) dash->min_wait_ms_before_next_request -= ellapsed;
9114 		else dash->min_wait_ms_before_next_request = 0;
9115 		return dash->min_wait_ms_before_next_request;
9116 	}
9117 	return 0;
9118 }
9119 
9120 GF_EXPORT
gf_dash_all_groups_done(GF_DashClient * dash)9121 Bool gf_dash_all_groups_done(GF_DashClient *dash)
9122 {
9123 	u32 i, count = gf_list_count(dash->groups);
9124 	for (i=0; i<count; i++) {
9125 		GF_DASH_Group *group = gf_list_get(dash->groups, i);
9126 		if (group->selection != GF_DASH_GROUP_SELECTED) continue;
9127 		if (!group->done) return GF_FALSE;
9128 		if (group->nb_cached_segments) return GF_FALSE;
9129 	}
9130 	return GF_TRUE;
9131 }
9132 
9133 GF_EXPORT
gf_dash_set_period_xlink_query_string(GF_DashClient * dash,const char * query_string)9134 void gf_dash_set_period_xlink_query_string(GF_DashClient *dash, const char *query_string)
9135 {
9136 	if (dash) dash->query_string = query_string;
9137 }
9138 
9139 GF_EXPORT
gf_dash_group_get_as_id(GF_DashClient * dash,u32 group_idx)9140 u32 gf_dash_group_get_as_id(GF_DashClient *dash, u32 group_idx)
9141 {
9142 	GF_DASH_Group *group;
9143 	if (!dash) return 0;
9144 	group = gf_list_get(dash->groups, group_idx);
9145 	if (!group) return 0;
9146 	return group->adaptation_set->id;
9147 }
9148 
9149 #endif //GPAC_DISABLE_DASH_CLIENT
9150 
9151