Improve HLS redundancy
authorChocobozzz <me@florianbigard.com>
Fri, 23 Aug 2019 08:19:44 +0000 (10:19 +0200)
committerChocobozzz <me@florianbigard.com>
Fri, 23 Aug 2019 08:19:58 +0000 (10:19 +0200)
client/src/assets/player/p2p-media-loader/p2p-media-loader-plugin.ts
client/src/assets/player/p2p-media-loader/redundancy-url-manager.ts [new file with mode: 0644]
client/src/assets/player/p2p-media-loader/segment-url-builder.ts
client/src/assets/player/peertube-player-manager.ts
client/src/assets/player/peertube-videojs-typings.ts

index 8fb7ba2eab4ec42e34a4105bc0d773a80ed0128e..0c8c612ee8834eeb47ba0fcf1eeb73d71073d845 100644 (file)
@@ -3,7 +3,7 @@
 import * as videojs from 'video.js'
 import { P2PMediaLoaderPluginOptions, PlayerNetworkInfo, VideoJSComponentInterface } from '../peertube-videojs-typings'
 import { Engine, initHlsJsPlayer, initVideoJsContribHlsJsPlayer } from 'p2p-media-loader-hlsjs'
-import { Events } from 'p2p-media-loader-core'
+import { Events, Segment } from 'p2p-media-loader-core'
 import { timeToInt } from '../utils'
 
 // videojs-hlsjs-plugin needs videojs in window
@@ -57,7 +57,6 @@ class P2pMediaLoaderPlugin extends Plugin {
     initVideoJsContribHlsJsPlayer(player)
 
     this.startTime = timeToInt(options.startTime)
-    console.log(this.startTime)
 
     player.src({
       type: options.type,
@@ -90,11 +89,13 @@ class P2pMediaLoaderPlugin extends Plugin {
       this.trigger('resolutionChange', { auto: this.hlsjs.autoLevelEnabled, resolutionId: data.height })
     })
 
-    this.p2pEngine.on(Events.SegmentError, (segment, err) => {
+    this.p2pEngine.on(Events.SegmentError, (segment: Segment, err) => {
       console.error('Segment error.', segment, err)
+
+      this.options.redundancyUrlManager.removeByOriginUrl(segment.url)
     })
 
-    this.statsP2PBytes.numPeers = 1 + this.options.redundancyBaseUrls.length
+    this.statsP2PBytes.numPeers = 1 + this.options.redundancyUrlManager.countBaseUrls()
 
     this.runStats()
 
diff --git a/client/src/assets/player/p2p-media-loader/redundancy-url-manager.ts b/client/src/assets/player/p2p-media-loader/redundancy-url-manager.ts
new file mode 100644 (file)
index 0000000..7fc2b6a
--- /dev/null
@@ -0,0 +1,57 @@
+import { basename, dirname } from 'path'
+
+class RedundancyUrlManager {
+
+  // Remember by what new URL we replaced an origin URL
+  private replacedSegmentUrls: { [originUrl: string]: string } = {}
+
+  constructor (private baseUrls: string[] = []) {
+    // empty
+  }
+
+  removeBySegmentUrl (segmentUrl: string) {
+    console.log('Removing redundancy of segment URL %s.', segmentUrl)
+
+    const baseUrl = dirname(segmentUrl)
+
+    this.baseUrls = this.baseUrls.filter(u => u !== baseUrl && u !== baseUrl + '/')
+  }
+
+  removeByOriginUrl (originUrl: string) {
+    const replaced = this.replacedSegmentUrls[originUrl]
+    if (!replaced) return
+
+    return this.removeBySegmentUrl(replaced)
+  }
+
+  buildUrl (url: string) {
+    delete this.replacedSegmentUrls[url]
+
+    const max = this.baseUrls.length + 1
+    const i = this.getRandomInt(max)
+
+    if (i === max - 1) return url
+
+    const newBaseUrl = this.baseUrls[i]
+    const slashPart = newBaseUrl.endsWith('/') ? '' : '/'
+
+    const newUrl = newBaseUrl + slashPart + basename(url)
+    this.replacedSegmentUrls[url] = newUrl
+
+    return newUrl
+  }
+
+  countBaseUrls () {
+    return this.baseUrls.length
+  }
+
+  private getRandomInt (max: number) {
+    return Math.floor(Math.random() * Math.floor(max))
+  }
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+  RedundancyUrlManager
+}
index fb990a19dd44558bcfcb0ee72a89da16421967f2..039777cea85564a48821c12faaf4ff321d213750 100644 (file)
@@ -1,17 +1,9 @@
-import { basename } from 'path'
 import { Segment } from 'p2p-media-loader-core'
+import { RedundancyUrlManager } from './redundancy-url-manager'
 
-function segmentUrlBuilderFactory (baseUrls: string[]) {
+function segmentUrlBuilderFactory (redundancyUrlManager: RedundancyUrlManager) {
   return function segmentBuilder (segment: Segment) {
-    const max = baseUrls.length + 1
-    const i = getRandomInt(max)
-
-    if (i === max - 1) return segment.url
-
-    const newBaseUrl = baseUrls[i]
-    const middlePart = newBaseUrl.endsWith('/') ? '' : '/'
-
-    return newBaseUrl + middlePart + basename(segment.url)
+    return redundancyUrlManager.buildUrl(segment.url)
   }
 }
 
@@ -20,9 +12,3 @@ function segmentUrlBuilderFactory (baseUrls: string[]) {
 export {
   segmentUrlBuilderFactory
 }
-
-// ---------------------------------------------------------------------------
-
-function getRandomInt (max: number) {
-  return Math.floor(Math.random() * Math.floor(max))
-}
index 6c8b13087c1889b4f56327b9fe34b253702b9698..7be9f8719b06565644f5af18304195c8ca194437 100644 (file)
@@ -17,6 +17,7 @@ import { buildVideoEmbed, buildVideoLink, copyToClipboard, getRtcConfig } from '
 import { getCompleteLocale, getShortLocale, is18nLocale, isDefaultLocale } from '../../../../shared/models/i18n/i18n'
 import { segmentValidatorFactory } from './p2p-media-loader/segment-validator'
 import { segmentUrlBuilderFactory } from './p2p-media-loader/segment-url-builder'
+import { RedundancyUrlManager } from './p2p-media-loader/redundancy-url-manager'
 
 // Change 'Playback Rate' to 'Speed' (smaller for our settings menu)
 videojsUntyped.getComponent('PlaybackRateMenuButton').prototype.controlText_ = 'Speed'
@@ -226,8 +227,10 @@ export class PeertubePlayerManager {
     }
 
     if (mode === 'p2p-media-loader') {
+      const redundancyUrlManager = new RedundancyUrlManager(options.p2pMediaLoader.redundancyBaseUrls)
+
       const p2pMediaLoader: P2PMediaLoaderPluginOptions = {
-        redundancyBaseUrls: options.p2pMediaLoader.redundancyBaseUrls,
+        redundancyUrlManager,
         type: 'application/x-mpegURL',
         startTime: commonOptions.startTime,
         src: p2pMediaLoaderOptions.playlistUrl
@@ -242,7 +245,7 @@ export class PeertubePlayerManager {
           segmentValidator: segmentValidatorFactory(options.p2pMediaLoader.segmentsSha256Url),
           rtcConfig: getRtcConfig(),
           requiredSegmentsPriority: 5,
-          segmentUrlBuilder: segmentUrlBuilderFactory(options.p2pMediaLoader.redundancyBaseUrls)
+          segmentUrlBuilder: segmentUrlBuilderFactory(redundancyUrlManager)
         },
         segments: {
           swarmId: p2pMediaLoaderOptions.playlistUrl
index a96b0bc8c4ab07e3889d1b44f50716ceacf9a579..b7f2eec9471af045617ea74734e7d60d3897d413 100644 (file)
@@ -7,6 +7,7 @@ import { PeerTubePlugin } from './peertube-plugin'
 import { WebTorrentPlugin } from './webtorrent/webtorrent-plugin'
 import { P2pMediaLoaderPlugin } from './p2p-media-loader/p2p-media-loader-plugin'
 import { PlayerMode } from './peertube-player-manager'
+import { RedundancyUrlManager } from './p2p-media-loader/redundancy-url-manager'
 
 declare namespace videojs {
   interface Player {
@@ -62,7 +63,7 @@ type WebtorrentPluginOptions = {
 }
 
 type P2PMediaLoaderPluginOptions = {
-  redundancyBaseUrls: string[]
+  redundancyUrlManager: RedundancyUrlManager
   type: string
   src: string