Files
UnrealEngine/Engine/Plugins/Media/ElectraCodecs/Source/ElectraDecoders/Private/ElectraDecodersUtils.cpp
2025-05-18 13:04:45 +08:00

495 lines
12 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "ElectraDecodersUtils.h"
namespace ElectraDecodersUtil
{
namespace
{
inline void LexFromStringHex(int32& OutValue, const TCHAR* Buffer)
{
OutValue = FCString::Strtoi(Buffer, nullptr, 16);
}
inline void LexFromStringHexU64(uint64& OutValue, const TCHAR* Buffer)
{
OutValue = FCString::Strtoui64(Buffer, nullptr, 16);
}
}
bool ParseMimeTypeWithCodec(FMimeTypeVideoCodecInfo& OutInfo, const FString& InMimeType)
{
return false;
}
bool ParseMimeTypeWithCodec(FMimeTypeAudioCodecInfo& OutInfo, const FString& InMimeType)
{
return false;
}
bool ParseCodecMP4A(FMimeTypeAudioCodecInfo& OutInfo, const FString& InCodecFormat)
{
if (!InCodecFormat.StartsWith("mp4a"))
{
return false;
}
OutInfo.Codec = TEXT("mp4a");
// Object and profile follow?
if (InCodecFormat.Len() > 6 && InCodecFormat[4] == TCHAR('.'))
{
// mp4a.xx.d is recognized.
FString OT, Profile;
int32 DotPos = InCodecFormat.Find(TEXT("."), ESearchCase::CaseSensitive, ESearchDir::FromStart, 5);
OT = InCodecFormat.Mid(5, DotPos != INDEX_NONE ? DotPos - 5 : DotPos);
Profile = InCodecFormat.Mid(DotPos != INDEX_NONE ? DotPos + 1 : DotPos);
OutInfo.ObjectType = FCString::Strtoi(*OT, nullptr, 16);
int32 ProfileValue = 0;
LexFromString(ProfileValue, *Profile);
OutInfo.Profile = ProfileValue;
}
return true;
}
bool ParseCodecH264(FMimeTypeVideoCodecInfo& OutInfo, const FString& InCodecFormat)
{
if (InCodecFormat.StartsWith("avc"))
{
// avc1 and avc3 (inband SPS/PPS) are recognized.
if (InCodecFormat.Len() > 3)
{
// avc1 or avc3 only!
if (InCodecFormat[3] != TCHAR('1') && InCodecFormat[3] != TCHAR('3'))
{
return false;
}
OutInfo.Codec = TEXT("avc");
// Profile and level follow?
if (InCodecFormat.Len() > 5 && InCodecFormat[4] == TCHAR('.'))
{
int32 DotPos;
InCodecFormat.FindLastChar(TCHAR('.'), DotPos);
FString Temp;
int32 TempValue;
// We recognize the expected format avcC.xxyyzz and for legacy reasons also avcC.xxx.zz
if (InCodecFormat.Len() == 11 && DotPos == 4)
{
Temp = InCodecFormat.Mid(5, 2);
LexFromStringHex(TempValue, *Temp);
OutInfo.Profile = TempValue;
Temp = InCodecFormat.Mid(7, 2);
LexFromStringHex(TempValue, *Temp);
OutInfo.Constraints = TempValue;
Temp = InCodecFormat.Mid(9, 2);
LexFromStringHex(TempValue, *Temp);
OutInfo.Level = TempValue;
}
else if (DotPos != INDEX_NONE)
{
Temp = InCodecFormat.Mid(5, DotPos-5);
LexFromString(TempValue, *Temp);
OutInfo.Profile = TempValue;
Temp = InCodecFormat.Mid(DotPos+1);
LexFromString(TempValue, *Temp);
OutInfo.Level = TempValue;
}
else
{
return false;
}
}
}
return true;
}
return false;
}
bool ParseCodecH265(FMimeTypeVideoCodecInfo& OutInfo, const FString& InCodecFormat)
{
if (InCodecFormat.StartsWith("hvc1") || InCodecFormat.StartsWith("hev1"))
{
// hvc1 and hev1 (inband VPS/SPS/PPS) are recognized.
if (InCodecFormat.Len() > 4)
{
OutInfo.Codec = TEXT("hevc");
FString oti = InCodecFormat;
FString Temp;
int32 DotPos;
if (oti.FindChar(TCHAR('.'), DotPos))
{
int32 general_profile_space = 0;
int32 general_tier_flag = 0;
int32 general_profile_idc = 0;
int32 general_level_idc = 0;
uint32 general_profile_compatibility_flag = 0;
uint64 contraint_flags = 0;
oti.RightChopInline(DotPos + 1);
// optional general_profile_space
if (oti[0] == TCHAR('A') || oti[0] == TCHAR('B') || oti[0] == TCHAR('C'))
{
general_profile_space = oti[0] - TCHAR('A') + 1;
oti.RightChopInline(1);
}
else if (oti[0] == TCHAR('a') || oti[0] == TCHAR('b') || oti[0] == TCHAR('c'))
{
general_profile_space = oti[0] - TCHAR('a') + 1;
oti.RightChopInline(1);
}
// general_profile_idc
if (oti.FindChar(TCHAR('.'), DotPos))
{
Temp = oti.Left(DotPos);
oti.RightChopInline(DotPos + 1);
LexFromString(general_profile_idc, *Temp);
}
// general_profile_compatibility_flags
if (oti.FindChar(TCHAR('.'), DotPos))
{
Temp = oti.Left(DotPos);
oti.RightChopInline(DotPos + 1);
LexFromString(general_profile_compatibility_flag, *Temp);
}
// general_tier_flag
if (oti[0] != TCHAR('L') && oti[0] != TCHAR('H') && oti[0] != TCHAR('l') && oti[0] != TCHAR('h'))
{
return false;
}
else if (oti[0] == TCHAR('H') || oti[0] == TCHAR('h'))
{
general_tier_flag = 1;
}
oti.RightChopInline(1);
// constraint_flags
FString ConstraintFlags;
if (oti.FindChar(TCHAR('.'), DotPos))
{
ConstraintFlags = oti.Mid(DotPos + 1);
oti.LeftInline(DotPos);
ConstraintFlags.ReplaceInline(TEXT("."), TEXT(""));
ConstraintFlags += TEXT("000000000000");
ConstraintFlags.LeftInline(12);
LexFromStringHexU64(contraint_flags, *ConstraintFlags);
}
// general_level_idc
LexFromString(general_level_idc, *oti);
OutInfo.Profile = general_profile_idc;
OutInfo.Level = general_level_idc;
OutInfo.ProfileSpace = general_profile_space;
OutInfo.CompatibilityFlags = BitReverse32(general_profile_compatibility_flag);
OutInfo.Tier = general_tier_flag;
OutInfo.Constraints = contraint_flags;
return true;
}
}
}
return false;
}
bool ParseCodecVP8(FMimeTypeVideoCodecInfo& OutInfo, const FString& InCodecFormat, const TArray<uint8>& InvpcCBox)
{
if (InCodecFormat.StartsWith("vp08"))
{
FString oti = InCodecFormat;
if (!InvpcCBox.IsEmpty())
{
// Enough data to represent the `vpcC` box, and is it of version 1?
if (InvpcCBox.Num() < 12 || InvpcCBox[0] != 1)
{
return false;
}
OutInfo.Profile = InvpcCBox[4];
OutInfo.Level = InvpcCBox[5];
OutInfo.NumBitsLuma = InvpcCBox[6] >> 4;
return true;
}
if (oti.Len() > 4)
{
OutInfo.Codec = TEXT("vp08");
FString Temp;
int32 DotPos;
if (oti.FindChar(TCHAR('.'), DotPos))
{
int32 Components[8] {0}, NumComponents=0;
oti.RightChopInline(DotPos + 1);
while(oti.Len() && NumComponents<UE_ARRAY_COUNT(Components))
{
if (oti.FindChar(TCHAR('.'), DotPos))
{
Temp = oti.Left(DotPos);
oti.RightChopInline(DotPos + 1);
LexFromString(Components[NumComponents++], *Temp);
}
else
{
LexFromString(Components[NumComponents++], *oti);
break;
}
}
OutInfo.Profile = Components[0];
OutInfo.Level = Components[1];
OutInfo.NumBitsLuma = Components[2];
return true;
}
}
}
return false;
}
bool ParseCodecVP9(FMimeTypeVideoCodecInfo& OutInfo, const FString& InCodecFormat, const TArray<uint8>& InvpcCBox)
{
if (InCodecFormat.StartsWith("vp09"))
{
FString oti = InCodecFormat;
if (!InvpcCBox.IsEmpty())
{
// Enough data to represent the `vpcC` box, and is it of version 1?
if (InvpcCBox.Num() < 12 || InvpcCBox[0] != 1)
{
return false;
}
OutInfo.Extras[0] = OutInfo.Profile = InvpcCBox[4];
OutInfo.Extras[1] = OutInfo.Level = InvpcCBox[5];
OutInfo.Extras[2] = OutInfo.NumBitsLuma = InvpcCBox[6] >> 4;
OutInfo.Extras[3] = (InvpcCBox[6] >> 1) & 7;
OutInfo.Extras[4] = InvpcCBox[7];
OutInfo.Extras[5] = InvpcCBox[8];
OutInfo.Extras[6] = InvpcCBox[9];
OutInfo.Extras[7] = InvpcCBox[6] & 1;
return true;
}
if (oti.Len() > 4)
{
OutInfo.Codec = TEXT("vp09");
FString Temp;
int32 DotPos;
if (oti.FindChar(TCHAR('.'), DotPos))
{
int32 Components[8] {0}, NumComponents=0;
oti.RightChopInline(DotPos + 1);
while(oti.Len() && NumComponents<UE_ARRAY_COUNT(Components))
{
if (oti.FindChar(TCHAR('.'), DotPos))
{
Temp = oti.Left(DotPos);
oti.RightChopInline(DotPos + 1);
LexFromString(Components[NumComponents++], *Temp);
}
else
{
LexFromString(Components[NumComponents++], *oti);
break;
}
}
OutInfo.Extras[0] = OutInfo.Profile = Components[0];
OutInfo.Extras[1] = OutInfo.Level = Components[1];
OutInfo.Extras[2] = OutInfo.NumBitsLuma = Components[2];
OutInfo.Extras[3] = Components[3];
OutInfo.Extras[4] = Components[4];
OutInfo.Extras[5] = Components[5];
OutInfo.Extras[6] = Components[6];
OutInfo.Extras[7] = Components[7];
return true;
}
}
}
return false;
}
int64 GetVariantValueSafeI64(const TMap<FString, FVariant>& InFromMap, const FString& InName, int64 InDefaultValue)
{
int64 V = InDefaultValue;
const FVariant* Var = InFromMap.Find(InName);
if (Var)
{
if (Var->GetType() == EVariantTypes::Int32)
{
V = Var->GetValue<int32>();
}
else if (Var->GetType() == EVariantTypes::Int64)
{
V = Var->GetValue<int64>();
}
else if (Var->GetType() == EVariantTypes::UInt64)
{
V = (int64) Var->GetValue<uint64>();
}
else if (Var->GetType() == EVariantTypes::UInt32)
{
V = (int64) Var->GetValue<uint32>();
}
else if (Var->GetType() == EVariantTypes::Int16)
{
V = Var->GetValue<int16>();
}
else if (Var->GetType() == EVariantTypes::Int8)
{
V = Var->GetValue<int8>();
}
else if (Var->GetType() == EVariantTypes::UInt16)
{
V = Var->GetValue<uint16>();
}
else if (Var->GetType() == EVariantTypes::UInt8)
{
V = Var->GetValue<uint8>();
}
else if (Var->GetType() == EVariantTypes::Float)
{
V = (int64) Var->GetValue<float>();
}
else if (Var->GetType() == EVariantTypes::Double)
{
V = (int64) Var->GetValue<double>();
}
else if (Var->GetType() == EVariantTypes::Bool)
{
V = Var->GetValue<bool>() ? 1 : 0;
}
}
return V;
}
uint64 GetVariantValueSafeU64(const TMap<FString, FVariant>& InFromMap, const FString& InName, uint64 InDefaultValue)
{
uint64 V = InDefaultValue;
const FVariant* Var = InFromMap.Find(InName);
if (Var)
{
if (Var->GetType() == EVariantTypes::Int32)
{
V = Var->GetValue<int32>();
}
else if (Var->GetType() == EVariantTypes::Int64)
{
V = (uint64) Var->GetValue<int64>();
}
else if (Var->GetType() == EVariantTypes::UInt64)
{
V = Var->GetValue<uint64>();
}
else if (Var->GetType() == EVariantTypes::UInt32)
{
V = Var->GetValue<uint32>();
}
else if (Var->GetType() == EVariantTypes::Int16)
{
V = Var->GetValue<int16>();
}
else if (Var->GetType() == EVariantTypes::Int8)
{
V = Var->GetValue<int8>();
}
else if (Var->GetType() == EVariantTypes::UInt16)
{
V = Var->GetValue<uint16>();
}
else if (Var->GetType() == EVariantTypes::UInt8)
{
V = Var->GetValue<uint8>();
}
else if (Var->GetType() == EVariantTypes::Float)
{
V = (uint64) Var->GetValue<float>();
}
else if (Var->GetType() == EVariantTypes::Double)
{
V = (uint64) Var->GetValue<double>();
}
else if (Var->GetType() == EVariantTypes::Bool)
{
V = Var->GetValue<bool>() ? 1 : 0;
}
}
return V;
}
double GetVariantValueSafeDouble(const TMap<FString, FVariant>& InFromMap, const FString& InName, double InDefaultValue)
{
double V = InDefaultValue;
const FVariant* Var = InFromMap.Find(InName);
if (Var)
{
if (Var->GetType() == EVariantTypes::Int32)
{
V = (double) Var->GetValue<int32>();
}
else if (Var->GetType() == EVariantTypes::Int64)
{
V = (double) Var->GetValue<int64>();
}
else if (Var->GetType() == EVariantTypes::UInt32)
{
V = (double) Var->GetValue<uint32>();
}
else if (Var->GetType() == EVariantTypes::Int16)
{
V = (double) Var->GetValue<int16>();
}
else if (Var->GetType() == EVariantTypes::Int8)
{
V = (double) Var->GetValue<int8>();
}
else if (Var->GetType() == EVariantTypes::UInt16)
{
V = (double) Var->GetValue<uint16>();
}
else if (Var->GetType() == EVariantTypes::UInt8)
{
V = (double) Var->GetValue<uint8>();
}
else if (Var->GetType() == EVariantTypes::Float)
{
V = (double) Var->GetValue<float>();
}
else if (Var->GetType() == EVariantTypes::Double)
{
V = (double) Var->GetValue<double>();
}
else if (Var->GetType() == EVariantTypes::Bool)
{
V = Var->GetValue<bool>() ? 1.0 : 0.0;
}
}
return V;
}
TArray<uint8> GetVariantValueUInt8Array(const TMap<FString, FVariant>& InFromMap, const FString& InName)
{
const FVariant* Var = InFromMap.Find(InName);
if (Var)
{
if (Var->GetType() == EVariantTypes::ByteArray)
{
return Var->GetValue<TArray<uint8>>();
}
}
return TArray<uint8>();
}
FString GetVariantValueFString(const TMap<FString, FVariant>& InFromMap, const FString& InName)
{
const FVariant* Var = InFromMap.Find(InName);
if (Var)
{
if (Var->GetType() == EVariantTypes::String)
{
return Var->GetValue<FString>();
}
}
return FString();
}
}