Hyper-P2P Module Tests / unit-all (push) Failing after 1h20m44s
- helper-swarm-coordinator: autoAssign, rebalance, resetUtilization - chunk-scheduler-media: enqueueBatch, dropBelowPriority, clear - qos-topic: queueDepths, clearQueues - Update routing-advanced category README highlights Co-authored-by: Cursor <[email protected]>
117 lines
3.1 KiB
JavaScript
117 lines
3.1 KiB
JavaScript
require('bare-process/global')
|
|
const EventEmitter = require('bare-events')
|
|
const { CHUNK_TYPES, mediaStats } = require('../../_shared/media-streaming-base.js')
|
|
|
|
const PROTOCOL = 'chunk-scheduler-media/v1'
|
|
|
|
const PRIORITY = {
|
|
[CHUNK_TYPES.KEYFRAME]: 100,
|
|
[CHUNK_TYPES.BASE]: 80,
|
|
[CHUNK_TYPES.AUDIO]: 70,
|
|
[CHUNK_TYPES.ENHANCEMENT]: 50,
|
|
[CHUNK_TYPES.DELTA]: 40
|
|
}
|
|
|
|
class HyperP2PChunkSchedulerMedia extends EventEmitter {
|
|
constructor (opts = {}) {
|
|
super()
|
|
this._queue = []
|
|
this._stats = { enqueued: 0, scheduled: 0 }
|
|
}
|
|
|
|
enqueue (chunk, priority = null) {
|
|
if (!chunk || chunk.seq == null) throw new Error('chunk with seq required')
|
|
const p = priority != null ? priority : (PRIORITY[chunk.type] ?? 30)
|
|
const entry = { chunk, priority: p, enqueuedAt: Date.now() }
|
|
this._queue.push(entry)
|
|
this._stats.enqueued++
|
|
this._sort()
|
|
this.emit('enqueue', entry)
|
|
return entry
|
|
}
|
|
|
|
_sort () {
|
|
this._queue.sort((a, b) => b.priority - a.priority || a.chunk.seq - b.chunk.seq)
|
|
}
|
|
|
|
nextChunks (limit = 8) {
|
|
const out = this._queue.splice(0, Math.max(0, limit | 0))
|
|
this._stats.scheduled += out.length
|
|
if (out.length) this.emit('schedule', { count: out.length })
|
|
return out.map((e) => e.chunk)
|
|
}
|
|
|
|
prioritizeKeyframes () {
|
|
for (const e of this._queue) {
|
|
if (e.chunk.type === CHUNK_TYPES.KEYFRAME || e.chunk.keyframe) {
|
|
e.priority = PRIORITY[CHUNK_TYPES.KEYFRAME]
|
|
}
|
|
}
|
|
this._sort()
|
|
return this._queue.length
|
|
}
|
|
|
|
schedulePlan () {
|
|
const plan = { keyframes: 0, audio: 0, enhancement: 0, other: 0 }
|
|
for (const e of this._queue) {
|
|
if (e.chunk.type === CHUNK_TYPES.KEYFRAME) plan.keyframes++
|
|
else if (e.chunk.type === CHUNK_TYPES.AUDIO) plan.audio++
|
|
else if (e.chunk.type === CHUNK_TYPES.ENHANCEMENT) plan.enhancement++
|
|
else plan.other++
|
|
}
|
|
return plan
|
|
}
|
|
|
|
pendingCount () { return this._queue.length }
|
|
|
|
peek (n = 5) {
|
|
return this._queue.slice(0, Math.max(0, n | 0)).map((e) => e.chunk)
|
|
}
|
|
|
|
dequeueSeq (seq) {
|
|
const idx = this._queue.findIndex((e) => e.chunk.seq === seq)
|
|
if (idx < 0) return null
|
|
const [entry] = this._queue.splice(idx, 1)
|
|
this._stats.scheduled++
|
|
return entry.chunk
|
|
}
|
|
|
|
boostType (type, amount = 20) {
|
|
for (const e of this._queue) {
|
|
if (e.chunk.type === type) e.priority += amount
|
|
}
|
|
this._sort()
|
|
return this._queue.length
|
|
}
|
|
|
|
enqueueBatch (chunks) {
|
|
if (!Array.isArray(chunks)) throw new Error('chunks must be an array')
|
|
return chunks.map((c) => this.enqueue(c.chunk || c, c.priority))
|
|
}
|
|
|
|
dropBelowPriority (minPriority) {
|
|
const before = this._queue.length
|
|
this._queue = this._queue.filter((e) => e.priority >= minPriority)
|
|
return before - this._queue.length
|
|
}
|
|
|
|
clear () {
|
|
const n = this._queue.length
|
|
this._queue = []
|
|
return n
|
|
}
|
|
|
|
getStats () {
|
|
return mediaStats(this._stats, PROTOCOL, { pending: this._queue.length })
|
|
}
|
|
|
|
async ready () { return this }
|
|
|
|
async close () {
|
|
this._queue = []
|
|
this.emit('closed')
|
|
}
|
|
}
|
|
|
|
module.exports = { HyperP2PChunkSchedulerMedia, PROTOCOL }
|