22

I am studying webRTC application.

My reference is this software

apprtc https://code.google.com/p/webrtc/source/browse/trunk/samples/js/apprtc/

demo https://apprtc.appspot.com/

My computer has bult-in video device and apprtc uses this video device . However I want to use USB-video camera instead.

I am searching the way to change input video devices. But I couldn't find any clue in source files.

does anyone has information?

PJunior
  • 2,649
  • 1
  • 33
  • 29
whitebear
  • 11,200
  • 24
  • 114
  • 237
  • 1
    Generally, when you first load the page and it asks you for permission to use a device, you can choose the device you want to use from a drop-down menu. Do you not get that option? What browser are you using? – HartleySan Jan 30 '13 at 22:29
  • To do this from javascript, here is the answer: http://stackoverflow.com/questions/20076377 – Aki Nov 27 '13 at 17:41

4 Answers4

20

On Chrome:

chrome://settings/content/camera
chrome://settings/content/microphone

enter image description here

On Firefox: media.navigator.permission.disabled=false

enter image description here

Muaz Khan
  • 7,138
  • 9
  • 41
  • 77
  • Can I ask you the version of Chrome ,my version is24.0.1312.57.However my one doesnt have microphone,camera select tab at 'Media' – whitebear Jan 31 '13 at 18:26
  • 1
    Sorry, it was chrome canary. On chrome stable, I'm unable to find such option. But I still be able to choose camera(s) from the notification bar when a webpage tries to access the camera. – Muaz Khan Feb 01 '13 at 02:10
  • I can find device select menu as you told.it sloves the problem thanks Muaz!! – whitebear Feb 01 '13 at 10:05
  • I first thought it was controllable from script.This is little bit disappointed design of webRTC – whitebear Feb 01 '13 at 10:07
  • 1
    What about on mobile devices? I cannot find this menu in chrome mobile (vanilla Android 4.3, Chrome 28) – Muki Aug 01 '13 at 07:52
  • Update today from the Chromium team: They're working on the spec to be able to select output devices, which is really needed to allow users to select a device without needing to dig through complicated Chrome settings. Anyone needing such a feature should star these issues: http://crbug.com/388648 and https://code.google.com/p/webrtc/issues/detail?id=2243 – jamesmortensen Jul 29 '14 at 16:51
18

Try this demo which is capturing all audio/video input devices:

https://www.webrtc-experiment.com/demos/MediaStreamTrack.getSources.html

You can capture any "specific" device using same API.

Edited at March 01, 2014:

MediaStreamTrack.getSources(function (media_sources) {
    for (var i = 0; i < media_sources.length; i++) {
        var media_source = media_sources[i];
        var constraints = {};

        // if audio device
        if (media_source.kind == 'audio') {
            constraints.audio = {
                optional: [{
                    sourceId: media_source.id
                }]
            };
        }

        // if video device
        if (media_source.kind == 'video') {
            constraints.video = {
                optional: [{
                    sourceId: media_source.id
                }]
            };
        }


        // invoke getUserMedia to capture this device
        navigator.webkitGetUserMedia(constraints, function (stream) {
            console.log(stream.id, stream);
        }, console.error);
    }
});

Updated at Sep 05, 2015:

Now Microsoft Edge, Chrome 44+, Firefox 38+, all these browsers are supporting navigator.mediaDevices.enumerateDevices API.

Here is a reusable script that provides cross-browser shim for all these media-sources APIs. It will work even in old-chrome (43 and older) (even on Android devices):

if (navigator.mediaDevices && navigator.mediaDevices.enumerateDevices) {
    // Firefox 38+, Microsoft Edge, and Chrome 44+ seems having support of enumerateDevices
    navigator.enumerateDevices = function(callback) {
        navigator.mediaDevices.enumerateDevices().then(callback);
    };
}

function getAllAudioVideoDevices(successCallback, failureCallback) {
    if (!navigator.enumerateDevices && window.MediaStreamTrack && window.MediaStreamTrack.getSources) {
        navigator.enumerateDevices = window.MediaStreamTrack.getSources.bind(window.MediaStreamTrack);
    }

    if (!navigator.enumerateDevices && navigator.mediaDevices.enumerateDevices) {
        navigator.enumerateDevices = navigator.mediaDevices.enumerateDevices.bind(navigator);
    }

    if (!navigator.enumerateDevices) {
        failureCallback(null, 'Neither navigator.mediaDevices.enumerateDevices NOR MediaStreamTrack.getSources are available.');
        return;
    }

    var allMdiaDevices = [];
    var allAudioDevices = [];
    var allVideoDevices = [];

    var audioInputDevices = [];
    var audioOutputDevices = [];
    var videoInputDevices = [];
    var videoOutputDevices = [];

    navigator.enumerateDevices(function(devices) {
        devices.forEach(function(_device) {
            var device = {};
            for (var d in _device) {
                device[d] = _device[d];
            }

            // make sure that we are not fetching duplicate devics
            var skip;
            allMdiaDevices.forEach(function(d) {
                if (d.id === device.id) {
                    skip = true;
                }
            });

            if (skip) {
                return;
            }

            // if it is MediaStreamTrack.getSources
            if (device.kind === 'audio') {
                device.kind = 'audioinput';
            }

            if (device.kind === 'video') {
                device.kind = 'videoinput';
            }

            if (!device.deviceId) {
                device.deviceId = device.id;
            }

            if (!device.id) {
                device.id = device.deviceId;
            }

            if (!device.label) {
                device.label = 'Please invoke getUserMedia once.';
            }

            if (device.kind === 'audioinput' || device.kind === 'audio') {
                audioInputDevices.push(device);
            }

            if (device.kind === 'audiooutput') {
                audioOutputDevices.push(device);
            }

            if (device.kind === 'videoinput' || device.kind === 'video') {
                videoInputDevices.push(device);
            }

            if (device.kind.indexOf('audio') !== -1) {
                allAudioDevices.push(device);
            }

            if (device.kind.indexOf('video') !== -1) {
                allVideoDevices.push(device);
            }

            // there is no 'videoouput' in the spec.
            // so videoOutputDevices will always be [empty]

            allMdiaDevices.push(device);
        });

        if (successCallback) {
            successCallback({
                allMdiaDevices: allMdiaDevices,
                allVideoDevices: allVideoDevices,
                allAudioDevices: allAudioDevices,
                videoInputDevices: videoInputDevices,
                audioInputDevices: audioInputDevices,
                audioOutputDevices: audioOutputDevices
            });
        }
    });
}

Here is how to use above reusable cross-browser shim:

getAllAudioVideoDevices(function(result) {
    if (result.allMdiaDevices.length) {
        console.debug('Number of audio/video devices available:', result.allMdiaDevices.length);
    }

    if (result.allVideoDevices.length) {
        console.debug('Number of video devices available:', result.allVideoDevices.length);
    }

    if (result.allAudioDevices.length) {
        console.debug('Number of audio devices available:', result.allAudioDevices.length);
    }

    if (result.videoInputDevices.length) {
        console.debug('Number of video-input devices available:', result.videoInputDevices.length);
    }

    if (result.audioInputDevices.length) {
        console.debug('Number of audio-input devices available:', result.audioInputDevices.length);
    }

    if (result.audioOutputDevices.length) {
        console.debug('Number of audio-output devices available:', result.audioOutputDevices.length);
    }

    if (result.allMdiaDevices.length && result.allMdiaDevices[0].label === 'Please invoke getUserMedia once.') {
        console.warn('It seems you did not invoke navigator-getUserMedia before using these API.');
    }

    console.info('All audio input devices:');
    result.audioInputDevices.forEach(function(device) {
        console.log('Audio input device id:', device.id, 'Device label:', device.label);
    });

    console.info('All audio output devices:');
    result.audioOutputDevices.forEach(function(device) {
        console.log('Audio output device id:', device.id, 'Device label:', device.label);
    });

    console.info('All video input devices:');
    result.videoInputDevices.forEach(function(device) {
        console.log('Video input device id:', device.id, 'Device label:', device.label);
    });
}, function(error) {
    alert(error);
});
mscdex
  • 104,356
  • 15
  • 192
  • 153
Muaz Khan
  • 7,138
  • 9
  • 41
  • 77
  • Hey Muaz, I think you need a closure in your getMediaDevices. It looks like the same microphone is captured each time (and you can tell because the indexes of all the mics are the same as the last index in the array. Hope this helps. Also, to make this a better Stack Overflow answer, you should really edit this and include the code in the body of your post. If your link ever breaks, this answer is useless to future visitors. Hope this helps. – jamesmortensen Mar 01 '14 at 01:31
  • Thanks for your edit and for your excellent, helpful demos! The MediaStreamTrack.getSources works exactly how I was hoping it would. +1 – jamesmortensen Mar 03 '14 at 20:06
  • @Muaz khan. when i follow the above mentioned methods i get this Error: Failed to execute 'getSources' on 'MediaStreamTrack': Failed to execute 'getSources' on 'MediaStreamTrack': Functionality not implemented yet . Does this mean it is yet to implemented ? – Hanu Mar 11 '14 at 13:06
  • Chances are that you're using chrome-beta on android; or you're using Firefox or you're using chrome M30 or earlier. As far as I know, these API are supported in chromium, yet. Maybe you enabled: chrome://flags/#disable-device-enumeration This flag is disabled by default. – Muaz Khan Mar 11 '14 at 14:19
  • @SamDutton your example uses MediaStreamTrack.getSources() which is deprecated as of 2016-09-23. As far I know,in his code example above Muaz Khan is not using MediaStreamTrack.getSources(), but MediaDevices.enumerateDevices() which is correct and active. – Chiwda May 24 '17 at 16:38
  • 1
    @Chiwda Thanks for heads-up — I haven't looked at that example for a while. Will fix! https://webrtc.github.io/samples has demos using the new API/methods/syntax. – Sam Dutton Jun 16 '17 at 09:28
2

It turns out that Chrome does support MediaStreamTrack API which allows you to do this. In Firefox this API is still experimental. Here is the Chrome implementation:

if (typeof MediaStreamTrack === 'undefined'){
  alert('This browser does not support MediaStreamTrack.\n\nTry Chrome Canary.');
} else {
  MediaStreamTrack.getSources( onSourcesAcquired);
}

function onSourcesAcquired(sources) {
  for (var i = 0; i != sources.length; ++i) {
    var source = sources[i];
    // source.id -> DEVICE ID
    // source.label -> DEVICE NAME
    // source.kind = "audio" OR "video"
    // TODO: add this to some datastructure of yours or a selection dialog
  }
}

....

And then when calling getUserMedia, specify the id in the constraints:

var constraints = {
  audio: {
    optional: [{sourceId: selected_audio_source_id}]
  },
  video: {
    optional: [{sourceId: selected_video_source_id}]
  }
};
navigator.getUserMedia(constraints, onSuccessCallback, onErrorCallback);
Aki
  • 3,709
  • 2
  • 29
  • 37
1

It sounds to me you are looking for facingMode. You can check it out in this document: http://www.w3.org/TR/2013/WD-mediacapture-streams-20130516/#idl-def-AllVideoCapabilities

Not sure how well it is supported yet though.

Mikael Holmgren
  • 2,466
  • 1
  • 18
  • 27
  • 1
    Not supported yet on any browser as far as I know, but implementation is planned. – Sam Dutton Nov 21 '13 at 01:38
  • 1
    **Update:** `facingMode` is available in Firefox for Android (since 26) and is now available in the [adapter.js](https://github.com/webrtc/adapter) polyfill for Chrome for Android. See demo in [this answer to a similar question](http://stackoverflow.com/a/32364912/918910). – jib May 26 '16 at 18:43