diff --git a/cmd/olcrtc/main.go b/cmd/olcrtc/main.go index ce82d96..c24127c 100644 --- a/cmd/olcrtc/main.go +++ b/cmd/olcrtc/main.go @@ -38,6 +38,8 @@ type config struct { videoHW string videoQRSize int videoCodec string + videoBModule int + videoBColors int vp8FPS int vp8BatchSize int } @@ -117,6 +119,8 @@ 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.videoCodec, "video-codec", "qrcode", "Visual codec: qrcode (slow, stable) or b (fast, unstable)") + flag.IntVar(&cfg.videoBModule, "video-b-module", 4, "B codec pixels per module (default 4)") + flag.IntVar(&cfg.videoBColors, "video-b-colors", 8, "B codec colors (4, 8, 16, 32, 64, 128, 256)") 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() @@ -173,6 +177,8 @@ func toSessionConfig(cfg config) session.Config { VideoHW: cfg.videoHW, VideoQRSize: cfg.videoQRSize, VideoCodec: cfg.videoCodec, + VideoBModule: cfg.videoBModule, + VideoBColors: cfg.videoBColors, VP8FPS: cfg.vp8FPS, VP8BatchSize: cfg.vp8BatchSize, } diff --git a/internal/app/session/session.go b/internal/app/session/session.go index 91c348e..6fe31e8 100644 --- a/internal/app/session/session.go +++ b/internal/app/session/session.go @@ -79,6 +79,8 @@ type Config struct { VideoHW string VideoQRSize int VideoCodec string + VideoBModule int + VideoBColors int VP8FPS int VP8BatchSize int } diff --git a/internal/link/direct/direct.go b/internal/link/direct/direct.go index 703bd1b..06840cd 100644 --- a/internal/link/direct/direct.go +++ b/internal/link/direct/direct.go @@ -30,6 +30,8 @@ func New(ctx context.Context, cfg link.Config) (link.Link, error) { VideoHW: cfg.VideoHW, VideoQRSize: cfg.VideoQRSize, VideoCodec: cfg.VideoCodec, + VideoBModule: cfg.VideoBModule, + VideoBColors: cfg.VideoBColors, VP8FPS: cfg.VP8FPS, VP8BatchSize: cfg.VP8BatchSize, }) diff --git a/internal/link/link.go b/internal/link/link.go index 186f6e6..59f538e 100644 --- a/internal/link/link.go +++ b/internal/link/link.go @@ -40,6 +40,8 @@ type Config struct { VideoHW string VideoQRSize int VideoCodec string + VideoBModule int + VideoBColors int VP8FPS int VP8BatchSize int } diff --git a/internal/transport/transport.go b/internal/transport/transport.go index c62e803..67a1599 100644 --- a/internal/transport/transport.go +++ b/internal/transport/transport.go @@ -48,6 +48,8 @@ type Config struct { VideoHW string VideoQRSize int VideoCodec string + VideoBModule int + VideoBColors int VP8FPS int VP8BatchSize int } diff --git a/internal/transport/videochannel/transport.go b/internal/transport/videochannel/transport.go index a90cff2..5769950 100644 --- a/internal/transport/videochannel/transport.go +++ b/internal/transport/videochannel/transport.go @@ -64,6 +64,8 @@ type streamTransport struct { videoHW string videoQRSize int videoCodec string + videoBModule int + videoBColors int } // New creates a visual videochannel transport backed by a carrier-specific provider. @@ -120,6 +122,8 @@ func New(ctx context.Context, cfg transport.Config) (transport.Transport, error) videoHW: cfg.VideoHW, videoQRSize: qrSize, videoCodec: cfg.VideoCodec, + videoBModule: cfg.VideoBModule, + videoBColors: cfg.VideoBColors, } if err := stream.AddTrack(track); err != nil { @@ -280,7 +284,7 @@ func (p *streamTransport) writerLoop() { var rawFrame []byte var err error if p.videoCodec == "b" { - rawFrame, err = renderVisualFrameB(payload, p.videoW, p.videoH) + rawFrame, err = renderVisualFrameB(payload, p.videoW, p.videoH, p.videoBModule, p.videoBColors) } else { rawFrame, err = renderVisualFrame(payload, p.videoW, p.videoH) } @@ -393,7 +397,7 @@ func (p *streamTransport) handleFrame(frame []byte) { var payload []byte var err error if p.videoCodec == "b" { - payload, err = extractVisualPayloadB(frame, p.videoW, p.videoH) + payload, err = extractVisualPayloadB(frame, p.videoW, p.videoH, p.videoBModule, p.videoBColors) } else { payload, err = extractVisualPayload(frame, p.videoW, p.videoH) } diff --git a/internal/transport/videochannel/visual_b.go b/internal/transport/videochannel/visual_b.go index c0a027f..e97c62b 100644 --- a/internal/transport/videochannel/visual_b.go +++ b/internal/transport/videochannel/visual_b.go @@ -10,7 +10,7 @@ import ( "github.com/zarazaex69/b/go" ) -func renderVisualFrameB(payload []byte, width, height int) ([]byte, error) { +func renderVisualFrameB(payload []byte, width, height, modulePx, colors int) ([]byte, error) { rgba := make([]byte, width*height*4) for i := 0; i < len(rgba); i += 4 { rgba[i] = 0xff @@ -24,6 +24,12 @@ func renderVisualFrameB(payload []byte, width, height int) ([]byte, error) { } cfg := b.DefaultConfig() + if modulePx > 0 { + cfg.ModulePx = uint32(modulePx) + } + if colors > 0 { + cfg.Colors = uint32(colors) + } result, err := b.Encode(payload, cfg) if err != nil { return nil, fmt.Errorf("b encode: %w", err) @@ -54,7 +60,7 @@ func renderVisualFrameB(payload []byte, width, height int) ([]byte, error) { var frameCounter int -func extractVisualPayloadB(frame []byte, width, height int) ([]byte, error) { +func extractVisualPayloadB(frame []byte, width, height, modulePx, colors int) ([]byte, error) { expectedSize := width * height * 4 if len(frame) != expectedSize { return nil, fmt.Errorf("unexpected frame size: %d (expected %dx%dx4=%d)", len(frame), width, height, expectedSize) @@ -72,6 +78,12 @@ func extractVisualPayloadB(frame []byte, width, height int) ([]byte, error) { } cfg := b.DefaultConfig() + if modulePx > 0 { + cfg.ModulePx = uint32(modulePx) + } + if colors > 0 { + cfg.Colors = uint32(colors) + } decoded, err := b.Decode(frame, uint32(width), uint32(height), cfg) if err != nil { logger.Debugf("b decode failed: %v", err)