From 433ecb1c5cea66deb900d5f7cbd392c38a728338 Mon Sep 17 00:00:00 2001 From: yujitai Date: Tue, 7 Nov 2023 19:34:40 +0800 Subject: [PATCH 1/6] =?UTF-8?q?RTC:=20=E4=BF=AE=E5=A4=8Dgop=E6=B2=A1?= =?UTF-8?q?=E6=9C=89sps/pps=E5=AF=BC=E8=87=B4=E7=9A=84=E7=A7=92=E5=BC=80?= =?UTF-8?q?=E5=A4=B1=E8=B4=A5=E9=97=AE=E9=A2=98=20(#2959)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit rtc场景下,如果directProxy设置为1,sdp没有传递sps/pps,因此gop的开始需要是rtp sps/pps配置帧而不是idr关键帧,这样才能保证秒开。 --- src/Extension/H264Rtp.cpp | 12 +++++++----- src/Extension/H264Rtp.h | 1 + src/Extension/H265Rtp.cpp | 10 ++++++---- src/Extension/H265Rtp.h | 1 + 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/Extension/H264Rtp.cpp b/src/Extension/H264Rtp.cpp index 28c775c6..442205a6 100644 --- a/src/Extension/H264Rtp.cpp +++ b/src/Extension/H264Rtp.cpp @@ -44,13 +44,15 @@ H264Frame::Ptr H264RtpDecoder::obtainFrame() { bool H264RtpDecoder::inputRtp(const RtpPacket::Ptr &rtp, bool key_pos) { auto seq = rtp->getSeq(); - auto ret = decodeRtp(rtp); - if (!_gop_dropped && seq != (uint16_t) (_last_seq + 1) && _last_seq) { + auto last_is_gop = _is_gop; + _is_gop = decodeRtp(rtp); + if (!_gop_dropped && seq != (uint16_t)(_last_seq + 1) && _last_seq) { _gop_dropped = true; WarnL << "start drop h264 gop, last seq:" << _last_seq << ", rtp:\r\n" << rtp->dumpString(); } _last_seq = seq; - return ret; + // 确保有sps rtp的时候,gop从sps开始;否则从关键帧开始 + return _is_gop && !last_is_gop; } /* @@ -74,7 +76,7 @@ bool H264RtpDecoder::singleFrame(const RtpPacket::Ptr &rtp, const uint8_t *ptr, _frame->_buffer.assign("\x00\x00\x00\x01", 4); _frame->_buffer.append((char *) ptr, size); _frame->_pts = stamp; - auto key = _frame->keyFrame(); + auto key = _frame->keyFrame() || _frame->configFrame(); outputFrame(rtp, _frame); return key; } @@ -127,7 +129,7 @@ bool H264RtpDecoder::mergeFu(const RtpPacket::Ptr &rtp, const uint8_t *ptr, ssiz if (!fu->end_bit) { //非末尾包 - return fu->start_bit ? _frame->keyFrame() : false; + return fu->start_bit ? (_frame->keyFrame() || _frame->configFrame()) : false; } //确保下一次fu必须收到第一个包 diff --git a/src/Extension/H264Rtp.h b/src/Extension/H264Rtp.h index 31200cb8..98d49cda 100644 --- a/src/Extension/H264Rtp.h +++ b/src/Extension/H264Rtp.h @@ -51,6 +51,7 @@ private: void outputFrame(const RtpPacket::Ptr &rtp, const H264Frame::Ptr &frame); private: + bool _is_gop = false; bool _gop_dropped = false; bool _fu_dropped = true; uint16_t _last_seq = 0; diff --git a/src/Extension/H265Rtp.cpp b/src/Extension/H265Rtp.cpp index 23bbb109..22866ff4 100644 --- a/src/Extension/H265Rtp.cpp +++ b/src/Extension/H265Rtp.cpp @@ -163,7 +163,7 @@ bool H265RtpDecoder::mergeFu(const RtpPacket::Ptr &rtp, const uint8_t *ptr, ssiz if (!e_bit) { //非末尾包 - return s_bit ? _frame->keyFrame() : false; + return s_bit ? (_frame->keyFrame() || _frame->configFrame()) : false; } //确保下一次fu必须收到第一个包 @@ -175,13 +175,15 @@ bool H265RtpDecoder::mergeFu(const RtpPacket::Ptr &rtp, const uint8_t *ptr, ssiz bool H265RtpDecoder::inputRtp(const RtpPacket::Ptr &rtp, bool) { auto seq = rtp->getSeq(); - auto ret = decodeRtp(rtp); + auto last_is_gop = _is_gop; + _is_gop = decodeRtp(rtp); if (!_gop_dropped && seq != (uint16_t) (_last_seq + 1) && _last_seq) { _gop_dropped = true; WarnL << "start drop h265 gop, last seq:" << _last_seq << ", rtp:\r\n" << rtp->dumpString(); } _last_seq = seq; - return ret; + // 确保有sps rtp的时候,gop从sps开始;否则从关键帧开始 + return _is_gop && !last_is_gop; } bool H265RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtp) { @@ -220,7 +222,7 @@ bool H265RtpDecoder::singleFrame(const RtpPacket::Ptr &rtp, const uint8_t *ptr, _frame->_buffer.assign("\x00\x00\x00\x01", 4); _frame->_buffer.append((char *) ptr, size); _frame->_pts = stamp; - auto key = _frame->keyFrame(); + auto key = _frame->keyFrame() || _frame->configFrame(); outputFrame(rtp, _frame); return key; } diff --git a/src/Extension/H265Rtp.h b/src/Extension/H265Rtp.h index cd9702f4..569d8091 100644 --- a/src/Extension/H265Rtp.h +++ b/src/Extension/H265Rtp.h @@ -51,6 +51,7 @@ private: void outputFrame(const RtpPacket::Ptr &rtp, const H265Frame::Ptr &frame); private: + bool _is_gop = false; bool _using_donl_field = false; bool _gop_dropped = false; bool _fu_dropped = true; From aa5343effaadb307f93650c563795f1f9f8d5403 Mon Sep 17 00:00:00 2001 From: xia-chu <771730766@qq.com> Date: Tue, 7 Nov 2023 23:15:16 +0800 Subject: [PATCH 2/6] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dhttp=E5=AE=A2=E6=88=B7?= =?UTF-8?q?=E7=AB=AF=E8=AE=BE=E7=BD=AEtcp=E8=BF=9E=E6=8E=A5=E8=B6=85?= =?UTF-8?q?=E6=97=B6=E6=97=B6=E9=97=B4=E5=8D=95=E4=BD=8D=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 须毫秒转换为秒 --- src/Http/HttpClient.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Http/HttpClient.cpp b/src/Http/HttpClient.cpp index 328872e6..dd1546db 100644 --- a/src/Http/HttpClient.cpp +++ b/src/Http/HttpClient.cpp @@ -80,7 +80,7 @@ void HttpClient::sendRequest(const string &url) { } if (!alive() || host_changed) { - startConnect(host, port, _wait_header_ms); + startConnect(host, port, _wait_header_ms / 1000.0f); } else { SockException ex; onConnect_l(ex); From 77b3c4312e93c937fbed6d2761727f52b5481889 Mon Sep 17 00:00:00 2001 From: xia-chu <771730766@qq.com> Date: Tue, 7 Nov 2023 23:15:55 +0800 Subject: [PATCH 3/6] =?UTF-8?q?=E6=9B=B4=E6=96=B0zltoolkit=EF=BC=8C?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=89=93=E5=BC=80=E6=96=87=E4=BB=B6=E6=8F=8F?= =?UTF-8?q?=E8=BF=B0=E7=AC=A6=E5=A4=AA=E5=A4=9A=E6=97=B6=EF=BC=8Ctcp?= =?UTF-8?q?=E6=9C=8D=E5=8A=A1=E5=99=A8=E6=97=A0=E5=93=8D=E5=BA=94=E7=9A=84?= =?UTF-8?q?bug=20(#2946)?= 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 a4771b35..12c2ec7b 160000 --- a/3rdpart/ZLToolKit +++ b/3rdpart/ZLToolKit @@ -1 +1 @@ -Subproject commit a4771b353d9ebe1bc162364237a31f4184a7c435 +Subproject commit 12c2ec7b07dce1b6a0543d9979f5c5231f69e828 From 1609fe67d735f4e520ae2d0de7b5862a771bba52 Mon Sep 17 00:00:00 2001 From: fruit Juice <2317232721@qq.com> Date: Tue, 7 Nov 2023 23:36:41 +0800 Subject: [PATCH 4/6] =?UTF-8?q?Track=E6=96=B0=E5=A2=9Eupdate=E6=96=B9?= =?UTF-8?q?=E6=B3=95=EF=BC=8C=E6=94=AF=E6=8C=81=E6=9B=B4=E6=96=B0=E5=AE=BD?= =?UTF-8?q?=E9=AB=98=E9=87=87=E6=A0=B7=E7=8E=87=E7=AD=89=E4=BF=A1=E6=81=AF?= =?UTF-8?q?=20(#2960)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 当变分辨率时候,实时更新MP4封装层的参数信息,避免出现封装层与编码(SPS)层视频宽高不一样,造成解码参数错误花屏;同时也支持更新音频采样率等信息。 --------- Co-authored-by: xia-chu <771730766@qq.com> --- src/Common/MultiMediaSourceMuxer.cpp | 1 + src/Extension/AAC.cpp | 98 +++++++++++++++------------- src/Extension/AAC.h | 1 + src/Extension/H264.cpp | 4 ++ src/Extension/H264.h | 1 + src/Extension/H265.cpp | 4 ++ src/Extension/H265.h | 1 + src/Extension/Track.h | 5 ++ src/Player/PlayerProxy.cpp | 1 + src/Record/MP4Muxer.cpp | 1 + src/Rtmp/Rtmp.cpp | 2 + 11 files changed, 75 insertions(+), 44 deletions(-) diff --git a/src/Common/MultiMediaSourceMuxer.cpp b/src/Common/MultiMediaSourceMuxer.cpp index bbb9b357..e3151093 100644 --- a/src/Common/MultiMediaSourceMuxer.cpp +++ b/src/Common/MultiMediaSourceMuxer.cpp @@ -45,6 +45,7 @@ static string getTrackInfoStr(const TrackSource *track_src){ _StrPrinter codec_info; auto tracks = track_src->getTracks(true); for (auto &track : tracks) { + track->update(); auto codec_type = track->getTrackType(); codec_info << track->getCodecName(); switch (codec_type) { diff --git a/src/Extension/AAC.cpp b/src/Extension/AAC.cpp index 1047a8b3..27f18359 100644 --- a/src/Extension/AAC.cpp +++ b/src/Extension/AAC.cpp @@ -21,53 +21,56 @@ namespace mediakit{ #ifndef ENABLE_MP4 unsigned const samplingFrequencyTable[16] = { 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350, 0, 0, 0 }; -class AdtsHeader{ +class AdtsHeader { public: - unsigned int syncword = 0; //12 bslbf 同步字The bit string ‘1111 1111 1111’,说明一个ADTS帧的开始 - unsigned int id; //1 bslbf MPEG 标示符, 设置为1 - unsigned int layer; //2 uimsbf Indicates which layer is used. Set to ‘00’ - unsigned int protection_absent; //1 bslbf 表示是否误码校验 - unsigned int profile; //2 uimsbf 表示使用哪个级别的AAC,如01 Low Complexity(LC)--- AACLC - unsigned int sf_index; //4 uimsbf 表示使用的采样率下标 - unsigned int private_bit; //1 bslbf - unsigned int channel_configuration; //3 uimsbf 表示声道数 - unsigned int original; //1 bslbf - unsigned int home; //1 bslbf - //下面的为改变的参数即每一帧都不同 - unsigned int copyright_identification_bit; //1 bslbf - unsigned int copyright_identification_start; //1 bslbf + unsigned int syncword = 0; // 12 bslbf 同步字The bit string ‘1111 1111 1111’,说明一个ADTS帧的开始 + unsigned int id; // 1 bslbf MPEG 标示符, 设置为1 + unsigned int layer; // 2 uimsbf Indicates which layer is used. Set to ‘00’ + unsigned int protection_absent; // 1 bslbf 表示是否误码校验 + unsigned int profile; // 2 uimsbf 表示使用哪个级别的AAC,如01 Low Complexity(LC)--- AACLC + unsigned int sf_index; // 4 uimsbf 表示使用的采样率下标 + unsigned int private_bit; // 1 bslbf + unsigned int channel_configuration; // 3 uimsbf 表示声道数 + unsigned int original; // 1 bslbf + unsigned int home; // 1 bslbf + // 下面的为改变的参数即每一帧都不同 + unsigned int copyright_identification_bit; // 1 bslbf + unsigned int copyright_identification_start; // 1 bslbf unsigned int aac_frame_length; // 13 bslbf 一个ADTS帧的长度包括ADTS头和raw data block - unsigned int adts_buffer_fullness; //11 bslbf 0x7FF 说明是码率可变的码流 - //no_raw_data_blocks_in_frame 表示ADTS帧中有number_of_raw_data_blocks_in_frame + 1个AAC原始帧. - //所以说number_of_raw_data_blocks_in_frame == 0 - //表示说ADTS帧中有一个AAC数据块并不是说没有。(一个AAC原始帧包含一段时间内1024个采样及相关数据) - unsigned int no_raw_data_blocks_in_frame; //2 uimsfb + unsigned int adts_buffer_fullness; // 11 bslbf 0x7FF 说明是码率可变的码流 + // no_raw_data_blocks_in_frame 表示ADTS帧中有number_of_raw_data_blocks_in_frame + 1个AAC原始帧. + // 所以说number_of_raw_data_blocks_in_frame == 0 + // 表示说ADTS帧中有一个AAC数据块并不是说没有。(一个AAC原始帧包含一段时间内1024个采样及相关数据) + unsigned int no_raw_data_blocks_in_frame; // 2 uimsfb }; static void dumpAdtsHeader(const AdtsHeader &hed, uint8_t *out) { - out[0] = (hed.syncword >> 4 & 0xFF); //8bit - out[1] = (hed.syncword << 4 & 0xF0); //4 bit - out[1] |= (hed.id << 3 & 0x08); //1 bit - out[1] |= (hed.layer << 1 & 0x06); //2bit - out[1] |= (hed.protection_absent & 0x01); //1 bit + out[0] = (hed.syncword >> 4 & 0xFF); // 8bit + out[1] = (hed.syncword << 4 & 0xF0); // 4 bit + out[1] |= (hed.id << 3 & 0x08); // 1 bit + out[1] |= (hed.layer << 1 & 0x06); // 2bit + out[1] |= (hed.protection_absent & 0x01); // 1 bit out[2] = (hed.profile << 6 & 0xC0); // 2 bit - out[2] |= (hed.sf_index << 2 & 0x3C); //4bit - out[2] |= (hed.private_bit << 1 & 0x02); //1 bit - out[2] |= (hed.channel_configuration >> 2 & 0x03); //1 bit - out[3] = (hed.channel_configuration << 6 & 0xC0); // 2 bit - out[3] |= (hed.original << 5 & 0x20); //1 bit - out[3] |= (hed.home << 4 & 0x10); //1 bit - out[3] |= (hed.copyright_identification_bit << 3 & 0x08); //1 bit - out[3] |= (hed.copyright_identification_start << 2 & 0x04); //1 bit - out[3] |= (hed.aac_frame_length >> 11 & 0x03); //2 bit - out[4] = (hed.aac_frame_length >> 3 & 0xFF); //8 bit - out[5] = (hed.aac_frame_length << 5 & 0xE0); //3 bit - out[5] |= (hed.adts_buffer_fullness >> 6 & 0x1F); //5 bit - out[6] = (hed.adts_buffer_fullness << 2 & 0xFC); //6 bit - out[6] |= (hed.no_raw_data_blocks_in_frame & 0x03); //2 bit + out[2] |= (hed.sf_index << 2 & 0x3C); // 4bit + out[2] |= (hed.private_bit << 1 & 0x02); // 1 bit + out[2] |= (hed.channel_configuration >> 2 & 0x03); // 1 bit + out[3] = (hed.channel_configuration << 6 & 0xC0); // 2 bit + out[3] |= (hed.original << 5 & 0x20); // 1 bit + out[3] |= (hed.home << 4 & 0x10); // 1 bit + out[3] |= (hed.copyright_identification_bit << 3 & 0x08); // 1 bit + out[3] |= (hed.copyright_identification_start << 2 & 0x04); // 1 bit + out[3] |= (hed.aac_frame_length >> 11 & 0x03); // 2 bit + out[4] = (hed.aac_frame_length >> 3 & 0xFF); // 8 bit + out[5] = (hed.aac_frame_length << 5 & 0xE0); // 3 bit + out[5] |= (hed.adts_buffer_fullness >> 6 & 0x1F); // 5 bit + out[6] = (hed.adts_buffer_fullness << 2 & 0xFC); // 6 bit + out[6] |= (hed.no_raw_data_blocks_in_frame & 0x03); // 2 bit } -static void parseAacConfig(const string &config, AdtsHeader &adts) { +static bool parseAacConfig(const string &config, AdtsHeader &adts) { + if (config.size() < 2) { + return false; + } uint8_t cfg1 = config[0]; uint8_t cfg2 = config[1]; @@ -94,6 +97,7 @@ static void parseAacConfig(const string &config, AdtsHeader &adts) { adts.aac_frame_length = 7; adts.adts_buffer_fullness = 2047; adts.no_raw_data_blocks_in_frame = 0; + return true; } #endif// ENABLE_MP4 @@ -168,10 +172,12 @@ int dumpAacConfig(const string &config, size_t length, uint8_t *out, size_t out_ #endif } -bool parseAacConfig(const string &config, int &samplerate, int &channels){ +bool parseAacConfig(const string &config, int &samplerate, int &channels) { #ifndef ENABLE_MP4 AdtsHeader header; - parseAacConfig(config, header); + if (!parseAacConfig(config, header)) { + return false; + } samplerate = samplingFrequencyTable[header.sf_index]; channels = header.channel_configuration; return true; @@ -326,11 +332,14 @@ bool AACTrack::inputFrame_l(const Frame::Ptr &frame) { return false; } +bool AACTrack::update() { + return parseAacConfig(_cfg, _sampleRate, _channel); +} + void AACTrack::onReady() { - if (_cfg.size() < 2) { - return; + if (!parseAacConfig(_cfg, _sampleRate, _channel)) { + _cfg.clear(); } - parseAacConfig(_cfg, _sampleRate, _channel); } Track::Ptr AACTrack::clone() { @@ -342,6 +351,7 @@ Sdp::Ptr AACTrack::getSdp() { WarnL << getCodecName() << " Track未准备好"; return nullptr; } + update(); return std::make_shared(getConfig(), getAudioSampleRate(), getAudioChannel(), getBitRate() / 1024); } diff --git a/src/Extension/AAC.h b/src/Extension/AAC.h index b95fc6f2..32cf934b 100644 --- a/src/Extension/AAC.h +++ b/src/Extension/AAC.h @@ -52,6 +52,7 @@ public: int getAudioSampleRate() const override; int getAudioSampleBit() const override; bool inputFrame(const Frame::Ptr &frame) override; + bool update() override; private: void onReady(); diff --git a/src/Extension/H264.cpp b/src/Extension/H264.cpp index eeccd600..eda6b188 100644 --- a/src/Extension/H264.cpp +++ b/src/Extension/H264.cpp @@ -168,6 +168,10 @@ bool H264Track::inputFrame(const Frame::Ptr &frame) { return ret; } +bool H264Track::update() { + return getAVCInfo(_sps, _width, _height, _fps); +} + void H264Track::onReady() { if (!getAVCInfo(_sps, _width, _height, _fps)) { _sps.clear(); diff --git a/src/Extension/H264.h b/src/Extension/H264.h index 30a8f747..0afe593d 100644 --- a/src/Extension/H264.h +++ b/src/Extension/H264.h @@ -128,6 +128,7 @@ public: int getVideoWidth() const override; float getVideoFps() const override; bool inputFrame(const Frame::Ptr &frame) override; + bool update() override; private: void onReady(); diff --git a/src/Extension/H265.cpp b/src/Extension/H265.cpp index 9985a9cb..926ab2a4 100644 --- a/src/Extension/H265.cpp +++ b/src/Extension/H265.cpp @@ -144,6 +144,10 @@ bool H265Track::inputFrame_l(const Frame::Ptr &frame) { return ret; } +bool H265Track::update() { + return getHEVCInfo(_vps, _sps, _width, _height, _fps); +} + void H265Track::onReady() { if (!getHEVCInfo(_vps, _sps, _width, _height, _fps)) { _vps.clear(); diff --git a/src/Extension/H265.h b/src/Extension/H265.h index 521663f4..912f1f46 100644 --- a/src/Extension/H265.h +++ b/src/Extension/H265.h @@ -150,6 +150,7 @@ public: int getVideoHeight() const override; float getVideoFps() const override; bool inputFrame(const Frame::Ptr &frame) override; + bool update() override; private: void onReady(); diff --git a/src/Extension/Track.h b/src/Extension/Track.h index e7aa2b7c..3a1b8519 100644 --- a/src/Extension/Track.h +++ b/src/Extension/Track.h @@ -39,6 +39,11 @@ public: */ virtual Track::Ptr clone() = 0; + /** + * 更新track信息,比如触发sps/pps解析 + */ + virtual bool update() { return false; } + /** * 生成sdp * @return sdp对象 diff --git a/src/Player/PlayerProxy.cpp b/src/Player/PlayerProxy.cpp index 2a319ff8..73e46b71 100644 --- a/src/Player/PlayerProxy.cpp +++ b/src/Player/PlayerProxy.cpp @@ -70,6 +70,7 @@ void PlayerProxy::setTranslationInfo() _transtalion_info.stream_info.clear(); auto tracks = _muxer->getTracks(); for (auto &track : tracks) { + track->update(); _transtalion_info.stream_info.emplace_back(); auto &back = _transtalion_info.stream_info.back(); back.bitrate = track->getBitRate(); diff --git a/src/Record/MP4Muxer.cpp b/src/Record/MP4Muxer.cpp index c79cc132..5458a31d 100644 --- a/src/Record/MP4Muxer.cpp +++ b/src/Record/MP4Muxer.cpp @@ -198,6 +198,7 @@ bool MP4MuxerInterface::addTrack(const Track::Ptr &track) { return false; } + track->update(); switch (track->getCodecId()) { case CodecG711A: case CodecG711U: diff --git a/src/Rtmp/Rtmp.cpp b/src/Rtmp/Rtmp.cpp index 140afeed..33c46f11 100644 --- a/src/Rtmp/Rtmp.cpp +++ b/src/Rtmp/Rtmp.cpp @@ -57,6 +57,7 @@ AudioMeta::AudioMeta(const AudioTrack::Ptr &audio) { } uint8_t getAudioRtmpFlags(const Track::Ptr &track) { + track->update(); switch (track->getTrackType()) { case TrackAudio: { auto audioTrack = std::dynamic_pointer_cast(track); @@ -115,6 +116,7 @@ uint8_t getAudioRtmpFlags(const Track::Ptr &track) { void Metadata::addTrack(AMFValue &metadata, const Track::Ptr &track) { Metadata::Ptr new_metadata; + track->update(); switch (track->getTrackType()) { case TrackVideo: { new_metadata = std::make_shared(std::dynamic_pointer_cast(track)); From f8285a3f6c89e3fc01c03d73702c4cd7c58206ec Mon Sep 17 00:00:00 2001 From: Dw9 Date: Tue, 7 Nov 2023 23:38:58 +0800 Subject: [PATCH 5/6] =?UTF-8?q?startSendRtp=E6=8E=A5=E5=8F=A3=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E6=94=AF=E6=8C=81=E5=90=8Cssrc=E5=90=91=E5=A4=9A?= =?UTF-8?q?=E4=B8=AA=E6=9C=8D=E5=8A=A1=E5=99=A8=E6=8E=A8=E6=B5=81=20(#2951?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增ssrc_multi_send参数,支持同ssrc向多个服务器推流,兼容当前startSendRtp/stopSendRtp接口 --- postman/ZLMediaKit.postman_collection.json | 8 +++++++- server/WebApi.cpp | 1 + src/Common/MediaSource.h | 2 ++ src/Common/MultiMediaSourceMuxer.cpp | 9 +++++++-- src/Common/MultiMediaSourceMuxer.h | 2 +- 5 files changed, 18 insertions(+), 4 deletions(-) diff --git a/postman/ZLMediaKit.postman_collection.json b/postman/ZLMediaKit.postman_collection.json index fdda9d2a..d8a49900 100644 --- a/postman/ZLMediaKit.postman_collection.json +++ b/postman/ZLMediaKit.postman_collection.json @@ -1710,10 +1710,16 @@ "value": "obs", "description": "流id,例如 obs" }, + { + "key": "ssrc_multi_send", + "value": "0", + "description": "是否支持同ssrc推流到多个上级服务器,该参数非必选参数 默认false", + "disabled": true + }, { "key": "ssrc", "value": "1", - "description": "rtp推流的ssrc,ssrc不同时,可以推流到多个上级服务器" + "description": "rtp推流的ssrc" }, { "key": "dst_url", diff --git a/server/WebApi.cpp b/server/WebApi.cpp index 5c63c943..1ddc0c0f 100755 --- a/server/WebApi.cpp +++ b/server/WebApi.cpp @@ -1245,6 +1245,7 @@ void installWebApi() { args.passive = false; 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"]; diff --git a/src/Common/MediaSource.h b/src/Common/MediaSource.h index ffced627..73306385 100644 --- a/src/Common/MediaSource.h +++ b/src/Common/MediaSource.h @@ -104,6 +104,8 @@ public: bool passive = false; // rtp payload type uint8_t pt = 96; + //是否支持同ssrc多服务器发送 + bool ssrc_multi_send = false; // 指定rtp ssrc std::string ssrc; // 指定本地发送端口 diff --git a/src/Common/MultiMediaSourceMuxer.cpp b/src/Common/MultiMediaSourceMuxer.cpp index e3151093..99898b95 100644 --- a/src/Common/MultiMediaSourceMuxer.cpp +++ b/src/Common/MultiMediaSourceMuxer.cpp @@ -291,12 +291,14 @@ void MultiMediaSourceMuxer::startSendRtp(MediaSource &sender, const MediaSourceE auto ring = _ring; auto ssrc = args.ssrc; + auto ssrc_multi_send = args.ssrc_multi_send; auto tracks = getTracks(false); auto poller = getOwnerPoller(sender); auto rtp_sender = std::make_shared(poller); + weak_ptr weak_self = shared_from_this(); - rtp_sender->startSend(args, [ssrc, weak_self, rtp_sender, cb, tracks, ring, poller](uint16_t local_port, const SockException &ex) mutable { + 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(); if (!strong_self || ex) { @@ -325,7 +327,10 @@ void MultiMediaSourceMuxer::startSendRtp(MediaSource &sender, const MediaSourceE // 可能归属线程发生变更 strong_self->getOwnerPoller(MediaSource::NullMediaSource())->async([=]() { - strong_self->_rtp_sender[ssrc] = std::move(reader); + if(!ssrc_multi_send) { + strong_self->_rtp_sender.erase(ssrc); + } + strong_self->_rtp_sender.emplace(ssrc,reader); }); }); #else diff --git a/src/Common/MultiMediaSourceMuxer.h b/src/Common/MultiMediaSourceMuxer.h index 4db34d42..45ab7623 100644 --- a/src/Common/MultiMediaSourceMuxer.h +++ b/src/Common/MultiMediaSourceMuxer.h @@ -168,7 +168,7 @@ private: toolkit::Ticker _last_check; Stamp _stamp[2]; std::weak_ptr _track_listener; - std::unordered_map _rtp_sender; + std::unordered_multimap _rtp_sender; FMP4MediaSourceMuxer::Ptr _fmp4; RtmpMediaSourceMuxer::Ptr _rtmp; RtspMediaSourceMuxer::Ptr _rtsp; From 6888f20d74b35558c0c3efd7cf26cb20014c6668 Mon Sep 17 00:00:00 2001 From: waken <33921191+mc373906408@users.noreply.github.com> Date: Thu, 9 Nov 2023 11:26:13 +0800 Subject: [PATCH 6/6] =?UTF-8?q?=E6=96=B0=E5=A2=9EopenRtpServerMultiplex?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=EF=BC=8C=E6=94=AF=E6=8C=81=E5=88=9B=E5=BB=BA?= =?UTF-8?q?=E5=A4=9A=E8=B7=AF=E5=A4=8D=E7=94=A8RTP=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E5=99=A8=E7=AB=AF=E5=8F=A3=20(#2954)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #2953 --- postman/ZLMediaKit.postman_collection.json | 56 ++++++++++++++++++++-- server/WebApi.cpp | 22 ++++++++- server/WebApi.h | 2 +- src/Rtp/RtpServer.cpp | 5 +- src/Rtp/RtpServer.h | 3 +- 5 files changed, 78 insertions(+), 10 deletions(-) diff --git a/postman/ZLMediaKit.postman_collection.json b/postman/ZLMediaKit.postman_collection.json index d8a49900..784bb60c 100644 --- a/postman/ZLMediaKit.postman_collection.json +++ b/postman/ZLMediaKit.postman_collection.json @@ -1,9 +1,10 @@ { "info": { - "_postman_id": "39e8a1df-cc8e-4e3f-bf5e-197c86e7bf0f", + "_postman_id": "509e5f6b-728c-4d5f-b3e8-521d76b2cc7a", "name": "ZLMediaKit", "description": "媒体服务器", - "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", + "_exporter_id": "29185956" }, "item": [ { @@ -918,7 +919,7 @@ "method": "GET", "header": [], "url": { - "raw": "{{ZLMediaKit_URL}}/index/api/broadcastMessage?secret={{ZLMediaKit_secret}}&schema=rtsp&vhost={{defaultVhost}}&app=live&stream=test&msg=Hello zlmediakit123", + "raw": "{{ZLMediaKit_URL}}/index/api/broadcastMessage?secret={{ZLMediaKit_secret}}&schema=rtsp&vhost={{defaultVhost}}&app=live&stream=test&msg=Hello ZLMediakit", "host": [ "{{ZLMediaKit_URL}}" ], @@ -1247,7 +1248,7 @@ }, { "key": "stamp", - "value": 1000, + "value": "1000", "description": "要设置的录像播放位置" } ] @@ -1478,6 +1479,53 @@ }, "response": [] }, + { + "name": "创建多路复用RTP服务器(openRtpServerMultiplex)", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{ZLMediaKit_URL}}/index/api/openRtpServer?secret={{ZLMediaKit_secret}}&port=0&tcp_mode=1&stream_id=test", + "host": [ + "{{ZLMediaKit_URL}}" + ], + "path": [ + "index", + "api", + "openRtpServer" + ], + "query": [ + { + "key": "secret", + "value": "{{ZLMediaKit_secret}}", + "description": "api操作密钥(配置文件配置)" + }, + { + "key": "port", + "value": "0", + "description": "绑定的端口,0时为随机端口" + }, + { + "key": "tcp_mode", + "value": "1", + "description": "tcp模式,0时为不启用tcp监听,1时为启用tcp监听" + }, + { + "key": "stream_id", + "value": "test", + "description": "该端口绑定的流id\n" + }, + { + "key": "only_audio", + "value": "0", + "description": "是否为单音频track,用于语音对讲", + "disabled": true + } + ] + } + }, + "response": [] + }, { "name": "连接RTP服务器(connectRtpServer)", "request": { diff --git a/server/WebApi.cpp b/server/WebApi.cpp index 1ddc0c0f..7833b933 100755 --- a/server/WebApi.cpp +++ b/server/WebApi.cpp @@ -404,7 +404,7 @@ Value makeMediaSourceJson(MediaSource &media){ } #if defined(ENABLE_RTPPROXY) -uint16_t openRtpServer(uint16_t local_port, const string &stream_id, int tcp_mode, const string &local_ip, bool re_use_port, uint32_t ssrc, bool only_audio) { +uint16_t openRtpServer(uint16_t local_port, const string &stream_id, int tcp_mode, const string &local_ip, bool re_use_port, uint32_t ssrc, bool only_audio, bool multiplex) { lock_guard lck(s_rtpServerMapMtx); if (s_rtpServerMap.find(stream_id) != s_rtpServerMap.end()) { //为了防止RtpProcess所有权限混乱的问题,不允许重复添加相同的stream_id @@ -412,7 +412,7 @@ uint16_t openRtpServer(uint16_t local_port, const string &stream_id, int tcp_mod } RtpServer::Ptr server = std::make_shared(); - server->start(local_port, stream_id, (RtpServer::TcpMode)tcp_mode, local_ip.c_str(), re_use_port, ssrc, only_audio); + server->start(local_port, stream_id, (RtpServer::TcpMode)tcp_mode, local_ip.c_str(), re_use_port, ssrc, only_audio, multiplex); server->setOnDetach([stream_id]() { //设置rtp超时移除事件 lock_guard lck(s_rtpServerMapMtx); @@ -1182,6 +1182,24 @@ void installWebApi() { //回复json val["port"] = port; }); + api_regist("/media/api/openRtpServerMultiplex", [](API_ARGS_MAP) { + CHECK_SECRET(); + CHECK_ARGS("port", "stream_id"); + auto stream_id = allArgs["stream_id"]; + auto tcp_mode = allArgs["tcp_mode"].as(); + if (allArgs["enable_tcp"].as() && !tcp_mode) { + // 兼容老版本请求,新版本去除enable_tcp参数并新增tcp_mode参数 + tcp_mode = 1; + } + + auto port = openRtpServer( + allArgs["port"], stream_id, tcp_mode, "::", true, 0, allArgs["only_audio"].as(),true); + if (port == 0) { + throw InvalidArgsException("该stream_id已存在"); + } + // 回复json + val["port"] = port; + }); api_regist("/index/api/connectRtpServer", [](API_ARGS_MAP_ASYNC) { CHECK_SECRET(); diff --git a/server/WebApi.h b/server/WebApi.h index 460d0132..f7ec0cb3 100755 --- a/server/WebApi.h +++ b/server/WebApi.h @@ -239,7 +239,7 @@ void installWebApi(); void unInstallWebApi(); #if defined(ENABLE_RTPPROXY) -uint16_t openRtpServer(uint16_t local_port, const std::string &stream_id, int tcp_mode, const std::string &local_ip, bool re_use_port, uint32_t ssrc, bool only_audio); +uint16_t openRtpServer(uint16_t local_port, const std::string &stream_id, int tcp_mode, const std::string &local_ip, bool re_use_port, uint32_t ssrc, bool only_audio, bool multiplex=false); void connectRtpServer(const std::string &stream_id, const std::string &dst_url, uint16_t dst_port, const std::function &cb); bool closeRtpServer(const std::string &stream_id); #endif diff --git a/src/Rtp/RtpServer.cpp b/src/Rtp/RtpServer.cpp index 34871562..1f71a4a8 100644 --- a/src/Rtp/RtpServer.cpp +++ b/src/Rtp/RtpServer.cpp @@ -156,7 +156,7 @@ private: EventPoller::DelayTask::Ptr _delay_task; }; -void RtpServer::start(uint16_t local_port, const string &stream_id, TcpMode tcp_mode, const char *local_ip, bool re_use_port, uint32_t ssrc, bool only_audio) { +void RtpServer::start(uint16_t local_port, const string &stream_id, TcpMode tcp_mode, const char *local_ip, bool re_use_port, uint32_t ssrc, bool only_audio, bool multiplex) { //创建udp服务器 Socket::Ptr rtp_socket = Socket::createSocket(nullptr, true); Socket::Ptr rtcp_socket = Socket::createSocket(nullptr, true); @@ -195,7 +195,8 @@ void RtpServer::start(uint16_t local_port, const string &stream_id, TcpMode tcp_ //创建udp服务器 UdpServer::Ptr udp_server; RtcpHelper::Ptr helper; - if (!stream_id.empty()) { + //增加了多路复用判断,如果多路复用为true,就走else逻辑,同时保留了原来stream_id为空走else逻辑 + if (!stream_id.empty() && !multiplex) { //指定了流id,那么一个端口一个流(不管是否包含多个ssrc的多个流,绑定rtp源后,会筛选掉ip端口不匹配的流) helper = std::make_shared(std::move(rtcp_socket), stream_id); helper->startRtcp(); diff --git a/src/Rtp/RtpServer.h b/src/Rtp/RtpServer.h index 4efce859..71aa88e7 100644 --- a/src/Rtp/RtpServer.h +++ b/src/Rtp/RtpServer.h @@ -42,9 +42,10 @@ public: * @param local_ip 绑定的本地网卡ip * @param re_use_port 是否设置socket为re_use属性 * @param ssrc 指定的ssrc + * @param multiplex 多路复用 */ void start(uint16_t local_port, const std::string &stream_id = "", TcpMode tcp_mode = PASSIVE, - const char *local_ip = "::", bool re_use_port = true, uint32_t ssrc = 0, bool only_audio = false); + const char *local_ip = "::", bool re_use_port = true, uint32_t ssrc = 0, bool only_audio = false, bool multiplex = false); /** * 连接到tcp服务(tcp主动模式)