Skip to content

Commit

Permalink
feat: Add SubStation Alpha (SSA) support (#3060)
Browse files Browse the repository at this point in the history
  • Loading branch information
Álvaro Velad Galván authored Feb 1, 2021
1 parent 54b8f6e commit 0845843
Show file tree
Hide file tree
Showing 9 changed files with 835 additions and 35 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,8 @@ Shaka Player supports:
- UTF-8 encoding only
- LyRiCs (LRC)
- UTF-8 encoding only
- SubStation Alpha (SSA, ASS)
- UTF-8 encoding only

Subtitles are rendered by the browser by default. Applications can create a
[text display plugin][] for customer rendering to go beyond browser-supported
Expand Down
2 changes: 2 additions & 0 deletions build/types/text
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,7 @@
+../../lib/text/mp4_ttml_parser.js
+../../lib/text/mp4_vtt_parser.js
+../../lib/text/srt_text_parser.js
+../../lib/text/ssa_text_parser.js
+../../lib/text/ttml_text_parser.js
+../../lib/text/vtt_text_parser.js
+../../lib/text/web_vtt_generator.js
24 changes: 23 additions & 1 deletion lib/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ goog.require('shaka.routing.Walker');
goog.require('shaka.text.SimpleTextDisplayer');
goog.require('shaka.text.TextEngine');
goog.require('shaka.text.UITextDisplayer');
goog.require('shaka.text.WebVttGenerator');
goog.require('shaka.util.AbortableOperation');
goog.require('shaka.util.ConfigUtils');
goog.require('shaka.util.Error');
Expand Down Expand Up @@ -3913,6 +3914,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
'webvtt': 'text/vtt',
'ttml': 'application/ttml+xml',
'lrc': 'application/x-subtitle-lrc',
'ssa': 'text/x-ssa',
'ass': 'text/x-ssa',
}[extension];

if (!mimeType) {
Expand Down Expand Up @@ -4058,6 +4061,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
'webvtt': 'text/vtt',
'ttml': 'application/ttml+xml',
'lrc': 'application/x-subtitle-lrc',
'ssa': 'text/x-ssa',
'ass': 'text/x-ssa',
}[extension];

if (!mimeType) {
Expand Down Expand Up @@ -4234,10 +4239,12 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
mimeType);
}
}
/** @type {Array.<!shaka.extern.Cue>} */
let cues;
if (mimeType === 'application/x-subtitle-lrc') {
const LrcTextParser = shaka.text.LrcTextParser;
if (LrcTextParser) {
return LrcTextParser.convertToWebVTT(data, this.video_.duration);
cues = LrcTextParser.getCues(data, this.video_.duration);
} else {
throw new shaka.util.Error(
shaka.util.Error.Severity.CRITICAL,
Expand All @@ -4246,6 +4253,21 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
mimeType);
}
}
if (mimeType === 'text/x-ssa') {
const SsaTextParser = shaka.text.SsaTextParser;
if (SsaTextParser) {
cues = SsaTextParser.getCues(data);
} else {
throw new shaka.util.Error(
shaka.util.Error.Severity.CRITICAL,
shaka.util.Error.Category.TEXT,
shaka.util.Error.Code.MISSING_TEXT_PLUGIN,
mimeType);
}
}
if (cues) {
return shaka.text.WebVttGenerator.convert(cues);
}
throw new shaka.util.Error(
shaka.util.Error.Severity.RECOVERABLE,
shaka.util.Error.Category.TEXT,
Expand Down
36 changes: 2 additions & 34 deletions lib/text/lrc_text_parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,15 @@ shaka.text.LrcTextParser = class {
* @export
*/
parseMedia(data, time) {
return shaka.text.LrcTextParser.getCues_(data, time.segmentEnd);
return shaka.text.LrcTextParser.getCues(data, time.segmentEnd);
}

/**
* @param {BufferSource} data
* @param {number} segmentEnd
* @return {!Array.<!shaka.extern.Cue>}
* @private
*/
static getCues_(data, segmentEnd) {
static getCues(data, segmentEnd) {
const StringUtils = shaka.util.StringUtils;
const LrcTextParser = shaka.text.LrcTextParser;

Expand Down Expand Up @@ -101,37 +100,6 @@ shaka.text.LrcTextParser = class {
const seconds = parseFloat(match[2].replace(',', '.'));
return minutes * 60 + seconds;
}

/**
* Convert a LRC input to WebVTT
*
* @param {BufferSource} data
* @param {number} segmentEnd
* @return {string}
* @export
*/
static convertToWebVTT(data, segmentEnd) {
const LrcTextParser = shaka.text.LrcTextParser;
const cues = LrcTextParser.getCues_(data, segmentEnd);
let webvttString = 'WEBVTT\n\n';
for (const cue of cues) {
const webvttTimeString = (time) => {
const hours = Math.floor(time / 3600);
const minutes = Math.floor(time / 60 % 60);
const seconds = Math.floor(time % 60);
const milliseconds = Math.floor(time * 1000 % 1000);
return (hours < 10 ? '0' : '') + hours + ':' +
(minutes < 10 ? '0' : '') + minutes + ':' +
(seconds < 10 ? '0' : '') + seconds + '.' +
(milliseconds < 100 ? (milliseconds < 10 ? '00' : '0') : '') +
milliseconds;
};
webvttString += webvttTimeString(cue.startTime) + ' --> ' +
webvttTimeString(cue.endTime) + '\n';
webvttString += cue.payload + '\n\n';
}
return webvttString;
}
};

/**
Expand Down
Loading

0 comments on commit 0845843

Please sign in to comment.