Skip to content

Commit 8ea1c99

Browse files
authored
Support new SIP media options. (#838)
1 parent e01f69d commit 8ea1c99

2 files changed

Lines changed: 145 additions & 36 deletions

File tree

autocomplete/fish_autocomplete

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -613,7 +613,7 @@ complete -x -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcomman
613613
complete -x -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from inbound in inbound-trunk; and not __fish_seen_subcommand_from list create update delete help h' -a 'create' -d 'Create an inbound SIP Trunk'
614614
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from inbound in inbound-trunk; and __fish_seen_subcommand_from create' -f -l name -r -d 'Sets a new name for the trunk'
615615
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from inbound in inbound-trunk; and __fish_seen_subcommand_from create' -f -l numbers -r -d 'Sets a list of numbers for the trunk'
616-
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from inbound in inbound-trunk; and __fish_seen_subcommand_from create' -f -l media-enc -r -d 'Sets media encryption for the trunk'
616+
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from inbound in inbound-trunk; and __fish_seen_subcommand_from create' -f -l media-enc -r -d 'Sets media encryption for outbound call'
617617
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from inbound in inbound-trunk; and __fish_seen_subcommand_from create' -f -l auth-user -r -d 'Set username for authentication'
618618
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from inbound in inbound-trunk; and __fish_seen_subcommand_from create' -f -l auth-pass -r -d 'Set password for authentication'
619619
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from inbound in inbound-trunk; and __fish_seen_subcommand_from create' -f -l help -s h -d 'show help'
@@ -641,7 +641,7 @@ complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_f
641641
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from outbound out outbound-trunk; and __fish_seen_subcommand_from create' -f -l address -r -d 'Sets a destination address for the trunk'
642642
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from outbound out outbound-trunk; and __fish_seen_subcommand_from create' -f -l transport -r -d 'Sets a transport for the trunk'
643643
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from outbound out outbound-trunk; and __fish_seen_subcommand_from create' -f -l destination-country -r -d 'Sets a destination country for the trunk as ISO 3166-1 alpha-2 (https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2)'
644-
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from outbound out outbound-trunk; and __fish_seen_subcommand_from create' -f -l media-enc -r -d 'Sets media encryption for the trunk'
644+
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from outbound out outbound-trunk; and __fish_seen_subcommand_from create' -f -l media-enc -r -d 'Sets media encryption for outbound call'
645645
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from outbound out outbound-trunk; and __fish_seen_subcommand_from create' -f -l numbers -r -d 'Sets a list of numbers for the trunk'
646646
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from outbound out outbound-trunk; and __fish_seen_subcommand_from create' -f -l auth-user -r -d 'Set username for authentication'
647647
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from outbound out outbound-trunk; and __fish_seen_subcommand_from create' -f -l auth-pass -r -d 'Set password for authentication'
@@ -671,6 +671,9 @@ complete -x -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcomman
671671
complete -x -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from dispatch dispatch-rule; and not __fish_seen_subcommand_from list create update delete help h' -a 'create' -d 'Create a SIP Dispatch Rule'
672672
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from dispatch dispatch-rule; and __fish_seen_subcommand_from create' -f -l name -r -d 'Sets a new name for the dispatch rule'
673673
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from dispatch dispatch-rule; and __fish_seen_subcommand_from create' -f -l trunks -r -d 'Sets a list of trunks for the dispatch rule'
674+
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from dispatch dispatch-rule; and __fish_seen_subcommand_from create' -f -l media-enc -r -d 'Sets media encryption for outbound call'
675+
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from dispatch dispatch-rule; and __fish_seen_subcommand_from create' -f -l no-default-codecs -d 'Disables a builtin list of default SIP codecs'
676+
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from dispatch dispatch-rule; and __fish_seen_subcommand_from create' -f -l codecs -r -d 'Sets a list of SIP codecs for outbound call'
674677
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from dispatch dispatch-rule; and __fish_seen_subcommand_from create' -f -l help -s h -d 'show help'
675678
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from dispatch dispatch-rule; and __fish_seen_subcommand_from create' -f -l direct -r -d 'Sets a direct dispatch to a specified room'
676679
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from dispatch dispatch-rule; and __fish_seen_subcommand_from create' -f -l caller -s individual -r -d 'Sets a individual caller dispatch to a new room with a specific prefix'
@@ -705,6 +708,9 @@ complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_f
705708
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from participant; and __fish_seen_subcommand_from create' -f -l wait -d 'wait for the call to dial (overrides json config)'
706709
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from participant; and __fish_seen_subcommand_from create' -f -l timeout -r -d 'timeout for the call to dial'
707710
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from participant; and __fish_seen_subcommand_from create' -f -l header -r -d 'Custom SIP header in format \'Key:Value\' (can be specified multiple times)'
711+
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from participant; and __fish_seen_subcommand_from create' -f -l media-enc -r -d 'Sets media encryption for outbound call'
712+
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from participant; and __fish_seen_subcommand_from create' -f -l no-default-codecs -d 'Disables a builtin list of default SIP codecs'
713+
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from participant; and __fish_seen_subcommand_from create' -f -l codecs -r -d 'Sets a list of SIP codecs for outbound call'
708714
complete -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from participant; and __fish_seen_subcommand_from create' -f -l help -s h -d 'show help'
709715
complete -x -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from participant; and __fish_seen_subcommand_from create; and not __fish_seen_subcommand_from help h' -a 'help' -d 'Shows a list of commands or help for one command'
710716
complete -x -c lk -n '__fish_seen_subcommand_from sip; and __fish_seen_subcommand_from participant; and not __fish_seen_subcommand_from create transfer help h' -a 'transfer' -d 'Transfer a SIP Participant'

cmd/lk/sip.go

Lines changed: 137 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,16 @@ import (
2020
"fmt"
2121
"maps"
2222
"slices"
23+
"strconv"
2324
"strings"
2425
"time"
2526

27+
"github.com/urfave/cli/v3"
28+
"google.golang.org/protobuf/types/known/durationpb"
29+
2630
"github.com/livekit/livekit-cli/v2/pkg/util"
2731
"github.com/livekit/protocol/livekit"
2832
lksdk "github.com/livekit/server-sdk-go/v2"
29-
"github.com/urfave/cli/v3"
30-
"google.golang.org/protobuf/types/known/durationpb"
3133
)
3234

3335
//lint:file-ignore SA1019 we still support older APIs for compatibility
@@ -63,10 +65,7 @@ var (
6365
Name: "numbers",
6466
Usage: "Sets a list of numbers for the trunk",
6567
},
66-
&cli.StringFlag{
67-
Name: "media-enc",
68-
Usage: "Sets media encryption for the trunk",
69-
},
68+
sipMediaEncFlag(),
7069
&cli.StringFlag{
7170
Name: "auth-user",
7271
Usage: "Set username for authentication",
@@ -146,10 +145,7 @@ var (
146145
Name: "destination-country",
147146
Usage: "Sets a destination country for the trunk as ISO 3166-1 alpha-2 (https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2)",
148147
},
149-
&cli.StringFlag{
150-
Name: "media-enc",
151-
Usage: "Sets media encryption for the trunk",
152-
},
148+
sipMediaEncFlag(),
153149
&cli.StringSliceFlag{
154150
Name: "numbers",
155151
Usage: "Sets a list of numbers for the trunk",
@@ -228,7 +224,7 @@ var (
228224
Usage: "Create a SIP Dispatch Rule",
229225
Action: createSIPDispatchRule,
230226
ArgsUsage: RequestDesc[livekit.CreateSIPDispatchRuleRequest](),
231-
Flags: []cli.Flag{
227+
Flags: appendSIPMediaFlags([]cli.Flag{
232228
&cli.StringFlag{
233229
Name: "name",
234230
Usage: "Sets a new name for the dispatch rule",
@@ -237,7 +233,7 @@ var (
237233
Name: "trunks",
238234
Usage: "Sets a list of trunks for the dispatch rule",
239235
},
240-
},
236+
}),
241237
MutuallyExclusiveFlags: []cli.MutuallyExclusiveFlags{
242238
{
243239
Flags: [][]cli.Flag{
@@ -334,7 +330,7 @@ var (
334330
Usage: "Create a SIP Participant",
335331
Action: createSIPParticipant,
336332
ArgsUsage: RequestDesc[livekit.CreateSIPParticipantRequest](),
337-
Flags: []cli.Flag{
333+
Flags: appendSIPMediaFlags([]cli.Flag{
338334
optional(roomFlag),
339335
optional(identityFlag),
340336
&cli.StringFlag{
@@ -374,7 +370,7 @@ var (
374370
Name: "header",
375371
Usage: "Custom SIP header in format 'Key:Value' (can be specified multiple times)",
376372
},
377-
},
373+
}),
378374
},
379375
{
380376
Name: "transfer",
@@ -481,6 +477,105 @@ func listSetFlag(cmd *cli.Command, setName string) ([]string, bool) {
481477
return val, true
482478
}
483479

480+
func optBoolFlag(cmd *cli.Command, setName string) (bool, bool) {
481+
if !cmd.IsSet(setName) {
482+
return false, false
483+
}
484+
return cmd.Bool(setName), true
485+
}
486+
487+
func sipMediaEncFlag() cli.Flag {
488+
return &cli.StringFlag{
489+
Name: "media-enc",
490+
Usage: "Sets media encryption for outbound call",
491+
}
492+
}
493+
494+
func appendSIPMediaFlags(flags []cli.Flag) []cli.Flag {
495+
flags = append(flags,
496+
sipMediaEncFlag(),
497+
&cli.BoolFlag{
498+
Name: "no-default-codecs",
499+
Usage: "Disables a builtin list of default SIP codecs",
500+
},
501+
&cli.StringSliceFlag{
502+
Name: "codecs",
503+
Usage: "Sets a list of SIP codecs for outbound call",
504+
},
505+
)
506+
return flags
507+
}
508+
509+
func parseSIPMediaEnc(cmd *cli.Command) (*livekit.SIPMediaEncryption, error) {
510+
val := cmd.String("media-enc")
511+
if val == "" {
512+
return nil, nil
513+
}
514+
val = strings.ToUpper(val)
515+
v, ok := livekit.SIPMediaEncryption_value[val]
516+
if !ok {
517+
v, ok = livekit.SIPMediaEncryption_value["SIP_MEDIA_ENCRYPT_"+val]
518+
}
519+
if !ok {
520+
return nil, fmt.Errorf("invalid value for SIP media encryption: %q", val)
521+
}
522+
return new(livekit.SIPMediaEncryption(v)), nil
523+
}
524+
525+
func parseSIPCodecs(vals []string) ([]*livekit.SIPCodec, error) {
526+
var out []*livekit.SIPCodec
527+
for _, s := range vals {
528+
sub := strings.SplitN(s, "/", 3)
529+
if len(sub) > 2 {
530+
return out, fmt.Errorf("invalid media codec: %q, expected: <name> or <name>/<sample-rate>", s)
531+
}
532+
name := sub[0]
533+
if name == "" {
534+
return out, fmt.Errorf("invalid media codec: %q: empty name", s)
535+
}
536+
var rate uint64
537+
if len(sub) >= 2 {
538+
var err error
539+
rate, err = strconv.ParseUint(sub[1], 10, 32)
540+
if err != nil {
541+
return out, fmt.Errorf("invalid media codec: %q: %w", s, err)
542+
}
543+
}
544+
out = append(out, &livekit.SIPCodec{
545+
Name: name,
546+
Rate: uint32(rate),
547+
})
548+
}
549+
return out, nil
550+
}
551+
552+
func parseSIPMediaConfig(cmd *cli.Command) (*livekit.SIPMediaConfig, error) {
553+
var m *livekit.SIPMediaConfig
554+
getMedia := func() *livekit.SIPMediaConfig {
555+
if m == nil {
556+
m = new(livekit.SIPMediaConfig)
557+
}
558+
return m
559+
}
560+
if enc, err := parseSIPMediaEnc(cmd); err == nil && enc != nil {
561+
getMedia().Encryption = enc
562+
} else if err != nil {
563+
return nil, err
564+
}
565+
if vals, ok := listSetFlag(cmd, "codecs"); ok {
566+
codecs, err := parseSIPCodecs(vals)
567+
if err != nil {
568+
return nil, err
569+
}
570+
m := getMedia()
571+
m.Codecs = codecs
572+
if val, ok := optBoolFlag(cmd, "no-default-codecs"); ok {
573+
m.OnlyListedCodecs = val
574+
}
575+
}
576+
return m, nil
577+
}
578+
484579
func createSIPClient(ctx context.Context, cmd *cli.Command) (*lksdk.SIPClient, error) {
485580
_, err := requireProject(ctx, cmd)
486581
if err != nil {
@@ -505,16 +600,10 @@ func createSIPInboundTrunk(ctx context.Context, cmd *cli.Command) error {
505600
if val, ok := listSetFlag(cmd, "numbers"); ok {
506601
p.Numbers = val
507602
}
508-
if val := cmd.String("media-enc"); val != "" {
509-
val = strings.ToUpper(val)
510-
v, ok := livekit.SIPMediaEncryption_value[val]
511-
if !ok {
512-
v, ok = livekit.SIPMediaEncryption_value["SIP_MEDIA_ENCRYPT_"+val]
513-
}
514-
if !ok {
515-
return fmt.Errorf("invalid value for SIP media encryption: %q", val)
516-
}
517-
p.MediaEncryption = livekit.SIPMediaEncryption(v)
603+
if enc, err := parseSIPMediaEnc(cmd); err == nil && enc != nil {
604+
p.MediaEncryption = *enc
605+
} else if err != nil {
606+
return err
518607
}
519608
if val := cmd.String("auth-user"); val != "" {
520609
p.AuthUsername = val
@@ -618,16 +707,10 @@ func createSIPOutboundTrunk(ctx context.Context, cmd *cli.Command) error {
618707
if val := cmd.String("destination-country"); val != "" {
619708
p.DestinationCountry = val
620709
}
621-
if val := cmd.String("media-enc"); val != "" {
622-
val = strings.ToUpper(val)
623-
v, ok := livekit.SIPMediaEncryption_value[val]
624-
if !ok {
625-
v, ok = livekit.SIPMediaEncryption_value["SIP_MEDIA_ENCRYPT_"+val]
626-
}
627-
if !ok {
628-
return fmt.Errorf("invalid value for SIP media encryption: %q", val)
629-
}
630-
p.MediaEncryption = livekit.SIPMediaEncryption(v)
710+
if enc, err := parseSIPMediaEnc(cmd); err == nil && enc != nil {
711+
p.MediaEncryption = *enc
712+
} else if err != nil {
713+
return err
631714
}
632715
if val, ok := listSetFlag(cmd, "numbers"); ok {
633716
p.Numbers = val
@@ -898,6 +981,16 @@ func createSIPDispatchRule(ctx context.Context, cmd *cli.Command) error {
898981
if val, ok := listSetFlag(cmd, "trunks"); ok {
899982
p.TrunkIds = val
900983
}
984+
if m, err := parseSIPMediaConfig(cmd); err == nil {
985+
p.Media = m
986+
} else if err != nil {
987+
return err
988+
}
989+
if enc, err := parseSIPMediaEnc(cmd); err == nil && enc != nil {
990+
p.MediaEncryption = *enc
991+
} else if err != nil {
992+
return err
993+
}
901994
if val := cmd.String("direct"); val != "" {
902995
if p.Rule != nil {
903996
return fmt.Errorf("only one dispatch rule type is allowed")
@@ -1155,6 +1248,16 @@ func createSIPParticipant(ctx context.Context, cmd *cli.Command) error {
11551248
if cmd.Bool("wait") {
11561249
req.WaitUntilAnswered = true
11571250
}
1251+
if m, err := parseSIPMediaConfig(cmd); err == nil {
1252+
req.Media = m
1253+
} else if err != nil {
1254+
return err
1255+
}
1256+
if enc, err := parseSIPMediaEnc(cmd); err == nil && enc != nil {
1257+
req.MediaEncryption = *enc
1258+
} else if err != nil {
1259+
return err
1260+
}
11581261

11591262
// Parse headers from repeatable "header" flag
11601263
if headers := cmd.StringSlice("header"); len(headers) > 0 {

0 commit comments

Comments
 (0)