Skip to content

Commit

Permalink
Merge pull request #220 from redSpoutnik/synchronize-subtitles
Browse files Browse the repository at this point in the history
Add subtitle synchronization on HTML video player
  • Loading branch information
anthonylavado committed Apr 30, 2019
2 parents 914b270 + 4d8ec5b commit d7a0a9b
Show file tree
Hide file tree
Showing 10 changed files with 399 additions and 1 deletion.
1 change: 1 addition & 0 deletions CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
- [RazeLighter777](https://github.com/RazeLighter777)
- [LogicalPhallacy](https://github.com/LogicalPhallacy)
- [thornbill](https://github.com/thornbill)
- [redSpoutnik](https://github.com/redSpoutnik)

# Emby Contributors

Expand Down
83 changes: 83 additions & 0 deletions src/components/htmlvideoplayer/plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,9 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
var currentAssRenderer;
var customTrackIndex = -1;

var showTrackOffset = false;
var currentTrackOffset;

var videoSubtitlesElem;
var currentTrackEvents;

Expand Down Expand Up @@ -543,6 +546,81 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
setCurrentTrackElement(index);
};

self.enableShowingSubtitleOffset = function() {
showTrackOffset = true;
}

self.disableShowingSubtitleOffset = function() {
showTrackOffset = false;
}

self.isShowingSubtitleOffsetEnabled = function() {
return showTrackOffset;
}

self.setSubtitleOffset = function(offset) {

var offsetValue = parseFloat(offset);
var videoElement = self._mediaElement;
var mediaStreamTextTracks = getMediaStreamTextTracks(self._currentPlayOptions.mediaSource);

Array.from(videoElement.textTracks)
.filter(function(trackElement) {
if (customTrackIndex === -1 ) {
// get showing .vtt textTacks
return trackElement.mode === 'showing';
} else {
// get current .ass textTrack
return ("textTrack" + customTrackIndex) === trackElement.id;
}
})
.forEach(function(trackElement) {

var track = mediaStreamTextTracks.filter(function(stream) {
return ("textTrack" + stream.Index) === trackElement.id;
})[0];

if(track) {
offsetValue = updateCurrentTrackOffset(offsetValue);
var format = (track.Codec || '').toLowerCase();
if (format !== 'ass' && format !== 'ssa') {
setVttSubtitleOffset(trackElement, offsetValue);
}
} else {
console.log("No available track, cannot apply offset : " + offsetValue);
}

});
};

function updateCurrentTrackOffset(offsetValue) {

var relativeOffset = offsetValue;
var newTrackOffset = offsetValue;
if(currentTrackOffset){
relativeOffset -= currentTrackOffset;
}
currentTrackOffset = newTrackOffset;
// relative to currentTrackOffset
return relativeOffset;
}

function setVttSubtitleOffset(currentTrack, offsetValue) {

if(currentTrack.cues) {
Array.from(currentTrack.cues)
.forEach(function(cue) {
cue.startTime -= offsetValue;
cue.endTime -= offsetValue;
});
}

}

self.getSubtitleOffset = function() {
return currentTrackOffset;
}

function isAudioStreamSupported(stream, deviceProfile) {

var codec = (stream.Codec || '').toLowerCase();
Expand Down Expand Up @@ -1167,6 +1245,11 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa

function updateSubtitleText(timeMs) {

// handle offset for ass tracks
if(currentTrackOffset) {
timeMs += (currentTrackOffset * 1000);
}

var clock = currentClock;
if (clock) {
try {
Expand Down
40 changes: 40 additions & 0 deletions src/components/playback/playbackmanager.js
Original file line number Diff line number Diff line change
Expand Up @@ -1635,6 +1635,46 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla
getPlayerData(player).subtitleStreamIndex = index;
};

self.supportSubtitleOffset = function(player) {
player = player || self._currentPlayer;
return player && 'setSubtitleOffset' in player;
}

self.enableShowingSubtitleOffset = function(player) {
player = player || self._currentPlayer;
player.enableShowingSubtitleOffset();
}

self.disableShowingSubtitleOffset = function(player) {
player = player || self._currentPlayer;
player.disableShowingSubtitleOffset();
}

self.isShowingSubtitleOffsetEnabled = function(player) {
player = player || self._currentPlayer;
return player.isShowingSubtitleOffsetEnabled();
}

self.isSubtitleStreamExternal = function(index, player) {
var stream = getSubtitleStream(player, index);
return stream ? getDeliveryMethod(stream) === 'External' : false;
}

self.setSubtitleOffset = function (value, player) {
player = player || self._currentPlayer;
player.setSubtitleOffset(value);
};

self.getPlayerSubtitleOffset = function(player) {
player = player || self._currentPlayer;
return player.getSubtitleOffset();
}

self.canHandleOffsetOnCurrentSubtitle = function(player) {
var index = self.getSubtitleStreamIndex(player);
return index !== -1 && self.isSubtitleStreamExternal(index, player);
}

self.seek = function (ticks, player) {

ticks = Math.max(0, ticks);
Expand Down
14 changes: 14 additions & 0 deletions src/components/playback/playersettingsmenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,15 @@ define(['connectionManager', 'actionsheet', 'datetime', 'playbackManager', 'glob
});
}

if (options.suboffset) {

menuItems.push({
name: globalize.translate('SubtitleOffset'),
id: 'suboffset',
asideText: null
});
}

return actionsheet.show({
items: menuItems,
positionTo: options.positionTo
Expand Down Expand Up @@ -248,6 +257,11 @@ define(['connectionManager', 'actionsheet', 'datetime', 'playbackManager', 'glob
options.onOption('stats');
}
return Promise.resolve();
case 'suboffset':
if (options.onOption) {
options.onOption('suboffset');
}
return Promise.resolve();
default:
break;
}
Expand Down
48 changes: 48 additions & 0 deletions src/components/subtitlesync/subtitlesync.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
.subtitleSyncContainer {
width: 40%;
margin-left: 30%;
margin-right: 30%;
height: 4.2em;
background: rgba(28,28,28,0.8);
border-radius: .3em;
color: #fff;
position: absolute;
}

.subtitleSync-closeButton {
position: absolute;
top: 0;
right: 0;
color: #ccc;
z-index: 2;
}

.subtitleSyncTextField {
position: absolute;
left: 0;
width: 40%;
margin-left: 30%;
margin-right: 30%;
top: 0.1em;
text-align: center;
font-size: 20px;
color: white;
z-index: 2;
}

#prompt {
flex-shrink: 0;
}

.subtitleSyncSliderContainer {
width: 98%;
margin-left: 1%;
margin-right: 1%;
top: 2.5em;
height: 1.4em;
-webkit-box-flex: 1;
-webkit-flex-grow: 1;
flex-grow: 1;
border-radius: .3em;
z-index: 1;
}

0 comments on commit d7a0a9b

Please sign in to comment.