diff --git a/daemon/README b/daemon/README index 27324592de409029e7bd1e9a1c5998c7cba49873..a96018a940c1bcfc7863cf3d9e9638e6ee8bb5ea 100644 --- a/daemon/README +++ b/daemon/README @@ -1,6 +1,6 @@ COPYRIGHT NOTICE -Copyright (C) 2004-2013 Savoir-Faire Linux Inc. +Copyright (C) 2004-2014 Savoir-Faire Linux Inc. 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 @@ -32,16 +32,16 @@ Introduction SFLPhone is a Voice-over-IP software phone. We want it to be: - user friendly (fast, sleek, easy to learn interface) -- corporate grade (transfers, holds, perfect audio quality) +- professional grade (transfers, holds, optimal audio quality) - fully compatible with Asterisk (SIP and IAX protocols) - customizable As the SIP/audio daemon and the user interface are separate processes, -it is easy to provide different user interfaces. SFLPhone0.8 comes with -a GTK graphical user interface, an interactive command line interface, -and even scripts to control the daemon from the shell. +it is easy to provide different user interfaces. SFLPhone comes with +a GTK graphical user interface and even scripts to control the daemon from +the shell. -SFLPhone is actually used by the support team of Savoir-Faire Linux Inc. +SFLPhone is currently used by the support team of Savoir-Faire Linux Inc. More information is available on the project homepage: http://www.sflphone.org/ @@ -50,15 +50,11 @@ More information is available on the project homepage: Short description of content of source tree ------------------------------------------- -- ringtones/ contains the different ringtones. -- src/ is the core of SFLphone. It contains the main.cpp, managerimpl.cpp - files, audio and gui directories, and files about signalisation SIP. Later, - it should be better, when IAX will be implemented, that a directory groups - these protocols. - The ManagerImpl class is the intermediaire between all the layer in the tree. -- src/audio/ is the audio layer. It contains all about tones, dtmf, - audiodriver, rtp layer, audio codec ulaw, alaw and gsm. -- src/dbus, the dbus xml interfaces, and c++ bindings +- src/ is the core of SFLphone. It contains main.cpp, the entry point and + managerimpl.cpp. + The ManagerImpl class is the intermediary between all the layers of the daemon. +- src/client/dbus, the D-Bus xml interfaces, and c++ bindings +- src/client/android, the JNI interfaces (Java bindings) About Savoir-Faire Linux @@ -75,42 +71,33 @@ How to compile cd libs ./compile_pjsip.sh -If you already have installed a different version of -pjsip on your system, you will most likely have some errors -such as : - -"ln: creating symbolic link `./libpjsip-sfl.a': File exists" - -after executing make install. - -Simply remove those files and run "make install" again. +Note that pjsip need not be installed, just built in-tree. # Then the daemon cd ../.. ./autogen.sh ./configure make -sudo make install +make install # And the GNOME client cd sflphone-client-gnome ./autogen.sh ./configure make -sudo make install +make install Done ! -Free SIP/IAx2 accounts ---------------------- +More details available here: +https://projects.savoirfairelinux.com/projects/sflphone/wiki/How_to_build -You may create a free SIP/IAX2 account through the account creation wizard in the both client (KDE and GNOME). -By doing this, you will be able to call other account registered to the same server. -Registered to this kind of account, you will be able to test your headset by dialing *100 -ECHO test --------- -Dial *100, on sip.sflphone.org +SIP/IAx2 accounts +--------------------- + +You may register an existing SIP/IAX2 account through the account wizard in both clients (KDE and GNOME). +By doing this, you will be able to call other accounts known to this server. Contributing to SFLPhone @@ -121,6 +108,13 @@ Of course we love patches. And contributions. And spring rolls. Development website: http://projects.savoirfairelinux.net/wiki/sflphone Do not hesitate to join us and post comments, suggestions, questions -and general feedback on the forge.novell mailing-list. +and general feedback on the SFLphone mailing-list: +http://lists.savoirfairelinux.net/mailman/listinfo/sflphone + +Bug reports: +https://projects.savoirfairelinux.com/projects/sflphone/wiki/BugReports + +IRC (on #freenode): +#sflphone -- The SFLPhone Team diff --git a/daemon/src/account.cpp b/daemon/src/account.cpp index 42e8d6520e682954dbcd3dafa97b6aa4da63c001..fd2a9ba16de6df171816c3808a4c961fcec40618 100644 --- a/daemon/src/account.cpp +++ b/daemon/src/account.cpp @@ -123,6 +123,7 @@ void Account::loadDefaultCodecs() result.push_back("3"); result.push_back("8"); result.push_back("9"); + result.push_back("104"); result.push_back("110"); result.push_back("111"); result.push_back("112"); @@ -296,6 +297,7 @@ Account::getDefaultAudioCodecs() result.push_back(3); result.push_back(8); result.push_back(9); + result.push_back(104); result.push_back(110); result.push_back(111); result.push_back(112); diff --git a/daemon/src/audio/audiortp/audio_rtp_stream.cpp b/daemon/src/audio/audiortp/audio_rtp_stream.cpp index 25936d2c1d473f8c84ee610c5adbaec596bf3e23..e63e0b88b374bb5b565725f7f4381e93f23eca88 100644 --- a/daemon/src/audio/audiortp/audio_rtp_stream.cpp +++ b/daemon/src/audio/audiortp/audio_rtp_stream.cpp @@ -241,16 +241,18 @@ void AudioRtpStream::setRtpMedia(const std::vector<AudioCodec*> &audioCodecs) return; } - AudioFormat f = Manager::instance().getMainBuffer().getInternalAudioFormat(); - audioCodecs[0]->setOptimalFormat(f.sample_rate, f.nb_channels); // FIXME: assuming right encoder/decoder are first? currentEncoderIndex_ = currentDecoderIndex_ = 0; - // FIXME: this is probably not the right payload type - const int pt = audioCodecs[0]->getPayloadType(); + AudioCodec& codec = *audioCodecs[currentEncoderIndex_]; + + AudioFormat f = Manager::instance().getMainBuffer().getInternalAudioFormat(); + codec.setOptimalFormat(f.sample_rate, f.nb_channels); + + const int pt = codec.getPayloadType(); encoder_.payloadType = decoder_.payloadType = pt; - encoder_.frameSize = decoder_.frameSize = audioCodecs[0]->getFrameSize(); + encoder_.frameSize = decoder_.frameSize = codec.getFrameSize(); - AudioFormat codecFormat(audioCodecs[0]->getCurrentClockRate(), audioCodecs[0]->getCurrentChannels()); + AudioFormat codecFormat(codec.getCurrentClockRate(), codec.getCurrentChannels()); if (codecFormat != decoder_.format or codecFormat != encoder_.format) { encoder_.format = decoder_.format = codecFormat; #if HAVE_SPEEXDSP @@ -258,10 +260,10 @@ void AudioRtpStream::setRtpMedia(const std::vector<AudioCodec*> &audioCodecs) #endif } Manager::instance().audioFormatUsed(codecFormat); - hasDynamicPayloadType_ = audioCodecs[0]->hasDynamicPayload(); + hasDynamicPayloadType_ = codec.hasDynamicPayload(); codecEncMutex_.unlock(); - resetDecoderPLC(audioCodecs[0]); + resetDecoderPLC(audioCodecs[currentDecoderIndex_]); codecDecMutex_.unlock(); } diff --git a/daemon/src/audio/audiortp/audio_rtp_stream.h b/daemon/src/audio/audiortp/audio_rtp_stream.h index 229ef42d69d114ae998a6ce4d4604b620b82bc9e..0debd2be6f21e6cf4e5cd5d3b12157a241a382e0 100644 --- a/daemon/src/audio/audiortp/audio_rtp_stream.h +++ b/daemon/src/audio/audiortp/audio_rtp_stream.h @@ -93,7 +93,13 @@ class AudioRtpStream { AudioRtpStream(const std::string &id); virtual ~AudioRtpStream(); void initBuffers(); + + /** + * Set the list of codecs supported by this stream. + * The codec used for encoding must be first. + */ void setRtpMedia(const std::vector<AudioCodec*> &codecs); + /** * Decode audio data received from peer */ diff --git a/daemon/src/audio/opensl/opensllayer.cpp b/daemon/src/audio/opensl/opensllayer.cpp index 5020dbef70b48741ee78a2adceef3895cc340ce2..bb0387f957f2fd5e655254391842d182a23b6d15 100644 --- a/daemon/src/audio/opensl/opensllayer.cpp +++ b/daemon/src/audio/opensl/opensllayer.cpp @@ -339,12 +339,15 @@ OpenSLLayer::initAudioCapture() // create audio recorder // (requires the RECORD_AUDIO permission) DEBUG("Create audio recorder\n"); - const SLInterfaceID id[] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE}; - const SLboolean req[] = {SL_BOOLEAN_TRUE}; + const SLInterfaceID id[2] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE, + SL_IID_ANDROIDCONFIGURATION}; + const SLboolean req[2] ={SL_BOOLEAN_TRUE, + SL_BOOLEAN_TRUE}; + SLAndroidConfigurationItf recorderConfig; if (engineInterface_ != nullptr) { result = (*engineInterface_)->CreateAudioRecorder(engineInterface_, - &recorderObject_, &audioSource, &audioSink, 1, id, req); + &recorderObject_, &audioSource, &audioSink, 2, id, req); } if (SL_RESULT_SUCCESS != result) { @@ -352,6 +355,22 @@ OpenSLLayer::initAudioCapture() return; } + /* Set Android configuration */ + result = (*recorderObject_)->GetInterface(recorderObject_, + SL_IID_ANDROIDCONFIGURATION, + &recorderConfig); + if (result == SL_RESULT_SUCCESS) { + SLint32 streamType = SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION; + result = (*recorderConfig)->SetConfiguration( + recorderConfig, SL_ANDROID_KEY_RECORDING_PRESET, + &streamType, sizeof(SLint32)); + } + + if (result != SL_RESULT_SUCCESS) { + DEBUG("Warning: Unable to set android recorder configuration"); + return; + } + // realize the audio recorder DEBUG("Realize the audio recorder\n"); result = (*recorderObject_)->Realize(recorderObject_, SL_BOOLEAN_FALSE); diff --git a/daemon/src/audio/ringbuffer.h b/daemon/src/audio/ringbuffer.h index 007dd9f4586c2d49825dbfe4cb9eb7c1cef2ebae..02b57f7128cfbd6a3f13d9d89d9dc1bf6deb28f0 100644 --- a/daemon/src/audio/ringbuffer.h +++ b/daemon/src/audio/ringbuffer.h @@ -133,7 +133,7 @@ class RingBuffer { * @param deadline The call is garenteed to end after this time point. If no deadline is provided, the the call blocks indefinitely. * @return available data for call_id after the call returned (same as calling getLength(call_id) ). */ - size_t waitForDataAvailable(const std::string &call_id, const size_t min_data_length = 0, const std::chrono::high_resolution_clock::time_point& deadline = std::chrono::high_resolution_clock::time_point() ) const; + size_t waitForDataAvailable(const std::string &call_id, const size_t min_data_length, const std::chrono::high_resolution_clock::time_point& deadline) const; /** * Debug function print mEnd, mStart, mBufferSize diff --git a/daemon/src/client/callmanager.cpp b/daemon/src/client/callmanager.cpp index 398b52bfbb0886b72a735e57972118682b4d5ecd..5152a59e8b9d823422550578441f6357fd391206 100644 --- a/daemon/src/client/callmanager.cpp +++ b/daemon/src/client/callmanager.cpp @@ -186,6 +186,12 @@ CallManager::getParticipantList(const std::string& confID) return Manager::instance().getParticipantList(confID); } +std::vector<std::string> +CallManager::getDisplayNames(const std::string& confID) +{ + return Manager::instance().getDisplayNames(confID); +} + std::string CallManager::getConferenceId(const std::string& callID) { diff --git a/daemon/src/client/callmanager.h b/daemon/src/client/callmanager.h index 0ad19356cfc558d82e175ea81d10b5a12c5d76d5..d85f8ad56a50683f3d163b638f21352e870f9784 100644 --- a/daemon/src/client/callmanager.h +++ b/daemon/src/client/callmanager.h @@ -120,6 +120,7 @@ class CallManager bool unholdConference(const std::string& confID); std::vector<std::string> getConferenceList(); std::vector<std::string> getParticipantList(const std::string& confID); + 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); diff --git a/daemon/src/client/dbus/callmanager-introspec.xml b/daemon/src/client/dbus/callmanager-introspec.xml index bd97bdff75af2b17f5935f947e7b30318d20f55c..41896bcc992f69c0c4ffbcd902af99195386dc9d 100644 --- a/daemon/src/client/dbus/callmanager-introspec.xml +++ b/daemon/src/client/dbus/callmanager-introspec.xml @@ -534,6 +534,23 @@ </arg> </signal> + <method name="getDisplayNames" tp:name-for-bindings="getDisplayNames"> + <tp:added version="1.3.0"/> + <tp:docstring> + Get the display name of every participant in a given conference, or their number as a fallback. + </tp:docstring> + <arg type="s" name="confID" direction="in"> + <tp:docstring> + The conference ID. + </tp:docstring> + </arg> + <arg type="as" name="list" direction="out"> + <tp:docstring> + The list of the display names. + </tp:docstring> + </arg> + </method> + <method name="getParticipantList" tp:name-for-bindings="getParticipantList"> <tp:added version="0.9.7"/> <tp:docstring> diff --git a/daemon/src/client/dbus/video_controls-introspec.xml b/daemon/src/client/dbus/video_controls-introspec.xml index c95cb19af8c7ab3ec6da81281fb66c05ac60bbb1..16cad00957bba2de5bbff4dc4f6c5bf2c2829fb1 100644 --- a/daemon/src/client/dbus/video_controls-introspec.xml +++ b/daemon/src/client/dbus/video_controls-introspec.xml @@ -111,7 +111,18 @@ </method> <method name="switchInput" tp:name-for-bindings="switchInput"> - <arg type="s" name="device" direction="in"> + <arg type="s" name="resource" direction="in"> + <tp:docstring> + A media resource locator (MRL). + Currently, the following are supported: + <ul> + <li>v4l2://DEVICE</li> + <li>display://DISPLAY_NAME[ WIDTHxHEIGHT]</li> + </ul> + </tp:docstring> + </arg> + <arg type="b" name="switched" direction="out"> + <tp:docstring>Returns true if the input stream was successfully changed, false otherwise</tp:docstring> </arg> </method> diff --git a/daemon/src/client/dbus/video_controls.cpp b/daemon/src/client/dbus/video_controls.cpp index 74efb5005c253e7d1a04e3c17a0c1f4d8402c3f4..29f3930e41aa38ab49feb39176321d780301ba7c 100644 --- a/daemon/src/client/dbus/video_controls.cpp +++ b/daemon/src/client/dbus/video_controls.cpp @@ -171,7 +171,7 @@ VideoControls::startCamera() return; } - const std::string& device = videoPreference_.getDevice(); + const std::string device = "v4l2://" + videoPreference_.getDevice(); videoInputSelector_.reset(new sfl_video::VideoInputSelector(device)); } @@ -188,15 +188,15 @@ VideoControls::stopCamera() } } -void -VideoControls::switchInput(const std::string &device) +bool +VideoControls::switchInput(const std::string &resource) { if (not hasCameraStarted()) { - ERROR("Video input selector not initialized"); - return; + ERROR("Input selector not initialized"); + return false; } - DEBUG("Switching input device to %s", device.c_str()); - videoInputSelector_->switchInput(device); + + return videoInputSelector_->switchInput(resource); } std::weak_ptr<sfl_video::VideoFrameActiveWriter> diff --git a/daemon/src/client/video_controls.h b/daemon/src/client/video_controls.h index b2e9832d1eb9cef59ecfdacbd546a812f1f6aa09..614078bce9b813d17703239dd0d6489c35abcf0c 100644 --- a/daemon/src/client/video_controls.h +++ b/daemon/src/client/video_controls.h @@ -128,7 +128,7 @@ class VideoControls : public org::sflphone::SFLphone::VideoControls_adaptor, void startCamera(); void stopCamera(); - void switchInput(const std::string& device); + bool switchInput(const std::string& resource); bool hasCameraStarted(); std::weak_ptr<sfl_video::VideoFrameActiveWriter> getVideoCamera(); }; diff --git a/daemon/src/conference.cpp b/daemon/src/conference.cpp index 87e88614386d2a50a7d19ba4d4b7d006cea274f5..0d745868ebd4d7b3f86e51ae0204adada9f45203 100644 --- a/daemon/src/conference.cpp +++ b/daemon/src/conference.cpp @@ -139,6 +139,19 @@ ParticipantSet Conference::getParticipantList() const return participants_; } +std::vector<std::string> +Conference::getDisplayNames() const +{ + std::vector<std::string> result; + + for (const auto &p : participants_) { + auto details = Manager::instance().getCallDetails(p); + const auto tmp = details["DISPLAY_NAME"]; + result.push_back(tmp.empty() ? details["PEER_NUMBER"] : tmp); + } + return result; +} + bool Conference::toggleRecording() { const bool startRecording = Recordable::toggleRecording(); diff --git a/daemon/src/conference.h b/daemon/src/conference.h index 52856c60fbae4e3bc04bc2747189f8ee1ccb7466..e2203da2e5153a61b80da09068959bb01aff16df 100644 --- a/daemon/src/conference.h +++ b/daemon/src/conference.h @@ -100,6 +100,12 @@ class Conference : public Recordable { */ ParticipantSet getParticipantList() const; + /** + * Get the display names or peer numbers for this conference + */ + std::vector<std::string> + getDisplayNames() const; + /** * Start/stop recording toggle */ diff --git a/daemon/src/ip_utils.cpp b/daemon/src/ip_utils.cpp index 38edb40de751ac94889626a683b4f7f0cf2bbf46..fde9b5547ae5df2dbced1d1a7c843ea396541024 100644 --- a/daemon/src/ip_utils.cpp +++ b/daemon/src/ip_utils.cpp @@ -104,12 +104,12 @@ ip_utils::addrToStr(const std::string& ip_str, bool include_port, bool force_ipv } pj_sockaddr -ip_utils::strToAddr(const std::string& str) +ip_utils::strToAddr(const std::string& str, pj_uint16_t family) { pj_str_t pjstring; pj_cstr(&pjstring, str.c_str()); pj_sockaddr ip; - auto status = pj_sockaddr_parse(pj_AF_UNSPEC(), 0, &pjstring, &ip); + auto status = pj_sockaddr_parse(family, 0, &pjstring, &ip); if (status != PJ_SUCCESS) ip.addr.sa_family = pj_AF_UNSPEC(); return ip; @@ -143,21 +143,30 @@ ip_utils::getLocalAddr(pj_uint16_t family) pj_sockaddr ip_utils::getInterfaceAddr(const std::string &interface, pj_uint16_t family) { - ERROR("getInterfaceAddr: %s %d", interface.c_str(), family); if (interface == DEFAULT_INTERFACE) return getLocalAddr(family); - auto unix_family = (family == pj_AF_INET()) ? AF_INET : AF_INET6; + + const auto unix_family = family == pj_AF_INET() ? AF_INET : AF_INET6; + int fd = socket(unix_family, SOCK_DGRAM, 0); - if(unix_family == AF_INET6) { - int val = (family == pj_AF_UNSPEC()) ? 0 : 1; - setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&val, sizeof(val)); + + pj_sockaddr pj_saddr = {}; + + if (fd < 0) { + ERROR("Could not open socket: %m"); + pj_saddr.addr.sa_family = pj_AF_UNSPEC(); + return pj_saddr; } - pj_sockaddr saddr; - if(fd < 0) { - ERROR("Could not open socket: %m", fd); - saddr.addr.sa_family = pj_AF_UNSPEC(); - return saddr; + + if (unix_family == AF_INET6) { + int val = family != pj_AF_UNSPEC(); + if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (void *) &val, sizeof(val)) < 0) { + ERROR("Could not setsockopt: %m"); + close(fd); + return pj_saddr; + } } + ifreq ifr; strncpy(ifr.ifr_name, interface.c_str(), sizeof ifr.ifr_name); // guarantee that ifr_name is NULL-terminated @@ -170,12 +179,16 @@ ip_utils::getInterfaceAddr(const std::string &interface, pj_uint16_t family) close(fd); sockaddr* unix_addr = &ifr.ifr_addr; - memcpy(&saddr, &ifr.ifr_addr, sizeof(pj_sockaddr)); - if ((ifr.ifr_addr.sa_family == AF_INET && IN_IS_ADDR_UNSPECIFIED(&((sockaddr_in *)unix_addr)->sin_addr )) - || (ifr.ifr_addr.sa_family == AF_INET6 && IN6_IS_ADDR_UNSPECIFIED(&((sockaddr_in6*)unix_addr)->sin6_addr))) { - return getLocalAddr(saddr.addr.sa_family); + + memcpy(&pj_saddr, unix_addr, unix_addr->sa_family == AF_INET6 ? + sizeof pj_saddr.ipv6 : sizeof pj_saddr.ipv4); + + if ((unix_addr->sa_family == AF_INET and IN_IS_ADDR_UNSPECIFIED(&((sockaddr_in *) unix_addr)->sin_addr)) + or (unix_addr->sa_family == AF_INET6 and IN6_IS_ADDR_UNSPECIFIED(&((sockaddr_in6*) unix_addr)->sin6_addr))) { + return getLocalAddr(pj_saddr.addr.sa_family); } - return saddr; + + return pj_saddr; } std::vector<std::string> diff --git a/daemon/src/ip_utils.h b/daemon/src/ip_utils.h index d48da727227856796aeb47469bff8e3a6d940e2e..0f9fa82fe2789cf9cd478abaaf026d815452297b 100644 --- a/daemon/src/ip_utils.h +++ b/daemon/src/ip_utils.h @@ -59,10 +59,10 @@ namespace ip_utils { /** * Convert a string representation of an IP adress to its binary counterpart. * - * Performs hostname resolution if necessary. + * Performs hostname resolution if necessary (with given address family). * If conversion fails, returned adress will have its family set to PJ_AF_UNSPEC. */ - pj_sockaddr strToAddr(const std::string& str); + pj_sockaddr strToAddr(const std::string& str, pj_uint16_t family = pj_AF_UNSPEC()); /** * Return the generic "any host" IP address of the specified family. diff --git a/daemon/src/managerimpl.cpp b/daemon/src/managerimpl.cpp index e9b388e9be42a034f6d067a93cb5ea5e0af83d82..34e00fe46e64f5a3fb632e4c47c1b37a71db1472 100644 --- a/daemon/src/managerimpl.cpp +++ b/daemon/src/managerimpl.cpp @@ -2798,7 +2798,23 @@ std::vector<std::string> ManagerImpl::getConferenceList() const return v; } -std::vector<std::string> ManagerImpl::getParticipantList(const std::string& confID) const +std::vector<std::string> +ManagerImpl::getDisplayNames(const std::string& confID) const +{ + std::vector<std::string> v; + ConferenceMap::const_iterator iter_conf = conferenceMap_.find(confID); + + if (iter_conf != conferenceMap_.end()) { + return iter_conf->second->getDisplayNames(); + } else { + WARN("Did not find conference %s", confID.c_str()); + } + + return v; +} + +std::vector<std::string> +ManagerImpl::getParticipantList(const std::string& confID) const { std::vector<std::string> v; ConferenceMap::const_iterator iter_conf = conferenceMap_.find(confID); diff --git a/daemon/src/managerimpl.h b/daemon/src/managerimpl.h index 256f2430d33f1f0e5b72801901f937fecdd2176e..2f4a76f7676232893c3db4570b83ade383353848 100644 --- a/daemon/src/managerimpl.h +++ b/daemon/src/managerimpl.h @@ -480,6 +480,12 @@ class ManagerImpl { */ std::vector<std::string> getParticipantList(const std::string& confID) const; + /** + * Get a list of the display names for everyone in a conference + * @return std::vector<std::string> A list of display names + */ + std::vector<std::string> getDisplayNames(const std::string& confID) const; + std::string getConferenceId(const std::string& callID); /** diff --git a/daemon/src/sip/sdp.cpp b/daemon/src/sip/sdp.cpp index 147246149cd517178b9bebdefed3acad89902bc4..7c96efcd9b9464c1ae6db26d4a06d10f2e720a56 100644 --- a/daemon/src/sip/sdp.cpp +++ b/daemon/src/sip/sdp.cpp @@ -60,7 +60,8 @@ Sdp::Sdp(pj_pool_t *pool) , activeRemoteSession_(nullptr) , audio_codec_list_() , video_codec_list_() - , sessionAudioMedia_() + , sessionAudioMediaLocal_() + , sessionAudioMediaRemote_() , sessionVideoMedia_() , publishedIpAddr_() , publishedIpAddrType_() @@ -123,6 +124,8 @@ void Sdp::setActiveLocalSdpSession(const pjmedia_sdp_session *sdp) { activeLocalSession_ = (pjmedia_sdp_session*) sdp; + sessionAudioMediaLocal_.clear(); + for (unsigned i = 0; i < activeLocalSession_->media_count; ++i) { pjmedia_sdp_media *current = activeLocalSession_->media[i]; @@ -140,14 +143,14 @@ void Sdp::setActiveLocalSdpSession(const pjmedia_sdp_session *sdp) if (!pj_stricmp2(¤t->desc.media, "audio")) { const unsigned long pt = pj_strtoul(¤t->desc.fmt[fmt]); - if (pt != telephoneEventPayload_ and not hasPayload(sessionAudioMedia_, pt)) { + if (pt != telephoneEventPayload_ and not hasPayload(sessionAudioMediaLocal_, pt)) { sfl::AudioCodec *codec = Manager::instance().audioCodecFactory.getCodec(pt); if (codec) - sessionAudioMedia_.push_back(codec); + sessionAudioMediaLocal_.push_back(codec); else { codec = findCodecByName(rtpmapToString(rtpmap)); if (codec) - sessionAudioMedia_.push_back(codec); + sessionAudioMediaLocal_.push_back(codec); else ERROR("Could not get codec for name %.*s", rtpmap->enc_name.slen, rtpmap->enc_name.ptr); } @@ -171,6 +174,8 @@ void Sdp::setActiveRemoteSdpSession(const pjmedia_sdp_session *sdp) activeRemoteSession_ = (pjmedia_sdp_session*) sdp; + sessionAudioMediaRemote_.clear(); + bool parsedTelelphoneEvent = false; for (unsigned i = 0; i < sdp->media_count; i++) { pjmedia_sdp_media *r_media = sdp->media[i]; @@ -202,16 +207,16 @@ void Sdp::setActiveRemoteSdpSession(const pjmedia_sdp_session *sdp) pjmedia_sdp_attr_to_rtpmap(memPool_, rtpMapAttribute, &rtpmap); const unsigned long pt = pj_strtoul(&r_media->desc.fmt[fmt]); - if (pt != telephoneEventPayload_ and not hasPayload(sessionAudioMedia_, pt)) { + if (pt != telephoneEventPayload_ and not hasPayload(sessionAudioMediaRemote_, pt)) { sfl::AudioCodec *codec = Manager::instance().audioCodecFactory.getCodec(pt); if (codec) { DEBUG("Adding codec with new payload type %d", pt); - sessionAudioMedia_.push_back(codec); + sessionAudioMediaRemote_.push_back(codec); } else { // Search by codec name, clock rate and param (channel count) codec = findCodecByName(rtpmapToString(rtpmap)); if (codec) - sessionAudioMedia_.push_back(codec); + sessionAudioMediaRemote_.push_back(codec); else ERROR("Could not get codec for name %.*s", rtpmap->enc_name.slen, rtpmap->enc_name.ptr); } @@ -219,7 +224,6 @@ void Sdp::setActiveRemoteSdpSession(const pjmedia_sdp_session *sdp) } } } - DEBUG("Ready to decode %u audio codecs", sessionAudioMedia_.size()); } string Sdp::getSessionVideoCodec() const @@ -231,24 +235,31 @@ string Sdp::getSessionVideoCodec() const return sessionVideoMedia_[0]; } -string Sdp::getAudioCodecNames() const -{ - std::string result; - char sep = ' '; - for (std::vector<sfl::AudioCodec*>::const_iterator i = sessionAudioMedia_.begin(); - i != sessionAudioMedia_.end(); ++i) { - if (i == sessionAudioMedia_.end() - 1) - sep = '\0'; - if (*i) - result += (*i)->getMimeSubtype() + sep; - } - return result; -} - std::vector<sfl::AudioCodec*> Sdp::getSessionAudioMedia() const { - return sessionAudioMedia_; + vector<sfl::AudioCodec*> codecs; + + // Common codecs first + for (auto c : sessionAudioMediaLocal_) { + if (std::find(sessionAudioMediaRemote_.begin(), sessionAudioMediaRemote_.end(), c) != sessionAudioMediaRemote_.end()) + codecs.push_back(c); + } + DEBUG("%u common audio codecs", codecs.size()); + + // Next, the other codecs we declared to be able to encode + for (auto c : sessionAudioMediaLocal_) { + if (std::find(codecs.begin(), codecs.end(), c) == codecs.end()) + codecs.push_back(c); + } + // Finally, the remote codecs so we can decode them + for (auto c : sessionAudioMediaRemote_) { + if (std::find(codecs.begin(), codecs.end(), c) == codecs.end()) + codecs.push_back(c); + } + DEBUG("Ready to decode %u audio codecs", codecs.size()); + + return codecs; } diff --git a/daemon/src/sip/sdp.h b/daemon/src/sip/sdp.h index d263f64fd168271e0043a131a32a0d2122cde638..96dd173d93f979da618cf86b1c0c7a1be489e1da 100644 --- a/daemon/src/sip/sdp.h +++ b/daemon/src/sip/sdp.h @@ -253,7 +253,6 @@ class Sdp { void setMediaTransportInfoFromRemoteSdp(); - std::string getAudioCodecNames() const; std::string getSessionVideoCodec() const; std::vector<sfl::AudioCodec*> getSessionAudioMedia() const; // Sets @param settings with appropriate values and returns true if @@ -313,7 +312,8 @@ class Sdp { /** * The codecs that will be used by the session (after the SDP negotiation) */ - std::vector<sfl::AudioCodec *> sessionAudioMedia_; + std::vector<sfl::AudioCodec *> sessionAudioMediaLocal_; + std::vector<sfl::AudioCodec *> sessionAudioMediaRemote_; std::vector<std::string> sessionVideoMedia_; std::string publishedIpAddr_; diff --git a/daemon/src/sip/sipcall.cpp b/daemon/src/sip/sipcall.cpp index 4350d21c6810e23e0159497d954e1a1b61a04e5f..3af942e9b81491cb6cf4b570ab7dd0f3de1489de 100644 --- a/daemon/src/sip/sipcall.cpp +++ b/daemon/src/sip/sipcall.cpp @@ -94,16 +94,6 @@ void SIPCall::answer() setState(ACTIVE); } -std::map<std::string, std::string> -SIPCall::getDetails() -{ - std::map<std::string, std::string> details(Call::getDetails()); -#ifdef SFL_VIDEO - videortp_.addReceivingDetails(details); -#endif - return details; -} - std::map<std::string, std::string> SIPCall::createHistoryEntry() const { diff --git a/daemon/src/sip/sipcall.h b/daemon/src/sip/sipcall.h index 3ce4223c174164f31cd35d8de73ac38537b1ced3..bf3106bca8525b1d1d115158da9adfacf7ae6f46 100644 --- a/daemon/src/sip/sipcall.h +++ b/daemon/src/sip/sipcall.h @@ -113,9 +113,6 @@ class SIPCall : public Call { void setContactHeader(pj_str_t *contact); private: - // override of Call::getDetails - std::map<std::string, std::string> - getDetails(); // override of Call::createHistoryEntry std::map<std::string, std::string> diff --git a/daemon/src/sip/sipvoiplink.cpp b/daemon/src/sip/sipvoiplink.cpp index 2b3f8eb66a67f2a6ef9636dec3bb1d4d393cc1fa..cd6d094da2d868c257063fdfaaa49105bccd7fe4 100644 --- a/daemon/src/sip/sipvoiplink.cpp +++ b/daemon/src/sip/sipvoiplink.cpp @@ -489,7 +489,6 @@ pj_bool_t transaction_request_cb(pjsip_rx_data *rdata) } // contactStr must stay in scope as long as tdata - ERROR("transaction_request_cb getContactHeader"); const pj_str_t contactStr(account->getContactHeader()); sip_utils::addContactHeader(&contactStr, tdata); @@ -1061,7 +1060,6 @@ SIPVoIPLink::answer(Call *call) updateSDPFromSTUN(*sipCall, *account, *SIPVoIPLink::instance().sipTransport); } - ERROR("answer getContactHeader"); pj_str_t contact(account->getContactHeader()); sipCall->setContactHeader(&contact); sipCall->answer(); @@ -1125,7 +1123,6 @@ SIPVoIPLink::hangup(const std::string& id, int reason) return; // contactStr must stay in scope as long as tdata - ERROR("hangup getContactHeader"); const pj_str_t contactStr(account->getContactHeader()); sip_utils::addContactHeader(&contactStr, tdata); @@ -1655,7 +1652,9 @@ SIPVoIPLink::SIPStartCall(SIPCall *call) pj_str_t pjContact(account->getContactHeader()); - ERROR("SIPStartCall getContactHeader %s / %s -> %s", std::string(pj_strbuf(&pjContact), pj_strlen(&pjContact)).c_str(), from.c_str(), toUri.c_str()); + const std::string debugContactHeader(pj_strbuf(&pjContact), pj_strlen(&pjContact)); + DEBUG("contact header: %s / %s -> %s", + debugContactHeader.c_str(), from.c_str(), toUri.c_str()); pjsip_dialog *dialog = NULL; @@ -1964,8 +1963,8 @@ void sdp_media_update_cb(pjsip_inv_session *inv, pj_status_t status) DEBUG("Local active SDP Session:\n%s", buffer); // Set active SDP sessions - sdpSession->setActiveRemoteSdpSession(remoteSDP); sdpSession->setActiveLocalSdpSession(local_sdp); + sdpSession->setActiveRemoteSdpSession(remoteSDP); // Update internal field for sdpSession->setMediaTransportInfoFromRemoteSdp(); @@ -2474,8 +2473,6 @@ void setCallMediaLocal(SIPCall* call, const pj_sockaddr& localIP) if (!account) return; - ERROR("setCallMediaLocal %s", ip_utils::addrToStr(localIP).c_str()); - // Reference: http://www.cs.columbia.edu/~hgs/rtp/faq.html#ports // We only want to set ports to new values if they haven't been set if (call->getLocalAudioPort() == 0) { diff --git a/daemon/src/video/test/test_video_input.cpp b/daemon/src/video/test/test_video_input.cpp index ea581de2af46787ea0fd842c8293645abf062bed..95b3ec2ecbf6c6eb18901a7df7120870edb49365 100644 --- a/daemon/src/video/test/test_video_input.cpp +++ b/daemon/src/video/test/test_video_input.cpp @@ -38,8 +38,12 @@ using namespace std; void VideoInputTest::testInput() { - sfl_video::VideoInput camera("/dev/video0"); - sleep(1); + map<string, string> args; + args["input"] = "/dev/video0"; + args["format"] = "video4linux2"; + args["mirror"] = true; + sfl_video::VideoInput camera(args); + usleep(10000); } int main () diff --git a/daemon/src/video/video_decoder.cpp b/daemon/src/video/video_decoder.cpp index 3cca8e2ad17352e17727917eadeb41d0ca7b8aca..d0eab6cb4063c0dd451f4336083464f770a77ba4 100644 --- a/daemon/src/video/video_decoder.cpp +++ b/daemon/src/video/video_decoder.cpp @@ -181,7 +181,9 @@ int VideoDecoder::decode(VideoFrame& result) if (ret == AVERROR(EAGAIN)) { return 0; } else if (ret < 0) { - ERROR("Couldn't read frame: %s\n", strerror(ret)); + char errbuf[64]; + av_strerror(ret, errbuf, sizeof(errbuf)); + ERROR("Couldn't read frame: %s\n", errbuf); return -1; } diff --git a/daemon/src/video/video_encoder.cpp b/daemon/src/video/video_encoder.cpp index c83ec325051555c52efd95d5369a03a981cdfaee..1810c59aa8a741a0b5a3102d3e6f9db9845d07fd 100644 --- a/daemon/src/video/video_encoder.cpp +++ b/daemon/src/video/video_encoder.cpp @@ -247,9 +247,9 @@ int VideoEncoder::encode(VideoFrame &input, bool is_keyframe, int64_t frame_numb pkt.stream_index = stream_->index; // write the compressed frame - ret = av_interleaved_write_frame(outputCtx_, &pkt); + ret = av_write_frame(outputCtx_, &pkt); if (ret < 0) - print_averror("av_interleaved_write_frame", ret); + print_averror("av_write_frame", ret); } #else @@ -279,9 +279,9 @@ int VideoEncoder::encode(VideoFrame &input, bool is_keyframe, int64_t frame_numb pkt.stream_index = stream_->index; // write the compressed frame - ret = av_interleaved_write_frame(outputCtx_, &pkt); + ret = av_write_frame(outputCtx_, &pkt); if (ret < 0) - print_averror("av_interleaved_write_frame", ret); + print_averror("av_write_frame", ret); #endif // LIBAVCODEC_VERSION_MAJOR >= 54 @@ -310,9 +310,9 @@ int VideoEncoder::flush() if (pkt.size and got_packet) { // write the compressed frame - ret = av_interleaved_write_frame(outputCtx_, &pkt); + ret = av_write_frame(outputCtx_, &pkt); if (ret < 0) - ERROR("interleaved_write_frame failed"); + ERROR("write_frame failed"); } #else ret = avcodec_encode_video(encoderCtx_, encoderBuffer_, @@ -327,9 +327,9 @@ int VideoEncoder::flush() pkt.size = ret; // write the compressed frame - ret = av_interleaved_write_frame(outputCtx_, &pkt); + ret = av_write_frame(outputCtx_, &pkt); if (ret < 0) - ERROR("interleaved_write_frame failed"); + ERROR("write_frame failed"); #endif av_free_packet(&pkt); diff --git a/daemon/src/video/video_input.cpp b/daemon/src/video/video_input.cpp index b8394a12d18405e02aa7a2b42de7de9b48c9286e..4d7bcdf2d14d923d13dd93f61349cc5fbabda6c7 100644 --- a/daemon/src/video/video_input.cpp +++ b/daemon/src/video/video_input.cpp @@ -43,27 +43,41 @@ namespace sfl_video { -VideoInput::VideoInput(const std::string& device) : +static std::string +extract(const std::map<std::string, std::string>& map, + const std::string& key) +{ + const auto iter = map.find(key); + + return iter == map.end() ? "" : iter->second; +} + +VideoInput::VideoInput(const std::map<std::string, std::string>& map) : VideoGenerator::VideoGenerator() , id_(SINK_ID) , decoder_(0) , sink_() - , mirror_(true) - - , input_() - , format_() - , channel_() - , framerate_() - , video_size_() + , mirror_(map.find("mirror") != map.end()) + , input_(extract(map, "input")) + , loop_(extract(map, "loop")) + , format_(extract(map, "format")) + , channel_(extract(map, "channel")) + , framerate_(extract(map, "framerate")) + , video_size_(extract(map, "video_size")) { - /* TODO better check for the X11 display name */ - if (device.find(':') != std::string::npos) { - DEBUG("Init screen display %s\n", device.c_str()); - initX11(device); - } else { - DEBUG("Init camera %s\n", device.c_str()); - initCamera(device); - } + DEBUG("initializing video input with: " + "mirror: %s, " + "input: '%s', " + "format: '%s', " + "channel: '%s', " + "framerate: '%s', " + "video_size: '%s'", + mirror_ ? "yes" : "no", + input_.c_str(), + format_.c_str(), + channel_.c_str(), + framerate_.c_str(), + video_size_.c_str()); start(); } @@ -74,38 +88,6 @@ VideoInput::~VideoInput() join(); } -void VideoInput::initCamera(std::string device) -{ - std::map<std::string, std::string> map; - - map = Manager::instance().getVideoControls()->getSettingsFor(device); - - input_ = map["input"]; - format_ = "video4linux2"; - channel_ = map["channel"]; - framerate_ = map["framerate"]; - video_size_ = map["video_size"]; -} - -void VideoInput::initX11(std::string device) -{ - size_t space = device.find(' '); - - if (space != std::string::npos) { - video_size_ = device.substr(space + 1); - input_ = device.erase(space); - } else { - input_ = device; - video_size_ = "vga"; - } - - format_ = "x11grab"; - framerate_ = "25"; - mirror_ = false; - - DEBUG("X11 display name %s (%s)", input_.c_str(), video_size_.c_str()); -} - bool VideoInput::setup() { decoder_ = new VideoDecoder(); @@ -116,6 +98,8 @@ bool VideoInput::setup() decoder_->setOption("video_size", video_size_.c_str()); if (!channel_.empty()) decoder_->setOption("channel", channel_.c_str()); + if (!loop_.empty()) + decoder_->setOption("loop", loop_.c_str()); decoder_->setInterruptCallback(interruptCb, this); diff --git a/daemon/src/video/video_input.h b/daemon/src/video/video_input.h index cb7bea75699a49de42bfcfba8218d3d456054ace..18813783dc2c9b7c6b1a58294a219cf8a2d511aa 100644 --- a/daemon/src/video/video_input.h +++ b/daemon/src/video/video_input.h @@ -51,7 +51,7 @@ class VideoInput : public SFLThread { public: - VideoInput(const std::string& device); + VideoInput(const std::map<std::string, std::string>& map); ~VideoInput(); // as VideoGenerator @@ -68,14 +68,12 @@ private: bool mirror_; std::string input_; + std::string loop_; std::string format_; std::string channel_; std::string framerate_; std::string video_size_; - void initCamera(std::string device); - void initX11(std::string device); - // as SFLThread bool setup(); void process(); diff --git a/daemon/src/video/video_input_selector.cpp b/daemon/src/video/video_input_selector.cpp index 90fc45efe80c59b950ac289e4ca16207b2a9201e..cb828a50050b7c0da31214f33bd3383fcb613691 100644 --- a/daemon/src/video/video_input_selector.cpp +++ b/daemon/src/video/video_input_selector.cpp @@ -35,17 +35,19 @@ #include "manager.h" #include "client/video_controls.h" +#include <unistd.h> + #include <map> #include <string> namespace sfl_video { -VideoInputSelector::VideoInputSelector(const std::string& device) : +VideoInputSelector::VideoInputSelector(const std::string& resource) : VideoFramePassiveReader::VideoFramePassiveReader() , VideoFrameActiveWriter::VideoFrameActiveWriter() - , currentInput_() + , currentInput_(nullptr) { - openInput(device); + switchInput(resource); } VideoInputSelector::~VideoInputSelector() @@ -60,25 +62,112 @@ VideoInputSelector::update(Observable<std::shared_ptr<sfl_video::VideoFrame>>* / } void -VideoInputSelector::openInput(const std::string& device) +VideoInputSelector::openInput(const std::map<std::string, std::string>& map) { - currentInput_ = new VideoInput(device); + currentInput_ = new VideoInput(map); currentInput_->attach(this); } void -VideoInputSelector::closeInput(void) +VideoInputSelector::closeInput() { - currentInput_->detach(this); - delete currentInput_; + if (currentInput_ == nullptr) + return; + + currentInput_->detach(this); + delete currentInput_; + currentInput_ = nullptr; } -void -VideoInputSelector::switchInput(const std::string& device) +static std::map<std::string, std::string> +initCamera(const std::string& device) { - DEBUG("Switching input to %s", device.c_str()); - closeInput(); - openInput(device); + std::map<std::string, std::string> map = + Manager::instance().getVideoControls()->getSettingsFor(device); + + map["format"] = "video4linux2"; + map["mirror"] = "true"; // only the key matters + + return map; +} + +static std::map<std::string, std::string> +initX11(std::string display) +{ + std::map<std::string, std::string> map; + size_t space = display.find(' '); + + if (space != std::string::npos) { + map["video_size"] = display.substr(space + 1); + map["input"] = display.erase(space); + } else { + map["input"] = display; + map["video_size"] = "vga"; + } + + map["format"] = "x11grab"; + map["framerate"] = "25"; + + return map; +} + +static std::map<std::string, std::string> +initFile(std::string path) +{ + size_t dot = path.find_last_of('.'); + std::string ext = dot == std::string::npos ? "" : path.substr(dot + 1); + std::map<std::string, std::string> map; + + /* File exists? */ + if (access(path.c_str(), R_OK) != 0) { + ERROR("file '%s' unavailable\n", path.c_str()); + return map; + } + + /* Supported image? */ + if (ext == "jpeg" || ext == "jpg" || ext == "png") { + map["input"] = path; + map["format"] = "image2"; + map["framerate"] = "1"; + map["loop"] = "1"; + } + + return map; +} + +bool +VideoInputSelector::switchInput(const std::string& resource) +{ + DEBUG("Switching input to MRL '%s'", resource.c_str()); + + // Supported MRL schemes + static const std::string v4l2("v4l2://"); + static const std::string display("display://"); + static const std::string file("file://"); + + std::map<std::string, std::string> map; + + /* Video4Linux2 */ + if (resource.compare(0, v4l2.size(), v4l2) == 0) + map = initCamera(resource.substr(v4l2.size())); + + /* X11 display name */ + else if (resource.compare(0, display.size(), display) == 0) + map = initX11(resource.substr(display.size())); + + /* Pathname */ + else if (resource.compare(0, file.size(), file) == 0) + map = initFile(resource.substr(file.size())); + + /* Unsupported MRL or failed initialization */ + if (map.empty()) { + ERROR("Failed to init input map for MRL '%s'\n", resource.c_str()); + return false; + } + + closeInput(); + openInput(map); + return true; } } // end namespace sfl_video diff --git a/daemon/src/video/video_input_selector.h b/daemon/src/video/video_input_selector.h index 87563518d5140979f66e3f40d21a65168432fdc3..393c4a86ea2163694f79786c3870de8d7c1c7cfb 100644 --- a/daemon/src/video/video_input_selector.h +++ b/daemon/src/video/video_input_selector.h @@ -44,18 +44,18 @@ class VideoInputSelector : public VideoFrameActiveWriter { public: - VideoInputSelector(const std::string& device); + VideoInputSelector(const std::string& resource); ~VideoInputSelector(); /* as of VideoFrameActiveReader (Observer) */ void update(Observable<std::shared_ptr<sfl_video::VideoFrame>>*, std::shared_ptr<VideoFrame>&); - void switchInput(const std::string& device); + bool switchInput(const std::string& resource); private: NON_COPYABLE(VideoInputSelector); - void openInput(const std::string& device); + void openInput(const std::map<std::string, std::string>& map); void closeInput(void); VideoInput *currentInput_; diff --git a/daemon/src/video/video_receive_thread.cpp b/daemon/src/video/video_receive_thread.cpp index a31fb838590736c6ac2bc576201eba53bb76bdd6..e6f71f9fd5df15ee80526460ac54b8873e73d646 100644 --- a/daemon/src/video/video_receive_thread.cpp +++ b/daemon/src/video/video_receive_thread.cpp @@ -207,19 +207,6 @@ bool VideoReceiveThread::decodeFrame() return false; } -void VideoReceiveThread::addReceivingDetails( - std::map<std::string, std::string> &details) -{ - if (isRunning() and dstWidth_ > 0 and dstHeight_ > 0) { - details["VIDEO_SHM_PATH"] = sink_.openedName(); - std::ostringstream os; - os << dstWidth_; - details["VIDEO_WIDTH"] = os.str(); - os.str(""); - os << dstHeight_; - details["VIDEO_HEIGHT"] = os.str(); - } -} void VideoReceiveThread::enterConference() { diff --git a/daemon/src/video/video_receive_thread.h b/daemon/src/video/video_receive_thread.h index 851d9329aba7117de85fe5ca320d22416b8d5165..37b01b6a893302286763274e990120eda8438252 100644 --- a/daemon/src/video/video_receive_thread.h +++ b/daemon/src/video/video_receive_thread.h @@ -54,7 +54,6 @@ public: void addIOContext(SocketPair &socketPair); void setRequestKeyFrameCallback(void (*)(const std::string &)); - void addReceivingDetails(std::map<std::string, std::string> &details); void enterConference(); void exitConference(); diff --git a/daemon/src/video/video_rtp_session.cpp b/daemon/src/video/video_rtp_session.cpp index 4cef369c2254659bb9844b681340107f7f627570..e72a67ef3eee7b4f55e38f67e78dd2fef5235e12 100644 --- a/daemon/src/video/video_rtp_session.cpp +++ b/daemon/src/video/video_rtp_session.cpp @@ -225,13 +225,6 @@ void VideoRtpSession::forceKeyFrame() sender_->forceKeyFrame(); } -void VideoRtpSession::addReceivingDetails(std::map<std::string, std::string> &details) -{ - std::lock_guard<std::mutex> lock(mutex_); - if (receiveThread_) - receiveThread_->addReceivingDetails(details); -} - void VideoRtpSession::setupConferenceVideoPipeline() { std::lock_guard<std::mutex> lock(mutex_); diff --git a/daemon/src/video/video_rtp_session.h b/daemon/src/video/video_rtp_session.h index 7ece5309f89764be5653091235aaee42b2b67ccd..b5cfb9fe678572080dfb54c8c7c7b28a70e6d7ae 100644 --- a/daemon/src/video/video_rtp_session.h +++ b/daemon/src/video/video_rtp_session.h @@ -62,7 +62,6 @@ public: unsigned int port); void updateSDP(const Sdp &sdp); void forceKeyFrame(); - void addReceivingDetails(std::map<std::string, std::string> &details); void bindMixer(VideoMixer* mixer); void unbindMixer(); void setupConferenceVideoPipeline(); diff --git a/daemon/src/video/video_v4l2.cpp b/daemon/src/video/video_v4l2.cpp index 62c04eb829f04ecd1a0fc74580f77ba75b59f6a6..b0daf86fe08fee4305fcd7c3983d19c6b97fe797 100644 --- a/daemon/src/video/video_v4l2.cpp +++ b/daemon/src/video/video_v4l2.cpp @@ -29,6 +29,7 @@ */ #include <string> +#include <cassert> #include <algorithm> #include <vector> #include <climits> @@ -366,6 +367,7 @@ VideoV4l2Device::getChannel(const string &name) const if (item.name == name) return item; + assert(not channels_.empty()); return channels_.back(); } diff --git a/daemon/src/video/video_v4l2_list.cpp b/daemon/src/video/video_v4l2_list.cpp index 3dd59aa1595d8a416813f71bbc60f04292e7df5d..430a55997ddae418ce358773a56e4c3a4bf61efd 100644 --- a/daemon/src/video/video_v4l2_list.cpp +++ b/daemon/src/video/video_v4l2_list.cpp @@ -213,14 +213,34 @@ VideoV4l2ListThread::~VideoV4l2ListThread() void VideoV4l2ListThread::updateDefault() { + if (devices_.empty()) { + ERROR("No devices"); + return; + } + const std::string &name = devices_.back().name; auto controls = Manager::instance().getVideoControls(); controls->setActiveDevice(name); - const auto channel = devices_.back().getChannelList()[0]; + + const auto channelList = devices_.back().getChannelList(); + if (channelList.empty()) { + ERROR("No channel list present"); + return; + } + + const auto channel = channelList[0]; controls->setActiveDeviceChannel(channel); - const auto size = devices_.back().getChannel(name).getSizeList()[0]; + + const auto sizeList = devices_.back().getChannel(name).getSizeList(); + if (sizeList.empty()) { + ERROR("No size list present"); + return; + } + + const auto size = sizeList[0]; controls->setActiveDeviceSize(size); const auto rateList(controls->getDeviceRateList(name, channel, size)); + // compare by integer value const auto highest = std::max_element(rateList.begin(), rateList.end(), [] (const std::string &l, const std::string &r) { diff --git a/gnome/data/org.sflphone.SFLphone.gschema.xml b/gnome/data/org.sflphone.SFLphone.gschema.xml index 64fd2e7431e52302bfbe288bfcb381d06f92836d..f7b5f2c763c589295dd75ff5c0240da97c0faf4f 100644 --- a/gnome/data/org.sflphone.SFLphone.gschema.xml +++ b/gnome/data/org.sflphone.SFLphone.gschema.xml @@ -20,6 +20,46 @@ <summary>Main window Y position</summary> <description>Main window Y position.</description> </key> + <key name="window-video-local-width" type="i"> + <default>200</default> + <summary>Local video window width</summary> + <description>Local video window width.</description> + </key> + <key name="window-video-local-height" type="i"> + <default>150</default> + <summary>Local video window height</summary> + <description>Local video window height.</description> + </key> + <key name="window-video-local-position-x" type="i"> + <default>100</default> + <summary>Local video window X position</summary> + <description>Local video window X position.</description> + </key> + <key name="window-video-local-position-y" type="i"> + <default>250</default> + <summary>Local video window Y position</summary> + <description>Local video window Y position.</description> + </key> + <key name="window-video-remote-width" type="i"> + <default>240</default> + <summary>Remote video window width</summary> + <description>Remote video window width.</description> + </key> + <key name="window-video-remote-height" type="i"> + <default>320</default> + <summary>Remote video window height</summary> + <description>Remote video window height.</description> + </key> + <key name="window-video-remote-position-x" type="i"> + <default>500</default> + <summary>Remote video window X position</summary> + <description>Remote video window X position.</description> + </key> + <key name="window-video-remote-position-y" type="i"> + <default>250</default> + <summary>Remote video window Y position</summary> + <description>Remote video window Y position.</description> + </key> <key name="show-dialpad" type="b"> <default>true</default> <summary>Display dialpad</summary> diff --git a/gnome/pixmaps/sflphone.png b/gnome/pixmaps/sflphone.png new file mode 100644 index 0000000000000000000000000000000000000000..48fcf69b3f5243b40e0807f99e50fa4a162a3768 Binary files /dev/null and b/gnome/pixmaps/sflphone.png differ diff --git a/gnome/src/actions.c b/gnome/src/actions.c index 773c4cde269f1a82aebb730fc3b4c6fbdb9e55e9..b4303f1ae9b7a24e22b42490863c82da3ce36003 100644 --- a/gnome/src/actions.c +++ b/gnome/src/actions.c @@ -1020,31 +1020,38 @@ sflphone_get_display(void) int width = gdk_screen_width(); int height = gdk_screen_height(); char *display = getenv("DISPLAY"); - char device[256]; + char resource[256]; - sprintf(device, "%s %dx%d", display, width, height); - return strdup(device); + sprintf(resource, "display://%s %dx%d", display, width, height); + return g_strdup(resource); } void sflphone_toggle_screenshare(void) { - static int screenshare = TRUE; - gchar *device; + static gboolean screenshare = TRUE; + gboolean switched; + gchar *resource; if (screenshare) { - device = sflphone_get_display(); - - g_debug("enabling screen sharing (%s)", device); - dbus_switch_video_input(device); + resource = sflphone_get_display(); + g_debug("enabling screen sharing (with MRL '%s')", resource); + switched = dbus_switch_video_input(resource); } else { - device = dbus_get_active_video_device(); + gchar *device; - g_debug("restoring camera \"%s\"", device); - dbus_switch_video_input(device); + device = dbus_get_active_video_device(); + resource = g_strconcat("v4l2://", device, NULL); + g_debug("restoring camera '%s' (with MRL '%s'", device, resource); + switched = dbus_switch_video_input(device); + g_free(device); } - g_free(device); - screenshare = !screenshare; + if (switched) + screenshare = !screenshare; + else + g_error("failed to switch to resource '%s'\n", resource); + + g_free(resource); } #endif diff --git a/gnome/src/config/accountconfigdialog.c b/gnome/src/config/accountconfigdialog.c index dc38d9f13b52e25b41be105b8548fb4cb95764a1..84de684870fd3a9ef2150e4093bdbe2b53cc44e1 100644 --- a/gnome/src/config/accountconfigdialog.c +++ b/gnome/src/config/accountconfigdialog.c @@ -1569,7 +1569,7 @@ show_account_window(const gchar *accountID, GtkDialog *parent, SFLPhoneClient *c } else { /* Custom tab for the IP to IP profile */ GtkWidget *ip_tab = create_direct_ip_calls_tab(account); - gtk_notebook_prepend_page(GTK_NOTEBOOK(notebook), ip_tab, gtk_label_new(_("Network"))); + gtk_notebook_prepend_page(GTK_NOTEBOOK(notebook), ip_tab, gtk_label_new(_("General"))); } // Emit signal to hide advanced and security tabs in case of IAX diff --git a/gnome/src/dbus/callmanager-introspec.xml b/gnome/src/dbus/callmanager-introspec.xml index bd97bdff75af2b17f5935f947e7b30318d20f55c..41896bcc992f69c0c4ffbcd902af99195386dc9d 100644 --- a/gnome/src/dbus/callmanager-introspec.xml +++ b/gnome/src/dbus/callmanager-introspec.xml @@ -534,6 +534,23 @@ </arg> </signal> + <method name="getDisplayNames" tp:name-for-bindings="getDisplayNames"> + <tp:added version="1.3.0"/> + <tp:docstring> + Get the display name of every participant in a given conference, or their number as a fallback. + </tp:docstring> + <arg type="s" name="confID" direction="in"> + <tp:docstring> + The conference ID. + </tp:docstring> + </arg> + <arg type="as" name="list" direction="out"> + <tp:docstring> + The list of the display names. + </tp:docstring> + </arg> + </method> + <method name="getParticipantList" tp:name-for-bindings="getParticipantList"> <tp:added version="0.9.7"/> <tp:docstring> diff --git a/gnome/src/dbus/dbus.c b/gnome/src/dbus/dbus.c index d677d824ed7fa2aa0535f26c21551c80dfe6f3c4..f2ae2adf19002ac07575e40bdc3869489dafd898 100644 --- a/gnome/src/dbus/dbus.c +++ b/gnome/src/dbus/dbus.c @@ -569,12 +569,24 @@ zrtp_not_supported_cb(G_GNUC_UNUSED DBusGProxy *proxy, const gchar *callID, SFLP } } + +#ifdef RTCP_DEBUG +static void +print_rtcp_stats(const gchar *key, gint value, G_GNUC_UNUSED gpointer data) +{ + g_debug("%s: %d", key, value); +} +#endif + static void on_rtcp_report_received_cb(G_GNUC_UNUSED DBusGProxy *proxy, const gchar *callID, const GHashTable *stats, SFLPhoneClient *client) { g_debug("Daemon notification of new RTCP report for %s", callID); +#ifdef RTCP_DEBUG + g_hash_table_foreach(stats, print_rtcp_stats, NULL); +#endif } static void @@ -1069,7 +1081,7 @@ gboolean dbus_connect(GError **error, SFLPhoneClient *client) void dbus_clean() { #ifdef SFL_VIDEO - g_object_unref(video_proxy); + g_object_unref(video_proxy); #endif g_object_unref(call_proxy); g_object_unref(config_proxy); @@ -1444,12 +1456,14 @@ dbus_set_video_codecs(const gchar *accountID, const GPtrArray *list) check_error(error); } -void -dbus_switch_video_input(const gchar *device) +gboolean +dbus_switch_video_input(const gchar *resource) { GError *error = NULL; - org_sflphone_SFLphone_VideoControls_switch_input(video_proxy, device, &error); + gboolean switched; + org_sflphone_SFLphone_VideoControls_switch_input(video_proxy, resource, &switched, &error); check_error(error); + return switched; } #endif @@ -1634,12 +1648,7 @@ dbus_set_noise_suppress_state(gboolean state) { GError *error = NULL; org_sflphone_SFLphone_ConfigurationManager_set_noise_suppress_state(config_proxy, state, &error); - - if (error) { - g_warning("Failed to call set_noise_suppress_state() on " - "ConfigurationManager: %s", error->message); - g_error_free(error); - } + check_error(error); } /** @@ -1666,12 +1675,7 @@ dbus_set_agc_state(gboolean state) { GError *error = NULL; org_sflphone_SFLphone_ConfigurationManager_set_agc_state(config_proxy, state, &error); - - if (error) { - g_warning("Failed to call set_agc_state() on " - "ConfigurationManager: %s", error->message); - g_error_free(error); - } + check_error(error); } int @@ -1685,36 +1689,58 @@ dbus_is_iax2_enabled() return res; } +static void +dbus_join_participant_async_cb(G_GNUC_UNUSED DBusGProxy *proxy, + gboolean result, + GError *error, + G_GNUC_UNUSED gpointer data) +{ + check_error(error); + if (!result) + g_warning("Failed to join participant"); +} + void dbus_join_participant(const gchar *sel_callID, const gchar *drag_callID) { - g_debug("Join participant %s and %s\n", sel_callID, drag_callID); - GError *error = NULL; - gboolean result; - org_sflphone_SFLphone_CallManager_join_participant(call_proxy, sel_callID, - drag_callID, &result, &error); + org_sflphone_SFLphone_CallManager_join_participant_async(call_proxy, sel_callID, + drag_callID, dbus_join_participant_async_cb, NULL); +} + +static void +dbus_add_participant_async_cb(G_GNUC_UNUSED DBusGProxy *proxy, + gboolean result, + GError *error, + G_GNUC_UNUSED gpointer data) +{ check_error(error); + if (!result) + g_warning("Failed to add participant"); } void dbus_add_participant(const gchar *callID, const gchar *confID) { - g_debug("Add participant %s to %s\n", callID, confID); - GError *error = NULL; - gboolean result; - org_sflphone_SFLphone_CallManager_add_participant(call_proxy, callID, - confID, &result, &error); + org_sflphone_SFLphone_CallManager_add_participant_async(call_proxy, callID, + confID, dbus_add_participant_async_cb, NULL); +} + +static void +dbus_add_main_participant_async_cb(G_GNUC_UNUSED DBusGProxy *proxy, + gboolean result, + GError *error, + G_GNUC_UNUSED gpointer data) +{ check_error(error); + if (!result) + g_warning("Failed to add main participant"); } void dbus_add_main_participant(const gchar *confID) { - GError *error = NULL; - gboolean result; - org_sflphone_SFLphone_CallManager_add_main_participant(call_proxy, confID, - &result, &error); - check_error(error); + org_sflphone_SFLphone_CallManager_add_main_participant_async(call_proxy, + confID, dbus_add_main_participant_async_cb, NULL); } void @@ -2045,13 +2071,24 @@ dbus_get_conference_list(void) return list; } +gchar ** +dbus_get_display_names(const gchar *confID) +{ + GError *error = NULL; + gchar **list = NULL; + + org_sflphone_SFLphone_CallManager_get_display_names(call_proxy, confID, &list, &error); + check_error(error); + + return list; +} + gchar ** dbus_get_participant_list(const gchar *confID) { GError *error = NULL; gchar **list = NULL; - g_debug("Get conference %s participant list", confID); org_sflphone_SFLphone_CallManager_get_participant_list(call_proxy, confID, &list, &error); check_error(error); @@ -2336,12 +2373,10 @@ dbus_screensaver_uninhibit(void) { if (cookie == 0) return; - g_debug("uninhibit"); GVariant *parameters = g_variant_new("(u)", cookie); if (parameters == NULL) { - g_warning("Could not create session manager uninhibit " - "parameters"); + g_warning("Could not create session manager uninhibit parameters"); return; } @@ -2356,7 +2391,6 @@ void dbus_presence_publish(const gchar *accountID, gboolean status) { GError *error = NULL; - g_debug("DBus: publish presence status."); org_sflphone_SFLphone_PresenceManager_publish(presence_proxy, accountID,status, "Tout va bien.", NULL); check_error(error); } @@ -2364,7 +2398,6 @@ dbus_presence_publish(const gchar *accountID, gboolean status) void dbus_presence_subscribe(const gchar *accountID, const gchar *uri, gboolean flag) { - g_debug("DBus: subscrbe presence status %s:%s.", uri, flag? "true" : "false"); GError *error = NULL; org_sflphone_SFLphone_PresenceManager_subscribe_buddy(presence_proxy, accountID, uri, flag, NULL); check_error(error); diff --git a/gnome/src/dbus/dbus.h b/gnome/src/dbus/dbus.h index cabd7065c477093cbc6b3f5608bfb24ea258c852..27112e298517ee5a2c9e3260c5f287e5a9a7e9be 100644 --- a/gnome/src/dbus/dbus.h +++ b/gnome/src/dbus/dbus.h @@ -259,10 +259,10 @@ dbus_set_video_codecs(const gchar *id, const GPtrArray *list); /** * ConfigurationManager - Switch the video input - * @param device The video device name to switch to + * @param resource A media resource locator (MRL) to switch to */ -void -dbus_switch_video_input(const gchar *device); +gboolean +dbus_switch_video_input(const gchar *resource); /** * ConfigurationManager - Get the list of available output audio plugins @@ -428,6 +428,12 @@ gint dbus_get_sip_address(void); */ void dbus_add_participant(const gchar *callID, const gchar *confID); +/** + * Return a list of display names for this conference (confID) + */ +gchar ** +dbus_get_display_names(const gchar *confID); + /** * Return a list of participant for this conference (confID) */ diff --git a/gnome/src/dbus/video_controls-introspec.xml b/gnome/src/dbus/video_controls-introspec.xml index c95cb19af8c7ab3ec6da81281fb66c05ac60bbb1..16cad00957bba2de5bbff4dc4f6c5bf2c2829fb1 100644 --- a/gnome/src/dbus/video_controls-introspec.xml +++ b/gnome/src/dbus/video_controls-introspec.xml @@ -111,7 +111,18 @@ </method> <method name="switchInput" tp:name-for-bindings="switchInput"> - <arg type="s" name="device" direction="in"> + <arg type="s" name="resource" direction="in"> + <tp:docstring> + A media resource locator (MRL). + Currently, the following are supported: + <ul> + <li>v4l2://DEVICE</li> + <li>display://DISPLAY_NAME[ WIDTHxHEIGHT]</li> + </ul> + </tp:docstring> + </arg> + <arg type="b" name="switched" direction="out"> + <tp:docstring>Returns true if the input stream was successfully changed, false otherwise</tp:docstring> </arg> </method> diff --git a/gnome/src/uimanager.c b/gnome/src/uimanager.c index e1b285c383e121b0bea8776171fdd90d0635989a..2e0417d714421e686627eb051494ff268b4b28a4 100644 --- a/gnome/src/uimanager.c +++ b/gnome/src/uimanager.c @@ -691,14 +691,24 @@ call_screenshare(G_GNUC_UNUSED GtkAction *action, G_GNUC_UNUSED SFLPhoneClient * static void call_switch_video_input(G_GNUC_UNUSED GtkWidget *widget, gchar *device) { - if (strcmp(device, "Screen") == 0) { - gchar *display = sflphone_get_display(); - dbus_switch_video_input(display); - g_free(display); + gboolean switched; + gchar *resource; + + if (g_strcmp0(device, "None") == 0) { + resource = g_strconcat("file://", ICONS_DIR "/sflphone.png", NULL); + switched = dbus_switch_video_input(resource); + } else if (g_strcmp0(device, "Screen") == 0) { + resource = sflphone_get_display(); + switched = dbus_switch_video_input(resource); } else { dbus_set_active_video_device(device); - dbus_switch_video_input(device); + resource = g_strconcat("v4l2://", device, NULL); + switched = dbus_switch_video_input(resource); } + + if (!switched) + g_warning("Failed to switch to '%s' (MRL '%s')\n", device, resource); + g_free(resource); } #endif @@ -1466,6 +1476,8 @@ show_popup_menu(GtkWidget *my_widget, GdkEventButton *event, SFLPhoneClient *cli } /* Add the special X11 device */ append_video_input_to_submenu(video_menu, "Screen"); + /* Add a None entry, which will display the client logo */ + append_video_input_to_submenu(video_menu, "None"); } #endif } else { diff --git a/gnome/src/video/video_callbacks.c b/gnome/src/video/video_callbacks.c index 0744edaddde5caf27e60fc7681386afe9e69e437..8accb4d9a61f01e8aae7407f9d812d2062bf5b8b 100644 --- a/gnome/src/video/video_callbacks.c +++ b/gnome/src/video/video_callbacks.c @@ -30,6 +30,7 @@ #include "video_callbacks.h" #include "video_renderer.h" +#include "sflphone_client.h" /* gsettings schema path */ #include "config/videoconf.h" #include <clutter/clutter.h> @@ -46,6 +47,7 @@ typedef enum { typedef struct { gchar *id; GtkWidget *window; + GSettings *settings; gboolean fullscreen; } VideoHandle; @@ -109,9 +111,14 @@ cleanup_handle(gpointer data) g_free(h->id); } + g_object_unref(h->settings); + g_free(h); } +/* + * Handle destroy event in the video windows. + */ static void video_window_deleted_cb(G_GNUC_UNUSED GtkWidget *widget, G_GNUC_UNUSED gpointer data) @@ -121,7 +128,39 @@ video_window_deleted_cb(G_GNUC_UNUSED GtkWidget *widget, } /* - * Handle button event in the video window + * Handle resizing and moving event in the video windows. + * This is usefull to store the previous behaviour and restore the user + * preferences using gsettings. + */ +static gboolean +video_window_configure_cb(GtkWidget *widget, + GdkEventConfigure *event, + gpointer data) +{ + VideoHandle *handle = (VideoHandle *) data; + + gint pos_x, pos_y; + + gtk_window_get_position(GTK_WINDOW(widget), &pos_x, &pos_y); + + if (video_is_local(handle->id)) { + g_settings_set_int(handle->settings, "window-video-local-width", event->width); + g_settings_set_int(handle->settings, "window-video-local-height", event->height); + g_settings_set_int(handle->settings, "window-video-local-position-x", pos_x); + g_settings_set_int(handle->settings, "window-video-local-position-y", pos_y); + } else { + g_settings_set_int(handle->settings, "window-video-remote-width", event->width); + g_settings_set_int(handle->settings, "window-video-remote-height", event->height); + g_settings_set_int(handle->settings, "window-video-remote-position-x", pos_x); + g_settings_set_int(handle->settings, "window-video-remote-position-y", pos_y); + } + + /* let the event propagate otherwise the video will not be re-scaled */ + return FALSE; +} + +/* + * Handle button event in the video windows. */ static void video_window_button_cb(GtkWindow *win, @@ -133,7 +172,6 @@ video_window_button_cb(GtkWindow *win, if (event->type == GDK_2BUTTON_PRESS) { /* Fullscreen switch on/off */ - g_debug("TOGGLING FULL SCREEEN!"); handle->fullscreen = !handle->fullscreen; if (handle->fullscreen) @@ -158,10 +196,30 @@ add_handle(const gchar *id) } VideoHandle *handle = g_new0(VideoHandle, 1); + handle->id = g_strdup(id); handle->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + handle->settings = g_settings_new(SFLPHONE_GSETTINGS_SCHEMA); handle->fullscreen = FALSE; + /* Get configuration stored in GSettings */ + gint width, height, pos_x, pos_y; + if (video_is_local(id)) { + width = g_settings_get_int(handle->settings, "window-video-local-width"); + height = g_settings_get_int(handle->settings, "window-video-local-height"); + pos_x = g_settings_get_int(handle->settings, "window-video-local-position-x"); + pos_y = g_settings_get_int(handle->settings, "window-video-local-position-y"); + } else { + width = g_settings_get_int(handle->settings, "window-video-remote-width"); + height = g_settings_get_int(handle->settings, "window-video-remote-height"); + pos_x = g_settings_get_int(handle->settings, "window-video-remote-position-x"); + pos_y = g_settings_get_int(handle->settings, "window-video-remote-position-y"); + } + + /* Restore the previous setting for the video size and position */ + gtk_window_set_default_size(GTK_WINDOW(handle->window), width, height); + gtk_window_move(GTK_WINDOW(handle->window), pos_x, pos_y); + /* handle button event */ g_signal_connect(handle->window, "button_press_event", G_CALLBACK(video_window_button_cb), @@ -172,6 +230,11 @@ add_handle(const gchar *id) G_CALLBACK(video_window_deleted_cb), NULL); + /* handle configure event */ + g_signal_connect(handle->window, "configure-event", + G_CALLBACK(video_window_configure_cb), + handle); + /* Preview video */ if (video_is_local(id)) { @@ -194,57 +257,27 @@ add_handle(const gchar *id) title_prefix = _("Conference with"); /* get all the participants name */ - gchar **participant_list = dbus_get_participant_list(id); - for (gchar **participant = participant_list; participant && *participant; participant++) { - g_debug("participant %s\n", *participant); - - gchar *new_name = NULL; - - /* create a new callable object to manipulate the id details */ - callable_obj_t *c = create_new_call_from_details(*participant, dbus_get_call_details(*participant)); - - /* if no display name, we show the peer_number */ - if (g_strcmp0(c->_display_name, "") != 0) { - new_name = g_strdup(c->_display_name); - } else { - new_name = g_strdup(c->_peer_number); - } - - /* if name exists we must add the new name to the list */ - if (name) { - gchar *name_list = g_strdup_printf("%s, %s", name, new_name); - g_free(name); - name = name_list; - } else { - name = g_strdup(new_name); - } - - g_free(new_name); - g_free(c); - } - - g_strfreev(participant_list); + gchar **display_names = dbus_get_display_names(id); + name = g_strjoinv(", ", display_names); + g_strfreev(display_names); } else if (call_type == IS_CALL) { /* on a simple call */ - /* create a new callable object to manipulate the call details */ - callable_obj_t *c = create_new_call_from_details(id, dbus_get_call_details(id)); + GHashTable *details = dbus_get_call_details(id); /* build the prefix title name */ title_prefix = _("Call with"); /* if no display name, we show the peer_number */ - if (g_strcmp0(c->_display_name, "") != 0) { - name = g_strdup(c->_display_name); - } else { - name = g_strdup(c->_peer_number); - } - - g_free(c); + const gchar *display_name = g_hash_table_lookup(details, "DISPLAY_NAME"); + if (strlen(display_name) != 0) + name = g_strdup(display_name); + else + name = g_strdup(g_hash_table_lookup(details, "PEER_NUMBER")); } /* build the final title name */ - window_title = g_strdup_printf("%s %s", title_prefix, name); + window_title = g_strjoin(" ", title_prefix, name, NULL); /* update the window title */ gtk_window_set_title(GTK_WINDOW(handle->window), window_title); @@ -318,11 +351,6 @@ started_decoding_video_cb(G_GNUC_UNUSED DBusGProxy *proxy, GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6); gtk_container_add(GTK_CONTAINER(vbox), video_area); - if (video_is_local(id)) - gtk_window_set_default_size(GTK_WINDOW(video_area), 200, 150); - else - gtk_window_set_default_size(GTK_WINDOW(video_area), width, height); - if (handle) { gtk_container_add(GTK_CONTAINER(handle->window), vbox); gtk_widget_show_all(handle->window);