From b370ada71114f052fed6d966fe3c88f46ad674a6 Mon Sep 17 00:00:00 2001 From: Mohamed Chibani <mohamed.chibani@savoirfairelinux.com> Date: Thu, 30 Sep 2021 16:30:56 -0400 Subject: [PATCH] multi-stream: handle media change request in conference Handle media change request in conference, specifically when adding video to a an audio only call. Gitlab: #638 Change-Id: I0eb892eb941d2a62b6046c7b2ac9d128f4bcbd12 --- src/call.h | 44 +++++++- src/conference.cpp | 59 +++++++++- src/conference.h | 9 ++ src/ice_transport.cpp | 6 +- src/manager.cpp | 19 +--- src/manager.h | 11 +- src/media/media_attribute.cpp | 4 - src/media/video/video_mixer.cpp | 4 +- src/media/video/video_rtp_session.cpp | 8 +- src/sip/sipcall.cpp | 153 +++++++++++++++++++++++--- src/sip/sipcall.h | 6 +- 11 files changed, 258 insertions(+), 65 deletions(-) diff --git a/src/call.h b/src/call.h index 8e4e930c30..3f817b4c46 100644 --- a/src/call.h +++ b/src/call.h @@ -219,9 +219,33 @@ public: virtual void answer(const std::vector<DRing::MediaMap>& mediaList) = 0; /** - * Answer to a media update request. The media attributes set by the - * caller of this method will determine the response sent to the - * peer and the configuration of the local media. + * Check the media of an incoming media change request. + * This method checks the new media against the current media. It + * determines if the differences are significant enough to require + * more processing. + * For instance, this can be used to check if the a change request + * must be reported to the client for confirmation or can be handled + * by the daemon. + * The conditions that cause this method to return true are implementation + * specific. + * + * @param the new media list from the remote + * @return true if the new media differs from the current media + **/ + virtual bool checkMediaChangeRequest(const std::vector<DRing::MediaMap>& remoteMediaList) = 0; + + /** + * Process incoming media change request. + * + * @param the new media list from the remote + */ + virtual void handleMediaChangeRequest(const std::vector<DRing::MediaMap>& remoteMediaList) = 0; + + /** + * Answer to a media update request. + * The media attributes set by the caller of this method will + * determine the response to send to the peer and the configuration + * of the local media. * @param mediaList The list of media attributes. An empty media * list means the media update request was not accepted, meaning the * call continue with the current media. It's up to the implementation @@ -318,11 +342,23 @@ public: - duration_start_); } -public: // media management + // media management virtual bool toggleRecording(); virtual std::vector<MediaAttribute> getMediaAttributeList() const = 0; + /** + * Add a dummy video stream with the attached sink. + * Typically needed in conference to display infos for participants + * that have joined the conference without video (audio only). + */ + virtual bool addDummyVideoRtpSession() = 0; + + /** + * Remove all dummy video streams. + */ + virtual void removeDummyVideoRtpSessions() = 0; + virtual void switchInput(const std::string& = {}) {}; /** diff --git a/src/conference.cpp b/src/conference.cpp index c4a4a75129..3da528b715 100644 --- a/src/conference.cpp +++ b/src/conference.cpp @@ -55,8 +55,8 @@ namespace jami { Conference::Conference(bool enableVideo) : id_(Manager::instance().callFactory.getNewCallID()) #ifdef ENABLE_VIDEO - , mediaInput_(Manager::instance().getVideoManager().videoDeviceMonitor.getMRLForDefaultDevice()) , videoEnabled_(enableVideo) + , mediaInput_(Manager::instance().getVideoManager().videoDeviceMonitor.getMRLForDefaultDevice()) #endif { JAMI_INFO("Create new conference %s", id_.c_str()); @@ -128,7 +128,6 @@ Conference::Conference(bool enableVideo) } lk.unlock(); if (!hostAdded) { - auto audioLocalMuted = shared->isMediaSourceMuted(MediaType::MEDIA_AUDIO); ParticipantInfo pi; pi.videoMuted = true; pi.audioLocalMuted = shared->isMediaSourceMuted(MediaType::MEDIA_AUDIO); @@ -310,6 +309,12 @@ Conference::takeOverMediaSourceControl(const std::string& callId) return; } + auto account = call->getAccount().lock(); + if (not account) { + JAMI_ERR("No account detected for call %s", callId.c_str()); + return; + } + auto mediaList = call->getMediaAttributeList(); std::vector<MediaType> mediaTypeList {MediaType::MEDIA_AUDIO, MediaType::MEDIA_VIDEO}; @@ -323,7 +328,7 @@ Conference::takeOverMediaSourceControl(const std::string& callId) auto iter = std::find_if(mediaList.begin(), mediaList.end(), check); if (iter == mediaList.end()) { - // Nothing to do if the call does not have a media that uses with + // Nothing to do if the call does not have a media with // a valid source type. JAMI_DBG("[Call: %s] Does not have an active [%s] media source", callId.c_str(), @@ -335,7 +340,7 @@ Conference::takeOverMediaSourceControl(const std::string& callId) // If the source state for the specified media type is not set // yet, the state will initialized using the state of the first // participant with a valid media source. - if (call->getAccount().lock()->isRendezVous()) { + if (account->isRendezVous()) { iter->muted_ = true; } setMediaSourceState(iter->type_, iter->muted_); @@ -371,6 +376,39 @@ Conference::takeOverMediaSourceControl(const std::string& callId) } } +void +Conference::handleMediaChangeRequest(const std::shared_ptr<Call>& call, + const std::vector<DRing::MediaMap>& remoteMediaList) +{ + JAMI_DBG("Conf [%s] Answer to media change request", getConfID().c_str()); + + // If the new media list has video, remove existing dummy + // video sessions if any. + if (MediaAttribute::hasMediaType(MediaAttribute::buildMediaAttributesList(remoteMediaList, + false), + MediaType::MEDIA_VIDEO)) { + call->removeDummyVideoRtpSessions(); + } + + // Check if we need to update the mixer. + // We need to check before the media is changed. + auto updateMixer = call->checkMediaChangeRequest(remoteMediaList); + + // NOTE: + // Since this is a conference, accept any media change request. + // This also means that if original call was an audio-only call, + // the local camera will be enabled, unless the video is disabled + // in the account settings. + + call->answerMediaChangeRequest(remoteMediaList); + call->enterConference(call->getConfId()); + + if (updateMixer and getState() == Conference::State::ACTIVE_ATTACHED) { + detachLocalParticipant(); + attachLocalParticipant(); + } +} + void Conference::addParticipant(const std::string& participant_id) { @@ -415,6 +453,14 @@ Conference::addParticipant(const std::string& participant_id) } #ifdef ENABLE_VIDEO if (auto call = getCall(participant_id)) { + // In conference, all participants need to have video session + // (with a sink) in order to display the participant info in + // the layout. So, if a participant joins with an audio only + // call, a dummy video stream is added to the call. + auto mediaList = call->getMediaAttributeList(); + if (not MediaAttribute::hasMediaType(mediaList, MediaType::MEDIA_VIDEO)) { + call->addDummyVideoRtpSession(); + } call->enterConference(getConfID()); // Continue the recording for the conference if one participant was recording if (call->isRecording()) { @@ -750,6 +796,8 @@ void Conference::switchInput(const std::string& input) { #ifdef ENABLE_VIDEO + JAMI_DBG("[Conf:%s] Setting video input to %s", id_.c_str(), input.c_str()); + mediaInput_ = input; // Done if the video is disabled @@ -877,8 +925,7 @@ Conference::onConfOrder(const std::string& callId, const std::string& confOrder) hangupParticipant(root["hangupParticipant"].asString()); } if (root.isMember("handRaised")) { - setHandRaised(root["handRaised"].asString(), - root["handState"].asString() == "true"); + setHandRaised(root["handRaised"].asString(), root["handState"].asString() == "true"); } } } diff --git a/src/conference.h b/src/conference.h index 424fa4e638..2bcdb64726 100644 --- a/src/conference.h +++ b/src/conference.h @@ -247,6 +247,15 @@ public: */ void takeOverMediaSourceControl(const std::string& callId); + /** + * Process incoming media change request. + * + * @param callId the call ID + * @param remoteMediaList new media list from the remote + */ + void handleMediaChangeRequest(const std::shared_ptr<Call>& call, + const std::vector<DRing::MediaMap>& remoteMediaList); + /** * Add a new participant to the conference */ diff --git a/src/ice_transport.cpp b/src/ice_transport.cpp index 293ba0864e..060311c13d 100644 --- a/src/ice_transport.cpp +++ b/src/ice_transport.cpp @@ -732,7 +732,7 @@ IceTransport::Impl::link() const auto laddr = getLocalAddress(absIdx); auto raddr = getRemoteAddress(absIdx); - if (laddr and raddr) { + if (laddr and laddr.getPort() != 0 and raddr and raddr.getPort() != 0) { out << " [" << i << "] " << laddr.toString(true, true) << " [" << getCandidateType(getSelectedCandidate(absIdx, false)) << "] " << " <-> " << raddr.toString(true, true) << " [" @@ -811,7 +811,7 @@ IceTransport::Impl::getSelectedCandidate(unsigned comp_id, bool remote) const const auto* sess = pj_ice_strans_get_valid_pair(icest_, comp_id); if (sess == nullptr) { - JAMI_ERR("[ice:%p] Component %i has no valid pair", this, comp_id); + JAMI_WARN("[ice:%p] Component %i has no valid pair (disabled)", this, comp_id); return nullptr; } @@ -829,7 +829,6 @@ IceTransport::Impl::getLocalAddress(unsigned comp_id) const if (auto cand = getSelectedCandidate(comp_id, false)) return cand->addr; - JAMI_ERR("[ice:%p] No local address for component %i", this, comp_id); return {}; } @@ -841,7 +840,6 @@ IceTransport::Impl::getRemoteAddress(unsigned comp_id) const if (auto cand = getSelectedCandidate(comp_id, true)) return cand->addr; - JAMI_ERR("[ice:%p] No remote address for component %i", this, comp_id); return {}; } diff --git a/src/manager.cpp b/src/manager.cpp index fd8dc23b0a..552e40aa2d 100644 --- a/src/manager.cpp +++ b/src/manager.cpp @@ -52,7 +52,6 @@ using random_device = dht::crypto::random_device; #include "sip/sip_utils.h" #include "sip/sipvoiplink.h" -#include "sip/sipaccount.h" #include "im/instant_messaging.h" @@ -1664,7 +1663,8 @@ Manager::createConfFromParticipantList(const std::vector<std::string>& participa bool videoEnabled {false}; for (const auto& numberaccount : participantList) { std::string tostr(numberaccount.substr(0, numberaccount.find(','))); - std::string accountId(numberaccount.substr(numberaccount.find(',') + 1, numberaccount.size())); + std::string accountId( + numberaccount.substr(numberaccount.find(',') + 1, numberaccount.size())); auto account = getAccount(accountId); if (account) { videoEnabled |= account->isVideoEnabled(); @@ -2107,20 +2107,6 @@ Manager::incomingCall(Call& call, const std::string& accountId) pimpl_->processIncomingCall(call, accountId); } -void -Manager::mediaChangeRequested(const std::string& callId, - const std::string& accountId, - const std::vector<DRing::MediaMap>& mediaList) -{ - JAMI_INFO("Media change request for call %s on account %s with %lu media", - callId.c_str(), - accountId.c_str(), - mediaList.size()); - - // Report the media change request. - emitSignal<DRing::CallSignal::MediaChangeRequested>(accountId, callId, mediaList); -} - void Manager::incomingMessage(const std::string& callID, const std::string& from, @@ -2883,7 +2869,6 @@ Manager::ManagerPimpl::processIncomingCall(Call& incomCall, const std::string& a } } - AudioFormat Manager::hardwareAudioFormatChanged(AudioFormat format) { diff --git a/src/manager.h b/src/manager.h index 464d8ba711..08e725da0b 100644 --- a/src/manager.h +++ b/src/manager.h @@ -55,7 +55,7 @@ namespace jami { namespace video { class SinkClient; class VideoGenerator; -} +} // namespace video class ChannelSocket; class RingBufferPool; struct VideoManager; @@ -214,15 +214,6 @@ public: */ void incomingCall(Call& call, const std::string& accountId); - /** - * Handle a media change request from the peer - * @param callId - * @param accountId - */ - void mediaChangeRequested(const std::string& callId, - const std::string& accountId, - const std::vector<DRing::MediaMap>& mediaList); - /** * Functions which occur with a user's action * Hangup the call diff --git a/src/media/media_attribute.cpp b/src/media/media_attribute.cpp index a8b1784846..3c9cc03a30 100644 --- a/src/media/media_attribute.cpp +++ b/src/media/media_attribute.cpp @@ -87,7 +87,6 @@ MediaAttribute::getMediaType(const DRing::MediaMap& map) { const auto& iter = map.find(DRing::Media::MediaAttributeKey::MEDIA_TYPE); if (iter == map.end()) { - JAMI_WARN("[MEDIA_TYPE] key not found in media map"); return {false, MediaType::MEDIA_NONE}; } @@ -117,7 +116,6 @@ MediaAttribute::getMediaSourceType(const DRing::MediaMap& map) { const auto& iter = map.find(DRing::Media::MediaAttributeKey::SOURCE_TYPE); if (iter == map.end()) { - JAMI_WARN("[MEDIA_TYPE] key not found in media map"); return {false, MediaSourceType::NONE}; } @@ -129,7 +127,6 @@ MediaAttribute::getBoolValue(const DRing::MediaMap& map, const std::string& key) { const auto& iter = map.find(key); if (iter == map.end()) { - JAMI_WARN("[%s] key not found for media", key.c_str()); return {false, false}; } @@ -148,7 +145,6 @@ MediaAttribute::getStringValue(const DRing::MediaMap& map, const std::string& ke { const auto& iter = map.find(key); if (iter == map.end()) { - JAMI_WARN("[%s] key not found in media map", key.c_str()); return {false, {}}; } diff --git a/src/media/video/video_mixer.cpp b/src/media/video/video_mixer.cpp index 9271534a7d..0540436d50 100644 --- a/src/media/video/video_mixer.cpp +++ b/src/media/video/video_mixer.cpp @@ -117,6 +117,8 @@ VideoMixer::~VideoMixer() void VideoMixer::switchInput(const std::string& input) { + JAMI_DBG("Set new input %s", input.c_str()); + if (auto local = videoLocal_) { // Detach videoInput from mixer local->detach(this); @@ -131,7 +133,7 @@ VideoMixer::switchInput(const std::string& input) } if (input.empty()) { - JAMI_DBG("[mixer:%s] Input is empty, don't add it in the mixer", id_.c_str()); + JAMI_DBG("[mixer:%s] Input is empty, don't add it to the mixer", id_.c_str()); return; } diff --git a/src/media/video/video_rtp_session.cpp b/src/media/video/video_rtp_session.cpp index 124987d5a8..8c23e70d8c 100644 --- a/src/media/video/video_rtp_session.cpp +++ b/src/media/video/video_rtp_session.cpp @@ -71,11 +71,13 @@ VideoRtpSession::VideoRtpSession(const string& callID, const DeviceParams& local { setupVideoBitrateInfo(); // reset bitrate cc = std::make_unique<CongestionControl>(); + JAMI_DBG("[%p] Video RTP session created", this); } VideoRtpSession::~VideoRtpSession() { stop(); + JAMI_DBG("[%p] Video RTP session destroyed", this); } /// Setup internal VideoBitrateInfo structure from media descriptors. @@ -97,7 +99,7 @@ void VideoRtpSession::startSender() { JAMI_DBG("Start video RTP sender: input [%s] - muted [%s]", - input_.c_str(), + conference_ ? "Video Mixer" : input_.c_str(), muteState_ ? "YES" : "NO"); if (send_.enabled and not send_.onHold) { @@ -321,8 +323,10 @@ VideoRtpSession::setupVideoPipeline() if (conference_) setupConferenceVideoPipeline(*conference_); else if (sender_) { - if (videoLocal_) + if (videoLocal_) { + JAMI_DBG("[call:%s] Setup video pipeline on local capture device", callID_.c_str()); videoLocal_->attach(sender_.get()); + } } else { videoLocal_.reset(); } diff --git a/src/sip/sipcall.cpp b/src/sip/sipcall.cpp index d7847892d9..c6667cd0f5 100644 --- a/src/sip/sipcall.cpp +++ b/src/sip/sipcall.cpp @@ -88,6 +88,8 @@ static constexpr auto MULTISTREAM_REQUIRED_VERSION_STR = "10.0.2"sv; static const std::vector<unsigned> MULTISTREAM_REQUIRED_VERSION = split_string_to_unsigned(MULTISTREAM_REQUIRED_VERSION_STR, '.'); +constexpr auto DUMMY_VIDEO_STR = "dummy video session"; + SIPCall::SIPCall(const std::shared_ptr<SIPAccountBase>& account, const std::string& callId, Call::CallType type, @@ -933,6 +935,16 @@ SIPCall::answerMediaChangeRequest(const std::vector<DRing::MediaMap>& mediaList) auto mediaAttrList = MediaAttribute::buildMediaAttributesList(mediaList, isSrtpEnabled()); + // TODO. is the right place? + // Disable video if disabled in the account. + if (not account->isVideoEnabled()) { + for (auto& mediaAttr : mediaAttrList) { + if (mediaAttr.type_ == MediaType::MEDIA_VIDEO) { + mediaAttr.enabled_ = false; + } + } + } + if (mediaAttrList.empty()) { JAMI_DBG("[call:%s] Media list size is empty. Ignoring the media change request", getCallId().c_str()); @@ -2218,7 +2230,7 @@ SIPCall::updateMediaStream(const MediaAttribute& newMediaAttr, size_t streamIdx) void SIPCall::updateAllMediaStreams(const std::vector<MediaAttribute>& mediaAttrList) { - JAMI_DBG("[call:%s] New local medias", getCallId().c_str()); + JAMI_DBG("[call:%s] New local media", getCallId().c_str()); unsigned idx = 0; for (auto const& newMediaAttr : mediaAttrList) { @@ -2284,6 +2296,26 @@ SIPCall::requestMediaChange(const std::vector<DRing::MediaMap>& mediaList) { auto mediaAttrList = MediaAttribute::buildMediaAttributesList(mediaList, isSrtpEnabled()); + // TODO. is the right place? + // Disable video if disabled in the account. + auto account = getSIPAccount(); + if (not account) { + JAMI_ERR("[call:%s] No account detected", getCallId().c_str()); + return false; + } + if (not account->isVideoEnabled()) { + for (auto& mediaAttr : mediaAttrList) { + if (mediaAttr.type_ == MediaType::MEDIA_VIDEO) { + // This an API misuse. The new medialist should not contain video + // if it was disabled in the account settings. + JAMI_ERR("[call:%s] New media has video, but it's disabled in the account. " + "Ignoring the change request!", + getCallId().c_str()); + return false; + } + } + } + // If the peer does not support multi-stream and the size of the new // media list is different from the current media list, the media // change request will be ignored. @@ -2440,6 +2472,59 @@ SIPCall::onIceNegoSucceed() startAllMedia(); } +bool +SIPCall::checkMediaChangeRequest(const std::vector<DRing::MediaMap>& remoteMediaList) +{ + // The current media is considered to have changed if one of the + // following condtions is true: + // + // - the number of media changed + // - the type of one of the media changed (unlikely) + // - one of the media was enabled/disabled + + JAMI_DBG("[call:%s] Received a media change request", getCallId().c_str()); + + auto remoteMediaAtrrList = MediaAttribute::buildMediaAttributesList(remoteMediaList, + isSrtpEnabled()); + if (remoteMediaAtrrList.size() != rtpStreams_.size()) + return true; + + for (size_t i = 0; i < rtpStreams_.size(); i++) { + if (remoteMediaAtrrList[i].type_ != rtpStreams_[i].mediaAttribute_->type_) + return true; + if (remoteMediaAtrrList[i].enabled_ != rtpStreams_[i].mediaAttribute_->enabled_) + return true; + } + + return false; +} + +void +SIPCall::handleMediaChangeRequest(const std::vector<DRing::MediaMap>& remoteMediaList) +{ + JAMI_DBG("[call:%s] Handling media change request", getCallId().c_str()); + + auto account = getAccount().lock(); + if (not account) { + JAMI_ERR("No account detected"); + return; + } + + // If multi-stream is supported and the offered media differ from + // the current media, the request is reported to the client to be + // processed. Otherwise, we answer with the current local media. + + if (account->isMultiStreamEnabled() and checkMediaChangeRequest(remoteMediaList)) { + // Report the media change request. + emitSignal<DRing::CallSignal::MediaChangeRequested>(getAccountId(), + getCallId(), + remoteMediaList); + } else { + auto localMediaList = MediaAttribute::mediaAttributesToMediaMaps(getMediaAttributeList()); + answerMediaChangeRequest(localMediaList); + } +} + pj_status_t SIPCall::onReceiveReinvite(const pjmedia_sdp_session* offer, pjsip_rx_data* rdata) { @@ -2476,20 +2561,17 @@ SIPCall::onReceiveReinvite(const pjmedia_sdp_session* offer, pjsip_rx_data* rdat pjsip_tx_data* tdata = nullptr; if (pjsip_inv_initial_answer(inviteSession_.get(), rdata, PJSIP_SC_TRYING, NULL, NULL, &tdata) != PJ_SUCCESS) { - JAMI_ERR("Could not create answer TRYING"); + JAMI_ERR("[call:%s] Could not create answer TRYING", getCallId().c_str()); return res; } // Report the change request. auto const& remoteMediaList = MediaAttribute::mediaAttributesToMediaMaps(mediaAttrList); - // TODO_MC. Validate this assessment. - // Report re-invites only if the number of media changed, otherwise answer - // using the current local attributes. - if (acc->isMultiStreamEnabled() and remoteMediaList.size() != rtpStreams_.size()) { - Manager::instance().mediaChangeRequested(getCallId(), getAccountId(), remoteMediaList); + + if (auto conf = Manager::instance().getConferenceFromCallID(getCallId())) { + conf->handleMediaChangeRequest(shared_from_this(), remoteMediaList); } else { - auto localMediaList = MediaAttribute::mediaAttributesToMediaMaps(getMediaAttributeList()); - answerMediaChangeRequest(localMediaList); + handleMediaChangeRequest(remoteMediaList); } return res; @@ -2719,10 +2801,12 @@ SIPCall::getDetails() const void SIPCall::enterConference(const std::string& confId) { + JAMI_DBG("[call:%s] Entering conference [%s]", getCallId().c_str(), confId.c_str()); + #ifdef ENABLE_VIDEO auto conf = Manager::instance().getConferenceFromID(confId); if (conf == nullptr) { - JAMI_ERR("Unknown conference [%s]", confId.c_str()); + JAMI_ERR("[call:%s] Unknown conference [%s]", getCallId().c_str(), confId.c_str()); return; } @@ -2745,6 +2829,14 @@ SIPCall::enterConference(const std::string& confId) void SIPCall::exitConference() { + auto confId = getConfId(); + if (not confId.empty()) { + JAMI_DBG("[call:%s] Leaving conference [%s]", getCallId().c_str(), confId.c_str()); + } else { + JAMI_ERR("[call:%s] The call is not bound to any conference", getCallId().c_str()); + return; + } + auto const& audioRtp = getAudioRtp(); if (audioRtp && !isCaptureDeviceMuted(MediaType::MEDIA_AUDIO)) { auto& rbPool = Manager::instance().getRingBufferPool(); @@ -2773,20 +2865,49 @@ SIPCall::getReceiveVideoFrameActiveWriter() return {}; } -std::shared_ptr<video::VideoRtpSession> +bool SIPCall::addDummyVideoRtpSession() { #ifdef ENABLE_VIDEO - MediaAttribute mediaAttr(MediaType::MEDIA_VIDEO, true, true, false, "", "dummy video session"); + JAMI_DBG("[call:%s] Add dummy video stream", getCallId().c_str()); + + MediaAttribute mediaAttr(MediaType::MEDIA_VIDEO, + true, + true, + false, + "dummy source", + DUMMY_VIDEO_STR); + addMediaStream(mediaAttr); auto& stream = rtpStreams_.back(); createRtpSession(stream); - if (stream.rtpSession_) { - return std::dynamic_pointer_cast<video::VideoRtpSession>(stream.rtpSession_); - } + return stream.rtpSession_ != nullptr; #endif - return {}; + return false; +} + +void +SIPCall::removeDummyVideoRtpSessions() +{ + // It's not expected to have more than one dummy video stream, but + // check just in case. + auto removed = std::remove_if(rtpStreams_.begin(), + rtpStreams_.end(), + [](const RtpStream& stream) { + return stream.mediaAttribute_->label_ == DUMMY_VIDEO_STR; + }); + auto count = std::distance(removed, rtpStreams_.end()); + rtpStreams_.erase(removed, rtpStreams_.end()); + + if (count > 0) { + JAMI_DBG("[call:%s] Removed %lu dummy video stream(s)", getCallId().c_str(), count); + if (count > 1) { + JAMI_WARN("[call:%s] Expected to find 1 dummy video stream, found %lu", + getCallId().c_str(), + count); + } + } } void diff --git a/src/sip/sipcall.h b/src/sip/sipcall.h index 7170636b0d..cdcb6c3467 100644 --- a/src/sip/sipcall.h +++ b/src/sip/sipcall.h @@ -129,6 +129,8 @@ private: public: void answer() override; void answer(const std::vector<DRing::MediaMap>& mediaList) override; + bool checkMediaChangeRequest(const std::vector<DRing::MediaMap>& remoteMediaList) override; + void handleMediaChangeRequest(const std::vector<DRing::MediaMap>& remoteMediaList) override; void answerMediaChangeRequest(const std::vector<DRing::MediaMap>& mediaList) override; void hangup(int reason) override; void refuse() override; @@ -270,10 +272,12 @@ public: * Returns a pointer to the VideoRtp object */ std::shared_ptr<video::VideoRtpSession> getVideoRtp() const; - std::shared_ptr<video::VideoRtpSession> addDummyVideoRtpSession(); + bool addDummyVideoRtpSession() override; + void removeDummyVideoRtpSessions() override; #endif // Get the list of current RTP sessions std::vector<std::shared_ptr<RtpSession>> getRtpSessionList() const; + static size_t getActiveMediaStreamCount(const std::vector<MediaAttribute>& mediaAttrList); void setPeerRegisteredName(const std::string& name) { peerRegisteredName_ = name; } -- GitLab