From 3e254de8bef59e4e25b74d1b0fde07de29654ada Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 3 Aug 2021 11:51:49 +0200 Subject: HLS v1 support --- .../assets/player/p2p-media-loader/hls-plugin.ts | 16 +++----- .../p2p-media-loader/p2p-media-loader-plugin.ts | 34 ++++++---------- .../player/p2p-media-loader/segment-url-builder.ts | 2 +- .../player/p2p-media-loader/segment-validator.ts | 2 +- .../src/assets/player/peertube-player-manager.ts | 45 +++++++++++++++++----- client/src/assets/player/peertube-plugin.ts | 1 + .../src/assets/player/peertube-videojs-typings.ts | 4 +- 7 files changed, 58 insertions(+), 46 deletions(-) (limited to 'client/src') diff --git a/client/src/assets/player/p2p-media-loader/hls-plugin.ts b/client/src/assets/player/p2p-media-loader/hls-plugin.ts index 53969a5a5..3050110cd 100644 --- a/client/src/assets/player/p2p-media-loader/hls-plugin.ts +++ b/client/src/assets/player/p2p-media-loader/hls-plugin.ts @@ -264,20 +264,16 @@ class Html5Hlsjs { if (this.errorCounts[ data.type ]) this.errorCounts[ data.type ] += 1 else this.errorCounts[ data.type ] = 1 - if (!data.fatal) { - console.warn(error.message) - return - } - - console.error(error.message) + if (data.fatal) console.warn(error.message) + else console.error(error.message, data) if (data.type === Hlsjs.ErrorTypes.NETWORK_ERROR) { error.code = 2 this._handleNetworkError(error) - } else if (data.type === Hlsjs.ErrorTypes.MEDIA_ERROR && data.details !== 'manifestIncompatibleCodecsError') { + } else if (data.fatal && data.type === Hlsjs.ErrorTypes.MEDIA_ERROR && data.details !== 'manifestIncompatibleCodecsError') { error.code = 3 this._handleMediaError(error) - } else { + } else if (data.fatal) { this.hls.destroy() console.info('bubbling error up to VIDEOJS') this.tech.error = () => error as any @@ -286,12 +282,12 @@ class Html5Hlsjs { } private switchQuality (qualityId: number) { - this.hls.nextLevel = qualityId + this.hls.currentLevel = qualityId } private _levelLabel (level: Hlsjs.Level) { if (this.player.srOptions_.levelLabelHandler) { - return this.player.srOptions_.levelLabelHandler(level) + return this.player.srOptions_.levelLabelHandler(level as any) } if (level.height) return level.height + 'p' 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 index 2eb849d2b..093795e48 100644 --- 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 @@ -1,7 +1,7 @@ import * as Hlsjs from 'hls.js/dist/hls.light.js' -import { Events, Segment } from 'p2p-media-loader-core' -import { Engine, initHlsJsPlayer, initVideoJsContribHlsJsPlayer } from 'p2p-media-loader-hlsjs' import videojs from 'video.js' +import { Events, Segment } from '@peertube/p2p-media-loader-core' +import { Engine, initHlsJsPlayer, initVideoJsContribHlsJsPlayer } from '@peertube/p2p-media-loader-hlsjs' import { timeToInt } from '@shared/core-utils' import { P2PMediaLoaderPluginOptions, PlayerNetworkInfo } from '../peertube-videojs-typings' import { registerConfigPlugin, registerSourceHandler } from './hls-plugin' @@ -36,9 +36,6 @@ class P2pMediaLoaderPlugin extends Plugin { private networkInfoInterval: any - private hlsjsCurrentLevel: number - private hlsjsLevels: Hlsjs.Level[] - constructor (player: videojs.Player, options?: P2PMediaLoaderPluginOptions) { super(player) @@ -88,13 +85,12 @@ class P2pMediaLoaderPlugin extends Plugin { } getCurrentLevel () { - return this.hlsjsLevels.find(l => l.level === this.hlsjsCurrentLevel) + return this.hlsjs.levels[this.hlsjs.currentLevel] } getLiveLatency () { - return undefined as number - // FIXME: Use latency when hls >= V1 - // return this.hlsjs.latency + // FIXME: typings + return Math.round((this.hlsjs as any).latency) } getHLSJS () { @@ -140,31 +136,23 @@ class P2pMediaLoaderPlugin extends Plugin { } private runStats () { - this.p2pEngine.on(Events.PieceBytesDownloaded, (method: string, size: number) => { + this.p2pEngine.on(Events.PieceBytesDownloaded, (method: string, _segment, bytes: number) => { const elem = method === 'p2p' ? this.statsP2PBytes : this.statsHTTPBytes - elem.pendingDownload.push(size) - elem.totalDownload += size + elem.pendingDownload.push(bytes) + elem.totalDownload += bytes }) - this.p2pEngine.on(Events.PieceBytesUploaded, (method: string, size: number) => { + this.p2pEngine.on(Events.PieceBytesUploaded, (method: string, _segment, bytes: number) => { const elem = method === 'p2p' ? this.statsP2PBytes : this.statsHTTPBytes - elem.pendingUpload.push(size) - elem.totalUpload += size + elem.pendingUpload.push(bytes) + elem.totalUpload += bytes }) this.p2pEngine.on(Events.PeerConnect, () => this.statsP2PBytes.numPeers++) this.p2pEngine.on(Events.PeerClose, () => this.statsP2PBytes.numPeers--) - this.hlsjs.on(Hlsjs.Events.MANIFEST_PARSED, (_e, manifest) => { - this.hlsjsCurrentLevel = manifest.firstLevel - this.hlsjsLevels = manifest.levels - }) - this.hlsjs.on(Hlsjs.Events.LEVEL_LOADED, (_e, level) => { - this.hlsjsCurrentLevel = level.levelId || (level as any).id - }) - this.networkInfoInterval = setInterval(() => { const p2pDownloadSpeed = this.arraySum(this.statsP2PBytes.pendingDownload) const p2pUploadSpeed = this.arraySum(this.statsP2PBytes.pendingUpload) diff --git a/client/src/assets/player/p2p-media-loader/segment-url-builder.ts b/client/src/assets/player/p2p-media-loader/segment-url-builder.ts index 039777cea..ad0e460ae 100644 --- a/client/src/assets/player/p2p-media-loader/segment-url-builder.ts +++ b/client/src/assets/player/p2p-media-loader/segment-url-builder.ts @@ -1,4 +1,4 @@ -import { Segment } from 'p2p-media-loader-core' +import { Segment } from '@peertube/p2p-media-loader-core' import { RedundancyUrlManager } from './redundancy-url-manager' function segmentUrlBuilderFactory (redundancyUrlManager: RedundancyUrlManager) { diff --git a/client/src/assets/player/p2p-media-loader/segment-validator.ts b/client/src/assets/player/p2p-media-loader/segment-validator.ts index 4a0caec5e..a28474793 100644 --- a/client/src/assets/player/p2p-media-loader/segment-validator.ts +++ b/client/src/assets/player/p2p-media-loader/segment-validator.ts @@ -1,5 +1,5 @@ import { wait } from '@root-helpers/utils' -import { Segment } from 'p2p-media-loader-core' +import { Segment } from '@peertube/p2p-media-loader-core' import { basename } from 'path' type SegmentsJSON = { [filename: string]: string | { [byterange: string]: string } } diff --git a/client/src/assets/player/peertube-player-manager.ts b/client/src/assets/player/peertube-player-manager.ts index 766ad203e..c45e8f53e 100644 --- a/client/src/assets/player/peertube-player-manager.ts +++ b/client/src/assets/player/peertube-player-manager.ts @@ -22,6 +22,7 @@ import './videojs-components/settings-panel-child' import './videojs-components/theater-button' import './playlist/playlist-plugin' import videojs from 'video.js' +import { HlsJsEngineSettings } from '@peertube/p2p-media-loader-hlsjs' import { PluginsManager } from '@root-helpers/plugins-manager' import { buildVideoLink, decorateVideoLink } from '@shared/core-utils' import { isDefaultLocale } from '@shared/core-utils/i18n' @@ -30,11 +31,12 @@ import { copyToClipboard } from '../../root-helpers/utils' import { RedundancyUrlManager } from './p2p-media-loader/redundancy-url-manager' import { segmentUrlBuilderFactory } from './p2p-media-loader/segment-url-builder' import { segmentValidatorFactory } from './p2p-media-loader/segment-validator' -import { getStoredP2PEnabled } from './peertube-player-local-storage' +import { getAverageBandwidthInStore, getStoredP2PEnabled, saveAverageBandwidth } from './peertube-player-local-storage' import { NextPreviousVideoButtonOptions, P2PMediaLoaderPluginOptions, PeerTubeLinkButtonOptions, + PlayerNetworkInfo, PlaylistPluginOptions, UserWatching, VideoJSCaption, @@ -148,7 +150,7 @@ export class PeertubePlayerManager { if (mode === 'webtorrent') await import('./webtorrent/webtorrent-plugin') if (mode === 'p2p-media-loader') { [ p2pMediaLoader ] = await Promise.all([ - import('p2p-media-loader-hlsjs'), + import('@peertube/p2p-media-loader-hlsjs'), import('./p2p-media-loader/p2p-media-loader-plugin') ]) } @@ -193,6 +195,12 @@ export class PeertubePlayerManager { mode }) + player.on('p2pInfo', (_, data: PlayerNetworkInfo) => { + if (data.source !== 'p2p-media-loader' || isNaN(data.bandwidthEstimate)) return + + saveAverageBandwidth(data.bandwidthEstimate) + }) + return res(player) }) }) @@ -359,12 +367,13 @@ export class PeertubePlayerManager { consumeOnly = true } - const p2pMediaLoaderConfig = { + const p2pMediaLoaderConfig: HlsJsEngineSettings = { loader: { trackerAnnounce, segmentValidator: segmentValidatorFactory(options.p2pMediaLoader.segmentsSha256Url, options.common.isLive), rtcConfig: getRtcConfig(), requiredSegmentsPriority: 1, + simultaneousHttpDownloads: 1, segmentUrlBuilder: segmentUrlBuilderFactory(redundancyUrlManager), useP2P: getStoredP2PEnabled(), consumeOnly @@ -373,6 +382,7 @@ export class PeertubePlayerManager { swarmId: p2pMediaLoaderOptions.playlistUrl } } + const hlsjs = { levelLabelHandler: (level: { height: number, width: number }) => { const resolution = Math.min(level.height || 0, level.width || 0) @@ -387,12 +397,7 @@ export class PeertubePlayerManager { return label }, html5: { - hlsjsConfig: { - capLevelToPlayerSize: true, - autoStartLoad: false, - liveSyncDurationCount: 5, - loader: new p2pMediaLoaderModule.Engine(p2pMediaLoaderConfig).createLoaderClass() - } + hlsjsConfig: this.getHLSOptions(p2pMediaLoaderModule, p2pMediaLoaderConfig) } } @@ -402,6 +407,28 @@ export class PeertubePlayerManager { return toAssign } + private static getHLSOptions (p2pMediaLoaderModule: any, p2pMediaLoaderConfig: HlsJsEngineSettings) { + const base = { + capLevelToPlayerSize: true, + autoStartLoad: false, + liveSyncDurationCount: 5, + + loader: new p2pMediaLoaderModule.Engine(p2pMediaLoaderConfig).createLoaderClass() + } + + const averageBandwidth = getAverageBandwidthInStore() + if (!averageBandwidth) return base + + return { + ...base, + + abrEwmaDefaultEstimate: averageBandwidth * 8, // We want bit/s + startLevel: -1, + testBandwidth: false, + debug: false + } + } + private static addWebTorrentOptions (plugins: VideoJSPluginOptions, options: PeertubePlayerManagerOptions) { const commonOptions = options.common const webtorrentOptions = options.webtorrent diff --git a/client/src/assets/player/peertube-plugin.ts b/client/src/assets/player/peertube-plugin.ts index 919b7c239..ade8e2ee4 100644 --- a/client/src/assets/player/peertube-plugin.ts +++ b/client/src/assets/player/peertube-plugin.ts @@ -228,6 +228,7 @@ class PeerTubePlugin extends Plugin { } } + console.log('Resolution changed.', data) this.trigger('resolutionChange', data) } diff --git a/client/src/assets/player/peertube-videojs-typings.ts b/client/src/assets/player/peertube-videojs-typings.ts index d3c75990b..f0eb129d4 100644 --- a/client/src/assets/player/peertube-videojs-typings.ts +++ b/client/src/assets/player/peertube-videojs-typings.ts @@ -1,4 +1,4 @@ -import { Config, Level } from 'hls.js' +import { HlsConfig, Level } from 'hls.js' import videojs from 'video.js' import { VideoFile, VideoPlaylist, VideoPlaylistElement } from '@shared/models' import { P2pMediaLoaderPlugin } from './p2p-media-loader/p2p-media-loader-plugin' @@ -60,7 +60,7 @@ export interface VideoJSTechHLS extends videojs.Tech { } export interface HlsjsConfigHandlerOptions { - hlsjsConfig?: Config & { cueHandler: any }// FIXME: typings + hlsjsConfig?: HlsConfig & { cueHandler: any }// FIXME: typings captionConfig?: any // FIXME: typings levelLabelHandler?: (level: Level) => string -- cgit v1.2.3