From 0dc32710e26841c34f432961e57b1ca8db8ea157 Mon Sep 17 00:00:00 2001
From: Gasuleg <o.gregoire07@gmail.com>
Date: Thu, 19 May 2016 13:46:48 -0400
Subject: [PATCH] daemon: add SmartInfo

This feature provides relevant advanced information during a call.
A user starts it with a refresh time and listens on a callback signal
to get the updated information. This information is presented in a form
of a map containing Call ID, codecs, framerate and resolution.

The public API has:
- startSmartInfo(<timeMS>)
- stopSmartInfo()
- CallSignal::SmartInfo(map) -- callback signal

Change-Id: Iaafc12b5b53aee8ad008b93536f72a4152c4ee02
Reviewed-by: Guillaume Roguez <guillaume.roguez@savoirfairelinux.com>
---
 bin/dbus/cx.ring.Ring.CallManager.xml    |  26 ++++-
 bin/dbus/dbuscallmanager.cpp             |  12 ++
 bin/dbus/dbuscallmanager.h               |   2 +
 bin/dbus/dbusclient.cpp                  |   3 +-
 src/Makefile.am                          |   4 +-
 src/client/callmanager.cpp               |  14 +++
 src/client/ring_signal.cpp               |   1 +
 src/dring/callmanager_interface.h        |  10 +-
 src/manager.h                            |  11 +-
 src/media/audio/audio_rtp_session.cpp    |   4 +
 src/media/media_decoder.cpp              |   3 +
 src/media/media_decoder.h                |   2 +
 src/media/media_encoder.cpp              |   6 +
 src/media/media_encoder.h                |   1 +
 src/media/video/sinkclient.cpp           |   8 +-
 src/media/video/sinkclient.h             |   2 +
 src/media/video/video_receive_thread.cpp |   4 +
 src/media/video/video_sender.cpp         |   4 +
 src/smartools.cpp                        | 139 +++++++++++++++++++++++
 src/smartools.h                          |  49 ++++++++
 20 files changed, 295 insertions(+), 10 deletions(-)
 create mode 100644 src/smartools.cpp
 create mode 100644 src/smartools.h

diff --git a/bin/dbus/cx.ring.Ring.CallManager.xml b/bin/dbus/cx.ring.Ring.CallManager.xml
index 526ace11aa..b5cebc337b 100644
--- a/bin/dbus/cx.ring.Ring.CallManager.xml
+++ b/bin/dbus/cx.ring.Ring.CallManager.xml
@@ -35,9 +35,16 @@
               </tp:docstring>
             </arg>
             <arg type="b" name="refuseSucceeded" direction="out"/>
-
         </method>
 
+        <signal name="SmartInfo" tp:name-for-bindings="SmartInfo">
+            <tp:docstring>
+              Once enabled using the startSmartInfo method, this signal is emitted every refreshTimeMS
+            </tp:docstring>
+             <annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="MapStringString"/>
+             <arg type="a{ss}" name="info" direction="out" />
+       </signal>
+
         <method name="accept" tp:name-for-bindings="accept">
             <tp:docstring>
               Answer an incoming call. Automatically puts the current call on HOLD.
@@ -344,6 +351,23 @@
             </arg>
         </method>
 
+        <method name="startSmartInfo" tp:name-for-bindings="startSmartInfo">
+            <tp:docstring>
+              Start sending the signal smartInfo
+            </tp:docstring>
+            <arg type="u" name="refreshTimeMs" direction="in">
+              <tp:docstring>
+                Set the sending time (in milliseconds) of the signal.
+              </tp:docstring>
+            </arg>
+        </method>
+
+        <method name="stopSmartInfo" tp:name-for-bindings="stopSmartInfo">
+            <tp:docstring>
+              Stop sending the signal smartInfo
+            </tp:docstring>
+        </method>
+
         <method name="getIsRecording" tp:name-for-bindings="getIsRecording">
             <tp:docstring>
               Tells whether or not a call is being recorded.
diff --git a/bin/dbus/dbuscallmanager.cpp b/bin/dbus/dbuscallmanager.cpp
index 843680f7c7..6d71f5291c 100644
--- a/bin/dbus/dbuscallmanager.cpp
+++ b/bin/dbus/dbuscallmanager.cpp
@@ -283,3 +283,15 @@ DBusCallManager::sendTextMessage(const std::string& callID, const std::map<std::
 {
     DRing::sendTextMessage(callID, messages, "Me", isMixed);
 }
+
+void
+DBusCallManager::startSmartInfo(const uint32_t& refreshTimeMs)
+{
+    DRing::startSmartInfo(refreshTimeMs);
+}
+
+void
+DBusCallManager::stopSmartInfo()
+{
+    DRing::stopSmartInfo();
+}
diff --git a/bin/dbus/dbuscallmanager.h b/bin/dbus/dbuscallmanager.h
index ebef2d6f46..69363f9e76 100644
--- a/bin/dbus/dbuscallmanager.h
+++ b/bin/dbus/dbuscallmanager.h
@@ -97,6 +97,8 @@ class DBusCallManager :
         void requestGoClear(const std::string& callID);
         void acceptEnrollment(const std::string& callID, const bool& accepted);
         void sendTextMessage(const std::string& callID, const std::map<std::string, std::string>& messages, const bool& isMixed);
+        void startSmartInfo(const uint32_t& refreshTimeMs);
+        void stopSmartInfo();
 };
 
 #endif // __RING_CALLMANAGER_H__
diff --git a/bin/dbus/dbusclient.cpp b/bin/dbus/dbusclient.cpp
index f049f455db..61886b11bb 100644
--- a/bin/dbus/dbusclient.cpp
+++ b/bin/dbus/dbusclient.cpp
@@ -166,7 +166,8 @@ DBusClient::initLibrary(int flags)
         exportable_callback<CallSignal::RtcpReportReceived>(bind(&DBusCallManager::onRtcpReportReceived, callM, _1, _2)),
         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::VideoMuted>(bind(&DBusCallManager::videoMuted, callM, _1, _2)),
+        exportable_callback<CallSignal::SmartInfo>(bind(&DBusCallManager::SmartInfo, callM, _1))
     };
 
     // Configuration event handlers
diff --git a/src/Makefile.am b/src/Makefile.am
index c26886ee55..c77f35c838 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -126,7 +126,9 @@ libring_la_SOURCES = \
 		string_utils.cpp \
 		rw_mutex.h \
 		ring_api.cpp \
-		rational.h
+		rational.h \
+		smartools.cpp \
+		smartools.h
 
 if HAVE_WIN32
 libring_la_SOURCES += \
diff --git a/src/client/callmanager.cpp b/src/client/callmanager.cpp
index 9882973892..2170c15f45 100644
--- a/src/client/callmanager.cpp
+++ b/src/client/callmanager.cpp
@@ -33,6 +33,8 @@
 #include "logger.h"
 #include "manager.h"
 
+#include "smartools.h"
+
 namespace DRing {
 
 void
@@ -142,6 +144,18 @@ removeConference(const std::string& conference_id)
    ring::Manager::instance().removeConference(conference_id);
 }
 
+void
+startSmartInfo(uint32_t refreshTimeMs)
+{
+    ring::Smartools::getInstance().start(std::chrono::milliseconds(refreshTimeMs));
+}
+
+void
+stopSmartInfo()
+{
+    ring::Smartools::getInstance().stop();
+}
+
 bool
 addParticipant(const std::string& callID, const std::string& confID)
 {
diff --git a/src/client/ring_signal.cpp b/src/client/ring_signal.cpp
index d8f91e9dce..766f88e5d1 100644
--- a/src/client/ring_signal.cpp
+++ b/src/client/ring_signal.cpp
@@ -53,6 +53,7 @@ getSignalHandlers()
         exported_callback<DRing::CallSignal::PeerHold>(),
         exported_callback<DRing::CallSignal::VideoMuted>(),
         exported_callback<DRing::CallSignal::AudioMuted>(),
+        exported_callback<DRing::CallSignal::SmartInfo>(),
 
         /* Configuration */
         exported_callback<DRing::ConfigurationSignal::VolumeChanged>(),
diff --git a/src/dring/callmanager_interface.h b/src/dring/callmanager_interface.h
index a1c1cba907..1c49dddaf8 100644
--- a/src/dring/callmanager_interface.h
+++ b/src/dring/callmanager_interface.h
@@ -67,6 +67,10 @@ std::vector<std::string> getDisplayNames(const std::string& confID);
 std::string getConferenceId(const std::string& callID);
 std::map<std::string, std::string> getConferenceDetails(const std::string& callID);
 
+/* Statistic related methods */
+void startSmartInfo(uint32_t refreshTimeMs);
+void stopSmartInfo();
+
 /* File Playback methods */
 bool startRecordedFilePlayback(const std::string& filepath);
 void stopRecordedFilePlayback(const std::string& filepath);
@@ -200,8 +204,12 @@ struct CallSignal {
                 constexpr static const char* name = "AudioMuted";
                 using cb_type = void(const std::string&, bool);
         };
+        struct SmartInfo {
+                constexpr static const char* name = "SmartInfo";
+                using cb_type = void(const std::map<std::string, std::string>&);
+        };
 };
 
-} // namespace DRing
+}; // namespace DRing
 
 #endif // DRING_CALLMANAGERI_H
diff --git a/src/manager.h b/src/manager.h
index 886718480f..166bdc67ac 100644
--- a/src/manager.h
+++ b/src/manager.h
@@ -61,7 +61,6 @@ class YamlEmitter;
 namespace video {
 class SinkClient;
 }
-
 class PluginManager;
 class AudioFile;
 class DTMF;
@@ -769,6 +768,12 @@ class Manager {
          */
         std::vector<std::string> loadAccountOrder() const;
 
+        /**
+         * Get the Call referred by callID. If the Call does not exist, return
+         * empty std::shared_ptr<Call> instance
+         */
+        std::shared_ptr<Call> getCallFromCallID(const std::string &callID) const;
+
     private:
         std::atomic_bool autoAnswer_ {false};
 
@@ -779,10 +784,6 @@ class Manager {
         // Set the ringtone or recorded call to be played
         void updateAudioFile(const std::string &file, int sampleRate);
 
-        /**
-         * Get the Call referred to by callID. If the Call does not exist, return NULL
-         */
-        std::shared_ptr<Call> getCallFromCallID(const std::string &callID) const;
 
         /**
          * Process remaining participant given a conference and the current call id.
diff --git a/src/media/audio/audio_rtp_session.cpp b/src/media/audio/audio_rtp_session.cpp
index cd76ee5bdd..7474886ae5 100644
--- a/src/media/audio/audio_rtp_session.cpp
+++ b/src/media/audio/audio_rtp_session.cpp
@@ -37,6 +37,7 @@
 #include "audio/ringbufferpool.h"
 #include "audio/resampler.h"
 #include "manager.h"
+#include "smartools.h"
 #include <sstream>
 
 namespace ring {
@@ -166,6 +167,7 @@ AudioSender::process()
         resampledData_.setFormat(accountAudioCodec->audioformat);
         resampledData_.resize(samplesToGet);
         resampler_->resample(micData_, resampledData_);
+        Smartools::getInstance().setLocalAudioCodec(audioEncoder_->getEncoderName());
         if (audioEncoder_->encode_audio(resampledData_) < 0)
             RING_ERR("encoding failed");
     } else {
@@ -281,6 +283,8 @@ AudioReceiveThread::process()
         case MediaDecoder::Status::FrameFinished:
             audioDecoder_->writeToRingBuffer(decodedFrame, *ringbuffer_,
                                              mainBuffFormat);
+            // Refresh the remote audio codec in the callback SmartInfo
+            Smartools::getInstance().setRemoteAudioCodec(audioDecoder_->getDecoderName());
             return;
 
         case MediaDecoder::Status::DecodeError:
diff --git a/src/media/media_decoder.cpp b/src/media/media_decoder.cpp
index 15855bf3ab..29ae832c6f 100644
--- a/src/media/media_decoder.cpp
+++ b/src/media/media_decoder.cpp
@@ -416,6 +416,9 @@ int MediaDecoder::getWidth() const
 int MediaDecoder::getHeight() const
 { return decoderCtx_->height; }
 
+std::string MediaDecoder::getDecoderName() const
+{ return decoderCtx_->codec->name; }
+
 rational<double>
 MediaDecoder::getFps() const
 {
diff --git a/src/media/media_decoder.h b/src/media/media_decoder.h
index f2076614aa..7012bc38bc 100644
--- a/src/media/media_decoder.h
+++ b/src/media/media_decoder.h
@@ -81,6 +81,8 @@ class MediaDecoder {
 
         int getWidth() const;
         int getHeight() const;
+        std::string getDecoderName() const;
+
         rational<double> getFps() const;
         int getPixelFormat() const;
 
diff --git a/src/media/media_encoder.cpp b/src/media/media_encoder.cpp
index cb7ce16981..81d3bdb508 100644
--- a/src/media/media_encoder.cpp
+++ b/src/media/media_encoder.cpp
@@ -116,6 +116,12 @@ MediaEncoder::getLastSeqValue()
         return 0;
 }
 
+std::string
+MediaEncoder::getEncoderName() const
+{
+    return encoderCtx_->codec->name;
+}
+
 void
 MediaEncoder::openOutput(const char *filename,
                          const ring::MediaDescription& args)
diff --git a/src/media/media_encoder.h b/src/media/media_encoder.h
index 03f374910a..6f736b2947 100644
--- a/src/media/media_encoder.h
+++ b/src/media/media_encoder.h
@@ -84,6 +84,7 @@ public:
     void setMuted(bool isMuted);
     void setInitSeqVal(uint16_t seqVal);
     uint16_t getLastSeqValue();
+    std::string getEncoderName() const;
 
     bool useCodec(const AccountCodecInfo* codec) const noexcept;
 
diff --git a/src/media/video/sinkclient.cpp b/src/media/video/sinkclient.cpp
index 3c2d4c3a2d..bdce0753c8 100644
--- a/src/media/video/sinkclient.cpp
+++ b/src/media/video/sinkclient.cpp
@@ -37,6 +37,7 @@
 #include "dring/videomanager_interface.h"
 #include "libav_utils.h"
 #include "video_scaler.h"
+#include "smartools.h"
 
 #ifndef _WIN32
 #include <sys/mman.h>
@@ -321,13 +322,18 @@ SinkClient::update(Observable<std::shared_ptr<VideoFrame>>* /*obs*/,
     const std::chrono::duration<double> seconds = currentTime - lastFrameDebug_;
     ++frameCount_;
     if (seconds.count() > 1) {
-        RING_DBG("%s: FPS %f", id_.c_str(), frameCount_ / seconds.count());
+        std::ostringstream fps;
+        fps << frameCount_ / seconds.count();
+        // Send the framerate in smartInfo
+        Smartools::getInstance().setFrameRate(id_, fps.str());
         frameCount_ = 0;
         lastFrameDebug_ = currentTime;
     }
 #endif
 
 #if HAVE_SHM
+    // Send the resolution in smartInfo
+    Smartools::getInstance().setResolution(id_, f.width(), f.height());
     shm_->renderFrame(f);
 #endif
 
diff --git a/src/media/video/sinkclient.h b/src/media/video/sinkclient.h
index 222ebcbef6..1f64d345c3 100644
--- a/src/media/video/sinkclient.h
+++ b/src/media/video/sinkclient.h
@@ -33,6 +33,8 @@
 #include <vector>
 #include <memory>
 
+#define DEBUG_FPS
+
 namespace ring { namespace video {
 
 #if HAVE_SHM
diff --git a/src/media/video/video_receive_thread.cpp b/src/media/video/video_receive_thread.cpp
index 20aa1dd3a0..286f781e2e 100644
--- a/src/media/video/video_receive_thread.cpp
+++ b/src/media/video/video_receive_thread.cpp
@@ -27,6 +27,7 @@
 #include "client/videomanager.h"
 #include "sinkclient.h"
 #include "logger.h"
+#include "smartools.h"
 
 #include <unistd.h>
 #include <map>
@@ -119,6 +120,9 @@ bool VideoReceiveThread::setup()
     if (!conf)
         exitConference();
 
+    // Send remote video codec in SmartInfo
+    Smartools::getInstance().setRemoteVideoCodec(videoDecoder_->getDecoderName(), id_);
+
     return true;
 }
 
diff --git a/src/media/video/video_sender.cpp b/src/media/video/video_sender.cpp
index d5ad9886fa..45e4ba6919 100644
--- a/src/media/video/video_sender.cpp
+++ b/src/media/video/video_sender.cpp
@@ -26,6 +26,7 @@
 #include "client/videomanager.h"
 #include "logger.h"
 #include "manager.h"
+#include "smartools.h"
 
 #include <map>
 #include <unistd.h>
@@ -67,6 +68,9 @@ VideoSender::encodeAndSendVideo(VideoFrame& input_frame)
 
     if (videoEncoder_->encode(input_frame, is_keyframe, frameNumber_++) < 0)
         RING_ERR("encoding failed");
+
+    // Send local video codec in SmartInfo
+    Smartools::getInstance().setLocalVideoCodec(videoEncoder_->getEncoderName());
 }
 
 void
diff --git a/src/smartools.cpp b/src/smartools.cpp
new file mode 100644
index 0000000000..ce579f0aa8
--- /dev/null
+++ b/src/smartools.cpp
@@ -0,0 +1,139 @@
+/*
+ *  Copyright (C) 2016 Savoir-faire Linux Inc.
+ *
+ *  Author: Olivier Grégoire <olivier.gregoire@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.
+ */
+#include "smartools.h"
+#include "media/media_decoder.h"
+#include "media/video/video_input.h"
+#include "media/video/video_device.h"
+#include "dring/callmanager_interface.h"
+#include "client/ring_signal.h"
+#include "string_utils.h"
+
+namespace ring {
+
+Smartools& Smartools::getInstance()
+{
+    // Meyers-Singleton
+    static Smartools instance_;
+    return instance_;
+}
+
+// Launch process() in new thread
+Smartools::Smartools()
+: loop_([this] { return true; }, [this] { process(); }, [] {})
+{}
+
+void
+Smartools::sendInfo()
+{
+    std::lock_guard<std::mutex> lk(mutexInfo_);
+    emitSignal<DRing::CallSignal::SmartInfo>(information_);
+    information_.clear();
+}
+
+void
+Smartools::process()
+{
+    // Send the signal SmartInfo
+    Smartools::sendInfo();
+    std::this_thread::sleep_for(refreshTimeMs_);
+}
+
+void
+Smartools::start(std::chrono::milliseconds refreshTimeMs)
+{
+    RING_DBG("Start SmartInfo");
+    refreshTimeMs_ = refreshTimeMs;
+    loop_.stop();
+    loop_.start();
+}
+
+void
+Smartools::stop()
+{
+    std::lock_guard<std::mutex> lk(mutexInfo_);
+    RING_DBG("Stop SmartInfo");
+    loop_.stop();
+    information_.clear();
+}
+
+
+//Set all the information in the map
+
+void
+Smartools::setFrameRate(const std::string& id, const std::string& fps)
+{
+    std::lock_guard<std::mutex> lk(mutexInfo_);
+    if(id == "local"){
+        information_["local FPS"]= fps;
+    } else {
+        information_["remote FPS"]= fps;
+    }
+}
+
+void
+Smartools::setResolution(const std::string& id, int width, int height)
+{
+    std::lock_guard<std::mutex> lk(mutexInfo_);
+    if(id == "local"){
+        information_["local width"] = to_string(width);
+        information_["local height"] = to_string(height);
+    } else {
+        information_["remote width"] = to_string(width);
+        information_["remote height"] = to_string(height);
+    }
+}
+
+void
+Smartools::setRemoteAudioCodec(const std::string& remoteAudioCodec)
+{
+    std::lock_guard<std::mutex> lk(mutexInfo_);
+    information_["remote audio codec"]= remoteAudioCodec;
+}
+
+void
+Smartools::setLocalAudioCodec(const std::string& localAudioCodec)
+{
+    std::lock_guard<std::mutex> lk(mutexInfo_);
+    information_["local audio codec"]= localAudioCodec;
+}
+
+void
+Smartools::setLocalVideoCodec(const std::string& localVideoCodec)
+{
+    std::lock_guard<std::mutex> lk(mutexInfo_);
+    information_["local video codec"]= localVideoCodec;
+}
+
+void
+Smartools::setRemoteVideoCodec(const std::string& remoteVideoCodec, const std::string& callID)
+{
+    std::lock_guard<std::mutex> lk(mutexInfo_);
+    information_["remote video codec"]= remoteVideoCodec;
+    auto confID = Manager::instance().getCallFromCallID(callID)->getConfId();
+    if (confID != ""){
+        information_["type"]= "conference";
+        information_["callID"]= confID;
+    } else {
+        information_["type"]= "no conference";
+        information_["callID"]= callID;
+    }
+ }
+
+ } // end namespace ring
diff --git a/src/smartools.h b/src/smartools.h
new file mode 100644
index 0000000000..2d3b6bf8c8
--- /dev/null
+++ b/src/smartools.h
@@ -0,0 +1,49 @@
+/*
+ *  Copyright (C) 2016 Savoir-faire Linux Inc.
+ *
+ *  Author: Olivier Grégoire <olivier.gregoire@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.
+ */
+#pragma once
+
+#include "threadloop.h"
+#include "manager.h"
+#include <string>
+
+namespace ring {
+class Smartools
+{
+    public:
+        static Smartools& getInstance();
+        void start(std::chrono::milliseconds refreshTimeMs);
+        void stop();
+        void setFrameRate(const std::string& id, const std::string& fps);
+        void setResolution(const std::string& id, int width, int height);
+        void setLocalVideoCodec(const std::string& localVideoCodec);
+        void setRemoteVideoCodec(const std::string& remoteVideoCodec, const std::string& callID);
+        void setRemoteAudioCodec(const std::string& remoteAudioCodec);
+        void setLocalAudioCodec(const std::string& remoteAudioCodec);
+        void sendInfo();
+
+    private:
+        Smartools();
+        void process();
+        std::map<std::string, std::string> information_;
+        std::mutex mutexInfo_; // Protect information_ from multithreading
+        std::chrono::milliseconds refreshTimeMs_;
+        ThreadLoop loop_; // Has to be last member
+};
+} //ring namespace
-- 
GitLab