conference: add recording to participant infos

So that everybody in the conference can know who is recording

Change-Id: Ibc2d670560c086e2fd1925cab6bb5ea12f563a73
GitLab: #699
This commit is contained in:
Sébastien Blin
2022-10-05 16:27:25 -04:00
parent bf886711b5
commit d570b2b38f
6 changed files with 95 additions and 10 deletions

View File

@ -115,7 +115,7 @@ Conference::Conference(const std::shared_ptr<Account>& account, bool attachHost)
// Handle participants showing their video
for (const auto& info : infos) {
std::string uri {};
bool isLocalMuted = false;
bool isLocalMuted = false, isPeerRecording = false;
std::string deviceId {};
auto active = false;
if (!info.callId.empty()) {
@ -123,6 +123,7 @@ Conference::Conference(const std::shared_ptr<Account>& account, bool attachHost)
if (auto call = std::dynamic_pointer_cast<SIPCall>(getCall(callId))) {
uri = call->getPeerNumber();
isLocalMuted = call->isPeerMuted();
isPeerRecording = call->isPeerRecording();
if (auto* transport = call->getTransport())
deviceId = transport->deviceId();
}
@ -146,7 +147,8 @@ Conference::Conference(const std::shared_ptr<Account>& account, bool attachHost)
isModeratorMuted,
isModerator,
isHandRaised,
isVoiceActive});
isVoiceActive,
isPeerRecording});
} else {
auto isModeratorMuted = false;
// If not local
@ -165,6 +167,7 @@ Conference::Conference(const std::shared_ptr<Account>& account, bool attachHost)
getCall(streamInfo.callId))) {
uri = call->getPeerNumber();
isLocalMuted = call->isPeerMuted();
isPeerRecording = call->isPeerRecording();
if (auto* transport = call->getTransport())
deviceId = transport->deviceId();
}
@ -180,6 +183,7 @@ Conference::Conference(const std::shared_ptr<Account>& account, bool attachHost)
peerId = "host"sv;
deviceId = acc->currentDeviceId();
isLocalMuted = shared->isMediaSourceMuted(MediaType::MEDIA_AUDIO);
isPeerRecording = shared->isRecording();
}
auto isHandRaised = shared->isHandRaised(deviceId);
auto isVoiceActive = shared->isVoiceActive(streamId);
@ -196,7 +200,8 @@ Conference::Conference(const std::shared_ptr<Account>& account, bool attachHost)
isModeratorMuted,
isModerator,
isHandRaised,
isVoiceActive});
isVoiceActive,
isPeerRecording});
}
}
if (auto videoMixer = shared->videoMixer_) {
@ -659,9 +664,7 @@ Conference::addParticipant(const std::string& participant_id)
{
JAMI_DBG("Adding call %s to conference %s", participant_id.c_str(), id_.c_str());
jami_tracepoint(conference_add_participant,
id_.c_str(),
participant_id.c_str());
jami_tracepoint(conference_add_participant, id_.c_str(), participant_id.c_str());
{
std::lock_guard<std::mutex> lk(participantsMtx_);
@ -1036,7 +1039,9 @@ Conference::toggleRecording()
// Notify each participant
foreachCall([&](auto call) { call->updateRecState(newState); });
return Recordable::toggleRecording();
auto res = Recordable::toggleRecording();
updateRecording();
return res;
}
std::string
@ -1453,6 +1458,21 @@ Conference::muteParticipant(const std::string& participant_id, const bool& state
muteCall(call->getCallId(), state);
}
void
Conference::updateRecording()
{
std::lock_guard<std::mutex> lk(confInfoMutex_);
for (auto& info : confInfo_) {
if (info.uri.empty()) {
info.recording = isRecording();
} else if (auto call = getCallWith(std::string(string_remove_suffix(info.uri, '@')),
info.device)) {
info.recording = call->isPeerRecording();
}
}
sendConferenceInfos();
}
void
Conference::updateMuted()
{
@ -1763,4 +1783,19 @@ Conference::getRemoteId(const std::shared_ptr<jami::Call>& call) const
return {};
}
void
Conference::stopRecording()
{
Recordable::stopRecording();
updateRecording();
}
bool
Conference::startRecording(const std::string& path)
{
auto res = Recordable::startRecording(path);
updateRecording();
return res;
}
} // namespace jami

View File

@ -76,6 +76,7 @@ struct ParticipantInfo
bool isModerator {false};
bool handRaised {false};
bool voiceActivity {false};
bool recording {false};
void fromJson(const Json::Value& v)
{
@ -93,6 +94,7 @@ struct ParticipantInfo
isModerator = v["isModerator"].asBool();
handRaised = v["handRaised"].asBool();
voiceActivity = v["voiceActivity"].asBool();
recording = v["recording"].asBool();
}
Json::Value toJson() const
@ -112,6 +114,7 @@ struct ParticipantInfo
val["isModerator"] = isModerator;
val["handRaised"] = handRaised;
val["voiceActivity"] = voiceActivity;
val["recording"] = recording;
return val;
}
@ -130,7 +133,8 @@ struct ParticipantInfo
{"audioModeratorMuted", audioModeratorMuted ? "true" : "false"},
{"isModerator", isModerator ? "true" : "false"},
{"handRaised", handRaised ? "true" : "false"},
{"voiceActivity", voiceActivity ? "true" : "false"}};
{"voiceActivity", voiceActivity ? "true" : "false"},
{"recording", recording ? "true" : "false"}};
}
friend bool operator==(const ParticipantInfo& p1, const ParticipantInfo& p2)
@ -141,7 +145,7 @@ struct ParticipantInfo
and p1.audioLocalMuted == p2.audioLocalMuted
and p1.audioModeratorMuted == p2.audioModeratorMuted
and p1.isModerator == p2.isModerator and p1.handRaised == p2.handRaised
and p1.voiceActivity == p2.voiceActivity;
and p1.voiceActivity == p2.voiceActivity and p1.recording == p2.recording;
}
friend bool operator!=(const ParticipantInfo& p1, const ParticipantInfo& p2)
@ -374,6 +378,7 @@ public:
const std::string& streamId,
const bool& state);
void updateMuted();
void updateRecording();
void updateVoiceActivity();
@ -390,6 +395,10 @@ public:
*/
std::vector<DRing::MediaMap> currentMediaList() const;
// Update layout if recording changes
void stopRecording() override;
bool startRecording(const std::string& path) override;
private:
std::weak_ptr<Conference> weak()
{

View File

@ -505,6 +505,9 @@ MediaEncoder::encode(AVFrame* frame, int streamIdx)
pkt.data = nullptr; // packet data will be allocated by the encoder
pkt.size = 0;
if (!encoderCtx)
return -1;
ret = avcodec_send_frame(encoderCtx, frame);
if (ret < 0)
return -1;

View File

@ -60,7 +60,7 @@ public:
/**
* Start recording
*/
bool startRecording(const std::string& path);
virtual bool startRecording(const std::string& path);
/**
* Return the file path for this recording

View File

@ -3489,6 +3489,8 @@ SIPCall::peerRecording(bool state)
emitSignal<DRing::CallSignal::RemoteRecordingChanged>(id, getPeerNumber(), false);
}
peerRecording_ = state;
if (auto conf = conf_.lock())
conf->updateRecording();
}
void

View File

@ -49,6 +49,7 @@ struct CallData
std::atomic_bool moderatorMuted {false};
std::atomic_bool raisedHand {false};
std::atomic_bool active {false};
std::atomic_bool recording {false};
void reset()
{
@ -60,6 +61,7 @@ struct CallData
moderatorMuted = false;
active = false;
raisedHand = false;
recording = false;
}
};
@ -96,6 +98,7 @@ private:
void testAudioConferenceConfInfo();
void testHostAddRmSecondVideo();
void testParticipantAddRmSecondVideo();
void testPropagateRecording();
CPPUNIT_TEST_SUITE(ConferenceTest);
CPPUNIT_TEST(testGetConference);
@ -115,10 +118,12 @@ private:
CPPUNIT_TEST(testAudioConferenceConfInfo);
CPPUNIT_TEST(testHostAddRmSecondVideo);
CPPUNIT_TEST(testParticipantAddRmSecondVideo);
CPPUNIT_TEST(testPropagateRecording);
CPPUNIT_TEST_SUITE_END();
// Common parts
std::string aliceId;
std::atomic_bool hostRecording {false};
std::string bobId;
std::string carlaId;
std::string daviId;
@ -155,6 +160,7 @@ ConferenceTest::setUp()
daviCall.reset();
confId = {};
confChanged = false;
hostRecording = false;
}
void
@ -236,22 +242,27 @@ ConferenceTest::registerSignalHandlers()
for (const auto& infos : participantsInfos) {
if (infos.at("uri").find(bobUri) != std::string::npos) {
bobCall.active = infos.at("active") == "true";
bobCall.recording = infos.at("recording") == "true";
bobCall.moderatorMuted = infos.at("audioModeratorMuted") == "true";
bobCall.raisedHand = infos.at("handRaised") == "true";
bobCall.device = infos.at("device");
bobCall.streamId = infos.at("sinkId");
} else if (infos.at("uri").find(carlaUri) != std::string::npos) {
carlaCall.active = infos.at("active") == "true";
carlaCall.recording = infos.at("recording") == "true";
carlaCall.moderatorMuted = infos.at("audioModeratorMuted") == "true";
carlaCall.raisedHand = infos.at("handRaised") == "true";
carlaCall.device = infos.at("device");
carlaCall.streamId = infos.at("sinkId");
} else if (infos.at("uri").find(daviUri) != std::string::npos) {
daviCall.active = infos.at("active") == "true";
daviCall.recording = infos.at("recording") == "true";
daviCall.moderatorMuted = infos.at("audioModeratorMuted") == "true";
daviCall.raisedHand = infos.at("handRaised") == "true";
daviCall.device = infos.at("device");
daviCall.streamId = infos.at("sinkId");
} else if (infos.at("uri").find(aliceUri) != std::string::npos) {
hostRecording = infos.at("recording") == "true";
}
}
cv.notify_one();
@ -883,6 +894,31 @@ ConferenceTest::testParticipantAddRmSecondVideo()
DRing::unregisterSignalHandlers();
}
void
ConferenceTest::testPropagateRecording()
{
registerSignalHandlers();
startConference();
JAMI_INFO("Play with recording state");
DRing::toggleRecording(bobId, bobCall.callId);
CPPUNIT_ASSERT(cv.wait_for(lk, 5s, [&] { return bobCall.recording.load(); }));
DRing::toggleRecording(bobId, bobCall.callId);
CPPUNIT_ASSERT(cv.wait_for(lk, 5s, [&] { return !bobCall.recording.load(); }));
DRing::toggleRecording(aliceId, confId);
CPPUNIT_ASSERT(cv.wait_for(lk, 5s, [&] { return hostRecording.load(); }));
DRing::toggleRecording(aliceId, confId);
CPPUNIT_ASSERT(cv.wait_for(lk, 5s, [&] { return !hostRecording.load(); }));
hangupConference();
DRing::unregisterSignalHandlers();
}
} // namespace test
} // namespace jami