1
0
Fork 0

Improve HLS redundancy

This commit is contained in:
Chocobozzz 2019-08-23 10:19:44 +02:00
parent 20ec03846d
commit da33241770
No known key found for this signature in database
GPG key ID: 583A612D890159BE
5 changed files with 72 additions and 24 deletions

View file

@ -3,7 +3,7 @@
import * as videojs from 'video.js' import * as videojs from 'video.js'
import { P2PMediaLoaderPluginOptions, PlayerNetworkInfo, VideoJSComponentInterface } from '../peertube-videojs-typings' import { P2PMediaLoaderPluginOptions, PlayerNetworkInfo, VideoJSComponentInterface } from '../peertube-videojs-typings'
import { Engine, initHlsJsPlayer, initVideoJsContribHlsJsPlayer } from 'p2p-media-loader-hlsjs' 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' import { timeToInt } from '../utils'
// videojs-hlsjs-plugin needs videojs in window // videojs-hlsjs-plugin needs videojs in window
@ -57,7 +57,6 @@ class P2pMediaLoaderPlugin extends Plugin {
initVideoJsContribHlsJsPlayer(player) initVideoJsContribHlsJsPlayer(player)
this.startTime = timeToInt(options.startTime) this.startTime = timeToInt(options.startTime)
console.log(this.startTime)
player.src({ player.src({
type: options.type, type: options.type,
@ -90,11 +89,13 @@ class P2pMediaLoaderPlugin extends Plugin {
this.trigger('resolutionChange', { auto: this.hlsjs.autoLevelEnabled, resolutionId: data.height }) 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) 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() this.runStats()

View file

@ -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
}

View file

@ -1,17 +1,9 @@
import { basename } from 'path'
import { Segment } from 'p2p-media-loader-core' 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) { return function segmentBuilder (segment: Segment) {
const max = baseUrls.length + 1 return redundancyUrlManager.buildUrl(segment.url)
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)
} }
} }
@ -20,9 +12,3 @@ function segmentUrlBuilderFactory (baseUrls: string[]) {
export { export {
segmentUrlBuilderFactory segmentUrlBuilderFactory
} }
// ---------------------------------------------------------------------------
function getRandomInt (max: number) {
return Math.floor(Math.random() * Math.floor(max))
}

View file

@ -17,6 +17,7 @@ import { buildVideoEmbed, buildVideoLink, copyToClipboard, getRtcConfig } from '
import { getCompleteLocale, getShortLocale, is18nLocale, isDefaultLocale } from '../../../../shared/models/i18n/i18n' import { getCompleteLocale, getShortLocale, is18nLocale, isDefaultLocale } from '../../../../shared/models/i18n/i18n'
import { segmentValidatorFactory } from './p2p-media-loader/segment-validator' import { segmentValidatorFactory } from './p2p-media-loader/segment-validator'
import { segmentUrlBuilderFactory } from './p2p-media-loader/segment-url-builder' 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) // Change 'Playback Rate' to 'Speed' (smaller for our settings menu)
videojsUntyped.getComponent('PlaybackRateMenuButton').prototype.controlText_ = 'Speed' videojsUntyped.getComponent('PlaybackRateMenuButton').prototype.controlText_ = 'Speed'
@ -226,8 +227,10 @@ export class PeertubePlayerManager {
} }
if (mode === 'p2p-media-loader') { if (mode === 'p2p-media-loader') {
const redundancyUrlManager = new RedundancyUrlManager(options.p2pMediaLoader.redundancyBaseUrls)
const p2pMediaLoader: P2PMediaLoaderPluginOptions = { const p2pMediaLoader: P2PMediaLoaderPluginOptions = {
redundancyBaseUrls: options.p2pMediaLoader.redundancyBaseUrls, redundancyUrlManager,
type: 'application/x-mpegURL', type: 'application/x-mpegURL',
startTime: commonOptions.startTime, startTime: commonOptions.startTime,
src: p2pMediaLoaderOptions.playlistUrl src: p2pMediaLoaderOptions.playlistUrl
@ -242,7 +245,7 @@ export class PeertubePlayerManager {
segmentValidator: segmentValidatorFactory(options.p2pMediaLoader.segmentsSha256Url), segmentValidator: segmentValidatorFactory(options.p2pMediaLoader.segmentsSha256Url),
rtcConfig: getRtcConfig(), rtcConfig: getRtcConfig(),
requiredSegmentsPriority: 5, requiredSegmentsPriority: 5,
segmentUrlBuilder: segmentUrlBuilderFactory(options.p2pMediaLoader.redundancyBaseUrls) segmentUrlBuilder: segmentUrlBuilderFactory(redundancyUrlManager)
}, },
segments: { segments: {
swarmId: p2pMediaLoaderOptions.playlistUrl swarmId: p2pMediaLoaderOptions.playlistUrl

View file

@ -7,6 +7,7 @@ import { PeerTubePlugin } from './peertube-plugin'
import { WebTorrentPlugin } from './webtorrent/webtorrent-plugin' import { WebTorrentPlugin } from './webtorrent/webtorrent-plugin'
import { P2pMediaLoaderPlugin } from './p2p-media-loader/p2p-media-loader-plugin' import { P2pMediaLoaderPlugin } from './p2p-media-loader/p2p-media-loader-plugin'
import { PlayerMode } from './peertube-player-manager' import { PlayerMode } from './peertube-player-manager'
import { RedundancyUrlManager } from './p2p-media-loader/redundancy-url-manager'
declare namespace videojs { declare namespace videojs {
interface Player { interface Player {
@ -62,7 +63,7 @@ type WebtorrentPluginOptions = {
} }
type P2PMediaLoaderPluginOptions = { type P2PMediaLoaderPluginOptions = {
redundancyBaseUrls: string[] redundancyUrlManager: RedundancyUrlManager
type: string type: string
src: string src: string