mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-09 10:34:17 +00:00
Refactor live stream page and add Shaka decoder
- Move files for `/tv/live/[id]` route into a dedicated directory structure. - Introduce Shaka player decoder for HLS playback. - Remove deprecated FLV decoder (mpegts.js). - Add basic chat page placeholder. - Improve cleanup logic for decoder instances.
This commit is contained in:
parent
68af8c6f93
commit
87f746d5b9
7
packages/app/src/pages/tv/live/[id]/chat/index.jsx
Normal file
7
packages/app/src/pages/tv/live/[id]/chat/index.jsx
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import React from "react"
|
||||||
|
|
||||||
|
const ChatPage = () => {
|
||||||
|
return <div>Chat</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ChatPage
|
@ -40,7 +40,6 @@ export default (player, sources = {}, options = {}) => {
|
|||||||
source += `?token=${options.authToken}`
|
source += `?token=${options.authToken}`
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("[HLS] Instance options >", options)
|
|
||||||
console.log(`[HLS] Loading source [${source}]`)
|
console.log(`[HLS] Loading source [${source}]`)
|
||||||
|
|
||||||
hlsInstance.attachMedia(player)
|
hlsInstance.attachMedia(player)
|
2
packages/app/src/pages/tv/live/[id]/decoders/index.js
Normal file
2
packages/app/src/pages/tv/live/[id]/decoders/index.js
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export { default as hls } from "./hls"
|
||||||
|
export { default as shaka } from "./shaka"
|
100
packages/app/src/pages/tv/live/[id]/decoders/shaka.js
Normal file
100
packages/app/src/pages/tv/live/[id]/decoders/shaka.js
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
import shaka from "shaka-player"
|
||||||
|
|
||||||
|
export default async (player, sources = {}, options = {}) => {
|
||||||
|
if (!player) {
|
||||||
|
console.error("[Shaka] player is not defined")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sources.hls) {
|
||||||
|
console.error("[Shaka] an hls source is not provided")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
let source = sources.hls
|
||||||
|
|
||||||
|
// Initialize shaka player
|
||||||
|
const shakaInstance = new shaka.Player(player)
|
||||||
|
|
||||||
|
// Helper function to sync to live edge
|
||||||
|
const syncToLive = () => {
|
||||||
|
if (shakaInstance.isLive()) {
|
||||||
|
const end = shakaInstance.seekRange().end
|
||||||
|
player.currentTime = end
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure for low-latency HLS
|
||||||
|
shakaInstance.configure({
|
||||||
|
streaming: {
|
||||||
|
lowLatencyMode: true,
|
||||||
|
inaccurateManifestTolerance: 0,
|
||||||
|
rebufferingGoal: 0.01,
|
||||||
|
bufferingGoal: 0.1,
|
||||||
|
bufferBehind: 30,
|
||||||
|
startAtSegmentBoundary: false,
|
||||||
|
durationBackoff: 0.2,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// Add request filter for authentication if token is provided
|
||||||
|
if (options.authToken) {
|
||||||
|
shakaInstance
|
||||||
|
.getNetworkingEngine()
|
||||||
|
.registerRequestFilter((type, request) => {
|
||||||
|
request.headers = {
|
||||||
|
...request.headers,
|
||||||
|
Authorization: `Bearer ${options.authToken}`,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
source += `?token=${options.authToken}`
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("[Shaka] Instance options >", options)
|
||||||
|
console.log(`[Shaka] Loading source [${source}]`)
|
||||||
|
|
||||||
|
// Error handling
|
||||||
|
shakaInstance.addEventListener("error", (error) => {
|
||||||
|
console.error("[Shaka] Error", error)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Buffer state monitoring
|
||||||
|
player.addEventListener("waiting", () => {
|
||||||
|
console.log("[Shaka] Buffer underrun")
|
||||||
|
})
|
||||||
|
|
||||||
|
// Handle stream end
|
||||||
|
player.addEventListener("ended", () => {
|
||||||
|
console.log("[Shaka] Stream ended")
|
||||||
|
if (typeof options.onSourceEnd === "function") {
|
||||||
|
options.onSourceEnd()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
try {
|
||||||
|
await shakaInstance.load(source)
|
||||||
|
console.log("[Shaka] Stream loaded successfully")
|
||||||
|
|
||||||
|
const tracks = shakaInstance.getVariantTracks()
|
||||||
|
console.log("[Shaka] Available qualities >", tracks)
|
||||||
|
} catch (error) {
|
||||||
|
console.error("[Shaka] Error loading stream:", error)
|
||||||
|
}
|
||||||
|
|
||||||
|
player.addEventListener("play", () => {
|
||||||
|
console.log("[SHAKA] Syncing to last position")
|
||||||
|
syncToLive()
|
||||||
|
})
|
||||||
|
|
||||||
|
// Add destroy method for cleanup
|
||||||
|
shakaInstance._destroy = () => {
|
||||||
|
try {
|
||||||
|
shakaInstance.unload()
|
||||||
|
shakaInstance.destroy()
|
||||||
|
} catch (error) {
|
||||||
|
console.error("[Shaka] Error during cleanup:", error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return shakaInstance
|
||||||
|
}
|
@ -29,7 +29,7 @@ async function fetchStream(stream_id) {
|
|||||||
stream = stream[0]
|
stream = stream[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!stream.sources) {
|
if (!stream.sources || !stream.sources.hls) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,6 +60,8 @@ export default class StreamViewer extends React.Component {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(`[TV] Switching decoder to: ${decoder}`)
|
||||||
|
|
||||||
await this.toggleLoading(true)
|
await this.toggleLoading(true)
|
||||||
|
|
||||||
// check if decoder is already loaded
|
// check if decoder is already loaded
|
||||||
@ -71,8 +73,6 @@ export default class StreamViewer extends React.Component {
|
|||||||
this.setState({ decoderInstance: null })
|
this.setState({ decoderInstance: null })
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`[TV] Switching decoder to: ${decoder}`)
|
|
||||||
|
|
||||||
const decoderInstance = await Decoders[decoder](...args)
|
const decoderInstance = await Decoders[decoder](...args)
|
||||||
|
|
||||||
await this.setState({
|
await this.setState({
|
||||||
@ -236,8 +236,12 @@ export default class StreamViewer extends React.Component {
|
|||||||
spectators: stream.viewers,
|
spectators: stream.viewers,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
try {
|
||||||
// joinStreamWebsocket
|
// joinStreamWebsocket
|
||||||
await this.joinStreamWebsocket(stream)
|
this.joinStreamWebsocket(stream)
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
|
||||||
// load decoder with provided data
|
// load decoder with provided data
|
||||||
await this.loadDecoder(
|
await this.loadDecoder(
|
||||||
@ -260,6 +264,10 @@ export default class StreamViewer extends React.Component {
|
|||||||
this.state.decoderInstance.destroy()
|
this.state.decoderInstance.destroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (typeof this.state.decoderInstance?._destroy === "function") {
|
||||||
|
this.state.decoderInstance._destroy()
|
||||||
|
}
|
||||||
|
|
||||||
if (this.state.websocket) {
|
if (this.state.websocket) {
|
||||||
if (typeof this.state.websocket.destroy === "function") {
|
if (typeof this.state.websocket.destroy === "function") {
|
||||||
this.state.websocket.destroy()
|
this.state.websocket.destroy()
|
@ -1,29 +0,0 @@
|
|||||||
import mpegts from "mpegts.js"
|
|
||||||
|
|
||||||
export default async (player, source, { onSourceEnd } = {}) => {
|
|
||||||
if (!source) {
|
|
||||||
console.error("Stream source is not defined")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
const decoderInstance = mpegts.createPlayer({
|
|
||||||
type: "flv",
|
|
||||||
isLive: true,
|
|
||||||
enableWorker: true,
|
|
||||||
url: source,
|
|
||||||
})
|
|
||||||
|
|
||||||
if (typeof onSourceEnd === "function") {
|
|
||||||
decoderInstance.on(mpegts.Events.ERROR, onSourceEnd)
|
|
||||||
}
|
|
||||||
|
|
||||||
decoderInstance.attachMediaElement(player)
|
|
||||||
|
|
||||||
decoderInstance.load()
|
|
||||||
|
|
||||||
await decoderInstance.play().catch((error) => {
|
|
||||||
console.error(error)
|
|
||||||
})
|
|
||||||
|
|
||||||
return decoderInstance
|
|
||||||
}
|
|
@ -1,2 +0,0 @@
|
|||||||
export { default as hls } from "./hls"
|
|
||||||
export { default as flv } from "./flv"
|
|
Loading…
x
Reference in New Issue
Block a user