Skip to content

Commit

Permalink
fix(HLS): Fix audio and video out of sync (#5658)
Browse files Browse the repository at this point in the history
Fixes #5584
Fixes #5013
Fixes #5397

timestampOffset calculation is no longer necessary in the HLS parser
because we introduce recently some change in MediaSourceEngine to
calculate the timestampOffset

Backported to v4.4.x and v4.3.x
  • Loading branch information
avelad authored and joeyparrish committed Oct 4, 2023
1 parent ee898e9 commit cb9789e
Show file tree
Hide file tree
Showing 3 changed files with 6 additions and 80 deletions.
11 changes: 3 additions & 8 deletions lib/hls/hls_parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -2427,15 +2427,13 @@ shaka.hls.HlsParser = class {
* @param {!Map.<string, string>} variables
* @param {string} absoluteMediaPlaylistUri
* @param {string} type
* @param {number} timestampOffset
* @param {shaka.extern.HlsAes128Key=} hlsAes128Key
* @return {shaka.media.SegmentReference}
* @private
*/
createSegmentReference_(
initSegmentReference, previousReference, hlsSegment, startTime,
variables, absoluteMediaPlaylistUri, type, timestampOffset,
hlsAes128Key) {
variables, absoluteMediaPlaylistUri, type, hlsAes128Key) {
const tags = hlsSegment.tags;
const absoluteSegmentUri = this.variableSubstitution_(
hlsSegment.absoluteUri, variables);
Expand Down Expand Up @@ -2539,7 +2537,7 @@ shaka.hls.HlsParser = class {
pStartByte,
pEndByte,
initSegmentReference,
/* timestampOffset= */ 0, // This value is ignored in sequence mode.
/* timestampOffset= */ 0,
/* appendWindowStart= */ 0,
/* appendWindowEnd= */ Infinity,
/* partialReferences= */ [],
Expand Down Expand Up @@ -2612,7 +2610,7 @@ shaka.hls.HlsParser = class {
startByte,
endByte,
initSegmentReference,
timestampOffset, // This value is ignored in sequence mode.
/* timestampOffset= */ 0,
/* appendWindowStart= */ 0,
/* appendWindowEnd= */ Infinity,
partialSegmentRefs,
Expand Down Expand Up @@ -2697,7 +2695,6 @@ shaka.hls.HlsParser = class {
const references = [];

let previousReference = null;
let lastDiscontinuityStartTime = firstStartTime;

for (let i = 0; i < hlsSegments.length; i++) {
const item = hlsSegments[i];
Expand All @@ -2709,7 +2706,6 @@ shaka.hls.HlsParser = class {
item.tags, 'EXT-X-DISCONTINUITY');
if (discontinuityTag) {
discontinuitySequence++;
lastDiscontinuityStartTime = startTime;
}

// Apply new AES-128 tags as you see them, keeping a running total.
Expand Down Expand Up @@ -2748,7 +2744,6 @@ shaka.hls.HlsParser = class {
variables,
playlist.absoluteUri,
type,
lastDiscontinuityStartTime,
hlsAes128Key);

if (reference) {
Expand Down
18 changes: 3 additions & 15 deletions lib/media/streaming_engine.js
Original file line number Diff line number Diff line change
Expand Up @@ -1693,10 +1693,10 @@ shaka.media.StreamingEngine = class {
}
}

const lastDiscontinuitySequence =
mediaState.lastSegmentReference ?
mediaState.lastSegmentReference.discontinuitySequence : null;
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.
Expand All @@ -1706,18 +1706,6 @@ shaka.media.StreamingEngine = class {
operations.push(this.playerInterface_.mediaSourceEngine.resync(
mediaState.type, reference.startTime));
}
} else {
// In segments mode, we need to resync to set the timestampOffset
// to the start of the current discontinuity sequence. This is
// because individual discontinuity sequences may have internal
// timestamps that overlap, so we adjust the timestampOffset to avoid
// having the SourceBuffer get overwritten.
if (reference.discontinuitySequence != lastDiscontinuitySequence) {
operations.push(
this.playerInterface_.mediaSourceEngine.resync(
mediaState.type,
reference.timestampOffset));
}
}

await Promise.all(operations);
Expand Down
57 changes: 0 additions & 57 deletions test/hls/hls_parser_unit.js
Original file line number Diff line number Diff line change
Expand Up @@ -1114,63 +1114,6 @@ describe('HlsParser', () => {
expect(references[6].discontinuitySequence).toBe(3);
});

it('sets reference timetampOffset based on discontinuity start time',
async () => {
const master = [
'#EXTM3U\n',
'#EXT-X-STREAM-INF:BANDWIDTH=2000000,CODECS="avc1"\n',
'video\n',
].join('');

const media = [
'#EXTM3U\n',
'#EXT-X-VERSION:3\n',
'#EXT-X-TARGETDURATION:5\n',
'#EXT-X-MEDIA-SEQUENCE:0\n',
'#EXTINF:3,\n',
'clip0-video-0.ts\n',
'#EXTINF:1,\n',
'clip0-video-1.ts\n',
'#EXT-X-DISCONTINUITY\n',
'#EXTINF:2,\n',
'clip1-video-1.ts\n',
'#EXTINF:3,\n',
'clip1-video-2.ts\n',
'#EXT-X-DISCONTINUITY\n',
'#EXTINF:1,\n',
'media-clip2-video-0.ts\n',
'#EXTINF:1,\n',
'media-clip2-video-1.ts\n',
'#EXT-X-DISCONTINUITY\n',
'#EXTINF:4,\n',
'media-clip3-video-1.ts\n',
'#EXT-X-ENDLIST\n',
].join('');

fakeNetEngine
.setResponseText('test:/master', master)
.setResponseText('test:/video', media);

const manifest = await parser.start('test:/master', playerInterface);
await manifest.variants[0].video.createSegmentIndex();

const segmentIndex = manifest.variants[0].video.segmentIndex;
const references = [];

for (let i = 0; i < 7; i++) {
references.push(segmentIndex.get(i));
}

expect(references[0].timestampOffset).toBe(0);
expect(references[1].timestampOffset).toBe(0);
expect(references[2].timestampOffset).toBe(4);
expect(references[3].timestampOffset).toBe(4);
expect(references[4].timestampOffset).toBe(9);
expect(references[5].timestampOffset).toBe(9);
expect(references[6].timestampOffset).toBe(11);
},
);

it('parses characteristics from audio tags', async () => {
const master = [
'#EXTM3U\n',
Expand Down

0 comments on commit cb9789e

Please sign in to comment.