Skip to content

Commit

Permalink
fix(HLS): getPlayheadTimeAsDate() differs from X-EXT-PROGRAM-DATE-TIME (
Browse files Browse the repository at this point in the history
#6371)

Fixes #5939
  • Loading branch information
avelad committed Apr 2, 2024
1 parent 7c6e846 commit c615cf4
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 2 deletions.
16 changes: 16 additions & 0 deletions lib/hls/hls_parser.js
Expand Up @@ -1003,6 +1003,22 @@ shaka.hls.HlsParser = class {
this.presentationTimeline_.setSegmentAvailabilityDuration(
segmentAvailabilityDuration);
}

if (!this.presentationTimeline_.isStartTimeLocked()) {
for (const streamInfo of this.uriToStreamInfosMap_.values()) {
if (!streamInfo.stream.segmentIndex) {
continue; // Not active.
}
if (streamInfo.type != 'audio' && streamInfo.type != 'video') {
continue;
}
const firstReference = streamInfo.stream.segmentIndex.get(0);
if (firstReference && firstReference.syncTime) {
const syncTime = firstReference.syncTime;
this.presentationTimeline_.setInitialProgramDateTime(syncTime);
}
}
}
} else {
// Use the minimum duration as the presentation duration.
this.presentationTimeline_.setDuration(this.getMinDuration_());
Expand Down
34 changes: 34 additions & 0 deletions lib/media/presentation_timeline.js
Expand Up @@ -96,6 +96,9 @@ shaka.media.PresentationTimeline = class {

/** @private {boolean} */
this.startTimeLocked_ = false;

/** @private {?number} */
this.initialProgramDateTime_ = null;
}


Expand Down Expand Up @@ -339,6 +342,37 @@ shaka.media.PresentationTimeline = class {
}


/**
* Returns if the presentation timeline's start time is locked.
*
* @return {boolean}
* @export
*/
isStartTimeLocked() {
return this.startTimeLocked_;
}


/**
* Sets the initial program date time.
*
* @param {number} initialProgramDateTime
* @export
*/
setInitialProgramDateTime(initialProgramDateTime) {
this.initialProgramDateTime_ = initialProgramDateTime;
}


/**
* @return {?number} The initial program date time in seconds.
* @export
*/
getInitialProgramDateTime() {
return this.initialProgramDateTime_;
}


/**
* Gives PresentationTimeline a Stream's minimum segment start time.
*
Expand Down
3 changes: 2 additions & 1 deletion lib/player.js
Expand Up @@ -4665,7 +4665,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {

if (this.manifest_) {
const timeline = this.manifest_.presentationTimeline;
const startTime = timeline.getPresentationStartTime();
const startTime = timeline.getInitialProgramDateTime() ||
timeline.getPresentationStartTime();
return new Date(/* ms= */ (startTime + presentationTime) * 1000);
} else if (this.video_ && this.video_.getStartDate) {
// Apple's native HLS gives us getStartDate(), which is only available if
Expand Down
14 changes: 13 additions & 1 deletion test/player_unit.js
Expand Up @@ -3800,11 +3800,14 @@ describe('Player', () => {
});

describe('getPlayheadTimeAsDate()', () => {
/** @type {?shaka.media.PresentationTimeline} */
let timeline;
beforeEach(async () => {
const timeline = new shaka.media.PresentationTimeline(300, 0);
timeline = new shaka.media.PresentationTimeline(300, 0);
timeline.setStatic(false);

manifest = shaka.test.ManifestGenerator.generate((manifest) => {
goog.asserts.assert(timeline, 'timeline must be non-null');
manifest.presentationTimeline = timeline;
manifest.addVariant(0, (variant) => {
variant.addVideo(1);
Expand All @@ -3822,6 +3825,15 @@ describe('Player', () => {
// (300 (presentation start time) + 20 (playhead time)) * 1000 (ms/sec)
expect(liveTimeUtc).toEqual(new Date(320 * 1000));
});

it('uses program date time', () => {
timeline.setInitialProgramDateTime(100);
playhead.getTime.and.returnValue(20);

const liveTimeUtc = player.getPlayheadTimeAsDate();
// (100 (program date time) + 20 (playhead time)) * 1000 (ms/sec)
expect(liveTimeUtc).toEqual(new Date(120 * 1000));
});
});

describe('getSegmentAvailabilityDuration()', () => {
Expand Down

0 comments on commit c615cf4

Please sign in to comment.