Diffusers
MuseTalk1.5 / server /web /src /hooks /useH264Decoder.js
Marcos
Add H.264 WebSocket streaming and React.js web interface
32bba92
Raw
History Blame Contribute Delete
1.93 kB
import { useState, useRef, useCallback, useEffect } from 'react'
export function useH264Decoder(canvasRef) {
const [isSupported] = useState('VideoDecoder' in window)
const decoderRef = useRef(null)
const frameCountRef = useRef(0)
const initDecoder = useCallback(async (width = 256, height = 256, onFrame) => {
if (!isSupported) return false
if (decoderRef.current) {
decoderRef.current.close()
}
decoderRef.current = new VideoDecoder({
output: (frame) => {
const canvas = canvasRef.current
if (canvas) {
const ctx = canvas.getContext('2d')
if (canvas.width !== frame.displayWidth) {
canvas.width = frame.displayWidth
canvas.height = frame.displayHeight
}
ctx.drawImage(frame, 0, 0)
}
frame.close()
frameCountRef.current++
onFrame?.(frameCountRef.current)
},
error: (e) => console.error('[H264 Decoder] Error:', e)
})
await decoderRef.current.configure({
codec: 'avc1.42E01E',
codedWidth: width,
codedHeight: height,
optimizeForLatency: true
})
return true
}, [isSupported, canvasRef])
const decodeFrame = useCallback((frameData, isKeyframe) => {
if (!decoderRef.current || decoderRef.current.state === 'closed') return
try {
const chunk = new EncodedVideoChunk({
type: isKeyframe ? 'key' : 'delta',
timestamp: frameCountRef.current * 40000,
data: frameData
})
decoderRef.current.decode(chunk)
} catch (e) {
console.error('[H264] Decode error:', e)
}
}, [])
const close = useCallback(() => {
if (decoderRef.current) {
decoderRef.current.close()
decoderRef.current = null
}
frameCountRef.current = 0
}, [])
useEffect(() => {
return () => close()
}, [close])
return { isSupported, initDecoder, decodeFrame, close }
}