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