Schedule XHR responses instead of delivering them on the second
Bandwidth calculation uses the length of the XHR so rounding delivery up to the nearest second can skew results. Instead, calculate the delivery time of XHRs and schedule a timeout to set the response body.
Showing
1 changed file
with
50 additions
and
49 deletions
... | @@ -111,7 +111,7 @@ | ... | @@ -111,7 +111,7 @@ |
111 | fakeXhr = sinon.useFakeXMLHttpRequest(); | 111 | fakeXhr = sinon.useFakeXMLHttpRequest(); |
112 | requests = []; | 112 | requests = []; |
113 | fakeXhr.onCreate = function(xhr) { | 113 | fakeXhr.onCreate = function(xhr) { |
114 | xhr.startTime = t; | 114 | xhr.startTime = +new Date(); |
115 | xhr.delivered = 0; | 115 | xhr.delivered = 0; |
116 | requests.push(xhr); | 116 | requests.push(xhr); |
117 | }; | 117 | }; |
... | @@ -159,10 +159,8 @@ | ... | @@ -159,10 +159,8 @@ |
159 | return left.time - right.time; | 159 | return left.time - right.time; |
160 | }); | 160 | }); |
161 | 161 | ||
162 | // advance time and collect simulation results | 162 | // pre-calculate the bandwidth at each second |
163 | for (t = i = 0; t < duration; clock.tick(1 * 1000), t++) { | 163 | for (t = i = 0; t < duration; t++) { |
164 | |||
165 | // determine the bandwidth value at this moment | ||
166 | while (bandwidths[i + 1] && bandwidths[i + 1].time <= t) { | 164 | while (bandwidths[i + 1] && bandwidths[i + 1].time <= t) { |
167 | i++; | 165 | i++; |
168 | } | 166 | } |
... | @@ -170,52 +168,55 @@ | ... | @@ -170,52 +168,55 @@ |
170 | time: t, | 168 | time: t, |
171 | bandwidth: bandwidths[i].bandwidth | 169 | bandwidth: bandwidths[i].bandwidth |
172 | }); | 170 | }); |
171 | } | ||
173 | 172 | ||
174 | // deliver responses if they're ready | 173 | // advance time and collect simulation results |
175 | requests.forEach(function(request, i) { | 174 | for (t = 0; t < duration; clock.tick(1000), t++) { |
176 | var arrival = request.startTime + propagationDelay, | 175 | // schedule response deliveries |
177 | segmentSize; | 176 | while (requests.length) { |
178 | 177 | (function(request) { | |
179 | // playlist responses | 178 | var segmentSize; |
180 | if (/playlist-\d+$/.test(request.url)) { | 179 | |
181 | // for simplicity, playlist responses have zero trasmission time | 180 | // playlist responses |
182 | if (t === arrival) { | 181 | if (/playlist-\d+$/.test(request.url)) { |
183 | request.respond(200, null, playlistResponse(+requests[0].url.match(/\d+$/))); | 182 | // for simplicity, playlist responses have zero trasmission time |
184 | // the request is completed | 183 | return setTimeout(function() { |
185 | return requests.splice(requests.indexOf(request), 1); | 184 | request.respond(200, null, playlistResponse(+request.url.match(/\d+$/))); |
185 | }, propagationDelay * 1000); | ||
186 | } | 186 | } |
187 | return; | 187 | |
188 | } | 188 | // segment responses |
189 | 189 | segmentSize = +request.url.match(/(\d+)-\d+$/)[1] * segmentDuration; | |
190 | // segment responses | 190 | // segment response headers arrive after the propogation delay |
191 | segmentSize = +request.url.match(/(\d+)-\d+$/)[1] * segmentDuration; | 191 | setTimeout(function() { |
192 | // segment response headers arrive after the propogation delay | 192 | var arrival = Math.ceil(+new Date() * 0.001); |
193 | if (t === arrival) { | 193 | results.playlists.push({ |
194 | results.playlists.push({ | 194 | time: arrival, |
195 | time: t, | 195 | bitrate: +request.url.match(/(\d+)-\d+$/)[1] |
196 | bitrate: +request.url.match(/(\d+)-\d+$/)[1] | 196 | }); |
197 | }); | 197 | request.setResponseHeaders({ |
198 | request.setResponseHeaders({ | 198 | 'Content-Type': 'video/mp2t' |
199 | 'Content-Type': 'video/mp2t' | 199 | }); |
200 | }); | 200 | |
201 | } | 201 | results.bandwidth.slice(arrival).every(function(value, i) { |
202 | // send the response body if all bytes have been delivered | 202 | var remaining = segmentSize - request.delivered; |
203 | if (request.delivered >= segmentSize) { | 203 | if (remaining - value.bandwidth <= 0) { |
204 | buffered += segmentDuration; | 204 | // send the response body once all bytes have been delivered |
205 | request.status = 200; | 205 | setTimeout(function() { |
206 | request.response = new Uint8Array(segmentSize * 0.125); | 206 | buffered += segmentDuration; |
207 | request.setResponseBody(''); | 207 | request.status = 200; |
208 | // the request is completed | 208 | request.response = new Uint8Array(segmentSize * 0.125); |
209 | return requests.splice(requests.indexOf(request), 1); | 209 | request.setResponseBody(''); |
210 | } | 210 | }, ((remaining / value.bandwidth) + i) * 1000); |
211 | // transmit the bits for this tick | 211 | return false; |
212 | if (t >= arrival) { | 212 | } |
213 | request.delivered += results.bandwidth[t].bandwidth; | 213 | // record the bits for this tick |
214 | 214 | request.delivered += value.bandwidth; | |
215 | } | 215 | return true; |
216 | // response has not arrived fully | 216 | }); |
217 | return; | 217 | }, propagationDelay * 1000); |
218 | }, []); | 218 | })(requests.shift()); |
219 | } | ||
219 | 220 | ||
220 | results.buffered.push({ | 221 | results.buffered.push({ |
221 | time: t, | 222 | time: t, | ... | ... |
-
Please register or sign in to post a comment