fix(HLS): Fix subtitle timing (#7625)

Simplifies subtitle management for sequence mode and segments mode 
Runs subtitle tests in segments mode as well (Tizen 3.0)

Fixes: https://github.com/shaka-project/shaka-player/issues/7447
This commit is contained in:
Álvaro Velad Galván
2024-11-19 18:36:49 +01:00
committed by GitHub
parent 67cbe9915d
commit 9e2b15ead4
14 changed files with 84 additions and 27 deletions
+2
View File
@@ -272,6 +272,8 @@ module.exports = (config) => {
{pattern: 'test/test/assets/hls-raw-ec3/*', included: false},
{pattern: 'test/test/assets/hls-raw-mp3/*', included: false},
{pattern: 'test/test/assets/hls-sample-aes/*', included: false},
// eslint-disable-next-line max-len
{pattern: 'test/test/assets/hls-text-no-discontinuity/*', included: false},
{pattern: 'test/test/assets/hls-text-offset/*', included: false},
{pattern: 'test/test/assets/hls-ts-aac/*', included: false},
{pattern: 'test/test/assets/hls-ts-ac3/*', included: false},
+1 -1
View File
@@ -4126,7 +4126,7 @@ shaka.hls.HlsParser = class {
let aesKey = undefined;
let discontinuitySequence = shaka.hls.Utils.getFirstTagWithNameAsNumber(
playlist.tags, 'EXT-X-DISCONTINUITY-SEQUENCE', 0);
playlist.tags, 'EXT-X-DISCONTINUITY-SEQUENCE', -1);
const mediaSequenceNumber = shaka.hls.Utils.getFirstTagWithNameAsNumber(
playlist.tags, 'EXT-X-MEDIA-SEQUENCE', 0);
const skipTag = shaka.hls.Utils.getFirstTagWithName(
+7 -2
View File
@@ -1139,7 +1139,8 @@ shaka.media.MediaSourceEngine = class {
const ContentType = shaka.util.ManifestParserUtils.ContentType;
if (contentType == ContentType.TEXT) {
if (this.sequenceMode_) {
if (this.manifestType_ == shaka.media.ManifestParser.HLS &&
reference.discontinuitySequence >= 0) {
// This won't be known until the first video segment is appended.
const offset = await this.textSequenceModeOffset_;
this.textEngine_.setTimestampOffset(offset);
@@ -1233,7 +1234,7 @@ shaka.media.MediaSourceEngine = class {
const isBestSourceBufferForTimestamps =
contentType == ContentType.VIDEO ||
!(ContentType.VIDEO in this.sourceBuffers_);
if (this.sequenceMode_ && isBestSourceBufferForTimestamps) {
if (isBestSourceBufferForTimestamps) {
this.textSequenceModeOffset_.resolve(timestampOffset);
}
}
@@ -1562,6 +1563,10 @@ shaka.media.MediaSourceEngine = class {
this.textSequenceModeOffset_ = new shaka.util.PublicPromise();
}
if (!this.sequenceMode_) {
return;
}
// Queue an abort() to help MSE splice together overlapping segments.
// We set appendWindowEnd when we change periods in DASH content, and the
// period transition may result in overlap.
+1 -1
View File
@@ -295,7 +295,7 @@ shaka.media.SegmentReference = class {
this.thumbnailSprite = null;
/** @type {number} */
this.discontinuitySequence = 0;
this.discontinuitySequence = -1;
/** @type {boolean} */
this.allPartialSegments = allPartialSegments;
+9 -11
View File
@@ -2152,17 +2152,15 @@ shaka.media.StreamingEngine = class {
}
}
if (this.manifest_.sequenceMode) {
const lastDiscontinuitySequence =
mediaState.lastSegmentReference ?
mediaState.lastSegmentReference.discontinuitySequence : null;
// Across discontinuity bounds, we should resync timestamps for
// sequence mode playbacks. The next segment appended should
// land at its theoretical timestamp from the segment index.
if (reference.discontinuitySequence != lastDiscontinuitySequence) {
operations.push(this.playerInterface_.mediaSourceEngine.resync(
mediaState.type, reference.startTime));
}
const lastDiscontinuitySequence =
mediaState.lastSegmentReference ?
mediaState.lastSegmentReference.discontinuitySequence : null;
// Across discontinuity bounds, we should resync timestamps. The next
// segment appended should land at its theoretical timestamp from the
// segment index.
if (reference.discontinuitySequence != lastDiscontinuitySequence) {
operations.push(this.playerInterface_.mediaSourceEngine.resync(
mediaState.type, reference.startTime));
}
await Promise.all(operations);
+2 -8
View File
@@ -86,13 +86,7 @@ shaka.text.VttTextParser = class {
// Only use 'X-TIMESTAMP-MAP' with HLS. This overrides offset above.
if (blocks[0].includes('X-TIMESTAMP-MAP') &&
this.manifestType_ == shaka.media.ManifestParser.HLS) {
if (this.sequenceMode_) {
// Compute a different, rollover-based offset for sequence mode.
offset = this.computeHlsSequenceModeOffset_(blocks[0], time);
} else {
// Calculate the offset from the segment startTime.
offset = time.segmentStart;
}
offset = this.computeHlsOffset_(blocks[0], time);
}
// Parse VTT regions.
@@ -129,7 +123,7 @@ shaka.text.VttTextParser = class {
* @return {number}
* @private
*/
computeHlsSequenceModeOffset_(headerBlock, time) {
computeHlsOffset_(headerBlock, time) {
// https://bit.ly/2K92l7y
// The 'X-TIMESTAMP-MAP' header is used in HLS to align text with
// the rest of the media.
+21 -4
View File
@@ -111,10 +111,6 @@ describe('HlsParser', () => {
});
it('supports text discontinuity', async () => {
if (!shaka.util.Platform.supportsSequenceMode()) {
pending('Sequence mode is not supported by the platform.');
}
player.setTextTrackVisibility(true);
await player.load('/base/test/test/assets/hls-text-offset/index.m3u8');
@@ -134,4 +130,25 @@ describe('HlsParser', () => {
await player.unload();
});
it('supports text without discontinuity', async () => {
player.setTextTrackVisibility(true);
// eslint-disable-next-line max-len
await player.load('/base/test/test/assets/hls-text-no-discontinuity/index.m3u8');
await video.play();
await waiter.waitUntilPlayheadReachesOrFailOnTimeout(video, 1, 30);
const cues = video.textTracks[0].cues;
expect(cues.length).toBe(3);
expect(cues[0].startTime).toBeCloseTo(0.6, 0);
expect(cues[0].endTime).toBeCloseTo(2.88, 0);
expect(cues[1].startTime).toBeCloseTo(2.88, 0);
expect(cues[1].endTime).toBeCloseTo(6.36, 0);
expect(cues[2].startTime).toBeCloseTo(6.36, 0);
expect(cues[2].endTime).toBeCloseTo(10.68, 0);
await player.unload();
});
});
+1
View File
@@ -1153,6 +1153,7 @@ describe('HlsParser', () => {
'#EXT-X-VERSION:3\n',
'#EXT-X-TARGETDURATION:5\n',
'#EXT-X-MEDIA-SEQUENCE:0\n',
'#EXT-X-DISCONTINUITY-SEQUENCE:0\n',
'#EXTINF:3,\n',
'clip0-video-0.ts\n',
'#EXTINF:1,\n',
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,11 @@
#EXTM3U
#EXT-X-VERSION:6
#EXT-X-TARGETDURATION:7
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-INDEPENDENT-SEGMENTS
#EXTINF:6.000000,
0.ts
#EXTINF:6.000000,
1.ts
#EXT-X-ENDLIST
@@ -0,0 +1,6 @@
#EXTM3U
#EXT-X-VERSION:6
#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="de",DEFAULT=NO,AUTOSELECT=NO,LANGUAGE="de",FORCED="NO",URI="text.m3u8"
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1240800,CODECS="avc1.640014,mp4a.40.2",RESOLUTION=256x144,SUBTITLES="subs"
av.m3u8
@@ -0,0 +1,15 @@
WEBVTT
1
00:00:00.600 --> 00:00:02.880
Heute haben wir einen wesentlichen Meilenstein
2
00:00:02.880 --> 00:00:06.360
auf dem Weg zur Science
City Hamburg Bahrenfeld erreicht.
3
00:00:06.360 --> 00:00:10.680
Die Science City ist ein 125 Hektar
großes Areal in Bahrenfeld,
@@ -0,0 +1,8 @@
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-TARGETDURATION:12
#EXTINF:12.000,
subtitle.vtt
#EXT-X-ENDLIST