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