diff --git a/display.go b/display.go index a6d19bf..b1dcc22 100644 --- a/display.go +++ b/display.go @@ -11,18 +11,21 @@ import "github.com/gorilla/websocket" // Display instance type Display struct { // WebSocket used to communicate with the display - sock *websocket.Conn - sockSendMtx sync.Mutex + sock *websocket.Conn; + sockSendMtx sync.Mutex; + + // User mutex + userMtx sync.Mutex; // User currently connected to the display - user *User + user *User; // One-time-pass currently shown on the display - otpMtx sync.Mutex - otp string + otpMtx sync.Mutex; + otp string; // Channel to pass the answer from the display coroutine to the user couroutine - answerCh chan string + answerCh chan string; } // Helper function to flush channels @@ -50,7 +53,7 @@ func chReadTimeout(ch *chan string, timeoutMS int) (string, error) { return data, nil // If no data has been received and the timeout is reached, return an error - case <-time.After(timeoutMS * time.Millisecond): + case <-time.After(time.Millisecond * time.Duration(timeoutMS)): return "", errors.New("timeout") } } @@ -206,7 +209,28 @@ func displayHandler(sock *websocket.Conn, dispID string, otp string) { disp.answerCh <- answer case "ice-candidate": - // TODO + // Check that the message contains an ice candidate + candidate, valid := msg.arguments["candidate"].(string) + if (!valid) { break; } + + // Acquire the user's display pointer + disp.userMtx.Lock(); + + // Check that a user is connected to a display + if (disp.user == nil) { + // Release the user's display pointer + disp.userMtx.Unlock(); + + // Send back an error + sendErrorMessage(sock, http.StatusForbidden); + continue; + } + + // Send the ice candidtate to the display + disp.user.iceCandidate(candidate); + + // Release the user's display pointer + disp.userMtx.Unlock(); default: // Give up diff --git a/user.go b/user.go index 2186097..eab2a59 100644 --- a/user.go +++ b/user.go @@ -10,6 +10,7 @@ import "github.com/gorilla/websocket" type User struct { // WebSocket used to communicate with the user sock *websocket.Conn; + sockSendMtx sync.Mutex; // Display mutex displayMtx sync.Mutex; @@ -18,6 +19,23 @@ type User struct { display *Display; } +// Send an ICE candiate to the user +func (this *User) iceCandidate(candidate string) { + // Acquire the sending mutex + this.sockSendMtx.Lock() + + // Send the candidate + sendMessage(this.sock, Message{ + mtype: "ice-candidate", + arguments: map[string]interface{}{ + "candidate": candidate, + }, + }) + + // Release the sending mutex + this.sockSendMtx.Unlock() +} + // Connection handler for users func userHandler(sock *websocket.Conn) { // Initialize the user instance @@ -170,8 +188,6 @@ func userHandler(sock *websocket.Conn) { // Send the ice candidtate to the display user.display.iceCandidate(candidate); - // TODO: Check error - // Release the user's display pointer user.displayMtx.Unlock(); diff --git a/www/index.html b/www/index.html index 740efc5..3a6f216 100644 --- a/www/index.html +++ b/www/index.html @@ -15,16 +15,16 @@

📺


-

-

- +

+

+
diff --git a/www/scripts/user.js b/www/scripts/user.js index 284528e..d0e31f5 100644 --- a/www/scripts/user.js +++ b/www/scripts/user.js @@ -4,37 +4,69 @@ let stream = null; let conn = null; // GUI Objects -let connForm = document.querySelector('#connForm'); -let dispNameTb = document.querySelector('#dispName'); -let connBtn = document.querySelector('#connect'); -let pinValForm = document.querySelector('#pinValForm'); -let dispPINTb = document.querySelector('#dispPIN'); -let validateBtn = document.querySelector('#validate'); -let streamForm = document.querySelector('#streamForm'); -let locPlayback = document.querySelector('#localPlayback'); +let connForm = document.getElementById('connForm'); +let dispNameTb = document.getElementById('dispName'); +let connBtn = document.getElementById('connect'); +let pinValForm = document.getElementById('pinValForm'); +let dispPINTb = document.getElementById('dispPIN'); +let validateBtn = document.getElementById('validate'); +let streamForm = document.getElementById('streamForm'); +let locPlayback = document.getElementById('localPlayback'); -// Connect to the server using WebSockets -console.log('Connecting to websocket...') -sock = new WebSocket(`ws://${location.host}/sig`); -sock.addEventListener('open', async (event) => { - console.log('Connected to websocket') +// User API class +class WisCastUserAPI { + // Socket to the API endpoint + #sock; - // // DEBUGGING ONLY - // await sock.send(JSON.stringify({ - // type: 'init', - // pin: dispPINTb.value - // })) + // Endpoint URL + #endpoint; - await sock.send(JSON.stringify({ - type: 'init', - clientType: 'user' - })); -}); + constructor(endpoint) { + // Save the endpoint + this.endpoint = endpoint; + } -sock.addEventListener('message', (event) => { - console.log(event.data) -}); + // Connect to the API + async connect() { + // Connect to the WebSocket endpoint + console.log('Connecting to the API...') + this.#sock = new WebSocket(endpoint); -sock.addEventListener('close', (event) => { - console.log('Disconnected from websocket') -}); \ No newline at end of file + // Handle connection + sock.addEventListener('open', this.#connectHandler); + + // Handle messages + sock.addEventListener('message', this.#messageHandler); + + // Handle disconnection + sock.addEventListener('close', this.#disconnectHandler); + } + + // Connect to a display using its ID and OTP + async connectDisplay(dispID, OTP) { + + } + + #connectHandler(event) { + console.log('Connected!') + } + + #messageHandler(event) { + console.log(event.data) + } + + #disconnectHandler(event) { + console.log('Disconnected :/') + } +} + +async function main() { + // Create the API connection + const api = new WisCastUserAPI(`ws://${location.host}/sig`); + + // Connect to the server + await api.connect(); +} + +// Run the main function +main(); \ No newline at end of file