Skip to content

Commit

Permalink
New: Support closed-captions/subtitles (#117)
Browse files Browse the repository at this point in the history
Added subtitles to the video menu. The subtitles menu only shows if the
video is detected to have subtitles in it. This is only supported in the
DashViewer. There is also a CC button for toggling subtitles on/off
  • Loading branch information
bhh1988 authored May 17, 2017
1 parent 7268548 commit 4c56532
Show file tree
Hide file tree
Showing 12 changed files with 553 additions and 13 deletions.
6 changes: 6 additions & 0 deletions src/i18n/en-US.properties
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,16 @@ media_play=Play
media_pause=Pause
# Label for Settings button in media player
media_settings=Settings
# Label for subtitles/closed-captions in media player
media_subtitles_cc=Subtitles/Closed Captions
# Used in ARIA label for volume
volume=Volume
# Used in ARIA label for timescrubber progress
of=of
# Label for subtitles in media player
subtitles=Subtitles
# Label for turning off subtitles in media player
off=Off


# 3D Preview
Expand Down
35 changes: 35 additions & 0 deletions src/lib/viewers/media/DashViewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class DashViewer extends VideoBaseViewer {
// tracks
this.hdRepresentation = {};
this.sdRepresentation = {};
this.textTracks = []; // Must be sorted by representation id

// dash specific class
this.wrapperEl.classList.add(CSS_CLASS_DASH);
Expand Down Expand Up @@ -59,6 +60,7 @@ class DashViewer extends VideoBaseViewer {
}
if (this.mediaControls) {
this.mediaControls.removeListener('qualitychange', this.handleQuality);
this.mediaControls.removeListener('subtitlechange', this.handleSubtitle);
}
this.removeStats();
super.destroy();
Expand Down Expand Up @@ -217,6 +219,26 @@ class DashViewer extends VideoBaseViewer {
this.player.configure({ abr: { enabled: adapt } });
}

/**
* Handler for subtitle
*
* @private
* @emits subtitlechange
* @return {void}
*/
handleSubtitle() {
const subtitleIdx = parseInt(cache.get('media-subtitles'), 10);
if (this.textTracks[subtitleIdx] !== undefined) {
const track = this.textTracks[subtitleIdx];
this.player.selectTextTrack(track);
this.player.setTextTrackVisibility(true);
this.emit('subtitlechange', track.language);
} else {
this.player.setTextTrackVisibility(false);
this.emit('subtitlechange', null);
}
}

/**
* Handler for hd/sd/auto video
*
Expand Down Expand Up @@ -279,8 +301,20 @@ class DashViewer extends VideoBaseViewer {
addEventListenersForMediaControls() {
super.addEventListenersForMediaControls();
this.mediaControls.addListener('qualitychange', this.handleQuality);
this.mediaControls.addListener('subtitlechange', this.handleSubtitle);
}

/**
* Loads captions/subtitles into the settings menu
*
* @return {void}
*/
loadSubtitles() {
this.textTracks = this.player.getTextTracks().sort((track1, track2) => track1.id - track2.id);
if (this.textTracks.length > 0) {
this.mediaControls.initSubtitles(this.textTracks.map((track) => track.language));
}
}
/**
* Handler for meta data load for the media element.
*
Expand All @@ -300,6 +334,7 @@ class DashViewer extends VideoBaseViewer {
this.handleVolume();
this.startBandwidthTracking();
this.handleQuality(); // should come after gettings rep ids
this.loadSubtitles();
this.showPlayButton();

this.loaded = true;
Expand Down
4 changes: 4 additions & 0 deletions src/lib/viewers/media/MediaBaseViewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -561,6 +561,10 @@ class MediaBaseViewer extends BaseViewer {
case 'shift+m':
this.toggleMute();
break;
case 'c':
case 'shift+c':
this.mediaControls.toggleSubtitles();
break;
default:
return false;
}
Expand Down
4 changes: 4 additions & 0 deletions src/lib/viewers/media/MediaControls.html
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@
<label class="bp-media-controls-label bp-media-controls-timecode">00:00</label>
<span class="bp-media-controls-label">&nbsp;&#47;&nbsp;</span>
<label class="bp-media-controls-label bp-media-controls-duration">00:00</label>
<button class="bp-media-controls-btn bp-media-cc-icon">
<span class="bp-media-controls-cc-icon-text">CC</span>
<div class="bp-media-cc-icon-on"></div>
</button>
<button class="bp-media-controls-btn bp-media-gear-icon" aria-haspopup="true">
<svg width="24" height="24" viewBox="0 0 24 24" focusable="false">
<path fill="#fff" d="M19.4 13c0-.3.1-.6.1-1s0-.7-.1-1l2.1-1.6c.2-.1.2-.4.1-.6l-2-3.5c-.1-.2-.4-.3-.6-.2l-2.5 1c-.5-.4-1.1-.7-1.7-1l-.4-2.7c.1-.2-.2-.4-.4-.4h-4c-.2 0-.5.2-.5.4l-.4 2.7c-.6.2-1.1.6-1.7 1l-2.5-1c-.2-.1-.4 0-.6.2l-2 3.5c-.1.1 0 .4.2.6L4.6 11c0 .3-.1.6-.1 1s0 .7.1 1l-2.1 1.6c-.2.1-.2.4-.1.6l2 3.5c.1.2.3.3.6.2l2.5-1c.5.4 1.1.7 1.7 1l.4 2.6c0 .2.2.4.5.4h4c.2 0 .5-.2.5-.4l.4-2.6c.6-.2 1.2-.6 1.7-1l2.5 1c.2.1.5 0 .6-.2l2-3.5c.1-.2.1-.5-.1-.6L19.4 13zM12 15.5c-1.9 0-3.5-1.6-3.5-3.5s1.6-3.5 3.5-3.5 3.5 1.6 3.5 3.5-1.6 3.5-3.5 3.5z"/>
Expand Down
42 changes: 42 additions & 0 deletions src/lib/viewers/media/MediaControls.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ class MediaControls extends EventEmitter {
this.settingsButtonEl = this.wrapperEl.querySelector('.bp-media-gear-icon');
this.setLabel(this.settingsButtonEl, __('media_settings'));

this.subtitlesButtonEl = this.wrapperEl.querySelector('.bp-media-cc-icon');
this.setLabel(this.subtitlesButtonEl, __('media_subtitles_cc'));

this.setDuration(this.mediaEl.duration);
this.setupSettings();
this.setupScrubbers();
Expand Down Expand Up @@ -96,6 +99,7 @@ class MediaControls extends EventEmitter {
if (this.settings) {
this.settings.removeListener('quality', this.handleQuality);
this.settings.removeListener('speed', this.handleRate);
this.settings.removeListener('subtitles', this.handleSubtitle);
this.settings.destroy();
this.settings = undefined;
}
Expand All @@ -116,6 +120,10 @@ class MediaControls extends EventEmitter {
removeActivationListener(this.settingsButtonEl, this.toggleSettingsHandler);
}

if (this.subtitlesButtonEl) {
removeActivationListener(this.subtitlesButtonEl, this.toggleSubtitlesHandler);
}

if (this.wrapperEl) {
this.wrapperEl.removeEventListener('mouseenter', this.mouseenterHandler);
this.wrapperEl.removeEventListener('mouseleave', this.mouseleaveHandler);
Expand Down Expand Up @@ -159,6 +167,16 @@ class MediaControls extends EventEmitter {
this.emit('qualitychange');
}

/**
* Subtitle handler
*
* @private
* @return {void}
*/
handleSubtitle() {
this.emit('subtitlechange');
}

/**
* Attaches settings menu
*
Expand Down Expand Up @@ -318,6 +336,17 @@ class MediaControls extends EventEmitter {
}
}

/**
* Toggles subtitles
*
* @return {void}
*/
toggleSubtitles() {
this.show();
this.settings.toggleSubtitles();
this.emit('togglesubtitles');
}

/**
* Toggles label for control element with more than one state
* @return {void}
Expand Down Expand Up @@ -496,11 +525,13 @@ class MediaControls extends EventEmitter {
this.toggleMuteHandler = activationHandler(this.toggleMute);
this.toggleFullscreenHandler = activationHandler(this.toggleFullscreen);
this.toggleSettingsHandler = activationHandler(this.toggleSettings);
this.toggleSubtitlesHandler = activationHandler(this.toggleSubtitles);

addActivationListener(this.playButtonEl, this.togglePlayHandler);
addActivationListener(this.volButtonEl, this.toggleMuteHandler);
addActivationListener(this.fullscreenButtonEl, this.toggleFullscreenHandler);
addActivationListener(this.settingsButtonEl, this.toggleSettingsHandler);
addActivationListener(this.subtitlesButtonEl, this.toggleSubtitlesHandler);

fullscreen.addListener('exit', this.setFullscreenLabel);
}
Expand Down Expand Up @@ -769,6 +800,17 @@ class MediaControls extends EventEmitter {
isVolumeScrubberFocused() {
return document.activeElement === this.volScrubberEl;
}

/**
* Takes a list of subtitle names and populates the settings menu
*
* @param {Array} subtitles - A list of subtitle names as strings
* @return {void}
*/
initSubtitles(subtitles) {
this.settings.addListener('subtitles', this.handleSubtitle);
this.settings.loadSubtitles(subtitles);
}
}

export default MediaControls;
41 changes: 41 additions & 0 deletions src/lib/viewers/media/MediaControls.scss
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,47 @@
right: 55px;
}

.bp-media-cc-icon {
position: absolute;
right: 100px;
visibility: visible;

.bp-media-settings-subtitles-unavailable & {
visibility: hidden;
}
}

.bp-media-cc-icon-on {
background-color: $white;
bottom: 6px;
display: inline-block;
height: 3px;
left: 12px;
opacity: 0;
position: absolute;
transition: opacity .3s;
width: 21px;
}

.bp-media-controls-cc-icon-text {
background: $white;
border-radius: 3px;
bottom: 2px;
color: $black;
font-size: 11px;
font-weight: 600;
letter-spacing: .1em;
line-height: 8px;
padding: 2px 2px 2px 3px;
position: relative;
}

.bp-media-settings-subtitles-on {
.bp-media-cc-icon-on {
opacity: 1;
}
}

.bp-media-controls-hd {
background: $box-blue;
border-radius: 3px;
Expand Down
Loading

0 comments on commit 4c56532

Please sign in to comment.