Commit 7e5cb333 by ziyue

开始对接js

parent 978917b4
...@@ -136,7 +136,7 @@ charSet=utf-8 ...@@ -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服务器监听端口
......
...@@ -127,6 +127,26 @@ static HttpApi toApi(const function<void(API_ARGS_JSON)> &cb) { ...@@ -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) ...@@ -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() { ...@@ -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() { ...@@ -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
......
...@@ -85,6 +85,8 @@ using ApiArgsType = map<string, variant, StrCaseCompare>; ...@@ -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 ...@@ -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) { ...@@ -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__)){ \
......
...@@ -25,6 +25,7 @@ ...@@ -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(){ ...@@ -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[]) { ...@@ -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];
......
...@@ -101,7 +101,10 @@ static bool registerAllItem(){ ...@@ -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;
} }
......
...@@ -445,6 +445,28 @@ public: ...@@ -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: ...@@ -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: ...@@ -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;
......
...@@ -62,7 +62,17 @@ void WebRtcTransport::onWrite(const char *buf, size_t len){ ...@@ -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 = "";
......
...@@ -7,6 +7,7 @@ ...@@ -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: ...@@ -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: ...@@ -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"
......
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> <html>
<head> <meta charset="utf-8">
<title>PeerConnection PRANSWER Demo</title> <head>
<!-- Load the polyfill to switch-hit between Chrome and Firefox --> <title>ZLM RTC demo</title>
<style> <script src="./ZLMRTCClient.js"></script>
video { </head>
border:5px solid black;
width:800px; <body>
height:600px;
<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
} }
</style> );
</head>
<body> player.on(ZLMRTCClient.Events.WEBRTC_ICE_CANDIDATE_ERROR,function(e)
<video id="vid2" autoplay></video> {// ICE 协商出错
<br> console.log('ICE 协商出错')
<p>ip_address</p> });
<input id="input1" type="text" name="ip_address" value="https://rp.zlmediakit.com:20443/webrtc?app=live&stream=test">
<br> player.on(ZLMRTCClient.Events.WEBRTC_ON_REMOTE_STREAMS,function(e)
<button id="btn1">Call</button> {//获取到了远端流,可以播放
<button id="btn3">Hang Up</button> console.log('播放成功',e.streams)
</body> });
<script src="js/adapter.js"></script>
<script src="js/common.js"></script> player.on(ZLMRTCClient.Events.WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED,function(e)
<script src="js/main.js"></script> {// offer anwser 交换失败
</html> 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 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论