DASH Client

You can override the default algorithm used by the DASH client with your own algorithm. See [the documentation](https://doxygen.gpac.io/group__pydash__grp.html) for further details.

The principle is as follows:

- the script can get notification of period start/end to reset statistics, setup live vs on demand cases, etc.
- the script can get notification of each created group (AdaptationSet in DASH, Variant Stream in HLS) with its various qualities. For HEVC tiling, each tile will be declared as a group, as well as the base tile track
- the script is notified after each segment download on which quality to pickup next
- the script can be notified while downloading a segment to decide if the download should be aborted
 
```
class MyCustomDASHAlgo:
	#get notifications when a DASH period starts or ends 
	def on_period_reset(self, type):
		print('period reset type ' + str(type))

	#get notification when a new group (i.e., set of adaptable qualities, `AdaptationSet` in DASH) is created. Some groups may be left unexposed by the DASH client 
	#the qualities are sorted from min bandwidth/quality to max bandwidth/quality
	def on_new_group(self, group):
		print('new group ' + str(group.idx) + ' qualities ' + str(len(group.qualities)) + ' codec ' + group.qualities[0].codec);

	#perform adaptation logic - return value is the new quality index, or -1 to keep as current, -2 to discard  (debug, segments won't be fetched/decoded)
	def on_rate_adaptation(self, group, base_group, force_low_complexity, stats):
		print('We are adapting on group ' + str(group.idx) )
		print('' + str(stats))
		# perform adaptation, check group.SRD to perform spatial adaptation, ...
		# 
		#in this example we simply cycle through qualities
		newq = stats.active_quality_idx + 1
		if newq >= len(group.qualities):
			newq = 0
		return newq

	# this callback is optional, use it only if your algo may abort a running transfer (this can be very costly as it will require closing and reopening the HTTP connection for HTTP 1.1  )
	#	-1 to continue download
	#	or -2 to abort download but without retrying to download a segment at lower quality for the same media time
	#	or the index of the new quality to download for the same media time
	def on_download_monitor(self, group, stats):
		print('download monitor group ' + str(group.idx) + ' stats ' + str(stats) );
		return -1


#create an instance of the algo
mydash = MyCustomDASHAlgo()

#define a custom filter session monitoring the creation of new filters
class MyFilterSession(gpac.FilterSession):
	def __init__(self, flags=0, blacklist=None, nb_threads=0, sched_type=0):
		gpac.FilterSession.__init__(self, flags, blacklist, nb_threads, sched_type)

	def on_filter_new(self, f):
		print("new filter " + f.name);
		#bind the dashin filter to our algorithm object
		if f.name == "dashin":
			f.bind(mydash);

	def on_filter_del(self, f):
		print("del filter " + f.name);

#create a session
fs = MyFilterSession()

#create a source, here to TelecomParis DASH test sequences
f1 = fs.load_src("https://download.tsi.telecom-paristech.fr/gpac/DASH_CONFORMANCE/TelecomParisTech/mp4-live-1s/mp4-live-1s-mpd-AV-BS.mpd")
if not f1:
    raise Exception('Failed to load source')

#load a sink, here video out
f2 = fs.load("vout")
if not f2:
    raise Exception('Failed to load sink')

#run the session in blocking mode
fs.run()

```