diff --git a/JitsiMeetJS.js b/JitsiMeetJS.js
index 016eeed52..cca2e9e34 100644
--- a/JitsiMeetJS.js
+++ b/JitsiMeetJS.js
@@ -3,6 +3,7 @@ var JitsiConferenceEvents = require("./JitsiConferenceEvents");
 var JitsiConnectionEvents = require("./JitsiConnectionEvents");
 var JitsiConnectionErrors = require("./JitsiConnectionErrors");
 var JitsiConferenceErrors = require("./JitsiConferenceErrors");
+var RTC = require("./modules/RTC/RTC");
 
 /**
  * Namespace for the interface of Jitsi Meet Library.
@@ -19,10 +20,15 @@ var LibJitsiMeet = {
         connection: JitsiConnectionErrors
     },
     init: function (options) {
-        require("./modules/RTC/RTC").init(options || {});
+        RTC.init(options || {});
+    },
+    isDeviceListAvailable: function () {
+        return RTC.isDeviceListAvailable();
+    },
+    enumerateDevices: function (callback) {
+        RTC.enumerateDevices(callback);
     }
-
-}
+};
 
 //Setups the promise object.
 window.Promise = window.Promise || require("es6-promise").polyfill();
diff --git a/modules/RTC/RTC.js b/modules/RTC/RTC.js
index 2de2b88be..de6cf91eb 100644
--- a/modules/RTC/RTC.js
+++ b/modules/RTC/RTC.js
@@ -211,6 +211,18 @@ RTC.getVideoSrc = function (element) {
     return RTCUtils.getVideoSrc(element);
 };
 
+RTC.isDeviceListAvailable = function () {
+    return RTCUtils.isDeviceListAvailable();
+};
+
+/**
+ * Allows to receive list of available cameras/microphones.
+ * @param {function} callback would receive array of devices as an argument
+ */
+RTC.enumerateDevices = function (callback) {
+    RTCUtils.enumerateDevices(callback);
+};
+
 RTC.setVideoSrc = function (element, src) {
     RTCUtils.setVideoSrc(element, src);
 };
diff --git a/modules/RTC/RTCUtils.js b/modules/RTC/RTCUtils.js
index 785f6665c..887e251bd 100644
--- a/modules/RTC/RTCUtils.js
+++ b/modules/RTC/RTCUtils.js
@@ -1,4 +1,5 @@
-/* global config, require, attachMediaStream, getUserMedia */
+/* global config, require, attachMediaStream, getUserMedia,
+ MediaStreamTrack, mozRTCPeerConnection */
 var RTCBrowserType = require("./RTCBrowserType");
 var Resolutions = require("../../service/RTC/Resolutions");
 var AdapterJS = require("./adapter.screenshare");
@@ -141,6 +142,131 @@ function getConstraints(um, resolution, bandwidth, fps, desktopStream) {
     return constraints;
 }
 
+/**
+ * Apply function with arguments if function exists.
+ * Do nothing if function not provided.
+ * @param {function} [fn] function to apply
+ * @param {Array} [args=[]] arguments for function
+ */
+function maybeApply(fn, args) {
+  if (fn) {
+    fn.apply(null, args || []);
+  }
+}
+
+var getUserMediaStatus = {
+  initialized: false,
+  callbacks: []
+};
+
+/**
+ * Wrap `getUserMedia` to allow others to know if it was executed at least
+ * once or not. Wrapper function uses `getUserMediaStatus` object.
+ * @param {Function} getUserMedia native function
+ * @returns {Function} wrapped function
+ */
+function wrapGetUserMedia(getUserMedia) {
+  return function (constraints, successCallback, errorCallback) {
+    getUserMedia(constraints, function (stream) {
+      maybeApply(successCallback, [stream]);
+      if (!getUserMediaStatus.initialized) {
+        getUserMediaStatus.initialized = true;
+        getUserMediaStatus.callbacks.forEach(function (callback) {
+          callback();
+        });
+        getUserMediaStatus.callbacks.length = 0;
+      }
+    }, function (error) {
+      maybeApply(errorCallback, [error]);
+    });
+  };
+}
+
+/**
+ * Create stub device which equals to auto selected device.
+ * @param {string} kind if that should be `audio` or `video` device
+ * @returns {Object} stub device description in `enumerateDevices` format
+ */
+function createAutoDeviceInfo(kind) {
+    return {
+        facing: null,
+        label: 'Auto',
+        kind: kind,
+        deviceId: '',
+        groupId: null
+    };
+}
+
+
+/**
+ * Execute function after getUserMedia was executed at least once.
+ * @param {Function} callback function to execute after getUserMedia
+ */
+function afterUserMediaInitialized(callback) {
+    if (getUserMediaStatus.initialized) {
+        callback();
+    } else {
+        getUserMediaStatus.callbacks.push(callback);
+    }
+}
+
+/**
+ * Wrapper function which makes enumerateDevices to wait
+ * until someone executes getUserMedia first time.
+ * @param {Function} enumerateDevices native function
+ * @returns {Funtion} wrapped function
+ */
+function wrapEnumerateDevices(enumerateDevices) {
+    return function (callback) {
+        // enumerate devices only after initial getUserMedia
+        afterUserMediaInitialized(function () {
+
+            enumerateDevices().then(function (devices) {
+                //add auto devices
+                devices.unshift(
+                    createAutoDeviceInfo('audioinput'),
+                    createAutoDeviceInfo('videoinput')
+                );
+
+                callback(devices);
+            }, function (err) {
+                console.error('cannot enumerate devices: ', err);
+
+                // return only auto devices
+                callback([createAutoDeviceInfo('audioInput'),
+                          createAutoDeviceInfo('videoinput')]);
+            });
+        });
+    };
+}
+
+/**
+ * Use old MediaStreamTrack to get devices list and
+ * convert it to enumerateDevices format.
+ * @param {Function} callback function to call when received devices list.
+ */
+function enumerateDevicesThroughMediaStreamTrack (callback) {
+    MediaStreamTrack.getSources(function (sources) {
+        var devices = sources.map(function (source) {
+            var kind = (source.kind || '').toLowerCase();
+            return {
+                facing: source.facing || null,
+                label: source.label,
+                kind: kind ? kind + 'input': null,
+                deviceId: source.id,
+                groupId: source.groupId || null
+            };
+        });
+
+        //add auto devices
+        devices.unshift(
+            createAutoDeviceInfo('audioinput'),
+            createAutoDeviceInfo('videoinput')
+        );
+        callback(devices);
+    });
+}
+
 //Options parameter is to pass config options. Currently uses only "useIPv6".
 var RTCUtils = {
     eventEmitter: new EventEmitter(),
@@ -150,7 +276,10 @@ var RTCUtils = {
             var FFversion = RTCBrowserType.getFirefoxVersion();
             if (FFversion >= 40) {
                 this.peerconnection = mozRTCPeerConnection;
-                this.getUserMedia = navigator.mozGetUserMedia.bind(navigator);
+                this.getUserMedia = wrapGetUserMedia(navigator.mozGetUserMedia.bind(navigator));
+                this.enumerateDevices = wrapEnumerateDevices(
+                    navigator.mediaDevices.enumerateDevices.bind(navigator.mediaDevices)
+                );
                 this.pc_constraints = {};
                 this.attachMediaStream = function (element, stream) {
                     //  srcObject is being standardized and FF will eventually
@@ -196,7 +325,16 @@ var RTCUtils = {
 
         } else if (RTCBrowserType.isChrome() || RTCBrowserType.isOpera()) {
             this.peerconnection = webkitRTCPeerConnection;
-            this.getUserMedia = navigator.webkitGetUserMedia.bind(navigator);
+            var getUserMedia = navigator.webkitGetUserMedia.bind(navigator);
+            if (navigator.mediaDevices) {
+                this.getUserMedia = wrapGetUserMedia(getUserMedia);
+                this.enumerateDevices = wrapEnumerateDevices(
+                    navigator.mediaDevices.enumerateDevices.bind(navigator.mediaDevices)
+                );
+            } else {
+                this.getUserMedia = getUserMedia;
+                this.enumerateDevices = enumerateDevicesThroughMediaStreamTrack;
+            }
             this.attachMediaStream = function (element, stream) {
                 element.attr('src', webkitURL.createObjectURL(stream));
             };
@@ -246,6 +384,7 @@ var RTCUtils = {
 
                 self.peerconnection = RTCPeerConnection;
                 self.getUserMedia = getUserMedia;
+                self.enumerateDevices = enumerateDevicesThroughMediaStreamTrack;
                 self.attachMediaStream = function (elSel, stream) {
 
                     if (stream.id === "dummyAudio" || stream.id === "dummyVideo") {
@@ -585,7 +724,20 @@ var RTCUtils = {
         }
 
         return newStream;
+    },
+
+    /**
+     * Checks if its possible to enumerate available cameras/micropones.
+     * @returns {boolean} true if available, false otherwise.
+     */
+    isDeviceListAvailable: function () {
+        var isEnumerateDevicesAvailable = navigator.mediaDevices && navigator.mediaDevices.enumerateDevices;
+        if (isEnumerateDevicesAvailable) {
+            return true;
+        }
+
+        return MediaStreamTrack && MediaStreamTrack.getSources;
     }
-}
+};
 
 module.exports = RTCUtils;