Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Simon Cleriot
Simon Cleriot

Posted on

     

Video.js: frame-accurate subtitles

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:

Demo gif

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(){}});
Enter fullscreen modeExit fullscreen mode

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
Enter fullscreen modeExit fullscreen mode

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();
Enter fullscreen modeExit fullscreen mode

Demo project is availablehere.

Originally published onmy personal blog.

Top comments(0)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

  • Location
    Paris, France
  • Joined

More fromSimon Cleriot

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp