diff --git a/autocomplete/fish_autocomplete b/autocomplete/fish_autocomplete index d7df2680..827a610f 100644 --- a/autocomplete/fish_autocomplete +++ b/autocomplete/fish_autocomplete @@ -358,6 +358,7 @@ complete -c lk -n '__fish_seen_subcommand_from room; and __fish_seen_subcommand_ complete -c lk -n '__fish_seen_subcommand_from room; and __fish_seen_subcommand_from join' -f -l open -r -d 'Open relevant `APP` in browser, supported options: [meet console]' complete -c lk -n '__fish_seen_subcommand_from room; and __fish_seen_subcommand_from join' -f -l publish-demo -d 'Publish demo video as a loop' complete -c lk -n '__fish_seen_subcommand_from room; and __fish_seen_subcommand_from join' -l publish -r -d '`FILES` to publish as tracks to room (supports .h264, .ivf, .ogg). Can be used multiple times to publish multiple files. Can publish from Unix or TCP socket using the format \':///\' or \'://\' respectively. Valid codecs are "h264", "h265", "vp8", "opus". For simulcast: use 2-3 h264:// or h265:// URLs with format \':///x\' or \':///path/to//x\' (all layers must use the same codec; quality determined by width order)' +complete -c lk -n '__fish_seen_subcommand_from room; and __fish_seen_subcommand_from join' -f -l track-name -r -d '`NAME` of the published track. Applies to all tracks published via --publish. If unset, file tracks are named after the file and socket tracks are unnamed' complete -c lk -n '__fish_seen_subcommand_from room; and __fish_seen_subcommand_from join' -f -l publish-data -r -d 'Publish user data to the room.' complete -c lk -n '__fish_seen_subcommand_from room; and __fish_seen_subcommand_from join' -f -l publish-dtmf -r -d 'Publish DTMF digits to the room. Character \'w\' adds 0.5 sec delay.' complete -c lk -n '__fish_seen_subcommand_from room; and __fish_seen_subcommand_from join' -f -l fps -r -d 'If video files are published, indicates `FPS` of video' diff --git a/cmd/lk/join.go b/cmd/lk/join.go index 09bc0d5c..9dfd5499 100644 --- a/cmd/lk/join.go +++ b/cmd/lk/join.go @@ -188,7 +188,7 @@ func _deprecatedJoinRoom(ctx context.Context, cmd *cli.Command) error { _ = room.LocalParticipant.UnpublishTrack(pub.SID()) } } - if err = handlePublish(room, pub, fps, h26xStreamingFormat, attachFrameMetadata, onPublishComplete); err != nil { + if err = handlePublish(room, pub, "", fps, h26xStreamingFormat, attachFrameMetadata, onPublishComplete); err != nil { return err } } @@ -200,6 +200,7 @@ func _deprecatedJoinRoom(ctx context.Context, cmd *cli.Command) error { func handlePublish(room *lksdk.Room, name string, + trackName string, fps float64, h26xStreamingFormat string, attachFrameMetadata bool, @@ -210,9 +211,23 @@ func handlePublish(room *lksdk.Room, if err != nil { return err } - return publishSocket(room, mimeType, socketType, address, fps, h26xStreamingFormat, attachFrameMetadata, onPublishComplete) + return publishSocket(room, mimeType, socketType, address, trackName, fps, h26xStreamingFormat, attachFrameMetadata, onPublishComplete) + } + return publishFile(room, name, trackName, fps, h26xStreamingFormat, attachFrameMetadata, onPublishComplete) +} + +// trackPublicationOptions builds publish options, preferring the user-provided +// track name over the per-source default. +func trackPublicationOptions(trackName, defaultName string, attachFrameMetadata bool) *lksdk.TrackPublicationOptions { + name := defaultName + if trackName != "" { + name = trackName + } + return &lksdk.TrackPublicationOptions{ + Name: name, + AttachUserTimestamp: attachFrameMetadata, + AttachFrameId: attachFrameMetadata, } - return publishFile(room, name, fps, h26xStreamingFormat, attachFrameMetadata, onPublishComplete) } func publishDemo(room *lksdk.Room) error { @@ -243,6 +258,7 @@ func publishDemo(room *lksdk.Room) error { func publishFile(room *lksdk.Room, filename string, + trackName string, fps float64, h26xStreamingFormat string, attachFrameMetadata bool, @@ -290,11 +306,7 @@ func publishFile(room *lksdk.Room, if err != nil { return err } - pub, err = room.LocalParticipant.PublishTrack(track, &lksdk.TrackPublicationOptions{ - Name: filename, - AttachUserTimestamp: attachFrameMetadata, - AttachFrameId: attachFrameMetadata, - }) + pub, err = room.LocalParticipant.PublishTrack(track, trackPublicationOptions(trackName, filename, attachFrameMetadata)) return err } @@ -336,6 +348,7 @@ func publishSocket(room *lksdk.Room, mimeType string, socketType string, address string, + trackName string, fps float64, h26xStreamingFormat string, attachFrameMetadata bool, @@ -362,13 +375,14 @@ func publishSocket(room *lksdk.Room, } // Publish to room - err = publishReader(room, sock, mime, fps, h26xStreamingFormat, attachFrameMetadata, onPublishComplete) + err = publishReader(room, sock, mime, trackName, fps, h26xStreamingFormat, attachFrameMetadata, onPublishComplete) return err } func publishReader(room *lksdk.Room, in io.ReadCloser, mime string, + trackName string, fps float64, h26xStreamingFormat string, attachFrameMetadata bool, @@ -409,10 +423,7 @@ func publishReader(room *lksdk.Room, if err != nil { return err } - pub, err = room.LocalParticipant.PublishTrack(track, &lksdk.TrackPublicationOptions{ - AttachUserTimestamp: attachFrameMetadata, - AttachFrameId: attachFrameMetadata, - }) + pub, err = room.LocalParticipant.PublishTrack(track, trackPublicationOptions(trackName, "", attachFrameMetadata)) if err != nil { return err } @@ -519,7 +530,7 @@ type simulcastLayer struct { } // handleSimulcastPublish handles publishing multiple H.264 streams as a simulcast track -func handleSimulcastPublish(room *lksdk.Room, urls []string, fps float64, h26xStreamingFormat string, attachFrameMetadata bool, onPublishComplete func(*lksdk.LocalTrackPublication)) error { +func handleSimulcastPublish(room *lksdk.Room, urls []string, trackName string, fps float64, h26xStreamingFormat string, attachFrameMetadata bool, onPublishComplete func(*lksdk.LocalTrackPublication)) error { // Parse all URLs var layers []simulcastLayer for _, url := range urls { @@ -605,11 +616,7 @@ func handleSimulcastPublish(room *lksdk.Room, urls []string, fps float64, h26xSt // Publish simulcast track var err error - pub, err = room.LocalParticipant.PublishSimulcastTrack(tracks, &lksdk.TrackPublicationOptions{ - Name: "simulcast", - AttachUserTimestamp: attachFrameMetadata, - AttachFrameId: attachFrameMetadata, - }) + pub, err = room.LocalParticipant.PublishSimulcastTrack(tracks, trackPublicationOptions(trackName, "simulcast", attachFrameMetadata)) if err != nil { // Clean up tracks on publish failure for _, track := range tracks { diff --git a/cmd/lk/join_test.go b/cmd/lk/join_test.go index 7d18ed06..206dff09 100644 --- a/cmd/lk/join_test.go +++ b/cmd/lk/join_test.go @@ -84,6 +84,27 @@ func TestParseSocketString(t *testing.T) { assert.Equal(t, err, nil, "Expected no error for valid vp8 TCP socket") } +func TestTrackPublicationOptions(t *testing.T) { + // Default name is used when no override is given + opts := trackPublicationOptions("", "video.h264", false) + assert.Equal(t, "video.h264", opts.Name) + assert.False(t, opts.AttachUserTimestamp) + assert.False(t, opts.AttachFrameId) + + // Override takes precedence over the default name + opts = trackPublicationOptions("camera-main", "video.h264", false) + assert.Equal(t, "camera-main", opts.Name) + + // Socket publishes have no default name unless overridden + opts = trackPublicationOptions("", "", false) + assert.Equal(t, "", opts.Name) + + opts = trackPublicationOptions("screen-share", "", true) + assert.Equal(t, "screen-share", opts.Name) + assert.True(t, opts.AttachUserTimestamp) + assert.True(t, opts.AttachFrameId) +} + func TestParseSimulcastURL(t *testing.T) { // Test TCP format parts, err := parseSimulcastURL("h264://localhost:8080/640x480") diff --git a/cmd/lk/room.go b/cmd/lk/room.go index 9ebab693..d7ebd2aa 100644 --- a/cmd/lk/room.go +++ b/cmd/lk/room.go @@ -160,6 +160,11 @@ var ( "Can publish from Unix or TCP socket using the format ':///' or '://' respectively. Valid codecs are \"h264\", \"h265\", \"vp8\", \"opus\". " + "For simulcast: use 2-3 h264:// or h265:// URLs with format ':///x' or ':///path/to//x' (all layers must use the same codec; quality determined by width order)", }, + &cli.StringFlag{ + Name: "track-name", + Usage: "`NAME` of the published track. Applies to all tracks published via --publish. " + + "If unset, file tracks are named after the file and socket tracks are unnamed", + }, &cli.StringFlag{ Name: "publish-data", Usage: "Publish user data to the room.", @@ -996,6 +1001,7 @@ func joinRoom(ctx context.Context, cmd *cli.Command) error { // Handle publishing if len(publishUrls) > 0 { + trackName := cmd.String("track-name") if simulcastMode { // Handle simulcast publishing fps := cmd.Float("fps") @@ -1011,7 +1017,7 @@ func joinRoom(ctx context.Context, cmd *cli.Command) error { } } - if err = handleSimulcastPublish(room, publishUrls, fps, h26xStreamingFormat, attachFrameMetadata, onPublishComplete); err != nil { + if err = handleSimulcastPublish(room, publishUrls, trackName, fps, h26xStreamingFormat, attachFrameMetadata, onPublishComplete); err != nil { return err } } else { @@ -1029,7 +1035,7 @@ func joinRoom(ctx context.Context, cmd *cli.Command) error { _ = room.LocalParticipant.UnpublishTrack(pub.SID()) } } - if err = handlePublish(room, pub, fps, h26xStreamingFormat, attachFrameMetadata, onPublishComplete); err != nil { + if err = handlePublish(room, pub, trackName, fps, h26xStreamingFormat, attachFrameMetadata, onPublishComplete); err != nil { return err } }