diff --git a/3rdpart/ZLToolKit b/3rdpart/ZLToolKit
index 04d1c47d..26d54bbc 160000
--- a/3rdpart/ZLToolKit
+++ b/3rdpart/ZLToolKit
@@ -1 +1 @@
-Subproject commit 04d1c47d2568f5ce1ff84260cefaf2754e514a5e
+Subproject commit 26d54bbc7b1860a450434dce49bbc8fcbcbae88b
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 72a4544e..cd0124a9 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -57,7 +57,8 @@ option(ENABLE_SCTP "Enable SCTP" ON)
option(ENABLE_WEBRTC "Enable WebRTC" ON)
option(ENABLE_X264 "Enable x264" OFF)
option(ENABLE_WEPOLL "Enable wepoll" ON)
-option(DISABLE_REPORT "Disable report to report.zlmediakit.com" off)
+option(ENABLE_VIDEOSTACK "Enable video stack" OFF)
+option(DISABLE_REPORT "Disable report to report.zlmediakit.com" OFF)
option(USE_SOLUTION_FOLDERS "Enable solution dir supported" ON)
##############################################################################
# 设置socket默认缓冲区大小为256k.如果设置为0则不设置socket的默认缓冲区大小,使用系统内核默认值(设置为0仅对linux有效)
@@ -203,7 +204,7 @@ elseif(WIN32)
if (MSVC)
set(COMPILE_OPTIONS_DEFAULT
# TODO: /wd4819 应该是不会生效
- "/wd4566;/wd4819"
+ "/wd4566;/wd4819;/utf-8"
# warning C4530: C++ exception handler used, but unwind semantics are not enabled.
"/EHsc")
# disable Windows logo
@@ -535,6 +536,6 @@ file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/default.pem" DESTINATION ${EXECUTABLE_OUT
# 拷贝VideoStack 无视频流时默认填充的背景图片
# Copy the default background image used by VideoStack when there is no video stream
-if (ENABLE_FFMPEG AND ENABLE_X264)
+if (ENABLE_VIDEOSTACK AND ENABLE_FFMPEG AND ENABLE_X264)
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/conf/novideo.yuv" DESTINATION ${EXECUTABLE_OUTPUT_PATH})
endif ()
diff --git a/README.md b/README.md
index 44c22aa9..59fd30a4 100644
--- a/README.md
+++ b/README.md
@@ -222,7 +222,7 @@ bash build_docker_images.sh
- 请关注微信公众号获取最新消息推送:
- - 也可以自愿有偿加入知识星球咨询和获取资料:
+ - 也可以自愿有偿加入知识星球咨询、获取资料以及加入微信技术群:
diff --git a/api/source/mk_events_objects.cpp b/api/source/mk_events_objects.cpp
index 758e38a3..8fdb38af 100644
--- a/api/source/mk_events_objects.cpp
+++ b/api/source/mk_events_objects.cpp
@@ -528,7 +528,7 @@ API_EXPORT void API_CALL mk_auth_invoker_clone_release(const mk_auth_invoker ctx
}
///////////////////////////////////////////WebRtcTransport/////////////////////////////////////////////
-API_EXPORT void API_CALL mk_rtc_sendDatachannel(const mk_rtc_transport ctx, uint16_t streamId, uint32_t ppid, const char *msg, size_t len) {
+API_EXPORT void API_CALL mk_rtc_send_datachannel(const mk_rtc_transport ctx, uint16_t streamId, uint32_t ppid, const char *msg, size_t len) {
#ifdef ENABLE_WEBRTC
assert(ctx && msg);
WebRtcTransport *transport = (WebRtcTransport *)ctx;
diff --git a/conf/config.ini b/conf/config.ini
index 3c8b67c8..872d2d32 100644
--- a/conf/config.ini
+++ b/conf/config.ini
@@ -219,7 +219,7 @@ timeout_sec=15
retry_count=3
[http]
-#http服务器字符编码,windows上默认gb2312
+#http服务器字符编码集
charSet=utf-8
#http链接超时时间
keepAliveSecond=30
diff --git a/ext-codec/G711Rtp.cpp b/ext-codec/G711Rtp.cpp
index 16a9c5c2..766d600f 100644
--- a/ext-codec/G711Rtp.cpp
+++ b/ext-codec/G711Rtp.cpp
@@ -46,7 +46,7 @@ bool G711RtpEncoder::inputFrame(const Frame::Ptr &frame) {
const size_t rtp_size = max_size;
n++;
stamp += _pkt_dur_ms;
- RtpCodec::inputRtp(getRtpInfo().makeRtp(TrackAudio, ptr, rtp_size, mark, stamp), true);
+ RtpCodec::inputRtp(getRtpInfo().makeRtp(TrackAudio, ptr, rtp_size, mark, stamp), false);
ptr += rtp_size;
remain_size -= rtp_size;
}
diff --git a/postman/ZLMediaKit.postman_collection.json b/postman/ZLMediaKit.postman_collection.json
index ae3879b0..c3ddd249 100644
--- a/postman/ZLMediaKit.postman_collection.json
+++ b/postman/ZLMediaKit.postman_collection.json
@@ -2044,6 +2044,47 @@
},
"response": []
},
+ {
+ "name": "获取rtp发送列表(listRtpSender)",
+ "request": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "{{ZLMediaKit_URL}}/index/api/listRtpSender?secret={{ZLMediaKit_secret}}&vhost={{defaultVhost}}&app=live&stream=test",
+ "host": [
+ "{{ZLMediaKit_URL}}"
+ ],
+ "path": [
+ "index",
+ "api",
+ "listRtpSender"
+ ],
+ "query": [
+ {
+ "key": "secret",
+ "value": "{{ZLMediaKit_secret}}",
+ "description": "api操作密钥(配置文件配置)"
+ },
+ {
+ "key": "vhost",
+ "value": "{{defaultVhost}}",
+ "description": "虚拟主机,例如__defaultVhost__"
+ },
+ {
+ "key": "app",
+ "value": "live",
+ "description": "应用名,例如 live"
+ },
+ {
+ "key": "stream",
+ "value": "test",
+ "description": "流id,例如 obs"
+ }
+ ]
+ }
+ },
+ "response": []
+ },
{
"name": "获取版本信息(version)",
"request": {
diff --git a/server/VideoStack.cpp b/server/VideoStack.cpp
index d5168147..21cb7ff6 100644
--- a/server/VideoStack.cpp
+++ b/server/VideoStack.cpp
@@ -1,4 +1,4 @@
-#if defined(ENABLE_X264) && defined(ENABLE_FFMPEG)
+#if defined(ENABLE_VIDEOSTACK) && defined(ENABLE_X264) && defined(ENABLE_FFMPEG)
#include "VideoStack.h"
#include "Codec/Transcode.h"
#include "Common/Device.h"
diff --git a/server/VideoStack.h b/server/VideoStack.h
index 99455b40..cc0bfaa8 100644
--- a/server/VideoStack.h
+++ b/server/VideoStack.h
@@ -1,5 +1,5 @@
#pragma once
-#if defined(ENABLE_X264) && defined(ENABLE_FFMPEG)
+#if defined(ENABLE_VIDEOSTACK) && defined(ENABLE_X264) && defined(ENABLE_FFMPEG)
#include "Codec/Transcode.h"
#include "Common/Device.h"
#include "Player/MediaPlayer.h"
diff --git a/server/WebApi.cpp b/server/WebApi.cpp
index a1ef1f65..8ea758f3 100755
--- a/server/WebApi.cpp
+++ b/server/WebApi.cpp
@@ -62,7 +62,7 @@
#include "ZLMVersion.h"
#endif
-#if defined(ENABLE_X264) && defined (ENABLE_FFMPEG)
+#if defined(ENABLE_VIDEOSTACK) && defined(ENABLE_X264) && defined (ENABLE_FFMPEG)
#include "VideoStack.h"
#endif
@@ -208,7 +208,7 @@ static ApiArgsType getAllArgs(const Parser &parser) {
if (parser["Content-Type"].find("application/x-www-form-urlencoded") == 0) {
auto contentArgs = parser.parseArgs(parser.content());
for (auto &pr : contentArgs) {
- allArgs[pr.first] = HttpSession::urlDecodeComponent(pr.second);
+ allArgs[pr.first] = strCoding::UrlDecodeComponent(pr.second);
}
} else if (parser["Content-Type"].find("application/json") == 0) {
try {
@@ -1315,7 +1315,7 @@ void installWebApi() {
if (!src) {
throw ApiRetException("can not find the source stream", API::NotFound);
}
- auto type = allArgs["type"].as();
+ auto type = allArgs["type"].empty() ? (int)MediaSourceEvent::SendRtpArgs::kRtpPS : allArgs["type"].as();
if (!allArgs["use_ps"].empty()) {
// 兼容之前的use_ps参数
type = allArgs["use_ps"].as();
@@ -1347,6 +1347,26 @@ void installWebApi() {
});
});
+ api_regist("/index/api/listRtpSender",[](API_ARGS_MAP_ASYNC){
+ CHECK_SECRET();
+ CHECK_ARGS("vhost", "app", "stream");
+
+ auto src = MediaSource::find(allArgs["vhost"], allArgs["app"], allArgs["stream"]);
+ if (!src) {
+ throw ApiRetException("can not find the source stream", API::NotFound);
+ }
+
+ auto muxer = src->getMuxer();
+ CHECK(muxer, "get muxer from media source failed");
+
+ src->getOwnerPoller()->async([=]() mutable {
+ muxer->forEachRtpSender([&](const std::string &ssrc) mutable {
+ val["data"].append(ssrc);
+ });
+ invoker(200, headerOut, val.toStyledString());
+ });
+ });
+
api_regist("/index/api/startSendRtpPassive",[](API_ARGS_MAP_ASYNC){
CHECK_SECRET();
CHECK_ARGS("vhost", "app", "stream", "ssrc");
@@ -1355,7 +1375,7 @@ void installWebApi() {
if (!src) {
throw ApiRetException("can not find the source stream", API::NotFound);
}
- auto type = allArgs["type"].as();
+ auto type = allArgs["type"].empty() ? (int)MediaSourceEvent::SendRtpArgs::kRtpPS : allArgs["type"].as();
if (!allArgs["use_ps"].empty()) {
// 兼容之前的use_ps参数
type = allArgs["use_ps"].as();
@@ -1910,7 +1930,7 @@ void installWebApi() {
}
});
-#if defined(ENABLE_X264) && defined(ENABLE_FFMPEG)
+#if defined(ENABLE_VIDEOSTACK) && defined(ENABLE_X264) && defined(ENABLE_FFMPEG)
VideoStackManager::Instance().loadBgImg("novideo.yuv");
NoticeCenter::Instance().addListener(nullptr, Broadcast::kBroadcastStreamNoneReader, [](BroadcastStreamNoneReaderArgs) {
auto id = sender.getMediaTuple().stream;
diff --git a/src/Common/MultiMediaSourceMuxer.cpp b/src/Common/MultiMediaSourceMuxer.cpp
index fa46e4fa..fb00a668 100644
--- a/src/Common/MultiMediaSourceMuxer.cpp
+++ b/src/Common/MultiMediaSourceMuxer.cpp
@@ -173,6 +173,12 @@ std::string MultiMediaSourceMuxer::shortUrl() const {
return _tuple.shortUrl();
}
+void MultiMediaSourceMuxer::forEachRtpSender(const std::function &cb) const {
+ for (auto &pr : _rtp_sender) {
+ cb(pr.first);
+ }
+}
+
MultiMediaSourceMuxer::MultiMediaSourceMuxer(const MediaTuple& tuple, float dur_sec, const ProtocolOption &option): _tuple(tuple) {
if (!option.stream_replace.empty()) {
// 支持在on_publish hook中替换stream_id
diff --git a/src/Common/MultiMediaSourceMuxer.h b/src/Common/MultiMediaSourceMuxer.h
index 9ca34370..a9775c8e 100644
--- a/src/Common/MultiMediaSourceMuxer.h
+++ b/src/Common/MultiMediaSourceMuxer.h
@@ -133,6 +133,8 @@ public:
const MediaTuple &getMediaTuple() const;
std::string shortUrl() const;
+ void forEachRtpSender(const std::function &cb) const;
+
protected:
/////////////////////////////////MediaSink override/////////////////////////////////
diff --git a/src/Common/Parser.cpp b/src/Common/Parser.cpp
index 7e7de860..47f3c2d8 100644
--- a/src/Common/Parser.cpp
+++ b/src/Common/Parser.cpp
@@ -294,8 +294,8 @@ void RtspUrl::setup(bool is_ssl, const string &url, const string &user, const st
splitUrl(ip, ip, port);
_url = std::move(url);
- _user = strCoding::UrlDecodeComponent(user);
- _passwd = strCoding::UrlDecodeComponent(passwd);
+ _user = strCoding::UrlDecodeUserOrPass(user);
+ _passwd = strCoding::UrlDecodeUserOrPass(passwd);
_host = std::move(ip);
_port = port;
_is_ssl = is_ssl;
diff --git a/src/Common/config.cpp b/src/Common/config.cpp
index a34ee84f..5a538ab4 100644
--- a/src/Common/config.cpp
+++ b/src/Common/config.cpp
@@ -181,12 +181,7 @@ static onceToken token([]() {
mINI::Instance()[kKeepAliveSecond] = 15;
mINI::Instance()[kDirMenu] = true;
mINI::Instance()[kVirtualPath] = "";
-
-#if defined(_WIN32)
- mINI::Instance()[kCharSet] = "gb2312";
-#else
mINI::Instance()[kCharSet] = "utf-8";
-#endif
mINI::Instance()[kRootPath] = "./www";
mINI::Instance()[kNotFound] = StrPrinter << ""
diff --git a/src/Common/strCoding.cpp b/src/Common/strCoding.cpp
index 1a0f0236..49a345ef 100644
--- a/src/Common/strCoding.cpp
+++ b/src/Common/strCoding.cpp
@@ -52,9 +52,7 @@ char HexStrToBin(const char *str) {
}
return (high << 4) | low;
}
-
-string strCoding::UrlEncodePath(const string &str) {
- const char *dont_escape = "!#&'*+:=?@/._-$,;~()";
+static string UrlEncodeCommon(const string &str,const char* dont_escape){
string out;
size_t len = str.size();
for (size_t i = 0; i < len; ++i) {
@@ -69,26 +67,7 @@ string strCoding::UrlEncodePath(const string &str) {
}
return out;
}
-
-string strCoding::UrlEncodeComponent(const string &str) {
- const char *dont_escape = "!'()*-._~";
- string out;
- size_t len = str.size();
- for (size_t i = 0; i < len; ++i) {
- char ch = str[i];
- if (isalnum((uint8_t) ch) || strchr(dont_escape, (uint8_t) ch) != NULL) {
- out.push_back(ch);
- } else {
- char buf[4];
- snprintf(buf, 4, "%%%X%X", (uint8_t) ch >> 4, (uint8_t) ch & 0x0F);
- out.append(buf);
- }
- }
- return out;
-}
-
-string strCoding::UrlDecodePath(const string &str) {
- const char *dont_unescape = "#$&+,/:;=?@";
+static string UrlDecodeCommon(const string &str,const char* dont_unescape){
string output;
size_t i = 0, len = str.length();
while (i < len) {
@@ -114,6 +93,36 @@ string strCoding::UrlDecodePath(const string &str) {
return output;
}
+string strCoding::UrlEncodePath(const string &str) {
+ const char *dont_escape = "!#&'*+:=?@/._-$,;~()";
+ return UrlEncodeCommon(str,dont_escape);
+}
+
+string strCoding::UrlEncodeComponent(const string &str) {
+ const char *dont_escape = "!'()*-._~";
+ return UrlEncodeCommon(str,dont_escape);
+}
+
+std::string strCoding::UrlEncodeUserOrPass(const std::string &str) {
+ // from rfc https://datatracker.ietf.org/doc/html/rfc3986
+ // §2.3 Unreserved characters (mark)
+ //'-', '_', '.', '~'
+ // §2.2 Reserved characters (reserved)
+ // '$', '&', '+', ',', '/', ':', ';', '=', '?', '@',
+ // §3.2.1
+ // The RFC allows ';', ':', '&', '=', '+', '$', and ',' in
+ // userinfo, so we must escape only '@', '/', and '?'.
+ // The parsing of userinfo treats ':' as special so we must escape
+ // that too.
+ const char *dont_escape = "$&+,;=-._~";
+ return UrlEncodeCommon(str,dont_escape);
+}
+
+string strCoding::UrlDecodePath(const string &str) {
+ const char *dont_unescape = "#$&+,/:;=?@";
+ return UrlDecodeCommon(str,dont_unescape);
+}
+
std::string strCoding::UrlDecodeComponent(const std::string &str) {
string output;
size_t i = 0, len = str.length();
@@ -143,6 +152,11 @@ std::string strCoding::UrlDecodeComponent(const std::string &str) {
return output;
}
+
+std::string strCoding::UrlDecodeUserOrPass(const std::string &str) {
+ const char *dont_unescape = "";
+ return UrlDecodeCommon(str,dont_unescape);
+}
///////////////////////////////windows专用///////////////////////////////////
#if defined(_WIN32)
void UnicodeToGB2312(char* pOut, wchar_t uData)
diff --git a/src/Common/strCoding.h b/src/Common/strCoding.h
index e715e74d..bfddf7ff 100644
--- a/src/Common/strCoding.h
+++ b/src/Common/strCoding.h
@@ -22,6 +22,8 @@ public:
static std::string UrlEncodeComponent(const std::string &str); // url参数 utf8编码
static std::string UrlDecodePath(const std::string &str); //url路径 utf8解码
static std::string UrlDecodeComponent(const std::string &str); // url参数 utf8解码
+ static std::string UrlEncodeUserOrPass(const std::string &str); // url中用户名与密码编码
+ static std::string UrlDecodeUserOrPass(const std::string &str); // url中用户名与密码解码
#if defined(_WIN32)
static std::string UTF8ToGB2312(const std::string &str);//utf_8转为gb2312
static std::string GB2312ToUTF8(const std::string &str); //gb2312 转utf_8
diff --git a/src/Http/HttpClient.cpp b/src/Http/HttpClient.cpp
index b9927934..84450d2b 100644
--- a/src/Http/HttpClient.cpp
+++ b/src/Http/HttpClient.cpp
@@ -66,7 +66,8 @@ void HttpClient::sendRequest(const string &url) {
_http_persistent = true;
if (_body && _body->remainSize()) {
_header.emplace("Content-Length", to_string(_body->remainSize()));
- _header.emplace("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
+ GET_CONFIG(string, charSet, Http::kCharSet);
+ _header.emplace("Content-Type", "application/x-www-form-urlencoded; charset=" + charSet);
}
bool host_changed = (_last_host != host + ":" + to_string(port)) || (_is_https != is_https);
diff --git a/src/Http/HttpSession.cpp b/src/Http/HttpSession.cpp
index ba74d803..ba25f3f4 100644
--- a/src/Http/HttpSession.cpp
+++ b/src/Http/HttpSession.cpp
@@ -683,34 +683,10 @@ void HttpSession::sendResponse(int code,
AsyncSender::onSocketFlushed(data);
}
-string HttpSession::urlDecodePath(const string &str) {
- auto ret = strCoding::UrlDecodePath(str);
-#ifdef _WIN32
- GET_CONFIG(string, charSet, Http::kCharSet);
- bool isGb2312 = !strcasecmp(charSet.data(), "gb2312");
- if (isGb2312) {
- ret = strCoding::UTF8ToGB2312(ret);
- }
-#endif // _WIN32
- return ret;
-}
-
-string HttpSession::urlDecodeComponent(const string &str) {
- auto ret = strCoding::UrlDecodeComponent(str);
-#ifdef _WIN32
- GET_CONFIG(string, charSet, Http::kCharSet);
- bool isGb2312 = !strcasecmp(charSet.data(), "gb2312");
- if (isGb2312) {
- ret = strCoding::UTF8ToGB2312(ret);
- }
-#endif // _WIN32
- return ret;
-}
-
void HttpSession::urlDecode(Parser &parser) {
- parser.setUrl(urlDecodePath(parser.url()));
+ parser.setUrl(strCoding::UrlDecodePath(parser.url()));
for (auto &pr : _parser.getUrlArgs()) {
- const_cast(pr.second) = urlDecodeComponent(pr.second);
+ const_cast(pr.second) = strCoding::UrlDecodeComponent(pr.second);
}
}
diff --git a/src/Http/HttpSession.h b/src/Http/HttpSession.h
index 0ffbf137..9f328063 100644
--- a/src/Http/HttpSession.h
+++ b/src/Http/HttpSession.h
@@ -44,8 +44,6 @@ public:
void onRecv(const toolkit::Buffer::Ptr &) override;
void onError(const toolkit::SockException &err) override;
void onManager() override;
- static std::string urlDecodePath(const std::string &str);
- static std::string urlDecodeComponent(const std::string &str);
void setTimeoutSec(size_t second);
void setMaxReqSize(size_t max_req_size);
diff --git a/src/Record/HlsMaker.cpp b/src/Record/HlsMaker.cpp
index 11d5190a..bc11028f 100644
--- a/src/Record/HlsMaker.cpp
+++ b/src/Record/HlsMaker.cpp
@@ -28,7 +28,7 @@ void HlsMaker::makeIndexFile(bool include_delay, bool eof) {
GET_CONFIG(uint32_t, segDelay, Hls::kSegmentDelay);
GET_CONFIG(uint32_t, segRetain, Hls::kSegmentRetain);
std::deque> temp(_seg_dur_list);
- if (!include_delay) {
+ if (!include_delay && _seg_number) {
while (temp.size() > _seg_number) {
temp.pop_front();
}
diff --git a/src/Rtsp/RtspPlayer.cpp b/src/Rtsp/RtspPlayer.cpp
index 1cf393e2..e83cc090 100644
--- a/src/Rtsp/RtspPlayer.cpp
+++ b/src/Rtsp/RtspPlayer.cpp
@@ -210,7 +210,8 @@ void RtspPlayer::handleResDESCRIBE(const Parser &parser) {
if (play_track != TrackInvalid) {
auto track = sdpParser.getTrack(play_track);
_sdp_track.emplace_back(track);
- sdp = track->toString();
+ auto title_track = sdpParser.getTrack(TrackTitle);
+ sdp = (title_track ? title_track->toString() : "") + track->toString();
} else {
_sdp_track = sdpParser.getAvailableTrack();
sdp = sdpParser.toString();
diff --git a/webrtc/RtpExt.cpp b/webrtc/RtpExt.cpp
index d1ade570..e186cbf0 100644
--- a/webrtc/RtpExt.cpp
+++ b/webrtc/RtpExt.cpp
@@ -204,7 +204,8 @@ static unordered_map s_url_to_type = {RTP_EXT_M
RtpExtType RtpExt::getExtType(const string &url) {
auto it = s_url_to_type.find(url);
if (it == s_url_to_type.end()) {
- throw std::invalid_argument(string("未识别的rtp ext url类型:") + url);
+ WarnL << "unknown rtp ext url type: " << url;
+ return RtpExtType::padding;
}
return it->second;
}