- android - RelativeLayout 背景可绘制重叠内容
- android - 如何链接 cpufeatures lib 以获取 native android 库?
- java - OnItemClickListener 不起作用,但 OnLongItemClickListener 在自定义 ListView 中起作用
- java - Android 文件转字符串
我已经设置了自己的 turnserver 以防止发生跨域错误。当客户端需要使用 TURN 服务器而不是 STUN 时,就没有视频源。然而,消息正在通过。我的 main.js(从 WebRTC Development 起飞)
'use strict';
var isChannelReady = false;
var isInitiator = false;
var isStarted = false;
var localStream;
var pc;
var remoteStream;
var turnReady;
var pcConfig = {
'iceServers': [{
'url': 'brett@66.172.10.133',
'credential': 'thorn'
}]
};
// Set up audio and video regardless of what devices are present.
var sdpConstraints = {
'mandatory': {
'OfferToReceiveAudio': true,
'OfferToReceiveVideo': true
}
};
/////////////////////////////////////////////
var room = 'foo2';
// Could prompt for room name:
// room = prompt('Enter room name:');
var socket = io.connect();
if (room !== '') {
socket.emit('create or join', room);
console.log('Attempted to create or join room', 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('joined: ' + room);
isChannelReady = true;
});
socket.on('log', function(array) {
console.log.apply(console, array);
});
////////////////////////////////////////////////
function sendMessage(message) {
console.log('Client sending message: ', message);
socket.emit('message', message);
}
// This client receives a 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');
navigator.mediaDevices.getUserMedia({
audio: false,
video: true
})
.then(gotStream)
.catch(function(e) {
alert('getUserMedia() error: ' + e.name);
});
function gotStream(stream) {
console.log('Adding local stream.');
localVideo.src = window.URL.createObjectURL(stream);
localStream = stream;
sendMessage('got user media');
if (isInitiator) {
maybeStart();
}
}
var constraints = {
video: true
};
console.log('Getting user media with constraints', constraints);
if (location.hostname !== 'localhost') {
requestTurn(
'https://computeengineondemand.appspot.com/turn?username=41784574&key=4080218913'
);
}
function maybeStart() {
console.log('>>>>>>> maybeStart() ', isStarted, localStream, isChannelReady);
if (!isStarted && typeof localStream !== 'undefined' && isChannelReady) {
console.log('>>>>>> creating peer connection');
createPeerConnection();
pc.addStream(localStream);
isStarted = true;
console.log('isInitiator', isInitiator);
if (isInitiator) {
doCall();
}
}
}
window.onbeforeunload = function() {
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('icecandidate 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: ', event);
}
function doCall() {
console.log('Sending offer to peer');
pc.createOffer(setLocalAndSendMessage, handleCreateOfferError);
}
function doAnswer() {
console.log('Sending answer to peer.');
pc.createAnswer().then(
setLocalAndSendMessage,
onCreateSessionDescriptionError
);
}
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 onCreateSessionDescriptionError(error) {
trace('Failed to create session description: ' + error.toString());
}
function requestTurn(turnURL) {
pcConfig.iceServers.push({
'url': 'turn:brett@66.172.10.133',
'credential': 'thorn'
});
turnReady = true;
}
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;
// 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;
}
出于某种原因,除非在同一网络上,否则没有视频馈送(因此我怀疑没有联系转弯服务器)。另外,下面是我正在使用的 html:
<!DOCTYPE html>
<html>
<head>
<title>Realtime communication with WebRTC</title>
<link rel="stylesheet" href="/css/main.css" />
</head>
<body>
<h1>Realtime communication with WebRTC</h1>
<div id="videos">
<video id="localVideo" autoplay muted></video>
<video id="remoteVideo" autoplay></video>
</div>
<script src="/socket.io/socket.io.js"></script>
<script src="js/lib/adapter.js"></script>
<script src="js/main.js"></script>
</body>
</html>
我们将不胜感激。
编辑:我已将用户名和密码设置为:
# Typically, the realm field must match the value of AuthenticationRealm
# defined in reTurnServer.config
#
# The state field (not case sensitive) can be one of:
#
# authorized (user authorized)
# refused (user denied access)
# restricted (for when bandwidth limiting is implemented)
#
# This file format is interchangeable with TurnServer.org's user database
#
# Comments can be inserted by starting a line with #
#
test:foobar:example.org:REFUSED
brett:thorn:66.172.10.133:AUTHORISED
我不明白为什么会有授权问题。
编辑:由于拼写错误的单词而导致的授权问题已修复。但是,我在浏览器中收到以下内容,但仍然没有视频源:
GET http://66.172.10.133:8080/socket.io/?EIO=3&transp...lling&t=1475036226795-4&sid=rlFY-bqt9vnv6S5FAAAA
200 OK
250ms
socket.io.js (line 2739)
Adding local stream.
main.js (line 109)
Client sending message: got user media
main.js (line 66)
>>>>>>> maybeStart() false LocalMediaStream { id="{0daeeab5-8929-40c8-b7ea-cf65028a5363}", currentTime=0, stop=stop(), more...} false
main.js (line 131)
Message from server: Client said: got user media
main.js (line 60)
Another peer made a request to join room foo2
main.js (line 49)
This peer is the initiator of room foo2!
main.js (line 50)
Client received message: got user media
main.js (line 72)
>>>>>>> maybeStart() false LocalMediaStream { id="{0daeeab5-8929-40c8-b7ea-cf65028a5363}", currentTime=9.666666666666666, stop=stop(), more...} true
main.js (line 131)
>>>>>> creating peer connection
main.js (line 133)
Created RTCPeerConnnection
main.js (line 156)
isInitiator true
main.js (line 137)
Sending offer to peer
main.js (line 189)
setLocalAndSendMessage sending message RTCSessionDescription { type="offer", sdp="v=0\r\no=mozilla...THIS_IS...e7-a980-692a3ea8198a}\r\n", toJSON=toJSON()}
main.js (line 205)
Client sending message: RTCSessionDescription { type="offer", sdp="v=0\r\no=mozilla...THIS_IS...e7-a980-692a3ea8198a}\r\n", toJSON=toJSON()}
main.js (line 66)
icecandidate event: icecandidate
main.js (line 165)
Client sending message: Object { type="candidate", label=0, id="sdparta_0", more...}
main.js (line 66)
icecandidate event: icecandidate
main.js (line 165)
Client sending message: Object { type="candidate", label=0, id="sdparta_0", more...}
main.js (line 66)
icecandidate event: icecandidate
main.js (line 165)
Client sending message: Object { type="candidate", label=0, id="sdparta_0", more...}
main.js (line 66)
icecandidate event: icecandidate
main.js (line 165)
Client sending message: Object { type="candidate", label=0, id="sdparta_0", more...}
main.js (line 66)
icecandidate event: icecandidate
main.js (line 165)
End of candidates.
main.js (line 174)
Message from server: Client said: Object { type="offer", sdp="v=0\r\no=mozilla...THIS_IS...e7-a980-692a3ea8198a}\r\n"}
main.js (line 60)
Message from server: Client said: Object { type="candidate", label=0, id="sdparta_0", more...}
main.js (line 60)
Message from server: Client said: Object { type="candidate", label=0, id="sdparta_0", more...}
main.js (line 60)
Message from server: Client said: Object { type="candidate", label=0, id="sdparta_0", more...}
main.js (line 60)
Message from server: Client said: Object { type="candidate", label=0, id="sdparta_0", more...}
main.js (line 60)
Client received message: Object { type="answer", sdp="v=0\r\no=- 679697878549041...69f-968e-efbdb26e287c\r\n"}
main.js (line 72)
Remote stream added.
main.js (line 223)
Client received message: Object { type="candidate", label=0, id="sdparta_0", more...}
main.js (line 72)
Client received message: Object { type="candidate", label=0, id="sdparta_0", more...}
main.js (line 72)
ICE failed, see about:webrtc for more details
另外,我在连接日志中得到这个(about:webrtc):
(registry/INFO) insert 'ice' (registry) succeeded: ice
(registry/INFO) insert 'ice.pref' (registry) succeeded: ice.pref
(registry/INFO) insert 'ice.pref.type' (registry) succeeded: ice.pref.type
(registry/INFO) insert 'ice.pref.type.srv_rflx' (UCHAR) succeeded: 0x64
(registry/INFO) insert 'ice.pref.type.peer_rflx' (UCHAR) succeeded: 0x6e
(registry/INFO) insert 'ice.pref.type.host' (UCHAR) succeeded: 0x7e
(registry/INFO) insert 'ice.pref.type.relayed' (UCHAR) succeeded: 0x05
(registry/INFO) insert 'ice.pref.type.srv_rflx_tcp' (UCHAR) succeeded: 0x63
(registry/INFO) insert 'ice.pref.type.peer_rflx_tcp' (UCHAR) succeeded: 0x6d
(registry/INFO) insert 'ice.pref.type.host_tcp' (UCHAR) succeeded: 0x7d
(registry/INFO) insert 'ice.pref.type.relayed_tcp' (UCHAR) succeeded: 0x00
(registry/INFO) insert 'stun' (registry) succeeded: stun
(registry/INFO) insert 'stun.client' (registry) succeeded: stun.client
(registry/INFO) insert 'stun.client.maximum_transmits' (UINT4) succeeded: 7
(registry/INFO) insert 'ice.trickle_grace_period' (UINT4) succeeded: 5000
(registry/INFO) insert 'ice.tcp' (registry) succeeded: ice.tcp
(registry/INFO) insert 'ice.tcp.so_sock_count' (INT4) succeeded: 0
(registry/INFO) insert 'ice.tcp.listen_backlog' (INT4) succeeded: 10
(registry/INFO) insert 'ice.tcp.disable' (char) succeeded: \001
(ice/NOTICE) ICE(PC:1475043603256000 (id=48 url=http://66.172.10.133:8080/)): peer (PC:1475043603256000 (id=48 url=http://66.172.10.133:8080/):default) no streams with non-empty check lists
(ice/NOTICE) ICE(PC:1475043603256000 (id=48 url=http://66.172.10.133:8080/)): peer (PC:1475043603256000 (id=48 url=http://66.172.10.133:8080/):default) no streams with pre-answer requests
(ice/NOTICE) ICE(PC:1475043603256000 (id=48 url=http://66.172.10.133:8080/)): peer (PC:1475043603256000 (id=48 url=http://66.172.10.133:8080/):default) no checks to start
(ice/INFO) ICE-PEER(PC:1475043603256000 (id=48 url=http://66.172.10.133:8080/):default)/CAND-PAIR(twON): setting pair to state FROZEN: twON|IP4:192.168.8.100:53095/UDP|IP4:10.76.93.143:51210/UDP(host(IP4:192.168.8.100:53095/UDP)|candidate:3063157045 1 udp 2122260223 10.76.93.143 51210 typ host generation 0)
(ice/INFO) ICE(PC:1475043603256000 (id=48 url=http://66.172.10.133:8080/))/CAND-PAIR(twON): Pairing candidate IP4:192.168.8.100:53095/UDP (7e7f00ff):IP4:10.76.93.143:51210/UDP (7e7f1eff) priority=9115005270282354174 (7e7f00fffcfe3dfe)
(ice/INFO) ICE-PEER(PC:1475043603256000 (id=48 url=http://66.172.10.133:8080/):default)/ICE-STREAM(0-1475043603256000 (id=48 url=http://66.172.10.133:8080/) aLevel=0): Starting check timer for stream.
(ice/INFO) ICE-PEER(PC:1475043603256000 (id=48 url=http://66.172.10.133:8080/):default)/CAND-PAIR(twON): setting pair to state WAITING: twON|IP4:192.168.8.100:53095/UDP|IP4:10.76.93.143:51210/UDP(host(IP4:192.168.8.100:53095/UDP)|candidate:3063157045 1 udp 2122260223 10.76.93.143 51210 typ host generation 0)
(ice/INFO) ICE-PEER(PC:1475043603256000 (id=48 url=http://66.172.10.133:8080/):default)/CAND-PAIR(twON): setting pair to state IN_PROGRESS: twON|IP4:192.168.8.100:53095/UDP|IP4:10.76.93.143:51210/UDP(host(IP4:192.168.8.100:53095/UDP)|candidate:3063157045 1 udp 2122260223 10.76.93.143 51210 typ host generation 0)
(ice/NOTICE) ICE(PC:1475043603256000 (id=48 url=http://66.172.10.133:8080/)): peer (PC:1475043603256000 (id=48 url=http://66.172.10.133:8080/):default) is now checking
(ice/WARNING) ICE(PC:1475043603256000 (id=48 url=http://66.172.10.133:8080/)): Error parsing attribute: candidate:4162317765 1 tcp 1518280447 10.76.93.143 0 typ host tcptype active generation 0
(ice/WARNING) ICE-PEER(PC:1475043603256000 (id=48 url=http://66.172.10.133:8080/):default): no pairs for 0-1475043603256000 (id=48 url=http://66.172.10.133:8080/) aLevel=0
(ice/INFO) ICE(PC:1475043603256000 (id=48 url=http://66.172.10.133:8080/)): peer (PC:1475043603256000 (id=48 url=http://66.172.10.133:8080/):default) Trickle grace period is over; marking every component with only failed pairs as failed.
(stun/INFO) STUN-CLIENT(twON|IP4:192.168.8.100:53095/UDP|IP4:10.76.93.143:51210/UDP(host(IP4:192.168.8.100:53095/UDP)|candidate:3063157045 1 udp 2122260223 10.76.93.143 51210 typ host generation 0)): Timed out
(ice/INFO) ICE-PEER(PC:1475043603256000 (id=48 url=http://66.172.10.133:8080/):default)/CAND-PAIR(twON): setting pair to state FAILED: twON|IP4:192.168.8.100:53095/UDP|IP4:10.76.93.143:51210/UDP(host(IP4:192.168.8.100:53095/UDP)|candidate:3063157045 1 udp 2122260223 10.76.93.143 51210 typ host generation 0)
(ice/INFO) ICE-PEER(PC:1475043603256000 (id=48 url=http://66.172.10.133:8080/):default)/STREAM(0-1475043603256000 (id=48 url=http://66.172.10.133:8080/) aLevel=0)/COMP(1): All pairs are failed, and grace period has elapsed. Marking component as failed.
(ice/INFO) ICE-PEER(PC:1475043603256000 (id=48 url=http://66.172.10.133:8080/):default): all checks completed success=0 fail=1
+++++++ END ++++++++
最佳答案
使用 https://webrtc.github.io/samples/src/content/peerconnection/trickle-ice/ 检查您的 TURN 服务器如果你得到一个中继候选人,你的 TURN 服务器就可以工作。我只能看到类型为 srflx 的候选人,这通常表明您的 turn 服务器可访问但身份验证失败。
此外,在 url 字段中使用用户名和 url 而不是旧的 user@host 语法。
关于javascript - 是否未检测到 turnserver?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39725559/
我找到了 this excellent question and answer它以 x/y(加上 center x/y 和 degrees/radians)开始并计算旋转- 到 x'/y'。这个计算很
全部: 我已经创建了一个 Windows 窗体和一个按钮。在另一个线程中,我试图更改按钮的文本,但它崩溃了;但是如果我尝试更改按钮的颜色,它肯定会成功。我认为如果您更改任何 Windows 窗体控件属
本网站的另一个问题已证实,C 中没有缩写的字面后缀,并且可以执行以下操作: short Number = (short)1; 但是转换它和不这样做有什么区别: short Number = 1; 您使
我有下表: ID (int) EMAIL (varchar(50)) CAMPAIGNID (int) isSubscribe (bit) isActionByUser (bit) 此表存储了用户对事
也就是说,无需触发Javascript事件即可改变的属性,如何保留我手动选中或取消选中的复选框的状态,然后复制到另一个地方? 运行下面的代码片段并选中或取消选中其中的一些,然后点击“复制”: $('#
我在网上找到的所有关于递增指针导致段错误的示例都涉及指针的取消引用 - 如果我只想递增它(例如在 for 循环的末尾)并且我不在乎它是否最终进入无效内存,因为我不会再使用它。例如,在这个程序中,每次迭
我有一个 Spring MVC REST 服务,它使用 XStream 将消息与 XML 相互转换。 有什么方法可以将请求和响应中的 xml(即正文)打印到普通的 log4j 记录器? 在 Contr
做我的任务有一个很大的挑战,那就是做相互依赖的任务我在这张照片中说的。假设我们有两个任务 A 和 B,执行子任务 A1、A2 和 B1、B2,假设任务 B 依赖于 A。 要理想地执行任务 B,您应该执
通过阅读该网站上的几个答案,我了解到 CoInitialize(Ex) should be called by the creator of a thread 。然后,在该线程中运行的任何代码都可以使
这个问题已经困扰我一段时间了。我以前从未真正使用过 ListViews,也没有使用过 FirebaseListAdapters。我想做的就是通过显示 id 和用户位置来启动列表的基础,但由于某种原因,
我很难解释这两个(看似简单)句子的含义: “受检异常由编译器在编译时检查” 这是什么意思?编译器检查是否捕获了所有已检查的异常(在代码中抛出)? “未经检查的异常在运行时检查,而不是编译时” 这句话中
我有一个包含排除子字符串的文本文件,我想迭代该文件以检查并返回不带排除子字符串的输入项。 这里我使用 python 2.4,因此下面的代码可以实现此目的,因为 with open 和 any 不起作用
Spring 的缓存框架能否了解请求上下文的身份验证状态,或者更容易推出自己的缓存解决方案? 最佳答案 尽管我发现这个用例 super 奇怪,但您可以为几乎任何与 SpEL 配合使用的内容设置缓存条件
我有以下函数模板: template HeldAs* duplicate(MostDerived *original, HeldAs *held) { // error checking omi
如果我的应用程序具有设备管理员/设备所有者权限(未获得 root 权限),我如何才能从我的应用程序中终止(或阻止启动)另一个应用程序? 最佳答案 设备所有者可以阻止应用程序: DevicePolicy
非常简单的问题,但我似乎无法让它正常工作。 我有一个组件,其中有一些 XSLT(用于导航)。它通过 XSLT TBB 使用 XSLT Mediator 发布。 发布后
我正在将一个对象拖动到一个可拖放的对象内,该对象也是可拖动的。放置对象后,它会嵌套在可放置对象内。同样,如果我将对象拖到可放置的外部,它就不再嵌套。 但是,如果我经常拖入和拖出可放置对象,则可拖动对象
我正在尝试为按钮和弹出窗口等多个指令实现“取消选择”功能。也就是说,我希望当用户单击不属于指令模板一部分的元素时触发我的函数。目前,我正在使用以下 JQuery 代码: $('body').click
我从 this question 得到了下面的代码,该脚本用于在 Google tasks 上更改 iframe[src="about:blank"] 内的 CSS使用 Chrome 扩展 Tempe
我有一些 @Mock 对象,但没有指定在该对象上调用方法的返回值。该方法返回 int (不是 Integer)。我很惊讶地发现 Mockito 没有抛出 NPE 并返回 0。这是预期的行为吗? 例如:
我是一名优秀的程序员,十分优秀!