From ebe414102695cf162ca4fd37518e53dc7cbb127d Mon Sep 17 00:00:00 2001 From: Dw9 Date: Sat, 16 Apr 2022 15:57:02 +0800 Subject: [PATCH] =?UTF-8?q?c=20api=E5=92=8C=E6=B5=8B=E8=AF=95=E8=8C=83?= =?UTF-8?q?=E4=BE=8B=E6=96=B0=E5=A2=9Ewebrtc=E5=8A=9F=E8=83=BD=20(#1574)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update Rtsp.cpp * c sdk识别到enable_webrtc * mk_api增加json库 * 对rtc http请求的处理 * c sdk 增加rtc服务器 --- CMakeLists.txt | 9 +- api/CMakeLists.txt | 4 +- api/include/mk_common.h | 8 ++ api/include/mk_events_objects.h | 8 ++ api/include/mk_webrtc_api.h | 225 +++++++++++++++++++++++++++++++ api/source/mk_common.cpp | 35 +++++ api/source/mk_events_objects.cpp | 53 ++++++++ api/tests/server.c | 43 +++--- 8 files changed, 361 insertions(+), 24 deletions(-) create mode 100644 api/include/mk_webrtc_api.h diff --git a/CMakeLists.txt b/CMakeLists.txt index edd7eccf..15dc6aca 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -336,10 +336,6 @@ endif () execute_process(COMMAND cp -r ${CMAKE_CURRENT_SOURCE_DIR}/www ${EXECUTABLE_OUTPUT_PATH}/) execute_process(COMMAND cp ${CMAKE_CURRENT_SOURCE_DIR}/conf/config.ini ${EXECUTABLE_OUTPUT_PATH}/) -#添加c库 -if (ENABLE_API) - add_subdirectory(api) -endif () if (ENABLE_WEBRTC) #查找srtp是否安装 @@ -361,6 +357,11 @@ if (ENABLE_WEBRTC) endif () endif () +#添加c库 +if (ENABLE_API) + add_subdirectory(api) +endif () + #ios不编译可执行程序 if (IOS) return() diff --git a/api/CMakeLists.txt b/api/CMakeLists.txt index c9c991c8..334a29c8 100644 --- a/api/CMakeLists.txt +++ b/api/CMakeLists.txt @@ -4,7 +4,7 @@ file(GLOB api_src_list include/*.h source/*.cpp source/*.h source/*.c) if (IOS) add_library(mk_api STATIC ${api_src_list}) - target_link_libraries(mk_api ${LINK_LIB_LIST}) + target_link_libraries(mk_api ${LINK_LIB_LIST} jsoncpp) else () if(ENABLE_API_STATIC_LIB) add_library(mk_api STATIC ${api_src_list}) @@ -17,7 +17,7 @@ else () add_definitions(-DMediaKitApi_EXPORTS) endif () endif() - target_link_libraries(mk_api ${LINK_LIB_LIST}) + target_link_libraries(mk_api ${LINK_LIB_LIST} jsoncpp) add_subdirectory(tests) file(GLOB api_header_list include/*.h) diff --git a/api/include/mk_common.h b/api/include/mk_common.h index e7b09445..f9193033 100755 --- a/api/include/mk_common.h +++ b/api/include/mk_common.h @@ -158,6 +158,14 @@ API_EXPORT uint16_t API_CALL mk_rtmp_server_start(uint16_t port, int ssl); */ API_EXPORT uint16_t API_CALL mk_rtp_server_start(uint16_t port); +/** + * 创建rtc服务器 + * @param port rtp监听端口 + * @return 0:失败,非0:端口号 + */ +API_EXPORT uint16_t API_CALL mk_rtc_server_start(uint16_t port); + + /** * 创建shell服务器 * @param port shell监听端口 diff --git a/api/include/mk_events_objects.h b/api/include/mk_events_objects.h index 487d5e0c..ca8c289c 100644 --- a/api/include/mk_events_objects.h +++ b/api/include/mk_events_objects.h @@ -176,6 +176,14 @@ API_EXPORT void API_CALL mk_http_response_invoker_do(const mk_http_response_invo int response_code, const char **response_header, const mk_http_body response_body); +/** + * HttpSession::HttpResponseInvoker(const string &codeOut, const StrCaseMap &headerOut, const HttpBody::Ptr &body); + Parser(); + SockInfo(); + */ +API_EXPORT void API_CALL mk_webrtc_http_response_invoker_do( const mk_http_response_invoker invoker, + const mk_parser parser, + const mk_sock_info sender); /** * HttpSession::HttpResponseInvoker(const string &codeOut, const StrCaseMap &headerOut, const string &body); diff --git a/api/include/mk_webrtc_api.h b/api/include/mk_webrtc_api.h new file mode 100644 index 00000000..0151c74b --- /dev/null +++ b/api/include/mk_webrtc_api.h @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/xia-chu/ZLMediaKit). + * + * Use of this source code is governed by MIT 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. + */ +#ifdef ENABLE_WEBRTC +#ifndef MK_WEBRTC_API_H +#define MK_WEBRTC_API_H + +#include "Common/Parser.h" +#include "Http/HttpSession.h" +#include "Network/Socket.h" +#include "jsoncpp/json.h" +#include +#include + +#include "../webrtc/WebRtcEchoTest.h" +#include "../webrtc/WebRtcPlayer.h" +#include "../webrtc/WebRtcPusher.h" +#include "../webrtc/WebRtcTransport.h" + +namespace API { +typedef enum { + NotFound = -500, //未找到 + Exception = -400, //代码抛异常 + InvalidArgs = -300, //参数不合法 + SqlFailed = -200, // sql执行失败 + AuthFailed = -100, //鉴权失败 + OtherFailed = -1, //业务代码执行失败, + Success = 0 //执行成功 +} ApiErr; +} // namespace API + +class ApiRetException : public std::runtime_error { +public: + ApiRetException(const char *str = "success", int code = API::Success) + : runtime_error(str) { + _code = code; + } + ~ApiRetException() = default; + int code() { return _code; } + +private: + int _code; +}; + +class AuthException : public ApiRetException { +public: + AuthException(const char *str) + : ApiRetException(str, API::AuthFailed) {} + ~AuthException() = default; +}; + +class InvalidArgsException : public ApiRetException { +public: + InvalidArgsException(const char *str) + : ApiRetException(str, API::InvalidArgs) {} + ~InvalidArgsException() = default; +}; + +class SuccessException : public ApiRetException { +public: + SuccessException() + : ApiRetException("success", API::Success) {} + ~SuccessException() = default; +}; + +using ApiArgsType = std::map; +template +std::string getValue(Args &args, const First &first) { + return args[first]; +} + +template +std::string getValue(Json::Value &args, const First &first) { + return args[first].asString(); +} + +template +std::string getValue(std::string &args, const First &first) { + return ""; +} + +template +std::string getValue(const mediakit::Parser &parser, const First &first) { + auto ret = parser.getUrlArgs()[first]; + if (!ret.empty()) { + return ret; + } + return parser.getHeader()[first]; +} + +template +std::string getValue(mediakit::Parser &parser, const First &first) { + return getValue((const mediakit::Parser &)parser, first); +} + +template +std::string getValue(const mediakit::Parser &parser, Args &args, const First &first) { + auto ret = getValue(args, first); + if (!ret.empty()) { + return ret; + } + return getValue(parser, first); +} + +template +class HttpAllArgs { +public: + HttpAllArgs(const mediakit::Parser &parser, Args &args) { + _get_args = [&args]() { return (void *)&args; }; + _get_parser = [&parser]() -> const mediakit::Parser & { return parser; }; + _get_value + = [](HttpAllArgs &that, const std::string &key) { return getValue(that.getParser(), that.getArgs(), key); }; + _clone = [&](HttpAllArgs &that) { + that._get_args = [args]() { return (void *)&args; }; + that._get_parser = [parser]() -> const mediakit::Parser & { return parser; }; + that._get_value = [](HttpAllArgs &that, const std::string &key) { + return getValue(that.getParser(), that.getArgs(), key); + }; + that._cache_able = true; + }; + } + + HttpAllArgs(const HttpAllArgs &that) { + if (that._cache_able) { + _get_args = that._get_args; + _get_parser = that._get_parser; + _get_value = that._get_value; + _cache_able = true; + } else { + that._clone(*this); + } + } + + ~HttpAllArgs() = default; + + template + toolkit::variant operator[](const Key &key) const { + return (toolkit::variant)_get_value(*(HttpAllArgs *)this, key); + } + + const mediakit::Parser &getParser() const { return _get_parser(); } + + Args &getArgs() { return *((Args *)_get_args()); } + + const Args &getArgs() const { return *((Args *)_get_args()); } + +private: + bool _cache_able = false; + std::function _get_args; + std::function _get_parser; + std::function _get_value; + std::function _clone; +}; + +#define API_ARGS_MAP \ + toolkit::SockInfo &sender, mediakit::HttpSession::KeyValue &headerOut, const HttpAllArgs &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 HttpAllArgs &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 HttpAllArgs &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请求参数是http原始请求信息的异步回复的http api +void api_regist(const std::string &api_path, const std::function &func); + +template +bool checkArgs(Args &args, const First &first) { + return !args[first].empty(); +} + +template +bool checkArgs(Args &args, const First &first, const KeyTypes &...keys) { + return checkArgs(args, first) && checkArgs(args, keys...); +} + +//检查http url中或body中或http header参数是否为空的宏 +#define CHECK_ARGS(...) \ + if (!checkArgs(allArgs, ##__VA_ARGS__)) { \ + throw InvalidArgsException("缺少必要参数:" #__VA_ARGS__); \ + } + +#ifdef ENABLE_WEBRTC +class WebRtcArgsImp : public WebRtcArgs { +public: + WebRtcArgsImp(const HttpAllArgs &args, std::string session_id) + : _args(args) + , _session_id(std::move(session_id)) {} + ~WebRtcArgsImp() override = default; + + 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"] << "/" << _args["app"] << "/" << _args["stream"] + << "?" << _args.getParser().Params() + "&session=" + _session_id; + } + +private: + HttpAllArgs _args; + std::string _session_id; +}; +#endif + +#endif // MK_WEBRTC_API_H +#endif // ENABLE_WEBRTC \ No newline at end of file diff --git a/api/source/mk_common.cpp b/api/source/mk_common.cpp index 33b5287d..6c157297 100644 --- a/api/source/mk_common.cpp +++ b/api/source/mk_common.cpp @@ -34,6 +34,11 @@ static TcpServer::Ptr shell_server; static std::shared_ptr rtpServer; #endif +#ifdef ENABLE_WEBRTC +#include "../webrtc/WebRtcSession.h" +static std::shared_ptr rtcServer; +#endif + //////////////////////////environment init/////////////////////////// API_EXPORT void API_CALL mk_env_init(const mk_config *cfg) { @@ -218,6 +223,36 @@ API_EXPORT uint16_t API_CALL mk_rtp_server_start(uint16_t port){ #endif } +API_EXPORT uint16_t API_CALL mk_rtc_server_start(uint16_t port) { +#ifdef ENABLE_WEBRTC + try { + //创建rtc服务器 + rtcServer = std::make_shared(); + rtcServer->setOnCreateSocket([](const EventPoller::Ptr &poller, const Buffer::Ptr &buf, struct sockaddr *, int) { + if (!buf) { + return Socket::createSocket(poller, false); + } + auto new_poller = WebRtcSession::queryPoller(buf); + if (!new_poller) { + //该数据对应的webrtc对象未找到,丢弃之 + return Socket::Ptr(); + } + return Socket::createSocket(new_poller, false); + }); + rtcServer->start(port); + return rtcServer->getPort(); + + } catch (std::exception &ex) { + rtpServer.reset(); + WarnL << ex.what(); + return 0; + } +#else + WarnL << "未启用该功能!"; + return 0; +#endif +} + API_EXPORT uint16_t API_CALL mk_shell_server_start(uint16_t port){ try { shell_server = std::make_shared(); diff --git a/api/source/mk_events_objects.cpp b/api/source/mk_events_objects.cpp index dd15424f..9b4b2971 100644 --- a/api/source/mk_events_objects.cpp +++ b/api/source/mk_events_objects.cpp @@ -14,9 +14,15 @@ #include "Record/MP4Recorder.h" #include "Http/HttpSession.h" #include "Http/HttpBody.h" + #include "Http/HttpClient.h" #include "Rtsp/RtspSession.h" +#ifdef ENABLE_WEBRTC +#include "jsoncpp/json.h" +#include "mk_webrtc_api.h" +#endif + using namespace toolkit; using namespace mediakit; @@ -323,6 +329,53 @@ API_EXPORT void API_CALL mk_http_response_invoker_do(const mk_http_response_invo (*invoker)(response_code,header,*body); } +API_EXPORT void API_CALL mk_webrtc_http_response_invoker_do(const mk_http_response_invoker ctx_invoker, + const mk_parser ctx_parser, + const mk_sock_info ctx_sock ) { + assert(ctx_parser && ctx_invoker && ctx_sock); +#ifdef ENABLE_WEBRTC + static auto webrtc_cb = [](API_ARGS_STRING_ASYNC){ + CHECK_ARGS("type"); + auto type = allArgs["type"]; + auto offer = allArgs.getArgs(); + CHECK(!offer.empty(), "http body(webrtc offer sdp) is empty"); + + WebRtcPluginManager::Instance().getAnswerSdp( + *(static_cast(&sender)), type, offer, WebRtcArgsImp(allArgs, sender.getIdentifier()), + [invoker, val, offer, headerOut](const WebRtcInterface &exchanger) mutable { + //设置返回类型 + headerOut["Content-Type"] = HttpFileManager::getContentType(".json"); + //设置跨域 + headerOut["Access-Control-Allow-Origin"] = "*"; + + try { + val["sdp"] = const_cast(exchanger).getAnswerSdp(offer); + val["id"] = exchanger.getIdentifier(); + val["type"] = "answer"; + invoker(200, headerOut, val.toStyledString()); + } catch (std::exception &ex) { + val["code"] = API::Exception; + val["msg"] = ex.what(); + invoker(200, headerOut, val.toStyledString()); + } + }); + }; + + Parser *parser = (Parser *)ctx_parser; + HttpSession::HttpResponseInvoker *invoker = (HttpSession::HttpResponseInvoker *)ctx_invoker; + SockInfo* sender = (SockInfo*)ctx_sock; + + GET_CONFIG(std::string, charSet, Http::kCharSet); + HttpSession::KeyValue headerOut; + headerOut["Content-Type"] = std::string("application/json; charset=") + charSet; + + Json::Value val; + val["code"] = API::Success; + + webrtc_cb(*sender, headerOut, HttpAllArgs(*parser, (std::string &)parser->Content()), val, *invoker); +#endif +}; + API_EXPORT mk_http_response_invoker API_CALL mk_http_response_invoker_clone(const mk_http_response_invoker ctx){ assert(ctx); HttpSession::HttpResponseInvoker *invoker = (HttpSession::HttpResponseInvoker *)ctx; diff --git a/api/tests/server.c b/api/tests/server.c index c5f40104..922e19ee 100644 --- a/api/tests/server.c +++ b/api/tests/server.c @@ -160,27 +160,33 @@ void API_CALL on_mk_http_request(const mk_parser parser, mk_parser_get_content(parser,NULL)); const char *url = mk_parser_get_url(parser); - if(strcmp(url,"/api/test") != 0){ + *consumed = 1; + + //拦截api: /api/test + if(strcmp(url,"/api/test") == 0) { + const char *response_header[] = { "Content-Type", "text/html", NULL }; + const char *content = "" + "" + "hello world" + "" + "" + "

hello world


" + "
" + "ZLMediaKit-4.0
" + "" + ""; + mk_http_body body = mk_http_body_from_string(content, 0); + mk_http_response_invoker_do(invoker, 200, response_header, body); + mk_http_body_release(body); + } + //拦截api: /index/api/webrtc + else if(strcmp(url,"/index/api/webrtc") == 0){ + mk_webrtc_http_response_invoker_do(invoker,parser,sender); + } + else{ *consumed = 0; return; } - - //只拦截api: /api/test - *consumed = 1; - const char *response_header[] = {"Content-Type","text/html",NULL}; - const char *content = - "" - "" - "hello world" - "" - "" - "

hello world


" - "
""ZLMediaKit-4.0
" - "" - ""; - mk_http_body body = mk_http_body_from_string(content,0); - mk_http_response_invoker_do(invoker, 200, response_header, body); - mk_http_body_release(body); } /** @@ -420,6 +426,7 @@ int main(int argc, char *argv[]) { mk_rtmp_server_start(1935, 0); mk_shell_server_start(9000); mk_rtp_server_start(10000); + mk_rtc_server_start(8000); mk_events events = { .on_mk_media_changed = on_mk_media_changed,