- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
我重新发布这个问题,因为我之前没有得到有效的解决方案。对带来的麻烦表示抱歉。我已经在 Heroku 上托管了 Codelab webrtc 演示教程第 6 步中所示的示例(使用 Node.js 进行信号传输的基本视频聊天应用程序),以尝试在不同的网络上使用它。我正在使用 Xirsys STUN/TURN 服务器。当两个系统位于同一网络上时它工作正常。但是,当我尝试跨不同网络连接系统时,远程视频上显示黑屏。我无法弄清楚这里的问题。我附上了 main.js 的代码。如有任何帮助,我们将不胜感激。
'use strict';
var isChannelReady;
var isInitiator = false;
var isStarted = false;
var localStream;
var pc;
var remoteStream;
var turnReady;
var pc_config = {'iceServers': [
{url:'stun:turn02.uswest.xirsys.com'},
{
url: 'turn:turn02.uswest.xirsys.com:443?transport=udp',
credential: 'my_credentials',
username: 'my_username'
},
{
url: 'turn:turn02.uswest.xirsys.com:443?transport=tcp',
credential: 'my_credentials',
username: 'my_username'
},
{
url: 'turn:turn02.uswest.xirsys.com:5349?transport=udp',
credential: 'my_credentials',
username: 'my_username'
},
{
url: 'turn:turn02.uswest.xirsys.com:5349?transport=tcp',
credential: 'my_credentials',
username: 'my_username'
}
]};
var pc_constraints = {'optional': [{'DtlsSrtpKeyAgreement': true}]};
// Set up audio and video regardless of what devices are present.
var sdpConstraints = {'mandatory': {
'OfferToReceiveAudio':true,
'OfferToReceiveVideo':true }};
/////////////////////////////////////////////
var room = location.pathname.substring(1);
if (room === '') {
room = prompt('Enter room name:');
// room = 'fool';
} else {
//
}
var socket = io.connect();
if (room !== '') {
console.log('Create or join room', room);
socket.emit('create or join', room);
}
socket.on('created', function (room){
console.log('Created room ' + room);
isInitiator = true;
});
socket.on('full', function (room){
console.log('Room ' + room + ' is full');
});
socket.on('join', function (room){
console.log('Another peer made a request to join room ' + room);
console.log('This peer is the initiator of room ' + room + '!');
isChannelReady = true;
});
socket.on('joined', function (room){
console.log('This peer has joined room ' + room);
isChannelReady = true;
});
socket.on('log', function (array){
console.log.apply(console, array);
});
////////////////////////////////////////////////
function sendMessage(message){
console.log('Client sending message: ', message);
// if (typeof message === 'object') {
// message = JSON.stringify(message);
// }
socket.emit('message', message);
}
socket.on('message', function (message){
console.log('Client received message:', message);
if (message === 'got user media') {
maybeStart();
} else if (message.type === 'offer') {
if (!isInitiator && !isStarted) {
maybeStart();
}
pc.setRemoteDescription(new RTCSessionDescription(message));
doAnswer();
} else if (message.type === 'answer' && isStarted) {
pc.setRemoteDescription(new RTCSessionDescription(message));
} else if (message.type === 'candidate' && isStarted) {
var candidate = new RTCIceCandidate({
sdpMLineIndex: message.label,
candidate: message.candidate
});
pc.addIceCandidate(candidate);
} else if (message === 'bye' && isStarted) {
handleRemoteHangup();
}
});
////////////////////////////////////////////////////
var localVideo = document.querySelector('#localVideo');
var remoteVideo = document.querySelector('#remoteVideo');
function handleUserMedia(stream) {
console.log('Adding local stream.');
localVideo.src = window.URL.createObjectURL(stream);
localStream = stream;
sendMessage('got user media');
if (isInitiator) {
maybeStart();
}
}
function handleUserMediaError(error){
console.log('getUserMedia error: ', error);
}
var constraints = {video: true};
getUserMedia(constraints, handleUserMedia, handleUserMediaError);
console.log('Getting user media with constraints', constraints);
// if (location.hostname != "localhost") {
// requestTurn('https://computeengineondemand.appspot.com/turn?username=41784574&key=4080218913');
// }
function maybeStart() {
if (!isStarted && typeof localStream != 'undefined' && isChannelReady) {
createPeerConnection();
pc.addStream(localStream);
isStarted = true;
console.log('isInitiator', isInitiator);
if (isInitiator) {
doCall();
}
}
}
window.onbeforeunload = function(e){
sendMessage('bye');
}
/////////////////////////////////////////////////////////
function createPeerConnection() {
try {
pc = new RTCPeerConnection(null);
pc.onicecandidate = handleIceCandidate;
pc.onaddstream = handleRemoteStreamAdded;
pc.onremovestream = handleRemoteStreamRemoved;
console.log('Created RTCPeerConnnection');
} catch (e) {
console.log('Failed to create PeerConnection, exception: ' + e.message);
alert('Cannot create RTCPeerConnection object.');
return;
}
}
function handleIceCandidate(event) {
console.log('handleIceCandidate 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.');
}
}
function handleRemoteStreamAdded(event) {
console.log('Remote stream added.');
remoteVideo.src = window.URL.createObjectURL(event.stream);
remoteStream = event.stream;
}
function handleCreateOfferError(event){
console.log('createOffer() error: ', e);
}
function doCall() {
console.log('Sending offer to peer');
pc.createOffer(setLocalAndSendMessage, handleCreateOfferError);
}
function doAnswer() {
console.log('Sending answer to peer.');
pc.createAnswer(setLocalAndSendMessage, null, sdpConstraints);
}
function setLocalAndSendMessage(sessionDescription) {
// Set Opus as the preferred codec in SDP if Opus is present.
sessionDescription.sdp = preferOpus(sessionDescription.sdp);
pc.setLocalDescription(sessionDescription);
console.log('setLocalAndSendMessage sending message' , sessionDescription);
sendMessage(sessionDescription);
}
function requestTurn(turn_url) {
var turnExists = false;
for (var i in pc_config.iceServers) {
if (pc_config.iceServers[i].url.substr(0, 5) === 'turn:') {
turnExists = true;
turnReady = true;
break;
}
}
if (!turnExists) {
console.log('Getting TURN server from ', turn_url);
// No TURN server. Get one from computeengineondemand.appspot.com:
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
if (xhr.readyState === 4 && xhr.status === 200) {
var turnServer = JSON.parse(xhr.responseText);
console.log('Got TURN server: ', turnServer);
pc_config.iceServers.push({
'url': 'turn:' + turnServer.username + '@' + turnServer.turn,
'credential': turnServer.password
});
turnReady = true;
}
};
xhr.open('GET', turn_url, true);
xhr.send();
}
}
function handleRemoteStreamAdded(event) {
console.log('Remote stream added.');
remoteVideo.src = window.URL.createObjectURL(event.stream);
remoteStream = event.stream;
}
function handleRemoteStreamRemoved(event) {
console.log('Remote stream removed. Event: ', event);
}
function hangup() {
console.log('Hanging up.');
stop();
sendMessage('bye');
}
function handleRemoteHangup() {
// console.log('Session terminated.');
// stop();
// isInitiator = false;
}
function stop() {
isStarted = false;
// isAudioMuted = false;
// isVideoMuted = false;
pc.close();
pc = null;
}
///////////////////////////////////////////
// Set Opus as the default audio codec if it's present.
function preferOpus(sdp) {
var sdpLines = sdp.split('\r\n');
var mLineIndex=null;
// Search for m line.
for (var i = 0; i < sdpLines.length; i++) {
if (sdpLines[i].search('m=audio') !== -1) {
mLineIndex = i;
break;
}
}
if (mLineIndex === null) {
return sdp;
}
// If Opus is available, set it as the default in m line.
for (i = 0; i < sdpLines.length; i++) {
if (sdpLines[i].search('opus/48000') !== -1) {
var opusPayload = extractSdp(sdpLines[i], /:(\d+) opus\/48000/i);
if (opusPayload) {
sdpLines[mLineIndex] = setDefaultCodec(sdpLines[mLineIndex], opusPayload);
}
break;
}
}
// Remove CN in m line and sdp.
sdpLines = removeCN(sdpLines, mLineIndex);
sdp = sdpLines.join('\r\n');
return sdp;
}
function extractSdp(sdpLine, pattern) {
var result = sdpLine.match(pattern);
return result && result.length === 2 ? result[1] : null;
}
// Set the selected codec to the first in m line.
function setDefaultCodec(mLine, payload) {
var elements = mLine.split(' ');
var newLine = [];
var index = 0;
for (var i = 0; i < elements.length; i++) {
if (index === 3) { // Format of media starts from the fourth.
newLine[index++] = payload; // Put target payload to the first.
}
if (elements[i] !== payload) {
newLine[index++] = elements[i];
}
}
return newLine.join(' ');
}
// Strip CN from sdp before CN constraints is ready.
function removeCN(sdpLines, mLineIndex) {
var mLineElements = sdpLines[mLineIndex].split(' ');
// Scan from end for the convenience of removing an item.
for (var i = sdpLines.length-1; i >= 0; i--) {
var payload = extractSdp(sdpLines[i], /a=rtpmap:(\d+) CN\/\d+/i);
if (payload) {
var cnPos = mLineElements.indexOf(payload);
if (cnPos !== -1) {
// Remove CN payload from m line.
mLineElements.splice(cnPos, 1);
}
// Remove CN line in sdp
sdpLines.splice(i, 1);
}
}
sdpLines[mLineIndex] = mLineElements.join(' ');
return sdpLines;
}
最佳答案
有几种可能性。即使您正在使用 STUN/TURN,也许某些防火墙仍然以某种方式阻止连接?
另一个想法是,您确定网络摄像头已打开并指向远程服务器的发光区域吗?
您需要发布完整的代码。
另一种可能性是你的 STUN/TURN 根本不起作用。您可以尝试不同的 STUN/TURN 服务器,并检查本地/除 Heroku 之外的其他服务器,例如可以确保防火墙关闭的 VPS。
关于javascript - Codelab(bitbucket.org) 示例步骤 6,无法跨网络工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35836137/
我在 Bitbucket 中有一个 CI 管道,它正在构建、测试和部署应用程序。 问题是在部署之后我想运行 selenium 测试。 Selenium 测试位于 Bitbucket 的另一个存储库中,
bitbucket 中某些文件中的编辑按钮被禁用。 例如: 但在这个 repo 中,我可以编辑其他文件。 像这样: 我无法理解原因。 最佳答案 请检查文件编码。 我以前也遇到过同样的问题。检查编辑按钮
我已经按照说明创建了一个应用密码 here 但是现在,我如何使用此应用程序密码访问存储库?网址是什么?有人可以将我定向到显示示例的页面吗? 下面是github的代码。我如何为 bitbucket 做到
我最近开始为我的 BitBucket 存储库使用问题跟踪器。我已经能够将所有 Unresolved 问题(只是错误和增强功能)移过去,但我注意到没有像“问题”这样的问题类型。这是我可以添加的东西吗?
我是 bitbuckt 管道的新手。在我的节点项目中,我在管道中添加了 bitbucket-pipelines.yml 我有一个构建容器并将其推送到 ECR 的步骤以及另一个部署步骤。 现在,每次我对
我有一个 Bitbucket 存储库,我可以使用我的凭据登录 Web 浏览器,现在我想在 Sourcetree 中 check out 它,所以我安装了 Sourcetree 2.6.10,它需要使用
我看到与 Bitbucket 管道的矛盾,我认为这是由于我的误解造成的 我想创建一个用于暂存的管道和一个用于生产的管道。这两个管道之间的区别在于每个管道都为 deployment 设置了不同的值,因此
如何找到从未登录过其 Bitbucket 帐户的用户列表?是否有任何查询或宏,还是我必须手动执行? 最佳答案 您可以使用对数据库的 SQL 查询来执行此操作: SELECT cu.lower_
我正在尝试使用代码片段而不是纯文本从相关的松弛 channel 回复关于 bitbucket 的 PR 上的评论。 最佳答案 这很简单。将代码行放在这些 (```) 之间。由于 StackOverfl
如何在 BitBucket 中搜索提交消息?我只能搜索代码,但这对我没有帮助。我要找关键字 HDMI在 提交标题 例如。 最佳答案 Bitbucket 网页上的提交下有一个搜索框。 关于bitbuck
当我尝试设置 slack 集成时出现 403 错误和一个挂起的页面,知道是什么原因造成的吗? 这些是网址 得到bitbucket.org/api/2.0/repositories/MyAccount/
如何更改我的 BitBucket 存储库的开发分支?现在我的 master分支被标记为 main和 development分支。 这是这个问题的几乎重复:How to change the main
是否可以使用他们的 REST API v1.0 通过日期时间过滤器获得对 Bitbucket 的提交?我阅读了 Bitbucket API 的整个文档,但找不到任何相关内容。我在问也许我确实错过了什么
在我的 BitBucket wiki 存储库我已经上传了一张图片,现在我正在尝试将其居中: ![Alt text](https://upload.wikimedia.org/wikipedia/com
我为我的 bitbucket 帐户启用了两因素身份验证。 现在 Osx 上的 Atlassian Sourcetree 应用程序不起作用,无法在 bitbucket 上登录。 我如何配置 Source
我在 bitbucket 管道中有两个存储库,都启用了管道。 另一个管道完成后如何执行管道? 最佳答案 使用 "Bitbucket trigger pipeline" pipe在管道的最后一步。 您可
我们的团队在 Bitbucket 上有一个帐户,我们必须为每个 repo 配置 webhook。有没有办法一次性为所有 repo 配置 webhook,而不是单独管理每个 repo 中的 webhoo
我正在 BitBucket Pipeline 中进行非常简单的 java 构建。唯一的区别是它位于存储库子目录中。 我的 bitbucket-pipelines.yml: pipelines: d
我有一个用于辅助项目的私有(private) bitbucket 存储库。我邀请某人观看它作为采访的一部分。 我决定不再继续采访了。如何删除此观察者? 最佳答案 好的,找到了。 要从存储库中删除观察者
我无法弄清楚或找到有关如何以新的位桶格式访问旧提交源的文档。这还有可能吗? 最佳答案 我了解您希望通过 BitBucket Web 界面下载旧版本,而不使用 Mercurial/Git 客户端。 检查
我是一名优秀的程序员,十分优秀!