Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: update m3u8 parser version and restore dateTimeObject and dateTimeString usage #1412

Merged
merged 3 commits into from
Aug 7, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/playlist-loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,7 @@ export default class PlaylistLoader extends EventTarget {
this.dateRangesStorage_.setPendingDateRanges(mediaPlaylist.dateRanges);
const availableDateRanges = this.dateRangesStorage_.getDateRangesToProcess();

if (!availableDateRanges.length) {
if (!availableDateRanges.length || !this.addDateRangesToTextTrack_) {
Copy link
Contributor Author

@harisha-swaminathan harisha-swaminathan Aug 4, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noticed that there were several !this.addDateRangesToTextTrack_ is not a function errors when I tested dateRanges with a specific stream. On logging !!his.addDateRangesToTextTrack_, noticed that the function was undefined on every alternate loadedplaylist event (line 410). Not sure why this is happening, so added a quick fix here before debugging in detail.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to be because the test stream has subtitles, which in this case we create a PlaylistLoader for, this does not have the addDateRangesToTextTrack_ function defined, as it is only needed for the mainPlaylistLoader for HLS.

return;
}

Expand Down
7 changes: 2 additions & 5 deletions src/segment-loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -3182,11 +3182,8 @@ export default class SegmentLoader extends videojs.EventTarget {
const Cue = window.WebKitDataCue || window.VTTCue;
const value = {
custom: segment.custom,
// Since we now have programDateTime available for every segment, dateTimeObject and dateTimeString
// are redundant
// TODO: Consider removing this in future major version
dateTimeObject: segment.programDateTime ? new Date(segment.programDateTime) : undefined,
dateTimeString: segment.programDateTime ? new Date(segment.programDateTime).toISOString() : undefined,
dateTimeObject: segment.dateTimeObject,
dateTimeString: segment.dateTimeString,
programDateTime: segment.programDateTime,
bandwidth: segmentInfo.playlist.attributes.BANDWIDTH,
resolution: segmentInfo.playlist.attributes.RESOLUTION,
Expand Down
12 changes: 6 additions & 6 deletions src/sync-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,11 @@ export const syncPointStrategies = [
const datetimeMapping =
syncController.timelineToDatetimeMappings[segment.timeline];

if (!datetimeMapping || !segment.programDateTime) {
if (!datetimeMapping || !segment.dateTimeObject) {
continue;
}

const segmentTime = segment.programDateTime / 1000;
const segmentTime = segment.dateTimeObject.getTime() / 1000;
let start = segmentTime + datetimeMapping;

// take part duration into account.
Expand Down Expand Up @@ -408,9 +408,9 @@ export default class SyncController extends videojs.EventTarget {

if (playlist.segments &&
playlist.segments.length &&
playlist.segments[0].programDateTime) {
playlist.segments[0].dateTimeObject) {
const firstSegment = playlist.segments[0];
const playlistTimestamp = firstSegment.programDateTime / 1000;
const playlistTimestamp = firstSegment.dateTimeObject.getTime() / 1000;

this.timelineToDatetimeMappings[firstSegment.timeline] = -playlistTimestamp;
}
Expand Down Expand Up @@ -449,10 +449,10 @@ export default class SyncController extends videojs.EventTarget {
}
}

const dateTime = segment.programDateTime;
const dateTime = segment.dateTimeObject;

if (segment.discontinuity && shouldSaveTimelineMapping && dateTime) {
this.timelineToDatetimeMappings[segment.timeline] = -(dateTime / 1000);
this.timelineToDatetimeMappings[segment.timeline] = -(dateTime.getTime() / 1000);
}
}

Expand Down
16 changes: 8 additions & 8 deletions src/util/time.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const SEGMENT_END_FUDGE_PERCENT = 0.25;
* @return {Date} program time
*/
export const playerTimeToProgramTime = (playerTime, segment) => {
if (!segment.programDateTime) {
if (!segment.dateTimeObject) {
// Can't convert without an "anchor point" for the program time (i.e., a time that can
// be used to map the start of a segment with a real world time).
return null;
Expand All @@ -39,7 +39,7 @@ export const playerTimeToProgramTime = (playerTime, segment) => {
const startOfSegment = transmuxedStart + transmuxerPrependedSeconds;
const offsetFromSegmentStart = playerTime - startOfSegment;

return new Date(segment.programDateTime + offsetFromSegmentStart * 1000);
return new Date(segment.dateTimeObject.getTime() + offsetFromSegmentStart * 1000);
};

export const originalSegmentVideoDuration = (videoTimingInfo) => {
Expand Down Expand Up @@ -74,28 +74,28 @@ export const findSegmentForProgramTime = (programTime, playlist) => {

let segment = playlist.segments[0];

if (dateTimeObject < new Date(segment.programDateTime)) {
if (dateTimeObject < new Date(segment.dateTimeObject)) {
// Requested time is before stream start.
return null;
}

for (let i = 0; i < playlist.segments.length - 1; i++) {
segment = playlist.segments[i];

const nextSegmentStart = new Date(playlist.segments[i + 1].programDateTime);
const nextSegmentStart = new Date(playlist.segments[i + 1].dateTimeObject);

if (dateTimeObject < nextSegmentStart) {
break;
}
}

const lastSegment = playlist.segments[playlist.segments.length - 1];
const lastSegmentStart = lastSegment.programDateTime;
const lastSegmentStart = lastSegment.dateTimeObject;
const lastSegmentDuration = lastSegment.videoTimingInfo ?
originalSegmentVideoDuration(lastSegment.videoTimingInfo) :
lastSegment.duration + lastSegment.duration * SEGMENT_END_FUDGE_PERCENT;
const lastSegmentEnd =
new Date(lastSegmentStart + lastSegmentDuration * 1000);
new Date(lastSegmentStart.getTime() + lastSegmentDuration * 1000);

if (dateTimeObject > lastSegmentEnd) {
// Beyond the end of the stream, or our best guess of the end of the stream.
Expand Down Expand Up @@ -230,7 +230,7 @@ export const verifyProgramDateTimeTags = (playlist) => {
for (let i = 0; i < playlist.segments.length; i++) {
const segment = playlist.segments[i];

if (!segment.programDateTime) {
if (!segment.dateTimeObject) {
return false;
}
}
Expand Down Expand Up @@ -355,7 +355,7 @@ export const seekToProgramTime = ({

const segment = matchedSegment.segment;
const mediaOffset = getOffsetFromTimestamp(
segment.programDateTime,
segment.dateTimeObject,
programTime
);

Expand Down
2 changes: 1 addition & 1 deletion test/playlist-loader.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1053,7 +1053,7 @@ QUnit.module('Playlist Loader', function(hooks) {
const segment = loader.main.playlists[0].segments[0];

assert.strictEqual(segment.custom.test, '#PARSER:parsed', 'parsed custom tag');
assert.ok(segment.programDateTime, 'converted and parsed custom time');
assert.ok(segment.dateTimeObject, 'converted and parsed custom time');

delete this.fakeVhs.options_;
});
Expand Down
28 changes: 14 additions & 14 deletions test/segment-loader.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4560,26 +4560,26 @@ QUnit.module('SegmentLoader', function(hooks) {

const segmentDurationMs = targetDuration * 1000;

const playlist1Start = new Date('2021-01-01T00:00:00.000-05:00').getTime();
const playlist1Start = new Date('2021-01-01T00:00:00.000-05:00');

playlist1.segments[0].programDateTime = playlist1Start;
playlist1.segments[1].programDateTime = new Date(playlist1Start + segmentDurationMs).getTime();
playlist1.segments[0].dateTimeObject = playlist1Start;
playlist1.segments[1].dateTimeObject = new Date(playlist1Start.getTime() + segmentDurationMs);
// jump of 0.5 seconds after disco (0.5 seconds of missing real world time, e.g.,
// an encoder went down briefly), should have a PDT mapping difference of -3.5
// seconds from first mapping
playlist1.segments[2].programDateTime = new Date(playlist1.segments[1].programDateTime + segmentDurationMs + 500).getTime();
playlist1.segments[3].programDateTime = new Date(playlist1.segments[2].programDateTime + segmentDurationMs).getTime();
playlist1.segments[2].dateTimeObject = new Date(playlist1.segments[1].dateTimeObject.getTime() + segmentDurationMs + 500);
playlist1.segments[3].dateTimeObject = new Date(playlist1.segments[2].dateTimeObject.getTime() + segmentDurationMs);

// offset by 0.25 seconds from playlist1
const playlist2Start = new Date('2021-01-01T00:00:00.250-05:00');

playlist2.segments[0].programDateTime = playlist2Start.getTime();
playlist2.segments[1].programDateTime = new Date(playlist2Start + segmentDurationMs).getTime();
playlist2.segments[0].dateTimeObject = playlist2Start;
playlist2.segments[1].dateTimeObject = new Date(playlist2Start.getTime() + segmentDurationMs);
// jump of 0.5 seconds after disco (0.5 seconds of missing real world time, e.g.,
// an encoder went down briefly), should have a PDT mapping difference of -3.5
// seconds from first mapping
playlist2.segments[2].programDateTime = new Date(playlist2.segments[1].programDateTime + segmentDurationMs + 500).getTime();
playlist2.segments[3].programDateTime = new Date(playlist2.segments[2].programDateTime + segmentDurationMs).getTime();
playlist2.segments[2].dateTimeObject = new Date(playlist2.segments[1].dateTimeObject.getTime() + segmentDurationMs + 500);
playlist2.segments[3].dateTimeObject = new Date(playlist2.segments[2].dateTimeObject.getTime() + segmentDurationMs);

const {
mediaSource_: mediaSource,
Expand Down Expand Up @@ -4680,11 +4680,11 @@ QUnit.module('SegmentLoader', function(hooks) {
const segment3Start = new Date(segment2Start.getTime() + segmentDurationMs + 500);

[playlist1, playlist2].forEach((playlist) => {
playlist.programDateTime = segment0Start;
playlist.segments[0].programDateTime = segment0Start;
playlist.segments[1].programDateTime = segment1Start;
playlist.segments[2].programDateTime = segment2Start;
playlist.segments[3].programDateTime = segment3Start;
playlist.dateTimeObject = segment0Start;
playlist.segments[0].dateTimeObject = segment0Start;
playlist.segments[1].dateTimeObject = segment1Start;
playlist.segments[2].dateTimeObject = segment2Start;
playlist.segments[3].dateTimeObject = segment3Start;
});

const {
Expand Down
38 changes: 19 additions & 19 deletions test/sync-controller.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ QUnit.test('returns correct sync point for ProgramDateTime strategy', function(a

assert.equal(syncPoint, null, 'no syncpoint when no date time to display time mapping');

playlist.segments[0].programDateTime = datetime.getTime();
playlist.segments[0].dateTimeObject = datetime;

this.syncController.setDateTimeMappingForStart(playlist);

Expand All @@ -55,7 +55,7 @@ QUnit.test('returns correct sync point for ProgramDateTime strategy', function(a

assert.equal(syncPoint, null, 'no syncpoint when datetimeObject not set on playlist');

newPlaylist.segments[0].programDateTime = new Date(2012, 11, 12, 12, 12, 22).getTime();
newPlaylist.segments[0].dateTimeObject = new Date(2012, 11, 12, 12, 12, 22);

syncPoint = strategy.run(this.syncController, newPlaylist, duration, timeline);

Expand All @@ -78,7 +78,7 @@ QUnit.test('ProgramDateTime strategy finds nearest segment for sync', function(a
assert.equal(syncPoint, null, 'no syncpoint when no date time to display time mapping');

playlist.segments.forEach((segment, index) => {
segment.programDateTime = new Date(2012, 11, 12, 12, 12, 12 + (index * 10)).getTime();
segment.dateTimeObject = new Date(2012, 11, 12, 12, 12, 12 + (index * 10));
});

this.syncController.setDateTimeMappingForStart(playlist);
Expand All @@ -90,7 +90,7 @@ QUnit.test('ProgramDateTime strategy finds nearest segment for sync', function(a
assert.equal(syncPoint, null, 'no syncpoint when datetimeObject not set on playlist');

newPlaylist.segments.forEach((segment, index) => {
segment.programDateTime = new Date(2012, 11, 12, 12, 12, 22 + (index * 10)).getTime();
segment.dateTimeObject = new Date(2012, 11, 12, 12, 12, 22 + (index * 10));
});

syncPoint = strategy.run(this.syncController, newPlaylist, duration, timeline, 170);
Expand All @@ -115,7 +115,7 @@ QUnit.test(
function(assert) {
const playlist = playlistWithDuration(40);

playlist.segments[1].programDateTime = new Date(2012, 11, 12, 12, 12, 12).getTime();
playlist.segments[1].dateTimeObject = new Date(2012, 11, 12, 12, 12, 12);

this.syncController.setDateTimeMappingForStart(playlist);

Expand All @@ -125,7 +125,7 @@ QUnit.test(
'did not set datetime mapping'
);

playlist.segments[0].programDateTime = new Date(2012, 11, 12, 12, 12, 2).getTime();
playlist.segments[0].dateTimeObject = new Date(2012, 11, 12, 12, 12, 2);

this.syncController.setDateTimeMappingForStart(playlist);

Expand All @@ -140,12 +140,12 @@ QUnit.test(
QUnit.test('uses separate date time to display time mapping for each timeline', function(assert) {
const playlist = playlistWithDuration(40, { discontinuityStarts: [1, 3] });

playlist.segments[0].programDateTime = new Date(2020, 1, 1, 1, 1, 1).getTime();
playlist.segments[0].dateTimeObject = new Date(2020, 1, 1, 1, 1, 1);
// 20 seconds later (10 more than default)
playlist.segments[1].programDateTime = new Date(2020, 1, 1, 1, 1, 21).getTime();
playlist.segments[2].programDateTime = new Date(2020, 1, 1, 1, 1, 31).getTime();
playlist.segments[1].dateTimeObject = new Date(2020, 1, 1, 1, 1, 21);
playlist.segments[2].dateTimeObject = new Date(2020, 1, 1, 1, 1, 31);
// 30 seconds later (20 more than default)
playlist.segments[3].programDateTime = new Date(2020, 1, 1, 1, 2, 1).getTime();
playlist.segments[3].dateTimeObject = new Date(2020, 1, 1, 1, 2, 1);

// after this call, the initial playlist mapping will be provided
this.syncController.setDateTimeMappingForStart(playlist);
Expand All @@ -165,7 +165,7 @@ QUnit.test('uses separate date time to display time mapping for each timeline',
assert.deepEqual(
this.syncController.timelineToDatetimeMappings,
{
0: -(playlist.segments[0].programDateTime / 1000)
0: -(playlist.segments[0].dateTimeObject.getTime() / 1000)
},
'has correct mapping for timeline 0'
);
Expand All @@ -183,8 +183,8 @@ QUnit.test('uses separate date time to display time mapping for each timeline',
assert.deepEqual(
this.syncController.timelineToDatetimeMappings,
{
0: -(playlist.segments[0].programDateTime / 1000),
1: -(playlist.segments[1].programDateTime / 1000)
0: -(playlist.segments[0].dateTimeObject.getTime() / 1000),
1: -(playlist.segments[1].dateTimeObject.getTime() / 1000)
},
'has correct mapping for timelines 0 and 1'
);
Expand All @@ -202,8 +202,8 @@ QUnit.test('uses separate date time to display time mapping for each timeline',
assert.deepEqual(
this.syncController.timelineToDatetimeMappings,
{
0: -(playlist.segments[0].programDateTime / 1000),
1: -(playlist.segments[1].programDateTime / 1000)
0: -(playlist.segments[0].dateTimeObject.getTime() / 1000),
1: -(playlist.segments[1].dateTimeObject.getTime() / 1000)
},
'does not add a new timeline mapping when no disco'
);
Expand All @@ -221,9 +221,9 @@ QUnit.test('uses separate date time to display time mapping for each timeline',
assert.deepEqual(
this.syncController.timelineToDatetimeMappings,
{
0: -(playlist.segments[0].programDateTime / 1000),
1: -(playlist.segments[1].programDateTime / 1000),
2: -(playlist.segments[3].programDateTime / 1000)
0: -(playlist.segments[0].dateTimeObject.getTime() / 1000),
1: -(playlist.segments[1].dateTimeObject.getTime() / 1000),
2: -(playlist.segments[3].dateTimeObject.getTime() / 1000)
},
'has correct mappings for timelines 0, 1, and 2'
);
Expand All @@ -241,7 +241,7 @@ QUnit.test('ProgramDateTime strategy finds nearest llhls sync point', function(a
assert.equal(syncPoint, null, 'no syncpoint when no date time to display time mapping');

playlist.segments.forEach((segment, index) => {
segment.programDateTime = new Date(2012, 11, 12, 12, 12, 12 + (index * 10)).getTime();
segment.dateTimeObject = new Date(2012, 11, 12, 12, 12, 12 + (index * 10));
});

this.syncController.setDateTimeMappingForStart(playlist);
Expand Down
Loading