Skip to content

Commit

Permalink
Add WHIP ICE Restart Support
Browse files Browse the repository at this point in the history
  • Loading branch information
Sean-Der committed May 14, 2024
1 parent b23206b commit 79f141a
Show file tree
Hide file tree
Showing 8 changed files with 203 additions and 25 deletions.
16 changes: 8 additions & 8 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ require (
github.com/livekit/go-rtmp v0.0.0-20230829211117-1c4f5a5c81ed
github.com/livekit/mageutil v0.0.0-20230125210925-54e8a70427c1
github.com/livekit/mediatransportutil v0.0.0-20240501132628-6105557bbb9a
github.com/livekit/protocol v1.15.0
github.com/livekit/protocol v1.16.1-0.20240514184417-5aa3d9771312
github.com/livekit/psrpc v0.5.3-0.20240426045048-8ba067a45715
github.com/livekit/server-sdk-go/v2 v2.1.2
github.com/pion/dtls/v2 v2.2.11
Expand All @@ -39,7 +39,7 @@ require (
github.com/benbjohnson/clock v1.3.5 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bep/debounce v1.2.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
Expand All @@ -55,14 +55,14 @@ require (
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.0 // indirect
github.com/jxskiss/base62 v1.1.0 // indirect
github.com/klauspost/compress v1.17.6 // indirect
github.com/klauspost/compress v1.17.8 // indirect
github.com/klauspost/cpuid/v2 v2.2.6 // indirect
github.com/lithammer/shortuuid/v4 v4.0.0 // indirect
github.com/mackerelio/go-osstat v0.2.4 // indirect
github.com/magefile/mage v1.15.0 // indirect
github.com/mattn/go-pointer v0.0.1 // indirect
github.com/mitchellh/mapstructure v1.4.1 // indirect
github.com/nats-io/nats.go v1.31.0 // indirect
github.com/nats-io/nats.go v1.34.1 // indirect
github.com/nats-io/nkeys v0.4.7 // indirect
github.com/nats-io/nuid v1.0.1 // indirect
github.com/pion/datachannel v1.5.5 // indirect
Expand Down Expand Up @@ -90,11 +90,11 @@ require (
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
go.uber.org/zap/exp v0.2.0 // indirect
golang.org/x/crypto v0.22.0 // indirect
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 // indirect
golang.org/x/crypto v0.23.0 // indirect
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect
golang.org/x/net v0.24.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.19.0 // indirect
golang.org/x/sys v0.20.0 // indirect
golang.org/x/text v0.15.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240513163218-0867130af1f8 // indirect
)
32 changes: 16 additions & 16 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
Expand Down Expand Up @@ -64,8 +64,8 @@ github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
github.com/jxskiss/base62 v1.1.0 h1:A5zbF8v8WXx2xixnAKD2w+abC+sIzYJX+nxmhA6HWFw=
github.com/jxskiss/base62 v1.1.0/go.mod h1:HhWAlUXvxKThfOlZbcuFzsqwtF5TcqS9ru3y5GfjWAc=
github.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI=
github.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU=
github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=
github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
Expand All @@ -83,8 +83,8 @@ github.com/livekit/mageutil v0.0.0-20230125210925-54e8a70427c1 h1:jm09419p0lqTkD
github.com/livekit/mageutil v0.0.0-20230125210925-54e8a70427c1/go.mod h1:Rs3MhFwutWhGwmY1VQsygw28z5bWcnEYmS1OG9OxjOQ=
github.com/livekit/mediatransportutil v0.0.0-20240501132628-6105557bbb9a h1:ATbv0x7G5tW2HgiouQ57csFE/G4gekl2oV1cxb2Dy24=
github.com/livekit/mediatransportutil v0.0.0-20240501132628-6105557bbb9a/go.mod h1:jwKUCmObuiEDH0iiuJHaGMXwRs3RjrB4G6qqgkr/5oE=
github.com/livekit/protocol v1.15.0 h1:JAatoWKYdFx3D0U4JBWg25ZlrY+NK26xHabFopS2Jhk=
github.com/livekit/protocol v1.15.0/go.mod h1:pnn0Dv+/0K0OFqKHX6J6SreYO1dZxl6tDuAZ1ns8L/w=
github.com/livekit/protocol v1.16.1-0.20240514184417-5aa3d9771312 h1:c7oUI6WFW8UmmzXih2Itln1SFxWN+LiyoOD4DAHKGBQ=
github.com/livekit/protocol v1.16.1-0.20240514184417-5aa3d9771312/go.mod h1:pnn0Dv+/0K0OFqKHX6J6SreYO1dZxl6tDuAZ1ns8L/w=
github.com/livekit/psrpc v0.5.3-0.20240426045048-8ba067a45715 h1:vhDMOe8fxEc/amYTFo799LySPM12Fk3vc+Nc6o4gYZQ=
github.com/livekit/psrpc v0.5.3-0.20240426045048-8ba067a45715/go.mod h1:CQUBSPfYYAaevg1TNCc6/aYsa8DJH4jSRFdCeSZk5u0=
github.com/livekit/server-sdk-go/v2 v2.1.2 h1:3MhFqptHjzpsNAcisHYDtn77qrJ9szsAy4zJkeI3Mic=
Expand All @@ -97,8 +97,8 @@ github.com/mattn/go-pointer v0.0.1 h1:n+XhsuGeVO6MEAp7xyEukFINEa+Quek5psIR/ylA6o
github.com/mattn/go-pointer v0.0.1/go.mod h1:2zXcozF6qYGgmsG+SeTZz3oAbFLdD3OWqnUbNvJZAlc=
github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/nats-io/nats.go v1.31.0 h1:/WFBHEc/dOKBF6qf1TZhrdEfTmOZ5JzdJ+Y3m6Y/p7E=
github.com/nats-io/nats.go v1.31.0/go.mod h1:di3Bm5MLsoB4Bx61CBTsxuarI36WbhAwOm8QrW39+i8=
github.com/nats-io/nats.go v1.34.1 h1:syWey5xaNHZgicYBemv0nohUPPmaLteiBEUT6Q5+F/4=
github.com/nats-io/nats.go v1.34.1/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8=
github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI=
github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc=
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
Expand Down Expand Up @@ -218,10 +218,10 @@ golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIi
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 h1:aAcj0Da7eBAtrTp03QXWvm88pSyOt+UgdZw2BFZ+lEw=
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ=
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM=
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
golang.org/x/image v0.16.0 h1:9kloLAKhUufZhA12l5fwnx2NZW39/we1UhBesW433jw=
golang.org/x/image v0.16.0/go.mod h1:ugSZItdV4nOxyqp56HmXwH0Ry0nBCpjnZdpDaIHdoPs=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
Expand Down Expand Up @@ -261,8 +261,8 @@ golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
Expand All @@ -289,8 +289,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de h1:cZGRis4/ot9uVm639a+rHCUaG0JJHEsdyzSQTMX+suY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240513163218-0867130af1f8 h1:mxSlqyb8ZAHsYDCfiXN1EDdNTdvjUJSLY+OnAUtYNYA=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240513163218-0867130af1f8/go.mod h1:I7Y+G38R2bu5j1aLzfFmQfTcU/WnFuqDwLZAbvKTKpM=
google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM=
google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
Expand Down
1 change: 1 addition & 0 deletions pkg/errors/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ var (
ErrInvalidSimulcast = psrpc.NewErrorf(psrpc.NotAcceptable, "invalid simulcast configuration")
ErrSimulcastTranscode = psrpc.NewErrorf(psrpc.NotAcceptable, "simulcast is not supported when transcoding")
ErrRoomDisconnected = psrpc.NewErrorf(psrpc.NotAcceptable, "room disonnected")
ErrInvalidWHIPRestartRequest = psrpc.NewErrorf(psrpc.InvalidArgument, "whip restart request was invalid")
ErrRoomDisconnectedUnexpectedly = RetryableError{psrpc.NewErrorf(psrpc.Unavailable, "room disonnected unexpectedly")}
)

Expand Down
7 changes: 7 additions & 0 deletions pkg/service/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,13 @@ func (h *Handler) DeleteWHIPResource(ctx context.Context, req *rpc.DeleteWHIPRes
return &google_protobuf2.Empty{}, nil
}

func (h *Handler) ICERestartWHIPResource(ctx context.Context, req *rpc.ICERestartWHIPResourceRequest) (*rpc.ICERestartWHIPResourceResponse, error) {
_, span := tracer.Start(ctx, "Handler.ICERestartWHIPResource")
defer span.End()

return &rpc.ICERestartWHIPResourceResponse{}, nil
}

func (h *Handler) GetPProf(ctx context.Context, req *ipc.PProfRequest) (*ipc.PProfResponse, error) {
ctx, span := tracer.Start(ctx, "Handler.GetPProf")
defer span.End()
Expand Down
5 changes: 5 additions & 0 deletions pkg/service/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,10 @@ func RegisterIngressRpcHandlers(server rpc.IngressHandlerServer, info *livekit.I
if err := server.RegisterDeleteWHIPResourceTopic(info.State.ResourceId); err != nil {
return err
}
if err := server.RegisterICERestartWHIPResourceTopic(info.State.ResourceId); err != nil {
return err
}

}

return nil
Expand All @@ -542,6 +546,7 @@ func DeregisterIngressRpcHandlers(server rpc.IngressHandlerServer, info *livekit

if info.InputType == livekit.IngressInput_WHIP_INPUT {
server.DeregisterDeleteWHIPResourceTopic(info.State.ResourceId)
server.DeregisterICERestartWHIPResourceTopic(info.State.ResourceId)
}
}

Expand Down
59 changes: 58 additions & 1 deletion pkg/whip/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"bytes"
"context"
"fmt"
"hash/crc32"
"io"
"net/http"
"strings"
Expand All @@ -31,6 +32,7 @@ import (
"github.com/livekit/ingress/pkg/stats"
"github.com/livekit/ingress/pkg/types"
"github.com/livekit/mediatransportutil/pkg/rtcconfig"

"github.com/livekit/protocol/logger"
"github.com/livekit/protocol/rpc"
"github.com/livekit/protocol/utils"
Expand Down Expand Up @@ -151,7 +153,61 @@ func (s *WHIPServer) Start(

// Trickle, ICE Restart unimplemented for now
r.HandleFunc("/{app}/{stream_key}/{resource_id}", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotImplemented)
vars := mux.Vars(r)
streamKey := vars["stream_key"]
resourceID := vars["resource_id"]

logger.Infow("handling ICE Restart request", "resourceID", resourceID)
w.Header().Set("Access-Control-Allow-Origin", "*")

if r.Header.Get("If-Match") != "*" {
logger.Warnw("WHIP ICE Restart must have If-Match='*'", err, "streamKey", streamKey, "resourceID", resourceID)
w.WriteHeader(http.StatusUnprocessableEntity)
_, _ = w.Write([]byte("ICE Restart Request must have If-Match='*' Header"))
return
}

body, err := io.ReadAll(r.Body)
if err != nil {
logger.Warnw("WHIP ICE Restart failed to read body", err, "streamKey", streamKey, "resourceID", resourceID)
s.handleError(errors.ErrInvalidWHIPRestartRequest, w)
return
}

// Only extract the ufrag/pwd and candidates from the request
// "WHIP does not support renegotiation of non-ICE related SDP information"
//
// https://www.ietf.org/archive/id/draft-ietf-wish-whip-14.html#name-ice-restarts
userFragment, password, err := extractICEDetails(body)
if err != nil {
logger.Warnw("WHIP ICE Restart failed to unmarshal SDP", err, "streamKey", streamKey, "resourceID", resourceID)
s.handleError(errors.ErrInvalidWHIPRestartRequest, w)
return
}

if userFragment == "" || password == "" {
logger.Warnw("WHIP ICE Restart failed to extract ice-ufrag/ice-pwd", err, "streamKey", streamKey, "resourceID", resourceID)
s.handleError(errors.ErrInvalidWHIPRestartRequest, w)
return
}

resp, err := s.rpcClient.ICERestartWHIPResource(s.ctx, resourceID, &rpc.ICERestartWHIPResourceRequest{
UserFragment: userFragment,
Password: password,
ResourceId: resourceID,
StreamKey: streamKey,
}, psrpc.WithRequestTimeout(5*time.Second))
if err == psrpc.ErrNoResponse {
s.handleError(errors.ErrIngressNotFound, w)
logger.Warnw("WHIP ICE Restart failed no such session", err, "streamKey", streamKey, "resourceID", resourceID)
return
}

w.Header().Set("Content-Type", "application/trickle-ice-sdpfrag")
w.Header().Set("ETag", fmt.Sprintf("%08x", crc32.ChecksumIEEE([]byte(resp.TrickleIceSdpfrag))))
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte(resp.TrickleIceSdpfrag))

}).Methods("PATCH")

r.HandleFunc("/{app}/{stream_key}/{resource_id}", func(w http.ResponseWriter, r *http.Request) {
Expand Down Expand Up @@ -264,6 +320,7 @@ func (s *WHIPServer) handleNewWhipClient(w http.ResponseWriter, r *http.Request,
w.Header().Set("Access-Control-Expose-Headers", "Location")
w.Header().Set("Content-Type", "application/sdp")
w.Header().Set("Location", fmt.Sprintf("/%s/%s/%s", app, streamKey, resourceId))
w.Header().Set("ETag", fmt.Sprintf("%08x", crc32.ChecksumIEEE(sdpOffer.Bytes())))
w.WriteHeader(http.StatusCreated)
_, _ = w.Write([]byte(sdp))

Expand Down
54 changes: 54 additions & 0 deletions pkg/whip/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/livekit/server-sdk-go/v2/pkg/jitter"
"github.com/pion/rtp"
"github.com/pion/rtp/codecs"
"github.com/pion/sdp/v3"
"github.com/pion/webrtc/v3"
)

Expand Down Expand Up @@ -64,3 +65,56 @@ func createJitterBuffer(track *webrtc.TrackRemote, logger logger.Logger, writePL

return jb, nil
}

func extractICEDetails(in []byte) (ufrag string, pwd string, err error) {
scanAttributes := func(attributes []sdp.Attribute) {
for _, a := range attributes {
if a.Key == "ice-ufrag" {
ufrag = a.Value
} else if a.Key == "ice-pwd" {
pwd = a.Value
}
}
}

var parsed sdp.SessionDescription
if err = parsed.Unmarshal(in); err != nil {
return
}

scanAttributes(parsed.Attributes)
for _, m := range parsed.MediaDescriptions {
scanAttributes(m.Attributes)
}

return
}

func replaceICEDetails(in, ufrag, pwd string) (string, error) {
var parsed sdp.SessionDescription
replaceAttributes := func(attributes []sdp.Attribute) {
for i := range attributes {
if attributes[i].Key == "ice-ufrag" {
attributes[i].Value = ufrag
} else if attributes[i].Key == "ice-pwd" {
attributes[i].Value = pwd
}
}
}

if err := parsed.UnmarshalString(in); err != nil {
return "", err
}

replaceAttributes(parsed.Attributes)
for _, m := range parsed.MediaDescriptions {
replaceAttributes(m.Attributes)
}

newRemoteDescription, err := parsed.Marshal()
if err != nil {
return "", err
}

return string(newRemoteDescription), nil
}
54 changes: 54 additions & 0 deletions pkg/whip/whip_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package whip

import (
"bufio"
"context"
"io"
"strings"
Expand Down Expand Up @@ -696,3 +697,56 @@ func (h *whipHandler) DeleteWHIPResource(ctx context.Context, req *rpc.DeleteWHI

return &google_protobuf2.Empty{}, nil
}

func (h *whipHandler) ICERestartWHIPResource(ctx context.Context, req *rpc.ICERestartWHIPResourceRequest) (*rpc.ICERestartWHIPResourceResponse, error) {
_, span := tracer.Start(ctx, "whipHandler.ICERestartWHIPResource")
defer span.End()

if h.pc == nil {
return nil, errors.ErrIngressNotFound
}

remoteDescription := h.pc.CurrentRemoteDescription()
if remoteDescription == nil {
return nil, errors.ErrIngressNotFound
}

// Replace the current remote description with the values from remote
newRemoteDescription, err := replaceICEDetails(remoteDescription.SDP, req.UserFragment, req.Password)
if err != nil {
return nil, errors.ErrIngressNotFound
}
remoteDescription.SDP = newRemoteDescription

if err := h.pc.SetRemoteDescription(*remoteDescription); err != nil {
return nil, errors.ErrIngressNotFound
}

answer, err := h.pc.CreateAnswer(nil)
if err != nil {
return nil, errors.ErrIngressNotFound
}

gatherComplete := webrtc.GatheringCompletePromise(h.pc)
if err = h.pc.SetLocalDescription(answer); err != nil {
return nil, errors.ErrIngressNotFound
}
<-gatherComplete

// Discard all `a=` lines that aren't ICE related
// "WHIP does not support renegotiation of non-ICE related SDP information"
//
// https://www.ietf.org/archive/id/draft-ietf-wish-whip-14.html#name-ice-restarts
var trickleIceSdpfrag strings.Builder
scanner := bufio.NewScanner(strings.NewReader(h.pc.LocalDescription().SDP))
for scanner.Scan() {
l := scanner.Text()
if strings.HasPrefix(l, "a=") && !strings.HasPrefix(l, "a=ice-pwd") && !strings.HasPrefix(l, "a=ice-ufrag") && !strings.HasPrefix(l, "a=candidate") {
continue
}

trickleIceSdpfrag.WriteString(l + "\n")
}

return &rpc.ICERestartWHIPResourceResponse{TrickleIceSdpfrag: trickleIceSdpfrag.String()}, nil
}

0 comments on commit 79f141a

Please sign in to comment.