From aad98be5ef7afd0b88ad0af740be500d551b529d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E4=BC=A0=E5=B3=B0?= <59160162+zhang-chuanfeng@users.noreply.github.com> Date: Wed, 17 Jul 2024 11:40:10 +0800 Subject: [PATCH 01/18] =?UTF-8?q?ehome=E5=8D=8F=E8=AE=AE=E6=8E=A8=E6=B5=81?= =?UTF-8?q?=E8=8A=B1=E5=B1=8F=E9=97=AE=E9=A2=98=20(#3720)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://github.com/ZLMediaKit/ZLMediaKit/issues/3710 当前修改方式 只检测一次 由于第一个数据是256字节需要检测两次 ;现在修改方式是检测3次, 如果失败就不是ehome。后续不需要检测 --- src/Rtp/RtpSplitter.cpp | 6 +++--- src/Rtp/RtpSplitter.h | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Rtp/RtpSplitter.cpp b/src/Rtp/RtpSplitter.cpp index 5a15c923..bdf4e960 100644 --- a/src/Rtp/RtpSplitter.cpp +++ b/src/Rtp/RtpSplitter.cpp @@ -46,7 +46,7 @@ const char *RtpSplitter::onSearchPacketTail(const char *data, size_t len) { return nullptr; } - if ( _is_ehome ) { + if (_check_ehome_count) { if (isEhome(data, len)) { //是ehome协议 if (len < kEHOME_OFFSET + 4) { @@ -59,7 +59,7 @@ const char *RtpSplitter::onSearchPacketTail(const char *data, size_t len) { //忽略ehome私有头 return onSearchPacketTail_l(data + kEHOME_OFFSET + 2, len - kEHOME_OFFSET - 2); } - _is_ehome = false; + _check_ehome_count--; } if ( _is_rtsp_interleaved ) { @@ -70,7 +70,7 @@ const char *RtpSplitter::onSearchPacketTail(const char *data, size_t len) { } _is_rtsp_interleaved = false; } - + //两个字节的rtp头 _offset = 2; return onSearchPacketTail_l(data, len); diff --git a/src/Rtp/RtpSplitter.h b/src/Rtp/RtpSplitter.h index 216f3021..c6b85257 100644 --- a/src/Rtp/RtpSplitter.h +++ b/src/Rtp/RtpSplitter.h @@ -31,7 +31,8 @@ protected: const char *onSearchPacketTail_l(const char *data, size_t len); private: - bool _is_ehome = true; + bool _is_ehome = false; + int _check_ehome_count = 3; bool _is_rtsp_interleaved = true; size_t _offset = 0; }; From e3dcd9119e988c52aaaef8a2aad204e994917ea4 Mon Sep 17 00:00:00 2001 From: mtdxc Date: Wed, 17 Jul 2024 11:41:20 +0800 Subject: [PATCH 02/18] use vars.VERSION_ISSUE_NO (#3716) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将持续集成编译发布issue版本号,设成可配置,当没设置时不自动发布版本 Co-authored-by: cqm --- .github/workflows/android.yml | 2 +- .github/workflows/linux.yml | 2 +- .github/workflows/macos.yml | 2 +- .github/workflows/windows.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index fb67e0f5..9bcf4edf 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -46,7 +46,7 @@ jobs: github-token: ${{ secrets.GITHUB_TOKEN }} script: | github.rest.issues.createComment({ - issue_number: 483, + issue_number: ${{vars.VERSION_ISSUE_NO}}, owner: context.repo.owner, repo: context.repo.repo, body: '- 下载地址: [${{ github.workflow }}_${{ env.BRANCH }}_${{ env.DATE }}](${{ steps.upload.outputs.artifact-url }})\n' diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 6de43e0f..704a9b92 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -121,7 +121,7 @@ jobs: github-token: ${{ secrets.GITHUB_TOKEN }} script: | github.rest.issues.createComment({ - issue_number: 483, + issue_number: ${{vars.VERSION_ISSUE_NO}}, owner: context.repo.owner, repo: context.repo.repo, body: '- 下载地址: [${{ github.workflow }}_${{ env.BRANCH }}_${{ env.DATE }}](${{ steps.upload.outputs.artifact-url }})\n' diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 3cc2cceb..f12ac283 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -52,7 +52,7 @@ jobs: github-token: ${{ secrets.GITHUB_TOKEN }} script: | github.rest.issues.createComment({ - issue_number: 483, + issue_number: ${{vars.VERSION_ISSUE_NO}}, owner: context.repo.owner, repo: context.repo.repo, body: '- 下载地址: [${{ github.workflow }}_${{ env.BRANCH }}_${{ env.DATE }}](${{ steps.upload.outputs.artifact-url }})\n' diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index d444bdbc..d9e23a7f 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -54,7 +54,7 @@ jobs: github-token: ${{ secrets.GITHUB_TOKEN }} script: | github.rest.issues.createComment({ - issue_number: 483, + issue_number: ${{vars.VERSION_ISSUE_NO}}, owner: context.repo.owner, repo: context.repo.repo, body: '- 下载地址: [${{ github.workflow }}_${{ env.BRANCH }}_${{ env.DATE }}](${{ steps.upload.outputs.artifact-url }})\n' From c6a8118c10a69c4288d1d912ddd2e80b98258f3b Mon Sep 17 00:00:00 2001 From: chdahuzi Date: Fri, 19 Jul 2024 17:10:22 +0800 Subject: [PATCH 03/18] =?UTF-8?q?fmp4=E5=B0=81=E8=A3=85=E8=B6=85=E8=BF=87?= =?UTF-8?q?=E4=B8=80=E5=AE=9AI=E5=B8=A7=E9=97=B4=E9=9A=94=EF=BC=8C?= =?UTF-8?q?=E5=BC=BA=E5=88=B6=E5=88=B7=E6=96=B0segment=EF=BC=8C=E9=98=B2?= =?UTF-8?q?=E6=AD=A2=E5=86=85=E5=AD=98=E4=B8=8A=E6=B6=A8=20(#3736)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Record/MP4Muxer.cpp | 15 +++++++++++++++ src/Record/MP4Muxer.h | 1 + 2 files changed, 16 insertions(+) diff --git a/src/Record/MP4Muxer.cpp b/src/Record/MP4Muxer.cpp index 46fda66f..6d686b50 100644 --- a/src/Record/MP4Muxer.cpp +++ b/src/Record/MP4Muxer.cpp @@ -99,6 +99,20 @@ bool MP4MuxerInterface::inputFrame(const Frame::Ptr &frame) { _started = true; } + // fmp4封装超过一定I帧间隔,强制刷新segment,防止内存上涨 + if (frame->getTrackType() == TrackVideo && _mov_writter->fmp4) { + if (frame->keyFrame()) { + _non_iframe_video_count = 0; + } else { + _non_iframe_video_count++; + } + + if (_non_iframe_video_count > 200) { + saveSegment(); + _non_iframe_video_count = 0; + } + } + // mp4文件时间戳需要从0开始 auto &track = it->second; switch (frame->getCodecId()) { @@ -164,6 +178,7 @@ bool MP4MuxerInterface::addTrack(const Track::Ptr &track) { } _tracks[track->getIndex()].track_id = track_id; _have_video = true; + _non_iframe_video_count = 0; } else if (track->getTrackType() == TrackAudio) { auto audio_track = dynamic_pointer_cast(track); CHECK(audio_track); diff --git a/src/Record/MP4Muxer.h b/src/Record/MP4Muxer.h index 4f764c3f..f0d7fce1 100644 --- a/src/Record/MP4Muxer.h +++ b/src/Record/MP4Muxer.h @@ -72,6 +72,7 @@ private: bool _started = false; bool _have_video = false; MP4FileIO::Writer _mov_writter; + int _non_iframe_video_count; // 非I帧个数 class FrameMergerImp : public FrameMerger { public: From a14e40f8d2bdcf1fc6fa6bcfe0d403af9f7e05e9 Mon Sep 17 00:00:00 2001 From: xia-chu <771730766@qq.com> Date: Sun, 21 Jul 2024 19:16:34 +0800 Subject: [PATCH 04/18] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dwebrtc=20rtp=E9=87=8D?= =?UTF-8?q?=E5=8F=91=E5=88=97=E9=98=9F=E5=8F=AF=E8=83=BD=E7=94=B1=E4=BA=8E?= =?UTF-8?q?rtp=E6=97=B6=E9=97=B4=E6=88=B3=E5=BC=82=E5=B8=B8=E5=AF=BC?= =?UTF-8?q?=E8=87=B4=E5=86=85=E5=AD=98=E6=BA=A2=E5=87=BA=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- conf/config.ini | 12 ++++++------ webrtc/Nack.cpp | 49 ++++++++++++++++++++++++++++++------------------- webrtc/Nack.h | 19 +++---------------- 3 files changed, 39 insertions(+), 41 deletions(-) diff --git a/conf/config.ini b/conf/config.ini index 95d3fa37..3fd805c4 100644 --- a/conf/config.ini +++ b/conf/config.ini @@ -369,13 +369,13 @@ start_bitrate=0 max_bitrate=0 min_bitrate=0 -#nack接收端 -#Nack缓存包最早时间间隔 -maxNackMS=5000 -#Nack包检查间隔(包数量) -rtpCacheCheckInterval=100 +#nack接收端, rtp发送端,zlm发送rtc流 +#rtp重发缓存列队最大长度,单位毫秒 +maxRtpCacheMS=5000 +#rtp重发缓存列队最大长度,单位个数 +maxRtpCacheSize=2048 -#nack发送端 +#nack发送端,rtp接收端,zlm接收rtc推流 #最大保留的rtp丢包状态个数 nackMaxSize=2048 #rtp丢包状态最长保留时间 diff --git a/webrtc/Nack.cpp b/webrtc/Nack.cpp index 4fd1f101..7ee1d245 100644 --- a/webrtc/Nack.cpp +++ b/webrtc/Nack.cpp @@ -19,12 +19,13 @@ namespace mediakit { // RTC配置项目 namespace Rtc { #define RTC_FIELD "rtc." -//~ nack接收端 -// Nack缓存包最早时间间隔 -const string kMaxNackMS = RTC_FIELD "maxNackMS"; -// Nack包检查间隔(包数量) -const string kRtpCacheCheckInterval = RTC_FIELD "rtpCacheCheckInterval"; -//~ nack发送端 +//~ nack接收端, rtp发送端 +// rtp重发缓存列队最大长度,单位毫秒 +const string kMaxRtpCacheMS = RTC_FIELD "maxRtpCacheMS"; +// rtp重发缓存列队最大长度,单位个数 +const string kMaxRtpCacheSize = RTC_FIELD "maxRtpCacheSize"; + +//~ nack发送端,rtp接收端 //最大保留的rtp丢包状态个数 const string kNackMaxSize = RTC_FIELD "nackMaxSize"; // rtp丢包状态最长保留时间 @@ -37,8 +38,8 @@ const string kNackIntervalRatio = RTC_FIELD "nackIntervalRatio"; const string kNackRtpSize = RTC_FIELD "nackRtpSize"; static onceToken token([]() { - mINI::Instance()[kMaxNackMS] = 5 * 1000; - mINI::Instance()[kRtpCacheCheckInterval] = 100; + mINI::Instance()[kMaxRtpCacheMS] = 5 * 1000; + mINI::Instance()[kMaxRtpCacheSize] = 2048; mINI::Instance()[kNackMaxSize] = 2048; mINI::Instance()[kNackMaxMS] = 3 * 1000; mINI::Instance()[kNackMaxCount] = 15; @@ -49,17 +50,26 @@ static onceToken token([]() { } // namespace Rtc void NackList::pushBack(RtpPacket::Ptr rtp) { + GET_CONFIG(uint32_t, max_rtp_cache_ms, Rtc::kMaxRtpCacheMS); + GET_CONFIG(uint32_t, max_rtp_cache_size, Rtc::kMaxRtpCacheSize); + + // 记录rtp auto seq = rtp->getSeq(); _nack_cache_seq.emplace_back(seq); _nack_cache_pkt.emplace(seq, std::move(rtp)); - GET_CONFIG(uint32_t, rtpcache_checkinterval, Rtc::kRtpCacheCheckInterval); - if (++_cache_ms_check < rtpcache_checkinterval) { + + // 限制rtp缓存最大个数 + if (_nack_cache_seq.size() > max_rtp_cache_size) { + popFront(); + } + + if (++_cache_ms_check < 100) { + // 每100个rtp包检测下缓存长度,节省cpu资源 return; } _cache_ms_check = 0; - GET_CONFIG(uint32_t, maxnackms, Rtc::kMaxNackMS); - while (getCacheMS() >= maxnackms) { - // 需要清除部分nack缓存 + // 限制rtp缓存最大时长 + while (getCacheMS() >= max_rtp_cache_ms) { popFront(); } } @@ -96,13 +106,13 @@ RtpPacket::Ptr *NackList::getRtp(uint16_t seq) { uint32_t NackList::getCacheMS() { while (_nack_cache_seq.size() > 2) { - auto back_stamp = getRtpStamp(_nack_cache_seq.back()); + auto back_stamp = getNtpStamp(_nack_cache_seq.back()); if (back_stamp == -1) { _nack_cache_seq.pop_back(); continue; } - auto front_stamp = getRtpStamp(_nack_cache_seq.front()); + auto front_stamp = getNtpStamp(_nack_cache_seq.front()); if (front_stamp == -1) { _nack_cache_seq.pop_front(); continue; @@ -111,18 +121,19 @@ uint32_t NackList::getCacheMS() { if (back_stamp >= front_stamp) { return back_stamp - front_stamp; } - // 很有可能回环了 - return back_stamp + (UINT32_MAX - front_stamp); + // ntp时间戳回退了,非法数据,丢掉 + _nack_cache_seq.pop_front(); } return 0; } -int64_t NackList::getRtpStamp(uint16_t seq) { +int64_t NackList::getNtpStamp(uint16_t seq) { auto it = _nack_cache_pkt.find(seq); if (it == _nack_cache_pkt.end()) { return -1; } - return it->second->getStampMS(false); + // 使用ntp时间戳,不会回退 + return it->second->getStampMS(true); } //////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/webrtc/Nack.h b/webrtc/Nack.h index 276f51e8..08219e2b 100644 --- a/webrtc/Nack.h +++ b/webrtc/Nack.h @@ -22,24 +22,11 @@ namespace mediakit { // RTC配置项目 namespace Rtc { - -//~ nack接收端(rtp发送端) -// Nack缓存包最早时间间隔 -extern const std::string kMaxNackMS; -// Nack包检查间隔(包数量) -extern const std::string kRtpCacheCheckInterval; - -//~ nack发送端(rtp接收端) +//~ nack发送端,rtp接收端 // 最大保留的rtp丢包状态个数 extern const std::string kNackMaxSize; // rtp丢包状态最长保留时间 extern const std::string kNackMaxMS; -// nack最多请求重传次数 -extern const std::string kNackMaxCount; -// nack重传频率,rtt的倍数 -extern const std::string kNackIntervalRatio; -// nack包中rtp个数,减小此值可以让nack包响应更灵敏 -extern const std::string kNackRtpSize; } // namespace Rtc class NackList { @@ -50,7 +37,7 @@ public: private: void popFront(); uint32_t getCacheMS(); - int64_t getRtpStamp(uint16_t seq); + int64_t getNtpStamp(uint16_t seq); RtpPacket::Ptr *getRtp(uint16_t seq); private: @@ -88,7 +75,7 @@ private: struct NackStatus { uint64_t first_stamp; uint64_t update_stamp; - int nack_count = 0; + uint32_t nack_count = 0; }; std::map _nack_send_status; }; From 66065f5359e11f08e7ab474b5da4321125a2d843 Mon Sep 17 00:00:00 2001 From: xia-chu <771730766@qq.com> Date: Sun, 21 Jul 2024 19:17:12 +0800 Subject: [PATCH 05/18] =?UTF-8?q?=E4=BF=AE=E5=A4=8Drtp=E6=8E=A8=E6=B5=81?= =?UTF-8?q?=E5=8D=8F=E8=AE=AE=E4=B8=BA=E7=A9=BA=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Rtp/RtpProcess.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Rtp/RtpProcess.cpp b/src/Rtp/RtpProcess.cpp index 90f1057a..6ae19456 100644 --- a/src/Rtp/RtpProcess.cpp +++ b/src/Rtp/RtpProcess.cpp @@ -30,6 +30,7 @@ RtpProcess::Ptr RtpProcess::createProcess(const MediaTuple &tuple) { } RtpProcess::RtpProcess(const MediaTuple &tuple) { + _media_info.schema = "rtp"; static_cast(_media_info) = tuple; GET_CONFIG(string, dump_dir, RtpProxy::kDumpDir); From 6583e2869acd90e6f308ff2446da1a0e4f64943f Mon Sep 17 00:00:00 2001 From: xia-chu <771730766@qq.com> Date: Sun, 21 Jul 2024 19:21:23 +0800 Subject: [PATCH 06/18] =?UTF-8?q?=E7=A1=AE=E4=BF=9D=E5=8A=A0=E8=BD=BDh264?= =?UTF-8?q?=E8=A7=86=E9=A2=91=E8=BD=AC=E6=B5=81=E5=AA=92=E4=BD=93=E6=97=B6?= =?UTF-8?q?=E9=97=B4=E6=88=B3=E7=B2=BE=E5=87=86=20(#3691)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/tests/h264_media_server.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/api/tests/h264_media_server.c b/api/tests/h264_media_server.c index 427afeaf..1fc4f982 100644 --- a/api/tests/h264_media_server.c +++ b/api/tests/h264_media_server.c @@ -29,9 +29,8 @@ static void on_h264_frame(void *user_data, mk_h264_splitter splitter, const char #else usleep(40 * 1000); #endif - static int dts = 0; + uint64_t dts = mk_util_get_current_millisecond(); mk_frame frame = mk_frame_create(MKCodecH264, dts, dts, data, size, NULL, NULL); - dts += 40; mk_media_input_frame((mk_media) user_data, frame); mk_frame_unref(frame); } From 9463d1291d8fe63d882ab28b16ae613c328d08df Mon Sep 17 00:00:00 2001 From: xia-chu <771730766@qq.com> Date: Sun, 21 Jul 2024 19:31:17 +0800 Subject: [PATCH 07/18] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=BC=96=E8=AF=91?= =?UTF-8?q?=E8=AD=A6=E5=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/include/mk_frame.h | 22 +++++++++++----------- api/source/mk_frame.cpp | 2 +- ext-codec/AACRtp.cpp | 2 +- ext-codec/JPEGRtp.cpp | 2 +- src/Player/PlayerProxy.h | 2 +- src/Rtp/RtpCache.h | 2 +- tests/test_bench_forward.cpp | 4 ++-- tests/test_bench_push.cpp | 2 +- tests/test_rtp.cpp | 2 +- 9 files changed, 20 insertions(+), 20 deletions(-) diff --git a/api/include/mk_frame.h b/api/include/mk_frame.h index 56309d4f..a17f0654 100644 --- a/api/include/mk_frame.h +++ b/api/include/mk_frame.h @@ -27,17 +27,17 @@ extern "C" { #define MK_FRAME_FLAG_NOT_DECODE_ABLE (1 << 3) //codec id常量定义 -API_EXPORT extern const int MKCodecH264; -API_EXPORT extern const int MKCodecH265; -API_EXPORT extern const int MKCodecAAC; -API_EXPORT extern const int MKCodecG711A; -API_EXPORT extern const int MKCodecG711U; -API_EXPORT extern const int MKCodecOpus; -API_EXPORT extern const int MKCodecL16; -API_EXPORT extern const int MKCodecVP8; -API_EXPORT extern const int MKCodecVP9; -API_EXPORT extern const int MKCodecAV1; -API_EXPORT extern const int MKCodecJPEG; +extern const int MKCodecH264; +extern const int MKCodecH265; +extern const int MKCodecAAC; +extern const int MKCodecG711A; +extern const int MKCodecG711U; +extern const int MKCodecOpus; +extern const int MKCodecL16; +extern const int MKCodecVP8; +extern const int MKCodecVP9; +extern const int MKCodecAV1; +extern const int MKCodecJPEG; typedef struct mk_frame_t *mk_frame; diff --git a/api/source/mk_frame.cpp b/api/source/mk_frame.cpp index adf03e1c..c84453d3 100644 --- a/api/source/mk_frame.cpp +++ b/api/source/mk_frame.cpp @@ -15,7 +15,7 @@ using namespace mediakit; extern "C" { -#define XX(name, type, value, str, mpeg_id, mp4_id) API_EXPORT const int MK##name = value; +#define XX(name, type, value, str, mpeg_id, mp4_id) const int MK##name = value; CODEC_MAP(XX) #undef XX } diff --git a/ext-codec/AACRtp.cpp b/ext-codec/AACRtp.cpp index c9e4bfea..006630f7 100644 --- a/ext-codec/AACRtp.cpp +++ b/ext-codec/AACRtp.cpp @@ -86,7 +86,7 @@ bool AACRtpDecoder::inputRtp(const RtpPacket::Ptr &rtp, bool key_pos) { } // 每个audio unit时间戳增量 - auto dts_inc = (stamp - _last_dts) / au_header_count; + auto dts_inc = static_cast(stamp - _last_dts) / au_header_count; if (dts_inc < 0 || dts_inc > 100) { // 时间戳增量异常,忽略 dts_inc = 0; diff --git a/ext-codec/JPEGRtp.cpp b/ext-codec/JPEGRtp.cpp index 99456b21..b2e2240e 100644 --- a/ext-codec/JPEGRtp.cpp +++ b/ext-codec/JPEGRtp.cpp @@ -598,7 +598,7 @@ void JPEGRtpEncoder::rtpSendJpeg(const uint8_t *buf, int size, uint64_t pts, uin { const uint8_t *qtables[4] = { NULL }; int nb_qtables = 0; - uint8_t w, h; + uint8_t w { 0 }, h { 0 }; uint8_t *p; int off = 0; /* fragment offset of the current JPEG frame */ int len; diff --git a/src/Player/PlayerProxy.h b/src/Player/PlayerProxy.h index cc10a4e6..c484ca6a 100644 --- a/src/Player/PlayerProxy.h +++ b/src/Player/PlayerProxy.h @@ -129,12 +129,12 @@ private: void setTranslationInfo(); private: - ProtocolOption _option; int _retry_count; int _reconnect_delay_min; int _reconnect_delay_max; int _reconnect_delay_step; MediaTuple _tuple; + ProtocolOption _option; std::string _pull_url; toolkit::Timer::Ptr _timer; std::function _on_disconnect; diff --git a/src/Rtp/RtpCache.h b/src/Rtp/RtpCache.h index bd570c47..c63e18f4 100644 --- a/src/Rtp/RtpCache.h +++ b/src/Rtp/RtpCache.h @@ -41,7 +41,7 @@ private: class RtpCachePS : public RtpCache, public PSEncoderImp { public: RtpCachePS(onFlushed cb, uint32_t ssrc, uint8_t payload_type = 96, bool ps_or_ts = true) : - RtpCache(std::move(cb)), PSEncoderImp(ssrc, ps_or_ts ? payload_type : Rtsp::PT_MP2T, ps_or_ts) {}; + RtpCache(std::move(cb)), PSEncoderImp(ssrc, ps_or_ts ? payload_type : static_cast(Rtsp::PT_MP2T), ps_or_ts) {}; void flush() override; diff --git a/tests/test_bench_forward.cpp b/tests/test_bench_forward.cpp index c75b45ed..20a3d2e5 100644 --- a/tests/test_bench_forward.cpp +++ b/tests/test_bench_forward.cpp @@ -223,8 +223,8 @@ int main(int argc, char *argv[]) { option.enable_hls = false; option.enable_mp4 = false; option.modify_stamp = (int)ProtocolOption::kModifyStampRelative; - //添加拉流代理 - auto tuple = MediaTuple{DEFAULT_VHOST, "app", std::to_string(i)}; + //添加拉流代理 + auto tuple = MediaTuple { DEFAULT_VHOST, "app", std::to_string(i), "" }; auto proxy = std::make_shared(tuple, option, -1, nullptr, 1); //开始拉流代理 proxy->play(input_urls[i]); diff --git a/tests/test_bench_push.cpp b/tests/test_bench_push.cpp index 8ae0d41c..317b0cdd 100644 --- a/tests/test_bench_push.cpp +++ b/tests/test_bench_push.cpp @@ -148,7 +148,7 @@ int main(int argc, char *argv[]) { MediaSource::Ptr src = nullptr; PlayerProxy::Ptr proxy = nullptr;; - auto tuple = MediaTuple{DEFAULT_VHOST, app, stream}; + auto tuple = MediaTuple { DEFAULT_VHOST, app, stream, "" }; if (end_with(in_url, ".mp4")) { // create MediaSource from mp4file auto reader = std::make_shared(tuple, in_url); diff --git a/tests/test_rtp.cpp b/tests/test_rtp.cpp index 8946aa59..bf9167e6 100644 --- a/tests/test_rtp.cpp +++ b/tests/test_rtp.cpp @@ -75,7 +75,7 @@ static bool loadFile(const char *path, const EventPoller::Ptr &poller) { return 0; } - auto diff = stamp - stamp_last; + auto diff = static_cast(stamp - stamp_last); if (diff < 0 || diff > 500) { diff = 1; } From 8398ae17e4312c7e66f42b7ea25d25856d4c3e36 Mon Sep 17 00:00:00 2001 From: xiongguangjie Date: Sun, 21 Jul 2024 20:43:52 +0800 Subject: [PATCH 08/18] compatible rtsp sdp parse no samplerate for audio (#3740) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 兼容rtsp sdp协商中未声明采样率但是可以通过a 字段中fmtp中有config中解析出来的情况 --- src/Rtsp/Rtsp.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/Rtsp/Rtsp.cpp b/src/Rtsp/Rtsp.cpp index 7a7b0b2d..d1cc0356 100644 --- a/src/Rtsp/Rtsp.cpp +++ b/src/Rtsp/Rtsp.cpp @@ -15,6 +15,7 @@ #include "Common/Parser.h" #include "Common/config.h" #include "Network/Socket.h" +#include "Extension/Factory.h" using namespace std; using namespace toolkit; @@ -236,10 +237,6 @@ void SdpParser::load(const string &sdp) { track._codec = codec; track._samplerate = samplerate; } - if (!track._samplerate && track._type == TrackVideo) { - // 未设置视频采样率时,赋值为90000 - track._samplerate = 90000; - } ++it; } @@ -260,6 +257,17 @@ void SdpParser::load(const string &sdp) { if (it != track._attr.end()) { track._control = it->second; } + + if (!track._samplerate && track._type == TrackVideo) { + // 未设置视频采样率时,赋值为90000 + track._samplerate = 90000; + } else if (!track._samplerate && track._type == TrackAudio) { + // some rtsp sdp no sample rate but has fmt config to parser get sample rate + auto t = Factory::getTrackBySdp(track_ptr); + if (t) { + track._samplerate = std::static_pointer_cast(t)->getAudioSampleRate(); + } + } } } From 3be77616e74394edecbdb7af1dab8662ccee053b Mon Sep 17 00:00:00 2001 From: xia-chu <771730766@qq.com> Date: Sun, 21 Jul 2024 20:38:41 +0800 Subject: [PATCH 09/18] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=93=BE=E6=8E=A5?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/include/mk_frame.h | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/api/include/mk_frame.h b/api/include/mk_frame.h index a17f0654..56309d4f 100644 --- a/api/include/mk_frame.h +++ b/api/include/mk_frame.h @@ -27,17 +27,17 @@ extern "C" { #define MK_FRAME_FLAG_NOT_DECODE_ABLE (1 << 3) //codec id常量定义 -extern const int MKCodecH264; -extern const int MKCodecH265; -extern const int MKCodecAAC; -extern const int MKCodecG711A; -extern const int MKCodecG711U; -extern const int MKCodecOpus; -extern const int MKCodecL16; -extern const int MKCodecVP8; -extern const int MKCodecVP9; -extern const int MKCodecAV1; -extern const int MKCodecJPEG; +API_EXPORT extern const int MKCodecH264; +API_EXPORT extern const int MKCodecH265; +API_EXPORT extern const int MKCodecAAC; +API_EXPORT extern const int MKCodecG711A; +API_EXPORT extern const int MKCodecG711U; +API_EXPORT extern const int MKCodecOpus; +API_EXPORT extern const int MKCodecL16; +API_EXPORT extern const int MKCodecVP8; +API_EXPORT extern const int MKCodecVP9; +API_EXPORT extern const int MKCodecAV1; +API_EXPORT extern const int MKCodecJPEG; typedef struct mk_frame_t *mk_frame; From fea7f3647e69629d8f0d44fc8a8bf23bb3284d77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=8F=E6=A5=9A?= <771730766@qq.com> Date: Tue, 23 Jul 2024 11:24:09 +0800 Subject: [PATCH 10/18] Update issue_lint.yml --- .github/workflows/issue_lint.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/issue_lint.yml b/.github/workflows/issue_lint.yml index 58015714..c33e4f72 100644 --- a/.github/workflows/issue_lint.yml +++ b/.github/workflows/issue_lint.yml @@ -42,6 +42,13 @@ jobs: issue_number: context.issue.number, body: '此issue由于不符合模板规范已经自动关闭,请重新按照模板规范确保包含模板中所有章节标题再提交\n', }); + + await github.rest.issues.addLabels({ + owner: context.issue.owner, + repo: context.issue.repo, + issue_number: context.issue.number, + labels: ['自动关闭'] + }); await github.rest.issues.update({ owner: context.issue.owner, From 81aef25583514a8fa91fb79c6bfdb0e857143247 Mon Sep 17 00:00:00 2001 From: xia-chu <771730766@qq.com> Date: Sun, 28 Jul 2024 22:55:33 +0800 Subject: [PATCH 11/18] =?UTF-8?q?=E6=95=B4=E7=90=86startSendRtp=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E6=8E=A5=E5=8F=A3=EF=BC=8C=E6=96=B0=E5=A2=9E=E6=94=AF?= =?UTF-8?q?=E6=8C=81udp=E8=A2=AB=E5=8A=A8=E6=A8=A1=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit startSendPassive接口指定is_udp为1时则为开启udp被动模式, 被动模式下需要对方先发数据给zlmediakit,然后才能回复rtp数据; 同时在被动模式指定src_port时,不再等待对方连接成功后再返回(而是直接返回成功和端口号,失败通知请监听on_send_rtp_stopped hook)。 --- api/include/mk_events_objects.h | 6 +- api/include/mk_media.h | 6 +- api/source/mk_events_objects.cpp | 8 +- api/source/mk_media.cpp | 8 +- postman/ZLMediaKit.postman_collection.json | 20 +- server/WebApi.cpp | 88 +++---- src/Common/MediaSource.h | 39 ++-- src/Common/MultiMediaSourceMuxer.cpp | 21 +- src/Rtp/RtpSender.cpp | 254 ++++++++++++--------- src/Rtp/RtpSender.h | 3 + 10 files changed, 241 insertions(+), 212 deletions(-) diff --git a/api/include/mk_events_objects.h b/api/include/mk_events_objects.h index f81cbe61..42b72814 100644 --- a/api/include/mk_events_objects.h +++ b/api/include/mk_events_objects.h @@ -159,9 +159,9 @@ API_EXPORT int API_CALL mk_media_source_seek_to(const mk_media_source ctx,uint32 */ typedef void(API_CALL *on_mk_media_source_send_rtp_result)(void *user_data, uint16_t local_port, int err, const char *msg); -//MediaSource::startSendRtp,请参考mk_media_start_send_rtp,注意ctx参数类型不一样 -API_EXPORT void API_CALL mk_media_source_start_send_rtp(const mk_media_source ctx, const char *dst_url, uint16_t dst_port, const char *ssrc, int is_udp, on_mk_media_source_send_rtp_result cb, void *user_data); -API_EXPORT void API_CALL mk_media_source_start_send_rtp2(const mk_media_source ctx, const char *dst_url, uint16_t dst_port, const char *ssrc, int is_udp, on_mk_media_source_send_rtp_result cb, void *user_data, on_user_data_free user_data_free); +// MediaSource::startSendRtp,请参考mk_media_start_send_rtp,注意ctx参数类型不一样 +API_EXPORT void API_CALL mk_media_source_start_send_rtp(const mk_media_source ctx, const char *dst_url, uint16_t dst_port, const char *ssrc, int con_type, on_mk_media_source_send_rtp_result cb, void *user_data); +API_EXPORT void API_CALL mk_media_source_start_send_rtp2(const mk_media_source ctx, const char *dst_url, uint16_t dst_port, const char *ssrc, int con_type, on_mk_media_source_send_rtp_result cb, void *user_data, on_user_data_free user_data_free); //MediaSource::stopSendRtp,请参考mk_media_stop_send_rtp,注意ctx参数类型不一样 API_EXPORT int API_CALL mk_media_source_stop_send_rtp(const mk_media_source ctx); diff --git a/api/include/mk_media.h b/api/include/mk_media.h index e7277462..85327dbf 100755 --- a/api/include/mk_media.h +++ b/api/include/mk_media.h @@ -269,12 +269,12 @@ typedef on_mk_media_source_send_rtp_result on_mk_media_send_rtp_result; * @param dst_url 目标ip或域名 * @param dst_port 目标端口 * @param ssrc rtp的ssrc,10进制的字符串打印 - * @param is_udp 是否为udp + * @param con_type 0: tcp主动,1:udp主动,2:tcp被动,3:udp被动 * @param cb 启动成功或失败回调 * @param user_data 回调用户指针 */ -API_EXPORT void API_CALL mk_media_start_send_rtp(mk_media ctx, const char *dst_url, uint16_t dst_port, const char *ssrc, int is_udp, on_mk_media_send_rtp_result cb, void *user_data); -API_EXPORT void API_CALL mk_media_start_send_rtp2(mk_media ctx, const char *dst_url, uint16_t dst_port, const char *ssrc, int is_udp, on_mk_media_send_rtp_result cb, void *user_data, on_user_data_free user_data_free); +API_EXPORT void API_CALL mk_media_start_send_rtp(mk_media ctx, const char *dst_url, uint16_t dst_port, const char *ssrc, int con_type, on_mk_media_send_rtp_result cb, void *user_data); +API_EXPORT void API_CALL mk_media_start_send_rtp2(mk_media ctx, const char *dst_url, uint16_t dst_port, const char *ssrc, int con_type, on_mk_media_send_rtp_result cb, void *user_data, on_user_data_free user_data_free); /** * 停止某路或全部ps-rtp发送,此api线程安全 diff --git a/api/source/mk_events_objects.cpp b/api/source/mk_events_objects.cpp index 46bc408e..91ab640d 100644 --- a/api/source/mk_events_objects.cpp +++ b/api/source/mk_events_objects.cpp @@ -295,11 +295,11 @@ API_EXPORT int API_CALL mk_media_source_seek_to(const mk_media_source ctx,uint32 MediaSource *src = (MediaSource *)ctx; return src->seekTo(stamp); } -API_EXPORT void API_CALL mk_media_source_start_send_rtp(const mk_media_source ctx, const char *dst_url, uint16_t dst_port, const char *ssrc, int is_udp, on_mk_media_source_send_rtp_result cb, void *user_data) { - mk_media_source_start_send_rtp2(ctx, dst_url, dst_port, ssrc, is_udp, cb, user_data, nullptr); +API_EXPORT void API_CALL mk_media_source_start_send_rtp(const mk_media_source ctx, const char *dst_url, uint16_t dst_port, const char *ssrc, int con_type, on_mk_media_source_send_rtp_result cb, void *user_data) { + mk_media_source_start_send_rtp2(ctx, dst_url, dst_port, ssrc, con_type, cb, user_data, nullptr); } -API_EXPORT void API_CALL mk_media_source_start_send_rtp2(const mk_media_source ctx, const char *dst_url, uint16_t dst_port, const char *ssrc, int is_udp, on_mk_media_source_send_rtp_result cb, void *user_data, on_user_data_free user_data_free){ +API_EXPORT void API_CALL mk_media_source_start_send_rtp2(const mk_media_source ctx, const char *dst_url, uint16_t dst_port, const char *ssrc, int con_type, on_mk_media_source_send_rtp_result cb, void *user_data, on_user_data_free user_data_free){ assert(ctx && dst_url && ssrc); MediaSource *src = (MediaSource *)ctx; @@ -307,7 +307,7 @@ API_EXPORT void API_CALL mk_media_source_start_send_rtp2(const mk_media_source c args.dst_url = dst_url; args.dst_port = dst_port; args.ssrc = ssrc; - args.is_udp = is_udp; + args.con_type = (mediakit::MediaSourceEvent::SendRtpArgs::ConType)con_type; std::shared_ptr ptr(user_data, user_data_free ? user_data_free : [](void *) {}); src->startSendRtp(args, [cb, ptr](uint16_t local_port, const SockException &ex){ diff --git a/api/source/mk_media.cpp b/api/source/mk_media.cpp index fda298af..df59aa0f 100755 --- a/api/source/mk_media.cpp +++ b/api/source/mk_media.cpp @@ -281,11 +281,11 @@ API_EXPORT int API_CALL mk_media_input_audio(mk_media ctx, const void *data, int return (*obj)->getChannel()->inputAudio((const char*)data, len, dts); } -API_EXPORT void API_CALL mk_media_start_send_rtp(mk_media ctx, const char *dst_url, uint16_t dst_port, const char *ssrc, int is_udp, on_mk_media_send_rtp_result cb, void *user_data) { - mk_media_start_send_rtp2(ctx, dst_url, dst_port, ssrc, is_udp, cb, user_data, nullptr); +API_EXPORT void API_CALL mk_media_start_send_rtp(mk_media ctx, const char *dst_url, uint16_t dst_port, const char *ssrc, int con_type, on_mk_media_send_rtp_result cb, void *user_data) { + mk_media_start_send_rtp2(ctx, dst_url, dst_port, ssrc, con_type, cb, user_data, nullptr); } -API_EXPORT void API_CALL mk_media_start_send_rtp2(mk_media ctx, const char *dst_url, uint16_t dst_port, const char *ssrc, int is_udp, on_mk_media_send_rtp_result cb, void *user_data, +API_EXPORT void API_CALL mk_media_start_send_rtp2(mk_media ctx, const char *dst_url, uint16_t dst_port, const char *ssrc, int con_type, on_mk_media_send_rtp_result cb, void *user_data, on_user_data_free user_data_free) { assert(ctx && dst_url && ssrc); MediaHelper::Ptr* obj = (MediaHelper::Ptr*) ctx; @@ -294,7 +294,7 @@ API_EXPORT void API_CALL mk_media_start_send_rtp2(mk_media ctx, const char *dst_ args.dst_url = dst_url; args.dst_port = dst_port; args.ssrc = ssrc; - args.is_udp = is_udp; + args.con_type = (mediakit::MediaSourceEvent::SendRtpArgs::ConType)con_type; // sender参数无用 auto ref = *obj; diff --git a/postman/ZLMediaKit.postman_collection.json b/postman/ZLMediaKit.postman_collection.json index 66464a82..8b80aede 100644 --- a/postman/ZLMediaKit.postman_collection.json +++ b/postman/ZLMediaKit.postman_collection.json @@ -1881,7 +1881,7 @@ "response": [] }, { - "name": "开始发送rtp(startSendRtp)", + "name": "开始active模式发送rtp(startSendRtp)", "request": { "method": "GET", "header": [], @@ -1940,7 +1940,7 @@ { "key": "is_udp", "value": "0", - "description": "是否为udp模式,否则为tcp模式" + "description": "1:udp active模式, 0:tcp active模式" }, { "key": "src_port", @@ -1955,9 +1955,9 @@ "disabled": true }, { - "key": "use_ps", + "key": "type", "value": "1", - "description": "rtp打包采用ps还是es模式,默认采用ps模式,该参数非必选参数", + "description": "rtp打包模式,0:es, 1: ps, 2: ts", "disabled": true }, { @@ -1990,7 +1990,7 @@ "response": [] }, { - "name": "开始tcp passive被动发送rtp(startSendRtpPassive)", + "name": "开始passive模式发送rtp(startSendRtpPassive)", "request": { "method": "GET", "header": [], @@ -2030,6 +2030,12 @@ "value": "1", "description": "rtp推流的ssrc,ssrc不同时,可以推流到多个上级服务器" }, + { + "key": "is_udp", + "value": "0", + "disabled": true, + "description": "1:udp passive模式, 0:tcp passive模式" + }, { "key": "src_port", "value": "0", @@ -2043,9 +2049,9 @@ "disabled": true }, { - "key": "use_ps", + "key": "type", "value": "1", - "description": "rtp打包采用ps还是es模式,默认采用ps模式,该参数非必选参数", + "description": "rtp打包模式,0:es, 1: ps, 2: ts", "disabled": true }, { diff --git a/server/WebApi.cpp b/server/WebApi.cpp index 2b08817a..e086fe6c 100755 --- a/server/WebApi.cpp +++ b/server/WebApi.cpp @@ -1377,10 +1377,7 @@ void installWebApi() { } }); - api_regist("/index/api/startSendRtp",[](API_ARGS_MAP_ASYNC){ - CHECK_SECRET(); - CHECK_ARGS("vhost", "app", "stream", "ssrc", "dst_url", "dst_port", "is_udp"); - + static auto start_send_rtp = [] (bool passive, API_ARGS_MAP_ASYNC) { auto src = MediaSource::find(allArgs["vhost"], allArgs["app"], allArgs["stream"], allArgs["from_mp4"].as()); if (!src) { throw ApiRetException("can not find the source stream", API::NotFound); @@ -1391,30 +1388,50 @@ void installWebApi() { type = allArgs["use_ps"].as(); } MediaSourceEvent::SendRtpArgs args; - args.passive = false; + if (passive) { + args.con_type = allArgs["is_udp"].as() ? mediakit::MediaSourceEvent::SendRtpArgs::kUdpPassive : mediakit::MediaSourceEvent::SendRtpArgs::kTcpPassive; + } else { + args.con_type = allArgs["is_udp"].as() ? mediakit::MediaSourceEvent::SendRtpArgs::kUdpActive : mediakit::MediaSourceEvent::SendRtpArgs::kTcpActive; + } args.dst_url = allArgs["dst_url"]; args.dst_port = allArgs["dst_port"]; args.ssrc_multi_send = allArgs["ssrc_multi_send"].empty() ? false : allArgs["ssrc_multi_send"].as(); args.ssrc = allArgs["ssrc"]; - args.is_udp = allArgs["is_udp"]; args.src_port = allArgs["src_port"]; args.pt = allArgs["pt"].empty() ? 96 : allArgs["pt"].as(); - args.type = (MediaSourceEvent::SendRtpArgs::Type)type; + args.data_type = (MediaSourceEvent::SendRtpArgs::DataType)type; args.only_audio = allArgs["only_audio"].as(); args.udp_rtcp_timeout = allArgs["udp_rtcp_timeout"]; args.recv_stream_id = allArgs["recv_stream_id"]; - TraceL << "startSendRtp, pt " << int(args.pt) << " rtp type " << type << " audio " << args.only_audio; - + args.close_delay_ms = allArgs["close_delay_ms"]; src->getOwnerPoller()->async([=]() mutable { - src->startSendRtp(args, [val, headerOut, invoker](uint16_t local_port, const SockException &ex) mutable { - if (ex) { - val["code"] = API::OtherFailed; - val["msg"] = ex.what(); - } - val["local_port"] = local_port; + try { + src->startSendRtp(args, [val, headerOut, invoker](uint16_t local_port, const SockException &ex) mutable { + if (ex) { + val["code"] = API::OtherFailed; + val["msg"] = ex.what(); + } + val["local_port"] = local_port; + invoker(200, headerOut, val.toStyledString()); + }); + } catch (std::exception &ex) { + val["code"] = API::Exception; + val["msg"] = ex.what(); invoker(200, headerOut, val.toStyledString()); - }); + } }); + }; + + api_regist("/index/api/startSendRtp",[](API_ARGS_MAP_ASYNC){ + CHECK_SECRET(); + CHECK_ARGS("vhost", "app", "stream", "ssrc", "dst_url", "dst_port", "is_udp"); + start_send_rtp(false, API_ARGS_VALUE, invoker); + }); + + api_regist("/index/api/startSendRtpPassive",[](API_ARGS_MAP_ASYNC){ + CHECK_SECRET(); + CHECK_ARGS("vhost", "app", "stream", "ssrc"); + start_send_rtp(true, API_ARGS_VALUE, invoker); }); api_regist("/index/api/listRtpSender",[](API_ARGS_MAP_ASYNC){ @@ -1437,45 +1454,6 @@ void installWebApi() { }); }); - api_regist("/index/api/startSendRtpPassive",[](API_ARGS_MAP_ASYNC){ - CHECK_SECRET(); - CHECK_ARGS("vhost", "app", "stream", "ssrc"); - - auto src = MediaSource::find(allArgs["vhost"], allArgs["app"], allArgs["stream"], allArgs["from_mp4"].as()); - if (!src) { - throw ApiRetException("can not find the source stream", API::NotFound); - } - auto type = allArgs["type"].empty() ? (int)MediaSourceEvent::SendRtpArgs::kRtpPS : allArgs["type"].as(); - if (!allArgs["use_ps"].empty()) { - // 兼容之前的use_ps参数 - type = allArgs["use_ps"].as(); - } - - MediaSourceEvent::SendRtpArgs args; - args.passive = true; - args.ssrc = allArgs["ssrc"]; - args.is_udp = false; - args.src_port = allArgs["src_port"]; - args.pt = allArgs["pt"].empty() ? 96 : allArgs["pt"].as(); - args.type = (MediaSourceEvent::SendRtpArgs::Type)type; - args.only_audio = allArgs["only_audio"].as(); - args.recv_stream_id = allArgs["recv_stream_id"]; - //tcp被动服务器等待链接超时时间 - args.tcp_passive_close_delay_ms = allArgs["close_delay_ms"]; - TraceL << "startSendRtpPassive, pt " << int(args.pt) << " rtp type " << type << " audio " << args.only_audio; - - src->getOwnerPoller()->async([=]() mutable { - src->startSendRtp(args, [val, headerOut, invoker](uint16_t local_port, const SockException &ex) mutable { - if (ex) { - val["code"] = API::OtherFailed; - val["msg"] = ex.what(); - } - val["local_port"] = local_port; - invoker(200, headerOut, val.toStyledString()); - }); - }); - }); - api_regist("/index/api/stopSendRtp",[](API_ARGS_MAP_ASYNC){ CHECK_SECRET(); CHECK_ARGS("vhost", "app", "stream"); diff --git a/src/Common/MediaSource.h b/src/Common/MediaSource.h index 0792b75b..06499698 100644 --- a/src/Common/MediaSource.h +++ b/src/Common/MediaSource.h @@ -96,18 +96,29 @@ public: class SendRtpArgs { public: - enum Type { kRtpRAW = 0, kRtpPS = 1, kRtpTS = 2 }; - // 是否采用udp方式发送rtp - bool is_udp = true; + enum DataType { + kRtpES = 0, // 发送ES流 + kRtpPS = 1, // 发送PS流 + kRtpTS = 2 // 发送TS流 + }; + + enum ConType { + kTcpActive = 0, // tcp主动模式,tcp客户端主动连接对方并发送rtp + kUdpActive = 1, // udp主动模式,主动发送数据给对方 + kTcpPassive = 2, // tcp被动模式,tcp服务器,等待对方连接并回复rtp + kUdpPassive = 3 // udp被动方式,等待对方发送nat打洞包,然后回复rtp至打洞包源地址 + }; + // rtp类型 - Type type = kRtpPS; - //发送es流时指定是否只发送纯音频流 + DataType data_type = kRtpPS; + // 连接类型 + ConType con_type = kUdpActive; + + // 发送es流时指定是否只发送纯音频流 bool only_audio = false; - //tcp被动方式 - bool passive = false; // rtp payload type uint8_t pt = 96; - //是否支持同ssrc多服务器发送 + // 是否支持同ssrc多服务器发送 bool ssrc_multi_send = false; // 指定rtp ssrc std::string ssrc; @@ -118,16 +129,16 @@ public: // 发送目标主机地址,可以是ip或域名 std::string dst_url; - //udp发送时,是否开启rr rtcp接收超时判断 + // udp发送时,是否开启rr rtcp接收超时判断 bool udp_rtcp_timeout = false; - //tcp被动发送服务器延时关闭事件,单位毫秒;设置为0时,则使用默认值5000ms - uint32_t tcp_passive_close_delay_ms = 0; - //udp 发送时,rr rtcp包接收超时时间,单位毫秒 + // passive被动、tcp主动模式超时时间 + uint32_t close_delay_ms = 0; + // udp 发送时,rr rtcp包接收超时时间,单位毫秒 uint32_t rtcp_timeout_ms = 30 * 1000; - //udp 发送时,发送sr rtcp包间隔,单位毫秒 + // udp 发送时,发送sr rtcp包间隔,单位毫秒 uint32_t rtcp_send_interval_ms = 5 * 1000; - //发送rtp同时接收,一般用于双向语言对讲, 如果不为空,说明开启接收 + // 发送rtp同时接收,一般用于双向语言对讲, 如果不为空,说明开启接收 std::string recv_stream_id; }; diff --git a/src/Common/MultiMediaSourceMuxer.cpp b/src/Common/MultiMediaSourceMuxer.cpp index cf4c34b3..3f8e7668 100644 --- a/src/Common/MultiMediaSourceMuxer.cpp +++ b/src/Common/MultiMediaSourceMuxer.cpp @@ -386,6 +386,17 @@ void MultiMediaSourceMuxer::startSendRtp(MediaSource &sender, const MediaSourceE weak_ptr weak_self = shared_from_this(); + rtp_sender->setOnClose([weak_self, ssrc](const toolkit::SockException &ex) { + if (auto strong_self = weak_self.lock()) { + // 可能归属线程发生变更 + strong_self->getOwnerPoller(MediaSource::NullMediaSource())->async([=]() { + WarnL << "stream:" << strong_self->shortUrl() << " stop send rtp:" << ssrc << ", reason:" << ex; + strong_self->_rtp_sender.erase(ssrc); + NOTICE_EMIT(BroadcastSendRtpStoppedArgs, Broadcast::kBroadcastSendRtpStopped, *strong_self, ssrc, ex); + }); + } + }); + rtp_sender->startSend(args, [ssrc,ssrc_multi_send, weak_self, rtp_sender, cb, tracks, ring, poller](uint16_t local_port, const SockException &ex) mutable { cb(local_port, ex); auto strong_self = weak_self.lock(); @@ -397,16 +408,6 @@ void MultiMediaSourceMuxer::startSendRtp(MediaSource &sender, const MediaSourceE rtp_sender->addTrack(track); } rtp_sender->addTrackCompleted(); - rtp_sender->setOnClose([weak_self, ssrc](const toolkit::SockException &ex) { - if (auto strong_self = weak_self.lock()) { - // 可能归属线程发生变更 - strong_self->getOwnerPoller(MediaSource::NullMediaSource())->async([=]() { - WarnL << "stream:" << strong_self->shortUrl() << " stop send rtp:" << ssrc << ", reason:" << ex; - strong_self->_rtp_sender.erase(ssrc); - NOTICE_EMIT(BroadcastSendRtpStoppedArgs, Broadcast::kBroadcastSendRtpStopped, *strong_self, ssrc, ex); - }); - } - }); auto reader = ring->attach(poller); reader->setReadCB([rtp_sender](const Frame::Ptr &frame) { diff --git a/src/Rtp/RtpSender.cpp b/src/Rtp/RtpSender.cpp index ed0d836e..3012dda7 100644 --- a/src/Rtp/RtpSender.cpp +++ b/src/Rtp/RtpSender.cpp @@ -40,86 +40,99 @@ void RtpSender::startSend(const MediaSourceEvent::SendRtpArgs &args, const funct if (!_interface) { //重连时不重新创建对象 auto lam = [this](std::shared_ptr> list) { onFlushRtpList(std::move(list)); }; - switch (args.type) { + switch (args.data_type) { case MediaSourceEvent::SendRtpArgs::kRtpPS: _interface = std::make_shared(lam, atoi(args.ssrc.data()), args.pt, true); break; case MediaSourceEvent::SendRtpArgs::kRtpTS: _interface = std::make_shared(lam, atoi(args.ssrc.data()), args.pt, false); break; - case MediaSourceEvent::SendRtpArgs::kRtpRAW: _interface = std::make_shared(lam, atoi(args.ssrc.data()), args.pt, args.only_audio); break; - default: CHECK(0, "invalid rtp type:" + to_string(args.type)); break; + case MediaSourceEvent::SendRtpArgs::kRtpES: _interface = std::make_shared(lam, atoi(args.ssrc.data()), args.pt, args.only_audio); break; + default: CHECK(0, "invalid rtp type: " + to_string(args.data_type)); break; } } + auto delay_ms = _args.close_delay_ms ? _args.close_delay_ms : 5000; weak_ptr weak_self = shared_from_this(); - if (args.passive) { - // tcp被动发流模式 - _args.is_udp = false; - // 默认等待链接 - bool is_wait = true; - try { - auto tcp_listener = Socket::createSocket(_poller, false); - if (args.src_port) { - //指定端口 - if (!tcp_listener->listen(args.src_port)) { - throw std::invalid_argument(StrPrinter << "open tcp passive server failed on port:" << args.src_port - << ", err:" << get_uv_errmsg(true)); - } - is_wait = true; - } else { - auto pr = std::make_pair(tcp_listener, Socket::createSocket(_poller, false)); - //从端口池获取随机端口 - makeSockPair(pr, "::", false, false); - // 随机端口不等待,保证调用者可以知道端口 - is_wait = false; + if (args.con_type == MediaSourceEvent::SendRtpArgs::kTcpPassive) { + auto tcp_listener = Socket::createSocket(_poller, false); + if (args.src_port) { + // 指定端口 + if (!tcp_listener->listen(args.src_port)) { + throw std::invalid_argument(StrPrinter << "open tcp passive server failed on port: " << args.src_port << ", err: " << get_uv_errmsg(true)); } - // tcp服务器默认开启5秒 - auto delay = _args.tcp_passive_close_delay_ms ? _args.tcp_passive_close_delay_ms : 5000; - auto delay_task = _poller->doDelayTask(delay, [tcp_listener, cb, is_wait]() mutable { - if (is_wait) { - cb(0, SockException(Err_timeout, "wait tcp connection timeout")); - } - tcp_listener = nullptr; - return 0; - }); - tcp_listener->setOnAccept([weak_self, cb, delay_task,is_wait](Socket::Ptr &sock, std::shared_ptr &complete) { - auto strong_self = weak_self.lock(); - if (!strong_self) { - return; - } - //立即关闭tcp服务器 - delay_task->cancel(); - strong_self->_socket_rtp = sock; - strong_self->onConnect(); - if (is_wait) { - cb(sock->get_local_port(), SockException()); - } - InfoL << "accept connection from:" << sock->get_peer_ip() << ":" << sock->get_peer_port(); - }); - InfoL << "start tcp passive server on:" << tcp_listener->get_local_port(); - if (!is_wait) { - // 随机端口马上返回端口,保证调用者知道端口 - cb(tcp_listener->get_local_port(), SockException()); - } - } catch (std::exception &ex) { - cb(0, SockException(Err_other, ex.what())); - return; + } else { + auto pr = std::make_pair(tcp_listener, Socket::createSocket(_poller, false)); + // 从端口池获取随机端口 + makeSockPair(pr, "::", true, false); } - return; - } - if (args.is_udp) { + // 定时器持有tcp_listener,保证超时时间内保持监听 + auto delay_task = _poller->doDelayTask(delay_ms, [weak_self, tcp_listener]() mutable { + // 防止循环引用 + tcp_listener = nullptr; + if (auto strong_self = weak_self.lock()) { + strong_self->onClose(SockException(Err_timeout, "wait tcp connection timeout")); + } + return 0; + }); + tcp_listener->setOnAccept([weak_self, delay_task](Socket::Ptr &sock, std::shared_ptr &complete) { + auto strong_self = weak_self.lock(); + if (!strong_self) { + return; + } + delay_task->cancel(); + strong_self->_socket_rtp = sock; + strong_self->onConnect(); + InfoL << "accept tcp connection from: " << sock->get_peer_ip() << ":" << sock->get_peer_port(); + }); + InfoL << "start tcp passive server on: " << tcp_listener->get_local_port(); + cb(tcp_listener->get_local_port(), SockException()); + + } else if (args.con_type == MediaSourceEvent::SendRtpArgs::kUdpPassive) { + if (args.src_port) { + // 指定端口 + if (!_socket_rtp->bindUdpSock(args.src_port, "::", true)) { + throw std::invalid_argument(StrPrinter << "open udp passive server failed on port: " << args.src_port << ", err: " << get_uv_errmsg(true)); + } + } else { + auto pr = std::make_pair(_socket_rtp, Socket::createSocket(_poller, false)); + // 从端口池获取随机端口 + makeSockPair(pr, "::", true, true); + } + auto delay_task = _poller->doDelayTask(delay_ms, [weak_self]() mutable { + if (auto strong_self = weak_self.lock()) { + // 关闭端口 + strong_self->_socket_rtp->closeSock(); + strong_self->onClose(SockException(Err_timeout, "wait udp connection timeout")); + } + return 0; + }); + _socket_rtp->setOnRead([weak_self, delay_task](const Buffer::Ptr &buf, struct sockaddr *addr, int addr_len) { + auto strong_self = weak_self.lock(); + if (!strong_self) { + return; + } + delay_task->cancel(); + strong_self->_socket_rtp->bindPeerAddr(addr, addr_len, true); + // 异步执行onConnect,防止在OnRead回调中调用setOnRead + strong_self->_poller->async([strong_self]() { strong_self->onConnect(); }, false); + InfoL << "accept udp connection from: " << strong_self->_socket_rtp->get_peer_ip() << ":" << strong_self->_socket_rtp->get_peer_port(); + }); + InfoL << "start udp passive server on: " << _socket_rtp->get_local_port(); + cb(_socket_rtp->get_local_port(), SockException()); + + } else if (args.con_type == MediaSourceEvent::SendRtpArgs::kUdpActive) { auto poller = _poller; WorkThreadPool::Instance().getPoller()->async([cb, args, weak_self, poller]() { struct sockaddr_storage addr; - //切换线程目的是为了dns解析放在后台线程执行 + // 切换线程目的是为了dns解析放在后台线程执行 if (!SockUtil::getDomainIP(args.dst_url.data(), args.dst_port, addr, AF_INET, SOCK_DGRAM, IPPROTO_UDP)) { poller->async([args, cb]() { - //切回自己的线程 - cb(0, SockException(Err_dns, StrPrinter << "dns解析域名失败:" << args.dst_url)); + // 切回自己的线程 + cb(0, SockException(Err_dns, StrPrinter << "dns resolution failed: " << args.dst_url)); }); return; } - //dns解析成功 + // dns解析成功 poller->async([args, addr, weak_self, cb]() { - //切回自己的线程 + // 切回自己的线程 auto strong_self = weak_self.lock(); if (!strong_self) { return; @@ -127,15 +140,14 @@ void RtpSender::startSend(const MediaSourceEvent::SendRtpArgs &args, const funct string ifr_ip = addr.ss_family == AF_INET ? "0.0.0.0" : "::"; try { if (args.src_port) { - //指定端口 - if (!strong_self->_socket_rtp->bindUdpSock(args.src_port, ifr_ip)) { - throw std::invalid_argument(StrPrinter << "bindUdpSock failed on port:" << args.src_port - << ", err:" << get_uv_errmsg(true)); + // 指定端口 + if (!strong_self->_socket_rtp->bindUdpSock(args.src_port, ifr_ip, true)) { + throw std::invalid_argument(StrPrinter << "open udp active client failed on port: " << args.src_port << ", err: " << get_uv_errmsg(true)); } } else { auto pr = std::make_pair(strong_self->_socket_rtp, Socket::createSocket(strong_self->_poller, false)); - //从端口池获取随机端口 - makeSockPair(pr, ifr_ip, true); + // 从端口池获取随机端口 + makeSockPair(pr, ifr_ip, true, true); } } catch (std::exception &ex) { cb(0, SockException(Err_other, ex.what())); @@ -146,19 +158,24 @@ void RtpSender::startSend(const MediaSourceEvent::SendRtpArgs &args, const funct cb(strong_self->_socket_rtp->get_local_port(), SockException()); }); }); - } else { - _socket_rtp->connect(args.dst_url, args.dst_port, [cb, weak_self](const SockException &err) { + InfoL << "start udp active send rtp to: " << args.dst_url << ":" << args.dst_port; + + } else if (args.con_type == MediaSourceEvent::SendRtpArgs::kTcpActive) { + _socket_rtp->connect(args.dst_url, args.dst_port,[cb, weak_self](const SockException &err) { auto strong_self = weak_self.lock(); if (strong_self) { if (!err) { - //tcp连接成功 + // tcp连接成功 strong_self->onConnect(); } cb(strong_self->_socket_rtp->get_local_port(), err); } else { cb(0, err); } - }, 5.0F, "::", args.src_port); + }, delay_ms / 1000.0, "::", args.src_port); + InfoL << "start tcp active send rtp to: " << args.dst_url << ":" << args.dst_port; + } else { + CHECK(0, "invalid con type"); } } @@ -168,8 +185,8 @@ void RtpSender::createRtcpSocket() { } _socket_rtcp = Socket::createSocket(_socket_rtp->getPoller(), false); //rtcp端口使用户rtp端口+1 - if(!_socket_rtcp->bindUdpSock(_socket_rtp->get_local_port() + 1, _socket_rtp->get_local_ip(), false)){ - WarnL << "bind rtcp udp socket failed:" << get_uv_errmsg(true); + if(!_socket_rtcp->bindUdpSock(_socket_rtp->get_local_port() + 1, _socket_rtp->get_local_ip(), true)){ + WarnL << "bind rtcp udp socket failed: " << get_uv_errmsg(true); _socket_rtcp = nullptr; return; } @@ -180,12 +197,18 @@ void RtpSender::createRtcpSocket() { _rtcp_context = std::make_shared(); weak_ptr weak_self = shared_from_this(); - _socket_rtcp->setOnRead([weak_self](const Buffer::Ptr &buf, struct sockaddr *, int) { + bool bind_addr = false; + _socket_rtcp->setOnRead([weak_self, bind_addr](const Buffer::Ptr &buf, struct sockaddr *addr, int addr_len) mutable { //接收receive report rtcp auto strong_self = weak_self.lock(); if (!strong_self) { return; } + if (!bind_addr) { + // 收到对方rtcp打洞包后,再回复rtcp + bind_addr = true; + strong_self->_socket_rtcp->bindPeerAddr(addr, addr_len, true); + } auto rtcp_arr = RtcpHeader::loadFromBytes(buf->data(), buf->size()); for (auto &rtcp : rtcp_arr) { strong_self->onRecvRtcp(rtcp); @@ -199,19 +222,19 @@ void RtpSender::onRecvRtcp(RtcpHeader *rtcp) { _rtcp_recv_ticker.resetTime(); } -//连接建立成功事件 -void RtpSender::onConnect(){ +// 连接建立成功事件 +void RtpSender::onConnect() { _is_connect = true; - //加大发送缓存,防止udp丢包之类的问题 + // 加大发送缓存,防止udp丢包之类的问题 SockUtil::setSendBuf(_socket_rtp->rawFD(), 4 * 1024 * 1024); - if (!_args.is_udp) { - //关闭tcp no_delay并开启MSG_MORE, 提高发送性能 + if (_args.con_type == MediaSourceEvent::SendRtpArgs::kTcpActive || _args.con_type == MediaSourceEvent::SendRtpArgs::kTcpPassive) { + // 关闭tcp no_delay并开启MSG_MORE, 提高发送性能 SockUtil::setNoDelay(_socket_rtp->rawFD(), false); _socket_rtp->setSendFlags(SOCKET_DEFAULE_FLAGS | FLAG_MORE); } else if (_args.udp_rtcp_timeout) { createRtcpSocket(); } - //连接建立成功事件 + // 连接建立成功事件 weak_ptr weak_self = shared_from_this(); if (!_args.recv_stream_id.empty()) { mINI ini; @@ -226,11 +249,13 @@ void RtpSender::onConnect(){ } try { strong_self->_rtp_session->onRecv(buf); - } catch (std::exception &ex){ + } catch (std::exception &ex) { SockException err(toolkit::Err_shutdown, ex.what()); strong_self->_rtp_session->shutdown(err); } }); + } else { + _socket_rtp->setOnRead(nullptr); } _socket_rtp->setOnErr([weak_self](const SockException &err) { auto strong_self = weak_self.lock(); @@ -238,12 +263,10 @@ void RtpSender::onConnect(){ strong_self->onErr(err); } }); - //获取本地端口,断开重连后确保端口不变 - _args.src_port = _socket_rtp->get_local_port(); - InfoL << "开始发送 rtp:" << _socket_rtp->get_peer_ip() << ":" << _socket_rtp->get_peer_port() << ", 是否为udp方式:" << _args.is_udp; + InfoL << "startSend rtp success: " << _socket_rtp->get_peer_ip() << ":" << _socket_rtp->get_peer_port() << ", data_type: " << _args.data_type << ", con_type: " << _args.con_type; } -bool RtpSender::addTrack(const Track::Ptr &track){ +bool RtpSender::addTrack(const Track::Ptr &track) { if (_args.only_audio && track->getTrackType() == TrackVideo) { // 如果只发送音频则忽略视频 return false; @@ -251,11 +274,11 @@ bool RtpSender::addTrack(const Track::Ptr &track){ return _interface->addTrack(track); } -void RtpSender::addTrackCompleted(){ +void RtpSender::addTrackCompleted() { _interface->addTrackCompleted(); } -void RtpSender::resetTracks(){ +void RtpSender::resetTracks() { _interface->resetTracks(); } @@ -265,13 +288,12 @@ void RtpSender::flush() { } } -//此函数在其他线程执行 bool RtpSender::inputFrame(const Frame::Ptr &frame) { if (_args.only_audio && frame->getTrackType() == TrackVideo) { // 如果只发送音频则忽略视频 return false; } - //连接成功后才做实质操作(节省cpu资源) + // 连接成功后才做实质操作(节省cpu资源) return _is_connect ? _interface->inputFrame(frame) : false; } @@ -283,20 +305,20 @@ void RtpSender::onSendRtpUdp(const toolkit::Buffer::Ptr &buf, bool check) { _rtcp_context->onRtp(rtp->getSeq(), rtp->getStamp(), rtp->ntp_stamp, 90000 /*not used*/, rtp->size()); if (!check) { - //减少判断次数 + // 减少判断次数 return; } - //每5秒发送一次rtcp + // 每5秒发送一次rtcp if (_rtcp_send_ticker.elapsedTime() > _args.rtcp_send_interval_ms) { _rtcp_send_ticker.resetTime(); - //rtcp ssrc为rtp ssrc + 1 - auto sr = _rtcp_context->createRtcpSR(atoi(_args.ssrc.data()) + 1); - //send sender report rtcp - _socket_rtcp->send(sr); + // rtcp ssrc为rtp ssrc + 1 + auto sr = _rtcp_context->createRtcpSR(atoi(_args.ssrc.data()) + 1); + // send sender report rtcp + _socket_rtcp->send(sr); } if (_rtcp_recv_ticker.elapsedTime() > _args.rtcp_timeout_ms) { - //接收rr rtcp超时 + // 接收rr rtcp超时 WarnL << "recv rr rtcp timeout"; _rtcp_recv_ticker.resetTime(); onClose(SockException(Err_timeout, "recv rr rtcp timeout")); @@ -306,28 +328,36 @@ void RtpSender::onSendRtpUdp(const toolkit::Buffer::Ptr &buf, bool check) { void RtpSender::onClose(const SockException &ex) { auto cb = _on_close; if (cb) { - //在下次循环时触发onClose,原因是防止遍历map时删除元素 + // 在下次循环时触发onClose,原因是防止遍历map时删除元素 _poller->async([cb, ex]() { cb(ex); }, false); } } -//此函数在其他线程执行 -void RtpSender::onFlushRtpList(shared_ptr > rtp_list) { - if(!_is_connect){ - //连接成功后才能发送数据 +// 此函数在其他线程执行 +void RtpSender::onFlushRtpList(shared_ptr> rtp_list) { + if (!_is_connect) { + // 连接成功后才能发送数据 return; } size_t i = 0; auto size = rtp_list->size(); rtp_list->for_each([&](Buffer::Ptr &packet) { - if (_args.is_udp) { - onSendRtpUdp(packet, i == 0); - // udp模式,rtp over tcp前4个字节可以忽略 - _socket_rtp->send(std::make_shared(std::move(packet), RtpPacket::kRtpTcpHeaderSize), nullptr, 0, ++i == size); - } else { - // tcp模式, rtp over tcp前2个字节可以忽略,只保留后续rtp长度的2个字节 - _socket_rtp->send(std::make_shared(std::move(packet), 2), nullptr, 0, ++i == size); + switch (_args.con_type) { + case MediaSourceEvent::SendRtpArgs::kUdpActive: + case MediaSourceEvent::SendRtpArgs::kUdpPassive: { + onSendRtpUdp(packet, i == 0); + // udp模式,rtp over tcp前4个字节可以忽略 + _socket_rtp->send(std::make_shared(std::move(packet), RtpPacket::kRtpTcpHeaderSize), nullptr, 0, ++i == size); + break; + } + case MediaSourceEvent::SendRtpArgs::kTcpActive: + case MediaSourceEvent::SendRtpArgs::kTcpPassive: { + // tcp模式, rtp over tcp前2个字节可以忽略,只保留后续rtp长度的2个字节 + _socket_rtp->send(std::make_shared(std::move(packet), 2), nullptr, 0, ++i == size); + break; + } + default: CHECK(0); } }); } @@ -338,9 +368,9 @@ void RtpSender::onErr(const SockException &ex) { onClose(ex); } -void RtpSender::setOnClose(std::function on_close){ +void RtpSender::setOnClose(std::function on_close) { _on_close = std::move(on_close); } -}//namespace mediakit -#endif// defined(ENABLE_RTPPROXY) +} // namespace mediakit +#endif // defined(ENABLE_RTPPROXY) diff --git a/src/Rtp/RtpSender.h b/src/Rtp/RtpSender.h index 0902d12d..3ad09e8d 100644 --- a/src/Rtp/RtpSender.h +++ b/src/Rtp/RtpSender.h @@ -63,6 +63,9 @@ public: */ virtual void resetTracks() override; + /** + * 设置发送rtp停止回调 + */ void setOnClose(std::function on_close); private: From e3cad7f8faf7cba7ab746e3715b7a5abc91d059f Mon Sep 17 00:00:00 2001 From: u7ko4 <1868690+u7ko4@users.noreply.github.com> Date: Thu, 1 Aug 2024 11:03:26 +0800 Subject: [PATCH 12/18] =?UTF-8?q?=E6=94=AF=E6=8C=81=E7=BB=91=E5=AE=9A?= =?UTF-8?q?=E5=88=B0=E6=8C=87=E5=AE=9A=E7=BD=91=E5=8D=A1=20(#3760)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/source/mk_rtp_server.cpp | 6 ++++-- conf/config.ini | 2 ++ server/WebApi.cpp | 4 ++-- server/main.cpp | 23 ++++++++++++----------- src/Common/config.cpp | 2 ++ src/Common/config.h | 2 ++ src/Rtp/RtpServer.cpp | 2 +- src/Rtp/RtpServer.h | 6 +++--- 8 files changed, 28 insertions(+), 19 deletions(-) diff --git a/api/source/mk_rtp_server.cpp b/api/source/mk_rtp_server.cpp index 4eca8844..316150e7 100644 --- a/api/source/mk_rtp_server.cpp +++ b/api/source/mk_rtp_server.cpp @@ -18,13 +18,15 @@ using namespace mediakit; API_EXPORT mk_rtp_server API_CALL mk_rtp_server_create(uint16_t port, int tcp_mode, const char *stream_id) { RtpServer::Ptr *server = new RtpServer::Ptr(new RtpServer); - (*server)->start(port, MediaTuple { DEFAULT_VHOST, kRtpAppName, stream_id, "" }, (RtpServer::TcpMode)tcp_mode); + GET_CONFIG(std::string, local_ip, General::kListenIP) + (*server)->start(port, local_ip.c_str(), MediaTuple { DEFAULT_VHOST, kRtpAppName, stream_id, "" }, (RtpServer::TcpMode)tcp_mode); return (mk_rtp_server)server; } API_EXPORT mk_rtp_server API_CALL mk_rtp_server_create2(uint16_t port, int tcp_mode, const char *vhost, const char *app, const char *stream_id) { RtpServer::Ptr *server = new RtpServer::Ptr(new RtpServer); - (*server)->start(port, MediaTuple { vhost, app, stream_id, "" }, (RtpServer::TcpMode)tcp_mode); + GET_CONFIG(std::string, local_ip, General::kListenIP) + (*server)->start(port, local_ip.c_str(), MediaTuple { vhost, app, stream_id, "" }, (RtpServer::TcpMode)tcp_mode); return (mk_rtp_server)server; } diff --git a/conf/config.ini b/conf/config.ini index 3fd805c4..6ffeb2e0 100644 --- a/conf/config.ini +++ b/conf/config.ini @@ -128,6 +128,8 @@ wait_add_track_ms=3000 unready_frame_cache=100 #是否启用观看人数变化事件广播,置1则启用,置0则关闭 broadcast_player_count_changed=0 +#绑定的本地网卡ip +listen_ip=:: [hls] #hls写文件的buf大小,调整参数可以提高文件io性能 diff --git a/server/WebApi.cpp b/server/WebApi.cpp index e086fe6c..3f7e18fd 100755 --- a/server/WebApi.cpp +++ b/server/WebApi.cpp @@ -482,7 +482,7 @@ uint16_t openRtpServer(uint16_t local_port, const mediakit::MediaTuple &tuple, i } auto server = s_rtp_server.makeWithAction(key, [&](RtpServer::Ptr server) { - server->start(local_port, tuple, (RtpServer::TcpMode)tcp_mode, local_ip.c_str(), re_use_port, ssrc, only_track, multiplex); + server->start(local_port, local_ip.c_str(), tuple, (RtpServer::TcpMode)tcp_mode, re_use_port, ssrc, only_track, multiplex); }); server->setOnDetach([key](const SockException &ex) { //设置rtp超时移除事件 @@ -1242,7 +1242,7 @@ void installWebApi() { // 兼容老版本请求,新版本去除only_audio参数并新增only_track参数 only_track = 1; } - std::string local_ip = "::"; + GET_CONFIG(std::string, local_ip, General::kListenIP) if (!allArgs["local_ip"].empty()) { local_ip = allArgs["local_ip"]; } diff --git a/server/main.cpp b/server/main.cpp index f418f2f8..92ca10ca 100644 --- a/server/main.cpp +++ b/server/main.cpp @@ -281,6 +281,7 @@ int start_main(int argc,char *argv[]) { }); } + std::string listen_ip = mINI::Instance()[General::kListenIP]; uint16_t shellPort = mINI::Instance()[Shell::kPort]; uint16_t rtspPort = mINI::Instance()[Rtsp::kPort]; uint16_t rtspsPort = mINI::Instance()[Rtsp::kSSLPort]; @@ -362,39 +363,39 @@ int start_main(int argc,char *argv[]) { try { //rtsp服务器,端口默认554 - if (rtspPort) { rtspSrv->start(rtspPort); } + if (rtspPort) { rtspSrv->start(rtspPort, listen_ip); } //rtsps服务器,端口默认322 - if (rtspsPort) { rtspSSLSrv->start(rtspsPort); } + if (rtspsPort) { rtspSSLSrv->start(rtspsPort, listen_ip); } //rtmp服务器,端口默认1935 - if (rtmpPort) { rtmpSrv->start(rtmpPort); } + if (rtmpPort) { rtmpSrv->start(rtmpPort, listen_ip); } //rtmps服务器,端口默认19350 - if (rtmpsPort) { rtmpsSrv->start(rtmpsPort); } + if (rtmpsPort) { rtmpsSrv->start(rtmpsPort, listen_ip); } //http服务器,端口默认80 - if (httpPort) { httpSrv->start(httpPort); } + if (httpPort) { httpSrv->start(httpPort, listen_ip); } //https服务器,端口默认443 - if (httpsPort) { httpsSrv->start(httpsPort); } + if (httpsPort) { httpsSrv->start(httpsPort, listen_ip); } //telnet远程调试服务器 - if (shellPort) { shellSrv->start(shellPort); } + if (shellPort) { shellSrv->start(shellPort, listen_ip); } #if defined(ENABLE_RTPPROXY) //创建rtp服务器 - if (rtpPort) { rtpServer->start(rtpPort); } + if (rtpPort) { rtpServer->start(rtpPort, listen_ip.c_str()); } #endif//defined(ENABLE_RTPPROXY) #if defined(ENABLE_WEBRTC) //webrtc udp服务器 - if (rtcPort) { rtcSrv_udp->start(rtcPort);} + if (rtcPort) { rtcSrv_udp->start(rtcPort, listen_ip);} - if (rtcTcpPort) { rtcSrv_tcp->start(rtcTcpPort);} + if (rtcTcpPort) { rtcSrv_tcp->start(rtcTcpPort, listen_ip);} #endif//defined(ENABLE_WEBRTC) #if defined(ENABLE_SRT) // srt udp服务器 - if (srtPort) { srtSrv->start(srtPort); } + if (srtPort) { srtSrv->start(srtPort, listen_ip); } #endif//defined(ENABLE_SRT) } catch (std::exception &ex) { diff --git a/src/Common/config.cpp b/src/Common/config.cpp index bbb97c39..a8df9bfe 100644 --- a/src/Common/config.cpp +++ b/src/Common/config.cpp @@ -84,6 +84,7 @@ const string kWaitTrackReadyMS = GENERAL_FIELD "wait_track_ready_ms"; const string kWaitAddTrackMS = GENERAL_FIELD "wait_add_track_ms"; const string kUnreadyFrameCache = GENERAL_FIELD "unready_frame_cache"; const string kBroadcastPlayerCountChanged = GENERAL_FIELD "broadcast_player_count_changed"; +const string kListenIP = GENERAL_FIELD "listen_ip"; static onceToken token([]() { mINI::Instance()[kFlowThreshold] = 1024; @@ -99,6 +100,7 @@ static onceToken token([]() { mINI::Instance()[kWaitAddTrackMS] = 3000; mINI::Instance()[kUnreadyFrameCache] = 100; mINI::Instance()[kBroadcastPlayerCountChanged] = 0; + mINI::Instance()[kListenIP] = "::"; }); } // namespace General diff --git a/src/Common/config.h b/src/Common/config.h index e456bdab..7846a65c 100644 --- a/src/Common/config.h +++ b/src/Common/config.h @@ -202,6 +202,8 @@ extern const std::string kWaitAddTrackMS; extern const std::string kUnreadyFrameCache; // 是否启用观看人数变化事件广播,置1则启用,置0则关闭 extern const std::string kBroadcastPlayerCountChanged; +// 绑定的本地网卡ip +extern const std::string kListenIP; } // namespace General namespace Protocol { diff --git a/src/Rtp/RtpServer.cpp b/src/Rtp/RtpServer.cpp index 513c3e56..5c657d19 100644 --- a/src/Rtp/RtpServer.cpp +++ b/src/Rtp/RtpServer.cpp @@ -122,7 +122,7 @@ private: std::shared_ptr _rtcp_addr; }; -void RtpServer::start(uint16_t local_port, const MediaTuple &tuple, TcpMode tcp_mode, const char *local_ip, bool re_use_port, uint32_t ssrc, int only_track, bool multiplex) { +void RtpServer::start(uint16_t local_port, const char *local_ip, const MediaTuple &tuple, TcpMode tcp_mode, bool re_use_port, uint32_t ssrc, int only_track, bool multiplex) { //创建udp服务器 auto poller = EventPollerPool::Instance().getPoller(); Socket::Ptr rtp_socket = Socket::createSocket(poller, true); diff --git a/src/Rtp/RtpServer.h b/src/Rtp/RtpServer.h index 70af06de..f98db95b 100644 --- a/src/Rtp/RtpServer.h +++ b/src/Rtp/RtpServer.h @@ -36,15 +36,15 @@ public: /** * 开启服务器,可能抛异常 * @param local_port 本地端口,0时为随机端口 + * @param local_ip 绑定的本地网卡ip * @param stream_id 流id,置空则使用ssrc * @param tcp_mode tcp服务模式 - * @param local_ip 绑定的本地网卡ip * @param re_use_port 是否设置socket为re_use属性 * @param ssrc 指定的ssrc * @param multiplex 多路复用 */ - void start(uint16_t local_port, const MediaTuple &tuple = MediaTuple{DEFAULT_VHOST, kRtpAppName, "", ""}, TcpMode tcp_mode = PASSIVE, - const char *local_ip = "::", bool re_use_port = true, uint32_t ssrc = 0, int only_track = 0, bool multiplex = false); + void start(uint16_t local_port, const char *local_ip = "::", const MediaTuple &tuple = MediaTuple{DEFAULT_VHOST, kRtpAppName, "", ""}, TcpMode tcp_mode = PASSIVE, + bool re_use_port = true, uint32_t ssrc = 0, int only_track = 0, bool multiplex = false); /** * 连接到tcp服务(tcp主动模式) From 47b8435d7ca629a83a728da605a84b0f24345232 Mon Sep 17 00:00:00 2001 From: xiongguangjie Date: Fri, 2 Aug 2024 21:32:32 +0800 Subject: [PATCH 13/18] =?UTF-8?q?=E4=BF=AE=E5=A4=8DaddStreamProxy=E6=8B=89?= =?UTF-8?q?=E6=B5=81=E4=BB=A3=E7=90=86=E5=90=8C=E5=8D=8F=E8=AE=AE=E4=B8=8B?= =?UTF-8?q?=E5=BC=80=E5=85=B3=E9=97=AE=E9=A2=98=20=20(#3779=20#3770)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Player/PlayerProxy.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Player/PlayerProxy.cpp b/src/Player/PlayerProxy.cpp index b601e16e..2c931212 100644 --- a/src/Player/PlayerProxy.cpp +++ b/src/Player/PlayerProxy.cpp @@ -191,13 +191,13 @@ void PlayerProxy::setDirectProxy() { if (dynamic_pointer_cast(_delegate)) { // rtsp拉流 GET_CONFIG(bool, directProxy, Rtsp::kDirectProxy); - if (directProxy) { + if (directProxy && _option.enable_rtsp) { mediaSource = std::make_shared(_tuple); } } else if (dynamic_pointer_cast(_delegate)) { // rtmp拉流 GET_CONFIG(bool, directProxy, Rtmp::kDirectProxy); - if (directProxy) { + if (directProxy && _option.enable_rtmp) { mediaSource = std::make_shared(_tuple); } } From da704ab2f1184c3c87be64fe18c9f413de101d0f Mon Sep 17 00:00:00 2001 From: xia-chu <771730766@qq.com> Date: Sun, 4 Aug 2024 20:46:05 +0800 Subject: [PATCH 14/18] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=B9=B6=E9=80=82?= =?UTF-8?q?=E9=85=8DZLToolKit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 3rdpart/ZLToolKit | 2 +- src/Common/macros.cpp | 15 --------------- src/Common/macros.h | 21 ++++----------------- src/Rtp/PSDecoder.cpp | 2 +- src/Rtsp/RtspSplitter.cpp | 2 +- 5 files changed, 7 insertions(+), 35 deletions(-) diff --git a/3rdpart/ZLToolKit b/3rdpart/ZLToolKit index fb695d20..abf61efe 160000 --- a/3rdpart/ZLToolKit +++ b/3rdpart/ZLToolKit @@ -1 +1 @@ -Subproject commit fb695d203421d906c473018022a736fa4a7a47e4 +Subproject commit abf61efe35d99a775669d417f0c3e8f8d0992f0e diff --git a/src/Common/macros.cpp b/src/Common/macros.cpp index 5e403b6a..f6529ce4 100644 --- a/src/Common/macros.cpp +++ b/src/Common/macros.cpp @@ -9,7 +9,6 @@ */ #include "macros.h" -#include "Util/util.h" using namespace toolkit; @@ -17,20 +16,6 @@ using namespace toolkit; #include "ZLMVersion.h" #endif -extern "C" { -void Assert_Throw(int failed, const char *exp, const char *func, const char *file, int line, const char *str) { - if (failed) { - _StrPrinter printer; - printer << "Assertion failed: (" << exp ; - if(str && *str){ - printer << ", " << str; - } - printer << "), function " << func << ", file " << file << ", line " << line << "."; - throw mediakit::AssertFailedException(printer); - } -} -} - namespace mediakit { /** diff --git a/src/Common/macros.h b/src/Common/macros.h index b1187810..e9f87826 100644 --- a/src/Common/macros.h +++ b/src/Common/macros.h @@ -11,9 +11,10 @@ #ifndef ZLMEDIAKIT_MACROS_H #define ZLMEDIAKIT_MACROS_H -#include "Util/logger.h" -#include #include +#include +#include "Util/util.h" +#include "Util/logger.h" #if defined(__MACH__) #include #include @@ -40,7 +41,7 @@ #define CHECK_RET(...) \ try { \ CHECK(__VA_ARGS__); \ - } catch (AssertFailedException & ex) { \ + } catch (toolkit::AssertFailedException & ex) { \ WarnL << ex.what(); \ return; \ } @@ -71,22 +72,8 @@ #define VHOST_KEY "vhost" #define DEFAULT_VHOST "__defaultVhost__" -#ifdef __cplusplus -extern "C" { -#endif -extern void Assert_Throw(int failed, const char *exp, const char *func, const char *file, int line, const char *str); -#ifdef __cplusplus -} -#endif - namespace mediakit { -class AssertFailedException : public std::runtime_error { -public: - template - AssertFailedException(T && ...args) : std::runtime_error(std::forward(args)...) {} -}; - extern const char kServerName[]; template diff --git a/src/Rtp/PSDecoder.cpp b/src/Rtp/PSDecoder.cpp index f176670a..4949e236 100644 --- a/src/Rtp/PSDecoder.cpp +++ b/src/Rtp/PSDecoder.cpp @@ -63,7 +63,7 @@ const char *PSDecoder::onSearchPacketTail(const char *data, size_t len) { //解析失败,丢弃所有数据 return data + len; - } catch (AssertFailedException &ex) { + } catch (toolkit::AssertFailedException &ex) { InfoL << "解析 ps 异常: bytes=" << len << ", exception=" << ex.what() << ", hex=" << hexdump(data, MIN(len, 32)); diff --git a/src/Rtsp/RtspSplitter.cpp b/src/Rtsp/RtspSplitter.cpp index 837728f2..aaf8bfe7 100644 --- a/src/Rtsp/RtspSplitter.cpp +++ b/src/Rtsp/RtspSplitter.cpp @@ -72,7 +72,7 @@ ssize_t RtspSplitter::onRecvHeader(const char *data, size_t len) { } try { _parser.parse(data, len); - } catch (mediakit::AssertFailedException &ex){ + } catch (toolkit::AssertFailedException &ex){ if (!_enableRecvRtp) { // 还在握手中,直接中断握手 throw; From 4d8b000198234b00ecde7d029956d2a019ffb596 Mon Sep 17 00:00:00 2001 From: "Weng, Qiang" <92919224+Tsubaki-01@users.noreply.github.com> Date: Sat, 10 Aug 2024 11:06:33 +0800 Subject: [PATCH 15/18] =?UTF-8?q?player=5Fopencv.c=E4=B8=ADbrg24=E5=8F=98?= =?UTF-8?q?=E9=87=8F=E7=9A=84=E5=86=85=E5=AD=98=E5=AF=B9=E9=BD=90=20(#3800?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在本地测试时发现,player_opencv.c程序如果不事先对brg24变量进行内存对齐,之后传入函数,运行到src/Codec/Transcode.cpp:FFmpegSws::inputFrame:sws_scale时可能会出现段错误 --- api/tests/player_opencv.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/api/tests/player_opencv.c b/api/tests/player_opencv.c index 042fd6ef..1f527284 100644 --- a/api/tests/player_opencv.c +++ b/api/tests/player_opencv.c @@ -30,7 +30,13 @@ void API_CALL on_frame_decode(void *user_data, mk_frame_pix frame) { int h = mk_get_av_frame_height(mk_frame_pix_get_av_frame(frame)); #if 1 - uint8_t *brg24 = malloc(w * h * 3); + int align = 32; + size_t pixel_size = 3; + size_t raw_linesize = w * pixel_size; + // 对齐后的宽度 + size_t aligned_linesize = (raw_linesize + align - 1) & ~(align - 1); + size_t total_size = aligned_linesize * h; + uint8_t* brg24 = malloc(total_size); mk_swscale_input_frame(ctx->swscale, frame, brg24); free(brg24); #else @@ -106,4 +112,4 @@ int main(int argc, char *argv[]) { mk_swscale_release(ctx.swscale); } return 0; -} \ No newline at end of file +} From ba1a88f202f1b9629ead0b8720c5183993835b9d Mon Sep 17 00:00:00 2001 From: xiongguangjie Date: Sat, 10 Aug 2024 11:09:54 +0800 Subject: [PATCH 16/18] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dffmpeg5=E4=BB=A5?= =?UTF-8?q?=E4=B8=8A=E9=BB=98=E8=AE=A4=E4=B8=8D=E5=90=88=E5=B9=B6=E5=B8=A7?= =?UTF-8?q?=E5=AF=BC=E8=87=B4=E8=A7=A3=E7=A0=81=E5=A4=B1=E8=B4=A5=E7=9A=84?= =?UTF-8?q?bug=20(#3809=20#3794)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit FFmpegDecoder 默认合并帧,ffmpeg 5以上不支持将不完整的帧送入解码器中,根据 issue #3794 做出修改 --- src/Codec/Transcode.cpp | 1 + src/Codec/Transcode.h | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Codec/Transcode.cpp b/src/Codec/Transcode.cpp index daffe5ac..8c07b9b2 100644 --- a/src/Codec/Transcode.cpp +++ b/src/Codec/Transcode.cpp @@ -439,6 +439,7 @@ FFmpegDecoder::FFmpegDecoder(const Track::Ptr &track, int thread_num, const std: if (codec->capabilities & AV_CODEC_CAP_TRUNCATED) { /* we do not send complete frames */ _context->flags |= AV_CODEC_FLAG_TRUNCATED; + _do_merger = false; } else { // 此时业务层应该需要合帧 _do_merger = true; diff --git a/src/Codec/Transcode.h b/src/Codec/Transcode.h index 4cfaee62..f5b148c7 100644 --- a/src/Codec/Transcode.h +++ b/src/Codec/Transcode.h @@ -114,7 +114,8 @@ private: bool decodeFrame(const char *data, size_t size, uint64_t dts, uint64_t pts, bool live, bool key_frame); private: - bool _do_merger = false; + // default merge frame + bool _do_merger = true; toolkit::Ticker _ticker; onDec _cb; std::shared_ptr _context; From c904019d7d1867da7ee52764b3446da51f80efa6 Mon Sep 17 00:00:00 2001 From: xia-chu <771730766@qq.com> Date: Sat, 10 Aug 2024 11:12:42 +0800 Subject: [PATCH 17/18] =?UTF-8?q?=E6=9B=B4=E6=96=B0ZLToolKit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 3rdpart/ZLToolKit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdpart/ZLToolKit b/3rdpart/ZLToolKit index abf61efe..a6e30e41 160000 --- a/3rdpart/ZLToolKit +++ b/3rdpart/ZLToolKit @@ -1 +1 @@ -Subproject commit abf61efe35d99a775669d417f0c3e8f8d0992f0e +Subproject commit a6e30e41f0c52f9d36c41eb79ac69b50020a6ac9 From 6889afbc76bec74e39cdcf4cdff3379eb4a1a35b Mon Sep 17 00:00:00 2001 From: yujitai Date: Wed, 14 Aug 2024 20:11:24 +0800 Subject: [PATCH 18/18] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=BC=B1=E7=BD=91?= =?UTF-8?q?=E4=B8=A2=E5=8C=85=E5=9C=BA=E6=99=AF=E4=B8=8Brtp=E8=A7=A3?= =?UTF-8?q?=E7=A0=81=E5=99=A8=E5=8F=AF=E8=83=BD=E4=B8=8D=E4=BC=9A=E8=BE=93?= =?UTF-8?q?=E5=87=BA=E9=85=8D=E7=BD=AE=E5=B8=A7=E7=9A=84=E9=97=AE=E9=A2=98?= =?UTF-8?q?=20(#3818)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在弱网丢包场景下,rtp解码器很可能进入gop dropped状态,需要等到idr到来才能继续输出帧, 这会导致idr前面的sps/pps无法被输出, 进而可能导致播放器渲染失败 --- ext-codec/H264Rtp.cpp | 2 +- ext-codec/H265Rtp.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ext-codec/H264Rtp.cpp b/ext-codec/H264Rtp.cpp index 1972a040..f7ae195d 100644 --- a/ext-codec/H264Rtp.cpp +++ b/ext-codec/H264Rtp.cpp @@ -184,7 +184,7 @@ void H264RtpDecoder::outputFrame(const RtpPacket::Ptr &rtp, const H264Frame::Ptr _gop_dropped = false; InfoL << "new gop received, rtp:\r\n" << rtp->dumpString(); } - if (!_gop_dropped) { + if (!_gop_dropped || frame->configFrame()) { RtpCodec::inputFrame(frame); } _frame = obtainFrame(); diff --git a/ext-codec/H265Rtp.cpp b/ext-codec/H265Rtp.cpp index 6f05e67f..69690423 100644 --- a/ext-codec/H265Rtp.cpp +++ b/ext-codec/H265Rtp.cpp @@ -240,7 +240,7 @@ void H265RtpDecoder::outputFrame(const RtpPacket::Ptr &rtp, const H265Frame::Ptr _gop_dropped = false; InfoL << "new gop received, rtp:\r\n" << rtp->dumpString(); } - if (!_gop_dropped) { + if (!_gop_dropped || frame->configFrame()) { RtpCodec::inputFrame(frame); } _frame = obtainFrame();