web rtc for webcam, audio, etc.
This commit is contained in:
@@ -6,37 +6,85 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type FrameWriter interface {
|
||||
type Kind string
|
||||
|
||||
const (
|
||||
KindDesktop Kind = "desktop"
|
||||
KindWebcam Kind = "webcam"
|
||||
KindAudio Kind = "audio"
|
||||
)
|
||||
|
||||
type VideoWriter interface {
|
||||
WriteH264(nalu []byte, dur time.Duration) error
|
||||
}
|
||||
|
||||
type AudioWriter interface {
|
||||
WriteAudio(pcm []int16) error
|
||||
}
|
||||
|
||||
type writerEntry struct {
|
||||
video VideoWriter
|
||||
audio AudioWriter
|
||||
}
|
||||
|
||||
var (
|
||||
writersMu sync.RWMutex
|
||||
writers = map[string]FrameWriter{}
|
||||
writers = map[string]map[string]writerEntry{} // kind → id → entry
|
||||
)
|
||||
|
||||
func registerWriter(id string, w FrameWriter) {
|
||||
func registerVideoWriter(kind Kind, id string, w VideoWriter) {
|
||||
if id == "" || w == nil {
|
||||
return
|
||||
}
|
||||
writersMu.Lock()
|
||||
writers[id] = w
|
||||
bucket := writers[string(kind)]
|
||||
if bucket == nil {
|
||||
bucket = map[string]writerEntry{}
|
||||
writers[string(kind)] = bucket
|
||||
}
|
||||
entry := bucket[id]
|
||||
entry.video = w
|
||||
bucket[id] = entry
|
||||
writersMu.Unlock()
|
||||
}
|
||||
|
||||
func unregisterWriter(id string) {
|
||||
func registerAudioWriter(kind Kind, id string, w AudioWriter) {
|
||||
if id == "" || w == nil {
|
||||
return
|
||||
}
|
||||
writersMu.Lock()
|
||||
bucket := writers[string(kind)]
|
||||
if bucket == nil {
|
||||
bucket = map[string]writerEntry{}
|
||||
writers[string(kind)] = bucket
|
||||
}
|
||||
entry := bucket[id]
|
||||
entry.audio = w
|
||||
bucket[id] = entry
|
||||
writersMu.Unlock()
|
||||
}
|
||||
|
||||
func unregisterWriter(kind Kind, id string) {
|
||||
if id == "" {
|
||||
return
|
||||
}
|
||||
writersMu.Lock()
|
||||
delete(writers, id)
|
||||
if bucket, ok := writers[string(kind)]; ok {
|
||||
delete(bucket, id)
|
||||
if len(bucket) == 0 {
|
||||
delete(writers, string(kind))
|
||||
}
|
||||
}
|
||||
writersMu.Unlock()
|
||||
}
|
||||
|
||||
func IsActive() bool {
|
||||
// IsActive reports whether any writer of the given kind is registered.
|
||||
// Callers in capture loops use this as a cheap "should I divert this frame
|
||||
// to WebRTC?" check before doing more expensive work.
|
||||
func IsActive(kind Kind) bool {
|
||||
writersMu.RLock()
|
||||
defer writersMu.RUnlock()
|
||||
return len(writers) > 0
|
||||
return len(writers[string(kind)]) > 0
|
||||
}
|
||||
|
||||
var keyframeWanted atomic.Bool
|
||||
@@ -49,21 +97,38 @@ func ConsumeKeyframeRequest() bool {
|
||||
return keyframeWanted.Swap(false)
|
||||
}
|
||||
|
||||
func WriteH264(nalu []byte, dur time.Duration) error {
|
||||
func WriteH264(kind Kind, nalu []byte, dur time.Duration) error {
|
||||
if len(nalu) == 0 {
|
||||
return nil
|
||||
}
|
||||
writersMu.RLock()
|
||||
defer writersMu.RUnlock()
|
||||
for _, w := range writers {
|
||||
_ = w.WriteH264(nalu, dur)
|
||||
bucket := writers[string(kind)]
|
||||
for _, w := range bucket {
|
||||
if w.video != nil {
|
||||
_ = w.video.WriteH264(nalu, dur)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func WriteAudio(kind Kind, pcm []int16) error {
|
||||
if len(pcm) == 0 {
|
||||
return nil
|
||||
}
|
||||
writersMu.RLock()
|
||||
defer writersMu.RUnlock()
|
||||
bucket := writers[string(kind)]
|
||||
for _, w := range bucket {
|
||||
if w.audio != nil {
|
||||
_ = w.audio.WriteAudio(pcm)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Deadass fuck WebRTC bro ts is so confusing
|
||||
type Options struct {
|
||||
// (e.g. https://server:5173/api/webrtc/agents/abc/whip).
|
||||
// (e.g. https://server:5173/api/webrtc/agents/abc/desktop/whip).
|
||||
WhipURL string
|
||||
// PublishToken is the bearer token issued by the server.
|
||||
PublishToken string
|
||||
@@ -71,6 +136,9 @@ type Options struct {
|
||||
TLSInsecureSkipVerify bool
|
||||
// TLSCAPath is an optional custom CA bundle.
|
||||
TLSCAPath string
|
||||
// HasVideo / HasAudio select which tracks to add to the peer connection.
|
||||
HasVideo bool
|
||||
HasAudio bool
|
||||
}
|
||||
|
||||
type ICECandidate struct {
|
||||
|
||||
Reference in New Issue
Block a user