Skip to content

Commit

Permalink
feat: CMCD v2 LTC and MSD keys (#7412)
Browse files Browse the repository at this point in the history
  • Loading branch information
JoaquinBCh authored Nov 29, 2024
1 parent eb4a45e commit b2673fd
Show file tree
Hide file tree
Showing 11 changed files with 317 additions and 17 deletions.
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ Peter Nycander <[email protected]>
Philo Inc. <*@philo.com>
PikachuEXE <[email protected]>
Prakash <[email protected]>
Qualabs <*@qualabs.com>
Robert Colantuoni <[email protected]>
Robert Galluccio <[email protected]>
Rodolphe Breton <[email protected]>
Expand Down
3 changes: 3 additions & 0 deletions CONTRIBUTORS
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,14 @@ Jeffrey Swan <[email protected]>
Jesper Haug Karsrud <[email protected]>
Jesse Gunsch <[email protected]>
Jimmy Ly <[email protected]>
Joaquin Bartaburu <[email protected]>
Joey Parrish <[email protected]>
Johan Sundström <[email protected]>
John Bowers <[email protected]>
Jonas Birmé <[email protected]>
Jono Ward <[email protected]>
Jozef Chúťka <[email protected]>
Juan Manuel Tomás <[email protected]>
Julian Domingo <[email protected]>
Jun Hong Chong <[email protected]>
Jürgen Kartnaller <[email protected]>
Expand Down Expand Up @@ -130,6 +132,7 @@ Sander Saares <[email protected]>
Sandra Lokshina <[email protected]>
Sanil Raut <[email protected]>
Satheesh Velmurugan <[email protected]>
Sebastián Piquerez <[email protected]>
Semih Gokceoglu <[email protected]>
Seongryun Jo <[email protected]>
Seth Madison <[email protected]>
Expand Down
1 change: 1 addition & 0 deletions demo/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,7 @@ shakaDemo.Config = class {
.addBoolInput_('Enabled', 'cmcd.enabled')
.addTextInput_('Session ID', 'cmcd.sessionId')
.addTextInput_('Content ID', 'cmcd.contentId')
.addTextInput_('Version', 'cmcd.version')
.addNumberInput_('RTP safety Factor', 'cmcd.rtpSafetyFactor',
/* canBeDecimal= */ true)
.addBoolInput_('Use Headers', 'cmcd.useHeaders');
Expand Down
17 changes: 16 additions & 1 deletion externs/cmcd.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@
* st: (string|undefined),
* v: (number|undefined),
* bs: (boolean|undefined),
* rtp: (number|undefined)
* rtp: (number|undefined),
* msd: (number|undefined),
* ltc: (number|undefined),
* }}
*
* @description
Expand Down Expand Up @@ -182,5 +184,18 @@
* 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.
*
* @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;
9 changes: 8 additions & 1 deletion externs/shaka/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -2101,7 +2101,8 @@ shaka.extern.AdvancedAbrConfiguration;
* sessionId: string,
* contentId: string,
* rtpSafetyFactor: number,
* includeKeys: !Array<string>
* includeKeys: !Array<string>,
* version: number
* }}
*
* @description
Expand Down Expand Up @@ -2140,6 +2141,12 @@ shaka.extern.AdvancedAbrConfiguration;
* will be included.
* <br>
* Defaults to <code>[]</code>.
* @property {number} version
* The CMCD version.
* Valid values are <code>1</code> or <code>2</code>, corresponding to CMCD v1
* and CMCD v2 specifications, respectively.
* <br>
* Defaults to <code>1</code>.
* @exportDoc
*/
shaka.extern.CmcdConfiguration;
Expand Down
27 changes: 23 additions & 4 deletions lib/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -1218,6 +1218,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
const onError = (error) => this.onVideoError_(error);
this.attachEventManager_.listen(mediaElement, 'error', onError);
this.video_ = mediaElement;
if (this.cmcdManager_) {
this.cmcdManager_.setMediaElement(mediaElement);
}
}

// Only initialize media source if the platform supports it.
Expand Down Expand Up @@ -2640,6 +2643,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
if (this.cmcdManager_) {
this.cmcdManager_.setLowLatency(
this.manifest_.isLowLatency && this.config_.streaming.lowLatencyMode);
this.cmcdManager_.setStartTimeOfLoad(startTimeOfLoad * 1000);
}

shaka.Player.applyPlayRange_(this.manifest_.presentationTimeline,
Expand Down Expand Up @@ -3752,6 +3756,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);
Expand Down Expand Up @@ -5772,6 +5777,22 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
return info;
}

/**
* 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.
*/
getLiveLatency() {
if (!this.video_) {
return null;
}
const now = this.getPresentationStartTimeAsDate().getTime() +
this.video_.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.
Expand Down Expand Up @@ -5841,10 +5862,8 @@ 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);
const latency = this.getLiveLatency() || 0;
this.stats_.setLiveLatency(latency / 1000);
}

if (this.manifest_) {
Expand Down
152 changes: 141 additions & 11 deletions lib/util/cmcd_manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ goog.require('goog.Uri');
goog.require('shaka.log');
goog.require('shaka.net.NetworkingEngine');
goog.require('shaka.util.ArrayUtils');
goog.require('shaka.util.EventManager');

goog.requireType('shaka.media.SegmentReference');

Expand Down Expand Up @@ -56,6 +57,44 @@ shaka.util.CmcdManager = class {
* @private {boolean}
*/
this.lowLatency_ = false;

/**
* @private {number|undefined}
*/
this.playbackPlayTime_ = undefined;

/**
* @private {number|undefined}
*/
this.playbackPlayingTime_ = undefined;

/**
* @private {number}
*/
this.startTimeOfLoad_ = 0;

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

/**
* @private {shaka.util.EventManager}
*/
this.eventManager_ = new shaka.util.EventManager();

/** @private {HTMLMediaElement} */
this.video_ = null;
}


/**
* Set media element and setup event listeners
* @param {HTMLMediaElement} mediaElement The video element
*/
setMediaElement(mediaElement) {
this.video_ = mediaElement;
this.setupMSDEventListeners_();
}

/**
Expand All @@ -77,6 +116,12 @@ shaka.util.CmcdManager = class {
this.buffering_ = true;
this.starved_ = false;
this.lowLatency_ = false;
this.playbackPlayTime_ = 0;
this.playbackPlayingTime_ = 0;
this.startTimeOfLoad_ = 0;
this.msdSent_ = false;
this.video_ = null;
this.eventManager_.removeAll();
}

/**
Expand Down Expand Up @@ -120,6 +165,24 @@ shaka.util.CmcdManager = class {
}
}

/**
* Set start time of load if autoplay is enabled
*
* @param {number} startTimeOfLoad
*/
setStartTimeOfLoad(startTimeOfLoad) {
if (this.video_) {
const playResult = this.video_.play();
if (playResult) {
playResult.then(() => {
this.startTimeOfLoad_ = startTimeOfLoad;
}).catch((e) => {
this.startTimeOfLoad_ = 0;
});
}
}
}

/**
* Apply CMCD data to a request.
*
Expand Down Expand Up @@ -360,6 +423,40 @@ shaka.util.CmcdManager = class {
}
}

/**
* Set playbackPlayTime_ when the play event is triggered
* @private
*/
onPlaybackPlay_() {
if (!this.playbackPlayTime_) {
this.playbackPlayTime_ = Date.now();
}
}

/**
* Set playbackPlayingTime_
* @private
*/
onPlaybackPlaying_() {
if (!this.playbackPlayingTime_) {
this.playbackPlayingTime_ = Date.now();
}
}

/**
* Setup event listeners for msd calculation
* @private
*/
setupMSDEventListeners_() {
const onPlaybackPlay = () => this.onPlaybackPlay_();
this.eventManager_.listenOnce(
this.video_, 'play', onPlaybackPlay);

const onPlaybackPlaying = () => this.onPlaybackPlaying_();
this.eventManager_.listenOnce(
this.video_, 'playing', onPlaybackPlaying);
}

/**
* Create baseline CMCD data
*
Expand All @@ -371,7 +468,7 @@ shaka.util.CmcdManager = class {
this.config_.sessionId = window.crypto.randomUUID();
}
return {
v: shaka.util.CmcdManager.Version,
v: this.config_.version,
sf: this.sf_,
sid: this.config_.sessionId,
cid: this.config_.contentId,
Expand Down Expand Up @@ -410,6 +507,18 @@ shaka.util.CmcdManager = class {
data.su = this.buffering_;
}

if (data.v === shaka.util.CmcdManager.Version.VERSION_2) {
if (this.playerInterface_.isLive()) {
data.ltc = this.playerInterface_.getLiveLatency();
}

const msd = this.calculateMSD_();
if (msd != undefined) {
data.msd = msd;
this.msdSent_ = true;
}
}

const output = this.filterKeys_(data);

if (useHeaders) {
Expand Down Expand Up @@ -614,6 +723,23 @@ shaka.util.CmcdManager = class {
return toPath.join('/');
}

/**
* Calculate messured start delay
*
* @return {number|undefined}
* @private
*/
calculateMSD_() {
if (!this.msdSent_ &&
this.playbackPlayingTime_ &&
this.playbackPlayTime_) {
const startTime = this.startTimeOfLoad_ || this.playbackPlayTime_;
return this.playbackPlayingTime_ - startTime;
}
return undefined;
}


/**
* Calculate requested maximun throughput
*
Expand Down Expand Up @@ -810,8 +936,8 @@ 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,
cid: 2, pr: 2, sf: 2, sid: 2, st: 2, v: 2,
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,
};

Expand Down Expand Up @@ -873,7 +999,8 @@ shaka.util.CmcdManager = class {
* getCurrentTime: function():number,
* getPlaybackRate: function():number,
* getVariantTracks: function():Array.<shaka.extern.Track>,
* isLive: function():boolean
* isLive: function():boolean,
* getLiveLatency: function():number
* }}
*
* @property {function():number} getBandwidthEstimate
Expand All @@ -888,6 +1015,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;

Expand All @@ -907,6 +1037,13 @@ shaka.util.CmcdManager.ObjectType = {
OTHER: 'o',
};

/**
* @enum {number}
*/
shaka.util.CmcdManager.Version = {
VERSION_1: 1,
VERSION_2: 2,
};

/**
* @enum {string}
Expand All @@ -929,10 +1066,3 @@ shaka.util.CmcdManager.StreamingFormat = {
SMOOTH: 's',
OTHER: 'o',
};


/**
* The CMCD spec version
* @const {number}
*/
shaka.util.CmcdManager.Version = 1;
Loading

0 comments on commit b2673fd

Please sign in to comment.