diff --git a/bin/dbus/cx.ring.Ring.ConfigurationManager.xml b/bin/dbus/cx.ring.Ring.ConfigurationManager.xml
index 517157d08..2de4955e3 100644
--- a/bin/dbus/cx.ring.Ring.ConfigurationManager.xml
+++ b/bin/dbus/cx.ring.Ring.ConfigurationManager.xml
@@ -867,6 +867,37 @@
+
+
+ Gets whether or not an audio meter is active for id. If id is empty, returns whether or not there is at least one audio meter active.
+
+ Ring buffer id.
+
+
+ If the audio meter is active for the passed in id.
+
+
+
+
+ Sets whether or not the audio meter should be active for id. If id is empty, applies to all ring buffers.
+
+ Ring buffer id.
+
+
+ True to enabled the audio meter, false to disable.
+
+
+
+
+ Signal containing volume level.
+
+ Ring buffer id.
+
+
+ RMS value for the volume. Conversion to dB can be done with dB=20*log10(level). Level is between 0 and 1.
+
+
+
diff --git a/bin/dbus/cx.ring.Ring.VideoManager.xml b/bin/dbus/cx.ring.Ring.VideoManager.xml
index c1b648a84..c5a79d3ab 100644
--- a/bin/dbus/cx.ring.Ring.VideoManager.xml
+++ b/bin/dbus/cx.ring.Ring.VideoManager.xml
@@ -72,6 +72,13 @@
+
+ Starts the audio layer stream, so the audio device can be read.
+
+
+
+
+
diff --git a/bin/dbus/dbusclient.cpp b/bin/dbus/dbusclient.cpp
index d412fe375..ac97d336b 100644
--- a/bin/dbus/dbusclient.cpp
+++ b/bin/dbus/dbusclient.cpp
@@ -198,8 +198,10 @@ DBusClient::initLibrary(int flags)
exportable_callback(bind(&DBusPresenceManager::subscriptionStateChanged, presM, _1, _2, _3)),
};
+ // Audio event handlers
const std::map audioEvHandlers = {
exportable_callback(bind(&DBusConfigurationManager::audioDeviceEvent, confM)),
+ exportable_callback(bind(&DBusConfigurationManager::audioMeter, confM, _1, _2)),
};
const std::map dataXferEvHandlers = {
diff --git a/bin/dbus/dbusconfigurationmanager.cpp b/bin/dbus/dbusconfigurationmanager.cpp
index 248e94198..387b497cb 100644
--- a/bin/dbus/dbusconfigurationmanager.cpp
+++ b/bin/dbus/dbusconfigurationmanager.cpp
@@ -711,3 +711,15 @@ DBusConfigurationManager::cancelDataTransfer(const uint64_t& id)
{
return uint32_t(DRing::cancelDataTransfer(id));
}
+
+bool
+DBusConfigurationManager::isAudioMeterActive(const std::string& id)
+{
+ return DRing::isAudioMeterActive(id);
+}
+
+void
+DBusConfigurationManager::setAudioMeterState(const std::string& id, const bool& state)
+{
+ return DRing::setAudioMeterState(id, state);
+}
diff --git a/bin/dbus/dbusconfigurationmanager.h b/bin/dbus/dbusconfigurationmanager.h
index b12f7e0f5..e7097dbe0 100644
--- a/bin/dbus/dbusconfigurationmanager.h
+++ b/bin/dbus/dbusconfigurationmanager.h
@@ -168,6 +168,9 @@ class DRING_PUBLIC DBusConfigurationManager :
void dataTransferBytesProgress(const uint64_t& id, uint32_t& error, int64_t& total, int64_t& progress);
uint32_t acceptFileTransfer(const uint64_t& id, const std::string& file_path, const int64_t& offset);
uint32_t cancelDataTransfer(const uint64_t& id);
+
+ bool isAudioMeterActive(const std::string& id);
+ void setAudioMeterState(const std::string& id, const bool& state);
};
#endif // __RING_DBUSCONFIGURATIONMANAGER_H__
diff --git a/bin/dbus/dbusvideomanager.cpp b/bin/dbus/dbusvideomanager.cpp
index 8c8ee6d5d..e867e5c1a 100644
--- a/bin/dbus/dbusvideomanager.cpp
+++ b/bin/dbus/dbusvideomanager.cpp
@@ -73,6 +73,18 @@ DBusVideoManager::stopCamera()
DRing::stopCamera();
}
+void
+DBusVideoManager::startAudioDevice()
+{
+ DRing::startAudioDevice();
+}
+
+void
+DBusVideoManager::stopAudioDevice()
+{
+ DRing::stopAudioDevice();
+}
+
auto
DBusVideoManager::switchInput(const std::string& resource) -> decltype(DRing::switchInput(resource))
{
diff --git a/bin/dbus/dbusvideomanager.h b/bin/dbus/dbusvideomanager.h
index 147114315..c0d0427d3 100644
--- a/bin/dbus/dbusvideomanager.h
+++ b/bin/dbus/dbusvideomanager.h
@@ -59,6 +59,8 @@ class DRING_PUBLIC DBusVideoManager :
std::string getDefaultDevice();
void startCamera();
void stopCamera();
+ void startAudioDevice();
+ void stopAudioDevice();
bool switchInput(const std::string& resource);
bool hasCameraStarted();
bool getDecodingAccelerated();
diff --git a/bin/jni/configurationmanager.i b/bin/jni/configurationmanager.i
index 71bb314e4..dcc5ff068 100644
--- a/bin/jni/configurationmanager.i
+++ b/bin/jni/configurationmanager.i
@@ -59,6 +59,8 @@ public:
virtual void hardwareDecodingChanged(bool /*state*/){}
virtual void hardwareEncodingChanged(bool /*state*/){}
+
+ virtual void audioMeter(const std::string& /*id*/, float /*level*/){}
};
%}
@@ -215,6 +217,8 @@ void enableProxyClient(const std::string& accountID, bool enable);
void setPushNotificationToken(const std::string& pushDeviceToken);
void pushNotificationReceived(const std::string& from, const std::map& data);
+bool isAudioMeterActive(const std::string& id);
+void setAudioMeterState(const std::string& id, bool state);
}
class ConfigurationCallback {
@@ -253,4 +257,6 @@ public:
virtual void hardwareDecodingChanged(bool /*state*/){}
virtual void hardwareEncodingChanged(bool /*state*/){}
+
+ virtual void audioMeter(const std::string& /*id*/, float /*level*/){}
};
diff --git a/bin/jni/videomanager.i b/bin/jni/videomanager.i
index 06f63f462..a84d6c83f 100644
--- a/bin/jni/videomanager.i
+++ b/bin/jni/videomanager.i
@@ -381,6 +381,8 @@ std::string getDefaultDevice();
void startCamera();
void stopCamera();
bool hasCameraStarted();
+void startAudioDevice();
+void stopAudioDevice();
bool switchInput(const std::string& resource);
bool switchToCamera();
std::map getSettings(const std::string& name);
diff --git a/bin/nodejs/configurationmanager.i b/bin/nodejs/configurationmanager.i
index 100fa81a4..17deafcf1 100644
--- a/bin/nodejs/configurationmanager.i
+++ b/bin/nodejs/configurationmanager.i
@@ -57,6 +57,8 @@ public:
virtual void hardwareDecodingChanged(bool /*state*/){}
virtual void hardwareEncodingChanged(bool /*state*/){}
};
+
+ virtual void audioMeter(const std::string& /*id*/, float /*level*/){}
%}
%feature("director") ConfigurationCallback;
@@ -206,6 +208,9 @@ int exportAccounts(std::vector accountIDs, std::string toDir, std::
int importAccounts(std::string archivePath, std::string password);
void connectivityChanged();
+
+bool isAudioMeterActive(const std::string& id);
+void setAudioMeterState(const std::string& id, bool state);
}
class ConfigurationCallback {
@@ -242,4 +247,5 @@ public:
virtual void hardwareDecodingChanged(bool /*state*/){}
virtual void hardwareEncodingChanged(bool /*state*/){}
+ virtual void audioMeter(const std::string& /*id*/, float /*level*/){}
};
diff --git a/bin/nodejs/videomanager.i b/bin/nodejs/videomanager.i
index 5e61935fb..ba119b4d9 100644
--- a/bin/nodejs/videomanager.i
+++ b/bin/nodejs/videomanager.i
@@ -51,6 +51,8 @@ std::string getDefaultDevice();
void startCamera();
void stopCamera();
bool hasCameraStarted();
+void startAudioDevice();
+void stopAudioDevice();
bool switchInput(const std::string& resource);
bool switchToCamera();
std::map getSettings(const std::string& name);
diff --git a/configure.ac b/configure.ac
index 6781decec..28a045b2a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2,7 +2,7 @@ dnl Jami - configure.ac for automake 1.9 and autoconf 2.59
dnl Process this file with autoconf to produce a configure script.
AC_PREREQ([2.65])
-AC_INIT([Ring Daemon],[7.3.0],[ring@gnu.org],[ring])
+AC_INIT([Ring Daemon],[7.4.0],[ring@gnu.org],[ring])
AC_COPYRIGHT([[Copyright (c) Savoir-faire Linux 2004-2018]])
AC_REVISION([$Revision$])
diff --git a/src/client/configurationmanager.cpp b/src/client/configurationmanager.cpp
index c567bf8a8..e1257106c 100644
--- a/src/client/configurationmanager.cpp
+++ b/src/client/configurationmanager.cpp
@@ -41,6 +41,7 @@
#include "account_const.h"
#include "client/ring_signal.h"
#include "upnp/upnp_context.h"
+#include "audio/ringbufferpool.h"
#ifdef __APPLE__
#include
@@ -969,4 +970,16 @@ void pushNotificationReceived(const std::string& from, const std::map(),
+ exported_callback(),
/* DataTransfer */
exported_callback(),
diff --git a/src/client/videomanager.cpp b/src/client/videomanager.cpp
index c26f07731..b99ae6378 100644
--- a/src/client/videomanager.cpp
+++ b/src/client/videomanager.cpp
@@ -154,6 +154,38 @@ AudioFrame::mix(const AudioFrame& frame)
}
}
+float
+AudioFrame::calcRMS() const
+{
+ double rms = 0.0;
+ auto fmt = static_cast(frame_->format);
+ bool planar = av_sample_fmt_is_planar(fmt);
+ int perChannel = planar ? frame_->nb_samples : frame_->nb_samples * frame_->channels;
+ int channels = planar ? frame_->channels : 1;
+ if (fmt == AV_SAMPLE_FMT_S16 || fmt == AV_SAMPLE_FMT_S16P) {
+ for (int c = 0; c < channels; ++c) {
+ auto buf = reinterpret_cast(frame_->extended_data[c]);
+ for (int i = 0; i < perChannel; ++i) {
+ auto sample = buf[i] * 0.000030517578125f;
+ rms += sample * sample;
+ }
+ }
+ } else if (fmt == AV_SAMPLE_FMT_FLT || fmt == AV_SAMPLE_FMT_FLTP) {
+ for (int c = 0; c < channels; ++c) {
+ auto buf = reinterpret_cast(frame_->extended_data[c]);
+ for (int i = 0; i < perChannel; ++i) {
+ rms += buf[i] * buf[i];
+ }
+ }
+ } else {
+ // Should not happen
+ RING_ERR() << "Unsupported format for getting volume level: " << av_get_sample_fmt_name(fmt);
+ return 0.0;
+ }
+ // divide by the number of multi-byte samples
+ return sqrt(rms / (frame_->nb_samples * frame_->channels));
+}
+
VideoFrame::~VideoFrame()
{
if (releaseBufferCb_)
@@ -359,6 +391,21 @@ stopCamera()
ring::Manager::instance().getVideoManager().videoPreview.reset();
}
+void
+startAudioDevice()
+{
+ ring::Manager::instance().initAudioDriver();
+ ring::Manager::instance().startAudioDriverStream();
+ ring::Manager::instance().getVideoManager().audioPreview = ring::getAudioInput(ring::RingBufferPool::DEFAULT_ID);
+}
+
+void
+stopAudioDevice()
+{
+ ring::Manager::instance().getVideoManager().audioPreview.reset();
+ ring::Manager::instance().checkAudio(); // stops audio layer if no calls
+}
+
std::string
startLocalRecorder(const bool& audioOnly, const std::string& filepath)
{
diff --git a/src/client/videomanager.h b/src/client/videomanager.h
index fe65f591e..98e8912b8 100644
--- a/src/client/videomanager.h
+++ b/src/client/videomanager.h
@@ -59,6 +59,7 @@ struct VideoManager
*/
std::map> audioInputs;
std::mutex audioMutex;
+ std::shared_ptr audioPreview;
};
std::shared_ptr getVideoCamera();
diff --git a/src/dring/configurationmanager_interface.h b/src/dring/configurationmanager_interface.h
index e0cca93be..1eabe1bcb 100644
--- a/src/dring/configurationmanager_interface.h
+++ b/src/dring/configurationmanager_interface.h
@@ -215,11 +215,30 @@ DRING_PUBLIC void setPushNotificationToken(const std::string& pushDeviceToken);
*/
DRING_PUBLIC void pushNotificationReceived(const std::string& from, const std::map& data);
+/**
+ * Returns whether or not the audio meter is enabled for ring buffer @id.
+ *
+ * NOTE If @id is empty, returns true if at least 1 audio meter is enabled.
+ */
+DRING_PUBLIC bool isAudioMeterActive(const std::string& id);
+
+/**
+ * Enables/disables an audio meter for the specified @id.
+ *
+ * NOTE If @id is empty, applies to all ring buffers.
+ */
+DRING_PUBLIC void setAudioMeterState(const std::string& id, bool state);
+
struct DRING_PUBLIC AudioSignal {
struct DRING_PUBLIC DeviceEvent {
constexpr static const char* name = "audioDeviceEvent";
using cb_type = void(void);
};
+ // Linear audio level (between 0 and 1). To get level in dB: dB=20*log10(level)
+ struct DRING_PUBLIC AudioMeter {
+ constexpr static const char* name = "AudioMeter";
+ using cb_type = void(const std::string& id, float level);
+ };
};
// Configuration signal type definitions
diff --git a/src/dring/videomanager_interface.h b/src/dring/videomanager_interface.h
index e53c01135..44bb0d144 100644
--- a/src/dring/videomanager_interface.h
+++ b/src/dring/videomanager_interface.h
@@ -102,6 +102,7 @@ public:
AudioFrame(const ring::AudioFormat& format, size_t nb_samples = 0);
~AudioFrame() {};
void mix(const AudioFrame& o);
+ float calcRMS() const;
private:
void setFormat(const ring::AudioFormat& format);
@@ -165,6 +166,8 @@ DRING_PUBLIC std::string getCurrentCodecName(const std::string& callID);
DRING_PUBLIC void startCamera();
DRING_PUBLIC void stopCamera();
DRING_PUBLIC bool hasCameraStarted();
+DRING_PUBLIC void startAudioDevice();
+DRING_PUBLIC void stopAudioDevice();
DRING_PUBLIC bool switchInput(const std::string& resource);
DRING_PUBLIC bool switchToCamera();
DRING_PUBLIC void registerSinkTarget(const std::string& sinkId, const SinkTarget& target);
diff --git a/src/media/audio/ringbuffer.cpp b/src/media/audio/ringbuffer.cpp
index f9374ba48..9a10c1dd1 100644
--- a/src/media/audio/ringbuffer.cpp
+++ b/src/media/audio/ringbuffer.cpp
@@ -25,7 +25,7 @@
#include "ringbuffer.h"
#include "logger.h"
-
+#include "client/ring_signal.h"
#include "media_buffer.h"
#include "libav_deps.h"
@@ -39,6 +39,8 @@ namespace ring {
// corresponds to 160 ms (about 5 rtp packets)
static const size_t MIN_BUFFER_SIZE = 1024;
+static constexpr const int RMS_SIGNAL_INTERVAL = 5;
+
RingBuffer::RingBuffer(const std::string& rbuf_id, size_t /*size*/, AudioFormat format)
: id(rbuf_id)
, endPos_(0)
@@ -175,6 +177,16 @@ void RingBuffer::putToBuffer(std::shared_ptr&& data)
endPos_ = pos;
+ if (rmsSignal_) {
+ ++rmsFrameCount_;
+ rmsLevel_ += newBuf->calcRMS();
+ if (rmsFrameCount_ == RMS_SIGNAL_INTERVAL) {
+ emitSignal(id, rmsLevel_ / RMS_SIGNAL_INTERVAL);
+ rmsLevel_ = 0;
+ rmsFrameCount_ = 0;
+ }
+ }
+
for (auto& offset : readoffsets_) {
if (offset.second.callback)
offset.second.callback(newBuf);
diff --git a/src/media/audio/ringbuffer.h b/src/media/audio/ringbuffer.h
index 9a1593477..9eba33d64 100644
--- a/src/media/audio/ringbuffer.h
+++ b/src/media/audio/ringbuffer.h
@@ -28,6 +28,7 @@
#include "audio_frame_resizer.h"
#include "resampler.h"
+#include
#include
#include
#include
@@ -144,6 +145,9 @@ public:
*/
void debug();
+ bool isAudioMeterActive() const { return rmsSignal_; }
+ void setAudioMeterState(bool state) { rmsSignal_ = state; }
+
private:
struct ReadOffset {
size_t offset;
@@ -197,6 +201,10 @@ private:
Resampler resampler_;
AudioFrameResizer resizer_;
+
+ std::atomic_bool rmsSignal_ {false};
+ double rmsLevel_ {0};
+ int rmsFrameCount_ {0};
};
} // namespace ring
diff --git a/src/media/audio/ringbufferpool.cpp b/src/media/audio/ringbufferpool.cpp
index 01086b47b..bf7630c25 100644
--- a/src/media/audio/ringbufferpool.cpp
+++ b/src/media/audio/ringbufferpool.cpp
@@ -404,4 +404,41 @@ RingBufferPool::flushAllBuffers()
}
}
+bool
+RingBufferPool::isAudioMeterActive(const std::string& id)
+{
+ std::lock_guard lk(stateLock_);
+ if (!id.empty()) {
+ if (auto rb = getRingBuffer(id)) {
+ return rb->isAudioMeterActive();
+ }
+ } else {
+ for (auto item = ringBufferMap_.begin(); item != ringBufferMap_.end(); ++item) {
+ if (const auto rb = item->second.lock()) {
+ if (rb->isAudioMeterActive()) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+void
+RingBufferPool::setAudioMeterState(const std::string& id, bool state)
+{
+ std::lock_guard lk(stateLock_);
+ if (!id.empty()) {
+ if (auto rb = getRingBuffer(id)) {
+ rb->setAudioMeterState(state);
+ }
+ } else {
+ for (auto item = ringBufferMap_.begin(); item != ringBufferMap_.end(); ++item) {
+ if (const auto rb = item->second.lock()) {
+ rb->setAudioMeterState(state);
+ }
+ }
+ }
+}
+
} // namespace ring
diff --git a/src/media/audio/ringbufferpool.h b/src/media/audio/ringbufferpool.h
index f4fc5e91e..4234406f7 100644
--- a/src/media/audio/ringbufferpool.h
+++ b/src/media/audio/ringbufferpool.h
@@ -117,6 +117,9 @@ public:
*/
std::shared_ptr getRingBuffer(const std::string& id) const;
+ bool isAudioMeterActive(const std::string& id);
+ void setAudioMeterState(const std::string& id, bool state);
+
private:
NON_COPYABLE(RingBufferPool);