mirror of
https://github.com/openlibrecommunity/olcrtc.git
synced 2026-05-26 07:08:11 +00:00
feat: add tile as videochannel visual codec via -video-codec tile
This commit is contained in:
@@ -38,9 +38,11 @@ type config struct {
|
||||
videoHW string
|
||||
videoQRSize int
|
||||
videoQRRecovery string
|
||||
videoCodec string
|
||||
vp8FPS int
|
||||
vp8BatchSize int
|
||||
videoCodec string
|
||||
videoTileModule int
|
||||
videoTileRS int
|
||||
vp8FPS int
|
||||
vp8BatchSize int
|
||||
}
|
||||
|
||||
func main() {
|
||||
@@ -118,7 +120,9 @@ func parseFlags() config {
|
||||
flag.StringVar(&cfg.videoHW, "video-hw", "", "Hardware acceleration (none, nvenc)")
|
||||
flag.IntVar(&cfg.videoQRSize, "video-qr-size", 0, "Video QR code fragment size (videochannel only)")
|
||||
flag.StringVar(&cfg.videoQRRecovery, "video-qr-recovery", "low", "QR error correction: low (7%), medium (15%), high (25%), highest (30%)")
|
||||
flag.StringVar(&cfg.videoCodec, "video-codec", "qrcode", "Visual codec: qrcode (slow, stable) or b (fast, unstable)")
|
||||
flag.StringVar(&cfg.videoCodec, "video-codec", "qrcode", "Visual codec: qrcode or tile")
|
||||
flag.IntVar(&cfg.videoTileModule, "video-tile-module", 0, "Tile module size in pixels 1..270 (videochannel tile only, default 4)")
|
||||
flag.IntVar(&cfg.videoTileRS, "video-tile-rs", 0, "Tile Reed-Solomon parity percent 0..200 (videochannel tile only, default 20)")
|
||||
flag.IntVar(&cfg.vp8FPS, "vp8-fps", 0, "VP8 frames per second (vp8channel only, default 25)")
|
||||
flag.IntVar(&cfg.vp8BatchSize, "vp8-batch", 0, "VP8 frames per tick (vp8channel only, default 1)")
|
||||
flag.Parse()
|
||||
@@ -175,9 +179,11 @@ func toSessionConfig(cfg config) session.Config {
|
||||
VideoHW: cfg.videoHW,
|
||||
VideoQRSize: cfg.videoQRSize,
|
||||
VideoQRRecovery: cfg.videoQRRecovery,
|
||||
VideoCodec: cfg.videoCodec,
|
||||
VP8FPS: cfg.vp8FPS,
|
||||
VP8BatchSize: cfg.vp8BatchSize,
|
||||
VideoCodec: cfg.videoCodec,
|
||||
VideoTileModule: cfg.videoTileModule,
|
||||
VideoTileRS: cfg.videoTileRS,
|
||||
VP8FPS: cfg.vp8FPS,
|
||||
VP8BatchSize: cfg.vp8BatchSize,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
1
go.mod
1
go.mod
@@ -36,6 +36,7 @@ require (
|
||||
github.com/jxskiss/base62 v1.1.0 // indirect
|
||||
github.com/klauspost/compress v1.18.4 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
||||
github.com/klauspost/reedsolomon v1.13.3 // indirect
|
||||
github.com/lithammer/shortuuid/v4 v4.2.0 // indirect
|
||||
github.com/livekit/mageutil v0.0.0-20250511045019-0f1ff63f7731 // indirect
|
||||
github.com/livekit/mediatransportutil v0.0.0-20251128105421-19c7a7b81c22 // indirect
|
||||
|
||||
2
go.sum
2
go.sum
@@ -87,6 +87,8 @@ github.com/klauspost/compress v1.18.4 h1:RPhnKRAQ4Fh8zU2FY/6ZFDwTVTxgJ/EMydqSTzE
|
||||
github.com/klauspost/compress v1.18.4/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
github.com/klauspost/reedsolomon v1.13.3 h1:01GwnO2xoCSaM0ShP4qwl+FsHg3csFShC6Tu/RS1ji0=
|
||||
github.com/klauspost/reedsolomon v1.13.3/go.mod h1:yjqqjgMTQkBUHSG97/rm4zipffCNbCiZcB3kTqr++sQ=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
|
||||
@@ -48,7 +48,7 @@ var (
|
||||
ErrVideoFPSRequired = errors.New("video fps required for videochannel (use -video-fps)")
|
||||
ErrVideoBitrateRequired = errors.New("video bitrate required for videochannel (use -video-bitrate)")
|
||||
ErrVideoHWRequired = errors.New("video hardware acceleration required for videochannel (use -video-hw none/nvenc)")
|
||||
ErrVideoCodecInvalid = errors.New("invalid video codec for videochannel (use -video-codec qrcode)")
|
||||
ErrVideoCodecInvalid = errors.New("invalid video codec for videochannel (use -video-codec qrcode or -video-codec tile)")
|
||||
|
||||
// VP8channel errors
|
||||
ErrVP8FPSRequired = errors.New("vp8 fps required for vp8channel (use -vp8-fps)")
|
||||
@@ -79,9 +79,11 @@ type Config struct {
|
||||
VideoHW string
|
||||
VideoQRSize int
|
||||
VideoQRRecovery string
|
||||
VideoCodec string
|
||||
VP8FPS int
|
||||
VP8BatchSize int
|
||||
VideoCodec string
|
||||
VideoTileModule int
|
||||
VideoTileRS int
|
||||
VP8FPS int
|
||||
VP8BatchSize int
|
||||
}
|
||||
|
||||
// RegisterDefaults registers built-in providers and transports.
|
||||
@@ -179,7 +181,7 @@ func Validate(cfg Config) error {
|
||||
if cfg.VideoHW == "" {
|
||||
return ErrVideoHWRequired
|
||||
}
|
||||
if cfg.VideoCodec != "" && cfg.VideoCodec != "qrcode" {
|
||||
if cfg.VideoCodec != "" && cfg.VideoCodec != "qrcode" && cfg.VideoCodec != "tile" {
|
||||
return ErrVideoCodecInvalid
|
||||
}
|
||||
}
|
||||
@@ -229,6 +231,8 @@ func Run(ctx context.Context, cfg Config) error {
|
||||
cfg.VideoQRSize,
|
||||
cfg.VideoQRRecovery,
|
||||
cfg.VideoCodec,
|
||||
cfg.VideoTileModule,
|
||||
cfg.VideoTileRS,
|
||||
cfg.VP8FPS,
|
||||
cfg.VP8BatchSize,
|
||||
)
|
||||
@@ -252,6 +256,8 @@ func Run(ctx context.Context, cfg Config) error {
|
||||
cfg.VideoQRSize,
|
||||
cfg.VideoQRRecovery,
|
||||
cfg.VideoCodec,
|
||||
cfg.VideoTileModule,
|
||||
cfg.VideoTileRS,
|
||||
cfg.VP8FPS,
|
||||
cfg.VP8BatchSize,
|
||||
)
|
||||
|
||||
@@ -63,10 +63,12 @@ func Run(
|
||||
videoQRSize int,
|
||||
videoQRRecovery string,
|
||||
videoCodec string,
|
||||
videoTileModule int,
|
||||
videoTileRS int,
|
||||
vp8FPS int,
|
||||
vp8BatchSize int,
|
||||
) error {
|
||||
return RunWithReady(ctx, linkName, transportName, carrierName, roomURL, keyHex, localAddr, dnsServer, socksUser, socksPass, nil, videoWidth, videoHeight, videoFPS, videoBitrate, videoHW, videoQRSize, videoQRRecovery, videoCodec, vp8FPS, vp8BatchSize)
|
||||
return RunWithReady(ctx, linkName, transportName, carrierName, roomURL, keyHex, localAddr, dnsServer, socksUser, socksPass, nil, videoWidth, videoHeight, videoFPS, videoBitrate, videoHW, videoQRSize, videoQRRecovery, videoCodec, videoTileModule, videoTileRS, vp8FPS, vp8BatchSize)
|
||||
}
|
||||
|
||||
// RunWithReady is like Run but accepts a callback that is called when the client is ready.
|
||||
@@ -90,6 +92,8 @@ func RunWithReady(
|
||||
videoQRSize int,
|
||||
videoQRRecovery string,
|
||||
videoCodec string,
|
||||
videoTileModule int,
|
||||
videoTileRS int,
|
||||
vp8FPS int,
|
||||
vp8BatchSize int,
|
||||
) error {
|
||||
@@ -119,7 +123,7 @@ func RunWithReady(
|
||||
|
||||
const linkCount = 1
|
||||
for i := range linkCount {
|
||||
if err := c.addLink(runCtx, linkName, transportName, carrierName, roomURL, i, cancel, dnsServer, "", 0, videoWidth, videoHeight, videoFPS, videoBitrate, videoHW, videoQRSize, videoQRRecovery, videoCodec, vp8FPS, vp8BatchSize); err != nil {
|
||||
if err := c.addLink(runCtx, linkName, transportName, carrierName, roomURL, i, cancel, dnsServer, "", 0, videoWidth, videoHeight, videoFPS, videoBitrate, videoHW, videoQRSize, videoQRRecovery, videoCodec, videoTileModule, videoTileRS, vp8FPS, vp8BatchSize); err != nil {
|
||||
return fmt.Errorf("addLink failed: %w", err)
|
||||
}
|
||||
}
|
||||
@@ -225,6 +229,8 @@ func (c *Client) addLink(
|
||||
videoQRSize int,
|
||||
videoQRRecovery string,
|
||||
videoCodec string,
|
||||
videoTileModule int,
|
||||
videoTileRS int,
|
||||
vp8FPS int,
|
||||
vp8BatchSize int,
|
||||
) error {
|
||||
@@ -245,6 +251,8 @@ func (c *Client) addLink(
|
||||
VideoQRSize: videoQRSize,
|
||||
VideoQRRecovery: videoQRRecovery,
|
||||
VideoCodec: videoCodec,
|
||||
VideoTileModule: videoTileModule,
|
||||
VideoTileRS: videoTileRS,
|
||||
VP8FPS: vp8FPS,
|
||||
VP8BatchSize: vp8BatchSize,
|
||||
})
|
||||
|
||||
@@ -31,6 +31,8 @@ func New(ctx context.Context, cfg link.Config) (link.Link, error) {
|
||||
VideoQRSize: cfg.VideoQRSize,
|
||||
VideoQRRecovery: cfg.VideoQRRecovery,
|
||||
VideoCodec: cfg.VideoCodec,
|
||||
VideoTileModule: cfg.VideoTileModule,
|
||||
VideoTileRS: cfg.VideoTileRS,
|
||||
VP8FPS: cfg.VP8FPS,
|
||||
VP8BatchSize: cfg.VP8BatchSize,
|
||||
})
|
||||
|
||||
@@ -41,6 +41,8 @@ type Config struct {
|
||||
VideoQRSize int
|
||||
VideoQRRecovery string
|
||||
VideoCodec string
|
||||
VideoTileModule int
|
||||
VideoTileRS int
|
||||
VP8FPS int
|
||||
VP8BatchSize int
|
||||
}
|
||||
|
||||
@@ -82,6 +82,8 @@ func Run(
|
||||
videoQRSize int,
|
||||
videoQRRecovery string,
|
||||
videoCodec string,
|
||||
videoTileModule int,
|
||||
videoTileRS int,
|
||||
vp8FPS int,
|
||||
vp8BatchSize int,
|
||||
) error {
|
||||
@@ -108,7 +110,7 @@ func Run(
|
||||
|
||||
const linkCount = 1
|
||||
for i := range linkCount {
|
||||
if err := s.addLink(runCtx, linkName, transportName, carrierName, roomURL, i, cancel, videoWidth, videoHeight, videoFPS, videoBitrate, videoHW, videoQRSize, videoQRRecovery, videoCodec, vp8FPS, vp8BatchSize); err != nil {
|
||||
if err := s.addLink(runCtx, linkName, transportName, carrierName, roomURL, i, cancel, videoWidth, videoHeight, videoFPS, videoBitrate, videoHW, videoQRSize, videoQRRecovery, videoCodec, videoTileModule, videoTileRS, vp8FPS, vp8BatchSize); err != nil {
|
||||
return fmt.Errorf("addLink failed: %w", err)
|
||||
}
|
||||
}
|
||||
@@ -197,6 +199,8 @@ func (s *Server) addLink(
|
||||
videoQRSize int,
|
||||
videoQRRecovery string,
|
||||
videoCodec string,
|
||||
videoTileModule int,
|
||||
videoTileRS int,
|
||||
vp8FPS int,
|
||||
vp8BatchSize int,
|
||||
) error {
|
||||
@@ -217,6 +221,8 @@ func (s *Server) addLink(
|
||||
VideoQRSize: videoQRSize,
|
||||
VideoQRRecovery: videoQRRecovery,
|
||||
VideoCodec: videoCodec,
|
||||
VideoTileModule: videoTileModule,
|
||||
VideoTileRS: videoTileRS,
|
||||
VP8FPS: vp8FPS,
|
||||
VP8BatchSize: vp8BatchSize,
|
||||
})
|
||||
|
||||
@@ -49,6 +49,8 @@ type Config struct {
|
||||
VideoQRSize int
|
||||
VideoQRRecovery string
|
||||
VideoCodec string
|
||||
VideoTileModule int
|
||||
VideoTileRS int
|
||||
VP8FPS int
|
||||
VP8BatchSize int
|
||||
}
|
||||
|
||||
@@ -65,6 +65,8 @@ type streamTransport struct {
|
||||
videoQRSize int
|
||||
videoQRRecovery string
|
||||
videoCodec string
|
||||
videoTileModule int
|
||||
videoTileRS int
|
||||
}
|
||||
|
||||
// New creates a visual videochannel transport backed by a carrier-specific provider.
|
||||
@@ -102,6 +104,16 @@ func New(ctx context.Context, cfg transport.Config) (transport.Transport, error)
|
||||
qrSize = defaultFragmentSize
|
||||
}
|
||||
|
||||
tileModule := cfg.VideoTileModule
|
||||
if tileModule <= 0 {
|
||||
tileModule = 4
|
||||
}
|
||||
|
||||
tileRS := cfg.VideoTileRS
|
||||
if tileRS < 0 {
|
||||
tileRS = 20
|
||||
}
|
||||
|
||||
tr := &streamTransport{
|
||||
stream: stream,
|
||||
track: track,
|
||||
@@ -122,6 +134,8 @@ func New(ctx context.Context, cfg transport.Config) (transport.Transport, error)
|
||||
videoQRSize: qrSize,
|
||||
videoQRRecovery: cfg.VideoQRRecovery,
|
||||
videoCodec: cfg.VideoCodec,
|
||||
videoTileModule: tileModule,
|
||||
videoTileRS: tileRS,
|
||||
}
|
||||
|
||||
if err := stream.AddTrack(track); err != nil {
|
||||
@@ -281,7 +295,7 @@ func (p *streamTransport) writerLoop() {
|
||||
|
||||
var rawFrame []byte
|
||||
var err error
|
||||
rawFrame, err = renderVisualFrame(payload, p.videoW, p.videoH, p.videoQRRecovery)
|
||||
rawFrame, err = renderVisualFrame(payload, p.videoW, p.videoH, p.videoCodec, p.videoQRRecovery, p.videoTileModule, p.videoTileRS)
|
||||
if err != nil {
|
||||
logger.Debugf("videochannel render error: %v", err)
|
||||
continue
|
||||
@@ -390,7 +404,7 @@ func (p *streamTransport) handleRemoteTrack(track *webrtc.TrackRemote, _ *webrtc
|
||||
func (p *streamTransport) handleFrame(frame []byte) {
|
||||
var payload []byte
|
||||
var err error
|
||||
payload, err = extractVisualPayload(frame, p.videoW, p.videoH)
|
||||
payload, err = extractVisualPayload(frame, p.videoW, p.videoH, p.videoCodec, p.videoTileModule, p.videoTileRS)
|
||||
if err != nil || len(payload) == 0 {
|
||||
if err != nil {
|
||||
logger.Debugf("videochannel extract visual payload error: %v", err)
|
||||
|
||||
@@ -7,12 +7,12 @@ import (
|
||||
|
||||
func TestVisualRoundTrip(t *testing.T) {
|
||||
payload := []byte("hello over visual videochannel")
|
||||
frame, err := renderVisualFrame(payload, 320, 240, "low")
|
||||
frame, err := renderVisualFrame(payload, 320, 240, "qrcode", "low", 4, 20)
|
||||
if err != nil {
|
||||
t.Fatalf("renderVisualFrame failed: %v", err)
|
||||
}
|
||||
|
||||
got, err := extractVisualPayload(frame, 320, 240)
|
||||
got, err := extractVisualPayload(frame, 320, 240, "qrcode", 4, 20)
|
||||
if err != nil {
|
||||
t.Fatalf("extractVisualPayload failed: %v", err)
|
||||
}
|
||||
@@ -22,17 +22,45 @@ func TestVisualRoundTrip(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIdleFrameIgnored(t *testing.T) {
|
||||
frame, err := renderVisualFrame(nil, 320, 240, "low")
|
||||
frame, err := renderVisualFrame(nil, 320, 240, "qrcode", "low", 4, 20)
|
||||
if err != nil {
|
||||
t.Fatalf("renderVisualFrame failed: %v", err)
|
||||
}
|
||||
|
||||
got, err := extractVisualPayload(frame, 320, 240)
|
||||
got, err := extractVisualPayload(frame, 320, 240, "qrcode", 4, 20)
|
||||
if err == nil && len(got) != 0 {
|
||||
t.Fatalf("expected idle frame to be ignored, got=%q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTileVisualRoundTrip(t *testing.T) {
|
||||
payload := []byte("hello over tile videochannel")
|
||||
frame, err := renderVisualFrame(payload, 1080, 1080, "tile", "", 4, 20)
|
||||
if err != nil {
|
||||
t.Fatalf("renderVisualFrame tile failed: %v", err)
|
||||
}
|
||||
|
||||
got, err := extractVisualPayload(frame, 1080, 1080, "tile", 4, 20)
|
||||
if err != nil {
|
||||
t.Fatalf("extractVisualPayload tile failed: %v", err)
|
||||
}
|
||||
if !bytes.Equal(got, payload) {
|
||||
t.Fatalf("payload mismatch: got=%q want=%q", got, payload)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTileIdleFrameIgnored(t *testing.T) {
|
||||
frame, err := renderVisualFrame(nil, 1080, 1080, "tile", "", 4, 20)
|
||||
if err != nil {
|
||||
t.Fatalf("renderVisualFrame tile failed: %v", err)
|
||||
}
|
||||
|
||||
got, err := extractVisualPayload(frame, 1080, 1080, "tile", 4, 20)
|
||||
if err == nil && len(got) != 0 {
|
||||
t.Fatalf("expected tile idle frame to be ignored, got=%q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTransportFrameRoundTrip(t *testing.T) {
|
||||
encoded := encodeDataFrame(42, 0xdeadbeef, 1024, 1, 3, []byte("chunk"))
|
||||
decoded, err := decodeTransportFrame(encoded)
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"strings"
|
||||
|
||||
grqr "github.com/zarazaex69/gr/qr"
|
||||
grtile "github.com/zarazaex69/gr/tile"
|
||||
)
|
||||
|
||||
func eccLevel(level string) grqr.ECCLevel {
|
||||
@@ -20,7 +21,14 @@ func eccLevel(level string) grqr.ECCLevel {
|
||||
}
|
||||
}
|
||||
|
||||
func renderVisualFrame(payload []byte, width, height int, recoveryLevel string) ([]byte, error) {
|
||||
func renderVisualFrame(payload []byte, width, height int, codec, recoveryLevel string, tileModule, tileRS int) ([]byte, error) {
|
||||
if codec == "tile" {
|
||||
return renderTileFrame(payload, tileModule, tileRS)
|
||||
}
|
||||
return renderQRFrame(payload, width, height, recoveryLevel)
|
||||
}
|
||||
|
||||
func renderQRFrame(payload []byte, width, height int, recoveryLevel string) ([]byte, error) {
|
||||
if len(payload) == 0 {
|
||||
frame := make([]byte, width*height)
|
||||
for i := range frame {
|
||||
@@ -29,7 +37,7 @@ func renderVisualFrame(payload []byte, width, height int, recoveryLevel string)
|
||||
return frame, nil
|
||||
}
|
||||
|
||||
codec, err := grqr.New(grqr.Config{
|
||||
c, err := grqr.New(grqr.Config{
|
||||
FrameW: width,
|
||||
FrameH: height,
|
||||
Margin: 2,
|
||||
@@ -39,15 +47,39 @@ func renderVisualFrame(payload []byte, width, height int, recoveryLevel string)
|
||||
return nil, fmt.Errorf("qr codec: %w", err)
|
||||
}
|
||||
|
||||
return codec.Encode(payload)
|
||||
return c.Encode(payload)
|
||||
}
|
||||
|
||||
func extractVisualPayload(frame []byte, width, height int) ([]byte, error) {
|
||||
func renderTileFrame(payload []byte, tileModule, tileRS int) ([]byte, error) {
|
||||
if len(payload) == 0 {
|
||||
frame := make([]byte, grtile.FrameW*grtile.FrameH)
|
||||
for i := range frame {
|
||||
frame[i] = 0xff
|
||||
}
|
||||
return frame, nil
|
||||
}
|
||||
|
||||
c, err := grtile.New(grtile.Config{Module: tileModule, RSPercent: tileRS})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("tile codec: %w", err)
|
||||
}
|
||||
|
||||
return c.Encode(payload, 0, 1)
|
||||
}
|
||||
|
||||
func extractVisualPayload(frame []byte, width, height int, codec string, tileModule, tileRS int) ([]byte, error) {
|
||||
if codec == "tile" {
|
||||
return extractTilePayload(frame, tileModule, tileRS)
|
||||
}
|
||||
return extractQRPayload(frame, width, height)
|
||||
}
|
||||
|
||||
func extractQRPayload(frame []byte, width, height int) ([]byte, error) {
|
||||
if len(frame) != width*height {
|
||||
return nil, fmt.Errorf("unexpected frame size: %d (expected %dx%d=%d)", len(frame), width, height, width*height)
|
||||
}
|
||||
|
||||
codec, err := grqr.New(grqr.Config{
|
||||
c, err := grqr.New(grqr.Config{
|
||||
FrameW: width,
|
||||
FrameH: height,
|
||||
Margin: 2,
|
||||
@@ -56,7 +88,7 @@ func extractVisualPayload(frame []byte, width, height int) ([]byte, error) {
|
||||
return nil, fmt.Errorf("qr codec: %w", err)
|
||||
}
|
||||
|
||||
data, err := codec.Decode(frame)
|
||||
data, err := c.Decode(frame)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "NotFoundException") || strings.Contains(err.Error(), "not found") {
|
||||
return nil, nil
|
||||
@@ -66,3 +98,21 @@ func extractVisualPayload(frame []byte, width, height int) ([]byte, error) {
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func extractTilePayload(frame []byte, tileModule, tileRS int) ([]byte, error) {
|
||||
if len(frame) != grtile.FrameW*grtile.FrameH {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
c, err := grtile.New(grtile.Config{Module: tileModule, RSPercent: tileRS})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("tile codec: %w", err)
|
||||
}
|
||||
|
||||
result, err := c.Decode(frame)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return result.Payload, nil
|
||||
}
|
||||
|
||||
@@ -123,7 +123,7 @@ func Start(roomID, keyHex string, socksPort int, socksUser, socksPass string) er
|
||||
close(localReady)
|
||||
})
|
||||
},
|
||||
0, 0, 0, "", "", 0, "", "", 0, 0,
|
||||
0, 0, 0, "", "", 0, "", "", 0, 0, 0, 0,
|
||||
)
|
||||
|
||||
mu.Lock()
|
||||
|
||||
Reference in New Issue
Block a user