#define MS_CLASS "RTC::SvcConsumer"
#include "RTC/SvcConsumer.hpp"
#include "DepLibUV.hpp"
#include "Logger.hpp"
#include "MediaSoupErrors.hpp"
#include "Utils.hpp"
#include "Channel/ChannelNotifier.hpp"
#include "RTC/Codecs/Tools.hpp"
namespace RTC
{
static constexpr uint64_t BweDowngradeConservativeMs{ 10000u }; static constexpr uint64_t BweDowngradeMinActiveMs{ 8000u };
SvcConsumer::SvcConsumer(
const std::string& id, const std::string& producerId, RTC::Consumer::Listener* listener, json& data)
: RTC::Consumer::Consumer(id, producerId, listener, data, RTC::RtpParameters::Type::SVC)
{
MS_TRACE();
if (this->consumableRtpEncodings.size() != 1u)
MS_THROW_TYPE_ERROR("invalid consumableRtpEncodings with size != 1");
auto& encoding = this->rtpParameters.encodings[0];
if (encoding.spatialLayers < 2u && encoding.temporalLayers < 2u)
MS_THROW_TYPE_ERROR("invalid number of layers");
auto jsonPreferredLayersIt = data.find("preferredLayers");
if (jsonPreferredLayersIt != data.end() && jsonPreferredLayersIt->is_object())
{
auto jsonSpatialLayerIt = jsonPreferredLayersIt->find("spatialLayer");
auto jsonTemporalLayerIt = jsonPreferredLayersIt->find("temporalLayer");
if (
jsonSpatialLayerIt == jsonPreferredLayersIt->end() ||
!Utils::Json::IsPositiveInteger(*jsonSpatialLayerIt)
)
{
MS_THROW_TYPE_ERROR("missing preferredLayers.spatialLayer");
}
this->preferredSpatialLayer = jsonSpatialLayerIt->get<int16_t>();
if (this->preferredSpatialLayer > encoding.spatialLayers - 1)
this->preferredSpatialLayer = encoding.spatialLayers - 1;
if (
jsonTemporalLayerIt != jsonPreferredLayersIt->end() &&
Utils::Json::IsPositiveInteger(*jsonTemporalLayerIt)
)
{
this->preferredTemporalLayer = jsonTemporalLayerIt->get<int16_t>();
if (this->preferredTemporalLayer > encoding.temporalLayers - 1)
this->preferredTemporalLayer = encoding.temporalLayers - 1;
}
else
{
this->preferredTemporalLayer = encoding.temporalLayers - 1;
}
}
else
{
this->preferredSpatialLayer = encoding.spatialLayers - 1;
this->preferredTemporalLayer = encoding.temporalLayers - 1;
}
const auto* mediaCodec = this->rtpParameters.GetCodecForEncoding(encoding);
if (!RTC::Codecs::Tools::IsValidTypeForCodec(this->type, mediaCodec->mimeType))
{
MS_THROW_TYPE_ERROR("%s codec not supported for svc", mediaCodec->mimeType.ToString().c_str());
}
RTC::Codecs::EncodingContext::Params params;
params.spatialLayers = encoding.spatialLayers;
params.temporalLayers = encoding.temporalLayers;
params.ksvc = encoding.ksvc;
this->encodingContext.reset(RTC::Codecs::Tools::GetEncodingContext(mediaCodec->mimeType, params));
MS_ASSERT(this->encodingContext, "no encoding context for this codec");
CreateRtpStream();
}
SvcConsumer::~SvcConsumer()
{
MS_TRACE();
delete this->rtpStream;
}
void SvcConsumer::FillJson(json& jsonObject) const
{
MS_TRACE();
RTC::Consumer::FillJson(jsonObject);
this->rtpStream->FillJson(jsonObject["rtpStream"]);
jsonObject["preferredSpatialLayer"] = this->preferredSpatialLayer;
jsonObject["targetSpatialLayer"] = this->encodingContext->GetTargetSpatialLayer();
jsonObject["currentSpatialLayer"] = this->encodingContext->GetCurrentSpatialLayer();
jsonObject["preferredTemporalLayer"] = this->preferredTemporalLayer;
jsonObject["targetTemporalLayer"] = this->encodingContext->GetTargetTemporalLayer();
jsonObject["currentTemporalLayer"] = this->encodingContext->GetCurrentTemporalLayer();
}
void SvcConsumer::FillJsonStats(json& jsonArray) const
{
MS_TRACE();
jsonArray.emplace_back(json::value_t::object);
this->rtpStream->FillJsonStats(jsonArray[0]);
if (this->producerRtpStream)
{
jsonArray.emplace_back(json::value_t::object);
this->producerRtpStream->FillJsonStats(jsonArray[1]);
}
}
void SvcConsumer::FillJsonScore(json& jsonObject) const
{
MS_TRACE();
MS_ASSERT(this->producerRtpStreamScores, "producerRtpStreamScores not set");
jsonObject["score"] = this->rtpStream->GetScore();
if (this->producerRtpStream)
jsonObject["producerScore"] = this->producerRtpStream->GetScore();
else
jsonObject["producerScore"] = 0;
jsonObject["producerScores"] = *this->producerRtpStreamScores;
}
void SvcConsumer::HandleRequest(Channel::ChannelRequest* request)
{
MS_TRACE();
switch (request->methodId)
{
case Channel::ChannelRequest::MethodId::CONSUMER_REQUEST_KEY_FRAME:
{
if (IsActive())
RequestKeyFrame();
request->Accept();
break;
}
case Channel::ChannelRequest::MethodId::CONSUMER_SET_PREFERRED_LAYERS:
{
auto previousPreferredSpatialLayer = this->preferredSpatialLayer;
auto previousPreferredTemporalLayer = this->preferredTemporalLayer;
auto jsonSpatialLayerIt = request->data.find("spatialLayer");
auto jsonTemporalLayerIt = request->data.find("temporalLayer");
if (
jsonSpatialLayerIt == request->data.end() ||
!Utils::Json::IsPositiveInteger(*jsonSpatialLayerIt)
)
{
MS_THROW_TYPE_ERROR("missing spatialLayer");
}
this->preferredSpatialLayer = jsonSpatialLayerIt->get<int16_t>();
if (this->preferredSpatialLayer > this->rtpStream->GetSpatialLayers() - 1)
this->preferredSpatialLayer = this->rtpStream->GetSpatialLayers() - 1;
if (
jsonTemporalLayerIt != request->data.end() &&
Utils::Json::IsPositiveInteger(*jsonTemporalLayerIt)
)
{
this->preferredTemporalLayer = jsonTemporalLayerIt->get<int16_t>();
if (this->preferredTemporalLayer > this->rtpStream->GetTemporalLayers() - 1)
this->preferredTemporalLayer = this->rtpStream->GetTemporalLayers() - 1;
}
else
{
this->preferredTemporalLayer = this->rtpStream->GetTemporalLayers() - 1;
}
MS_DEBUG_DEV(
"preferred layers changed [spatial:%" PRIi16 ", temporal:%" PRIi16 ", consumerId:%s]",
this->preferredSpatialLayer,
this->preferredTemporalLayer,
this->id.c_str());
json data = json::object();
data["spatialLayer"] = this->preferredSpatialLayer;
data["temporalLayer"] = this->preferredTemporalLayer;
request->Accept(data);
if (
IsActive() &&
(
this->preferredSpatialLayer != previousPreferredSpatialLayer ||
this->preferredTemporalLayer != previousPreferredTemporalLayer
)
)
{
MayChangeLayers( true);
}
break;
}
default:
{
RTC::Consumer::HandleRequest(request);
}
}
}
void SvcConsumer::ProducerRtpStream(RTC::RtpStream* rtpStream, uint32_t )
{
MS_TRACE();
this->producerRtpStream = rtpStream;
}
void SvcConsumer::ProducerNewRtpStream(RTC::RtpStream* rtpStream, uint32_t )
{
MS_TRACE();
this->producerRtpStream = rtpStream;
EmitScore();
if (IsActive())
MayChangeLayers();
}
void SvcConsumer::ProducerRtpStreamScore(
RTC::RtpStream* , uint8_t score, uint8_t previousScore)
{
MS_TRACE();
EmitScore();
if (RTC::Consumer::IsActive())
{
if (
!this->externallyManagedBitrate ||
(score == 0u || previousScore == 0u)
)
{
MayChangeLayers();
}
}
}
void SvcConsumer::ProducerRtcpSenderReport(RTC::RtpStream* , bool )
{
MS_TRACE();
}
uint8_t SvcConsumer::GetBitratePriority() const
{
MS_TRACE();
MS_ASSERT(this->externallyManagedBitrate, "bitrate is not externally managed");
if (!IsActive())
return 0u;
return this->priority;
}
uint32_t SvcConsumer::IncreaseLayer(uint32_t bitrate, bool considerLoss)
{
MS_TRACE();
MS_ASSERT(this->externallyManagedBitrate, "bitrate is not externally managed");
MS_ASSERT(IsActive(), "should be active");
if (this->producerRtpStream->GetScore() == 0u)
return 0u;
if (
this->provisionalTargetSpatialLayer == this->preferredSpatialLayer &&
this->provisionalTargetTemporalLayer == this->preferredTemporalLayer
)
{
return 0u;
}
uint32_t virtualBitrate;
if (considerLoss)
{
auto lossPercentage = this->rtpStream->GetLossPercentage();
if (lossPercentage < 2)
virtualBitrate = 1.08 * bitrate;
else if (lossPercentage > 10)
virtualBitrate = (1 - 0.5 * (lossPercentage / 100)) * bitrate;
else
virtualBitrate = bitrate;
}
else
{
virtualBitrate = bitrate;
}
uint32_t requiredBitrate{ 0u };
int16_t spatialLayer{ 0 };
int16_t temporalLayer{ 0 };
auto nowMs = DepLibUV::GetTimeMs();
for (; spatialLayer < this->producerRtpStream->GetSpatialLayers(); ++spatialLayer)
{
if (nowMs - this->lastBweDowngradeAtMs < BweDowngradeConservativeMs)
{
if (this->provisionalTargetSpatialLayer > -1 && spatialLayer > this->encodingContext->GetCurrentSpatialLayer())
{
MS_DEBUG_DEV(
"avoid upgrading to spatial layer %" PRIi16 " due to recent BWE downgrade", spatialLayer);
goto done;
}
}
if (spatialLayer < this->provisionalTargetSpatialLayer)
continue;
temporalLayer = 0;
for (; temporalLayer < this->producerRtpStream->GetTemporalLayers(); ++temporalLayer)
{
if (
spatialLayer == this->provisionalTargetSpatialLayer &&
temporalLayer <= this->provisionalTargetTemporalLayer
)
{
continue;
}
requiredBitrate =
this->producerRtpStream->GetLayerBitrate(nowMs, spatialLayer, temporalLayer);
MS_DEBUG_DEV(
"testing layers %" PRIi16 ":%" PRIi16 " [virtual bitrate:%" PRIu32
", required bitrate:%" PRIu32 "]",
spatialLayer,
temporalLayer,
virtualBitrate,
requiredBitrate);
if (requiredBitrate)
goto done;
else
break;
}
if (spatialLayer >= this->preferredSpatialLayer)
break;
}
done:
if (!requiredBitrate)
return 0u;
if (requiredBitrate > virtualBitrate)
return 0u;
this->provisionalTargetSpatialLayer = spatialLayer;
this->provisionalTargetTemporalLayer = temporalLayer;
MS_DEBUG_DEV(
"upgrading to layers %" PRIi16 ":%" PRIi16 " [virtual bitrate:%" PRIu32
", required bitrate:%" PRIu32 "]",
this->provisionalTargetSpatialLayer,
this->provisionalTargetTemporalLayer,
virtualBitrate,
requiredBitrate);
if (requiredBitrate <= bitrate)
return requiredBitrate;
else if (requiredBitrate <= virtualBitrate)
return bitrate;
else
return requiredBitrate; }
void SvcConsumer::ApplyLayers()
{
MS_TRACE();
MS_ASSERT(this->externallyManagedBitrate, "bitrate is not externally managed");
MS_ASSERT(IsActive(), "should be active");
auto provisionalTargetSpatialLayer = this->provisionalTargetSpatialLayer;
auto provisionalTargetTemporalLayer = this->provisionalTargetTemporalLayer;
this->provisionalTargetSpatialLayer = -1;
this->provisionalTargetTemporalLayer = -1;
if (!IsActive())
return;
if (
provisionalTargetSpatialLayer != this->encodingContext->GetTargetSpatialLayer() ||
provisionalTargetTemporalLayer != this->encodingContext->GetTargetTemporalLayer()
)
{
UpdateTargetLayers(provisionalTargetSpatialLayer, provisionalTargetTemporalLayer);
if (
this->rtpStream->GetActiveMs() > BweDowngradeMinActiveMs &&
this->encodingContext->GetTargetSpatialLayer() < this->encodingContext->GetCurrentSpatialLayer() &&
this->encodingContext->GetCurrentSpatialLayer() <= this->preferredSpatialLayer
)
{
MS_DEBUG_DEV(
"possible target spatial layer downgrade (from %" PRIi16 " to %" PRIi16
") due to BWE limitation",
this->encodingContext->GetCurrentSpatialLayer(),
this->encodingContext->GetTargetSpatialLayer());
this->lastBweDowngradeAtMs = DepLibUV::GetTimeMs();
}
}
}
uint32_t SvcConsumer::GetDesiredBitrate() const
{
MS_TRACE();
MS_ASSERT(this->externallyManagedBitrate, "bitrate is not externally managed");
if (!IsActive())
return 0u;
auto nowMs = DepLibUV::GetTimeMs();
uint32_t desiredBitrate = this->producerRtpStream->GetBitrate(nowMs);
auto maxBitrate = this->rtpParameters.encodings[0].maxBitrate;
if (maxBitrate > desiredBitrate)
desiredBitrate = maxBitrate;
return desiredBitrate;
}
void SvcConsumer::SendRtpPacket(RTC::RtpPacket* packet, std::shared_ptr<RTC::RtpPacket>& sharedPacket)
{
MS_TRACE();
if (!IsActive())
return;
if (
this->encodingContext->GetTargetSpatialLayer() == -1 ||
this->encodingContext->GetTargetTemporalLayer() == -1
)
{
return;
}
auto payloadType = packet->GetPayloadType();
if (this->supportedCodecPayloadTypes.find(payloadType) == this->supportedCodecPayloadTypes.end())
{
MS_DEBUG_DEV("payload type not supported [payloadType:%" PRIu8 "]", payloadType);
return;
}
if (this->syncRequired && !packet->IsKeyFrame())
return;
bool isSyncPacket = this->syncRequired;
if (isSyncPacket)
{
if (packet->IsKeyFrame())
MS_DEBUG_TAG(rtp, "sync key frame received");
this->rtpSeqManager.Sync(packet->GetSequenceNumber() - 1);
this->syncRequired = false;
}
auto previousSpatialLayer = this->encodingContext->GetCurrentSpatialLayer();
auto previousTemporalLayer = this->encodingContext->GetCurrentTemporalLayer();
bool marker{ false };
bool origMarker = packet->HasMarker();
if (!packet->ProcessPayload(this->encodingContext.get(), marker))
{
this->rtpSeqManager.Drop(packet->GetSequenceNumber());
return;
}
if (
previousSpatialLayer != this->encodingContext->GetCurrentSpatialLayer() ||
previousTemporalLayer != this->encodingContext->GetCurrentTemporalLayer()
)
{
EmitLayersChange();
}
uint16_t seq;
this->rtpSeqManager.Input(packet->GetSequenceNumber(), seq);
auto origSsrc = packet->GetSsrc();
auto origSeq = packet->GetSequenceNumber();
packet->SetSsrc(this->rtpParameters.encodings[0].ssrc);
packet->SetSequenceNumber(seq);
if (marker)
{
packet->SetMarker(true);
}
if (isSyncPacket)
{
MS_DEBUG_TAG(
rtp,
"sending sync packet [ssrc:%" PRIu32 ", seq:%" PRIu16 ", ts:%" PRIu32
"] from original [seq:%" PRIu16 "]",
packet->GetSsrc(),
packet->GetSequenceNumber(),
packet->GetTimestamp(),
origSeq);
}
if (this->rtpStream->ReceivePacket(packet, sharedPacket))
{
this->listener->OnConsumerSendRtpPacket(this, packet);
EmitTraceEventRtpAndKeyFrameTypes(packet);
}
else
{
MS_WARN_TAG(
rtp,
"failed to send packet [ssrc:%" PRIu32 ", seq:%" PRIu16 ", ts:%" PRIu32
"] from original [ssrc:%" PRIu32 ", seq:%" PRIu16 "]",
packet->GetSsrc(),
packet->GetSequenceNumber(),
packet->GetTimestamp(),
origSsrc,
origSeq);
}
packet->SetSsrc(origSsrc);
packet->SetSequenceNumber(origSeq);
packet->SetMarker(origMarker);
packet->RestorePayload();
}
void SvcConsumer::GetRtcp(
RTC::RTCP::CompoundPacket* packet, RTC::RtpStreamSend* rtpStream, uint64_t nowMs)
{
MS_TRACE();
MS_ASSERT(rtpStream == this->rtpStream, "RTP stream does not match");
if (static_cast<float>((nowMs - this->lastRtcpSentTime) * 1.15) < this->maxRtcpInterval)
return;
auto* report = this->rtpStream->GetRtcpSenderReport(nowMs);
if (!report)
return;
packet->AddSenderReport(report);
auto* sdesChunk = this->rtpStream->GetRtcpSdesChunk();
packet->AddSdesChunk(sdesChunk);
auto* dlrr = this->rtpStream->GetRtcpXrDelaySinceLastRr(nowMs);
if (dlrr)
{
auto* report = new RTC::RTCP::DelaySinceLastRr();
report->AddSsrcInfo(dlrr);
packet->AddDelaySinceLastRr(report);
}
this->lastRtcpSentTime = nowMs;
}
void SvcConsumer::NeedWorstRemoteFractionLost(uint32_t , uint8_t& worstRemoteFractionLost)
{
MS_TRACE();
if (!IsActive())
return;
auto fractionLost = this->rtpStream->GetFractionLost();
if (fractionLost > worstRemoteFractionLost)
worstRemoteFractionLost = fractionLost;
}
void SvcConsumer::ReceiveNack(RTC::RTCP::FeedbackRtpNackPacket* nackPacket)
{
MS_TRACE();
if (!IsActive())
return;
EmitTraceEventNackType();
this->rtpStream->ReceiveNack(nackPacket);
}
void SvcConsumer::ReceiveKeyFrameRequest(RTC::RTCP::FeedbackPs::MessageType messageType, uint32_t ssrc)
{
MS_TRACE();
switch (messageType)
{
case RTC::RTCP::FeedbackPs::MessageType::PLI:
{
EmitTraceEventPliType(ssrc);
break;
}
case RTC::RTCP::FeedbackPs::MessageType::FIR:
{
EmitTraceEventFirType(ssrc);
break;
}
default:;
}
this->rtpStream->ReceiveKeyFrameRequest(messageType);
if (IsActive())
RequestKeyFrame();
}
void SvcConsumer::ReceiveRtcpReceiverReport(RTC::RTCP::ReceiverReport* report)
{
MS_TRACE();
this->rtpStream->ReceiveRtcpReceiverReport(report);
}
void SvcConsumer::ReceiveRtcpXrReceiverReferenceTime(RTC::RTCP::ReceiverReferenceTime* report)
{
MS_TRACE();
this->rtpStream->ReceiveRtcpXrReceiverReferenceTime(report);
}
uint32_t SvcConsumer::GetTransmissionRate(uint64_t nowMs)
{
MS_TRACE();
if (!IsActive())
return 0u;
return this->rtpStream->GetBitrate(nowMs);
}
float SvcConsumer::GetRtt() const
{
MS_TRACE();
return this->rtpStream->GetRtt();
}
void SvcConsumer::UserOnTransportConnected()
{
MS_TRACE();
this->syncRequired = true;
if (IsActive())
MayChangeLayers();
}
void SvcConsumer::UserOnTransportDisconnected()
{
MS_TRACE();
this->lastBweDowngradeAtMs = 0u;
this->rtpStream->Pause();
UpdateTargetLayers(-1, -1);
}
void SvcConsumer::UserOnPaused()
{
MS_TRACE();
this->lastBweDowngradeAtMs = 0u;
this->rtpStream->Pause();
UpdateTargetLayers(-1, -1);
if (this->externallyManagedBitrate)
this->listener->OnConsumerNeedZeroBitrate(this);
}
void SvcConsumer::UserOnResumed()
{
MS_TRACE();
this->syncRequired = true;
if (IsActive())
MayChangeLayers();
}
void SvcConsumer::CreateRtpStream()
{
MS_TRACE();
auto& encoding = this->rtpParameters.encodings[0];
const auto* mediaCodec = this->rtpParameters.GetCodecForEncoding(encoding);
MS_DEBUG_TAG(
rtp, "[ssrc:%" PRIu32 ", payloadType:%" PRIu8 "]", encoding.ssrc, mediaCodec->payloadType);
RTC::RtpStream::Params params;
params.ssrc = encoding.ssrc;
params.payloadType = mediaCodec->payloadType;
params.mimeType = mediaCodec->mimeType;
params.clockRate = mediaCodec->clockRate;
params.cname = this->rtpParameters.rtcp.cname;
params.spatialLayers = encoding.spatialLayers;
params.temporalLayers = encoding.temporalLayers;
if (mediaCodec->parameters.HasInteger("useinbandfec") && mediaCodec->parameters.GetInteger("useinbandfec") == 1)
{
MS_DEBUG_TAG(rtp, "in band FEC enabled");
params.useInBandFec = true;
}
if (mediaCodec->parameters.HasInteger("usedtx") && mediaCodec->parameters.GetInteger("usedtx") == 1)
{
MS_DEBUG_TAG(rtp, "DTX enabled");
params.useDtx = true;
}
if (encoding.dtx)
{
MS_DEBUG_TAG(rtp, "DTX enabled");
params.useDtx = true;
}
for (const auto& fb : mediaCodec->rtcpFeedback)
{
if (!params.useNack && fb.type == "nack" && fb.parameter.empty())
{
MS_DEBUG_2TAGS(rtp, rtcp, "NACK supported");
params.useNack = true;
}
else if (!params.usePli && fb.type == "nack" && fb.parameter == "pli")
{
MS_DEBUG_2TAGS(rtp, rtcp, "PLI supported");
params.usePli = true;
}
else if (!params.useFir && fb.type == "ccm" && fb.parameter == "fir")
{
MS_DEBUG_2TAGS(rtp, rtcp, "FIR supported");
params.useFir = true;
}
}
this->rtpStream = new RTC::RtpStreamSend(this, params, this->rtpParameters.mid);
this->rtpStreams.push_back(this->rtpStream);
if (IsPaused() || IsProducerPaused())
this->rtpStream->Pause();
const auto* rtxCodec = this->rtpParameters.GetRtxCodecForEncoding(encoding);
if (rtxCodec && encoding.hasRtx)
this->rtpStream->SetRtx(rtxCodec->payloadType, encoding.rtx.ssrc);
}
void SvcConsumer::RequestKeyFrame()
{
MS_TRACE();
if (this->kind != RTC::Media::Kind::VIDEO)
return;
auto mappedSsrc = this->consumableRtpEncodings[0].ssrc;
this->listener->OnConsumerKeyFrameRequested(this, mappedSsrc);
}
void SvcConsumer::MayChangeLayers(bool force)
{
MS_TRACE();
int16_t newTargetSpatialLayer;
int16_t newTargetTemporalLayer;
if (RecalculateTargetLayers(newTargetSpatialLayer, newTargetTemporalLayer))
{
if (this->externallyManagedBitrate)
{
if (newTargetSpatialLayer != this->encodingContext->GetTargetSpatialLayer() || force)
this->listener->OnConsumerNeedBitrateChange(this);
}
else
{
UpdateTargetLayers(newTargetSpatialLayer, newTargetTemporalLayer);
}
}
}
bool SvcConsumer::RecalculateTargetLayers(
int16_t& newTargetSpatialLayer, int16_t& newTargetTemporalLayer) const
{
MS_TRACE();
newTargetSpatialLayer = -1;
newTargetTemporalLayer = -1;
auto nowMs = DepLibUV::GetTimeMs();
int16_t spatialLayer{ 0 };
if (!this->producerRtpStream)
goto done;
if (this->producerRtpStream->GetScore() == 0u)
goto done;
for (; spatialLayer < this->producerRtpStream->GetSpatialLayers(); ++spatialLayer)
{
if (nowMs - this->lastBweDowngradeAtMs < BweDowngradeConservativeMs)
{
if (newTargetSpatialLayer > -1 && spatialLayer > this->encodingContext->GetCurrentSpatialLayer())
continue;
}
if (!this->producerRtpStream->GetSpatialLayerBitrate(nowMs, spatialLayer))
continue;
newTargetSpatialLayer = spatialLayer;
if (spatialLayer >= this->preferredSpatialLayer)
break;
}
if (newTargetSpatialLayer != -1)
{
if (newTargetSpatialLayer == this->preferredSpatialLayer)
newTargetTemporalLayer = this->preferredTemporalLayer;
else if (newTargetSpatialLayer < this->preferredSpatialLayer)
newTargetTemporalLayer = this->rtpStream->GetTemporalLayers() - 1;
else
newTargetTemporalLayer = 0;
}
done:
return (
newTargetSpatialLayer != this->encodingContext->GetTargetSpatialLayer() ||
newTargetTemporalLayer != this->encodingContext->GetTargetTemporalLayer()
);
}
void SvcConsumer::UpdateTargetLayers(int16_t newTargetSpatialLayer, int16_t newTargetTemporalLayer)
{
MS_TRACE();
if (newTargetSpatialLayer == -1)
{
this->encodingContext->SetTargetSpatialLayer(-1);
this->encodingContext->SetCurrentSpatialLayer(-1);
this->encodingContext->SetTargetTemporalLayer(-1);
this->encodingContext->SetCurrentTemporalLayer(-1);
MS_DEBUG_TAG(
svc, "target layers changed [spatial:-1, temporal:-1, consumerId:%s]", this->id.c_str());
EmitLayersChange();
return;
}
this->encodingContext->SetTargetSpatialLayer(newTargetSpatialLayer);
this->encodingContext->SetTargetTemporalLayer(newTargetTemporalLayer);
MS_DEBUG_TAG(
svc,
"target layers changed [spatial:%" PRIi16 ", temporal:%" PRIi16 ", consumerId:%s]",
newTargetSpatialLayer,
newTargetTemporalLayer,
this->id.c_str());
if (newTargetSpatialLayer != this->encodingContext->GetCurrentSpatialLayer())
{
if (this->encodingContext->IsKSvc())
{
MS_DEBUG_DEV("K-SVC: requesting keyframe to target spatial change");
RequestKeyFrame();
}
else if (newTargetSpatialLayer > this->encodingContext->GetCurrentSpatialLayer())
{
MS_DEBUG_DEV("full SVC: requesting keyframe to target spatial upgrade");
RequestKeyFrame();
}
}
}
inline void SvcConsumer::EmitScore() const
{
MS_TRACE();
json data = json::object();
FillJsonScore(data);
Channel::ChannelNotifier::Emit(this->id, "score", data);
}
inline void SvcConsumer::EmitLayersChange() const
{
MS_TRACE();
MS_DEBUG_DEV(
"current layers changed to [spatial:%" PRIi16 ", temporal:%" PRIi16 ", consumerId:%s]",
this->encodingContext->GetCurrentSpatialLayer(),
this->encodingContext->GetCurrentTemporalLayer(),
this->id.c_str());
json data = json::object();
if (this->encodingContext->GetCurrentSpatialLayer() >= 0)
{
data["spatialLayer"] = this->encodingContext->GetCurrentSpatialLayer();
data["temporalLayer"] = this->encodingContext->GetCurrentTemporalLayer();
}
else
{
data = nullptr;
}
Channel::ChannelNotifier::Emit(this->id, "layerschange", data);
}
inline void SvcConsumer::OnRtpStreamScore(
RTC::RtpStream* , uint8_t , uint8_t )
{
MS_TRACE();
EmitScore();
if (IsActive())
{
if (!this->externallyManagedBitrate)
MayChangeLayers();
}
}
inline void SvcConsumer::OnRtpStreamRetransmitRtpPacket(
RTC::RtpStreamSend* , RTC::RtpPacket* packet)
{
MS_TRACE();
this->listener->OnConsumerRetransmitRtpPacket(this, packet);
EmitTraceEventRtpAndKeyFrameTypes(packet, this->rtpStream->HasRtx());
}
}