Commit c3a19140 authored by Éloi Bail's avatar Éloi Bail Committed by Guillaume Roguez

daemon: implement media muting

Simple muting implementation per call and media, eg : audio, video.
When a media is muted, black frames are sent in case of video.
Null samples are sent in case of audio.

In order to enhance bandwidth saving, a re-invite could be done
in a next version to properly stop muted media processing.

Refs #66722
Refs #69846

Change-Id: I9f03a7de77a6df3a3396197b9d966004300ebfcc
Signed-off-by: Guillaume Roguez's avatarGuillaume Roguez <guillaume.roguez@savoirfairelinux.com>
parent 5090c00a
......@@ -99,6 +99,31 @@
<arg type="b" name="unHoldSucceeded" direction="out"/>
</method>
<method name="muteLocalMedia" tp:name-for-bindings="muteLocalMedia">
<arg type="s" name="callid" direction="in">
<tp:docstring>
The callid to mute
</tp:docstring>
</arg>
<arg type="s" name="mediaType" direction="in">
<tp:docstring>
The type of media (AUDIO or VIDEO)
</tp:docstring>
</arg>
<arg type="b" name="mute" direction="in">
<tp:docstring>
True to mute audio capture, false to unmute.
</tp:docstring>
</arg>
<arg type="b" name="res" direction="out">
<tp:docstring>
True if mute succeed. False otherwise
</tp:docstring>
</arg>
</method>
<method name="transfer" tp:name-for-bindings="transfer">
<tp:docstring>
Transfer a call to the given phone number.
......@@ -786,5 +811,16 @@
<arg type="b" name="peerHolding" />
</signal>
<signal name="audioMuted" tp:name-for-bindings="audioMuted">
<tp:added version="2.1.0"/>
<arg type="s" name="callID" />
<arg type="b" name="audioMuted" />
</signal>
<signal name="videoMuted" tp:name-for-bindings="videoMuted">
<tp:added version="2.1.0"/>
<arg type="s" name="callID" />
<arg type="b" name="videoMuted" />
</signal>
</interface>
</node>
......@@ -71,6 +71,13 @@ DBusCallManager::unhold(const std::string& callID) -> decltype(DRing::unhold(cal
return DRing::unhold(callID);
}
auto
DBusCallManager::muteLocalMedia(const std::string& callid, const std::string& mediaType, const bool& mute)
-> decltype(DRing::muteLocalMedia(callid, mediaType, mute))
{
return DRing::muteLocalMedia(callid, mediaType, mute);
}
auto
DBusCallManager::transfer(const std::string& callID, const std::string& to) -> decltype(DRing::transfer(callID, to))
{
......
......@@ -70,6 +70,7 @@ class DBusCallManager :
bool hangUp(const std::string& callID);
bool hold(const std::string& callID);
bool unhold(const std::string& callID);
bool muteLocalMedia(const std::string& callid, const std::string& mediaType, const bool& mute);
bool transfer(const std::string& callID, const std::string& to);
bool attendedTransfer(const std::string& transferID, const std::string& targetID);
std::map<std::string, std::string> getCallDetails(const std::string& callID);
......
......@@ -161,7 +161,8 @@ nobase_include_HEADERS= dring/dring.h \
dring/configurationmanager_interface.h \
dring/presencemanager_interface.h \
dring/account_const.h \
dring/call_const.h
dring/call_const.h \
dring/media_const.h
if RING_VIDEO
nobase_include_HEADERS+= \
......
......@@ -248,6 +248,8 @@ Call::getDetails() const
{DRing::Call::Details::CONF_ID, confID_},
{DRing::Call::Details::TIMESTAMP_START, ring::to_string(timestamp_start_)},
{DRing::Call::Details::ACCOUNTID, getAccountId()},
{DRing::Call::Details::AUDIO_MUTED, std::string(bool_to_str(isAudioMuted_))},
{DRing::Call::Details::VIDEO_MUTED, std::string(bool_to_str(isVideoMuted_))},
};
}
......
......@@ -279,6 +279,13 @@ class Call : public Recordable, public std::enable_shared_from_this<Call> {
*/
virtual void offhold() = 0;
/**
* mute/unmute a media of a call
* @param mediaType type of media
* @param isMuted true for muting, false for unmuting
*/
virtual void muteMedia(const std::string& mediaType, bool isMuted) = 0;
/**
* Peer Hung up a call
*/
......@@ -328,6 +335,9 @@ class Call : public Recordable, public std::enable_shared_from_this<Call> {
std::shared_ptr<IceTransport> iceTransport_ {};
bool isAudioMuted_{false};
bool isVideoMuted_{false};
private:
bool validTransition(CallState newState);
......@@ -375,6 +385,7 @@ class Call : public Recordable, public std::enable_shared_from_this<Call> {
time_t timestamp_start_ {0};
time_t timestamp_stop_ {0};
};
} // namespace ring
......
......@@ -109,6 +109,12 @@ unhold(const std::string& callID)
return ring::Manager::instance().offHoldCall(callID);
}
bool
muteLocalMedia(const std::string& callid, const std::string& mediaType, bool mute)
{
return ring::Manager::instance().muteMediaCall(callid, mediaType, mute);
}
bool
transfer(const std::string& callID, const std::string& to)
{
......
......@@ -61,6 +61,8 @@ getSignalHandlers()
exported_callback<DRing::CallSignal::ZrtpNegotiationFailed>(),
exported_callback<DRing::CallSignal::RtcpReportReceived>(),
exported_callback<DRing::CallSignal::PeerHold>(),
exported_callback<DRing::CallSignal::VideoMuted>(),
exported_callback<DRing::CallSignal::AudioMuted>(),
/* Configuration */
exported_callback<DRing::ConfigurationSignal::VolumeChanged>(),
......
......@@ -46,6 +46,8 @@ constexpr static char ACCOUNTID [] = "ACCOUNTID" ;
constexpr static char PEER_HOLDING [] = "PEER_HOLDING" ;
constexpr static char TLS_PEER_CERT [] = "TLS_PEER_CERT" ;
constexpr static char TLS_CIPHER [] = "TLS_CIPHER" ;
constexpr static char AUDIO_MUTED [] = "AUDIO_MUTED" ;
constexpr static char VIDEO_MUTED [] = "VIDEO_MUTED" ;
}
......
......@@ -53,6 +53,7 @@ bool accept(const std::string& callID);
bool hangUp(const std::string& callID);
bool hold(const std::string& callID);
bool unhold(const std::string& callID);
bool muteLocalMedia(const std::string& callid, const std::string& mediaType, bool mute);
bool transfer(const std::string& callID, const std::string& to);
bool attendedTransfer(const std::string& transferID, const std::string& targetID);
std::map<std::string, std::string> getCallDetails(const std::string& callID);
......@@ -202,6 +203,14 @@ struct CallSignal {
constexpr static const char* name = "PeerHold";
using cb_type = void(const std::string&, bool);
};
struct VideoMuted {
constexpr static const char* name = "VideoMuted";
using cb_type = void(const std::string&, bool);
};
struct AudioMuted {
constexpr static const char* name = "AudioMuted";
using cb_type = void(const std::string&, bool);
};
};
} // namespace DRing
......
/*
* Copyright (C) 2015 Savoir-Faire Linux Inc.
* Author: Eloi Bail <eloi.bail@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Additional permission under GNU GPL version 3 section 7:
*
* If you modify this program, or any covered work, by linking or
* combining it with the OpenSSL project's OpenSSL library (or a
* modified version of that library), containing parts covered by the
* terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
* grants you additional permission to convey the resulting work.
* Corresponding Source for a non-source form of such a combination
* shall include the source code for the parts of OpenSSL used as well
* as that of the covered work.
*/
#ifndef DRING_MEDIA_H
#define DRING_MEDIA_H
namespace DRing {
namespace Media {
namespace Details {
constexpr static char MEDIA_TYPE_AUDIO[] = "MEDIA_TYPE_AUDIO";
constexpr static char MEDIA_TYPE_VIDEO[] = "MEDIA_TYPE_VIDEO";
}
} //namespace DRing::Media
} //namespace DRing
#endif
......@@ -131,6 +131,9 @@ class IAXCall : public Call
void offhold();
//TODO: implement mute for IAX
void muteMedia(const std::string& mediaType, bool isMuted) {}
void peerHungup();
void carryingDTMFdigits(char code);
......
......@@ -684,6 +684,19 @@ ManagerImpl::offHoldCall(const std::string& callId)
return result;
}
bool
ManagerImpl::muteMediaCall(const std::string& callId, const std::string& mediaType, bool is_muted)
{
if (auto call = getCallFromCallID(callId)) {
call->muteMedia(mediaType, is_muted);
return true;
} else {
RING_DBG("CallID %s doesn't exist in call muting", callId.c_str());
return false;
}
}
//THREAD=Main
bool
ManagerImpl::transferCall(const std::string& callId, const std::string& to)
......
......@@ -201,6 +201,15 @@ class ManagerImpl {
*/
bool offHoldCall(const std::string& id);
/**
* Functions which occur with a user's action
* Put the media of a call on mute or unmute
* @param callID The call identifier
* @param mediaType The media type; eg : AUDIO or VIDEO
* @param is_muted true to mute, false to unmute
*/
bool muteMediaCall(const std::string& callId, const std::string& mediaType, bool is_muted);
/**
* Functions which occur with a user's action
* Transfer the call
......
......@@ -59,6 +59,8 @@ class AudioSender {
SocketPair& socketPair);
~AudioSender();
void setMuted(bool isMuted);
private:
NON_COPYABLE(AudioSender);
......@@ -178,6 +180,12 @@ AudioSender::process()
RING_ERR("encoding failed");
}
}
void
AudioSender::setMuted(bool isMuted)
{
audioEncoder_->setMuted(isMuted);
}
class AudioReceiveThread
{
......@@ -461,5 +469,12 @@ AudioRtpSession::stop()
sender_.reset();
socketPair_.reset();
}
void
AudioRtpSession::setMuted(bool isMuted)
{
std::lock_guard<std::recursive_mutex> lock(mutex_);
if (sender_)
sender_->setMuted(isMuted);
}
} // namespace ring
......@@ -55,6 +55,7 @@ class AudioRtpSession : public RtpSession {
void start(std::unique_ptr<IceSocket> rtp_sock,
std::unique_ptr<IceSocket> rtcp_sock);
void stop();
void setMuted(bool isMuted);
private:
void startSender();
......
......@@ -30,6 +30,8 @@
#include "audiobuffer.h"
#include "logger.h"
#include <string.h>
#include <cstring> // memset
namespace ring {
......@@ -172,6 +174,13 @@ size_t AudioBuffer::interleave(AudioSample* out) const
return frames() * channels();
}
size_t AudioBuffer::fillWithZero(AudioSample* out) const
{
const auto n = channels() * frames();
std::memset(out, 0, n * sizeof(*out));
return n;
}
size_t AudioBuffer::interleave(std::vector<AudioSample>& out) const
{
out.resize(capacity());
......
......@@ -261,6 +261,14 @@ class AudioBuffer {
*/
size_t interleave(AudioSample* out) const;
/**
* Write null data (silence) to the out buffer (fixed-point 16-bits).
* The out buffer must be at least of size capacity()*sizeof(AudioSample) bytes.
*
* @returns Number of samples writen.
*/
size_t fillWithZero(AudioSample* out) const;
/**
* Write interleaved multichannel data to the out buffer (fixed-point 16-bits).
* The out buffer is resized to hold the full content of this buffer.
......
......@@ -234,6 +234,7 @@ MediaEncoder::encode(VideoFrame& input, bool is_keyframe,
* keeping also the input aspect ratio.
*/
yuv422_clear_to_black(scaledFrame_); // to fill blank space left by the "keep aspect"
scaler_.scale_with_aspect(input, scaledFrame_);
auto frame = scaledFrame_.pointer();
......@@ -336,7 +337,13 @@ int MediaEncoder::encode_audio(const AudioBuffer &buffer)
AudioSample *offset_ptr = sample_data;
int nb_frames = buffer.frames();
buffer.interleave(sample_data);
if (not is_muted) {
//only fill buffer with samples if not muted
buffer.interleave(sample_data);
} else {
//otherwise filll buffer with zero
buffer.fillWithZero(sample_data);
}
const auto layout = buffer.channels() == 2 ? AV_CH_LAYOUT_STEREO : AV_CH_LAYOUT_MONO;
const auto sample_rate = buffer.getSampleRate();
......@@ -377,6 +384,7 @@ int MediaEncoder::encode_audio(const AudioBuffer &buffer)
av_frame_free(&frame);
return -1;
}
nb_frames -= frame->nb_samples;
offset_ptr += frame->nb_samples * buffer.channels();
......@@ -628,4 +636,10 @@ void MediaEncoder::extractProfileLevelID(const std::string &parameters,
RING_DBG("Using profile %x and level %d", ctx->profile, ctx->level);
}
void
MediaEncoder::setMuted(bool isMuted)
{
is_muted = isMuted;
}
} // namespace ring
......@@ -90,6 +90,8 @@ public:
int getWidth() const { return device_.width; }
int getHeight() const { return device_.height; }
void setMuted(bool isMuted);
private:
NON_COPYABLE(MediaEncoder);
void setOptions(const MediaDescription& args);
......@@ -116,6 +118,7 @@ private:
uint8_t *encoderBuffer_ = nullptr;
int encoderBufferSize_ = 0;
#endif
bool is_muted = false;
protected:
AVDictionary *options_ = nullptr;
......
......@@ -78,4 +78,9 @@ void VideoSender::forceKeyFrame()
++forceKeyFrame_;
}
void VideoSender::setMuted(bool isMuted)
{
videoEncoder_->setMuted(isMuted);
}
}} // namespace ring::video
......@@ -63,6 +63,8 @@ public:
void update(Observable<std::shared_ptr<VideoFrame> >* obs,
std::shared_ptr<VideoFrame> &);
void setMuted(bool isMuted);
private:
NON_COPYABLE(VideoSender);
......
......@@ -54,6 +54,7 @@
#endif
#include "dring/call_const.h"
#include "dring/media_const.h"
#include "client/ring_signal.h"
#ifdef RING_VIDEO
......@@ -126,6 +127,7 @@ SIPCall::SIPCall(SIPAccountBase& account, const std::string& id, Call::CallType
#ifdef RING_VIDEO
// The ID is used to associate video streams to calls
, videortp_(id, getVideoSettings())
, videoInput_(videoManager.videoDeviceMonitor.getMRLForDefaultDevice())
#endif
, sdp_(new Sdp(id))
{
......@@ -827,8 +829,6 @@ SIPCall::startAllMedia()
#ifdef RING_VIDEO
if (local.type == MEDIA_VIDEO) {
if (videoInput_.empty())
videoInput_ = videoManager.videoDeviceMonitor.getMRLForDefaultDevice();
videortp_.switchInput(videoInput_);
}
#endif
......@@ -856,6 +856,23 @@ SIPCall::stopAllMedia()
#endif
}
void
SIPCall::muteMedia(const std::string& mediaType, bool isMuted)
{
if (mediaType.compare(DRing::Media::Details::MEDIA_TYPE_VIDEO) == 0) {
RING_WARN("video muting %s", bool_to_str(isMuted));
isVideoMuted_ = isMuted;
videoInput_ = isVideoMuted_ ? "" : videoManager.videoDeviceMonitor.getMRLForDefaultDevice();
DRing::switchInput(getCallId(), videoInput_);
emitSignal<DRing::CallSignal::VideoMuted>(getCallId(), isVideoMuted_);
}else if (mediaType.compare(DRing::Media::Details::MEDIA_TYPE_AUDIO) == 0) {
RING_WARN("audio muting %s", bool_to_str(isMuted));
isAudioMuted_ = isMuted;
avformatrtp_->setMuted(isAudioMuted_);
emitSignal<DRing::CallSignal::AudioMuted>(getCallId(), isAudioMuted_);
}
}
void
SIPCall::onMediaUpdate()
{
......
......@@ -207,6 +207,8 @@ class SIPCall : public Call
void openPortsUPnP();
void muteMedia(const std::string& mediaType, bool isMuted);
virtual std::map<std::string, std::string> getDetails() const;
private:
......
......@@ -44,6 +44,12 @@ namespace ring {
constexpr static const char* TRUE_STR = "true";
constexpr static const char* FALSE_STR = "false";
constexpr static const char*
bool_to_str(bool b) noexcept
{
return b ? TRUE_STR : FALSE_STR;
}
#ifdef __ANDROID__
template <typename T>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment