@amazon/vinyl 3.1.0 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +207 -164
- package/dist/index.cjs.map +4 -4
- package/dist/index.js +195 -151
- package/dist/index.js.map +4 -4
- package/dist/test/integ.es5.js +201 -199
- package/dist/test/integ.js +417 -222
- package/dist/test/integ.js.map +4 -4
- package/dist/types/current/drm/DrmControllerImpl.d.ts +17 -1
- package/dist/types/current/drm/DrmControllerImpl.d.ts.map +1 -1
- package/dist/types/current/drm/DrmControllerImpl.js +45 -10
- package/dist/types/current/streaming/LoadMetric.d.ts +15 -1
- package/dist/types/current/streaming/LoadMetric.d.ts.map +1 -1
- package/dist/types/current/streaming/mediaTimelineFilter.d.ts +4 -2
- package/dist/types/current/streaming/mediaTimelineFilter.d.ts.map +1 -1
- package/dist/types/current/streaming/mediaTimelineFilter.js +23 -8
- package/dist/types/current/track/filters/sampleRateFilter.d.ts +8 -4
- package/dist/types/current/track/filters/sampleRateFilter.d.ts.map +1 -1
- package/dist/types/current/track/filters/sampleRateFilter.js +31 -21
- package/dist/types/current/track/mse/MseTrack.d.ts +0 -9
- package/dist/types/current/track/mse/MseTrack.d.ts.map +1 -1
- package/dist/types/current/track/mse/MseTrack.js +2 -29
- package/dist/types/current/tsconfig.tsbuildinfo +1 -1
- package/dist/types/current/vinyl/VinylPlayer.d.ts +5 -0
- package/dist/types/current/vinyl/VinylPlayer.d.ts.map +1 -1
- package/dist/types/current/vinyl/VinylPlayer.js +23 -1
- package/dist/types/ts3.7/drm/DrmControllerImpl.d.ts +17 -1
- package/dist/types/ts3.7/streaming/LoadMetric.d.ts +15 -1
- package/dist/types/ts3.7/streaming/mediaTimelineFilter.d.ts +4 -2
- package/dist/types/ts3.7/track/filters/sampleRateFilter.d.ts +8 -4
- package/dist/types/ts3.7/track/mse/MseTrack.d.ts +0 -9
- package/dist/types/ts3.7/vinyl/VinylPlayer.d.ts +5 -0
- package/dist/types/ts4.1/drm/DrmControllerImpl.d.ts +17 -1
- package/dist/types/ts4.1/streaming/LoadMetric.d.ts +15 -1
- package/dist/types/ts4.1/streaming/mediaTimelineFilter.d.ts +4 -2
- package/dist/types/ts4.1/track/filters/sampleRateFilter.d.ts +8 -4
- package/dist/types/ts4.1/track/mse/MseTrack.d.ts +0 -9
- package/dist/types/ts4.1/vinyl/VinylPlayer.d.ts +5 -0
- package/dist/types/ts4.7/drm/DrmControllerImpl.d.ts +17 -1
- package/dist/types/ts4.7/streaming/LoadMetric.d.ts +15 -1
- package/dist/types/ts4.7/streaming/mediaTimelineFilter.d.ts +4 -2
- package/dist/types/ts4.7/track/filters/sampleRateFilter.d.ts +8 -4
- package/dist/types/ts4.7/track/mse/MseTrack.d.ts +0 -9
- package/dist/types/ts4.7/vinyl/VinylPlayer.d.ts +5 -0
- package/dist/unit/types/current/drm/DrmControllerImpl.test.js +53 -9
- package/dist/unit/types/current/streaming/MseTrack.test.js +11 -8
- package/dist/unit/types/current/streaming/mediaTimelineFilter.test.js +60 -54
- package/dist/unit/types/current/track/filters/sampleRateFilter.test.js +75 -0
- package/dist/unit/types/current/tsconfig.tsbuildinfo +1 -1
- package/dist/unit/types/current/vinyl/VinylPlayer.test.js +33 -2
- package/dist/vinylTestUtil/types/current/tsconfig.tsbuildinfo +1 -1
- package/package.json +10 -10
package/dist/test/integ.js
CHANGED
|
@@ -5592,9 +5592,6 @@
|
|
|
5592
5592
|
function map(arrayLike, transform, thisArg) {
|
|
5593
5593
|
return Array.prototype.map.call(arrayLike, transform, thisArg);
|
|
5594
5594
|
}
|
|
5595
|
-
function every(arrayLike, predicate, thisArg) {
|
|
5596
|
-
return Array.prototype.every.call(arrayLike, predicate, thisArg);
|
|
5597
|
-
}
|
|
5598
5595
|
function first(arrayLike) {
|
|
5599
5596
|
if (!arrayLike) return void 0;
|
|
5600
5597
|
return arrayLike[0];
|
|
@@ -14017,6 +14014,10 @@ ${this.lastInitStack}` : "";
|
|
|
14017
14014
|
__publicField(this, "bufferingTrackUri", null);
|
|
14018
14015
|
__publicField(this, "sessionAbort", null);
|
|
14019
14016
|
__publicField(this, "pendingMessageProps", null);
|
|
14017
|
+
// Armed per track load; latches on the first session so only the
|
|
14018
|
+
// critical-path session's EME/CDM setup is measured.
|
|
14019
|
+
__publicField(this, "keySetupSpanArmed", true);
|
|
14020
|
+
__publicField(this, "keySetupSpanStart", null);
|
|
14020
14021
|
__publicField(this, "disposer", createDisposer());
|
|
14021
14022
|
__publicField(this, "checkIsSupportedCached", memoize(
|
|
14022
14023
|
async (params) => {
|
|
@@ -14182,6 +14183,7 @@ ${this.lastInitStack}` : "";
|
|
|
14182
14183
|
if (!hasMimeType(drmInfo)) {
|
|
14183
14184
|
throw new DrmError("Encrypted content must have a mimeType.");
|
|
14184
14185
|
}
|
|
14186
|
+
this.markKeySetupStart();
|
|
14185
14187
|
const mediaKeys = await this.attachMediaKeys(drmInfo);
|
|
14186
14188
|
this.abortIfDisposed();
|
|
14187
14189
|
const keySystemOptions = (_a3 = this.options.keySystems[mediaKeys.keySystem]) != null ? _a3 : this.options.keySystems["*"];
|
|
@@ -14226,6 +14228,28 @@ ${this.lastInitStack}` : "";
|
|
|
14226
14228
|
});
|
|
14227
14229
|
return newSession;
|
|
14228
14230
|
}
|
|
14231
|
+
/**
|
|
14232
|
+
* Captures the key-setup span start at the top of the session-creation path
|
|
14233
|
+
* (before media keys are attached), latching so only the first session of
|
|
14234
|
+
* the current track load is measured.
|
|
14235
|
+
*/
|
|
14236
|
+
markKeySetupStart() {
|
|
14237
|
+
if (!this.keySetupSpanArmed) return;
|
|
14238
|
+
this.keySetupSpanArmed = false;
|
|
14239
|
+
this.keySetupSpanStart = Date.now();
|
|
14240
|
+
}
|
|
14241
|
+
dispatchLoadSpan(kind, startTime, endTime, messageProps) {
|
|
14242
|
+
this.dispatch("loadSpanMeasured", __spreadValues(__spreadValues({
|
|
14243
|
+
kind,
|
|
14244
|
+
startTime,
|
|
14245
|
+
endTime,
|
|
14246
|
+
mimeType: messageProps.mimeType
|
|
14247
|
+
}, messageProps.trackUri != null && {
|
|
14248
|
+
trackUri: messageProps.trackUri
|
|
14249
|
+
}), messageProps.contentType != null && {
|
|
14250
|
+
contentType: messageProps.contentType
|
|
14251
|
+
}));
|
|
14252
|
+
}
|
|
14229
14253
|
initializeForPlayback(drmInfo, options) {
|
|
14230
14254
|
var _a3;
|
|
14231
14255
|
logDebug(this, "initializeForPlayback", drmInfo);
|
|
@@ -14249,6 +14273,8 @@ ${this.lastInitStack}` : "";
|
|
|
14249
14273
|
this.drmInfo = drmInfo;
|
|
14250
14274
|
this.bufferingTrackUri = (_a3 = options == null ? void 0 : options.trackUri) != null ? _a3 : null;
|
|
14251
14275
|
this.sessionAbort = (_b = options == null ? void 0 : options.abort) != null ? _b : null;
|
|
14276
|
+
this.keySetupSpanArmed = true;
|
|
14277
|
+
this.keySetupSpanStart = null;
|
|
14252
14278
|
}
|
|
14253
14279
|
/**
|
|
14254
14280
|
* If the drm metadata contains initialization data for the current key system, create a new session.
|
|
@@ -14260,6 +14286,7 @@ ${this.lastInitStack}` : "";
|
|
|
14260
14286
|
this.handleError(new DrmError("DRM not supported."));
|
|
14261
14287
|
return;
|
|
14262
14288
|
}
|
|
14289
|
+
this.markKeySetupStart();
|
|
14263
14290
|
const mediaKeys = await this.attachMediaKeys(drmInfo);
|
|
14264
14291
|
const drmProtection = (_a3 = drmInfo.contentProtections.find((cP) => {
|
|
14265
14292
|
return cP.keySystem === mediaKeys.keySystem;
|
|
@@ -14398,7 +14425,9 @@ ${this.lastInitStack}` : "";
|
|
|
14398
14425
|
event,
|
|
14399
14426
|
// Captured per session, so a concurrent preloaded track's
|
|
14400
14427
|
// license exchange keeps its own attribution.
|
|
14401
|
-
trackUri
|
|
14428
|
+
trackUri,
|
|
14429
|
+
mimeType: drmInfo.mimeType,
|
|
14430
|
+
contentType: drmInfo.contentType
|
|
14402
14431
|
}),
|
|
14403
14432
|
LICENSE_TIMEOUT,
|
|
14404
14433
|
{ message: LICENSE_TIMEOUT_MESSAGE }
|
|
@@ -14420,6 +14449,15 @@ ${this.lastInitStack}` : "";
|
|
|
14420
14449
|
this.pendingMessageProps = messageProps;
|
|
14421
14450
|
const keySystem = mediaKeys.keySystem;
|
|
14422
14451
|
logDebug(this, "message", keySystem, event.message.byteLength);
|
|
14452
|
+
if (this.keySetupSpanStart != null) {
|
|
14453
|
+
this.dispatchLoadSpan(
|
|
14454
|
+
"keySetup",
|
|
14455
|
+
this.keySetupSpanStart,
|
|
14456
|
+
Date.now(),
|
|
14457
|
+
messageProps
|
|
14458
|
+
);
|
|
14459
|
+
this.keySetupSpanStart = null;
|
|
14460
|
+
}
|
|
14423
14461
|
const keySystemOptions = (_a3 = this.options.keySystems[keySystem]) != null ? _a3 : this.options.keySystems["*"];
|
|
14424
14462
|
const licenseServerOptionsProvider = keySystemOptions == null ? void 0 : keySystemOptions.licenseServer;
|
|
14425
14463
|
const licenseServerOptions = (_b = clone(await resolveValueProvider(licenseServerOptionsProvider))) != null ? _b : {};
|
|
@@ -14441,13 +14479,12 @@ ${this.lastInitStack}` : "";
|
|
|
14441
14479
|
challenge
|
|
14442
14480
|
);
|
|
14443
14481
|
if (this.disposed) return;
|
|
14444
|
-
this.
|
|
14445
|
-
|
|
14446
|
-
|
|
14447
|
-
|
|
14448
|
-
|
|
14449
|
-
|
|
14450
|
-
}));
|
|
14482
|
+
this.dispatchLoadSpan(
|
|
14483
|
+
"license",
|
|
14484
|
+
licenseSpanStart,
|
|
14485
|
+
Date.now(),
|
|
14486
|
+
messageProps
|
|
14487
|
+
);
|
|
14451
14488
|
this.pendingMessageProps = null;
|
|
14452
14489
|
if (session.disposed) return;
|
|
14453
14490
|
logDebug(this, "update");
|
|
@@ -15177,7 +15214,7 @@ ${this.lastInitStack}` : "";
|
|
|
15177
15214
|
// src/global/environment.ts
|
|
15178
15215
|
var vinylVersion = parseVersion(
|
|
15179
15216
|
/* istanbul ignore next */
|
|
15180
|
-
"3.
|
|
15217
|
+
"3.2.0"
|
|
15181
15218
|
);
|
|
15182
15219
|
|
|
15183
15220
|
// src/global/globalRef.ts
|
|
@@ -17743,6 +17780,86 @@ ${this.lastInitStack}` : "";
|
|
|
17743
17780
|
return filtered;
|
|
17744
17781
|
}
|
|
17745
17782
|
|
|
17783
|
+
// src/streaming/DescriptorRecord.ts
|
|
17784
|
+
function getDescriptorValue(record2, uri, id) {
|
|
17785
|
+
var _a3, _b;
|
|
17786
|
+
let arr = record2[uri];
|
|
17787
|
+
if (!arr) return null;
|
|
17788
|
+
if (id !== void 0) arr = arr.filter((descriptor) => descriptor.id === id);
|
|
17789
|
+
return (_b = (_a3 = first(arr)) == null ? void 0 : _a3.value) != null ? _b : null;
|
|
17790
|
+
}
|
|
17791
|
+
var descriptorValidator = object({
|
|
17792
|
+
id: string().maybe().optional(),
|
|
17793
|
+
value: string().maybe().optional()
|
|
17794
|
+
});
|
|
17795
|
+
var descriptorRecordValidator = record(string(), array(descriptorValidator).readonly().orUndefined());
|
|
17796
|
+
|
|
17797
|
+
// src/streaming/MediaQualityMetadata.ts
|
|
17798
|
+
var ALL_CONTENT_TYPES = [
|
|
17799
|
+
"video",
|
|
17800
|
+
"audio",
|
|
17801
|
+
"text"
|
|
17802
|
+
];
|
|
17803
|
+
var RESTRICTABLE_CONTENT_TYPES = [
|
|
17804
|
+
"video",
|
|
17805
|
+
"audio"
|
|
17806
|
+
];
|
|
17807
|
+
var drmProtectionValidator = descriptorValidator.extend({
|
|
17808
|
+
keySystem: enumOf(DrmKeySystem),
|
|
17809
|
+
pro: string().maybe().optional(),
|
|
17810
|
+
pssh: string().maybe().optional()
|
|
17811
|
+
});
|
|
17812
|
+
var mediaFormatMetadataValidator = object({
|
|
17813
|
+
contentProtections: array(drmProtectionValidator).readonly(),
|
|
17814
|
+
contentType: isOneOf("audio", "video", "text").orNull(),
|
|
17815
|
+
initDataType: string().orNull(),
|
|
17816
|
+
encryptionScheme: string().orNull(),
|
|
17817
|
+
mimeType: string().orNull()
|
|
17818
|
+
});
|
|
17819
|
+
function createEmptyMediaFormatMetadata() {
|
|
17820
|
+
return {
|
|
17821
|
+
contentType: null,
|
|
17822
|
+
mimeType: null,
|
|
17823
|
+
initDataType: null,
|
|
17824
|
+
encryptionScheme: null,
|
|
17825
|
+
contentProtections: []
|
|
17826
|
+
};
|
|
17827
|
+
}
|
|
17828
|
+
function createEmptyMediaQualityMetadata() {
|
|
17829
|
+
return __spreadProps(__spreadValues({}, createEmptyMediaFormatMetadata()), {
|
|
17830
|
+
decoderId: createShortUid(),
|
|
17831
|
+
qualityId: createShortUid(),
|
|
17832
|
+
codecs: null,
|
|
17833
|
+
bandwidth: null,
|
|
17834
|
+
bandwidthTotal: null,
|
|
17835
|
+
supplementalProperties: {},
|
|
17836
|
+
audioSamplingRate: null,
|
|
17837
|
+
frameRate: null,
|
|
17838
|
+
height: null,
|
|
17839
|
+
width: null,
|
|
17840
|
+
lang: null,
|
|
17841
|
+
characteristics: [],
|
|
17842
|
+
groupId: createShortUid(),
|
|
17843
|
+
switchingGroupIds: null
|
|
17844
|
+
});
|
|
17845
|
+
}
|
|
17846
|
+
var mediaQualityMetadataValidator = mediaFormatMetadataValidator.extend({
|
|
17847
|
+
audioSamplingRate: array(number()).readonly().orNull(),
|
|
17848
|
+
bandwidth: number().orNull(),
|
|
17849
|
+
bandwidthTotal: number().orNull(),
|
|
17850
|
+
codecs: string().orNull(),
|
|
17851
|
+
decoderId: string(),
|
|
17852
|
+
frameRate: tuple(number(), number()).readonly().orNull(),
|
|
17853
|
+
height: number().orNull(),
|
|
17854
|
+
qualityId: string(),
|
|
17855
|
+
supplementalProperties: descriptorRecordValidator,
|
|
17856
|
+
width: number().orNull(),
|
|
17857
|
+
lang: string().orNull(),
|
|
17858
|
+
characteristics: array(string()).readonly(),
|
|
17859
|
+
groupId: string(),
|
|
17860
|
+
switchingGroupIds: array(string()).readonly().orNull()
|
|
17861
|
+
});
|
|
17862
|
+
|
|
17746
17863
|
// src/track/filters/languageFilter.ts
|
|
17747
17864
|
function throwLanguagesUnsupported() {
|
|
17748
17865
|
throw new MediaUnsupportedError(
|
|
@@ -18024,6 +18141,13 @@ ${this.lastInitStack}` : "";
|
|
|
18024
18141
|
}
|
|
18025
18142
|
|
|
18026
18143
|
// src/streaming/mediaTimelineFilter.ts
|
|
18144
|
+
function assertMediaStreamsRetained(original, filtered, throwError) {
|
|
18145
|
+
for (const contentType2 of RESTRICTABLE_CONTENT_TYPES) {
|
|
18146
|
+
if (original.some((q) => q.metadata.contentType === contentType2) && !filtered.some((q) => q.metadata.contentType === contentType2)) {
|
|
18147
|
+
throwError();
|
|
18148
|
+
}
|
|
18149
|
+
}
|
|
18150
|
+
}
|
|
18027
18151
|
function filterTimelineQualities(filter, throwError, timeline) {
|
|
18028
18152
|
if (!filter) return timeline;
|
|
18029
18153
|
const mapMetadataArray = memoize(
|
|
@@ -18035,11 +18159,9 @@ ${this.lastInitStack}` : "";
|
|
|
18035
18159
|
const filtered = period.qualities.filter(
|
|
18036
18160
|
(q, index) => filter(q.metadata, index, mapMetadataArray(period.qualities))
|
|
18037
18161
|
);
|
|
18162
|
+
assertMediaStreamsRetained(period.qualities, filtered, throwError);
|
|
18038
18163
|
return __spreadProps(__spreadValues({}, period), { qualities: filtered });
|
|
18039
18164
|
});
|
|
18040
|
-
if (periods.some((p) => p.qualities.length === 0)) {
|
|
18041
|
-
throwError();
|
|
18042
|
-
}
|
|
18043
18165
|
return __spreadProps(__spreadValues({}, timeline), { periods });
|
|
18044
18166
|
}
|
|
18045
18167
|
async function filterTimelineQualitiesAsync(filter, throwError, timeline) {
|
|
@@ -18057,11 +18179,9 @@ ${this.lastInitStack}` : "";
|
|
|
18057
18179
|
filtered.push(period.qualities[i]);
|
|
18058
18180
|
}
|
|
18059
18181
|
}
|
|
18182
|
+
assertMediaStreamsRetained(period.qualities, filtered, throwError);
|
|
18060
18183
|
periods.push(__spreadProps(__spreadValues({}, period), { qualities: filtered }));
|
|
18061
18184
|
}
|
|
18062
|
-
if (periods.some((p) => p.qualities.length === 0)) {
|
|
18063
|
-
throwError();
|
|
18064
|
-
}
|
|
18065
18185
|
return __spreadProps(__spreadValues({}, timeline), { periods });
|
|
18066
18186
|
}
|
|
18067
18187
|
|
|
@@ -18135,23 +18255,28 @@ ${this.lastInitStack}` : "";
|
|
|
18135
18255
|
if (metadata.contentType !== "audio") return true;
|
|
18136
18256
|
const samplingRate = last(metadata.audioSamplingRate);
|
|
18137
18257
|
if (!samplingRate) return true;
|
|
18258
|
+
const { minSampleRate } = options;
|
|
18259
|
+
const maxSampleRate = (_a3 = options.maxSampleRate) != null ? _a3 : options.capabilities.sampleRate;
|
|
18138
18260
|
const audioRates = () => map(
|
|
18139
18261
|
array2,
|
|
18140
18262
|
(item) => item.contentType === "audio" ? last(item.audioSamplingRate) : void 0
|
|
18141
18263
|
).filter((rate) => rate != null);
|
|
18142
|
-
const
|
|
18264
|
+
const outOfBandFallback = () => {
|
|
18265
|
+
const rates = audioRates();
|
|
18266
|
+
const inBand = (rate) => (minSampleRate == null || rate >= minSampleRate) && (!maxSampleRate || rate <= maxSampleRate);
|
|
18267
|
+
if (rates.some(inBand)) return void 0;
|
|
18268
|
+
const decodable = maxSampleRate ? rates.filter((rate) => rate <= maxSampleRate) : rates;
|
|
18269
|
+
return decodable.length ? max(decodable) : min(rates);
|
|
18270
|
+
};
|
|
18143
18271
|
if (minSampleRate != null && samplingRate < minSampleRate) {
|
|
18144
|
-
|
|
18145
|
-
return every(rates2, (rate) => rate < minSampleRate) && samplingRate === max(rates2);
|
|
18272
|
+
return samplingRate === outOfBandFallback();
|
|
18146
18273
|
}
|
|
18147
|
-
const maxSampleRate = (_a3 = options.maxSampleRate) != null ? _a3 : options.capabilities.sampleRate;
|
|
18148
18274
|
if (!maxSampleRate) return true;
|
|
18149
18275
|
if (hasBrowser("Firefox" /* FIREFOX */)) {
|
|
18150
18276
|
return samplingRate <= 48e3;
|
|
18151
18277
|
}
|
|
18152
18278
|
if (samplingRate <= maxSampleRate) return true;
|
|
18153
|
-
|
|
18154
|
-
return every(rates, (rate) => rate > maxSampleRate) && samplingRate === min(rates);
|
|
18279
|
+
return samplingRate === outOfBandFallback();
|
|
18155
18280
|
}
|
|
18156
18281
|
|
|
18157
18282
|
// src/util/media/codecOverrides.ts
|
|
@@ -18238,20 +18363,6 @@ ${this.lastInitStack}` : "";
|
|
|
18238
18363
|
});
|
|
18239
18364
|
}
|
|
18240
18365
|
|
|
18241
|
-
// src/streaming/DescriptorRecord.ts
|
|
18242
|
-
function getDescriptorValue(record2, uri, id) {
|
|
18243
|
-
var _a3, _b;
|
|
18244
|
-
let arr = record2[uri];
|
|
18245
|
-
if (!arr) return null;
|
|
18246
|
-
if (id !== void 0) arr = arr.filter((descriptor) => descriptor.id === id);
|
|
18247
|
-
return (_b = (_a3 = first(arr)) == null ? void 0 : _a3.value) != null ? _b : null;
|
|
18248
|
-
}
|
|
18249
|
-
var descriptorValidator = object({
|
|
18250
|
-
id: string().maybe().optional(),
|
|
18251
|
-
value: string().maybe().optional()
|
|
18252
|
-
});
|
|
18253
|
-
var descriptorRecordValidator = record(string(), array(descriptorValidator).readonly().orUndefined());
|
|
18254
|
-
|
|
18255
18366
|
// src/streaming/ManifestControllerImpl.ts
|
|
18256
18367
|
var ManifestControllerImpl = class {
|
|
18257
18368
|
constructor(deps) {
|
|
@@ -18351,72 +18462,6 @@ ${this.lastInitStack}` : "";
|
|
|
18351
18462
|
}
|
|
18352
18463
|
};
|
|
18353
18464
|
|
|
18354
|
-
// src/streaming/MediaQualityMetadata.ts
|
|
18355
|
-
var ALL_CONTENT_TYPES = [
|
|
18356
|
-
"video",
|
|
18357
|
-
"audio",
|
|
18358
|
-
"text"
|
|
18359
|
-
];
|
|
18360
|
-
var RESTRICTABLE_CONTENT_TYPES = [
|
|
18361
|
-
"video",
|
|
18362
|
-
"audio"
|
|
18363
|
-
];
|
|
18364
|
-
var drmProtectionValidator = descriptorValidator.extend({
|
|
18365
|
-
keySystem: enumOf(DrmKeySystem),
|
|
18366
|
-
pro: string().maybe().optional(),
|
|
18367
|
-
pssh: string().maybe().optional()
|
|
18368
|
-
});
|
|
18369
|
-
var mediaFormatMetadataValidator = object({
|
|
18370
|
-
contentProtections: array(drmProtectionValidator).readonly(),
|
|
18371
|
-
contentType: isOneOf("audio", "video", "text").orNull(),
|
|
18372
|
-
initDataType: string().orNull(),
|
|
18373
|
-
encryptionScheme: string().orNull(),
|
|
18374
|
-
mimeType: string().orNull()
|
|
18375
|
-
});
|
|
18376
|
-
function createEmptyMediaFormatMetadata() {
|
|
18377
|
-
return {
|
|
18378
|
-
contentType: null,
|
|
18379
|
-
mimeType: null,
|
|
18380
|
-
initDataType: null,
|
|
18381
|
-
encryptionScheme: null,
|
|
18382
|
-
contentProtections: []
|
|
18383
|
-
};
|
|
18384
|
-
}
|
|
18385
|
-
function createEmptyMediaQualityMetadata() {
|
|
18386
|
-
return __spreadProps(__spreadValues({}, createEmptyMediaFormatMetadata()), {
|
|
18387
|
-
decoderId: createShortUid(),
|
|
18388
|
-
qualityId: createShortUid(),
|
|
18389
|
-
codecs: null,
|
|
18390
|
-
bandwidth: null,
|
|
18391
|
-
bandwidthTotal: null,
|
|
18392
|
-
supplementalProperties: {},
|
|
18393
|
-
audioSamplingRate: null,
|
|
18394
|
-
frameRate: null,
|
|
18395
|
-
height: null,
|
|
18396
|
-
width: null,
|
|
18397
|
-
lang: null,
|
|
18398
|
-
characteristics: [],
|
|
18399
|
-
groupId: createShortUid(),
|
|
18400
|
-
switchingGroupIds: null
|
|
18401
|
-
});
|
|
18402
|
-
}
|
|
18403
|
-
var mediaQualityMetadataValidator = mediaFormatMetadataValidator.extend({
|
|
18404
|
-
audioSamplingRate: array(number()).readonly().orNull(),
|
|
18405
|
-
bandwidth: number().orNull(),
|
|
18406
|
-
bandwidthTotal: number().orNull(),
|
|
18407
|
-
codecs: string().orNull(),
|
|
18408
|
-
decoderId: string(),
|
|
18409
|
-
frameRate: tuple(number(), number()).readonly().orNull(),
|
|
18410
|
-
height: number().orNull(),
|
|
18411
|
-
qualityId: string(),
|
|
18412
|
-
supplementalProperties: descriptorRecordValidator,
|
|
18413
|
-
width: number().orNull(),
|
|
18414
|
-
lang: string().orNull(),
|
|
18415
|
-
characteristics: array(string()).readonly(),
|
|
18416
|
-
groupId: string(),
|
|
18417
|
-
switchingGroupIds: array(string()).readonly().orNull()
|
|
18418
|
-
});
|
|
18419
|
-
|
|
18420
18465
|
// src/streaming/MediaTimeline.ts
|
|
18421
18466
|
function getMediaPeriodAtTime(timeline, time) {
|
|
18422
18467
|
const index = sortedInsertionIndex(timeline.periods, time, (time2, period2) => {
|
|
@@ -20529,10 +20574,7 @@ ${this.lastInitStack}` : "";
|
|
|
20529
20574
|
this.deps = deps;
|
|
20530
20575
|
add(
|
|
20531
20576
|
deps.contentTypesValue.onData((contentTypesPromise) => {
|
|
20532
|
-
contentTypesPromise.then((value) =>
|
|
20533
|
-
this.setContentTypes(value);
|
|
20534
|
-
this.validateRequiredContentTypesPlayable();
|
|
20535
|
-
}).catch(this.errorHandler);
|
|
20577
|
+
contentTypesPromise.then((value) => this.setContentTypes(value)).catch(this.errorHandler);
|
|
20536
20578
|
})
|
|
20537
20579
|
);
|
|
20538
20580
|
this.on("streamingQualityChange", (event) => {
|
|
@@ -20633,33 +20675,8 @@ ${this.lastInitStack}` : "";
|
|
|
20633
20675
|
});
|
|
20634
20676
|
}
|
|
20635
20677
|
}
|
|
20636
|
-
this.validateRequiredContentTypesPlayable();
|
|
20637
20678
|
});
|
|
20638
20679
|
}
|
|
20639
|
-
/**
|
|
20640
|
-
* Fails fast when a media content type the manifest declared (audio/video)
|
|
20641
|
-
* has no playable qualities left after timeline filtering. Its stream can
|
|
20642
|
-
* never create a source buffer, so without this the track waits forever for
|
|
20643
|
-
* a source buffer that never arrives, surfacing only as an opaque
|
|
20644
|
-
* `readyToAppend` timeout. Runs once both the required content types and the
|
|
20645
|
-
* filtered timeline qualities are known (either order).
|
|
20646
|
-
*/
|
|
20647
|
-
validateRequiredContentTypesPlayable() {
|
|
20648
|
-
const qualities = this._qualities;
|
|
20649
|
-
if (!qualities) return;
|
|
20650
|
-
const present = new Set(qualities.map((q) => q.contentType));
|
|
20651
|
-
for (const contentType2 of this._contentTypes) {
|
|
20652
|
-
if (!present.has(contentType2)) {
|
|
20653
|
-
this.errorHandler(
|
|
20654
|
-
new MediaUnsupportedError(
|
|
20655
|
-
`No playable ${contentType2} rendition`,
|
|
20656
|
-
`no-playable-${contentType2}`
|
|
20657
|
-
)
|
|
20658
|
-
);
|
|
20659
|
-
return;
|
|
20660
|
-
}
|
|
20661
|
-
}
|
|
20662
|
-
}
|
|
20663
20680
|
async getAds() {
|
|
20664
20681
|
const timeline = await this.deps.mediaTimeline.value;
|
|
20665
20682
|
return {
|
|
@@ -24081,6 +24098,7 @@ ${this.lastInitStack}` : "";
|
|
|
24081
24098
|
playerRegistryRef.value.addPlayer(this);
|
|
24082
24099
|
this.redispatchSubControllerEvents();
|
|
24083
24100
|
this.redispatchCurrentTrackEvents();
|
|
24101
|
+
this.initializeLoadSpanLogging();
|
|
24084
24102
|
this.initializeAutoResetHandling();
|
|
24085
24103
|
}
|
|
24086
24104
|
get [Symbol.toStringTag]() {
|
|
@@ -24098,6 +24116,26 @@ ${this.lastInitStack}` : "";
|
|
|
24098
24116
|
get autoResetController() {
|
|
24099
24117
|
return this.deps.autoResetController;
|
|
24100
24118
|
}
|
|
24119
|
+
/**
|
|
24120
|
+
* Logs each republished load span once at the verbose level, formatting its
|
|
24121
|
+
* bounds as ISO date strings and its duration in milliseconds.
|
|
24122
|
+
*/
|
|
24123
|
+
initializeLoadSpanLogging() {
|
|
24124
|
+
this.disposer.add(
|
|
24125
|
+
this.on("loadSpan", (span) => {
|
|
24126
|
+
if (getLogLevel() > 0 /* VERBOSE */) return;
|
|
24127
|
+
logVerbose(this, "loadSpan", {
|
|
24128
|
+
kind: span.kind,
|
|
24129
|
+
trackUri: span.trackUri,
|
|
24130
|
+
mimeType: span.mimeType,
|
|
24131
|
+
contentType: span.contentType,
|
|
24132
|
+
startTime: new Date(span.startTime).toISOString(),
|
|
24133
|
+
endTime: new Date(span.endTime).toISOString(),
|
|
24134
|
+
durationMs: (span.endTime - span.startTime).toFixed(2)
|
|
24135
|
+
});
|
|
24136
|
+
})
|
|
24137
|
+
);
|
|
24138
|
+
}
|
|
24101
24139
|
redispatchSubControllerEvents() {
|
|
24102
24140
|
const add = this.disposer.add;
|
|
24103
24141
|
add(
|
|
@@ -31554,7 +31592,7 @@ ${(_a3 = this.failedResult.failedExpectations[0]) == null ? void 0 : _a3.message
|
|
|
31554
31592
|
drmController.setBufferingDrmInfo(drmInfo);
|
|
31555
31593
|
await emitEncrypted(new Uint8Array([1, 2, 3]), "cenc");
|
|
31556
31594
|
await emitMessage(0, new ArrayBuffer(1));
|
|
31557
|
-
expect(spy).
|
|
31595
|
+
expect(spy).toHaveBeenCalledWith(
|
|
31558
31596
|
objectContaining2({
|
|
31559
31597
|
kind: "license",
|
|
31560
31598
|
startTime: any4(Number),
|
|
@@ -31562,22 +31600,65 @@ ${(_a3 = this.failedResult.failedExpectations[0]) == null ? void 0 : _a3.message
|
|
|
31562
31600
|
})
|
|
31563
31601
|
);
|
|
31564
31602
|
});
|
|
31603
|
+
it("measures the key setup, contiguous with and before the license", async () => {
|
|
31604
|
+
licenseProvider.and.resolveTo(new Uint8Array([1]).buffer);
|
|
31605
|
+
const spy = createEventSpy(drmController, "loadSpanMeasured");
|
|
31606
|
+
drmController.setBufferingDrmInfo(drmInfo, { trackUri: "track-a" });
|
|
31607
|
+
await emitEncrypted(new Uint8Array([1, 2, 3]), "cenc");
|
|
31608
|
+
await emitMessage(0, new ArrayBuffer(1));
|
|
31609
|
+
const spans = spy.calls.all().map((call) => call.args[0]);
|
|
31610
|
+
expect(spans.map((span) => span.kind)).toEqual([
|
|
31611
|
+
"keySetup",
|
|
31612
|
+
"license"
|
|
31613
|
+
]);
|
|
31614
|
+
const [keySetup, license] = spans;
|
|
31615
|
+
expect(keySetup.trackUri).toEqual("track-a");
|
|
31616
|
+
expect(keySetup.endTime).toEqual(license.startTime);
|
|
31617
|
+
});
|
|
31618
|
+
it("carries the content mime and content type on the license span", async () => {
|
|
31619
|
+
licenseProvider.and.resolveTo(new Uint8Array([1]).buffer);
|
|
31620
|
+
const spy = createEventSpy(drmController, "loadSpanMeasured");
|
|
31621
|
+
drmController.setBufferingDrmInfo(drmInfo);
|
|
31622
|
+
await emitEncrypted(new Uint8Array([1, 2, 3]), "cenc");
|
|
31623
|
+
await emitMessage(0, new ArrayBuffer(1));
|
|
31624
|
+
expect(spy).toHaveBeenCalledWith(
|
|
31625
|
+
objectContaining2({
|
|
31626
|
+
kind: "license",
|
|
31627
|
+
mimeType: "audio/mp4",
|
|
31628
|
+
contentType: "audio"
|
|
31629
|
+
})
|
|
31630
|
+
);
|
|
31631
|
+
});
|
|
31565
31632
|
it("does not measure a failed license exchange", async () => {
|
|
31566
31633
|
licenseProvider.and.rejectWith(new Error("license failed"));
|
|
31567
31634
|
const spy = createEventSpy(drmController, "loadSpanMeasured");
|
|
31568
31635
|
drmController.setBufferingDrmInfo(drmInfo);
|
|
31569
31636
|
await emitEncrypted(new Uint8Array([1, 2, 3]), "cenc");
|
|
31570
31637
|
await emitMessage(0, new ArrayBuffer(1));
|
|
31571
|
-
expect(spy).not.
|
|
31638
|
+
expect(spy).not.toHaveBeenCalledWith(
|
|
31639
|
+
objectContaining2({ kind: "license" })
|
|
31640
|
+
);
|
|
31572
31641
|
errorSpy.calls.reset();
|
|
31573
31642
|
});
|
|
31643
|
+
it("measures key setup only for the first session of a track load", async () => {
|
|
31644
|
+
licenseProvider.and.resolveTo(new Uint8Array([1]).buffer);
|
|
31645
|
+
const spy = createEventSpy(drmController, "loadSpanMeasured");
|
|
31646
|
+
drmController.setBufferingDrmInfo(drmInfo, { trackUri: "track-a" });
|
|
31647
|
+
await emitEncrypted(new Uint8Array([1]), "cenc");
|
|
31648
|
+
await emitEncrypted(new Uint8Array([2]), "cenc");
|
|
31649
|
+
expect(mediaKeys.createSession).toHaveBeenCalledTimes(2);
|
|
31650
|
+
await emitMessage(0, new ArrayBuffer(1));
|
|
31651
|
+
await emitMessage(1, new ArrayBuffer(1));
|
|
31652
|
+
const keySetupSpans = spy.calls.all().filter((call) => call.args[0].kind === "keySetup");
|
|
31653
|
+
expect(keySetupSpans.length).toEqual(1);
|
|
31654
|
+
});
|
|
31574
31655
|
it("attributes the license span to the buffering track uri", async () => {
|
|
31575
31656
|
licenseProvider.and.resolveTo(new Uint8Array([1]).buffer);
|
|
31576
31657
|
const spy = createEventSpy(drmController, "loadSpanMeasured");
|
|
31577
31658
|
drmController.setBufferingDrmInfo(drmInfo, { trackUri: "track-a" });
|
|
31578
31659
|
await emitEncrypted(new Uint8Array([1, 2, 3]), "cenc");
|
|
31579
31660
|
await emitMessage(0, new ArrayBuffer(1));
|
|
31580
|
-
expect(spy).
|
|
31661
|
+
expect(spy).toHaveBeenCalledWith(
|
|
31581
31662
|
objectContaining2({
|
|
31582
31663
|
kind: "license",
|
|
31583
31664
|
trackUri: "track-a"
|
|
@@ -31606,16 +31687,10 @@ ${(_a3 = this.failedResult.failedExpectations[0]) == null ? void 0 : _a3.message
|
|
|
31606
31687
|
expect(mediaKeys.createSession).toHaveBeenCalledTimes(2);
|
|
31607
31688
|
await emitMessage(1, new ArrayBuffer(1));
|
|
31608
31689
|
await emitMessage(0, new ArrayBuffer(1));
|
|
31609
|
-
|
|
31610
|
-
expect(
|
|
31611
|
-
|
|
31612
|
-
|
|
31613
|
-
trackUri: "track-preloaded"
|
|
31614
|
-
})
|
|
31615
|
-
);
|
|
31616
|
-
expect(spy).toHaveBeenCalledWith(
|
|
31617
|
-
objectContaining2({ kind: "license", trackUri: "track-active" })
|
|
31618
|
-
);
|
|
31690
|
+
const licenseTrackUris = spy.calls.all().map((call) => call.args[0]).filter((span) => span.kind === "license").map((span) => span.trackUri);
|
|
31691
|
+
expect(licenseTrackUris.length).toEqual(2);
|
|
31692
|
+
expect(licenseTrackUris).toContain("track-preloaded");
|
|
31693
|
+
expect(licenseTrackUris).toContain("track-active");
|
|
31619
31694
|
});
|
|
31620
31695
|
});
|
|
31621
31696
|
describe("serverCertificate", () => {
|
|
@@ -38396,105 +38471,101 @@ ${(_a3 = this.failedResult.failedExpectations[0]) == null ? void 0 : _a3.message
|
|
|
38396
38471
|
getSegment: () => Promise.resolve(null)
|
|
38397
38472
|
};
|
|
38398
38473
|
}
|
|
38399
|
-
|
|
38400
|
-
|
|
38401
|
-
periods: [
|
|
38402
|
-
{
|
|
38403
|
-
startTime: 0,
|
|
38404
|
-
endTime: 10,
|
|
38405
|
-
qualities: [
|
|
38406
|
-
createQuality2("audio", 128e3),
|
|
38407
|
-
createQuality2("audio", 256e3),
|
|
38408
|
-
createQuality2("video", 5e5)
|
|
38409
|
-
]
|
|
38410
|
-
}
|
|
38411
|
-
],
|
|
38474
|
+
function timelineOf2(qualities) {
|
|
38475
|
+
return {
|
|
38476
|
+
periods: [{ startTime: 0, endTime: 10, qualities: [...qualities] }],
|
|
38412
38477
|
minBufferTime: 2,
|
|
38413
38478
|
getAdBreaks: () => Promise.resolve([]),
|
|
38414
38479
|
getDuration: () => Promise.resolve(Infinity)
|
|
38415
38480
|
};
|
|
38481
|
+
}
|
|
38482
|
+
function contentTypesOf(timeline) {
|
|
38483
|
+
return new Set(
|
|
38484
|
+
timeline.periods[0].qualities.map((q) => q.metadata.contentType)
|
|
38485
|
+
);
|
|
38486
|
+
}
|
|
38487
|
+
function throwEmpty() {
|
|
38488
|
+
throw new Error("no qualities");
|
|
38489
|
+
}
|
|
38490
|
+
describe("filterTimelineQualities", () => {
|
|
38491
|
+
const timeline = timelineOf2([
|
|
38492
|
+
createQuality2("audio", 128e3),
|
|
38493
|
+
createQuality2("audio", 256e3),
|
|
38494
|
+
createQuality2("video", 5e5)
|
|
38495
|
+
]);
|
|
38416
38496
|
it("returns the timeline unchanged when the filter is null", () => {
|
|
38417
|
-
const result = filterTimelineQualities(
|
|
38418
|
-
null,
|
|
38419
|
-
() => {
|
|
38420
|
-
throw new Error("empty");
|
|
38421
|
-
},
|
|
38422
|
-
timeline
|
|
38423
|
-
);
|
|
38497
|
+
const result = filterTimelineQualities(null, throwEmpty, timeline);
|
|
38424
38498
|
expect(result).toBe(timeline);
|
|
38425
38499
|
});
|
|
38426
|
-
it("filters qualities by predicate", () => {
|
|
38500
|
+
it("filters qualities by predicate while keeping every present stream", () => {
|
|
38427
38501
|
const result = filterTimelineQualities(
|
|
38428
|
-
(q) => q.
|
|
38429
|
-
|
|
38430
|
-
throw new Error("empty");
|
|
38431
|
-
},
|
|
38502
|
+
(q) => q.bandwidth !== 256e3,
|
|
38503
|
+
throwEmpty,
|
|
38432
38504
|
timeline
|
|
38433
38505
|
);
|
|
38434
38506
|
expect(result.periods[0].qualities.length).toBe(2);
|
|
38435
|
-
expect(
|
|
38436
|
-
result.periods[0].qualities.every(
|
|
38437
|
-
(q) => q.metadata.contentType === "audio"
|
|
38438
|
-
)
|
|
38439
|
-
).toBeTrue();
|
|
38507
|
+
expect(contentTypesOf(result)).toEqual(/* @__PURE__ */ new Set(["audio", "video"]));
|
|
38440
38508
|
});
|
|
38441
38509
|
it("preserves minBufferTime", () => {
|
|
38442
|
-
const result = filterTimelineQualities(
|
|
38443
|
-
() => true,
|
|
38444
|
-
() => {
|
|
38445
|
-
throw new Error("empty");
|
|
38446
|
-
},
|
|
38447
|
-
timeline
|
|
38448
|
-
);
|
|
38510
|
+
const result = filterTimelineQualities(() => true, throwEmpty, timeline);
|
|
38449
38511
|
expect(result.minBufferTime).toBe(2);
|
|
38450
38512
|
});
|
|
38451
38513
|
it("throws when filtering removes all qualities", () => {
|
|
38514
|
+
expect(
|
|
38515
|
+
() => filterTimelineQualities(() => false, throwEmpty, timeline)
|
|
38516
|
+
).toThrowError("no qualities");
|
|
38517
|
+
});
|
|
38518
|
+
it("throws when filtering drops a present video stream to zero", () => {
|
|
38452
38519
|
expect(
|
|
38453
38520
|
() => filterTimelineQualities(
|
|
38454
|
-
() =>
|
|
38455
|
-
|
|
38456
|
-
throw new Error("no qualities");
|
|
38457
|
-
},
|
|
38521
|
+
(q) => q.contentType === "audio",
|
|
38522
|
+
throwEmpty,
|
|
38458
38523
|
timeline
|
|
38459
38524
|
)
|
|
38460
38525
|
).toThrowError("no qualities");
|
|
38461
38526
|
});
|
|
38527
|
+
it("throws when filtering drops a present audio stream to zero", () => {
|
|
38528
|
+
expect(
|
|
38529
|
+
() => filterTimelineQualities(
|
|
38530
|
+
(q) => q.contentType === "video",
|
|
38531
|
+
throwEmpty,
|
|
38532
|
+
timeline
|
|
38533
|
+
)
|
|
38534
|
+
).toThrowError("no qualities");
|
|
38535
|
+
});
|
|
38536
|
+
it("does not throw when losing only an optional text sidecar stream", () => {
|
|
38537
|
+
const withText = timelineOf2([
|
|
38538
|
+
createQuality2("audio", 128e3),
|
|
38539
|
+
createQuality2("video", 5e5),
|
|
38540
|
+
createQuality2("text", 1e3)
|
|
38541
|
+
]);
|
|
38542
|
+
const result = filterTimelineQualities(
|
|
38543
|
+
(q) => q.contentType !== "text",
|
|
38544
|
+
throwEmpty,
|
|
38545
|
+
withText
|
|
38546
|
+
);
|
|
38547
|
+
expect(contentTypesOf(result)).toEqual(/* @__PURE__ */ new Set(["audio", "video"]));
|
|
38548
|
+
});
|
|
38462
38549
|
});
|
|
38463
38550
|
describe("filterTimelineQualitiesAsync", () => {
|
|
38464
|
-
const timeline =
|
|
38465
|
-
|
|
38466
|
-
|
|
38467
|
-
|
|
38468
|
-
|
|
38469
|
-
|
|
38470
|
-
createQuality2("audio", 128e3),
|
|
38471
|
-
createQuality2("video", 5e5)
|
|
38472
|
-
]
|
|
38473
|
-
}
|
|
38474
|
-
],
|
|
38475
|
-
minBufferTime: 2,
|
|
38476
|
-
getAdBreaks: () => Promise.resolve([]),
|
|
38477
|
-
getDuration: () => Promise.resolve(Infinity)
|
|
38478
|
-
};
|
|
38479
|
-
it("filters qualities by async predicate", async () => {
|
|
38551
|
+
const timeline = timelineOf2([
|
|
38552
|
+
createQuality2("audio", 128e3),
|
|
38553
|
+
createQuality2("audio", 256e3),
|
|
38554
|
+
createQuality2("video", 5e5)
|
|
38555
|
+
]);
|
|
38556
|
+
it("filters qualities by async predicate while keeping every present stream", async () => {
|
|
38480
38557
|
const result = await filterTimelineQualitiesAsync(
|
|
38481
|
-
(q) => Promise.resolve(q.
|
|
38482
|
-
|
|
38483
|
-
throw new Error("empty");
|
|
38484
|
-
},
|
|
38558
|
+
(q) => Promise.resolve(q.bandwidth !== 256e3),
|
|
38559
|
+
throwEmpty,
|
|
38485
38560
|
timeline
|
|
38486
38561
|
);
|
|
38487
|
-
expect(result.periods[0].qualities.length).toBe(
|
|
38488
|
-
expect(result.
|
|
38489
|
-
"audio"
|
|
38490
|
-
);
|
|
38562
|
+
expect(result.periods[0].qualities.length).toBe(2);
|
|
38563
|
+
expect(contentTypesOf(result)).toEqual(/* @__PURE__ */ new Set(["audio", "video"]));
|
|
38491
38564
|
});
|
|
38492
38565
|
it("preserves minBufferTime", async () => {
|
|
38493
38566
|
const result = await filterTimelineQualitiesAsync(
|
|
38494
38567
|
() => Promise.resolve(true),
|
|
38495
|
-
|
|
38496
|
-
throw new Error("empty");
|
|
38497
|
-
},
|
|
38568
|
+
throwEmpty,
|
|
38498
38569
|
timeline
|
|
38499
38570
|
);
|
|
38500
38571
|
expect(result.minBufferTime).toBe(2);
|
|
@@ -38503,13 +38574,33 @@ ${(_a3 = this.failedResult.failedExpectations[0]) == null ? void 0 : _a3.message
|
|
|
38503
38574
|
await expectAsync(
|
|
38504
38575
|
filterTimelineQualitiesAsync(
|
|
38505
38576
|
() => Promise.resolve(false),
|
|
38506
|
-
|
|
38507
|
-
|
|
38508
|
-
|
|
38577
|
+
throwEmpty,
|
|
38578
|
+
timeline
|
|
38579
|
+
)
|
|
38580
|
+
).toBeRejectedWithError("no qualities");
|
|
38581
|
+
});
|
|
38582
|
+
it("throws when filtering drops a present video stream to zero", async () => {
|
|
38583
|
+
await expectAsync(
|
|
38584
|
+
filterTimelineQualitiesAsync(
|
|
38585
|
+
(q) => Promise.resolve(q.contentType === "audio"),
|
|
38586
|
+
throwEmpty,
|
|
38509
38587
|
timeline
|
|
38510
38588
|
)
|
|
38511
38589
|
).toBeRejectedWithError("no qualities");
|
|
38512
38590
|
});
|
|
38591
|
+
it("does not throw when losing only an optional text sidecar stream", async () => {
|
|
38592
|
+
const withText = timelineOf2([
|
|
38593
|
+
createQuality2("audio", 128e3),
|
|
38594
|
+
createQuality2("video", 5e5),
|
|
38595
|
+
createQuality2("text", 1e3)
|
|
38596
|
+
]);
|
|
38597
|
+
const result = await filterTimelineQualitiesAsync(
|
|
38598
|
+
(q) => Promise.resolve(q.contentType !== "text"),
|
|
38599
|
+
throwEmpty,
|
|
38600
|
+
withText
|
|
38601
|
+
);
|
|
38602
|
+
expect(contentTypesOf(result)).toEqual(/* @__PURE__ */ new Set(["audio", "video"]));
|
|
38603
|
+
});
|
|
38513
38604
|
});
|
|
38514
38605
|
|
|
38515
38606
|
// test/unit/streaming/mediaTimelineLanguageFilter.test.ts
|
|
@@ -39341,7 +39432,8 @@ ${(_a3 = this.failedResult.failedExpectations[0]) == null ? void 0 : _a3.message
|
|
|
39341
39432
|
expect(track2.qualities).toEqual([qualityMetadata]);
|
|
39342
39433
|
expect(track2.qualitiesUnfiltered).toEqual([qualityMetadata]);
|
|
39343
39434
|
});
|
|
39344
|
-
it("
|
|
39435
|
+
it("plays the surviving content type when another is entirely filtered out", async () => {
|
|
39436
|
+
deps.contentTypesValue.value = Promise.resolve(/* @__PURE__ */ new Set(["video"]));
|
|
39345
39437
|
const videoQuality2 = __spreadProps(__spreadValues({}, createEmptyMediaQualityMetadata()), {
|
|
39346
39438
|
contentType: "video",
|
|
39347
39439
|
qualityId: "v1"
|
|
@@ -39365,10 +39457,10 @@ ${(_a3 = this.failedResult.failedExpectations[0]) == null ? void 0 : _a3.message
|
|
|
39365
39457
|
});
|
|
39366
39458
|
track2 = createTrack();
|
|
39367
39459
|
await flushPromises();
|
|
39368
|
-
expect(track2.error).
|
|
39369
|
-
expect(track2.
|
|
39370
|
-
|
|
39371
|
-
);
|
|
39460
|
+
expect(track2.error).toBeNull();
|
|
39461
|
+
expect(track2.contentTypes).toEqual(/* @__PURE__ */ new Set(["video"]));
|
|
39462
|
+
expect(getStream("video")).not.toBeNull();
|
|
39463
|
+
expect(getStream("audio")).toBeNull();
|
|
39372
39464
|
});
|
|
39373
39465
|
it("reads qualities from the filtered timeline but qualitiesUnfiltered from the raw one", async () => {
|
|
39374
39466
|
const makeTimeline = (qualities) => ({
|
|
@@ -47894,6 +47986,25 @@ x`);
|
|
|
47894
47986
|
expect(withinMin(mid, 1, [low, mid])).toBe(true);
|
|
47895
47987
|
expect(withinMin(low, 0, [low, mid])).toBe(false);
|
|
47896
47988
|
});
|
|
47989
|
+
it("keeps the highest when every rate (of three) is below the floor", () => {
|
|
47990
|
+
const low = audioQuality2([16e3]);
|
|
47991
|
+
const mid = audioQuality2([24e3]);
|
|
47992
|
+
const high = audioQuality2([32e3]);
|
|
47993
|
+
expect(withinMin(high, 2, [low, mid, high])).toBe(true);
|
|
47994
|
+
expect(withinMin(mid, 1, [low, mid, high])).toBe(false);
|
|
47995
|
+
expect(withinMin(low, 0, [low, mid, high])).toBe(false);
|
|
47996
|
+
});
|
|
47997
|
+
it("keeps the highest below-floor rate when no platform max is reported", () => {
|
|
47998
|
+
capabilities.sampleRate = null;
|
|
47999
|
+
const low = audioQuality2([16e3]);
|
|
48000
|
+
const high = audioQuality2([32e3]);
|
|
48001
|
+
expect(withinMin(high, 1, [low, high])).toBe(true);
|
|
48002
|
+
expect(withinMin(low, 0, [low, high])).toBe(false);
|
|
48003
|
+
});
|
|
48004
|
+
it("keeps a single audio rendition below the floor", () => {
|
|
48005
|
+
const only = audioQuality2([24e3]);
|
|
48006
|
+
expect(withinMin(only, 0, [only])).toBe(true);
|
|
48007
|
+
});
|
|
47897
48008
|
it("ignores non-audio qualities in the floor fallback", () => {
|
|
47898
48009
|
const audio = audioQuality2([24e3]);
|
|
47899
48010
|
const video = createEmptyMediaQualityMetadata();
|
|
@@ -47903,6 +48014,55 @@ x`);
|
|
|
47903
48014
|
expect(withinMin(video, 1, [audio, video])).toBe(true);
|
|
47904
48015
|
});
|
|
47905
48016
|
});
|
|
48017
|
+
describe("withinAudioSampleRateRange straddling an empty band", () => {
|
|
48018
|
+
let capabilities;
|
|
48019
|
+
beforeEach(() => {
|
|
48020
|
+
setUserAgent("Chrome");
|
|
48021
|
+
capabilities = new MockCapabilities();
|
|
48022
|
+
capabilities.sampleRate = 48e3;
|
|
48023
|
+
});
|
|
48024
|
+
function audioQuality2(rate) {
|
|
48025
|
+
const quality = createEmptyMediaQualityMetadata();
|
|
48026
|
+
quality.contentType = "audio";
|
|
48027
|
+
quality.audioSamplingRate = rate;
|
|
48028
|
+
return quality;
|
|
48029
|
+
}
|
|
48030
|
+
function within(metadata, index, array2) {
|
|
48031
|
+
return withinAudioSampleRateRange(
|
|
48032
|
+
{ capabilities, minSampleRate: 44100 },
|
|
48033
|
+
metadata,
|
|
48034
|
+
index,
|
|
48035
|
+
array2
|
|
48036
|
+
);
|
|
48037
|
+
}
|
|
48038
|
+
it("keeps the highest decodable rate when one is below the floor and one above the ceiling", () => {
|
|
48039
|
+
const low = audioQuality2([22050]);
|
|
48040
|
+
const high = audioQuality2([96e3]);
|
|
48041
|
+
expect(within(low, 0, [low, high])).toBe(true);
|
|
48042
|
+
expect(within(high, 1, [low, high])).toBe(false);
|
|
48043
|
+
});
|
|
48044
|
+
it("keeps the below-floor rate when the ceiling matches the output device rate", () => {
|
|
48045
|
+
capabilities.sampleRate = 44100;
|
|
48046
|
+
const low = audioQuality2([22050]);
|
|
48047
|
+
const high = audioQuality2([48e3]);
|
|
48048
|
+
expect(within(low, 0, [low, high])).toBe(true);
|
|
48049
|
+
expect(within(high, 1, [low, high])).toBe(false);
|
|
48050
|
+
});
|
|
48051
|
+
it("still prefers an in-band rate over the straddle fallback", () => {
|
|
48052
|
+
const low = audioQuality2([22050]);
|
|
48053
|
+
const mid = audioQuality2([44100]);
|
|
48054
|
+
const high = audioQuality2([96e3]);
|
|
48055
|
+
expect(within(mid, 1, [low, mid, high])).toBe(true);
|
|
48056
|
+
expect(within(low, 0, [low, mid, high])).toBe(false);
|
|
48057
|
+
expect(within(high, 2, [low, mid, high])).toBe(false);
|
|
48058
|
+
});
|
|
48059
|
+
it("keeps the lowest when every rate is above the ceiling", () => {
|
|
48060
|
+
const low = audioQuality2([88200]);
|
|
48061
|
+
const high = audioQuality2([96e3]);
|
|
48062
|
+
expect(within(low, 0, [low, high])).toBe(true);
|
|
48063
|
+
expect(within(high, 1, [low, high])).toBe(false);
|
|
48064
|
+
});
|
|
48065
|
+
});
|
|
47906
48066
|
|
|
47907
48067
|
// test/unit/track/hls/buildHlsMediaTimeline.test.ts
|
|
47908
48068
|
describe("buildHlsMediaTimeline", () => {
|
|
@@ -53347,6 +53507,7 @@ x`);
|
|
|
53347
53507
|
let depFactories;
|
|
53348
53508
|
let player;
|
|
53349
53509
|
let mockOptions;
|
|
53510
|
+
const loggerRef2 = useMockLogger();
|
|
53350
53511
|
polyfillCustomEvent();
|
|
53351
53512
|
beforeEach(() => {
|
|
53352
53513
|
mockOptions = { media: new MockHTMLAudioElement() };
|
|
@@ -53571,6 +53732,40 @@ x`);
|
|
|
53571
53732
|
});
|
|
53572
53733
|
expect(spy).not.toHaveBeenCalled();
|
|
53573
53734
|
});
|
|
53735
|
+
it("logs each republished span at the verbose level with formatted times", () => {
|
|
53736
|
+
loggerRef2.value.logLevel = 0 /* VERBOSE */;
|
|
53737
|
+
deps.drmController.dispatch("loadSpanMeasured", {
|
|
53738
|
+
kind: "license",
|
|
53739
|
+
startTime: 1e3,
|
|
53740
|
+
endTime: 3500,
|
|
53741
|
+
trackUri: "track-a",
|
|
53742
|
+
mimeType: "audio/mp4",
|
|
53743
|
+
contentType: "audio"
|
|
53744
|
+
});
|
|
53745
|
+
expect(loggerRef2.value.verbose).toHaveBeenCalledOnceWith(
|
|
53746
|
+
player,
|
|
53747
|
+
"loadSpan",
|
|
53748
|
+
{
|
|
53749
|
+
kind: "license",
|
|
53750
|
+
trackUri: "track-a",
|
|
53751
|
+
mimeType: "audio/mp4",
|
|
53752
|
+
contentType: "audio",
|
|
53753
|
+
startTime: (/* @__PURE__ */ new Date(1e3)).toISOString(),
|
|
53754
|
+
endTime: (/* @__PURE__ */ new Date(3500)).toISOString(),
|
|
53755
|
+
durationMs: "2500.00"
|
|
53756
|
+
}
|
|
53757
|
+
);
|
|
53758
|
+
});
|
|
53759
|
+
it("does not format or log a span when the verbose level is off", () => {
|
|
53760
|
+
loggerRef2.value.logLevel = 1 /* DEBUG */;
|
|
53761
|
+
deps.drmController.dispatch("loadSpanMeasured", {
|
|
53762
|
+
kind: "license",
|
|
53763
|
+
startTime: 1e3,
|
|
53764
|
+
endTime: 3500,
|
|
53765
|
+
trackUri: "track-a"
|
|
53766
|
+
});
|
|
53767
|
+
expect(loggerRef2.value.verbose).not.toHaveBeenCalled();
|
|
53768
|
+
});
|
|
53574
53769
|
});
|
|
53575
53770
|
it("reloads the current track on a codecUnsupported event", () => {
|
|
53576
53771
|
const trackController = deps.trackController;
|