diff --git a/src/app/Http/Controllers/API/V4/OpenViduController.php b/src/app/Http/Controllers/API/V4/OpenViduController.php
--- a/src/app/Http/Controllers/API/V4/OpenViduController.php
+++ b/src/app/Http/Controllers/API/V4/OpenViduController.php
@@ -280,16 +280,16 @@
// Initialize connection tokens
if ($init) {
// Choose the connection role
- if ($isOwner) {
- $role = Room::ROLE_MODERATOR;
- } elseif (request()->input('role') === Room::ROLE_PUBLISHER) {
- $role = Room::ROLE_PUBLISHER;
+ if (request()->input('role') === Room::ROLE_PUBLISHER) {
+ $req_role = Room::ROLE_PUBLISHER;
} else {
- $role = Room::ROLE_SUBSCRIBER;
+ $req_role = Room::ROLE_SUBSCRIBER;
}
+ $role = $isOwner ? Room::ROLE_MODERATOR : $req_role;
+
// Create session token for the current user/connection
- $response = $room->getSessionToken($role);
+ $response = $room->getSessionToken($role, ['role' => $req_role]);
if (empty($response)) {
return $this->errorResponse(500, \trans('meet.session-join-error'));
@@ -306,6 +306,7 @@
$response['role'] = $role;
$response['owner'] = $isOwner;
$response['config'] = $config;
+ $response['role'] = $req_role;
} else {
$response_code = 422;
$response['code'] = 322;
diff --git a/src/app/OpenVidu/Room.php b/src/app/OpenVidu/Room.php
--- a/src/app/OpenVidu/Room.php
+++ b/src/app/OpenVidu/Room.php
@@ -169,10 +169,14 @@
/**
* Create a OpenVidu session (connection) token
*
+ * @param string $role User role
+ * @param array $data User data to attach to the connection.
+ * It will be available client-side for everybody.
+ *
* @return array|null Token data on success, NULL otherwise
* @throws \Exception if session does not exist
*/
- public function getSessionToken($role = self::ROLE_PUBLISHER): ?array
+ public function getSessionToken($role = self::ROLE_PUBLISHER, $data = []): ?array
{
if (!$this->session_id) {
throw new \Exception("The room session does not exist");
@@ -181,7 +185,12 @@
$url = 'sessions/' . $this->session_id . '/connection';
$post = [
'json' => [
- 'role' => $role
+ 'role' => $role,
+ // FIXME: Looks like passing the role in 'data' is the only way
+ // to make it visible for everyone in a room. So, for example we can
+ // handle/style subscribers and publishers differently on the client-side.
+ // Is this a security issue?
+ 'data' => !empty($data) ? json_encode($data) : null
]
];
diff --git a/src/resources/js/meet/app.js b/src/resources/js/meet/app.js
--- a/src/resources/js/meet/app.js
+++ b/src/resources/js/meet/app.js
@@ -37,6 +37,7 @@
let chatCount = 0
let volumeElement
let setupProps
+ let subscribersContainer
OV = new OpenVidu()
screenOV = new OpenVidu()
@@ -82,6 +83,11 @@
// avatar: undefined // avatar image
}
+ // Create a container for subscribers
+ if (!subscribersContainer) {
+ subscribersContainer = $('
').appendTo(container).get(0)
+ }
+
sessionData = data
// Init a session
@@ -96,25 +102,20 @@
}
// This is the first event executed when a user joins in.
- // We'll create the video wrapper here, which will be re-used
+ // We'll create the video wrapper here, which can be re-used
// in 'streamCreated' event handler.
- // Note: For a user with no cam/mic enabled streamCreated event
+ // Note: For a user with a subscriber role 'streamCreated' event
// is not being dispatched at all
- // TODO: We may consider placing users with no video enabled
- // in a separate place, so they do not fill the precious
- // screen estate
-
+ let metadata = connectionData(event.connection)
let connectionId = event.connection.connectionId
- let metadata = JSON.parse(event.connection.data)
metadata.connId = connectionId
- let wrapper = videoWrapperCreate(container, metadata)
- connections[connectionId] = {
- element: wrapper
- }
+ let element = participantCreate(metadata)
- updateLayout()
+ connections[connectionId] = { element }
+
+ resize()
// Send the current user status to the connecting user
// otherwise e.g. nickname might be not up to date
@@ -124,18 +125,20 @@
session.on('connectionDestroyed', event => {
let conn = connections[event.connection.connectionId]
if (conn) {
+ if ($(conn.element).is('.meet-video')) {
+ numOfVideos--
+ }
$(conn.element).remove()
- numOfVideos--
- updateLayout()
delete connections[event.connection.connectionId]
}
+ resize()
})
// On every new Stream received...
session.on('streamCreated', event => {
let connection = event.stream.connection
let connectionId = connection.connectionId
- let metadata = JSON.parse(connection.data)
+ let metadata = connectionData(connection)
let wrapper = connections[connectionId].element
let props = {
// Prepend the video element so it is always before the watermark element
@@ -150,14 +153,14 @@
tabindex: -1
})
- updateLayout()
+ resize()
})
/*
subscriber.on('videoElementDestroyed', event => {
})
*/
// Update the wrapper controls/status
- videoWrapperUpdate(wrapper, event.stream)
+ publisherUpdate(wrapper, event.stream)
})
/*
session.on('streamDestroyed', event => {
@@ -169,7 +172,7 @@
data.onDestroy(event)
}
- updateLayout()
+ resize()
})
// Handle signals from all participants
@@ -178,8 +181,8 @@
// Connect with the token
session.connect(data.token, data.params)
.then(() => {
- let params = { publisher: true, audioActive, videoActive }
- let wrapper = videoWrapperCreate(container, Object.assign({}, data.params, params))
+ let wrapper
+ let params = Object.assign({}, data.params, { self: true, audioActive, videoActive })
publisher.on('videoElementCreated', event => {
$(event.element).prop({
@@ -187,17 +190,20 @@
disablePictureInPicture: true, // this does not work in Firefox
tabindex: -1
})
- updateLayout()
+ resize()
})
- publisher.createVideoElement(wrapper, 'PREPEND')
-
- sessionData.wrapper = wrapper
-
- // Publish the stream
- if (sessionData.role != 'SUBSCRIBER') {
+ if (sessionData.role == 'PUBLISHER') {
+ wrapper = participantCreate(params)
+ publisher.createVideoElement(wrapper, 'PREPEND')
session.publish(publisher)
+ } else {
+ wrapper = subscriberCreate(params)
}
+
+ resize()
+
+ sessionData.wrapper = wrapper
})
.catch(error => {
console.error('There was an error connecting to the session: ', error.message);
@@ -427,7 +433,7 @@
if (conn = connections[connId]) {
data = JSON.parse(signal.data)
- videoWrapperUpdate(conn.element, data)
+ publisherUpdate(conn.element, data)
nicknameUpdate(data.nickname, connId)
}
break
@@ -571,7 +577,7 @@
try {
publisher.publishAudio(!audioActive)
audioActive = !audioActive
- videoWrapperUpdate(sessionData.wrapper, { audioActive })
+ publisherUpdate(sessionData.wrapper, { audioActive })
signalUserUpdate()
} catch (e) {
console.error(e)
@@ -593,7 +599,7 @@
try {
publisher.publishVideo(!videoActive)
videoActive = !videoActive
- videoWrapperUpdate(sessionData.wrapper, { videoActive })
+ publisherUpdate(sessionData.wrapper, { videoActive })
signalUserUpdate()
} catch (e) {
console.error(e)
@@ -648,25 +654,33 @@
}
}
+ /**
+ * Create a participant element in the matrix. Depending on the role
+ * parameter it will be a video element wrapper inside the matrix
+ * or a simple tag-like element on the subscribers list.
+ *
+ * @param params Connection metadata/params
+ *
+ * @return The element
+ */
+ function participantCreate(params) {
+ if (params.role == 'SUBSCRIBER') {
+ return subscriberCreate(params)
+ }
+
+ return publisherCreate(params)
+ }
+
/**
* Create a