-
Notifications
You must be signed in to change notification settings - Fork 2
/
env.py
255 lines (215 loc) · 9.28 KB
/
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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
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
NOISE_LOW = 0.9
NOISE_HIGH = 1.1
VIDEO_SIZE_FILE = "./video_size/ori/video_size_"
def initialize_tasks(task_list, all_file_names):
task2idx = {}
for task_id in task_list:
for trace_id in range(len(all_file_names)):
if task_id in all_file_names[trace_id]:
try:
task2idx[task_id].append(trace_id)
except:
task2idx[task_id] = []
task2idx[task_id].append(trace_id)
# assert(len(task2idx)==len(task_list))
return task2idx
class Environment:
def __init__(
self,
all_cooked_time,
all_cooked_bw,
random_seed=RANDOM_SEED,
all_file_names=None,
a2br=False,
):
assert len(all_cooked_time) == len(all_cooked_bw)
np.random.seed(random_seed)
self.task_list = [
"bus.ljansbakken",
"car.snaroya",
"ferry.nesoddtangen",
"metro.kalbakken",
"norway_bus",
"norway_car",
"norway_metro",
"norway_train",
"norway_tram",
"amazon",
"yahoo",
"facebook",
"youtube",
]
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
if all_file_names is not None:
self.task2idx = initialize_tasks(self.task_list, all_file_names)
self.task_id = int(0)
self.a2br_flag = a2br
if self.a2br_flag:
idx_member = self.task2idx[self.task_list[self.task_id]]
idx_str = np.random.randint(len(idx_member))
self.trace_idx = idx_member[idx_str]
else:
self.trace_idx = np.random.randint(len(self.all_cooked_time))
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 trace
# note: trace file starts with time 0
self.mahimahi_ptr = np.random.randint(1, len(self.cooked_bw))
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 reset(self):
if self.a2br_flag:
idx_member = self.task2idx[self.task_list[self.task_id]]
idx_str = np.random.randint(len(idx_member))
self.trace_idx = idx_member[idx_str]
else:
self.trace_idx = np.random.randint(len(self.all_cooked_time))
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 = np.random.randint(1, len(self.cooked_bw))
self.last_mahimahi_time = self.cooked_time[self.mahimahi_ptr - 1]
self.video_chunk_counter = 0
self.buffer_size = 0
def set_task(self, idx):
self.task_id = int(idx)
idx_member = self.task2idx[self.task_list[self.task_id]]
idx_str = np.random.randint(len(idx_member))
self.trace_idx = idx_member[idx_str]
self.cooked_time = self.all_cooked_time[self.trace_idx]
self.cooked_bw = self.all_cooked_bw[self.trace_idx]
return self.task_id == len(self.task_list) - 1
def reset_task(self):
self.task_id = int(0)
def get_video_size(self):
return self.video_size
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
assert self.last_mahimahi_time <= self.cooked_time[self.mahimahi_ptr]
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 = self.cooked_time[self.mahimahi_ptr - 1]
delay *= MILLISECONDS_IN_SECOND
delay += LINK_RTT
# add a multiplicative noise to the delay
delay *= np.random.uniform(NOISE_LOW, NOISE_HIGH)
# 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 = self.cooked_time[self.mahimahi_ptr - 1]
# 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
# pick a random trace file
if self.a2br_flag:
idx_member = self.task2idx[self.task_list[self.task_id]]
idx_str = np.random.randint(len(idx_member))
self.trace_idx = idx_member[idx_str]
else:
self.trace_idx = np.random.randint(len(self.all_cooked_time))
if self.trace_idx >= len(self.all_cooked_time):
self.trace_idx = 0
# self.trace_idx = np.random.randint(len(self.all_cooked_time))
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 = np.random.randint(1, len(self.cooked_bw))
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,
)