docs: function addPlayer(video, hasAudio) {
  	const player = {};
  	video[IIV] = player;
  	player.paused = true; // Track whether 'pause' events have been fired
  	player.hasAudio = hasAudio;
  	player.video = video;
  	player.updater = frameIntervalometer(update.bind(player));
  
  	if (hasAudio) {
  		player.driver = getAudioFromVideo(video);
  	} else {
  		video.addEventListener('canplay', () => {
  			if (!video.paused) {
  				// // console.log('oncanplay');
  				dispatchEventAsync(video, 'playing');
  			}
  		});
  		player.driver = {
  			src: video.src || video.currentSrc || 'data:',
  			muted: true,
  			paused: true,
  			pause: () => {
  				player.driver.paused = true;
  			},
  			play: () => {
  				player.driver.paused = false;
  				// Media automatically goes to 0 if .play() is called when it's done
  				if (isPlayerEnded(player)) {
  					setTime(video, 0);
  				}
  			},
  			get ended() {
  				return isPlayerEnded(player);
  			}
  		};
  	}
  
  	// .load() causes the emptied event
  	video.addEventListener('emptied', () => {
  		// // console.log('driver src is', player.driver.src);
  		const wasEmpty = !player.driver.src || player.driver.src === 'data:';
  		if (player.driver.src && player.driver.src !== video.src) {
  			// // console.log('src changed to', video.src);
  			setTime(video, 0, true);
  			player.driver.src = video.src;
  			// Playing videos will only keep playing if no src was present when .play()’ed
  			if (wasEmpty || (!hasAudio && video.autoplay)) {
  				player.driver.play();
  			} else {
  				player.updater.stop();
  			}
  		}
  	}, false);
  
  	// Stop programmatic player when OS takes over
  	video.addEventListener('webkitbeginfullscreen', () => {
  		if (!video.paused) {
  			// Make sure that the <audio> and the syncer/updater are stopped
  			video.pause();
  
  			// Play video natively
  			video[IIVPlay]();
  		} else if (hasAudio && player.driver.buffered.length === 0) {
  			// If the first play is native,
  			// the <audio> needs to be buffered manually
  			// so when the fullscreen ends, it can be set to the same current time
  			player.driver.load();
  		}
  	});
  	if (hasAudio) {
  		video.addEventListener('webkitendfullscreen', () => {
  			// Sync audio to new video position
  			player.driver.currentTime = video.currentTime;
  			// // console.assert(player.driver.currentTime === video.currentTime, 'Audio not synced');
  		});
  
  		// Allow seeking
  		video.addEventListener('seeking', () => {
  			if (lastRequests.indexOf(video.currentTime * 100 | 0 / 100) < 0) {
  				// // console.log('User-requested seeking');
  				player.driver.currentTime = video.currentTime;
  			}
  		});
  	}
  }