/* Copyright (c) MediaArea.net SARL. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license that can
* be found in the License.html file in the root of the source tree.
*/
//---------------------------------------------------------------------------
// Pre-compilation
#include "MediaInfo/PreComp.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
#include "MediaInfo/Setup.h"
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
#if defined(MEDIAINFO_AV1_YES)
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
#include "MediaInfo/Video/File_Av1.h"
#include "MediaInfo/MediaInfo_Config_MediaInfo.h"
//---------------------------------------------------------------------------
namespace MediaInfoLib
{
//***************************************************************************
// Constants
//***************************************************************************
//---------------------------------------------------------------------------
extern const char* Mpegv_colour_primaries(int8u colour_primaries);
extern const char* Mpegv_transfer_characteristics(int8u transfer_characteristics);
extern const char* Mpegv_matrix_coefficients(int8u matrix_coefficients);
extern const char* Mpegv_matrix_coefficients_ColorSpace(int8u matrix_coefficients);
extern const char* Avc_video_full_range[];
//---------------------------------------------------------------------------
const char* Av1_obu_type(int8u obu_type)
{
switch (obu_type)
{
case 0x1 : return "sequence_header";
case 0x2 : return "temporal_delimiter";
case 0x3 : return "frame_header";
case 0x4 : return "tile_group";
case 0x5 : return "metadata";
case 0x6 : return "frame";
case 0x7 : return "redundant_frame_header";
case 0x8 : return "tile_list";
case 0xF : return "padding";
default : return "";
}
}
//---------------------------------------------------------------------------
const char* Av1_seq_profile(int8u seq_profile)
{
switch (seq_profile)
{
case 0x0 : return "Main";
case 0x1 : return "High";
case 0x2 : return "Professional";
default : return "";
}
}
//---------------------------------------------------------------------------
const char* Av1_frame_type[4] =
{
"Key",
"Inter",
"Intra Only",
"Switch",
};
//***************************************************************************
// Constructor/Destructor
//***************************************************************************
//---------------------------------------------------------------------------
File_Av1::File_Av1()
{
//Config
#if MEDIAINFO_EVENTS
StreamIDs_Width[0]=0;
#endif //MEDIAINFO_EVENTS
#if MEDIAINFO_TRACE
Trace_Layers_Update(8); //Stream
#endif //MEDIAINFO_TRACE
StreamSource=IsStream;
//In
Frame_Count_Valid=0;
FrameIsAlwaysComplete=false;
//Temp
maximum_content_light_level=0;
maximum_frame_average_light_level=0;
sequence_header_Parsed=false;
SeenFrameHeader=false;
}
//---------------------------------------------------------------------------
File_Av1::~File_Av1()
{
}
//***************************************************************************
// Streams management
//***************************************************************************
//---------------------------------------------------------------------------
void File_Av1::Streams_Accept()
{
Fill(Stream_General, 0, General_Format, "AV1");
Stream_Prepare(Stream_Video);
Fill(Stream_Video, 0, Video_Format, "AV1");
if (!Frame_Count_Valid)
Frame_Count_Valid=Config->ParseSpeed>=0.3?8:2;
}
//---------------------------------------------------------------------------
void File_Av1::Streams_Fill()
{
}
//---------------------------------------------------------------------------
void File_Av1::Streams_Finish()
{
Fill(Stream_Video, 0, Video_Format_Settings_GOP, GOP_Detect(GOP));
if (!MasteringDisplay_ColorPrimaries.empty())
{
Fill(Stream_Video, 0, "MasteringDisplay_ColorPrimaries", MasteringDisplay_ColorPrimaries);
Fill(Stream_Video, 0, "MasteringDisplay_Luminance", MasteringDisplay_Luminance);
}
if (maximum_content_light_level)
Fill(Stream_Video, 0, "MaxCLL", Ztring::ToZtring(maximum_content_light_level) + __T(" cd/m2"));
if (maximum_frame_average_light_level)
Fill(Stream_Video, 0, "MaxFALL", Ztring::ToZtring(maximum_frame_average_light_level) + __T(" cd/m2"));
}
//***************************************************************************
// Buffer - Global
//***************************************************************************
//---------------------------------------------------------------------------
void File_Av1::Read_Buffer_OutOfBand()
{
//Parsing
bool initial_presentation_delay_present;
BS_Begin();
Mark_1 ();
Skip_S1(7, "version");
Skip_S1(3, "seq_profile");
Skip_S1(5, "seq_level_idx_0");
Skip_SB( "seq_tier_0");
Skip_SB( "high_bitdepth");
Skip_SB( "twelve_bit");
Skip_SB( "monochrome");
Skip_SB( "chroma_subsampling_x");
Skip_SB( "chroma_subsampling_y");
Skip_S1(2, "chroma_sample_position");
Skip_S1(3, "reserved");
Get_SB ( initial_presentation_delay_present, "initial_presentation_delay_present");
Skip_S1(4, initial_presentation_delay_present?"initial_presentation_delay_minus_one":"reserved");
BS_End();
Open_Buffer_Continue(Buffer, Buffer_Size);
}
//***************************************************************************
// Buffer - Per element
//***************************************************************************
//---------------------------------------------------------------------------
void File_Av1::Header_Parse()
{
//Parsing
int8u obu_type;
bool obu_extension_flag;
BS_Begin();
Mark_0 ();
Get_S1 ( 4, obu_type, "obu_type");
Get_SB ( obu_extension_flag, "obu_extension_flag");
Skip_SB( "obu_has_size_field");
Skip_SB( "obu_reserved_1bit");
if (obu_extension_flag)
{
Skip_S1(3, "temporal_id");
Skip_S1(2, "spatial_id");
Skip_S1(3, "extension_header_reserved_3bits");
}
BS_End();
int64u obu_size = 0;
for (int8u i=0; i<8; i++)
{
int8u uleb128_byte;
Get_B1(uleb128_byte, "uleb128_byte");
obu_size|=((uleb128_byte & 0x7f) << (i * 7));
if (!(uleb128_byte&0x80))
break;
}
FILLING_BEGIN();
Header_Fill_Size(Element_Offset+obu_size);
FILLING_END();
if (FrameIsAlwaysComplete && (Element_IsWaitingForMoreData() || Element_Offset+obu_size>Element_Size))
{
// Trashing the remaining bytes, as the the frame is always complete so remaining bytes should not be prepending the next frame
Buffer_Offset=Buffer_Size;
Element_Offset=0;
return;
}
FILLING_BEGIN();
Header_Fill_Code(obu_type, Av1_obu_type(obu_type));
FILLING_END();
}
//---------------------------------------------------------------------------
void File_Av1::Data_Parse()
{
//Probing mode in case of raw stream //TODO: better reject of bad files
if (!IsSub && !Status[IsAccepted] && (!Element_Code || Element_Code>5))
{
Reject();
return;
}
//Parsing
switch (Element_Code)
{
case 0x1 : sequence_header(); break;
case 0x2 : temporal_delimiter(); break;
case 0x3 : frame_header(); break;
case 0x4 : tile_group(); break;
case 0x5 : metadata(); break;
case 0xF : padding(); break;
default : Skip_XX(Element_Size-Element_Offset, "Data");
}
}
//***************************************************************************
// Elements
//***************************************************************************
//---------------------------------------------------------------------------
void File_Av1::sequence_header()
{
//Parsing
int32u max_frame_width_minus_1, max_frame_height_minus_1;
int8u seq_profile, seq_level_idx[33], operating_points_cnt_minus_1, buffer_delay_length_minus_1, frame_width_bits_minus_1, frame_height_bits_minus_1, seq_force_screen_content_tools, BitDepth, color_primaries, transfer_characteristics, matrix_coefficients;
bool reduced_still_picture_header, seq_tier[33], timing_info_present_flag, decoder_model_info_present_flag, seq_choose_screen_content_tools, mono_chrome, color_range, color_description_present_flag, subsampling_x, subsampling_y;
BS_Begin();
Get_S1 ( 3, seq_profile, "seq_profile"); Param_Info1(Av1_seq_profile(seq_profile));
Skip_SB( "still_picture");
Get_SB ( reduced_still_picture_header, "reduced_still_picture_header");
if (reduced_still_picture_header)
{
Get_S1 ( 5, seq_level_idx[0], "seq_level_idx[0]");
decoder_model_info_present_flag=false;
seq_tier[0]=false;
}
else
{
TEST_SB_GET(timing_info_present_flag, "timing_info_present_flag");
bool equal_picture_interval;
Skip_S4(32, "num_units_in_tick");
Skip_S4(32, "time_scale");
Get_SB (equal_picture_interval, "equal_picture_interval");
if (equal_picture_interval)
Skip_UE( "num_ticks_per_picture_minus1");
TEST_SB_GET (decoder_model_info_present_flag, "decoder_model_info_present_flag");
Get_S1 ( 5, buffer_delay_length_minus_1, "buffer_delay_length_minus_1");
Skip_S4(32, "num_units_in_decoding_tick");
Skip_S1( 5, "buffer_removal_time_length_minus_1");
Skip_S1( 5, "frame_presentation_time_length_minus_1");
TEST_SB_END();
TEST_SB_END();
Skip_SB( "initial_display_delay_present_flag");
Get_S1 ( 5, operating_points_cnt_minus_1, "operating_points_cnt_minus_1");
for (int8u i=0; i<=operating_points_cnt_minus_1; i++)
{
Element_Begin1("operating_point");
Skip_S2(12, "operating_point_idc[i]");
Get_S1(5, seq_level_idx[i], "seq_level_idx[i]");
if (seq_level_idx[i]>7)
Get_SB(seq_tier[i], "seq_tier[i]");
if (timing_info_present_flag && decoder_model_info_present_flag)
{
TEST_SB_SKIP( "decoder_model_present_for_this_op[i]");
Skip_S5(buffer_delay_length_minus_1+1, "decoder_buffer_delay[op]");
Skip_S5(buffer_delay_length_minus_1+1, "encoder_buffer_delay[op]");
Skip_SB( "low_delay_mode_flag[op]");
TEST_SB_END();
}
Element_End0();
}
}
Get_S1 ( 4, frame_width_bits_minus_1, "frame_width_bits_minus_1");
Get_S1 ( 4, frame_height_bits_minus_1, "frame_height_bits_minus_1");
Get_S4 (frame_width_bits_minus_1+1, max_frame_width_minus_1, "max_frame_width_minus_1");
Get_S4 (frame_height_bits_minus_1+1, max_frame_height_minus_1, "max_frame_height_minus_1");
if (!reduced_still_picture_header)
{
TEST_SB_SKIP( "frame_id_numbers_present_flag");
Skip_S1(4, "delta_frame_id_length_minus2");
Skip_S1(3, "frame_id_length_minus1");
TEST_SB_END();
}
Skip_SB( "use_128x128_superblock");
Skip_SB( "enable_dual_filter");
Skip_SB( "enable_intra_edge_filter");
if (!reduced_still_picture_header)
{
bool enable_order_hint;
Skip_SB( "enable_interintra_compound");
Skip_SB( "enable_masked_compound");
Skip_SB( "enable_warped_motion");
Skip_SB( "enable_dual_filter");
TEST_SB_GET (enable_order_hint, "enable_order_hint");
Skip_SB( "enable_jnt_comp");
Skip_SB( "enable_ref_frame_mvs");
TEST_SB_END();
Get_SB (seq_choose_screen_content_tools, "seq_choose_screen_content_tools");
if (seq_choose_screen_content_tools)
seq_force_screen_content_tools=2;
else
Get_S1 (1, seq_force_screen_content_tools, "seq_force_screen_content_tools");
if (seq_force_screen_content_tools)
{
bool seq_choose_integer_mv;
Get_SB(seq_choose_integer_mv, "seq_choose_integer_mv");
if (!seq_choose_integer_mv)
Skip_S1(1, "seq_force_integer_mv");
}
if (enable_order_hint)
Skip_S1(3, "order_hint_bits_minus_1");
}
Skip_SB( "enable_superres");
Skip_SB( "enable_cdef");
Skip_SB( "enable_restoration");
Element_Begin1("color_config");
bool high_bitdepth;
Get_SB (high_bitdepth, "high_bitdepth");
BitDepth=high_bitdepth?10:8;
if (seq_profile>=2 && high_bitdepth)
{
bool twelve_bit;
Get_SB (twelve_bit, "twelve_bit");
if (twelve_bit)
BitDepth+=2;
}
if (seq_profile==1)
mono_chrome=false;
else
Get_SB (mono_chrome, "mono_chrome");
TESTELSE_SB_GET (color_description_present_flag, "color_description_present_flag");
Get_S1 (8, color_primaries, "color_primaries"); Param_Info1(Mpegv_colour_primaries(color_primaries));
Get_S1 (8, transfer_characteristics, "transfer_characteristics"); Param_Info1(Mpegv_transfer_characteristics(transfer_characteristics));
Get_S1 (8, matrix_coefficients, "matrix_coefficients"); Param_Info1(Mpegv_matrix_coefficients(matrix_coefficients));
TESTELSE_SB_ELSE( "color_description_present_flag");
color_primaries=2;
transfer_characteristics=2;
matrix_coefficients=2;
TESTELSE_SB_END();
if (mono_chrome)
{
color_range=true;
subsampling_x=true;
subsampling_y=true;
}
else if (color_primaries==1 && transfer_characteristics==13 && matrix_coefficients==0)
{
subsampling_x=false;
subsampling_y=false;
}
else
{
Get_SB(color_range, "color_range"); Param_Info1(Avc_video_full_range[color_range]);
if (seq_profile==0)
{
subsampling_x=true;
subsampling_y=true;
}
else if (seq_profile==1)
{
subsampling_x=false;
subsampling_y=false;
}
else
{
if ( BitDepth == 12 )
{
Get_SB(subsampling_x, "subsampling_x");
if (subsampling_x)
Get_SB(subsampling_y, "subsampling_y");
else
subsampling_y=false;
}
else
{
subsampling_x=true;
subsampling_y=false;
}
}
if (subsampling_x && subsampling_y)
Skip_S1( 2, "chroma_sample_position");
}
Skip_SB( "separate_uv_delta_q");
Element_End0();
Skip_SB( "film_grain_params_present");
Mark_1();
if (Data_BS_Remain()<8)
while (Data_BS_Remain())
Mark_0();
BS_End();
FILLING_BEGIN_PRECISE();
if (!sequence_header_Parsed)
{
if (IsSub)
Accept();
Fill(Stream_Video, 0, Video_Format_Profile, Ztring().From_UTF8(Av1_seq_profile(seq_profile))+(seq_level_idx[0]==31?Ztring():(__T("@L")+Ztring().From_Number(2+(seq_level_idx[0]>>2))+__T(".")+Ztring().From_Number(seq_level_idx[0]&3))));
Fill(Stream_Video, 0, Video_Width, max_frame_width_minus_1+1);
Fill(Stream_Video, 0, Video_Height, max_frame_height_minus_1+1);
Fill(Stream_Video, 0, Video_BitDepth, BitDepth);
Fill(Stream_Video, 0, Video_ColorSpace, mono_chrome?"Y":((color_primaries==1 && transfer_characteristics==13 && matrix_coefficients==0)?"RGB":"YUV"));
if (Retrieve(Stream_Video, 0, Video_ColorSpace)==__T("YUV"))
Fill(Stream_Video, 0, Video_ChromaSubsampling, subsampling_x?(subsampling_y?"4:2:0":"4:2:2"):"4:4:4"); // "!subsampling_x && subsampling_y" (4:4:0) not possible
if (color_description_present_flag)
{
Fill(Stream_Video, 0, Video_colour_description_present, "Yes");
Fill(Stream_Video, 0, Video_colour_primaries, Mpegv_colour_primaries(color_primaries));
Fill(Stream_Video, 0, Video_transfer_characteristics, Mpegv_transfer_characteristics(transfer_characteristics));
Fill(Stream_Video, 0, Video_matrix_coefficients, Mpegv_matrix_coefficients(matrix_coefficients));
}
if (mono_chrome || !(color_primaries==1 && transfer_characteristics==13 && matrix_coefficients==0))
Fill(Stream_Video, 0, Video_colour_range, Avc_video_full_range[color_range]);
sequence_header_Parsed=true;
}
FILLING_END();
}
//---------------------------------------------------------------------------
void File_Av1::temporal_delimiter()
{
SeenFrameHeader=false;
FILLING_BEGIN_PRECISE();
FILLING_END();
}
//---------------------------------------------------------------------------
void File_Av1::frame_header()
{
if (SeenFrameHeader)
{
Skip_XX(Element_Size, "Duplicated data");
return;
}
SeenFrameHeader=1;
if (!sequence_header_Parsed)
{
Skip_XX(Element_Size, "Data");
return;
}
//Parsing
BS_Begin();
Element_Begin1("uncompressed_header");
int8u frame_type;
TEST_SB_SKIP( "show_existing_frame");
BS_End();
Skip_XX(Element_Size-Element_Offset, "Data");
return;
TEST_SB_END();
Get_S1 (2, frame_type, "frame_type"); Param_Info1(Av1_frame_type[frame_type]);
FILLING_BEGIN();
GOP.push_back((frame_type&1)?'P':'I');
FILLING_ELSE();
GOP.push_back(' ');
FILLING_END();
if (GOP.size()>=512)
GOP.resize(384);
Element_End0();
BS_End();
FILLING_BEGIN();
if (!Status[IsAccepted])
Accept();
Frame_Count++;
if (Frame_Count>=Frame_Count_Valid)
Finish();
FILLING_END();
}
//---------------------------------------------------------------------------
void File_Av1::tile_group()
{
Skip_XX(Element_Size, "Data");
}
//---------------------------------------------------------------------------
void File_Av1::metadata()
{
//Parsing
int16u metadata_type;
Get_B2 (metadata_type, "metadata_type");
switch (metadata_type)
{
case 1 : metadata_hdr_cll(); break;
case 2 : metadata_hdr_mdcv(); break;
default : Skip_XX(Element_Size-Element_Offset, "Data");
}
}
//---------------------------------------------------------------------------
void File_Av1::metadata_hdr_cll()
{
//Parsing
Get_B2(maximum_content_light_level, "maximum_content_light_level");
Get_B2(maximum_frame_average_light_level, "maximum_frame_average_light_level");
}
//---------------------------------------------------------------------------
void File_Av1::metadata_hdr_mdcv()
{
//Parsing
Get_MasteringDisplayColorVolume(MasteringDisplay_ColorPrimaries, MasteringDisplay_Luminance);
}
//---------------------------------------------------------------------------
void File_Av1::padding()
{
Skip_XX(Element_Size, "Padding");
}
//***************************************************************************
// Helpers
//***************************************************************************
//---------------------------------------------------------------------------
//TODO generic code
std::string File_Av1::GOP_Detect (std::string PictureTypes)
{
//Finding a string without blanks
size_t PictureTypes_Limit=PictureTypes.find(' ');
if (PictureTypes_Limit!=string::npos)
{
if (PictureTypes_Limit>PictureTypes.size()/2)
PictureTypes.resize(PictureTypes_Limit);
else
{
//Trim
size_t TrimPos;
TrimPos=PictureTypes.find_first_not_of(' ');
if (TrimPos!=string::npos)
PictureTypes.erase(0, TrimPos);
TrimPos=PictureTypes.find_last_not_of(' ');
if (TrimPos!=string::npos)
PictureTypes.erase(TrimPos+1);
//Finding the longest string
ZtringList List; List.Separator_Set(0, __T(" "));
List.Write(Ztring().From_UTF8(PictureTypes));
size_t MaxLength=0;
size_t MaxLength_Pos=0;
for (size_t Pos=0; Pos<List.size(); Pos++)
if (List[Pos].size()>MaxLength)
{
MaxLength=List[Pos].size();
MaxLength_Pos=Pos;
}
PictureTypes=List[MaxLength_Pos].To_Local();
}
}
//Creating all GOP values
std::vector<Ztring> GOPs;
size_t GOP_Frame_Count=0;
size_t GOP_BFrames_Max=0;
size_t I_Pos1=PictureTypes.find('I');
while (I_Pos1!=std::string::npos)
{
size_t I_Pos2=PictureTypes.find('I', I_Pos1+1);
if (I_Pos2!=std::string::npos)
{
std::vector<size_t> P_Positions;
size_t P_Position=I_Pos1;
do
{
P_Position=PictureTypes.find('P', P_Position+1);
if (P_Position<I_Pos2)
P_Positions.push_back(P_Position);
}
while (P_Position<I_Pos2);
if (P_Positions.size()>1 && P_Positions[0]>I_Pos1+1 && P_Positions[P_Positions.size()-1]==I_Pos2-1)
P_Positions.resize(P_Positions.size()-1); //Removing last P-Frame for next test, this is often a terminating P-Frame replacing a B-Frame
Ztring GOP;
bool IsOK=true;
if (!P_Positions.empty())
{
size_t Delta=P_Positions[0]-I_Pos1;
for (size_t Pos=1; Pos<P_Positions.size(); Pos++)
if (P_Positions[Pos]-P_Positions[Pos-1]!=Delta)
{
IsOK=false;
break;
}
if (IsOK)
{
GOP+=__T("M=")+Ztring::ToZtring(P_Positions[0]-I_Pos1)+__T(", ");
if (P_Positions[0]-I_Pos1>GOP_BFrames_Max)
GOP_BFrames_Max=P_Positions[0]-I_Pos1;
}
}
if (IsOK)
{
GOP+=__T("N=")+Ztring::ToZtring(I_Pos2-I_Pos1);
GOPs.push_back(GOP);
}
else
GOPs.push_back(Ztring()); //There is a problem, blank
GOP_Frame_Count+=I_Pos2-I_Pos1;
}
I_Pos1=I_Pos2;
}
//Some clean up
if (GOP_Frame_Count+GOP_BFrames_Max>Frame_Count && !GOPs.empty())
GOPs.resize(GOPs.size()-1); //Removing the last one, there may have uncomplete B-frame filling
if (GOPs.size()>4)
GOPs.erase(GOPs.begin()); //Removing the first one, it is sometime different and we have enough to deal with
//Filling
if (GOPs.size()>=4)
{
bool IsOK=true;
for (size_t Pos=1; Pos<GOPs.size(); Pos++)
if (GOPs[Pos]!=GOPs[0])
{
IsOK=false;
break;
}
if (IsOK)
return GOPs[0].To_Local();
}
return string();
}
//---------------------------------------------------------------------------
} //NameSpace
#endif //MEDIAINFO_AV1_YES
↑ V610 Undefined behavior. Check the shift operator '<<'. The right operand ('(i * 7)' = [0..49]) is greater than or equal to the length in bits of the promoted left operand.
↑ V629 Consider inspecting the '(uleb128_byte & 0x7f) << (i * 7)' expression. Bit shifting of the 32-bit value with a subsequent expansion to the 64-bit type.
↑ V1020 The function exited without calling the 'Element_End' function. Check lines: 488, 483.
↑ V688 The 'GOP' local variable possesses the same name as one of the class members, which can result in a confusion.
↑ V779 Unreachable code detected. It is possible that an error is present.
↑ V525 The code contains the collection of similar blocks. Check items '3', '2', '3' in lines 196, 197, 198.
↑ V525 The code contains the collection of similar blocks. Check items 'Skip_S4_', 'Skip_S1_', 'Skip_S1_' in lines 284, 285, 286.
↑ V823 Decreased performance. Object may be created in-place in the 'GOPs' container. Consider replacing methods: 'push_back' -> 'emplace_back'.
↑ V826 Consider replacing the 'GOPs' std::vector with std::deque. Overall efficiency of operations will increase.