SimulcastConsumer::SendRtpPacket

#SimulcastConsumer::SendRtpPacket

void SimulcastConsumer::SendRtpPacket(RTC::RtpPacket* packet)
	{
		MS_TRACE();

		//1.consumer处于不活动状态则直接返回
		if (!IsActive())
			return;
		//2.目标时间域名层无效则直接返回
		if (this->targetTemporalLayer == -1)
			return;

		auto payloadType = packet->GetPayloadType();

		//3.不支持的负载类型则直接返回 NOTE: This may happen if this Consumer supports just some codecs of those in the corresponding Producer.
		if (this->supportedCodecPayloadTypes.find(payloadType) == this->supportedCodecPayloadTypes.end())
		{
			MS_DEBUG_DEV("payload type not supported [payloadType:%" PRIu8 "]", payloadType);

			return;
		}

		auto spatialLayer = this->mapMappedSsrcSpatialLayer.at(packet->GetSsrc());
		bool shouldSwitchCurrentSpatialLayer{ false };

		//4.检查:当前空间层不是目标空间层且packet所属的空间层是目标空间层:则判断是否是关键帧,不是关键帧忽略直接返回,是关键帧则设置需要切换当前空间层标志,同时设置需要同步标志
		//  Check whether this is the packet we are waiting for in order to update the current spatial layer.
		if (this->currentSpatialLayer != this->targetSpatialLayer && spatialLayer == this->targetSpatialLayer)
		{
			// Ignore if not a key frame.
			if (!packet->IsKeyFrame())
				return;

			shouldSwitchCurrentSpatialLayer = true;

			// Need to resync the stream.
			this->syncRequired = true;
		}
		//4.检查:如果packet所属空间层不是当前空间层则直接返回
		//  If the packet belongs to different spatial layer than the one being sent, drop it.
		else if (spatialLayer != this->currentSpatialLayer)
		{
			return;
		}

		//5.如果需要同步但不是关键帧则直接返回
		//  If we need to sync and this is not a key frame, ignore the packet.
		if (this->syncRequired && !packet->IsKeyFrame())
			return;

		// Whether this is the first packet after re-sync.
		bool isSyncPacket = this->syncRequired;

		//6.需要同步后的第一个packet,同步序列号和时间戳
		// Sync sequence number and timestamp if required.
		if (isSyncPacket)
		{
			if (packet->IsKeyFrame())
				MS_DEBUG_TAG(rtp, "sync key frame received");

			uint32_t tsOffset{ 0u };

			//6.1 同步RtpStream的rtp时间戳 // Sync our RTP stream's RTP timestamp.
			if (spatialLayer == this->tsReferenceSpatialLayer) //如果packet所属空间层为时间参考的空间层,则认为tsOffset时间偏移为0
			{
				tsOffset = 0u;
			}
			//6.2 如果packet所属空间层不为时间参考的空间层,则需要基于NTP时间进行Rtp时间戳同步 
			// If this is not the RTP stream we use as TS reference, do NTP based RTP TS synchronization.
			else
			{
				auto* producerTsReferenceRtpStream = GetProducerTsReferenceRtpStream();
				auto* producerTargetRtpStream      = GetProducerTargetRtpStream();

				// NOTE: If we are here is because we have Sender Reports for both the TS reference stream and the target one.
				MS_ASSERT(producerTsReferenceRtpStream->GetSenderReportNtpMs(), "no Sender Report for TS reference RTP stream");
				MS_ASSERT(producerTargetRtpStream->GetSenderReportNtpMs(), "no Sender Report for current RTP stream");

				// Calculate NTP and TS stuff.
				auto ntpMs1 = producerTsReferenceRtpStream->GetSenderReportNtpMs();
				auto ts1    = producerTsReferenceRtpStream->GetSenderReportTs();
				auto ntpMs2 = producerTargetRtpStream->GetSenderReportNtpMs();
				auto ts2    = producerTargetRtpStream->GetSenderReportTs();

				//6.3 计算RtpStream rtp时间戳差值,换到到rtp时间单位
				int64_t diffMs;
				if (ntpMs2 >= ntpMs1)
					diffMs = ntpMs2 - ntpMs1;
				else
					diffMs = -1 * (ntpMs1 - ntpMs2);

				int64_t diffTs  = diffMs * this->rtpStream->GetClockRate() / 1000;
				uint32_t newTs2 = ts2 - diffTs;

				//6.4 此tsOffset是两个rtpstream rtp时间戳差值
				// Apply offset. This is the difference that later must be removed from the sending RTP packet.
				tsOffset = newTs2 - ts1;
			}

			//6.5 当切换到新rtpstream时,可能一个问题是关键帧的时间戳小于已经发送到对端的最大时间戳,为此选定的Producer流的整个实时过程中应用额外的偏移量以“修复”它
			// When switching to a new stream it may happen that the timestamp of this
			// key frame is lower than the highest timestamp sent to the remote endpoint.
			// If so, apply an extra offset to "fix" it for the whole live of this selected
			// Producer stream.

			// clang-format off
			if (
				shouldSwitchCurrentSpatialLayer &&
				(packet->GetTimestamp() - tsOffset <= this->rtpStream->GetMaxPacketTs()) //关键帧时间戳小于最大发送数据包时间戳,需要额外补偿
			)
			// clang-format on
			{
				// Max delay in ms we allow for the stream when switching.
				// https://en.wikipedia.org/wiki/Audio-to-video_synchronization#Recommendations
				static const uint32_t MaxExtraOffsetMs{ 75u }; 

				int64_t maxTsExtraOffset = MaxExtraOffsetMs * this->rtpStream->GetClockRate() / 1000; //允许流切换允许的最大延迟
				uint32_t tsExtraOffset = this->rtpStream->GetMaxPacketTs() - packet->GetTimestamp() + tsOffset; //关键帧与发送最大数据包时间差值

				// NOTE: Don't ask for a key frame if already done.
				if (this->keyFrameForTsOffsetRequested) //如果已经发送关键帧请求不再请求
				{
					// Give up and use the theoretical offset.
					if (tsExtraOffset > maxTsExtraOffset) //如果发送完关键帧请求后,关键帧的时间戳差值仍大于允许的最大延迟,则放弃额外offset补偿,使用理论差值offset同步
					{
						tsExtraOffset = 1u; //默认比理论offset大1
					}
				}
				else if (tsExtraOffset > maxTsExtraOffset) //如果没有请求过关键帧,则进行关键帧请求,本次同步结束返回等待下一个关键帧进行同步
				{
					RequestKeyFrameForTargetSpatialLayer();

					this->keyFrameForTsOffsetRequested = true;

					return;
				}
				// It's common that, when switching spatial layer, the resulting TS for the outgoing packet matches the highest seen in the previous stream. Fix it.
				else if (tsExtraOffset == 0u) //如果关键帧时间戳刚好是发送最大数据包时间戳,则以30fps时间单位偏移
				{
					// Apply an expected offset for a new frame in a 30fps stream.
					static const uint8_t MsOffset{ 33u }; // (1 / 30 * 1000).

					tsExtraOffset = MsOffset * this->rtpStream->GetClockRate() / 1000;
				}

				if (tsExtraOffset > 0u)
				{
					MS_DEBUG_TAG(
					  simulcast,
					  "RTP timestamp extra offset generated for stream switching: %" PRIu32,
					  tsExtraOffset);

					// Increase the timestamp offset for the whole life of this Producer stream (until switched to a different one).
					tsOffset -= tsExtraOffset; //计算出的额外offset偏移
				}
			}

			this->tsOffset = tsOffset; //保存当前偏移

			// Sync our RTP stream's sequence number.
			this->rtpSeqManager.Sync(packet->GetSequenceNumber() - 1); //设置同步起始序列号

			this->encodingContext->SyncRequired();

			this->syncRequired                 = false; //同步完成不再需求同步
			this->keyFrameForTsOffsetRequested = false; //关键帧时间戳同步完成不再需求同步
		}

		if (shouldSwitchCurrentSpatialLayer) //应该切换当前空间层
		{
			// Update current spatial layer.
			this->currentSpatialLayer = this->targetSpatialLayer;

			// Update target and current temporal layer.
			this->encodingContext->SetTargetTemporalLayer(this->targetTemporalLayer);
			this->encodingContext->SetCurrentTemporalLayer(packet->GetTemporalLayer());

			// Reset the score of our RtpStream to 10.
			this->rtpStream->ResetScore(10u, /*notify*/ false);

			// Emit the layersChange event.
			EmitLayersChange();

			// Emit the score event.
			EmitScore();

			// Rewrite payload if needed.
			packet->ProcessPayload(this->encodingContext.get());
		}
		else //不需要切换
		{
			auto previousTemporalLayer = this->encodingContext->GetCurrentTemporalLayer();

			// Rewrite payload if needed. Drop packet if necessary.
			if (!packet->ProcessPayload(this->encodingContext.get()))
			{
				this->rtpSeqManager.Drop(packet->GetSequenceNumber());
				return;
			}

			if (previousTemporalLayer != this->encodingContext->GetCurrentTemporalLayer())
				EmitLayersChange();
		}

		// Update RTP seq number and timestamp based on NTP offset.
		uint16_t seq;
		uint32_t timestamp = packet->GetTimestamp() - this->tsOffset;

		this->rtpSeqManager.Input(packet->GetSequenceNumber(), seq); //更新数据包序列号

		// Save original packet fields.
		auto origSsrc      = packet->GetSsrc();
		auto origSeq       = packet->GetSequenceNumber();
		auto origTimestamp = packet->GetTimestamp();

		// Rewrite packet.
		packet->SetSsrc(this->rtpParameters.encodings[0].ssrc);
		packet->SetSequenceNumber(seq);
		packet->SetTimestamp(timestamp);

		// Process the packet.
		if (this->rtpStream->ReceivePacket(packet))
		{
			// Send the packet.
			this->listener->OnConsumerSendRtpPacket(this, packet);
		}

		// Restore packet fields.
		packet->SetSsrc(origSsrc);
		packet->SetSequenceNumber(origSeq);
		packet->SetTimestamp(origTimestamp);

		// Restore the original payload if needed.
		packet->RestorePayload();
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值