开始对接js

This commit is contained in:
ziyue 2021-03-31 17:15:26 +08:00
parent 978917b465
commit 7e5cb33395
14 changed files with 7807 additions and 2662 deletions

View File

@ -136,7 +136,7 @@ charSet=utf-8
#http链接超时时间 #http链接超时时间
keepAliveSecond=30 keepAliveSecond=30
#http请求体最大字节数如果post的body太大则不适合缓存body在内存 #http请求体最大字节数如果post的body太大则不适合缓存body在内存
maxReqSize=4096 maxReqSize=40960
#404网页内容用户可以自定义404网页 #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> 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服务器监听端口 #http服务器监听端口

View File

@ -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) { void api_regist(const string &api_path, const function<void(API_ARGS_MAP)> &func) {
s_map_api.emplace(api_path, toApi(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)); 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参数 //获取HTTP请求中url参数、content参数
static ApiArgsType getAllArgs(const Parser &parser) { static ApiArgsType getAllArgs(const Parser &parser) {
ApiArgsType allArgs; ApiArgsType allArgs;
@ -1054,9 +1082,9 @@ void installWebApi() {
#ifdef ENABLE_WEBRTC #ifdef ENABLE_WEBRTC
static list<WebRtcTransportImp::Ptr> rtcs; static list<WebRtcTransportImp::Ptr> rtcs;
api_regist("/webrtc",[](API_ARGS_MAP_ASYNC){ api_regist("/index/api/webrtc",[](API_ARGS_STRING){
CHECK_ARGS("app", "stream"); 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) { if (!src) {
throw ApiRetException("流不存在", API::NotFound); throw ApiRetException("流不存在", API::NotFound);
} }
@ -1064,7 +1092,8 @@ void installWebApi() {
headerOut["Access-Control-Allow-Origin"] = "*"; headerOut["Access-Control-Allow-Origin"] = "*";
auto rtc = WebRtcTransportImp::create(EventPollerPool::Instance().getPoller()); auto rtc = WebRtcTransportImp::create(EventPollerPool::Instance().getPoller());
rtc->attach(src); rtc->attach(src);
invoker(200, headerOut, rtc->GetLocalSdp()); val["sdp"] = rtc->getAnswerSdp(allArgs.Content());
val["type"] = "answer";
rtcs.emplace_back(rtc); rtcs.emplace_back(rtc);
}); });
#endif #endif

View File

@ -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_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 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_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 #define API_ARGS_VALUE sender, headerIn, headerOut, allArgs, val
//注册http请求参数是map<string, variant, StrCaseCompare>类型的http api //注册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 //注册http请求参数是Json::Value类型但是可以异步回复的的http api
void api_regist(const string &api_path, const function<void(API_ARGS_JSON_ASYNC)> &func); 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> template<typename Args, typename First>
bool checkArgs(Args &&args, First &&first) { bool checkArgs(Args &&args, First &&first) {
return !args[first].empty(); 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)...); 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参数是否为空的宏 //检查http参数是否为空的宏
#define CHECK_ARGS(...) \ #define CHECK_ARGS(...) \
if(!checkArgs(allArgs,##__VA_ARGS__)){ \ if(!checkArgs(allArgs,##__VA_ARGS__)){ \

View File

@ -25,6 +25,7 @@
#include "Rtp/RtpServer.h" #include "Rtp/RtpServer.h"
#include "WebApi.h" #include "WebApi.h"
#include "WebHook.h" #include "WebHook.h"
#include "../webrtc/Sdp.h"
#if defined(ENABLE_VERSION) #if defined(ENABLE_VERSION)
#include "Version.h" #include "Version.h"
@ -209,6 +210,153 @@ static void inline listen_shell_input(){
//全局变量在WebApi中用于保存配置文件用 //全局变量在WebApi中用于保存配置文件用
string g_ini_file; 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[]) { int start_main(int argc,char *argv[]) {
{ {
CMD_main cmd_main; 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 shellPort = mINI::Instance()[Shell::kPort];
uint16_t rtspPort = mINI::Instance()[Rtsp::kPort]; uint16_t rtspPort = mINI::Instance()[Rtsp::kPort];
uint16_t rtspsPort = mINI::Instance()[Rtsp::kSSLPort]; uint16_t rtspsPort = mINI::Instance()[Rtsp::kSSLPort];

View File

@ -101,7 +101,10 @@ static bool registerAllItem(){
registerSdpItem<SdpDirectionRecvonly>(); registerSdpItem<SdpDirectionRecvonly>();
registerSdpItem<SdpDirectionSendrecv>(); registerSdpItem<SdpDirectionSendrecv>();
registerSdpItem<SdpDirectionInactive>(); registerSdpItem<SdpDirectionInactive>();
registerSdpItem<SdpAttrMsid>();
registerSdpItem<SdpAttrExtmapAllowMixed>();
registerSdpItem<SdpAttrRid>();
registerSdpItem<SdpAttrSimulcast>();
return true; return true;
} }

View File

@ -445,6 +445,28 @@ public:
const char* getKey() const override { return "candidate";} 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 { class RtcSdpBase {
public: public:
vector<SdpItem::Ptr> items; vector<SdpItem::Ptr> items;
@ -575,6 +597,7 @@ public:
RtcSSRC rtx_ssrc; RtcSSRC rtx_ssrc;
//////// simulcast //////// //////// simulcast ////////
bool simulcast{false};
RtcSSRC rtp_ssrc_low; RtcSSRC rtp_ssrc_low;
RtcSSRC rtp_ssrc_mid; RtcSSRC rtp_ssrc_mid;
RtcSSRC rtp_ssrc_high; RtcSSRC rtp_ssrc_high;
@ -611,6 +634,8 @@ public:
class RtcSession{ class RtcSession{
public: public:
using Ptr = std::shared_ptr<RtcSession>;
uint32_t version; uint32_t version;
SdpOrigin origin; SdpOrigin origin;
string session_name; string session_name;

View File

@ -62,7 +62,17 @@ void WebRtcTransport::onWrite(const char *buf, size_t len){
onWrite(buf, len, (struct sockaddr_in *)tuple); 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; RTC::DtlsTransport::Fingerprint remote_fingerprint;
remote_fingerprint.algorithm = RTC::DtlsTransport::GetFingerprintAlgorithm("sha-256"); remote_fingerprint.algorithm = RTC::DtlsTransport::GetFingerprintAlgorithm("sha-256");
remote_fingerprint.value = ""; remote_fingerprint.value = "";

View File

@ -7,6 +7,7 @@
#include "IceServer.hpp" #include "IceServer.hpp"
#include "SrtpSession.hpp" #include "SrtpSession.hpp"
#include "StunPacket.hpp" #include "StunPacket.hpp"
#include "Sdp.h"
class WebRtcTransport : public RTC::DtlsTransport::Listener, public RTC::IceServer::Listener { class WebRtcTransport : public RTC::DtlsTransport::Listener, public RTC::IceServer::Listener {
public: public:
@ -17,9 +18,9 @@ public:
/// 销毁对象 /// 销毁对象
virtual void onDestory(); virtual void onDestory();
/// 获取本地sdp std::string getAnswerSdp(const string &offer);
/// \return
std::string GetLocalSdp(); std::string getOfferSdp();
/// 收到udp数据 /// 收到udp数据
/// \param buf /// \param buf
@ -76,6 +77,8 @@ private:
std::shared_ptr<RTC::IceServer> ice_server_; std::shared_ptr<RTC::IceServer> ice_server_;
std::shared_ptr<RTC::DtlsTransport> dtls_transport_; std::shared_ptr<RTC::DtlsTransport> dtls_transport_;
std::shared_ptr<RTC::SrtpSession> srtp_session_; std::shared_ptr<RTC::SrtpSession> srtp_session_;
RtcSession::Ptr _offer_sdp;
RtcSession::Ptr _answer_sdp;
}; };
#include "Poller/EventPoller.h" #include "Poller/EventPoller.h"

7466
www/webrtc/ZLMRTCClient.js Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -1,34 +1,97 @@
<!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> <html>
<meta charset="utf-8">
<head> <head>
<title>PeerConnection PRANSWER Demo</title> <title>ZLM RTC demo</title>
<!-- Load the polyfill to switch-hit between Chrome and Firefox --> <script src="./ZLMRTCClient.js"></script>
<style>
video {
border:5px solid black;
width:800px;
height:600px;
}
</style>
</head> </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>
<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>

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +0,0 @@
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);
}
}

View File

@ -1,134 +0,0 @@
/*
* 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 <20><><EFBFBD><EFBFBD><EFBFBD>ִ<EFBFBD>д<EFBFBD><D0B4><EFBFBD>
xmlhttp=new XMLHttpRequest();
}
else
{
// IE6, IE5 <20><><EFBFBD><EFBFBD><EFBFBD>ִ<EFBFBD>д<EFBFBD><D0B4><EFBFBD>
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
}
}