├── dune-project
├── .gitignore
├── Makefile
├── dashc.opam
├── topology_from_the_paper
├── qoe-master.py
├── README.md
├── scalability_tests.py
└── qoe-top.py
├── src
├── dashc.ml
├── dune
├── segm_result.ml
├── tests.ml
├── representation.ml
├── playback.ml
└── adapt_algo.ml
├── configure
├── test_mpd
├── bbb_enc_10min_x264_dash.mpd
├── bbb_enc_10min_x264_dash_rev1.mpd
├── bbb_enc_10min_x264_dash_rev2.mpd
├── bbb_enc_10min_x264_dash_mixed.mpd
├── tearsofsteel_enc_x264_dash.mpd
└── segmentlist_bbb_enc_10min_x264_dash.mpd.txt
├── CHANGES.md
├── ocaml-cohttp_
└── cohttp-async
│ └── src
│ └── client.ml
├── README.md
└── LICENSE.txt
/dune-project:
--------------------------------------------------------------------------------
1 | (lang dune 1.0)
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.exe
2 | *.install
3 | *.vscode
4 | *.res
5 | _build
6 | .merlin
7 | ocaml-cohttp
8 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | .PHONY: build clean test install uninstall clean
2 |
3 | all:
4 | @dune build --profile release src/dashc.exe
5 | cp _build/default/src/dashc.exe dashc.exe
6 | test:
7 | @dune build src/tests.exe
8 | dune build @_build/default/src/runtest
9 | clean:
10 | dune clean
11 |
--------------------------------------------------------------------------------
/dashc.opam:
--------------------------------------------------------------------------------
1 | opam-version: "2.0"
2 | name: "dashc"
3 | version: "0.1.22"
4 | synopsis: "DASH video client emulator"
5 | maintainer: "ar11@cs.ucc.ie"
6 | authors: ["Aleksandr Reviakin"]
7 | homepage: "https://www.ucc.ie/en/compsci/"
8 | bug-reports: "https://github.com/uccmisl/dashc/issues"
9 | dev-repo: "https://github.com/uccmisl/dashc.git"
10 | depends: ["dune" {build}]
11 | build: ["dune" "build" "-p" name]
12 | available: [ ocaml-version >= "4.07.1"]
13 |
--------------------------------------------------------------------------------
/topology_from_the_paper/qoe-master.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | import subprocess
4 |
5 | #delays = [10, 100, 250]
6 | delays = [250]
7 | #rate_limits = [0.375, 0.75, 1.5, 3.0, 4.5]
8 | rate_limits = [4.5]
9 | algs = ['arbiter']
10 | #number_of_clients = [2, 4, 6, 8, 10, 20, 40, 80, 100]
11 | number_of_clients = [100]
12 | #number_of_clients_skip = [20, 40, 80, 100]
13 | count = 1
14 |
15 | for curr in range(count):
16 | for alg in algs:
17 | for client_number in number_of_clients:
18 | # make full 10 tests for clients not in number_of_clients_skip
19 | #if (curr > 2 and client_number in number_of_clients_skip):
20 | # continue
21 | for rate_limit in rate_limits:
22 | for delay in delays:
23 | run_top_script = 'sudo python qoe-top.py ' + str(client_number) + ' ' + str(rate_limit) + ' ' + str(delay) + ' ' + alg + ' ' + str(curr)
24 | print(run_top_script)
25 | subprocess.run(run_top_script.split(' '))
--------------------------------------------------------------------------------
/src/dashc.ml:
--------------------------------------------------------------------------------
1 | (*
2 | * dashc, client emulator for DASH video streaming
3 | * Copyright (c) 2016-2018, Aleksandr Reviakin, University College Cork
4 | *
5 | * This program is free software; you can redistribute it and/or
6 | * modify it under the terms of the GNU General Public License
7 | * as published by the Free Software Foundation; either version 2
8 | * of the License, or (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program; if not, write to the Free Software
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 | * 02110-1301, USA.
19 | *)
20 |
21 | open Core
22 |
23 | let command =
24 | Command.group ~summary:"Modes: play"
25 | [ "play", Playback.play ]
26 |
27 | let () = Command.run command ~version:"0.1.21" ~build_info:"OCaml 4.07.1 was used"
28 |
--------------------------------------------------------------------------------
/topology_from_the_paper/README.md:
--------------------------------------------------------------------------------
1 | # Topologies from the paper
2 |
3 | The original files (topologies) used for getting the results has been lost, however, these files are close approximations restored from the backup.
4 |
5 | Most of the tests were made during the summer 2017, the code of the dashc was slightly different in comparison to the current one in the repository. The tapas_impl variable should be set to true (in the adapt_algo.ml file), if TAPAS version of the conventional algorithm is required.
6 |
7 | ## The 1st test, scalability
8 |
9 | Files: scalability_tests.py.
10 |
11 | To be able to measure CPU load and RAM usage /usr/bin/time application was used. It was run as “/usr/bin/time -f \"%U %S %e %M\" -o results.txt -a” and then video client with necessary parameters. The CPU load was calculated as (%U + %S) / %e, where %U is a total number of CPU-seconds that the process used directly (in a user mode), in seconds; %S is a total number of CPU-seconds used by the system on behalf of the process (in a kernel mode), in seconds; %e is an elapsed real (wall clock) time used by the process, in seconds. %M is maximum resident set size of the process during its lifetime.
12 |
13 | ## The 2nd one, QoE
14 |
15 | Files: qoe-master.py, qoe-top.py.
--------------------------------------------------------------------------------
/src/dune:
--------------------------------------------------------------------------------
1 | (executables
2 | (names dashc tests)
3 | (libraries
4 | core_kernel
5 | core
6 | async
7 | cohttp
8 | cohttp-async
9 | xml-light
10 | alcotest-async)
11 | (preprocess (pps ppx_jane))
12 | (modes native)
13 | (modules dashc playback representation segm_result adapt_algo tests)
14 | )
15 |
16 | (alias
17 | (name runtest)
18 | (deps tests.exe
19 | segmentlist_bbb_enc_10min_x264_dash.mpd.txt
20 | bbb_enc_10min_x264_dash.mpd
21 | bbb_enc_10min_x264_dash_rev1.mpd
22 | bbb_enc_10min_x264_dash_rev2.mpd
23 | bbb_enc_10min_x264_dash_mixed.mpd
24 | tearsofsteel_enc_x264_dash.mpd)
25 | (action (run ./tests.exe))
26 | )
27 |
28 | (rule (copy ../test_mpd/segmentlist_bbb_enc_10min_x264_dash.mpd.txt segmentlist_bbb_enc_10min_x264_dash.mpd.txt))
29 | (rule (copy ../test_mpd/bbb_enc_10min_x264_dash.mpd bbb_enc_10min_x264_dash.mpd))
30 | (rule (copy ../test_mpd/bbb_enc_10min_x264_dash_rev1.mpd bbb_enc_10min_x264_dash_rev1.mpd))
31 | (rule (copy ../test_mpd/bbb_enc_10min_x264_dash_rev2.mpd bbb_enc_10min_x264_dash_rev2.mpd))
32 | (rule (copy ../test_mpd/bbb_enc_10min_x264_dash_mixed.mpd bbb_enc_10min_x264_dash_mixed.mpd))
33 | (rule (copy ../test_mpd/tearsofsteel_enc_x264_dash.mpd tearsofsteel_enc_x264_dash.mpd))
--------------------------------------------------------------------------------
/configure:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | grep Ubuntu /etc/lsb-release &> /dev/null && DIST="Ubuntu"
4 |
5 | if [ "$DIST" = "Ubuntu" ]; then
6 | sudo apt -y install bubblewrap m4 git python-3-pip net-tools curl mercurial darcs libcap-dev unzip
7 |
8 | if dpkg-query -l | grep -E '^ii' | grep ocaml
9 | then
10 | echo "removing Ocaml pinned links"
11 | opam pin remove -y cohttp ocaml-cohttp
12 | opam remove -y cohttp cohttp-async
13 | opam pin remove -y cohttp-async ocaml-cohttp
14 | fi
15 |
16 | pip3 install numpy
17 |
18 | elif [ "$(uname)" == "Darwin" ]; then
19 | brew install m4 git
20 | fi
21 |
22 | sh <(curl -sL https://raw.githubusercontent.com/ocaml/opam/master/shell/install.sh)
23 | opam init --auto-setup --comp 4.07.1 --yes
24 | eval $(opam env)
25 | opam install -y dune core async xml-light alcotest alcotest-async merlin ocp-indent utop cohttp 'cohttp-async<2.1.1' 'base64<3.0.0'
26 |
27 | echo "Clone cohttp and switch to the first commit of 2018.10.19, version 1.2.0"
28 | git clone https://github.com/mirage/ocaml-cohttp.git
29 | cd ocaml-cohttp/
30 | git checkout 13f4bd609e9153b3d92acfddd1e257996e8a4a91
31 | cd ..
32 |
33 | rm ocaml-cohttp/cohttp-async/src/client.ml
34 | rm ocaml-cohttp/cohttp-async/src/client.mli
35 | cp ocaml-cohttp_/cohttp-async/src/client.ml ocaml-cohttp/cohttp-async/src/client.ml
36 |
37 | opam remove -y cohttp cohttp-async
38 |
39 | opam pin add -y cohttp ocaml-cohttp
40 | opam pin add -y cohttp-async ocaml-cohttp
41 |
--------------------------------------------------------------------------------
/topology_from_the_paper/scalability_tests.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | from mininet.net import Mininet
3 | from mininet.node import Controller,OVSKernelSwitch
4 | from mininet.link import TCLink
5 | from mininet.cli import CLI
6 | import time
7 | import os
8 |
9 | def topology():
10 |
11 | "Create a network."
12 | net = Mininet( controller=Controller, link=TCLink, switch=OVSKernelSwitch )
13 |
14 | print "*** Creating nodes"
15 |
16 | h1 = net.addHost( 'h1', mac='00:00:00:00:00:01', ip='10.0.0.1/8' )
17 |
18 | c1 = net.addController( 'c1', controller=Controller, ip='127.0.0.1' )
19 | s1 = net.addSwitch( 's1' )
20 |
21 | number_of_hosts = 50
22 | hosts = range(number_of_hosts)
23 | for i in range(number_of_hosts):
24 | host = 'host' + str(i).zfill(3)
25 | # hex is wrong here, [:-2] part
26 | hosts[i] = net.addHost(host, mac='00:00:00:00:00:' + hex(i)[:-2], ip='10.0.0.' + str(i + 2) + '/8')
27 |
28 | print "*** Associating and Creating links"
29 | link_main = net.addLink(s1, h1, bw=100)
30 | host_links = []
31 | for i in range(number_of_hosts):
32 | host_links.append(net.addLink(s1, hosts[i], bw=3))
33 |
34 | print "*** Starting network"
35 | net.build()
36 | c1.start()
37 | s1.start( [c1] )
38 |
39 | h1.cmdPrint('./caddy -host 10.0.0.1 -port 8080 -root ~/Downloads &')
40 |
41 | os.system('sleep 3')
42 |
43 | subfolder = "results"
44 | for i in range(number_of_hosts):
45 | #hosts[i].cmdPrint('cd ~/Downloads/tapas-master; /usr/bin/time -o results.txt -a python play.py -a conventional -m fake -u http://10.0.0.1:8080/bbb/bbb_enc_10min_x264_dash_old.mpd &')
46 | #hosts[i].cmdPrint('cd ~/Downloads/tapas-master; /usr/bin/time -f \"%U %S %e %M\" -o results.txt -a python play.py -a conventional -m fake -u http://10.0.0.1:8080/bbb/bbb_enc_10min_x264_dash_old.mpd &')
47 | #hosts[i].cmdPrint('/usr/bin/time -o results.txt -a ./dashc.native http://10.0.0.1:8080/bbb/bbb_enc_10min_x264_dash.mpd -adapt conv -initb 2 -maxbuf 60 -persist true -lastsegmindex 75 -logname sta1 -v 20 -turnlogon false &')
48 | #hosts[i].cmdPrint('/usr/bin/time -f \"%U %S %e %M\" -o results.txt -a ./dashc.native http://10.0.0.1:8080/bbb/bbb_enc_10min_x264_dash.mpd -adapt conv -initb 2 -maxbuf 60 -persist true -lastsegmindex 75 -logname sta' + str(i).zfill(3) + ' -v 20 -turnlogon true -subfolder ' + subfolder + ' &')
49 | #hosts[i].cmdPrint('./dashc.native http://10.0.0.1:8080/bbb/bbb_enc_10min_x264_dash.mpd -adapt arbiter -initb 2 -maxbuf 60 -persist true -segmentlist remote -lastsegmindex 75 -logname sta' + str(i).zfill(3) + ' -v 20 -turnlogon true -subfolder ' + subfolder + ' &')
50 | #hosts[i].cmdPrint('cd ~/Downloads/tapas-master; python play.py -a conventional -m fake -u http://10.0.0.1:8080/bbb/bbb_enc_10min_x264_dash_old.mpd &')
51 | #hosts[i].cmdPrint('./dashc.native http://10.0.0.1:8080/bbb/bbb_enc_10min_x264_dash.mpd -adapt conv -initb 2 -maxbuf 60 -persist true -lastsegmindex 75 -logname sta1 -v 20 -turnlogon true &')
52 | pass
53 |
54 | bw = 3
55 | #for i in range(21):
56 | for i in range(16):
57 | print(str(i))
58 | os.system('sleep 20')
59 | if bw == 3:
60 | print('set bw to 1 mbps')
61 | for link in host_links:
62 | link.intf1.config( bw=1 )
63 | bw = 1
64 | else:
65 | print('set bw to 3 mbps')
66 | for link in host_links:
67 | link.intf1.config( bw=3 )
68 | bw = 3
69 |
70 | print "*** Stopping network"
71 | net.stop()
72 |
73 | if __name__ == '__main__':
74 | topology()
75 |
--------------------------------------------------------------------------------
/test_mpd/bbb_enc_10min_x264_dash.mpd:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | bbb_enc_10min_x264_dash.mpd generated by GPAC
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/test_mpd/bbb_enc_10min_x264_dash_rev1.mpd:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | bbb_enc_10min_x264_dash.mpd generated by GPAC
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/test_mpd/bbb_enc_10min_x264_dash_rev2.mpd:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | bbb_enc_10min_x264_dash.mpd generated by GPAC
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/test_mpd/bbb_enc_10min_x264_dash_mixed.mpd:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | bbb_enc_10min_x264_dash.mpd generated by GPAC
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/topology_from_the_paper/qoe-top.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | from mininet.net import Mininet
3 | from mininet.node import Controller,OVSKernelSwitch
4 | from mininet.link import TCLink
5 | from mininet.cli import CLI
6 |
7 | import time
8 | import os
9 | import sys
10 | import math
11 |
12 | def roundup(x):
13 | return int(math.ceil(x / 100.0)) * 100
14 |
15 | def topology():
16 | if len(sys.argv) != 6:
17 | print('Usage: qoe-top.py number_of_clients rate_limit delay alg r_number')
18 | exit()
19 | number_of_clients = int(sys.argv[1])
20 | rate_limit = float(sys.argv[2])
21 | delay = int(sys.argv[3])
22 | alg = sys.argv[4]
23 | r_number = sys.argv[5]
24 | total_rate_limit = number_of_clients * rate_limit
25 |
26 | number_of_hosts = number_of_clients
27 | subfolder = 'results' + r_number + '/dashc_' + alg + '_' + str(number_of_hosts).zfill(3) + '_' + str(rate_limit) + 'Mbps_' + str(delay).zfill(3) + 'ms'
28 | #subfolder = 'results' + r_number
29 | if (os.path.isdir(subfolder)):
30 | exit()
31 |
32 | print "Create a network."
33 | net = Mininet( controller=Controller, link=TCLink, switch=OVSKernelSwitch )
34 |
35 | print "*** Creating nodes"
36 |
37 | h1 = net.addHost( 'h1', mac='00:00:00:00:00:01', ip='10.0.0.1/8' )
38 |
39 | c1 = net.addController( 'c1', controller=Controller, ip='127.0.0.1' )
40 | s1 = net.addSwitch( 's1' )
41 |
42 | number_of_hosts = number_of_clients
43 | hosts = range(number_of_hosts)
44 | for i in range(number_of_hosts):
45 | host = 'host' + str(i).zfill(3)
46 | # hex is wrong here, [:-2] part
47 | hosts[i] = net.addHost(host, mac='00:00:00:00:00:' + hex(i)[:-2], ip='10.0.0.' + str(i + 2) + '/8')
48 |
49 | print "*** Associating and Creating links"
50 | queue_size = (total_rate_limit * delay * 1000) / (1500 * 8)
51 | queue_size = roundup(queue_size)
52 | link_main = net.addLink(s1, h1, bw=total_rate_limit, delay=(str(delay) + 'ms'), max_queue_size=queue_size)
53 | #link_main = net.addLink(s1, h1, bw=total_rate_limit, delay=(str(delay) + 'ms'))
54 | host_links = []
55 | for i in range(number_of_hosts):
56 | host_links.append(net.addLink(s1, hosts[i], bw=100))
57 |
58 | print "*** Starting network"
59 | net.build()
60 | c1.start()
61 | s1.start( [c1] )
62 |
63 | h1.cmdPrint('./caddy -host 10.0.0.1 -port 8080 -root ~/Downloads &')
64 | #h1.cmdPrint('iperf -s &')
65 |
66 | os.system('sleep 3')
67 |
68 | #alg = 'iperf'
69 |
70 | #subfolder = "results/dashc_bba-2_4_3Mbps_250ms"
71 | subfolder = 'results' + r_number + '/dashc_' + alg + '_' + str(number_of_hosts).zfill(3) + '_' + str(rate_limit) + 'Mbps_' + str(delay).zfill(3) + 'ms'
72 | #subfolder = 'results' + r_number
73 | os.system('mkdir -p ' + subfolder)
74 | for i in range(number_of_hosts):
75 | #hosts[i].cmdPrint('cd ~/Downloads/tapas-master; /usr/bin/time -o results.txt -a python play.py -a conventional -m fake -u http://10.0.0.1:8080/bbb/bbb_enc_10min_x264_dash_old.mpd &')
76 | #hosts[i].cmdPrint('cd ~/Downloads/tapas-master; /usr/bin/time -f \"%U %S %e %M\" -o results.txt -a python play.py -a conventional -m fake -u http://10.0.0.1:8080/bbb/bbb_enc_10min_x264_dash_old.mpd &')
77 | #hosts[i].cmdPrint('/usr/bin/time -o results.txt -a ./dashc.native http://10.0.0.1:8080/bbb/bbb_enc_10min_x264_dash.mpd -adapt conv -initb 2 -maxbuf 60 -persist true -lastsegmindex 75 -logname sta1 -v 20 -turnlogon false &')
78 | #hosts[i].cmdPrint('/usr/bin/time -f \"%U %S %e %M\" -o results.txt -a ./dashc.native http://10.0.0.1:8080/bbb/bbb_enc_10min_x264_dash.mpd -adapt conv -initb 2 -maxbuf 60 -persist true -lastsegmindex 75 -logname sta' + str(i).zfill(3) + ' -v 20 -turnlogon true -subfolder ' + subfolder + ' &')
79 |
80 | #hosts[i].cmdPrint('iperf -c 10.0.0.1 -t 300 | tee -a ' + subfolder + '/results' + str(number_of_clients).zfill(3) + '.txt &')
81 | #hosts[i].cmdPrint('iperf -c 10.0.0.1 -t 300 | tee -a ' + subfolder + '/' + alg + '_' + str(number_of_hosts).zfill(3) + '_' + str(rate_limit) + 'Mbps_' + str(delay).zfill(3) + 'ms' + '.txt &')
82 | hosts[i].cmdPrint('./dashc.native play http://10.0.0.1:8080/bbb/bbb_enc_10min_x264_dash.mpd -adapt ' + alg + ' -initb 2 -maxbuf 60 -persist true -segmentlist remote -lastsegmindex 75 -logname sta' + str(i).zfill(3) + ' -v 20 -r ' + r_number + ' -turnlogon true -subfolder ' + subfolder + ' &')
83 |
84 | #hosts[i].cmdPrint('cd ~/Downloads/tapas-master; python play.py -a conventional -m fake -u http://10.0.0.1:8080/bbb/bbb_enc_10min_x264_dash_old.mpd &')
85 | #hosts[i].cmdPrint('./dashc.native http://10.0.0.1:8080/bbb/bbb_enc_10min_x264_dash.mpd -adapt conv -initb 2 -maxbuf 60 -persist true -lastsegmindex 75 -logname sta1 -v 20 -turnlogon true &')
86 |
87 | print "*** Sleep for 400 seconds (300 video + 100 for everything else)"
88 | os.system('sleep 400')
89 |
90 | print "*** Stopping network"
91 | net.stop()
92 |
93 | if __name__ == '__main__':
94 | topology()
95 |
96 |
--------------------------------------------------------------------------------
/src/segm_result.ml:
--------------------------------------------------------------------------------
1 | (*
2 | * dashc, client emulator for DASH video streaming
3 | * Copyright (c) 2016-2018, Aleksandr Reviakin, University College Cork
4 | *
5 | * This program is free software; you can redistribute it and/or
6 | * modify it under the terms of the GNU General Public License
7 | * as published by the Free Software Foundation; either version 2
8 | * of the License, or (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program; if not, write to the Free Software
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 | * 02110-1301, USA.
19 | *)
20 |
21 | open Core
22 | open Async
23 |
24 | let measure = 1000
25 | let measure_float = 1000.
26 | let us = 1000
27 | let us_float = 1000.
28 |
29 | type segm_result = {
30 | segment_number : int;
31 | (* arrival_time is absolute time since the request of MPD or
32 | request of the first segment, ms *)
33 | arrival_time : int;
34 | (* time_for_delivey is request_time - response_time,
35 | us, because in a local network it can be less than 1 ms
36 | which results in 0 during conversion to int *)
37 | time_for_delivery : int;
38 | (* stall_dur is a time which was spent with buffer equal to 0
39 | (during waiting of the next segment), ms *)
40 | stall_dur : int;
41 | (* representation_rate is a bandwidth of this segment
42 | according to an MPD file in bits/s *)
43 | representation_rate : int;
44 | (* actual_representation_rate is equal to
45 | (segment_size / segment_duration) in K(something)bits/s *)
46 | actual_representation_rate : int;
47 | (* segment_size, bytes *)
48 | segment_size : int;
49 | (* buffer_level_in_momentum, seconds *)
50 | buffer_level_in_momentum : float;
51 | (* representation level *)
52 | repr_level : int;
53 | }
54 |
55 | let rec print_int_zeros ~number ~zeroes_number ~zero =
56 | let digits_number = (String.length @@ Int.to_string number) in
57 | if digits_number = zeroes_number then print_int number
58 | else if zero then begin
59 | print_int 0;
60 | print_int_zeros ~zero:zero ~number:number ~zeroes_number:(zeroes_number - 1)
61 | end
62 | else begin
63 | print_string " ";
64 | print_int_zeros ~zero:zero ~number:number ~zeroes_number:(zeroes_number - 1)
65 | end
66 |
67 | let print_result result_ = function
68 | | None ->
69 | print_int_zeros ~number:result_.segment_number ~zeroes_number:5 ~zero:false;
70 | print_string " ";
71 | print_int_zeros ~number:(result_.arrival_time / us) ~zeroes_number:8 ~zero:false;
72 | print_string " ";
73 | print_int_zeros ~number:(result_.time_for_delivery / us) ~zeroes_number:8 ~zero:false;
74 | print_string " ";
75 | print_int_zeros ~number:result_.stall_dur ~zeroes_number:9 ~zero:false;
76 | print_string " ";
77 | print_int_zeros
78 | ~number:(result_.representation_rate / measure) ~zeroes_number:9 ~zero:false;
79 | print_string " ";
80 | print_int_zeros
81 | ~number:(int_of_float
82 | (((float_of_int result_.segment_size *. 8. *. measure_float) /.
83 | float_of_int result_.time_for_delivery) *. us_float /. measure_float))
84 | ~zeroes_number:8 ~zero:false;
85 | print_string " ";
86 | print_int_zeros
87 | ~number:(result_.actual_representation_rate / measure)
88 | ~zeroes_number:8 ~zero:false;
89 | print_string " ";
90 | print_int_zeros ~number:(result_.segment_size) ~zeroes_number:9 ~zero:false;
91 | print_string " ";
92 | printf "%10.3f" result_.buffer_level_in_momentum;
93 | print_endline ""
94 | | Some outc ->
95 | Out_channel.output_string outc @@ Printf.sprintf "%5d" result_.segment_number ^ " ";
96 | Out_channel.output_string outc @@
97 | Printf.sprintf "%8d" (result_.arrival_time / us) ^ " ";
98 | Out_channel.output_string outc @@
99 | Printf.sprintf "%8d" (result_.time_for_delivery / us) ^ " ";
100 | Out_channel.output_string outc @@ Printf.sprintf "%9d" result_.stall_dur ^ " ";
101 | Out_channel.output_string outc @@
102 | Printf.sprintf "%9d" (result_.representation_rate / measure) ^ " ";
103 | Out_channel.output_string outc @@
104 | Printf.sprintf "%8d" (int_of_float
105 | (((float_of_int result_.segment_size *. 8. *. measure_float) /.
106 | float_of_int result_.time_for_delivery) *. us_float /. measure_float)) ^ " ";
107 | Out_channel.output_string outc @@
108 | Printf.sprintf "%8d" (result_.actual_representation_rate / measure) ^ " ";
109 | Out_channel.output_string outc @@ Printf.sprintf "%9d" result_.segment_size ^ " ";
110 | Out_channel.output_string outc @@
111 | Printf.sprintf "%10.3f" result_.buffer_level_in_momentum ^ " ";
112 | Out_channel.newline outc;
113 | Out_channel.flush outc
114 |
--------------------------------------------------------------------------------
/src/tests.ml:
--------------------------------------------------------------------------------
1 | (*
2 | * dashc, client emulator for DASH video streaming
3 | * Copyright (c) 2016-2018, Aleksandr Reviakin, University College Cork
4 | *
5 | * This program is free software; you can redistribute it and/or
6 | * modify it under the terms of the GNU General Public License
7 | * as published by the Free Software Foundation; either version 2
8 | * of the License, or (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program; if not, write to the Free Software
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 | * 02110-1301, USA.
19 | *)
20 |
21 | open Core
22 | open Async
23 | open Playback
24 | open Representation
25 |
26 | let mpd_file_name_1 = "bbb_enc_10min_x264_dash.mpd"
27 | let mpd_file_segment_duration_1 = 4
28 | let mpdf_file_link_1 = "https://127.0.0.1/bbb_enc_10min_x264_dash.mpd"
29 |
30 | let mpd_file_name_2 = "tearsofsteel_enc_x264_dash.mpd"
31 | let mpd_file_segment_duration_2 = 6
32 |
33 | let mpd_file_name_3 = "bbb_enc_10min_x264_dash_rev1.mpd"
34 | let mpd_file_segment_duration_3 = 4
35 | let mpd_file_name_4 = "bbb_enc_10min_x264_dash_rev2.mpd"
36 | let mpd_file_segment_duration_4 = 4
37 | let mpd_file_name_5 = "bbb_enc_10min_x264_dash_mixed.mpd"
38 | let mpd_file_segment_duration_5 = 4
39 |
40 | let input_algorithm () =
41 | Alcotest.check Alcotest.unit
42 | "BBA-2 is supported" () (check_input_algorithm_existence "bba-2")
43 |
44 | let parse_mpd file_name =
45 | let body = In_channel.read_all file_name in
46 | let mpd = Xml.parse_string body in
47 | let representations : (int, representation) Hashtbl.t = repr_table_from_mpd mpd in
48 | representations
49 |
50 | let mpd_parsing_bandwidth_1 () =
51 | let representations = parse_mpd mpd_file_name_1 in
52 | Alcotest.check Alcotest.int "The bandwidth value in the given mpd file is"
53 | 4275756
54 | (Hashtbl.find_exn representations 10).bandwidth
55 |
56 | let mpd_parsing_bandwidth_2 () =
57 | let representations = parse_mpd mpd_file_name_2 in
58 | Alcotest.check Alcotest.int "The bandwidth value in the given mpd file is"
59 | 236378
60 | (Hashtbl.find_exn representations 1).bandwidth
61 |
62 | let mpd_parsing_bandwidth_3 () =
63 | let representations = parse_mpd mpd_file_name_3 in
64 | Alcotest.check Alcotest.int "The bandwidth value in the given mpd file is"
65 | 232385
66 | (Hashtbl.find_exn representations 1).bandwidth
67 |
68 | let mpd_parsing_bandwidth_4 () =
69 | let representations = parse_mpd mpd_file_name_4 in
70 | Alcotest.check Alcotest.int "The bandwidth value in the given mpd file is"
71 | 232385
72 | (Hashtbl.find_exn representations 1).bandwidth
73 |
74 | let mpd_parsing_bandwidth_5 () =
75 | let representations = parse_mpd mpd_file_name_5 in
76 | Alcotest.check Alcotest.int "The bandwidth value in the given mpd file is"
77 | 232385
78 | (Hashtbl.find_exn representations 1).bandwidth
79 |
80 | let mpd_parsing_repr_number_1 () =
81 | let representations = parse_mpd mpd_file_name_1 in
82 | Alcotest.check Alcotest.int "The number of representations in the given mpd file is"
83 | 10
84 | (Hashtbl.length representations)
85 |
86 | let mpd_parsing_repr_number_2 () =
87 | let representations = parse_mpd mpd_file_name_2 in
88 | Alcotest.check Alcotest.int "The number of representations in the given mpd file is"
89 | 13
90 | (Hashtbl.length representations)
91 |
92 | let test_get_last_segment_index_1 () =
93 | let body = In_channel.read_all mpd_file_name_1 in
94 | let mpd = Xml.parse_string body in
95 | let last_segment_index =
96 | get_last_segment_index mpd mpd_file_segment_duration_1 None in
97 | Alcotest.check Alcotest.int "The last segment index is"
98 | 150
99 | last_segment_index
100 |
101 | let test_get_last_segment_index_2 () =
102 | let body = In_channel.read_all mpd_file_name_2 in
103 | let mpd = Xml.parse_string body in
104 | let last_segment_index =
105 | get_last_segment_index mpd mpd_file_segment_duration_2 None in
106 | Alcotest.check Alcotest.int "The last segment index is"
107 | 123
108 | last_segment_index
109 |
110 | let test_read_segment_size_file () =
111 | Thread_safe.block_on_async_exn(fun () ->
112 | let representations = parse_mpd mpd_file_name_1 in
113 |
114 | let body = In_channel.read_all mpd_file_name_1 in
115 | let mpd = Xml.parse_string body in
116 | let last_segment_index =
117 | get_last_segment_index mpd mpd_file_segment_duration_1 None in
118 |
119 | read_segment_size_file
120 | ?remote_string:None
121 | ~link:mpdf_file_link_1
122 | ~number_of_representations:(Hashtbl.length representations)
123 | ~last_segment_index:last_segment_index
124 | >>| fun chunk_sizes_per_repr ->
125 | Alcotest.check Alcotest.int
126 | "The number of representations in the given chunk size file is"
127 | 10
128 | (Hashtbl.length chunk_sizes_per_repr)
129 | )
130 |
131 | let () =
132 | Alcotest.run "tests" [
133 | "first", [
134 | "input algorithm availability", `Quick, input_algorithm;
135 | "bandwidth file 1", `Quick, mpd_parsing_bandwidth_1;
136 | "bandwidth file 2", `Quick, mpd_parsing_bandwidth_2;
137 | "bandwidth file 3", `Quick, mpd_parsing_bandwidth_3;
138 | "bandwidth file 4", `Quick, mpd_parsing_bandwidth_4;
139 | "bandwidth file 5", `Quick, mpd_parsing_bandwidth_5;
140 | "number of representations file 1", `Quick, mpd_parsing_repr_number_1;
141 | "number of representations file 2", `Quick, mpd_parsing_repr_number_2;
142 | "get_last_segment_index, file 1", `Quick, test_get_last_segment_index_1;
143 | "get_last_segment_index, file 2", `Quick, test_get_last_segment_index_2;
144 | "read_segment_size_file fun", `Slow, test_read_segment_size_file;
145 | ];
146 | ]
--------------------------------------------------------------------------------
/CHANGES.md:
--------------------------------------------------------------------------------
1 | Known issues
2 | --------------------
3 | * full_byte_range, main_byte_range, onDemand_byte_range profiles are not supported
4 |
5 | 0.1.22 (2019.04)
6 | --------------------
7 | * updates to configure and Makefile to remove build errors with updated verison of ocaml, cohttp, etc.
8 |
9 | 0.1.21 (2018.10)
10 | --------------------
11 | * Cohttp link was updated to 2018.10.19, version 1.2.0
12 | * Update to OCaml 4.07.1 from 4.07.0
13 |
14 | 0.1.20 (2018.09)
15 | --------------------
16 | * content-length (from the header) is now used instead of the (String.length body)
17 | for segment size determination
18 | * The bug related to double printing of the log on the screen was fixed.
19 | It happened when the logging to the file was turned off.
20 | * Reconnection was added, if the server closes connection unexpectedly.
21 | It happens only once, the second time will force the tool to shutdown.
22 | * mkdir is not called anymore with an empty string (no more "mkdir: missing operand" message)
23 | * Ported from jbuilder to dune
24 | * Cohttp link was updated to 2018.09.06, version 1.1.1
25 | * Update to OCaml 4.07.0 from 4.06.1
26 | * Parsing of mpd files is more generic now (#2, reported by pedosb)
27 | * Ported from opam 1.2.2 to 2.0.0
28 |
29 | 0.1.19 (2018.04.30)
30 | --------------------
31 | * Cohttp link was updated to 2018.04.19, version 1.1.0
32 | * Added parsing of the mediaPresentationDuration, lastsegmindex argument is optional now
33 | * Fixed regression added in 0.1.18 (incorrect calculation of the total_number_of_repr)
34 | * A few tests were added
35 |
36 | 0.1.18 (2018.03.02)
37 | --------------------
38 | * Update to OCaml 4.06.1 from 4.06.0
39 | * Initial test was added (alcotest package is used)
40 |
41 | 0.1.17 (2018.01.07)
42 | --------------------
43 | * Necessary changes were applied for compatibility with core/async v0.10.0
44 | (v0.10.0 has support of glibc-2.26 which is used in Ubuntu 17.10)
45 | * Cohttp master 2018.01.07, version 1.0.2 is used now instead of the master 2017.11.21, version 1.0.0
46 |
47 | 0.1.16 (2017.11.23)
48 | --------------------
49 | * now representations hash table is created according to the number of representations in the MPD file
50 | * Added support of variable number of AdaptationSet tags in an MPD file
51 | (GPAC 0.7.2 creates several of them in comparison with GPAC 0.5.2 based on aspect ratio of video)
52 |
53 | 0.1.15 (2017.11.21)
54 | --------------------
55 | * Update cohttp from 0.99 to 1.0.0 (to be precise to the commit from 2017.11.21)
56 | * Update to OCaml 4.06.0 from 4.05.0
57 |
58 | 0.1.14 (2017.11.20)
59 | --------------------
60 | * Fix in BBA-2: prev_time_for_delivery previously was calculated by dividing int by int (us / 1_000_000), what gave incorrect result (it was technically correct, but precision of 1 second is useless). Previously the previous representation level would be determined incorrectly based on an approximate representation rate. This changed was applied in BBA-1 (in calculate_reservoir as well), BBA-2, ARBITER
61 | * full, live, main profiles (created by GPAC) are supported. They were supported before, but checked only now
62 |
63 | 0.1.13 (2017.09.29)
64 | --------------------
65 | * Fixes in ARBITER, Conventional algorithms and in print_result related to possible overflow of int in 32 bit OS.
66 | It happens for sure in Raspberry Pi 3 in 32 bit OS. The 64 bit float will help to avoid it.
67 |
68 | 0.1.12 (2017.09.27)
69 | --------------------
70 | * Support of SegmentList type of MPD was added
71 |
72 | 0.1.11 (2017.09.26)
73 | --------------------
74 | * Fixes in throughput mean and throughput variance calculation in ARBITER:
75 | previously results starting from the beginning were used for calculations instead of the most recent one
76 |
77 | 0.1.10 (2017.09.25)
78 | --------------------
79 | * Fix in BBA-1 (BBA-2 uses BBA-1 code) in selection of the next requested representation,
80 | previously the representation level could've been changed only by one level up/down
81 |
82 | 0.1.9 (2017.08.24)
83 | --------------------
84 | * Added flag to change an implementation of conventional algorithm to an alternative one
85 | * Port to Jbuilder
86 |
87 | 0.1.8 (2017.08.14)
88 | --------------------
89 | * Conventional algorithm coefficients were changed from 0.2 to 0.4 and from 0.8 to 0.6
90 |
91 | 0.1.7 (2017.07.25)
92 | --------------------
93 | * Update to OCaml 4.05.0 from 4.02.2 (4.02.2 was set by mistake instead of 4.04.2 in 0.1.5)
94 |
95 | 0.1.6 (2017.07.25)
96 | --------------------
97 | * Update to cohttp/cohttp-async 0.99
98 |
99 | 0.1.5 (2017.07.16)
100 | --------------------
101 | * default OCaml version is set back to 4.04.2
102 | conduit and cohttp depend on ppx_deriving, ppx_deriving 4.1 has
103 | Available ocaml-version >= "4.02.1" & ocaml-version < "4.05" & opam-version >= "1.2"
104 |
105 | 0.1.4 (2017.07.15)
106 | --------------------
107 | * default OCaml version is set to 4.05.0
108 | * change back to opam-based core library (in 0.9.3 there was an important for Raspberry Pi build fix)
109 | https://github.com/janestreet/base/issues/15
110 |
111 | 0.1.3 (2017.06.07)
112 | --------------------
113 | * new repository from janestreet, mentioned in 0.1.2, was not added actually,
114 | and it is not now, because these libraries were recently updated in opam, so, no need
115 | * ocaml-cohttp folder added to .gitignore
116 |
117 | 0.1.2 (2017.05.03)
118 | --------------------
119 | * Update to the new janestreet repository:
120 | opam repo add janestreet-dev https://ocaml.janestreet.com/opam-repository
121 |
122 | 0.1.1 (2017.04.26)
123 | --------------------
124 | * ARBITER now has 60 seconds maxbuf instead of taking it from command line parameters
125 | * ARBITER was added somewhen in January 2017 (right after 0.1.0)
126 |
127 | 0.1.0 (2016.12.30)
128 | --------------------
129 | * Initial release
130 | * implemented algorithms: Conventional (Probe and Adapt: Rate Adaptation for HTTP Video Streaming At Scale, https://arxiv.org/abs/1305.0510), bba-0, bba-1, bba-2 (A Buffer-Based Approach to Rate Adaptation: Evidence from a Large Video Streaming Service, http://yuba.stanford.edu/~nickm/papers/sigcomm2014-video.pdf)
131 |
--------------------------------------------------------------------------------
/test_mpd/tearsofsteel_enc_x264_dash.mpd:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | tearsofsteel_enc_x264_dash.mpd generated by GPAC
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/ocaml-cohttp_/cohttp-async/src/client.ml:
--------------------------------------------------------------------------------
1 | open Base
2 | open Async_kernel
3 | open Async_unix
4 |
5 | module Request = struct
6 | include Cohttp.Request
7 | include (Make(Io) : module type of Make(Io) with type t := t)
8 | end
9 |
10 | module Response = struct
11 | include Cohttp.Response
12 | include (Make(Io) : module type of Make(Io) with type t := t)
13 | end
14 |
15 | module Net = struct
16 |
17 | let lookup uri =
18 | let host = Option.value (Uri.host uri) ~default:"localhost" in
19 | match Uri_services.tcp_port_of_uri ~default:"http" uri with
20 | | None -> Deferred.Or_error.error_string
21 | "Net.lookup: failed to get TCP port form Uri"
22 | | Some port ->
23 | let open Unix in
24 | Addr_info.get ~host [ Addr_info.AI_FAMILY PF_INET
25 | ; Addr_info.AI_SOCKTYPE SOCK_STREAM]
26 | >>| function
27 | | { Addr_info.ai_addr=ADDR_INET (addr,_); _ }::_ ->
28 | Or_error.return (host, Ipaddr_unix.of_inet_addr addr, port)
29 | | _ -> Or_error.error "Failed to resolve Uri" uri Uri_sexp.sexp_of_t
30 |
31 | let connect_uri ?interrupt ?ssl_config uri =
32 | lookup uri
33 | |> Deferred.Or_error.ok_exn
34 | >>= fun (host, addr, port) ->
35 | let mode =
36 | match (Uri.scheme uri, ssl_config) with
37 | | Some "https", Some config -> `OpenSSL_with_config (host, addr, port, config)
38 | | Some "https", None -> `OpenSSL (host, addr, port)
39 | | Some "httpunix", _ -> `Unix_domain_socket host
40 | | _ -> `TCP (addr, port)
41 | in
42 | Conduit_async.connect ?interrupt mode
43 | end
44 |
45 | let read_request ic =
46 | Response.read ic >>| function
47 | | `Eof -> failwith "Connection closed by remote host"
48 | | `Invalid reason -> failwith reason
49 | | `Ok res ->
50 | (* Build a response pipe for the body *)
51 | let reader = Response.make_body_reader res ic in
52 | let pipe = Body_raw.pipe_of_body Response.read_body_chunk reader in
53 | (res, pipe)
54 |
55 | let request ?conn ?interrupt ?ssl_config ?uri ?(body=`Empty) req =
56 | (* Connect to the remote side *)
57 | let uri =
58 | match uri with
59 | | Some t -> t
60 | | None -> Request.uri req in
61 | match conn with
62 | | None ->
63 | Net.connect_uri ?interrupt ?ssl_config uri
64 | >>= fun (ic, oc) ->
65 | try_with (fun () ->
66 | Request.write (fun writer ->
67 | Body_raw.write_body Request.write_body body writer) req oc
68 | >>= fun () ->
69 | read_request ic >>| fun (resp, body) ->
70 | don't_wait_for (
71 | Pipe.closed body >>= fun () ->
72 | Deferred.all_unit [Reader.close ic; Writer.close oc]);
73 | (resp, `Pipe body)) >>= begin function
74 | | Ok res -> return res
75 | | Error e ->
76 | don't_wait_for (Reader.close ic);
77 | don't_wait_for (Writer.close oc);
78 | raise e
79 | end
80 | | Some (ic,oc) ->
81 | try_with (fun () ->
82 | Request.write (fun writer ->
83 | Body_raw.write_body Request.write_body body writer) req oc
84 | >>= fun () ->
85 | read_request ic >>| fun (resp, body) ->
86 | (resp, `Pipe body)) >>= begin function
87 | | Ok res -> return res
88 | | Error e ->
89 | don't_wait_for (Reader.close ic);
90 | don't_wait_for (Writer.close oc);
91 | raise e
92 | end
93 |
94 | let callv ?interrupt ?ssl_config uri reqs =
95 | let reqs_c = ref 0 in
96 | let resp_c = ref 0 in
97 | Net.connect_uri ?interrupt ?ssl_config uri >>= fun (ic, oc) ->
98 | try_with (fun () ->
99 | reqs
100 | |> Pipe.iter ~f:(fun (req, body) ->
101 | Int.incr reqs_c;
102 | Request.write (fun w -> Body_raw.write_body Request.write_body body w)
103 | req oc)
104 | |> don't_wait_for;
105 | let last_body_drained = ref Deferred.unit in
106 | let responses = Reader.read_all ic (fun ic ->
107 | !last_body_drained >>= fun () ->
108 | if Pipe.is_closed reqs && (!resp_c >= !reqs_c) then
109 | return `Eof
110 | else
111 | ic |> read_request >>| fun (resp, body) ->
112 | Int.incr resp_c;
113 | last_body_drained := Pipe.closed body;
114 | `Ok (resp, `Pipe body)
115 | ) in
116 | don't_wait_for (
117 | Pipe.closed reqs >>= fun () ->
118 | Pipe.closed responses >>= fun () ->
119 | Writer.close oc
120 | );
121 | return responses)
122 | >>= begin function
123 | | Ok x -> return x
124 | | Error e ->
125 | don't_wait_for (Reader.close ic);
126 | don't_wait_for (Writer.close oc);
127 | raise e
128 | end
129 |
130 | let call ?conn ?interrupt ?ssl_config ?headers ?(chunked=false) ?(body=`Empty) meth uri =
131 | (* Create a request, then make the request. Figure out an appropriate
132 | transfer encoding *)
133 | let req =
134 | match chunked with
135 | | false ->
136 | Body_raw.disable_chunked_encoding body >>| fun (_body, body_length) ->
137 | Request.make_for_client ?headers ~chunked ~body_length meth uri
138 | | true -> begin
139 | Body.is_empty body >>| function
140 | | true -> (* Don't used chunked encoding with an empty body *)
141 | Request.make_for_client ?headers ~chunked:false ~body_length:0L meth uri
142 | | false -> (* Use chunked encoding if there is a body *)
143 | Request.make_for_client ?headers ~chunked:true meth uri
144 | end
145 | in
146 | req >>= request ?conn ?interrupt ?ssl_config ~body ~uri
147 |
148 | let get ?conn ?interrupt ?ssl_config ?headers uri =
149 | call ?conn ?interrupt ?ssl_config ?headers ~chunked:false `GET uri
150 |
151 | let head ?conn ?interrupt ?ssl_config ?headers uri =
152 | call ?conn ?interrupt ?ssl_config ?headers ~chunked:false `HEAD uri
153 | >>| fun (res, body) ->
154 | (match body with
155 | | `Pipe p -> Pipe.close_read p;
156 | | _ -> ());
157 | res
158 |
159 | let post ?interrupt ?ssl_config ?headers ?(chunked=false) ?body uri =
160 | call ?interrupt ?ssl_config ?headers ~chunked ?body `POST uri
161 |
162 | let post_form ?interrupt ?ssl_config ?headers ~params uri =
163 | let headers = Cohttp.Header.add_opt_unless_exists headers
164 | "content-type" "application/x-www-form-urlencoded" in
165 | let body = Body.of_string (Uri.encoded_of_query params) in
166 | post ?interrupt ?ssl_config ~headers ~chunked:false ~body uri
167 |
168 | let put ?interrupt ?ssl_config ?headers ?(chunked=false) ?body uri =
169 | call ?interrupt ?ssl_config ?headers ~chunked ?body `PUT uri
170 |
171 | let patch ?interrupt ?ssl_config ?headers ?(chunked=false) ?body uri =
172 | call ?interrupt ?ssl_config ?headers ~chunked ?body `PATCH uri
173 |
174 | let delete ?interrupt ?ssl_config ?headers ?(chunked=false) ?body uri =
175 | call ?interrupt ?ssl_config ?headers ~chunked ?body `DELETE uri
176 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # dashc, client emulator for DASH video streaming
2 |
3 | ## Description
4 |
5 | dashc is an emulator of the video player, it was intended to be used as a test tool. The original goal was to have a lightweight test tool for network performance experiments with a real DASH video streaming traffic.
6 |
7 | dashc has several options which can be changed through the command line arguments:
8 |
9 | * video adaptation algorithm (Conventional, BBA-0/BBA-1/BBA-2, ARBITER);
10 | * initial buffer size (in segments);
11 | * last segment index for play;
12 | * log file and folder name (the log file can be on/off);
13 | * maximum buffer size (in seconds);
14 | * persistent connection (on/off);
15 | * generation of the meta information for BBA-2 and ARBITER (file with segment sizes);
16 |
17 | ## Installation
18 |
19 | dashc had been used in Ubuntu 16.04/16.10/17.04/17.10/18.04 with x64 architecture (continuously during 2017), in Ubuntu 16.04.3 with Raspberry Pi 2/3 (briefly during 2017), in macOS High Sierra 10.13 (briefly during 2018). Ubuntu 16.04/16.10/17.04/17.10 were used for tests when they were the newest available versions - dashc might work or might not work with them right now.
20 |
21 | the current version of dashc has been tested with 18.04.02.
22 |
23 | To install (./configure might ask for a sudo password to install opam, m4, git, bubblewrap and other dependencies (for Linux only)):
24 |
25 | ```bash
26 | ./configure
27 | make
28 | ```
29 |
30 | During ./configure, some questions might be asked:
31 |
32 | Q: ## Where should it be installed ? [/usr/local/bin]
33 |
34 | A: Accept it (just an enter key on a keyboard, not a "y" and then enter key).
35 |
36 | Q: Do you want opam to modify ~/.profile? [N/y/f]
37 |
38 | A: y
39 |
40 | Q: A hook can be added to opam's init scripts to ensure that the shell remains in sync with the opam environment when they are loaded. Set that up? [y/N]
41 |
42 | A: y
43 |
44 | To run tests (not necessary):
45 |
46 | ```bash
47 | make test
48 | ```
49 |
50 | ## Usage
51 |
52 | dashc can be run as simple as:
53 |
54 | ```bash
55 | ./dashc.exe play http://www.cs.ucc.ie/~jq5/www_dataset_temp/x264_4sec/bbb_10min/DASH_Files/VOD/bbb_enc_10min_x264_dash.mpd
56 | ```
57 |
58 | or (all available options):
59 |
60 | ```bash
61 | ./dashc.exe play http://127.0.0.1/bbb.mpd [-adapt conv] [-initb 2] [-maxbuf 60] [-persist true] [-turnlogon true] [-logname now] [-subfolder qwe]
62 | ```
63 |
64 | Where all flags except the link to the MPD file are optional. Possible flags are:
65 |
66 | * [-adapt], adaptation algorithm (conv; bba-0; bba-1; bba-2; arbiter);
67 | * [-initb], initial buffer (in segments);
68 | * [-lastsegmindex], last segment index for play;
69 | * [-logname], name of the log file ("now" means usage of the current time stamp and it is used by default);
70 | * [-maxbuf], maximum buffer size (in seconds);
71 | * [-persist], persistent connection (true/false);
72 | * [-segmentlist], this parameter is used to tell where to get meta data infromation (segment sizes, for example, for BBA-2 and ARBITER). Possible options are head (dashc will send head requests before starting to stream), local (file with segment size should be at the same folder with dashc), remote (file with segment size should be at the same location where target MPD file is stored) get segment sizes from local - local segmentlist_%mpd_name%.txt file. The default option is head;
73 | * [-subfolder], subfolder for the file;
74 | * [-turnlogon], turn on logging to file;
75 | * [-gensegmfile], generate segmentlist_%mpd_name%.txt file only (it will be rewritten if exists);
76 | * [-help], print this help text and exit, (alias: -?);
77 |
78 | The detailed help can be found by running it with the -help flag:
79 |
80 | ```bash
81 | ./dashc.exe play -help
82 | ```
83 |
84 | If you don't want to wait while "meta information" is being downloaded "Starting of downloading headers for representation %NUMBER%" (segment sizes for BBA-1/2 and ARBITER) you can use -gensegmfile flag to pre-download one, for example, by typing
85 |
86 | ```bash
87 | ./dashc.exe play http://www.cs.ucc.ie/~jq5/www_dataset_temp/x264_4sec/ed_10min/DASH_Files/VOD/ed_enc_10min_x264_dash.mpd -gensegmfile true
88 | ```
89 |
90 | file "segmentlist_ed_enc_10min_x264_dash.mpd.txt" will be created in the current folder. Then you can run it like
91 |
92 | ```bash
93 | ./dashc.exe play http://www.cs.ucc.ie/~jq5/www_dataset_temp/x264_4sec/ed_10min/DASH_Files/VOD/ed_enc_10min_x264_dash.mpd -adapt arbiter -segmentlist local
94 | ```
95 |
96 | The typical log file will look like the example below (here the data only for the first 30 segments is shown, ARBITER adaptation algorithm is used, bbb-264 4 seconds segment size clip is used):
97 |
98 | ```bash
99 | Seg_# Arr_time Del_Time Stall_Dur Rep_Level Del_Rate Act_Rate Byte_Size Buff_Level
100 | 1 810 810 0 232 1225 248 124131 4.000
101 | 2 1554 744 0 232 1487 276 138452 8.000
102 | 3 2283 728 0 232 1496 272 136272 11.272
103 | 4 3696 1413 0 374 1591 562 281170 13.858
104 | 5 4273 576 0 752 1884 271 135890 17.282
105 | 6 5571 1298 0 752 1963 637 318725 19.983
106 | 7 10756 5184 0 1060 1322 1713 856959 18.799
107 | 8 15020 4263 0 752 823 877 438974 18.535
108 | 9 16572 1551 0 560 1350 523 261860 20.983
109 | 10 17832 1260 0 560 1212 381 190970 23.723
110 | 11 19904 2072 0 752 1117 578 289478 25.651
111 | 12 22951 3046 0 752 1032 786 393344 26.605
112 | 13 26412 3461 0 752 1019 882 441029 27.143
113 | 14 28438 2026 0 752 1536 778 389046 29.117
114 | 15 29046 607 0 752 1732 263 131513 32.510
115 | 16 29996 949 0 752 2105 499 249943 35.560
116 | 17 32062 2066 0 1060 2034 1050 525440 37.494
117 | 18 34956 2893 0 1060 1111 803 401864 38.601
118 | 19 39174 4218 0 1060 1232 1299 649641 38.382
119 | 20 44807 5631 0 1060 1560 2196 1098391 36.750
120 | 21 46942 2134 0 1060 1357 723 361990 38.616
121 | 22 48440 1498 0 1060 1071 401 200595 41.118
122 | 23 51428 2987 0 1060 1055 788 394241 42.130
123 | 24 53142 1713 0 1060 1036 443 221995 44.416
124 | 25 55589 2446 0 1060 1185 724 362490 45.970
125 | 26 57293 1704 0 752 1590 677 338960 48.265
126 | 27 59909 2615 0 752 1200 784 392369 49.649
127 | 28 63206 3297 0 1060 1254 1034 517165 50.352
128 | 29 66206 2999 0 1060 1740 1305 652588 51.353
129 | 30 70260 4053 0 1060 1822 1847 923626 51.299
130 | ```
131 |
132 | The columns include next information:
133 |
134 | * segment number starting from 1;
135 | * arrival time of a segment in milliseconds;
136 | * time spent for delivery of this segment in milliseconds;
137 | * stall (freeze) duration in milliseconds;
138 | * representation rate of downloaded segment in Kbit/s (taken from MPD file);
139 | * delivery rate of the network in Kbit/s (segment size divided by time for delivery);
140 | * the actual bitrate of this segment (segment size divided by the segment duration) in Kbit/s;
141 | * segment size in bytes;
142 | * buffer level after this segment was just downloaded, in seconds;
143 |
144 | ## Possible datasets for usage
145 |
146 | iVID-Datasets for AVC (H.264) and HEVC (H.265): . The example of the log file above was taken with this dataset and caddy web server ().
147 |
148 | AVC and HEVC UHD 4K DASH Datasets:
149 |
150 |
151 | ## Support
152 |
153 | dashc is not in active development, if you have an issue, please, create a new one in github with necessary information (type of MPD file, OS version and etc.) - maybe somebody will answer.
154 |
155 | ## FAQ
156 |
157 | Q: I have opam 1.2.2 already, do I really need to install opam 2?
158 |
159 | A: Yes, read the .
160 |
161 | Q: X package (dependency) cannot be installed.
162 |
163 | A: Update of the packages database might help. opam-repository () changes constantly - some packages might break temporarily.
164 |
165 | ```bash
166 | opam update # Update the packages database
167 | opam upgrade # Bring everything to the latest version possible
168 | ```
169 |
170 | Q: After the first ./configure, the make prints something like:
171 |
172 | ```bash
173 | make: dune: Command not found
174 | Makefile:4: recipe for target 'all' failed
175 | make: *** [all] Error 127
176 | ```
177 |
178 | A: Log out of the OS and log in back might help.
179 |
180 | Q: Why one of Cohttp library files is swapped during ./configure?
181 |
182 | A: Cohttp library has (had?) interface only for non-persistent connections (requests), so it is slightly modifed by this file. configure keeps the hash of the last commit () when everything was fine, it make sense to update it sometimes (cohttp might get support of a new OCaml compiler or new features/bug fixes).
183 |
184 | Q: Why there was a need in a new implementation instead of updating already existing similar solutions?
185 |
186 | A: Initially GPAC player was supposed to be used for doing the main phd research, but it didn't work properly for some reason. At the same time it was clear that making a custom small emulator of the video player won't take much time.
187 |
188 | Q: Why OCaml was used (not a C/C++/Java/C#/Python)?
189 |
190 | A: The main reason for OCaml choice was a past expirience with it. It took around two weeks of development to make a tool with conventional and BBA-0/1/2 algorithms during December 2016 (Christmas holidays!).
191 |
192 | Q: What is OCaml?
193 |
194 | A: A good set of resources can be found here:
195 | *
196 | *
197 | *
198 | *
199 | *
200 | * (OCaml weekly news)
201 | *
202 | * Some videos about OCaml (, , )
203 |
204 | Q: Does anybody use OCaml in production?
205 |
206 | A:
207 |
208 | Q: Are there any good IDEs for OCaml?
209 |
210 | A: Visual Studio Code with reasonml plugin () is good. Merlin and ocp-indent should be installed as well:
211 | ```bash
212 | opam install merlin ocp-indent
213 | ```
214 |
215 | Q: Are there any open source code examples in OCaml?
216 |
217 | A: Some open source projects:
218 | *
219 | *
220 | *
221 | *
222 | *
223 |
224 |
225 | ## Citation
226 |
227 | Aleksandr Reviakin, Ahmed H. Zahran, Cormac J. Sreenan. dashc : a highly scalable client emulator for DASH video. MMSys 2018 Open Dataset & Software Track,
228 |
229 | ## Licence
230 |
231 | This software has emanated from research conducted with the financial support of Science Foundation Ireland (SFI) under Grant Number 13/IA/1892.
232 |
233 | University College Cork
234 |
235 | GPL-2 License
236 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 2, June 1991
3 |
4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6 | Everyone is permitted to copy and distribute verbatim copies
7 | of this license document, but changing it is not allowed.
8 |
9 | Preamble
10 |
11 | The licenses for most software are designed to take away your
12 | freedom to share and change it. By contrast, the GNU General Public
13 | License is intended to guarantee your freedom to share and change free
14 | software--to make sure the software is free for all its users. This
15 | General Public License applies to most of the Free Software
16 | Foundation's software and to any other program whose authors commit to
17 | using it. (Some other Free Software Foundation software is covered by
18 | the GNU Lesser General Public License instead.) You can apply it to
19 | your programs, too.
20 |
21 | When we speak of free software, we are referring to freedom, not
22 | price. Our General Public Licenses are designed to make sure that you
23 | have the freedom to distribute copies of free software (and charge for
24 | this service if you wish), that you receive source code or can get it
25 | if you want it, that you can change the software or use pieces of it
26 | in new free programs; and that you know you can do these things.
27 |
28 | To protect your rights, we need to make restrictions that forbid
29 | anyone to deny you these rights or to ask you to surrender the rights.
30 | These restrictions translate to certain responsibilities for you if you
31 | distribute copies of the software, or if you modify it.
32 |
33 | For example, if you distribute copies of such a program, whether
34 | gratis or for a fee, you must give the recipients all the rights that
35 | you have. You must make sure that they, too, receive or can get the
36 | source code. And you must show them these terms so they know their
37 | rights.
38 |
39 | We protect your rights with two steps: (1) copyright the software, and
40 | (2) offer you this license which gives you legal permission to copy,
41 | distribute and/or modify the software.
42 |
43 | Also, for each author's protection and ours, we want to make certain
44 | that everyone understands that there is no warranty for this free
45 | software. If the software is modified by someone else and passed on, we
46 | want its recipients to know that what they have is not the original, so
47 | that any problems introduced by others will not reflect on the original
48 | authors' reputations.
49 |
50 | Finally, any free program is threatened constantly by software
51 | patents. We wish to avoid the danger that redistributors of a free
52 | program will individually obtain patent licenses, in effect making the
53 | program proprietary. To prevent this, we have made it clear that any
54 | patent must be licensed for everyone's free use or not licensed at all.
55 |
56 | The precise terms and conditions for copying, distribution and
57 | modification follow.
58 |
59 | GNU GENERAL PUBLIC LICENSE
60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61 |
62 | 0. This License applies to any program or other work which contains
63 | a notice placed by the copyright holder saying it may be distributed
64 | under the terms of this General Public License. The "Program", below,
65 | refers to any such program or work, and a "work based on the Program"
66 | means either the Program or any derivative work under copyright law:
67 | that is to say, a work containing the Program or a portion of it,
68 | either verbatim or with modifications and/or translated into another
69 | language. (Hereinafter, translation is included without limitation in
70 | the term "modification".) Each licensee is addressed as "you".
71 |
72 | Activities other than copying, distribution and modification are not
73 | covered by this License; they are outside its scope. The act of
74 | running the Program is not restricted, and the output from the Program
75 | is covered only if its contents constitute a work based on the
76 | Program (independent of having been made by running the Program).
77 | Whether that is true depends on what the Program does.
78 |
79 | 1. You may copy and distribute verbatim copies of the Program's
80 | source code as you receive it, in any medium, provided that you
81 | conspicuously and appropriately publish on each copy an appropriate
82 | copyright notice and disclaimer of warranty; keep intact all the
83 | notices that refer to this License and to the absence of any warranty;
84 | and give any other recipients of the Program a copy of this License
85 | along with the Program.
86 |
87 | You may charge a fee for the physical act of transferring a copy, and
88 | you may at your option offer warranty protection in exchange for a fee.
89 |
90 | 2. You may modify your copy or copies of the Program or any portion
91 | of it, thus forming a work based on the Program, and copy and
92 | distribute such modifications or work under the terms of Section 1
93 | above, provided that you also meet all of these conditions:
94 |
95 | a) You must cause the modified files to carry prominent notices
96 | stating that you changed the files and the date of any change.
97 |
98 | b) You must cause any work that you distribute or publish, that in
99 | whole or in part contains or is derived from the Program or any
100 | part thereof, to be licensed as a whole at no charge to all third
101 | parties under the terms of this License.
102 |
103 | c) If the modified program normally reads commands interactively
104 | when run, you must cause it, when started running for such
105 | interactive use in the most ordinary way, to print or display an
106 | announcement including an appropriate copyright notice and a
107 | notice that there is no warranty (or else, saying that you provide
108 | a warranty) and that users may redistribute the program under
109 | these conditions, and telling the user how to view a copy of this
110 | License. (Exception: if the Program itself is interactive but
111 | does not normally print such an announcement, your work based on
112 | the Program is not required to print an announcement.)
113 |
114 | These requirements apply to the modified work as a whole. If
115 | identifiable sections of that work are not derived from the Program,
116 | and can be reasonably considered independent and separate works in
117 | themselves, then this License, and its terms, do not apply to those
118 | sections when you distribute them as separate works. But when you
119 | distribute the same sections as part of a whole which is a work based
120 | on the Program, the distribution of the whole must be on the terms of
121 | this License, whose permissions for other licensees extend to the
122 | entire whole, and thus to each and every part regardless of who wrote it.
123 |
124 | Thus, it is not the intent of this section to claim rights or contest
125 | your rights to work written entirely by you; rather, the intent is to
126 | exercise the right to control the distribution of derivative or
127 | collective works based on the Program.
128 |
129 | In addition, mere aggregation of another work not based on the Program
130 | with the Program (or with a work based on the Program) on a volume of
131 | a storage or distribution medium does not bring the other work under
132 | the scope of this License.
133 |
134 | 3. You may copy and distribute the Program (or a work based on it,
135 | under Section 2) in object code or executable form under the terms of
136 | Sections 1 and 2 above provided that you also do one of the following:
137 |
138 | a) Accompany it with the complete corresponding machine-readable
139 | source code, which must be distributed under the terms of Sections
140 | 1 and 2 above on a medium customarily used for software interchange; or,
141 |
142 | b) Accompany it with a written offer, valid for at least three
143 | years, to give any third party, for a charge no more than your
144 | cost of physically performing source distribution, a complete
145 | machine-readable copy of the corresponding source code, to be
146 | distributed under the terms of Sections 1 and 2 above on a medium
147 | customarily used for software interchange; or,
148 |
149 | c) Accompany it with the information you received as to the offer
150 | to distribute corresponding source code. (This alternative is
151 | allowed only for noncommercial distribution and only if you
152 | received the program in object code or executable form with such
153 | an offer, in accord with Subsection b above.)
154 |
155 | The source code for a work means the preferred form of the work for
156 | making modifications to it. For an executable work, complete source
157 | code means all the source code for all modules it contains, plus any
158 | associated interface definition files, plus the scripts used to
159 | control compilation and installation of the executable. However, as a
160 | special exception, the source code distributed need not include
161 | anything that is normally distributed (in either source or binary
162 | form) with the major components (compiler, kernel, and so on) of the
163 | operating system on which the executable runs, unless that component
164 | itself accompanies the executable.
165 |
166 | If distribution of executable or object code is made by offering
167 | access to copy from a designated place, then offering equivalent
168 | access to copy the source code from the same place counts as
169 | distribution of the source code, even though third parties are not
170 | compelled to copy the source along with the object code.
171 |
172 | 4. You may not copy, modify, sublicense, or distribute the Program
173 | except as expressly provided under this License. Any attempt
174 | otherwise to copy, modify, sublicense or distribute the Program is
175 | void, and will automatically terminate your rights under this License.
176 | However, parties who have received copies, or rights, from you under
177 | this License will not have their licenses terminated so long as such
178 | parties remain in full compliance.
179 |
180 | 5. You are not required to accept this License, since you have not
181 | signed it. However, nothing else grants you permission to modify or
182 | distribute the Program or its derivative works. These actions are
183 | prohibited by law if you do not accept this License. Therefore, by
184 | modifying or distributing the Program (or any work based on the
185 | Program), you indicate your acceptance of this License to do so, and
186 | all its terms and conditions for copying, distributing or modifying
187 | the Program or works based on it.
188 |
189 | 6. Each time you redistribute the Program (or any work based on the
190 | Program), the recipient automatically receives a license from the
191 | original licensor to copy, distribute or modify the Program subject to
192 | these terms and conditions. You may not impose any further
193 | restrictions on the recipients' exercise of the rights granted herein.
194 | You are not responsible for enforcing compliance by third parties to
195 | this License.
196 |
197 | 7. If, as a consequence of a court judgment or allegation of patent
198 | infringement or for any other reason (not limited to patent issues),
199 | conditions are imposed on you (whether by court order, agreement or
200 | otherwise) that contradict the conditions of this License, they do not
201 | excuse you from the conditions of this License. If you cannot
202 | distribute so as to satisfy simultaneously your obligations under this
203 | License and any other pertinent obligations, then as a consequence you
204 | may not distribute the Program at all. For example, if a patent
205 | license would not permit royalty-free redistribution of the Program by
206 | all those who receive copies directly or indirectly through you, then
207 | the only way you could satisfy both it and this License would be to
208 | refrain entirely from distribution of the Program.
209 |
210 | If any portion of this section is held invalid or unenforceable under
211 | any particular circumstance, the balance of the section is intended to
212 | apply and the section as a whole is intended to apply in other
213 | circumstances.
214 |
215 | It is not the purpose of this section to induce you to infringe any
216 | patents or other property right claims or to contest validity of any
217 | such claims; this section has the sole purpose of protecting the
218 | integrity of the free software distribution system, which is
219 | implemented by public license practices. Many people have made
220 | generous contributions to the wide range of software distributed
221 | through that system in reliance on consistent application of that
222 | system; it is up to the author/donor to decide if he or she is willing
223 | to distribute software through any other system and a licensee cannot
224 | impose that choice.
225 |
226 | This section is intended to make thoroughly clear what is believed to
227 | be a consequence of the rest of this License.
228 |
229 | 8. If the distribution and/or use of the Program is restricted in
230 | certain countries either by patents or by copyrighted interfaces, the
231 | original copyright holder who places the Program under this License
232 | may add an explicit geographical distribution limitation excluding
233 | those countries, so that distribution is permitted only in or among
234 | countries not thus excluded. In such case, this License incorporates
235 | the limitation as if written in the body of this License.
236 |
237 | 9. The Free Software Foundation may publish revised and/or new versions
238 | of the General Public License from time to time. Such new versions will
239 | be similar in spirit to the present version, but may differ in detail to
240 | address new problems or concerns.
241 |
242 | Each version is given a distinguishing version number. If the Program
243 | specifies a version number of this License which applies to it and "any
244 | later version", you have the option of following the terms and conditions
245 | either of that version or of any later version published by the Free
246 | Software Foundation. If the Program does not specify a version number of
247 | this License, you may choose any version ever published by the Free Software
248 | Foundation.
249 |
250 | 10. If you wish to incorporate parts of the Program into other free
251 | programs whose distribution conditions are different, write to the author
252 | to ask for permission. For software which is copyrighted by the Free
253 | Software Foundation, write to the Free Software Foundation; we sometimes
254 | make exceptions for this. Our decision will be guided by the two goals
255 | of preserving the free status of all derivatives of our free software and
256 | of promoting the sharing and reuse of software generally.
257 |
258 | NO WARRANTY
259 |
260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268 | REPAIR OR CORRECTION.
269 |
270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278 | POSSIBILITY OF SUCH DAMAGES.
279 |
280 | END OF TERMS AND CONDITIONS
--------------------------------------------------------------------------------
/src/representation.ml:
--------------------------------------------------------------------------------
1 | (*
2 | * dashc, client emulator for DASH video streaming
3 | * Copyright (c) 2016-2018, Aleksandr Reviakin, University College Cork
4 | *
5 | * This program is free software; you can redistribute it and/or
6 | * modify it under the terms of the GNU General Public License
7 | * as published by the Free Software Foundation; either version 2
8 | * of the License, or (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program; if not, write to the Free Software
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 | * 02110-1301, USA.
19 | *)
20 |
21 | open Core
22 | open Async
23 | open Cohttp
24 | open Cohttp_async
25 | open Xml
26 |
27 | type media_url =
28 | | Template_url of string
29 | | List_url of string List.t
30 |
31 | type representation = {
32 | width : int;
33 | height : int;
34 | bandwidth : int;
35 | media : media_url;
36 | startNumber : int;
37 | segment_duration : int;
38 | }
39 |
40 | let calculate_average_chunk_size_per_repr chunk_sizes_per_repr =
41 | let rec hashtbl_iteri ~index ~average_chunk_size_per_repr =
42 | match index > Hashtbl.length chunk_sizes_per_repr with
43 | | true -> List.rev average_chunk_size_per_repr
44 | | false ->
45 | let sum_of_segm_sizes =
46 | List.length (Hashtbl.find_exn chunk_sizes_per_repr index) in
47 | let mean =
48 | sum_of_segm_sizes / List.length (Hashtbl.find_exn chunk_sizes_per_repr index) in
49 | hashtbl_iteri
50 | ~index:(index + 1)
51 | ~average_chunk_size_per_repr:(mean :: average_chunk_size_per_repr)
52 | in
53 | hashtbl_iteri ~index:1 ~average_chunk_size_per_repr:[]
54 |
55 | let link_of_media media segmentNumber =
56 | match media with
57 | | Template_url media ->
58 | (String.chop_suffix_exn media
59 | ~suffix:"$Number$.m4s") ^ (string_of_int segmentNumber) ^ ".m4s"
60 | | List_url list_url -> List.nth_exn list_url (segmentNumber - 1)
61 |
62 | let media_presentation_duration_from_mpd (mpd : xml) =
63 | match mpd with
64 | | Element ("MPD", attrs, _) ->
65 | let duration_str = (Caml.List.assoc "mediaPresentationDuration" attrs) in
66 | begin match
67 | (String.split_on_chars
68 | duration_str
69 | ~on:['P'; 'Y'; 'M'; 'W'; 'D'; 'T'; 'H'; 'M'; 'S']) with
70 | | [_; _; sec; _] -> float_of_string sec
71 | | [_; _; min; sec; _] -> (float_of_string min *. 60.) +. float_of_string sec
72 | | [_; _; hours; min; sec; _] ->
73 | (float_of_string hours *. 60. *. 60.) +.
74 | (float_of_string min *. 60.) +.
75 | float_of_string sec
76 | | [_; days; _; hours; min; sec; _] ->
77 | (float_of_string days *. 24. *. 60. *. 60.) +.
78 | (float_of_string hours *. 60. *. 60.) +.
79 | (float_of_string min *. 60.) +. float_of_string sec
80 | | [_; months; days; _; hours; min; sec; _] ->
81 | (float_of_string months *.30. *. 24. *. 60. *. 60.) +.
82 | (float_of_string days *. 24. *. 60. *. 60.) +.
83 | (float_of_string hours *. 60. *. 60.) +.
84 | (float_of_string min *. 60.) +. float_of_string sec
85 | | [_; years; months; days; _; hours; min; sec; _] ->
86 | (float_of_string years *. 365. *. 24. *. 60. *. 60.) +.
87 | (float_of_string months *.30. *. 24. *. 60. *. 60.) +.
88 | (float_of_string days *. 24. *. 60. *. 60.) +.
89 | (float_of_string hours *. 60. *. 60.) +.
90 | (float_of_string min *. 60.) +. float_of_string sec
91 | | _ -> failwith "Incorrect mediaPresentationDuration attribute"
92 | end
93 | | _ -> failwith "mediaPresentationDuration attribute was not found"
94 |
95 | let get_last_segment_index xml_str segment_duration last_segment_index =
96 | let duration = media_presentation_duration_from_mpd xml_str in
97 | let total_number_of_segments =
98 | int_of_float (round ~dir:`Up (duration /. float_of_int segment_duration)) in
99 | match last_segment_index with
100 | | Some last_segment_index ->
101 | if last_segment_index < total_number_of_segments then last_segment_index
102 | else total_number_of_segments
103 | | None -> total_number_of_segments
104 |
105 | let repr_table_from_mpd (mpd : xml) =
106 | let adaptationSets = Xml.fold (fun acc x ->
107 | match x with
108 | | Element ("Period", _, clist) -> clist
109 | (* skip ProgramInformation attribute *)
110 | | _ -> acc
111 | ) [] mpd in
112 | let total_number_of_repr_per_adaptation_set =
113 | List.fold adaptationSets ~init:[] ~f:(fun acc adaptationSetTag ->
114 | let repr_total_number = Xml.fold (fun acc xml_ ->
115 | acc +
116 | match xml_ with
117 | | Element ("Representation", _, _) -> 1
118 | | _ -> 0
119 | ) 0 adaptationSetTag in
120 | acc @ [repr_total_number]
121 | ) in
122 | let total_number_of_repr =
123 | List.fold
124 | total_number_of_repr_per_adaptation_set ~init:0 ~f:(fun acc x -> acc + x) in
125 | let representations : (int, representation) Hashtbl.t =
126 | Hashtbl.Poly.create ~size:total_number_of_repr () in
127 | let representations_not_sorted = ref [] in
128 | List.iter adaptationSets ~f:(fun adaptationSetTag ->
129 | Xml.iter (fun nextChild ->
130 | match nextChild with
131 | | Element ("Representation", attrs, clist) ->
132 | (* timescale attribute is used in template based MPD in SegmentTemplate tag *)
133 | let timescale = match (List.hd_exn clist) with
134 | | Element ("SegmentTemplate", attrs, _) ->
135 | int_of_string @@ Caml.List.assoc "timescale" attrs;
136 | | _ -> 0;
137 | in
138 | (* duration attribute is used in template based MPD in SegmentTemplate tag *)
139 | let duration = match (List.hd_exn clist) with
140 | | Element ("SegmentTemplate", attrs, _) ->
141 | int_of_string @@ Caml.List.assoc "duration" attrs;
142 | | _ -> 0;
143 | in
144 | let width = int_of_string (Caml.List.assoc "width" attrs) in
145 | let height = int_of_string (Caml.List.assoc "height" attrs) in
146 | let bandwidth = int_of_string (Caml.List.assoc "bandwidth" attrs) in
147 | let startNumber = match (List.hd_exn clist) with
148 | | Element ("SegmentTemplate", attrs, _) ->
149 | int_of_string @@ Caml.List.assoc "startNumber" attrs;
150 | | _ -> 1;
151 | in
152 | let media, duration_new =
153 | List.fold ~init:(Template_url "", 0) clist ~f:(fun acc x ->
154 | match x with
155 | | Element ("SegmentTemplate", attrs, _) ->
156 | Template_url (Caml.List.assoc "media" attrs), 0
157 | | Element ("SegmentList", attrs, clist) ->
158 | let duration_from_segment_list =
159 | int_of_string @@ Caml.List.assoc "duration" attrs in
160 | let media_url_list = List.fold ~init:[] clist ~f:(fun acc x ->
161 | match x with
162 | | Element ("SegmentURL", attrs, _) ->
163 | Caml.List.assoc "media" attrs :: acc
164 | | _ -> acc
165 | ) in
166 | List_url (List.rev media_url_list), duration_from_segment_list
167 | | _ -> acc
168 | ) in
169 | let duration_final =
170 | if timescale = 0 || duration = 0 then duration_new else duration / timescale in
171 | representations_not_sorted :=
172 | {
173 | width = width;
174 | height = height;
175 | bandwidth = bandwidth;
176 | (* go inside SegmentTemplate tag as well *)
177 | media = media;
178 | (* there is no startNumber in the old MPD standard,
179 | at least in our examples *)
180 | startNumber = startNumber;
181 | segment_duration = duration_final;
182 | } :: !representations_not_sorted;
183 | | _ -> ()
184 | ) adaptationSetTag;
185 | );
186 | representations_not_sorted := List.sort ~compare:(fun x y ->
187 | if x.bandwidth > y.bandwidth then 1
188 | else if x.bandwidth < y.bandwidth then -1
189 | else 0) !representations_not_sorted;
190 | List.iteri !representations_not_sorted ~f:(fun idx repr ->
191 | Hashtbl.add_exn representations
192 | ~key:(idx + 1)
193 | ~data:repr
194 | );
195 | representations
196 |
197 | let download_chunk_sizes_per_repr ?conn ~root_link ~representations ~last_segment_index =
198 | let chunk_sizes_per_repr : (int, int List.t) Hashtbl.t =
199 | Hashtbl.Poly.create ~size:10 () in
200 | let rec download_next_chunk ~curr_index ~media ~chunks =
201 | match curr_index > last_segment_index with
202 | | true -> return @@ List.rev chunks
203 | | false ->
204 | Client.head
205 | ?conn:conn (Uri.of_string (root_link ^ (link_of_media media curr_index)))
206 | >>= fun resp ->
207 | match (Header.get (Response.headers resp) "content-length") with
208 | | Some cont_len ->
209 | download_next_chunk
210 | ~curr_index:(curr_index + 1)
211 | ~media:media ~chunks:(int_of_string cont_len :: chunks)
212 | | None -> failwith "Server does not include content-length field"
213 | in
214 | let rec download_next_repr ~curr_index =
215 | match curr_index < (Hashtbl.length representations + 1) with
216 | | true ->
217 | let curr_repr = Hashtbl.find_exn representations curr_index in
218 | print_string "Starting of downloading headers for representation ";
219 | print_int curr_index;
220 | print_endline "";
221 | download_next_chunk ~curr_index:1 ~media:curr_repr.media ~chunks:[]
222 | >>= fun chunk_list ->
223 | Hashtbl.add_exn chunk_sizes_per_repr ~key:curr_index ~data:chunk_list;
224 | download_next_repr ~curr_index:(curr_index + 1)
225 | | false -> Deferred.unit
226 | in
227 | download_next_repr ~curr_index:1 >>= fun () -> return chunk_sizes_per_repr
228 |
229 | let open_connection link = function
230 | | true -> Client.Net.connect_uri (Uri.of_string link)
231 | >>= fun (ic,oc) -> return (Some (ic,oc))
232 | | false -> return None
233 |
234 | let make_segment_size_file ~link ~persist =
235 | try_with (fun () ->
236 | let _, segmlist_mpd = String.rsplit2_exn link ~on:'/' in
237 | let outc = Out_channel.create @@ "segmentlist_" ^ segmlist_mpd ^ ".txt" in
238 | open_connection link persist >>= fun conn ->
239 | Client.get ?conn:conn (Uri.of_string link) >>= fun (_, body) ->
240 | body |> Cohttp_async.Body.to_string >>= fun body ->
241 | let mpd = Xml.parse_string body in
242 | let representations : (int, representation) Hashtbl.t = repr_table_from_mpd mpd in
243 | let segment_duration = (Hashtbl.find_exn representations 1).segment_duration in
244 | let last_segment_index = get_last_segment_index mpd segment_duration None in
245 | let root_link, _ = String.rsplit2_exn link ~on:'/' in
246 | let root_link = root_link ^ "/" in
247 | download_chunk_sizes_per_repr
248 | ?conn:conn
249 | ~root_link:root_link
250 | ~representations:representations
251 | ~last_segment_index:last_segment_index
252 | >>= fun chunk_sizes_per_repr ->
253 | print_endline "All segment sizes were successfully downloaded";
254 | let rec hashtbl_iteri index =
255 | let segm_list = Hashtbl.find_exn chunk_sizes_per_repr index in
256 | Out_channel.output_string outc @@ string_of_int index ^ " ";
257 | Out_channel.output_string outc @@ string_of_int @@ List.length segm_list;
258 | Out_channel.newline outc;
259 | List.iter segm_list ~f:(fun data ->
260 | Out_channel.output_string outc @@ string_of_int data;
261 | Out_channel.newline outc;
262 | );
263 | match index = Hashtbl.length chunk_sizes_per_repr with
264 | | true -> ()
265 | | false -> hashtbl_iteri (index + 1)
266 | in
267 | hashtbl_iteri 1;
268 | Out_channel.close outc;
269 | Deferred.unit
270 | )
271 | >>| function
272 | | Ok () -> ()
273 | | Error e ->
274 | match (String.is_substring (Exn.to_string e) ~substring:"connection attempt timeout") with
275 | | true -> print_endline @@ "Connection attempt timeout (10 sec default) to " ^ link
276 | | false -> print_endline @@ Exn.to_string e
277 |
278 | (* read segment sizes from segmentlist%mpd%.txt compatible file
279 | which can be created by download_chunk_sizes_per_repr function,
280 | if remote_string is passed then
281 | it means the files was downloaded from remote location and passed as a string *)
282 | let read_segment_size_file
283 | ?remote_string ~link ~number_of_representations ~last_segment_index =
284 | let chunk_sizes_per_repr : (int, int List.t) Hashtbl.t =
285 | Hashtbl.Poly.create ~size:10 () in
286 | let string_file =
287 | match remote_string with
288 | | Some line -> ref line
289 | | None -> ref ""
290 | in
291 | let possible_in_channel =
292 | match remote_string with
293 | | Some _ -> None
294 | | None ->
295 | let _, segmlist_mpd = String.rsplit2_exn link ~on:'/' in
296 | Some (In_channel.create @@ "segmentlist_" ^ segmlist_mpd ^ ".txt")
297 | in
298 | let rec read_next_repr () =
299 | let info_line =
300 | match possible_in_channel with
301 | | Some in_channel -> In_channel.input_line in_channel
302 | | None ->
303 | (* think about moving to option function instead of exception variant *)
304 | let hd_line, tl_line = String.lsplit2_exn !string_file ~on:'\n' in
305 | string_file := tl_line;
306 | Some hd_line
307 | in
308 | let (repr_index, total_segment_number) =
309 | match info_line with
310 | | Some line -> let separate_values = String.split line ~on:' ' in
311 | (int_of_string @@
312 | List.hd_exn separate_values, int_of_string @@ List.last_exn separate_values)
313 | | None -> failwith "Incorrect segmentlist.txt"
314 | in
315 | (* check if the passed parameter last_segment_index is higher than
316 | the number of segment sizes in file then something is wrong *)
317 | if total_segment_number < last_segment_index then
318 | failwith "total segment number in file is less than \
319 | the expected number of segments for playing";
320 | let rec read_next_segm_list ~curr_index ~chunks =
321 | match curr_index > total_segment_number with
322 | | true -> List.rev chunks
323 | | false ->
324 | let raw_string =
325 | match possible_in_channel with
326 | | Some in_channel -> In_channel.input_line in_channel
327 | | None ->
328 | (* think about moving to option function instead of exception variant *)
329 | let hd_line, tl_line = String.lsplit2_exn !string_file ~on:'\n' in
330 | string_file := tl_line;
331 | Some hd_line
332 | in
333 | let segm_size =
334 | match raw_string with
335 | | Some line -> int_of_string line
336 | | None -> failwith "Incorrect segmentlist.txt"
337 | in
338 | read_next_segm_list ~curr_index:(curr_index + 1) ~chunks:(segm_size :: chunks)
339 | in
340 | let chunk_list = read_next_segm_list ~curr_index:1 ~chunks:[] in
341 | Hashtbl.add_exn chunk_sizes_per_repr ~key:repr_index ~data:chunk_list;
342 | match repr_index < number_of_representations with
343 | | true -> read_next_repr ()
344 | | false -> ()
345 | in
346 | read_next_repr ();
347 | let () =
348 | match possible_in_channel with
349 | | Some in_channel -> In_channel.close in_channel
350 | | None -> ()
351 | in
352 | return chunk_sizes_per_repr
353 |
354 | (* this function was made for debug purposes, delete it in future *)
355 | let print_reprs representations =
356 | Hashtbl.iteri representations ~f:(fun ~key ~data ->
357 | print_int key;
358 | print_endline "";
359 | print_int data.width;
360 | print_endline "";
361 | print_int data.height;
362 | print_endline "";
363 | print_int data.bandwidth;
364 | print_endline "";
365 | let media_template =
366 | match data.media with
367 | | Template_url media -> media
368 | | List_url _ -> "list_based_mpd"
369 | in
370 | print_string media_template;
371 | print_endline "";
372 | print_int data.startNumber;
373 | print_endline "";
374 | print_int data.segment_duration;
375 | print_endline "";
376 | print_endline "";
377 | )
378 |
--------------------------------------------------------------------------------
/src/playback.ml:
--------------------------------------------------------------------------------
1 | (*
2 | * dashc, client emulator for DASH video streaming
3 | * Copyright (c) 2016-2018, Aleksandr Reviakin, University College Cork
4 | *
5 | * This program is free software; you can redistribute it and/or
6 | * modify it under the terms of the GNU General Public License
7 | * as published by the Free Software Foundation; either version 2
8 | * of the License, or (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program; if not, write to the Free Software
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 | * 02110-1301, USA.
19 | *)
20 |
21 | open Core
22 | open Async
23 | open Cohttp_async
24 |
25 | open Representation
26 | open Segm_result
27 | open Adapt_algo
28 |
29 | let supported_algorithms = ["conv"; "bba-0"; "bba-1"; "bba-2"; "arbiter"]
30 | let algorithms_with_required_chunk_sizes = ["bba-1"; "bba-2"; "arbiter"]
31 | let log_columns = ["Seg_#"; "Arr_time"; "Del_Time"; "Stall_Dur";
32 | "Rep_Level"; "Del_Rate"; "Act_Rate"; "Byte_Size"; "Buff_Level"]
33 |
34 | let is_buffer_full buffer_size maxbuf segment_duration =
35 | buffer_size > (maxbuf -. float_of_int segment_duration)
36 |
37 | let next_repr_number representations results last_segment_index = function
38 | | Conv ->
39 | Conv.next_representation_level ~representations:representations ~results:results
40 | | BBA_0 algo ->
41 | BBA_0.next_representation_level
42 | ~algo:algo ~representations:representations ~results:results
43 | | BBA_1 algo ->
44 | BBA_1.next_representation_level
45 | ~algo:algo ~representations:representations ~results:results last_segment_index
46 | | BBA_2 algo ->
47 | BBA_2.next_representation_level
48 | ~algo:algo ~representations:representations ~results:results last_segment_index
49 | | ARBITER algo -> ARBITER.next_representation_level
50 | ~algo:algo ~representations:representations ~results:results last_segment_index
51 |
52 | let try_request_next_chunk ?conn link_to_next_chunk =
53 | try_with (fun () ->
54 | Client.get ?conn:conn (Uri.of_string link_to_next_chunk)
55 | >>| fun (resp, body) -> Some (resp, body)
56 | )
57 | >>| function
58 | | Ok resp_and_body -> resp_and_body
59 | | Error e -> begin
60 | match (String.is_substring (Exn.to_string e) ~substring:"Connection closed by remote host") with
61 | | true -> None
62 | | false -> failwith @@ Exn.to_string e
63 | end
64 |
65 | let rec playback
66 | ?conn
67 | ?outc
68 | ~root_link
69 | ~representations
70 | ~results
71 | ~absolute_start_time
72 | ~buffer_size
73 | ~segment_number
74 | ~initb
75 | ~maxbuf
76 | ~adapt_method
77 | ~last_segment_index
78 | ~segment_duration
79 | ~link
80 | ~persist =
81 | match is_buffer_full buffer_size maxbuf segment_duration with
82 | | true ->
83 | Clock.after (Time.Span.of_sec 0.1) >>= fun () ->
84 | playback
85 | ?conn:conn
86 | ?outc:outc
87 | ~root_link:root_link
88 | ~representations:representations
89 | ~results:results
90 | ~absolute_start_time:absolute_start_time
91 | ~buffer_size:(buffer_size -. 0.1)
92 | ~segment_number:(segment_number)
93 | ~initb:initb
94 | ~maxbuf:maxbuf
95 | ~adapt_method:adapt_method
96 | ~last_segment_index:last_segment_index
97 | ~segment_duration:segment_duration
98 | ~link:link
99 | ~persist:persist
100 | | false ->
101 | let start = Time.now () in
102 | let next_repr_number =
103 | next_repr_number representations results last_segment_index adapt_method in
104 | let next_repr = Hashtbl.find_exn representations next_repr_number in
105 | let link_to_next_chunk =
106 | (root_link ^ (link_of_media next_repr.media segment_number)) in
107 | try_request_next_chunk ?conn:conn link_to_next_chunk
108 | >>= fun resp_and_body -> begin
109 | match resp_and_body with
110 | | None -> begin
111 | open_connection link persist >>= fun conn ->
112 | try_request_next_chunk ?conn:conn link_to_next_chunk
113 | >>= fun resp_and_body_2 ->
114 | match resp_and_body_2 with
115 | | None -> failwith "The second disconnect in a raw"
116 | | Some (resp, body) ->
117 | print_endline "New connection was opened";
118 | return (resp, body, conn)
119 | end
120 | | Some (resp, body) -> return (resp, body, conn)
121 | end
122 | >>= fun (resp, body, conn) ->
123 | body |> Cohttp_async.Body.drain >>= fun () ->
124 | let content_length = match resp |> Cohttp_async.Response.headers |> Cohttp.Header.get_content_range with
125 | | Some content_length -> Int64.to_int_exn content_length
126 | | None -> 0
127 | in
128 | let current_time = Time.now () in
129 | let time_diff = Time.diff current_time start in
130 | let diff = Time.Span.to_sec time_diff in
131 | let initb = match buffer_size >= (initb *. float_of_int segment_duration) with
132 | | true -> 0.
133 | | false -> initb
134 | in
135 | let buf = match buffer_size > (initb *. float_of_int segment_duration) with
136 | | true -> begin
137 | match buffer_size <= diff with
138 | | true -> 0.
139 | | false -> buffer_size -. diff
140 | end
141 | | false -> buffer_size
142 | in
143 | let stall = match buffer_size > (initb *. float_of_int segment_duration)
144 | && buffer_size < diff with
145 | (* check this condition for startup phaze *)
146 | | true -> int_of_float @@ (diff -. buffer_size) *. 1000.
147 | | false -> 0
148 | in
149 | let results =
150 | {
151 | segment_number = segment_number;
152 | arrival_time =
153 | int_of_float @@ Time.Span.to_us (Time.diff current_time absolute_start_time);
154 | time_for_delivery = int_of_float @@ Time.Span.to_us time_diff;
155 | stall_dur = stall;
156 | representation_rate = next_repr.bandwidth;
157 | actual_representation_rate = 8 * content_length / segment_duration;
158 | segment_size = content_length;
159 | buffer_level_in_momentum = (buf +. float_of_int segment_duration);
160 | repr_level = next_repr_number;
161 | } :: results
162 | in
163 | print_result (List.hd_exn results) None;
164 | let () = match outc with
165 | | Some outc -> print_result (List.hd_exn results) (Some outc)
166 | | None -> ()
167 | in
168 | match segment_number >= last_segment_index with
169 | | true -> Deferred.unit
170 | | false ->
171 | playback
172 | ?conn:conn
173 | ?outc:outc
174 | ~root_link:root_link
175 | ~representations:representations
176 | ~results:results
177 | ~absolute_start_time:absolute_start_time
178 | ~buffer_size:(buf +. float_of_int segment_duration)
179 | ~segment_number:(segment_number + 1)
180 | ~initb:initb
181 | ~maxbuf:maxbuf
182 | ~adapt_method:adapt_method
183 | ~last_segment_index:last_segment_index
184 | ~segment_duration:segment_duration
185 | ~link:link
186 | ~persist:persist
187 |
188 | let check_input_algorithm_existence input_alg =
189 | match List.exists supported_algorithms ~f:(fun x -> x = input_alg) with
190 | | true -> ()
191 | | false ->
192 | failwith "The chosen adaptation algorithm is not supported, please check help"
193 |
194 | let create_log_channel logname log_folder v_number r_number = function
195 | | true ->
196 | begin match logname, v_number with
197 | | "now", _ ->
198 | begin match log_folder with
199 | | "" ->
200 | Some (Out_channel.create @@
201 | Time.to_filename_string (Time.now ()) ~zone:Time.Zone.utc)
202 | | _ ->
203 | Some (Out_channel.create
204 | (log_folder ^ "/" ^ Time.to_filename_string (Time.now ()) ~zone:Time.Zone.utc))
205 | end
206 | | _, "-V99" ->
207 | begin match log_folder with
208 | | "" -> Some (Out_channel.create logname)
209 | | _ -> Some (Out_channel.create (log_folder ^ "/" ^ logname))
210 | end
211 | | _, _ ->
212 | begin match log_folder with
213 | | "" ->
214 | Some (Out_channel.create
215 | ("trace-" ^ Time.to_filename_string (Time.now ())
216 | ~zone:Time.Zone.utc ^ "-" ^ logname ^ v_number ^ r_number ^ ".res"))
217 | | _ ->
218 | Some (Out_channel.create
219 | (log_folder ^ "/" ^ "trace-" ^ Time.to_filename_string (Time.now ())
220 | ~zone:Time.Zone.utc ^ "-" ^ logname ^ v_number ^ r_number ^ ".res"))
221 | end
222 | end
223 | | false -> None
224 |
225 | let check_requirement_for_chunk_sizes input_alg =
226 | List.exists algorithms_with_required_chunk_sizes ~f:(fun x -> x = input_alg)
227 |
228 | let chunk_sizes
229 | adapt_alg
230 | segm_size_from_file
231 | link representations
232 | last_segment_index
233 | root_link
234 | segmlist_mpd
235 | conn =
236 | match check_requirement_for_chunk_sizes adapt_alg with
237 | | true ->
238 | let chunk_sizes_per_repr = match segm_size_from_file with
239 | | "local" ->
240 | read_segment_size_file
241 | ?remote_string:None
242 | ~link:link
243 | ~number_of_representations:(Hashtbl.length representations)
244 | ~last_segment_index:last_segment_index
245 | | "remote" ->
246 | let segm_list_remote_link =
247 | root_link ^ "/" ^ "segmentlist_" ^ segmlist_mpd ^ ".txt" in
248 | Client.get ?conn:conn (Uri.of_string segm_list_remote_link)
249 | >>= fun (_, body) ->
250 | body |> Cohttp_async.Body.to_string >>= fun body_string ->
251 | read_segment_size_file
252 | ~remote_string:body_string
253 | ~link:link
254 | ~number_of_representations:(Hashtbl.length representations)
255 | ~last_segment_index:last_segment_index
256 | | _ ->
257 | download_chunk_sizes_per_repr
258 | ?conn:None
259 | ~root_link:root_link
260 | ~representations:representations
261 | ~last_segment_index:last_segment_index
262 | in
263 | chunk_sizes_per_repr >>| fun chunk_sizes_per_repr ->
264 | let average_chunk_size_per_repr =
265 | calculate_average_chunk_size_per_repr chunk_sizes_per_repr in
266 | (chunk_sizes_per_repr, average_chunk_size_per_repr)
267 | | false -> return (Hashtbl.Poly.create ~size:10 (), [])
268 |
269 | let new_adapt_method chunk_sizes_per_repr average_chunk_size_per_repr = function
270 | | "conv" -> return Conv
271 | | "bba-0" -> return @@ BBA_0 { BBA_0.maxb = 240. }
272 | | "bba-1" ->
273 | return @@ BBA_1 {
274 | BBA_1.maxb = 240.;
275 | BBA_1.chunk_sizes_per_repr = chunk_sizes_per_repr;
276 | average_chunk_size_per_repr = average_chunk_size_per_repr
277 | }
278 | | "bba-2" ->
279 | return @@ BBA_2 {
280 | BBA_1.maxb = 240.;
281 | BBA_1.chunk_sizes_per_repr = chunk_sizes_per_repr;
282 | average_chunk_size_per_repr = average_chunk_size_per_repr
283 | }
284 | | "arbiter" ->
285 | return @@ ARBITER {
286 | ARBITER.maxb = 60.;
287 | ARBITER.chunk_sizes_per_repr = chunk_sizes_per_repr;
288 | }
289 | | _ -> failwith "The chosen adaptation algorithm is not supported"
290 |
291 | let print_log_header_on_screen =
292 | List.iteri log_columns
293 | ~f:(fun idx x -> print_string x;
294 | match not @@ phys_equal idx (List.length log_columns - 1) with
295 | | true -> print_string " "
296 | | false -> print_endline ""
297 | )
298 |
299 | let print_log_header_into_file = function
300 | | Some outc -> List.iteri log_columns
301 | ~f:(fun idx x -> Out_channel.output_string outc x;
302 | match not @@ phys_equal idx (List.length log_columns - 1) with
303 | | true -> Out_channel.output_string outc " "
304 | | false -> Out_channel.newline outc
305 | );
306 | | None -> ()
307 |
308 | let get_max_muf maxbuf = function
309 | | BBA_0 algo -> int_of_float algo.BBA_0.maxb
310 | | BBA_1 algo -> int_of_float algo.BBA_1.maxb
311 | | BBA_2 algo -> int_of_float algo.BBA_1.maxb
312 | | ARBITER algo -> int_of_float algo.ARBITER.maxb
313 | | _ -> maxbuf
314 |
315 | let run_client
316 | ~link
317 | ~adapt_alg
318 | ~initb
319 | ~maxbuf
320 | ~persist
321 | ~turnlogon
322 | ~logname
323 | ~v_number
324 | ~r_number
325 | ~log_folder
326 | ~last_segment_index
327 | ~segm_size_from_file =
328 | try_with (fun () ->
329 | check_input_algorithm_existence adapt_alg;
330 | begin
331 | match (log_folder = "") with
332 | | true -> return ()
333 | | false -> Deferred.ignore @@ Sys.command @@ "mkdir -p " ^ log_folder
334 | end
335 | >>= fun _ ->
336 | let outc =
337 | create_log_channel
338 | logname log_folder ("-V" ^ v_number) ("-R" ^ r_number) turnlogon in
339 | open_connection link persist >>= fun conn ->
340 | Client.get ?conn:conn (Uri.of_string link) >>= fun (_, body) ->
341 | body |> Cohttp_async.Body.to_string >>= fun body ->
342 | let mpd = Xml.parse_string body in
343 | let representations : (int, representation) Hashtbl.t = repr_table_from_mpd mpd in
344 | let segment_duration = (Hashtbl.find_exn representations 1).segment_duration in
345 | let last_segment_index =
346 | get_last_segment_index mpd segment_duration last_segment_index in
347 | let root_link, segmlist_mpd = String.rsplit2_exn link ~on:'/' in
348 | let root_link = root_link ^ "/" in
349 | chunk_sizes
350 | adapt_alg
351 | segm_size_from_file
352 | link
353 | representations
354 | last_segment_index
355 | root_link
356 | segmlist_mpd conn
357 | >>= fun (chunk_sizes_per_repr, average_chunk_size_per_repr) ->
358 | new_adapt_method chunk_sizes_per_repr average_chunk_size_per_repr adapt_alg
359 | >>= fun adapt_method ->
360 | print_log_header_on_screen;
361 | print_log_header_into_file outc;
362 | let maxbuf = get_max_muf maxbuf adapt_method in
363 | playback
364 | ?conn:conn
365 | ?outc:outc
366 | ~root_link:root_link
367 | ~representations:representations
368 | ~results:[]
369 | ~absolute_start_time:(Time.now ())
370 | ~buffer_size:0.
371 | ~segment_number:1
372 | ~initb:(float_of_int initb)
373 | ~maxbuf:(float_of_int maxbuf)
374 | ~adapt_method:adapt_method
375 | ~last_segment_index:last_segment_index
376 | ~segment_duration:segment_duration
377 | ~link:link
378 | ~persist:persist
379 | >>= fun () ->
380 | match outc with
381 | | Some outc -> Out_channel.close outc; Deferred.unit
382 | | None -> Deferred.unit
383 | )
384 | >>| function
385 | | Ok () -> ()
386 | | Error e -> begin
387 | match (String.is_substring (Exn.to_string e) ~substring:"connection attempt timeout") with
388 | | true -> print_endline @@ "Connection attempt timeout (10 sec default) to " ^ link
389 | | false -> print_endline @@ Exn.to_string e
390 | end
391 |
392 | let play =
393 | Command.async
394 | ~summary:"Dash simulation client"
395 | ~readme:(fun () -> "Usage example: ./dashc.native play http://10.0.0.1/bbb.mpd \
396 | [-adapt conv] [-initb 2] [-maxbuf 60] [-persist true] \
397 | [-turnlogon true] [-logname now] [-v 20] [-subfolder qwe]")
398 | (
399 | let open Command.Let_syntax in
400 | [%map_open
401 | let link = anon ("link_to_mpd" %: string)
402 | and adapt_alg = flag "-adapt" (optional_with_default "conv" string)
403 | ~doc:" adaptation algorithm [conv; bba-0; bba-1; bba-2; arbiter]"
404 | and initb = flag "-initb" (optional_with_default 2 int)
405 | ~doc:" initial buffer in segments"
406 | and maxbuf = flag "-maxbuf" (optional_with_default 60 int)
407 | ~doc:" maximum buffer size"
408 | and persist = flag "-persist" (optional_with_default true bool)
409 | ~doc:" persistent connection"
410 | and turnlogon = flag "-turnlogon" (optional_with_default false bool)
411 | ~doc:" turn on logging to file"
412 | and logname = flag "-logname" (optional_with_default "now" string)
413 | ~doc:" name of the log file (\"now\" means usage of the current time stamp \
414 | and it is used by default)"
415 | and v_number = flag "-v" (optional_with_default "99" string)
416 | ~doc:" video number according to StreamTraceAnalysis.py"
417 | and r_number = flag "-r" (optional_with_default "0" string)
418 | ~doc:" the run (R) number for the trace file"
419 | and subfolder = flag "-subfolder" (optional_with_default "" string)
420 | ~doc:" subfolder for the file"
421 | and last_segment_index = flag "-lastsegmindex" (optional int)
422 | ~doc:" last segment index for the playback"
423 | and gensegmfile = flag "-gensegmfile" (optional_with_default false bool)
424 | ~doc:" generate segmentlist_%mpd_name%.txt file only \
425 | (it will be rewritten if exists)"
426 | and segm_size_from_file =
427 | flag "-segmentlist" (optional_with_default "head" string)
428 | ~doc:" get segment sizes from \n \
429 | local - local segmentlist_%mpd_name%.txt file \n \
430 | remote - download remote segmentlist_%mpd_name%.txt file \
431 | from the same folder where mpd is located \n \
432 | head - get segment size by sending head requests \
433 | before playing for each segment per representation \
434 | usage of head requests works only with non-persistent connection so far \
435 | and it it set by default (for this operation)"
436 | in
437 | fun () -> match gensegmfile with
438 | | true -> make_segment_size_file ~link:link ~persist:false
439 | | false -> run_client
440 | ~link:link
441 | ~adapt_alg:adapt_alg
442 | ~initb:initb
443 | ~maxbuf:maxbuf
444 | ~persist:persist
445 | ~turnlogon:turnlogon
446 | ~logname:logname
447 | ~v_number:v_number
448 | ~r_number:r_number
449 | ~log_folder:subfolder
450 | ~last_segment_index:last_segment_index
451 | ~segm_size_from_file:segm_size_from_file
452 | ]
453 | )
--------------------------------------------------------------------------------
/test_mpd/segmentlist_bbb_enc_10min_x264_dash.mpd.txt:
--------------------------------------------------------------------------------
1 | 1 150
2 | 124131
3 | 138452
4 | 136272
5 | 175744
6 | 49761
7 | 99300
8 | 157812
9 | 131993
10 | 109544
11 | 85085
12 | 101129
13 | 117811
14 | 145859
15 | 126221
16 | 49319
17 | 93381
18 | 139046
19 | 106398
20 | 154815
21 | 231456
22 | 89141
23 | 52758
24 | 80003
25 | 55815
26 | 85567
27 | 107978
28 | 128746
29 | 120316
30 | 147622
31 | 197369
32 | 153841
33 | 108542
34 | 91167
35 | 123696
36 | 128476
37 | 145513
38 | 83688
39 | 126762
40 | 136070
41 | 58300
42 | 149092
43 | 100457
44 | 106420
45 | 82978
46 | 169095
47 | 198881
48 | 187996
49 | 62005
50 | 175355
51 | 92519
52 | 85674
53 | 102647
54 | 212208
55 | 166224
56 | 101173
57 | 59654
58 | 122133
59 | 74678
60 | 90066
61 | 71230
62 | 156976
63 | 213902
64 | 126038
65 | 123918
66 | 113887
67 | 106868
68 | 98271
69 | 142557
70 | 146420
71 | 137728
72 | 92623
73 | 51756
74 | 48157
75 | 183199
76 | 85902
77 | 149177
78 | 129488
79 | 117151
80 | 187594
81 | 130684
82 | 158231
83 | 154983
84 | 130659
85 | 97169
86 | 79760
87 | 100818
88 | 182553
89 | 166768
90 | 147597
91 | 168084
92 | 98164
93 | 131470
94 | 94703
95 | 70462
96 | 73438
97 | 70636
98 | 38830
99 | 77935
100 | 70779
101 | 113032
102 | 67672
103 | 88600
104 | 164108
105 | 133955
106 | 117794
107 | 278099
108 | 154731
109 | 147220
110 | 209621
111 | 116146
112 | 170142
113 | 124085
114 | 76111
115 | 66245
116 | 96603
117 | 71156
118 | 133932
119 | 97972
120 | 80872
121 | 71293
122 | 55207
123 | 54280
124 | 82662
125 | 41633
126 | 46782
127 | 71449
128 | 85786
129 | 114673
130 | 104554
131 | 107204
132 | 98200
133 | 106080
134 | 107188
135 | 176259
136 | 201284
137 | 188828
138 | 193931
139 | 184609
140 | 230136
141 | 169187
142 | 92699
143 | 50942
144 | 47254
145 | 55508
146 | 41570
147 | 44481
148 | 41401
149 | 78641
150 | 97570
151 | 15090
152 | 2 150
153 | 199592
154 | 227268
155 | 211531
156 | 281170
157 | 69286
158 | 157538
159 | 261058
160 | 214866
161 | 172467
162 | 137855
163 | 148619
164 | 182445
165 | 238693
166 | 197565
167 | 71321
168 | 135004
169 | 205866
170 | 156832
171 | 238562
172 | 387392
173 | 139986
174 | 79284
175 | 132230
176 | 85152
177 | 133407
178 | 169862
179 | 199517
180 | 186790
181 | 235286
182 | 320878
183 | 241648
184 | 172739
185 | 145979
186 | 195801
187 | 202632
188 | 227462
189 | 135537
190 | 201603
191 | 212453
192 | 91957
193 | 240697
194 | 158689
195 | 168712
196 | 130492
197 | 268999
198 | 312896
199 | 294032
200 | 99671
201 | 281705
202 | 149238
203 | 136704
204 | 163010
205 | 326778
206 | 268616
207 | 160353
208 | 98458
209 | 195911
210 | 121965
211 | 144829
212 | 114169
213 | 263361
214 | 336446
215 | 195429
216 | 198418
217 | 179839
218 | 169847
219 | 158569
220 | 232316
221 | 238198
222 | 222977
223 | 152604
224 | 84733
225 | 76338
226 | 283005
227 | 134729
228 | 281171
229 | 214669
230 | 176673
231 | 288812
232 | 206465
233 | 247890
234 | 243047
235 | 208115
236 | 148140
237 | 115017
238 | 176963
239 | 307509
240 | 284603
241 | 241926
242 | 280752
243 | 163987
244 | 220863
245 | 156551
246 | 110612
247 | 112653
248 | 105377
249 | 58082
250 | 114044
251 | 108856
252 | 168405
253 | 104657
254 | 135195
255 | 245724
256 | 219839
257 | 174055
258 | 458626
259 | 266611
260 | 235475
261 | 328821
262 | 174840
263 | 270769
264 | 186970
265 | 113254
266 | 103525
267 | 147369
268 | 129474
269 | 232621
270 | 161317
271 | 122892
272 | 108894
273 | 86469
274 | 86075
275 | 126017
276 | 69829
277 | 90373
278 | 143796
279 | 166876
280 | 217179
281 | 186509
282 | 192221
283 | 180451
284 | 204009
285 | 187679
286 | 286553
287 | 406359
288 | 336216
289 | 372069
290 | 321477
291 | 374586
292 | 264360
293 | 161992
294 | 71280
295 | 58130
296 | 64134
297 | 55589
298 | 55689
299 | 50967
300 | 97742
301 | 123069
302 | 19363
303 | 3 150
304 | 305387
305 | 339170
306 | 310784
307 | 375420
308 | 108310
309 | 247294
310 | 410482
311 | 325869
312 | 261860
313 | 190970
314 | 215187
315 | 300714
316 | 329489
317 | 295202
318 | 103930
319 | 196198
320 | 304207
321 | 226819
322 | 349573
323 | 580811
324 | 200398
325 | 115450
326 | 207156
327 | 122814
328 | 196453
329 | 258373
330 | 286506
331 | 275838
332 | 354310
333 | 478943
334 | 359395
335 | 263664
336 | 220687
337 | 286800
338 | 308582
339 | 345466
340 | 220123
341 | 299959
342 | 307940
343 | 135534
344 | 355700
345 | 233072
346 | 242575
347 | 191342
348 | 400290
349 | 463402
350 | 432725
351 | 150589
352 | 417057
353 | 220799
354 | 203195
355 | 241658
356 | 483744
357 | 397068
358 | 236085
359 | 145444
360 | 304619
361 | 186598
362 | 219186
363 | 164338
364 | 415521
365 | 478177
366 | 287111
367 | 292601
368 | 273237
369 | 259126
370 | 236964
371 | 358823
372 | 366733
373 | 330666
374 | 239671
375 | 135662
376 | 120897
377 | 406603
378 | 196438
379 | 483655
380 | 323604
381 | 258052
382 | 415093
383 | 299812
384 | 349871
385 | 345421
386 | 299785
387 | 212836
388 | 181320
389 | 268694
390 | 478578
391 | 431585
392 | 367800
393 | 430792
394 | 255163
395 | 335287
396 | 238996
397 | 172798
398 | 167943
399 | 155517
400 | 84040
401 | 164076
402 | 162451
403 | 244573
404 | 141685
405 | 197651
406 | 347342
407 | 318319
408 | 252021
409 | 680815
410 | 409679
411 | 339336
412 | 493552
413 | 249986
414 | 387206
415 | 269924
416 | 158806
417 | 142446
418 | 203038
419 | 216592
420 | 385388
421 | 244084
422 | 178725
423 | 157052
424 | 126124
425 | 132389
426 | 187298
427 | 111370
428 | 142686
429 | 249766
430 | 291362
431 | 368143
432 | 341106
433 | 309330
434 | 316679
435 | 282032
436 | 263528
437 | 428006
438 | 554413
439 | 519829
440 | 534331
441 | 513350
442 | 576080
443 | 445247
444 | 240215
445 | 98273
446 | 89511
447 | 106450
448 | 76193
449 | 69801
450 | 65370
451 | 128668
452 | 161926
453 | 25385
454 | 4 150
455 | 407748
456 | 450106
457 | 417036
458 | 520324
459 | 135890
460 | 318725
461 | 551997
462 | 438974
463 | 349934
464 | 262749
465 | 289478
466 | 393344
467 | 441029
468 | 389046
469 | 131513
470 | 249943
471 | 392676
472 | 291511
473 | 467632
474 | 794404
475 | 273473
476 | 148007
477 | 268842
478 | 161542
479 | 259725
480 | 338960
481 | 392369
482 | 372750
483 | 467133
484 | 644227
485 | 468266
486 | 349018
487 | 294237
488 | 382411
489 | 408383
490 | 455921
491 | 285784
492 | 390491
493 | 413285
494 | 178989
495 | 480896
496 | 313214
497 | 326722
498 | 267519
499 | 536607
500 | 616173
501 | 573752
502 | 203716
503 | 566608
504 | 302782
505 | 277957
506 | 321683
507 | 637568
508 | 531390
509 | 319758
510 | 194922
511 | 399514
512 | 249376
513 | 299576
514 | 239586
515 | 564603
516 | 641855
517 | 382508
518 | 389117
519 | 366766
520 | 343159
521 | 313354
522 | 482164
523 | 484108
524 | 436788
525 | 331640
526 | 183731
527 | 156712
528 | 539416
529 | 264609
530 | 685930
531 | 430471
532 | 339337
533 | 557774
534 | 407831
535 | 469395
536 | 462847
537 | 401572
538 | 281061
539 | 237061
540 | 368029
541 | 659809
542 | 594420
543 | 491212
544 | 585712
545 | 344437
546 | 460693
547 | 324356
548 | 222926
549 | 214641
550 | 201582
551 | 109955
552 | 209992
553 | 204710
554 | 317599
555 | 190620
556 | 253914
557 | 456625
558 | 450729
559 | 322006
560 | 909976
561 | 579600
562 | 464273
563 | 650825
564 | 329219
565 | 520932
566 | 349777
567 | 202934
568 | 190250
569 | 274010
570 | 313173
571 | 537894
572 | 339892
573 | 234644
574 | 215426
575 | 168766
576 | 174127
577 | 242218
578 | 156222
579 | 217646
580 | 352052
581 | 407211
582 | 508706
583 | 476076
584 | 438434
585 | 442582
586 | 389815
587 | 362066
588 | 567726
589 | 741641
590 | 694805
591 | 706403
592 | 686256
593 | 756387
594 | 596297
595 | 337459
596 | 142054
597 | 124381
598 | 143081
599 | 99667
600 | 91628
601 | 87124
602 | 166887
603 | 207330
604 | 31276
605 | 5 150
606 | 600672
607 | 596577
608 | 590608
609 | 610571
610 | 219306
611 | 470054
612 | 856959
613 | 638463
614 | 498193
615 | 384842
616 | 363338
617 | 518486
618 | 638020
619 | 551596
620 | 172016
621 | 342934
622 | 525440
623 | 401864
624 | 649641
625 | 1098391
626 | 361990
627 | 200595
628 | 394241
629 | 221995
630 | 362490
631 | 482715
632 | 542207
633 | 517165
634 | 652588
635 | 923626
636 | 664334
637 | 481447
638 | 414576
639 | 541363
640 | 574435
641 | 616414
642 | 422906
643 | 549362
644 | 569711
645 | 252293
646 | 672624
647 | 437259
648 | 450561
649 | 364646
650 | 746738
651 | 857171
652 | 797868
653 | 279254
654 | 781347
655 | 429981
656 | 388569
657 | 444284
658 | 863763
659 | 725590
660 | 441678
661 | 283519
662 | 589760
663 | 359354
664 | 422821
665 | 337855
666 | 832239
667 | 873786
668 | 514233
669 | 529773
670 | 533927
671 | 487764
672 | 437855
673 | 690192
674 | 695610
675 | 606334
676 | 479930
677 | 274684
678 | 227771
679 | 714370
680 | 361708
681 | 1166267
682 | 587529
683 | 447614
684 | 740763
685 | 546000
686 | 616396
687 | 640935
688 | 534229
689 | 377945
690 | 302974
691 | 549317
692 | 922390
693 | 861539
694 | 696231
695 | 810570
696 | 482844
697 | 680500
698 | 465920
699 | 322863
700 | 302990
701 | 266799
702 | 152831
703 | 281247
704 | 284322
705 | 421423
706 | 251207
707 | 343366
708 | 609009
709 | 596824
710 | 436177
711 | 1280238
712 | 819822
713 | 645124
714 | 938666
715 | 441987
716 | 733665
717 | 442827
718 | 268984
719 | 261587
720 | 377707
721 | 465434
722 | 797591
723 | 496887
724 | 313404
725 | 281099
726 | 239234
727 | 252174
728 | 342283
729 | 232974
730 | 338127
731 | 492865
732 | 593709
733 | 701412
734 | 644850
735 | 659178
736 | 658814
737 | 591556
738 | 536967
739 | 835073
740 | 1199169
741 | 1093382
742 | 1060033
743 | 1024976
744 | 1050927
745 | 901668
746 | 569292
747 | 233917
748 | 152226
749 | 177290
750 | 127028
751 | 106669
752 | 101251
753 | 195131
754 | 247691
755 | 37969
756 | 6 150
757 | 1039113
758 | 1019473
759 | 1031136
760 | 957238
761 | 330368
762 | 764504
763 | 1480225
764 | 1080885
765 | 814468
766 | 653747
767 | 586858
768 | 788447
769 | 1098390
770 | 907626
771 | 256848
772 | 527756
773 | 799162
774 | 649014
775 | 1085003
776 | 1805679
777 | 622491
778 | 316827
779 | 640954
780 | 381142
781 | 592698
782 | 796977
783 | 950412
784 | 880842
785 | 1039798
786 | 1560578
787 | 1060396
788 | 823590
789 | 690024
790 | 877218
791 | 951335
792 | 997519
793 | 670760
794 | 862105
795 | 945220
796 | 438845
797 | 1114069
798 | 742119
799 | 747395
800 | 662731
801 | 1237025
802 | 1373830
803 | 1307373
804 | 494413
805 | 1320061
806 | 746453
807 | 679127
808 | 735642
809 | 1377900
810 | 1191778
811 | 768890
812 | 505634
813 | 961274
814 | 604661
815 | 725477
816 | 638702
817 | 1406632
818 | 1414255
819 | 839999
820 | 860756
821 | 883242
822 | 795237
823 | 713588
824 | 1168626
825 | 1129682
826 | 970832
827 | 855262
828 | 482040
829 | 388620
830 | 1147794
831 | 598133
832 | 1985381
833 | 978990
834 | 712508
835 | 1231092
836 | 919290
837 | 995397
838 | 1058058
839 | 891604
840 | 617659
841 | 499738
842 | 937498
843 | 1589628
844 | 1468221
845 | 1155102
846 | 1365707
847 | 824752
848 | 1174663
849 | 815211
850 | 511737
851 | 480216
852 | 432180
853 | 253308
854 | 440373
855 | 460177
856 | 676732
857 | 471588
858 | 556992
859 | 974128
860 | 1015847
861 | 679273
862 | 2156179
863 | 1454593
864 | 1096882
865 | 1538666
866 | 707934
867 | 1184125
868 | 693360
869 | 422647
870 | 430361
871 | 639382
872 | 887286
873 | 1423060
874 | 884677
875 | 506082
876 | 487299
877 | 418713
878 | 418155
879 | 547535
880 | 415332
881 | 659732
882 | 875281
883 | 1033784
884 | 1188604
885 | 1104723
886 | 1117806
887 | 1143521
888 | 1031008
889 | 932007
890 | 1408464
891 | 2034797
892 | 1819973
893 | 1761754
894 | 1727326
895 | 1731760
896 | 1502328
897 | 1036723
898 | 462151
899 | 292104
900 | 306615
901 | 223737
902 | 168464
903 | 163112
904 | 308318
905 | 389586
906 | 56863
907 | 7 150
908 | 1333033
909 | 1387992
910 | 1454423
911 | 1081157
912 | 456013
913 | 1063086
914 | 2052686
915 | 1473441
916 | 1044613
917 | 668982
918 | 683252
919 | 1081897
920 | 1392514
921 | 1270223
922 | 392058
923 | 855096
924 | 1187781
925 | 941443
926 | 1505618
927 | 2478551
928 | 756377
929 | 422248
930 | 854660
931 | 484398
932 | 817684
933 | 1168914
934 | 1211065
935 | 1152043
936 | 1487677
937 | 2231366
938 | 1430945
939 | 1104073
940 | 963201
941 | 1179304
942 | 1289082
943 | 1352803
944 | 1030779
945 | 1179284
946 | 1234085
947 | 581400
948 | 1485050
949 | 976928
950 | 959718
951 | 806367
952 | 1664989
953 | 1854242
954 | 1753271
955 | 633054
956 | 1727118
957 | 942101
958 | 842067
959 | 957959
960 | 1799421
961 | 1530530
962 | 959310
963 | 707276
964 | 1567158
965 | 873310
966 | 974123
967 | 692324
968 | 1866215
969 | 1812137
970 | 1059379
971 | 1154202
972 | 1270864
973 | 1116232
974 | 986754
975 | 1711185
976 | 1631216
977 | 1303961
978 | 1194734
979 | 716671
980 | 586003
981 | 1455571
982 | 746169
983 | 2560866
984 | 1405204
985 | 960866
986 | 1564791
987 | 1161916
988 | 1244750
989 | 1392690
990 | 1143213
991 | 799253
992 | 651096
993 | 1329383
994 | 2061420
995 | 1940139
996 | 1638907
997 | 1769444
998 | 1237391
999 | 1555756
1000 | 1140336
1001 | 774587
1002 | 715613
1003 | 561207
1004 | 322592
1005 | 585646
1006 | 658802
1007 | 887113
1008 | 516493
1009 | 786527
1010 | 1229727
1011 | 1062047
1012 | 919646
1013 | 2868735
1014 | 1867575
1015 | 1445612
1016 | 2058938
1017 | 914637
1018 | 1485855
1019 | 881681
1020 | 555747
1021 | 521991
1022 | 841148
1023 | 1429771
1024 | 2144556
1025 | 1107967
1026 | 673409
1027 | 560190
1028 | 545886
1029 | 600014
1030 | 756899
1031 | 546368
1032 | 843732
1033 | 1248629
1034 | 1504414
1035 | 1644619
1036 | 1491555
1037 | 1578851
1038 | 1506831
1039 | 1381776
1040 | 1196359
1041 | 1658247
1042 | 2517575
1043 | 2351333
1044 | 2148845
1045 | 2052971
1046 | 2292886
1047 | 1948071
1048 | 1356873
1049 | 564378
1050 | 393276
1051 | 448793
1052 | 318174
1053 | 227801
1054 | 224158
1055 | 430451
1056 | 541478
1057 | 79901
1058 | 8 150
1059 | 1685226
1060 | 1764279
1061 | 1839839
1062 | 1400787
1063 | 573197
1064 | 1368326
1065 | 2643728
1066 | 1897900
1067 | 1314349
1068 | 860682
1069 | 877281
1070 | 1361398
1071 | 1758274
1072 | 1593426
1073 | 484064
1074 | 1092035
1075 | 1488120
1076 | 1194352
1077 | 1902554
1078 | 3155452
1079 | 978061
1080 | 530888
1081 | 1078109
1082 | 648258
1083 | 1059734
1084 | 1485845
1085 | 1607110
1086 | 1502843
1087 | 1846664
1088 | 2837324
1089 | 1802294
1090 | 1407669
1091 | 1230411
1092 | 1498520
1093 | 1627063
1094 | 1708480
1095 | 1283445
1096 | 1449055
1097 | 1581065
1098 | 762964
1099 | 1905910
1100 | 1272657
1101 | 1246144
1102 | 1103093
1103 | 2119689
1104 | 2328608
1105 | 2191546
1106 | 825212
1107 | 2203067
1108 | 1222423
1109 | 1109838
1110 | 1222461
1111 | 2248168
1112 | 1920809
1113 | 1243898
1114 | 913895
1115 | 1958532
1116 | 1125075
1117 | 1264767
1118 | 963636
1119 | 2380565
1120 | 2300246
1121 | 1357395
1122 | 1447441
1123 | 1635359
1124 | 1410647
1125 | 1249816
1126 | 2173852
1127 | 2027274
1128 | 1633766
1129 | 1578478
1130 | 913373
1131 | 717786
1132 | 1829899
1133 | 955512
1134 | 3297356
1135 | 1778249
1136 | 1191672
1137 | 2014778
1138 | 1506350
1139 | 1586395
1140 | 1799605
1141 | 1472710
1142 | 1021318
1143 | 819557
1144 | 1686624
1145 | 2636240
1146 | 2486607
1147 | 2064915
1148 | 2249123
1149 | 1594603
1150 | 2017849
1151 | 1476348
1152 | 975016
1153 | 888324
1154 | 702909
1155 | 412247
1156 | 732820
1157 | 820427
1158 | 1116162
1159 | 680098
1160 | 994139
1161 | 1570967
1162 | 1385636
1163 | 1160464
1164 | 3660086
1165 | 2488612
1166 | 1867968
1167 | 2616223
1168 | 1149003
1169 | 1863720
1170 | 1094615
1171 | 686480
1172 | 671975
1173 | 1111370
1174 | 1891416
1175 | 2789931
1176 | 1443309
1177 | 849027
1178 | 730757
1179 | 701081
1180 | 766266
1181 | 951929
1182 | 723023
1183 | 1143205
1184 | 1638770
1185 | 1950256
1186 | 2118459
1187 | 1895046
1188 | 2004135
1189 | 1944617
1190 | 1789596
1191 | 1545632
1192 | 2111492
1193 | 3170768
1194 | 3013928
1195 | 2755879
1196 | 2662966
1197 | 2902187
1198 | 2513523
1199 | 1797828
1200 | 773788
1201 | 541688
1202 | 569186
1203 | 403480
1204 | 293216
1205 | 290801
1206 | 545183
1207 | 678490
1208 | 96937
1209 | 9 150
1210 | 2283390
1211 | 2275661
1212 | 2396674
1213 | 1424991
1214 | 690084
1215 | 1755855
1216 | 3409477
1217 | 2494389
1218 | 1602505
1219 | 928153
1220 | 1023442
1221 | 1655789
1222 | 2292188
1223 | 2086583
1224 | 663534
1225 | 1611269
1226 | 1964557
1227 | 1628125
1228 | 2528207
1229 | 4055225
1230 | 1131071
1231 | 652083
1232 | 1360974
1233 | 778515
1234 | 1408336
1235 | 1993729
1236 | 1953030
1237 | 1874465
1238 | 2411750
1239 | 4009026
1240 | 2265736
1241 | 1704616
1242 | 1572828
1243 | 1910282
1244 | 2039206
1245 | 2112534
1246 | 1802504
1247 | 1947786
1248 | 1995030
1249 | 979981
1250 | 2423099
1251 | 1624348
1252 | 1522750
1253 | 1340366
1254 | 2715973
1255 | 2941395
1256 | 2793340
1257 | 1073618
1258 | 2874259
1259 | 1528910
1260 | 1336602
1261 | 1518595
1262 | 2855425
1263 | 2393553
1264 | 1560993
1265 | 1231996
1266 | 2903396
1267 | 1514212
1268 | 1618769
1269 | 1079162
1270 | 2898200
1271 | 2890707
1272 | 1696799
1273 | 1823469
1274 | 2191569
1275 | 1849244
1276 | 1626683
1277 | 2963684
1278 | 2771516
1279 | 2028414
1280 | 2107769
1281 | 1318465
1282 | 1050428
1283 | 2190376
1284 | 1129122
1285 | 3852973
1286 | 2435656
1287 | 1554043
1288 | 2444345
1289 | 1806257
1290 | 1927332
1291 | 2305709
1292 | 1789384
1293 | 1260882
1294 | 1061352
1295 | 2237997
1296 | 3402757
1297 | 3286406
1298 | 2794690
1299 | 2838975
1300 | 2152303
1301 | 2716981
1302 | 1975297
1303 | 1299998
1304 | 1203565
1305 | 861285
1306 | 501373
1307 | 907589
1308 | 1080506
1309 | 1389378
1310 | 806323
1311 | 1346629
1312 | 1903722
1313 | 1489901
1314 | 1449981
1315 | 4738547
1316 | 2947955
1317 | 2365847
1318 | 3334474
1319 | 1431234
1320 | 2240335
1321 | 1305072
1322 | 840346
1323 | 815479
1324 | 1422529
1325 | 2516657
1326 | 3635023
1327 | 1759411
1328 | 1077816
1329 | 880441
1330 | 905576
1331 | 1014856
1332 | 1237151
1333 | 929209
1334 | 1527048
1335 | 2358383
1336 | 2775459
1337 | 3003596
1338 | 2651948
1339 | 2777920
1340 | 2648541
1341 | 2372541
1342 | 2052691
1343 | 2603391
1344 | 2578533
1345 | 2613953
1346 | 2492981
1347 | 2445612
1348 | 3286906
1349 | 2874119
1350 | 1959116
1351 | 1271481
1352 | 847826
1353 | 900103
1354 | 662020
1355 | 440564
1356 | 432999
1357 | 841217
1358 | 1051596
1359 | 147640
1360 | 10 150
1361 | 2559407
1362 | 2544986
1363 | 2661696
1364 | 1593595
1365 | 762041
1366 | 1955719
1367 | 3880949
1368 | 2777836
1369 | 1758687
1370 | 1030814
1371 | 1138183
1372 | 1835157
1373 | 2549858
1374 | 2311934
1375 | 738026
1376 | 1801745
1377 | 2181048
1378 | 1804320
1379 | 2811097
1380 | 4520157
1381 | 1269826
1382 | 724180
1383 | 1519370
1384 | 888939
1385 | 1605463
1386 | 2217356
1387 | 2248266
1388 | 2130911
1389 | 2653059
1390 | 4453235
1391 | 2509225
1392 | 1891977
1393 | 1747463
1394 | 2119869
1395 | 2266098
1396 | 2340467
1397 | 1965181
1398 | 2161810
1399 | 2235207
1400 | 1090612
1401 | 2724186
1402 | 1833663
1403 | 1712878
1404 | 1556783
1405 | 3072520
1406 | 3259696
1407 | 3091866
1408 | 1198451
1409 | 3216486
1410 | 1719981
1411 | 1501306
1412 | 1685886
1413 | 3155975
1414 | 2652440
1415 | 1755185
1416 | 1370816
1417 | 3232463
1418 | 1678973
1419 | 1815774
1420 | 1257982
1421 | 3253539
1422 | 3240636
1423 | 1908281
1424 | 2029129
1425 | 2466898
1426 | 2058815
1427 | 1815397
1428 | 3319623
1429 | 3058323
1430 | 2254125
1431 | 2391318
1432 | 1462121
1433 | 1144048
1434 | 2425030
1435 | 1266790
1436 | 4339548
1437 | 2751279
1438 | 1711264
1439 | 2726979
1440 | 2022385
1441 | 2144167
1442 | 2586271
1443 | 2003419
1444 | 1411036
1445 | 1179909
1446 | 2497788
1447 | 3786602
1448 | 3666502
1449 | 3099401
1450 | 3149250
1451 | 2415374
1452 | 3056088
1453 | 2220480
1454 | 1446035
1455 | 1329669
1456 | 954868
1457 | 562853
1458 | 1006666
1459 | 1201465
1460 | 1543767
1461 | 905269
1462 | 1495427
1463 | 2106192
1464 | 1672144
1465 | 1608176
1466 | 5309730
1467 | 3346584
1468 | 2658447
1469 | 3703353
1470 | 1587910
1471 | 2471907
1472 | 1438960
1473 | 925684
1474 | 915588
1475 | 1616710
1476 | 2846889
1477 | 4087608
1478 | 1976231
1479 | 1204613
1480 | 992717
1481 | 1020126
1482 | 1136577
1483 | 1369790
1484 | 1050232
1485 | 1749204
1486 | 2634318
1487 | 3096199
1488 | 3357328
1489 | 2983750
1490 | 3136366
1491 | 2977746
1492 | 2655631
1493 | 2292561
1494 | 2933440
1495 | 2910071
1496 | 2910620
1497 | 2781423
1498 | 2758069
1499 | 3653159
1500 | 3228234
1501 | 2230943
1502 | 1452719
1503 | 968854
1504 | 999503
1505 | 733099
1506 | 491321
1507 | 488478
1508 | 937948
1509 | 1163906
1510 | 160023
1511 |
--------------------------------------------------------------------------------
/src/adapt_algo.ml:
--------------------------------------------------------------------------------
1 | (*
2 | * dashc, client emulator for DASH video streaming
3 | * Copyright (c) 2016-2018, Aleksandr Reviakin, University College Cork
4 | *
5 | * This program is free software; you can redistribute it and/or
6 | * modify it under the terms of the GNU General Public License
7 | * as published by the Free Software Foundation; either version 2
8 | * of the License, or (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program; if not, write to the Free Software
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 | * 02110-1301, USA.
19 | *)
20 |
21 | open Core
22 | open Async
23 |
24 | open Representation
25 | open Segm_result
26 |
27 | (* Probe and Adapt: Rate Adaptation for HTTP Video Streaming At Scale:
28 | https://arxiv.org/abs/1305.0510 *)
29 | module Conv : sig
30 | val next_representation_level :
31 | representations:(int, representation) Hashtbl.t ->
32 | results:segm_result List.t ->
33 | int
34 | end = struct
35 |
36 | let moving_average = ref 0.
37 | (* The main difference in TAPAS tool implementation
38 | is conversion of the target rate to representation level,
39 | it also has buffer size of 60 seconds and
40 | only the first segment is downloaded in the lowest quality.
41 | This flag affects only conversion implementation.
42 | The code of the TAPAS tool
43 | could've been found here (https://github.com/ldecicco/tapas) during 08.2017.
44 | The default value is taps_impl=false, it was added for tests only. *)
45 | let tapas_impl = false
46 |
47 | let next_representation_level ~representations ~results =
48 | if List.length results < 2 then 1
49 | else
50 | let conv_weight = 0.85 in
51 | let last_result = List.hd_exn results in
52 | let throughput =
53 | ((float_of_int last_result.segment_size) *. 8. *. us_float *. us_float) /.
54 | float_of_int (last_result.time_for_delivery) in
55 | moving_average :=
56 | if List.length results = 3 then throughput
57 | else throughput *. 0.4 +. !moving_average *. 0.6;
58 | if tapas_impl then
59 | let level = last_result.repr_level in
60 | let r_up = Hashtbl.fold representations ~init:1 ~f:(fun ~key ~data acc ->
61 | if (data.bandwidth < int_of_float (conv_weight *. !moving_average) &&
62 | data.bandwidth > (Hashtbl.find_exn representations acc).bandwidth) then
63 | key else acc) in
64 | let r_down = Hashtbl.fold representations ~init:1 ~f:(fun ~key ~data acc ->
65 | if (data.bandwidth < int_of_float !moving_average &&
66 | data.bandwidth > (Hashtbl.find_exn representations acc).bandwidth) then
67 | key else acc) in
68 | let new_level =
69 | if level < r_up then r_up
70 | else if r_up <= level && level <= r_down then level
71 | else r_down
72 | in
73 | new_level
74 | else
75 | let next_repr = Hashtbl.fold representations ~init:1 ~f:(fun ~key ~data acc ->
76 | if (data.bandwidth < int_of_float (conv_weight *. !moving_average) &&
77 | data.bandwidth > (Hashtbl.find_exn representations acc).bandwidth) then
78 | key else acc) in
79 | next_repr
80 | end
81 |
82 | (* A Buffer-Based Approach to Rate Adaptation:
83 | Evidence from a Large Video Streaming Service
84 | http://yuba.stanford.edu/~nickm/papers/sigcomm2014-video.pdf
85 | https://yuba.stanford.edu/~nickm/papers/ty-thesis.pdf *)
86 | module BBA_0 = struct
87 | type t = { maxb : float }
88 | (*let create maxb = { maxb }*)
89 | let next_representation_level ~algo ~representations ~results =
90 | let buf_now =
91 | if (List.length results) = 0 then 0.
92 | else (List.hd_exn results).buffer_level_in_momentum in
93 | let rate_prev =
94 | if (List.length results) = 0 then (Hashtbl.find_exn representations 1).bandwidth
95 | else
96 | (List.hd_exn results).representation_rate
97 | in
98 | let repr_prev = Hashtbl.fold representations ~init:1 ~f:(fun ~key ~data acc ->
99 | if data.bandwidth = rate_prev then key
100 | else acc
101 | ) in
102 |
103 | let reservoir = 90. in
104 | let cushion = 126. in
105 | let maxbuf = algo.maxb in
106 | (* rate_prev is used for ~init below only as a start value,
107 | there is no meaning in this particular value, but it cannot be less
108 | than the lowest rate among representations *)
109 | let rate_min = Hashtbl.fold representations ~init:rate_prev ~f:(fun ~key:_ ~data acc ->
110 | if data.bandwidth < acc then data.bandwidth else acc) in
111 | let rate_max = Hashtbl.fold representations ~init:1 ~f:(fun ~key:_ ~data acc ->
112 | if data.bandwidth > acc then data.bandwidth else acc) in
113 | let f buf =
114 | let slope = (float_of_int (rate_max -rate_min)) /.
115 | (0.9 *. maxbuf -. reservoir) in
116 | let target_rate = slope *. (buf -. reservoir) in
117 | int_of_float target_rate
118 | in
119 | let f_buf = f buf_now in
120 | let rate_plus =
121 | if rate_prev = rate_max then rate_max
122 | else (Hashtbl.find_exn representations (repr_prev + 1)).bandwidth in
123 | let rate_minus =
124 | if rate_prev = rate_min then rate_min
125 | else (Hashtbl.find_exn representations (repr_prev - 1)).bandwidth in
126 | let rate_next =
127 | if buf_now <= reservoir then rate_min
128 | else if buf_now >= (reservoir +. cushion) then rate_max
129 | else if f_buf >= rate_plus then
130 | Hashtbl.fold representations ~init:rate_min ~f:(fun ~key:_ ~data acc ->
131 | if data.bandwidth > acc &&
132 | data.bandwidth < f_buf then data.bandwidth
133 | else acc)
134 | else if f_buf <= rate_minus then
135 | Hashtbl.fold representations ~init:rate_max ~f:(fun ~key:_ ~data acc ->
136 | if data.bandwidth < acc &&
137 | data.bandwidth > f_buf then data.bandwidth
138 | else acc)
139 | else rate_prev in
140 | let repr_next = Hashtbl.fold representations ~init:1 ~f:(fun ~key ~data acc ->
141 | if data.bandwidth = rate_next then key else acc) in
142 | repr_next
143 | end
144 |
145 | (* See papers from BBA_0 *)
146 | module BBA_1 = struct
147 | type t = {
148 | maxb : float;
149 | chunk_sizes_per_repr : (int, int List.t) Hashtbl.t;
150 | average_chunk_size_per_repr : int List.t;
151 | }
152 |
153 | let calculate_reservoir
154 | ?(debug=false) ~algo ~representations ~results last_segment_index =
155 | let segment_duration = ((Hashtbl.find_exn representations 1).segment_duration) in
156 | let number_of_downloaded_segments = List.length results in
157 | let last_window_segment =
158 | if ((int_of_float algo.maxb) * 2) / segment_duration <
159 | last_segment_index - number_of_downloaded_segments then
160 | number_of_downloaded_segments + (int_of_float algo.maxb) * 2 / segment_duration
161 | else
162 | last_segment_index
163 | in
164 |
165 | let rate_prev =
166 | if (List.length results) = 0 then (Hashtbl.find_exn representations 1).bandwidth
167 | else
168 | (* when the representation rate is saved in a trace file,
169 | some level of precision will be lost,
170 | because original value of bit/s will be saved in kbit/s int type value,
171 | for example, 232385 will be saved as 232, and when it is read in debug mode
172 | it will look like 232000, so the repr_prev will be chosen based on a wrong value.
173 | To fix it this value 232000 (for example) will be converted into the closest
174 | within 5% the representation rate from the representations hash table *)
175 | if debug then Hashtbl.fold representations ~init:1 ~f:(fun ~key:_ ~data acc ->
176 | if (float_of_int data.bandwidth *. 0.95) <
177 | float_of_int (List.hd_exn results).representation_rate &&
178 | (float_of_int data.bandwidth *. 1.05) >
179 | float_of_int (List.hd_exn results).representation_rate
180 | then data.bandwidth
181 | else acc
182 | )
183 | else (List.hd_exn results).representation_rate
184 | in
185 | let rate_min = Hashtbl.fold representations ~init:rate_prev ~f:(fun ~key:_ ~data acc ->
186 | if data.bandwidth < acc then data.bandwidth else acc) in
187 | let average_chunk_size_per_min_rate = rate_min * segment_duration / 8 in
188 |
189 | let rec look_ahead_for_chunk_sizes
190 | ~curr_index (large_chunks_size, small_chunks_size) =
191 | (* check of equality to last_window_segment is made
192 | because List.nth_exn counts starting from 0 *)
193 | if curr_index = last_window_segment then (large_chunks_size, small_chunks_size)
194 | else
195 | (* curr_index because nth_exn starts to count from 0 *)
196 | let segm_size =
197 | List.nth_exn (Hashtbl.find_exn algo.chunk_sizes_per_repr 1) curr_index in
198 | (* equal chunk_sizes are included to small category *)
199 | if average_chunk_size_per_min_rate < segm_size then
200 | look_ahead_for_chunk_sizes
201 | ~curr_index:(curr_index + 1)
202 | (large_chunks_size + segm_size, small_chunks_size)
203 | else
204 | look_ahead_for_chunk_sizes
205 | ~curr_index:(curr_index + 1)
206 | (large_chunks_size, small_chunks_size + segm_size)
207 | in
208 | let large_chunks_size, small_chunks_size =
209 | look_ahead_for_chunk_sizes ~curr_index:number_of_downloaded_segments (0, 0) in
210 | (* reservoir in seconds *)
211 | let reservoir = (large_chunks_size - small_chunks_size) / (rate_min / 8) in
212 | let () =
213 | if debug then begin
214 | print_endline @@ "large_chunks_size: " ^ string_of_int large_chunks_size;
215 | print_endline @@ "small_chunks_size: " ^ string_of_int small_chunks_size;
216 | print_endline @@ "reservoir: " ^ string_of_int reservoir;
217 | end
218 | in
219 | (* from paper: we bound the reservoir size to be between 8 and 140 seconds.
220 | here the bound is between 2 segments and 35 segments *)
221 | let reservoir_checked =
222 | if reservoir < segment_duration * 2 then segment_duration * 2
223 | else if reservoir > segment_duration * 35 then segment_duration * 35
224 | else reservoir
225 | in
226 | reservoir_checked
227 |
228 | (* the main difference from BBA_0 is usage of chunk map instead of rate map
229 | and dynamic reservoir *)
230 | let next_representation_level
231 | ?(debug=false) ~algo ~representations ~results last_segment_index =
232 | let buf_now =
233 | if (List.length results) = 0 then 0.
234 | else (List.hd_exn results).buffer_level_in_momentum in
235 | (* segm_number_next is next, if we agree that segment number begins from 0 *)
236 | let segm_number_next = List.length results in
237 |
238 | let rate_prev =
239 | if (List.length results) = 0 then (Hashtbl.find_exn representations 1).bandwidth
240 | else
241 | (* when the representation rate is saved in a trace file,
242 | some level of precision will be lost,
243 | because original value of bit/s will be saved in kbit/s int type value,
244 | for example, 232385 will be saved as 232, and when it is read in debug mode
245 | it will look like 232000,
246 | so the repr_prev will be chosen based on a wrong value.
247 | To fix it this value 232000 (for example) will be converted into the closest
248 | within 5% the representation rate from the representations hash table *)
249 | if debug then Hashtbl.fold representations ~init:1 ~f:(fun ~key:_ ~data acc ->
250 | if (float_of_int data.bandwidth *. 0.95) <
251 | float_of_int (List.hd_exn results).representation_rate &&
252 | (float_of_int data.bandwidth *. 1.05) >
253 | float_of_int (List.hd_exn results).representation_rate
254 | then data.bandwidth
255 | else acc
256 | )
257 | else (List.hd_exn results).representation_rate
258 | in
259 |
260 | let repr_prev = Hashtbl.fold representations ~init:1 ~f:(fun ~key ~data acc ->
261 | if data.bandwidth = rate_prev then key
262 | else acc
263 | ) in
264 | let () =
265 | if debug then begin
266 | print_endline @@ "rate_prev (BBA-1): " ^ string_of_int rate_prev;
267 | print_endline @@ "repr_prev (BBA-1): " ^ string_of_int repr_prev;
268 | end
269 | in
270 |
271 | let chunk_size_prev =
272 | if (List.length results) = 0 then
273 | List.nth_exn (Hashtbl.find_exn algo.chunk_sizes_per_repr 1) 0
274 | else
275 | List.nth_exn
276 | (Hashtbl.find_exn algo.chunk_sizes_per_repr repr_prev) (segm_number_next - 1)
277 | in
278 |
279 | (* update reservoir on each iteration *)
280 | let reservoir =
281 | float_of_int
282 | (calculate_reservoir
283 | ~debug:debug ~algo:algo ~representations ~results last_segment_index) in
284 | let maxbuf = algo.maxb in
285 |
286 | (* rate_prev is used for ~init below only as a start value,
287 | there is no meaning in this particular value, but it cannot be less
288 | than the lowest rate among representations *)
289 | let rate_min = Hashtbl.fold representations ~init:rate_prev ~f:(fun ~key:_ ~data acc ->
290 | if data.bandwidth < acc then data.bandwidth else acc) in
291 | let rate_max = Hashtbl.fold representations ~init:1 ~f:(fun ~key:_ ~data acc ->
292 | if data.bandwidth > acc then data.bandwidth else acc) in
293 |
294 | let chunk_size_min = List.hd_exn algo.average_chunk_size_per_repr in
295 | let chunk_size_max = List.last_exn algo.average_chunk_size_per_repr in
296 |
297 | let rec get_chunk_sizes_per_segm_number ~curr_index ~chunk_sizes =
298 | if curr_index > (Hashtbl.length representations) then
299 | List.rev chunk_sizes
300 | else
301 | let chunk_size =
302 | List.nth_exn
303 | (Hashtbl.find_exn algo.chunk_sizes_per_repr curr_index)
304 | segm_number_next in
305 | get_chunk_sizes_per_segm_number
306 | ~curr_index:(curr_index + 1)
307 | ~chunk_sizes:(chunk_size :: chunk_sizes)
308 | in
309 | (* chunk_sizes_per_segm_number is a list of chunk sizes
310 | with the index as a representation number *)
311 | let chunk_sizes_per_segm_number =
312 | get_chunk_sizes_per_segm_number ~curr_index:1 ~chunk_sizes:[] in
313 |
314 | let f buf =
315 | let slope = (float_of_int (chunk_size_max - chunk_size_min)) /.
316 | (0.9 *. maxbuf -. reservoir) in
317 | let target_chunk_size = slope *. (buf -. reservoir) in
318 | int_of_float target_chunk_size
319 | in
320 | let chunk_size_opt = f buf_now in
321 | let () =
322 | if debug then begin
323 | print_endline @@ "chunk_size_opt (BBA-1): " ^ string_of_int chunk_size_opt;
324 | end
325 | in
326 | let chunk_size_opt_discrete =
327 | List.fold
328 | chunk_sizes_per_segm_number
329 | ~init:(List.nth_exn chunk_sizes_per_segm_number 0) ~f:(fun acc x ->
330 | let () =
331 | if debug then begin
332 | print_endline @@ "List.fold chunk_sizes_per_segm_number: " ^ string_of_int x;
333 | end
334 | in
335 | if (x > acc) && (x < chunk_size_opt) then x
336 | else acc
337 | ) in
338 | let () =
339 | if debug then begin
340 | print_endline @@
341 | "chunk_size_opt_discrete (BBA-1): " ^ string_of_int chunk_size_opt_discrete;
342 | end
343 | in
344 |
345 | (* next highest chunk size for the next segment *)
346 | let (chunk_size_plus, _) =
347 | if rate_prev = rate_max then
348 | List.nth_exn
349 | (Hashtbl.find_exn algo.chunk_sizes_per_repr repr_prev)
350 | segm_number_next, repr_prev
351 | else
352 | List.nth_exn
353 | (Hashtbl.find_exn algo.chunk_sizes_per_repr (repr_prev + 1))
354 | segm_number_next, repr_prev + 1
355 | in
356 | let chunk_size_minus, _ =
357 | if rate_prev = rate_min then
358 | List.nth_exn
359 | (Hashtbl.find_exn algo.chunk_sizes_per_repr 1) segm_number_next, repr_prev
360 | else
361 | List.nth_exn
362 | (Hashtbl.find_exn algo.chunk_sizes_per_repr (repr_prev - 1))
363 | segm_number_next, repr_prev - 1
364 | in
365 |
366 | (* from ty-thesis, page 68: the algorithm stays at the
367 | current video rate as long as the chunk size suggested by the map
368 | does not pass the size of
369 | the next upcoming chunk at the next highest available video rate (Rate + )
370 | or the next lowest
371 | available video rate (Rate ). *)
372 | (* the returned repr_next here begins from 0,
373 | but it should from 1, so it is increased later *)
374 | let _, repr_next =
375 | (* the old version
376 | if chunk_size_opt > chunk_size_plus then chunk_size_plus,repr_plus
377 | else if chunk_size_opt < chunk_size_minus then chunk_size_minus, repr_minus*)
378 | if chunk_size_opt_discrete >= chunk_size_plus then
379 | List.foldi
380 | chunk_sizes_per_segm_number ~init:(chunk_size_plus, 0) ~f:(fun idx acc x ->
381 | let chunk_size_curr, _ = acc in
382 | if (x >= chunk_size_curr) && (x <= chunk_size_opt_discrete) then (x, idx)
383 | else acc
384 | )
385 | else if chunk_size_opt_discrete <= chunk_size_minus then
386 | List.foldi
387 | chunk_sizes_per_segm_number ~init:(chunk_size_plus, 0) ~f:(fun idx acc x ->
388 | let chunk_size_curr, _ = acc in
389 | if (x <= chunk_size_curr) && (x <= chunk_size_opt_discrete) then (x, idx)
390 | else acc
391 | )
392 | else chunk_size_prev, repr_prev - 1
393 | in
394 | (* repr_next *)
395 | repr_next + 1
396 |
397 | end
398 |
399 | (* see papers from BBA_1 *)
400 | (* From sigcomm2014-video.pdf about BBA-2.
401 | Based on the preceding observation, BBA-2 works as fol-
402 | lows. At time t = 0, since the buffer is empty, BBA-2 only
403 | picks the next highest video rate, if the ∆B increases by
404 | more than 0.875V s. Since ∆B = V − ChunkSize/c[k],
405 | ∆B > 0.875V also means that the chunk is downloaded
406 | eight times faster than it is played. As the buffer grows, we
407 | use the accumulated buffer to absorb the chunk size variation
408 | and we let BBA-2 increase the video rate faster. Whereas at
409 | the start, BBA-2 only increases the video rate if the chunk
410 | downloads eight times faster than it is played, by the time
411 | it fills the cushion, BBA-2 is prepared to step up the video
412 | rate if the chunk downloads twice as fast as it is played. The
413 | threshold decreases linearly, from the first chunk until the
414 | cushion is full. The blue line in Figure 16 shows BBA-2
415 | ramping up faster. BBA-2 continues to use this startup al-
416 | gorithm until (1) the buffer is decreasing, or (2) the chunk
417 | map suggests a higher rate. Afterwards, we use the f (B)
418 | defined in the BBA-1 algorithm to pick a rate.
419 | *)
420 | module BBA_2 = struct
421 | type t = BBA_1.t
422 |
423 | (* The termination of the startup phase will happen in case of
424 | 1) the buffer is decreasing or 2) the chunk map suggests a higher rate.
425 | After that even if cushion is not full,
426 | the algorithms will be in steady-state all the time.*)
427 | let startup_phase = ref true
428 |
429 | let next_representation_level
430 | ?(debug=false) ~algo ~representations ~results last_segment_index =
431 | let open BBA_1 in
432 | let repr_next_bba_1 =
433 | BBA_1.next_representation_level
434 | ~debug:debug
435 | ~algo:algo
436 | ~representations:representations
437 | ~results:results
438 | last_segment_index in
439 | if List.length results = 0 then
440 | repr_next_bba_1
441 | else
442 | let buf_now =
443 | (* this condition was already checked above,
444 | but it will be checked again just in case this code maybe copy-pasted *)
445 | if (List.length results) = 0 then 0.
446 | else (List.hd_exn results).buffer_level_in_momentum
447 | in
448 | let segment_duration = ((Hashtbl.find_exn representations 1).segment_duration) in
449 | (* time_for_delivery is stored in us *)
450 | let prev_time_for_delivery =
451 | float_of_int (List.hd_exn results).time_for_delivery /. (us_float *. us_float) in
452 | (* positive delta_b means the buffer is increasing *)
453 | let delta_b = float_of_int segment_duration -. prev_time_for_delivery in
454 | let () =
455 | if debug then begin
456 | print_endline @@
457 | "prev_time_for_delivery = " ^ string_of_float @@
458 | float_of_int (List.hd_exn results).time_for_delivery /.
459 | (us_float *. us_float);
460 | print_endline @@ "delta_b = " ^ string_of_float delta_b;
461 | end
462 | in
463 | (* BBA-2 continues to use this startup algorithm until
464 | (1) the buffer is decreasing, or (2) the chunk map suggests a higher rate.
465 | If any of these conditions is false then switch to the steady-state forever. *)
466 | if delta_b >= 0. && !startup_phase then
467 | (* According to the BBA-2 paper,
468 | the bitrate increases only
469 | if the chunk is downloaded 8x (0.875 coefficient) faster than segment duration
470 | and this condition linearly decreases to 2x (0.5 coefficient)
471 | by the time cushion is full from the time when the first chunk was downloaded,
472 | so the coefficient can be calculated as a function of bitrate level.*)
473 | let f buf =
474 | let slope =
475 | (0.5 -. 0.875) /. (0.9 *. algo.maxb -. float_of_int segment_duration) in
476 | let target_coefficient = 0.875 +. slope *. buf in
477 | let () =
478 | if debug then begin
479 | print_endline @@
480 | "target_coefficient = " ^ string_of_float target_coefficient;
481 | end
482 | in
483 | target_coefficient
484 | in
485 | (* target coefficient depends on the current buffer level *)
486 | let target_coefficient = f buf_now in
487 | let rate_prev =
488 | if (List.length results) = 0 then (Hashtbl.find_exn representations 1).bandwidth
489 | else
490 | (* when the representation rate is saved in a trace file,
491 | some level of precision will be lost,
492 | because original value of bit/s will be saved in kbit/s int type value,
493 | for example, 232385 will be saved as 232, and when it is read in debug mode
494 | it will look like 232000,
495 | so the repr_prev will be chosen based on a wrong value.
496 | To fix it this value 232000 (for example) will be converted into the closest
497 | within 5% the representation rate from the representations hash table *)
498 | if debug then Hashtbl.fold representations ~init:1 ~f:(fun ~key:_ ~data acc ->
499 | if (float_of_int data.bandwidth *. 0.95) <
500 | float_of_int (List.hd_exn results).representation_rate &&
501 | (float_of_int data.bandwidth *. 1.05) >
502 | float_of_int (List.hd_exn results).representation_rate
503 | then data.bandwidth
504 | else acc
505 | )
506 | else (List.hd_exn results).representation_rate
507 | in
508 | let repr_prev = Hashtbl.fold representations ~init:1 ~f:(fun ~key ~data acc ->
509 | if data.bandwidth = rate_prev then key
510 | else acc
511 | ) in
512 | let () =
513 | if debug then begin
514 | print_endline @@ "rate_prev (BBA-2): " ^ string_of_int rate_prev;
515 | print_endline @@ "repr_prev (BBA-2): " ^ string_of_int repr_prev;
516 | end
517 | in
518 | let rate_max = Hashtbl.fold representations ~init:1 ~f:(fun ~key:_ ~data acc ->
519 | if data.bandwidth > acc then data.bandwidth else acc) in
520 | let repr_plus =
521 | if rate_prev = rate_max then repr_prev
522 | else repr_prev + 1
523 | in
524 | (* suggessted repr depends on how fast the buffer is growing *)
525 | let suggested_repr =
526 | let () =
527 | if debug then begin
528 | print_endline @@
529 | "target_coefficient *. (float_of_int segment_duration) = " ^
530 | string_of_float @@ target_coefficient *. (float_of_int segment_duration);
531 | end
532 | in
533 | if delta_b > target_coefficient *. (float_of_int segment_duration) then
534 | let () =
535 | if debug then begin
536 | print_endline @@
537 | "repr increase based on delta_b > target_coefficient *. \
538 | (float_of_int segment_duration)";
539 | end
540 | in
541 | repr_plus
542 | else
543 | repr_prev
544 | in
545 | if repr_next_bba_1 <= suggested_repr then
546 | let () =
547 | if debug then begin
548 | print_endline @@ "suggested_repr (BBA-2): " ^ string_of_int suggested_repr;
549 | end
550 | in
551 | suggested_repr
552 | else begin
553 | let () =
554 | if debug then begin
555 | print_endline @@
556 | "repr_next_bba_1 (just switched to BBA-1): " ^
557 | string_of_int repr_next_bba_1;
558 | end
559 | in
560 | startup_phase := false;
561 | repr_next_bba_1
562 | end
563 | else begin
564 | let () =
565 | if debug then begin
566 | print_endline @@ "repr_next_bba_1: " ^ string_of_int repr_next_bba_1;
567 | end
568 | in
569 | startup_phase := false;
570 | repr_next_bba_1
571 | end
572 |
573 | end
574 |
575 | (* ARBITER: Adaptive rate-based intelligent HTTP streaming algorithm
576 | http://ieeexplore.ieee.org/abstract/document/7574709/ *)
577 | module ARBITER : sig
578 | type t = {
579 | maxb : float;
580 | chunk_sizes_per_repr : (int, int List.t) Hashtbl.t;
581 | }
582 |
583 | val next_representation_level :
584 | ?debug:bool ->
585 | algo:t ->
586 | representations:(int, representation) Hashtbl.t ->
587 | results:segm_result List.t ->
588 | int ->
589 | int
590 | end = struct
591 | type t = {
592 | maxb : float;
593 | chunk_sizes_per_repr : (int, int List.t) Hashtbl.t;
594 | }
595 |
596 | let next_representation_level
597 | ?(debug=false) ~algo ~representations ~results last_segment_index =
598 | if List.length results < 2 then 1
599 | else
600 | let total_number_of_downloaded_segments = List.length results in
601 | (* estimation_window is static parameter according to the paper *)
602 | let estimation_window = 10 in
603 | let window_size =
604 | if estimation_window < total_number_of_downloaded_segments then estimation_window
605 | else total_number_of_downloaded_segments
606 | in
607 | let segm_number_prev = total_number_of_downloaded_segments - 1 in
608 | (* exponential_weight is static parameter according to the paper *)
609 | let exponential_weight = 0.4 in
610 | let rec calculate_weights ~curr_index ~acc_weights =
611 | if curr_index >= window_size then
612 | List.rev acc_weights
613 | else
614 | let numerator =
615 | exponential_weight *. (1. -. exponential_weight) ** float_of_int curr_index in
616 | let denominator =
617 | 1. -. (1. -. exponential_weight) ** float_of_int estimation_window in
618 | let () =
619 | if debug then begin
620 | print_endline @@ "numerator: " ^ string_of_float numerator;
621 | print_endline @@ "denominator = " ^ string_of_float denominator;
622 | end
623 | in
624 | calculate_weights
625 | ~curr_index:(curr_index + 1)
626 | ~acc_weights:(numerator /. denominator :: acc_weights)
627 | in
628 | let weights = calculate_weights ~curr_index:0 ~acc_weights:[] in
629 | let rec calculate_weighted_throughput_mean ~curr_index ~acc =
630 | if curr_index >= window_size then
631 | acc
632 | else
633 | let result_ = List.nth_exn results curr_index in
634 | let measured_throughput =
635 | ((float_of_int result_.segment_size) *. 8. *. us_float *. us_float) /.
636 | float_of_int (result_.time_for_delivery) in
637 | let product = (List.nth_exn weights curr_index) *. measured_throughput in
638 | let () =
639 | if debug then begin
640 | print_endline @@ "segm_number_prev: " ^ string_of_int segm_number_prev;
641 | print_endline @@ "curr_index: " ^ string_of_int curr_index;
642 | print_endline @@
643 | "result_.segment_number: " ^ string_of_int result_.segment_number;
644 | print_endline @@
645 | "(List.nth_exn weights curr_index): " ^
646 | string_of_float (List.nth_exn weights curr_index);
647 | print_endline @@
648 | "measured_throughput = " ^ string_of_float measured_throughput;
649 | end
650 | in
651 | calculate_weighted_throughput_mean
652 | ~curr_index:(curr_index + 1)
653 | ~acc:(acc +. product)
654 | in
655 | let weighted_throughput_mean =
656 | calculate_weighted_throughput_mean ~curr_index:0 ~acc:0. in
657 |
658 | let rec calculate_throughput_variance ~curr_index ~acc =
659 | if curr_index >= window_size then
660 | float_of_int estimation_window *. acc /. (float_of_int estimation_window -. 1.)
661 | else
662 | let result_ = List.nth_exn results curr_index in
663 | let measured_throughput =
664 | ((float_of_int result_.segment_size) *. 8. *. us_float *. us_float) /.
665 | float_of_int (result_.time_for_delivery) in
666 | let () =
667 | if debug then begin
668 | print_endline @@
669 | "(result_.segment_size * 8 * us * us): " ^
670 | string_of_int (result_.segment_size * 8 * us * us);
671 | print_endline @@
672 | "result_.segment_size = " ^ string_of_int result_.segment_size;
673 | print_endline @@
674 | "result_.time_for_delivery = " ^ string_of_int result_.time_for_delivery;
675 | print_endline @@
676 | "measured_throughput = " ^ string_of_float measured_throughput;
677 | end
678 | in
679 | let next_sum =
680 | (List.nth_exn weights curr_index) *. ((measured_throughput -.
681 | weighted_throughput_mean) ** 2.) in
682 | calculate_throughput_variance
683 | ~curr_index:(curr_index + 1) ~acc:(acc +. next_sum)
684 | in
685 | let throughput_variance = calculate_throughput_variance ~curr_index:0 ~acc:0. in
686 | let variation_coefficient_theta =
687 | (sqrt throughput_variance) /.weighted_throughput_mean in
688 | (* bw_safety_factor is static parameter according to the paper *)
689 | let bw_safety_factor = 0.3 in
690 | let throughput_variance_scaling_factor =
691 | bw_safety_factor +.
692 | (1. -. bw_safety_factor) *. ((1. -. (min variation_coefficient_theta 1.)) ** 2.)
693 | in
694 |
695 | let buf_now =
696 | if (List.length results) = 0 then 0.
697 | else (List.hd_exn results).buffer_level_in_momentum
698 | in
699 | (* lower_buffer_bound,
700 | upper_buffer_bound are static parameters according to the paper *)
701 | let lower_buffer_bound, upper_buffer_bound = 0.5, 1.5 in
702 | let buffer_based_scaling_factor =
703 | lower_buffer_bound +.
704 | (upper_buffer_bound -. lower_buffer_bound) *. (buf_now /. algo.maxb) in
705 |
706 | let adaptive_throughput_estimate =
707 | weighted_throughput_mean *.
708 | throughput_variance_scaling_factor *.
709 | buffer_based_scaling_factor in
710 | let () =
711 | if debug then begin
712 | print_endline @@
713 | "weighted_throughput_mean (the same unit as Del_Rate) = " ^ string_of_int @@
714 | int_of_float @@ weighted_throughput_mean /. 1000.;
715 | print_endline @@
716 | "throughput_variance_scaling_factor = " ^
717 | string_of_float throughput_variance_scaling_factor;
718 | print_endline @@
719 | "buffer_based_scaling_factor = " ^
720 | string_of_float buffer_based_scaling_factor;
721 | print_endline @@
722 | "adaptive_throughput_estimate (the same unit as Del_Rate) = " ^
723 | string_of_int @@ int_of_float @@ adaptive_throughput_estimate /. 1000.;
724 | end
725 | in
726 |
727 | let rate_prev =
728 | (* this should never happen *)
729 | if (List.length results) = 0 then (Hashtbl.find_exn representations 1).bandwidth
730 | else
731 | (* when the representation rate is saved in a trace file,
732 | some level of precision will be lost,
733 | because original value of bit/s will be saved in kbit/s int type value,
734 | for example, 232385 will be saved as 232, and when it is read in debug mode
735 | it will look like 232000,
736 | so the repr_prev will be chosen based on a wrong value.
737 | To fix it this value 232000 (for example) will be converted into the closest
738 | within 5% the representation rate from the representations hash table *)
739 | if debug then Hashtbl.fold representations ~init:1 ~f:(fun ~key:_ ~data acc ->
740 | if (float_of_int data.bandwidth *. 0.95) <
741 | float_of_int (List.hd_exn results).representation_rate &&
742 | (float_of_int data.bandwidth *. 1.05) >
743 | float_of_int (List.hd_exn results).representation_rate
744 | then data.bandwidth
745 | else acc
746 | )
747 | else (List.hd_exn results).representation_rate
748 | in
749 | let () =
750 | if debug then begin
751 | print_endline @@ "rate_prev = " ^ string_of_int rate_prev;
752 | end
753 | in
754 | let repr_prev = Hashtbl.fold representations ~init:1 ~f:(fun ~key ~data acc ->
755 | if data.bandwidth = rate_prev then key
756 | else acc
757 | ) in
758 | let () =
759 | if debug then begin
760 | print_endline @@ "repr_prev = " ^ string_of_int repr_prev;
761 | end
762 | in
763 | let rate_min = (Hashtbl.find_exn representations 1).bandwidth in
764 | let () =
765 | if debug then begin
766 | print_endline @@ "rate_min = " ^ string_of_int rate_min;
767 | end
768 | in
769 | let s_rate = Hashtbl.fold representations ~init:rate_min ~f:(fun ~key:_ ~data acc ->
770 | let () =
771 | if debug then begin
772 | print_endline @@ "data.bandwidth = " ^ string_of_int data.bandwidth;
773 | end
774 | in
775 | if data.bandwidth > acc &&
776 | data.bandwidth < int_of_float adaptive_throughput_estimate then
777 | data.bandwidth
778 | else
779 | acc) in
780 | let s_repr = Hashtbl.fold representations ~init:1 ~f:(fun ~key ~data acc ->
781 | if data.bandwidth = s_rate then key else acc) in
782 | let () =
783 | if debug then begin
784 | print_endline @@ "s_rate = " ^ string_of_int s_rate;
785 | print_endline @@ "s_repr = " ^ string_of_int s_repr;
786 | end
787 | in
788 |
789 | (* up_switch_limit is static parameter according to the paper *)
790 | let up_switch_limit = 2 in
791 | let next_repr =
792 | if (s_repr - repr_prev) > up_switch_limit then
793 | repr_prev + up_switch_limit
794 | else
795 | s_repr
796 | in
797 | (* look_ahead_window is static parameter according to the paper *)
798 | let look_ahead_window =
799 | if (last_segment_index - total_number_of_downloaded_segments) < 5 then
800 | (last_segment_index - total_number_of_downloaded_segments)
801 | else
802 | 5
803 | in
804 | let segment_duration = ((Hashtbl.find_exn representations 1).segment_duration) in
805 | let rec calculate_actual_rate ~next_repr_candidate ~curr_index ~acc =
806 | if curr_index > look_ahead_window then
807 | acc * 8 / (look_ahead_window * segment_duration)
808 | else
809 | let next_segment_size =
810 | List.nth_exn
811 | (Hashtbl.find_exn algo.chunk_sizes_per_repr next_repr_candidate)
812 | (segm_number_prev + curr_index) in
813 | calculate_actual_rate
814 | ~next_repr_candidate:next_repr_candidate
815 | ~curr_index:(curr_index + 1)
816 | ~acc:(acc + next_segment_size)
817 | in
818 | let rec highest_possible_actual_rate ~next_repr_candidate =
819 | let actual_rate =
820 | calculate_actual_rate
821 | ~next_repr_candidate:next_repr_candidate
822 | ~curr_index:1 ~acc:0 in
823 | let () =
824 | if debug then begin
825 | print_endline @@ "actual_rate = " ^ string_of_int actual_rate;
826 | end
827 | in
828 | if next_repr_candidate > 1 &&
829 | not (actual_rate <= int_of_float adaptive_throughput_estimate) then
830 | highest_possible_actual_rate ~next_repr_candidate:(next_repr_candidate - 1)
831 | else
832 | next_repr_candidate
833 | in
834 | let actual_next_rate =
835 | highest_possible_actual_rate ~next_repr_candidate:next_repr in
836 | actual_next_rate
837 | end
838 |
839 | type alg =
840 | | Conv
841 | | BBA_0 of BBA_0.t
842 | | BBA_1 of BBA_1.t
843 | | BBA_2 of BBA_1.t
844 | | ARBITER of ARBITER.t
845 |
--------------------------------------------------------------------------------