mirror of
https://git.jami.net/savoirfairelinux/jami-daemon.git
synced 2025-08-12 22:09:25 +08:00
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:
@ -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
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
Reference in New Issue
Block a user