fix crashing issue due to H264 if running at the same time.

This commit is contained in:
KingKDot
2026-05-21 21:11:10 -05:00
parent 0959c90e46
commit 3c3e20e079
3 changed files with 130 additions and 19 deletions
+39 -19
View File
@@ -418,6 +418,10 @@ var (
prevMu sync.Mutex
prevFrame *prevImage
hvncPrevMu sync.Mutex
hvncPrevFrame *prevImage
hvncLastKeyframe atomic.Int64
)
type prevImage struct {
@@ -713,14 +717,14 @@ func buildFrameHVNC(img *image.RGBA, display int, quality int) (wire.Frame, time
codec = "jpeg"
} else {
if webrtcpub.ConsumeKeyframeRequest() {
resetH264Encoder()
resetH264EncoderHVNC()
}
h264Bytes, err := encodeH264Frame(img)
h264Bytes, err := encodeH264FrameHVNC(img)
if err == nil && len(h264Bytes) > 0 {
prevMu.Lock()
copyPrev(img)
prevMu.Unlock()
lastKeyframe.Store(now.UnixNano())
hvncPrevMu.Lock()
copyPrevHVNC(img)
hvncPrevMu.Unlock()
hvncLastKeyframe.Store(now.UnixNano())
statFullFrames.Add(1)
frame := wire.Frame{Type: "frame", Header: wire.FrameHeader{Monitor: display, FPS: 0, Format: "h264", HVNC: true}, Data: h264Bytes}
return frame, time.Since(encStart), nil
@@ -737,16 +741,16 @@ func buildFrameHVNC(img *image.RGBA, display int, quality int) (wire.Frame, time
}
}
prevMu.Lock()
pf := prevFrame
prevMu.Unlock()
hvncPrevMu.Lock()
pf := hvncPrevFrame
hvncPrevMu.Unlock()
if pf == nil || pf.w != width || pf.h != height || now.Sub(time.Unix(0, lastKeyframe.Load())) > keyframeEvery {
if pf == nil || pf.w != width || pf.h != height || now.Sub(time.Unix(0, hvncLastKeyframe.Load())) > keyframeEvery {
jpegBytes, err := encodeJPEG(img, quality)
prevMu.Lock()
copyPrev(img)
prevMu.Unlock()
lastKeyframe.Store(now.UnixNano())
hvncPrevMu.Lock()
copyPrevHVNC(img)
hvncPrevMu.Unlock()
hvncLastKeyframe.Store(now.UnixNano())
statFullFrames.Add(1)
frame := wire.Frame{Type: "frame", Header: wire.FrameHeader{Monitor: display, FPS: 0, Format: "jpeg", HVNC: true}, Data: jpegBytes}
return frame, time.Since(encStart), err
@@ -772,10 +776,10 @@ func buildFrameHVNC(img *image.RGBA, display int, quality int) (wire.Frame, time
if changedRatio > maxBlockRatio {
jpegBytes, err := encodeJPEG(img, quality)
prevMu.Lock()
copyPrev(img)
prevMu.Unlock()
lastKeyframe.Store(now.UnixNano())
hvncPrevMu.Lock()
copyPrevHVNC(img)
hvncPrevMu.Unlock()
hvncLastKeyframe.Store(now.UnixNano())
statBlockFallbacks.Add(1)
statFullFrames.Add(1)
frame := wire.Frame{Type: "frame", Header: wire.FrameHeader{Monitor: display, FPS: 0, Format: "jpeg", HVNC: true}, Data: jpegBytes}
@@ -1315,6 +1319,22 @@ func copyPrev(img *image.RGBA) {
}
}
func copyPrevHVNC(img *image.RGBA) {
width := img.Bounds().Dx()
height := img.Bounds().Dy()
n := len(img.Pix)
if hvncPrevFrame != nil && cap(hvncPrevFrame.pix) >= n {
hvncPrevFrame.w = width
hvncPrevFrame.h = height
hvncPrevFrame.pix = hvncPrevFrame.pix[:n]
copy(hvncPrevFrame.pix, img.Pix)
} else {
buf := make([]byte, n)
copy(buf, img.Pix)
hvncPrevFrame = &prevImage{w: width, h: height, pix: buf}
}
}
func encodeBlockRaw(img *image.RGBA, x, y, w, h int) []byte {
stride := img.Stride
buf := make([]byte, w*h*4)
@@ -1470,7 +1490,7 @@ func sendBlackFrameHVNC(ctx context.Context, env *rt.Env) error {
img := image.NewRGBA(image.Rect(0, 0, 64, 64))
quality := 60
frame, _, err := buildFrame(img, 0, quality)
frame, _, err := buildFrameHVNC(img, 0, quality)
if err != nil {
return err
}
@@ -21,4 +21,10 @@ func h264AvailabilityDetail() string {
func resetH264Encoder() {}
func resetH264EncoderHVNC() {}
func encodeH264FrameHVNC(_ *image.RGBA) ([]byte, error) {
return nil, errors.New("h264 support not available (cgo disabled)")
}
func SetH264TargetFPS(_ int) {}
@@ -136,3 +136,88 @@ func closeH264EncoderLocked() {
h264FPS = 0
h264Buf.Reset()
}
var (
hvncH264Mu sync.Mutex
hvncH264Enc *x264.Encoder
hvncH264Buf bytes.Buffer
hvncH264Width int
hvncH264Height int
hvncH264FPS int
hvncH264Scratch []byte
)
func encodeH264FrameHVNC(img *image.RGBA) ([]byte, error) {
bounds := img.Bounds()
width := bounds.Dx()
height := bounds.Dy()
hvncH264Mu.Lock()
defer hvncH264Mu.Unlock()
if err := ensureHVNCH264EncoderLocked(width, height); err != nil {
return nil, err
}
hvncH264Buf.Reset()
if err := hvncH264Enc.Encode(img); err != nil {
return nil, err
}
n := hvncH264Buf.Len()
if n == 0 {
return nil, nil
}
if cap(hvncH264Scratch) < n {
hvncH264Scratch = make([]byte, n, n+(n/4))
} else {
hvncH264Scratch = hvncH264Scratch[:n]
}
copy(hvncH264Scratch, hvncH264Buf.Bytes())
return hvncH264Scratch, nil
}
func resetH264EncoderHVNC() {
hvncH264Mu.Lock()
defer hvncH264Mu.Unlock()
closeHVNCH264EncoderLocked()
}
func ensureHVNCH264EncoderLocked(width, height int) error {
fps := activeH264FPS()
if hvncH264Enc != nil && hvncH264Width == width && hvncH264Height == height && hvncH264FPS == fps {
return nil
}
closeHVNCH264EncoderLocked()
opts := &x264.Options{
Width: width,
Height: height,
FrameRate: fps,
Tune: "zerolatency",
Preset: "veryfast",
Profile: "main",
RateControl: "crf",
RateConstant: 23,
LogLevel: x264.LogError,
}
enc, err := x264.NewEncoder(&hvncH264Buf, opts)
if err != nil {
return err
}
hvncH264Enc = enc
hvncH264Width = width
hvncH264Height = height
hvncH264FPS = fps
return nil
}
func closeHVNCH264EncoderLocked() {
if hvncH264Enc != nil {
_ = hvncH264Enc.Close()
hvncH264Enc = nil
}
hvncH264Width = 0
hvncH264Height = 0
hvncH264FPS = 0
hvncH264Buf.Reset()
}