Skip to content

Commit

Permalink
WHIP: Fix bug for converting WHIP to RTMP/HLS. v5.0.204 v6.0.107 (oss…
Browse files Browse the repository at this point in the history
…rs#3911)

1. When converting RTC to RTMP, it is necessary to synchronize the audio
and video timestamps. When the synchronization status changes, whether
it is unsynchronized or synchronized, print logs to facilitate
troubleshooting of such issues.
2. Chrome uses the STAP-A packet, which means a single RTP packet
contains SPS/PPS information. OBS WHIP, on the other hand, sends SPS and
PPS in separate RTP packets. Therefore, SPS and PPS are in two
independent RTP packets, and SRS needs to cache these two packets.

---------

Co-authored-by: john <[email protected]>
  • Loading branch information
winlinvip and xiaozhihong committed Jan 3, 2024
1 parent 360f583 commit ff20852
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 34 deletions.
2 changes: 2 additions & 0 deletions trunk/doc/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ The changelog for SRS.
## SRS 6.0 Changelog
* v6.0, 2023-12-30, Merge [#3916](https://github.com/ossrs/srs/pull/3916): Enhancing the compatibility of options.sh. v6.0.108 (#3916)
* v6.0, 2023-12-30, Merge [#3914](https://github.com/ossrs/srs/pull/3914): Forward: when unpublish crash caused by uninitialized forward connection. v6.0.107 (#3914)
* v6.0, 2023-12-29, Merge [#3911](https://github.com/ossrs/srs/pull/3911): WHIP: Fix bug for converting WHIP to RTMP/HLS. v6.0.107 (#3911)
* v6.0, 2023-12-15, Merge [#3854](https://github.com/ossrs/srs/pull/3854): Typo: line 263 - srs_app_srt_conn.cpp. v6.0.106 (#3854)
* v6.0, 2023-12-14, Merge [#3910](https://github.com/ossrs/srs/pull/3910): RTC: Support OPUS stereo SDP option. v6.0.105 (#3910)
* v6.0, 2023-12-14, Merge [#3902](https://github.com/ossrs/srs/pull/3902): Security: Support IP whitelist for HTTP-FLV, HLS, WebRTC, and SRT. v6.0.104 (#3902)
Expand Down Expand Up @@ -121,6 +122,7 @@ The changelog for SRS.

## SRS 5.0 Changelog
* v5.0, 2023-12-30, Merge [#3916](https://github.com/ossrs/srs/pull/3916): Enhancing the compatibility of options.sh. v5.0.204 (#3916)
* v5.0, 2023-12-29, Merge [#3911](https://github.com/ossrs/srs/pull/3911): WHIP: Fix bug for converting WHIP to RTMP/HLS. v5.0.204 (#3911)
* v5.0, 2023-12-14, Merge [#3910](https://github.com/ossrs/srs/pull/3910): RTC: Support OPUS stereo SDP option. v5.0.203 (#3910)
* v5.0, 2023-12-14, Merge [#3902](https://github.com/ossrs/srs/pull/3902): Security: Support IP whitelist for HTTP-FLV, HLS, WebRTC, and SRT. v5.0.202 (#3902)
* v5.0, 2023-11-22, Merge [#3891](https://github.com/ossrs/srs/pull/3891): fix 'sed' error in options.sh. v5.0.201 (#3891)
Expand Down
107 changes: 73 additions & 34 deletions trunk/src/app/srs_app_rtc_source.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1318,12 +1318,16 @@ SrsRtcFrameBuilder::SrsRtcFrameBuilder(ISrsStreamBridge* bridge)
header_sn_ = 0;
memset(cache_video_pkts_, 0, sizeof(cache_video_pkts_));
rtp_key_frame_ts_ = -1;
sync_state_ = -1;
obs_whip_sps_ = obs_whip_pps_ = NULL;
}

SrsRtcFrameBuilder::~SrsRtcFrameBuilder()
{
srs_freep(codec_);
clear_cached_video();
srs_freep(obs_whip_sps_);
srs_freep(obs_whip_pps_);
}

srs_error_t SrsRtcFrameBuilder::initialize(SrsRequest* r)
Expand Down Expand Up @@ -1366,8 +1370,18 @@ srs_error_t SrsRtcFrameBuilder::on_rtp(SrsRtpPacket *pkt)

// Have no received any sender report, can't calculate avsync_time,
// discard it to avoid timestamp problem in live source
const SrsRtpHeader& h = pkt->header;
if (pkt->get_avsync_time() <= 0) {
if (sync_state_ < 0) {
srs_trace("RTC: Discard no-sync %s, ssrc=%u, seq=%u, ts=%u, state=%d", pkt->is_audio() ? "Audio" : "Video",
h.get_ssrc(), h.get_sequence(), h.get_timestamp(), sync_state_);
sync_state_ = 0;
}
return err;
} else if (sync_state_ < 1) {
srs_trace("RTC: Accept sync %s, ssrc=%u, seq=%u, ts=%u, state=%d", pkt->is_audio() ? "Audio" : "Video",
h.get_ssrc(), h.get_sequence(), h.get_timestamp(), sync_state_);
sync_state_ = 2;
}

if (pkt->is_audio()) {
Expand Down Expand Up @@ -1499,46 +1513,71 @@ srs_error_t SrsRtcFrameBuilder::packet_video_key_frame(SrsRtpPacket* pkt)
{
srs_error_t err = srs_success;

// TODO: handle sps and pps in 2 rtp packets
// For OBS WHIP, it uses RTP Raw packet with SPS/PPS/IDR frame.
SrsRtpRawPayload* raw_payload = dynamic_cast<SrsRtpRawPayload*>(pkt->payload());
if (raw_payload) {
if (pkt->nalu_type == SrsAvcNaluTypeSPS) {
srs_freep(obs_whip_sps_);
obs_whip_sps_ = pkt->copy();
} else if (pkt->nalu_type == SrsAvcNaluTypePPS) {
srs_freep(obs_whip_pps_);
obs_whip_pps_ = pkt->copy();
}
// Ignore if one of OBS WHIP SPS/PPS is not ready.
if (!obs_whip_pps_ || !obs_whip_pps_) {
return err;
}
}

// Generally, there will be SPS+PPS+IDR in a STAP-A packet.
SrsRtpSTAPPayload* stap_payload = dynamic_cast<SrsRtpSTAPPayload*>(pkt->payload());
if (stap_payload) {
SrsSample* sps = stap_payload->get_sps();
SrsSample* pps = stap_payload->get_pps();
if (NULL == sps || NULL == pps) {

// Handle SPS/PPS in cache or STAP-A packet.
if (stap_payload || raw_payload) {
// Get the SPS/PPS from cache or STAP-A packet.
SrsSample* sps = stap_payload ? stap_payload->get_sps() : NULL;
if (!sps && obs_whip_sps_) sps = dynamic_cast<SrsRtpRawPayload*>(obs_whip_sps_->payload())->sample_;
SrsSample* pps = stap_payload ? stap_payload->get_pps() : NULL;
if (!pps && obs_whip_pps_) pps = dynamic_cast<SrsRtpRawPayload*>(obs_whip_pps_->payload())->sample_;
if (!sps || !pps) {
return srs_error_new(ERROR_RTC_RTP_MUXER, "no sps or pps in stap-a rtp. sps: %p, pps:%p", sps, pps);
} else {
// h264 raw to h264 packet.
std::string sh;
SrsRawH264Stream* avc = new SrsRawH264Stream();
SrsAutoFree(SrsRawH264Stream, avc);
}

if ((err = avc->mux_sequence_header(string(sps->bytes, sps->size), string(pps->bytes, pps->size), sh)) != srs_success) {
return srs_error_wrap(err, "mux sequence header");
}
// Reset SPS/PPS cache, ensuring that the next SPS/PPS will be handled when both are received.
SrsAutoFree(SrsRtpPacket, obs_whip_sps_);
SrsAutoFree(SrsRtpPacket, obs_whip_pps_);

// h264 packet to flv packet.
char* flv = NULL;
int nb_flv = 0;
if ((err = avc->mux_avc2flv(sh, SrsVideoAvcFrameTypeKeyFrame, SrsVideoAvcFrameTraitSequenceHeader, pkt->get_avsync_time(),
pkt->get_avsync_time(), &flv, &nb_flv)) != srs_success) {
return srs_error_wrap(err, "avc to flv");
}
// h264 raw to h264 packet.
std::string sh;
SrsRawH264Stream* avc = new SrsRawH264Stream();
SrsAutoFree(SrsRawH264Stream, avc);

SrsMessageHeader header;
header.initialize_video(nb_flv, pkt->get_avsync_time(), 1);
SrsCommonMessage rtmp;
if ((err = rtmp.create(&header, flv, nb_flv)) != srs_success) {
return srs_error_wrap(err, "create rtmp");
}
if ((err = avc->mux_sequence_header(string(sps->bytes, sps->size), string(pps->bytes, pps->size), sh)) != srs_success) {
return srs_error_wrap(err, "mux sequence header");
}

SrsSharedPtrMessage msg;
if ((err = msg.create(&rtmp)) != srs_success) {
return srs_error_wrap(err, "create message");
}
// h264 packet to flv packet.
char* flv = NULL;
int nb_flv = 0;
if ((err = avc->mux_avc2flv(sh, SrsVideoAvcFrameTypeKeyFrame, SrsVideoAvcFrameTraitSequenceHeader, pkt->get_avsync_time(),
pkt->get_avsync_time(), &flv, &nb_flv)) != srs_success) {
return srs_error_wrap(err, "avc to flv");
}

if ((err = bridge_->on_frame(&msg)) != srs_success) {
return err;
}
SrsMessageHeader header;
header.initialize_video(nb_flv, pkt->get_avsync_time(), 1);
SrsCommonMessage rtmp;
if ((err = rtmp.create(&header, flv, nb_flv)) != srs_success) {
return srs_error_wrap(err, "create rtmp");
}

SrsSharedPtrMessage msg;
if ((err = msg.create(&rtmp)) != srs_success) {
return srs_error_wrap(err, "create message");
}

if ((err = bridge_->on_frame(&msg)) != srs_success) {
return err;
}
}

Expand Down Expand Up @@ -1583,7 +1622,7 @@ srs_error_t SrsRtcFrameBuilder::packet_video_key_frame(SrsRtpPacket* pkt)
if (-1 == sn) {
if (check_frame_complete(header_sn_, tail_sn)) {
if ((err = packet_video_rtmp(header_sn_, tail_sn)) != srs_success) {
err = srs_error_wrap(err, "fail to packet key frame");
err = srs_error_wrap(err, "fail to packet frame");
}
}
} else if (-2 == sn) {
Expand Down
7 changes: 7 additions & 0 deletions trunk/src/app/srs_app_rtc_source.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,13 @@ class SrsRtcFrameBuilder
uint16_t header_sn_;
uint16_t lost_sn_;
int64_t rtp_key_frame_ts_;
private:
// The state for timestamp sync state. -1 for init. 0 not sync. 1 sync.
int sync_state_;
private:
// For OBS WHIP, send SPS/PPS in dedicated RTP packet.
SrsRtpPacket* obs_whip_sps_;
SrsRtpPacket* obs_whip_pps_;
public:
SrsRtcFrameBuilder(ISrsStreamBridge* bridge);
virtual ~SrsRtcFrameBuilder();
Expand Down
6 changes: 6 additions & 0 deletions trunk/src/kernel/srs_kernel_rtc_rtp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -970,12 +970,14 @@ SrsRtpRawPayload::SrsRtpRawPayload()
{
payload = NULL;
nn_payload = 0;
sample_ = new SrsSample();

++_srs_pps_objs_rraw->sugar;
}

SrsRtpRawPayload::~SrsRtpRawPayload()
{
srs_freep(sample_);
}

uint64_t SrsRtpRawPayload::nb_bytes()
Expand Down Expand Up @@ -1007,6 +1009,9 @@ srs_error_t SrsRtpRawPayload::decode(SrsBuffer* buf)
payload = buf->head();
nn_payload = buf->left();

sample_->bytes = payload;
sample_->size = nn_payload;

return srs_success;
}

Expand All @@ -1016,6 +1021,7 @@ ISrsRtpPayloader* SrsRtpRawPayload::copy()

cp->payload = payload;
cp->nn_payload = nn_payload;
cp->sample_ = sample_->copy();

return cp;
}
Expand Down
3 changes: 3 additions & 0 deletions trunk/src/kernel/srs_kernel_rtc_rtp.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,9 @@ class SrsRtpRawPayload : public ISrsRtpPayloader
// @remark We only refer to the memory, user must free its bytes.
char* payload;
int nn_payload;
public:
// Use the whole RAW RTP payload as a sample.
SrsSample* sample_;
public:
SrsRtpRawPayload();
virtual ~SrsRtpRawPayload();
Expand Down

0 comments on commit ff20852

Please sign in to comment.