fix: refactor signaler; fix logic for local watcher

This commit is contained in:
Charles Hathaway
2023-09-21 21:50:13 -07:00
parent 9bbe917e59
commit 7fbd4fff69
8 changed files with 920 additions and 1076 deletions
+100 -79
View File
@@ -27,14 +27,41 @@ const (
h264FrameDuration = time.Millisecond * 33
)
func withAuth[T any](token string, v *T) *connect.Request[T] {
req := connect.NewRequest[T](v)
req.Header().Add("authorization", "Bearer "+token)
return req
}
func main() { //nolint
ctx := context.Background()
client := servicepb.NewSignalerServiceClient(&http.Client{}, "http://localhost:8080/")
httpClient := &http.Client{}
client := servicepb.NewSignalerServiceClient(httpClient, "http://localhost:8080/")
authToken, err := client.CreateAuthToken(ctx, connect.NewRequest(&pb.CreateAuthTokenRequest{
Type: &pb.CreateAuthTokenRequest_Camera_{
Camera: &pb.CreateAuthTokenRequest_Camera{
Id: "movie",
},
},
}))
if err != nil {
log.Fatalf("Failed to get auth token: %v", err)
}
token := authToken.Msg.GetToken()
// Assert that we have an audio or video file
_, err := os.Stat(videoFileName)
_, err = os.Stat(videoFileName)
haveVideoFile := !os.IsNotExist(err)
iceConnectedCtx, iceConnectedCtxCancel := context.WithCancel(context.Background())
// Create a new RTCPeerConnection
// Wait for a session request
session, err := client.PopSession(ctx, withAuth(token, &pb.PopSessionRequest{}))
if err != nil {
log.Fatalf("error creating session: %v", err)
}
peerConnection, err := webrtc.NewPeerConnection(webrtc.Configuration{
ICEServers: []webrtc.ICEServer{
{
@@ -51,8 +78,6 @@ func main() { //nolint
}
}()
iceConnectedCtx, iceConnectedCtxCancel := context.WithCancel(context.Background())
if haveVideoFile {
// Create a video track
videoTrack, videoTrackErr := webrtc.NewTrackLocalStaticSample(webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeH264}, "video", "pion")
@@ -139,90 +164,86 @@ func main() { //nolint
}
})
var iceCandidates []*pb.IceCandidate
peerConnection.OnICECandidate(func(i *webrtc.ICECandidate) {
if i == nil {
return
}
c := i.ToJSON()
iceCandidates = append(iceCandidates, &pb.IceCandidate{
Candidate: c.Candidate,
SdpMid: c.SDPMid,
SdpLineIndex: proto.Int32(int32(*c.SDPMLineIndex)),
UsernameFragment: proto.String(*c.UsernameFragment),
})
client.CreateIceMessage(ctx, withAuth(token, &pb.CreateIceMessageRequest{
SessionIdentifier: session.Msg.GetId(),
IceMessage: &pb.IceMessage{
Type: &pb.IceMessage_Candidate{
Candidate: &pb.IceCandidate{
Candidate: c.Candidate,
SdpMid: c.SDPMid,
SdpLineIndex: proto.Int32(int32(*c.SDPMLineIndex)),
UsernameFragment: proto.String(*c.UsernameFragment),
},
},
},
}))
})
// Wait for a session request
var session *pb.Session
for session == nil {
resp, err := client.ListSessions(ctx, connect.NewRequest(&pb.ListSessionsRequest{}))
if err != nil {
log.Fatalf("error creating session: %v", err)
}
if len(resp.Msg.Sessions) > 0 {
session = resp.Msg.Sessions[0]
}
time.Sleep(time.Millisecond * 500)
}
// Add ICE candidates from remote
for _, candidate := range session.GetClientIceCandidates() {
var sdpMLine *uint16
if candidate.SdpLineIndex != nil {
t := uint16(candidate.GetSdpLineIndex())
sdpMLine = &t
go func() {
for {
msg, err := client.PopIceMessage(ctx, withAuth(token, &pb.PopIceMessageRequest{
SessionIdentifier: session.Msg.GetId(),
}))
if err != nil {
log.Fatalf("failed to pop ice message: %v", err)
}
switch msg.Msg.Type.(type) {
case *pb.IceMessage_Candidate:
candidate := msg.Msg.GetCandidate()
var sdpMLine *uint16
if candidate.SdpLineIndex != nil {
t := uint16(candidate.GetSdpLineIndex())
sdpMLine = &t
}
if err := peerConnection.AddICECandidate(webrtc.ICECandidateInit{
Candidate: candidate.GetCandidate(),
SDPMid: candidate.SdpMid,
SDPMLineIndex: sdpMLine,
}); err != nil {
log.Fatalf("Failed to add ice candidate: %v", err)
}
case *pb.IceMessage_Session:
session := msg.Msg.GetSession()
offer := webrtc.SessionDescription{
Type: webrtc.SDPType(session.SdpType),
SDP: session.Sdp,
}
if err := peerConnection.SetLocalDescription(offer); err != nil {
log.Fatalf("Failed to set location description: %v", err)
}
// Send back an answer
answer, err := peerConnection.CreateAnswer(nil)
if err != nil {
log.Fatalf("Failed to create an answer: %v", err)
}
if err := peerConnection.SetRemoteDescription(answer); err != nil {
log.Fatalf("Failed to set remote description: %v", err)
}
_, err = client.CreateIceMessage(ctx, withAuth(token, &pb.CreateIceMessageRequest{
IceMessage: &pb.IceMessage{
Type: &pb.IceMessage_Session{
Session: &pb.IceSessionDescription{
SdpType: int64(answer.Type),
Sdp: answer.SDP,
},
},
},
}))
if err != nil {
log.Fatalf("Failed to send answer: %v", err)
}
}
}
peerConnection.AddICECandidate(webrtc.ICECandidateInit{
Candidate: candidate.GetCandidate(),
SDPMid: candidate.SdpMid,
SDPMLineIndex: sdpMLine,
})
}
cameraOffer, err := peerConnection.CreateOffer(nil)
if err != nil {
log.Fatalf("error creating session: %v", err)
}
// Set the remote SessionDescription
if err = peerConnection.SetRemoteDescription(cameraOffer); err != nil {
panic(err)
}
// Create channel that is blocked until ICE Gathering is complete
gatherComplete := webrtc.GatheringCompletePromise(peerConnection)
// Block until ICE Gathering is complete, disabling trickle ICE
// we do this because we only can exchange one signaling message
// in a production application you should exchange ICE Candidates via OnICECandidate
log.Printf("Waiting to gather ICE")
<-gatherComplete
log.Printf("Done gathering ICE")
session.CameraIceCandidates = iceCandidates
session.CameraOffer = &pb.IceSessionDescription{
SdpType: int64(cameraOffer.Type),
Sdp: cameraOffer.SDP,
}
// Send it back
resp, err := client.UpdateSession(ctx, connect.NewRequest(&pb.UpdateSessionRequest{
Session: session,
WaitForUpdate: true,
}))
if err != nil {
log.Fatalf("error creating session: %v", err)
}
answer := webrtc.SessionDescription{
Type: webrtc.SDPType(resp.Msg.ClientAnswer.SdpType),
SDP: resp.Msg.ClientAnswer.Sdp,
}
// Sets the LocalDescription, and starts our UDP listeners
if err = peerConnection.SetLocalDescription(answer); err != nil {
panic(err)
}
}()
// Block forever
select {}