In the post-production and broadcast industry videos are processed frame by frame.
Subtitles follow the same rule : in and out timecodes are frame accurate (ex:00:00:23:22
-> 23 seconds and 22 images).
Browsers process videos with millisecond timestamps, that's why subtitles have to be converted based on video framerate: for video at 25 images per second the previous timecode would look like00:00:23.880
in milliseconds format (1000/25 * 22 = 880
).
Frame accuracy is really important : subtitles need to disappear right before the next cut and appear right after the previous one.
Problem is that default HTML5 video's refresh rate is too low: subtitles appear and disappear too late creating a poor viewer's experience (and a non-broadcast compliant one).
Example below higlights the issue. Subtitles in the fixed video disappear right before the change of plan:
It might seem marginal, but the post production industry needs frame accuracy.
Due to browser limitations,timeupdate
(event fired when the playing position of a video has changed) is fired every 150-250 milliseconds. It is not enough for frame-accuracy: 25fps means an update every 40ms.
We need to compute which subtitle has to be displayed every frame (instead of doing it every 4-5 frames by default). Video.js subtitles engine does the computation each time the text track's attributeactiveCues
getter is called.
text-track.js extract from Video.js source code:
Object.defineProperty(tt,'activeCues',{get(){if(!this.loaded_){returnnull;}// nothing to doif(this.cues.length===0){returnactiveCues;}constct=this.tech_.currentTime();constactive=[];for(leti=0,l=this.cues.length;i<l;i++){constcue=this.cues[i];if(cue.startTime<=ct&&cue.endTime>=ct){active.push(cue);}elseif(cue.startTime===cue.endTime&&cue.startTime<=ct&&cue.startTime+0.5>=ct){active.push(cue);}}changed=false;if(active.length!==this.activeCues_.length){changed=true;}else{for(leti=0;i<active.length;i++){if(this.activeCues_.indexOf(active[i])===-1){changed=true;}}}this.activeCues_=active;activeCues.setCues_(this.activeCues_);returnactiveCues;},set(){}});
Then you need to calltrigger('cuechange')
on the text track to make sure the video display is up to date:
player.textTracks()[0].activeCues;// computes the current subtitle based on current timeplayer.textTracks()[0].trigger('cuechange');// updates the display
requestAnimationFrame
is optimized for animations and has a lot less delay thansetInterval
orsetTimeout
, so we are going to use it for our time sensitive loop (Frame rate control source here).
Here is the complete source code:
varfps=25;varnow;varthen=Date.now();varinterval=1000/fps;vardelta;functionreloadCues(){requestAnimationFrame(reloadCues);now=Date.now();delta=now-then;if(delta>interval){then=now-(delta%interval);if(videojs.players.player.textTracks().length==1){videojs.players.player.textTracks()[0].activeCues;videojs.players.player.textTracks()[0].trigger('cuechange')}}}reloadCues();
Demo project is availablehere.
Originally published onmy personal blog.
Top comments(0)
For further actions, you may consider blocking this person and/orreporting abuse