There’s a new feature that you can use on your custom UI with eyeson-js since v1.8.6.

Picture-in-picture camera is a nice addition to screenshare presentation as it includes the presenter’s camera in the screenshare.

Support

It is only supported in chromium-based browsers like Chrome, Edge, Opera, etc. on desktop. So it will not work with Firefox and Safari whether on desktop nor on mobile.

The reason for Firefox is that it doesn’t support the picture-in-picture API. And the reason for Safari is that it needs multiple user guestures to allow programmatic access to getUserMedia and picture-in-picture API.

It’s not supported on mobile devices simply because browser screenshare is not available on mobile devices.

How it works

When a screenshare presentation is started, you need to check the type of screenshare. There are different types available: “monitor”, “window”, “browser”. Picture-in-picture camera only makes sense in combination with “monitor”, because otherwise it will not be part of the screenshare.

The presentation_started event includes the new parameter present. In case it is set to screen-monitor and the check for FeatureDetector.hasPipCamSupport() is successfull, you can call eyeson.send({ type: 'start_pip_cam' }).

It can be stopped with eyeson.send({ type: 'stop_pip_cam' }).

New events

{ type: 'pip_cam', active: bool }
It indicates the current state of pip-cam and is triggered whenever the status changes.

{ type: 'pip_cam_error' }
Triggered when pip-cam start has failed.

{ type: 'pip_cam_warning_video_ended' }
Triggered when an active pip-cam suddenly stopped working.

{ type: 'pip_cam_warning_unsupported' }
Triggered when pip-cam is started on an unsupported environment.

Show me the code

Here’s an example that shows how the picture-in-picture camera feature can be combined with screenshare presentation.

Please note that this is just a very basic example for demonstration purpose.

import eyeson, { FeatureDetector } from 'eyeson';

const pipCamButton = document.querySelector('...');
pipCamButton.hidden = FeatureDetector.hasPipCamSupport() === false;

let pipCamActive = false;

const tryShowPipCam = present => {
    if (present === 'screen-monitor' && FeatureDetector.hasPipCamSupport()) {
        showPipCam();
    }
};

const showPipCam = () => {
    if (!pipCamActive) {
        eyeson.send({ type: 'start_pip_cam' });
    }
};

const hidePipCam = () => {
    if (pipCamActive) {
        eyeson.send({ type: 'stop_pip_cam' });
    }
};

pipCamButton.addEventListener('click', () => {
    if (!pipCamActive) {
        showPipCam();
    } else {
        hidePipCam();
    }
});

eyeson.onEvent(event => {
    const { type } = event;
    if (type === 'presentation_started') {
        tryShowPipCam(event.present);
    }
    else if (type === 'presentation_ended') {
        hidePipCam();
    }
    else if (type === 'pip_cam') {
        pipCamActive = event.active;
        pipCamButton.textContent =
            event.active ? 'Hide camera' : 'Show camera';
    }
    else if (type === 'pip_cam_error') {
        showError('...');
    }
    else if (type === 'pip_cam_warning_video_ended') {
        showError('...');
    }
});

eyeson.send({
    type: 'start_screen_capture',
    audio: true,
    screen: true,
 });

To make it even work better, you can combine it with pre-selected screensharing media:

// ... extend the example above

const startScreenCaptureOptions = {
    type: 'start_screen_capture',
    audio: true,
    screen: true,
 };

if (FeatureDetector.canChooseDisplaySurface()) {
    startScreenCaptureOptions.surface = 'monitor';
}

eyeson.send(startScreenCaptureOptions);

Feedback or questions

If you have any feedback or question, feel free to add a ticket to the js-docs Github repository.