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 + })) +} 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,