diff options
Diffstat (limited to 'client/src/assets/player/p2p-media-loader/p2p-media-loader-plugin.ts')
-rw-r--r-- | client/src/assets/player/p2p-media-loader/p2p-media-loader-plugin.ts | 135 |
1 files changed, 135 insertions, 0 deletions
diff --git a/client/src/assets/player/p2p-media-loader/p2p-media-loader-plugin.ts b/client/src/assets/player/p2p-media-loader/p2p-media-loader-plugin.ts new file mode 100644 index 000000000..f9a2707fb --- /dev/null +++ b/client/src/assets/player/p2p-media-loader/p2p-media-loader-plugin.ts | |||
@@ -0,0 +1,135 @@ | |||
1 | // FIXME: something weird with our path definition in tsconfig and typings | ||
2 | // @ts-ignore | ||
3 | import * as videojs from 'video.js' | ||
4 | import { P2PMediaLoaderPluginOptions, PlayerNetworkInfo, VideoJSComponentInterface } from '../peertube-videojs-typings' | ||
5 | import { Engine, initHlsJsPlayer, initVideoJsContribHlsJsPlayer } from 'p2p-media-loader-hlsjs' | ||
6 | import { Events } from 'p2p-media-loader-core' | ||
7 | |||
8 | // videojs-hlsjs-plugin needs videojs in window | ||
9 | window['videojs'] = videojs | ||
10 | require('@streamroot/videojs-hlsjs-plugin') | ||
11 | |||
12 | const Plugin: VideoJSComponentInterface = videojs.getPlugin('plugin') | ||
13 | class P2pMediaLoaderPlugin extends Plugin { | ||
14 | |||
15 | private readonly CONSTANTS = { | ||
16 | INFO_SCHEDULER: 1000 // Don't change this | ||
17 | } | ||
18 | private readonly options: P2PMediaLoaderPluginOptions | ||
19 | |||
20 | private hlsjs: any // Don't type hlsjs to not bundle the module | ||
21 | private p2pEngine: Engine | ||
22 | private statsP2PBytes = { | ||
23 | pendingDownload: [] as number[], | ||
24 | pendingUpload: [] as number[], | ||
25 | numPeers: 0, | ||
26 | totalDownload: 0, | ||
27 | totalUpload: 0 | ||
28 | } | ||
29 | private statsHTTPBytes = { | ||
30 | pendingDownload: [] as number[], | ||
31 | pendingUpload: [] as number[], | ||
32 | totalDownload: 0, | ||
33 | totalUpload: 0 | ||
34 | } | ||
35 | |||
36 | private networkInfoInterval: any | ||
37 | |||
38 | constructor (player: videojs.Player, options: P2PMediaLoaderPluginOptions) { | ||
39 | super(player, options) | ||
40 | |||
41 | this.options = options | ||
42 | |||
43 | videojs.Html5Hlsjs.addHook('beforeinitialize', (videojsPlayer: any, hlsjs: any) => { | ||
44 | this.hlsjs = hlsjs | ||
45 | }) | ||
46 | |||
47 | initVideoJsContribHlsJsPlayer(player) | ||
48 | |||
49 | player.src({ | ||
50 | type: options.type, | ||
51 | src: options.src | ||
52 | }) | ||
53 | |||
54 | player.ready(() => this.initialize()) | ||
55 | } | ||
56 | |||
57 | dispose () { | ||
58 | clearInterval(this.networkInfoInterval) | ||
59 | } | ||
60 | |||
61 | private initialize () { | ||
62 | initHlsJsPlayer(this.hlsjs) | ||
63 | |||
64 | this.p2pEngine = this.player.tech_.options_.hlsjsConfig.loader.getEngine() | ||
65 | |||
66 | // Avoid using constants to not import hls.hs | ||
67 | // https://github.com/video-dev/hls.js/blob/master/src/events.js#L37 | ||
68 | this.hlsjs.on('hlsLevelSwitching', (_: any, data: any) => { | ||
69 | this.trigger('resolutionChange', { auto: this.hlsjs.autoLevelEnabled, resolutionId: data.height }) | ||
70 | }) | ||
71 | |||
72 | this.p2pEngine.on(Events.SegmentError, (segment, err) => { | ||
73 | console.error('Segment error.', segment, err) | ||
74 | }) | ||
75 | |||
76 | this.statsP2PBytes.numPeers = 1 + this.options.redundancyBaseUrls.length | ||
77 | |||
78 | this.runStats() | ||
79 | } | ||
80 | |||
81 | private runStats () { | ||
82 | this.p2pEngine.on(Events.PieceBytesDownloaded, (method: string, size: number) => { | ||
83 | const elem = method === 'p2p' ? this.statsP2PBytes : this.statsHTTPBytes | ||
84 | |||
85 | elem.pendingDownload.push(size) | ||
86 | elem.totalDownload += size | ||
87 | }) | ||
88 | |||
89 | this.p2pEngine.on(Events.PieceBytesUploaded, (method: string, size: number) => { | ||
90 | const elem = method === 'p2p' ? this.statsP2PBytes : this.statsHTTPBytes | ||
91 | |||
92 | elem.pendingUpload.push(size) | ||
93 | elem.totalUpload += size | ||
94 | }) | ||
95 | |||
96 | this.p2pEngine.on(Events.PeerConnect, () => this.statsP2PBytes.numPeers++) | ||
97 | this.p2pEngine.on(Events.PeerClose, () => this.statsP2PBytes.numPeers--) | ||
98 | |||
99 | this.networkInfoInterval = setInterval(() => { | ||
100 | const p2pDownloadSpeed = this.arraySum(this.statsP2PBytes.pendingDownload) | ||
101 | const p2pUploadSpeed = this.arraySum(this.statsP2PBytes.pendingUpload) | ||
102 | |||
103 | const httpDownloadSpeed = this.arraySum(this.statsHTTPBytes.pendingDownload) | ||
104 | const httpUploadSpeed = this.arraySum(this.statsHTTPBytes.pendingUpload) | ||
105 | |||
106 | this.statsP2PBytes.pendingDownload = [] | ||
107 | this.statsP2PBytes.pendingUpload = [] | ||
108 | this.statsHTTPBytes.pendingDownload = [] | ||
109 | this.statsHTTPBytes.pendingUpload = [] | ||
110 | |||
111 | return this.player.trigger('p2pInfo', { | ||
112 | http: { | ||
113 | downloadSpeed: httpDownloadSpeed, | ||
114 | uploadSpeed: httpUploadSpeed, | ||
115 | downloaded: this.statsHTTPBytes.totalDownload, | ||
116 | uploaded: this.statsHTTPBytes.totalUpload | ||
117 | }, | ||
118 | p2p: { | ||
119 | downloadSpeed: p2pDownloadSpeed, | ||
120 | uploadSpeed: p2pUploadSpeed, | ||
121 | numPeers: this.statsP2PBytes.numPeers, | ||
122 | downloaded: this.statsP2PBytes.totalDownload, | ||
123 | uploaded: this.statsP2PBytes.totalUpload | ||
124 | } | ||
125 | } as PlayerNetworkInfo) | ||
126 | }, this.CONSTANTS.INFO_SCHEDULER) | ||
127 | } | ||
128 | |||
129 | private arraySum (data: number[]) { | ||
130 | return data.reduce((a: number, b: number) => a + b, 0) | ||
131 | } | ||
132 | } | ||
133 | |||
134 | videojs.registerPlugin('p2pMediaLoader', P2pMediaLoaderPlugin) | ||
135 | export { P2pMediaLoaderPlugin } | ||