mirror of
https://github.com/videojs/video.js.git
synced 2025-07-05 00:58:52 +02:00
feat: allow displaying of multiple text tracks at once (#5817)
This allows the user to display multiple tracks when `allowMultipleShowingTracks` is passed to the `TextTrackDisplay`. Currently, multiple tracks must be shown programmatically and cannot be done via the subtitles menus. In addition, this adds two new classes to cue elements: `vjs-text-track-cue` and `vjs-text-track-cue-${track.language}`. This allows easier targetting with CSS. Example usage: ```js var player = videojs('example-video', { textTrackDisplay: { allowMultipleShowingTracks: true } }); ``` Fixes #5798.
This commit is contained in:
60
sandbox/double-sub-video.html.example
Normal file
60
sandbox/double-sub-video.html.example
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>Video.js Text Descriptions, Chapters & Captions Example</title>
|
||||||
|
|
||||||
|
<!-- Load the source files -->
|
||||||
|
<link href="../dist/video-js.css" rel="stylesheet" type="text/css">
|
||||||
|
<script src="../dist/video.js"></script>
|
||||||
|
|
||||||
|
<!-- Set the location of the flash SWF -->
|
||||||
|
<script>
|
||||||
|
videojs.options.flash.swf = '../node_modules/videojs-flash/node_modules/videojs-swf/dist/video-js.swf';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<!-- NOTE: we have to disable native Text Track support for the HTML5 tech,
|
||||||
|
since even HTML5 video players with native Text Track support
|
||||||
|
don't currently support 'description' text tracks in any
|
||||||
|
useful way! Currently this means that iOS will not display
|
||||||
|
ANY text tracks -->
|
||||||
|
<video id="example_video_1" class="video-js" controls preload="none" width="640" height="360"
|
||||||
|
poster="//d2zihajmogu5jn.cloudfront.net/elephantsdream/poster.png">
|
||||||
|
|
||||||
|
<source src="//d2zihajmogu5jn.cloudfront.net/elephantsdream/ed_hd.mp4" type="video/mp4">
|
||||||
|
<source src="//d2zihajmogu5jn.cloudfront.net/elephantsdream/ed_hd.ogg" type="video/ogg">
|
||||||
|
|
||||||
|
<track kind="captions" src="../docs/examples/elephantsdream/captions.en.vtt" srclang="en" label="English" default>
|
||||||
|
<track kind="captions" src="../docs/examples/elephantsdream/captions.sv.vtt" srclang="sv" label="Swedish">
|
||||||
|
<track kind="captions" src="../docs/examples/elephantsdream/captions.ru.vtt" srclang="ru" label="Russian">
|
||||||
|
<track kind="captions" src="../docs/examples/elephantsdream/captions.ja.vtt" srclang="ja" label="Japanese">
|
||||||
|
<track kind="captions" src="../docs/examples/elephantsdream/captions.ar.vtt" srclang="ar" label="Arabic">
|
||||||
|
|
||||||
|
<track kind="descriptions" src="../docs/examples/elephantsdream/descriptions.en.vtt" srclang="en" label="English">
|
||||||
|
|
||||||
|
<track kind="chapters" src="../docs/examples/elephantsdream/chapters.en.vtt" srclang="en" label="English">
|
||||||
|
|
||||||
|
<p class="vjs-no-js">To view this video please enable JavaScript, and consider upgrading to a web browser that <a href="https://videojs.com/html5-video-support/" target="_blank">supports HTML5 video</a></p>
|
||||||
|
</video>
|
||||||
|
<script>
|
||||||
|
var vid = document.getElementById('example_video_1');
|
||||||
|
var player = videojs(vid, {
|
||||||
|
textTrackDisplay: {
|
||||||
|
allowMultipleShowingTracks: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
player.on('loadedmetadata', function() {
|
||||||
|
player.textTracks()[0].mode = 'showing';
|
||||||
|
player.textTracks()[1].mode = 'showing';
|
||||||
|
player.textTracks()[2].mode = 'showing';
|
||||||
|
player.textTracks()[3].mode = 'showing';
|
||||||
|
player.textTracks()[4].mode = 'showing';
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -3,6 +3,7 @@
|
|||||||
*/
|
*/
|
||||||
import Component from '../component';
|
import Component from '../component';
|
||||||
import * as Fn from '../utils/fn.js';
|
import * as Fn from '../utils/fn.js';
|
||||||
|
import * as Dom from '../utils/dom.js';
|
||||||
import window from 'global/window';
|
import window from 'global/window';
|
||||||
|
|
||||||
const darkGray = '#222';
|
const darkGray = '#222';
|
||||||
@ -240,9 +241,25 @@ class TextTrackDisplay extends Component {
|
|||||||
*/
|
*/
|
||||||
updateDisplay() {
|
updateDisplay() {
|
||||||
const tracks = this.player_.textTracks();
|
const tracks = this.player_.textTracks();
|
||||||
|
const allowMultipleShowingTracks = this.options_.allowMultipleShowingTracks;
|
||||||
|
|
||||||
this.clearDisplay();
|
this.clearDisplay();
|
||||||
|
|
||||||
|
if (allowMultipleShowingTracks) {
|
||||||
|
const showingTracks = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < tracks.length; ++i) {
|
||||||
|
const track = tracks[i];
|
||||||
|
|
||||||
|
if (track.mode !== 'showing') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
showingTracks.push(track);
|
||||||
|
}
|
||||||
|
this.updateForTrack(showingTracks);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Track display prioritization model: if multiple tracks are 'showing',
|
// Track display prioritization model: if multiple tracks are 'showing',
|
||||||
// display the first 'subtitles' or 'captions' track which is 'showing',
|
// display the first 'subtitles' or 'captions' track which is 'showing',
|
||||||
// otherwise display the first 'descriptions' track which is 'showing'
|
// otherwise display the first 'descriptions' track which is 'showing'
|
||||||
@ -277,29 +294,14 @@ class TextTrackDisplay extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add an {@link TextTrack} to to the {@link Tech}s {@link TextTrackList}.
|
* Style {@Link TextTrack} activeCues according to {@Link TextTrackSettings}.
|
||||||
*
|
*
|
||||||
* @param {TextTrack} track
|
* @param {TextTrack} track
|
||||||
* Text track object to be added to the list.
|
* Text track object containing active cues to style.
|
||||||
*/
|
*/
|
||||||
updateForTrack(track) {
|
updateDisplayState(track) {
|
||||||
if (typeof window.WebVTT !== 'function' || !track.activeCues) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const cues = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < track.activeCues.length; i++) {
|
|
||||||
cues.push(track.activeCues[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
window.WebVTT.processCues(window, cues, this.el_);
|
|
||||||
|
|
||||||
if (!this.player_.textTrackSettings) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const overrides = this.player_.textTrackSettings.getValues();
|
const overrides = this.player_.textTrackSettings.getValues();
|
||||||
|
const cues = track.activeCues;
|
||||||
|
|
||||||
let i = cues.length;
|
let i = cues.length;
|
||||||
|
|
||||||
@ -378,6 +380,53 @@ class TextTrackDisplay extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an {@link TextTrack} to to the {@link Tech}s {@link TextTrackList}.
|
||||||
|
*
|
||||||
|
* @param {TextTrack|TextTrack[]} tracks
|
||||||
|
* Text track object or text track array to be added to the list.
|
||||||
|
*/
|
||||||
|
updateForTrack(tracks) {
|
||||||
|
if (!Array.isArray(tracks)) {
|
||||||
|
tracks = [tracks];
|
||||||
|
}
|
||||||
|
if (typeof window.WebVTT !== 'function' ||
|
||||||
|
tracks.every((track)=> {
|
||||||
|
return !track.activeCues;
|
||||||
|
})) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cues = [];
|
||||||
|
|
||||||
|
// push all active track cues
|
||||||
|
for (let i = 0; i < tracks.length; ++i) {
|
||||||
|
const track = tracks[i];
|
||||||
|
|
||||||
|
for (let j = 0; j < track.activeCues.length; ++j) {
|
||||||
|
cues.push(track.activeCues[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// removes all cues before it processes new ones
|
||||||
|
window.WebVTT.processCues(window, cues, this.el_);
|
||||||
|
|
||||||
|
// add unique class to each language text track & add settings styling if necessary
|
||||||
|
for (let i = 0; i < tracks.length; ++i) {
|
||||||
|
const track = tracks[i];
|
||||||
|
|
||||||
|
for (let j = 0; j < track.activeCues.length; ++j) {
|
||||||
|
const cueEl = track.activeCues[j].displayState;
|
||||||
|
|
||||||
|
Dom.addClass(cueEl, 'vjs-text-track-cue');
|
||||||
|
Dom.addClass(cueEl, 'vjs-text-track-cue-' + ((track.language) ? track.language : i));
|
||||||
|
}
|
||||||
|
if (this.player_.textTrackSettings) {
|
||||||
|
this.updateDisplayState(track);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.registerComponent('TextTrackDisplay', TextTrackDisplay);
|
Component.registerComponent('TextTrackDisplay', TextTrackDisplay);
|
||||||
|
Reference in New Issue
Block a user