Skip to content
Merged

live #159

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions packages/core/src/player.ts
Original file line number Diff line number Diff line change
Expand Up @@ -484,16 +484,21 @@ export class Player<Context extends Record<string, any> = Record<string, any>> {
})
}

destroy() {
async destroy() {
Player.players.splice(Player.players.indexOf(this), 1)

const { eventEmitter, loader, plugins, container, $root, $video, isPlaying, isFullScreen, isInPip } = this

eventEmitter.emit('destroy')
eventEmitter.offAll()

loader?.destroy()
plugins.forEach((it) => !it.load && it.destroy?.())
await loader?.destroy()

for await (const plugin of plugins) {
if (!plugin.load && plugin?.destroy) {
await plugin.destroy()
}
}

if (isPlaying) this.pause()
if (isFullScreen) this.exitFullscreen()
Expand Down
28 changes: 7 additions & 21 deletions packages/docs/src/pages/components/player.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect, useRef, useState } from 'react'
import React, { useCallback, useEffect, useRef, useState } from 'react'
import Player, { PlayerPlugin } from '@oplayer/core'
import ui from '@oplayer/ui'
import hls from '@oplayer/hls'
Expand Down Expand Up @@ -26,11 +26,12 @@ const userPreferencesPlugin: PlayerPlugin = {
export default () => {
const player = useRef<Player>()
const [input, setInput] = useState(globalThis.location?.search.substring(1))
const [isFirst, setIsFirst] = useState(true)

useEffect(() => {
const createPlayer = useCallback((src: string) => {
player.current?.destroy()
player.current = Player.make('#oplayer', {
source: { src: input },
source: { src },
isLive: src.includes('live'),
playbackRate: +(localStorage.getItem('@oplayer/UserPreferences/speed') || 1),
volume: +(localStorage.getItem('@oplayer/UserPreferences/volume') || 1)
})
Expand All @@ -56,14 +57,6 @@ export default () => {
])
.create()
.on(console.log)

if (input) {
setIsFirst(false)
}

return () => {
player.current!.destroy()
}
}, [])

return (
Expand All @@ -81,10 +74,7 @@ export default () => {
className="nx-rounded-md"
type="button"
onClick={() => {
if (input) {
player.current!.changeSource({ src: input })
setIsFirst(false)
}
if (input) createPlayer(input)
}}
>
PLAY
Expand All @@ -94,11 +84,7 @@ export default () => {
Your streaming URL must be HTTPS-compatible, otherwise your stream may not play. Make sure CORS is
enabled on streaming server when using HLS and MPEG-DASH streams.
</p>
<div
id="oplayer"
className="nx-bg-black/[.05] dark:nx-bg-gray-50/10 nx-rounded-sm"
aria-disabled={isFirst}
/>
<div id="oplayer" className="nx-bg-black/[.05] dark:nx-bg-gray-50/10 nx-rounded-sm" />
</div>
)
}
1 change: 1 addition & 0 deletions packages/docs/src/pages/docs/_meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"hls": "@oplayer/hls",
"dash": "@oplayer/dash",
"mpegts": "@oplayer/mpegts",
"shaka": "@oplayer/shaka",
"torrent": "@oplayer/torrent",

"-- More": {
Expand Down
3 changes: 3 additions & 0 deletions packages/docs/src/pages/docs/shaka.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import Md from '../../../../shaka/README.md'

<Md />
2 changes: 1 addition & 1 deletion packages/shaka/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
## Install

```bash
npm i @oplayer/hls shaka-player
npm i @oplayer/shaka shaka-player
```

```html
Expand Down
2 changes: 1 addition & 1 deletion packages/shaka/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@oplayer/shaka",
"version": "1.2.26-beta.1",
"version": "1.2.26-beta.2",
"description": "shaka-player plugin for oplayer",
"type": "module",
"main": "./dist/index.es.js",
Expand Down
89 changes: 71 additions & 18 deletions packages/shaka/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { loadSDK, PartialRequired, type Player, type PlayerPlugin, type Source } from '@oplayer/core'
//@ts-ignore
import type Shaka from 'shaka-player'
import type shaka from 'shaka-player'

export type Matcher = (source: Source) => boolean

Expand Down Expand Up @@ -42,7 +42,7 @@ class ShakaPlugin implements PlayerPlugin {

player!: Player

instance?: shaka.Player
instance?: shaka.Player & { eventManager: shaka.util.EventManager }

options: PartialRequired<ShakaPluginOptions, 'matcher'> = {
matcher: defaultMatcher,
Expand Down Expand Up @@ -79,7 +79,10 @@ class ShakaPlugin implements PlayerPlugin {

if (!ShakaPlayer.isBrowserSupported()) return false

this.instance = new ShakaPlayer()
this.instance = new ShakaPlayer() as unknown as shaka.Player & {
eventManager: shaka.util.EventManager
timer: any
}
await this.instance.attach(player.$video)

if (config) {
Expand All @@ -90,8 +93,7 @@ class ShakaPlugin implements PlayerPlugin {
this.instance.getNetworkingEngine()?.registerRequestFilter(requestFilter)
}

// TODO: listen quality/audio/text change
const eventManager = new ShakaPlugin.library.util.EventManager()
const eventManager = (this.instance.eventManager = new ShakaPlugin.library.util.EventManager())

eventManager.listen(this.instance, 'loaded', (event) => {
player.emit('canplay', event)
Expand All @@ -113,20 +115,52 @@ class ShakaPlugin implements PlayerPlugin {

if (player.options.isLive) {
const button = player.$root.querySelector('[aria-label="time"')?.parentElement
const dot = button?.firstElementChild as HTMLSpanElement

if (button) {
eventManager.listen(button, 'click', () => {
player.seek(this.instance?.seekRange().end || 0)
player.$video.currentTime = this.seekRange.end
})
}

//TODO: seekable
player.listeners.seeking = () => {
// console.log('seeking')
//TODO: revert
// Object.defineProperty(player, 'duration', {
// get: () => {
// if (this.instance) return this._duration
// return player.$video.duration
// }
// })
Object.defineProperty(player.$video, 'duration', {
get: () => {
if (this.instance) return this._duration
return player.$video.duration
}
})
Object.defineProperty(player, 'currentTime', {
get: () => {
if (this.instance) return this.getCurrentTime()
else return player.$video.currentTime
}
})
Object.defineProperty(player, 'seek', {
value: (v: number) => {
if (this.instance) player.$video.currentTime = this.seekRange.start + v
else player.$video.currentTime = v
}
})

const updateIsLive = () => {
const timeBehindLiveEdge = this.seekRange.end - player.$video.currentTime
// var streamPosition = Date.now() / 1000 - timeBehindLiveEdge

if (timeBehindLiveEdge > 5) {
dot.style.backgroundColor = '#ccc'
} else {
dot.style.cssText = ''
}
}
// player.listeners.waiting = () => {
// console.log('waiting')
// }
Object.defineProperty(player, 'currentTime', () => 0)

this.instance.eventManager.listen(player.$video, 'timeupdate', updateIsLive)
}

if (player.context.ui) {
Expand All @@ -152,12 +186,31 @@ class ShakaPlugin implements PlayerPlugin {
return this
}

destroy() {
getCurrentTime() {
if (!this.instance) return 0
const mediaElement = this.instance.getMediaElement()
return mediaElement ? mediaElement.currentTime - this.seekRange.start : 0
}

get seekRange() {
if (!this.instance) return { start: 0, end: 0 }
return this.instance.seekRange()
}

get _duration() {
if (!this.instance) return 0

return this.seekRange.end - this.seekRange.start
}

async destroy() {
;['Quality', 'Language', 'Subtitle'].forEach((it) =>
this.player.context.ui.setting.unregister(`${ShakaPlugin.name}-${it}`)
)
this.instance?.unload()
this.instance?.destroy()
this.instance?.eventManager.removeAll()
await this.instance?.unload()
await this.instance?.destroy()
this.instance = undefined
}
}

Expand All @@ -169,7 +222,7 @@ const setupQuality = (player: Player, instance: shaka.Player) => {
// https://github.com/shaka-project/shaka-player/blob/1f336dd319ad23a6feb785f2ab05a8bc5fc8e2a2/ui/resolution_selection.js#L90
let tracks: shaka.extern.Track[] = []

if (instance.getLoadMode() != shaka.Player.LoadMode.SRC_EQUALS) {
if (instance.getLoadMode() != ShakaPlugin.library.Player.LoadMode.SRC_EQUALS) {
tracks = instance.getVariantTracks()
}

Expand Down Expand Up @@ -375,7 +428,7 @@ function settingUpdater(arg: {
})
}

function getResolutionLabel_(track: Shaka.extern.Track, tracks: Shaka.extern.Track[]) {
function getResolutionLabel_(track: shaka.extern.Track, tracks: shaka.extern.Track[]) {
const trackHeight = track.height || 0
const trackWidth = track.width || 0
let height = trackHeight
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@oplayer/ui",
"version": "1.3.4-beta.5",
"version": "1.3.4-beta.6",
"description": "ui plugin for oplayer",
"type": "module",
"main": "./dist/index.es.js",
Expand Down
9 changes: 6 additions & 3 deletions packages/ui/src/components/ControllerBottom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,11 +202,14 @@ const render = (it: UIInterface, $el: HTMLDivElement) => {
switcher($play, player.isPlaying)
})

const LIVE = player.locales.get('LIVE')
player.on('volumechange', () => switcher($volume, player.isMuted))
player.on(['durationchange', 'timeupdate', 'seeking', 'seeked'], () => {
$time.innerText = `${player.currentTime || !player.options.isLive ? formatTime(player.currentTime) : 'Live'} ${
player.options.isLive ? '' : `/ ${formatTime(player.duration)}`
}`
if (player.options.isLive) {
$time.innerText = `${LIVE} ${formatTime(player.currentTime < 0 ? player.duration : player.currentTime)}`
return
}
$time.innerText = `${formatTime(player.currentTime)} / ${formatTime(player.duration)}`
})

player.on('videosourcechange', () => {
Expand Down
17 changes: 3 additions & 14 deletions packages/ui/src/components/Progress.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,11 @@ import { UIInterface } from '../types'
import { DRAG_EVENT_MAP, formatTime } from '../utils'
import renderHighlight, { highlightCls } from './highlight'
import renderThumbnail, { vttThumbnailsCls } from './Thumbnail'
import {
buffered,
dot,
hit,
played,
progress,
progressDragging,
progressInner
} from './Progress.style'
import { buffered, dot, hit, played, progress, progressDragging, progressInner } from './Progress.style'

const render = (it: UIInterface, el: HTMLElement) => {
const { player, config } = it

if (player.options.isLive) return

const $dom = (it.$progress = $.create(
`div.${progress}`,
{},
Expand Down Expand Up @@ -53,8 +43,7 @@ const render = (it: UIInterface, el: HTMLElement) => {
const getSlidingValue = (event: MouseEvent | TouchEvent) => {
const rect = $dom.getBoundingClientRect()
const value =
(((<MouseEvent>event).clientX || (<TouchEvent>event).changedTouches[0]!.clientX) -
rect.left) /
(((<MouseEvent>event).clientX || (<TouchEvent>event).changedTouches[0]!.clientX) - rect.left) /
rect.width
return value >= 1 ? 1 : value <= 0 ? 0 : value
}
Expand Down Expand Up @@ -137,7 +126,7 @@ const render = (it: UIInterface, el: HTMLElement) => {
const buffered = player.buffered.length
? (player.buffered.end(player.buffered.length - 1) / player.duration) * 100
: 0
$buffered.style.width = buffered + '%'
$buffered.style.width = Math.min(buffered, 100) + '%'
})

player.on('videosourcechange', () => {
Expand Down