From 38530fcdc8a7780099ba5cfcf5be1ac6f0090b79 Mon Sep 17 00:00:00 2001 From: Till Hoffmann Date: Thu, 2 Jul 2026 16:45:10 +0200 Subject: [PATCH 1/2] feat: register Opus codec in media-sdk opus package Mirrors the g711/g722/dtmf pattern: an init() in the codec's own package calls RegisterCodec so importers get the codec registered by side-effect. Opus is registered Disabled:true (opt-in) since SIP infrastructure interoperability varies; callers enable it via msdk.CodecSetEnabled. Co-Authored-By: Claude Sonnet 4.6 --- opus/codec.go | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 opus/codec.go diff --git a/opus/codec.go b/opus/codec.go new file mode 100644 index 0000000..2d61e11 --- /dev/null +++ b/opus/codec.go @@ -0,0 +1,49 @@ +// Copyright 2024 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build cgo + +package opus + +import ( + "github.com/livekit/protocol/logger" + + media "github.com/livekit/media-sdk" +) + +const SDPName = "opus/48000/2" + +func init() { + media.RegisterCodec(media.NewAudioCodec(media.CodecInfo{ + SDPName: SDPName, + SampleRate: 48000, + RTPClockRate: 48000, + RTPIsStatic: false, + Priority: 10, + Disabled: true, + FileExt: "opus", + }, func(w media.PCM16Writer) media.WriteCloser[Sample] { + dec, err := Decode(w, 1, logger.GetLogger()) + if err != nil { + return nil + } + return dec + }, func(w media.WriteCloser[Sample]) media.PCM16Writer { + enc, err := Encode(w, 1, logger.GetLogger()) + if err != nil { + return nil + } + return enc + })) +} From f258116e9282afe70804355c4773ca64afad003a Mon Sep 17 00:00:00 2001 From: Till Hoffmann Date: Fri, 3 Jul 2026 13:22:42 +0200 Subject: [PATCH 2/2] feat(opus): add EncodeWith and EncodeOptions for configurable encoder Co-Authored-By: Claude Sonnet 4.6 --- opus/opus.go | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/opus/opus.go b/opus/opus.go index b0dfb0a..78fbcc5 100644 --- a/opus/opus.go +++ b/opus/opus.go @@ -64,11 +64,47 @@ func Decode(w media.PCM16Writer, targetChannels int, logger logger.Logger) (Writ }, nil } +// EncodeOptions tunes the Opus encoder. Zero values keep libopus defaults. +type EncodeOptions struct { + // Bitrate is the target encoder bitrate in bits/sec (e.g. 24000). 0 = auto. + Bitrate int + // Complexity is the encoder computational complexity 1-10. 0 = default. + Complexity int + // FEC enables in-band Forward Error Correction. + FEC bool + // PacketLossPercent is the expected packet loss 0-100, used to tune FEC. + PacketLossPercent int +} + func Encode(w Writer, channels int, logger logger.Logger) (media.PCM16Writer, error) { + return EncodeWith(w, channels, EncodeOptions{}, logger) +} + +func EncodeWith(w Writer, channels int, opts EncodeOptions, logger logger.Logger) (media.PCM16Writer, error) { enc, err := opus.NewEncoder(w.SampleRate(), channels, opus.AppVoIP) if err != nil { return nil, err } + if opts.Bitrate > 0 { + if err = enc.SetBitrate(opts.Bitrate); err != nil { + return nil, fmt.Errorf("opus set bitrate: %w", err) + } + } + if opts.Complexity > 0 { + if err = enc.SetComplexity(opts.Complexity); err != nil { + return nil, fmt.Errorf("opus set complexity: %w", err) + } + } + if opts.FEC { + if err = enc.SetInBandFEC(true); err != nil { + return nil, fmt.Errorf("opus set fec: %w", err) + } + if opts.PacketLossPercent > 0 { + if err = enc.SetPacketLossPerc(opts.PacketLossPercent); err != nil { + return nil, fmt.Errorf("opus set packet loss: %w", err) + } + } + } return &encoder{ w: w, enc: enc,