You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

191 lines
5.1 KiB

import {
useFakeEnvironment,
useFakeMediaSource,
createPlayer,
openMediaSource,
standardXHRResponse,
} from '../../test/test-helpers.js';
// the number of seconds of video in each segment
const segmentDuration = 9; // seconds
// the number of segments in the video
const segmentCount = 100;
// the length of the simulation
const duration = segmentDuration * segmentCount;
// the number of seconds it takes for a single bit to be
// transmitted from the client to the server, or vice-versa
const propagationDelay = 0.5;
// send a mock playlist response
const playlistResponse = function(request) {
let match = request.url.match(/\d+/);
let bitrate = match[0];
let i = segmentCount;
let response =
'#EXTM3U\n' +
'#EXT-X-PLAYLIST-TYPE:VOD\n' +
'#EXT-X-TARGETDURATION:' + segmentDuration + '\n';
while (i--) {
response += '#EXTINF:' + segmentDuration + ',\n';
response += bitrate + '-' + (segmentCount - i) + '.ts\n';
}
response += '#EXT-X-ENDLIST\n';
return response;
};
// run the simulation
const runSimulation = function(options, done) {
// SETUP
let results = {
bandwidth: [],
effectiveBandwidth: [],
playlists: [],
buffered: [],
options: options
};
let t = 0;
let i = 0;
let env = useFakeEnvironment();
let clock = env.clock;
let requests = env.requests;
let mse = useFakeMediaSource();
let buffered = 0;
let currentTime = 0;
let player = window.player = createPlayer();
document.querySelector('#qunit-fixture').style = 'display: none;';
player.src({
src: 'http://example.com/master.m3u8',
type: 'application/x-mpegurl'
});
openMediaSource(player, clock);
// run next tick so that Flash doesn't swallow exceptions
let master = '#EXTM3U\n';
options.playlists.forEach((bandwidth) => {
master+= '#EXT-X-STREAM-INF:BANDWIDTH=' + bandwidth + '\n';
master += 'playlist-' + bandwidth + '.m3u8\n';
});
// simulate buffered and currentTime during playback
let getBuffer = (buff) => {
return videojs.createTimeRange(0, currentTime + buffered);
};
player.tech_.buffered = getBuffer;
Object.defineProperty(player.tech_, 'time_', {
get: () => currentTime
});
options.bandwidths.sort(function(left, right) {
return left.time - right.time;
});
// respond to the playlist requests
let masterRequest = requests.shift();
masterRequest.bandwidth = options.bandwidths[0].bandwidth;
masterRequest.respond(200, null, master);
let playlistRequest = requests.shift();
playlistRequest.bandwidth = options.bandwidths[0].bandwidth;
playlistRequest.respond(200, null, playlistResponse(playlistRequest));
let sourceBuffer = player.tech_.hls.mediaSource.sourceBuffers[0];
Object.defineProperty(sourceBuffer, 'buffered', {
get: () => buffered
});
// record the measured bandwidth for the playlist requests
results.effectiveBandwidth.push({
time: 0,
bandwidth: player.tech_.hls.bandwidth
});
// advance time and collect simulation results
for (t = i = 0; t < duration; clock.tick(1000), t++) {
while (options.bandwidths[i + 1] && options.bandwidths[i + 1].time <= t) {
i++;
}
let bandwidth = options.bandwidths[i].bandwidth;
results.bandwidth.push({
time: t,
bandwidth: bandwidth
});
// schedule response deliveries
while (requests.length) {
let request = requests.shift();
request.bandwidth = bandwidth;
// playlist responses
if (/\.m3u8$/.test(request.url)) {
// for simplicity, playlist responses have zero trasmission time
request.respond(200, null, playlistResponse(request));
continue;
}
// segment responses
let segmentSize = request.url.match(/(\d+)-\d+/)[1] * segmentDuration;
//console.log(segmentSize);
//console.log(bandwidth);
console.log(request.url);
let timeToTake = segmentSize/bandwidth + (propagationDelay * 1);
setTimeout(() => {
if (request.aborted) {
console.error("Request for segment aborted, download timedout")
return;
}
request.response = new Uint8Array(segmentSize * 0.125);
request.respond(200, null, '');
sourceBuffer.trigger('updateend');
results.playlists.push({
time: t,
bitrate: +request.url.match(/(\d+)-\d+/)[1]
});
buffered += segmentDuration;
results.effectiveBandwidth.push({
time: t,
bandwidth: player.tech_.hls.bandwidth
});
}, timeToTake * 1000);
// console.log(`taking ${timeToTake}s for response`);
}
results.buffered.push({
time: t,
buffered: buffered
});
// simulate playback
if (buffered > 0) {
buffered--;
currentTime++;
}
player.trigger('timeupdate');
}
// update the fragment identifier so this scenario can be re-run easily
window.location.hash = '#' + options.bandwidths.map(function(interval) {
return 't' + interval.time + '=' + interval.bandwidth;
}).join('&');
player.dispose();
mse.restore();
env.restore();
console.log(results);
done(null, results);
};
export default runSimulation;