mirror of
https://gitee.com/xia-chu/ZLMediaKit.git
synced 2026-05-24 10:37:49 +08:00
Compare commits
10 Commits
7534a70f34
...
df9f3bd8a9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
df9f3bd8a9 | ||
|
|
96c62cdac0 | ||
|
|
881238fcf3 | ||
|
|
85524f102b | ||
|
|
a28b0fc0a4 | ||
|
|
b0cf40d281 | ||
|
|
b118c5e936 | ||
|
|
171b354e64 | ||
|
|
e3b3630f34 | ||
|
|
9b37d69bfd |
@ -1 +1 @@
|
|||||||
Subproject commit 7302286cf4be39d416b023fec3fd4ca9c54af762
|
Subproject commit 493d14e1682cc0c79e72b3893dd98f865d45b8e9
|
||||||
@ -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)
|
||||||
|
|||||||
BIN
DejaVuSans.ttf
BIN
DejaVuSans.ttf
Binary file not shown.
@ -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;
|
||||||
|
|||||||
@ -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)
|
|
||||||
@ -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
|
|
||||||
)
|
|
||||||
@ -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播放推流、播放超时时间,单位秒
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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();
|
||||||
|
|||||||
@ -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();
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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()
|
||||||
|
|||||||
@ -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);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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();
|
||||||
|
|||||||
@ -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 << "程序退出中,请等待...";
|
||||||
|
|||||||
@ -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 ¤t_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
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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_YUVJ420P,PNG:AV_PIX_FMT_RGB24
|
* @param fmt jpg:AV_PIX_FMT_YUVJ420P,PNG: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
|
||||||
|
|||||||
@ -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) {
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取录制状态
|
* 获取录制状态
|
||||||
|
|||||||
@ -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
|
||||||
|
|
||||||
|
|||||||
@ -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 { \
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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());
|
||||||
|
|||||||
@ -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) {
|
||||||
|
|||||||
@ -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()) {
|
||||||
|
|||||||
@ -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());
|
||||||
|
|||||||
@ -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
|
||||||
|
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -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) {
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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--) {
|
||||||
|
|||||||
@ -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]
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
@ -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));
|
||||||
|
|||||||
@ -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>);
|
||||||
|
|
||||||
|
|||||||
@ -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;
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user