RTMP: Ignore FMLE start packet after flash publish. v7.0.129 (#4588)

We have discovered that some IP cameras send two publish packets in a
row.

The first packet is flash publish `publish('xxx')`
The second packet is FMLE publish `FCPublish('xxx|@setDataFrame()`

It seems that this is not processed correctly on the SRS side. In fact,
the stream is simply deinitialized, and republish is simply not
supported in this case.

As a fix, I suggest simply ignoring the FMLE publish packet after the
flash publish.

<img width="720" alt="screen"
src="https://github.com/user-attachments/assets/2db806ab-71b9-4e7b-bcf9-c16ea12df671"
/>
This commit is contained in:
artem-smorodin-dacast 2025-11-27 17:28:46 +03:00 committed by GitHub
parent e59b30301a
commit 4101900daf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 14 additions and 8 deletions

View File

@ -7,6 +7,7 @@ The changelog for SRS.
<a name="v7-changes"></a>
## SRS 7.0 Changelog
* v7.0, 2025-11-27, Merge [#4588](https://github.com/ossrs/srs/pull/4588): RTMP: Ignore FMLE start packet after flash publish. v7.0.129 (#4588)
* v7.0, 2025-11-18, AI: API: Change pagination default count to 10, minimum 1. v7.0.128
* v7.0, 2025-11-14, AI: Fix race condition causing immediate deletion of new sources. v7.0.127 (#4449)
* v7.0, 2025-11-11, AI: WebRTC: Support optional msid attribute per RFC 8830. v7.0.126 (#4570)

View File

@ -3604,8 +3604,8 @@ srs_error_t SrsRtcPublisherNegotiator::negotiate_publish_capability(SrsRtcUserCo
// Generate msid because it's optional in sdp.
string msid_tracker = ssrc_info.msid_tracker_;
if (msid_tracker.empty()) {
msid_tracker = srs_fmt_sprintf("track-%s-%s-%d",
track_desc->type_.c_str(), ssrc_info.cname_.c_str(), ssrc_info.ssrc_);
msid_tracker = srs_fmt_sprintf("track-%s-%s-%d",
track_desc->type_.c_str(), ssrc_info.cname_.c_str(), ssrc_info.ssrc_);
}
// Generate msid because it's optional in sdp.

View File

@ -1176,6 +1176,11 @@ srs_error_t SrsRtmpConn::handle_publish_message(SrsSharedPtr<SrsLiveSource> &sou
// for flash, any packet is republish.
if (info_->type_ == SrsRtmpConnFlashPublish) {
if (dynamic_cast<SrsFMLEStartPacket *>(pkt.get())) {
srs_warn("flash late FMLE start packet");
return err;
}
// flash unpublish.
// TODO: maybe need to support republish.
srs_trace("flash flash publish finished.");

View File

@ -9,6 +9,6 @@
#define VERSION_MAJOR 7
#define VERSION_MINOR 0
#define VERSION_REVISION 128
#define VERSION_REVISION 129
#endif

View File

@ -20,9 +20,9 @@
#include <srs_kernel_utility.hpp>
#include <srs_protocol_sdp.hpp>
#include <srs_protocol_utility.hpp>
#include <srs_utest_ai16.hpp>
#include <srs_utest_manual_kernel.hpp>
#include <srs_utest_manual_mock.hpp>
#include <srs_utest_ai16.hpp>
#ifdef SRS_FFMPEG_FIT
#include <srs_app_rtc_codec.hpp>
@ -1241,7 +1241,7 @@ VOID TEST(SdpTest, ParseLibdatachannelSdpFromIssue4570)
EXPECT_TRUE(sdp.media_descs_.size() == 2);
// Verify first media description is video
SrsMediaDesc* video_desc = &sdp.media_descs_[0];
SrsMediaDesc *video_desc = &sdp.media_descs_[0];
EXPECT_TRUE(video_desc->type_ == "video");
EXPECT_TRUE(video_desc->mid_ == "video");
EXPECT_TRUE(video_desc->sendonly_);
@ -1253,7 +1253,7 @@ VOID TEST(SdpTest, ParseLibdatachannelSdpFromIssue4570)
EXPECT_TRUE(video_desc->payload_types_.size() >= 1);
// Find H264 payload (PT 96)
SrsMediaPayloadType* h264_payload = NULL;
SrsMediaPayloadType *h264_payload = NULL;
for (size_t i = 0; i < video_desc->payload_types_.size(); i++) {
if (video_desc->payload_types_[i].payload_type_ == 96) {
h264_payload = &video_desc->payload_types_[i];
@ -1277,7 +1277,7 @@ VOID TEST(SdpTest, ParseLibdatachannelSdpFromIssue4570)
EXPECT_TRUE(found_video_ssrc);
// Verify second media description is audio
SrsMediaDesc* audio_desc = &sdp.media_descs_[1];
SrsMediaDesc *audio_desc = &sdp.media_descs_[1];
EXPECT_TRUE(audio_desc->type_ == "audio");
EXPECT_TRUE(audio_desc->mid_ == "audio");
EXPECT_TRUE(audio_desc->sendonly_);
@ -1289,7 +1289,7 @@ VOID TEST(SdpTest, ParseLibdatachannelSdpFromIssue4570)
EXPECT_TRUE(audio_desc->payload_types_.size() >= 1);
// Find Opus payload (PT 111)
SrsMediaPayloadType* opus_payload = NULL;
SrsMediaPayloadType *opus_payload = NULL;
for (size_t i = 0; i < audio_desc->payload_types_.size(); i++) {
if (audio_desc->payload_types_[i].payload_type_ == 111) {
opus_payload = &audio_desc->payload_types_[i];