mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-09 02:24:16 +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}`
|
||||
}
|
||||
|
||||
console.log("[HLS] Instance options >", options)
|
||||
console.log(`[HLS] Loading source [${source}]`)
|
||||
|
||||
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]
|
||||
}
|
||||
|
||||
if (!stream.sources) {
|
||||
if (!stream.sources || !stream.sources.hls) {
|
||||
return false
|
||||
}
|
||||
|
||||
@ -60,6 +60,8 @@ export default class StreamViewer extends React.Component {
|
||||
return false
|
||||
}
|
||||
|
||||
console.log(`[TV] Switching decoder to: ${decoder}`)
|
||||
|
||||
await this.toggleLoading(true)
|
||||
|
||||
// check if decoder is already loaded
|
||||
@ -71,8 +73,6 @@ export default class StreamViewer extends React.Component {
|
||||
this.setState({ decoderInstance: null })
|
||||
}
|
||||
|
||||
console.log(`[TV] Switching decoder to: ${decoder}`)
|
||||
|
||||
const decoderInstance = await Decoders[decoder](...args)
|
||||
|
||||
await this.setState({
|
||||
@ -236,8 +236,12 @@ export default class StreamViewer extends React.Component {
|
||||
spectators: stream.viewers,
|
||||
})
|
||||
|
||||
// joinStreamWebsocket
|
||||
await this.joinStreamWebsocket(stream)
|
||||
try {
|
||||
// joinStreamWebsocket
|
||||
this.joinStreamWebsocket(stream)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
|
||||
// load decoder with provided data
|
||||
await this.loadDecoder(
|
||||
@ -260,6 +264,10 @@ export default class StreamViewer extends React.Component {
|
||||
this.state.decoderInstance.destroy()
|
||||
}
|
||||
|
||||
if (typeof this.state.decoderInstance?._destroy === "function") {
|
||||
this.state.decoderInstance._destroy()
|
||||
}
|
||||
|
||||
if (this.state.websocket) {
|
||||
if (typeof this.state.websocket.destroy === "function") {
|
||||
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