From e4212b346b028ce8fd12546cb4863ac6ecfe3455 Mon Sep 17 00:00:00 2001 From: zarazaex69 Date: Thu, 7 May 2026 01:08:35 +0300 Subject: [PATCH] feat(sei): add sei config --- cmd/olcrtc/main.go | 12 ++ cmd/olcrtc/main_test.go | 15 +- internal/app/session/session.go | 72 +++++++-- internal/app/session/session_test.go | 54 +++++++ internal/client/client.go | 15 ++ internal/e2e/tunnel_test.go | 8 + internal/link/direct/direct.go | 4 + internal/link/link.go | 36 +++-- internal/server/server.go | 10 ++ internal/transport/seichannel/transport.go | 149 +++++++++++++----- .../seichannel/transport_unit_test.go | 13 +- internal/transport/transport.go | 34 ++-- mobile/mobile.go | 8 + mobile/mobile_test.go | 20 +++ 14 files changed, 356 insertions(+), 94 deletions(-) diff --git a/cmd/olcrtc/main.go b/cmd/olcrtc/main.go index f49dc1c..8fbde5a 100644 --- a/cmd/olcrtc/main.go +++ b/cmd/olcrtc/main.go @@ -54,6 +54,10 @@ type config struct { videoTileRS int vp8FPS int vp8BatchSize int + seiFPS int + seiBatchSize int + seiFragmentSize int + seiAckTimeoutMS int } func main() { @@ -160,6 +164,10 @@ func parseFlagsFrom(args []string, errorHandling flag.ErrorHandling) (config, er "Tile Reed-Solomon parity percent 0..200 (videochannel tile only, default 20)") fs.IntVar(&cfg.vp8FPS, "vp8-fps", 0, "VP8 frames per second (vp8channel only, default 25)") fs.IntVar(&cfg.vp8BatchSize, "vp8-batch", 0, "VP8 frames per tick (vp8channel only, default 1)") + fs.IntVar(&cfg.seiFPS, "fps", 0, "Frames per second for transports that use video timing (seichannel)") + fs.IntVar(&cfg.seiBatchSize, "batch", 0, "Transport frames per tick for batched transports (seichannel)") + fs.IntVar(&cfg.seiFragmentSize, "frag", 0, "Fragment size in bytes for fragmented transports (seichannel)") + fs.IntVar(&cfg.seiAckTimeoutMS, "ack-ms", 0, "ACK timeout in milliseconds for reliable visual transports (seichannel)") return cfg, fs.Parse(args) } @@ -222,6 +230,10 @@ func toSessionConfig(cfg config) session.Config { VideoTileRS: cfg.videoTileRS, VP8FPS: cfg.vp8FPS, VP8BatchSize: cfg.vp8BatchSize, + SEIFPS: cfg.seiFPS, + SEIBatchSize: cfg.seiBatchSize, + SEIFragmentSize: cfg.seiFragmentSize, + SEIAckTimeoutMS: cfg.seiAckTimeoutMS, } } diff --git a/cmd/olcrtc/main_test.go b/cmd/olcrtc/main_test.go index 26f4258..5873293 100644 --- a/cmd/olcrtc/main_test.go +++ b/cmd/olcrtc/main_test.go @@ -38,11 +38,17 @@ func TestToSessionConfigAndFirstNonEmpty(t *testing.T) { videoTileRS: 20, vp8FPS: 25, vp8BatchSize: 8, + seiFPS: 40, + seiBatchSize: 3, + seiFragmentSize: 512, + seiAckTimeoutMS: 1500, } got := toSessionConfig(cfg) if got.Mode != cfg.mode || got.Carrier != "jazz" || got.SOCKSPort != cfg.socksPort || - got.VideoTileRS != cfg.videoTileRS || got.VP8BatchSize != cfg.vp8BatchSize { + got.VideoTileRS != cfg.videoTileRS || got.VP8BatchSize != cfg.vp8BatchSize || + got.SEIFPS != cfg.seiFPS || got.SEIBatchSize != cfg.seiBatchSize || + got.SEIFragmentSize != cfg.seiFragmentSize || got.SEIAckTimeoutMS != cfg.seiAckTimeoutMS { t.Fatalf("toSessionConfig() = %+v", got) } @@ -88,13 +94,18 @@ func TestParseFlagsFrom(t *testing.T) { "-video-tile-rs", "40", "-vp8-fps", "24", "-vp8-batch", "3", + "-fps", "40", + "-batch", "4", + "-frag", "512", + "-ack-ms", "1500", }, flag.ContinueOnError) if err != nil { t.Fatalf("parseFlagsFrom() error = %v", err) } if cfg.mode != "srv" || cfg.carrier != "telemost" || cfg.roomID != "room" || cfg.debug != true || cfg.videoCodec != "tile" || cfg.videoTileRS != 40 || - cfg.vp8FPS != 24 || cfg.vp8BatchSize != 3 { + cfg.vp8FPS != 24 || cfg.vp8BatchSize != 3 || cfg.seiFPS != 40 || + cfg.seiBatchSize != 4 || cfg.seiFragmentSize != 512 || cfg.seiAckTimeoutMS != 1500 { t.Fatalf("parseFlagsFrom() = %+v", cfg) } diff --git a/internal/app/session/session.go b/internal/app/session/session.go index 61edd02..4b660d8 100644 --- a/internal/app/session/session.go +++ b/internal/app/session/session.go @@ -72,6 +72,14 @@ var ( ErrVP8FPSRequired = errors.New("vp8 fps required for vp8channel (use -vp8-fps)") // ErrVP8BatchSizeRequired indicates that vp8 batch size is required for vp8channel. ErrVP8BatchSizeRequired = errors.New("vp8 batch size required for vp8channel (use -vp8-batch)") + // ErrSEIFPSRequired indicates that seichannel fps is required. + ErrSEIFPSRequired = errors.New("fps required for seichannel (use -fps)") + // ErrSEIBatchSizeRequired indicates that seichannel batch size is required. + ErrSEIBatchSizeRequired = errors.New("batch size required for seichannel (use -batch)") + // ErrSEIFragmentSizeRequired indicates that seichannel fragment size is required. + ErrSEIFragmentSizeRequired = errors.New("fragment size required for seichannel (use -frag)") + // ErrSEIAckTimeoutRequired indicates that seichannel ack timeout is required. + ErrSEIAckTimeoutRequired = errors.New("ack timeout required for seichannel (use -ack-ms)") // ErrSOCKSHostRequired indicates that socks host is required for cnc mode. ErrSOCKSHostRequired = errors.New("socks host required for cnc mode (use -socks-host)") @@ -83,23 +91,23 @@ var ( // Config holds runtime session settings. type Config struct { - Mode string - Link string - Transport string - Carrier string - RoomID string - ClientID string - KeyHex string - SOCKSHost string - SOCKSPort int - DNSServer string - SOCKSProxyAddr string - SOCKSProxyPort int - VideoWidth int - VideoHeight int - VideoFPS int - VideoBitrate string - VideoHW string + Mode string + Link string + Transport string + Carrier string + RoomID string + ClientID string + KeyHex string + SOCKSHost string + SOCKSPort int + DNSServer string + SOCKSProxyAddr string + SOCKSProxyPort int + VideoWidth int + VideoHeight int + VideoFPS int + VideoBitrate string + VideoHW string VideoQRSize int VideoQRRecovery string VideoCodec string @@ -107,6 +115,10 @@ type Config struct { VideoTileRS int VP8FPS int VP8BatchSize int + SEIFPS int + SEIBatchSize int + SEIFragmentSize int + SEIAckTimeoutMS int } // RegisterDefaults registers built-in providers and transports. @@ -207,6 +219,8 @@ func validateTransportConfig(cfg Config) error { return validateVideoChannel(cfg) case "vp8channel": return validateVP8Channel(cfg) + case "seichannel": + return validateSEIChannel(cfg) default: return nil } @@ -251,6 +265,22 @@ func validateVP8Channel(cfg Config) error { return nil } +func validateSEIChannel(cfg Config) error { + if cfg.SEIFPS == 0 { + return ErrSEIFPSRequired + } + if cfg.SEIBatchSize == 0 { + return ErrSEIBatchSizeRequired + } + if cfg.SEIFragmentSize == 0 { + return ErrSEIFragmentSizeRequired + } + if cfg.SEIAckTimeoutMS == 0 { + return ErrSEIAckTimeoutRequired + } + return nil +} + func validateModeConfig(cfg Config) error { if cfg.Mode != modeCNC { return nil @@ -293,6 +323,10 @@ func Run(ctx context.Context, cfg Config) error { cfg.VideoTileRS, cfg.VP8FPS, cfg.VP8BatchSize, + cfg.SEIFPS, + cfg.SEIBatchSize, + cfg.SEIFragmentSize, + cfg.SEIAckTimeoutMS, ); err != nil { return fmt.Errorf("server: %w", err) } @@ -322,6 +356,10 @@ func Run(ctx context.Context, cfg Config) error { cfg.VideoTileRS, cfg.VP8FPS, cfg.VP8BatchSize, + cfg.SEIFPS, + cfg.SEIBatchSize, + cfg.SEIFragmentSize, + cfg.SEIAckTimeoutMS, ); err != nil { return fmt.Errorf("client: %w", err) } diff --git a/internal/app/session/session_test.go b/internal/app/session/session_test.go index 5cf46a4..dc6a3bc 100644 --- a/internal/app/session/session_test.go +++ b/internal/app/session/session_test.go @@ -244,6 +244,60 @@ func TestValidate(t *testing.T) { return cfg }(), }, + { + name: "seichannel requires fps", + cfg: func() Config { + cfg := base + cfg.Transport = "seichannel" + return cfg + }(), + want: ErrSEIFPSRequired, + }, + { + name: "seichannel requires batch size", + cfg: func() Config { + cfg := base + cfg.Transport = "seichannel" + cfg.SEIFPS = 20 + return cfg + }(), + want: ErrSEIBatchSizeRequired, + }, + { + name: "seichannel requires fragment size", + cfg: func() Config { + cfg := base + cfg.Transport = "seichannel" + cfg.SEIFPS = 20 + cfg.SEIBatchSize = 1 + return cfg + }(), + want: ErrSEIFragmentSizeRequired, + }, + { + name: "seichannel requires ack timeout", + cfg: func() Config { + cfg := base + cfg.Transport = "seichannel" + cfg.SEIFPS = 20 + cfg.SEIBatchSize = 1 + cfg.SEIFragmentSize = 900 + return cfg + }(), + want: ErrSEIAckTimeoutRequired, + }, + { + name: "seichannel valid", + cfg: func() Config { + cfg := base + cfg.Transport = "seichannel" + cfg.SEIFPS = 20 + cfg.SEIBatchSize = 1 + cfg.SEIFragmentSize = 900 + cfg.SEIAckTimeoutMS = 3000 + return cfg + }(), + }, { name: "cnc requires socks host", cfg: func() Config { diff --git a/internal/client/client.go b/internal/client/client.go index 3073b50..46791a2 100644 --- a/internal/client/client.go +++ b/internal/client/client.go @@ -74,6 +74,10 @@ func Run( videoTileRS int, vp8FPS int, vp8BatchSize int, + seiFPS int, + seiBatchSize int, + seiFragmentSize int, + seiAckTimeoutMS int, ) error { return RunWithReady( ctx, linkName, transportName, carrierName, roomURL, keyHex, clientID, localAddr, @@ -81,6 +85,7 @@ func Run( videoWidth, videoHeight, videoFPS, videoBitrate, videoHW, videoQRSize, videoQRRecovery, videoCodec, videoTileModule, videoTileRS, vp8FPS, vp8BatchSize, + seiFPS, seiBatchSize, seiFragmentSize, seiAckTimeoutMS, ) } @@ -110,6 +115,10 @@ func RunWithReady( videoTileRS int, vp8FPS int, vp8BatchSize int, + seiFPS int, + seiBatchSize int, + seiFragmentSize int, + seiAckTimeoutMS int, ) error { runCtx, cancel := context.WithCancel(ctx) defer cancel() @@ -127,6 +136,7 @@ func RunWithReady( videoWidth, videoHeight, videoFPS, videoBitrate, videoHW, videoQRSize, videoQRRecovery, videoCodec, videoTileModule, videoTileRS, vp8FPS, vp8BatchSize, + seiFPS, seiBatchSize, seiFragmentSize, seiAckTimeoutMS, ); err != nil { return err } @@ -164,6 +174,7 @@ func (c *Client) bringUpLink( videoCodec string, videoTileModule, videoTileRS int, vp8FPS, vp8BatchSize int, + seiFPS, seiBatchSize, seiFragmentSize, seiAckTimeoutMS int, ) error { ln, err := link.New(ctx, linkName, link.Config{ Transport: transportName, @@ -187,6 +198,10 @@ func (c *Client) bringUpLink( VideoTileRS: videoTileRS, VP8FPS: vp8FPS, VP8BatchSize: vp8BatchSize, + SEIFPS: seiFPS, + SEIBatchSize: seiBatchSize, + SEIFragmentSize: seiFragmentSize, + SEIAckTimeoutMS: seiAckTimeoutMS, }) if err != nil { return fmt.Errorf("failed to create link: %w", err) diff --git a/internal/e2e/tunnel_test.go b/internal/e2e/tunnel_test.go index a057256..1908aad 100644 --- a/internal/e2e/tunnel_test.go +++ b/internal/e2e/tunnel_test.go @@ -314,6 +314,10 @@ func startTunnel(t *testing.T, serverClientID, clientClientID string) *tunnelRun 0, 0, 0, + 0, + 0, + 0, + 0, ) }() room.waitConnected(t, 1) @@ -346,6 +350,10 @@ func startTunnel(t *testing.T, serverClientID, clientClientID string) *tunnelRun 0, 0, 0, + 0, + 0, + 0, + 0, ) }() waitForReady(t, ready) diff --git a/internal/link/direct/direct.go b/internal/link/direct/direct.go index b1c4286..0b40d5f 100644 --- a/internal/link/direct/direct.go +++ b/internal/link/direct/direct.go @@ -36,6 +36,10 @@ func New(ctx context.Context, cfg link.Config) (link.Link, error) { VideoTileRS: cfg.VideoTileRS, VP8FPS: cfg.VP8FPS, VP8BatchSize: cfg.VP8BatchSize, + SEIFPS: cfg.SEIFPS, + SEIBatchSize: cfg.SEIBatchSize, + SEIFragmentSize: cfg.SEIFragmentSize, + SEIAckTimeoutMS: cfg.SEIAckTimeoutMS, }) if err != nil { return nil, fmt.Errorf("create transport for direct link: %w", err) diff --git a/internal/link/link.go b/internal/link/link.go index 66f6114..29e39c9 100644 --- a/internal/link/link.go +++ b/internal/link/link.go @@ -25,27 +25,31 @@ type Link interface { // Config holds common link configuration. type Config struct { - Transport string - Carrier string - RoomURL string - ClientID string - Name string - OnData func([]byte) - DNSServer string - ProxyAddr string - ProxyPort int - VideoWidth int - VideoHeight int - VideoFPS int - VideoBitrate string - VideoHW string + Transport string + Carrier string + RoomURL string + ClientID string + Name string + OnData func([]byte) + DNSServer string + ProxyAddr string + ProxyPort int + VideoWidth int + VideoHeight int + VideoFPS int + VideoBitrate string + VideoHW string VideoQRSize int VideoQRRecovery string VideoCodec string VideoTileModule int VideoTileRS int - VP8FPS int - VP8BatchSize int + VP8FPS int + VP8BatchSize int + SEIFPS int + SEIBatchSize int + SEIFragmentSize int + SEIAckTimeoutMS int } // Factory creates a link instance. diff --git a/internal/server/server.go b/internal/server/server.go index 2acd57a..aaa6779 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -80,6 +80,10 @@ func Run( videoTileRS int, vp8FPS int, vp8BatchSize int, + seiFPS int, + seiBatchSize int, + seiFragmentSize int, + seiAckTimeoutMS int, ) error { runCtx, cancel := context.WithCancel(ctx) defer cancel() @@ -103,6 +107,7 @@ func Run( videoWidth, videoHeight, videoFPS, videoBitrate, videoHW, videoQRSize, videoQRRecovery, videoCodec, videoTileModule, videoTileRS, vp8FPS, vp8BatchSize, + seiFPS, seiBatchSize, seiFragmentSize, seiAckTimeoutMS, ); err != nil { return err } @@ -175,6 +180,7 @@ func (s *Server) bringUpLink( videoCodec string, videoTileModule, videoTileRS int, vp8FPS, vp8BatchSize int, + seiFPS, seiBatchSize, seiFragmentSize, seiAckTimeoutMS int, ) error { ln, err := link.New(ctx, linkName, link.Config{ Transport: transportName, @@ -198,6 +204,10 @@ func (s *Server) bringUpLink( VideoTileRS: videoTileRS, VP8FPS: vp8FPS, VP8BatchSize: vp8BatchSize, + SEIFPS: seiFPS, + SEIBatchSize: seiBatchSize, + SEIFragmentSize: seiFragmentSize, + SEIAckTimeoutMS: seiAckTimeoutMS, }) if err != nil { return fmt.Errorf("failed to create link: %w", err) diff --git a/internal/transport/seichannel/transport.go b/internal/transport/seichannel/transport.go index 663454f..4d7b521 100644 --- a/internal/transport/seichannel/transport.go +++ b/internal/transport/seichannel/transport.go @@ -24,6 +24,8 @@ const ( defaultFragmentSize = 900 defaultAckTimeout = 3 * time.Second defaultFrameInterval = 50 * time.Millisecond + defaultFPS = 20 + defaultBatchSize = 1 defaultConnectTimeout = 30 * time.Second maxSendAttempts = 4 sampleBuilderMaxLate = 128 @@ -72,23 +74,27 @@ type inboundMessage struct { } type streamTransport struct { - stream carrier.VideoTrack - track *webrtc.TrackLocalStaticSample - onData func([]byte) - outbound chan []byte - outboundAck chan []byte - closeCh chan struct{} - writerDone chan struct{} - nextSeq atomic.Uint32 - closed atomic.Bool - writerUp atomic.Bool - sendMu sync.Mutex - startWriter sync.Once - ackMu sync.Mutex - ackWaiters map[uint32]chan uint32 - recvMu sync.Mutex - inbound map[uint32]*inboundMessage - delivered map[uint32]uint32 + stream carrier.VideoTrack + track *webrtc.TrackLocalStaticSample + onData func([]byte) + outbound chan []byte + outboundAck chan []byte + closeCh chan struct{} + writerDone chan struct{} + nextSeq atomic.Uint32 + closed atomic.Bool + writerUp atomic.Bool + sendMu sync.Mutex + startWriter sync.Once + ackMu sync.Mutex + ackWaiters map[uint32]chan uint32 + recvMu sync.Mutex + inbound map[uint32]*inboundMessage + delivered map[uint32]uint32 + fragmentSize int + ackTimeout time.Duration + frameInterval time.Duration + batchSize int } // New creates a seichannel transport backed by a carrier-specific provider. @@ -129,17 +135,38 @@ func New(ctx context.Context, cfg transport.Config) (transport.Transport, error) return nil, fmt.Errorf("create local video track: %w", err) } + fps := cfg.SEIFPS + if fps <= 0 { + fps = defaultFPS + } + batchSize := cfg.SEIBatchSize + if batchSize <= 0 { + batchSize = defaultBatchSize + } + fragmentSize := cfg.SEIFragmentSize + if fragmentSize <= 0 { + fragmentSize = defaultFragmentSize + } + ackTimeout := defaultAckTimeout + if cfg.SEIAckTimeoutMS > 0 { + ackTimeout = time.Duration(cfg.SEIAckTimeoutMS) * time.Millisecond + } + tr := &streamTransport{ - stream: stream, - track: track, - onData: cfg.OnData, - outbound: make(chan []byte, 256), - outboundAck: make(chan []byte, 64), - closeCh: make(chan struct{}), - writerDone: make(chan struct{}), - ackWaiters: make(map[uint32]chan uint32), - inbound: make(map[uint32]*inboundMessage), - delivered: make(map[uint32]uint32), + stream: stream, + track: track, + onData: cfg.OnData, + outbound: make(chan []byte, 256), + outboundAck: make(chan []byte, 64), + closeCh: make(chan struct{}), + writerDone: make(chan struct{}), + ackWaiters: make(map[uint32]chan uint32), + inbound: make(map[uint32]*inboundMessage), + delivered: make(map[uint32]uint32), + fragmentSize: fragmentSize, + ackTimeout: ackTimeout, + frameInterval: time.Second / time.Duration(fps), + batchSize: batchSize, } if err := stream.AddTrack(track); err != nil { @@ -178,7 +205,7 @@ func (p *streamTransport) Send(data []byte) error { seq := p.nextSeq.Add(1) crc := crc32.ChecksumIEEE(data) - fragments := fragmentPayload(data, defaultFragmentSize) + fragments := fragmentPayload(data, p.effectiveFragmentSize()) waiter := make(chan uint32, 1) p.ackMu.Lock() @@ -198,7 +225,7 @@ func (p *streamTransport) Send(data []byte) error { } } - timer := time.NewTimer(defaultAckTimeout) + timer := time.NewTimer(p.effectiveAckTimeout()) select { case ackCRC := <-waiter: timer.Stop() @@ -260,14 +287,42 @@ func (p *streamTransport) Features() transport.Features { Reliable: true, Ordered: true, MessageOriented: true, - MaxPayloadSize: defaultMaxPayloadSize, + MaxPayloadSize: p.effectiveFragmentSize() * 8, } } +func (p *streamTransport) effectiveFragmentSize() int { + if p.fragmentSize <= 0 { + return defaultFragmentSize + } + return p.fragmentSize +} + +func (p *streamTransport) effectiveAckTimeout() time.Duration { + if p.ackTimeout <= 0 { + return defaultAckTimeout + } + return p.ackTimeout +} + +func (p *streamTransport) effectiveFrameInterval() time.Duration { + if p.frameInterval <= 0 { + return defaultFrameInterval + } + return p.frameInterval +} + +func (p *streamTransport) effectiveBatchSize() int { + if p.batchSize <= 0 { + return defaultBatchSize + } + return p.batchSize +} + func (p *streamTransport) writerLoop() { defer close(p.writerDone) - ticker := time.NewTicker(defaultFrameInterval) + ticker := time.NewTicker(p.effectiveFrameInterval()) defer ticker.Stop() idle := buildVideoAccessUnit(nil) @@ -277,24 +332,32 @@ func (p *streamTransport) writerLoop() { case <-p.closeCh: return case <-ticker.C: - payload, ok := p.nextOutboundFrame() - if !ok { + if !p.writeBatch(idle) { return } - - sample := idle - if payload != nil { - sample = buildVideoAccessUnit(payload) - } - - _ = p.track.WriteSample(media.Sample{ - Data: sample, - Duration: defaultFrameInterval, - }) } } } +func (p *streamTransport) writeBatch(idle []byte) bool { + frameInterval := p.effectiveFrameInterval() + for i := 0; i < p.effectiveBatchSize(); i++ { + payload, ok := p.nextOutboundFrame() + if !ok { + return false + } + if payload == nil { + if i > 0 { + return true + } + _ = p.track.WriteSample(media.Sample{Data: idle, Duration: frameInterval}) + return true + } + _ = p.track.WriteSample(media.Sample{Data: buildVideoAccessUnit(payload), Duration: frameInterval}) + } + return true +} + func (p *streamTransport) nextOutboundFrame() ([]byte, bool) { select { case <-p.closeCh: diff --git a/internal/transport/seichannel/transport_unit_test.go b/internal/transport/seichannel/transport_unit_test.go index 5f4c1f1..f7e026e 100644 --- a/internal/transport/seichannel/transport_unit_test.go +++ b/internal/transport/seichannel/transport_unit_test.go @@ -67,7 +67,13 @@ func TestNewConnectCallbacksAndFeatures(t *testing.T) { return &fakeVideoSession{stream: stream}, nil }) - trIface, err := New(context.Background(), transport.Config{Carrier: name}) + trIface, err := New(context.Background(), transport.Config{ + Carrier: name, + SEIFPS: 40, + SEIBatchSize: 3, + SEIFragmentSize: 512, + SEIAckTimeoutMS: 1500, + }) if err != nil { t.Fatalf("New() error = %v", err) } @@ -94,6 +100,11 @@ func TestNewConnectCallbacksAndFeatures(t *testing.T) { if features := tr.Features(); !features.Reliable || !features.Ordered || !features.MessageOriented || features.MaxPayloadSize == 0 { t.Fatalf("Features() = %+v", features) } + if tr.fragmentSize != 512 || tr.batchSize != 3 || tr.frameInterval != 25*time.Millisecond || + tr.ackTimeout != 1500*time.Millisecond { + t.Fatalf("seichannel settings fragment=%d batch=%d interval=%v ack=%v", + tr.fragmentSize, tr.batchSize, tr.frameInterval, tr.ackTimeout) + } if err := tr.Close(); err != nil { t.Fatalf("Close() error = %v", err) } diff --git a/internal/transport/transport.go b/internal/transport/transport.go index 7e4aee2..7c9d5e8 100644 --- a/internal/transport/transport.go +++ b/internal/transport/transport.go @@ -34,26 +34,30 @@ type Transport interface { // Config holds common transport configuration. type Config struct { - Carrier string - RoomURL string - ClientID string - Name string - OnData func([]byte) - DNSServer string - ProxyAddr string - ProxyPort int - VideoWidth int - VideoHeight int - VideoFPS int - VideoBitrate string - VideoHW string + Carrier string + RoomURL string + ClientID string + Name string + OnData func([]byte) + DNSServer string + ProxyAddr string + ProxyPort int + VideoWidth int + VideoHeight int + VideoFPS int + VideoBitrate string + VideoHW string VideoQRSize int VideoQRRecovery string VideoCodec string VideoTileModule int VideoTileRS int - VP8FPS int - VP8BatchSize int + VP8FPS int + VP8BatchSize int + SEIFPS int + SEIBatchSize int + SEIFragmentSize int + SEIAckTimeoutMS int } // Factory creates a transport instance. diff --git a/mobile/mobile.go b/mobile/mobile.go index 0ee9a45..bad7757 100644 --- a/mobile/mobile.go +++ b/mobile/mobile.go @@ -230,6 +230,10 @@ func Check( 0, clamp(vp8FPS, 1, 120), clamp(vp8BatchSize, 1, 64), + 0, + 0, + 0, + 0, ) }() @@ -318,6 +322,10 @@ func startWithConfig( 0, cfg.vp8FPS, cfg.vp8BatchSize, + 0, + 0, + 0, + 0, ) mu.Lock() diff --git a/mobile/mobile_test.go b/mobile/mobile_test.go index c773644..a328046 100644 --- a/mobile/mobile_test.go +++ b/mobile/mobile_test.go @@ -179,6 +179,10 @@ func TestStartWithInjectedRunnerLifecycle(t *testing.T) { videoTileRS int, vp8FPS int, vp8BatchSize int, + seiFPS int, + seiBatchSize int, + seiFragmentSize int, + seiAckTimeoutMS int, ) error { if linkName != defaultLink || transportName != dataTransport || carrierName != carrierJazz || roomURL != "any" || clientID != "client" || localAddr != "127.0.0.1:1080" || @@ -230,6 +234,10 @@ func TestStartUsesDefaultsAndCheckWithInjectedRunner(t *testing.T) { videoTileRS int, vp8FPS int, vp8BatchSize int, + seiFPS int, + seiBatchSize int, + seiFragmentSize int, + seiAckTimeoutMS int, ) error { if transportName != defaultTransport || roomURL != "https://telemost.yandex.ru/j/room" || localAddr != "127.0.0.1:1081" || socksUser != "u" || socksPass != "p" { @@ -267,6 +275,10 @@ func TestStartUsesDefaultsAndCheckWithInjectedRunner(t *testing.T) { videoTileRS int, vp8FPS int, vp8BatchSize int, + seiFPS int, + seiBatchSize int, + seiFragmentSize int, + seiAckTimeoutMS int, ) error { if transportName != dataTransport || vp8FPS != 1 || vp8BatchSize != 64 { t.Fatalf("Check args mismatch: transport=%q vp8=%d/%d", transportName, vp8FPS, vp8BatchSize) @@ -308,6 +320,10 @@ func TestCheckTimeoutAndRunError(t *testing.T) { videoTileRS int, vp8FPS int, vp8BatchSize int, + seiFPS int, + seiBatchSize int, + seiFragmentSize int, + seiAckTimeoutMS int, ) error { <-ctx.Done() return nil @@ -333,6 +349,10 @@ func TestCheckTimeoutAndRunError(t *testing.T) { int, int, int, + int, + int, + int, + int, ) error { return want }