diff --git a/.gitignore b/.gitignore index 5cc452b..2b8d4cd 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,5 @@ discord_bot/graph.png discord_bot/config.py env_win/ velconnect.db +velconnect*.db .idea/ diff --git a/unity_package/Runtime/VELConnectManager.cs b/unity_package/Runtime/VELConnectManager.cs index a34ff96..46e8034 100644 --- a/unity_package/Runtime/VELConnectManager.cs +++ b/unity_package/Runtime/VELConnectManager.cs @@ -14,7 +14,7 @@ namespace VELConnect public class VELConnectManager : MonoBehaviour { public string velConnectUrl = "http://localhost"; - public static VELConnectManager instance; + private static VELConnectManager instance; public class State { @@ -36,7 +36,8 @@ namespace VELConnect /// public string TryGetData(string key) { - return data?.TryGetValue(key, out string val) == true ? val : null; + string val = null; + return data?.TryGetValue(key, out val) == true ? val : null; } } @@ -50,6 +51,15 @@ namespace VELConnect public string last_modified; public string last_accessed; public Dictionary data; + + /// + /// Returns the value if it exists, otherwise null + /// + public string TryGetData(string key) + { + string val = null; + return data?.TryGetValue(key, out val) == true ? val : null; + } } public Device device; @@ -63,6 +73,25 @@ namespace VELConnect public static Action OnDeviceDataChanged; public static Action OnRoomDataChanged; + private static readonly Dictionary> deviceFieldCallbacks = new Dictionary>(); + private static readonly Dictionary> deviceDataCallbacks = new Dictionary>(); + private static readonly Dictionary> roomDataCallbacks = new Dictionary>(); + + private struct CallbackListener + { + /// + /// Used so that other objects don't have to remove listeners themselves + /// + public MonoBehaviour keepAliveObject; + + public Action callback; + + /// + /// Sends the first state received from the network or the state at binding time + /// + public bool sendInitialState; + } + public static int PairingCode { get @@ -80,10 +109,10 @@ namespace VELConnect { get { -#if UNITY_STANDALONE_WIN && !UNITY_EDITOR - // allows running multiple builds on the same computer - // return SystemInfo.deviceUniqueIdentifier + Hash128.Compute(Application.dataPath); - return SystemInfo.deviceUniqueIdentifier + "_BUILD"; +#if UNITY_EDITOR + // allows running multiple builds on the same computer + // return SystemInfo.deviceUniqueIdentifier + Hash128.Compute(Application.dataPath); + return SystemInfo.deviceUniqueIdentifier + "_EDITOR"; #else return SystemInfo.deviceUniqueIdentifier; #endif @@ -137,7 +166,7 @@ namespace VELConnect { "version", Application.version }, { "platform", SystemInfo.operatingSystem }, }; - PostRequestCallback(velConnectUrl + "/api/v2/update_user_count", JsonConvert.SerializeObject(postData)); + PostRequestCallback(velConnectUrl + "/api/update_user_count", JsonConvert.SerializeObject(postData)); }); } @@ -147,11 +176,13 @@ namespace VELConnect { try { - GetRequestCallback(velConnectUrl + "/api/v2/get_state/" + DeviceId, json => + GetRequestCallback(velConnectUrl + "/api/v2/device/get_data/" + DeviceId, json => { State state = JsonConvert.DeserializeObject(json); if (state == null) return; + bool isInitialState = false; + // first load stuff if (lastState == null) { @@ -164,8 +195,9 @@ namespace VELConnect Debug.LogError(e); } - lastState = state; - return; + isInitialState = true; + // lastState = state; + // return; } @@ -176,52 +208,126 @@ namespace VELConnect foreach (FieldInfo fieldInfo in fields) { string newValue = fieldInfo.GetValue(state.device) as string; - string oldValue = fieldInfo.GetValue(lastState.device) as string; + string oldValue = lastState != null ? fieldInfo.GetValue(lastState.device) as string : null; if (newValue != oldValue) { try { - OnDeviceFieldChanged?.Invoke(fieldInfo.Name, newValue); + if (!isInitialState) OnDeviceFieldChanged?.Invoke(fieldInfo.Name, newValue); } catch (Exception e) { Debug.LogError(e); } + + // send specific listeners data + if (deviceFieldCallbacks.ContainsKey(fieldInfo.Name)) + { + // clear the list of old listeners + deviceFieldCallbacks[fieldInfo.Name].RemoveAll(e => e.keepAliveObject == null); + + // send the callbacks + deviceFieldCallbacks[fieldInfo.Name].ForEach(e => + { + if (!isInitialState || e.sendInitialState) + { + try + { + e.callback(newValue); + } + catch (Exception ex) + { + Debug.LogError(ex); + } + } + }); + } } } - foreach (KeyValuePair elem in state.device.data) + if (state.device.data != null) { - lastState.device.data.TryGetValue(elem.Key, out string oldValue); - if (elem.Value != oldValue) + foreach (KeyValuePair elem in state.device.data) { - try + string oldValue = null; + lastState?.device.data.TryGetValue(elem.Key, out oldValue); + if (elem.Value != oldValue) { - OnDeviceDataChanged?.Invoke(elem.Key, elem.Value); - } - catch (Exception e) - { - Debug.LogError(e); + try + { + if (!isInitialState) OnDeviceDataChanged?.Invoke(elem.Key, elem.Value); + } + catch (Exception ex) + { + Debug.LogError(ex); + } + + // send specific listeners data + if (deviceDataCallbacks.ContainsKey(elem.Key)) + { + // clear the list of old listeners + deviceDataCallbacks[elem.Key].RemoveAll(e => e.keepAliveObject == null); + + // send the callbacks + deviceDataCallbacks[elem.Key].ForEach(e => + { + if (!isInitialState || e.sendInitialState) + { + try + { + e.callback(elem.Value); + } + catch (Exception ex) + { + Debug.LogError(ex); + } + } + }); + } } } } } - if (state.room.modified_by != DeviceId) + if (state.room.modified_by != DeviceId && state.room.data != null) { foreach (KeyValuePair elem in state.room.data) { - lastState.room.data.TryGetValue(elem.Key, out string oldValue); + string oldValue = null; + lastState?.room.data.TryGetValue(elem.Key, out oldValue); if (elem.Value != oldValue) { try { - OnRoomDataChanged?.Invoke(elem.Key, elem.Value); + if (!isInitialState) OnRoomDataChanged?.Invoke(elem.Key, elem.Value); } catch (Exception e) { Debug.LogError(e); } + + // send specific listeners data + if (roomDataCallbacks.ContainsKey(elem.Key)) + { + // clear the list of old listeners + roomDataCallbacks[elem.Key].RemoveAll(e => e.keepAliveObject == null); + + // send the callbacks + roomDataCallbacks[elem.Key].ForEach(e => + { + if (!isInitialState || e.sendInitialState) + { + try + { + e.callback(elem.Value); + } + catch (Exception ex) + { + Debug.LogError(ex); + } + } + }); + } } } } @@ -239,16 +345,130 @@ namespace VELConnect } } + /// + /// Adds a change listener callback to a particular field name within the Device main fields. + /// + public static void AddDeviceFieldListener(string key, MonoBehaviour keepAliveObject, Action callback, bool sendInitialState = false) + { + if (!deviceFieldCallbacks.ContainsKey(key)) + { + deviceFieldCallbacks[key] = new List(); + } + + deviceFieldCallbacks[key].Add(new CallbackListener() + { + keepAliveObject = keepAliveObject, + callback = callback, + sendInitialState = sendInitialState + }); + + if (sendInitialState) + { + if (instance != null && instance.lastState?.device != null) + { + if (instance.lastState.device.GetType().GetField(key)?.GetValue(instance.lastState.device) is string val) + { + try + { + callback(val); + } + catch (Exception e) + { + Debug.LogError(e); + } + } + } + } + } + + /// + /// Adds a change listener callback to a particular field name within the Device data JSON. + /// + public static void AddDeviceDataListener(string key, MonoBehaviour keepAliveObject, Action callback, bool sendInitialState = false) + { + if (!deviceDataCallbacks.ContainsKey(key)) + { + deviceDataCallbacks[key] = new List(); + } + + deviceDataCallbacks[key].Add(new CallbackListener() + { + keepAliveObject = keepAliveObject, + callback = callback, + sendInitialState = sendInitialState + }); + + if (sendInitialState) + { + string val = GetDeviceData(key); + if (val != null) + { + try + { + callback(val); + } + catch (Exception e) + { + Debug.LogError(e); + } + } + } + } + + /// + /// Adds a change listener callback to a particular field name within the Room data JSON. + /// + public static void AddRoomDataListener(string key, MonoBehaviour keepAliveObject, Action callback, bool sendInitialState = false) + { + if (!roomDataCallbacks.ContainsKey(key)) + { + roomDataCallbacks[key] = new List(); + } + + roomDataCallbacks[key].Add(new CallbackListener() + { + keepAliveObject = keepAliveObject, + callback = callback, + sendInitialState = sendInitialState + }); + + if (sendInitialState) + { + string val = GetRoomData(key); + if (val != null) + { + try + { + callback(val); + } + catch (Exception e) + { + Debug.LogError(e); + } + } + } + } + + public static string GetDeviceData(string key) + { + return instance != null ? instance.lastState?.device?.TryGetData(key) : null; + } + + public static string GetRoomData(string key) + { + return instance != null ? instance.lastState?.room?.TryGetData(key) : null; + } + /// /// Sets data on the device keys themselves /// public static void SetDeviceBaseData(Dictionary data) { - data["modified_by"] = DeviceId; instance.PostRequestCallback( instance.velConnectUrl + "/api/v2/device/set_data/" + DeviceId, - JsonConvert.SerializeObject(data) + JsonConvert.SerializeObject(data), + new Dictionary { { "modified_by", DeviceId } } ); } @@ -259,11 +479,8 @@ namespace VELConnect { instance.PostRequestCallback( instance.velConnectUrl + "/api/v2/device/set_data/" + DeviceId, - JsonConvert.SerializeObject(new Dictionary - { - { "modified_by", DeviceId }, - { "data", data } - }) + JsonConvert.SerializeObject(new Dictionary { { "data", data } }), + new Dictionary { { "modified_by", DeviceId } } ); } @@ -275,10 +492,10 @@ namespace VELConnect return; } - data["modified_by"] = DeviceId; instance.PostRequestCallback( instance.velConnectUrl + "/api/v2/set_data/" + Application.productName + "_" + VelNetManager.Room, - JsonConvert.SerializeObject(data) + JsonConvert.SerializeObject(data), + new Dictionary { { "modified_by", DeviceId } } ); } @@ -308,13 +525,13 @@ namespace VELConnect } } - public void PostRequestCallback(string url, string postData, Action successCallback = null, + public void PostRequestCallback(string url, string postData, Dictionary headers = null, Action successCallback = null, Action failureCallback = null) { - StartCoroutine(PostRequestCallbackCo(url, postData, successCallback, failureCallback)); + StartCoroutine(PostRequestCallbackCo(url, postData, headers, successCallback, failureCallback)); } - private static IEnumerator PostRequestCallbackCo(string url, string postData, Action successCallback = null, + private static IEnumerator PostRequestCallbackCo(string url, string postData, Dictionary headers = null, Action successCallback = null, Action failureCallback = null) { UnityWebRequest webRequest = new UnityWebRequest(url, "POST"); @@ -322,6 +539,14 @@ namespace VELConnect UploadHandlerRaw uploadHandler = new UploadHandlerRaw(bodyRaw); webRequest.uploadHandler = uploadHandler; webRequest.SetRequestHeader("Content-Type", "application/json"); + if (headers != null) + { + foreach (KeyValuePair keyValuePair in headers) + { + webRequest.SetRequestHeader(keyValuePair.Key, keyValuePair.Value); + } + } + yield return webRequest.SendWebRequest(); switch (webRequest.result) diff --git a/velconnect/db.py b/velconnect/db.py index 8f5927f..6356ea1 100644 --- a/velconnect/db.py +++ b/velconnect/db.py @@ -24,6 +24,7 @@ class DB: return conn, curr def query(self, query_string: str, data: dict = None) -> list: + conn = None try: conn, curr = self.create_or_connect() if data is not None: @@ -35,7 +36,8 @@ class DB: return values except: print(traceback.print_exc()) - conn.close() + if conn is not None: + conn.close() raise def insert(self, query_string: str, data: dict = None) -> bool: diff --git a/velconnect/routes/api_v2.py b/velconnect/routes/api_v2.py index 790d804..a4e0723 100644 --- a/velconnect/routes/api_v2.py +++ b/velconnect/routes/api_v2.py @@ -64,12 +64,12 @@ def get_device_by_pairing_code(pairing_code: str): def create_device(hw_id: str): db.insert(""" - INSERT IGNORE INTO `Device`(hw_id) VALUES (:hw_id); + INSERT OR IGNORE INTO `Device`(hw_id) VALUES (:hw_id); """, {'hw_id': hw_id}) @router.get('/device/get_data/{hw_id}') -def get_state(hw_id: str): +def get_state(request: fastapi.Request, hw_id: str): """Gets the device state""" devices = db.query(""" @@ -77,26 +77,35 @@ def get_state(hw_id: str): """, {'hw_id': hw_id}) if len(devices) == 0: return {'error': "Can't find device with that id."} + block = dict(devices[0]) + if 'data' in block and block['data'] is not None: + block['data'] = json.loads(block['data']) - room_data = get_data(f"{devices[0]['current_app']}_{devices[0]['current_room']}") + room_key: str = f"{devices[0]['current_app']}_{devices[0]['current_room']}" + room_data = get_data(room_key) - return {'device': devices[0], 'room': room_data} + if "error" in room_data: + set_data(request, data={}, key=room_key, modified_by=None, category="room") + room_data = get_data(room_key) + + return {'device': block, 'room': room_data} @router.post('/device/set_data/{hw_id}') -def set_state(hw_id: str, data: dict, request: fastapi.Request): +def set_state(request: fastapi.Request, hw_id: str, data: dict, modified_by: str = None): """Sets the device state""" create_device(hw_id) # add the client's IP address if no sender specified - if 'modified_by' not in data: - data['modified_by'] = str(request.client) + "_" + str(request.headers) + if 'modified_by' in data: + modified_by = data['modified_by'] + if modified_by is None: + modified_by: str = str(request.client) + "_" + str(request.headers) allowed_keys: list[str] = [ 'os_info', 'friendly_name', - 'modified_by', 'current_app', 'current_room', 'pairing_code', @@ -107,15 +116,17 @@ def set_state(hw_id: str, data: dict, request: fastapi.Request): db.insert(f""" UPDATE `Device` SET {key}=:value, - last_modified=CURRENT_TIMESTAMP + last_modified=CURRENT_TIMESTAMP, + modified_by=:modified_by WHERE `hw_id`=:hw_id; """, { 'value': data[key], 'hw_id': hw_id, - 'sender_id': data['sender_id'] + 'modified_by': modified_by }) if key == "data": + new_data = data['data'] # get the old json values and merge the data old_data_query = db.query(""" SELECT data @@ -124,8 +135,10 @@ def set_state(hw_id: str, data: dict, request: fastapi.Request): """, {"hw_id": hw_id}) if len(old_data_query) == 1: - old_data: dict = json.loads(old_data_query[0]["data"]) - data = {**old_data, **data} + old_data: dict = {} + if old_data_query[0]['data'] is not None: + old_data = json.loads(old_data_query[0]["data"]) + new_data = {**old_data, **new_data} # add the data to the db db.insert(""" @@ -133,7 +146,7 @@ def set_state(hw_id: str, data: dict, request: fastapi.Request): SET data=:data, last_modified=CURRENT_TIMESTAMP WHERE hw_id=:hw_id; - """, {"hw_id": hw_id, "data": json.dumps(data)}) + """, {"hw_id": hw_id, "data": json.dumps(new_data)}) return {'success': True} @@ -143,18 +156,22 @@ def generate_id(length: int = 4) -> str: @router.post('/set_data') -def store_data_with_random_key(request: fastapi.Request, data: dict, category: str = None) -> dict: +def set_data_with_random_key(request: fastapi.Request, data: dict, modified_by: str = None, + category: str = None) -> dict: """Creates a little storage bucket for arbitrary data with a random key""" - return store_data(request, data, None, category) + return set_data(request, data, None, modified_by, category) @router.post('/set_data/{key}') -def store_data(request: fastapi.Request, data: dict, key: str = None, modified_by: str = None, category: str = None) -> dict: +def set_data(request: fastapi.Request, data: dict, key: str = None, modified_by: str = None, + category: str = None) -> dict: """Creates a little storage bucket for arbitrary data""" # add the client's IP address if no sender specified + if 'modified_by' in data: + modified_by = data['modified_by'] if modified_by is None: - modified_by = str(request.client) + "_" + str(request.headers) + modified_by: str = str(request.client) + "_" + str(request.headers) # generates a key if none was supplied if key is None: @@ -189,7 +206,7 @@ def get_data(key: str) -> dict: """Gets data from a storage bucket for arbitrary data""" data = db.query(""" - SELECT data + SELECT * FROM `DataBlock` WHERE id=:id """, {"id": key}) @@ -202,7 +219,11 @@ def get_data(key: str) -> dict: try: if len(data) == 1: - return json.loads(data[0]) + block = dict(data[0]) + if 'data' in block and block['data'] is not None: + block['data'] = json.loads(block['data']) + return block return {'error': 'Not found'} - except: + except Exception as e: + print(e) return {'error': 'Unknown. Maybe no data at this key.'} diff --git a/velconnect/static/js/util.js b/velconnect/static/js/util.js index dae3ab8..47a1c2f 100644 --- a/velconnect/static/js/util.js +++ b/velconnect/static/js/util.js @@ -18,8 +18,8 @@ function httpGetAsync(theUrl, callback, failCallback) { function httpPostAsync(theUrl, data, callback, failCallback) { var xmlHttp = new XMLHttpRequest(); xmlHttp.onreadystatechange = function () { - if (xmlHttp.readyState == 4) { - if (xmlHttp.status == 200) { + if (xmlHttp.readyState === 4) { + if (xmlHttp.status === 200) { callback(xmlHttp.responseText); } else { failCallback(xmlHttp.status); diff --git a/velconnect/templates/index.html b/velconnect/templates/index.html index a6adf72..f1b2c4f 100644 --- a/velconnect/templates/index.html +++ b/velconnect/templates/index.html @@ -94,7 +94,7 @@
Last Login
-
---
+
---
@@ -167,6 +167,8 @@
Current Room
+ @@ -185,14 +187,36 @@

+ +
+ +
@@ -208,6 +232,7 @@ let headset_details = document.getElementById('headset_details'); let hw_id_field = document.getElementById('hw_id'); let failure = document.getElementById('failure'); + let current_app = document.getElementById('current_app'); let current_room = document.getElementById('current_room'); let set_room_id = document.getElementById('set_room_id'); let set_user_color = document.getElementById('set_user_color'); @@ -221,9 +246,10 @@ // check cookie let hw_id = getCookie('hw_id'); + if (hw_id !== "" && hw_id !== undefined && hw_id !== "undefined") { - httpGetAsync('/api/get_state/' + hw_id, (resp) => { + httpGetAsync('/api/v2/device/get_data/' + hw_id, (resp) => { console.log(resp); let respData = JSON.parse(resp); @@ -231,20 +257,17 @@ window.location.href = "/pair"; } - writeClass('hw_id', respData['user']['hw_id']); - writeClass('pairing_code', respData['user']['pairing_code']); - writeValue('current_room', respData['user']['current_room']); - writeClass('date_created', respData['user']['date_created'] + "
" + timeSinceString(respData['user']['date_created']) + " ago"); - writeClass('last_used', respData['user']['last_used'] + "
" + timeSinceString(respData['user']['last_used']) + " ago"); - writeValue('user_color', respData['user']['user_color']); - if (user_color) user_color.parentElement.style.color = "" + respData['user']['user_color']; - writeValue('user_name', respData['user']['user_name']); - writeValue('avatar_url', respData['user']['avatar_url']); - if (respData['room']) { - writeValue('tv_url', respData['room']['tv_url']); - writeValue('carpet_color', respData['room']['carpet_color']); - if (carpet_color) carpet_color.parentElement.style.color = "" + respData['room']['carpet_color']; - } + writeClass('hw_id', respData['device']['hw_id']); + writeClass('pairing_code', respData['device']['pairing_code']); + writeValue('current_app', respData['device']['current_app']); + writeValue('current_room', respData['device']['current_room']); + writeClass('date_created', respData['device']['date_created'] + "
" + timeSinceString(respData['device']['date_created']) + " ago"); + writeClass('last_modified', respData['device']['last_modified'] + "
" + timeSinceString(respData['device']['last_modified']) + " ago"); + writeValue('user_name', respData['device']['friendly_name']); + writeValue('avatar_url', respData['device']['data']?.['avatar_url']); + writeValue('tv_url', respData['room']?.['data']?.['tv_url']); + writeValue('carpet_color', respData['room']?.['data']?.['carpet_color']); + if (carpet_color) carpet_color.parentElement.style.color = "" + respData['room']?.['data']?.['carpet_color']; Coloris({ @@ -271,45 +294,61 @@ failure.style.display = "block"; }); - function setUserData(data) { - data["sender_id"] = "web"; - httpPostAsync('/api/set_headset_details/' + hw_id, - data, - (resp) => { - console.log('success'); + + function setDeviceField(data) { + fetch('/api/v2/device/set_data/' + hw_id, { + method: 'POST', + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json' }, - (status) => { - console.log('fail'); - } - ); + + body: JSON.stringify(data) + }) + .then(_ => console.log('success')) + .catch(_ => console.log('fail')); + } + + function setDeviceData(data) { + fetch('/api/v2/device/set_data/' + hw_id, { + method: 'POST', + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json' + }, + + body: JSON.stringify({"data": data}) + }) + .then(_ => console.log('success')) + .catch(_ => console.log('fail')); } function setRoomData(data) { - data["sender_id"] = "web"; - httpPostAsync('/api/set_room_details/' + current_room.value, - data, - (resp) => { - console.log('success'); + fetch('/api/v2/set_data/' + current_app.value + "_" + current_room.value, { + method: 'POST', + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json' }, - (status) => { - console.log('fail'); - } - ); + body: JSON.stringify(data) + }) + .then(_ => console.log('success')) + .catch(_ => console.log('fail')); } if (set_room_id) { set_room_id.addEventListener('click', () => { - setUserData({"current_room": current_room.value}); + setDeviceField({"current_room": current_room.value}); }); } if (set_user_color) { set_user_color.addEventListener('click', () => { - setUserData({"user_color": document.getElementById('user_color').value}); + setDeviceData({"user_color": document.getElementById('user_color').value}); }); } if (set_user_name) { set_user_name.addEventListener('click', () => { - setUserData({"user_name": document.getElementById('user_name').value}); + setDeviceField({"friendly_name": document.getElementById('user_name').value}); }); } if (set_tv_url) { @@ -324,7 +363,7 @@ } if (set_avatar_url) { set_avatar_url.addEventListener('click', () => { - setUserData({"avatar_url": document.getElementById('avatar_url').value}); + setDeviceData({"avatar_url": document.getElementById('avatar_url').value}); }); } @@ -351,6 +390,84 @@ }); + const roomName = (Math.random() + 1).toString(36).substring(7); + const pubVideo = document.getElementById("pub_video"); + const subVideo = document.getElementById("sub_video"); + const bntPubCam = document.getElementById("bnt_pubcam"); + const bntPubScreen = document.getElementById("bnt_pubscreen"); + + setDeviceData({"streamer_stream_id": roomName}); + + const serverURL = "wss://velnet.ugavel.com/ws"; + + const config = { + iceServers: [ + { + urls: "stun:stun.l.google.com:19302", + }, + ], + }; + + const signalLocal = new Signal.IonSFUJSONRPCSignal(serverURL); + const clientLocal = new IonSDK.Client(signalLocal, config); + + signalLocal.onopen = () => clientLocal.join(roomName); + + const start = (type) => { + if (type) { + IonSDK.LocalStream.getUserMedia({ + resolution: "vga", + audio: true, + video: true, + codec: "vp8", + }).then((media) => { + pubVideo.srcObject = media; + pubVideo.autoplay = true; + pubVideo.controls = true; + pubVideo.muted = true; + bntPubCam.disabled = true; + bntPubScreen.disabled = true; + clientLocal.publish(media); + }).catch(console.error); + } else { + IonSDK.LocalStream.getDisplayMedia({ + audio: true, + video: true, + codec: "vp8", + }).then((media) => { + pubVideo.srcObject = media; + pubVideo.autoplay = true; + pubVideo.controls = true; + pubVideo.muted = true; + bntPubCam.disabled = true; + bntPubScreen.disabled = true; + clientLocal.publish(media); + }).catch(console.error); + } + } + + clientLocal.ontrack = (track, stream) => { + console.log("got track: ", track.id, "for stream: ", stream.id); + stream.mute(); + stream.unmute(); + if (track.kind === "video") { + subVideo.srcObject = stream; + subVideo.play(); + } + //track.onunmute = () => { + //subVideo.srcObject = stream; + //subVideo.autoplay = true; + //subVideo.muted = true; + + //subVideo.play(); + + //stream.onremovetrack = () => { + //subVideo.srcObject = null; + //} + //} + } + + diff --git a/velconnect/templates/pair.html b/velconnect/templates/pair.html index c28968e..cd8f2e0 100644 --- a/velconnect/templates/pair.html +++ b/velconnect/templates/pair.html @@ -45,48 +45,48 @@ -
- +
+ -
-
- -
-
-
Enter Pairing Code
-
-
-
- You can find the code in the bottom left of your menu tablet in conVRged. -
- +
+
+ +
+
+
Enter Pairing Code
+
+
+
+ You can find the code in the bottom left of your menu tablet in conVRged. +
+
+
- + }); + \ No newline at end of file