From b424a5d0bddd926dee92a9eab2d614be3b0f3b3e Mon Sep 17 00:00:00 2001 From: Pierre Lespagnol <pierre.lespagnol@savoirfairelinux.com> Date: Mon, 31 Aug 2020 11:34:28 -0400 Subject: [PATCH] recorder: add remote recording indicator Change-Id: I711a0b2f446f1620d7dff66945bf9295fd8372ac --- bin/dbus/cx.ring.Ring.CallManager.xml | 7 +++++ bin/dbus/dbusclient.cpp | 3 ++- bin/jni/callmanager.i | 3 +++ bin/jni/jni_interface.i | 3 ++- configure.ac | 2 +- src/client/ring_signal.cpp | 1 + src/dring/callmanager_interface.h | 4 +++ src/media/video/video_rtp_session.cpp | 5 ++++ src/media/video/video_rtp_session.h | 5 ++++ src/sip/sipcall.cpp | 37 +++++++++++++++++++++++++++ src/sip/sipcall.h | 4 +++ src/sip/sipvoiplink.cpp | 17 ++++++++++++ 12 files changed, 88 insertions(+), 3 deletions(-) diff --git a/bin/dbus/cx.ring.Ring.CallManager.xml b/bin/dbus/cx.ring.Ring.CallManager.xml index 76c1cf8427..7ae32d13dc 100644 --- a/bin/dbus/cx.ring.Ring.CallManager.xml +++ b/bin/dbus/cx.ring.Ring.CallManager.xml @@ -842,5 +842,12 @@ <annotation name="org.qtproject.QtDBus.QtTypeName.In1" value="VectorMapStringString"/> <arg type="aa{ss}" name="infos" /> </signal> + + <signal name="remoteRecordingChanged" tp:name-for-bindings="remoteRecordingChanged"> + <tp:added version="1.0.0"/> + <arg type="s" name="callID" /> + <arg type="s" name="peerNumber" /> + <arg type="b" name="remoteRecordingState" /> + </signal> </interface> </node> diff --git a/bin/dbus/dbusclient.cpp b/bin/dbus/dbusclient.cpp index a848a2fa71..c4057bfef0 100644 --- a/bin/dbus/dbusclient.cpp +++ b/bin/dbus/dbusclient.cpp @@ -177,7 +177,8 @@ DBusClient::initLibrary(int flags) exportable_callback<CallSignal::PeerHold>(bind(&DBusCallManager::peerHold, callM, _1, _2)), exportable_callback<CallSignal::AudioMuted>(bind(&DBusCallManager::audioMuted, callM, _1, _2)), exportable_callback<CallSignal::VideoMuted>(bind(&DBusCallManager::videoMuted, callM, _1, _2)), - exportable_callback<CallSignal::SmartInfo>(bind(&DBusCallManager::SmartInfo, callM, _1)) + exportable_callback<CallSignal::SmartInfo>(bind(&DBusCallManager::SmartInfo, callM, _1)), + exportable_callback<CallSignal::RemoteRecordingChanged>(bind(&DBusCallManager::remoteRecordingChanged, callM, _1, _2, _3)) }; // Configuration event handlers diff --git a/bin/jni/callmanager.i b/bin/jni/callmanager.i index b0d5f1e9c6..077e6b4b8d 100644 --- a/bin/jni/callmanager.i +++ b/bin/jni/callmanager.i @@ -48,6 +48,7 @@ public: virtual void onConferenceInfosUpdated(const std::string& confId, const std::vector<std::map<std::string, std::string>>& infos) {} virtual void peerHold(const std::string& call_id, bool holding){} virtual void connectionUpdate(const std::string& id, int state){} + virtual void remoteRecordingChanged(const std::string& call_id, const std::string& peer_number, bool state){} }; @@ -137,4 +138,6 @@ public: virtual void onConferenceInfosUpdated(const std::string& confId, const std::vector<std::map<std::string, std::string>>& infos) {} virtual void peerHold(const std::string& call_id, bool holding){} virtual void connectionUpdate(const std::string& id, int state){} + virtual void remoteRecordingChanged(const std::string& call_id, const std::string& peer_number, bool state){} + }; diff --git a/bin/jni/jni_interface.i b/bin/jni/jni_interface.i index 13c1005fd9..e68d64747d 100644 --- a/bin/jni/jni_interface.i +++ b/bin/jni/jni_interface.i @@ -249,7 +249,8 @@ void init(ConfigurationCallback* confM, Callback* callM, PresenceCallback* presM exportable_callback<CallSignal::RtcpReportReceived>(bind(&Callback::onRtcpReportReceived, callM, _1, _2)), exportable_callback<CallSignal::OnConferenceInfosUpdated>(bind(&Callback::onConferenceInfosUpdated, callM, _1, _2)), exportable_callback<CallSignal::PeerHold>(bind(&Callback::peerHold, callM, _1, _2)), - exportable_callback<CallSignal::ConnectionUpdate>(bind(&Callback::connectionUpdate, callM, _1, _2)) + exportable_callback<CallSignal::ConnectionUpdate>(bind(&Callback::connectionUpdate, callM, _1, _2)), + exportable_callback<CallSignal::RemoteRecordingChanged>(bind(&Callback::remoteRecordingChanged, callM, _1, _2, _3)) }; // Configuration event handlers diff --git a/configure.ac b/configure.ac index 455f6256a2..2efeab8971 100644 --- a/configure.ac +++ b/configure.ac @@ -4,7 +4,7 @@ dnl Process this file with autoconf to produce a configure script. AC_PREREQ([2.65]) AC_INIT([Jami Daemon],[9.5.0],[ring@gnu.org],[jami]) -AC_COPYRIGHT([[Copyright (c) Savoir-faire Linux 2004-2019]]) +AC_COPYRIGHT([[Copyright (c) Savoir-faire Linux 2004-2020]]) AC_REVISION([$Revision$]) dnl Where to find configure files diff --git a/src/client/ring_signal.cpp b/src/client/ring_signal.cpp index dbbb657a46..c63eada36f 100644 --- a/src/client/ring_signal.cpp +++ b/src/client/ring_signal.cpp @@ -49,6 +49,7 @@ getSignalHandlers() exported_callback<DRing::CallSignal::SmartInfo>(), exported_callback<DRing::CallSignal::ConnectionUpdate>(), exported_callback<DRing::CallSignal::OnConferenceInfosUpdated>(), + exported_callback<DRing::CallSignal::RemoteRecordingChanged>(), /* Configuration */ exported_callback<DRing::ConfigurationSignal::VolumeChanged>(), diff --git a/src/dring/callmanager_interface.h b/src/dring/callmanager_interface.h index 60820a97f1..41baed45c6 100644 --- a/src/dring/callmanager_interface.h +++ b/src/dring/callmanager_interface.h @@ -228,6 +228,10 @@ struct DRING_PUBLIC CallSignal using cb_type = void(const std::string&, const std::vector<std::map<std::string, std::string>>&); }; + struct DRING_PUBLIC RemoteRecordingChanged { + constexpr static const char* name = "RemoteRecordingChanged"; + using cb_type = void(const std::string&, const std::string&, bool); + }; }; } // namespace DRing diff --git a/src/media/video/video_rtp_session.cpp b/src/media/video/video_rtp_session.cpp index e7fa759d8f..54837382a8 100644 --- a/src/media/video/video_rtp_session.cpp +++ b/src/media/video/video_rtp_session.cpp @@ -649,6 +649,8 @@ VideoRtpSession::initRecorder(std::shared_ptr<MediaRecorder>& rec) } } } + if (recordingStateCallback_) + recordingStateCallback_(true); } void @@ -664,6 +666,9 @@ VideoRtpSession::deinitRecorder(std::shared_ptr<MediaRecorder>& rec) input->detach(ob); } } + if (recordingStateCallback_) + recordingStateCallback_(false); + } void diff --git a/src/media/video/video_rtp_session.h b/src/media/video/video_rtp_session.h index 35dc6807b4..d7f404c7f3 100644 --- a/src/media/video/video_rtp_session.h +++ b/src/media/video/video_rtp_session.h @@ -97,6 +97,9 @@ public: const std::string& getInput() const { return input_; } void setChangeOrientationCallback(std::function<void(int)> cb); + void setRecStateCallback(std::function<void(bool)> cb) { + recordingStateCallback_ = std::move(cb); + } void initRecorder(std::shared_ptr<MediaRecorder>& rec) override; void deinitRecorder(std::shared_ptr<MediaRecorder>& rec) override; @@ -161,6 +164,8 @@ private: std::function<void(int)> changeOrientationCallback_; + std::function<void(bool)> recordingStateCallback_; + // interval in seconds between RTCP checkings std::chrono::seconds rtcp_checking_interval {4}; diff --git a/src/sip/sipcall.cpp b/src/sip/sipcall.cpp index e606e9f14a..222aa60a83 100644 --- a/src/sip/sipcall.cpp +++ b/src/sip/sipcall.cpp @@ -335,6 +335,25 @@ SIPCall::sendSIPInfo(const char* const body, const char* const subtype) pjsip_dlg_send_request(inv->dlg, tdata, Manager::instance().sipVoIPLink().getModId(), NULL); } +void +SIPCall::updateRecState(bool state) +{ + std::string BODY = + "<?xml version=\"1.0\" encoding=\"utf-8\" ?>" + "<media_control><vc_primitive><to_encoder>" + "<recording_state=" + std::to_string(state) + "/>" + "</to_encoder></vc_primitive></media_control>"; + // see https://tools.ietf.org/html/rfc5168 for XML Schema for Media Control details + + JAMI_DBG("Sending recording state via SIP INFO"); + + try { + sendSIPInfo(BODY.c_str(), "media_control+xml"); + } catch (const std::exception& e) { + JAMI_ERR("Error sending recording state: %s", e.what()); + } +} + void SIPCall::requestKeyframe() { @@ -1037,6 +1056,12 @@ SIPCall::startAllMedia() this_->setVideoOrientation(angle); }); }); + videortp_->setRecStateCallback([wthis = weak()] (bool state) { + runOnMainThread([wthis, state] { + if (auto this_ = wthis.lock()) + this_->updateRecState(state); + }); + }); #endif for (const auto& slot : slots) { @@ -1599,4 +1624,16 @@ SIPCall::rtpSetupSuccess(MediaType type) toggleRecording(); } +void +SIPCall::setRemoteRecording(bool state) +{ + if (state) { + JAMI_WARN("SIP remote recording enabled"); + emitSignal<DRing::CallSignal::RemoteRecordingChanged>(getCallId(), getPeerNumber(), true); + } else { + JAMI_WARN("SIP remote recording disabled"); + emitSignal<DRing::CallSignal::RemoteRecordingChanged>(getCallId(), getPeerNumber(), false); + } +} + } // namespace jami diff --git a/src/sip/sipcall.h b/src/sip/sipcall.h index 750b54083b..72478f8108 100644 --- a/src/sip/sipcall.h +++ b/src/sip/sipcall.h @@ -174,6 +174,8 @@ public: // SIP related void requestKeyframe(); + void updateRecState(bool state); + SIPAccountBase& getSIPAccount() const; void updateSDPFromSTUN(); @@ -233,6 +235,8 @@ public: // NOT SIP RELATED (good candidates to be moved elsewhere) void rtpSetupSuccess(MediaType type); + void setRemoteRecording(bool state); + private: using clock = std::chrono::steady_clock; using time_point = clock::time_point; diff --git a/src/sip/sipvoiplink.cpp b/src/sip/sipvoiplink.cpp index 72cac8deae..8c0dc4ed17 100644 --- a/src/sip/sipvoiplink.cpp +++ b/src/sip/sipvoiplink.cpp @@ -1052,6 +1052,7 @@ handleMediaControl(SIPCall& call, pjsip_msg_body* body) pj_strset(&control_st, (char*) body->data, body->len); static constexpr pj_str_t PICT_FAST_UPDATE = CONST_PJ_STR("picture_fast_update"); static constexpr pj_str_t DEVICE_ORIENTATION = CONST_PJ_STR("device_orientation"); + static constexpr pj_str_t RECORDING_STATE = CONST_PJ_STR("recording_state"); if (pj_strstr(&control_st, &PICT_FAST_UPDATE)) { call.sendKeyframe(); @@ -1078,6 +1079,22 @@ handleMediaControl(SIPCall& call, pjsip_msg_body* body) return true; } } + else if (pj_strstr(&control_st, &RECORDING_STATE)) { + static const std::regex REC_REGEX("recording_state=([0-1])"); + std::string body_msg(control_st.ptr, control_st.slen); + std::smatch matched_pattern; + std::regex_search(body_msg, matched_pattern, REC_REGEX); + + if (matched_pattern.ready() && !matched_pattern.empty() && matched_pattern[1].matched) { + try { + bool state = std::stoi(matched_pattern[1]); + call.setRemoteRecording(state); + } catch (const std::exception& e) { + JAMI_WARN("Error parsing state remote recording: %s", e.what()); + } + return true; + } + } } return false; -- GitLab