diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/externalvideo/ExternalVideoApp2x.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/externalvideo/ExternalVideoApp2x.scala
new file mode 100644
index 0000000000000000000000000000000000000000..0630711d6408f2a45fe3ec0c446c82978ca08142
--- /dev/null
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/externalvideo/ExternalVideoApp2x.scala
@@ -0,0 +1,13 @@
+package org.bigbluebutton.core.apps.externalvideo
+
+import akka.actor.ActorContext
+import akka.event.Logging
+
+class ExternalVideoApp2x(implicit val context: ActorContext)
+  extends StartExternalVideoPubMsgHdlr
+  with UpdateExternalVideoPubMsgHdlr
+  with StopExternalVideoPubMsgHdlr {
+
+  val log = Logging(context.system, getClass)
+
+}
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/externalvideo/StartExternalVideoPubMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/externalvideo/StartExternalVideoPubMsgHdlr.scala
new file mode 100644
index 0000000000000000000000000000000000000000..39a81d9aab31dd6bac923e3dfeaafe53659b66cc
--- /dev/null
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/externalvideo/StartExternalVideoPubMsgHdlr.scala
@@ -0,0 +1,27 @@
+package org.bigbluebutton.core.apps.externalvideo
+
+import org.bigbluebutton.common2.msgs._
+import org.bigbluebutton.core.bus.MessageBus
+import org.bigbluebutton.core.running.{ LiveMeeting }
+
+trait StartExternalVideoPubMsgHdlr {
+  this: ExternalVideoApp2x =>
+
+  def handle(msg: StartExternalVideoPubMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
+    log.info("Received StartExternalVideoPubMsgr meetingId={} url={}", liveMeeting.props.meetingProp.intId, msg.body.externalVideoUrl)
+
+    def broadcastEvent(msg: StartExternalVideoPubMsg) {
+
+      val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, liveMeeting.props.meetingProp.intId, "nodeJSapp")
+      val envelope = BbbCoreEnvelope(StartExternalVideoEvtMsg.NAME, routing)
+      val header = BbbClientMsgHeader(StartExternalVideoEvtMsg.NAME, liveMeeting.props.meetingProp.intId, msg.header.userId)
+
+      val body = StartExternalVideoEvtMsgBody(msg.body.externalVideoUrl)
+      val event = StartExternalVideoEvtMsg(header, body)
+      val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
+      bus.outGW.send(msgEvent)
+    }
+
+    broadcastEvent(msg)
+  }
+}
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/externalvideo/StopExternalVideoPubMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/externalvideo/StopExternalVideoPubMsgHdlr.scala
new file mode 100644
index 0000000000000000000000000000000000000000..fb8ef02953d5936fea88f483f7eaead8f2510e9e
--- /dev/null
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/externalvideo/StopExternalVideoPubMsgHdlr.scala
@@ -0,0 +1,27 @@
+package org.bigbluebutton.core.apps.externalvideo
+
+import org.bigbluebutton.common2.msgs._
+import org.bigbluebutton.core.bus.MessageBus
+import org.bigbluebutton.core.running.{ LiveMeeting }
+
+trait StopExternalVideoPubMsgHdlr {
+  this: ExternalVideoApp2x =>
+
+  def handle(msg: StopExternalVideoPubMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
+    log.info("Received StopExternalVideoPubMsgr meetingId={}", liveMeeting.props.meetingProp.intId)
+
+    def broadcastEvent() {
+
+      val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, liveMeeting.props.meetingProp.intId, "nodeJSapp")
+      val envelope = BbbCoreEnvelope(StopExternalVideoEvtMsg.NAME, routing)
+      val header = BbbClientMsgHeader(StopExternalVideoEvtMsg.NAME, liveMeeting.props.meetingProp.intId, msg.header.userId)
+
+      val body = StopExternalVideoEvtMsgBody()
+      val event = StopExternalVideoEvtMsg(header, body)
+      val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
+      bus.outGW.send(msgEvent)
+    }
+
+    broadcastEvent()
+  }
+}
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/externalvideo/UpdateExternalVideoPubMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/externalvideo/UpdateExternalVideoPubMsgHdlr.scala
new file mode 100644
index 0000000000000000000000000000000000000000..e343f5fa3a26e2ac8c99e91e45de82f8487ab68d
--- /dev/null
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/externalvideo/UpdateExternalVideoPubMsgHdlr.scala
@@ -0,0 +1,23 @@
+package org.bigbluebutton.core.apps.externalvideo
+
+import org.bigbluebutton.common2.msgs._
+import org.bigbluebutton.core.bus.MessageBus
+import org.bigbluebutton.core.running.{ LiveMeeting }
+
+trait UpdateExternalVideoPubMsgHdlr {
+
+  def handle(msg: UpdateExternalVideoPubMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
+    def broadcastEvent(msg: UpdateExternalVideoPubMsg) {
+      val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, liveMeeting.props.meetingProp.intId, "nodeJSapp")
+      val envelope = BbbCoreEnvelope(UpdateExternalVideoEvtMsg.NAME, routing)
+      val header = BbbClientMsgHeader(UpdateExternalVideoEvtMsg.NAME, liveMeeting.props.meetingProp.intId, msg.header.userId)
+
+      val body = UpdateExternalVideoEvtMsgBody(msg.body.status, msg.body.rate, msg.body.time, msg.body.state)
+      val event = UpdateExternalVideoEvtMsg(header, body)
+      val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
+      bus.outGW.send(msgEvent)
+    }
+
+    broadcastEvent(msg)
+  }
+}
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/pubsub/senders/ReceivedJsonMsgHandlerActor.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/pubsub/senders/ReceivedJsonMsgHandlerActor.scala
index 1842b9a24ed18138c39b045629f78ced7484775f..57dadfa39db2bee69c9cf7db87142069323db572 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/pubsub/senders/ReceivedJsonMsgHandlerActor.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/pubsub/senders/ReceivedJsonMsgHandlerActor.scala
@@ -331,6 +331,14 @@ class ReceivedJsonMsgHandlerActor(
       case CreateGroupChatReqMsg.NAME =>
         routeGenericMsg[CreateGroupChatReqMsg](envelope, jsonNode)
 
+      // ExternalVideo
+      case StartExternalVideoPubMsg.NAME =>
+        routeGenericMsg[StartExternalVideoPubMsg](envelope, jsonNode)
+      case UpdateExternalVideoPubMsg.NAME =>
+        routeGenericMsg[UpdateExternalVideoPubMsg](envelope, jsonNode)
+      case StopExternalVideoPubMsg.NAME =>
+        routeGenericMsg[StopExternalVideoPubMsg](envelope, jsonNode)
+
       case _ =>
         log.error("Cannot route envelope name " + envelope.name)
       // do nothing
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala
index c0a1735a92841210e0d1f19457236444c06383ef..28c995302079898028d952b39bf696a664056dda 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala
@@ -16,6 +16,7 @@ import org.bigbluebutton.core.api._
 import org.bigbluebutton.core.apps._
 import org.bigbluebutton.core.apps.caption.CaptionApp2x
 import org.bigbluebutton.core.apps.chat.ChatApp2x
+import org.bigbluebutton.core.apps.externalvideo.ExternalVideoApp2x
 import org.bigbluebutton.core.apps.screenshare.ScreenshareApp2x
 import org.bigbluebutton.core.apps.presentation.PresentationApp2x
 import org.bigbluebutton.core.apps.users.UsersApp2x
@@ -115,6 +116,7 @@ class MeetingActor(
   val captionApp2x = new CaptionApp2x
   val sharedNotesApp2x = new SharedNotesApp2x
   val chatApp2x = new ChatApp2x
+  val externalVideoApp2x = new ExternalVideoApp2x
   val usersApp = new UsersApp(liveMeeting, outGW, eventBus)
   val groupChatApp = new GroupChatHdlrs
   val presentationPodsApp = new PresentationPodHdlrs
@@ -491,6 +493,11 @@ class MeetingActor(
         state = groupChatApp.handle(m, state, liveMeeting, msgBus)
         updateUserLastActivity(m.body.msg.sender.id)
 
+      // ExternalVideo
+      case m: StartExternalVideoPubMsg    => externalVideoApp2x.handle(m, liveMeeting, msgBus)
+      case m: UpdateExternalVideoPubMsg   => externalVideoApp2x.handle(m, liveMeeting, msgBus)
+      case m: StopExternalVideoPubMsg     => externalVideoApp2x.handle(m, liveMeeting, msgBus)
+
       case m: ValidateConnAuthTokenSysMsg => handleValidateConnAuthTokenSysMsg(m)
 
       case m: UserActivitySignCmdMsg      => handleUserActivitySignCmdMsg(m)
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/FromAkkaAppsMsgSenderActor.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/FromAkkaAppsMsgSenderActor.scala
index 9c4888d74a5ab7bb6cf1964e998c3838fda8349e..29d52d6c14ac27de7b909f0ba934512320434d99 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/FromAkkaAppsMsgSenderActor.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/FromAkkaAppsMsgSenderActor.scala
@@ -59,13 +59,13 @@ class FromAkkaAppsMsgSenderActor(msgSender: MessageSender)
 
       // Whiteboard
       case SendWhiteboardAnnotationEvtMsg.NAME =>
-        msgSender.send(fromAkkaAppsWbRedisChannel, json)
+        msgSender.send("from-akka-apps-frontend-redis-channel", json)
       case SendCursorPositionEvtMsg.NAME =>
-        msgSender.send(fromAkkaAppsWbRedisChannel, json)
+        msgSender.send("from-akka-apps-frontend-redis-channel", json)
       case ClearWhiteboardEvtMsg.NAME =>
-        msgSender.send(fromAkkaAppsWbRedisChannel, json)
+        msgSender.send("from-akka-apps-frontend-redis-channel", json)
       case UndoWhiteboardEvtMsg.NAME =>
-        msgSender.send(fromAkkaAppsWbRedisChannel, json)
+        msgSender.send("from-akka-apps-frontend-redis-channel", json)
 
       // Chat
       case SendPublicMessageEvtMsg.NAME =>
@@ -107,6 +107,25 @@ class FromAkkaAppsMsgSenderActor(msgSender: MessageSender)
       case UserRespondedToPollRecordMsg.NAME =>
       //==================================================================
 
+      case ValidateAuthTokenRespMsg.NAME =>
+        msgSender.send("from-akka-apps-frontend-redis-channel", json)
+
+      // Message duplicated for frontend and backend processes
+      case MeetingCreatedEvtMsg.NAME =>
+        msgSender.send(fromAkkaAppsRedisChannel, json)
+        msgSender.send("from-akka-apps-frontend-redis-channel", json)
+
+      case MeetingEndingEvtMsg.NAME =>
+        msgSender.send(fromAkkaAppsRedisChannel, json)
+        msgSender.send("from-akka-apps-frontend-redis-channel", json)
+
+      case MeetingDestroyedEvtMsg.NAME =>
+        msgSender.send(fromAkkaAppsRedisChannel, json)
+        msgSender.send("from-akka-apps-frontend-redis-channel", json)
+
+      case UpdateExternalVideoEvtMsg.NAME =>
+        msgSender.send("from-akka-apps-frontend-redis-channel", json)
+
       case _ =>
         msgSender.send(fromAkkaAppsRedisChannel, json)
     }
diff --git a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/ExternalVideoMsgs.scala b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/ExternalVideoMsgs.scala
new file mode 100644
index 0000000000000000000000000000000000000000..7a66abd7e74828afd2e11092893e7c11129630d0
--- /dev/null
+++ b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/ExternalVideoMsgs.scala
@@ -0,0 +1,27 @@
+package org.bigbluebutton.common2.msgs
+
+// In messages
+object StartExternalVideoPubMsg { val NAME = "StartExternalVideoPubMsg" }
+case class StartExternalVideoPubMsg(header: BbbClientMsgHeader, body: StartExternalVideoPubMsgBody) extends StandardMsg
+case class StartExternalVideoPubMsgBody(externalVideoUrl: String)
+
+object UpdateExternalVideoPubMsg { val NAME = "UpdateExternalVideoPubMsg" }
+case class UpdateExternalVideoPubMsg(header: BbbClientMsgHeader, body: UpdateExternalVideoPubMsgBody) extends StandardMsg
+case class UpdateExternalVideoPubMsgBody(status: String, rate: Double, time: Double, state: Boolean)
+
+object StopExternalVideoPubMsg { val NAME = "StopExternalVideoPubMsg" }
+case class StopExternalVideoPubMsg(header: BbbClientMsgHeader, body: StopExternalVideoPubMsgBody) extends StandardMsg
+case class StopExternalVideoPubMsgBody()
+
+// Out messages
+object StartExternalVideoEvtMsg { val NAME = "StartExternalVideoEvtMsg" }
+case class StartExternalVideoEvtMsg(header: BbbClientMsgHeader, body: StartExternalVideoEvtMsgBody) extends BbbCoreMsg
+case class StartExternalVideoEvtMsgBody(externalVideoUrl: String)
+
+object UpdateExternalVideoEvtMsg { val NAME = "UpdateExternalVideoEvtMsg" }
+case class UpdateExternalVideoEvtMsg(header: BbbClientMsgHeader, body: UpdateExternalVideoEvtMsgBody) extends BbbCoreMsg
+case class UpdateExternalVideoEvtMsgBody(status: String, rate: Double, time: Double, state: Boolean)
+
+object StopExternalVideoEvtMsg { val NAME = "StopExternalVideoEvtMsg" }
+case class StopExternalVideoEvtMsg(header: BbbClientMsgHeader, body: StopExternalVideoEvtMsgBody) extends BbbCoreMsg
+case class StopExternalVideoEvtMsgBody()
diff --git a/bigbluebutton-html5/imports/api/external-videos/server/eventHandlers.js b/bigbluebutton-html5/imports/api/external-videos/server/eventHandlers.js
new file mode 100644
index 0000000000000000000000000000000000000000..2cd4077869200dfe86e8ce0edcc9986aa76b7418
--- /dev/null
+++ b/bigbluebutton-html5/imports/api/external-videos/server/eventHandlers.js
@@ -0,0 +1,8 @@
+import RedisPubSub from '/imports/startup/server/redis';
+import handleStartExternalVideo from './handlers/startExternalVideo';
+import handleStopExternalVideo from './handlers/stopExternalVideo';
+import handleUpdateExternalVideo from './handlers/updateExternalVideo';
+
+RedisPubSub.on('StartExternalVideoEvtMsg', handleStartExternalVideo);
+RedisPubSub.on('StopExternalVideoEvtMsg', handleStopExternalVideo);
+RedisPubSub.on('UpdateExternalVideoEvtMsg', handleUpdateExternalVideo);
diff --git a/bigbluebutton-html5/imports/api/external-videos/server/handlers/startExternalVideo.js b/bigbluebutton-html5/imports/api/external-videos/server/handlers/startExternalVideo.js
new file mode 100644
index 0000000000000000000000000000000000000000..c58cd99bf4c773439b3f506d1e7efe869faaf374
--- /dev/null
+++ b/bigbluebutton-html5/imports/api/external-videos/server/handlers/startExternalVideo.js
@@ -0,0 +1,19 @@
+import { check } from 'meteor/check';
+import Logger from '/imports/startup/server/logger';
+import Users from '/imports/api/users';
+import Meetings from '/imports/api/meetings';
+
+export default function handleStartExternalVideo({ header, body }, meetingId) {
+  const { userId } = header;
+  check(body, Object);
+  check(meetingId, String);
+  check(userId, String);
+
+  const externalVideoUrl = body.externalVideoUrl;
+  const user = Users.findOne({ meetingId: meetingId, userId: userId })
+
+  if (user && user.presenter) {
+      Logger.info(`User id=${userId} sharing an external video: ${externalVideoUrl} for meeting ${meetingId}`);
+      Meetings.update({ meetingId }, { $set: { externalVideoUrl } });
+  }
+}
diff --git a/bigbluebutton-html5/imports/api/external-videos/server/handlers/stopExternalVideo.js b/bigbluebutton-html5/imports/api/external-videos/server/handlers/stopExternalVideo.js
new file mode 100644
index 0000000000000000000000000000000000000000..aea39acaf32dfd2007fe3bb1d36af875cfe2bbe5
--- /dev/null
+++ b/bigbluebutton-html5/imports/api/external-videos/server/handlers/stopExternalVideo.js
@@ -0,0 +1,18 @@
+import { check } from 'meteor/check';
+import Logger from '/imports/startup/server/logger';
+import Users from '/imports/api/users';
+import Meetings from '/imports/api/meetings';
+
+export default function handleStopExternalVideo({ header, body }, meetingId) {
+  const { userId } = header;
+  check(body, Object);
+  check(meetingId, String);
+  check(userId, String);
+
+  const user = Users.findOne({ meetingId: meetingId, userId: userId })
+
+  if (user && user.presenter) {
+      Logger.info(`User id=${userId} stop sharing an external video for meeting ${meetingId}`);
+      Meetings.update({ meetingId }, { $set: { externalVideoUrl: null } });
+  }
+}
diff --git a/bigbluebutton-html5/imports/api/external-videos/server/handlers/updateExternalVideo.js b/bigbluebutton-html5/imports/api/external-videos/server/handlers/updateExternalVideo.js
new file mode 100644
index 0000000000000000000000000000000000000000..0fe792d8a97e98749424dcc7003bad4d59048f09
--- /dev/null
+++ b/bigbluebutton-html5/imports/api/external-videos/server/handlers/updateExternalVideo.js
@@ -0,0 +1,19 @@
+import { check } from 'meteor/check';
+import Logger from '/imports/startup/server/logger';
+import Users from '/imports/api/users';
+import ExternalVideoStreamer from '/imports/api/external-videos/server/streamer';
+
+export default function handleUpdateExternalVideo({ header, body }, meetingId) {
+  const { userId } = header;
+  check(body, Object);
+  check(meetingId, String);
+  check(userId, String);
+
+  const user = Users.findOne({ meetingId: meetingId, userId: userId })
+
+  if (user && user.presenter) {
+      Logger.info(`UpdateExternalVideoEvtMsg received for user ${userId} and meeting ${meetingId} event:${body.status}`);
+      ExternalVideoStreamer(meetingId).emit(body.status, {...body, meetingId: meetingId, userId: userId } );
+  }
+
+}
diff --git a/bigbluebutton-html5/imports/api/external-videos/server/index.js b/bigbluebutton-html5/imports/api/external-videos/server/index.js
index 2e0f48d559b9bc87827266a1d1225cc2af4c156f..b0d5d3295b0d8f62cda43093cb867e79f59ca357 100644
--- a/bigbluebutton-html5/imports/api/external-videos/server/index.js
+++ b/bigbluebutton-html5/imports/api/external-videos/server/index.js
@@ -1,2 +1,2 @@
 import './methods';
-
+import './eventHandlers';
diff --git a/bigbluebutton-html5/imports/api/external-videos/server/methods.js b/bigbluebutton-html5/imports/api/external-videos/server/methods.js
index 7df620cb7a18089285bfccefa51d2aaffbde4e50..10c056a2a9fe61561eb1cf896afcfacaf6ee6b71 100644
--- a/bigbluebutton-html5/imports/api/external-videos/server/methods.js
+++ b/bigbluebutton-html5/imports/api/external-videos/server/methods.js
@@ -1,11 +1,9 @@
 import { Meteor } from 'meteor/meteor';
 import startWatchingExternalVideo from './methods/startWatchingExternalVideo';
 import stopWatchingExternalVideo from './methods/stopWatchingExternalVideo';
-import initializeExternalVideo from './methods/initializeExternalVideo';
 import emitExternalVideoEvent from './methods/emitExternalVideoEvent';
 
 Meteor.methods({
-  initializeExternalVideo,
   startWatchingExternalVideo,
   stopWatchingExternalVideo,
   emitExternalVideoEvent,
diff --git a/bigbluebutton-html5/imports/api/external-videos/server/methods/emitExternalVideoEvent.js b/bigbluebutton-html5/imports/api/external-videos/server/methods/emitExternalVideoEvent.js
index 8c5d1535b8a926cf274e8d783769e4d069df5a58..1771741e116fe232fce6dc8ebfd84250dbf2e7fb 100644
--- a/bigbluebutton-html5/imports/api/external-videos/server/methods/emitExternalVideoEvent.js
+++ b/bigbluebutton-html5/imports/api/external-videos/server/methods/emitExternalVideoEvent.js
@@ -1,20 +1,36 @@
-import Users from '/imports/api/users';
+import { check } from 'meteor/check';
 import Logger from '/imports/startup/server/logger';
+import Users from '/imports/api/users';
+import RedisPubSub from '/imports/startup/server/redis';
 import { extractCredentials } from '/imports/api/common/server/helpers';
 
-export default function emitExternalVideoEvent(messageName, ...rest) {
-  const { meetingId, requesterUserId: userId } = extractCredentials(this.userId);
+export default function emitExternalVideoEvent(options) {
+  const REDIS_CONFIG = Meteor.settings.private.redis;
+  const CHANNEL = REDIS_CONFIG.channels.toAkkaApps;
+  const EVENT_NAME = 'UpdateExternalVideoPubMsg';
+
+  const { meetingId, requesterUserId } = extractCredentials(this.userId);
+
+  const { status, playerStatus } = options;
 
-  const user = Users.findOne({ userId, meetingId });
+  const user = Users.findOne({ meetingId: meetingId, userId: requesterUserId })
 
   if (user && user.presenter) {
-    const streamerName = `external-videos-${meetingId}`;
-    const streamer = Meteor.StreamerCentral.instances[streamerName];
-
-    if (streamer) {
-      streamer.emit(messageName, ...rest);
-    } else {
-      Logger.error(`External Video Streamer not found for meetingId: ${meetingId} userId: ${userId}`);
-    }
-  }
+
+    check(status, String);
+    check(playerStatus, {
+      rate: Match.Maybe(Number),
+      time: Match.Maybe(Number),
+      state: Match.Maybe(Boolean),
+    });
+
+    let rate = playerStatus.rate || 0;
+    let time = playerStatus.time || 0;
+    let state = playerStatus.state || 0;
+    const payload = { status, rate, time, state };
+
+    Logger.debug(`User id=${requesterUserId} sending ${EVENT_NAME} event:${state} for meeting ${meetingId}`);
+    return RedisPubSub.publishUserMessage(CHANNEL, EVENT_NAME, meetingId, requesterUserId, payload);
+
+   }
 }
diff --git a/bigbluebutton-html5/imports/api/external-videos/server/methods/startWatchingExternalVideo.js b/bigbluebutton-html5/imports/api/external-videos/server/methods/startWatchingExternalVideo.js
index e157b7a0b3e0ede194b9d16fa76152a59633bc1b..36e8962d6a0473aae3c6d538230d8ba746286112 100644
--- a/bigbluebutton-html5/imports/api/external-videos/server/methods/startWatchingExternalVideo.js
+++ b/bigbluebutton-html5/imports/api/external-videos/server/methods/startWatchingExternalVideo.js
@@ -1,25 +1,24 @@
-import { Meteor } from 'meteor/meteor';
 import { check } from 'meteor/check';
 import Logger from '/imports/startup/server/logger';
-import Meetings from '/imports/api/meetings';
+import Users from '/imports/api/users';
 import RedisPubSub from '/imports/startup/server/redis';
 import { extractCredentials } from '/imports/api/common/server/helpers';
 
 export default function startWatchingExternalVideo(options) {
   const REDIS_CONFIG = Meteor.settings.private.redis;
   const CHANNEL = REDIS_CONFIG.channels.toAkkaApps;
-  const EVENT_NAME = 'StartExternalVideoMsg';
+  const EVENT_NAME = 'StartExternalVideoPubMsg';
 
   const { meetingId, requesterUserId } = extractCredentials(this.userId);
   const { externalVideoUrl } = options;
 
-  check(externalVideoUrl, String);
+  const user = Users.findOne({ meetingId: meetingId, userId: requesterUserId })
 
-  Meetings.update({ meetingId }, { $set: { externalVideoUrl } });
+  if (user && user.presenter) {
+    check(externalVideoUrl, String);
+    const payload = { externalVideoUrl };
+    Logger.debug(`User id=${requesterUserId} sending ${EVENT_NAME} url:${externalVideoUrl} for meeting ${meetingId}`);
+    return RedisPubSub.publishUserMessage(CHANNEL, EVENT_NAME, meetingId, requesterUserId, payload);
+  }
 
-  const payload = { externalVideoUrl };
-
-  Logger.info(`User id=${requesterUserId} sharing an external video: ${externalVideoUrl} for meeting ${meetingId}`);
-
-  return RedisPubSub.publishUserMessage(CHANNEL, EVENT_NAME, meetingId, requesterUserId, payload);
 }
diff --git a/bigbluebutton-html5/imports/api/external-videos/server/methods/stopWatchingExternalVideo.js b/bigbluebutton-html5/imports/api/external-videos/server/methods/stopWatchingExternalVideo.js
index 60384b82f772230d68d5c5399008f7de9e780610..e57b9d7bf3dad55061371631f0b915a950fc3a82 100644
--- a/bigbluebutton-html5/imports/api/external-videos/server/methods/stopWatchingExternalVideo.js
+++ b/bigbluebutton-html5/imports/api/external-videos/server/methods/stopWatchingExternalVideo.js
@@ -1,27 +1,22 @@
-import { Meteor } from 'meteor/meteor';
+import { check } from 'meteor/check';
 import Logger from '/imports/startup/server/logger';
-import Meetings from '/imports/api/meetings';
+import Users from '/imports/api/users';
 import RedisPubSub from '/imports/startup/server/redis';
 import { extractCredentials } from '/imports/api/common/server/helpers';
 
 export default function stopWatchingExternalVideo(options) {
   const REDIS_CONFIG = Meteor.settings.private.redis;
   const CHANNEL = REDIS_CONFIG.channels.toAkkaApps;
-  const EVENT_NAME = 'StopExternalVideoMsg';
+  const EVENT_NAME = 'StopExternalVideoPubMsg';
 
-  if (this.userId) {
-    options = extractCredentials(this.userId);
-  }
-
-  const { meetingId, requesterUserId } = options;
+  const { meetingId, requesterUserId } = extractCredentials(this.userId);
 
-  const meeting = Meetings.findOne({ meetingId });
-  if (!meeting || meeting.externalVideoUrl === null) return;
+  const user = Users.findOne({ meetingId: meetingId, userId: requesterUserId })
 
-  Meetings.update({ meetingId }, { $set: { externalVideoUrl: null } });
-  const payload = {};
-
-  Logger.info(`User id=${requesterUserId} stopped sharing an external video for meeting=${meetingId}`);
+  if (user && user.presenter) {
+    const payload = { };
+    Logger.debug(`User id=${requesterUserId} sending ${EVENT_NAME} for meeting ${meetingId}`);
+    return RedisPubSub.publishUserMessage(CHANNEL, EVENT_NAME, meetingId, requesterUserId, payload);
+  }
 
-  RedisPubSub.publishUserMessage(CHANNEL, EVENT_NAME, meetingId, requesterUserId, payload);
 }
diff --git a/bigbluebutton-html5/imports/api/external-videos/server/streamer.js b/bigbluebutton-html5/imports/api/external-videos/server/streamer.js
new file mode 100644
index 0000000000000000000000000000000000000000..fbb34bf112b6f4241b9b4f130d86ac05cb1273fe
--- /dev/null
+++ b/bigbluebutton-html5/imports/api/external-videos/server/streamer.js
@@ -0,0 +1,45 @@
+import { Meteor } from 'meteor/meteor';
+import Logger from '/imports/startup/server/logger';
+
+const allowRecentMessages = (eventName, message) => {
+
+  const {
+    userId,
+    meetingId,
+    time,
+    rate,
+    state,
+  } = message;
+
+  Logger.debug(`ExternalVideo Streamer auth allowed userId: ${userId}, meetingId: ${meetingId}, event: ${eventName}, time: ${time} rate: ${rate}, state: ${state}`);
+  return true;
+};
+
+export function removeExternalVideoStreamer(meetingId) {
+  const streamName = `external-videos-${meetingId}`;
+
+  if (Meteor.StreamerCentral.instances[streamName]) {
+    Logger.info(`Destroying External Video streamer object for ${streamName}`);
+    delete Meteor.StreamerCentral.instances[streamName];
+  }
+}
+
+export function addExternalVideoStreamer(meetingId) {
+
+  const streamName = `external-videos-${meetingId}`;
+  if (!Meteor.StreamerCentral.instances[streamName]) {
+
+    const streamer = new Meteor.Streamer(streamName);
+    streamer.allowRead('all');
+    streamer.allowWrite('none');
+    streamer.allowEmit(allowRecentMessages);
+    Logger.info(`Created External Video streamer for ${streamName}`);
+  } else {
+    Logger.debug(`External Video streamer is already created for ${streamName}`);
+  }
+}
+
+export default function get(meetingId) {
+  const streamName = `external-videos-${meetingId}`;
+  return Meteor.StreamerCentral.instances[streamName];
+}
diff --git a/bigbluebutton-html5/imports/api/meetings/server/handlers/meetingDestruction.js b/bigbluebutton-html5/imports/api/meetings/server/handlers/meetingDestruction.js
index cacfd42b057a6c2bcace371737d19bf4c9e0f296..61c1863f55bcb9aa1b831efe13ba7fcc5f145ca6 100644
--- a/bigbluebutton-html5/imports/api/meetings/server/handlers/meetingDestruction.js
+++ b/bigbluebutton-html5/imports/api/meetings/server/handlers/meetingDestruction.js
@@ -1,18 +1,21 @@
 import RedisPubSub from '/imports/startup/server/redis';
 import { check } from 'meteor/check';
 
-import destroyExternalVideo from '/imports/api/external-videos/server/methods/destroyExternalVideo';
 import { removeAnnotationsStreamer } from '/imports/api/annotations/server/streamer';
 import { removeCursorStreamer } from '/imports/api/cursor/server/streamer';
+import { removeExternalVideoStreamer } from '/imports/api/external-videos/server/streamer';
 
 export default function handleMeetingDestruction({ body }) {
   check(body, Object);
   const { meetingId } = body;
   check(meetingId, String);
 
-  destroyExternalVideo(meetingId);
-  removeAnnotationsStreamer(meetingId);
-  removeCursorStreamer(meetingId);
+
+  if (!process.env.METEOR_ROLE || process.env.METEOR_ROLE === 'frontend') {
+    removeAnnotationsStreamer(meetingId);
+    removeCursorStreamer(meetingId);
+    removeExternalVideoStreamer(meetingId);
+  }
 
   return RedisPubSub.destroyMeetingQueue(meetingId);
 }
diff --git a/bigbluebutton-html5/imports/api/meetings/server/modifiers/addMeeting.js b/bigbluebutton-html5/imports/api/meetings/server/modifiers/addMeeting.js
index 7f15e961f29f8ed9e213b22aedb5296603d7dc15..698eabfca07f3f9238e6171bdf14d87e4220c70e 100755
--- a/bigbluebutton-html5/imports/api/meetings/server/modifiers/addMeeting.js
+++ b/bigbluebutton-html5/imports/api/meetings/server/modifiers/addMeeting.js
@@ -10,6 +10,7 @@ import createNote from '/imports/api/note/server/methods/createNote';
 import createCaptions from '/imports/api/captions/server/methods/createCaptions';
 import { addAnnotationsStreamer } from '/imports/api/annotations/server/streamer';
 import { addCursorStreamer } from '/imports/api/cursor/server/streamer';
+import { addExternalVideoStreamer } from '/imports/api/external-videos/server/streamer';
 import BannedUsers from '/imports/api/users/server/store/bannedUsers';
 
 export default function addMeeting(meeting) {
@@ -198,8 +199,12 @@ export default function addMeeting(meeting) {
     ...recordProp,
   }, cbRecord);
 
-  addAnnotationsStreamer(meetingId);
-  addCursorStreamer(meetingId);
+
+  if (!process.env.METEOR_ROLE || process.env.METEOR_ROLE === 'frontend') {
+    addAnnotationsStreamer(meetingId);
+    addCursorStreamer(meetingId);
+    addExternalVideoStreamer(meetingId);
+  }
 
   return Meetings.upsert(selector, modifier, cb);
 }
diff --git a/bigbluebutton-html5/imports/api/meetings/server/modifiers/meetingHasEnded.js b/bigbluebutton-html5/imports/api/meetings/server/modifiers/meetingHasEnded.js
index 3b9efcbb1698899b0e0347f2f196d546067e882a..f736e5d7aa130b7a8388c8db793c87eda8e2a93b 100755
--- a/bigbluebutton-html5/imports/api/meetings/server/modifiers/meetingHasEnded.js
+++ b/bigbluebutton-html5/imports/api/meetings/server/modifiers/meetingHasEnded.js
@@ -3,6 +3,8 @@ import Logger from '/imports/startup/server/logger';
 
 import { removeAnnotationsStreamer } from '/imports/api/annotations/server/streamer';
 import { removeCursorStreamer } from '/imports/api/cursor/server/streamer';
+import { removeExternalVideoStreamer } from '/imports/api/external-videos/server/streamer';
+import BannedUsers from '/imports/api/users/server/store/bannedUsers';
 
 import clearUsers from '/imports/api/users/server/modifiers/clearUsers';
 import clearUsersSettings from '/imports/api/users-settings/server/modifiers/clearUsersSettings';
@@ -23,9 +25,15 @@ import clearRecordMeeting from './clearRecordMeeting';
 import clearVoiceCallStates from '/imports/api/voice-call-states/server/modifiers/clearVoiceCallStates';
 import clearVideoStreams from '/imports/api/video-streams/server/modifiers/clearVideoStreams';
 
+
 export default function meetingHasEnded(meetingId) {
-  removeAnnotationsStreamer(meetingId);
-  removeCursorStreamer(meetingId);
+
+
+  if (!process.env.METEOR_ROLE || process.env.METEOR_ROLE === 'frontend') {
+    removeAnnotationsStreamer(meetingId);
+    removeCursorStreamer(meetingId);
+    removeExternalVideoStreamer(meetingId);
+  }
 
   return Meetings.remove({ meetingId }, () => {
     clearCaptions(meetingId);
@@ -46,6 +54,7 @@ export default function meetingHasEnded(meetingId) {
     clearRecordMeeting(meetingId);
     clearVoiceCallStates(meetingId);
     clearVideoStreams(meetingId);
+    BannedUsers.delete(meetingId);
 
     return Logger.info(`Cleared Meetings with id ${meetingId}`);
   });
diff --git a/bigbluebutton-html5/imports/api/users/server/handlers/validateAuthToken.js b/bigbluebutton-html5/imports/api/users/server/handlers/validateAuthToken.js
index 010874c9af1ad9b196390daf6ba9592a5a61a592..1d02b8942a112377461d61cf232757c302c70e9e 100644
--- a/bigbluebutton-html5/imports/api/users/server/handlers/validateAuthToken.js
+++ b/bigbluebutton-html5/imports/api/users/server/handlers/validateAuthToken.js
@@ -28,6 +28,9 @@ export default function handleValidateAuthToken({ body }, meetingId) {
   check(waitForApproval, Boolean);
 
   const pendingAuths = pendingAuthenticationsStore.take(meetingId, userId, authToken);
+  
+  Logger.info(`PendingAuths length [${pendingAuths.length}]`);
+  if (pendingAuths.length === 0) return;
 
   if (!valid) {
     pendingAuths.forEach(
diff --git a/bigbluebutton-html5/imports/api/users/server/store/bannedUsers.js b/bigbluebutton-html5/imports/api/users/server/store/bannedUsers.js
index 5355e3d1dcec3bf441259010f1d0ac484201cef3..dfe0745441cf25f7bc2f1b174e2610ea228684f8 100644
--- a/bigbluebutton-html5/imports/api/users/server/store/bannedUsers.js
+++ b/bigbluebutton-html5/imports/api/users/server/store/bannedUsers.js
@@ -1,34 +1,88 @@
+import { check } from 'meteor/check';
 import Logger from '/imports/startup/server/logger';
 
 class BannedUsers {
   constructor() {
     Logger.debug('BannedUsers :: Initializing');
-    this.store = {};
+    this.store = new Mongo.Collection('users-banned');
+
+    if (Meteor.isServer) {
+      // types of queries for the users:
+      // 1. meetingId
+      // 2. meetingId, userId
+      this.store._ensureIndex({ meetingId: 1, userId: 1 });
+    }
   }
 
   init(meetingId) {
     Logger.debug('BannedUsers :: init', meetingId);
 
-    if (!this.store[meetingId]) this.store[meetingId] = new Set();
+    //if (!this.store[meetingId]) this.store[meetingId] = new Set();
   }
 
   add(meetingId, externalId) {
+
+    check(meetingId, String);
+    check(externalId, String);
+
     Logger.debug('BannedUsers :: add', { meetingId, externalId });
-    if (!this.store[meetingId]) this.store[meetingId] = new Set();
 
-    this.store[meetingId].add(externalId);
+    const selector = {
+      meetingId,
+      externalId: externalId,
+    };
+
+    const modifier = Object.assign(
+      { meetingId },
+      { externalId: externalId },
+    );
+
+    const cb = (err, numChanged) => {
+      if (err != null) {
+        return Logger.error(`Adding { meetingId, externalId } to BannedUsers  collection`);
+      }
+
+      const { insertedId } = numChanged;
+      if (insertedId) {
+        return Logger.info(`Added { meetingId, externalId } to BannedUsers  collection`);
+      }
+
+      return Logger.info(`Upserted { meetingId, externalId } to BannedUsers  collection`);
+    };
+
+    return this.store.upsert(selector, modifier, cb);
+
   }
 
   delete(meetingId) {
-    Logger.debug('BannedUsers :: delete', meetingId);
-    delete this.store[meetingId];
+
+    check(meetingId, String);
+
+    const selector = {
+        meetingId: meetingId
+    };
+
+    const cb = (err) => {
+      if (err) {
+        return Logger.error(`Removing BannedUsers from collection: ${err}`);
+      }
+
+      return Logger.info(`Removed BannedUsers meetingId=${meetingId}`);
+    };
+
+    return this.store.remove(selector, cb);
+
   }
 
   has(meetingId, externalId) {
-    Logger.debug('BannedUsers :: has', { meetingId, externalId });
-    if (!this.store[meetingId]) this.store[meetingId] = new Set();
 
-    return this.store[meetingId].has(externalId);
+    check(meetingId, String);
+    check(externalId, String);
+    
+    Logger.info('BannedUsers :: has', { meetingId, externalId });
+
+    return this.store.findOne({ meetingId: meetingId, externalId: externalId })
+
   }
 }
 
diff --git a/bigbluebutton-html5/imports/startup/server/index.js b/bigbluebutton-html5/imports/startup/server/index.js
index 63c4d9008b4649b1c45f468ace07e4fff9650bc2..7ea0d33ee6560f9beae640a4e7673b09e6ab8bb3 100755
--- a/bigbluebutton-html5/imports/startup/server/index.js
+++ b/bigbluebutton-html5/imports/startup/server/index.js
@@ -74,29 +74,31 @@ Meteor.startup(() => {
 
   setMinBrowserVersions();
 
-  Meteor.setInterval(() => {
-    const currentTime = Date.now();
-    Logger.info('Checking for inactive users');
-    const users = Users.find({
-      connectionStatus: 'online',
-      clientType: 'HTML5',
-      lastPing: {
-        $lt: (currentTime - INTERVAL_TIME), // get user who has not pinged in the last 10 seconds
-      },
-      loginTime: {
-        $lt: (currentTime - INTERVAL_TIME),
-      },
-    }).fetch();
-    if (!users.length) return Logger.info('No inactive users');
-    Logger.info('Removing inactive users');
-    users.forEach((user) => {
-      Logger.info(`Detected inactive user, userId:${user.userId}, meetingId:${user.meetingId}`);
-      return userLeaving(user.meetingId, user.userId, user.connectionId);
-    });
-    return Logger.info('All inactive users have been removed');
-  }, INTERVAL_TIME);
+  if (!process.env.METEOR_ROLE || process.env.METEOR_ROLE === 'backend') {
+      Meteor.setInterval(() => {
+        const currentTime = Date.now();
+        Logger.info('Checking for inactive users');
+        const users = Users.find({
+          connectionStatus: 'online',
+          clientType: 'HTML5',
+          lastPing: {
+            $lt: (currentTime - INTERVAL_TIME), // get user who has not pinged in the last 10 seconds
+          },
+          loginTime: {
+            $lt: (currentTime - INTERVAL_TIME),
+          },
+        }).fetch();
+        if (!users.length) return Logger.info('No inactive users');
+        Logger.info('Removing inactive users');
+        users.forEach((user) => {
+          Logger.info(`Detected inactive user, userId:${user.userId}, meetingId:${user.meetingId}`);
+          return userLeaving(user.meetingId, user.userId, user.connectionId);
+        });
+        return Logger.info('All inactive users have been removed');
+      }, INTERVAL_TIME);
+  }
 
-  Logger.warn(`SERVER STARTED.\nENV=${env},\nnodejs version=${process.version}\nCDN=${CDN_URL}\n`, APP_CONFIG);
+  Logger.warn(`SERVER STARTED.\nENV=${env},\nnodejs version=${process.version}\nMETEOR_ROLE=${process.env.METEOR_ROLE}\nCDN=${CDN_URL}\n`, APP_CONFIG);
 });
 
 WebApp.connectHandlers.use('/check', (req, res) => {
diff --git a/bigbluebutton-html5/imports/startup/server/redis.js b/bigbluebutton-html5/imports/startup/server/redis.js
index 739164c47d70326e5719acbc3ea0e85e1d2e1fe1..812a428332d949e9ed6a3dbe8548f37bfb23ed8b 100755
--- a/bigbluebutton-html5/imports/startup/server/redis.js
+++ b/bigbluebutton-html5/imports/startup/server/redis.js
@@ -134,11 +134,27 @@ class RedisPubSub {
 
     const channelsToSubscribe = this.config.subscribeTo;
 
-    channelsToSubscribe.forEach((channel) => {
+    /*channelsToSubscribe.forEach((channel) => {
       this.sub.psubscribe(channel);
     });
 
-    this.debug(`Subscribed to '${channelsToSubscribe}'`);
+   */
+
+   switch (process.env.METEOR_ROLE) {
+         case 'frontend':
+            this.sub.psubscribe('from-akka-apps-frontend-redis-channel');
+            this.debug(`Subscribed to from-akka-apps-frontend-redis-channel`);
+            break;
+         default:
+           const channelsToSubscribe = this.config.subscribeTo;
+           channelsToSubscribe.forEach((channel) => {
+             this.sub.psubscribe(channel);
+             this.debug(`Subscribed to '${channel}'`)
+            });
+
+           break;
+       }
+
   }
 
   updateConfig(config) {
diff --git a/bigbluebutton-html5/imports/ui/components/external-video-player/service.js b/bigbluebutton-html5/imports/ui/components/external-video-player/service.js
index 66bd3ea7ccc3d2fe70c51db90ce3a4c454af614e..bf82da4fee6dadd8d57b4db8a616cab0090d62b1 100644
--- a/bigbluebutton-html5/imports/ui/components/external-video-player/service.js
+++ b/bigbluebutton-html5/imports/ui/components/external-video-player/service.js
@@ -28,14 +28,31 @@ const stopWatching = () => {
   makeCall('stopWatchingExternalVideo');
 };
 
+let lastMessage = null;
+
 const sendMessage = (event, data) => {
-  const meetingId = Auth.meetingID;
-  const userId = Auth.userID;
 
-  makeCall('emitExternalVideoEvent', event, { ...data, meetingId, userId });
+  // don't re-send repeated update messages
+   if (lastMessage && lastMessage.event === event
+       && event === 'playerUpdate' && lastMessage.time === data.time) {
+     return;
+   }
+
+   // don't register to redis a viewer joined message
+  if (event === 'viewerJoined') {
+    return;
+  }
+
+  lastMessage = { ...data, event };
+
+  //const meetingId = Auth.meetingID;
+  //const userId = Auth.userID;
+
+  makeCall('emitExternalVideoEvent', { status: event, playerStatus: data });
 };
 
 const onMessage = (message, func) => {
+
   const streamer = getStreamer(Auth.meetingID);
   streamer.on(message, func);
 };
diff --git a/bigbluebutton-html5/private/config/settings.yml b/bigbluebutton-html5/private/config/settings.yml
index 78672e2c02c6cd73c24df763a9223cd0b5b245d2..678ae94945baccd5e86f69437d3343ac8a4a5062 100755
--- a/bigbluebutton-html5/private/config/settings.yml
+++ b/bigbluebutton-html5/private/config/settings.yml
@@ -487,7 +487,7 @@ private:
       toThirdParty: to-third-party-redis-channel
     subscribeTo:
     - to-html5-redis-channel
-    - from-akka-apps-*
+    - from-akka-apps-[^f]*
     - from-third-party-redis-channel
     - from-etherpad-redis-channel
     async: