Adds server-side speaker stats handling.

Adds the component which receives the messages from client and a module which enabled on a virtual host will start advertising the component. When clients discover the component they will send message to the component with the name of the room where the dominant speaker event happen.
This commit is contained in:
damencho 2018-10-01 14:02:51 -05:00 committed by Hristo Terezov
parent f1ca2cac96
commit 3b4037553a
4 changed files with 191 additions and 1 deletions

View File

@ -0,0 +1,22 @@
# Enabling speakerstats prosody module
To enable the speaker stats we need to enable speakerstats module under the main
virtual host, this is to enable the advertising the speaker stats component,
which address needs to be specified in `speakerstats_component` option.
We need to also enable the component with the address specified in `speakerstats_component`.
The component needs also to have the option with the muc component address in
`muc_component` option.
```lua
VirtualHost "jitsi.example.com"
speakerstats_component = "speakerstats.jitsi.example.com"
modules_enabled = {
"speakerstats";
}
Component "speakerstats.jitsi.example.com" "speakerstats_component"
muc_component = "conference.jitsi.example.com"
Component "conference.jitsi.example.com" "muc"
```

View File

@ -31,11 +31,28 @@ local function missed(stanza, call_id)
module:log("warn", "Implement this lib to trigger external events.")
end
-- Event that speaker stats for a conference are available
-- this is a table where key is the jid and the value is a table:
--{
-- totalDominantSpeakerTime
-- nick
-- displayName
--}
-- This trigger is left unimplemented. The implementation is expected
-- to be specific to the deployment.
local function speaker_stats(room, speakerStats)
module:log(
"warn",
"A module has been configured that triggers external events."
)
module:log("warn", "Implement this lib to trigger external events.")
end
local ext_events = {
missed = missed,
invite = invite,
cancel = cancel
cancel = cancel,
speaker_stats = speaker_stats
}
return ext_events

View File

@ -0,0 +1,7 @@
local speakerstats_component
= module:get_option_string(
"speakerstats_component", "speakerstats"..module.host);
-- Advertise speaker stats so client can pick up the address and start sending
-- dominant speaker events
module:add_identity("component", "speakerstats", speakerstats_component);

View File

@ -0,0 +1,144 @@
local get_room_from_jid = module:require "util".get_room_from_jid;
local jid_resource = require "util.jid".resource;
local ext_events = module:require "ext_events"
local muc_component_host = module:get_option_string("muc_component");
if muc_component_host == nil then
log("error", "No muc_component specified. No muc to operate on!");
return;
end
local muc_module = module:context("conference."..muc_component_host);
if muc_module == nil then
log("error", "No such muc found, check muc_component config.");
return;
end
log("debug", "Starting speakerstats for %s", muc_component_host);
-- receives messages from client currently connected to the room
-- clients indicates their own dominant speaker events
function on_message(event)
-- Check the type of the incoming stanza to avoid loops:
if event.stanza.attr.type == "error" then
return; -- We do not want to reply to these, so leave.
end
local speakerStats
= event.stanza:get_child('speakerstats', 'http://jitsi.org/jitmeet');
if speakerStats then
local roomAddress = speakerStats.attr.room;
local room = get_room_from_jid(roomAddress);
if not room then
log("warn", "No room found %s", roomAddress);
return false;
end
local roomSpeakerStats = room.speakerStats;
local from = event.stanza.attr.from;
local occupant = room:get_occupant_by_real_jid(from);
if not occupant then
log("warn", "No occupant %s found for %s", from, roomAddress);
return false;
end
local newDominantSpeaker = roomSpeakerStats[occupant.jid];
local oldDominantSpeakerId = roomSpeakerStats['dominantSpeakerId'];
if oldDominantSpeakerId then
roomSpeakerStats[oldDominantSpeakerId]:setIsDominantSpeaker(false);
end
if newDominantSpeaker then
newDominantSpeaker:setIsDominantSpeaker(true);
end
room.speakerStats['dominantSpeakerId'] = occupant.jid;
end
return true
end
--- Start SpeakerStats implementation
local SpeakerStats = {};
SpeakerStats.__index = SpeakerStats;
function new_SpeakerStats(nick)
return setmetatable({
totalDominantSpeakerTime = 0;
_dominantSpeakerStart = nil;
_isDominantSpeaker = false;
nick = nick;
displayName = nil;
}, SpeakerStats);
end
-- Changes the dominantSpeaker data for current occupant
-- saves start time if it is new dominat speaker
-- or calculates and accumulates time of speaking
function SpeakerStats:setIsDominantSpeaker(isNowDominantSpeaker)
log("debug",
"set isDominant %s for %s", tostring(isNowDominantSpeaker), self.nick);
if not self._isDominantSpeaker and isNowDominantSpeaker then
self._dominantSpeakerStart = os.time();
elseif self._isDominantSpeaker and not isNowDominantSpeaker then
local now = os.time();
local timeElapsed = now - (self._dominantSpeakerStart or 0);
self.totalDominantSpeakerTime
= self.totalDominantSpeakerTime + timeElapsed;
self._dominantSpeakerStart = nil;
end
self._isDominantSpeaker = isNowDominantSpeaker;
end
--- End SpeakerStats
-- create speakerStats for the room
function room_created(event)
local room = event.room;
room.speakerStats = {};
end
-- Create SpeakerStats object for the joined user
function occupant_joined(event)
local room = event.room;
local occupant = event.occupant;
local nick = jid_resource(occupant.nick);
if room.speakerStats then
room.speakerStats[occupant.jid] = new_SpeakerStats(nick);
end
end
-- Occupant left set its dominant speaker to false and update the store the
-- display name
function occupant_leaving(event)
local room = event.room;
local occupant = event.occupant;
local speakerStatsForOccupant = room.speakerStats[occupant.jid];
if speakerStatsForOccupant then
speakerStatsForOccupant:setIsDominantSpeaker(false);
-- set display name
local displayName = occupant:get_presence():get_child_text(
'nick', 'http://jabber.org/protocol/nick');
speakerStatsForOccupant.displayName = displayName;
end
end
-- Conference ended, send speaker stats
function room_destroyed(event)
local room = event.room;
ext_events.speaker_stats(room, room.speakerStats);
end
module:hook("message/host", on_message);
muc_module:hook("muc-room-created", room_created, -1);
muc_module:hook("muc-occupant-joined", occupant_joined, -1);
muc_module:hook("muc-occupant-pre-leave", occupant_leaving, -1);
muc_module:hook("muc-room-destroyed", room_destroyed, -1);