mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-26 17:01:20 -08:00
fix(danmuku): [PKG-DANMUKU-12] keep frame sampling during async placement
This commit is contained in:
1 parent
df6a6fe82e
commit
4f559cb31d
20 files changed
+6192
-64
No files matched your search
@@ -1,5 +1,6 @@
|
||||
import type Danmuku from './danmuku'
|
||||
import type { DanmuItem } from './types'
|
||||
import SchedulingBuffer from './scheduling-buffer'
|
||||
|
||||
const cancelled = Symbol('cancelled danmuku frame')
|
||||
|
||||
@@ -21,6 +22,7 @@ export default class Scheduler {
|
||||
declare running: boolean
|
||||
declare fault: boolean
|
||||
declare failedItems: Set<DanmuItem>
|
||||
declare buffer: SchedulingBuffer
|
||||
|
||||
constructor(owner: Danmuku) {
|
||||
this.owner = owner
|
||||
@@ -32,6 +34,7 @@ export default class Scheduler {
|
||||
this.running = false
|
||||
this.fault = false
|
||||
this.failedItems = new Set()
|
||||
this.buffer = new SchedulingBuffer()
|
||||
}
|
||||
|
||||
active(operation: FrameOperation) {
|
||||
@@ -58,6 +61,7 @@ export default class Scheduler {
|
||||
}
|
||||
this.owner.workerClient?.cancel()
|
||||
this.failedItems.clear()
|
||||
this.buffer.clear()
|
||||
}
|
||||
|
||||
report(error: unknown) {
|
||||
@@ -95,43 +99,62 @@ export default class Scheduler {
|
||||
|
||||
schedule() {
|
||||
const owner = this.owner
|
||||
if (!this.running || this.closed || this.fault || owner.art.isDestroy || owner.isStop || this.frame !== null || this.operation)
|
||||
if (!this.running || this.closed || this.fault || owner.art.isDestroy || owner.isStop || this.frame !== null)
|
||||
return
|
||||
this.frame = window.requestAnimationFrame(() => {
|
||||
this.frame = null
|
||||
owner.timer = null
|
||||
if (this.closed || this.fault || owner.isStop || owner.art.isDestroy)
|
||||
return
|
||||
const generation = this.generation
|
||||
try {
|
||||
if (owner.art.playing && !owner.isHide) {
|
||||
owner.filter('emit', (danmu) => {
|
||||
const now = Date.now()
|
||||
danmu.$restTime -= (now - danmu.$lastStartTime) / 1000
|
||||
danmu.$lastStartTime = now
|
||||
if (danmu.$restTime <= 0)
|
||||
owner.makeWait(danmu)
|
||||
})
|
||||
const readys = owner.readys
|
||||
if (generation !== this.generation)
|
||||
return
|
||||
this.buffer.capture(readys, this.failedItems)
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
if (generation === this.generation)
|
||||
this.fail(error)
|
||||
return
|
||||
}
|
||||
this.schedule()
|
||||
if (this.operation || !owner.art.playing || owner.isHide)
|
||||
return
|
||||
const readys = this.buffer.take()
|
||||
if (!readys.length)
|
||||
return
|
||||
let cancel!: () => void
|
||||
const cancellation = new Promise<typeof cancelled>(resolve => cancel = () => resolve(cancelled))
|
||||
const operation: FrameOperation = { generation: this.generation, cancel, wait: value => Promise.race([value, cancellation]), ref: null }
|
||||
this.operation = operation
|
||||
return this.run(operation).catch((error) => {
|
||||
return this.run(operation, readys).catch((error) => {
|
||||
if (this.active(operation))
|
||||
this.fail(error)
|
||||
}).finally(() => {
|
||||
this.release(operation)
|
||||
if (this.operation === operation) {
|
||||
this.operation = null
|
||||
this.schedule()
|
||||
this.buffer.complete()
|
||||
}
|
||||
})
|
||||
})
|
||||
owner.timer = this.frame
|
||||
}
|
||||
|
||||
async run(operation: FrameOperation) {
|
||||
async run(operation: FrameOperation, readys: DanmuItem[]) {
|
||||
const owner = this.owner
|
||||
if (!owner.art.playing || !this.active(operation))
|
||||
return
|
||||
owner.filter('emit', (danmu) => {
|
||||
const emitTime = (Date.now() - danmu.$lastStartTime) / 1000
|
||||
danmu.$restTime -= emitTime
|
||||
danmu.$lastStartTime = Date.now()
|
||||
if (danmu.$restTime <= 0)
|
||||
owner.makeWait(danmu)
|
||||
})
|
||||
const readys = owner.readys
|
||||
for (const danmu of readys) {
|
||||
if (!this.active(operation))
|
||||
return
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import type { DanmuItem } from './types'
|
||||
|
||||
// Sampling owns references only; the public state pools stay under Danmuku's control.
|
||||
export default class SchedulingBuffer {
|
||||
private pending = new Set<DanmuItem>()
|
||||
private reserved = new Set<DanmuItem>()
|
||||
|
||||
capture(items: DanmuItem[], failed: Set<DanmuItem>) {
|
||||
for (const item of items) {
|
||||
if (!this.reserved.has(item) && !failed.has(item))
|
||||
this.pending.add(item)
|
||||
}
|
||||
}
|
||||
|
||||
take() {
|
||||
const ready: DanmuItem[] = []
|
||||
const waiting: DanmuItem[] = []
|
||||
for (const item of this.pending) {
|
||||
if (item.$state === 'ready')
|
||||
ready.push(item)
|
||||
else if (item.$state === 'wait')
|
||||
waiting.push(item)
|
||||
}
|
||||
this.pending.clear()
|
||||
const batch = [...ready, ...waiting]
|
||||
this.reserved = new Set(batch)
|
||||
return batch
|
||||
}
|
||||
|
||||
complete() {
|
||||
this.reserved.clear()
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.pending.clear()
|
||||
this.reserved.clear()
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user