如何发送和接收通过 getUsermedia() 生成的桌面捕获流

2024-01-09

我正在使用 WebRTC + Socket.io 制作一个屏幕共享应用程序,但卡在一个地方。 使用 WebRTC + Socket.io 连接两个浏览器并可以发送文字

我正在寻求支持codelab https://codelabs.developers.google.com/codelabs/webrtc-web/#8但它不适用于流。(如果解决方案基于此链接,则非常有帮助)

如何发送 getUserMedia() 流:

dataChannel.send(stream);

并在channel.onmessage()上接收相同的流: 我将 event.data 作为“[object MediaStream]”而不是流获取。

channel.onmessage = function(event){
  // unable to get correct stream
  // event.data is "[object MediaStream]"  in string
}

function createPeerConnection(isInitiator, config) {
    console.log('Creating Peer connection as initiator?', isInitiator, 'config:', config);
    peerConn = new RTCPeerConnection(config);

    // send any ice candidates to the other peer
    peerConn.onicecandidate = function (event) {
        console.log('onIceCandidate event:', event);
        if (event.candidate) {
            sendMessage({
                type: 'candidate',
                label: event.candidate.sdpMLineIndex,
                id: event.candidate.sdpMid,
                candidate: event.candidate.candidate
            });
        } else {
            console.log('End of candidates.');
        }
    };

    if (isInitiator) {
        console.log('Creating Data Channel');
        dataChannel = peerConn.createDataChannel("screen");
        onDataChannelCreated(dataChannel);

        console.log('Creating an offer');
        peerConn.createOffer(onLocalSessionCreated, logError);
    } else {
        peerConn.ondatachannel = function (event) {
            console.log('ondatachannel:', event.channel);
            dataChannel = event.channel;
            onDataChannelCreated(dataChannel);
        };
    }
}

它适用于字符串或 json,即 dataChannel.send('Hello');

我为此创建了一个维基页面:wiki https://github.com/muaz-khan/WebRTC-Experiment/wiki/Unable-to-receive-correctly-desktop-capture-stream-that-got-by-getUserMedia-but-working-fine-for-string

请帮忙。


请尝试这样的事情:(代码末尾有解释)

var btnShareYourCamera = document.querySelector('#share-your-camera');
var localVideo = document.querySelector('#local-video');
var remoteVideo = document.querySelector('#remote-video');

var websocket = new WebSocket('wss://path-to-server:port/');
websocket.onmessage = function(event) {
    var data = JSON.parse(event.data);
    if (data.sdp) {
        if (data.sdp.type === 'offer') {
            getUserMedia(function(video_stream) {
                localVideo.srcObject = video_stream;
                answererPeer(new RTCSessionDescription(data.sdp), video_stream);
            });
        }

        if (data.sdp.type === 'answer') {
            offerer.setRemoteDescription(new RTCSessionDescription(data.sdp));
        }
    }

    if (data.candidate) {
        addIceCandidate((offerer || answerer), new RTCIceCandidate(data.candidate));
    }
};

var iceTransportPolicy = 'all';
var iceTransportLimitation = 'udp';

function addIceCandidate(peer, candidate) {
    if (iceTransportLimitation === 'tcp') {
        if (candidate.candidate.toLowerCase().indexOf('tcp') === -1) {
            return; // ignore UDP
        }
    }

    peer.addIceCandidate(candidate);
}

var offerer, answerer;

var iceServers = {
    iceServers: [{
        'urls': [
            'stun:stun.l.google.com:19302',
            'stun:stun1.l.google.com:19302',
            'stun:stun2.l.google.com:19302',
            'stun:stun.l.google.com:19302?transport=udp',
        ]
    }],
    iceTransportPolicy: iceTransportPolicy,
    rtcpMuxPolicy: 'require',
    bundlePolicy: 'max-bundle'
};

// https://https;//cdn.webrtc-experiment.com/IceServersHandler.js
if (typeof IceServersHandler !== 'undefined') {
    iceServers.iceServers = IceServersHandler.getIceServers();
}

var mediaConstraints = {
    OfferToReceiveAudio: true,
    OfferToReceiveVideo: true
};

/* offerer */

function offererPeer(video_stream) {
    offerer = new RTCPeerConnection(iceServers);
    offerer.idx = 1;

    video_stream.getTracks().forEach(function(track) {
        offerer.addTrack(track, video_stream);
    });

    offerer.ontrack = function(event) {
        remoteVideo.srcObject = event.streams[0];
    };

    offerer.onicecandidate = function(event) {
        if (!event || !event.candidate) return;
        websocket.send(JSON.stringify({
            candidate: event.candidate
        }));
    };

    offerer.createOffer(mediaConstraints).then(function(offer) {
        offerer.setLocalDescription(offer).then(function() {
            websocket.send(JSON.stringify({
                sdp: offer
            }));
        });
    });
}

/* answerer */

function answererPeer(offer, video_stream) {
    answerer = new RTCPeerConnection(iceServers);
    answerer.idx = 2;

    video_stream.getTracks().forEach(function(track) {
        answerer.addTrack(track, video_stream);
    });

    answerer.ontrack = function(event) {
        remoteVideo.srcObject = event.streams[0];
    };

    answerer.onicecandidate = function(event) {
        if (!event || !event.candidate) return;
        websocket.send(JSON.stringify({
            candidate: event.candidate
        }));
    };

    answerer.setRemoteDescription(offer).then(function() {
        answerer.createAnswer(mediaConstraints).then(function(answer) {
            answerer.setLocalDescription(answer).then(function() {
                websocket.send(JSON.stringify({
                    sdp: answer
                }));
            });
        });
    });
}

var video_constraints = {
    mandatory: {},
    optional: []
};

function getUserMedia(successCallback) {
    function errorCallback(e) {
        alert(JSON.stringify(e, null, '\t'));
    }

    var mediaConstraints = {
        video: true,
        audio: true
    };

    navigator.mediaDevices.getUserMedia(mediaConstraints).then(successCallback).catch(errorCallback);
}

btnShareYourCamera.onclick = function() {
    getUserMedia(function(video_stream) {
        localVideo.srcObject = video_stream;
        offererPeer(video_stream);
    });
};
  1. 您必须使用附加流peer.addTrack正如你在上面的例子中看到的
  2. 您必须使用接收远程流peer.ontrack正如你在上面的例子中看到的

即使用addTrack连接相机并使用ontrack接收远程摄像头。

您绝不能使用以下方式发送流dataChannel.send。两者是完全不同的协议。 AMediaStream必须使用 RTP 共享;不是 SCTP。仅当您拨打电话时才使用 RTPpeer.addTrack附加相机流的方法。

此过程发生在您打开或加入房间之前。

在这里查看单页演示:https://www.webrtc-experiment.com/getStats/ https://www.webrtc-experiment.com/getStats/

上述代码片段的 HTML:

<button id="share-your-camera"></button>
<video id="local-video" controls autoplay playsinline></video>
<video id="remote-video" controls autoplay playsinline></video>
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何发送和接收通过 getUsermedia() 生成的桌面捕获流 的相关文章

  • 计算字符串中的唯一单词

    下面我尝试将字符串数组提供给一个函数 该函数将唯一单词添加到单词数组中 并且如果该单词已经在数组中 则增加计数数组中相应元素的计数 var words var counts calculate a b calculate a c funct
  • 有没有办法根据渲染的字符串创建 DOM 对象?

    有没有办法从整个字符串而不只是innerHTML 创建DOM 对象 我有一个完整呈现的 DOM 形式的字符串
  • asp.net 将值从 JS/jquery 传递到 C# 背后的代码

    我已经尝试了 所有 可能的方法 将 screen width vlaue 从 aspx 页面上的 JS 脚本发送到后面代码中的 c 虽然我可以看到 screen width 被正确分配 但它永远不会分配给我的隐藏字段价值
  • AngularJS:选择非 2 路绑定到模型

    我正在使用选择来显示客户名称 用户应该能够选择现有客户端 然后更新范围属性 控制器 初始化 首选 if scope clients length gt 0 scope existingClient scope clients 0 View
  • 我需要一个 jQuery Autocomplete 使用 ajax 返回 id 和 name 的示例

    我需要一个示例 说明如何编写 jQuery 自动完成代码来填充product id 同时显示调用ajax 页面 remote php 的product name
  • 是否可以进行条件解构或有后备?

    我有一个具有许多深层嵌套属性的对象 我希望能够访问 MY KEY 上的属性 如下 但如果该属性不存在 则获取 MY OTHER KEY 我怎样才能做到这一点 const X Y MY KEY Values segments segment
  • 如何在 HTML 表格上使用分页?

    我正在尝试使用这个分页library http flaviusmatis github io simplePagination js 在我的 HTML 表格页面 特别是浅色主题 中 但不知何故 我无法理解如何在我的 HTML 页面中以这种方
  • jQuery 选择器定位具有 id AND class 的元素不起作用

    我有以下事件处理函数 jQuery document on click button submitb function e alert jQuery 包含在 html 文档中 但是 如果我点击 div class submitb Go di
  • 三.js环境光意想不到的效果

    在下面的代码中 我渲染了一些立方体并使用点光源和环境光照亮它们 然而 当设置为 0xffffff 时 AmbientLight 会将侧面的颜色更改为白色 无论其指定的颜色如何 奇怪的是 点光源按预期工作 我怎样才能使环境光表现得像点光 因为
  • 如何避免 TypeScript 中出现虚假的“未使用参数”警告

    我遇到过很多次这种情况 最后决定弄清楚正确的方法是什么 如果我有一个声明方法的抽象父类 然后一些具体子类在其实现中实现真正的逻辑 并且显然使用方法参数 但某些子类不需要在该方法中执行任何操作 因此不要使用方法参数 那些不必执行任何操作的方法
  • 如何在美人鱼节点描述中添加链接?

    我想 如下图所示 div class mermaid graph TD A hello B an b important b link A gt B div 在下面添加实际链接link指向http google com 我尝试将相关节点修改
  • 在 JQuery ui 自动完成中显示图像

    我有一个带有 JQuery ui 自动完成功能的脚本 可以完美运行 有一个显示用户名字和姓氏的搜索过程 但在我的数据库中 还有用户的图片 我想将其显示在带有名字和姓氏的建议中 数据库中pic包含图片url 剧本 function searc
  • Intro.js 2页然后返回首页

    我在用intro js http introjs com 为我的网站创建一个小介绍 我希望游览从第 1 页 主页 2 另一页 然后回到第 1 页 主页 我已经成功地从第 1 2 页开始 但不确定如何让它返回到第 1 页 我对 javascr
  • 我们如何使用 thymeleaf 绑定对象列表的列表

    我有一个表单 用户可以在其中添加任意数量的内容表对象这也可以包含他想要的列对象 就像在 SQL 中构建表一样 我尝试了下面的代码 但没有任何效果 并且当我尝试绑定两个列表时 表单不再出现 控制器 ModelAttribute page pu
  • iPhone 点击时使 div 变暗

    当您的 div 附加了点击处理程序时 当点击该 div 时 iPhone 会使该 div 变暗 作为点击指示器 示例 在移动 Safari 上查看http jsbin com awejo3 4 http jsbin com awejo3 4
  • Facebook API Javascript JSON 响应

    function getUser FB api me function response console log Response is response alert Your name is response first name ale
  • Angular 停止 Enter 键提交

    I am trying to stop the Enter from submitting my button and rather make it point to another function I tried trapping th
  • 动态 dom 操作后,如何在浏览器历史记录中保留 dom 状态?

    是否有一个通用的解决方案来保留 dom 状态 以便当用户使用后退 前进返回页面时 整个页面处于他们离开时的确切状态 这篇文章询问并回答了为什么不同浏览器和不同 javascript 库的行为不一致 Ajax 后退按钮和 DOM 更新 htt
  • 在firefox上用js改变表单方法

    我需要使用 javascript jQuery 或纯 更改表单的方法属性 我的表单有 method post 我尝试用以下方法更改它 submit button click function var url input id url val
  • 如何在 Twilio 可编程聊天中的单个通道上侦听消息

    Using twilio chat js https www npmjs com package twilio chat如何在单个频道上收听消息 我发现这个问题 https stackoverflow com questions 54687

随机推荐