mirror of
https://github.com/ZLMediaKit/ZLMediaKit.git
synced 2024-11-26 04:31:37 +08:00
开始对接js
This commit is contained in:
parent
978917b465
commit
7e5cb33395
@ -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) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
@ -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__)){ \
|
||||||
|
149
server/main.cpp
149
server/main.cpp
@ -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];
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
25
webrtc/Sdp.h
25
webrtc/Sdp.h
@ -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;
|
||||||
|
@ -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 @@
|
|||||||
#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
7466
www/webrtc/ZLMRTCClient.js
Normal file
File diff suppressed because it is too large
Load Diff
1
www/webrtc/ZLMRTCClient.js.map
Normal file
1
www/webrtc/ZLMRTCClient.js.map
Normal file
File diff suppressed because one or more lines are too long
@ -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
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user