Files
UnrealEngine/Engine/Plugins/Media/ElectraCodecs/Source/ElectraDecoders/Private/Utils/VideoDecoderHelpers.cpp
Brandyn / Techy fcc1b09210 init
2026-04-04 15:40:51 -05:00

357 lines
14 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "Utils/VideoDecoderHelpers.h"
#include "Utilities/UtilitiesMP4.h"
#include "MediaDecoderOutput.h"
namespace Electra
{
namespace MPEG
{
void FColorimetryHelper::Reset()
{
CurrentColorimetry.Reset();
}
void FColorimetryHelper::Update(uint8 colour_primaries, uint8 transfer_characteristics, uint8 matrix_coeffs, uint8 video_full_range_flag, uint8 video_format)
{
if (CurrentColorimetry.IsValid() &&
CurrentColorimetry->Colorimetry.ColourPrimaries == colour_primaries &&
CurrentColorimetry->Colorimetry.MatrixCoefficients == matrix_coeffs &&
CurrentColorimetry->Colorimetry.TransferCharacteristics == transfer_characteristics &&
CurrentColorimetry->Colorimetry.VideoFullRangeFlag == video_full_range_flag &&
CurrentColorimetry->Colorimetry.VideoFormat == video_format)
{
return;
}
CurrentColorimetry = MakeShareable(new FVideoDecoderColorimetry(colour_primaries, transfer_characteristics, matrix_coeffs, video_full_range_flag, video_format));
}
void FColorimetryHelper::Update(const TArray<uint8>& InFromCOLRBox)
{
Electra::UtilitiesMP4::FMP4AtomReader boxReader(InFromCOLRBox);
uint32 Type;
if (!boxReader.Read(Type))
{
return;
}
if (Type != Electra::UtilitiesMP4::MakeBoxAtom('n','c','l','x') && Type != Electra::UtilitiesMP4::MakeBoxAtom('n', 'c', 'l', 'c'))
{
return;
}
uint16 colour_primaries, transfer_characteristics, matrix_coeffs;
uint8 video_full_range_flag = 0;
if (!boxReader.Read(colour_primaries) || !boxReader.Read(transfer_characteristics) || !boxReader.Read(matrix_coeffs))
{
return;
}
if (Type == Electra::UtilitiesMP4::MakeBoxAtom('n', 'c', 'l', 'x') && !boxReader.Read(video_full_range_flag))
{
return;
}
Update((uint8) colour_primaries, (uint8)transfer_characteristics, (uint8)matrix_coeffs, (uint8)video_full_range_flag, 5 /*Unspecified video format*/);
}
void FColorimetryHelper::UpdateParamDict(FParamDict& InOutDictionary)
{
InOutDictionary.Set(IDecoderOutputOptionNames::Colorimetry, FVariantValue(CurrentColorimetry));
}
bool FColorimetryHelper::GetCurrentValues(uint8& colour_primaries, uint8& transfer_characteristics, uint8& matrix_coeffs) const
{
if (CurrentColorimetry.IsValid() && CurrentColorimetry->GetMPEGDefinition())
{
colour_primaries = CurrentColorimetry->GetMPEGDefinition()->ColourPrimaries;
transfer_characteristics = CurrentColorimetry->GetMPEGDefinition()->TransferCharacteristics;
matrix_coeffs = CurrentColorimetry->GetMPEGDefinition()->MatrixCoefficients;
return true;
}
else
{
colour_primaries = transfer_characteristics = matrix_coeffs = 2;
return false;
}
}
void FHDRHelper::Reset()
{
CurrentHDRInfo.Reset();
ActiveMasteringDisplayColourVolume.Reset();
ActiveContentLightLevelInfo.Reset();
ActiveAlternativeTransferCharacteristics.Reset();
CurrentAlternativeTransferCharacteristics = -1;
bIsFirst = true;
}
void FHDRHelper::SetHDRType(int32 BitDepth, const FColorimetryHelper& InColorimetry)
{
// Set HDR type from colorimetry!
IVideoDecoderHDRInformation::EType hdrType = IVideoDecoderHDRInformation::EType::Unknown;
uint8 colour_primaries, transfer_characteristics, matrix_coeffs;
InColorimetry.GetCurrentValues(colour_primaries, transfer_characteristics, matrix_coeffs);
if (colour_primaries == 9 && matrix_coeffs == 9)
{
if (BitDepth == 10 && transfer_characteristics == 16)
{
hdrType = IVideoDecoderHDRInformation::EType::PQ10;
if (CurrentHDRInfo.IsValid() && CurrentHDRInfo->GetMasteringDisplayColourVolume() && CurrentHDRInfo->GetContentLightLevelInfo())
{
hdrType = IVideoDecoderHDRInformation::EType::HDR10;
}
}
else if (BitDepth >= 10 && (transfer_characteristics == 18 || (transfer_characteristics == 14 && CurrentAlternativeTransferCharacteristics == 18)))
{
hdrType = IVideoDecoderHDRInformation::EType::HLG10;
}
}
if (CurrentHDRInfo.IsValid())
{
CurrentHDRInfo->SetHDRType(hdrType);
}
}
void FHDRHelper::Update(int32 BitDepth, const FColorimetryHelper& InColorimetry, const TArray<ElectraDecodersUtil::MPEG::FSEIMessage>& InGlobalPrefixSEIs, const TArray<ElectraDecodersUtil::MPEG::FSEIMessage>& InLocalPrefixSEIs, bool bIsNewCLVS)
{
auto UseSEI = [](TOptional<ElectraDecodersUtil::MPEG::FSEIMessage>& InOutWhere, const TArray<ElectraDecodersUtil::MPEG::FSEIMessage>& InSeis, ElectraDecodersUtil::MPEG::FSEIMessage::EPayloadType InWhich) -> bool
{
bool bUpdated = false;
for(auto &sei : InSeis)
{
if (sei.PayloadType == InWhich)
{
bUpdated = !InOutWhere.IsSet() || InOutWhere.GetValue().Message != sei.Message;
if (bUpdated)
{
InOutWhere = sei;
}
break;
}
}
return bUpdated;
};
bool bChanged = false;
if (bIsFirst)
{
bChanged |= UseSEI(ActiveMasteringDisplayColourVolume, InGlobalPrefixSEIs, ElectraDecodersUtil::MPEG::FSEIMessage::EPayloadType::PT_mastering_display_colour_volume);
bChanged |= UseSEI(ActiveContentLightLevelInfo, InGlobalPrefixSEIs, ElectraDecodersUtil::MPEG::FSEIMessage::EPayloadType::PT_content_light_level_info);
bChanged |= UseSEI(ActiveAlternativeTransferCharacteristics, InGlobalPrefixSEIs, ElectraDecodersUtil::MPEG::FSEIMessage::EPayloadType::PT_alternative_transfer_characteristics);
}
bChanged |= UseSEI(ActiveMasteringDisplayColourVolume, InLocalPrefixSEIs, ElectraDecodersUtil::MPEG::FSEIMessage::EPayloadType::PT_mastering_display_colour_volume);
bChanged |= UseSEI(ActiveContentLightLevelInfo, InLocalPrefixSEIs, ElectraDecodersUtil::MPEG::FSEIMessage::EPayloadType::PT_content_light_level_info);
bChanged |= UseSEI(ActiveAlternativeTransferCharacteristics, InLocalPrefixSEIs, ElectraDecodersUtil::MPEG::FSEIMessage::EPayloadType::PT_alternative_transfer_characteristics);
if (bChanged)
{
CurrentHDRInfo = MakeShareable(new FVideoDecoderHDRInformation);
// Mastering Display Colour Volume
if (ActiveMasteringDisplayColourVolume.IsSet())
{
ElectraDecodersUtil::MPEG::FSEImastering_display_colour_volume seim;
if (ElectraDecodersUtil::MPEG::ParseFromSEIMessage(seim, ActiveMasteringDisplayColourVolume.GetValue()))
{
const float ST2086_Chroma_Scale = 50000.0f;
const float ST2086_Luma_Scale = 10000.0f;
FVideoDecoderHDRMetadata_mastering_display_colour_volume mdcv;
// The order in the HEVC SEI messages is G,B,R
mdcv.display_primaries_x[0] = seim.display_primaries_x[2] / ST2086_Chroma_Scale;
mdcv.display_primaries_y[0] = seim.display_primaries_y[2] / ST2086_Chroma_Scale;
mdcv.display_primaries_x[1] = seim.display_primaries_x[0] / ST2086_Chroma_Scale;
mdcv.display_primaries_y[1] = seim.display_primaries_y[0] / ST2086_Chroma_Scale;
mdcv.display_primaries_x[2] = seim.display_primaries_x[1] / ST2086_Chroma_Scale;
mdcv.display_primaries_y[2] = seim.display_primaries_y[1] / ST2086_Chroma_Scale;
mdcv.white_point_x = seim.white_point_x / ST2086_Chroma_Scale;
mdcv.white_point_y = seim.white_point_y / ST2086_Chroma_Scale;
mdcv.max_display_mastering_luminance = seim.max_display_mastering_luminance / ST2086_Luma_Scale;
mdcv.min_display_mastering_luminance = seim.min_display_mastering_luminance / ST2086_Luma_Scale;
CurrentHDRInfo->SetMasteringDisplayColourVolume(mdcv);
}
}
// Content Light Level Info
if (ActiveContentLightLevelInfo.IsSet())
{
ElectraDecodersUtil::MPEG::FSEIcontent_light_level_info seim;
if (ElectraDecodersUtil::MPEG::ParseFromSEIMessage(seim, ActiveContentLightLevelInfo.GetValue()))
{
FVideoDecoderHDRMetadata_content_light_level_info ll;
ll.max_content_light_level = seim.max_content_light_level;
ll.max_pic_average_light_level = seim.max_pic_average_light_level;
CurrentHDRInfo->SetContentLightLevelInfo(ll);
}
}
// Alternative Transfer Characteristics
if (ActiveAlternativeTransferCharacteristics.IsSet())
{
ElectraDecodersUtil::MPEG::FSEIalternative_transfer_characteristics seim;
if (ElectraDecodersUtil::MPEG::ParseFromSEIMessage(seim, ActiveAlternativeTransferCharacteristics.GetValue()))
{
CurrentAlternativeTransferCharacteristics = seim.preferred_transfer_characteristics;
}
}
}
SetHDRType(BitDepth, InColorimetry);
bIsFirst = false;
}
void FHDRHelper::UpdateWith(const ElectraDecodersUtil::MPEG::FSEImastering_display_colour_volume& InSEI)
{
if (!CurrentHDRInfo.IsValid())
{
CurrentHDRInfo = MakeShareable(new FVideoDecoderHDRInformation);
}
const float ST2086_Chroma_Scale = 50000.0f;
const float ST2086_Luma_Scale = 10000.0f;
FVideoDecoderHDRMetadata_mastering_display_colour_volume mdcv;
// The order in the HEVC SEI messages is G,B,R
mdcv.display_primaries_x[0] = InSEI.display_primaries_x[2] / ST2086_Chroma_Scale;
mdcv.display_primaries_y[0] = InSEI.display_primaries_y[2] / ST2086_Chroma_Scale;
mdcv.display_primaries_x[1] = InSEI.display_primaries_x[0] / ST2086_Chroma_Scale;
mdcv.display_primaries_y[1] = InSEI.display_primaries_y[0] / ST2086_Chroma_Scale;
mdcv.display_primaries_x[2] = InSEI.display_primaries_x[1] / ST2086_Chroma_Scale;
mdcv.display_primaries_y[2] = InSEI.display_primaries_y[1] / ST2086_Chroma_Scale;
mdcv.white_point_x = InSEI.white_point_x / ST2086_Chroma_Scale;
mdcv.white_point_y = InSEI.white_point_y / ST2086_Chroma_Scale;
mdcv.max_display_mastering_luminance = InSEI.max_display_mastering_luminance / ST2086_Luma_Scale;
mdcv.min_display_mastering_luminance = InSEI.min_display_mastering_luminance / ST2086_Luma_Scale;
CurrentHDRInfo->SetMasteringDisplayColourVolume(mdcv);
}
void FHDRHelper::UpdateWith(const ElectraDecodersUtil::MPEG::FSEIcontent_light_level_info& InSEI)
{
if (!CurrentHDRInfo.IsValid())
{
CurrentHDRInfo = MakeShareable(new FVideoDecoderHDRInformation);
}
FVideoDecoderHDRMetadata_content_light_level_info ll;
ll.max_content_light_level = InSEI.max_content_light_level;
ll.max_pic_average_light_level = InSEI.max_pic_average_light_level;
CurrentHDRInfo->SetContentLightLevelInfo(ll);
}
void FHDRHelper::UpdateWith(const ElectraDecodersUtil::MPEG::FSEIalternative_transfer_characteristics& InSEI)
{
if (!CurrentHDRInfo.IsValid())
{
CurrentHDRInfo = MakeShareable(new FVideoDecoderHDRInformation);
}
CurrentAlternativeTransferCharacteristics = InSEI.preferred_transfer_characteristics;
}
void FHDRHelper::UpdateFromMPEGBoxes(int32 BitDepth, const FColorimetryHelper& InColorimetry, const TArray<uint8>& InMDCVBox, const TArray<uint8>& InCLLIBox)
{
if (!CurrentHDRInfo.IsValid() && (InMDCVBox.Num() || InCLLIBox.Num()))
{
CurrentHDRInfo = MakeShareable(new FVideoDecoderHDRInformation);
}
if (InMDCVBox.Num())
{
Electra::UtilitiesMP4::FMP4AtomReader boxReader(InMDCVBox);
struct mastering_display_colour_volume
{
uint16 display_primaries_x[3]{ 0 };
uint16 display_primaries_y[3]{ 0 };
uint16 white_point_x = 0;
uint16 white_point_y = 0;
uint32 max_display_mastering_luminance = 0;
uint32 min_display_mastering_luminance = 0;
};
mastering_display_colour_volume boxData;
for(int32 i=0; i<3; ++i)
{
if (!boxReader.Read(boxData.display_primaries_x[i]) || !boxReader.Read(boxData.display_primaries_y[i]))
{
return;
}
}
if (!boxReader.Read(boxData.white_point_x) || !boxReader.Read(boxData.white_point_y))
{
return;
}
if (!boxReader.Read(boxData.max_display_mastering_luminance) || !boxReader.Read(boxData.min_display_mastering_luminance))
{
return;
}
const float ST2086_Chroma_Scale = 50000.0f;
const float ST2086_Luma_Scale = 10000.0f;
FVideoDecoderHDRMetadata_mastering_display_colour_volume mdcv;
// The order in the MDCV box is G,B,R
mdcv.display_primaries_x[0] = boxData.display_primaries_x[2] / ST2086_Chroma_Scale;
mdcv.display_primaries_y[0] = boxData.display_primaries_y[2] / ST2086_Chroma_Scale;
mdcv.display_primaries_x[1] = boxData.display_primaries_x[0] / ST2086_Chroma_Scale;
mdcv.display_primaries_y[1] = boxData.display_primaries_y[0] / ST2086_Chroma_Scale;
mdcv.display_primaries_x[2] = boxData.display_primaries_x[1] / ST2086_Chroma_Scale;
mdcv.display_primaries_y[2] = boxData.display_primaries_y[1] / ST2086_Chroma_Scale;
mdcv.white_point_x = boxData.white_point_x / ST2086_Chroma_Scale;
mdcv.white_point_y = boxData.white_point_y / ST2086_Chroma_Scale;
mdcv.max_display_mastering_luminance = boxData.max_display_mastering_luminance / ST2086_Luma_Scale;
mdcv.min_display_mastering_luminance = boxData.min_display_mastering_luminance / ST2086_Luma_Scale;
CurrentHDRInfo->SetMasteringDisplayColourVolume(mdcv);
}
if (InCLLIBox.Num())
{
Electra::UtilitiesMP4::FMP4AtomReader boxReader(InCLLIBox);
uint16 max_content_light_level = 0; // MaxCLL
uint16 max_pic_average_light_level = 0; // MaxFALL
if (!boxReader.Read(max_content_light_level) || !boxReader.Read(max_pic_average_light_level))
{
return;
}
FVideoDecoderHDRMetadata_content_light_level_info ll;
ll.max_content_light_level = max_content_light_level;
ll.max_pic_average_light_level = max_pic_average_light_level;
CurrentHDRInfo->SetContentLightLevelInfo(ll);
}
SetHDRType(BitDepth, InColorimetry);
}
void FHDRHelper::Update(int32 BitDepth, const FColorimetryHelper& InColorimetry, const TOptional<FVideoDecoderHDRMetadata_mastering_display_colour_volume>& InMDCV, const TOptional<FVideoDecoderHDRMetadata_content_light_level_info>& InCLLI)
{
if (!InMDCV.IsSet() && !InCLLI.IsSet())
{
return;
}
if (!CurrentHDRInfo.IsValid())
{
CurrentHDRInfo = MakeShareable(new FVideoDecoderHDRInformation);
}
if (InMDCV.IsSet())
{
CurrentHDRInfo->SetMasteringDisplayColourVolume(InMDCV.GetValue());
}
if (InCLLI.IsSet())
{
CurrentHDRInfo->SetContentLightLevelInfo(InCLLI.GetValue());
}
SetHDRType(BitDepth, InColorimetry);
}
void FHDRHelper::UpdateParamDict(FParamDict& InOutDictionary)
{
InOutDictionary.Set(IDecoderOutputOptionNames::HDRInfo, FVariantValue(CurrentHDRInfo));
}
} // namespace MPEG
} // namespace Electra