Commit 7e5cb333 by ziyue

开始对接js

parent 978917b4
......@@ -136,7 +136,7 @@ charSet=utf-8
#http链接超时时间
keepAliveSecond=30
#http请求体最大字节数,如果post的body太大,则不适合缓存body在内存
maxReqSize=4096
maxReqSize=40960
#404网页内容,用户可以自定义404网页
notFound=<html><head><title>404 Not Found</title></head><body bgcolor="white"><center><h1>您访问的资源不存在!</h1></center><hr><center>ZLMediaKit-4.0</center></body></html>
#http服务器监听端口
......
......@@ -127,6 +127,26 @@ static HttpApi toApi(const function<void(API_ARGS_JSON)> &cb) {
});
}
static HttpApi toApi(const function<void(API_ARGS_STRING_ASYNC)> &cb) {
return [cb](const Parser &parser, const HttpSession::HttpResponseInvoker &invoker, SockInfo &sender) {
GET_CONFIG(string, charSet, Http::kCharSet);
HttpSession::KeyValue headerOut;
headerOut["Content-Type"] = string("application/json; charset=") + charSet;
Json::Value val;
val["code"] = API::Success;
cb(sender, parser.getHeader(), headerOut, parser, val, invoker);
};
}
static HttpApi toApi(const function<void(API_ARGS_STRING)> &cb) {
return toApi([cb](API_ARGS_STRING_ASYNC) {
cb(API_ARGS_VALUE);
invoker(200, headerOut, val.toStyledString());
});
}
void api_regist(const string &api_path, const function<void(API_ARGS_MAP)> &func) {
s_map_api.emplace(api_path, toApi(func));
}
......@@ -143,6 +163,14 @@ void api_regist(const string &api_path, const function<void(API_ARGS_JSON_ASYNC)
s_map_api.emplace(api_path, toApi(func));
}
void api_regist(const string &api_path, const function<void(API_ARGS_STRING)> &func){
s_map_api.emplace(api_path, toApi(func));
}
void api_regist(const string &api_path, const function<void(API_ARGS_STRING_ASYNC)> &func){
s_map_api.emplace(api_path, toApi(func));
}
//获取HTTP请求中url参数、content参数
static ApiArgsType getAllArgs(const Parser &parser) {
ApiArgsType allArgs;
......@@ -1054,9 +1082,9 @@ void installWebApi() {
#ifdef ENABLE_WEBRTC
static list<WebRtcTransportImp::Ptr> rtcs;
api_regist("/webrtc",[](API_ARGS_MAP_ASYNC){
api_regist("/index/api/webrtc",[](API_ARGS_STRING){
CHECK_ARGS("app", "stream");
auto src = dynamic_pointer_cast<RtspMediaSource>(MediaSource::find(RTSP_SCHEMA, DEFAULT_VHOST, allArgs["app"], allArgs["stream"]));
auto src = dynamic_pointer_cast<RtspMediaSource>(MediaSource::find(RTSP_SCHEMA, DEFAULT_VHOST, allArgs.getUrlArgs()["app"], allArgs.getUrlArgs()["stream"]));
if (!src) {
throw ApiRetException("流不存在", API::NotFound);
}
......@@ -1064,7 +1092,8 @@ void installWebApi() {
headerOut["Access-Control-Allow-Origin"] = "*";
auto rtc = WebRtcTransportImp::create(EventPollerPool::Instance().getPoller());
rtc->attach(src);
invoker(200, headerOut, rtc->GetLocalSdp());
val["sdp"] = rtc->getAnswerSdp(allArgs.Content());
val["type"] = "answer";
rtcs.emplace_back(rtc);
});
#endif
......
......@@ -85,6 +85,8 @@ using ApiArgsType = map<string, variant, StrCaseCompare>;
#define API_ARGS_MAP_ASYNC API_ARGS_MAP, const HttpSession::HttpResponseInvoker &invoker
#define API_ARGS_JSON SockInfo &sender, HttpSession::KeyValue &headerIn, HttpSession::KeyValue &headerOut, Json::Value &allArgs, Json::Value &val
#define API_ARGS_JSON_ASYNC API_ARGS_JSON, const HttpSession::HttpResponseInvoker &invoker
#define API_ARGS_STRING SockInfo &sender, HttpSession::KeyValue &headerIn, HttpSession::KeyValue &headerOut, const Parser &allArgs, Json::Value &val
#define API_ARGS_STRING_ASYNC API_ARGS_STRING, const HttpSession::HttpResponseInvoker &invoker
#define API_ARGS_VALUE sender, headerIn, headerOut, allArgs, val
//注册http请求参数是map<string, variant, StrCaseCompare>类型的http api
......@@ -97,6 +99,11 @@ void api_regist(const string &api_path, const function<void(API_ARGS_JSON)> &fun
//注册http请求参数是Json::Value类型,但是可以异步回复的的http api
void api_regist(const string &api_path, const function<void(API_ARGS_JSON_ASYNC)> &func);
//注册http请求参数是http原始请求信息的http api
void api_regist(const string &api_path, const function<void(API_ARGS_STRING)> &func);
//注册http请求参数是http原始请求信息的异步回复的http api
void api_regist(const string &api_path, const function<void(API_ARGS_STRING_ASYNC)> &func);
template<typename Args, typename First>
bool checkArgs(Args &&args, First &&first) {
return !args[first].empty();
......@@ -107,6 +114,16 @@ bool checkArgs(Args &&args, First &&first, KeyTypes &&...keys) {
return !args[first].empty() && checkArgs(std::forward<Args>(args), std::forward<KeyTypes>(keys)...);
}
template<typename First>
bool checkArgs(const Parser &args, First &&first) {
return !args.getUrlArgs()[first].empty();
}
template<typename First, typename ...KeyTypes>
bool checkArgs(const Parser &args, First &&first, KeyTypes &&...keys) {
return !args.getUrlArgs()[first].empty() && checkArgs(args, std::forward<KeyTypes>(keys)...);
}
//检查http参数是否为空的宏
#define CHECK_ARGS(...) \
if(!checkArgs(allArgs,##__VA_ARGS__)){ \
......
......@@ -25,6 +25,7 @@
#include "Rtp/RtpServer.h"
#include "WebApi.h"
#include "WebHook.h"
#include "../webrtc/Sdp.h"
#if defined(ENABLE_VERSION)
#include "Version.h"
......@@ -209,6 +210,153 @@ static void inline listen_shell_input(){
//全局变量,在WebApi中用于保存配置文件用
string g_ini_file;
void test_sdp(){
char str1[] = "v=0\n"
"o=- 380154348540553537 2 IN IP4 127.0.0.1\n"
"s=-\n"
"b=CT:1900\n"
"t=0 0\n"
"a=group:BUNDLE video\n"
"a=msid-semantic: WMS\n"
"m=video 9 RTP/SAVPF 96\n"
"c=IN IP4 0.0.0.0\n"
"a=rtcp:9 IN IP4 0.0.0.0\n"
"a=ice-ufrag:1ZFN\n"
"a=ice-pwd:70P3H0jPlGz1fiJl5XZfXMZH\n"
"a=ice-options:trickle\n"
"a=fingerprint:sha-256 3E:10:35:6B:9A:9E:B0:55:AC:2A:88:F5:74:C1:70:32:B5:8D:88:1D:37:B0:9C:69:A6:DD:07:10:73:27:1A:16\n"
"a=setup:active\n"
"a=mid:video\n"
"a=recvonly\n"
"a=rtcp-mux\n"
"a=rtpmap:96 H264/90000\n"
"a=fmtp:96 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01f";
char str2[] = "v=0\n"
"o=- 2584450093346841581 2 IN IP4 127.0.0.1\n"
"s=-\n"
"t=0 0\n"
"a=group:BUNDLE audio video data\n"
"a=msid-semantic: WMS 616cfbb1-33a3-4d8c-8275-a199d6005549\n"
"m=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 110 112 113 126\n"
"c=IN IP4 0.0.0.0\n"
"a=rtcp:9 IN IP4 0.0.0.0\n"
"a=ice-ufrag:sXJ3\n"
"a=ice-pwd:yEclOTrLg1gEubBFefOqtmyV\n"
"a=fingerprint:sha-256 22:14:B5:AF:66:12:C7:C7:8D:EF:4B:DE:40:25:ED:5D:8F:17:54:DD:88:33:C0:13:2E:FD:1A:FA:7E:7A:1B:79\n"
"a=setup:actpass\n"
"a=mid:audio\n"
"a=extmap:1/sendonly urn:ietf:params:rtp-hdrext:ssrc-audio-level\n"
"a=sendrecv\n"
"a=rtcp-mux\n"
"a=rtpmap:111 opus/48000/2\n"
"a=rtcp-fb:111 transport-cc\n"
"a=fmtp:111 minptime=10;useinbandfec=1\n"
"a=rtpmap:103 ISAC/16000\n"
"a=rtpmap:104 ISAC/32000\n"
"a=rtpmap:9 G722/8000\n"
"a=rtpmap:0 PCMU/8000\n"
"a=rtpmap:8 PCMA/8000\n"
"a=rtpmap:106 CN/32000\n"
"a=rtpmap:105 CN/16000\n"
"a=rtpmap:13 CN/8000\n"
"a=rtpmap:110 telephone-event/48000\n"
"a=rtpmap:112 telephone-event/32000\n"
"a=rtpmap:113 telephone-event/16000\n"
"a=rtpmap:126 telephone-event/8000\n"
"a=ssrc:120276603 cname:iSkJ2vn5cYYubTve\n"
"a=ssrc:120276603 msid:616cfbb1-33a3-4d8c-8275-a199d6005549 1da3d329-7399-4fe9-b20f-69606bebd363\n"
"a=ssrc:120276603 mslabel:616cfbb1-33a3-4d8c-8275-a199d6005549\n"
"a=ssrc:120276603 label:1da3d329-7399-4fe9-b20f-69606bebd363\n"
"m=video 9 UDP/TLS/RTP/SAVPF 96 98 100 102 127 97 99 101 125\n"
"c=IN IP4 0.0.0.0\n"
"a=rtcp:9 IN IP4 0.0.0.0\n"
"a=ice-ufrag:sXJ3\n"
"a=ice-pwd:yEclOTrLg1gEubBFefOqtmyV\n"
"a=fingerprint:sha-256 22:14:B5:AF:66:12:C7:C7:8D:EF:4B:DE:40:25:ED:5D:8F:17:54:DD:88:33:C0:13:2E:FD:1A:FA:7E:7A:1B:79\n"
"a=setup:actpass\n"
"a=mid:video\n"
"a=extmap:2 urn:ietf:params:rtp-hdrext:toffset\n"
"a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\n"
"a=extmap:4 urn:3gpp:video-orientation\n"
"a=extmap:5 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01\n"
"a=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay\n"
"a=sendrecv\n"
"a=rtcp-mux\n"
"a=rtcp-rsize\n"
"a=rtpmap:96 VP8/90000\n"
"a=rtcp-fb:96 ccm fir\n"
"a=rtcp-fb:96 nack\n"
"a=rtcp-fb:96 nack pli\n"
"a=rtcp-fb:96 goog-remb\n"
"a=rtcp-fb:96 transport-cc\n"
"a=rtpmap:98 VP9/90000\n"
"a=rtcp-fb:98 ccm fir\n"
"a=rtcp-fb:98 nack\n"
"a=rtcp-fb:98 nack pli\n"
"a=rtcp-fb:98 goog-remb\n"
"a=rtcp-fb:98 transport-cc\n"
"a=rtpmap:100 H264/90000\n"
"a=rtcp-fb:100 ccm fir\n"
"a=rtcp-fb:100 nack\n"
"a=rtcp-fb:100 nack pli\n"
"a=rtcp-fb:100 goog-remb\n"
"a=rtcp-fb:100 transport-cc\n"
"a=fmtp:100 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f\n"
"a=rtpmap:102 red/90000\n"
"a=rtpmap:127 ulpfec/90000\n"
"a=rtpmap:97 rtx/90000\n"
"a=fmtp:97 apt=96\n"
"a=rtpmap:99 rtx/90000\n"
"a=fmtp:99 apt=98\n"
"a=rtpmap:101 rtx/90000\n"
"a=fmtp:101 apt=100\n"
"a=rtpmap:125 rtx/90000\n"
"a=fmtp:125 apt=102\n"
"a=ssrc-group:FID 2580761338 611523443\n"
"a=ssrc:2580761338 cname:iSkJ2vn5cYYubTve\n"
"a=ssrc:2580761338 msid:616cfbb1-33a3-4d8c-8275-a199d6005549 bf270496-a23e-47b5-b901-ef23096cd961\n"
"a=ssrc:2580761338 mslabel:616cfbb1-33a3-4d8c-8275-a199d6005549\n"
"a=ssrc:2580761338 label:bf270496-a23e-47b5-b901-ef23096cd961\n"
"a=ssrc:611523443 cname:iSkJ2vn5cYYubTve\n"
"a=ssrc:611523443 msid:616cfbb1-33a3-4d8c-8275-a199d6005549 bf270496-a23e-47b5-b901-ef23096cd961\n"
"a=ssrc:611523443 mslabel:616cfbb1-33a3-4d8c-8275-a199d6005549\n"
"a=ssrc:611523443 label:bf270496-a23e-47b5-b901-ef23096cd961\n"
"a=candidate:3575467457 1 udp 2113937151 10.15.83.23 57857 typ host generation 0 ufrag 6R0z network-cost 999\n"
"m=application 9 DTLS/SCTP 5000\n"
"c=IN IP4 0.0.0.0\n"
"a=ice-ufrag:sXJ3\n"
"a=ice-pwd:yEclOTrLg1gEubBFefOqtmyV\n"
"a=fingerprint:sha-256 22:14:B5:AF:66:12:C7:C7:8D:EF:4B:DE:40:25:ED:5D:8F:17:54:DD:88:33:C0:13:2E:FD:1A:FA:7E:7A:1B:79\n"
"a=setup:actpass\n"
"a=mid:data\n"
"a=sctpmap:5000 webrtc-datachannel 1024\n"
"a=sctp-port:5000";
RtcSessionSdp sdp1;
sdp1.parse(str1);
RtcSessionSdp sdp2;
sdp2.parse(str2);
for (auto media : sdp1.medias) {
InfoL << getRtpDirectionString(media.getDirection());
}
for (auto media : sdp2.medias) {
InfoL << getRtpDirectionString(media.getDirection());
}
InfoL << sdp1.toString();
InfoL << sdp2.toString();
RtcSession session1;
session1.loadFrom(str1);
RtcSession session2;
session2.loadFrom(str2);
DebugL << session1.toString();
DebugL << session2.toString();
}
int start_main(int argc,char *argv[]) {
{
CMD_main cmd_main;
......@@ -266,6 +414,7 @@ int start_main(int argc,char *argv[]) {
});
}
// test_sdp();
uint16_t shellPort = mINI::Instance()[Shell::kPort];
uint16_t rtspPort = mINI::Instance()[Rtsp::kPort];
uint16_t rtspsPort = mINI::Instance()[Rtsp::kSSLPort];
......
......@@ -101,7 +101,10 @@ static bool registerAllItem(){
registerSdpItem<SdpDirectionRecvonly>();
registerSdpItem<SdpDirectionSendrecv>();
registerSdpItem<SdpDirectionInactive>();
registerSdpItem<SdpAttrMsid>();
registerSdpItem<SdpAttrExtmapAllowMixed>();
registerSdpItem<SdpAttrRid>();
registerSdpItem<SdpAttrSimulcast>();
return true;
}
......
......@@ -445,6 +445,28 @@ public:
const char* getKey() const override { return "candidate";}
};
class SdpAttrMsid : public SdpItem{
public:
const char* getKey() const override { return "msid";}
};
class SdpAttrExtmapAllowMixed : public SdpItem{
public:
const char* getKey() const override { return "extmap-allow-mixed";}
};
class SdpAttrSimulcast : public SdpItem{
public:
//todo
const char* getKey() const override { return "simulcast";}
};
class SdpAttrRid : public SdpItem{
public:
//todo
const char* getKey() const override { return "rid";}
};
class RtcSdpBase {
public:
vector<SdpItem::Ptr> items;
......@@ -575,6 +597,7 @@ public:
RtcSSRC rtx_ssrc;
//////// simulcast ////////
bool simulcast{false};
RtcSSRC rtp_ssrc_low;
RtcSSRC rtp_ssrc_mid;
RtcSSRC rtp_ssrc_high;
......@@ -611,6 +634,8 @@ public:
class RtcSession{
public:
using Ptr = std::shared_ptr<RtcSession>;
uint32_t version;
SdpOrigin origin;
string session_name;
......
......@@ -62,7 +62,17 @@ void WebRtcTransport::onWrite(const char *buf, size_t len){
onWrite(buf, len, (struct sockaddr_in *)tuple);
}
std::string WebRtcTransport::GetLocalSdp() {
std::string WebRtcTransport::getAnswerSdp(const string &offer){
InfoL << offer;
_offer_sdp = std::make_shared<RtcSession>();
_offer_sdp->loadFrom(offer);
RtcConfigure configure;
_answer_sdp = configure.createAnswer(*_offer_sdp);
return _answer_sdp->toString();
}
std::string WebRtcTransport::getOfferSdp() {
RTC::DtlsTransport::Fingerprint remote_fingerprint;
remote_fingerprint.algorithm = RTC::DtlsTransport::GetFingerprintAlgorithm("sha-256");
remote_fingerprint.value = "";
......
......@@ -7,6 +7,7 @@
#include "IceServer.hpp"
#include "SrtpSession.hpp"
#include "StunPacket.hpp"
#include "Sdp.h"
class WebRtcTransport : public RTC::DtlsTransport::Listener, public RTC::IceServer::Listener {
public:
......@@ -17,9 +18,9 @@ public:
/// 销毁对象
virtual void onDestory();
/// 获取本地sdp
/// \return
std::string GetLocalSdp();
std::string getAnswerSdp(const string &offer);
std::string getOfferSdp();
/// 收到udp数据
/// \param buf
......@@ -76,6 +77,8 @@ private:
std::shared_ptr<RTC::IceServer> ice_server_;
std::shared_ptr<RTC::DtlsTransport> dtls_transport_;
std::shared_ptr<RTC::SrtpSession> srtp_session_;
RtcSession::Ptr _offer_sdp;
RtcSession::Ptr _answer_sdp;
};
#include "Poller/EventPoller.h"
......
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
<!DOCTYPE html>
<!--
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
-->
<html>
<head>
<title>PeerConnection PRANSWER Demo</title>
<!-- Load the polyfill to switch-hit between Chrome and Firefox -->
<style>
video {
border:5px solid black;
width:800px;
height:600px;
}
</style>
</head>
<body>
<video id="vid2" autoplay></video>
<br>
<p>ip_address</p>
<input id="input1" type="text" name="ip_address" value="https://rp.zlmediakit.com:20443/webrtc?app=live&stream=test">
<br>
<button id="btn1">Call</button>
<button id="btn3">Hang Up</button>
</body>
<script src="js/adapter.js"></script>
<script src="js/common.js"></script>
<script src="js/main.js"></script>
</html>
<meta charset="utf-8">
<head>
<title>ZLM RTC demo</title>
<script src="./ZLMRTCClient.js"></script>
</head>
<body>
<div style="text-align: center;">
<div>
<video id='video' controls autoplay style="text-align:left;">
Your browser is too old which doesn't support HTML5 video.
</video>
<video id='selfVideo' controls autoplay style="text-align:right;">
Your browser is too old which doesn't support HTML5 video.
</video>
</div>
<div>
<p>
<label for="streamUrl">url:</label>
<input type="text" id='streamUrl' value="https://rp.zlmediakit.com:20443/index/api/webrtc?app=live&stream=test">
</p>
<p>
<label for="simulecast">simulecast:</label>
<input type="checkbox" id='simulecast' checked="checked">
</p>
<button onclick="start()">开始</button>
<button onclick="stop()">停止</button>
</div>
</div>
<script>
var player = null
function start()
{
stop();
player = new ZLMRTCClient.Endpoint(
{
element: document.getElementById('video'),// video 标签
debug: true,// 是否打印日志
zlmsdpUrl:document.getElementById('streamUrl').value,//流地址
simulecast:document.getElementById('simulecast').checked
}
);
player.on(ZLMRTCClient.Events.WEBRTC_ICE_CANDIDATE_ERROR,function(e)
{// ICE 协商出错
console.log('ICE 协商出错')
});
player.on(ZLMRTCClient.Events.WEBRTC_ON_REMOTE_STREAMS,function(e)
{//获取到了远端流,可以播放
console.log('播放成功',e.streams)
});
player.on(ZLMRTCClient.Events.WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED,function(e)
{// offer anwser 交换失败
console.log('offer anwser 交换失败',e)
});
player.on(ZLMRTCClient.Events.WEBRTC_ON_LOCAL_STREAM,function(s)
{// 获取到了本地流
document.getElementById('selfVideo').srcObject=s;
//console.log('offer anwser 交换失败',e)
});
}
function stop()
{
if(player)
{
player.close();
player = null;
var local = document.getElementById('selfVideo');
local.removeAttribute('srcObject');
local.load();
}
}
</script>
</body>
<script>
</script>
</html>
\ No newline at end of file
function trace(text) {
// This function is used for logging.
if (text[text.length - 1] === '\n') {
text = text.substring(0, text.length - 1);
}
if (window.performance) {
var now = (window.performance.now() / 1000).toFixed(3);
console.log(now + ': ' + text);
} else {
console.log(text);
}
}
/*
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
'use strict';
var vid2 = document.getElementById('vid2');
var btn1 = document.getElementById('btn1');
var btn3 = document.getElementById('btn3');
var input1 = document.getElementById('input1');
btn1.addEventListener('click', start);
btn3.addEventListener('click', stop);
btn1.disabled = false;
btn3.disabled = true;
var pc2 = null;
var xmlhttp = null;
function start() {
btn1.disabled = true;
btn3.disabled = false;
trace('Starting Call');
var servers = null;
var addr = input1.value;
pc2 = new RTCPeerConnection(servers);
trace('Created remote peer connection object pc2');
pc2.onicecandidate = iceCallback;
pc2.onaddstream = gotRemoteStream;
if (window.XMLHttpRequest)
{
// IE7+, Firefox, Chrome, Opera, Safari �����ִ�д���
xmlhttp=new XMLHttpRequest();
}
else
{
// IE6, IE5 �����ִ�д���
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange=function()
{
if (xmlhttp.readyState==4 && xmlhttp.status==200)
{
var res = xmlhttp.responseText;
gotoffer(res);
}
}
xmlhttp.open("GET",addr,true);
xmlhttp.send();
}
function onCreateSessionDescriptionError(error) {
trace('Failed to create session description: ' + error.toString());
stop();
}
function onCreateAnswerError(error) {
trace('Failed to set createAnswer: ' + error.toString());
stop();
}
function onSetLocalDescriptionError(error) {
trace('Failed to set setLocalDescription: ' + error.toString());
stop();
}
function onSetLocalDescriptionSuccess() {
trace('localDescription success.');
}
function gotoffer(offer) {
trace('Offer from server \n' + offer);
//??????offer sdp????????RTCSessionDescription????
var desc = new RTCSessionDescription();
desc.sdp = offer;
desc.type = 'offer';
pc2.setRemoteDescription(desc);
// Since the 'remote' side has no media stream we need
// to pass in the right constraints in order for it to
// accept the incoming offer of audio and video.
pc2.createAnswer().then(
gotDescription2,
onCreateSessionDescriptionError
);
}
function gotDescription2(desc) {
// Provisional answer, set a=inactive & set sdp type to pranswer.
/*desc.sdp = desc.sdp.replace(/a=recvonly/g, 'a=inactive');
desc.type = 'pranswer';*/
pc2.setLocalDescription(desc).then(
onSetLocalDescriptionSuccess,
onSetLocalDescriptionError
);
trace('Pranswer from pc2 \n' + desc.sdp);
//conn.send(JSON.stringify(desc));
// send desc.sdp to server
}
function stop() {
trace('Ending Call' + '\n\n');
pc2.close();
pc2 = null;
}
function gotRemoteStream(e) {
vid2.srcObject = e.stream;
trace('Received remote stream');
}
function iceCallback(event) {
if (event.candidate) {
trace('Remote ICE candidate: \n ' + event.candidate.candidate);
//conn.send(JSON.stringify(event.candidate));
}
else {
// All ICE candidates have been sent
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论