Compare commits

..

10 Commits

Author SHA1 Message Date
xia-chu
df9f3bd8a9 更新StreamUI
Some checks failed
Android / build (push) Has been cancelled
CodeQL / Analyze (cpp) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Docker / build (push) Has been cancelled
Linux / build (push) Has been cancelled
macOS / build (push) Has been cancelled
Windows / build (push) Has been cancelled
2025-12-03 15:21:19 +08:00
xia-chu
96c62cdac0 添加submodule 2025-12-03 15:08:08 +08:00
xia-chu
881238fcf3 只拷贝frontend前端页面到www/StreamUI 2025-12-03 15:02:48 +08:00
xia-chu
85524f102b 修复gil线程安全bug 2025-12-03 14:51:51 +08:00
xia-chu
a28b0fc0a4 支持HTTP PUT 2025-12-03 14:49:21 +08:00
xia-chu
b0cf40d281 支持回调http请求到fastapi 2025-12-03 14:44:15 +08:00
xia-chu
b118c5e936 完成配置文件相关python接口 2025-12-02 20:48:10 +08:00
xia-chu
171b354e64 on_flow_report回调到python层 2025-12-02 20:33:21 +08:00
xia-chu
e3b3630f34 on_play鉴权回调到python层 2025-12-02 20:21:41 +08:00
xia-chu
9b37d69bfd 添加python插件 2025-12-02 16:21:40 +08:00
48 changed files with 264 additions and 730 deletions

@ -1 +1 @@
Subproject commit 7302286cf4be39d416b023fec3fd4ca9c54af762 Subproject commit 493d14e1682cc0c79e72b3893dd98f865d45b8e9

View File

@ -44,7 +44,6 @@ option(ENABLE_FFMPEG "Enable FFmpeg" OFF)
option(ENABLE_HLS "Enable HLS" ON) option(ENABLE_HLS "Enable HLS" ON)
option(ENABLE_JEMALLOC_STATIC "Enable static linking to the jemalloc library" OFF) option(ENABLE_JEMALLOC_STATIC "Enable static linking to the jemalloc library" OFF)
option(ENABLE_JEMALLOC_DUMP "Enable jemalloc to dump malloc statistics" OFF) option(ENABLE_JEMALLOC_DUMP "Enable jemalloc to dump malloc statistics" OFF)
option(ENABLE_TCMALLOC "Enable linking to the tcmalloc library" OFF)
option(ENABLE_MEM_DEBUG "Enable Memory Debug" OFF) option(ENABLE_MEM_DEBUG "Enable Memory Debug" OFF)
option(ENABLE_MP4 "Enable MP4" ON) option(ENABLE_MP4 "Enable MP4" ON)
option(ENABLE_MSVC_MT "Enable MSVC Mt/Mtd lib" ON) option(ENABLE_MSVC_MT "Enable MSVC Mt/Mtd lib" ON)
@ -258,8 +257,8 @@ endif()
# Multiple modules depend on ffmpeg related libraries, unified search # Multiple modules depend on ffmpeg related libraries, unified search
if(ENABLE_FFMPEG) if(ENABLE_FFMPEG)
find_package(PkgConfig QUIET) find_package(PkgConfig QUIET)
# ffmpeg/libavutil # ffmpeg/libutil
# find ffmpeg/libavutil installed # find ffmpeg/libutil installed
if(PKG_CONFIG_FOUND) if(PKG_CONFIG_FOUND)
pkg_check_modules(AVUTIL QUIET IMPORTED_TARGET libavutil) pkg_check_modules(AVUTIL QUIET IMPORTED_TARGET libavutil)
if(AVUTIL_FOUND) if(AVUTIL_FOUND)
@ -298,19 +297,8 @@ if(ENABLE_FFMPEG)
endif() endif()
endif() endif()
# ffmpeg/libavfilter # ffmpeg/libutil
# find ffmpeg/libavfilter installed # find ffmpeg/libutil installed
if(PKG_CONFIG_FOUND)
pkg_check_modules(AVFILTER QUIET IMPORTED_TARGET libavfilter)
if(AVFILTER_FOUND)
update_cached_list(MK_LINK_LIBRARIES PkgConfig::AVFILTER)
message(STATUS "found library: ${AVFILTER_LIBRARIES}")
endif()
endif()
# ffmpeg/libavutil
# find ffmpeg/libavutil installed
if(NOT AVUTIL_FOUND) if(NOT AVUTIL_FOUND)
find_package(AVUTIL QUIET) find_package(AVUTIL QUIET)
if(AVUTIL_FOUND) if(AVUTIL_FOUND)
@ -353,16 +341,7 @@ if(ENABLE_FFMPEG)
endif() endif()
endif() endif()
if(NOT AVFILTER_FOUND) if(AVUTIL_FOUND AND AVCODEC_FOUND AND SWSCALE_FOUND AND SWRESAMPLE_FOUND)
find_package(AVFILTER QUIET)
if(AVFILTER_FOUND)
include_directories(SYSTEM ${AVFILTER_INCLUDE_DIR})
update_cached_list(MK_LINK_LIBRARIES ${AVFILTER_LIBRARIES})
message(STATUS "found library: ${AVFILTER_LIBRARIES}")
endif()
endif()
if(AVUTIL_FOUND AND AVCODEC_FOUND AND SWSCALE_FOUND AND SWRESAMPLE_FOUND AND AVFILTER_FOUND)
update_cached_list(MK_COMPILE_DEFINITIONS ENABLE_FFMPEG) update_cached_list(MK_COMPILE_DEFINITIONS ENABLE_FFMPEG)
update_cached_list(MK_LINK_LIBRARIES ${CMAKE_DL_LIBS}) update_cached_list(MK_LINK_LIBRARIES ${CMAKE_DL_LIBS})
else() else()
@ -423,19 +402,6 @@ if(JEMALLOC_FOUND)
endif () endif ()
endif() endif()
# tcmalloc
# find tcmalloc installed
if(ENABLE_TCMALLOC)
find_package(TCMALLOC QUIET)
if(TCMALLOC_FOUND)
message(STATUS "Link with tcmalloc library: ${TCMALLOC_LIBRARIES}")
update_cached_list(MK_LINK_LIBRARIES ${TCMALLOC_LIBRARIES})
else()
set(ENABLE_TCMALLOC OFF)
message(WARNING "tcmalloc 相关功能未找到")
endif()
endif()
# openssl # openssl
# find openssl installed # find openssl installed
find_package(OpenSSL QUIET) find_package(OpenSSL QUIET)
@ -614,9 +580,6 @@ if (ENABLE_PYTHON)
) )
endif () endif ()
if (ENABLE_FFMPEG)
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/DejaVuSans.ttf" DESTINATION ${EXECUTABLE_OUTPUT_PATH})
endif ()
# VideoStack # VideoStack
# Copy the default background image used by VideoStack when there is no video stream # Copy the default background image used by VideoStack when there is no video stream
if (ENABLE_VIDEOSTACK AND ENABLE_FFMPEG AND ENABLE_X264) if (ENABLE_VIDEOSTACK AND ENABLE_FFMPEG AND ENABLE_X264)

Binary file not shown.

View File

@ -327,7 +327,6 @@ API_EXPORT uint16_t API_CALL mk_ice_server_start(uint16_t port){
iceServer_udp = std::make_shared<UdpServer>(); iceServer_udp = std::make_shared<UdpServer>();
iceServer_udp->start<IceSession>(port); iceServer_udp->start<IceSession>(port);
iceServer_tcp->start<IceSession>(port); iceServer_tcp->start<IceSession>(port);
return 0;
} catch (std::exception &ex) { } catch (std::exception &ex) {
iceServer_udp = nullptr; iceServer_udp = nullptr;
iceServer_tcp = nullptr; iceServer_tcp = nullptr;

View File

@ -1,16 +0,0 @@
find_path(AVFILTER_INCLUDE_DIR
NAMES libavfilter/avfilter.h
HINTS ${FFMPEG_PATH_ROOT}
PATH_SUFFIXES include)
find_library(AVFILTER_LIBRARY
NAMES avfilter
HINTS ${FFMPEG_PATH_ROOT}
PATH_SUFFIXES bin lib)
set(AVFILTER_LIBRARIES ${AVFILTER_LIBRARY})
set(AVFILTER_INCLUDE_DIRS ${AVFILTER_INCLUDE_DIR})
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(AVFILTER DEFAULT_MSG AVFILTER_LIBRARY AVFILTER_INCLUDE_DIR)

View File

@ -1,16 +0,0 @@
find_path(Tcmalloc_INCLUDE_DIR
NAMES google/tcmalloc.h
)
find_library(Tcmalloc_LIBRARY
NAMES tcmalloc_minimal tcmalloc
)
set(TCMALLOC_LIBRARIES ${Tcmalloc_LIBRARY})
set(TCMALLOC_INCLUDE_DIRS ${Tcmalloc_INCLUDE_DIR})
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(TCMALLOC
DEFAULT_MSG
TCMALLOC_LIBRARIES TCMALLOC_INCLUDE_DIRS
)

View File

@ -412,14 +412,10 @@ nackMaxMS=3000
nackMaxCount=15 nackMaxCount=15
#nack重传频率rtt的倍数 #nack重传频率rtt的倍数
nackIntervalRatio=1.0 nackIntervalRatio=1.0
#视频nack包中rtp个数减小此值可以让nack包响应更灵敏 #nack包中rtp个数减小此值可以让nack包响应更灵敏
nackRtpSize=8 nackRtpSize=8
#音频nack包中rtp个数减小此值可以让nack包响应更灵敏
nackAudioRtpSize=4
#是否尝试过滤 b帧 #是否尝试过滤 b帧
bfilter=0 bfilter=0
# 是否优先采用webrtc over tcp模式
preferred_tcp=0
[srt] [srt]
#srt播放推流、播放超时时间,单位秒 #srt播放推流、播放超时时间,单位秒

View File

@ -57,7 +57,7 @@ public:
toolkit::Buffer::Ptr getExtraData() const override; toolkit::Buffer::Ptr getExtraData() const override;
void setExtraData(const uint8_t *data, size_t size) override; void setExtraData(const uint8_t *data, size_t size) override;
protected: protected:
aom_av1_t _context {}; aom_av1_t _context = {0};
}; };
} // namespace mediakit } // namespace mediakit

View File

@ -20,7 +20,7 @@ using namespace toolkit;
namespace mediakit { namespace mediakit {
Buffer::Ptr G711Track::getExtraData() const { Buffer::Ptr G711Track::getExtraData() const {
struct wave_format_t wav {}; struct wave_format_t wav = {0};
wav.wFormatTag = getCodecId() == CodecG711A ? WAVE_FORMAT_ALAW : WAVE_FORMAT_MULAW; wav.wFormatTag = getCodecId() == CodecG711A ? WAVE_FORMAT_ALAW : WAVE_FORMAT_MULAW;
wav.nChannels = getAudioChannel(); wav.nChannels = getAudioChannel();
wav.nSamplesPerSec = getAudioSampleRate(); wav.nSamplesPerSec = getAudioSampleRate();

View File

@ -28,7 +28,7 @@ void OpusTrack::setExtraData(const uint8_t *data, size_t size) {
} }
Buffer::Ptr OpusTrack::getExtraData() const { Buffer::Ptr OpusTrack::getExtraData() const {
struct opus_head_t opus {}; struct opus_head_t opus = { 0 };
opus.version = 1; opus.version = 1;
opus.channels = getAudioChannel(); opus.channels = getAudioChannel();
opus.input_sample_rate = getAudioSampleRate(); opus.input_sample_rate = getAudioSampleRate();

View File

@ -41,7 +41,7 @@ public:
toolkit::Buffer::Ptr getExtraData() const override; toolkit::Buffer::Ptr getExtraData() const override;
void setExtraData(const uint8_t *data, size_t size) override; void setExtraData(const uint8_t *data, size_t size) override;
private: private:
webm_vpx_t _vpx {}; webm_vpx_t _vpx = {0};
}; };
} // namespace mediakit } // namespace mediakit

View File

@ -334,7 +334,7 @@ bool VP8RtpEncoder::inputFrame(const Frame::Ptr &frame) {
bool key = frame->keyFrame(); bool key = frame->keyFrame();
bool mark = false; bool mark = false;
for (size_t pos = 0; pos < len; pos += pdu_size) { for (size_t pos = 0; pos < len; pos += pdu_size) {
if (static_cast<int>(len - pos) <= pdu_size) { if (len - pos <= pdu_size) {
pdu_size = len - pos; pdu_size = len - pos;
mark = true; mark = true;
} }

View File

@ -56,6 +56,9 @@ public:
using Ptr = std::shared_ptr<VP8RtpEncoder>; using Ptr = std::shared_ptr<VP8RtpEncoder>;
bool inputFrame(const Frame::Ptr &frame) override; bool inputFrame(const Frame::Ptr &frame) override;
private:
uint16_t _pic_id = 0;
}; };
}//namespace mediakit }//namespace mediakit

View File

@ -41,7 +41,7 @@ public:
toolkit::Buffer::Ptr getExtraData() const override; toolkit::Buffer::Ptr getExtraData() const override;
void setExtraData(const uint8_t *data, size_t size) override; void setExtraData(const uint8_t *data, size_t size) override;
private: private:
webm_vpx_t _vpx {}; webm_vpx_t _vpx = {0};
}; };
} // namespace mediakit } // namespace mediakit

View File

@ -297,7 +297,7 @@ bool VP9RtpEncoder::inputFrame(const Frame::Ptr &frame) {
int pdu_size = getRtpInfo().getMaxSize() - nheader; int pdu_size = getRtpInfo().getMaxSize() - nheader;
bool mark = false; bool mark = false;
for (int pos = 0; pos < len; pos += pdu_size) { for (size_t pos = 0; pos < len; pos += pdu_size) {
if (len - pos <= pdu_size) { if (len - pos <= pdu_size) {
pdu_size = len - pos; pdu_size = len - pos;
header[0] |= kEBit; header[0] |= kEBit;

View File

@ -50,10 +50,6 @@ target_compile_definitions(MediaServer
target_compile_options(MediaServer target_compile_options(MediaServer
PRIVATE ${COMPILE_OPTIONS_DEFAULT}) PRIVATE ${COMPILE_OPTIONS_DEFAULT})
if(MINGW)
update_cached_list(MK_LINK_LIBRARIES dbghelp)
endif()
if(CMAKE_SYSTEM_NAME MATCHES "Linux") if(CMAKE_SYSTEM_NAME MATCHES "Linux")
target_link_libraries(MediaServer -Wl,--start-group ${MK_LINK_LIBRARIES} -Wl,--end-group) target_link_libraries(MediaServer -Wl,--start-group ${MK_LINK_LIBRARIES} -Wl,--end-group)
else() else()

View File

@ -115,6 +115,8 @@ static void responseApi(int code, const string &msg, const HttpSession::HttpResp
responseApi(res, invoker); responseApi(res, invoker);
} }
static ApiArgsType getAllArgs(const Parser &parser);
static HttpApi toApi(const function<void(API_ARGS_MAP_ASYNC)> &cb) { static HttpApi toApi(const function<void(API_ARGS_MAP_ASYNC)> &cb) {
return [cb](const Parser &parser, const HttpSession::HttpResponseInvoker &invoker, SockInfo &sender) { return [cb](const Parser &parser, const HttpSession::HttpResponseInvoker &invoker, SockInfo &sender) {
GET_CONFIG(string, charSet, Http::kCharSet); GET_CONFIG(string, charSet, Http::kCharSet);
@ -213,7 +215,7 @@ void api_regist(const string &api_path, const function<void(API_ARGS_STRING_ASYN
// 获取HTTP请求中url参数、content参数 [AUTO-TRANSLATED:d161a1e1] // 获取HTTP请求中url参数、content参数 [AUTO-TRANSLATED:d161a1e1]
// Get URL parameters and content parameters from the HTTP request // Get URL parameters and content parameters from the HTTP request
ApiArgsType getAllArgs(const Parser &parser) { static ApiArgsType getAllArgs(const Parser &parser) {
ApiArgsType allArgs; ApiArgsType allArgs;
if (parser["Content-Type"].find("application/x-www-form-urlencoded") == 0) { if (parser["Content-Type"].find("application/x-www-form-urlencoded") == 0) {
auto contentArgs = parser.parseArgs(parser.content()); auto contentArgs = parser.parseArgs(parser.content());
@ -1077,7 +1079,6 @@ void installWebApi() {
} }
fillSockInfo(jsession, session.get()); fillSockInfo(jsession, session.get());
jsession["id"] = id; jsession["id"] = id;
jsession["type"] = session->getSock()->sockType() == SockNum::Sock_TCP ? "tcp" : "udp";
jsession["typeid"] = toolkit::demangle(typeid(*session).name()); jsession["typeid"] = toolkit::demangle(typeid(*session).name());
val["data"].append(jsession); val["data"].append(jsession);
}); });

View File

@ -238,7 +238,6 @@ uint16_t openRtpServer(uint16_t local_port, const mediakit::MediaTuple &tuple, i
#endif #endif
Json::Value makeMediaSourceJson(mediakit::MediaSource &media); Json::Value makeMediaSourceJson(mediakit::MediaSource &media);
ApiArgsType getAllArgs(const mediakit::Parser &parser);
void getStatisticJson(const std::function<void(Json::Value &val)> &cb); void getStatisticJson(const std::function<void(Json::Value &val)> &cb);
void addStreamProxy(const mediakit::MediaTuple &tuple, const std::string &url, int retry_count, 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 mediakit::ProtocolOption &option, int rtp_type, float timeout_sec, const toolkit::mINI &args,

View File

@ -18,7 +18,6 @@
#include "Http/HttpRequester.h" #include "Http/HttpRequester.h"
#include "Network/Session.h" #include "Network/Session.h"
#include "Rtsp/RtspSession.h" #include "Rtsp/RtspSession.h"
#include "Player/PlayerProxy.h"
#include "WebHook.h" #include "WebHook.h"
#include "WebApi.h" #include "WebApi.h"
@ -501,6 +500,10 @@ void installWebHook() {
// 监听rtsp、rtmp源注册或注销事件 [AUTO-TRANSLATED:6396afa8] // 监听rtsp、rtmp源注册或注销事件 [AUTO-TRANSLATED:6396afa8]
// Listen to rtsp, rtmp source registration or deregistration events // Listen to rtsp, rtmp source registration or deregistration events
NoticeCenter::Instance().addListener(&web_hook_tag, Broadcast::kBroadcastMediaChanged, [](BroadcastMediaChangedArgs) { NoticeCenter::Instance().addListener(&web_hook_tag, Broadcast::kBroadcastMediaChanged, [](BroadcastMediaChangedArgs) {
GET_CONFIG(string, hook_stream_changed, Hook::kOnStreamChanged);
if (!hook_enable || hook_stream_changed.empty()) {
return;
}
GET_CONFIG_FUNC(std::set<std::string>, stream_changed_set, Hook::kStreamChangedSchemas, [](const std::string &str) { GET_CONFIG_FUNC(std::set<std::string>, stream_changed_set, Hook::kStreamChangedSchemas, [](const std::string &str) {
std::set<std::string> ret; std::set<std::string> ret;
auto vec = split(str, "/"); auto vec = split(str, "/");
@ -517,15 +520,6 @@ void installWebHook() {
// This protocol registration deregistration event is ignored // This protocol registration deregistration event is ignored
return; return;
} }
#if defined(ENABLE_PYTHON)
if (PythonInvoker::Instance().on_media_changed(bRegist, sender)) {
return;
}
#endif
GET_CONFIG(string, hook_stream_changed, Hook::kOnStreamChanged);
if (!hook_enable || hook_stream_changed.empty()) {
return;
}
ArgsType body; ArgsType body;
if (bRegist) { if (bRegist) {
@ -805,14 +799,6 @@ void installWebHook() {
do_http_hook(rtp_server_timeout, body); do_http_hook(rtp_server_timeout, body);
}); });
NoticeCenter::Instance().addListener(&web_hook_tag, Broadcast::kBroadcastPlayerProxyFailed, [](BroadcastPlayerProxyFailedArgs) {
#if defined(ENABLE_PYTHON)
if (PythonInvoker::Instance().on_player_proxy_failed(sender, ex)) {
return;
}
#endif
});
// 汇报服务器重新启动 [AUTO-TRANSLATED:bd7d83df] // 汇报服务器重新启动 [AUTO-TRANSLATED:bd7d83df]
// Report server restart // Report server restart
reportServerStarted(); reportServerStarted();

View File

@ -273,16 +273,6 @@ int start_main(int argc,char *argv[]) {
} }
#endif //! defined(_WIN32) #endif //! defined(_WIN32)
// 设置poller线程数和cpu亲和性,该函数必须在使用ZLToolKit网络相关对象之前调用才能生效 [AUTO-TRANSLATED:7f03a1e5]
// Set the number of poller threads and CPU affinity. This function must be called before using ZLToolKit network related objects to take effect.
// 如果需要调用getSnap和addFFmpegSource接口可以关闭cpu亲和性 [AUTO-TRANSLATED:7629f7bc]
// If you need to call the getSnap and addFFmpegSource interfaces, you can turn off CPU affinity
EventPollerPool::setPoolSize(threads);
WorkThreadPool::setPoolSize(threads);
EventPollerPool::enableCpuAffinity(affinity);
WorkThreadPool::enableCpuAffinity(affinity);
// 开启崩溃捕获等 [AUTO-TRANSLATED:9c7c759c] // 开启崩溃捕获等 [AUTO-TRANSLATED:9c7c759c]
// Enable crash capture, etc. // Enable crash capture, etc.
System::systemSetup(); System::systemSetup();
@ -339,6 +329,15 @@ int start_main(int argc,char *argv[]) {
uint16_t httpsPort = mINI::Instance()[Http::kSSLPort]; uint16_t httpsPort = mINI::Instance()[Http::kSSLPort];
uint16_t rtpPort = mINI::Instance()[RtpProxy::kPort]; uint16_t rtpPort = mINI::Instance()[RtpProxy::kPort];
// 设置poller线程数和cpu亲和性,该函数必须在使用ZLToolKit网络相关对象之前调用才能生效 [AUTO-TRANSLATED:7f03a1e5]
// Set the number of poller threads and CPU affinity. This function must be called before using ZLToolKit network related objects to take effect.
// 如果需要调用getSnap和addFFmpegSource接口可以关闭cpu亲和性 [AUTO-TRANSLATED:7629f7bc]
// If you need to call the getSnap and addFFmpegSource interfaces, you can turn off CPU affinity
EventPollerPool::setPoolSize(threads);
WorkThreadPool::setPoolSize(threads);
EventPollerPool::enableCpuAffinity(affinity);
// 简单的telnet服务器可用于服务器调试但是不能使用23端口否则telnet上了莫名其妙的现象 [AUTO-TRANSLATED:f9324c6e] // 简单的telnet服务器可用于服务器调试但是不能使用23端口否则telnet上了莫名其妙的现象 [AUTO-TRANSLATED:f9324c6e]
// Simple telnet server, can be used for server debugging, but cannot use port 23, otherwise telnet will have inexplicable phenomena // Simple telnet server, can be used for server debugging, but cannot use port 23, otherwise telnet will have inexplicable phenomena
// 测试方法:telnet 127.0.0.1 9000 [AUTO-TRANSLATED:de0ac883] // 测试方法:telnet 127.0.0.1 9000 [AUTO-TRANSLATED:de0ac883]
@ -509,11 +508,9 @@ int start_main(int argc,char *argv[]) {
#endif #endif
#if defined(ENABLE_PYTHON) #if defined(ENABLE_PYTHON)
// 初始化python解释器
auto &ref = PythonInvoker::Instance();
auto py_plugin = mINI::Instance()[Python::kPlugin]; auto py_plugin = mINI::Instance()[Python::kPlugin];
if (!py_plugin.empty()) { if (!py_plugin.empty()) {
ref.load(py_plugin); PythonInvoker::Instance().load(py_plugin);
} }
#endif #endif
sem.wait(); sem.wait();
@ -522,10 +519,6 @@ int start_main(int argc,char *argv[]) {
unInstallWebHook(); unInstallWebHook();
onProcessExited(); onProcessExited();
#if defined(ENABLE_PYTHON)
PythonInvoker::release();
#endif
// 休眠1秒再退出防止资源释放顺序错误 [AUTO-TRANSLATED:1b11a74f] // 休眠1秒再退出防止资源释放顺序错误 [AUTO-TRANSLATED:1b11a74f]
// sleep for 1 second before exiting, to prevent resource release order errors // sleep for 1 second before exiting, to prevent resource release order errors
InfoL << "程序退出中,请等待..."; InfoL << "程序退出中,请等待...";

View File

@ -7,10 +7,7 @@
#include <iostream> #include <iostream>
#include <string> #include <string>
#include <type_traits> #include <type_traits>
#include "WebApi.h"
#include "WebHook.h" #include "WebHook.h"
#include "Util/util.h"
#include "Util/File.h"
#include "Common/Parser.h" #include "Common/Parser.h"
#include "Http/HttpSession.h" #include "Http/HttpSession.h"
@ -73,11 +70,6 @@ py::dict to_python(const SockInfo &info) {
return jsonToPython(json); return jsonToPython(json);
} }
template <typename T>
std::shared_ptr<T> to_python2(const T &t) {
return std::shared_ptr<T>(const_cast<T *>(&t), py::nodelete());
}
template <typename T> template <typename T>
T &to_native(const py::capsule &cap) { T &to_native(const py::capsule &cap) {
static auto name_str = toolkit::demangle(typeid(T).name()); static auto name_str = toolkit::demangle(typeid(T).name());
@ -118,22 +110,6 @@ void handle_http_request(const py::object &check_route, const py::object &submit
} }
consumed = true; consumed = true;
// http api被python拦截了再api统一鉴权
try {
auto args = getAllArgs(parser);
auto allArgs = ArgsMap(parser, args);
GET_CONFIG(std::string, api_secret, API::kSecret);
CHECK_SECRET(); // 检测secret
} catch (std::exception &ex) {
Json::Value val;
val["code"] = API::Exception;
val["msg"] = ex.what();
HttpSession::KeyValue headerOut;
headerOut["Content-Type"] = "application/json";
invoker(200, headerOut, val.toStyledString());
return;
}
StrCaseMap resp_headers; StrCaseMap resp_headers;
std::string resp_body; std::string resp_body;
int status = 500; int status = 500;
@ -176,18 +152,11 @@ PYBIND11_EMBEDDED_MODULE(mk_loader, m) {
} }
return ""; return "";
}); });
m.def("get_full_path", [](const std::string &path, const std::string &current_path) -> std::string {
py::gil_scoped_release release;
return File::absolutePath(path, current_path);
});
m.def("set_config", [](const std::string &key, const std::string &value) -> bool { m.def("set_config", [](const std::string &key, const std::string &value) -> bool {
py::gil_scoped_release release; py::gil_scoped_release release;
mINI::Instance()[key]= value; mINI::Instance()[key]= value;
return true; return true;
}); });
m.def("update_config", []() { m.def("update_config", []() {
NOTICE_EMIT(BroadcastReloadConfigArgs, Broadcast::kBroadcastReloadConfig); NOTICE_EMIT(BroadcastReloadConfigArgs, Broadcast::kBroadcastReloadConfig);
mINI::Instance().dumpFile(g_ini_file); mINI::Instance().dumpFile(g_ini_file);
@ -202,7 +171,6 @@ PYBIND11_EMBEDDED_MODULE(mk_loader, m) {
auto &invoker = to_native<Broadcast::PublishAuthInvoker>(cap); auto &invoker = to_native<Broadcast::PublishAuthInvoker>(cap);
invoker(err, option); invoker(err, option);
}); });
m.def("auth_invoker_do", [](const py::capsule &cap, const std::string &err) { m.def("auth_invoker_do", [](const py::capsule &cap, const std::string &err) {
// 执行c++代码时释放gil锁 // 执行c++代码时释放gil锁
py::gil_scoped_release release; py::gil_scoped_release release;
@ -218,46 +186,6 @@ PYBIND11_EMBEDDED_MODULE(mk_loader, m) {
}); });
}); });
py::enum_<TrackType>(m, "TrackType")
.value("Invalid", TrackInvalid)
.value("Video", TrackVideo)
.value("Audio", TrackAudio)
.value("Title", TrackTitle)
.value("Application", TrackApplication)
.export_values();
py::class_<MediaSource, MediaSource::Ptr>(m, "MediaSource")
.def("getSchema", &MediaSource::getSchema)
.def("getUrl", &MediaSource::getUrl)
.def("getMediaTuple", &MediaSource::getMediaTuple)
.def("getTimeStamp", &MediaSource::getTimeStamp)
.def("setTimeStamp", &MediaSource::setTimeStamp)
.def("getBytesSpeed", &MediaSource::getBytesSpeed)
.def("getTotalBytes", &MediaSource::getTotalBytes)
.def("getCreateStamp", &MediaSource::getCreateStamp)
.def("getAliveSecond", &MediaSource::getAliveSecond)
.def("readerCount", &MediaSource::readerCount)
.def("totalReaderCount", &MediaSource::totalReaderCount)
.def("getOriginType", &MediaSource::getOriginType)
.def("getOriginUrl", &MediaSource::getOriginUrl)
.def("getOriginSock", &MediaSource::getOriginSock)
.def("seekTo", &MediaSource::seekTo)
.def("pause", &MediaSource::pause)
.def("speed", &MediaSource::speed)
.def("close", &MediaSource::close)
.def("setupRecord", &MediaSource::setupRecord)
.def("isRecording", &MediaSource::isRecording)
.def("stopSendRtp", &MediaSource::stopSendRtp)
.def("getLossRate", &MediaSource::getLossRate);
py::class_<MediaTuple, std::shared_ptr<MediaTuple>>(m, "MediaTuple")
.def_readwrite("vhost", &MediaTuple::vhost)
.def_readwrite("app", &MediaTuple::app)
.def_readwrite("stream", &MediaTuple::stream)
.def_readwrite("params", &MediaTuple::params)
.def("shortUrl", &MediaTuple::shortUrl);
py::class_<SockException, std::shared_ptr<SockException>>(m, "SockException").def("what", &SockException::what).def("code", &SockException::getErrCode);
} }
namespace mediakit { namespace mediakit {
@ -287,18 +215,9 @@ bool set_python_path() {
return true; return true;
} }
static std::shared_ptr<PythonInvoker> g_instance;
PythonInvoker &PythonInvoker::Instance() { PythonInvoker &PythonInvoker::Instance() {
static toolkit::onceToken s_token([]() { static std::shared_ptr<PythonInvoker> instance(new PythonInvoker);
g_instance.reset(new PythonInvoker); return *instance;
});
return *g_instance;
}
void PythonInvoker::release() {
g_instance = nullptr;
} }
PythonInvoker::PythonInvoker() { PythonInvoker::PythonInvoker() {
@ -325,34 +244,32 @@ PythonInvoker::~PythonInvoker() {
} }
_on_exit = py::object(); _on_exit = py::object();
_on_publish = py::object(); _on_publish = py::object();
_on_play = py::object();
_on_flow_report = py::object();
_on_reload_config = py::object();
_on_media_changed = py::object();
_on_player_proxy_failed = py::object();
_module = py::module(); _module = py::module();
} }
delete _rel; delete _rel;
delete _interpreter; delete _interpreter;
} }
#define GET_FUNC(instance, name) \
if (hasattr(instance, #name)) { \
_##name = instance.attr(#name); \
}
void PythonInvoker::load(const std::string &module_name) { void PythonInvoker::load(const std::string &module_name) {
try { try {
py::gil_scoped_acquire gil; // 加锁 py::gil_scoped_acquire gil; // 加锁
_module = py::module::import(module_name.c_str()); _module = py::module::import(module_name.c_str());
GET_FUNC(_module, on_exit); if (hasattr(_module, "on_exit")) {
GET_FUNC(_module, on_publish); _on_exit = _module.attr("on_exit");
GET_FUNC(_module, on_play); }
GET_FUNC(_module, on_flow_report); if (hasattr(_module, "on_publish")) {
GET_FUNC(_module, on_reload_config); _on_publish = _module.attr("on_publish");
GET_FUNC(_module, on_media_changed); }
GET_FUNC(_module, on_player_proxy_failed); if (hasattr(_module, "on_play")) {
_on_play = _module.attr("on_play");
}
if (hasattr(_module, "on_flow_report")) {
_on_flow_report = _module.attr("on_flow_report");
}
if (hasattr(_module, "on_reload_config")) {
_on_reload_config = _module.attr("on_reload_config");
}
if (hasattr(_module, "on_start")) { if (hasattr(_module, "on_start")) {
py::object on_start = _module.attr("on_start"); py::object on_start = _module.attr("on_start");
if (on_start) { if (on_start) {
@ -388,22 +305,6 @@ bool PythonInvoker::on_flow_report(BroadcastFlowReportArgs) const {
return _on_flow_report(to_python(args), totalBytes, totalDuration, isPlayer, to_python(sender)).cast<bool>(); return _on_flow_report(to_python(args), totalBytes, totalDuration, isPlayer, to_python(sender)).cast<bool>();
} }
bool PythonInvoker::on_media_changed(BroadcastMediaChangedArgs) const {
py::gil_scoped_acquire gil; // 确保在 Python 调用期间持有 GIL
if (!_on_media_changed) {
return false;
}
return _on_media_changed(bRegist, to_python2(sender)).cast<bool>();
}
bool PythonInvoker::on_player_proxy_failed(BroadcastPlayerProxyFailedArgs) const {
py::gil_scoped_acquire gil; // 确保在 Python 调用期间持有 GIL
if (!_on_player_proxy_failed) {
return false;
}
return _on_player_proxy_failed(sender.getUrl(), to_python2(sender.getMediaTuple()), to_python2(ex)).cast<bool>();
}
} // namespace mediakit } // namespace mediakit
#endif #endif

View File

@ -11,7 +11,6 @@
#include "Util/logger.h" #include "Util/logger.h"
#include "Common/config.h" #include "Common/config.h"
#include "Common/MediaSource.h" #include "Common/MediaSource.h"
#include "Player/PlayerProxy.h"
namespace py = pybind11; namespace py = pybind11;
@ -22,14 +21,11 @@ public:
~PythonInvoker(); ~PythonInvoker();
static PythonInvoker& Instance(); static PythonInvoker& Instance();
static void release();
void load(const std::string &module_name); void load(const std::string &module_name);
bool on_publish(BroadcastMediaPublishArgs) const; bool on_publish(BroadcastMediaPublishArgs) const;
bool on_play(BroadcastMediaPlayedArgs) const; bool on_play(BroadcastMediaPlayedArgs) const;
bool on_flow_report(BroadcastFlowReportArgs) const; bool on_flow_report(BroadcastFlowReportArgs) const;
bool on_media_changed(BroadcastMediaChangedArgs) const;
bool on_player_proxy_failed(BroadcastPlayerProxyFailedArgs) const;
private: private:
PythonInvoker(); PythonInvoker();
@ -50,10 +46,6 @@ private:
py::object _on_flow_report; py::object _on_flow_report;
// 配置文件热更新回调 // 配置文件热更新回调
py::object _on_reload_config; py::object _on_reload_config;
// 媒体注册注销
py::object _on_media_changed;
// 拉流代理失败
py::object _on_player_proxy_failed;
}; };
} // namespace mediakit } // namespace mediakit

View File

@ -231,6 +231,10 @@ FFmpegFrame::FFmpegFrame(std::shared_ptr<AVFrame> frame) {
} }
FFmpegFrame::~FFmpegFrame() { FFmpegFrame::~FFmpegFrame() {
if (_data) {
delete[] _data;
_data = nullptr;
}
} }
AVFrame *FFmpegFrame::get() const { AVFrame *FFmpegFrame::get() const {
@ -238,9 +242,9 @@ AVFrame *FFmpegFrame::get() const {
} }
void FFmpegFrame::fillPicture(AVPixelFormat target_format, int target_width, int target_height) { void FFmpegFrame::fillPicture(AVPixelFormat target_format, int target_width, int target_height) {
auto buffer_size = av_image_get_buffer_size(target_format, target_width, target_height, 32); assert(_data == nullptr);
_data = std::unique_ptr<char[]>(new char[buffer_size]); _data = new char[av_image_get_buffer_size(target_format, target_width, target_height, 32)];
av_image_fill_arrays(_frame->data, _frame->linesize, (uint8_t *)_data.get(), target_format, target_width, target_height, 32); av_image_fill_arrays(_frame->data, _frame->linesize, (uint8_t *) _data, target_format, target_width, target_height, 32);
} }
int FFmpegFrame::getChannels() const { int FFmpegFrame::getChannels() const {
@ -252,14 +256,6 @@ int FFmpegFrame::getChannels() const {
#endif #endif
} }
// 资源池复用前调用
void FFmpegFrame::reset() {
_data.reset();
if (_frame) {
av_frame_unref(_frame.get()); // 清理AVFrame数据引用
}
}
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
template<bool decoder = true> template<bool decoder = true>
@ -738,7 +734,6 @@ FFmpegFrame::Ptr FFmpegSws::inputFrame(const FFmpegFrame::Ptr &frame, int &ret,
} }
if (_ctx) { if (_ctx) {
auto out = _sws_frame_pool.obtain2(); auto out = _sws_frame_pool.obtain2();
out->reset(); // 清理旧数据和帧引用
if (!out->get()->data[0]) { if (!out->get()->data[0]) {
if (data) { if (data) {
av_image_fill_arrays(out->get()->data, out->get()->linesize, data, _target_format, target_width, target_height, 32); av_image_fill_arrays(out->get()->data, out->get()->linesize, data, _target_format, target_width, target_height, 32);
@ -761,16 +756,45 @@ FFmpegFrame::Ptr FFmpegSws::inputFrame(const FFmpegFrame::Ptr &frame, int &ret,
return nullptr; return nullptr;
} }
std::tuple<bool, std::string> FFmpegUtils::saveFrame(const FFmpegFrame::Ptr &frame, const char *filename, AVPixelFormat fmt, int w, int h, const char *font_path) { std::tuple<bool, std::string> FFmpegUtils::saveFrame(const FFmpegFrame::Ptr &frame, const char *filename, AVPixelFormat fmt) {
std::shared_ptr<AVFilterGraph> _filter_graph;
AVFilterContext *buffersrc_ctx = nullptr;
AVFilterContext *buffersink_ctx = nullptr;
const AVFilter *buffersrc = nullptr;
const AVFilter *buffersink = nullptr;
// kServerName
const string mark = "ZLMediaKit";
char drawtext_args1[512];
_StrPrinter ss; _StrPrinter ss;
const AVCodec *jpeg_codec = avcodec_find_encoder(fmt == AV_PIX_FMT_YUVJ420P ? AV_CODEC_ID_MJPEG : AV_CODEC_ID_PNG);
std::unique_ptr<AVCodecContext, void (*)(AVCodecContext *)> jpeg_codec_ctx(
jpeg_codec ? avcodec_alloc_context3(jpeg_codec) : nullptr, [](AVCodecContext *ctx) { avcodec_free_context(&ctx); });
if (!jpeg_codec_ctx) {
ss << "Could not allocate JPEG/PNG codec context";
DebugL << ss;
return make_tuple<bool, std::string>(false, ss.data());
}
jpeg_codec_ctx->width = frame->get()->width;
jpeg_codec_ctx->height = frame->get()->height;
jpeg_codec_ctx->pix_fmt = fmt;
jpeg_codec_ctx->time_base = { 1, 1 };
auto ret = avcodec_open2(jpeg_codec_ctx.get(), jpeg_codec, NULL);
if (ret < 0) {
ss << "Could not open JPEG/PNG codec, " << ffmpeg_err(ret);
DebugL << ss;
return make_tuple<bool, std::string>(false, ss.data());
}
FFmpegSws sws(fmt, 0, 0);
auto new_frame = sws.inputFrame(frame);
if (!new_frame) {
ss << "Could not scale the frame: " << ffmpeg_err(ret);
DebugL << ss;
return make_tuple<bool, std::string>(false, ss.data());
}
auto pkt = alloc_av_packet();
ret = avcodec_send_frame(jpeg_codec_ctx.get(), new_frame->get());
if (ret < 0) {
ss << "Error sending a frame for encoding, " << ffmpeg_err(ret);
DebugL << ss;
return make_tuple<bool, std::string>(false, ss.data());
}
std::unique_ptr<FILE, void (*)(FILE *)> tmp_save_file_jpg(File::create_file(filename, "wb"), [](FILE *fp) { std::unique_ptr<FILE, void (*)(FILE *)> tmp_save_file_jpg(File::create_file(filename, "wb"), [](FILE *fp) {
if (fp) { if (fp) {
@ -784,104 +808,10 @@ std::tuple<bool, std::string> FFmpegUtils::saveFrame(const FFmpegFrame::Ptr &fra
return make_tuple<bool, std::string>(false, ss.data()); return make_tuple<bool, std::string>(false, ss.data());
} }
std::string fontfile(""); while (avcodec_receive_packet(jpeg_codec_ctx.get(), pkt.get()) == 0) {
if (font_path && File::fileExist(font_path)) { fwrite(pkt.get()->data, pkt.get()->size, 1, tmp_save_file_jpg.get());
fontfile = font_path;
} else {
// Fallback to common default
fontfile = exeDir() + "/DejaVuSans.ttf";
}
snprintf(drawtext_args1, sizeof(drawtext_args1), "text='%s':fontfile='%s':fontcolor=white@0.1:fontsize=h/50:x=w*0.02:y=h-th-h*0.02", mark.data(), fontfile.c_str());
const AVCodec *jpeg_codec = avcodec_find_encoder(fmt == AV_PIX_FMT_YUVJ420P ? AV_CODEC_ID_MJPEG : AV_CODEC_ID_PNG);
std::unique_ptr<AVCodecContext, void (*)(AVCodecContext *)> jpeg_codec_ctx(
jpeg_codec ? avcodec_alloc_context3(jpeg_codec) : nullptr, [](AVCodecContext *ctx) { avcodec_free_context(&ctx); });
if (!jpeg_codec_ctx) {
ss << "Could not allocate JPEG/PNG codec context";
DebugL << ss;
return make_tuple<bool, std::string>(false, ss.data());
}
jpeg_codec_ctx->width = (w > 0 && w < 8192) ? w : frame->get()->width;
jpeg_codec_ctx->height = (h > 0 && h < 4320) ? h : frame->get()->height;
jpeg_codec_ctx->pix_fmt = fmt;
jpeg_codec_ctx->time_base = { 1, 1 };
auto ret = avcodec_open2(jpeg_codec_ctx.get(), jpeg_codec, NULL);
if (ret < 0) {
ss << "Could not open JPEG/PNG codec, " << ffmpeg_err(ret);
DebugL << ss;
return make_tuple<bool, std::string>(false, ss.data());
}
FFmpegSws sws(fmt, jpeg_codec_ctx->width, jpeg_codec_ctx->height);
auto new_frame = sws.inputFrame(frame);
if (!new_frame) {
ss << "Could not scale the frame";
DebugL << ss;
return make_tuple<bool, std::string>(false, ss.data());
}
_filter_graph.reset(avfilter_graph_alloc(), [](AVFilterGraph *ctx) { avfilter_graph_free(&ctx); });
if (!_filter_graph) {
ss << "avfilter_graph_alloc failed";
DebugL << ss;
return make_tuple<bool, std::string>(false, ss.data());
}
char args[512];
snprintf(
args, sizeof(args), "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d", jpeg_codec_ctx->width, jpeg_codec_ctx->height,
jpeg_codec_ctx->pix_fmt, jpeg_codec_ctx->time_base.num, jpeg_codec_ctx->time_base.den, jpeg_codec_ctx->sample_aspect_ratio.num,
jpeg_codec_ctx->sample_aspect_ratio.den);
buffersrc = avfilter_get_by_name("buffer");
if ((ret = avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in", args, NULL, _filter_graph.get())) < 0) {
ss << "avfilter_graph_create_filter buffersrc failed: " << ret << " " << ffmpeg_err(ret);
DebugL << ss;
return make_tuple<bool, std::string>(false, ss.data());
}
buffersink = avfilter_get_by_name("buffersink");
if ((ret = avfilter_graph_create_filter(&buffersink_ctx, buffersink, "out", NULL, NULL, _filter_graph.get())) < 0) {
ss << "avfilter_graph_create_filter buffersink failed: " << ret << " " << ffmpeg_err(ret);
return make_tuple<bool, std::string>(false, ss.data());
}
AVFilterContext *drawtext_ctx1 = nullptr;
const AVFilter *drawtext_filter = avfilter_get_by_name("drawtext");
if ((ret = avfilter_graph_create_filter(&drawtext_ctx1, drawtext_filter, "drawtext", drawtext_args1, NULL, _filter_graph.get())) < 0) {
ss << "avfilter_graph_create_filter drawtext_filter failed: " << ret << " " << ffmpeg_err(ret);
return make_tuple<bool, std::string>(false, ss.data());
}
if ((ret = avfilter_link(buffersrc_ctx, 0, drawtext_ctx1, 0) < 0 || avfilter_link(drawtext_ctx1, 0, buffersink_ctx, 0))< 0) {
ss << "avfilter_link: " << ret << " " << ffmpeg_err(ret);
return make_tuple<bool, std::string>(false, ss.data());
}
if ((ret = avfilter_graph_config(_filter_graph.get(), NULL)) < 0) {
ss << "avfilter_graph_config failed: " << ret << " " << ffmpeg_err(ret);
return make_tuple<bool, std::string>(false, ss.data());
}
if ((ret = av_buffersrc_add_frame_flags(buffersrc_ctx, new_frame->get(), 0)) < 0) {
ss << "av_buffersink_get_frame failed: " << ret << " " << ffmpeg_err(ret);
return make_tuple<bool, std::string>(false, ss.data());
}
auto pkt = alloc_av_packet();
while (av_buffersink_get_frame(buffersink_ctx, new_frame->get()) >= 0) {
if (avcodec_send_frame(jpeg_codec_ctx.get(), new_frame->get()) == 0) {
while (avcodec_receive_packet(jpeg_codec_ctx.get(), pkt.get()) == 0) {
fwrite(pkt.get()->data, pkt.get()->size, 1, tmp_save_file_jpg.get());
}
}
} }
DebugL << "Screenshot successful: " << filename;
return make_tuple<bool, std::string>(true, ""); return make_tuple<bool, std::string>(true, "");
} }

View File

@ -27,9 +27,6 @@ extern "C" {
#include "libavutil/audio_fifo.h" #include "libavutil/audio_fifo.h"
#include "libavutil/imgutils.h" #include "libavutil/imgutils.h"
#include "libavutil/frame.h" #include "libavutil/frame.h"
#include "libavfilter/avfilter.h"
#include "libavfilter/buffersink.h"
#include "libavfilter/buffersrc.h"
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif
@ -48,10 +45,9 @@ public:
AVFrame *get() const; AVFrame *get() const;
void fillPicture(AVPixelFormat target_format, int target_width, int target_height); void fillPicture(AVPixelFormat target_format, int target_width, int target_height);
int getChannels() const; int getChannels() const;
void reset();
private: private:
std::unique_ptr<char[]> _data; char *_data = nullptr;
std::shared_ptr<AVFrame> _frame; std::shared_ptr<AVFrame> _frame;
}; };
@ -174,11 +170,9 @@ public:
* @param frame * @param frame
* @param filename * @param filename
* @param fmt jpg:AV_PIX_FMT_YUVJ420PPNG:AV_PIX_FMT_RGB24 * @param fmt jpg:AV_PIX_FMT_YUVJ420PPNG:AV_PIX_FMT_RGB24
* @param w h ()
* @param font_path (), default DejaVuSans.ttf
* @return * @return
*/ */
static std::tuple<bool, std::string> saveFrame(const FFmpegFrame::Ptr &frame, const char *filename, AVPixelFormat fmt = AV_PIX_FMT_YUVJ420P, int w = 0, int h = 0, const char *font_path = nullptr); static std::tuple<bool, std::string> saveFrame(const FFmpegFrame::Ptr &frame, const char *filename, AVPixelFormat fmt = AV_PIX_FMT_YUVJ420P);
}; };
}//namespace mediakit }//namespace mediakit

View File

@ -176,9 +176,7 @@ void MediaSink::checkTrackIfReady() {
} }
void MediaSink::addTrackCompleted() { void MediaSink::addTrackCompleted() {
if (!_track_map.empty()) { setMaxTrackCount(_track_map.size());
setMaxTrackCount(_track_map.size());
}
} }
void MediaSink::setMaxTrackCount(size_t i) { void MediaSink::setMaxTrackCount(size_t i) {

View File

@ -287,36 +287,6 @@ public:
// Maximum number of tracks // Maximum number of tracks
size_t max_track = 2; size_t max_track = 2;
#define OPT_VALUE(XX) \
XX(modify_stamp) \
XX(enable_audio) \
XX(add_mute_audio) \
XX(auto_close) \
XX(continue_push_ms) \
XX(paced_sender_ms) \
\
XX(enable_hls) \
XX(enable_hls_fmp4) \
XX(enable_mp4) \
XX(enable_rtsp) \
XX(enable_rtmp) \
XX(enable_ts) \
XX(enable_fmp4) \
\
XX(hls_demand) \
XX(rtsp_demand) \
XX(rtmp_demand) \
XX(ts_demand) \
XX(fmp4_demand) \
\
XX(mp4_max_second) \
XX(mp4_as_player) \
XX(mp4_save_path) \
\
XX(hls_save_path) \
XX(stream_replace) \
XX(max_track)
template <typename MAP> template <typename MAP>
ProtocolOption(const MAP &allArgs) : ProtocolOption() { ProtocolOption(const MAP &allArgs) : ProtocolOption() {
load(allArgs); load(allArgs);
@ -324,18 +294,35 @@ public:
template <typename MAP> template <typename MAP>
void load(const MAP &allArgs) { void load(const MAP &allArgs) {
#define GET(key) getArgsValue(allArgs, #key, key); #define GET_OPT_VALUE(key) getArgsValue(allArgs, #key, key)
OPT_VALUE(GET) GET_OPT_VALUE(modify_stamp);
#undef GET GET_OPT_VALUE(enable_audio);
} GET_OPT_VALUE(add_mute_audio);
GET_OPT_VALUE(auto_close);
GET_OPT_VALUE(continue_push_ms);
GET_OPT_VALUE(paced_sender_ms);
template <typename MAP> GET_OPT_VALUE(enable_hls);
MAP as() { GET_OPT_VALUE(enable_hls_fmp4);
MAP ret; GET_OPT_VALUE(enable_mp4);
#define SET(key) ret[#key] = key; GET_OPT_VALUE(enable_rtsp);
OPT_VALUE(SET) GET_OPT_VALUE(enable_rtmp);
#undef SET GET_OPT_VALUE(enable_ts);
return ret; GET_OPT_VALUE(enable_fmp4);
GET_OPT_VALUE(hls_demand);
GET_OPT_VALUE(rtsp_demand);
GET_OPT_VALUE(rtmp_demand);
GET_OPT_VALUE(ts_demand);
GET_OPT_VALUE(fmp4_demand);
GET_OPT_VALUE(mp4_max_second);
GET_OPT_VALUE(mp4_as_player);
GET_OPT_VALUE(mp4_save_path);
GET_OPT_VALUE(hls_save_path);
GET_OPT_VALUE(stream_replace);
GET_OPT_VALUE(max_track);
} }
}; };

View File

@ -401,20 +401,15 @@ bool MultiMediaSourceMuxer::setupRecord(Recorder::type type, bool start, const s
} }
} }
std::string MultiMediaSourceMuxer::startRecord(const std::string &file_path, int back_time_ms, int forward_time_ms) { std::string MultiMediaSourceMuxer::startRecord(const std::string &file_path, uint32_t back_time_ms, uint32_t forward_time_ms) {
#if !defined(ENABLE_MP4) #if !defined(ENABLE_MP4)
throw std::invalid_argument("mp4相关功能未打开请开启ENABLE_MP4宏后编译再测试"); throw std::invalid_argument("mp4相关功能未打开请开启ENABLE_MP4宏后编译再测试");
#else #else
if (!_ring) { if (!_ring) {
throw std::runtime_error("frame gop cache disabled, start record event video failed"); throw std::runtime_error("frame gop cache disabled, start record event video failed");
} }
std::string path; auto path = Recorder::getRecordPath(Recorder::type_mp4, _tuple, _option.mp4_save_path);
if (!start_with(file_path, "/")) { path += file_path;
path = Recorder::getRecordPath(Recorder::type_mp4, _tuple, _option.mp4_save_path);
path += file_path;
} else {
path = file_path;
}
TraceL << "mp4 save path: " << path; TraceL << "mp4 save path: " << path;
auto muxer = std::make_shared<MP4Muxer>(); auto muxer = std::make_shared<MP4Muxer>();
@ -424,124 +419,68 @@ std::string MultiMediaSourceMuxer::startRecord(const std::string &file_path, int
} }
muxer->addTrackCompleted(); muxer->addTrackCompleted();
bool have_history = false; std::list<Frame::Ptr> history;
if (back_time_ms > 0) { _ring->flushGop([&](const Frame::Ptr &frame) { history.emplace_back(frame); });
// 回溯录制 if (!history.empty()) {
std::list<Frame::Ptr> history; auto now_dts = history.back()->dts();
_ring->flushGop([&](const Frame::Ptr &frame) { history.emplace_back(frame); });
decltype(history)::iterator pos = history.end();
for (auto it = history.rbegin(); it != history.rend(); ++it) {
auto &frame = *it;
if (frame->getTrackType() != TrackVideo || (!frame->configFrame() && !frame->keyFrame())) {
continue;
}
// 如果视频关键帧到末尾的时长超过一定的时间,那前面的数据应该全部删除
if (frame->dts() + back_time_ms < now_dts) {
pos = it.base();
--pos;
break;
}
}
if (pos != history.end()) {
// 移除前面过多的数据
TraceL << "clear history video: " << history.front()->dts() << " -> " << (*pos)->dts();
history.erase(history.begin(), pos);
}
if (!history.empty()) { if (!history.empty()) {
auto now_dts = history.back()->dts(); auto &front = history.front();
InfoL << "start record: " << path
<< ", start_dts: " << front->dts() << ", key_frame: " << front->keyFrame() << ", config_frame: " << front->configFrame()
<< ", now_dts: " << now_dts;
}
decltype(history)::iterator pos = history.end(); for (auto &frame : history) {
for (auto it = history.rbegin(); it != history.rend(); ++it) { muxer->inputFrame(frame);
auto &frame = *it;
if (frame->getTrackType() != TrackVideo || (!frame->configFrame() && !frame->keyFrame())) {
continue;
}
// 如果视频关键帧到末尾的时长超过一定的时间,那前面的数据应该全部删除
if (frame->dts() + back_time_ms < now_dts) {
pos = it.base();
--pos;
break;
}
}
if (pos != history.end()) {
// 移除历史视频前面过多的数据
DebugL << "clear history front video: " << history.front()->dts() << " -> " << (*pos)->dts();
history.erase(history.begin(), pos);
}
if (forward_time_ms < 0) {
// 如果后向录制时长为负,说明回溯录制要截取一段尾部
pos = history.end();
for (auto it = history.rbegin(); it != history.rend(); ++it) {
auto &frame = *it;
if (frame->getTrackType() != TrackVideo) {
continue;
}
if (frame->dts() < now_dts + forward_time_ms) {
pos = it.base();
++pos;
break;
}
}
if (pos != history.end()) {
// 移除历史视频后面过多的数据
DebugL << "clear history tail video: " << (*pos)->dts() << " -> " << now_dts;
history.erase(pos, history.end());
}
}
if (!history.empty()) {
auto &front = history.front();
InfoL << "start record: " << path
<< ", start_dts: " << front->dts() << ", key_frame: " << front->keyFrame() << ", config_frame: " << front->configFrame()
<< ", now_dts: " << now_dts;
have_history = true;
}
for (auto &frame : history) {
muxer->inputFrame(frame);
}
} }
} }
if (forward_time_ms > 0) { auto reader = _ring->attach(MultiMediaSourceMuxer::getOwnerPoller(MediaSource::NullMediaSource()), false);
if (!have_history) { uint64_t now_dts = 0;
InfoL << "start record: " << path << ", back_time_ms: " << back_time_ms << ", forward_time_ms: " << forward_time_ms; int selected_index = -1;
Ticker ticker;
bool is_live_stream = _dur_sec < 0.01;
reader->setReadCB([muxer, now_dts, selected_index, forward_time_ms, reader, path, ticker, is_live_stream](const Frame::Ptr &frame) mutable {
// 循环引用自身
if (!now_dts) {
now_dts = frame->dts();
selected_index = frame->getIndex();
} }
// 新增兜底机制如果直播录制任务时长超过预期时间3秒不管数据时间戳是否增长是否达到预期都强制停止录制
weak_ptr<MultiMediaSourceMuxer> weak_self = shared_from_this(); if ((frame->getIndex() == selected_index && now_dts + forward_time_ms < frame->dts()) || (is_live_stream && ticker.createdTime() > forward_time_ms + 3000)) {
auto lam = [weak_self, muxer, forward_time_ms, have_history, path]() { InfoL << "stop record: " << path << ", end dts: " << frame->dts();
auto strong_self = weak_self.lock(); WorkThreadPool::Instance().getPoller()->async([muxer]() { muxer->closeMP4(); });
if (!strong_self) { reader = nullptr;
return; return;
}
uint64_t now_dts = 0;
int selected_index = -1;
Ticker ticker;
bool is_live_stream = strong_self->_dur_sec < 0.01;
auto reader = strong_self->_ring->attach(strong_self->MultiMediaSourceMuxer::getOwnerPoller(MediaSource::NullMediaSource()), !have_history, 1);
reader->setReadCB([muxer, now_dts, selected_index, forward_time_ms, reader, path, ticker, is_live_stream](const Frame::Ptr &frame) mutable {
if (!reader) {
// 已经关闭录制
return;
}
// 循环引用自身
if (!now_dts) {
now_dts = frame->dts();
selected_index = frame->getIndex();
}
// 新增兜底机制如果直播录制任务时长超过预期时间3秒不管数据时间戳是否增长是否达到预期都强制停止录制
if ((frame->getIndex() == selected_index && now_dts + forward_time_ms < frame->dts())
|| (is_live_stream && ticker.createdTime() > forward_time_ms + 3000ULL)) {
InfoL << "stop record: " << path << ", end dts: " << frame->dts();
WorkThreadPool::Instance().getPoller()->async([muxer]() { muxer->closeMP4(); });
reader = nullptr;
return;
}
muxer->inputFrame(frame);
});
std::weak_ptr<RingType::RingReader> weak_reader = reader;
reader->setDetachCB([weak_reader]() {
if (auto strong_reader = weak_reader.lock()) {
// 防止循环引用
strong_reader->setReadCB(nullptr);
}
});
};
if (back_time_ms >= 0) {
// 立即前向录制
lam();
} else {
// 延时启动录制
MultiMediaSourceMuxer::getOwnerPoller(MediaSource::NullMediaSource())->doDelayTask(-back_time_ms, [lam]() {
lam();
return 0;
});
} }
} muxer->inputFrame(frame);
});
std::weak_ptr<RingType::RingReader> weak_reader = reader;
reader->setDetachCB([weak_reader]() {
if (auto strong_reader = weak_reader.lock()) {
// 防止循环引用
strong_reader->setReadCB(nullptr);
}
});
return path; return path;
#endif #endif

View File

@ -129,7 +129,7 @@ public:
* @param forward_time_ms * @param forward_time_ms
* @return * @return
*/ */
std::string startRecord(const std::string &file_path, int back_time_ms, int forward_time_ms); std::string startRecord(const std::string &file_path, uint32_t back_time_ms, uint32_t forward_time_ms);
/** /**
* *

View File

@ -81,7 +81,6 @@ const string kBroadcastRtcSctpClosed = "kBroadcastRtcSctpClosed";
const string kBroadcastRtcSctpSend = "kBroadcastRtcSctpSend"; const string kBroadcastRtcSctpSend = "kBroadcastRtcSctpSend";
const string kBroadcastRtcSctpReceived = "kBroadcastRtcSctpReceived"; const string kBroadcastRtcSctpReceived = "kBroadcastRtcSctpReceived";
const string kBroadcastPlayerCountChanged = "kBroadcastPlayerCountChanged"; const string kBroadcastPlayerCountChanged = "kBroadcastPlayerCountChanged";
const string kBroadcastPlayerProxyFailed = "kBroadcastPlayerProxyFailed";
} // namespace Broadcast } // namespace Broadcast

View File

@ -161,9 +161,6 @@ extern const std::string kBroadcastRtcSctpReceived;
extern const std::string kBroadcastPlayerCountChanged; extern const std::string kBroadcastPlayerCountChanged;
#define BroadcastPlayerCountChangedArgs const MediaTuple& args, const int& count #define BroadcastPlayerCountChangedArgs const MediaTuple& args, const int& count
extern const std::string kBroadcastPlayerProxyFailed;
#define BroadcastPlayerProxyFailedArgs const PlayerProxy& sender, const toolkit::SockException &ex
#define ReloadConfigTag ((void *)(0xFF)) #define ReloadConfigTag ((void *)(0xFF))
#define RELOAD_KEY(arg, key) \ #define RELOAD_KEY(arg, key) \
do { \ do { \

View File

@ -110,9 +110,7 @@ void PlayerProxy::play(const string &strUrlTmp) {
if (!strongSelf) { if (!strongSelf) {
return; return;
} }
if (err) {
NOTICE_EMIT(BroadcastPlayerProxyFailedArgs, Broadcast::kBroadcastPlayerProxyFailed, *strongSelf, err);
}
if (strongSelf->_on_play) { if (strongSelf->_on_play) {
strongSelf->_on_play(err); strongSelf->_on_play(err);
strongSelf->_on_play = nullptr; strongSelf->_on_play = nullptr;
@ -148,9 +146,6 @@ void PlayerProxy::play(const string &strUrlTmp) {
if (!strongSelf) { if (!strongSelf) {
return; return;
} }
if (err) {
NOTICE_EMIT(BroadcastPlayerProxyFailedArgs, Broadcast::kBroadcastPlayerProxyFailed, *strongSelf, err);
}
// 注销直接拉流代理产生的流:#532 [AUTO-TRANSLATED:c6343a3b] // 注销直接拉流代理产生的流:#532 [AUTO-TRANSLATED:c6343a3b]
// Unregister the stream generated by the direct stream proxy: #532 // Unregister the stream generated by the direct stream proxy: #532

View File

@ -18,7 +18,8 @@
namespace mediakit { namespace mediakit {
struct StreamInfo { struct StreamInfo
{
TrackType codec_type; TrackType codec_type;
std::string codec_name; std::string codec_name;
int bitrate; int bitrate;
@ -29,7 +30,8 @@ struct StreamInfo {
int video_height; int video_height;
float video_fps; float video_fps;
StreamInfo() { StreamInfo()
{
codec_type = TrackInvalid; codec_type = TrackInvalid;
codec_name = "none"; codec_name = "none";
bitrate = -1; bitrate = -1;
@ -42,12 +44,14 @@ struct StreamInfo {
} }
}; };
struct TranslationInfo { struct TranslationInfo
{
std::vector<StreamInfo> stream_info; std::vector<StreamInfo> stream_info;
int byte_speed; int byte_speed;
uint64_t start_time_stamp; uint64_t start_time_stamp;
TranslationInfo() { TranslationInfo()
{
byte_speed = -1; byte_speed = -1;
start_time_stamp = 0; start_time_stamp = 0;
} }

View File

@ -115,13 +115,7 @@ void PusherProxy::rePublish(const string &dst_url, int failed_cnt) {
return false; return false;
} }
WarnL << "推流重试[" << failed_cnt << "]:" << dst_url; WarnL << "推流重试[" << failed_cnt << "]:" << dst_url;
try { strong_self->MediaPusher::publish(dst_url);
strong_self->MediaPusher::publish(dst_url);
} catch (std::exception &e) {
WarnL << e.what();
// 回调推流失败,一般是媒体注销了
strong_self->_on_close(SockException(Err_other, e.what()));
}
return false; return false;
}, },
getPoller()); getPoller());

View File

@ -19,11 +19,7 @@ using namespace toolkit;
namespace mediakit { namespace mediakit {
MP4Muxer::~MP4Muxer() { MP4Muxer::~MP4Muxer() {
try { closeMP4();
closeMP4();
} catch (std::exception &e) {
WarnL << e.what();
}
} }
void MP4Muxer::openMP4(const string &file) { void MP4Muxer::openMP4(const string &file) {

View File

@ -56,33 +56,30 @@ AudioMeta::AudioMeta(const AudioTrack::Ptr &audio) {
} }
uint8_t getCodecFlags(CodecId cid) { uint8_t getCodecFlags(CodecId cid) {
switch (cid) { switch(cid) {
#define XX(a, b, c) \ #define XX(a, b, c) case a: return static_cast<uint8_t>(b);
case a: return static_cast<uint8_t>(b); RTMP_CODEC_MAP(XX)
RTMP_CODEC_MAP(XX)
#undef XX #undef XX
default: return 0;
} }
return 0;
} }
uint32_t getCodecFourCC(CodecId cid) { uint32_t getCodecFourCC(CodecId cid) {
switch (cid) { switch(cid) {
#define XX(a, b, c) \ #define XX(a, b, c) case a: return static_cast<uint32_t>(c);
case a: return static_cast<uint32_t>(c); RTMP_CODEC_MAP(XX)
RTMP_CODEC_MAP(XX)
#undef XX #undef XX
default: return 0;
} }
return 0;
} }
CodecId getFourccCodec(uint32_t id) { CodecId getFourccCodec(uint32_t id) {
switch (id) { switch(id) {
#define XX(a, b, c) \ #define XX(a, b, c) case (uint32_t)c: return a;
case (uint32_t)c: return a; RTMP_CODEC_MAP(XX)
RTMP_CODEC_MAP(XX)
#undef XX #undef XX
default: return CodecInvalid;
} }
return CodecInvalid;
} }
uint8_t getAudioRtmpFlags(const Track::Ptr &track) { uint8_t getAudioRtmpFlags(const Track::Ptr &track) {
@ -198,10 +195,12 @@ bool RtmpPacket::isConfigFrame() const {
switch (type_id) { switch (type_id) {
case MSG_AUDIO: { case MSG_AUDIO: {
switch ((RtmpAudioCodec)getRtmpCodecId()) { switch ((RtmpAudioCodec)getRtmpCodecId()) {
case RtmpAudioCodec::aac: return (RtmpAACPacketType)buffer[1] == RtmpAACPacketType::aac_config_header; case RtmpAudioCodec::aac:
case RtmpAudioCodec::ex_header: return (RtmpPacketType)(buffer[0] & 0x0f) == RtmpPacketType::PacketTypeSequenceStart; return (RtmpAACPacketType)buffer[1] == RtmpAACPacketType::aac_config_header;
default: return false; case RtmpAudioCodec::ex_header:
return (RtmpPacketType)(buffer[0] & 0x0f) == RtmpPacketType::PacketTypeSequenceStart;
} }
return false;
} }
case MSG_VIDEO: { case MSG_VIDEO: {
if (!isVideoKeyFrame()) { if (!isVideoKeyFrame()) {

View File

@ -26,35 +26,11 @@ using namespace toolkit;
#define S2_FMS_KEY_SIZE 68 #define S2_FMS_KEY_SIZE 68
#define C1_OFFSET_SIZE 4 #define C1_OFFSET_SIZE 4
#ifdef ENABLE_OPENSSL #ifdef ENABLE_OPENSSL
#include "Util/SSLBox.h" #include "Util/SSLBox.h"
#include <openssl/hmac.h> #include <openssl/hmac.h>
#include <openssl/opensslv.h> #include <openssl/opensslv.h>
static uint8_t FMSKey[] = {
0x47, 0x65, 0x6e, 0x75, 0x69, 0x6e, 0x65, 0x20,
0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x46, 0x6c,
0x61, 0x73, 0x68, 0x20, 0x4d, 0x65, 0x64, 0x69,
0x61, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72,
0x20, 0x30, 0x30, 0x31, // Genuine Adobe Flash Media Server 001
0xf0, 0xee, 0xc2, 0x4a, 0x80, 0x68, 0xbe, 0xe8,
0x2e, 0x00, 0xd0, 0xd1, 0x02, 0x9e, 0x7e, 0x57,
0x6e, 0xec, 0x5d, 0x2d, 0x29, 0x80, 0x6f, 0xab,
0x93, 0xb8, 0xe6, 0x36, 0xcf, 0xeb, 0x31, 0xae
}; // 68
static uint8_t FPKey[] = {
0x47, 0x65, 0x6E, 0x75, 0x69, 0x6E, 0x65, 0x20,
0x41, 0x64, 0x6F, 0x62, 0x65, 0x20, 0x46, 0x6C,
0x61, 0x73, 0x68, 0x20, 0x50, 0x6C, 0x61, 0x79,
0x65, 0x72, 0x20, 0x30, 0x30, 0x31, // Genuine Adobe Flash Player 001
0xF0, 0xEE, 0xC2, 0x4A, 0x80, 0x68, 0xBE, 0xE8,
0x2E, 0x00, 0xD0, 0xD1, 0x02, 0x9E, 0x7E, 0x57,
0x6E, 0xEC, 0x5D, 0x2D, 0x29, 0x80, 0x6F, 0xAB,
0x93, 0xB8, 0xE6, 0x36, 0xCF, 0xEB, 0x31, 0xAE
}; // 62
static string openssl_HMACsha256(const void *key, size_t key_len, const void *data, size_t data_len){ static string openssl_HMACsha256(const void *key, size_t key_len, const void *data, size_t data_len){
std::shared_ptr<char> out(new char[32], [](char *ptr) { delete[] ptr; }); std::shared_ptr<char> out(new char[32], [](char *ptr) { delete[] ptr; });
unsigned int out_len; unsigned int out_len;
@ -353,16 +329,8 @@ const char* RtmpProtocol::handle_S0S1S2(const char *data, size_t len, const func
} }
// 发送 C2 [AUTO-TRANSLATED:e51c339e] // 发送 C2 [AUTO-TRANSLATED:e51c339e]
// Send C2 // Send C2
uint8_t *pS1 = (uint8_t*)data + 1; const char *pcC2 = data + 1;
RtmpHandshake c2(0); onSendRawData(obtainBuffer(pcC2, C1_HANDSHARK_SIZE));
memcpy(&c2, pS1, sizeof(c2));
#ifdef ENABLE_OPENSSL
if(pS1[4] >=3){ // 复杂握手计算c2
handle_S1_complex((char*)pS1, c2);
}
#endif
onSendRawData(obtainBuffer(&c2, C1_HANDSHARK_SIZE));
// 握手结束 [AUTO-TRANSLATED:9df763ff] // 握手结束 [AUTO-TRANSLATED:9df763ff]
// Handshake finished // Handshake finished
_next_step_func = [this](const char *data, size_t len) { _next_step_func = [this](const char *data, size_t len) {
@ -440,7 +408,7 @@ void RtmpProtocol::handle_C1_complex(const char *data){
check_C1_Digest(digest, c1_joined); check_C1_Digest(digest, c1_joined);
send_complex_S0S1S2(0, digest); send_complex_S0S1S2(0, digest);
// InfoL << "schema0"; // InfoL << "schema0";
} catch (std::exception &) { } catch (std::exception &) {
// 貌似flash从来都不用schema1 [AUTO-TRANSLATED:2c6d140f] // 貌似flash从来都不用schema1 [AUTO-TRANSLATED:2c6d140f]
// It seems that flash never uses schema1 // It seems that flash never uses schema1
@ -458,70 +426,40 @@ void RtmpProtocol::handle_C1_complex(const char *data){
check_C1_Digest(digest, c1_joined); check_C1_Digest(digest, c1_joined);
send_complex_S0S1S2(1, digest); send_complex_S0S1S2(1, digest);
// InfoL << "schema1"; // InfoL << "schema1";
} catch (std::exception &) { } catch (std::exception &) {
//WarnL << "try rtmp complex schema1 failed:" << ex.what(); // WarnL << "try rtmp complex schema1 failed:" << ex.what();
handle_C1_simple(data); handle_C1_simple(data);
} }
} }
} }
void RtmpProtocol::check_S1_Digest(const std::string &digest,const std::string &data){ #if !defined(u_int8_t)
auto sha256 = openssl_HMACsha256(FMSKey, S1_FMS_KEY_SIZE, data.data(), data.size()); #define u_int8_t unsigned char
if (sha256 != digest) { #endif // !defined(u_int8_t)
throw std::runtime_error("digest mismatched");
} else {
InfoL << "check rtmp complex handshark success!";
}
}
void RtmpProtocol::handle_S1_complex(const char *data,RtmpHandshake &c2){ static u_int8_t FMSKey[] = {
0x47, 0x65, 0x6e, 0x75, 0x69, 0x6e, 0x65, 0x20,
0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x46, 0x6c,
0x61, 0x73, 0x68, 0x20, 0x4d, 0x65, 0x64, 0x69,
0x61, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72,
0x20, 0x30, 0x30, 0x31, // Genuine Adobe Flash Media Server 001
0xf0, 0xee, 0xc2, 0x4a, 0x80, 0x68, 0xbe, 0xe8,
0x2e, 0x00, 0xd0, 0xd1, 0x02, 0x9e, 0x7e, 0x57,
0x6e, 0xec, 0x5d, 0x2d, 0x29, 0x80, 0x6f, 0xab,
0x93, 0xb8, 0xe6, 0x36, 0xcf, 0xeb, 0x31, 0xae
}; // 68
const char *s1_start = data; static u_int8_t FPKey[] = {
const char *schema_start = s1_start + 8; 0x47, 0x65, 0x6E, 0x75, 0x69, 0x6E, 0x65, 0x20,
char *digest_start; 0x41, 0x64, 0x6F, 0x62, 0x65, 0x20, 0x46, 0x6C,
std::string digest; 0x61, 0x73, 0x68, 0x20, 0x50, 0x6C, 0x61, 0x79,
try { 0x65, 0x72, 0x20, 0x30, 0x30, 0x31, // Genuine Adobe Flash Player 001
/* c1s1 schema0 0xF0, 0xEE, 0xC2, 0x4A, 0x80, 0x68, 0xBE, 0xE8,
time: 4bytes 0x2E, 0x00, 0xD0, 0xD1, 0x02, 0x9E, 0x7E, 0x57,
version: 4bytes 0x6E, 0xEC, 0x5D, 0x2D, 0x29, 0x80, 0x6F, 0xAB,
key: 764bytes 0x93, 0xB8, 0xE6, 0x36, 0xCF, 0xEB, 0x31, 0xAE
digest: 764bytes }; // 62
*/
digest = get_C1_digest((uint8_t *) schema_start + C1_SCHEMA_SIZE, &digest_start);
string s1_joined(s1_start, C1_HANDSHARK_SIZE);
s1_joined.erase(digest_start - s1_start, C1_DIGEST_SIZE);
check_S1_Digest(digest, s1_joined);
//InfoL << "schema0";
} catch (std::exception &ex) {
// 貌似flash从来都不用schema1 [AUTO-TRANSLATED:2c6d140f]
// It seems that flash never uses schema1
//WarnL << "try rtmp complex schema0 failed:" << ex.what();
try {
/* c1s1 schema1
time: 4bytes
version: 4bytes
digest: 764bytes
key: 764bytes
*/
digest = get_C1_digest((uint8_t *) schema_start, &digest_start);
string s1_joined(s1_start, C1_HANDSHARK_SIZE);
s1_joined.erase(digest_start - s1_start, C1_DIGEST_SIZE);
check_S1_Digest(digest, s1_joined);
//send_complex_S0S1S2(1, digest);
//InfoL << "schema1";
} catch (std::exception &ex) {
WarnL << "try rtmp complex schema1 failed:" << ex.what();
return;
}
}
//InfoL << "send complex C2";
auto c2_key = openssl_HMACsha256(FPKey, sizeof(FPKey), digest.data(), digest.size());
std::string c2_str((char*)(&c2), sizeof(c2)- C1_DIGEST_SIZE);
auto c2_digest = openssl_HMACsha256(c2_key.data(), c2_key.size(), c2_str.data(), c2_str.size());
memcpy(c2.random + RANDOM_LEN - C1_DIGEST_SIZE, c2_digest.data(), C1_DIGEST_SIZE);
}
void RtmpProtocol::check_C1_Digest(const string &digest,const string &data){ void RtmpProtocol::check_C1_Digest(const string &digest,const string &data){
auto sha256 = openssl_HMACsha256(FPKey, C1_FPKEY_SIZE, data.data(), data.size()); auto sha256 = openssl_HMACsha256(FPKey, C1_FPKEY_SIZE, data.data(), data.size());

View File

@ -63,16 +63,14 @@ protected:
void sendRtmp(uint8_t type, uint32_t stream_index, const std::string &buffer, uint32_t stamp, int chunk_id); void sendRtmp(uint8_t type, uint32_t stream_index, const std::string &buffer, uint32_t stamp, int chunk_id);
void sendRtmp(uint8_t type, uint32_t stream_index, const toolkit::Buffer::Ptr &buffer, uint32_t stamp, int chunk_id); void sendRtmp(uint8_t type, uint32_t stream_index, const toolkit::Buffer::Ptr &buffer, uint32_t stamp, int chunk_id);
toolkit::BufferRaw::Ptr obtainBuffer(const void *data = nullptr, size_t len = 0); toolkit::BufferRaw::Ptr obtainBuffer(const void *data = nullptr, size_t len = 0);
private: private:
void handle_C1_simple(const char *data); void handle_C1_simple(const char *data);
#ifdef ENABLE_OPENSSL #ifdef ENABLE_OPENSSL
void handle_S1_complex(const char *data, RtmpHandshake &c2);
void handle_C1_complex(const char *data); void handle_C1_complex(const char *data);
std::string get_C1_digest(const uint8_t *ptr,char **digestPos); std::string get_C1_digest(const uint8_t *ptr,char **digestPos);
std::string get_C1_key(const uint8_t *ptr); std::string get_C1_key(const uint8_t *ptr);
void check_C1_Digest(const std::string &digest,const std::string &data); void check_C1_Digest(const std::string &digest,const std::string &data);
void check_S1_Digest(const std::string &digest,const std::string &data);
void send_complex_S0S1S2(int schemeType,const std::string &digest); void send_complex_S0S1S2(int schemeType,const std::string &digest);
#endif //ENABLE_OPENSSL #endif //ENABLE_OPENSSL

View File

@ -446,11 +446,7 @@ void RtspPlayer::sendOptions() {
} }
void RtspPlayer::sendKeepAlive() { void RtspPlayer::sendKeepAlive() {
if (_play_check_timer) _on_response = [](const Parser &parser) {};
{
WarnL << "receive RTP packet before handleResPAUSE";
}
_on_keepalive_reponse = [](const Parser &parser) {};
if (_supported_cmd.find("GET_PARAMETER") != _supported_cmd.end()) { if (_supported_cmd.find("GET_PARAMETER") != _supported_cmd.end()) {
// 支持GET_PARAMETER用此命令保活 [AUTO-TRANSLATED:b45cd737] // 支持GET_PARAMETER用此命令保活 [AUTO-TRANSLATED:b45cd737]
// Support GET_PARAMETER, use this command to keep alive // Support GET_PARAMETER, use this command to keep alive
@ -536,10 +532,6 @@ void RtspPlayer::onWholeRtspPacket(Parser &parser) {
try { try {
decltype(_on_response) func; decltype(_on_response) func;
_on_response.swap(func); _on_response.swap(func);
if (!func)
{
_on_keepalive_reponse.swap(func);
}
if (func) { if (func) {
func(parser); func(parser);
} }

View File

@ -162,7 +162,6 @@ private:
float _speed = 0.0f; float _speed = 0.0f;
std::vector<SdpTrack::Ptr> _sdp_track; std::vector<SdpTrack::Ptr> _sdp_track;
std::function<void(const Parser&)> _on_response; std::function<void(const Parser&)> _on_response;
std::function<void(const Parser&)> _on_keepalive_reponse;
protected: protected:
// RTP端口,trackid idx 为数组下标 [AUTO-TRANSLATED:77c186bb] // RTP端口,trackid idx 为数组下标 [AUTO-TRANSLATED:77c186bb]
// RTP port, trackid idx is the array subscript // RTP port, trackid idx is the array subscript

View File

@ -64,6 +64,7 @@ public:
private: private:
MultiMediaSourceMuxer::Ptr _muxer; MultiMediaSourceMuxer::Ptr _muxer;
uint64_t timeStamp = 0;
uint64_t timeStamp_last = 0; uint64_t timeStamp_last = 0;
}; };

View File

@ -83,10 +83,6 @@ struct sniff_tcp {
#define TH_URG 0x20 #define TH_URG 0x20
#define TH_ECE 0x40 #define TH_ECE 0x40
#define TH_CWR 0x80 #define TH_CWR 0x80
#if defined(TH_FLAGS)
#undef TH_FLAGS
#endif
#define TH_FLAGS (TH_FINTH_SYNTH_RSTTH_ACKTH_URGTH_ECETH_CWR) #define TH_FLAGS (TH_FINTH_SYNTH_RSTTH_ACKTH_URGTH_ECETH_CWR)
u_short th_win; /* TCP滑动窗口 */ u_short th_win; /* TCP滑动窗口 */
u_short th_sum; /* 头部校验和 */ u_short th_sum; /* 头部校验和 */
@ -158,7 +154,7 @@ static bool loadFile(const char *path, const EventPoller::Ptr &poller) {
return false; return false;
} }
auto total_size = std::make_shared<size_t>(0); auto total_size = std::make_shared<size_t>(0);
struct pcap_pkthdr header {}; struct pcap_pkthdr header = {0};
while (true) { while (true) {
const u_char *pkt_buff = pcap_next(handle.get(), &header); const u_char *pkt_buff = pcap_next(handle.get(), &header);
if (!pkt_buff) { if (!pkt_buff) {

View File

@ -1175,7 +1175,7 @@ void IceAgent::connectivityCheck(const Pair::Ptr &pair, CandidateTuple& candidat
} }
void IceAgent::tryTriggerredCheck(const Pair::Ptr& pair) { void IceAgent::tryTriggerredCheck(const Pair::Ptr& pair) {
// DebugL; DebugL;
//FIXME 暂不实现,因为当前实现基本收到candidate就会发起check //FIXME 暂不实现,因为当前实现基本收到candidate就会发起check
} }

View File

@ -46,7 +46,6 @@ const string kNackIntervalRatio = RTC_FIELD "nackIntervalRatio";
// nack包中rtp个数减小此值可以让nack包响应更灵敏 [AUTO-TRANSLATED:12393868] // nack包中rtp个数减小此值可以让nack包响应更灵敏 [AUTO-TRANSLATED:12393868]
// Number of rtp in nack packet, reducing this value can make nack packet response more sensitive // Number of rtp in nack packet, reducing this value can make nack packet response more sensitive
const string kNackRtpSize = RTC_FIELD "nackRtpSize"; const string kNackRtpSize = RTC_FIELD "nackRtpSize";
const string kNackAudioRtpSize = RTC_FIELD "nackAudioRtpSize";
static onceToken token([]() { static onceToken token([]() {
mINI::Instance()[kMaxRtpCacheMS] = 5 * 1000; mINI::Instance()[kMaxRtpCacheMS] = 5 * 1000;
@ -56,7 +55,6 @@ static onceToken token([]() {
mINI::Instance()[kNackMaxCount] = 15; mINI::Instance()[kNackMaxCount] = 15;
mINI::Instance()[kNackIntervalRatio] = 1.0f; mINI::Instance()[kNackIntervalRatio] = 1.0f;
mINI::Instance()[kNackRtpSize] = 8; mINI::Instance()[kNackRtpSize] = 8;
mINI::Instance()[kNackAudioRtpSize] = 4;
}); });
} // namespace Rtc } // namespace Rtc
@ -157,8 +155,7 @@ int64_t NackList::getNtpStamp(uint16_t seq) {
//////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////
NackContext::NackContext(TrackType type) { NackContext::NackContext() {
_type = type;
setOnNack(nullptr); setOnNack(nullptr);
} }
@ -220,9 +217,7 @@ void NackContext::makeNack(uint16_t max_seq, bool flush) {
// 最多生成5个nack包防止seq大幅跳跃导致一直循环 [AUTO-TRANSLATED:9cc5da25] // 最多生成5个nack包防止seq大幅跳跃导致一直循环 [AUTO-TRANSLATED:9cc5da25]
// Generate at most 5 nack packets to prevent seq from jumping significantly and causing continuous loops // Generate at most 5 nack packets to prevent seq from jumping significantly and causing continuous loops
auto max_nack = 5u; auto max_nack = 5u;
GET_CONFIG(uint32_t, nack_video_rtpsize, Rtc::kNackRtpSize); GET_CONFIG(uint32_t, nack_rtpsize, Rtc::kNackRtpSize);
GET_CONFIG(uint32_t, nack_audio_rtpsize, Rtc::kNackAudioRtpSize);
auto nack_rtpsize = _type == TrackVideo ? nack_video_rtpsize : nack_audio_rtpsize;
// kNackRtpSize must between 0 and 16 // kNackRtpSize must between 0 and 16
nack_rtpsize = std::min<uint32_t>(nack_rtpsize, FCI_NACK::kBitSize); nack_rtpsize = std::min<uint32_t>(nack_rtpsize, FCI_NACK::kBitSize);
while (_nack_seq != max_seq && max_nack--) { while (_nack_seq != max_seq && max_nack--) {

View File

@ -55,7 +55,7 @@ public:
using Ptr = std::shared_ptr<NackContext>; using Ptr = std::shared_ptr<NackContext>;
using onNack = std::function<void(const FCI_NACK &nack)>; using onNack = std::function<void(const FCI_NACK &nack)>;
NackContext(TrackType type = TrackVideo); NackContext();
void received(uint16_t seq, bool is_rtx = false); void received(uint16_t seq, bool is_rtx = false);
void setOnNack(onNack cb); void setOnNack(onNack cb);
@ -71,7 +71,6 @@ private:
private: private:
bool _started = false; bool _started = false;
int _rtt = 50; int _rtt = 50;
TrackType _type;
onNack _cb; onNack _cb;
std::set<uint16_t> _seq; std::set<uint16_t> _seq;
// 最新nack包中的rtp seq值 [AUTO-TRANSLATED:6984d95a] // 最新nack包中的rtp seq值 [AUTO-TRANSLATED:6984d95a]

View File

@ -204,16 +204,11 @@ SrtpSession::SrtpSession(Type type, CryptoSuite cryptoSuite, uint8_t *key, size_
policy.key = key; policy.key = key;
// Required for sending RTP retransmission without RTX. // Required for sending RTP retransmission without RTX.
policy.allow_repeat_tx = 1; policy.allow_repeat_tx = 1;
#if 0
if (type == Type::OUTBOUND) { if (type == Type::OUTBOUND) {
policy.window_size = 0x8000 - 1; policy.window_size = 0x8000 - 1;
} else { } else {
policy.window_size = 1024; policy.window_size = 1024;
} }
#else
// TODO 关闭防重放攻击
policy.window_size = 0x8000 - 1;
#endif
policy.next = nullptr; policy.next = nullptr;
// Set the SRTP session. // Set the SRTP session.

View File

@ -69,11 +69,10 @@ void WebRtcSession::onRecv_l(const char *data, size_t len) {
auto sock = Socket::createSocket(transport->getPoller(), false); auto sock = Socket::createSocket(transport->getPoller(), false);
// 1、克隆socket(fd不变)切换poller线程到WebRtcTransport所在线程 [AUTO-TRANSLATED:f930bfab] // 1、克隆socket(fd不变)切换poller线程到WebRtcTransport所在线程 [AUTO-TRANSLATED:f930bfab]
// 1. Clone socket (fd remains unchanged), switch poller thread to the thread where WebRtcTransport is located // 1. Clone socket (fd remains unchanged), switch poller thread to the thread where WebRtcTransport is located
auto on_complete = sock->cloneSocket(*(getSock())); sock->cloneSocket(*(getSock()));
auto server = _server; auto server = _server;
std::string str(data, len); std::string str(data, len);
// on_complete在创建WebRtcSession后才析构(才开始网络事件监听) sock->getPoller()->async([sock, server, str](){
sock->getPoller()->async([sock, server, str, on_complete](){
auto strong_server = server.lock(); auto strong_server = server.lock();
if (strong_server) { if (strong_server) {
auto session = static_pointer_cast<WebRtcSession>(strong_server->createSession(sock)); auto session = static_pointer_cast<WebRtcSession>(strong_server->createSession(sock));

View File

@ -82,7 +82,6 @@ const string kMinBitrate = RTC_FIELD "min_bitrate";
// 数据通道设置 [AUTO-TRANSLATED:2dc48bc3] // 数据通道设置 [AUTO-TRANSLATED:2dc48bc3]
// Data channel setting // Data channel setting
const string kDataChannelEcho = RTC_FIELD "datachannel_echo"; const string kDataChannelEcho = RTC_FIELD "datachannel_echo";
const string kPreferredTcp = RTC_FIELD "preferred_tcp";
static onceToken token([]() { static onceToken token([]() {
mINI::Instance()[kTimeOutSec] = 15; mINI::Instance()[kTimeOutSec] = 15;
@ -106,7 +105,6 @@ static onceToken token([]() {
mINI::Instance()[kIceTransportPolicy] = 0; // 默认值:不限制(kAll) mINI::Instance()[kIceTransportPolicy] = 0; // 默认值:不限制(kAll)
mINI::Instance()[kIceUfrag] = "ZLMediaKit"; mINI::Instance()[kIceUfrag] = "ZLMediaKit";
mINI::Instance()[kIcePwd] = "ZLMediaKit"; mINI::Instance()[kIcePwd] = "ZLMediaKit";
mINI::Instance()[kPreferredTcp] = 0;
}); });
} // namespace Rtc } // namespace Rtc
@ -1104,7 +1102,7 @@ void WebRtcTransportImp::setIceCandidate(vector<SdpAttrCandidate> cands) {
class RtpChannel : public RtpTrackImp, public std::enable_shared_from_this<RtpChannel> { class RtpChannel : public RtpTrackImp, public std::enable_shared_from_this<RtpChannel> {
public: public:
RtpChannel(TrackType type, EventPoller::Ptr poller, RtpTrackImp::OnSorted cb, function<void(const FCI_NACK &nack)> on_nack) : _nack_ctx(type){ RtpChannel(EventPoller::Ptr poller, RtpTrackImp::OnSorted cb, function<void(const FCI_NACK &nack)> on_nack) {
_poller = std::move(poller); _poller = std::move(poller);
_on_nack = std::move(on_nack); _on_nack = std::move(on_nack);
setOnSorted(std::move(cb)); setOnSorted(std::move(cb));
@ -1316,7 +1314,7 @@ void WebRtcTransportImp::createRtpChannel(const string &rid, uint32_t ssrc, Medi
// rid --> RtpReceiverImp // rid --> RtpReceiverImp
auto &ref = track.rtp_channel[rid]; auto &ref = track.rtp_channel[rid];
weak_ptr<WebRtcTransportImp> weak_self = static_pointer_cast<WebRtcTransportImp>(shared_from_this()); weak_ptr<WebRtcTransportImp> weak_self = static_pointer_cast<WebRtcTransportImp>(shared_from_this());
ref = std::make_shared<RtpChannel>(track.media->type, ref = std::make_shared<RtpChannel>(
getPoller(), [&track, this, rid](RtpPacket::Ptr rtp) mutable { onSortedRtp(track, rid, std::move(rtp)); }, getPoller(), [&track, this, rid](RtpPacket::Ptr rtp) mutable { onSortedRtp(track, rid, std::move(rtp)); },
[&track, weak_self, ssrc](const FCI_NACK &nack) mutable { [&track, weak_self, ssrc](const FCI_NACK &nack) mutable {
// nack发送可能由定时器异步触发 [AUTO-TRANSLATED:186d6723] // nack发送可能由定时器异步触发 [AUTO-TRANSLATED:186d6723]
@ -1633,7 +1631,6 @@ WebRtcPluginManager &WebRtcPluginManager::Instance() {
} }
void WebRtcPluginManager::registerPlugin(const string &type, Plugin cb) { void WebRtcPluginManager::registerPlugin(const string &type, Plugin cb) {
InfoL << "Load webrtc plugin:" << type;
lock_guard<mutex> lck(_mtx_creator); lock_guard<mutex> lck(_mtx_creator);
_map_creator[type] = std::move(cb); _map_creator[type] = std::move(cb);
} }
@ -1670,7 +1667,6 @@ void echo_plugin(SocketHelper& sender, const WebRtcArgs &args, const onCreateWeb
cb(*WebRtcEchoTest::create(EventPollerPool::Instance().getPoller())); cb(*WebRtcEchoTest::create(EventPollerPool::Instance().getPoller()));
} }
template<typename Type>
void push_plugin(SocketHelper& sender, const WebRtcArgs &args, const onCreateWebRtc &cb) { void push_plugin(SocketHelper& sender, const WebRtcArgs &args, const onCreateWebRtc &cb) {
MediaInfo info(args["url"]); MediaInfo info(args["url"]);
Broadcast::PublishAuthInvoker invoker = [cb, info](const string &err, const ProtocolOption &option) mutable { Broadcast::PublishAuthInvoker invoker = [cb, info](const string &err, const ProtocolOption &option) mutable {
@ -1715,7 +1711,7 @@ void push_plugin(SocketHelper& sender, const WebRtcArgs &args, const onCreateWeb
push_src_ownership = push_src->getOwnership(); push_src_ownership = push_src->getOwnership();
push_src->setProtocolOption(option); push_src->setProtocolOption(option);
} }
auto rtc = Type::create(EventPollerPool::Instance().getPoller(), push_src, push_src_ownership, info, option, auto rtc = WebRtcPusher::create(EventPollerPool::Instance().getPoller(), push_src, push_src_ownership, info, option,
WebRtcTransport::Role::PEER, WebRtcTransport::SignalingProtocols::WHEP_WHIP); WebRtcTransport::Role::PEER, WebRtcTransport::SignalingProtocols::WHEP_WHIP);
push_src->setListener(rtc); push_src->setListener(rtc);
cb(*rtc); cb(*rtc);
@ -1786,12 +1782,9 @@ static void setWebRtcArgs(const WebRtcArgs &args, WebRtcInterface &rtc) {
} }
} }
auto preferred_tcp = args["preferred_tcp"]; bool preferred_tcp = args["preferred_tcp"];
if (!preferred_tcp.empty()) { {
rtc.setPreferredTcp(preferred_tcp); rtc.setPreferredTcp(preferred_tcp);
} else {
GET_CONFIG(bool, s_preferred_tcp, Rtc::kPreferredTcp);
rtc.setPreferredTcp(s_preferred_tcp);
} }
{ {
@ -1839,7 +1832,7 @@ static onceToken s_rtc_auto_register([]() {
// Enable echo plugin only in debug mode // Enable echo plugin only in debug mode
WebRtcPluginManager::Instance().registerPlugin("echo", echo_plugin); WebRtcPluginManager::Instance().registerPlugin("echo", echo_plugin);
#endif #endif
WebRtcPluginManager::Instance().registerPlugin("push", push_plugin<WebRtcPusher>); WebRtcPluginManager::Instance().registerPlugin("push", push_plugin);
WebRtcPluginManager::Instance().registerPlugin("play", play_plugin<WebRtcPlayer>); WebRtcPluginManager::Instance().registerPlugin("play", play_plugin<WebRtcPlayer>);
WebRtcPluginManager::Instance().registerPlugin("talk", play_plugin<WebRtcTalk>); WebRtcPluginManager::Instance().registerPlugin("talk", play_plugin<WebRtcTalk>);

View File

@ -286,7 +286,7 @@ public:
struct WrappedMediaTrack { struct WrappedMediaTrack {
MediaTrack::Ptr track; MediaTrack::Ptr track;
explicit WrappedMediaTrack(MediaTrack::Ptr ptr): track(std::move(ptr)) {} explicit WrappedMediaTrack(MediaTrack::Ptr ptr): track(ptr) {}
virtual ~WrappedMediaTrack() {} virtual ~WrappedMediaTrack() {}
virtual void inputRtp(const char *buf, size_t len, uint64_t stamp_ms, RtpHeader *rtp) = 0; virtual void inputRtp(const char *buf, size_t len, uint64_t stamp_ms, RtpHeader *rtp) = 0;
}; };