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 program date time handling #45

Merged
merged 4 commits into from
Mar 2, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,8 @@
"dependencies": {
"aes-decrypter": "1.0.3",
"global": "^4.3.0",
"m3u8-parser": "2.1.0",
"mpd-parser": "0.4.0",
"m3u8-parser": "4.2.0",
"mux.js": "4.3.2",
"url-toolkit": "^2.1.3",
"video.js": "^6.2.0",
Expand Down
46 changes: 35 additions & 11 deletions src/sync-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,38 @@ export const syncPointStrategies = [
{
name: 'ProgramDateTime',
run: (syncController, playlist, duration, currentTimeline, currentTime) => {
if (syncController.datetimeToDisplayTime && playlist.dateTimeObject) {
let playlistTime = playlist.dateTimeObject.getTime() / 1000;
let playlistStart = playlistTime + syncController.datetimeToDisplayTime;
let syncPoint = {
time: playlistStart,
segmentIndex: 0
};
if (!syncController.datetimeToDisplayTime) {
return null;
}

return syncPoint;
let segments = playlist.segments || [];
let syncPoint = null;
let lastDistance = null;

currentTime = currentTime || 0;

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

if (segment.dateTimeObject) {
let segmentTime = segment.dateTimeObject.getTime() / 1000;
let segmentStart = segmentTime + syncController.datetimeToDisplayTime;
let distance = Math.abs(currentTime - segmentStart);

// Once the distance begins to increase, we have passed
// currentTime and can stop looking for better candidates
if (lastDistance !== null && lastDistance < distance) {
break;
}

lastDistance = distance;
syncPoint = {
time: segmentStart,
segmentIndex: i
};
}
}
return null;
return syncPoint;
}
},
// Stategy "Segment": We have a known time mapping for a timeline and a
Expand Down Expand Up @@ -336,8 +357,11 @@ export default class SyncController extends videojs.EventTarget {
* @param {Playlist} playlist - The currently active playlist
*/
setDateTimeMapping(playlist) {
if (!this.datetimeToDisplayTime && playlist.dateTimeObject) {
let playlistTimestamp = playlist.dateTimeObject.getTime() / 1000;
if (!this.datetimeToDisplayTime &&
playlist.segments &&
playlist.segments.length &&
playlist.segments[0].dateTimeObject) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we only want to consider the first segment?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think yes. The intention of this date time mapping that is being stored is supposed to be the offset of real time to display time for the start of stream (at time of join, i.e. first segment in first playlist). If a segment other than the first is used, you'd have to account for the duration of the segments that came before it in the playlist when trying to calculate sync points from it. This has the potential for inaccuracies

let playlistTimestamp = playlist.segments[0].dateTimeObject.getTime() / 1000;

this.datetimeToDisplayTime = -playlistTimestamp;
}
Expand Down
56 changes: 54 additions & 2 deletions test/sync-controller.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ QUnit.test('returns correct sync point for ProgramDateTime strategy', function(a

assert.equal(syncPoint, null, 'no syncpoint when datetimeToDisplayTime not set');

playlist.dateTimeObject = datetime;
playlist.segments[0].dateTimeObject = datetime;

this.syncController.setDateTimeMapping(playlist);

Expand All @@ -56,7 +56,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.dateTimeObject = new Date(2012, 11, 12, 12, 12, 22);
newPlaylist.segments[0].dateTimeObject = new Date(2012, 11, 12, 12, 12, 22);

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

Expand All @@ -66,6 +66,58 @@ QUnit.test('returns correct sync point for ProgramDateTime strategy', function(a
}, 'syncpoint found for ProgramDateTime set');
});

QUnit.test('ProgramDateTime strategy finds nearest segment for sync', function(assert) {
let strategy = getStrategy('ProgramDateTime');
let playlist = playlistWithDuration(40);
let timeline = 0;
let duration = Infinity;
let syncPoint;

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

assert.equal(syncPoint, null, 'no syncpoint when datetimeToDisplayTime not set');

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

this.syncController.setDateTimeMapping(playlist);

let newPlaylist = playlistWithDuration(40);

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

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

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

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

assert.deepEqual(syncPoint, {
time: 20,
segmentIndex: 1
}, 'syncpoint found for ProgramDateTime set');
});

QUnit.test('Does not set date time mapping if date time info not on first segment',
function(assert) {
let playlist = playlistWithDuration(40);

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

this.syncController.setDateTimeMapping(playlist);

assert.notOk(this.syncController.datetimeToDisplayTime, 'did not set datetime mapping');

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

this.syncController.setDateTimeMapping(playlist);

assert.ok(this.syncController.datetimeToDisplayTime, 'did set date time mapping');
});

QUnit.test('returns correct sync point for Segment strategy', function(assert) {
let strategy = getStrategy('Segment');
let playlist = {
Expand Down