#define MS_CLASS "RTC::Codecs::H264_SVC"
#include "RTC/Codecs/H264_SVC.hpp"
#include "Logger.hpp"
#include "Utils.hpp"
namespace RTC
{
namespace Codecs
{
H264_SVC::PayloadDescriptor* H264_SVC::Parse(
const uint8_t* data, size_t len, RTC::RtpPacket::FrameMarking* frameMarking, uint8_t frameMarkingLen)
{
MS_TRACE();
if (len < 2)
{
return nullptr;
}
std::unique_ptr<PayloadDescriptor> payloadDescriptor(new PayloadDescriptor());
if (frameMarking)
{
payloadDescriptor->s = frameMarking->start;
payloadDescriptor->e = frameMarking->end;
payloadDescriptor->i = frameMarking->independent;
payloadDescriptor->d = frameMarking->discardable;
payloadDescriptor->b = frameMarking->base;
payloadDescriptor->tlIndex = frameMarking->tid;
payloadDescriptor->hasTlIndex = true;
if (frameMarkingLen >= 2)
{
payloadDescriptor->hasSlIndex = true;
payloadDescriptor->slIndex = frameMarking->lid >> 4 & 0x07;
}
if (frameMarkingLen == 3)
{
payloadDescriptor->hasTl0picidx = true;
payloadDescriptor->tl0picidx = frameMarking->tl0picidx;
}
if (frameMarking->start && frameMarking->independent)
{
payloadDescriptor->isKeyFrame = true;
}
}
if (!frameMarking || !payloadDescriptor->isKeyFrame)
{
const uint8_t nal = *data & 0x1F;
switch (nal)
{
case 1:
case 5:
case 7:
case 14:
case 20:
{
payloadDescriptor =
H264_SVC::ParseSingleNalu(data, len, std::move(payloadDescriptor), true);
if (payloadDescriptor == nullptr)
{
return nullptr;
}
break;
}
case 24:
{
size_t offset{ 1 };
len -= 1;
while (len >= 3)
{
auto naluSize = Utils::Byte::Get2Bytes(data, offset);
payloadDescriptor = H264_SVC::ParseSingleNalu(
(data + offset + sizeof(naluSize)),
(len - sizeof(naluSize)),
std::move(payloadDescriptor),
true);
if (payloadDescriptor == nullptr)
{
return nullptr;
}
if (payloadDescriptor->isKeyFrame)
{
break;
}
if (len < (naluSize + sizeof(naluSize)))
{
break;
}
offset += naluSize + sizeof(naluSize);
len -= naluSize + sizeof(naluSize);
}
break;
}
case 28:
case 29:
{
const uint8_t startBit = *(data + 1) & 0x80;
if (startBit == 128)
{
payloadDescriptor = H264_SVC::ParseSingleNalu(
(data + 1), (len - 1), std::move(payloadDescriptor), (startBit == 128 ? true : false));
}
if (payloadDescriptor == nullptr)
{
return nullptr;
}
break;
}
}
}
return payloadDescriptor.release();
}
std::unique_ptr<H264_SVC::PayloadDescriptor> H264_SVC::ParseSingleNalu(
const uint8_t* data,
size_t len,
std::unique_ptr<H264_SVC::PayloadDescriptor> payloadDescriptor,
bool isStartBit)
{
const uint8_t nal = *data & 0x1F;
switch (nal)
{
case 5:
payloadDescriptor->isKeyFrame = true;
case 1:
{
payloadDescriptor->slIndex = 0;
payloadDescriptor->tlIndex = 0;
payloadDescriptor->hasSlIndex = false;
payloadDescriptor->hasTlIndex = false;
break;
}
case 14:
case 20:
{
size_t offset{ 1 };
uint8_t byte = data[offset];
payloadDescriptor->idr = byte >> 6 & 0x01;
payloadDescriptor->priorityId = byte & 0x06;
payloadDescriptor->isKeyFrame = (isStartBit && payloadDescriptor->idr) ? true : false;
if (len < ++offset + 1)
{
return nullptr;
}
byte = data[offset];
payloadDescriptor->noIntLayerPredFlag = byte >> 7 & 0x01;
payloadDescriptor->slIndex = byte >> 4 & 0x03;
if (len < ++offset + 1)
{
return nullptr;
}
byte = data[offset];
payloadDescriptor->tlIndex = byte >> 5 & 0x03;
payloadDescriptor->hasSlIndex = payloadDescriptor->slIndex ? true : false;
payloadDescriptor->hasTlIndex = payloadDescriptor->tlIndex ? true : false;
break;
}
case 7:
{
payloadDescriptor->isKeyFrame = isStartBit ? true : false;
break;
}
}
return payloadDescriptor;
}
void H264_SVC::ProcessRtpPacket(RTC::RtpPacket* packet)
{
MS_TRACE();
auto* data = packet->GetPayload();
auto len = packet->GetPayloadLength();
RtpPacket::FrameMarking* frameMarking{ nullptr };
uint8_t frameMarkingLen{ 0 };
packet->ReadFrameMarking(&frameMarking, frameMarkingLen);
PayloadDescriptor* payloadDescriptor =
H264_SVC::Parse(data, len, frameMarking, frameMarkingLen);
if (!payloadDescriptor)
{
return;
}
auto* payloadDescriptorHandler = new PayloadDescriptorHandler(payloadDescriptor);
packet->SetPayloadDescriptorHandler(payloadDescriptorHandler);
}
void H264_SVC::PayloadDescriptor::Dump() const
{
MS_TRACE();
MS_DUMP("<PayloadDescriptor>");
MS_DUMP(
" s:%" PRIu8 "|e:%" PRIu8 "|i:%" PRIu8 "|d:%" PRIu8 "|b:%" PRIu8,
this->s,
this->e,
this->i,
this->d,
this->b);
MS_DUMP(" hasSlIndex : %s", this->hasSlIndex ? "true" : "false");
MS_DUMP(" hasTlIndex : %s", this->hasTlIndex ? "true" : "false");
MS_DUMP(" tl0picidx : %" PRIu8, this->tl0picidx);
MS_DUMP(" noIntLayerPredFlag : %" PRIu8, this->noIntLayerPredFlag);
MS_DUMP(" isKeyFrame : %s", this->isKeyFrame ? "true" : "false");
MS_DUMP("</PayloadDescriptor>");
}
H264_SVC::PayloadDescriptorHandler::PayloadDescriptorHandler(
H264_SVC::PayloadDescriptor* payloadDescriptor)
{
MS_TRACE();
this->payloadDescriptor.reset(payloadDescriptor);
}
bool H264_SVC::PayloadDescriptorHandler::Process(
RTC::Codecs::EncodingContext* encodingContext, uint8_t* , bool& marker)
{
MS_TRACE();
auto* context = static_cast<RTC::Codecs::H264_SVC::EncodingContext*>(encodingContext);
MS_ASSERT(context->GetTargetSpatialLayer() >= 0, "target spatial layer cannot be -1");
MS_ASSERT(context->GetTargetTemporalLayer() >= 0, "target temporal layer cannot be -1");
auto packetSpatialLayer = GetSpatialLayer();
auto packetTemporalLayer = GetTemporalLayer();
auto tmpSpatialLayer = context->GetCurrentSpatialLayer();
auto tmpTemporalLayer = context->GetCurrentTemporalLayer();
if (
packetSpatialLayer >= context->GetSpatialLayers() ||
packetTemporalLayer >= context->GetTemporalLayers()
)
{
MS_WARN_TAG(
rtp, "too high packet layers %" PRIu8 ":%" PRIu8, packetSpatialLayer, packetTemporalLayer);
return false;
}
const bool isOldPacket = false;
if (context->GetTargetSpatialLayer() > context->GetCurrentSpatialLayer())
{
if (this->payloadDescriptor->isKeyFrame)
{
MS_DEBUG_DEV(
"upgrading tmpSpatialLayer from %" PRIu16 " to %" PRIu16 " (packet:%" PRIu8 ":%" PRIu8
")",
context->GetCurrentSpatialLayer(),
context->GetTargetSpatialLayer(),
packetSpatialLayer,
packetTemporalLayer);
tmpSpatialLayer = context->GetTargetSpatialLayer();
tmpTemporalLayer = 0; }
}
else if (context->GetTargetSpatialLayer() < context->GetCurrentSpatialLayer())
{
if (context->IsKSvc())
{
if (this->payloadDescriptor->isKeyFrame)
{
MS_DEBUG_DEV(
"downgrading tmpSpatialLayer from %" PRIu16 " to %" PRIu16 " (packet:%" PRIu8
":%" PRIu8 ") after keyframe (K-SVC)",
context->GetCurrentSpatialLayer(),
context->GetTargetSpatialLayer(),
packetSpatialLayer,
packetTemporalLayer);
tmpSpatialLayer = context->GetTargetSpatialLayer();
tmpTemporalLayer = 0; }
}
else
{
if (
packetSpatialLayer == context->GetTargetSpatialLayer() &&
this->payloadDescriptor->e
)
{
MS_DEBUG_DEV(
"downgrading tmpSpatialLayer from %" PRIu16 " to %" PRIu16 " (packet:%" PRIu8
":%" PRIu8 ") without keyframe (full SVC)",
context->GetCurrentSpatialLayer(),
context->GetTargetSpatialLayer(),
packetSpatialLayer,
packetTemporalLayer);
tmpSpatialLayer = context->GetTargetSpatialLayer();
tmpTemporalLayer = 0; }
}
}
if (packetSpatialLayer > tmpSpatialLayer && !isOldPacket)
{
return false;
}
if (!isOldPacket)
{
if (context->GetTargetTemporalLayer() > context->GetCurrentTemporalLayer())
{
if (
packetTemporalLayer >= context->GetCurrentTemporalLayer() + 1 &&
this->payloadDescriptor->s
)
{
MS_DEBUG_DEV(
"upgrading tmpTemporalLayer from %" PRIu16 " to %" PRIu8 " (packet:%" PRIu8 ":%" PRIu8
")",
context->GetCurrentTemporalLayer(),
packetTemporalLayer,
packetSpatialLayer,
packetTemporalLayer);
tmpTemporalLayer = packetTemporalLayer;
}
}
else if (context->GetTargetTemporalLayer() < context->GetCurrentTemporalLayer())
{
if (
packetTemporalLayer == context->GetTargetTemporalLayer() &&
this->payloadDescriptor->e
)
{
MS_DEBUG_DEV(
"downgrading tmpTemporalLayer from %" PRIu16 " to %" PRIu16 " (packet:%" PRIu8
":%" PRIu8 ")",
context->GetCurrentTemporalLayer(),
context->GetTargetTemporalLayer(),
packetSpatialLayer,
packetTemporalLayer);
tmpTemporalLayer = context->GetTargetTemporalLayer();
}
}
if (packetTemporalLayer > tmpTemporalLayer)
{
return false;
}
}
if (packetSpatialLayer == tmpSpatialLayer && this->payloadDescriptor->e)
{
marker = true;
}
if (tmpSpatialLayer != context->GetCurrentSpatialLayer())
{
context->SetCurrentSpatialLayer(tmpSpatialLayer);
}
if (tmpTemporalLayer != context->GetCurrentTemporalLayer())
{
context->SetCurrentTemporalLayer(tmpTemporalLayer);
}
return true;
}
void H264_SVC::PayloadDescriptorHandler::Restore(uint8_t* )
{
MS_TRACE();
}
} }