forked from shaka-project/shaka-player
-
Notifications
You must be signed in to change notification settings - Fork 1
/
offline.js
503 lines (461 loc) · 15.9 KB
/
offline.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
/*! @license
* Shaka Player
* Copyright 2016 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @externs
*/
/**
* @typedef {{
* basic: boolean,
* encrypted: !Object.<string, boolean>
* }}
*
* @property {boolean} basic
* True if offline is usable at all.
* @property {!Object.<string, boolean>} encrypted
* A map of key system name to whether it supports offline playback.
* @exportDoc
*/
shaka.extern.OfflineSupport;
/**
* @typedef {{
* offlineUri: ?string,
* originalManifestUri: string,
* duration: number,
* size: number,
* expiration: number,
* tracks: !Array.<shaka.extern.Track>,
* appMetadata: Object,
* isIncomplete: boolean
* }}
*
* @property {?string} offlineUri
* An offline URI to access the content. This can be passed directly to
* Player. If the uri is null, it means that the content has not finished
* downloading and is not ready to play.
* @property {string} originalManifestUri
* The original manifest URI of the content stored.
* @property {number} duration
* The duration of the content, in seconds.
* @property {number} size
* The size of the content, in bytes.
* @property {number} expiration
* The time that the encrypted license expires, in milliseconds. If the media
* is clear or the license never expires, this will equal Infinity.
* @property {!Array.<shaka.extern.Track>} tracks
* The tracks that are stored.
* @property {Object} appMetadata
* The metadata passed to store().
* @property {boolean} isIncomplete
* If true, the content is still downloading. Manifests with this set cannot
* be played yet.
* @exportDoc
*/
shaka.extern.StoredContent;
/**
* @typedef {{
* creationTime: number,
* originalManifestUri: string,
* duration: number,
* size: number,
* expiration: number,
* streams: !Array.<shaka.extern.StreamDB>,
* sessionIds: !Array.<string>,
* drmInfo: ?shaka.extern.DrmInfo,
* appMetadata: Object,
* isIncomplete: (boolean|undefined)
* }}
*
* @property {number} creationTime
* The date time when the asset was created.
* @property {string} originalManifestUri
* The URI that the manifest was originally loaded from.
* @property {number} duration
* The total duration of the media, in seconds.
* @property {number} size
* The total size of all stored segments, in bytes.
* @property {number} expiration
* The license expiration, in milliseconds; or Infinity if not applicable.
* Note that upon JSON serialization, Infinity becomes null, and must be
* converted back upon loading from storage.
* @property {!Array.<shaka.extern.StreamDB>} streams
* The Streams that are stored.
* @property {!Array.<string>} sessionIds
* The DRM offline session IDs for the media.
* @property {?shaka.extern.DrmInfo} drmInfo
* The DRM info used to initialize EME.
* @property {Object} appMetadata
* A metadata object passed from the application.
* @property {(boolean|undefined)} isIncomplete
* If true, the content is still downloading.
*/
shaka.extern.ManifestDB;
/**
* @typedef {{
* id: number,
* originalId: ?string,
* primary: boolean,
* type: string,
* mimeType: string,
* codecs: string,
* frameRate: (number|undefined),
* pixelAspectRatio: (string|undefined),
* hdr: (string|undefined),
* kind: (string|undefined),
* language: string,
* label: ?string,
* width: ?number,
* height: ?number,
* encrypted: boolean,
* keyIds: !Set.<string>,
* segments: !Array.<shaka.extern.SegmentDB>,
* variantIds: !Array.<number>,
* roles: !Array.<string>,
* forced: boolean,
* channelsCount: ?number,
* audioSamplingRate: ?number,
* spatialAudio: boolean,
* closedCaptions: Map.<string, string>,
* tilesLayout: (string|undefined)
* }}
*
* @property {number} id
* The unique id of the stream.
* @property {?string} originalId
* The original ID, if any, that appeared in the manifest. For example, in
* DASH, this is the "id" attribute of the Representation element.
* @property {boolean} primary
* Whether the stream set was primary.
* @property {string} type
* The type of the stream, 'audio', 'text', or 'video'.
* @property {string} mimeType
* The MIME type of the stream.
* @property {string} codecs
* The codecs of the stream.
* @property {(number|undefined)} frameRate
* The Stream's framerate in frames per second.
* @property {(string|undefined)} pixelAspectRatio
* The Stream's pixel aspect ratio
* @property {(string|undefined)} hdr
* The Stream's HDR info
* @property {(string|undefined)} kind
* The kind of text stream; undefined for audio/video.
* @property {string} language
* The language of the stream; '' for video.
* @property {?string} label
* The label of the stream; '' for video.
* @property {?number} width
* The width of the stream; null for audio/text.
* @property {?number} height
* The height of the stream; null for audio/text.
* @property {boolean} encrypted
* Whether this stream is encrypted.
* @property {!Set.<string>} keyIds
* The key IDs this stream is encrypted with.
* @property {!Array.<shaka.extern.SegmentDB>} segments
* An array of segments that make up the stream.
* @property {!Array.<number>} variantIds
* An array of ids of variants the stream is a part of.
* @property {!Array.<string>} roles
* The roles of the stream as they appear on the manifest,
* e.g. 'main', 'caption', or 'commentary'.
* @property {boolean} forced
* Whether the stream set was forced.
* @property {?number} channelsCount
* The channel count information for the audio stream.
* @property {?number} audioSamplingRate
* Specifies the maximum sampling rate of the content.
* @property {boolean} spatialAudio
* Whether the stream set has spatial audio.
* @property {Map.<string, string>} closedCaptions
* A map containing the description of closed captions, with the caption
* channel number (CC1 | CC2 | CC3 | CC4) as the key and the language code
* as the value. If the channel number is not provided by the description,
* we'll set an 0-based index as the key.
* Example: {'CC1': 'eng'; 'CC3': 'swe'}, or {'1', 'eng'; '2': 'swe'}, etc.
* @property {(string|undefined)} tilesLayout
* The value is a grid-item-dimension consisting of two positive decimal
* integers in the format: column-x-row ('4x3'). It describes the arrangement
* of Images in a Grid. The minimum valid LAYOUT is '1x1'.
*/
shaka.extern.StreamDB;
/**
* @typedef {{
* initSegmentKey: ?number,
* startTime: number,
* endTime: number,
* appendWindowStart: number,
* appendWindowEnd: number,
* timestampOffset: number,
* tilesLayout: ?string,
* pendingSegmentRefId: (string|undefined),
* pendingInitSegmentRefId: (string|undefined),
* dataKey: number
* }}
*
* @property {?number} initSegmentKey
* The storage key where the init segment is found; null if no init segment.
* @property {number} startTime
* The start time of the segment in the presentation timeline.
* @property {number} endTime
* The end time of the segment in the presentation timeline.
* @property {number} appendWindowStart
* A start timestamp before which media samples will be truncated.
* @property {number} appendWindowEnd
* An end timestamp beyond which media samples will be truncated.
* @property {number} timestampOffset
* An offset which MediaSource will add to the segment's media timestamps
* during ingestion, to align to the presentation timeline.
* @property {?string} tilesLayout
* The value is a grid-item-dimension consisting of two positive decimal
* integers in the format: column-x-row ('4x3'). It describes the
* arrangement of Images in a Grid. The minimum valid LAYOUT is '1x1'.
* @property {(string|undefined)} pendingSegmentRefId
* Contains an id that identifies what the segment was, originally. Used to
* coordinate where segments are stored, during the downloading process.
* If this field is non-null, it's assumed that the segment is not fully
* downloaded.
* @property {(string|undefined)} pendingInitSegmentRefId
* Contains an id that identifies what the init segment was, originally.
* Used to coordinate where init segments are stored, during the downloading
* process.
* If this field is non-null, it's assumed that the init segment is not fully
* downloaded.
* @property {number} dataKey
* The key to the data in storage.
*/
shaka.extern.SegmentDB;
/**
* @typedef {{
* data: !ArrayBuffer
* }}
*
* @property {!ArrayBuffer} data
* The data contents of the segment.
*/
shaka.extern.SegmentDataDB;
/**
* @typedef {{
* sessionId: string,
* keySystem: string,
* licenseUri: string,
* serverCertificate: Uint8Array,
* audioCapabilities: !Array.<MediaKeySystemMediaCapability>,
* videoCapabilities: !Array.<MediaKeySystemMediaCapability>
* }}
*
* @property {string} sessionId
* The EME session ID.
* @property {string} keySystem
* The EME key system string the session belongs to.
* @property {string} licenseUri
* The URI for the license server.
* @property {Uint8Array} serverCertificate
* A key-system-specific server certificate used to encrypt license requests.
* Its use is optional and is meant as an optimization to avoid a round-trip
* to request a certificate.
* @property {!Array.<MediaKeySystemMediacapability>} audioCapabilities
* The EME audio capabilities used to create the session.
* @property {!Array.<MediaKeySystemMediacapability>} videoCapabilities
* The EME video capabilities used to create the session.
*/
shaka.extern.EmeSessionDB;
/**
* An interface that defines access to collection of segments and manifests. All
* methods are designed to be batched operations allowing the implementations to
* optimize their operations based on how they store data.
*
* The storage cell is one of two exposed APIs used to control where and how
* offline content is saved. The storage cell is responsible for converting
* information between its internal structures and the external (library)
* structures.
*
* @interface
*/
shaka.extern.StorageCell = class {
constructor() {}
/**
* Free all resources used by this cell. This should not affect the stored
* content.
*
* @return {!Promise}
*/
destroy() {}
/**
* Check if the cell can support new keys. If a cell has a fixed key space,
* then all add-operations will fail as no new keys can be added. All
* remove-operations and update-operations should still work.
*
* @return {boolean}
*/
hasFixedKeySpace() {}
/**
* Add a group of segments. Will return a promise that resolves with a list
* of keys for each segment. If one segment fails to be added, all segments
* should fail to be added.
*
* @param {!Array.<shaka.extern.SegmentDataDB>} segments
* @return {!Promise.<!Array.<number>>}
*/
addSegments(segments) {}
/**
* Remove a group of segments using their keys to identify them. If a key
* is not found, then that removal should be considered successful.
*
* @param {!Array.<number>} keys
* @param {function(number)} onRemove A callback for when a segment is removed
* from the cell. The key of the segment
* will be passed to the callback.
* @return {!Promise}
*/
removeSegments(keys, onRemove) {}
/**
* Get a group of segments using their keys to identify them. If any key is
* not found, the promise chain will be rejected.
*
* @param {!Array.<number>} keys
* @return {!Promise.<!Array.<shaka.extern.SegmentDataDB>>}
*/
getSegments(keys) {}
/**
* Add a group of manifests. Will return a promise that resolves with a list
* of keys for each manifest. If one manifest fails to be added, all manifests
* should fail to be added.
*
* @param {!Array.<shaka.extern.ManifestDB>} manifests
* @return {!Promise<!Array.<number>>} keys
*/
addManifests(manifests) {}
/**
* Updates the given manifest, stored at the given key.
*
* @param {number} key
* @param {!shaka.extern.ManifestDB} manifest
* @return {!Promise}
*/
updateManifest(key, manifest) {}
/**
* Replace the expiration time of the manifest stored under |key| with
* |newExpiration|. If no manifest is found under |key| then this should
* act as a no-op.
*
* @param {number} key
* @param {number} expiration
* @return {!Promise}
*/
updateManifestExpiration(key, expiration) {}
/**
* Remove a group of manifests using their keys to identify them. If a key
* is not found, then that removal should be considered successful.
*
* @param {!Array.<number>} keys
* @param {function(number)} onRemove A callback for when a manifest is
* removed from the cell. The key of the
* manifest will be passed to the callback.
* @return {!Promise}
*/
removeManifests(keys, onRemove) {}
/**
* Get a group of manifests using their keys to identify them. If any key is
* not found, the promise chain will be rejected.
*
* @param {!Array.<number>} keys
* @return {!Promise<!Array.<shaka.extern.ManifestDB>>}
*/
getManifests(keys) {}
/**
* Get all manifests stored in this cell. Since manifests are small compared
* to the asset they describe, it is assumed that it is feasible to have them
* all in main memory at one time.
*
* @return {!Promise<!Map.<number, shaka.extern.ManifestDB>>}
*/
getAllManifests() {}
};
/**
* Similar to storage cells (shaka.extern.StorageCell), an EmeSessionStorageCell
* stores data persistently. This only stores the license's session info, not
* the license itself. The license itself is stored using EME.
*
* @interface
*/
shaka.extern.EmeSessionStorageCell = class {
constructor() {}
/**
* Free all resources used by this cell. This won't affect the stored content.
* @return {!Promise}
*/
destroy() {}
/**
* Gets the currently stored sessions.
* @return {!Promise.<!Array.<shaka.extern.EmeSessionDB>>}
*/
getAll() {}
/**
* Adds the given sessions to the store.
* @param {!Array.<shaka.extern.EmeSessionDB>} sessions
* @return {!Promise}
*/
add(sessions) {}
/**
* Removes the given session IDs from the store.
* @param {!Array.<string>} sessionIds
* @return {!Promise}
*/
remove(sessionIds) {}
};
/**
* Storage mechanisms are one of two exported storage APIs. Storage mechanisms
* are groups of storage cells (shaka.extern.StorageCell). Storage mechanisms
* are responsible for managing the life cycle of resources shared between
* storage cells in the same block.
*
* For example, a storage mechanism may manage a single database connection
* while each cell would manage different tables in the database via the same
* connection.
*
* @interface
*/
shaka.extern.StorageMechanism = class {
constructor() {}
/**
* Initialize the storage mechanism for first use. This should only be called
* once. Calling |init| multiple times has an undefined behaviour.
*
* @return {!Promise}
*/
init() {}
/**
* Free all resources used by the storage mechanism and its cells. This should
* not affect the stored content.
*
* @return {!Promise}
*/
destroy() {}
/**
* Get a map of all the cells managed by the storage mechanism. Editing the
* map should have no effect on the storage mechanism. The map key is the
* cell's address in the mechanism and should be consistent between calls to
* |getCells|.
*
* @return {!Map.<string, !shaka.extern.StorageCell>}
*/
getCells() {}
/**
* Get the current EME session storage cell.
* @return {!shaka.extern.EmeSessionStorageCell}
*/
getEmeSessionCell() {}
/**
* Erase all content from storage and leave storage in an empty state. Erase
* may be called with or without |init|. This allows for storage to be wiped
* in case of a version mismatch.
*
* After calling |erase|, the mechanism will be in an initialized state.
*
* @return {!Promise}
*/
erase() {}
};