Объединение двух видео MediaStream потоков в 1

Задача: Нужно на стороне клиента (в браузере) объединить поток с вебкамерой и поток с демонстрацией экрана в один общий поток, так, чтобы видео с вебкамеры закрыло небольшую часть видео с демонстрацией экрана (например видео с вебки в правом верхнем углу).

Я знаю что можно получить MediaStreamTrack объекты с помощью getTracks, а затем объединить их в один MediaStream объект. Но я подозреваю, что получу просто быструю смену вебки и демонстрации экрана (1 кадр вебка, 1 кадр экран). И этот способ никак не позволяет мне управлять местоположением вебкамеры

Можно ли провернуть такое на стороне клиента или придётся прибегать к рендерингу на стороне сервера? Очень бы не хотелось это делать на сервере, потому что это требует много ресурсов


Ответы (1 шт):

Автор решения: Ein

Можно отрисовать всё в один canvas, а потом захватить результат с него.

(async () => {
    let webCam = streamToVideo(await getWebCam());
    let desktop = streamToVideo(await getDesktop());

    let canvas = document.createElement('canvas');
    let context = canvas.getContext('2d');

    // 1024x576 - 16:9
    canvas.width = 1024;
    canvas.height = 576;

    (function draw() {
        context.drawImage(desktop, 0, 0, canvas.width, canvas.height);

        // 200x150 - 4:3
        let w = 200;
        let h = 150;
        context.drawImage(webCam, canvas.width - w, canvas.height - h, w, h);

        requestAnimationFrame(draw);
    })();

    // Вот тут вы получаете тот результат который хотели
    let resultMediaStream = canvas.captureStream();

    // Но для примера выводим на страницу
    let video = streamToVideo(resultMediaStream);
    document.body.appendChild(video);


})();


function streamToVideo(stream) {
    let video = document.createElement('video');

    video.srcObject = stream;

    video.style.width = stream.width;
    video.style.height = stream.height;

    video.play();

    return video;
}

async function getWebCam() {
    return await navigator.mediaDevices.getUserMedia({video: true});
}

async function getDesktop() {
    return await navigator.mediaDevices.getDisplayMedia({video: true});
}

codepen.io/loonybtard/pen/oNxywLV

captureStream | MDN

→ Ссылка