Multiple video files is the way to go. Currently, the most common approach to adaptive streaming is MPEG-DASH. The different video sizes and a MPD manifest, which is like a playlist for the different video sizes, can be generated using ffmpeg and mp4box. Many videoplayers, e.g. Video.js or Dash.js support adaptive streaming with MPEG-DASH.
Generate video files:
ffmpeg -y -i movie.avi -an -c:v libx264 -x264opts 'keyint=24:min-keyint=24:no-scenecut' -b:v 1500k -maxrate 1500k -bufsize 3000k -vf "scale=-1:720" movie-720.mp4
ffmpeg -y -i movie.avi -an -c:v libx264 -x264opts 'keyint=24:min-keyint=24:no-scenecut' -b:v 800k -maxrate 800k -bufsize 1600k -vf "scale=-1:540" movie-540.mp4
ffmpeg -y -i movie.avi -an -c:v libx264 -x264opts 'keyint=24:min-keyint=24:no-scenecut' -b:v 400k -maxrate 400k -bufsize 800k -vf "scale=-1:360" movie-360.mp4
ffmpeg -y -i movie.avi -vn -c:a aac -b:a 128k movie.m4a
Generate the manifest:
mp4box -dash-strict 2000 -rap -frag-rap -bs-switching no -profile "dashavc264:live" -out movie-dash.mpd movie-720.mp4 movie-540.mp4 movie-360.mp4 movie.m4a
Original source: https://gist.github.com/andriika/8da427632cf6027a3e0036415cce5f54