mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-08 19:06:15 -08:00
224 lines
4.6 KiB
JavaScript
224 lines
4.6 KiB
JavaScript
import createAudioEngine from './audioEngine.js'
|
|
import createVideoEngine from './videoEngine.js'
|
|
|
|
export default function createMediabunnyEngine({
|
|
canvas,
|
|
ctx,
|
|
events,
|
|
option = {},
|
|
}) {
|
|
const defaults = {
|
|
timeupdateInterval: 250,
|
|
avSyncTolerance: 0.12,
|
|
loadTimeout: 12000,
|
|
dropLateFrames: false,
|
|
poster: '',
|
|
}
|
|
|
|
const audio = createAudioEngine(events)
|
|
const video = createVideoEngine({
|
|
canvas,
|
|
ctx,
|
|
events,
|
|
timeupdateInterval: option.timeupdateInterval ?? defaults.timeupdateInterval,
|
|
avSyncTolerance: option.avSyncTolerance ?? defaults.avSyncTolerance,
|
|
dropLateFrames: option.dropLateFrames ?? defaults.dropLateFrames,
|
|
poster: option.poster ?? defaults.poster,
|
|
preflightRange: option.preflightRange ?? false,
|
|
})
|
|
|
|
let _paused = true
|
|
let _ended = false
|
|
let _readyState = 0
|
|
let _networkState = 0
|
|
let _error = null
|
|
let _seeking = false
|
|
let loadSeq = 0
|
|
|
|
events.addEventListener?.('ended', () => {
|
|
_ended = true
|
|
_paused = true
|
|
})
|
|
|
|
async function load(src) {
|
|
const id = ++loadSeq
|
|
pause()
|
|
_ended = false
|
|
_error = null
|
|
_networkState = 2
|
|
_readyState = 0
|
|
setTimeout(() => events.emit('waiting'), 0)
|
|
setTimeout(() => events.emit('loadstart'), 0)
|
|
|
|
const loadTimeout = Number.isFinite(option.loadTimeout)
|
|
? option.loadTimeout
|
|
: defaults.loadTimeout
|
|
|
|
let timer = null
|
|
const timeoutPromise
|
|
= loadTimeout > 0
|
|
? new Promise((_, reject) => {
|
|
timer = setTimeout(() => reject(new Error('Load timeout')), loadTimeout)
|
|
})
|
|
: null
|
|
|
|
const loadTask = (async () => {
|
|
try {
|
|
let videoMetadataLoaded = false
|
|
let audioMetadataLoaded = false
|
|
|
|
const checkMetadata = () => {
|
|
if (videoMetadataLoaded && audioMetadataLoaded) {
|
|
_readyState = 1
|
|
events.emit('loadedmetadata')
|
|
events.emit('durationchange')
|
|
}
|
|
}
|
|
|
|
const p1 = video.load(src, () => {
|
|
if (id !== loadSeq)
|
|
return
|
|
videoMetadataLoaded = true
|
|
checkMetadata()
|
|
})
|
|
|
|
const p2 = audio.load(src, () => {
|
|
if (id !== loadSeq)
|
|
return
|
|
audioMetadataLoaded = true
|
|
checkMetadata()
|
|
})
|
|
|
|
await Promise.all([p1, p2])
|
|
if (id !== loadSeq)
|
|
return
|
|
|
|
_readyState = 4
|
|
_networkState = 1
|
|
events.emit('loadeddata')
|
|
events.emit('canplay')
|
|
events.emit('canplaythrough')
|
|
}
|
|
catch (err) {
|
|
if (id !== loadSeq)
|
|
return
|
|
_error = { code: 4, message: err.message }
|
|
_networkState = 3
|
|
events.emit('error')
|
|
console.error(err)
|
|
}
|
|
finally {
|
|
if (timer)
|
|
clearTimeout(timer)
|
|
}
|
|
})()
|
|
|
|
if (timeoutPromise) {
|
|
try {
|
|
await Promise.race([loadTask, timeoutPromise])
|
|
}
|
|
catch (err) {
|
|
if (id !== loadSeq)
|
|
return
|
|
loadSeq++
|
|
_error = { code: 4, message: err.message }
|
|
_networkState = 3
|
|
events.emit('error')
|
|
}
|
|
}
|
|
else {
|
|
await loadTask
|
|
}
|
|
}
|
|
|
|
async function play() {
|
|
if (!_paused)
|
|
return
|
|
if (_ended) {
|
|
_ended = false
|
|
await seek(0)
|
|
}
|
|
_paused = false
|
|
await audio.play()
|
|
video.start(audio)
|
|
events.emit('play')
|
|
events.emit('playing')
|
|
}
|
|
|
|
function pause() {
|
|
if (_paused)
|
|
return
|
|
_paused = true
|
|
audio.pause()
|
|
video.stop()
|
|
events.emit('pause')
|
|
}
|
|
|
|
async function seek(time) {
|
|
const shouldResume = !_paused
|
|
_ended = false
|
|
_seeking = true
|
|
events.emit('seeking')
|
|
events.emit('waiting')
|
|
pause()
|
|
await audio.seek(time)
|
|
await video.seek(time)
|
|
_seeking = false
|
|
events.emit('seeked')
|
|
if (shouldResume && !_ended) {
|
|
await play()
|
|
}
|
|
}
|
|
|
|
function destroy() {
|
|
pause()
|
|
audio.destroy()
|
|
video.destroy()
|
|
}
|
|
|
|
return {
|
|
load,
|
|
play,
|
|
pause,
|
|
seek,
|
|
destroy,
|
|
|
|
setVolume: audio.setVolume,
|
|
setPlaybackRate(rate) {
|
|
audio.setPlaybackRate(rate)
|
|
video.setPlaybackRate(rate)
|
|
},
|
|
|
|
get currentTime() {
|
|
return audio.currentTime
|
|
},
|
|
get duration() {
|
|
return audio.duration || video.duration
|
|
},
|
|
get paused() {
|
|
return _paused
|
|
},
|
|
get seeking() {
|
|
return _seeking
|
|
},
|
|
get ended() {
|
|
return _ended
|
|
},
|
|
get readyState() {
|
|
return _readyState
|
|
},
|
|
get networkState() {
|
|
return _networkState
|
|
},
|
|
get error() {
|
|
return _error
|
|
},
|
|
get videoWidth() {
|
|
return video.width
|
|
},
|
|
get videoHeight() {
|
|
return video.height
|
|
},
|
|
}
|
|
}
|