aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/assets/player/shared/player-options-builder/hls-options-builder.ts
diff options
context:
space:
mode:
Diffstat (limited to 'client/src/assets/player/shared/player-options-builder/hls-options-builder.ts')
-rw-r--r--client/src/assets/player/shared/player-options-builder/hls-options-builder.ts227
1 files changed, 227 insertions, 0 deletions
diff --git a/client/src/assets/player/shared/player-options-builder/hls-options-builder.ts b/client/src/assets/player/shared/player-options-builder/hls-options-builder.ts
new file mode 100644
index 000000000..10df2db5d
--- /dev/null
+++ b/client/src/assets/player/shared/player-options-builder/hls-options-builder.ts
@@ -0,0 +1,227 @@
1import { HybridLoaderSettings } from '@peertube/p2p-media-loader-core'
2import { HlsJsEngineSettings } from '@peertube/p2p-media-loader-hlsjs'
3import { logger } from '@root-helpers/logger'
4import { LiveVideoLatencyMode } from '@shared/models'
5import { getAverageBandwidthInStore } from '../../peertube-player-local-storage'
6import { P2PMediaLoader, P2PMediaLoaderPluginOptions, PeerTubePlayerContructorOptions, PeerTubePlayerLoadOptions } from '../../types'
7import { getRtcConfig, isSameOrigin } from '../common'
8import { RedundancyUrlManager } from '../p2p-media-loader/redundancy-url-manager'
9import { segmentUrlBuilderFactory } from '../p2p-media-loader/segment-url-builder'
10import { SegmentValidator } from '../p2p-media-loader/segment-validator'
11
12type ConstructorOptions =
13 Pick<PeerTubePlayerContructorOptions, 'pluginsManager' | 'serverUrl' | 'authorizationHeader'> &
14 Pick<PeerTubePlayerLoadOptions, 'videoPassword' | 'requiresUserAuth' | 'videoFileToken' | 'requiresPassword' |
15 'isLive' | 'liveOptions' | 'p2pEnabled' | 'hls'>
16
17export class HLSOptionsBuilder {
18
19 constructor (
20 private options: ConstructorOptions,
21 private p2pMediaLoaderModule?: any
22 ) {
23
24 }
25
26 async getPluginOptions () {
27 const redundancyUrlManager = new RedundancyUrlManager(this.options.hls.redundancyBaseUrls)
28 const segmentValidator = new SegmentValidator({
29 segmentsSha256Url: this.options.hls.segmentsSha256Url,
30 authorizationHeader: this.options.authorizationHeader,
31 requiresUserAuth: this.options.requiresUserAuth,
32 serverUrl: this.options.serverUrl,
33 requiresPassword: this.options.requiresPassword,
34 videoPassword: this.options.videoPassword
35 })
36
37 const p2pMediaLoaderConfig = await this.options.pluginsManager.runHook(
38 'filter:internal.player.p2p-media-loader.options.result',
39 this.getP2PMediaLoaderOptions({ redundancyUrlManager, segmentValidator })
40 )
41 const loader = new this.p2pMediaLoaderModule.Engine(p2pMediaLoaderConfig).createLoaderClass() as P2PMediaLoader
42
43 const p2pMediaLoader: P2PMediaLoaderPluginOptions = {
44 requiresUserAuth: this.options.requiresUserAuth,
45 videoFileToken: this.options.videoFileToken,
46
47 redundancyUrlManager,
48 type: 'application/x-mpegURL',
49 src: this.options.hls.playlistUrl,
50 segmentValidator,
51 loader
52 }
53
54 const hlsjs = {
55 hlsjsConfig: this.getHLSJSOptions(loader),
56
57 levelLabelHandler: (level: { height: number, width: number }) => {
58 const resolution = Math.min(level.height || 0, level.width || 0)
59
60 const file = this.options.hls.videoFiles.find(f => f.resolution.id === resolution)
61 // We don't have files for live videos
62 if (!file) return level.height
63
64 let label = file.resolution.label
65 if (file.fps >= 50) label += file.fps
66
67 return label
68 }
69 }
70
71 return { p2pMediaLoader, hlsjs }
72 }
73
74 // ---------------------------------------------------------------------------
75
76 private getP2PMediaLoaderOptions (options: {
77 redundancyUrlManager: RedundancyUrlManager
78 segmentValidator: SegmentValidator
79 }): HlsJsEngineSettings {
80 const { redundancyUrlManager, segmentValidator } = options
81
82 let consumeOnly = false
83 if ((navigator as any)?.connection?.type === 'cellular') {
84 logger.info('We are on a cellular connection: disabling seeding.')
85 consumeOnly = true
86 }
87
88 const trackerAnnounce = this.options.hls.trackerAnnounce
89 .filter(t => t.startsWith('ws'))
90
91 const specificLiveOrVODOptions = this.options.isLive
92 ? this.getP2PMediaLoaderLiveOptions()
93 : this.getP2PMediaLoaderVODOptions()
94
95 return {
96 loader: {
97 trackerAnnounce,
98 rtcConfig: getRtcConfig(),
99
100 simultaneousHttpDownloads: 1,
101 httpFailedSegmentTimeout: 1000,
102
103 xhrSetup: (xhr, url) => {
104 const { requiresUserAuth, requiresPassword } = this.options
105
106 if (!(requiresUserAuth || requiresPassword)) return
107
108 if (!isSameOrigin(this.options.serverUrl, url)) return
109
110 if (requiresPassword) xhr.setRequestHeader('x-peertube-video-password', this.options.videoPassword())
111
112 else xhr.setRequestHeader('Authorization', this.options.authorizationHeader())
113 },
114
115 segmentValidator: segmentValidator.validate.bind(segmentValidator),
116
117 segmentUrlBuilder: segmentUrlBuilderFactory(redundancyUrlManager),
118
119 useP2P: this.options.p2pEnabled,
120 consumeOnly,
121
122 ...specificLiveOrVODOptions
123 },
124 segments: {
125 swarmId: this.options.hls.playlistUrl,
126 forwardSegmentCount: specificLiveOrVODOptions.p2pDownloadMaxPriority ?? 20
127 }
128 }
129 }
130
131 private getP2PMediaLoaderLiveOptions (): Partial<HybridLoaderSettings> {
132 const base = {
133 requiredSegmentsPriority: 1
134 }
135
136 const latencyMode = this.options.liveOptions.latencyMode
137
138 switch (latencyMode) {
139 case LiveVideoLatencyMode.SMALL_LATENCY:
140 return {
141 ...base,
142
143 useP2P: false,
144 requiredSegmentsPriority: 10
145 }
146
147 case LiveVideoLatencyMode.HIGH_LATENCY:
148 return base
149
150 default:
151 return base
152 }
153 }
154
155 private getP2PMediaLoaderVODOptions (): Partial<HybridLoaderSettings> {
156 return {
157 requiredSegmentsPriority: 3,
158 skipSegmentBuilderPriority: 1,
159
160 cachedSegmentExpiration: 86400000,
161 cachedSegmentsCount: 100,
162
163 httpDownloadMaxPriority: 9,
164 httpDownloadProbability: 0.06,
165 httpDownloadProbabilitySkipIfNoPeers: true,
166
167 p2pDownloadMaxPriority: 50
168 }
169 }
170
171 // ---------------------------------------------------------------------------
172
173 private getHLSJSOptions (loader: P2PMediaLoader) {
174 const specificLiveOrVODOptions = this.options.isLive
175 ? this.getHLSLiveOptions()
176 : this.getHLSVODOptions()
177
178 const base = {
179 capLevelToPlayerSize: true,
180 autoStartLoad: false,
181
182 loader,
183
184 ...specificLiveOrVODOptions
185 }
186
187 const averageBandwidth = getAverageBandwidthInStore()
188 if (!averageBandwidth) return base
189
190 return {
191 ...base,
192
193 abrEwmaDefaultEstimate: averageBandwidth * 8, // We want bit/s
194 backBufferLength: 90,
195 startLevel: -1,
196 testBandwidth: false,
197 debug: false
198 }
199 }
200
201 private getHLSLiveOptions () {
202 const latencyMode = this.options.liveOptions.latencyMode
203
204 switch (latencyMode) {
205 case LiveVideoLatencyMode.SMALL_LATENCY:
206 return {
207 liveSyncDurationCount: 2
208 }
209
210 case LiveVideoLatencyMode.HIGH_LATENCY:
211 return {
212 liveSyncDurationCount: 10
213 }
214
215 default:
216 return {
217 liveSyncDurationCount: 5
218 }
219 }
220 }
221
222 private getHLSVODOptions () {
223 return {
224 liveSyncDurationCount: 5
225 }
226 }
227}