-
Notifications
You must be signed in to change notification settings - Fork 14
/
Copy pathfixed_env.py
160 lines (126 loc) · 5.92 KB
/
fixed_env.py
1
2
3
4
5
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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
import numpy as np
MILLISECONDS_IN_SECOND = 1000.0
B_IN_MB = 1000000.0
BITS_IN_BYTE = 8.0
RANDOM_SEED = 42
VIDEO_CHUNCK_LEN = 4000.0 # millisec, every time add this amount to buffer
BITRATE_LEVELS = 6
TOTAL_VIDEO_CHUNCK = 48
BUFFER_THRESH = 60.0 * MILLISECONDS_IN_SECOND # millisec, max buffer limit
DRAIN_BUFFER_SLEEP_TIME = 500.0 # millisec
PACKET_PAYLOAD_PORTION = 0.95
LINK_RTT = 80 # millisec
PACKET_SIZE = 1500 # bytes
VIDEO_SIZE_FILE = 'video/video_size_'
class Environment:
def __init__(self, all_cooked_time, all_cooked_bw, all_file_names, random_seed=RANDOM_SEED):
assert len(all_cooked_time) == len(all_cooked_bw)
self.all_file_names = all_file_names
np.random.seed(random_seed)
self.all_cooked_time = all_cooked_time
self.all_cooked_bw = all_cooked_bw
self.video_chunk_counter = 0
self.buffer_size = 0
# pick a random trace file
self.trace_idx = 0
self.cooked_time = self.all_cooked_time[self.trace_idx]
self.cooked_bw = self.all_cooked_bw[self.trace_idx]
self.mahimahi_start_ptr = 1
# randomize the start point of the trace
# note: trace file starts with time 0
self.mahimahi_ptr = 1
self.last_mahimahi_time = self.cooked_time[self.mahimahi_ptr - 1]
self.video_size = {} # in bytes
for bitrate in range(BITRATE_LEVELS):
self.video_size[bitrate] = []
with open(VIDEO_SIZE_FILE + str(bitrate)) as f:
for line in f:
self.video_size[bitrate].append(int(line.split()[0]))
def get_video_chunk(self, quality):
assert quality >= 0
assert quality < BITRATE_LEVELS
video_chunk_size = self.video_size[quality][self.video_chunk_counter]
# use the delivery opportunity in mahimahi
delay = 0.0 # in ms
video_chunk_counter_sent = 0 # in bytes
while True: # download video chunk over mahimahi
throughput = self.cooked_bw[self.mahimahi_ptr] * B_IN_MB / BITS_IN_BYTE
duration = self.cooked_time[self.mahimahi_ptr] - self.last_mahimahi_time
packet_payload = throughput * duration * PACKET_PAYLOAD_PORTION
if video_chunk_counter_sent + packet_payload > video_chunk_size:
fractional_time = (video_chunk_size - video_chunk_counter_sent) / throughput / PACKET_PAYLOAD_PORTION
delay += fractional_time
self.last_mahimahi_time += fractional_time
break
video_chunk_counter_sent += packet_payload
delay += duration
self.last_mahimahi_time = self.cooked_time[self.mahimahi_ptr]
self.mahimahi_ptr += 1
if self.mahimahi_ptr >= len(self.cooked_bw):
# loop back in the beginning
# note: trace file starts with time 0
self.mahimahi_ptr = 1
self.last_mahimahi_time = 0
delay *= MILLISECONDS_IN_SECOND
delay += LINK_RTT
# rebuffer time
rebuf = np.maximum(delay - self.buffer_size, 0.0)
# update the buffer
self.buffer_size = np.maximum(self.buffer_size - delay, 0.0)
# add in the new chunk
self.buffer_size += VIDEO_CHUNCK_LEN
# sleep if buffer gets too large
sleep_time = 0
if self.buffer_size > BUFFER_THRESH:
# exceed the buffer limit
# we need to skip some network bandwidth here
# but do not add up the delay
drain_buffer_time = self.buffer_size - BUFFER_THRESH
sleep_time = np.ceil(drain_buffer_time / DRAIN_BUFFER_SLEEP_TIME) * DRAIN_BUFFER_SLEEP_TIME
self.buffer_size -= sleep_time
while True:
duration = self.cooked_time[self.mahimahi_ptr] \
- self.last_mahimahi_time
if duration > sleep_time / MILLISECONDS_IN_SECOND:
self.last_mahimahi_time += sleep_time / MILLISECONDS_IN_SECOND
break
sleep_time -= duration * MILLISECONDS_IN_SECOND
self.last_mahimahi_time = self.cooked_time[self.mahimahi_ptr]
self.mahimahi_ptr += 1
if self.mahimahi_ptr >= len(self.cooked_bw):
# loop back in the beginning
# note: trace file starts with time 0
self.mahimahi_ptr = 1
self.last_mahimahi_time = 0
# the "last buffer size" return to the controller
# Note: in old version of dash the lowest buffer is 0.
# In the new version the buffer always have at least
# one chunk of video
return_buffer_size = self.buffer_size
self.video_chunk_counter += 1
video_chunk_remain = TOTAL_VIDEO_CHUNCK - self.video_chunk_counter
end_of_video = False
if self.video_chunk_counter >= TOTAL_VIDEO_CHUNCK:
end_of_video = True
self.buffer_size = 0
self.video_chunk_counter = 0
self.trace_idx += 1
if self.trace_idx >= len(self.all_cooked_time):
self.trace_idx = 0
self.cooked_time = self.all_cooked_time[self.trace_idx]
self.cooked_bw = self.all_cooked_bw[self.trace_idx]
# randomize the start point of the video
# note: trace file starts with time 0
self.mahimahi_ptr = self.mahimahi_start_ptr
self.last_mahimahi_time = self.cooked_time[self.mahimahi_ptr - 1]
next_video_chunk_sizes = []
for i in range(BITRATE_LEVELS):
next_video_chunk_sizes.append(self.video_size[i][self.video_chunk_counter])
return delay, \
sleep_time, \
return_buffer_size / MILLISECONDS_IN_SECOND, \
rebuf / MILLISECONDS_IN_SECOND, \
video_chunk_size, \
next_video_chunk_sizes, \
end_of_video, \
video_chunk_remain