From fa5932ca8f604952590734bf8bdc27ad8e69e8d8 Mon Sep 17 00:00:00 2001 From: Casey Occhialini <1508707+littlespex@users.noreply.github.com> Date: Mon, 10 Jan 2022 18:42:16 -0500 Subject: [PATCH] fix: Fix CMCD property mangling (#3842) Move CMCD type definition to externs to avoid minifying property names Co-authored-by: Dan Sparacio Fixes #3839 --- externs/cmcd.js | 176 +++++++++++++++++++++++++++++++ lib/util/cmcd_manager.js | 186 ++------------------------------- test/util/cmcd_manager_unit.js | 8 ++ 3 files changed, 190 insertions(+), 180 deletions(-) create mode 100644 externs/cmcd.js diff --git a/externs/cmcd.js b/externs/cmcd.js new file mode 100644 index 0000000000..540b268d08 --- /dev/null +++ b/externs/cmcd.js @@ -0,0 +1,176 @@ +/** + * @externs + */ + +/** + * @typedef {{ + * br: (number|undefined), + * d: (number|undefined), + * ot: (string|undefined), + * tb: (number|undefined), + * bl: (number|undefined), + * dl: (number|undefined), + * mtp: (number|undefined), + * nor: (string|undefined), + * nrr: (string|undefined), + * su: (boolean|undefined), + * cid: (string|undefined), + * pr: (number|undefined), + * sf: (string|undefined), + * sid: (string|undefined), + * st: (string|undefined), + * v: (number|undefined), + * bs: (boolean|undefined), + * rtp: (number|undefined) + * }} + * + * @description + * Client Media Common Data (CMCD) data. + * + * @property {number} br + * The encoded bitrate of the audio or video object being requested. This may + * not be known precisely by the player; however, it MAY be estimated based + * upon playlist/manifest declarations. If the playlist declares both peak and + * average bitrate values, the peak value should be transmitted. + * + * @property {number} d + * The playback duration in milliseconds of the object being requested. If a + * partial segment is being requested, then this value MUST indicate the + * playback duration of that part and not that of its parent segment. This + * value can be an approximation of the estimated duration if the explicit + * value is not known. + * + * @property {string} ot + * The media type of the current object being requested: + * - `m` = text file, such as a manifest or playlist + * - `a` = audio only + * - `v` = video only + * - `av` = muxed audio and video + * - `i` = init segment + * - `c` = caption or subtitle + * - `tt` = ISOBMFF timed text track + * - `k` = cryptographic key, license or certificate. + * - `o` = other + * + * If the object type being requested is unknown, then this key MUST NOT be + * used. + * + * @property {number} tb + * The highest bitrate rendition in the manifest or playlist that the client + * is allowed to play, given current codec, licensing and sizing constraints. + * + * @property {number} bl + * The buffer length associated with the media object being requested. This + * value MUST be rounded to the nearest 100 ms. This key SHOULD only be sent + * with an object type of ‘a’, ‘v’ or ‘av’. + * + * @property {number} dl + * Deadline from the request time until the first sample of this + * Segment/Object needs to be available in order to not create a buffer + * underrun or any other playback problems. This value MUST be rounded to the + * nearest 100ms. For a playback rate of 1, this may be equivalent to the + * player’s remaining buffer length. + * + * @property {number} mtp + * The throughput between client and server, as measured by the client and + * MUST be rounded to the nearest 100 kbps. This value, however derived, + * SHOULD be the value that the client is using to make its next Adaptive + * Bitrate switching decision. If the client is connected to multiple + * servers concurrently, it must take care to report only the throughput + * measured against the receiving server. If the client has multiple + * concurrent connections to the server, then the intent is that this value + * communicates the aggregate throughput the client sees across all those + * connections. + * + * @property {string} nor + * Relative path of the next object to be requested. This can be used to + * trigger pre-fetching by the CDN. This MUST be a path relative to the + * current request. This string MUST be URLEncoded. The client SHOULD NOT + * depend upon any pre-fetch action being taken - it is merely a request for + * such a pre-fetch to take place. + * + * @property {string} nrr + * If the next request will be a partial object request, then this string + * denotes the byte range to be requested. If the ‘nor’ field is not set, then + * the object is assumed to match the object currently being requested. The + * client SHOULD NOT depend upon any pre-fetch action being taken – it is + * merely a request for such a pre-fetch to take place. Formatting is similar + * to the HTTP Range header, except that the unit MUST be ‘byte’, the ‘Range:’ + * prefix is NOT required and specifying multiple ranges is NOT allowed. Valid + * combinations are: + * + * - `"\-"` + * - `"\-\"` + * - `"-\"` + * + * @property {boolean} su + * Key is included without a value if the object is needed urgently due to + * startup, seeking or recovery after a buffer-empty event. The media SHOULD + * not be rendering when this request is made. This key MUST not be sent if it + * is FALSE. + * + * @property {string} cid + * A unique string identifying the current content. Maximum length is 64 + * characters. This value is consistent across multiple different sessions and + * devices and is defined and updated at the discretion of the service + * provider. + * + * @property {number} pr + * The playback rate. `1` if real-time, `2` if double speed, `0` if not + * playing. SHOULD only be sent if not equal to `1`. + * + * @property {string} sf + * The streaming format that defines the current request. + * + * - `d` = MPEG DASH + * - `h` = HTTP Live Streaming (HLS) + * - `s` = Smooth Streaming + * - `o` = other + * + * If the streaming format being requested is unknown, then this key MUST NOT + * be used. + * + * @property {string} sid + * A GUID identifying the current playback session. A playback session + * typically ties together segments belonging to a single media asset. Maximum + * length is 64 characters. It is RECOMMENDED to conform to the UUID + * specification. + * + * @property {string} st + * Stream type + * - `v` = all segments are available – e.g., VOD + * - `l` = segments become available over time – e.g., LIVE + * + * @property {number} v + * The version of this specification used for interpreting the defined key + * names and values. If this key is omitted, the client and server MUST + * interpret the values as being defined by version 1. Client SHOULD omit this + * field if the version is 1. + * + * @property {boolean} bs + * Buffer starvation key is included without a value if the buffer was starved + * at some point between the prior request and this object request, resulting + * in the player being in a rebuffering state and the video or audio playback + * being stalled. This key MUST NOT be sent if the buffer was not starved + * since the prior request. + * + * If the object type `ot` key is sent along with this key, then the `bs` key + * refers to the buffer associated with the particular object type. If no + * object type is communicated, then the buffer state applies to the current + * session. + * + * @property {number} rtp + * Requested maximum throughput + * + * The requested maximum throughput that the client considers sufficient for + * delivery of the asset. Values MUST be rounded to the nearest 100kbps. For + * example, a client would indicate that the current segment, encoded at + * 2Mbps, is to be delivered at no more than 10Mbps, by using rtp=10000. + * + * Note: This can benefit clients by preventing buffer saturation through + * over-delivery and can also deliver a community benefit through fair-share + * 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. + */ +var CmcdData; diff --git a/lib/util/cmcd_manager.js b/lib/util/cmcd_manager.js index 6ea7287b96..894e15650e 100644 --- a/lib/util/cmcd_manager.js +++ b/lib/util/cmcd_manager.js @@ -217,7 +217,7 @@ shaka.util.CmcdManager = class { /** * Create baseline CMCD data * - * @return {shaka.util.CmcdManager.Data} + * @return {CmcdData} * @private */ createData_() { @@ -237,7 +237,7 @@ shaka.util.CmcdManager = class { * Apply CMCD data to a request. * * @param {!shaka.extern.Request} request The request to apply CMCD data to - * @param {!shaka.util.CmcdManager.Data} data The data object + * @param {!CmcdData} data The data object * @param {boolean} useHeaders Send data via request headers * @private */ @@ -411,7 +411,7 @@ shaka.util.CmcdManager = class { * section 3.2 of * [CTA-5004](https://cdn.cta.tech/cta/media/media/resources/standards/pdfs/cta-5004-final.pdf). * - * @param {shaka.util.CmcdManager.Data} data The CMCD data object + * @param {CmcdData} data The CMCD data object * @return {string} */ static serialize(data) { @@ -463,7 +463,7 @@ shaka.util.CmcdManager = class { let result; if (type === 'string' && key !== 'ot' && key !== 'sf' && key !== 'st') { - result = `${key}="${value.replace(/"/g, '"')}"`; + result = `${key}=${JSON.stringify(value)}`; } else if (type === 'boolean') { result = key; } else if (type === 'symbol') { @@ -483,7 +483,7 @@ shaka.util.CmcdManager = class { * defined in the section 2.1 and 3.2 of * [CTA-5004](https://cdn.cta.tech/cta/media/media/resources/standards/pdfs/cta-5004-final.pdf). * - * @param {shaka.util.CmcdManager.Data} data The CMCD data object + * @param {CmcdData} data The CMCD data object * @return {!Object} */ static toHeaders(data) { @@ -519,7 +519,7 @@ shaka.util.CmcdManager = class { * defined in the section 2.2 and 3.2 of * [CTA-5004](https://cdn.cta.tech/cta/media/media/resources/standards/pdfs/cta-5004-final.pdf). * - * @param {shaka.util.CmcdManager.Data} data The CMCD data object + * @param {CmcdData} data The CMCD data object * @return {string} */ static toQuery(data) { @@ -615,180 +615,6 @@ shaka.util.CmcdManager.SegmentInfo; shaka.util.CmcdManager.ManifestInfo; -/** - * @typedef {{ - * br: (number|undefined), - * d: (number|undefined), - * ot: (shaka.util.CmcdManager.ObjectType|undefined), - * tb: (number|undefined), - * bl: (number|undefined), - * dl: (number|undefined), - * mtp: (number|undefined), - * nor: (string|undefined), - * nrr: (string|undefined), - * su: (boolean|undefined), - * cid: (string|undefined), - * pr: (number|undefined), - * sf: (shaka.util.CmcdManager.StreamingFormat|undefined), - * sid: (string|undefined), - * st: (shaka.util.CmcdManager.StreamType|undefined), - * v: (number|undefined), - * bs: (boolean|undefined), - * rtp: (number|undefined) - * }} - * - * @description - * Client Media Common Data (CMCD) data. - * - * @property {number} br - * The encoded bitrate of the audio or video object being requested. This may - * not be known precisely by the player; however, it MAY be estimated based - * upon playlist/manifest declarations. If the playlist declares both peak and - * average bitrate values, the peak value should be transmitted. - * - * @property {number} d - * The playback duration in milliseconds of the object being requested. If a - * partial segment is being requested, then this value MUST indicate the - * playback duration of that part and not that of its parent segment. This - * value can be an approximation of the estimated duration if the explicit - * value is not known. - * - * @property {shaka.util.CmcdManager.ObjectType} ot - * The media type of the current object being requested: - * - `m` = text file, such as a manifest or playlist - * - `a` = audio only - * - `v` = video only - * - `av` = muxed audio and video - * - `i` = init segment - * - `c` = caption or subtitle - * - `tt` = ISOBMFF timed text track - * - `k` = cryptographic key, license or certificate. - * - `o` = other - * - * If the object type being requested is unknown, then this key MUST NOT be - * used. - * - * @property {number} tb - * The highest bitrate rendition in the manifest or playlist that the client - * is allowed to play, given current codec, licensing and sizing constraints. - * - * @property {number} bl - * The buffer length associated with the media object being requested. This - * value MUST be rounded to the nearest 100 ms. This key SHOULD only be sent - * with an object type of ‘a’, ‘v’ or ‘av’. - * - * @property {number} dl - * Deadline from the request time until the first sample of this - * Segment/Object needs to be available in order to not create a buffer - * underrun or any other playback problems. This value MUST be rounded to the - * nearest 100ms. For a playback rate of 1, this may be equivalent to the - * player’s remaining buffer length. - * - * @property {number} mtp - * The throughput between client and server, as measured by the client and - * MUST be rounded to the nearest 100 kbps. This value, however derived, - * SHOULD be the value that the client is using to make its next Adaptive - * Bitrate switching decision. If the client is connected to multiple - * servers concurrently, it must take care to report only the throughput - * measured against the receiving server. If the client has multiple - * concurrent connections to the server, then the intent is that this value - * communicates the aggregate throughput the client sees across all those - * connections. - * - * @property {string} nor - * Relative path of the next object to be requested. This can be used to - * trigger pre-fetching by the CDN. This MUST be a path relative to the - * current request. This string MUST be URLEncoded. The client SHOULD NOT - * depend upon any pre-fetch action being taken - it is merely a request for - * such a pre-fetch to take place. - * - * @property {string} nrr - * If the next request will be a partial object request, then this string - * denotes the byte range to be requested. If the ‘nor’ field is not set, then - * the object is assumed to match the object currently being requested. The - * client SHOULD NOT depend upon any pre-fetch action being taken – it is - * merely a request for such a pre-fetch to take place. Formatting is similar - * to the HTTP Range header, except that the unit MUST be ‘byte’, the ‘Range:’ - * prefix is NOT required and specifying multiple ranges is NOT allowed. Valid - * combinations are: - * - * - `"\-"` - * - `"\-\"` - * - `"-\"` - * - * @property {boolean} su - * Key is included without a value if the object is needed urgently due to - * startup, seeking or recovery after a buffer-empty event. The media SHOULD - * not be rendering when this request is made. This key MUST not be sent if it - * is FALSE. - * - * @property {string} cid - * A unique string identifying the current content. Maximum length is 64 - * characters. This value is consistent across multiple different sessions and - * devices and is defined and updated at the discretion of the service - * provider. - * - * @property {number} pr - * The playback rate. `1` if real-time, `2` if double speed, `0` if not - * playing. SHOULD only be sent if not equal to `1`. - * - * @property {shaka.util.CmcdManager.StreamingFormat} sf - * The streaming format that defines the current request. - * - * - `d` = MPEG DASH - * - `h` = HTTP Live Streaming (HLS) - * - `s` = Smooth Streaming - * - `o` = other - * - * If the streaming format being requested is unknown, then this key MUST NOT - * be used. - * - * @property {string} sid - * A GUID identifying the current playback session. A playback session - * typically ties together segments belonging to a single media asset. Maximum - * length is 64 characters. It is RECOMMENDED to conform to the UUID - * specification. - * - * @property {shaka.util.CmcdManager.StreamType} st - * Stream type - * - `v` = all segments are available – e.g., VOD - * - `l` = segments become available over time – e.g., LIVE - * - * @property {number} v - * The version of this specification used for interpreting the defined key - * names and values. If this key is omitted, the client and server MUST - * interpret the values as being defined by version 1. Client SHOULD omit this - * field if the version is 1. - * - * @property {boolean} bs - * Buffer starvation key is included without a value if the buffer was starved - * at some point between the prior request and this object request, resulting - * in the player being in a rebuffering state and the video or audio playback - * being stalled. This key MUST NOT be sent if the buffer was not starved - * since the prior request. - * - * If the object type `ot` key is sent along with this key, then the `bs` key - * refers to the buffer associated with the particular object type. If no - * object type is communicated, then the buffer state applies to the current - * session. - * - * @property {number} rtp - * Requested maximum throughput - * - * The requested maximum throughput that the client considers sufficient for - * delivery of the asset. Values MUST be rounded to the nearest 100kbps. For - * example, a client would indicate that the current segment, encoded at - * 2Mbps, is to be delivered at no more than 10Mbps, by using rtp=10000. - * - * Note: This can benefit clients by preventing buffer saturation through - * over-delivery and can also deliver a community benefit through fair-share - * 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. - */ -shaka.util.CmcdManager.Data; - - /** * @enum {string} */ diff --git a/test/util/cmcd_manager_unit.js b/test/util/cmcd_manager_unit.js index a1522656d5..3dd20fe581 100644 --- a/test/util/cmcd_manager_unit.js +++ b/test/util/cmcd_manager_unit.js @@ -42,6 +42,14 @@ describe('CmcdManager', () => { 'sid%3D%22c936730c-031e-4a73-976f-92bc34039c60%22'; expect(query).toBe(result); }); + + it('escapes reserve character in string values', () => { + const query = CmcdManager.toQuery({ + 'com.test-escape': 'Double "Quotes"', + }); + const result = 'CMCD=com.test-escape%3D%22Double%20%5C%22Quotes%5C%22%22'; + expect(query).toBe(result); + }); }); describe('Header serialization', () => {