diff --git a/akka-bbb-apps/src/main/resources/application.conf b/akka-bbb-apps/src/main/resources/application.conf index d7c9e9de65..4de5614cf6 100755 --- a/akka-bbb-apps/src/main/resources/application.conf +++ b/akka-bbb-apps/src/main/resources/application.conf @@ -82,4 +82,8 @@ services { apps { checkPermissions = true +} + +voiceConf { + recordPath = "/var/freeswitch/meetings" } \ No newline at end of file diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/SystemConfiguration.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/SystemConfiguration.scala index fc33700250..840843e845 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/SystemConfiguration.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/SystemConfiguration.scala @@ -61,4 +61,6 @@ trait SystemConfiguration { lazy val telizePort = Try(config.getInt("services.telizePort")).getOrElse(80) lazy val applyPermissionCheck = Try(config.getBoolean("apps.checkPermissions")).getOrElse(false) + + lazy val voiceConfRecordPath = Try(config.getString("voiceConf.recordPath")).getOrElse("/var/freeswitch/meetings") } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/voice/UserJoinedVoiceConfEvtMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/voice/UserJoinedVoiceConfEvtMsgHdlr.scala index 7c262c2bc7..8b1c4cdf49 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/voice/UserJoinedVoiceConfEvtMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/voice/UserJoinedVoiceConfEvtMsgHdlr.scala @@ -1,13 +1,15 @@ package org.bigbluebutton.core.apps.voice +import org.bigbluebutton.SystemConfiguration import org.bigbluebutton.common2.msgs._ import org.bigbluebutton.core.apps.breakout.BreakoutHdlrHelpers import org.bigbluebutton.core.models.{ VoiceUserState, VoiceUsers } import org.bigbluebutton.core.running.{ BaseMeetingActor, LiveMeeting, OutMsgRouter } +import org.bigbluebutton.core.util.TimeUtil import org.bigbluebutton.core2.MeetingStatus2x import org.bigbluebutton.core2.message.senders.MsgBuilder -trait UserJoinedVoiceConfEvtMsgHdlr extends BreakoutHdlrHelpers { +trait UserJoinedVoiceConfEvtMsgHdlr extends BreakoutHdlrHelpers with SystemConfiguration { this: BaseMeetingActor => val liveMeeting: LiveMeeting @@ -62,9 +64,11 @@ trait UserJoinedVoiceConfEvtMsgHdlr extends BreakoutHdlrHelpers { liveMeeting.props.recordProp.record && !MeetingStatus2x.isVoiceRecording(liveMeeting.status)) { - log.info("Send START RECORDING voice conf. meetingId=" + liveMeeting.props.meetingProp.intId - + " voice conf=" + liveMeeting.props.voiceProp.voiceConf) - VoiceApp.startRecordingVoiceConference(liveMeeting, outGW) + val meetingId = liveMeeting.props.meetingProp.intId + val recordFile = VoiceApp.genRecordPath(voiceConfRecordPath, meetingId, TimeUtil.timeNowInMs()) + log.info("Send START RECORDING voice conf. meetingId=" + meetingId + " voice conf=" + liveMeeting.props.voiceProp.voiceConf) + + VoiceApp.startRecordingVoiceConference(liveMeeting, outGW, recordFile) } else { log.info("Not recording audio as numVoiceUsers={} and isRecording={} and recordProp={}", numVoiceUsers, MeetingStatus2x.isVoiceRecording(liveMeeting.status), liveMeeting.props.recordProp.record) diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/voice/VoiceApp.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/voice/VoiceApp.scala index 2d4dc81352..bc56c5163e 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/voice/VoiceApp.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/voice/VoiceApp.scala @@ -1,16 +1,27 @@ package org.bigbluebutton.core.apps.voice +import java.io.File + import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter } import org.bigbluebutton.core2.MeetingStatus2x import org.bigbluebutton.core2.message.senders.MsgBuilder object VoiceApp { - def startRecordingVoiceConference(liveMeeting: LiveMeeting, outGW: OutMsgRouter): Unit = { + def genRecordPath(recordDir: String, meetingId: String, timestamp: Long): String = { + if (recordDir.endsWith("/")) { + recordDir.concat(meetingId).concat("-").concat(timestamp.toString).concat(".wav") + } else { + recordDir.concat("/").concat(meetingId).concat("-").concat(timestamp.toString).concat(".wav") + } + } + + def startRecordingVoiceConference(liveMeeting: LiveMeeting, outGW: OutMsgRouter, stream: String): Unit = { MeetingStatus2x.startRecordingVoice(liveMeeting.status) val event = MsgBuilder.buildStartRecordingVoiceConfSysMsg( liveMeeting.props.meetingProp.intId, - liveMeeting.props.voiceProp.voiceConf + liveMeeting.props.voiceProp.voiceConf, + stream ) outGW.send(event) } 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 26c19d2110..0d79d2ab79 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 @@ -136,6 +136,8 @@ class ReceivedJsonMsgHandlerActor( routeVoiceMsg[UserDisconnectedFromGlobalAudioMsg](envelope, jsonNode) case MuteMeetingCmdMsg.NAME => routeGenericMsg[MuteMeetingCmdMsg](envelope, jsonNode) + case VoiceConfRunningEvtMsg.NAME => + routeVoiceMsg[VoiceConfRunningEvtMsg](envelope, jsonNode) // Breakout rooms case BreakoutRoomsListMsg.NAME => diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/record/events/RecordEvent.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/record/events/RecordEvent.scala index eb85c6584b..e9057b505f 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/record/events/RecordEvent.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/record/events/RecordEvent.scala @@ -19,6 +19,7 @@ package org.bigbluebutton.core.record.events +import java.text.SimpleDateFormat import java.util.Date import scala.collection.mutable.HashMap @@ -34,7 +35,8 @@ trait RecordEvent { final def timestampUTC(utc: Long): Unit = { eventMap.put(TIMESTAMP_UTC, utc.toString) - eventMap.put(DATE, (new Date(utc)).toString) + val sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX") + eventMap.put(DATE, sdf.format(utc)) } /** 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 e8d02e70a5..77886b2181 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 @@ -412,14 +412,17 @@ class MeetingActor( val now = TimeUtil.timeNowInMs() val elapsedInMs = now - lastRecBreakSentOn val elapsedInMin = TimeUtil.millisToMinutes(elapsedInMs) - println("******************* ElapsedInMS=" + elapsedInMs + ", elapsedInMIN=" + elapsedInMin) + if (elapsedInMin > 1) { lastRecBreakSentOn = now val event = MsgBuilder.buildRecordingChapterBreakSysMsg(props.meetingProp.intId, TimeUtil.timeNowInMs()) outGW.send(event) VoiceApp.stopRecordingVoiceConference(liveMeeting, outGW) - VoiceApp.startRecordingVoiceConference(liveMeeting, outGW) + + val meetingId = liveMeeting.props.meetingProp.intId + val recordFile = VoiceApp.genRecordPath(voiceConfRecordPath, meetingId, now) + VoiceApp.startRecordingVoiceConference(liveMeeting, outGW, recordFile) } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActorAudit.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActorAudit.scala index 64e41437b0..f92d8f5b7e 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActorAudit.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActorAudit.scala @@ -1,17 +1,21 @@ package org.bigbluebutton.core.running import java.io.{ PrintWriter, StringWriter } +import java.text.SimpleDateFormat +import java.util.Date import akka.actor.Actor import akka.actor.ActorLogging import akka.actor.Props import akka.actor.OneForOneStrategy import akka.actor.SupervisorStrategy.Resume + import scala.concurrent.duration._ import org.bigbluebutton.SystemConfiguration import org.bigbluebutton.common2.domain.DefaultProps import org.bigbluebutton.core.api._ import org.bigbluebutton.core.bus.{ BigBlueButtonEvent, InternalEventBus } + import scala.concurrent.ExecutionContext.Implicits.global object MeetingActorAudit { diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/senders/MsgBuilder.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/senders/MsgBuilder.scala index 39ae45662f..8ddf5eb3bd 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/senders/MsgBuilder.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/senders/MsgBuilder.scala @@ -258,11 +258,11 @@ object MsgBuilder { BbbCommonEnvCoreMsg(envelope, event) } - def buildStartRecordingVoiceConfSysMsg(meetingId: String, voiceConf: String): BbbCommonEnvCoreMsg = { + def buildStartRecordingVoiceConfSysMsg(meetingId: String, voiceConf: String, stream: String): BbbCommonEnvCoreMsg = { val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka") val envelope = BbbCoreEnvelope(StartRecordingVoiceConfSysMsg.NAME, routing) val header = BbbCoreHeaderWithMeetingId(StartRecordingVoiceConfSysMsg.NAME, meetingId) - val body = StartRecordingVoiceConfSysMsgBody(voiceConf, meetingId) + val body = StartRecordingVoiceConfSysMsgBody(voiceConf, meetingId, stream) val event = StartRecordingVoiceConfSysMsg(header, body) BbbCommonEnvCoreMsg(envelope, event) } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/voiceconf/VoiceConfActor.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/voiceconf/VoiceConfActor.scala new file mode 100755 index 0000000000..a7349f270e --- /dev/null +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/voiceconf/VoiceConfActor.scala @@ -0,0 +1,20 @@ +package org.bigbluebutton.core2.voiceconf + +import akka.actor.{ Actor, ActorLogging, FSM } +import org.bigbluebutton.SystemConfiguration + +sealed trait VoiceConfState +case object VoiceConfStarted extends VoiceConfState +case object StartRecording extends VoiceConfState +case object RecordingStarted extends VoiceConfState +case object StopRecording extends VoiceConfState +case object RecordingStopped extends VoiceConfState +case object VoiceConfStopped extends VoiceConfState + +case class VoiceConfData(record: Boolean, numUsers: Int, streams: Vector[RecordStream]) +case class RecordStream(stream: String, started: Boolean) + +class VoiceConfActor extends Actor with ActorLogging with FSM[VoiceConfState, VoiceConfData] with SystemConfiguration { + +} + diff --git a/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/freeswitch/FreeswitchApplication.java b/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/freeswitch/FreeswitchApplication.java index dcfc6274f1..f8eaf52bc8 100755 --- a/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/freeswitch/FreeswitchApplication.java +++ b/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/freeswitch/FreeswitchApplication.java @@ -114,10 +114,7 @@ public class FreeswitchApplication { return TimeUnit.NANOSECONDS.toMillis(System.nanoTime()); } - public void startRecording(String voiceConfId, String meetingid) { - String RECORD_DIR = "/var/freeswitch/meetings"; - String voicePath = RECORD_DIR + File.separatorChar + meetingid + "-" + genTimestamp() + ".wav"; - + public void startRecording(String voiceConfId, String meetingid, String voicePath) { RecordConferenceCommand rcc = new RecordConferenceCommand(voiceConfId, USER, true, voicePath); queueMessage(rcc); } diff --git a/akka-bbb-fsesl/src/main/scala/org/bigbluebutton/freeswitch/RxJsonMsgDeserializer.scala b/akka-bbb-fsesl/src/main/scala/org/bigbluebutton/freeswitch/RxJsonMsgDeserializer.scala index 67e34b23df..7e69fdbdc1 100755 --- a/akka-bbb-fsesl/src/main/scala/org/bigbluebutton/freeswitch/RxJsonMsgDeserializer.scala +++ b/akka-bbb-fsesl/src/main/scala/org/bigbluebutton/freeswitch/RxJsonMsgDeserializer.scala @@ -149,7 +149,7 @@ trait RxJsonMsgDeserializer { for { m <- deserialize(jsonNode) } yield { - fsApp.startRecording(m.body.voiceConf, m.body.meetingId) + fsApp.startRecording(m.body.voiceConf, m.body.meetingId, m.body.stream) } } diff --git a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/VoiceConfMsgs.scala b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/VoiceConfMsgs.scala index 9521dad5fd..69565b9eed 100755 --- a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/VoiceConfMsgs.scala +++ b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/VoiceConfMsgs.scala @@ -191,7 +191,7 @@ case class MeetingMutedEvtMsgBody(muted: Boolean, mutedBy: String) object StartRecordingVoiceConfSysMsg { val NAME = "StartRecordingVoiceConfSysMsg" } case class StartRecordingVoiceConfSysMsg(header: BbbCoreHeaderWithMeetingId, body: StartRecordingVoiceConfSysMsgBody) extends BbbCoreMsg - case class StartRecordingVoiceConfSysMsgBody(voiceConf: String, meetingId: String) + case class StartRecordingVoiceConfSysMsgBody(voiceConf: String, meetingId: String, stream: String) /** * Sent to FS to stop recording voice conference.