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(&current->desc.media, "audio")) {
                 const unsigned long pt = pj_strtoul(&current->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);