Compare commits

...

7 Commits

Author SHA1 Message Date
xia-chu
a54a0b35c7 优化代码
Some checks failed
Android / build (push) Has been cancelled
CodeQL / Analyze (cpp) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Docker / build (push) Has been cancelled
Linux / build (push) Has been cancelled
macOS / build (push) Has been cancelled
Windows / build (push) Has been cancelled
2026-01-09 11:41:07 +08:00
xia-chu
c53730f36c 修复推流代理失败无限重试的问题
解决媒体注销但还保持无限重试推流的bug
2026-01-09 11:40:42 +08:00
xia-chu
128d2a057c 新增支持HTTP PUT方法 2026-01-09 11:39:41 +08:00
xia-chu
fb491f3e79 新增webrtc配置选项nackAudioRtpSize、preferred_tcp
nackAudioRtpSize: 音频nack包中rtp个数,减小此值可以让nack包响应更灵敏
preferred_tcp: 是否优先采用webrtc over tcp模式
2026-01-09 11:39:22 +08:00
xia-chu
ae3d551c8a 解决webrtc丢包重传误判为重放攻击的问题 2026-01-09 11:35:37 +08:00
xia-chu
9cc4563fae getAllSession接口返回链接类型 2026-01-09 11:34:40 +08:00
xia-chu
cd8a14d1ca 事件视频录制前溯和后溯时间支持负数 2026-01-09 11:34:18 +08:00
16 changed files with 230 additions and 121 deletions

View File

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

View File

@ -1079,6 +1079,7 @@ void installWebApi() {
}
fillSockInfo(jsession, session.get());
jsession["id"] = id;
jsession["type"] = session->getSock()->sockType() == SockNum::Sock_TCP ? "tcp" : "udp";
jsession["typeid"] = toolkit::demangle(typeid(*session).name());
val["data"].append(jsession);
});

View File

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

View File

@ -287,6 +287,36 @@ public:
// Maximum number of tracks
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>
ProtocolOption(const MAP &allArgs) : ProtocolOption() {
load(allArgs);
@ -294,35 +324,18 @@ public:
template <typename MAP>
void load(const MAP &allArgs) {
#define GET_OPT_VALUE(key) getArgsValue(allArgs, #key, key)
GET_OPT_VALUE(modify_stamp);
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);
#define GET(key) getArgsValue(allArgs, #key, key);
OPT_VALUE(GET)
#undef GET
}
GET_OPT_VALUE(enable_hls);
GET_OPT_VALUE(enable_hls_fmp4);
GET_OPT_VALUE(enable_mp4);
GET_OPT_VALUE(enable_rtsp);
GET_OPT_VALUE(enable_rtmp);
GET_OPT_VALUE(enable_ts);
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);
template <typename MAP>
MAP as() {
MAP ret;
#define SET(key) ret[#key] = key;
OPT_VALUE(SET)
#undef SET
return ret;
}
};

View File

@ -401,15 +401,20 @@ bool MultiMediaSourceMuxer::setupRecord(Recorder::type type, bool start, const s
}
}
std::string MultiMediaSourceMuxer::startRecord(const std::string &file_path, uint32_t back_time_ms, uint32_t forward_time_ms) {
std::string MultiMediaSourceMuxer::startRecord(const std::string &file_path, int back_time_ms, int forward_time_ms) {
#if !defined(ENABLE_MP4)
throw std::invalid_argument("mp4相关功能未打开请开启ENABLE_MP4宏后编译再测试");
#else
if (!_ring) {
throw std::runtime_error("frame gop cache disabled, start record event video failed");
}
auto path = Recorder::getRecordPath(Recorder::type_mp4, _tuple, _option.mp4_save_path);
path += file_path;
std::string path;
if (!start_with(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;
auto muxer = std::make_shared<MP4Muxer>();
@ -419,68 +424,124 @@ std::string MultiMediaSourceMuxer::startRecord(const std::string &file_path, uin
}
muxer->addTrackCompleted();
std::list<Frame::Ptr> history;
_ring->flushGop([&](const Frame::Ptr &frame) { history.emplace_back(frame); });
if (!history.empty()) {
auto now_dts = history.back()->dts();
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);
}
bool have_history = false;
if (back_time_ms > 0) {
// 回溯录制
std::list<Frame::Ptr> history;
_ring->flushGop([&](const Frame::Ptr &frame) { history.emplace_back(frame); });
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;
}
auto now_dts = history.back()->dts();
for (auto &frame : history) {
muxer->inputFrame(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()) {
// 移除历史视频前面过多的数据
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);
}
}
}
auto reader = _ring->attach(MultiMediaSourceMuxer::getOwnerPoller(MediaSource::NullMediaSource()), false);
uint64_t now_dts = 0;
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();
if (forward_time_ms > 0) {
if (!have_history) {
InfoL << "start record: " << path << ", back_time_ms: " << back_time_ms << ", forward_time_ms: " << forward_time_ms;
}
// 新增兜底机制如果直播录制任务时长超过预期时间3秒不管数据时间戳是否增长是否达到预期都强制停止录制
if ((frame->getIndex() == selected_index && now_dts + forward_time_ms < frame->dts()) || (is_live_stream && ticker.createdTime() > forward_time_ms + 3000)) {
InfoL << "stop record: " << path << ", end dts: " << frame->dts();
WorkThreadPool::Instance().getPoller()->async([muxer]() { muxer->closeMP4(); });
reader = nullptr;
return;
weak_ptr<MultiMediaSourceMuxer> weak_self = shared_from_this();
auto lam = [weak_self, muxer, forward_time_ms, have_history, path]() {
auto strong_self = weak_self.lock();
if (!strong_self) {
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;
#endif

View File

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

View File

@ -55,31 +55,31 @@ extern const std::string kBroadcastRecordTs;
// 收到http api请求广播 [AUTO-TRANSLATED:c72e7c3f]
// Broadcast for receiving http api request
extern const std::string kBroadcastHttpRequest;
#define BroadcastHttpRequestArgs const Parser &parser, const HttpSession::HttpResponseInvoker &invoker, bool &consumed, SockInfo &sender
#define BroadcastHttpRequestArgs const Parser &parser, const HttpSession::HttpResponseInvoker &invoker, bool &consumed, toolkit::SockInfo &sender
// 在http文件服务器中,收到http访问文件或目录的广播,通过该事件控制访问http目录的权限 [AUTO-TRANSLATED:2de426b4]
// In the http file server, broadcast for receiving http access to files or directories. Control access permissions to the http directory through this event.
extern const std::string kBroadcastHttpAccess;
#define BroadcastHttpAccessArgs const Parser &parser, const std::string &path, const bool &is_dir, const HttpSession::HttpAccessPathInvoker &invoker, SockInfo &sender
#define BroadcastHttpAccessArgs const Parser &parser, const std::string &path, const bool &is_dir, const HttpSession::HttpAccessPathInvoker &invoker, toolkit::SockInfo &sender
// 在http文件服务器中,收到http访问文件或目录前的广播,通过该事件可以控制http url到文件路径的映射 [AUTO-TRANSLATED:0294d0c5]
// In the http file server, broadcast before receiving http access to files or directories. Control the mapping from http url to file path through this event.
// 在该事件中通过自行覆盖path参数可以做到譬如根据虚拟主机或者app选择不同http根目录的目的 [AUTO-TRANSLATED:1bea3efb]
// By overriding the path parameter in this event, you can achieve the purpose of selecting different http root directories based on virtual hosts or apps.
extern const std::string kBroadcastHttpBeforeAccess;
#define BroadcastHttpBeforeAccessArgs const Parser &parser, std::string &path, SockInfo &sender
#define BroadcastHttpBeforeAccessArgs const Parser &parser, std::string &path, toolkit::SockInfo &sender
// 该流是否需要认证是的话调用invoker并传入realm,否则传入空的realm.如果该事件不监听则不认证 [AUTO-TRANSLATED:5f436d8f]
// Does this stream need authentication? If yes, call invoker and pass in realm, otherwise pass in an empty realm. If this event is not listened to, no authentication will be performed.
extern const std::string kBroadcastOnGetRtspRealm;
#define BroadcastOnGetRtspRealmArgs const MediaInfo &args, const RtspSession::onGetRealm &invoker, SockInfo &sender
#define BroadcastOnGetRtspRealmArgs const MediaInfo &args, const RtspSession::onGetRealm &invoker, toolkit::SockInfo &sender
// 请求认证用户密码事件user_name为用户名must_no_encrypt如果为true则必须提供明文密码(因为此时是base64认证方式),否则会导致认证失败 [AUTO-TRANSLATED:22b6dfcc]
// Request authentication user password event, user_name is the username, must_no_encrypt if true, then the plaintext password must be provided (because it is base64 authentication method at this time), otherwise it will lead to authentication failure.
// 获取到密码后请调用invoker并输入对应类型的密码和密码类型invoker执行时会匹配密码 [AUTO-TRANSLATED:8c57fd43]
// After getting the password, please call invoker and input the corresponding type of password and password type. The invoker will match the password when executing.
extern const std::string kBroadcastOnRtspAuth;
#define BroadcastOnRtspAuthArgs const MediaInfo &args, const std::string &realm, const std::string &user_name, const bool &must_no_encrypt, const RtspSession::onAuth &invoker, SockInfo &sender
#define BroadcastOnRtspAuthArgs const MediaInfo &args, const std::string &realm, const std::string &user_name, const bool &must_no_encrypt, const RtspSession::onAuth &invoker, toolkit::SockInfo &sender
// 推流鉴权结果回调对象 [AUTO-TRANSLATED:7e508ed1]
// Push stream authentication result callback object
@ -90,7 +90,7 @@ using PublishAuthInvoker = std::function<void(const std::string &err, const Prot
// 收到rtsp/rtmp推流事件广播通过该事件控制推流鉴权 [AUTO-TRANSLATED:72417373]
// Broadcast for receiving rtsp/rtmp push stream event. Control push stream authentication through this event.
extern const std::string kBroadcastMediaPublish;
#define BroadcastMediaPublishArgs const MediaOriginType &type, const MediaInfo &args, const Broadcast::PublishAuthInvoker &invoker, SockInfo &sender
#define BroadcastMediaPublishArgs const MediaOriginType &type, const MediaInfo &args, const Broadcast::PublishAuthInvoker &invoker, toolkit::SockInfo &sender
// 播放鉴权结果回调对象 [AUTO-TRANSLATED:c980162b]
// Playback authentication result callback object
@ -101,22 +101,22 @@ using AuthInvoker = std::function<void(const std::string &err)>;
// 播放rtsp/rtmp/http-flv事件广播通过该事件控制播放鉴权 [AUTO-TRANSLATED:eddd7014]
// Broadcast for playing rtsp/rtmp/http-flv events. Control playback authentication through this event.
extern const std::string kBroadcastMediaPlayed;
#define BroadcastMediaPlayedArgs const MediaInfo &args, const Broadcast::AuthInvoker &invoker, SockInfo &sender
#define BroadcastMediaPlayedArgs const MediaInfo &args, const Broadcast::AuthInvoker &invoker, toolkit::SockInfo &sender
// shell登录鉴权 [AUTO-TRANSLATED:26b135d4]
// Shell login authentication
extern const std::string kBroadcastShellLogin;
#define BroadcastShellLoginArgs const std::string &user_name, const std::string &passwd, const Broadcast::AuthInvoker &invoker, SockInfo &sender
#define BroadcastShellLoginArgs const std::string &user_name, const std::string &passwd, const Broadcast::AuthInvoker &invoker, toolkit::SockInfo &sender
// 停止rtsp/rtmp/http-flv会话后流量汇报事件广播 [AUTO-TRANSLATED:69df61d8]
// Broadcast for traffic reporting event after stopping rtsp/rtmp/http-flv session
extern const std::string kBroadcastFlowReport;
#define BroadcastFlowReportArgs const MediaInfo &args, const uint64_t &totalBytes, const uint64_t &totalDuration, const bool &isPlayer, SockInfo &sender
#define BroadcastFlowReportArgs const MediaInfo &args, const uint64_t &totalBytes, const uint64_t &totalDuration, const bool &isPlayer, toolkit::SockInfo &sender
// 未找到流后会广播该事件,请在监听该事件后去拉流或其他方式产生流,这样就能按需拉流了 [AUTO-TRANSLATED:0c00171d]
// This event will be broadcast after the stream is not found. Please pull the stream or other methods to generate the stream after listening to this event, so that you can pull the stream on demand.
extern const std::string kBroadcastNotFoundStream;
#define BroadcastNotFoundStreamArgs const MediaInfo &args, SockInfo &sender, const std::function<void()> &closePlayer
#define BroadcastNotFoundStreamArgs const MediaInfo &args, toolkit::SockInfo &sender, const std::function<void()> &closePlayer
// 某个流无人消费时触发,目的为了实现无人观看时主动断开拉流等业务逻辑 [AUTO-TRANSLATED:3c45f002]
// Triggered when a stream is not consumed by anyone. The purpose is to achieve business logic such as actively disconnecting the pull stream when no one is watching.

View File

@ -61,6 +61,7 @@ ssize_t HttpSession::onRecvHeader(const char *header, size_t len) {
static onceToken token([]() {
s_func_map.emplace("GET", &HttpSession::onHttpRequest_GET);
s_func_map.emplace("POST", &HttpSession::onHttpRequest_POST);
s_func_map.emplace("PUT", &HttpSession::onHttpRequest_POST);
// DELETE命令用于whip/whep用只用于触发http api [AUTO-TRANSLATED:f3b7aaea]
// DELETE command is used for whip/whep, only used to trigger http api
s_func_map.emplace("DELETE", &HttpSession::onHttpRequest_POST);

View File

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

View File

@ -56,30 +56,33 @@ AudioMeta::AudioMeta(const AudioTrack::Ptr &audio) {
}
uint8_t getCodecFlags(CodecId cid) {
switch(cid) {
#define XX(a, b, c) case a: return static_cast<uint8_t>(b);
RTMP_CODEC_MAP(XX)
switch (cid) {
#define XX(a, b, c) \
case a: return static_cast<uint8_t>(b);
RTMP_CODEC_MAP(XX)
#undef XX
default: return 0;
}
}
uint32_t getCodecFourCC(CodecId cid) {
switch(cid) {
#define XX(a, b, c) case a: return static_cast<uint32_t>(c);
RTMP_CODEC_MAP(XX)
switch (cid) {
#define XX(a, b, c) \
case a: return static_cast<uint32_t>(c);
RTMP_CODEC_MAP(XX)
#undef XX
default: return 0;
}
}
CodecId getFourccCodec(uint32_t id) {
switch(id) {
#define XX(a, b, c) case (uint32_t)c: return a;
RTMP_CODEC_MAP(XX)
switch (id) {
#define XX(a, b, c) \
case (uint32_t)c: return a;
RTMP_CODEC_MAP(XX)
#undef XX
default: return CodecInvalid;
}
return CodecInvalid;
}
uint8_t getAudioRtmpFlags(const Track::Ptr &track) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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