fix(whip): clean up lifecycle — proper net import and checkPortFree implementation
This commit is contained in:
parent
b1057756d2
commit
a22b8c68f0
1 changed files with 13 additions and 24 deletions
|
|
@ -2,6 +2,7 @@ package webrtc
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
appcfg "github.com/datarhei/core/v16/restream/app"
|
||||
)
|
||||
|
|
@ -103,7 +104,7 @@ func (s *Subsystem) onWHIPProcessStop(id string) {
|
|||
|
||||
// allocAdjacentPortPair finds two consecutive free loopback UDP ports
|
||||
// (V, V+1) and returns V. It retries up to allocAttempts times because
|
||||
// the kernel may hand us an odd-ended pair whose +1 neighbor is taken.
|
||||
// the kernel may hand us a port whose +1 neighbor is already taken.
|
||||
//
|
||||
// The caller owns the returned port numbers; FFmpeg will bind them
|
||||
// immediately on process start via the ConfigIO input legs.
|
||||
|
|
@ -115,14 +116,14 @@ func allocAdjacentPortPair() (int, error) {
|
|||
lastErr = err
|
||||
continue
|
||||
}
|
||||
// Verify the audio port (videoPort+1) is also free.
|
||||
// We do a quick bind-and-release on videoPort+1.
|
||||
audioPort := videoPort + 1
|
||||
if audioPort > 65535 {
|
||||
lastErr = fmt.Errorf("video port %d would require audio port > 65535", videoPort)
|
||||
lastErr = fmt.Errorf("video port %d would push audio port above 65535", videoPort)
|
||||
continue
|
||||
}
|
||||
// Try to claim videoPort+1 momentarily to verify availability.
|
||||
// Verify the audio port (videoPort+1) is also free by attempting
|
||||
// a momentary bind. TOCTOU race is accepted; FFmpeg will fail-fast
|
||||
// on the actual bind and the process will restart cleanly.
|
||||
if err := checkPortFree(audioPort); err != nil {
|
||||
lastErr = err
|
||||
continue
|
||||
|
|
@ -135,27 +136,15 @@ func allocAdjacentPortPair() (int, error) {
|
|||
return 0, fmt.Errorf("after %d attempts: %w", allocAttempts, lastErr)
|
||||
}
|
||||
|
||||
// checkPortFree does a quick UDP bind-and-release on the given loopback
|
||||
// port. Returns nil if the port appears free, non-nil if it is in use.
|
||||
// There is an inherent TOCTOU race between this check and FFmpeg's bind;
|
||||
// callers should handle the bind failure gracefully (process restart).
|
||||
// checkPortFree attempts a momentary UDP bind on the given loopback
|
||||
// port. Returns nil if the port appears available, non-nil otherwise.
|
||||
func checkPortFree(port int) error {
|
||||
import_net_listen := func() error {
|
||||
// Inline the check without importing net again (already in portalloc.go
|
||||
// via the package-level Alloc function). We re-use Alloc's pattern:
|
||||
// bind :port, get error, close.
|
||||
//
|
||||
// This file is in the same package so we can call the unexported helper.
|
||||
// However, since we only have Alloc() (which uses :0), we implement
|
||||
// this using net directly here.
|
||||
return nil
|
||||
c, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: port})
|
||||
if err != nil {
|
||||
return fmt.Errorf("port %d not free: %w", port, err)
|
||||
}
|
||||
_ = import_net_listen
|
||||
// Use net directly since this package already imports it via portalloc.go.
|
||||
// We can't import "net" twice, but since this file is in the same package
|
||||
// as portalloc.go which imports "net", the package-level net functions are
|
||||
// available to us via the Alloc() pattern. We implement the check manually:
|
||||
return checkUDPPortFree(port)
|
||||
_ = c.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// buildWHIPInputLegs produces the two FFmpeg input ConfigIO legs for
|
||||
|
|
|
|||
Loading…
Reference in a new issue