├── 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 | --------------------------------------------------------------------------------