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