feat: 增加webrtc代理拉流 (#4389)

- 增加客户端模式,支持主动拉流、推流:
   - addStreamProxy接口新增支持whep主动拉流,拉流地址目前只兼容zlm的whep url。
   - addStreamPusherProxy接口新增支持whip主动推流,推流地址目前只兼容zlm的whip url。
   - 以上推流url格式为webrtc[s]://server_host:server_port/app/stream_id?key=value, 内部会自动转换为http[s]://server_host:server_port/index/api/[whip/whep]?app=app&stream=stream_id&key=value。

- 增加WebRtc p2p 模式:
  - 增加 ICE FULL模式。
  - 增加STUN/TURN 服务器。
  - 增加websocket 信令。
  - 增加P2P代理拉流。

---------

Co-authored-by: xia-chu <771730766@qq.com>
Co-authored-by: mtdxc <mtdxc@126.com>
Co-authored-by: cqm <cqm@97kid.com>
This commit is contained in:
baigao-X
2025-09-20 16:23:30 +08:00
committed by GitHub
parent 97d2a1fb08
commit 3fb43c5fef
72 changed files with 16912 additions and 10319 deletions

View File

@@ -57,6 +57,10 @@
#include "../webrtc/WebRtcPlayer.h"
#include "../webrtc/WebRtcPusher.h"
#include "../webrtc/WebRtcEchoTest.h"
#include "../webrtc/WebRtcSignalingPeer.h"
#include "../webrtc/WebRtcSignalingSession.h"
#include "../webrtc/WebRtcProxyPlayer.h"
#include "../webrtc/WebRtcProxyPlayerImp.h"
#endif
#if defined(ENABLE_VERSION)
@@ -314,84 +318,6 @@ static inline void addHttpListener(){
});
}
template <typename Type>
class ServiceController {
public:
using Pointer = std::shared_ptr<Type>;
void clear() {
decltype(_map) copy;
{
std::lock_guard<std::recursive_mutex> lck(_mtx);
copy.swap(_map);
}
}
size_t erase(const std::string &key) {
Pointer erase_ptr;
{
std::lock_guard<std::recursive_mutex> lck(_mtx);
auto itr = _map.find(key);
if (itr != _map.end()) {
erase_ptr = itr->second;
_map.erase(itr);
return 1;
}
}
return 0;
}
size_t size() {
std::lock_guard<std::recursive_mutex> lck(_mtx);
return _map.size();
}
Pointer find(const std::string &key) const {
std::lock_guard<std::recursive_mutex> lck(_mtx);
auto it = _map.find(key);
if (it == _map.end()) {
return nullptr;
}
return it->second;
}
void for_each(const std::function<void(const std::string&, const Pointer&)>& cb) {
std::lock_guard<std::recursive_mutex> lck(_mtx);
auto it = _map.begin();
while (it != _map.end()) {
cb(it->first, it->second);
it++;
}
}
template<class ..._Args>
Pointer make(const std::string &key, _Args&& ...__args) {
// assert(!find(key));
auto server = std::make_shared<Type>(std::forward<_Args>(__args)...);
std::lock_guard<std::recursive_mutex> lck(_mtx);
auto it = _map.emplace(key, server);
assert(it.second);
return server;
}
template<class ..._Args>
Pointer makeWithAction(const std::string &key, function<void(Pointer)> action, _Args&& ...__args) {
// assert(!find(key));
auto server = std::make_shared<Type>(std::forward<_Args>(__args)...);
action(server);
std::lock_guard<std::recursive_mutex> lck(_mtx);
auto it = _map.emplace(key, server);
assert(it.second);
return server;
}
private:
std::unordered_map<std::string, Pointer> _map;
mutable std::recursive_mutex _mtx;
};
// 拉流代理器列表 [AUTO-TRANSLATED:6dcfb11f]
// Pull stream proxy list
static ServiceController<PlayerProxy> s_player_proxy;
@@ -2127,35 +2053,6 @@ void installWebApi() {
});
#ifdef ENABLE_WEBRTC
class WebRtcArgsImp : public WebRtcArgs {
public:
WebRtcArgsImp(const ArgsString &args, std::string session_id)
: _args(args)
, _session_id(std::move(session_id)) {}
~WebRtcArgsImp() override = default;
toolkit::variant operator[](const string &key) const override {
if (key == "url") {
return getUrl();
}
return _args[key];
}
private:
string getUrl() const{
auto &allArgs = _args;
CHECK_ARGS("app", "stream");
string auth = _args["Authorization"]; // Authorization Bearer
return StrPrinter << "rtc://" << _args["Host"] << "/" << _args["app"] << "/" << _args["stream"] << "?"
<< _args.parser.params() + "&session=" + _session_id + (auth.empty() ? "" : ("&Authorization=" + auth));
}
private:
ArgsString _args;
std::string _session_id;
};
api_regist("/index/api/webrtc",[](API_ARGS_STRING_ASYNC){
CHECK_ARGS("type");
auto type = allArgs["type"];
@@ -2163,7 +2060,7 @@ void installWebApi() {
CHECK(!offer.empty(), "http body(webrtc offer sdp) is empty");
auto &session = static_cast<Session&>(sender);
auto args = std::make_shared<WebRtcArgsImp>(allArgs, sender.getIdentifier());
auto args = std::make_shared<WebRtcArgsImp<std::string>>(allArgs, sender.getIdentifier());
WebRtcPluginManager::Instance().negotiateSdp(session, type, *args, [invoker, val, offer, headerOut](const WebRtcInterface &exchanger) mutable {
auto &handler = const_cast<WebRtcInterface &>(exchanger);
try {
@@ -2186,7 +2083,7 @@ void installWebApi() {
auto &session = static_cast<Session&>(sender);
auto location = std::string(session.overSsl() ? "https://" : "http://") + allArgs["host"] + delete_webrtc_url;
auto args = std::make_shared<WebRtcArgsImp>(allArgs, sender.getIdentifier());
auto args = std::make_shared<WebRtcArgsImp<std::string>>(allArgs, sender.getIdentifier());
WebRtcPluginManager::Instance().negotiateSdp(session, type, *args, [invoker, offer, headerOut, location](const WebRtcInterface &exchanger) mutable {
auto &handler = const_cast<WebRtcInterface &>(exchanger);
try {
@@ -2220,6 +2117,103 @@ void installWebApi() {
obj->safeShutdown(SockException(Err_shutdown, "deleted by http api"));
invoker(200, headerOut, "");
});
// 获取WebRTCProxyPlayer 连接信息
api_regist("/index/api/getWebrtcProxyPlayerInfo", [](API_ARGS_MAP_ASYNC) {
CHECK_SECRET();
CHECK_ARGS("key");
auto player_proxy = s_player_proxy.find(allArgs["key"]);
if (!player_proxy) {
throw ApiRetException("Stream proxy not found", API::NotFound);
}
auto media_player = player_proxy->getDelegate();
if (!media_player) {
throw ApiRetException("Media player not found", API::OtherFailed);
}
auto webrtc_player_imp = std::dynamic_pointer_cast<WebRtcProxyPlayerImp>(media_player);
if (!webrtc_player_imp) {
throw ApiRetException("Stream proxy is not WebRTC type", API::OtherFailed);
}
auto webrtc_transport = webrtc_player_imp->getWebRtcTransport();
if (!webrtc_transport) {
throw ApiRetException("WebRTC transport not available", API::OtherFailed);
}
std::string stream_key = allArgs["key"];
webrtc_transport->getTransportInfo([val, headerOut, invoker, stream_key](Json::Value transport_info) mutable {
transport_info["stream_key"] = stream_key;
if (transport_info.isMember("error")) {
Json::Value error_val;
error_val["code"] = API::OtherFailed;
error_val["msg"] = transport_info["error"].asString();
invoker(200, headerOut, error_val.toStyledString());
return;
}
// 成功返回结果
Json::Value success_val;
success_val["code"] = API::Success;
success_val["msg"] = "success";
success_val["data"] = transport_info;
invoker(200, headerOut, success_val.toStyledString());
});
});
api_regist("/index/api/addWebrtcRoomKeeper",[](API_ARGS_MAP_ASYNC){
CHECK_SECRET();
CHECK_ARGS("server_host", "server_port", "room_id", "ssl");
//server_host: 信令服务器host
//server_post: 信令服务器host
//room_id: 注册的id,信令服务器会对该id进行唯一性检查
addWebrtcRoomKeeper(allArgs["server_host"], allArgs["server_port"], allArgs["room_id"], allArgs["ssl"],
[val, headerOut, invoker](const SockException &ex, const string &key) mutable {
if (ex) {
val["code"] = API::OtherFailed;
val["msg"] = ex.what();
} else {
val["msg"] = "success";
val["data"]["room_key"] = key;
}
invoker(200, headerOut, val.toStyledString());
});
});
api_regist("/index/api/delWebrtcRoomKeeper",[](API_ARGS_MAP_ASYNC){
CHECK_SECRET();
CHECK_ARGS("room_key");
delWebrtcRoomKeeper(allArgs["room_key"],
[val, headerOut, invoker](const SockException &ex) mutable {
if (ex) {
val["code"] = API::OtherFailed;
val["msg"] = ex.what();
}
invoker(200, headerOut, val.toStyledString());
});
});
api_regist("/index/api/listWebrtcRoomKeepers", [](API_ARGS_MAP) {
CHECK_SECRET();
listWebrtcRoomKeepers([&val](const std::string& key, const WebRtcSignalingPeer::Ptr& p) {
Json::Value item = ToJson(p);
item["room_key"] = key;
val["data"].append(item);
});
});
api_regist("/index/api/listWebrtcRooms", [](API_ARGS_MAP) {
CHECK_SECRET();
listWebrtcRooms([&val](const std::string& key, const WebRtcSignalingSession::Ptr& p) {
Json::Value item = ToJson(p);
item["room_id"] = key;
val["data"].append(item);
});
});
#endif
#if defined(ENABLE_VERSION)

View File

@@ -1,228 +1,364 @@
/*
* Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
*
* Use of this source code is governed by MIT-like license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef ZLMEDIAKIT_WEBAPI_H
#define ZLMEDIAKIT_WEBAPI_H
#include <string>
#include <functional>
#include "json/json.h"
#include "Common/Parser.h"
#include "Network/Socket.h"
#include "Http/HttpSession.h"
#include "Common/MultiMediaSourceMuxer.h"
// 配置文件路径 [AUTO-TRANSLATED:8a373c2f]
// Configuration file path
extern std::string g_ini_file;
namespace mediakit {
// //////////RTSP服务器配置/////////// [AUTO-TRANSLATED:950e1981]
// //////////RTSP server configuration///////////
namespace Rtsp {
extern const std::string kPort;
} //namespace Rtsp
// //////////RTMP服务器配置/////////// [AUTO-TRANSLATED:8de6f41f]
// //////////RTMP server configuration///////////
namespace Rtmp {
extern const std::string kPort;
} //namespace RTMP
} // namespace mediakit
namespace API {
typedef enum {
NotFound = -500,//未找到
Exception = -400,//代码抛异常
InvalidArgs = -300,//参数不合法
SqlFailed = -200,//sql执行失败
AuthFailed = -100,//鉴权失败
OtherFailed = -1,//业务代码执行失败,
Success = 0//执行成功
} ApiErr;
extern const std::string kSecret;
}//namespace API
class ApiRetException: public std::runtime_error {
public:
ApiRetException(const char *str = "success" ,int code = API::Success):runtime_error(str){
_code = code;
}
int code(){ return _code; }
private:
int _code;
};
class AuthException : public ApiRetException {
public:
AuthException(const char *str):ApiRetException(str,API::AuthFailed){}
};
class InvalidArgsException: public ApiRetException {
public:
InvalidArgsException(const char *str):ApiRetException(str,API::InvalidArgs){}
};
class SuccessException: public ApiRetException {
public:
SuccessException():ApiRetException("success",API::Success){}
};
using ApiArgsType = std::map<std::string, std::string, mediakit::StrCaseCompare>;
template<typename Args, typename Key>
std::string getValue(Args &args, const Key &key) {
auto it = args.find(key);
if (it == args.end()) {
return "";
}
return it->second;
}
template<typename Key>
std::string getValue(Json::Value &args, const Key &key) {
auto value = args.find(key);
if (value == nullptr) {
return "";
}
return value->asString();
}
template<typename Key>
std::string getValue(std::string &args, const Key &key) {
return "";
}
template <typename Key>
std::string getValue(const mediakit::Parser &parser, const Key &key) {
auto ret = getValue(parser.getUrlArgs(), key);
if (!ret.empty()) {
return ret;
}
return getValue(parser.getHeader(), key);
}
template<typename Args, typename Key>
std::string getValue(const mediakit::Parser &parser, Args &args, const Key &key) {
auto ret = getValue(args, key);
if (!ret.empty()) {
return ret;
}
return getValue(parser, key);
}
template<typename Args>
class HttpAllArgs {
mediakit::Parser* _parser = nullptr;
Args* _args = nullptr;
public:
const mediakit::Parser& parser;
Args& args;
HttpAllArgs(const mediakit::Parser &p, Args &a): parser(p), args(a) {}
HttpAllArgs(const HttpAllArgs &that): _parser(new mediakit::Parser(that.parser)),
_args(new Args(that.args)),
parser(*_parser), args(*_args) {}
~HttpAllArgs() {
if (_parser) {
delete _parser;
}
if (_args) {
delete _args;
}
}
template<typename Key>
toolkit::variant operator[](const Key &key) const {
return (toolkit::variant)getValue(parser, args, key);
}
};
using ArgsMap = HttpAllArgs<ApiArgsType>;
using ArgsJson = HttpAllArgs<Json::Value>;
using ArgsString = HttpAllArgs<std::string>;
#define API_ARGS_MAP toolkit::SockInfo &sender, mediakit::HttpSession::KeyValue &headerOut, const ArgsMap &allArgs, Json::Value &val
#define API_ARGS_MAP_ASYNC API_ARGS_MAP, const mediakit::HttpSession::HttpResponseInvoker &invoker
#define API_ARGS_JSON toolkit::SockInfo &sender, mediakit::HttpSession::KeyValue &headerOut, const ArgsJson &allArgs, Json::Value &val
#define API_ARGS_JSON_ASYNC API_ARGS_JSON, const mediakit::HttpSession::HttpResponseInvoker &invoker
#define API_ARGS_STRING toolkit::SockInfo &sender, mediakit::HttpSession::KeyValue &headerOut, const ArgsString &allArgs, Json::Value &val
#define API_ARGS_STRING_ASYNC API_ARGS_STRING, const mediakit::HttpSession::HttpResponseInvoker &invoker
#define API_ARGS_VALUE sender, headerOut, allArgs, val
// 注册http请求参数是map<string, variant, StrCaseCompare>类型的http api [AUTO-TRANSLATED:8a273897]
// Register http request parameters as map<string, variant, StrCaseCompare> type http api
void api_regist(const std::string &api_path, const std::function<void(API_ARGS_MAP)> &func);
// 注册http请求参数是map<string, variant, StrCaseCompare>类型,但是可以异步回复的的http api [AUTO-TRANSLATED:9da5d5f5]
// Register http request parameters as map<string, variant, StrCaseCompare> type, but can be replied asynchronously http api
void api_regist(const std::string &api_path, const std::function<void(API_ARGS_MAP_ASYNC)> &func);
// 注册http请求参数是Json::Value类型的http api(可以支持多级嵌套的json参数对象) [AUTO-TRANSLATED:c4794456]
// Register http request parameters as Json::Value type http api (can support multi-level nested json parameter objects)
void api_regist(const std::string &api_path, const std::function<void(API_ARGS_JSON)> &func);
// 注册http请求参数是Json::Value类型但是可以异步回复的的http api [AUTO-TRANSLATED:742e57fd]
// Register http request parameters as Json::Value type, but can be replied asynchronously http api
void api_regist(const std::string &api_path, const std::function<void(API_ARGS_JSON_ASYNC)> &func);
// 注册http请求参数是http原始请求信息的http api [AUTO-TRANSLATED:72d3fe93]
// Register http request parameters as http original request information http api
void api_regist(const std::string &api_path, const std::function<void(API_ARGS_STRING)> &func);
// 注册http请求参数是http原始请求信息的异步回复的http api [AUTO-TRANSLATED:49feefa8]
// Register http request parameters as http original request information asynchronous reply http api
void api_regist(const std::string &api_path, const std::function<void(API_ARGS_STRING_ASYNC)> &func);
template<typename Args, typename Key>
bool checkArgs(Args &args, const Key &key) {
return !args[key].empty();
}
template<typename Args, typename Key, typename ...KeyTypes>
bool checkArgs(Args &args, const Key &key, const KeyTypes &...keys) {
return checkArgs(args, key) && checkArgs(args, keys...);
}
// 检查http url中或body中或http header参数是否为空的宏 [AUTO-TRANSLATED:9de001a4]
// Check whether the http url, body or http header parameters are empty
#define CHECK_ARGS(...) \
if(!checkArgs(allArgs,##__VA_ARGS__)){ \
throw InvalidArgsException("Required parameter missed: " #__VA_ARGS__); \
}
// 检查http参数中是否附带secret密钥的宏127.0.0.1的ip不检查密钥 [AUTO-TRANSLATED:7546956c]
// Check whether the http parameters contain the secret key, the ip of 127.0.0.1 does not check the key
// 同时检测是否在ip白名单内 [AUTO-TRANSLATED:d12f963d]
// Check whether it is in the ip whitelist at the same time
#define CHECK_SECRET() \
do { \
auto ip = sender.get_peer_ip(); \
if (!HttpFileManager::isIPAllowed(ip)) { \
throw AuthException("Your ip is not allowed to access the service."); \
} \
CHECK_ARGS("secret"); \
if (api_secret != allArgs["secret"]) { \
throw AuthException("Incorrect secret"); \
} \
} while(false);
void installWebApi();
void unInstallWebApi();
#if defined(ENABLE_RTPPROXY)
uint16_t openRtpServer(uint16_t local_port, const mediakit::MediaTuple &tuple, int tcp_mode, const std::string &local_ip, bool re_use_port, uint32_t ssrc, int only_track, bool multiplex=false);
#endif
Json::Value makeMediaSourceJson(mediakit::MediaSource &media);
void getStatisticJson(const std::function<void(Json::Value &val)> &cb);
void addStreamProxy(const mediakit::MediaTuple &tuple, const std::string &url, int retry_count,
const mediakit::ProtocolOption &option, int rtp_type, float timeout_sec, const toolkit::mINI &args,
const std::function<void(const toolkit::SockException &ex, const std::string &key)> &cb);
#endif //ZLMEDIAKIT_WEBAPI_H
/*
* Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
*
* Use of this source code is governed by MIT-like license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef ZLMEDIAKIT_WEBAPI_H
#define ZLMEDIAKIT_WEBAPI_H
#include <string>
#include <functional>
#include "json/json.h"
#include "Common/Parser.h"
#include "Network/Socket.h"
#include "Http/HttpSession.h"
#include "Common/MultiMediaSourceMuxer.h"
#if defined(ENABLE_WEBRTC)
#include "webrtc/WebRtcTransport.h"
#endif
// 配置文件路径 [AUTO-TRANSLATED:8a373c2f]
// Configuration file path
extern std::string g_ini_file;
namespace mediakit {
// //////////RTSP服务器配置/////////// [AUTO-TRANSLATED:950e1981]
// //////////RTSP server configuration///////////
namespace Rtsp {
extern const std::string kPort;
} //namespace Rtsp
// //////////RTMP服务器配置/////////// [AUTO-TRANSLATED:8de6f41f]
// //////////RTMP server configuration///////////
namespace Rtmp {
extern const std::string kPort;
} //namespace RTMP
} // namespace mediakit
namespace API {
typedef enum {
NotFound = -500,//未找到
Exception = -400,//代码抛异常
InvalidArgs = -300,//参数不合法
SqlFailed = -200,//sql执行失败
AuthFailed = -100,//鉴权失败
OtherFailed = -1,//业务代码执行失败,
Success = 0//执行成功
} ApiErr;
extern const std::string kSecret;
}//namespace API
class ApiRetException: public std::runtime_error {
public:
ApiRetException(const char *str = "success" ,int code = API::Success):runtime_error(str){
_code = code;
}
int code(){ return _code; }
private:
int _code;
};
class AuthException : public ApiRetException {
public:
AuthException(const char *str):ApiRetException(str,API::AuthFailed){}
};
class InvalidArgsException: public ApiRetException {
public:
InvalidArgsException(const char *str):ApiRetException(str,API::InvalidArgs){}
};
class SuccessException: public ApiRetException {
public:
SuccessException():ApiRetException("success",API::Success){}
};
using ApiArgsType = std::map<std::string, std::string, mediakit::StrCaseCompare>;
template<typename Args, typename Key>
std::string getValue(Args &args, const Key &key) {
auto it = args.find(key);
if (it == args.end()) {
return "";
}
return it->second;
}
template<typename Key>
std::string getValue(Json::Value &args, const Key &key) {
auto value = args.find(key);
if (value == nullptr) {
return "";
}
return value->asString();
}
template<typename Key>
std::string getValue(std::string &args, const Key &key) {
return "";
}
template <typename Key>
std::string getValue(const mediakit::Parser &parser, const Key &key) {
auto ret = getValue(parser.getUrlArgs(), key);
if (!ret.empty()) {
return ret;
}
return getValue(parser.getHeader(), key);
}
template<typename Key>
std::string getValue(mediakit::Parser &parser, const Key &key) {
return getValue((const mediakit::Parser &) parser, key);
}
template<typename Args, typename Key>
std::string getValue(const mediakit::Parser &parser, Args &args, const Key &key) {
auto ret = getValue(args, key);
if (!ret.empty()) {
return ret;
}
return getValue(parser, key);
}
template<typename Args>
class HttpAllArgs {
mediakit::Parser* _parser = nullptr;
Args* _args = nullptr;
public:
const mediakit::Parser& parser;
Args& args;
HttpAllArgs(const mediakit::Parser &p, Args &a): parser(p), args(a) {}
HttpAllArgs(const HttpAllArgs &that): _parser(new mediakit::Parser(that.parser)),
_args(new Args(that.args)),
parser(*_parser), args(*_args) {}
~HttpAllArgs() {
if (_parser) {
delete _parser;
}
if (_args) {
delete _args;
}
}
template<typename Key>
toolkit::variant operator[](const Key &key) const {
return (toolkit::variant)getValue(parser, args, key);
}
const Args& getArgs() const {
return args;
}
const mediakit::Parser &getParser() const {
return parser;
}
};
using ArgsMap = HttpAllArgs<ApiArgsType>;
using ArgsJson = HttpAllArgs<Json::Value>;
using ArgsString = HttpAllArgs<std::string>;
#define API_ARGS_MAP toolkit::SockInfo &sender, mediakit::HttpSession::KeyValue &headerOut, const ArgsMap &allArgs, Json::Value &val
#define API_ARGS_MAP_ASYNC API_ARGS_MAP, const mediakit::HttpSession::HttpResponseInvoker &invoker
#define API_ARGS_JSON toolkit::SockInfo &sender, mediakit::HttpSession::KeyValue &headerOut, const ArgsJson &allArgs, Json::Value &val
#define API_ARGS_JSON_ASYNC API_ARGS_JSON, const mediakit::HttpSession::HttpResponseInvoker &invoker
#define API_ARGS_STRING toolkit::SockInfo &sender, mediakit::HttpSession::KeyValue &headerOut, const ArgsString &allArgs, Json::Value &val
#define API_ARGS_STRING_ASYNC API_ARGS_STRING, const mediakit::HttpSession::HttpResponseInvoker &invoker
#define API_ARGS_VALUE sender, headerOut, allArgs, val
// 注册http请求参数是map<string, variant, StrCaseCompare>类型的http api [AUTO-TRANSLATED:8a273897]
// Register http request parameters as map<string, variant, StrCaseCompare> type http api
void api_regist(const std::string &api_path, const std::function<void(API_ARGS_MAP)> &func);
// 注册http请求参数是map<string, variant, StrCaseCompare>类型,但是可以异步回复的的http api [AUTO-TRANSLATED:9da5d5f5]
// Register http request parameters as map<string, variant, StrCaseCompare> type, but can be replied asynchronously http api
void api_regist(const std::string &api_path, const std::function<void(API_ARGS_MAP_ASYNC)> &func);
// 注册http请求参数是Json::Value类型的http api(可以支持多级嵌套的json参数对象) [AUTO-TRANSLATED:c4794456]
// Register http request parameters as Json::Value type http api (can support multi-level nested json parameter objects)
void api_regist(const std::string &api_path, const std::function<void(API_ARGS_JSON)> &func);
// 注册http请求参数是Json::Value类型但是可以异步回复的的http api [AUTO-TRANSLATED:742e57fd]
// Register http request parameters as Json::Value type, but can be replied asynchronously http api
void api_regist(const std::string &api_path, const std::function<void(API_ARGS_JSON_ASYNC)> &func);
// 注册http请求参数是http原始请求信息的http api [AUTO-TRANSLATED:72d3fe93]
// Register http request parameters as http original request information http api
void api_regist(const std::string &api_path, const std::function<void(API_ARGS_STRING)> &func);
// 注册http请求参数是http原始请求信息的异步回复的http api [AUTO-TRANSLATED:49feefa8]
// Register http request parameters as http original request information asynchronous reply http api
void api_regist(const std::string &api_path, const std::function<void(API_ARGS_STRING_ASYNC)> &func);
template<typename Args, typename Key>
bool checkArgs(Args &args, const Key &key) {
return !args[key].empty();
}
template<typename Args, typename Key, typename ...KeyTypes>
bool checkArgs(Args &args, const Key &key, const KeyTypes &...keys) {
return checkArgs(args, key) && checkArgs(args, keys...);
}
// 检查http url中或body中或http header参数是否为空的宏 [AUTO-TRANSLATED:9de001a4]
// Check whether the http url, body or http header parameters are empty
#define CHECK_ARGS(...) \
if(!checkArgs(allArgs,##__VA_ARGS__)){ \
throw InvalidArgsException("Required parameter missed: " #__VA_ARGS__); \
}
// 检查http参数中是否附带secret密钥的宏127.0.0.1的ip不检查密钥 [AUTO-TRANSLATED:7546956c]
// Check whether the http parameters contain the secret key, the ip of 127.0.0.1 does not check the key
// 同时检测是否在ip白名单内 [AUTO-TRANSLATED:d12f963d]
// Check whether it is in the ip whitelist at the same time
#define CHECK_SECRET() \
do { \
auto ip = sender.get_peer_ip(); \
if (!HttpFileManager::isIPAllowed(ip)) { \
throw AuthException("Your ip is not allowed to access the service."); \
} \
CHECK_ARGS("secret"); \
if (api_secret != allArgs["secret"]) { \
throw AuthException("Incorrect secret"); \
} \
} while(false);
void installWebApi();
void unInstallWebApi();
#if defined(ENABLE_RTPPROXY)
uint16_t openRtpServer(uint16_t local_port, const mediakit::MediaTuple &tuple, int tcp_mode, const std::string &local_ip, bool re_use_port, uint32_t ssrc, int only_track, bool multiplex=false);
#endif
Json::Value makeMediaSourceJson(mediakit::MediaSource &media);
void getStatisticJson(const std::function<void(Json::Value &val)> &cb);
void addStreamProxy(const mediakit::MediaTuple &tuple, const std::string &url, int retry_count,
const mediakit::ProtocolOption &option, int rtp_type, float timeout_sec, const toolkit::mINI &args,
const std::function<void(const toolkit::SockException &ex, const std::string &key)> &cb);
template <typename Type>
class ServiceController {
public:
using Pointer = std::shared_ptr<Type>;
std::unordered_map<std::string, Pointer> _map;
mutable std::recursive_mutex _mtx;
void clear() {
decltype(_map) copy;
{
std::lock_guard<std::recursive_mutex> lck(_mtx);
copy.swap(_map);
}
}
size_t erase(const std::string &key) {
Pointer erase_ptr;
{
std::lock_guard<std::recursive_mutex> lck(_mtx);
auto itr = _map.find(key);
if (itr != _map.end()) {
erase_ptr = std::move(itr->second);
_map.erase(itr);
return 1;
}
}
return 0;
}
size_t size() {
std::lock_guard<std::recursive_mutex> lck(_mtx);
return _map.size();
}
Pointer find(const std::string &key) const {
std::lock_guard<std::recursive_mutex> lck(_mtx);
auto it = _map.find(key);
if (it == _map.end()) {
return nullptr;
}
return it->second;
}
void for_each(const std::function<void(const std::string&, const Pointer&)>& cb) {
std::lock_guard<std::recursive_mutex> lck(_mtx);
auto it = _map.begin();
while (it != _map.end()) {
cb(it->first, it->second);
it++;
}
}
template<class ..._Args>
Pointer make(const std::string &key, _Args&& ...__args) {
// assert(!find(key));
auto server = std::make_shared<Type>(std::forward<_Args>(__args)...);
std::lock_guard<std::recursive_mutex> lck(_mtx);
auto it = _map.emplace(key, server);
assert(it.second);
return server;
}
template<class ..._Args>
Pointer makeWithAction(const std::string &key, std::function<void(Pointer)> action, _Args&& ...__args) {
// assert(!find(key));
auto server = std::make_shared<Type>(std::forward<_Args>(__args)...);
action(server);
std::lock_guard<std::recursive_mutex> lck(_mtx);
auto it = _map.emplace(key, server);
assert(it.second);
return server;
}
template<class ..._Args>
Pointer emplace(const std::string &key, _Args&& ...__args) {
// assert(!find(key));
auto server = std::static_pointer_cast<Type>(std::forward<_Args>(__args)...);
std::lock_guard<std::recursive_mutex> lck(_mtx);
auto it = _map.emplace(key, server);
assert(it.second);
return server;
}
};
#if defined(ENABLE_WEBRTC)
template <typename Args>
class WebRtcArgsImp : public mediakit::WebRtcArgs {
public:
WebRtcArgsImp(const HttpAllArgs<Args> &args, std::string session_id)
: _args(args)
, _session_id(std::move(session_id)) {}
~WebRtcArgsImp() override = default;
toolkit::variant operator[](const std::string &key) const override {
if (key == "url") {
return getUrl();
}
return _args[key];
}
private:
std::string getUrl() const {
auto &allArgs = _args;
CHECK_ARGS("app", "stream");
return StrPrinter << RTC_SCHEMA << "://" << (_args["Host"].empty() ? DEFAULT_VHOST : _args["Host"].data()) << "/" << _args["app"] << "/"
<< _args["stream"] << "?" << _args.getParser().params() + "&session=" + _session_id;
}
private:
HttpAllArgs<Args> _args;
std::string _session_id;
};
#endif
#endif //ZLMEDIAKIT_WEBAPI_H

View File

@@ -30,6 +30,8 @@
#if defined(ENABLE_WEBRTC)
#include "../webrtc/WebRtcTransport.h"
#include "../webrtc/WebRtcSession.h"
#include "../webrtc/WebRtcSignalingSession.h"
#include "../webrtc/IceSession.hpp"
#endif
#if defined(ENABLE_SRT)
@@ -368,8 +370,17 @@ int start_main(int argc,char *argv[]) {
}
return Socket::createSocket(new_poller, false);
});
auto signaleSrv = std::make_shared<TcpServer>();
auto signalsSrv = std::make_shared<TcpServer>();
auto iceTcpSrv = std::make_shared<TcpServer>();
auto iceSrv = std::make_shared<UdpServer>();
uint16_t rtcPort = mINI::Instance()[Rtc::kPort];
uint16_t rtcTcpPort = mINI::Instance()[Rtc::kTcpPort];
uint16_t signalingPort = mINI::Instance()[Rtc::kSignalingPort];
uint16_t signalSslPort = mINI::Instance()[Rtc::kSignalingSslPort];
uint16_t icePort = mINI::Instance()[Rtc::kIcePort];
uint16_t iceTcpPort = mINI::Instance()[Rtc::kIceTcpPort];
#endif//defined(ENABLE_WEBRTC)
@@ -435,6 +446,12 @@ int start_main(int argc,char *argv[]) {
if (rtcTcpPort) { rtcSrv_tcp->start<WebRtcSession>(rtcTcpPort, listen_ip);}
//webrtc 信令服务器
if (signalingPort) { signaleSrv->start<WebRtcWebcosktSignalingSession>(signalingPort);}
if (signalSslPort) { signalsSrv->start<WebRtcWebcosktSignalSslSession>(signalSslPort);}
//STUN/TURN服务
if (icePort) { iceSrv->start<IceSession>(icePort);}
if (iceTcpPort) { iceTcpSrv->start<IceSession>(iceTcpPort);}
#endif//defined(ENABLE_WEBRTC)
#if defined(ENABLE_SRT)