Hi! Now we are preparing ourselves for reading the content of the different text tracks and display them.
But before that, I must introduce what we call the TextTrack object that is a JavaScript object that is associated to the HTML elements.
A track has two different views... maybe it is simpler to say that it has got a HTML view that means you can do a getElementById and manipulate the HTML element, this track element here, from JavaScript.
Or we can also work with its twin brother that is a text track, and this new view is the one we are going to use forforcing a track to be loaded, and for reading its content.
And also for forcing subtitles or caption track to be displayed.
So we just slightly modify the previous example by displaying the mode.
The mode is a property from the TextTrack, not from the HTML track.
And this mode can be "disabled", "showing" or "hidden".
And when it is disabled, reading the video will not fire any event related to the track.
We will talk about events later but a disabled track is the same if we have no track at all.
A track that is "showing" is displayed in the video, if the implementation of the video playersupports that.
And a track that is "hidden" is just not displayed.
So how did we manipulated and accessed this mode property?
The displayTrackStatuses function, that we wrote earlier, displayed the different properties of the HTML track, like the label, the kind or the language.
This time, we accessed his twin brother, the TextTrack by using the track property.
Every HTML track element has a track property that is a TextTrack.
So here, from the current HTML track, I am getting the TextTrack (currentTextTrack).
This is the object we use to access the mode and display it here.
Another interestingthing is that if we set the mode, if we modify the value of the mode, from "disabled" to "showing"or to "hidden", it will force the track to be loaded asynchronously in the background bythe browser.
So we added in this example two buttons, "force load track 0" and "force load track 2" because by default, the track 0, the English subtitles, is not loaded.
And the chapters, in track number 2, are not loaded either.
We are going to force the track 0 to be loaded.
If I click here "force load track 0", you see that the status changes - the mode changes to "hidden" and the track now is loaded.
So what happened in the background?
Let's have a look at the code we wrote.
So I am going to zoom a little bit...
The button we clicked is this one: "force load track 0" here, called a function named forceLoadTrack(0)that I prepared.
What does this function do?
It will call another function called getTrack that will check if the track is already loaded.
If it is already loaded, then the second parameter here, is a callback function,
It will be called because the track is ready to be read.
In the case the track has not been loaded, we will set the mode to "hidden" and then we will trigger the browser so that it will load asynchronously, in the background, the track.
And when the track is ready, then, and only then, we will call readContent.
Let's have a look at this getTrack function that we wrote.
It says getTrack, please load me the TextTracks corresponding to the HTML track number n.
So here is the function.
The first thing we do is that from the HTML track, we get the text track.
Then we check on the HTML track if it is already loaded.
If it is the case, then we will call the function that has been passed as the second parameter: it's the readContent.
And the readContent is just here, for the moment it will not read the content really, but it will just update the status.
So if I click on « force load track 2 » for example, it will load the track and when the track is arrived, it will call the displayStatus() that will show the updated status of the track.
In the case the track is not here, the readyState is not equal to 2, then we will force the track to be loaded.
By doing this we set the mode to "hidden".
This may will take some time: you understand that the browser is loading on the Web the track.
It may take 2 seconds for example.
So we need to have a listener that will listen to the load event.
So htmlTrack.addEventListenner('load'...) will trigger only when the track has been loaded, and only in that case we will call the callback function: the readContent that has been passed in the second parameter, in order to read the track.
If I look at the console, and if I start again the application.
Only the second track has been loaded, I click "force load track 0", it says "forcing the track to be loaded", it loads the track and it calls the callback "reading content of loaded track".
If I click again the same button, it says "the text track is already loaded" and I am going to read it now.
We cannot load a track several times, if it is already loaded, we must just use it.
In the next video, we will show how we can effectively read the content of the track and do something with it.
The object that contains the cues (subtitles or captions or chapter description from the WebVTT file) is not the HTML track itself. It is another object that is associated with it: a TextTrack object!
The TextTrack JavaScript object has different methods and properties for manipulating track content, and is associated with different events. But before going into detail, let's see how to obtain a TextTrack object.
The HTML track element has a track property which returns the associated TextTrack object. Example source code:
// HTML tracks
var htmlTracks = document.querySelectorAll("track");
// The TextTrack object associated with the first HTML track
var textTrack = htmlTracks[0].track;
var kind = textTrack.kind;
var label = textTrack.label;
var lang = textTrack.language;
// etc.
Note that once we get a TextTrack object, we can manipulate the kind, label, language attributes (be careful, it's not srclang, like the equivalent attribute name for HTML tracks). Other attributes and methods are described later in this lesson.
The <video> element (and <audio> element too) has a TextTrack property accessible from JavaScript:
var videoElement = document.querySelector("#myVideo");
var textTracks = videoElement.textTracks; // one TextTrack for each HTML track element
var textTrack = textTracks[0]; // corresponds to the first track element
var kind = textTrack.kind // e.g. "subtitles"
var mode = textTrack.mode // e.g. "disabled", "hidden" or "showing"
TextTrack objects have a mode property, that is set to one of:
"showing": the track is either already loaded, or is being loaded by the browser. As soon as it is completely loaded, subtitles or captions will be displayed in the video. Other kinds of track will be loaded but will not necessarily show anything visible in the document. All tracks that have mode="showing" will fire events while the video is being played.
"hidden": the track is either already loaded, or is being loaded by the browser. All tracks that have mode="hidden" will fire events while the video is being played. Nothing will be visible in the standard video player GUI.
"disabled": this is the mode where tracks are not being loaded. If a loaded track has its mode set to "disabled", it will stop firing events, and if it was in mode="showing" the subtitles or captions will stop being displayed in the video player.
TextTrack content can only be accessed if a track has been loaded! Use the mode property to force a track to be loaded!
Here is an example that will test if a track has been loaded, and if it hasn't, will force it to be loaded by setting its mode to "hidden". We could have used "showing"; in this case, if the file is a subtitle or a caption file, then the subtitles or captions will be displayed on the video as soon as the track has finished loading.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Using HTML views of tracks</title>
</head>
<body>
<video id="myVideo" preload="metadata" controls crossOrigin="anonymous">
<source src="https://mainline.i3s.unice.fr/mooc/elephants-dream-medium.mp4" type="video/mp4">
<source src="https://mainline.i3s.unice.fr/mooc/elephants-dream-medium.webm" type="video/webm">
<track label="English subtitles" kind="subtitles" srclang="en"
src="https://mainline.i3s.unice.fr/mooc/elephants-dream-subtitles-en.vtt" >
<track label="Deutsch subtitles" kind="subtitles" srclang="de"
src="https://mainline.i3s.unice.fr/mooc/elephants-dream-subtitles-de.vtt" default>
<track label="English chapters" kind="chapters" srclang="en"
src="https://mainline.i3s.unice.fr/mooc/elephants-dream-chapters-en.vtt">
</video>
<h3>HTML track descriptions</h3>
<button id="buttonLoadFirstTrack" onclick="forceLoadTrack(0);" disabled>Force load track 0</button>
<button id="buttonLoadThirdTrack" onclick="forceLoadTrack(2);" disabled>Force load track 2</button><p>
<div id="trackStatusesDiv">
</div>
</body>
</html>
Here is what we added to the HTML code:
<button id="buttonLoadFirstTrack"
onclick="forceLoadTrack(0);"
disabled>
Force load track 0
</button>
<button id="buttonLoadThirdTrack"
onclick="forceLoadTrack(2);"
disabled>
Force load track 2
</button>
The buttons will call a function named forceLoadTrack(trackNumber) that takes as a parameter the number of the track to get (and force load if necessary).
Here are the additions we made to the JavaScript code from the previous example:
function readContent(track) {
console.log("reading content of loaded track...");
displayTrackStatuses(htmlTracks); // update document with new track statuses
}
function getTrack(htmlTrack, callback) {
// TextTrack associated to the htmlTrack
var textTrack = htmlTrack.track;
if(htmlTrack.readyState === 2) {
console.log("text track already loaded");
// call the callback function, the track is available
callback(textTrack);
} else {
console.log("Forcing the text track to be loaded");
// this will force the track to be loaded
textTrack.mode = "hidden";
// loading a track is asynchronous, we must use an event listener
htmlTrack.addEventListener('load', function(e) {
// the track is arrived, call the callback function
callback(textTrack);
});
}
}
function forceLoadTrack(n) {
// first parameter = track number,
// second = a callback function called when the track is loaded,
// that takes the loaded TextTrack as parameter
getTrack(htmlTracks[n], readContent);
Lines 26-31: the function called when a button has been clicked. This function in turn calls the getTrack(trackNumber, callback) function. It passes the readContent callback function as a parameter. This is typical JavaScript asynchronous programming: the getTrack() function may force the browser to load the track and this can take some time (a few seconds), then when the track has downloaded, we ask the getTrack function to call the function we passed (the readContent function, which is known as a callback function), with the loaded track as a parameter.
Line 6: the getTrack function. It first checks if the HTML track is already loaded (line 10). If it is, it calls the callback function passed by the caller, with the loaded TextTrack as a parameter. If the TextTrack is not loaded, then it sets its mode to "hidden". This will instruct the browser to load the track. Because that may take some time, we must use a load event listener on the HTML track before calling the callback function. This allows us to be sure that the track is really completely loaded.
Lines 1-4: the readContent function is only called with a loaded TextTrack. Here we do nothing special for the moment except that we refresh the different track statuses in the HTML document.