mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-29 19:41:25 -08:00
feat: enable fullscreenWeb option in Artplayer configuration
feat: add danmuku plugin and i18n setup documentation - Introduced a new danmuku plugin with multiple run code options in the documentation. - Added language settings documentation for i18n, including default languages, import instructions, and examples for adding and modifying languages. - Implemented input handling for media sources in the artplayer-proxy-mediabunny package, supporting various formats including HLS. - Created m3u8 control functionalities for managing audio and video quality settings in the player. Update mediabunny.js
This commit is contained in:
1 parent
23c8f46878
commit
26841c5b1c
69 files changed
+49762
-28126
No files matched your search
@@ -3,6 +3,7 @@
|
||||
* Coordinates audio and video playback
|
||||
*/
|
||||
import AudioEngine from './AudioEngine.js'
|
||||
import { createInput, getHlsState, resolveDuration, selectPlaybackTracks } from './input.js'
|
||||
import VideoEngine from './VideoEngine.js'
|
||||
|
||||
export default class MediaBunnyEngine {
|
||||
@@ -31,6 +32,8 @@ export default class MediaBunnyEngine {
|
||||
this.error = null
|
||||
this.seeking = false
|
||||
this.loadSeq = 0
|
||||
this.input = null
|
||||
this.media = null
|
||||
|
||||
// Listen to ended event
|
||||
events.addEventListener?.('ended', () => {
|
||||
@@ -43,6 +46,7 @@ export default class MediaBunnyEngine {
|
||||
const id = ++this.loadSeq
|
||||
|
||||
this.pause()
|
||||
this.disposeInput()
|
||||
this.ended = false
|
||||
this.error = null
|
||||
this.networkState = 2 // NETWORK_LOADING
|
||||
@@ -73,8 +77,28 @@ export default class MediaBunnyEngine {
|
||||
}
|
||||
|
||||
async performLoad(src, id) {
|
||||
let videoMetadataLoaded = false
|
||||
let audioMetadataLoaded = false
|
||||
if (!(await this.video.preflight(src)))
|
||||
return
|
||||
|
||||
const input = createInput(src)
|
||||
if (!input) {
|
||||
this.video.handleNoVideoTrack()
|
||||
this.audio.handleNoAudioTrack()
|
||||
return
|
||||
}
|
||||
|
||||
const media = await selectPlaybackTracks(input, src)
|
||||
if (id !== this.loadSeq) {
|
||||
input.dispose()
|
||||
return
|
||||
}
|
||||
|
||||
this.disposeInput()
|
||||
this.input = input
|
||||
this.media = media
|
||||
|
||||
let videoMetadataLoaded = !media.videoTrack
|
||||
let audioMetadataLoaded = !media.audioTrack
|
||||
|
||||
const checkMetadata = () => {
|
||||
if (videoMetadataLoaded && audioMetadataLoaded) {
|
||||
@@ -87,13 +111,13 @@ export default class MediaBunnyEngine {
|
||||
|
||||
try {
|
||||
await Promise.all([
|
||||
this.video.load(src, () => {
|
||||
this.video.load(media, () => {
|
||||
if (id !== this.loadSeq)
|
||||
return
|
||||
videoMetadataLoaded = true
|
||||
checkMetadata()
|
||||
}),
|
||||
this.audio.load(src, () => {
|
||||
this.audio.load(media, () => {
|
||||
if (id !== this.loadSeq)
|
||||
return
|
||||
audioMetadataLoaded = true
|
||||
@@ -128,6 +152,124 @@ export default class MediaBunnyEngine {
|
||||
})
|
||||
}
|
||||
|
||||
disposeInput() {
|
||||
this.input?.dispose()
|
||||
this.input = null
|
||||
this.media = null
|
||||
}
|
||||
|
||||
async replaceTracks({ videoTrack, audioTrack, videoMode, audioMode }) {
|
||||
if (!this.media)
|
||||
return
|
||||
|
||||
const nextMedia = {
|
||||
...this.media,
|
||||
videoTrack,
|
||||
audioTrack,
|
||||
videoMode,
|
||||
audioMode,
|
||||
}
|
||||
|
||||
const shouldResume = !this.paused
|
||||
const currentTime = this.currentTime
|
||||
|
||||
this.pause()
|
||||
this.ended = false
|
||||
this.seeking = true
|
||||
this.events.emit('seeking')
|
||||
this.events.emit('waiting')
|
||||
|
||||
nextMedia.duration = await resolveDuration(nextMedia)
|
||||
this.media = nextMedia
|
||||
|
||||
await Promise.all([
|
||||
this.video.load(nextMedia),
|
||||
this.audio.load(nextMedia),
|
||||
])
|
||||
|
||||
await Promise.all([
|
||||
this.video.seek(currentTime),
|
||||
this.audio.seek(currentTime),
|
||||
])
|
||||
|
||||
this.readyState = 4
|
||||
this.networkState = 1
|
||||
this.seeking = false
|
||||
this.events.emit('loadedmetadata')
|
||||
this.events.emit('durationchange')
|
||||
this.events.emit('progress')
|
||||
this.events.emit('loadeddata')
|
||||
this.events.emit('canplay')
|
||||
this.events.emit('canplaythrough')
|
||||
this.events.emit('seeked')
|
||||
|
||||
if (shouldResume) {
|
||||
await this.play()
|
||||
}
|
||||
}
|
||||
|
||||
async selectHlsQuality(value) {
|
||||
if (!this.media?.isHls || !this.input)
|
||||
return
|
||||
|
||||
const videoTracks = await this.input.getVideoTracks()
|
||||
const videoTrack = value === 'auto'
|
||||
? await this.input.getPrimaryVideoTrack()
|
||||
: videoTracks.find(track => track.id === value) ?? this.media.videoTrack
|
||||
|
||||
if (!videoTrack)
|
||||
return
|
||||
|
||||
let audioTrack = this.media.audioTrack
|
||||
let audioMode = this.media.audioMode
|
||||
|
||||
if (!audioTrack || !videoTrack.canBePairedWith(audioTrack)) {
|
||||
audioTrack = await videoTrack.getPrimaryPairableAudioTrack()
|
||||
audioMode = 'auto'
|
||||
}
|
||||
|
||||
await this.replaceTracks({
|
||||
videoTrack,
|
||||
audioTrack,
|
||||
videoMode: value === 'auto' ? 'auto' : 'manual',
|
||||
audioMode,
|
||||
})
|
||||
}
|
||||
|
||||
async selectHlsAudio(value) {
|
||||
if (!this.media?.isHls || !this.input)
|
||||
return
|
||||
|
||||
const audioTracks = await this.input.getAudioTracks()
|
||||
const audioTrack = value === 'auto'
|
||||
? this.media.videoTrack
|
||||
? await this.media.videoTrack.getPrimaryPairableAudioTrack()
|
||||
: await this.input.getPrimaryAudioTrack()
|
||||
: audioTracks.find(track => track.id === value) ?? this.media.audioTrack
|
||||
|
||||
if (!audioTrack)
|
||||
return
|
||||
|
||||
let videoTrack = this.media.videoTrack
|
||||
let videoMode = this.media.videoMode
|
||||
|
||||
if (!videoTrack || !audioTrack.canBePairedWith(videoTrack)) {
|
||||
videoTrack = await audioTrack.getPrimaryPairableVideoTrack()
|
||||
videoMode = 'auto'
|
||||
}
|
||||
|
||||
await this.replaceTracks({
|
||||
videoTrack,
|
||||
audioTrack,
|
||||
videoMode,
|
||||
audioMode: value === 'auto' ? 'auto' : 'manual',
|
||||
})
|
||||
}
|
||||
|
||||
getHlsState() {
|
||||
return getHlsState(this.media)
|
||||
}
|
||||
|
||||
async play() {
|
||||
if (!this.paused)
|
||||
return
|
||||
@@ -193,6 +335,7 @@ export default class MediaBunnyEngine {
|
||||
|
||||
destroy() {
|
||||
this.pause()
|
||||
this.disposeInput()
|
||||
this.audio.destroy()
|
||||
this.video.destroy()
|
||||
}
|
||||
@@ -203,7 +346,7 @@ export default class MediaBunnyEngine {
|
||||
}
|
||||
|
||||
get duration() {
|
||||
return this.audio.duration || this.video.duration
|
||||
return this.media?.duration ?? this.audio.duration ?? this.video.duration
|
||||
}
|
||||
|
||||
get videoWidth() {
|
||||
|
||||
Reference in new issue
Block a user