HLS v1 support

pull/4317/head
Chocobozzz 2021-08-03 11:51:49 +02:00 committed by Chocobozzz
parent ff4de38385
commit 3e254de8be
9 changed files with 122 additions and 83 deletions

View File

@ -48,6 +48,8 @@
"@ngx-loading-bar/core": "^5.0.0",
"@ngx-loading-bar/http-client": "^5.0.0",
"@ngx-loading-bar/router": "^5.0.0",
"@peertube/p2p-media-loader-core": "^1.0.2",
"@peertube/p2p-media-loader-hlsjs": "^1.0.4",
"@types/chart.js": "^2.9.16",
"@types/core-js": "^2.5.2",
"@types/debug": "^4.1.5",
@ -76,7 +78,7 @@
"dexie": "^3.0.0",
"file-loader": "^6.0.0",
"focus-visible": "^5.0.2",
"hls.js": "^0.14.16",
"hls.js": "^1.0.7",
"html-loader": "^2.1.2",
"html-webpack-plugin": "^5.3.1",
"https-browserify": "^1.0.0",
@ -93,7 +95,6 @@
"markdown-it": "12.0.6",
"mini-css-extract-plugin": "^1.6.0",
"ngx-uploadx": "^4.1.0",
"p2p-media-loader-hlsjs": "^0.6.2",
"path-browserify": "^1.0.0",
"primeng": "^12.0.0-rc.1",
"process": "^0.11.10",

View File

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

View File

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

View File

@ -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) {

View File

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

View File

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

View File

@ -228,6 +228,7 @@ class PeerTubePlugin extends Plugin {
}
}
console.log('Resolution changed.', data)
this.trigger('resolutionChange', data)
}

View File

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

View File

@ -1333,6 +1333,26 @@
node-gyp "^7.1.0"
read-package-json-fast "^2.0.1"
"@peertube/p2p-media-loader-core@^1.0.2":
version "1.0.2"
resolved "https://registry.yarnpkg.com/@peertube/p2p-media-loader-core/-/p2p-media-loader-core-1.0.2.tgz#ef36a23df5a5393cc5d180a45dfb70d2c3ecff87"
integrity sha512-2F6Cx2ncXe+ySaaGiAWf7jJ8snJpyd7WNCxYbJ5Zadst1mNSQlxUqH4/qEl/bai4DiuzzhudlzXS8U3SX9c9Jw==
dependencies:
bittorrent-tracker "^9.16.1"
debug "^4.3.1"
events "^3.3.0"
sha.js "^2.4.11"
simple-peer "^9.10.0"
"@peertube/p2p-media-loader-hlsjs@^1.0.4":
version "1.0.4"
resolved "https://registry.yarnpkg.com/@peertube/p2p-media-loader-hlsjs/-/p2p-media-loader-hlsjs-1.0.4.tgz#849809067886e41bf2ba7e71a2da33478863bc66"
integrity sha512-FFFlYPFwTGxB3CPChqlzPqJw65ajIjCCxCn4IQRNuagYpo0fdaxrgepMHNPW3Zl5DmvEppv+ohUfYyr98U1wiw==
dependencies:
"@peertube/p2p-media-loader-core" "^1.0.2"
events "^3.3.0"
m3u8-parser "^4.6.0"
"@polka/url@^1.0.0-next.15":
version "1.0.0-next.15"
resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.15.tgz#6a9d143f7f4f49db2d782f9e1c8839a29b43ae23"
@ -2445,7 +2465,7 @@ bittorrent-protocol@^3.2.0:
speedometer "^1.1.0"
unordered-array-remove "^1.0.2"
bittorrent-tracker@^9.0.0, bittorrent-tracker@^9.14.4:
bittorrent-tracker@^9.0.0:
version "9.17.2"
resolved "https://registry.yarnpkg.com/bittorrent-tracker/-/bittorrent-tracker-9.17.2.tgz#1afb02d3d2fb474c13389c45e8a2b6919bff40bd"
integrity sha512-hXjed0OnB16da+ScJUZnrAZbf9gMgSLKqh5rJebtYnTRgN4o1mX0DOPH3Nf5RFCs935ibhSmZN5nwbkh+3MdEA==
@ -2475,6 +2495,36 @@ bittorrent-tracker@^9.0.0, bittorrent-tracker@^9.14.4:
bufferutil "^4.0.3"
utf-8-validate "^5.0.5"
bittorrent-tracker@^9.16.1:
version "9.17.4"
resolved "https://registry.yarnpkg.com/bittorrent-tracker/-/bittorrent-tracker-9.17.4.tgz#663f51064a924e945cb6ca19a0c293aca258128b"
integrity sha512-ykhdVQHtLfn4DYSJUQD/zFAbP8YwnF6nGlj2SBnCY4xkW5bhwXPeFZUhryAtdITl0qNL/FpmFOamBZfxIwkbxg==
dependencies:
bencode "^2.0.1"
bittorrent-peerid "^1.3.3"
bn.js "^5.2.0"
chrome-dgram "^3.0.6"
compact2string "^1.4.1"
debug "^4.1.1"
ip "^1.1.5"
lru "^3.1.0"
minimist "^1.2.5"
once "^1.4.0"
queue-microtask "^1.2.3"
random-iterate "^1.0.1"
randombytes "^2.1.0"
run-parallel "^1.2.0"
run-series "^1.1.9"
simple-get "^4.0.0"
simple-peer "^9.11.0"
simple-websocket "^9.1.0"
string2compact "^1.3.0"
unordered-array-remove "^1.0.2"
ws "^7.4.5"
optionalDependencies:
bufferutil "^4.0.3"
utf-8-validate "^5.0.5"
bl@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a"
@ -4179,12 +4229,12 @@ etag@~1.8.1:
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=
eventemitter3@^4.0.0, eventemitter3@^4.0.3:
eventemitter3@^4.0.0:
version "4.0.7"
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==
events@^3.0.0, events@^3.2.0:
events@^3.2.0, events@^3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400"
integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==
@ -4642,7 +4692,7 @@ gensync@^1.0.0-beta.1, gensync@^1.0.0-beta.2:
resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==
get-browser-rtc@^1.0.2, get-browser-rtc@^1.1.0:
get-browser-rtc@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/get-browser-rtc/-/get-browser-rtc-1.1.0.tgz#d1494e299b00f33fc8e9d6d3343ba4ba99711a2c"
integrity sha512-MghbMJ61EJrRsDe7w1Bvqt3ZsBuqhce5nrn/XAwgwOXhcsz53/ltdxOse1h/8eKXj5slzxdsz56g5rzOFSGwfQ==
@ -4930,13 +4980,10 @@ hex-color-regex@^1.1.0:
resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e"
integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==
hls.js@^0.14.16:
version "0.14.17"
resolved "https://registry.yarnpkg.com/hls.js/-/hls.js-0.14.17.tgz#0127cff2ec2f994a54eb955fe669ef6153a8e317"
integrity sha512-25A7+m6qqp6UVkuzUQ//VVh2EEOPYlOBg32ypr34bcPO7liBMOkKFvbjbCBfiPAOTA/7BSx1Dujft3Th57WyFg==
dependencies:
eventemitter3 "^4.0.3"
url-toolkit "^2.1.6"
hls.js@^1.0.7:
version "1.0.7"
resolved "https://registry.yarnpkg.com/hls.js/-/hls.js-1.0.7.tgz#b4ab75e7a46650d02245a1da091efd15f07d16eb"
integrity sha512-NsGaksvOuYNyTmu/w239EtYFMadFzIoDkmStc1FDRcrGmvxOfMGKad/hLj4NHoE8H1S+Q8dT7HufMJJ6yiht7g==
hosted-git-info@^2.1.4:
version "2.8.9"
@ -6341,7 +6388,7 @@ lru@^3.1.0:
dependencies:
inherits "^2.0.1"
m3u8-parser@4.7.0, m3u8-parser@^4.4.0:
m3u8-parser@4.7.0, m3u8-parser@^4.6.0:
version "4.7.0"
resolved "https://registry.yarnpkg.com/m3u8-parser/-/m3u8-parser-4.7.0.tgz#e01e8ce136098ade1b14ee691ea20fc4dc60abf6"
integrity sha512-48l/OwRyjBm+QhNNigEEcRcgbRvnUjL7rxs597HmW9QSNbyNvt+RcZ9T/d9vxi9A9z7EZrB1POtZYhdRlwYQkQ==
@ -7374,27 +7421,6 @@ p-try@^2.0.0:
resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
p2p-media-loader-core@^0.6.2:
version "0.6.2"
resolved "https://registry.yarnpkg.com/p2p-media-loader-core/-/p2p-media-loader-core-0.6.2.tgz#7e46cf8fc4357596f389e106bee850908cc974ef"
integrity sha512-yspgCOrVVYitVNece5CA6W/kcVA0UybvbD4kyBE5ooyhCAXQK5/q6JsIpXiVQ3VkQw8Qs4mfZjU39Vt6vEk6aw==
dependencies:
bittorrent-tracker "^9.14.4"
debug "^4.1.1"
events "^3.0.0"
get-browser-rtc "^1.0.2"
sha.js "^2.4.11"
simple-peer "^9.5.0"
p2p-media-loader-hlsjs@^0.6.2:
version "0.6.2"
resolved "https://registry.yarnpkg.com/p2p-media-loader-hlsjs/-/p2p-media-loader-hlsjs-0.6.2.tgz#b66f977a5d28986c8f6e62d2ffa297aec3c05186"
integrity sha512-5LgqWPDsgyST9rxoHGDpExZU1rIDZIT0qft2wAnlg8Cb8aVeaBxUsmF4Sj692Qb5/GBDsi8vLE03LW8gpvlh1g==
dependencies:
events "^3.0.0"
m3u8-parser "^4.4.0"
p2p-media-loader-core "^0.6.2"
package-json-versionify@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/package-json-versionify/-/package-json-versionify-1.0.4.tgz#5860587a944873a6b7e6d26e8e51ffb22315bf17"
@ -9313,7 +9339,7 @@ simple-get@^4.0.0:
once "^1.3.1"
simple-concat "^1.0.0"
simple-peer@^9.11.0, simple-peer@^9.5.0, simple-peer@^9.9.3:
simple-peer@^9.10.0, simple-peer@^9.11.0, simple-peer@^9.9.3:
version "9.11.0"
resolved "https://registry.yarnpkg.com/simple-peer/-/simple-peer-9.11.0.tgz#e8d27609c7a610c3ddd75767da868e8daab67571"
integrity sha512-qvdNu/dGMHBm2uQ7oLhQBMhYlrOZC1ywXNCH/i8I4etxR1vrjCnU6ZSQBptndB1gcakjo2+w4OHo7Sjza1SHxg==
@ -10538,7 +10564,7 @@ url-parse@^1.4.3, url-parse@^1.5.1:
querystringify "^2.1.1"
requires-port "^1.0.0"
url-toolkit@^2.1.6, url-toolkit@^2.2.1:
url-toolkit@^2.2.1:
version "2.2.2"
resolved "https://registry.yarnpkg.com/url-toolkit/-/url-toolkit-2.2.2.tgz#51ef27b56d3187185f9ecf4a8ac7e8f55203c89d"
integrity sha512-l25w6Sy+Iy3/IbogunxhWwljPaDnqpiKvrQRoLBm6DfISco7NyRIS7Zf6+Oxhy1T8kHxWdwLND7ZZba6NjXMug==