From f805efe39b5b83c1f3cd6cf3a0ddf69344535aae Mon Sep 17 00:00:00 2001 From: Joaquin Bartaburu Date: Fri, 20 Sep 2024 16:57:24 -0300 Subject: [PATCH 01/10] feature: play and playing time obtained for msd key --- lib/player.js | 7 +++++++ lib/util/cmcd_manager.js | 22 ++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/lib/player.js b/lib/player.js index 3a628b1d07..9a35e0d9fd 100644 --- a/lib/player.js +++ b/lib/player.js @@ -2448,6 +2448,13 @@ shaka.Player = class extends shaka.util.FakeEventTarget { movePlayhead: (delta) => { mediaElement.currentTime += delta; }, }); + if(this.cmcdManager_) { + this.loadEventManager_.listen( + mediaElement, 'play', this.cmcdManager_.onPlaybackPlay_); + this.loadEventManager_.listen( + mediaElement, 'playing', this.cmcdManager_.onPlaybackPlaying_); + } + const updateStateHistory = () => this.updateStateHistory_(); const onRateChange = () => this.onRateChange_(); this.loadEventManager_.listen( diff --git a/lib/util/cmcd_manager.js b/lib/util/cmcd_manager.js index a7294be83e..2a0252136f 100644 --- a/lib/util/cmcd_manager.js +++ b/lib/util/cmcd_manager.js @@ -56,6 +56,16 @@ shaka.util.CmcdManager = class { * @private {boolean} */ this.lowLatency_ = false; + + /** + * @private {number} + */ + this.playbackPlayTime_ = null; + + /** + * @private {number} + */ + this.playbackPlayingTime_ = null; } /** @@ -192,6 +202,18 @@ shaka.util.CmcdManager = class { } } + onPlaybackPlay_() { + if (!this.playbackPlayTime_) { + this.playbackPlayTime_ = performance.now(); + } + } + + onPlaybackPlaying_() { + if (!this.playbackPlayingTime_) { + this.playbackPlayingTime_ = performance.now(); + } + } + /** * Apply CMCD data to a segment request * From d224c7bcc249ab4de550208b032d5474e90a5fdb Mon Sep 17 00:00:00 2001 From: Sebastian Piquerez Date: Wed, 25 Sep 2024 13:50:41 -0300 Subject: [PATCH 02/10] Implement msd CMCDv2 key --- externs/cmcd.js | 9 ++++++- externs/shaka/player.js | 19 +++++++++++++- lib/player.js | 11 ++++---- lib/util/cmcd_manager.js | 44 +++++++++++++++++++++----------- lib/util/player_configuration.js | 5 ++++ 5 files changed, 66 insertions(+), 22 deletions(-) diff --git a/externs/cmcd.js b/externs/cmcd.js index a8cf49e3b4..d68b49c52a 100644 --- a/externs/cmcd.js +++ b/externs/cmcd.js @@ -31,7 +31,8 @@ * st: (string|undefined), * v: (number|undefined), * bs: (boolean|undefined), - * rtp: (number|undefined) + * rtp: (number|undefined), + * msd: (number|undefined), * }} * * @description @@ -182,5 +183,11 @@ * delivery. The concept is that each client receives the throughput necessary * for great performance, but no more. The CDN may not support the rtp * feature. + * + * @property {number} msd + * The Media Start Delay represents in milliseconds the delay between the + * initiation of the playback request and the moment the first frame is + * rendered. This is sent only once when it is calculated. + * */ var CmcdData; diff --git a/externs/shaka/player.js b/externs/shaka/player.js index 244c13b32c..ee6440edcf 100644 --- a/externs/shaka/player.js +++ b/externs/shaka/player.js @@ -2091,6 +2091,20 @@ shaka.extern.AbrConfiguration; */ shaka.extern.AdvancedAbrConfiguration; +/** + * @typedef {{ + * version: number + * }} + * @property {number} version + */ +shaka.extern.RequestMode; + +/** + * @typedef {{ + * requestMode: shaka.extern.RequestMode + * }} + */ +shaka.extern.Reporting; /** * @typedef {{ @@ -2099,7 +2113,8 @@ shaka.extern.AdvancedAbrConfiguration; * sessionId: string, * contentId: string, * rtpSafetyFactor: number, - * includeKeys: !Array + * includeKeys: !Array, + * reporting: shaka.extern.Reporting * }} * * @description @@ -2138,6 +2153,8 @@ shaka.extern.AdvancedAbrConfiguration; * will be included. *
* Defaults to []. + * @property {shaka.extern.Reporting} reporting + * Reporting mode configuration. * @exportDoc */ shaka.extern.CmcdConfiguration; diff --git a/lib/player.js b/lib/player.js index 9a35e0d9fd..d6878e0574 100644 --- a/lib/player.js +++ b/lib/player.js @@ -2448,11 +2448,12 @@ shaka.Player = class extends shaka.util.FakeEventTarget { movePlayhead: (delta) => { mediaElement.currentTime += delta; }, }); - if(this.cmcdManager_) { - this.loadEventManager_.listen( - mediaElement, 'play', this.cmcdManager_.onPlaybackPlay_); - this.loadEventManager_.listen( - mediaElement, 'playing', this.cmcdManager_.onPlaybackPlaying_); + if (this.cmcdManager_) { + const onPlaybackPlay = () => this.cmcdManager_.onPlaybackPlay(); + const onPlaybackPlaying = () => this.cmcdManager_.onPlaybackPlaying(); + + this.loadEventManager_.listen(mediaElement, 'play', onPlaybackPlay); + this.loadEventManager_.listen(mediaElement, 'playing', onPlaybackPlaying); } const updateStateHistory = () => this.updateStateHistory_(); diff --git a/lib/util/cmcd_manager.js b/lib/util/cmcd_manager.js index 2a0252136f..272602009b 100644 --- a/lib/util/cmcd_manager.js +++ b/lib/util/cmcd_manager.js @@ -58,14 +58,19 @@ shaka.util.CmcdManager = class { this.lowLatency_ = false; /** - * @private {number} + * @private {number|undefined} */ - this.playbackPlayTime_ = null; + this.playbackPlayTime_ = undefined; /** - * @private {number} + * @private {number|undefined} */ - this.playbackPlayingTime_ = null; + this.playbackPlayingTime_ = undefined; + + /** + * @private {boolean} + */ + this.msdSent_ = false; } /** @@ -202,13 +207,19 @@ shaka.util.CmcdManager = class { } } - onPlaybackPlay_() { + /** + * Apply CMCD data to a manifest request. + */ + onPlaybackPlay() { if (!this.playbackPlayTime_) { this.playbackPlayTime_ = performance.now(); } } - onPlaybackPlaying_() { + /** + * Apply CMCD data to a manifest request. + */ + onPlaybackPlaying() { if (!this.playbackPlayingTime_) { this.playbackPlayingTime_ = performance.now(); } @@ -393,7 +404,7 @@ shaka.util.CmcdManager = class { this.config_.sessionId = window.crypto.randomUUID(); } return { - v: shaka.util.CmcdManager.Version, + v: this.config_.reporting.requestMode.version, sf: this.sf_, sid: this.config_.sessionId, cid: this.config_.contentId, @@ -432,6 +443,16 @@ shaka.util.CmcdManager = class { data.su = this.buffering_; } + if ( + data.v === 2 && + !this.msdSent_ && + this.playbackPlayingTime_ && + this.playbackPlayTime_ + ) { + data.msd = this.playbackPlayingTime_ - this.playbackPlayTime_; + this.msdSent_ = true; + } + const output = this.filterKeys_(data); if (useHeaders) { @@ -833,7 +854,7 @@ shaka.util.CmcdManager = class { const headerMap = { br: 0, d: 0, ot: 0, tb: 0, bl: 1, dl: 1, mtp: 1, nor: 1, nrr: 1, su: 1, - cid: 2, pr: 2, sf: 2, sid: 2, st: 2, v: 2, + cid: 2, pr: 2, sf: 2, sid: 2, st: 2, v: 2, msd: 2, bs: 3, rtp: 3, }; @@ -951,10 +972,3 @@ shaka.util.CmcdManager.StreamingFormat = { SMOOTH: 's', OTHER: 'o', }; - - -/** - * The CMCD spec version - * @const {number} - */ -shaka.util.CmcdManager.Version = 1; diff --git a/lib/util/player_configuration.js b/lib/util/player_configuration.js index eb30b2ef65..e371dc580d 100644 --- a/lib/util/player_configuration.js +++ b/lib/util/player_configuration.js @@ -369,6 +369,11 @@ shaka.util.PlayerConfiguration = class { rtpSafetyFactor: 5, useHeaders: false, includeKeys: [], + reporting: { + requestMode: { + version: 1, + }, + }, }; const cmsd = { From 51b686681933c416c6eeb2e35e1369aeca356681 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Manuel=20Tom=C3=A1s?= Date: Thu, 26 Sep 2024 16:24:44 -0300 Subject: [PATCH 03/10] Implement ltc CMCDv2 key --- externs/cmcd.js | 8 ++++++++ lib/player.js | 19 +++++++++++++++---- lib/util/cmcd_manager.js | 12 ++++++++++-- test/util/cmcd_manager_unit.js | 1 + 4 files changed, 34 insertions(+), 6 deletions(-) diff --git a/externs/cmcd.js b/externs/cmcd.js index d68b49c52a..d3091094e1 100644 --- a/externs/cmcd.js +++ b/externs/cmcd.js @@ -33,6 +33,7 @@ * bs: (boolean|undefined), * rtp: (number|undefined), * msd: (number|undefined), + * ltc: (number|undefined), * }} * * @description @@ -189,5 +190,12 @@ * initiation of the playback request and the moment the first frame is * rendered. This is sent only once when it is calculated. * + * @property {number} ltc + * Live Stream Latency + * + * The time delta between when a given media timestamp was made available at + * the origin and when it was rendered by the client. The accuracy of this + * estimate is dependent on synchronization between the packager and the + * player clocks. */ var CmcdData; diff --git a/lib/player.js b/lib/player.js index d6878e0574..846bc7e802 100644 --- a/lib/player.js +++ b/lib/player.js @@ -3695,6 +3695,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget { getNetworkingEngine: () => this.getNetworkingEngine(), getVariantTracks: () => this.getVariantTracks(), isLive: () => this.isLive(), + getLiveLatency: () => this.getLiveLatency(), }; return new shaka.util.CmcdManager(playerInterface, this.config_.cmcd); @@ -5621,6 +5622,19 @@ shaka.Player = class extends shaka.util.FakeEventTarget { return info; } + /** + * Get latency in milliseconds between the live edge and what's currently + * playing. + * + * @return {number} + */ + getLiveLatency() { + const element = this.video_; + const now = this.getPresentationStartTimeAsDate().valueOf() + + element.currentTime * 1000; + return Date.now() - now; + } + /** * Get statistics for the current playback session. If the player is not * playing content, this will return an empty stats object. @@ -5690,10 +5704,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget { } if (this.isLive()) { - const now = this.getPresentationStartTimeAsDate().valueOf() + - element.currentTime * 1000; - const latency = (Date.now() - now) / 1000; - this.stats_.setLiveLatency(latency); + this.stats_.setLiveLatency(this.getLiveLatency() / 1000); } if (this.manifest_) { diff --git a/lib/util/cmcd_manager.js b/lib/util/cmcd_manager.js index 272602009b..16bb7da579 100644 --- a/lib/util/cmcd_manager.js +++ b/lib/util/cmcd_manager.js @@ -443,6 +443,10 @@ shaka.util.CmcdManager = class { data.su = this.buffering_; } + if (data.v === 2 && this.playerInterface_.isLive()) { + data.ltc = this.playerInterface_.getLiveLatency(); + } + if ( data.v === 2 && !this.msdSent_ && @@ -853,7 +857,7 @@ shaka.util.CmcdManager = class { const headerGroups = [{}, {}, {}, {}]; const headerMap = { br: 0, d: 0, ot: 0, tb: 0, - bl: 1, dl: 1, mtp: 1, nor: 1, nrr: 1, su: 1, + bl: 1, dl: 1, mtp: 1, nor: 1, nrr: 1, su: 1, ltc: 1, cid: 2, pr: 2, sf: 2, sid: 2, st: 2, v: 2, msd: 2, bs: 3, rtp: 3, }; @@ -916,7 +920,8 @@ shaka.util.CmcdManager = class { * getCurrentTime: function():number, * getPlaybackRate: function():number, * getVariantTracks: function():Array., - * isLive: function():boolean + * isLive: function():boolean, + * getLiveLatency: function():number * }} * * @property {function():number} getBandwidthEstimate @@ -931,6 +936,9 @@ shaka.util.CmcdManager = class { * Get the variant tracks * @property {function():boolean} isLive * Get if the player is playing live content. + * @property {function():number} getLiveLatency + * Get latency in milliseconds between the live edge and what's currently + * playing. */ shaka.util.CmcdManager.PlayerInterface; diff --git a/test/util/cmcd_manager_unit.js b/test/util/cmcd_manager_unit.js index c846aa3521..3a636e0c96 100644 --- a/test/util/cmcd_manager_unit.js +++ b/test/util/cmcd_manager_unit.js @@ -31,6 +31,7 @@ describe('CmcdManager', () => { const playerInterface = { isLive: () => false, + getLiveLatency: () => 0, getBandwidthEstimate: () => 10000000, getBufferedInfo: () => ({ video: [ From e91b872b38b32b0d929ff7b6d9f6b9b74fbdd9d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Manuel=20Tom=C3=A1s?= Date: Fri, 27 Sep 2024 16:59:14 -0300 Subject: [PATCH 04/10] Add unit tests for CMCDv2 keys --- test/util/cmcd_manager_unit.js | 66 ++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/test/util/cmcd_manager_unit.js b/test/util/cmcd_manager_unit.js index 3a636e0c96..4f1cf84880 100644 --- a/test/util/cmcd_manager_unit.js +++ b/test/util/cmcd_manager_unit.js @@ -506,6 +506,72 @@ describe('CmcdManager', () => { const result = request.uris[0]; expect(result).not.toContain('?CMCD='); }); + + it('returns cmcd v2 data in query if version is 2', async () => { + cmcdManager = createCmcdManager({ + reporting: { + requestMode: { + version: 2, + }, + }, + includeKeys: ['ltc', 'msd'], + }); + await networkingEngine.request(RequestType.MANIFEST, request, + {type: AdvancedRequestType.MPD}); + const result = request.uris[0]; + expect(result).toContain(encodeURIComponent('ltc')); + expect(result).toContain(encodeURIComponent('msd')); + }); + + it('doesnt return cmcd v2 data in query if version is not 2', + async () => { + cmcdManager = createCmcdManager({ + reporting: { + requestMode: { + version: 1, + }, + }, + includeKeys: ['ltc', 'msd'], + }); + await networkingEngine.request(RequestType.MANIFEST, request, + {type: AdvancedRequestType.MPD}); + const result = request.uris[0]; + expect(result).not.toContain(encodeURIComponent('ltc')); + expect(result).not.toContain(encodeURIComponent('msd')); + }); + + it('returns cmcd v2 data in header if version is 2', async () => { + cmcdManager = createCmcdManager({ + reporting: { + requestMode: { + useHeaders: true, + version: 2, + }, + }, + includeKeys: ['ltc', 'msd'], + }); + await networkingEngine.request(RequestType.MANIFEST, request, + {type: AdvancedRequestType.MPD}); + expect(request.headers['CMCD-Request']).toContain('ltc'); + expect(request.headers['CMCD-Session']).toContain('msd'); + }); + + it('doesnt return cmcd v2 data in headers if version is not 2', + async () => { + cmcdManager = createCmcdManager({ + reporting: { + requestMode: { + useHeaders: true, + version: 1, + }, + }, + includeKeys: ['ltc', 'msd'], + }); + await networkingEngine.request(RequestType.MANIFEST, request, + {type: AdvancedRequestType.MPD}); + expect(request.headers['CMCD-Request']).not.toContain('ltc'); + expect(request.headers['CMCD-Session']).not.toContain('msd'); + }); }); }); }); From be1c4bf7c9be97f1fbd022e8adc50c19be148173 Mon Sep 17 00:00:00 2001 From: Joaquin Bartaburu Date: Wed, 2 Oct 2024 10:58:27 -0300 Subject: [PATCH 05/10] Unit tests fixed --- demo/config.js | 1 + test/cast/cast_utils_unit.js | 1 + test/util/cmcd_manager_unit.js | 44 ++++++++++++++++++++++++++++++---- 3 files changed, 42 insertions(+), 4 deletions(-) diff --git a/demo/config.js b/demo/config.js index 54a4b5793c..518611be1c 100644 --- a/demo/config.js +++ b/demo/config.js @@ -338,6 +338,7 @@ shakaDemo.Config = class { .addBoolInput_('Enabled', 'cmcd.enabled') .addTextInput_('Session ID', 'cmcd.sessionId') .addTextInput_('Content ID', 'cmcd.contentId') + .addTextInput_('Version', 'cmcd.reporting.requestMode.version') .addNumberInput_('RTP safety Factor', 'cmcd.rtpSafetyFactor', /* canBeDecimal= */ true) .addBoolInput_('Use Headers', 'cmcd.useHeaders'); diff --git a/test/cast/cast_utils_unit.js b/test/cast/cast_utils_unit.js index b9f394a5b3..ef7667a4cf 100644 --- a/test/cast/cast_utils_unit.js +++ b/test/cast/cast_utils_unit.js @@ -35,6 +35,7 @@ describe('CastUtils', () => { 'getNonDefaultConfiguration', 'addFont', 'getFetchedPlaybackInfo', + 'getLiveLatency', // Test helper methods (not @export'd) 'createDrmEngine', diff --git a/test/util/cmcd_manager_unit.js b/test/util/cmcd_manager_unit.js index 4f1cf84880..ec9b7aff80 100644 --- a/test/util/cmcd_manager_unit.js +++ b/test/util/cmcd_manager_unit.js @@ -65,6 +65,11 @@ describe('CmcdManager', () => { rtpSafetyFactor: 5, useHeaders: false, includeKeys: [], + reporting: { + requestMode: { + version: 1, + }, + }, }; function createCmcdConfig(cfg = {}) { @@ -508,24 +513,36 @@ describe('CmcdManager', () => { }); it('returns cmcd v2 data in query if version is 2', async () => { + // Set live to true to enable ltc + playerInterface.isLive = () => true; cmcdManager = createCmcdManager({ reporting: { requestMode: { version: 2, }, }, - includeKeys: ['ltc', 'msd'], + includeKeys: ['ltc', 'msd', 'v'], }); + networkingEngine = createNetworkingEngine(cmcdManager); + + // Trigger Play and Playing events + cmcdManager.onPlaybackPlay(); + cmcdManager.onPlaybackPlaying(); + const request = NetworkingEngine.makeRequest([uri], retry); await networkingEngine.request(RequestType.MANIFEST, request, {type: AdvancedRequestType.MPD}); const result = request.uris[0]; + expect(result).toContain(encodeURIComponent('v=2')); expect(result).toContain(encodeURIComponent('ltc')); expect(result).toContain(encodeURIComponent('msd')); }); it('doesnt return cmcd v2 data in query if version is not 2', async () => { - cmcdManager = createCmcdManager({ + // Set live to true to enable ltc + playerInterface.isLive = () => true; + + const cmcdManagerTmp = createCmcdManager({ reporting: { requestMode: { version: 1, @@ -533,6 +550,13 @@ describe('CmcdManager', () => { }, includeKeys: ['ltc', 'msd'], }); + networkingEngine = createNetworkingEngine(cmcdManagerTmp); + + // Trigger Play and Playing events + cmcdManagerTmp.onPlaybackPlay(); + cmcdManagerTmp.onPlaybackPlaying(); + + const request = NetworkingEngine.makeRequest([uri], retry); await networkingEngine.request(RequestType.MANIFEST, request, {type: AdvancedRequestType.MPD}); const result = request.uris[0]; @@ -541,15 +565,22 @@ describe('CmcdManager', () => { }); it('returns cmcd v2 data in header if version is 2', async () => { + playerInterface.isLive = () => true; cmcdManager = createCmcdManager({ reporting: { requestMode: { - useHeaders: true, version: 2, }, }, includeKeys: ['ltc', 'msd'], + useHeaders: true, }); + networkingEngine = createNetworkingEngine(cmcdManager); + + // Trigger Play and Playing events + cmcdManager.onPlaybackPlay(); + cmcdManager.onPlaybackPlaying(); + const request = NetworkingEngine.makeRequest([uri], retry); await networkingEngine.request(RequestType.MANIFEST, request, {type: AdvancedRequestType.MPD}); expect(request.headers['CMCD-Request']).toContain('ltc'); @@ -558,15 +589,20 @@ describe('CmcdManager', () => { it('doesnt return cmcd v2 data in headers if version is not 2', async () => { + playerInterface.isLive = () => true; cmcdManager = createCmcdManager({ reporting: { requestMode: { - useHeaders: true, version: 1, }, }, includeKeys: ['ltc', 'msd'], + useHeaders: true, }); + networkingEngine = createNetworkingEngine(cmcdManager); + cmcdManager.onPlaybackPlay(); + cmcdManager.onPlaybackPlaying(); + const request = NetworkingEngine.makeRequest([uri], retry); await networkingEngine.request(RequestType.MANIFEST, request, {type: AdvancedRequestType.MPD}); expect(request.headers['CMCD-Request']).not.toContain('ltc'); From 459c766d3fed782360b6eb2365fa641819a7e96b Mon Sep 17 00:00:00 2001 From: Joaquin Bartaburu Date: Wed, 2 Oct 2024 14:47:15 -0300 Subject: [PATCH 06/10] fix: listenOnce instead of listen for msd calculation --- lib/player.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/player.js b/lib/player.js index 2c070b9097..1288d80491 100644 --- a/lib/player.js +++ b/lib/player.js @@ -2453,8 +2453,10 @@ shaka.Player = class extends shaka.util.FakeEventTarget { const onPlaybackPlay = () => this.cmcdManager_.onPlaybackPlay(); const onPlaybackPlaying = () => this.cmcdManager_.onPlaybackPlaying(); - this.loadEventManager_.listen(mediaElement, 'play', onPlaybackPlay); - this.loadEventManager_.listen(mediaElement, 'playing', onPlaybackPlaying); + this.loadEventManager_.listenOnce( + mediaElement, 'play', onPlaybackPlay); + this.loadEventManager_.listenOnce( + mediaElement, 'playing', onPlaybackPlaying); } const updateStateHistory = () => this.updateStateHistory_(); From 29a7634a7d34c8d14fa122f4005e31c2e8930d4a Mon Sep 17 00:00:00 2001 From: Joaquin Bartaburu Date: Thu, 10 Oct 2024 11:34:01 -0300 Subject: [PATCH 07/10] constants for version added --- lib/util/cmcd_manager.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/lib/util/cmcd_manager.js b/lib/util/cmcd_manager.js index 16bb7da579..f76104ab61 100644 --- a/lib/util/cmcd_manager.js +++ b/lib/util/cmcd_manager.js @@ -443,12 +443,15 @@ shaka.util.CmcdManager = class { data.su = this.buffering_; } - if (data.v === 2 && this.playerInterface_.isLive()) { + if ( + data.v === shaka.util.CmcdManager.Version.VERSION_2 && + this.playerInterface_.isLive() + ) { data.ltc = this.playerInterface_.getLiveLatency(); } if ( - data.v === 2 && + data.v === shaka.util.CmcdManager.Version.VERSION_2 && !this.msdSent_ && this.playbackPlayingTime_ && this.playbackPlayTime_ @@ -958,6 +961,13 @@ shaka.util.CmcdManager.ObjectType = { OTHER: 'o', }; +/** + * @enum {number} + */ +shaka.util.CmcdManager.Version = { + VERSION_1: 1, + VERSION_2: 2, +}; /** * @enum {string} From 2d840b53641c8653866fa40327d0ae7761a331b9 Mon Sep 17 00:00:00 2001 From: Joaquin Bartaburu Date: Thu, 10 Oct 2024 15:46:47 -0300 Subject: [PATCH 08/10] feat: contributors updated --- AUTHORS | 1 + CONTRIBUTORS | 3 +++ 2 files changed, 4 insertions(+) diff --git a/AUTHORS b/AUTHORS index c68b50c292..6f3197ffb8 100644 --- a/AUTHORS +++ b/AUTHORS @@ -79,6 +79,7 @@ Peter Nycander Philo Inc. <*@philo.com> PikachuEXE Prakash +Qualabs <*@qualabs.com> Robert Colantuoni Robert Galluccio Rodolphe Breton diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 6a875a83c5..849b8676dc 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -75,12 +75,14 @@ Jeffrey Swan Jesper Haug Karsrud Jesse Gunsch Jimmy Ly +Joaquin Bartaburu Joey Parrish Johan Sundström John Bowers Jonas Birmé Jono Ward Jozef Chúťka +Juan Manuel Tomás Julian Domingo Jun Hong Chong Jürgen Kartnaller @@ -128,6 +130,7 @@ Sander Saares Sandra Lokshina Sanil Raut Satheesh Velmurugan +Sebastián Piquerez Semih Gokceoglu Seongryun Jo Seth Madison From f0a584317817d9d2fd02263b305472ae085d8247 Mon Sep 17 00:00:00 2001 From: Joaquin Bartaburu Date: Fri, 18 Oct 2024 16:09:52 -0300 Subject: [PATCH 09/10] fix: getLiveLatency() and add unit tests, enhance CMCD typedefs --- externs/shaka/player.js | 25 ++++++++++++++++++++++--- lib/player.js | 12 +++++++++--- lib/util/cmcd_manager.js | 4 ++-- test/player_unit.js | 38 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 71 insertions(+), 8 deletions(-) diff --git a/externs/shaka/player.js b/externs/shaka/player.js index 889a96428e..d8d05f1e40 100644 --- a/externs/shaka/player.js +++ b/externs/shaka/player.js @@ -2086,15 +2086,34 @@ shaka.extern.AdvancedAbrConfiguration; * @typedef {{ * version: number * }} + * + * @description + * Configuration options for CMCD Request Mode reporting. + * * @property {number} version + * The CMCD version to use for Request Mode reporting. + * Valid values are 1 or 2, corresponding to CMCD v1 + * and CMCD v2 specifications, respectively. + *
+ * Defaults to 2. */ shaka.extern.RequestMode; /** * @typedef {{ - * requestMode: shaka.extern.RequestMode - * }} - */ +* requestMode: shaka.extern.RequestMode +* }} +* +* @description +* Configuration for CMCD reporting modes. +* +* @property {shaka.extern.RequestMode} requestMode +* Configuration options for Request Mode reporting. +*
+* Request Mode attaches CMCD data to each media object request, matching the +* default delivery mode in CMCD v1. It is mandatory for CMCD clients to support +* this mode. +*/ shaka.extern.Reporting; /** diff --git a/lib/player.js b/lib/player.js index 4a46197c28..b78c7dfdde 100644 --- a/lib/player.js +++ b/lib/player.js @@ -5655,11 +5655,16 @@ shaka.Player = class extends shaka.util.FakeEventTarget { * Get latency in milliseconds between the live edge and what's currently * playing. * - * @return {number} + * @return {?number} The latency in milliseconds, or null if nothing is playing. */ getLiveLatency() { const element = this.video_; - const now = this.getPresentationStartTimeAsDate().valueOf() + + + if (!element) { + return null; + } + + const now = this.getPresentationStartTimeAsDate().getTime() + element.currentTime * 1000; return Date.now() - now; } @@ -5733,7 +5738,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget { } if (this.isLive()) { - this.stats_.setLiveLatency(this.getLiveLatency() / 1000); + const latency = this.getLiveLatency() || 0; + this.stats_.setLiveLatency(latency / 1000); } if (this.manifest_) { diff --git a/lib/util/cmcd_manager.js b/lib/util/cmcd_manager.js index f76104ab61..843e9d3d2c 100644 --- a/lib/util/cmcd_manager.js +++ b/lib/util/cmcd_manager.js @@ -212,7 +212,7 @@ shaka.util.CmcdManager = class { */ onPlaybackPlay() { if (!this.playbackPlayTime_) { - this.playbackPlayTime_ = performance.now(); + this.playbackPlayTime_ = Date.now(); } } @@ -221,7 +221,7 @@ shaka.util.CmcdManager = class { */ onPlaybackPlaying() { if (!this.playbackPlayingTime_) { - this.playbackPlayingTime_ = performance.now(); + this.playbackPlayingTime_ = Date.now(); } } diff --git a/test/player_unit.js b/test/player_unit.js index b405c875e5..f1d8a7b6ed 100644 --- a/test/player_unit.js +++ b/test/player_unit.js @@ -2876,6 +2876,44 @@ describe('Player', () => { } }); // describe('languages') + describe('getLiveLatency()', () => { + let timeline; + + beforeEach(async () => { + // Create a presentation timeline for a live stream. + timeline = new shaka.media.PresentationTimeline(300, 0); + timeline.setStatic(false); // Indicate that this is a live stream. + + // Set an initial program date time to simulate a live stream with a known start time. + timeline.setInitialProgramDateTime(1000); + + // Generate a manifest that uses this timeline. + manifest = shaka.test.ManifestGenerator.generate((manifest) => { + manifest.presentationTimeline = timeline; + manifest.addVariant(0, (variant) => { + variant.addVideo(1); + }); + }); + + // Load the player with the live manifest. + await player.load(fakeManifestUri, null, fakeMimeType); + + video.currentTime = 10; // Simulate that we're 10 seconds into playback. + }); + + it('returns null if video element does not exist', () => { + player.video_ = null; + const latency = player.getLiveLatency(); + expect(latency).toBeNull(); + }); + + it('returns correct latency when video is playing', () => { + Date.now = () => 2000 * 1000; + const latency = player.getLiveLatency(); + expect(latency).toBe(990000); + }); + }); + describe('getStats', () => { const oldDateNow = Date.now; From 0730e20440e9e99f29d50291b179c353d5a8408f Mon Sep 17 00:00:00 2001 From: Joaquin Bartaburu Date: Mon, 21 Oct 2024 11:39:13 -0300 Subject: [PATCH 10/10] fix: linter errors --- externs/shaka/player.js | 4 ++-- lib/player.js | 5 +++-- test/player_unit.js | 7 ++++--- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/externs/shaka/player.js b/externs/shaka/player.js index d8d05f1e40..b725f3a115 100644 --- a/externs/shaka/player.js +++ b/externs/shaka/player.js @@ -2111,8 +2111,8 @@ shaka.extern.RequestMode; * Configuration options for Request Mode reporting. *
* Request Mode attaches CMCD data to each media object request, matching the -* default delivery mode in CMCD v1. It is mandatory for CMCD clients to support -* this mode. +* default delivery mode in CMCD v1. It is mandatory for CMCD clients to +* support this mode. */ shaka.extern.Reporting; diff --git a/lib/player.js b/lib/player.js index f8cce36ba3..57faab1583 100644 --- a/lib/player.js +++ b/lib/player.js @@ -5662,11 +5662,12 @@ shaka.Player = class extends shaka.util.FakeEventTarget { * Get latency in milliseconds between the live edge and what's currently * playing. * - * @return {?number} The latency in milliseconds, or null if nothing is playing. + * @return {?number} The latency in milliseconds, or null if nothing + * is playing. */ getLiveLatency() { const element = this.video_; - + if (!element) { return null; } diff --git a/test/player_unit.js b/test/player_unit.js index f1d8a7b6ed..b265c42981 100644 --- a/test/player_unit.js +++ b/test/player_unit.js @@ -2884,7 +2884,8 @@ describe('Player', () => { timeline = new shaka.media.PresentationTimeline(300, 0); timeline.setStatic(false); // Indicate that this is a live stream. - // Set an initial program date time to simulate a live stream with a known start time. + // Set an initial program date time to simulate a live stream with a + // known start time. timeline.setInitialProgramDateTime(1000); // Generate a manifest that uses this timeline. @@ -2901,8 +2902,8 @@ describe('Player', () => { video.currentTime = 10; // Simulate that we're 10 seconds into playback. }); - it('returns null if video element does not exist', () => { - player.video_ = null; + it('returns null if video element does not exist', async () => { + await player.detach(); const latency = player.getLiveLatency(); expect(latency).toBeNull(); });