diff --git a/pkg/logging.go b/pkg/logging.go
index cefd316..a0583ff 100644
--- a/pkg/logging.go
+++ b/pkg/logging.go
@@ -87,8 +87,20 @@ func LogChannel(s *Server, channel string, username string, msg messages.Message
)
}
+// Tear down log files for subscribers.
+func (s *Subscriber) teardownLogs() {
+ if s.logfh == nil {
+ return
+ }
+
+ for username, fh := range s.logfh {
+ log.Error("TeardownLogs(%s/%s)", s.Username, username)
+ fh.Close()
+ }
+}
+
// Initialize a logging directory.
-func initLogFile(sub LogCacheable, components ...string) (io.Writer, error) {
+func initLogFile(sub LogCacheable, components ...string) (io.WriteCloser, error) {
// Initialize the logfh cache?
var logfh = sub.GetLogFilehandleCache()
@@ -126,19 +138,19 @@ func initLogFile(sub LogCacheable, components ...string) (io.Writer, error) {
// Interface for objects that hold log filehandle caches.
type LogCacheable interface {
- GetLogFilehandleCache() map[string]io.Writer
+ GetLogFilehandleCache() map[string]io.WriteCloser
}
// Implementations of LogCacheable.
-func (sub *Subscriber) GetLogFilehandleCache() map[string]io.Writer {
+func (sub *Subscriber) GetLogFilehandleCache() map[string]io.WriteCloser {
if sub.logfh == nil {
- sub.logfh = map[string]io.Writer{}
+ sub.logfh = map[string]io.WriteCloser{}
}
return sub.logfh
}
-func (s *Server) GetLogFilehandleCache() map[string]io.Writer {
+func (s *Server) GetLogFilehandleCache() map[string]io.WriteCloser {
if s.logfh == nil {
- s.logfh = map[string]io.Writer{}
+ s.logfh = map[string]io.WriteCloser{}
}
return s.logfh
}
diff --git a/pkg/server.go b/pkg/server.go
index 0dfaf64..1511c2a 100644
--- a/pkg/server.go
+++ b/pkg/server.go
@@ -19,7 +19,7 @@ type Server struct {
subscribers map[*Subscriber]struct{}
// Cached filehandles for channel logging.
- logfh map[string]io.Writer
+ logfh map[string]io.WriteCloser
}
// NewServer initializes the Server.
diff --git a/pkg/websocket.go b/pkg/websocket.go
index 424a782..e2ad059 100644
--- a/pkg/websocket.go
+++ b/pkg/websocket.go
@@ -48,7 +48,7 @@ type Subscriber struct {
// Logging.
log bool
- logfh map[string]io.Writer
+ logfh map[string]io.WriteCloser
}
// ReadLoop spawns a goroutine that reads from the websocket connection.
@@ -288,6 +288,9 @@ func (s *Server) DeleteSubscriber(sub *Subscriber) {
sub.cancel()
}
+ // Clean up any log files.
+ sub.teardownLogs()
+
s.subscribersMu.Lock()
delete(s.subscribers, sub)
s.subscribersMu.Unlock()
diff --git a/src/App.vue b/src/App.vue
index 590b777..66732c8 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -445,7 +445,7 @@ export default {
}
}
},
- "webcam.rememberExpresslyClosed": function() {
+ "webcam.rememberExpresslyClosed": function () {
LocalStorage.set('rememberExpresslyClosed', this.webcam.rememberExpresslyClosed);
},
@@ -899,6 +899,28 @@ export default {
return;
}
+ // DEBUGGING: print WebRTC statistics
+ if (this.message.toLowerCase().indexOf("/debug-webrtc") === 0) {
+ let lines = [
+ "WebRTC PeerConnections:"
+ ];
+ for (let username of Object.keys(this.WebRTC.pc)) {
+ let pc = this.WebRTC.pc[username];
+ let line = `${username}: `;
+ if (pc.offerer != undefined) {
+ line += "offerer; ";
+ }
+ if (pc.answerer != undefined) {
+ line += "answerer; ";
+ }
+ lines.push(line);
+ }
+
+ this.ChatClient(lines.join("
"));
+ this.message = "";
+ return;
+ }
+
// console.debug("Send message: %s", this.message);
this.ws.conn.send(JSON.stringify({
action: "message",
@@ -1633,6 +1655,7 @@ export default {
// The user has closed our video feed.
delete (this.webcam.watching[msg.username]);
this.playSound("Unwatch");
+ this.cleanupPeerConnections();
},
sendWatch(username, watching) {
// Send the watch or unwatch message to backend.
@@ -2108,7 +2131,7 @@ export default {
// If the local user had expressly closed this user's camera before, forget
// this action because the user now is expressly OPENING this camera on purpose.
- delete(this.WebRTC.expresslyClosed[user.username]);
+ delete (this.WebRTC.expresslyClosed[user.username]);
// Debounce so we don't spam too much for the same user.
if (this.WebRTC.debounceOpens[user.username]) return;
@@ -2174,20 +2197,38 @@ export default {
delete (this.WebRTC.streams[username]);
delete (this.WebRTC.muted[username]);
delete (this.WebRTC.poppedOut[username]);
+
+ // Should we close the WebRTC PeerConnection? If they were watching our video back, closing
+ // the connection MAY cause our video to freeze on their side: if we have the "auto-open my viewer's
+ // camera" option set, and our viewer sent their video on their open request, and they still have
+ // our camera open, do not close the connection so we don't freeze their side of the video.
if (this.WebRTC.pc[username] != undefined && this.WebRTC.pc[username].offerer != undefined) {
- this.WebRTC.pc[username].offerer.close();
- delete (this.WebRTC.pc[username]);
+ if (this.webcam.mutualOpen && this.isWatchingMe(username)) {
+ console.log(`OFFERER(${username}): Close video locally only: do not hang up the connection.`);
+ } else {
+ this.WebRTC.pc[username].offerer.close();
+ delete (this.WebRTC.pc[username]);
+ }
}
// Inform backend we have closed it.
this.sendWatch(username, false);
+ this.cleanupPeerConnections();
return;
} else if (name === "answerer") {
- // We have turned off our camera, kick off viewers.
+ // Should we close the WebRTC PeerConnection? If they were watching our video back, closing
+ // the connection MAY cause our video to freeze on their side: if we have the "auto-open my viewer's
+ // camera" option set, and our viewer sent their video on their open request, and they still have
+ // our camera open, do not close the connection so we don't freeze their side of the video.
if (this.WebRTC.pc[username] != undefined && this.WebRTC.pc[username].answerer != undefined) {
- this.WebRTC.pc[username].answerer.close();
- delete (this.WebRTC.pc[username]);
+ if (this.webcam.mutualOpen && this.isWatchingMe(username)) {
+ console.log(`ANSWERER(${username}): Close video locally only: do not hang up the connection.`);
+ } else {
+ this.WebRTC.pc[username].answerer.close();
+ delete (this.WebRTC.pc[username]);
+ }
}
+ this.cleanupPeerConnections();
return;
}
@@ -2215,6 +2256,7 @@ export default {
// Inform backend we have closed it.
this.sendWatch(username, false);
+ this.cleanupPeerConnections();
},
expresslyCloseVideo(username, name) {
// Like closeVideo but communicates the user's intent to expressly
@@ -2230,6 +2272,43 @@ export default {
this.closeVideo(username, "offerer");
}
},
+ cleanupPeerConnections() {
+ // Helper function to check and clean up WebRTC PeerConnections.
+ //
+ // This is fired on Unwatch and CloseVideo events, to double check
+ // which videos the local user has open + who online is watching our
+ // video, to close out any lingering WebRTC connections.
+ for (let username of Object.keys(this.WebRTC.pc)) {
+ let pc = this.WebRTC.pc[username];
+
+ // Is their video on our screen?
+ if (this.WebRTC.streams[username] != undefined) {
+ continue;
+ }
+
+ // Are they watching us?
+ if (this.isWatchingMe(username)) {
+ continue;
+ }
+
+ // Are they an admin that we booted?
+ if (this.isBootedAdmin(username)) {
+ continue;
+ }
+
+ // The WebRTC connections should be closed out.
+ if (pc.answerer != undefined) {
+ console.log("Clean up WebRTC answerer connection with " + username);
+ pc.answerer.close();
+ delete (this.WebRTC.pc[username]);
+ }
+ if (pc.offerer != undefined) {
+ console.log("Clean up WebRTC offerer connection with " + username);
+ pc.offerer.close();
+ delete (this.WebRTC.pc[username]);
+ }
+ }
+ },
muteAllVideos() {
// Mute the mic of all open videos.
let count = 0;
@@ -2357,7 +2436,7 @@ export default {
}
this.sendUnboot(username);
- delete(this.WebRTC.booted[username]);
+ delete (this.WebRTC.booted[username]);
return;
}
@@ -2379,7 +2458,7 @@ export default {
}
// Remove them from our list.
- delete(this.webcam.watching[username]);
+ delete (this.webcam.watching[username]);
this.ChatClient(
`You have booted ${username} off your camera. They will no longer be able ` +
@@ -2612,8 +2691,8 @@ export default {
}
// Were we at mentioned in this message?
- if (message.indexOf("@"+this.username) > -1) {
- let re = new RegExp("@"+this.username+"\\b", "ig");
+ if (message.indexOf("@" + this.username) > -1) {
+ let re = new RegExp("@" + this.username + "\\b", "ig");
message = message.replace(re, `@${this.username}`);
}
@@ -3097,7 +3176,8 @@ export default {