Skip to content

Commit

Permalink
feat: (xgplayer-flv) update download speed evaluate strategy
Browse files Browse the repository at this point in the history
feat: (xgplayer-flv) export configuration for speed evaluate
fix: (xgplayer-flv) seamless switch error case canplay、playing event emit again
feat: (xgplayer-flv) export totalSize、 totalCost for speedInfo()
  • Loading branch information
王伟 authored and xiyuyizhi committed May 23, 2024
1 parent 4248c17 commit 5aad780
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 18 deletions.
30 changes: 22 additions & 8 deletions packages/xgplayer-flv/src/flv/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,11 @@ export class Flv extends EventEmitter {
this._opts
)
this._seiService = new SeiService(this)
this._bandwidthService = new BandwidthService()
this._bandwidthService = new BandwidthService({
chunkCountForSpeed: this._opts.chunkCountForSpeed,
skipChunkSize: this._opts.skipChunkSize,
longtimeNoReceived: this._opts.longtimeNoReceived
})
this._stats = new MediaStatsService(this)

if (!this._opts.softDecode) {
Expand Down Expand Up @@ -131,7 +135,9 @@ export class Flv extends EventEmitter {
speedInfo () {
return {
speed: this._bandwidthService.getLatestSpeed(),
avgSpeed: this._bandwidthService.getAvgSpeed()
avgSpeed: this._bandwidthService.getAvgSpeed(),
totalSize: this._bandwidthService.getTotalSize(),
totalCost: this._bandwidthService.getTotalCost()
}
}

Expand Down Expand Up @@ -288,7 +294,10 @@ export class Flv extends EventEmitter {

this._mediaLoader.finnalUrl = finnalUrl

this.emit(EVENT.LOAD_START, { url: finnalUrl, seamlessSwitching: this._seamlessSwitching })
this.emit(EVENT.LOAD_START, {
url: finnalUrl,
seamlessSwitching: this._seamlessSwitching
})

logger.debug('load data, loading:', this._loading, finnalUrl)

Expand All @@ -301,7 +310,7 @@ export class Flv extends EventEmitter {
await this._mediaLoader.load({ url: finnalUrl, range })
} catch (error) {
this._loading = false
return this._emitError(StreamingError.network(error))
return this._emitError(StreamingError.network(error), false)
}
}

Expand Down Expand Up @@ -384,7 +393,7 @@ export class Flv extends EventEmitter {
if (!this.isLive) return

const { maxReaderInterval } = this._opts
if (maxReaderInterval) {
if (maxReaderInterval && this._firstProgressEmit) {
clearTimeout(this._maxChunkWaitTimer)
this._maxChunkWaitTimer = setTimeout(() => {
if (this._disconnectRetryCount) {
Expand Down Expand Up @@ -446,7 +455,9 @@ export class Flv extends EventEmitter {
} else {
if (!media.currentTime && this._gapService) {
// 起播跳洞检测
const gapJump = this._opts.mseLowLatency || (this._opts.mseLowLatency === false && this.bufferInfo(MAX_START_GAP).nextStart)
const gapJump =
this._opts.mseLowLatency ||
(this._opts.mseLowLatency === false && this.bufferInfo(MAX_START_GAP).nextStart)
if (gapJump) {
this._gapService.do(media, opts.maxJumpDistance, this.isLive, 3)
}
Expand Down Expand Up @@ -477,7 +488,7 @@ export class Flv extends EventEmitter {
if (this.isLive && !this._opts.mseLowLatency) {
// update duration to Infinity
if (this.media.duration !== Infinity) {
this._bufferService.updateDuration(Infinity).catch(e=>{})
this._bufferService.updateDuration(Infinity).catch(e => {})
}
}
}
Expand All @@ -500,7 +511,10 @@ export class Flv extends EventEmitter {
const latency = bufferEnd - currentTime
if (latency >= opts.maxLatency) {
this.media.currentTime = bufferEnd - opts.targetLatency
this.emit(EVENT.CHASEFRAME, {currentTime: this.media.currentTime, latency: opts.targetLatency})
this.emit(EVENT.CHASEFRAME, {
currentTime: this.media.currentTime,
latency: opts.targetLatency
})
}
}
this._seiService.throw(currentTime, true)
Expand Down
6 changes: 6 additions & 0 deletions packages/xgplayer-flv/src/flv/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@
* preferMMS?: boolean,
* mseLowLatency?: boolean,
* durationForLowLatency?: number, // s
* chunkCountForSpeed?: number,
* skipChunkSize?: number, // Byte
* longtimeNoReceived?: number,
* preProcessUrl?: (url: string, ext?: { [propName: string]: any }) => { url: string, [propName: string]: any }
* }} FlvOption
*/
Expand Down Expand Up @@ -54,6 +57,9 @@ export function getOption (opts) {
preferMMS: false,
mseLowLatency: true, // mse 低延迟模式渲染 https://issues.chromium.org/issues/41161663
durationForMSELowLatencyOff: 6, // s
chunkCountForSpeed: 50,
skipChunkSize: 1000,
longtimeNoReceived: 3000,
...opts
}

Expand Down
98 changes: 88 additions & 10 deletions packages/xgplayer-streaming-shared/src/services/bandwidth.js
Original file line number Diff line number Diff line change
@@ -1,37 +1,115 @@


const SKIP_SMALL_CHUNK = 1000
const MAX_CHUNK_SAVE_SIZE = 50
const MAX_SEGMENT_SAVE_SIZE = 3
const LONGTIME_NO_RECEIVE = 3000

export class BandwidthService {
_chunkSpeeds = [] // bps
_chunkSpeed = 0 // bps
_chunkCache = []
_speeds = [] // bps
_totalSize = 0
_totalCost = 0

/**
* @typedef {{
* chunkCountForSpeed?: number,
* skipChunkSize?: number,
* longtimeNoReceived?: number
* }} Opts
*
* @param {Opts} opts
*/
constructor (opts) {
this._opts = opts || {}
}

addRecord (totalByte, ms) {
if (!totalByte || !ms) return
this._speeds.push(8000 * totalByte / ms)
this._speeds = this._speeds.slice(-3)
this._speeds = this._speeds.slice(-MAX_SEGMENT_SAVE_SIZE)
}

addChunkRecord (totalByte, ms) {
if (!totalByte || !ms) return
this._chunkSpeeds.push(8000 * totalByte / ms)
this._chunkSpeeds = this._chunkSpeeds.slice(-100)
if (!totalByte || !ms || totalByte < (this._opts?.skipChunkSize || SKIP_SMALL_CHUNK)) return

this._totalSize += totalByte
this._totalCost += ms

this._chunkSpeed = 8000 * totalByte / ms
this._chunkCache.push({
size: totalByte,
duration: ms,
timestamp: performance.now()
})

const size = this._opts?.chunkCountForSpeed || MAX_CHUNK_SAVE_SIZE

if (this._chunkCache.length > size) {
this._chunkCache = this._chunkCache.slice(-size)
}
}

/**
*
* @returns { number }
*/
getAvgSpeed () {
if (!this._chunkSpeeds.length && !this._speeds.length) return 0
if (!this._chunkCache.length && !this._speeds.length) return 0
if (this._speeds.length) {
return this._speeds.reduce((a, c) => (a += c)) / this._speeds.length
}
return this._chunkSpeeds.reduce((a, c) => (a += c)) / this._chunkSpeeds.length

// for long time no chunk received, append 0 size chunk
const lastSample = this._chunkCache[this._chunkCache.length - 1]
const cost = performance.now() - lastSample.timestamp
if ( cost > (this._opts?.longtimeNoReceived || LONGTIME_NO_RECEIVE)) {
this._chunkCache.push({
size: 0,
duration: cost,
timestamp: performance.now()
})
}

const totalSize = this._chunkCache.reduce((a, c) => a += c.size, 0)
const totalDuration = this._chunkCache.reduce((a, c) => a += c.duration, 0)
return 8000 * totalSize / totalDuration
}

/**
*
* @returns { number }
*/
getLatestSpeed () {
if (!this._chunkSpeeds.length && !this._speeds.length) return 0
if (!this._chunkCache.length && !this._speeds.length) return 0
if (this._speeds.length) {
return this._speeds[this._speeds.length - 1]
}
return this._chunkSpeeds[this._chunkSpeeds.length - 1]
return this._chunkSpeed
}

/**
*
* @returns { number }
*/
getTotalSize () {
return this._totalSize
}

/**
*
* @returns { number }
*/
getTotalCost () {
return this._totalCost
}

reset () {
this._chunkSpeeds = []
this._chunkCache = []
this._speeds = []
this._totalSize = 0
this._totalCost = 0
}
}

2 changes: 2 additions & 0 deletions packages/xgplayer-streaming-shared/src/services/stats.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@ class MediaStatsService {
...this._stats.getStats(),
downloadSpeed: this._core?.speedInfo?.().speed || 0,
avgSpeed: this._core?.speedInfo?.().avgSpeed || 0,
totalReceivedByte: this._core?.speedInfo?.().totalSize || 0,
totalReceivedCost: this._core?.speedInfo?.().totalCost || 0,
currentTime,
bufferEnd: this._core?.bufferInfo()?.remaining || 0,
decodeFps
Expand Down

0 comments on commit 5aad780

Please sign in to comment.