Veljko Stefanovic
Veljko Stefanovic

Reputation: 511

Display subtitles based on current video timestamp

I'm trying to build a player that will display subtitles and will work in all browsers, since support for html5 video subtitles is still in alpha/beta phase and it doesn't work or is faulty in different browsers.

Each subtitle line has a start and end time. The current time for the video is known (this.state.currentTime).

My question is what my approach should be?

Here is what I've tried:

/*Stuck here*/
let currTime = (this.state.currentTime * 1000); //In ms.

let subLine = (currTime < 904) ? "blah" : "trt";

let inc = 0;

if (this.state.subs) {

    let subDur = 0;

    setInterval(() => {

        while (inc < this.state.subs.length) {
            subDur = this.state.subs[inc].end - this.state.subs[inc].start;
            if (currTime <= this.state.subs[inc].end && currTime >= this.state.subs[inc].start) {
                console.log(this.state.subs[inc].text);
            }

            if (currTime === this.state.subs[inc].end) {
                inc++;
            }

        }

    }, subDur);
    console.log(currTime);
    console.log(inc);
}

this.state.subs contain a array of subtitles with their accompanying starts and ends:

[
  {
    start: 20000, // time in ms
    end: 24400,
    text: 'Bla Bla Bla Bla'
  },
  {
    start: 24600,
    end: 27800,
    text: 'Bla Bla Bla Bla',
    settings: 'align:middle line:90%' // WebVTT only
  }
]

I've used this library for getting info from .srt files.

Upvotes: 1

Views: 1146

Answers (2)

frobinsonj
frobinsonj

Reputation: 1167

You can make use of the video's timeupdate event (which has good browser support). This will fire when the time indicated by the currentTime attribute has been updated.

In the new function handSubtitles(), we first check the active subtitle to see if it's ended (there's no point in searching for a new subtitle if the current one hasn't finished). Next, we loop through all of the subtitles (this allows us to go forward and back in the video) to check for the next subtitle. If it's found, we display it and then update activeSubtitle.

Warning: This has only been tested with small numbers of subtitles. Further optimisation may be required. It's more of a proof of concept.

function handleSubtitles(currentTime) {
  // If there is an active subtitle and it should be hidden
  if (activeSubtitle !== null && (currentTime >= subtitles[activeSubtitle].end || currentTime < subtitles[activeSubtitle].start)) {
    subtitleDisplay.innerText = ''; // Hide subtitle
    activeSubtitle = null;
  }

  // If there is no active subtitle
  if (activeSubtitle === null) {
    for (var i = 0; i < subtitleCount; i++) {
      if (currentTime >= subtitles[i].start && currentTime < subtitles[i].end) {
        subtitleDisplay.innerText = subtitles[i].text; // Show subtitle
        activeSubtitle = i;
        break;
      }
    }
  }
}

const video = document.getElementById('test-video');
const subtitleDisplay = document.getElementById('subtitle');

let activeSubtitle = null;

const subtitles = [{
    start: 500,
    end: 2000,
    text: 'Start at .5 secs end at 2 secs'
  },
  {
    start: 2500,
    end: 5000,
    text: 'Start at 2.5 secs end at 5 secs'
  },
  {
    start: 5000,
    end: 10000,
    text: 'Start at 5 secs end at 10 secs'
  }
];

const subtitleCount = subtitles.length;

if (subtitleCount) {
  video.addEventListener('timeupdate', () => handleSubtitles(video.currentTime * 1000));
}
<video id="test-video" width="400" controls>
  <source src="http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4" type="video/mp4">
</video>
<p id="subtitle"></p>

Upvotes: 1

Veljko Stefanovic
Veljko Stefanovic

Reputation: 511

Sorted. Here's a working code:

        let currTime = (this.state.currentTime*1000);//In ms.
        let inc = 0;

        if(this.state.subs){

            let subDur = 0;

            while(inc<this.state.subs.length){
                subDur = this.state.subs[inc].end-this.state.subs[inc].start;

                let start = this.state.subs[inc].start;
                let end = this.state.subs[inc].end;

                if(currTime<=end && currTime>=start){

                    this.setState({
                        line: this.state.subs[inc].text,
                    });

                }

                if(currTime>=end){

                    this.setState({
                        line: "",
                    });

                }

                inc++;
            }

        }

Upvotes: 0

Related Questions