shaka-player 3.1.4 → 3.3.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/.github/ISSUE_TEMPLATE/bug_report.md +1 -1
- package/.github/ISSUE_TEMPLATE/feature_request.md +1 -1
- package/.github/ISSUE_TEMPLATE/question.md +1 -1
- package/AUTHORS +6 -5
- package/CHANGELOG.md +363 -0
- package/CONTRIBUTORS +4 -2
- package/README.md +4 -4
- package/build/types/core +3 -0
- package/build/types/offline +1 -0
- package/build/types/polyfill +2 -0
- package/build/types/ui +2 -0
- package/demo/common/asset.js +9 -7
- package/demo/common/assets.js +28 -1
- package/demo/common/message_ids.js +15 -2
- package/demo/config.js +35 -2
- package/demo/index.html +1 -1
- package/demo/locales/en.json +14 -1
- package/demo/locales/source.json +56 -4
- package/demo/main.js +2 -1
- package/dist/controls.css +26 -2
- package/dist/controls.css.map +1 -1
- package/dist/demo.compiled.debug.js +24 -21
- package/dist/demo.compiled.debug.map +3 -3
- package/dist/demo.compiled.js +24 -21
- package/dist/demo.compiled.map +3 -3
- package/dist/deps.js +33 -25
- package/dist/locales.js +31 -29
- package/dist/receiver.compiled.debug.js +14 -12
- package/dist/receiver.compiled.debug.map +3 -3
- package/dist/receiver.compiled.js +14 -12
- package/dist/receiver.compiled.map +3 -3
- package/dist/shaka-player.compiled.d.ts +258 -20
- package/dist/shaka-player.compiled.debug.d.ts +258 -20
- package/dist/shaka-player.compiled.debug.externs.js +1005 -695
- package/dist/shaka-player.compiled.debug.js +720 -640
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +1005 -695
- package/dist/shaka-player.compiled.js +850 -817
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +274 -21
- package/dist/shaka-player.ui.debug.d.ts +274 -21
- package/dist/shaka-player.ui.debug.externs.js +766 -429
- package/dist/shaka-player.ui.debug.js +863 -765
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +766 -429
- package/dist/shaka-player.ui.js +1042 -996
- package/dist/shaka-player.ui.map +4 -4
- package/docs/design/bg-fetch-after.gv +29 -0
- package/docs/design/bg-fetch-after.gv.png +0 -0
- package/docs/design/bg-fetch-before.gv +18 -0
- package/docs/design/bg-fetch-before.gv.png +0 -0
- package/docs/design/bg-fetch.md +134 -0
- package/docs/tutorials/blob-url.md +15 -0
- package/docs/tutorials/index.json +1 -0
- package/docs/tutorials/ui-customization.md +66 -0
- package/docs/tutorials/ui.md +5 -5
- package/externs/aria.js +6 -0
- package/externs/ima.js +3 -0
- package/externs/shaka/ads.js +29 -1
- package/externs/shaka/manifest.js +18 -2
- package/externs/shaka/manifest_parser.js +10 -1
- package/externs/shaka/net.js +20 -4
- package/externs/shaka/offline.js +36 -2
- package/externs/shaka/player.js +182 -13
- package/externs/shaka/text.js +24 -10
- package/externs/texttrack.js +3 -0
- package/externs/webcrypto.js +18 -0
- package/lib/abr/ewma.js +14 -0
- package/lib/abr/ewma_bandwidth_estimator.js +16 -2
- package/lib/abr/simple_abr_manager.js +6 -0
- package/lib/ads/ad_manager.js +16 -15
- package/lib/ads/client_side_ad.js +8 -0
- package/lib/ads/client_side_ad_manager.js +14 -6
- package/lib/ads/server_side_ad.js +7 -0
- package/lib/ads/server_side_ad_manager.js +19 -2
- package/lib/cast/cast_proxy.js +8 -0
- package/lib/cast/cast_utils.js +7 -1
- package/lib/cea/cea608_data_channel.js +56 -56
- package/lib/cea/cea608_memory.js +33 -33
- package/lib/cea/cea708_service.js +43 -43
- package/lib/cea/cea708_window.js +38 -38
- package/lib/cea/cea_decoder.js +13 -14
- package/lib/cea/cea_utils.js +23 -23
- package/lib/cea/dtvcc_packet_builder.js +7 -7
- package/lib/cea/i_caption_decoder.js +2 -3
- package/lib/cea/i_cea_parser.js +2 -2
- package/lib/cea/mp4_cea_parser.js +6 -6
- package/lib/dash/dash_parser.js +53 -14
- package/lib/dash/segment_base.js +25 -1
- package/lib/dash/segment_list.js +18 -9
- package/lib/dash/segment_template.js +23 -10
- package/lib/hls/hls_parser.js +210 -30
- package/lib/media/closed_caption_parser.js +1 -2
- package/lib/media/drm_engine.js +162 -192
- package/lib/media/quality_observer.js +291 -0
- package/lib/media/segment_index.js +9 -1
- package/lib/media/segment_reference.js +37 -5
- package/lib/media/streaming_engine.js +48 -10
- package/lib/net/http_fetch_plugin.js +31 -11
- package/lib/net/http_plugin_utils.js +1 -0
- package/lib/net/http_xhr_plugin.js +40 -14
- package/lib/net/networking_engine.js +82 -18
- package/lib/offline/download_info.js +68 -0
- package/lib/offline/download_manager.js +14 -7
- package/lib/offline/indexeddb/base_storage_cell.js +35 -0
- package/lib/offline/indexeddb/storage_mechanism.js +1 -1
- package/lib/offline/indexeddb/v1_storage_cell.js +1 -0
- package/lib/offline/indexeddb/v2_storage_cell.js +1 -0
- package/lib/offline/indexeddb/v5_storage_cell.js +5 -0
- package/lib/offline/manifest_converter.js +3 -1
- package/lib/offline/storage.js +404 -297
- package/lib/offline/stored_content_utils.js +2 -0
- package/lib/player.js +622 -153
- package/lib/polyfill/aria.js +82 -0
- package/lib/polyfill/random_uuid.js +52 -0
- package/lib/routing/walker.js +2 -3
- package/lib/text/cue.js +12 -0
- package/lib/text/srt_text_parser.js +4 -4
- package/lib/text/text_engine.js +2 -2
- package/lib/text/ttml_text_parser.js +25 -2
- package/lib/text/ui_text_displayer.js +2 -2
- package/lib/text/vtt_text_parser.js +2 -2
- package/lib/text/web_vtt_generator.js +55 -12
- package/lib/util/cmcd_manager.js +826 -0
- package/lib/util/error.js +12 -0
- package/lib/util/language_utils.js +12 -0
- package/lib/util/manifest_parser_utils.js +1 -0
- package/lib/util/mp4_box_parsers.js +20 -20
- package/lib/util/multi_map.js +8 -0
- package/lib/util/mutex.js +52 -0
- package/lib/util/periods.js +98 -64
- package/lib/util/player_configuration.js +31 -1
- package/lib/util/stream_utils.js +140 -85
- package/lib/util/xml_utils.js +31 -12
- package/package.json +1 -1
- package/roadmap.md +24 -22
- package/shaka-player.uncompiled.js +4 -0
- package/test/cast/cast_utils_unit.js +1 -0
- package/test/cea/cea608_memory_unit.js +1 -1
- package/test/cea/cea708_service_unit.js +8 -8
- package/test/cea/cea708_window_unit.js +6 -6
- package/test/cea/cea_decoder_unit.js +1 -1
- package/test/cea/dtvcc_packet_builder_unit.js +2 -2
- package/test/dash/dash_parser_content_protection_unit.js +2 -0
- package/test/dash/dash_parser_live_unit.js +101 -1
- package/test/dash/dash_parser_manifest_unit.js +78 -2
- package/test/dash/dash_parser_segment_base_unit.js +2 -0
- package/test/dash/dash_parser_segment_list_unit.js +2 -1
- package/test/dash/dash_parser_segment_template_unit.js +2 -1
- package/test/hls/hls_live_unit.js +2 -0
- package/test/hls/hls_parser_unit.js +252 -0
- package/test/media/drm_engine_integration.js +86 -12
- package/test/media/drm_engine_unit.js +310 -450
- package/test/media/quality_observer_unit.js +139 -0
- package/test/media/segment_reference_unit.js +16 -1
- package/test/media/streaming_engine_integration.js +2 -1
- package/test/media/streaming_engine_unit.js +2 -0
- package/test/net/http_plugin_unit.js +20 -15
- package/test/offline/manifest_convert_unit.js +2 -0
- package/test/offline/storage_integration.js +117 -23
- package/test/player_integration.js +151 -0
- package/test/player_src_equals_integration.js +52 -0
- package/test/player_unit.js +11 -33
- package/test/test/assets/chapters.srt +11 -0
- package/test/test/assets/chapters.vtt +10 -0
- package/test/test/util/cea_utils.js +12 -12
- package/test/test/util/dash_parser_util.js +4 -0
- package/test/test/util/fake_ad.js +7 -0
- package/test/test/util/fake_ad_manager.js +7 -0
- package/test/test/util/fake_drm_engine.js +9 -2
- package/test/test/util/manifest_generator.js +5 -2
- package/test/test/util/manifest_parser_util.js +3 -1
- package/test/test/util/test_scheme.js +14 -1
- package/test/text/ttml_text_parser_unit.js +47 -0
- package/test/text/web_vtt_generator_unit.js +81 -7
- package/test/ui/ui_unit.js +250 -0
- package/test/util/cmcd_manager_unit.js +277 -0
- package/test/util/stream_utils_unit.js +118 -10
- package/ui/airplay_button.js +2 -1
- package/ui/audio_language_selection.js +9 -5
- package/ui/cast_button.js +5 -6
- package/ui/constants.js +0 -3
- package/ui/context_menu.js +109 -0
- package/ui/controls.js +41 -7
- package/ui/controls.less +1 -0
- package/ui/enums.js +4 -0
- package/ui/externs/ui.js +28 -1
- package/ui/fast_forward_button.js +16 -8
- package/ui/fullscreen_button.js +2 -3
- package/ui/language_utils.js +1 -1
- package/ui/less/buttons.less +0 -4
- package/ui/less/containers.less +74 -0
- package/ui/less/overflow_menu.less +0 -10
- package/ui/less/tooltip.less +97 -0
- package/ui/locales/en.json +1 -0
- package/ui/locales/source.json +4 -0
- package/ui/loop_button.js +6 -3
- package/ui/mute_button.js +3 -4
- package/ui/overflow_menu.js +7 -28
- package/ui/pip_button.js +10 -7
- package/ui/play_button.js +2 -2
- package/ui/playback_rate_selection.js +11 -15
- package/ui/presentation_time.js +1 -3
- package/ui/resolution_selection.js +11 -7
- package/ui/rewind_button.js +17 -9
- package/ui/seek_bar.js +6 -6
- package/ui/settings_menu.js +36 -4
- package/ui/small_play_button.js +1 -0
- package/ui/spacer.js +1 -1
- package/ui/statistics_button.js +253 -0
- package/ui/text_selection.js +19 -15
- package/ui/ui.js +30 -0
- package/ui/ui_utils.js +1 -1
- package/ui/volume_bar.js +2 -4
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
/*! @license
|
|
2
|
+
* Shaka Player
|
|
3
|
+
* Copyright 2016 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
goog.provide('shaka.media.QualityObserver');
|
|
8
|
+
|
|
9
|
+
goog.require('shaka.media.IPlayheadObserver');
|
|
10
|
+
goog.require('shaka.log');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Monitors the quality of content being appended to the source
|
|
14
|
+
* buffers and fires onQualityChange events when the media quality
|
|
15
|
+
* at the playhead changes.
|
|
16
|
+
*
|
|
17
|
+
* @implements {shaka.media.IPlayheadObserver}
|
|
18
|
+
* @final
|
|
19
|
+
*/
|
|
20
|
+
shaka.media.QualityObserver = class {
|
|
21
|
+
/**
|
|
22
|
+
* Creates a new QualityObserver.
|
|
23
|
+
*
|
|
24
|
+
* @param {!function():!shaka.extern.BufferedInfo} getBufferedInfo
|
|
25
|
+
* Buffered info is needed to purge QualityChanges that are no
|
|
26
|
+
* longer relevant.
|
|
27
|
+
*/
|
|
28
|
+
constructor(getBufferedInfo) {
|
|
29
|
+
/**
|
|
30
|
+
* @private {!Map.<string,!shaka.media.QualityObserver.ContentTypeState>}
|
|
31
|
+
*/
|
|
32
|
+
this.contentTypeStates_ = new Map();
|
|
33
|
+
|
|
34
|
+
/** @private {shaka.media.QualityObserver.EventListener} */
|
|
35
|
+
this.onQualityChange_ = (mediaQuality, position) => {};
|
|
36
|
+
|
|
37
|
+
/** @private function():!shaka.extern.BufferedInfo */
|
|
38
|
+
this.getBufferedInfo_ = getBufferedInfo;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Set all the listeners. This overrides any previous calls to |setListeners|.
|
|
43
|
+
*
|
|
44
|
+
* @param {shaka.media.QualityObserver.EventListener} onQualityChange
|
|
45
|
+
*/
|
|
46
|
+
setListeners(onQualityChange) {
|
|
47
|
+
this.onQualityChange_ = onQualityChange;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** @override */
|
|
51
|
+
release() {
|
|
52
|
+
this.contentTypeStates_.clear();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Get the ContenTypeState for a contentType, creating a new
|
|
57
|
+
* one if necessary.
|
|
58
|
+
*
|
|
59
|
+
* @param {!string} contentType
|
|
60
|
+
* The contend type e.g. "video" or "audio".
|
|
61
|
+
* @return {!shaka.media.QualityObserver.ContentTypeState}
|
|
62
|
+
* @private
|
|
63
|
+
*/
|
|
64
|
+
getContentTypeState_(contentType) {
|
|
65
|
+
let contentTypeState = this.contentTypeStates_.get(contentType);
|
|
66
|
+
if (!contentTypeState) {
|
|
67
|
+
contentTypeState = {
|
|
68
|
+
qualityChangePositions: [],
|
|
69
|
+
currentQuality: null,
|
|
70
|
+
contentType: contentType,
|
|
71
|
+
};
|
|
72
|
+
this.contentTypeStates_.set(contentType, contentTypeState);
|
|
73
|
+
}
|
|
74
|
+
return contentTypeState;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Adds a QualityChangePosition for the contentType identified by
|
|
79
|
+
* the mediaQuality.contentType.
|
|
80
|
+
*
|
|
81
|
+
* @param {!shaka.extern.MediaQualityInfo} mediaQuality
|
|
82
|
+
* @param {!number} position
|
|
83
|
+
* Position in seconds of the quality change.
|
|
84
|
+
*/
|
|
85
|
+
addMediaQualityChange(mediaQuality, position) {
|
|
86
|
+
const contentTypeState =
|
|
87
|
+
this.getContentTypeState_(mediaQuality.contentType);
|
|
88
|
+
|
|
89
|
+
// Remove unneeded QualityChangePosition(s) before adding the new one
|
|
90
|
+
this.purgeQualityChangePositions_(contentTypeState);
|
|
91
|
+
|
|
92
|
+
const newChangePosition = {
|
|
93
|
+
mediaQuality: mediaQuality,
|
|
94
|
+
position: position,
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const changePositions = contentTypeState.qualityChangePositions;
|
|
98
|
+
const insertBeforeIndex = changePositions.findIndex(
|
|
99
|
+
(qualityChange) => (qualityChange.position >= position));
|
|
100
|
+
|
|
101
|
+
if (insertBeforeIndex >= 0) {
|
|
102
|
+
const duplicatePositions =
|
|
103
|
+
(changePositions[insertBeforeIndex].position == position) ? 1 : 0;
|
|
104
|
+
changePositions.splice(
|
|
105
|
+
insertBeforeIndex, duplicatePositions, newChangePosition);
|
|
106
|
+
} else {
|
|
107
|
+
changePositions.push(newChangePosition);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Determines the media quality at a specific position in the source buffer.
|
|
113
|
+
*
|
|
114
|
+
* @param {!number} position
|
|
115
|
+
* Position in seconds
|
|
116
|
+
* @param {!shaka.media.QualityObserver.ContentTypeState} contentTypeState
|
|
117
|
+
* @return {?shaka.extern.MediaQualityInfo}
|
|
118
|
+
* @private
|
|
119
|
+
*/
|
|
120
|
+
static getMediaQualityAtPosition_(position, contentTypeState) {
|
|
121
|
+
// The qualityChangePositions must be ordered by position ascending
|
|
122
|
+
// Find the last QualityChangePosition prior to the position
|
|
123
|
+
const changePositions = contentTypeState.qualityChangePositions;
|
|
124
|
+
for (let i = changePositions.length - 1; i >= 0; i--) {
|
|
125
|
+
const qualityChange = changePositions[i];
|
|
126
|
+
if (qualityChange.position <= position) {
|
|
127
|
+
return qualityChange.mediaQuality;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Determines if two MediaQualityInfo objects are the same or not.
|
|
135
|
+
*
|
|
136
|
+
* @param {?shaka.extern.MediaQualityInfo} mq1
|
|
137
|
+
* @param {?shaka.extern.MediaQualityInfo} mq2
|
|
138
|
+
* @return {boolean}
|
|
139
|
+
* @private
|
|
140
|
+
*/
|
|
141
|
+
static mediaQualitiesAreTheSame_(mq1, mq2) {
|
|
142
|
+
if (mq1 === mq2) {
|
|
143
|
+
return true;
|
|
144
|
+
}
|
|
145
|
+
if (!mq1 || !mq2) {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
return (mq1.bandwidth == mq2.bandwidth) &&
|
|
149
|
+
(mq1.audioSamplingRate == mq2.audioSamplingRate) &&
|
|
150
|
+
(mq1.codecs == mq2.codecs) &&
|
|
151
|
+
(mq1.contentType == mq2.contentType) &&
|
|
152
|
+
(mq1.frameRate == mq2.frameRate) &&
|
|
153
|
+
(mq1.height == mq2.height) &&
|
|
154
|
+
(mq1.mimeType == mq2.mimeType) &&
|
|
155
|
+
(mq1.channelsCount == mq2.channelsCount) &&
|
|
156
|
+
(mq1.pixelAspectRatio == mq2.pixelAspectRatio) &&
|
|
157
|
+
(mq1.width == mq2.width);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** @override */
|
|
161
|
+
poll(positionInSeconds, wasSeeking) {
|
|
162
|
+
for (const contentTypeState of this.contentTypeStates_.values()) {
|
|
163
|
+
const qualityAtPosition =
|
|
164
|
+
shaka.media.QualityObserver.getMediaQualityAtPosition_(
|
|
165
|
+
positionInSeconds, contentTypeState);
|
|
166
|
+
if (qualityAtPosition &&
|
|
167
|
+
!shaka.media.QualityObserver.mediaQualitiesAreTheSame_(
|
|
168
|
+
contentTypeState.currentQuality, qualityAtPosition)) {
|
|
169
|
+
if (this.positionIsBuffered_(
|
|
170
|
+
positionInSeconds, qualityAtPosition.contentType)) {
|
|
171
|
+
contentTypeState.currentQuality = qualityAtPosition;
|
|
172
|
+
shaka.log.debug('Media quality changed at position ' +
|
|
173
|
+
positionInSeconds + ' ' + JSON.stringify(qualityAtPosition));
|
|
174
|
+
this.onQualityChange_(
|
|
175
|
+
qualityAtPosition, positionInSeconds);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Determine if a position is buffered for a given content type.
|
|
183
|
+
*
|
|
184
|
+
* @param {!number} position
|
|
185
|
+
* @param {!string} contentType
|
|
186
|
+
* @private
|
|
187
|
+
*/
|
|
188
|
+
positionIsBuffered_(position, contentType) {
|
|
189
|
+
const bufferedInfo = this.getBufferedInfo_();
|
|
190
|
+
const bufferedRanges = bufferedInfo[contentType];
|
|
191
|
+
if (bufferedRanges && bufferedRanges.length > 0) {
|
|
192
|
+
const bufferStart = bufferedRanges[0].start;
|
|
193
|
+
const bufferEnd = bufferedRanges[bufferedRanges.length - 1].end;
|
|
194
|
+
if (position >= bufferStart && position < bufferEnd) {
|
|
195
|
+
return true;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Removes the QualityChangePosition(s) that are not relevant to the buffered
|
|
203
|
+
* content of the specified contentType. Note that this function is
|
|
204
|
+
* invoked just before adding the quality change info associated with
|
|
205
|
+
* the next media segment to be appended.
|
|
206
|
+
*
|
|
207
|
+
* @param {!shaka.media.QualityObserver.ContentTypeState} contentTypeState
|
|
208
|
+
* @private
|
|
209
|
+
*/
|
|
210
|
+
purgeQualityChangePositions_(contentTypeState) {
|
|
211
|
+
const bufferedInfo = this.getBufferedInfo_();
|
|
212
|
+
const bufferedRanges = bufferedInfo[contentTypeState.contentType];
|
|
213
|
+
|
|
214
|
+
if (bufferedRanges && bufferedRanges.length > 0) {
|
|
215
|
+
const bufferStart = bufferedRanges[0].start;
|
|
216
|
+
const bufferEnd = bufferedRanges[bufferedRanges.length - 1].end;
|
|
217
|
+
const oldChangePositions = contentTypeState.qualityChangePositions;
|
|
218
|
+
contentTypeState.qualityChangePositions =
|
|
219
|
+
oldChangePositions.filter(
|
|
220
|
+
(qualityChange, index) => {
|
|
221
|
+
// Remove all but last quality change before bufferStart.
|
|
222
|
+
if ((qualityChange.position <= bufferStart) &&
|
|
223
|
+
(index + 1 < oldChangePositions.length) &&
|
|
224
|
+
(oldChangePositions[index + 1].position <= bufferStart)) {
|
|
225
|
+
return false;
|
|
226
|
+
}
|
|
227
|
+
// Remove all quality changes after bufferEnd.
|
|
228
|
+
if (qualityChange.position >= bufferEnd) {
|
|
229
|
+
return false;
|
|
230
|
+
}
|
|
231
|
+
return true;
|
|
232
|
+
});
|
|
233
|
+
} else {
|
|
234
|
+
// Nothing is buffered; so remove all quality changes.
|
|
235
|
+
contentTypeState.qualityChangePositions = [];
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* @typedef {function(shaka.extern.MediaQualityInfo, number)}
|
|
242
|
+
*
|
|
243
|
+
* @description
|
|
244
|
+
* A callback function used to notify the player when media quality changes
|
|
245
|
+
* are detected at the playhead.
|
|
246
|
+
*
|
|
247
|
+
* The first argument is information about media quality at the playhead
|
|
248
|
+
* position.
|
|
249
|
+
*
|
|
250
|
+
* The second argument is the playhead position in seconds.
|
|
251
|
+
*/
|
|
252
|
+
shaka.media.QualityObserver.EventListener;
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* @typedef {{
|
|
256
|
+
* mediaQuality: !shaka.extern.MediaQualityInfo,
|
|
257
|
+
* position: !number
|
|
258
|
+
* }}
|
|
259
|
+
*
|
|
260
|
+
* @description
|
|
261
|
+
* Identifies the position of a media quality change in the
|
|
262
|
+
* source buffer.
|
|
263
|
+
*
|
|
264
|
+
* @property {shaka.extern.MediaQualityInfo} !mediaQuality
|
|
265
|
+
* The new media quality for content after position in the source buffer.
|
|
266
|
+
* @property {number} !position
|
|
267
|
+
* A position in seconds in the source buffer
|
|
268
|
+
*/
|
|
269
|
+
shaka.media.QualityObserver.QualityChangePosition;
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* @typedef {{
|
|
273
|
+
* qualityChangePositions:
|
|
274
|
+
* !Array.<shaka.media.QualityObserver.QualityChangePosition>,
|
|
275
|
+
* currentQuality: ?shaka.extern.MediaQualityInfo,
|
|
276
|
+
* contentType: !string
|
|
277
|
+
* }}
|
|
278
|
+
*
|
|
279
|
+
* @description
|
|
280
|
+
* Contains media quality information for a specific content type
|
|
281
|
+
* e.g video or audio.
|
|
282
|
+
*
|
|
283
|
+
* @property {!Array.<shaka.media.QualityObserver.QualityChangePosition>}
|
|
284
|
+
* qualityChangePositions
|
|
285
|
+
* Quality changes ordered by position ascending.
|
|
286
|
+
* @property {?shaka.media.MediaQualityInfo} currentMediaQuality
|
|
287
|
+
* The media quality at the playhead position.
|
|
288
|
+
* @property {string} contentType
|
|
289
|
+
* The contentType e.g. 'video' or 'audio'
|
|
290
|
+
*/
|
|
291
|
+
shaka.media.QualityObserver.ContentTypeState;
|
|
@@ -345,7 +345,9 @@ shaka.media.SegmentIndex = class {
|
|
|
345
345
|
lastReference.initSegmentReference,
|
|
346
346
|
lastReference.timestampOffset,
|
|
347
347
|
lastReference.appendWindowStart,
|
|
348
|
-
lastReference.appendWindowEnd
|
|
348
|
+
lastReference.appendWindowEnd,
|
|
349
|
+
lastReference.partialReferences,
|
|
350
|
+
lastReference.tilesLayout);
|
|
349
351
|
}
|
|
350
352
|
|
|
351
353
|
|
|
@@ -425,6 +427,12 @@ shaka.media.SegmentIndex = class {
|
|
|
425
427
|
return new shaka.media.SegmentIterator(this, index, partialSegmentIndex);
|
|
426
428
|
}
|
|
427
429
|
|
|
430
|
+
/**
|
|
431
|
+
* @return {boolean}
|
|
432
|
+
*/
|
|
433
|
+
isEmpty() {
|
|
434
|
+
return this.references.length == 0;
|
|
435
|
+
}
|
|
428
436
|
|
|
429
437
|
/**
|
|
430
438
|
* Create a SegmentIndex for a single segment of the given start time and
|
|
@@ -23,11 +23,13 @@ shaka.media.InitSegmentReference = class {
|
|
|
23
23
|
* of the resource containing the segment.
|
|
24
24
|
* @param {number} startByte The offset from the start of the resource to the
|
|
25
25
|
* start of the segment.
|
|
26
|
-
* @param {?number} endByte The offset from the start of the resource
|
|
27
|
-
* end of the segment, inclusive. A value of null indicates that the
|
|
26
|
+
* @param {?number} endByte The offset from the start of the resource
|
|
27
|
+
* to the end of the segment, inclusive. A value of null indicates that the
|
|
28
28
|
* segment extends to the end of the resource.
|
|
29
|
+
* @param {null|shaka.extern.MediaQualityInfo=} mediaQuality Information about
|
|
30
|
+
* the quality of the media associated with this init segment.
|
|
29
31
|
*/
|
|
30
|
-
constructor(uris, startByte, endByte) {
|
|
32
|
+
constructor(uris, startByte, endByte, mediaQuality = null) {
|
|
31
33
|
/** @type {function():!Array.<string>} */
|
|
32
34
|
this.getUris = uris;
|
|
33
35
|
|
|
@@ -36,6 +38,9 @@ shaka.media.InitSegmentReference = class {
|
|
|
36
38
|
|
|
37
39
|
/** @const {?number} */
|
|
38
40
|
this.endByte = endByte;
|
|
41
|
+
|
|
42
|
+
/** @const {shaka.extern.MediaQualityInfo|null} */
|
|
43
|
+
this.mediaQuality = mediaQuality;
|
|
39
44
|
}
|
|
40
45
|
|
|
41
46
|
/**
|
|
@@ -73,6 +78,16 @@ shaka.media.InitSegmentReference = class {
|
|
|
73
78
|
}
|
|
74
79
|
}
|
|
75
80
|
|
|
81
|
+
/**
|
|
82
|
+
* Returns media quality information for the segments associated with
|
|
83
|
+
* this init segment.
|
|
84
|
+
*
|
|
85
|
+
* @return {?shaka.extern.MediaQualityInfo}
|
|
86
|
+
*/
|
|
87
|
+
getMediaQuality() {
|
|
88
|
+
return this.mediaQuality;
|
|
89
|
+
}
|
|
90
|
+
|
|
76
91
|
/**
|
|
77
92
|
* Check if two initSegmentReference have all the same values.
|
|
78
93
|
* @param {?shaka.media.InitSegmentReference} reference1
|
|
@@ -135,12 +150,16 @@ shaka.media.SegmentReference = class {
|
|
|
135
150
|
* presentation. Any content from after this time will be removed by
|
|
136
151
|
* MediaSource.
|
|
137
152
|
* @param {!Array.<!shaka.media.SegmentReference>=} partialReferences
|
|
138
|
-
|
|
153
|
+
* A list of SegmentReferences for the partial segments.
|
|
154
|
+
* @param {?string=} tilesLayout
|
|
155
|
+
* The value is a grid-item-dimension consisting of two positive decimal
|
|
156
|
+
* integers in the format: column-x-row ('4x3'). It describes the
|
|
157
|
+
* arrangement of Images in a Grid. The minimum valid LAYOUT is '1x1'.
|
|
139
158
|
*/
|
|
140
159
|
constructor(
|
|
141
160
|
startTime, endTime, uris, startByte, endByte, initSegmentReference,
|
|
142
161
|
timestampOffset, appendWindowStart, appendWindowEnd,
|
|
143
|
-
partialReferences = []) {
|
|
162
|
+
partialReferences = [], tilesLayout = '') {
|
|
144
163
|
// A preload hinted Partial Segment has the same startTime and endTime.
|
|
145
164
|
goog.asserts.assert(startTime <= endTime,
|
|
146
165
|
'startTime must be less than or equal to endTime');
|
|
@@ -184,6 +203,9 @@ shaka.media.SegmentReference = class {
|
|
|
184
203
|
|
|
185
204
|
/** @type {!Array.<!shaka.media.SegmentReference>} */
|
|
186
205
|
this.partialReferences = partialReferences;
|
|
206
|
+
|
|
207
|
+
/** @type {?string} */
|
|
208
|
+
this.tilesLayout = tilesLayout;
|
|
187
209
|
}
|
|
188
210
|
|
|
189
211
|
/**
|
|
@@ -258,6 +280,16 @@ shaka.media.SegmentReference = class {
|
|
|
258
280
|
hasPartialSegments() {
|
|
259
281
|
return this.partialReferences.length > 0;
|
|
260
282
|
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Returns the segment's tiles layout. Only defined in image segments.
|
|
286
|
+
*
|
|
287
|
+
* @return {?string}
|
|
288
|
+
* @export
|
|
289
|
+
*/
|
|
290
|
+
getTilesLayout() {
|
|
291
|
+
return this.tilesLayout;
|
|
292
|
+
}
|
|
261
293
|
};
|
|
262
294
|
|
|
263
295
|
|
|
@@ -431,6 +431,11 @@ shaka.media.StreamingEngine = class {
|
|
|
431
431
|
this.playerInterface_.mediaSourceEngine.reinitText(fullMimeType);
|
|
432
432
|
}
|
|
433
433
|
|
|
434
|
+
// Releases the segmentIndex of the old stream.
|
|
435
|
+
if (mediaState.stream.closeSegmentIndex) {
|
|
436
|
+
mediaState.stream.closeSegmentIndex();
|
|
437
|
+
}
|
|
438
|
+
|
|
434
439
|
mediaState.stream = stream;
|
|
435
440
|
mediaState.segmentIterator = null;
|
|
436
441
|
|
|
@@ -863,7 +868,7 @@ shaka.media.StreamingEngine = class {
|
|
|
863
868
|
return;
|
|
864
869
|
}
|
|
865
870
|
|
|
866
|
-
// Make sure the segment index exists.
|
|
871
|
+
// Make sure the segment index exists. If not, create the segment index.
|
|
867
872
|
if (!mediaState.stream.segmentIndex) {
|
|
868
873
|
const thisStream = mediaState.stream;
|
|
869
874
|
|
|
@@ -873,6 +878,12 @@ shaka.media.StreamingEngine = class {
|
|
|
873
878
|
// We switched streams while in the middle of this async call to
|
|
874
879
|
// createSegmentIndex. Abandon this update and schedule a new one if
|
|
875
880
|
// there's not already one pending.
|
|
881
|
+
// Releases the segmentIndex of the old stream.
|
|
882
|
+
if (thisStream.closeSegmentIndex) {
|
|
883
|
+
goog.asserts.assert(!mediaState.stream.segmentIndex,
|
|
884
|
+
'mediastate.stream should not have segmentIndex yet.');
|
|
885
|
+
thisStream.closeSegmentIndex();
|
|
886
|
+
}
|
|
876
887
|
if (mediaState.updateTimer == null) {
|
|
877
888
|
this.scheduleUpdate_(mediaState, 0);
|
|
878
889
|
}
|
|
@@ -1001,10 +1012,10 @@ shaka.media.StreamingEngine = class {
|
|
|
1001
1012
|
if (bufferedAhead >= scaledBufferingGoal) {
|
|
1002
1013
|
shaka.log.v2(logPrefix, 'buffering goal met');
|
|
1003
1014
|
|
|
1004
|
-
// Do not try to predict the next update. Just poll
|
|
1005
|
-
// The playback rate can change at any time, so
|
|
1006
|
-
// could be terribly invalid soon.
|
|
1007
|
-
return
|
|
1015
|
+
// Do not try to predict the next update. Just poll according to
|
|
1016
|
+
// configuration (seconds). The playback rate can change at any time, so
|
|
1017
|
+
// any prediction we make now could be terribly invalid soon.
|
|
1018
|
+
return this.config_.updateIntervalSeconds / 2;
|
|
1008
1019
|
}
|
|
1009
1020
|
|
|
1010
1021
|
const bufferEnd =
|
|
@@ -1016,7 +1027,7 @@ shaka.media.StreamingEngine = class {
|
|
|
1016
1027
|
// In any case just try again... if the manifest is incomplete or is not
|
|
1017
1028
|
// being updated then we'll idle forever; otherwise, we'll end up getting
|
|
1018
1029
|
// a SegmentReference eventually.
|
|
1019
|
-
return
|
|
1030
|
+
return this.config_.updateIntervalSeconds;
|
|
1020
1031
|
}
|
|
1021
1032
|
|
|
1022
1033
|
// Do not let any one stream get far ahead of any other.
|
|
@@ -1047,7 +1058,7 @@ shaka.media.StreamingEngine = class {
|
|
|
1047
1058
|
// For example, let video buffering catch up to audio buffering before
|
|
1048
1059
|
// fetching another audio segment.
|
|
1049
1060
|
shaka.log.v2(logPrefix, 'waiting for other streams to buffer');
|
|
1050
|
-
return
|
|
1061
|
+
return this.config_.updateIntervalSeconds;
|
|
1051
1062
|
}
|
|
1052
1063
|
|
|
1053
1064
|
const p = this.fetchAndAppend_(mediaState, presentationTime, reference);
|
|
@@ -1530,6 +1541,8 @@ shaka.media.StreamingEngine = class {
|
|
|
1530
1541
|
throw error;
|
|
1531
1542
|
}
|
|
1532
1543
|
};
|
|
1544
|
+
this.playerInterface_.onInitSegmentAppended(
|
|
1545
|
+
reference.startTime, reference.initSegmentReference);
|
|
1533
1546
|
operations.push(append());
|
|
1534
1547
|
}
|
|
1535
1548
|
}
|
|
@@ -1555,7 +1568,9 @@ shaka.media.StreamingEngine = class {
|
|
|
1555
1568
|
|
|
1556
1569
|
const hasClosedCaptions = stream.closedCaptions &&
|
|
1557
1570
|
stream.closedCaptions.size > 0;
|
|
1558
|
-
if (stream.emsgSchemeIdUris != null &&
|
|
1571
|
+
if ((stream.emsgSchemeIdUris != null &&
|
|
1572
|
+
stream.emsgSchemeIdUris.length > 0) ||
|
|
1573
|
+
this.config_.dispatchAllEmsgBoxes) {
|
|
1559
1574
|
new shaka.util.Mp4Parser()
|
|
1560
1575
|
.fullBox(
|
|
1561
1576
|
'emsg',
|
|
@@ -1641,7 +1656,8 @@ shaka.media.StreamingEngine = class {
|
|
|
1641
1656
|
// See DASH sec. 5.10.3.3.1
|
|
1642
1657
|
// If a DASH client detects an event message box with a scheme that is not
|
|
1643
1658
|
// defined in MPD, the client is expected to ignore it.
|
|
1644
|
-
if (emsgSchemeIdUris.includes(schemeId)
|
|
1659
|
+
if (emsgSchemeIdUris.includes(schemeId) ||
|
|
1660
|
+
this.config_.dispatchAllEmsgBoxes) {
|
|
1645
1661
|
// See DASH sec. 5.10.4.1
|
|
1646
1662
|
// A special scheme in DASH used to signal manifest updates.
|
|
1647
1663
|
if (schemeId == 'urn:mpeg:dash:event:2012') {
|
|
@@ -1764,6 +1780,19 @@ shaka.media.StreamingEngine = class {
|
|
|
1764
1780
|
|
|
1765
1781
|
shaka.log.v2('fetching: reference=', reference);
|
|
1766
1782
|
|
|
1783
|
+
const stream = mediaState.stream;
|
|
1784
|
+
this.playerInterface_.modifySegmentRequest(
|
|
1785
|
+
request,
|
|
1786
|
+
{
|
|
1787
|
+
type: stream.type,
|
|
1788
|
+
init: reference instanceof shaka.media.InitSegmentReference,
|
|
1789
|
+
duration: reference.endTime - reference.startTime,
|
|
1790
|
+
mimeType: stream.mimeType,
|
|
1791
|
+
codecs: stream.codecs,
|
|
1792
|
+
bandwidth: stream.bandwidth,
|
|
1793
|
+
},
|
|
1794
|
+
);
|
|
1795
|
+
|
|
1767
1796
|
const op = this.playerInterface_.netEngine.request(requestType, request);
|
|
1768
1797
|
mediaState.operation = op;
|
|
1769
1798
|
const response = await op.promise;
|
|
@@ -1927,12 +1956,15 @@ shaka.media.StreamingEngine = class {
|
|
|
1927
1956
|
* @typedef {{
|
|
1928
1957
|
* getPresentationTime: function():number,
|
|
1929
1958
|
* getBandwidthEstimate: function():number,
|
|
1959
|
+
* modifySegmentRequest: function(shaka.extern.Request,
|
|
1960
|
+
* shaka.util.CmcdManager.SegmentInfo),
|
|
1930
1961
|
* mediaSourceEngine: !shaka.media.MediaSourceEngine,
|
|
1931
1962
|
* netEngine: shaka.net.NetworkingEngine,
|
|
1932
1963
|
* onError: function(!shaka.util.Error),
|
|
1933
1964
|
* onEvent: function(!Event),
|
|
1934
1965
|
* onManifestUpdate: function(),
|
|
1935
|
-
* onSegmentAppended: function()
|
|
1966
|
+
* onSegmentAppended: function(),
|
|
1967
|
+
* onInitSegmentAppended: function(!number,!shaka.media.InitSegmentReference)
|
|
1936
1968
|
* }}
|
|
1937
1969
|
*
|
|
1938
1970
|
* @property {function():number} getPresentationTime
|
|
@@ -1940,6 +1972,9 @@ shaka.media.StreamingEngine = class {
|
|
|
1940
1972
|
* viewer is seeing on screen right now.
|
|
1941
1973
|
* @property {function():number} getBandwidthEstimate
|
|
1942
1974
|
* Get the estimated bandwidth in bits per second.
|
|
1975
|
+
* @property {function(shaka.extern.Request,
|
|
1976
|
+
* shaka.extern.Cmcd.SegmentInfo)} modifySegmentRequest
|
|
1977
|
+
* The request modifier
|
|
1943
1978
|
* @property {!shaka.media.MediaSourceEngine} mediaSourceEngine
|
|
1944
1979
|
* The MediaSourceEngine. The caller retains ownership.
|
|
1945
1980
|
* @property {shaka.net.NetworkingEngine} netEngine
|
|
@@ -1954,6 +1989,9 @@ shaka.media.StreamingEngine = class {
|
|
|
1954
1989
|
* Called when an embedded 'emsg' box should trigger a manifest update.
|
|
1955
1990
|
* @property {function()} onSegmentAppended
|
|
1956
1991
|
* Called after a segment is successfully appended to a MediaSource.
|
|
1992
|
+
* @property
|
|
1993
|
+
* {function(!number, !shaka.media.InitSegmentReference)} onInitSegmentAppended
|
|
1994
|
+
* Called when an init segment is appended to a MediaSource.
|
|
1957
1995
|
*/
|
|
1958
1996
|
shaka.media.StreamingEngine.PlayerInterface;
|
|
1959
1997
|
|
|
@@ -27,10 +27,12 @@ shaka.net.HttpFetchPlugin = class {
|
|
|
27
27
|
* @param {shaka.net.NetworkingEngine.RequestType} requestType
|
|
28
28
|
* @param {shaka.extern.ProgressUpdated} progressUpdated Called when a
|
|
29
29
|
* progress event happened.
|
|
30
|
+
* @param {shaka.extern.HeadersReceived} headersReceived Called when the
|
|
31
|
+
* headers for the download are received, but before the body is.
|
|
30
32
|
* @return {!shaka.extern.IAbortableOperation.<shaka.extern.Response>}
|
|
31
33
|
* @export
|
|
32
34
|
*/
|
|
33
|
-
static parse(uri, request, requestType, progressUpdated) {
|
|
35
|
+
static parse(uri, request, requestType, progressUpdated, headersReceived) {
|
|
34
36
|
const headers = new shaka.net.HttpFetchPlugin.Headers_();
|
|
35
37
|
shaka.util.MapUtils.asMap(request.headers).forEach((value, key) => {
|
|
36
38
|
headers.append(key, value);
|
|
@@ -55,7 +57,7 @@ shaka.net.HttpFetchPlugin = class {
|
|
|
55
57
|
};
|
|
56
58
|
|
|
57
59
|
const pendingRequest = shaka.net.HttpFetchPlugin.request_(
|
|
58
|
-
uri, requestType, init, abortStatus, progressUpdated,
|
|
60
|
+
uri, requestType, init, abortStatus, progressUpdated, headersReceived,
|
|
59
61
|
request.streamDataCallback);
|
|
60
62
|
|
|
61
63
|
/** @type {!shaka.util.AbortableOperation} */
|
|
@@ -92,12 +94,13 @@ shaka.net.HttpFetchPlugin = class {
|
|
|
92
94
|
* @param {!RequestInit} init
|
|
93
95
|
* @param {shaka.net.HttpFetchPlugin.AbortStatus} abortStatus
|
|
94
96
|
* @param {shaka.extern.ProgressUpdated} progressUpdated
|
|
97
|
+
* @param {shaka.extern.HeadersReceived} headersReceived
|
|
95
98
|
* @param {?function(BufferSource):!Promise} streamDataCallback
|
|
96
99
|
* @return {!Promise<!shaka.extern.Response>}
|
|
97
100
|
* @private
|
|
98
101
|
*/
|
|
99
102
|
static async request_(uri, requestType, init, abortStatus, progressUpdated,
|
|
100
|
-
streamDataCallback) {
|
|
103
|
+
headersReceived, streamDataCallback) {
|
|
101
104
|
const fetch = shaka.net.HttpFetchPlugin.fetch_;
|
|
102
105
|
const ReadableStream = shaka.net.HttpFetchPlugin.ReadableStream_;
|
|
103
106
|
let response;
|
|
@@ -113,6 +116,10 @@ shaka.net.HttpFetchPlugin = class {
|
|
|
113
116
|
// headers are available. The download itself isn't done until the promise
|
|
114
117
|
// for retrieving the data (arrayBuffer, blob, etc) has resolved.
|
|
115
118
|
response = await fetch(uri, init);
|
|
119
|
+
// At this point in the process, we have the headers of the response, but
|
|
120
|
+
// not the body yet.
|
|
121
|
+
headersReceived(shaka.net.HttpFetchPlugin.headersToGenericObject_(
|
|
122
|
+
response.headers));
|
|
116
123
|
// Getting the reader in this way allows us to observe the process of
|
|
117
124
|
// downloading the body, instead of just waiting for an opaque promise to
|
|
118
125
|
// resolve.
|
|
@@ -194,19 +201,28 @@ shaka.net.HttpFetchPlugin = class {
|
|
|
194
201
|
}
|
|
195
202
|
}
|
|
196
203
|
|
|
197
|
-
const headers =
|
|
198
|
-
|
|
199
|
-
const responseHeaders = response.headers;
|
|
200
|
-
responseHeaders.forEach((value, key) => {
|
|
201
|
-
// Since Edge incorrectly return the header with a leading new line
|
|
202
|
-
// character ('\n'), we trim the header here.
|
|
203
|
-
headers[key.trim()] = value;
|
|
204
|
-
});
|
|
204
|
+
const headers = shaka.net.HttpFetchPlugin.headersToGenericObject_(
|
|
205
|
+
response.headers);
|
|
205
206
|
|
|
206
207
|
return shaka.net.HttpPluginUtils.makeResponse(
|
|
207
208
|
headers, arrayBuffer, response.status, uri, response.url, requestType);
|
|
208
209
|
}
|
|
209
210
|
|
|
211
|
+
/**
|
|
212
|
+
* @param {!Headers} headers
|
|
213
|
+
* @return {!Object.<string, string>}
|
|
214
|
+
* @private
|
|
215
|
+
*/
|
|
216
|
+
static headersToGenericObject_(headers) {
|
|
217
|
+
const headersObj = {};
|
|
218
|
+
headers.forEach((value, key) => {
|
|
219
|
+
// Since Edge incorrectly return the header with a leading new line
|
|
220
|
+
// character ('\n'), we trim the header here.
|
|
221
|
+
headersObj[key.trim()] = value;
|
|
222
|
+
});
|
|
223
|
+
return headersObj;
|
|
224
|
+
}
|
|
225
|
+
|
|
210
226
|
/**
|
|
211
227
|
* Determine if the Fetch API is supported in the browser. Note: this is
|
|
212
228
|
* deliberately exposed as a method to allow the client app to use the same
|
|
@@ -290,4 +306,8 @@ if (shaka.net.HttpFetchPlugin.isSupported()) {
|
|
|
290
306
|
'https', shaka.net.HttpFetchPlugin.parse,
|
|
291
307
|
shaka.net.NetworkingEngine.PluginPriority.PREFERRED,
|
|
292
308
|
/* progressSupport= */ true);
|
|
309
|
+
shaka.net.NetworkingEngine.registerScheme(
|
|
310
|
+
'blob', shaka.net.HttpFetchPlugin.parse,
|
|
311
|
+
shaka.net.NetworkingEngine.PluginPriority.PREFERRED,
|
|
312
|
+
/* progressSupport= */ true);
|
|
293
313
|
}
|