mirror of
https://github.com/openlibrecommunity/olcrtc.git
synced 2026-05-30 17:09:43 +00:00
247 lines
7.1 KiB
Go
247 lines
7.1 KiB
Go
package goolom
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"math/rand/v2"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/openlibrecommunity/olcrtc/internal/logger"
|
|
"github.com/openlibrecommunity/olcrtc/internal/protect"
|
|
)
|
|
|
|
func (s *Session) processSendQueue(workerID int, sessionCloseCh <-chan struct{}) {
|
|
for {
|
|
select {
|
|
case <-sessionCloseCh:
|
|
return
|
|
case <-s.closeCh:
|
|
return
|
|
case data := <-s.sendQueue:
|
|
if len(data) > s.trafficShape.MaxMessageSize {
|
|
logger.Debugf("oversized message size=%d limit=%d", len(data), s.trafficShape.MaxMessageSize)
|
|
continue
|
|
}
|
|
|
|
waited, err := s.waitBufferedAmount(workerID, sessionCloseCh)
|
|
if err != nil {
|
|
return
|
|
}
|
|
if waited > 0 {
|
|
logger.Verbosef("[WORKER-%d] Drained after %v", workerID, waited)
|
|
}
|
|
|
|
if err := s.dc.Send(data); err != nil {
|
|
logger.Debugf("send error: %v", err)
|
|
s.queueReconnect()
|
|
return
|
|
}
|
|
|
|
if s.trafficShape.MinDelay > 0 {
|
|
time.Sleep(s.calculateDelay())
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (s *Session) waitBufferedAmount(workerID int, sessionCloseCh <-chan struct{}) (time.Duration, error) {
|
|
start := time.Now()
|
|
for s.dc.BufferedAmount() > defaultBufferHighWaterMark {
|
|
select {
|
|
case <-sessionCloseCh:
|
|
return 0, ErrSessionClosed
|
|
case <-s.closeCh:
|
|
return 0, ErrPeerClosed
|
|
case <-time.After(10 * time.Millisecond):
|
|
if time.Since(start) > 5*time.Second {
|
|
logger.Debugf("buffer wait timeout worker=%d", workerID)
|
|
return time.Since(start), nil
|
|
}
|
|
}
|
|
}
|
|
return time.Since(start), nil
|
|
}
|
|
|
|
func (s *Session) calculateDelay() time.Duration {
|
|
minDelay := s.trafficShape.MinDelay
|
|
maxDelay := s.trafficShape.MaxDelay
|
|
if maxDelay <= minDelay {
|
|
return minDelay
|
|
}
|
|
return minDelay + time.Duration(rand.Int64N(int64(maxDelay-minDelay))) //nolint:gosec,lll // G404: non-cryptographic shaping randomness
|
|
}
|
|
|
|
func (s *Session) startTelemetry(ctx context.Context, serverHello map[string]any) {
|
|
endpoint, interval, ok := parseTelemetryCfg(serverHello)
|
|
if !ok {
|
|
return
|
|
}
|
|
if !s.telemetryActive.CompareAndSwap(false, true) {
|
|
return
|
|
}
|
|
|
|
s.wg.Add(1)
|
|
go func() {
|
|
defer s.wg.Done()
|
|
defer s.telemetryActive.Store(false)
|
|
|
|
ticker := time.NewTicker(interval)
|
|
defer ticker.Stop()
|
|
|
|
s.sendTelemetry(ctx, endpoint, "join")
|
|
for {
|
|
select {
|
|
case <-ticker.C:
|
|
s.sendTelemetry(ctx, endpoint, "stats")
|
|
case <-s.telemetryCh:
|
|
s.sendTelemetry(ctx, endpoint, "leave")
|
|
return
|
|
case <-s.closeCh:
|
|
s.sendTelemetry(ctx, endpoint, "leave")
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
}
|
|
|
|
func parseTelemetryCfg(serverHello map[string]any) (string, time.Duration, bool) {
|
|
cfg, ok := serverHello["telemetryConfiguration"].(map[string]any)
|
|
if !ok {
|
|
return "", 0, false
|
|
}
|
|
endpoint, ok := cfg["logEndpoint"].(string)
|
|
if !ok || endpoint == "" {
|
|
endpoint, ok = cfg["endpoint"].(string)
|
|
if !ok || endpoint == "" {
|
|
endpoint, _ = cfg["url"].(string)
|
|
}
|
|
}
|
|
if endpoint == "" {
|
|
return "", 0, false
|
|
}
|
|
interval := defaultTelemetryInterval
|
|
if raw, ok := cfg["sendingInterval"].(float64); ok && raw > 0 {
|
|
interval = time.Duration(raw) * time.Millisecond
|
|
}
|
|
return endpoint, interval, true
|
|
}
|
|
|
|
func (s *Session) stopTelemetry() {
|
|
if s.telemetryActive.Load() {
|
|
select {
|
|
case s.telemetryCh <- struct{}{}:
|
|
default:
|
|
}
|
|
}
|
|
}
|
|
|
|
func (s *Session) sendTelemetry(ctx context.Context, endpoint, event string) {
|
|
body, err := json.Marshal(map[string]any{
|
|
"event": event,
|
|
"timestamp": time.Now().UnixMilli(),
|
|
"peerId": s.peerID,
|
|
"roomId": s.roomID,
|
|
"displayName": s.name,
|
|
"implementation": "browser",
|
|
"dataChannel": map[string]any{
|
|
"bufferedAmount": s.GetBufferedAmount(),
|
|
"sendQueue": len(s.sendQueue),
|
|
},
|
|
})
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, bytes.NewReader(body))
|
|
if err != nil {
|
|
logger.Verbosef("Telemetry req error: %v", err)
|
|
return
|
|
}
|
|
req.Header.Set("Content-Type", "application/json")
|
|
req.Header.Set("User-Agent", "Mozilla/5.0 (X11; Linux x86_64; rv:149.0) Gecko/20100101 Firefox/149.0")
|
|
if s.telemetryReferer != "" {
|
|
req.Header.Set("Referer", s.telemetryReferer)
|
|
}
|
|
req.Header.Set("X-Requested-With", "XMLHttpRequest")
|
|
req.Header.Set("Client-Instance-Id", uuid.New().String())
|
|
req.Header.Set("X-Telemost-Client-Version", "187.1.0")
|
|
req.Header.Set("Idempotency-Key", uuid.New().String())
|
|
|
|
client := protect.NewHTTPClient()
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
logger.Verbosef("Telemetry send error: %v", err)
|
|
return
|
|
}
|
|
defer func() { _ = resp.Body.Close() }()
|
|
}
|
|
|
|
func goolomCapabilitiesOffer() map[string]any {
|
|
return map[string]any{
|
|
"offerAnswerMode": []string{"SEPARATE"},
|
|
"initialSubscriberOffer": []string{"ON_HELLO"},
|
|
"slotsMode": []string{"FROM_CONTROLLER"},
|
|
"simulcastMode": []string{"DISABLED", "STATIC"},
|
|
"selfVadStatus": []string{"FROM_SERVER", "FROM_CLIENT"},
|
|
"dataChannelSharing": []string{"TO_RTP"},
|
|
"videoEncoderConfig": []string{"NO_CONFIG", "ONLY_INIT_CONFIG", "RUNTIME_CONFIG"},
|
|
"dataChannelVideoCodec": []string{"VP8", "UNIQUE_CODEC_FROM_TRACK_DESCRIPTION"},
|
|
"bandwidthLimitationReason": []string{
|
|
"BANDWIDTH_REASON_DISABLED",
|
|
"BANDWIDTH_REASON_ENABLED",
|
|
},
|
|
"sdkDefaultDeviceManagement": []string{
|
|
"SDK_DEFAULT_DEVICE_MANAGEMENT_DISABLED",
|
|
"SDK_DEFAULT_DEVICE_MANAGEMENT_ENABLED",
|
|
},
|
|
"joinOrderLayout": []string{"JOIN_ORDER_LAYOUT_DISABLED", "JOIN_ORDER_LAYOUT_ENABLED"},
|
|
"pinLayout": []string{"PIN_LAYOUT_DISABLED"},
|
|
"sendSelfViewVideoSlot": []string{
|
|
"SEND_SELF_VIEW_VIDEO_SLOT_DISABLED",
|
|
"SEND_SELF_VIEW_VIDEO_SLOT_ENABLED",
|
|
},
|
|
"serverLayoutTransition": []string{"SERVER_LAYOUT_TRANSITION_DISABLED"},
|
|
"sdkPublisherOptimizeBitrate": []string{
|
|
"SDK_PUBLISHER_OPTIMIZE_BITRATE_DISABLED",
|
|
"SDK_PUBLISHER_OPTIMIZE_BITRATE_FULL",
|
|
"SDK_PUBLISHER_OPTIMIZE_BITRATE_ONLY_SELF",
|
|
},
|
|
"sdkNetworkLostDetection": []string{"SDK_NETWORK_LOST_DETECTION_DISABLED"},
|
|
"sdkNetworkPathMonitor": []string{"SDK_NETWORK_PATH_MONITOR_DISABLED"},
|
|
"publisherVp9": []string{"PUBLISH_VP9_DISABLED", "PUBLISH_VP9_ENABLED"},
|
|
"svcMode": []string{"SVC_MODE_DISABLED", "SVC_MODE_L3T3", "SVC_MODE_L3T3_KEY"},
|
|
"subscriberOfferAsyncAck": []string{"SUBSCRIBER_OFFER_ASYNC_ACK_DISABLED", "SUBSCRIBER_OFFER_ASYNC_ACK_ENABLED"},
|
|
"androidBluetoothRoutingFix": []string{
|
|
"ANDROID_BLUETOOTH_ROUTING_FIX_DISABLED",
|
|
},
|
|
"fixedIceCandidatesPoolSize": []string{
|
|
"FIXED_ICE_CANDIDATES_POOL_SIZE_DISABLED",
|
|
},
|
|
"sdkAndroidTelecomIntegration": []string{
|
|
"SDK_ANDROID_TELECOM_INTEGRATION_DISABLED",
|
|
},
|
|
"setActiveCodecsMode": []string{
|
|
"SET_ACTIVE_CODECS_MODE_DISABLED",
|
|
"SET_ACTIVE_CODECS_MODE_VIDEO_ONLY",
|
|
},
|
|
"subscriberDtlsPassiveMode": []string{
|
|
"SUBSCRIBER_DTLS_PASSIVE_MODE_DISABLED",
|
|
},
|
|
"publisherOpusDred": []string{
|
|
"PUBLISHER_OPUS_DRED_DISABLED",
|
|
},
|
|
"publisherOpusLowBitrate": []string{
|
|
"PUBLISHER_OPUS_LOW_BITRATE_DISABLED",
|
|
},
|
|
"sdkAndroidDestroySessionOnTaskRemoved": []string{
|
|
"SDK_ANDROID_DESTROY_SESSION_ON_TASK_REMOVED_DISABLED",
|
|
},
|
|
"svcModes": []string{"FALSE"},
|
|
"reportTelemetryModes": []string{"TRUE"},
|
|
"keepDefaultDevicesModes": []string{"FALSE"},
|
|
}
|
|
}
|