diff --git a/bin/dbus/cx.ring.Ring.CallManager.xml b/bin/dbus/cx.ring.Ring.CallManager.xml index 526ace11aa45535d1e3378c771e22517e22d7dc7..b5cebc337b97e053d515add9a14065351865a374 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 843680f7c7d14e1a63f37564e126bfd65a9e4f0e..6d71f5291c7b9a811d3b0803ba2dd5f13c361ed7 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 ebef2d6f46b145e9515539db520b1b3907bce133..69363f9e766e4b0d40da2fcb7d7bc86e66d5ed1f 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 f049f455dbbf7a5010a5bc4d934b7250e189e68f..61886b11bb50a118fa510b2b634d4ebbe65ba862 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 c26886ee551377344d812ffa29768a204c958493..c77f35c83831f565a5222133f754a994039a26ac 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 9882973892eb3fdc1c8a36153736c0572132c549..2170c15f4509cb63fa55439c90fe114b313e6890 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 d8f91e9dce638abdc473b42e63301b88a98347e4..766f88e5d16e8d48bb0b307972301070c5ee14f9 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 a1c1cba9079e9ab7e58672617701f5aa6a1f63c7..1c49dddaf8446a2b57184788848562a47c8cda55 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 886718480f7ca67770c69e89aa55279c638bf468..166bdc67ac9f28274e023f491d14bf48f099f4cf 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 cd76ee5bdd5c50ea2832b5bc4fba315d81f53d2e..7474886ae5e03b773fafe8c6ba42c36bb117b8b0 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 15855bf3ab51057a883e2e5e3dd68181fe91e19a..29ae832c6f2107fb5536f56802a2955cf763f71d 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 f2076614aa132962153ae989222967fe1140c21e..7012bc38bc0c66160435a610d10ff92103111dac 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 cb7ce16981141c913e89ef1aea0a4d1df6f93148..81d3bdb508d265d632801e6f3e28c7257a8233ae 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 03f374910a28f2d3548e3b0b97c3f71fbb05bab3..6f736b29479290dd6730bef292704f7f1dca6244 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 3c2d4c3a2d7bd629269aac07e26a404e385e1e26..bdce0753c87ef0e2c617b13a290f7e55cd5f4b4e 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 222ebcbef6851f064c989f4d67ae846dbe5e8c24..1f64d345c3493c71ec198129f1d11f13b996cb30 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 20aa1dd3a0bf5eb46fb5e2a0b338e4f811b693bd..286f781e2ef18a0492339948f42f9faf0a414f20 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 d5ad9886faef6fc5b64ad9407bf9c0f176ec54e8..45e4ba6919bffbdd4bd30e31b1b98a65e20acde7 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 0000000000000000000000000000000000000000..ce579f0aa876bc5052845c25cd6cf3a3ea8c7f19 --- /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 0000000000000000000000000000000000000000..2d3b6bf8c832519bb1b930f7b7954cb193efd663 --- /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