#define MS_CLASS "RTC::Codecs::H264"
#include "RTC/Codecs/H264.hpp"
#include "Logger.hpp"
#include "Utils.hpp"
namespace RTC
{
namespace Codecs
{
H264::PayloadDescriptor* H264::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->tid = frameMarking->tid;
payloadDescriptor->hasTid = true;
if (frameMarkingLen >= 2)
{
payloadDescriptor->hasLid = true;
payloadDescriptor->lid = frameMarking->lid;
}
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 7:
{
payloadDescriptor->isKeyFrame = true;
break;
}
case 24:
{
size_t offset{ 1 };
len -= 1;
while (len >= 3)
{
auto naluSize = Utils::Byte::Get2Bytes(data, offset);
const uint8_t subnal = *(data + offset + sizeof(naluSize)) & 0x1F;
if (subnal == 7)
{
payloadDescriptor->isKeyFrame = true;
break;
}
if (len < (naluSize + sizeof(naluSize)))
{
break;
}
offset += naluSize + sizeof(naluSize);
len -= naluSize + sizeof(naluSize);
}
break;
}
case 28:
case 29:
{
const uint8_t subnal = *(data + 1) & 0x1F;
const uint8_t startBit = *(data + 1) & 0x80;
if (subnal == 7 && startBit == 128)
{
payloadDescriptor->isKeyFrame = true;
}
break;
}
}
}
return payloadDescriptor.release();
}
void H264::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::Parse(data, len, frameMarking, frameMarkingLen);
if (!payloadDescriptor)
{
return;
}
auto* payloadDescriptorHandler = new PayloadDescriptorHandler(payloadDescriptor);
packet->SetPayloadDescriptorHandler(payloadDescriptorHandler);
}
void H264::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);
if (this->hasTid)
{
MS_DUMP(" tid : %" PRIu8, this->tid);
}
if (this->hasLid)
{
MS_DUMP(" lid : %" PRIu8, this->lid);
}
if (this->hasTl0picidx)
{
MS_DUMP(" tl0picidx : %" PRIu8, this->tl0picidx);
}
MS_DUMP(" isKeyFrame : %s", this->isKeyFrame ? "true" : "false");
MS_DUMP("</PayloadDescriptor>");
}
H264::PayloadDescriptorHandler::PayloadDescriptorHandler(H264::PayloadDescriptor* payloadDescriptor)
{
MS_TRACE();
this->payloadDescriptor.reset(payloadDescriptor);
}
bool H264::PayloadDescriptorHandler::Process(
RTC::Codecs::EncodingContext* encodingContext, uint8_t* , bool& )
{
MS_TRACE();
auto* context = static_cast<RTC::Codecs::H264::EncodingContext*>(encodingContext);
MS_ASSERT(context->GetTargetTemporalLayer() >= 0, "target temporal layer cannot be -1");
if (context->GetTemporalLayers() > 1 && !this->payloadDescriptor->hasTid)
{
MS_WARN_DEV("stream is supposed to have >1 temporal layers but does not have tid field");
}
if (
this->payloadDescriptor->hasTid &&
this->payloadDescriptor->tid > context->GetTargetTemporalLayer()
)
{
return false;
}
else if (
this->payloadDescriptor->hasTid &&
this->payloadDescriptor->tid > context->GetCurrentTemporalLayer() &&
!this->payloadDescriptor->b
)
{
return false;
}
if (
this->payloadDescriptor->hasTid &&
this->payloadDescriptor->tid > context->GetCurrentTemporalLayer()
)
{
context->SetCurrentTemporalLayer(this->payloadDescriptor->tid);
}
else if (!this->payloadDescriptor->hasTid)
{
context->SetCurrentTemporalLayer(0);
}
if (context->GetCurrentTemporalLayer() > context->GetTargetTemporalLayer())
{
context->SetCurrentTemporalLayer(context->GetTargetTemporalLayer());
}
return true;
}
void H264::PayloadDescriptorHandler::Restore(uint8_t* )
{
MS_TRACE();
}
} }