Reputation: 511
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
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
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