/* 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.
*/
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
// http://www.ffmpeg.org/~michael/ffv1.html
//
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//---------------------------------------------------------------------------
// Pre-compilation
#include "MediaInfo/PreComp.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
#include "MediaInfo/Setup.h"
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
#if defined(MEDIAINFO_FFV1_YES)
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
#include "MediaInfo/Video/File_Ffv1.h"
#include "MediaInfo/MediaInfo_Config_MediaInfo.h"
#include "ZenLib/BitStream.h"
//---------------------------------------------------------------------------
#include <algorithm>
#include <math.h>
using namespace std;
//---------------------------------------------------------------------------
namespace MediaInfoLib
{
using namespace FFV1;
//***************************************************************************
// Const
//***************************************************************************
extern const int32u Psi_CRC_32_Table[256];
const int32u Slice::Context::N0 = 128;
const int32s Slice::Context::Cmax = 127;
const int32s Slice::Context::Cmin = -128;
//***************************************************************************
// Helpers
//***************************************************************************
//---------------------------------------------------------------------------
static int32u FFv1_CRC_Compute(const int8u* Buffer, size_t Size)
{
int32u CRC_32 = 0;
const int8u* CRC_32_Buffer=Buffer;
const int8u* CRC_32_Buffer_End=CRC_32_Buffer+Size;
while(CRC_32_Buffer<CRC_32_Buffer_End)
{
CRC_32=(CRC_32<<8) ^ Psi_CRC_32_Table[(CRC_32>>24)^(*CRC_32_Buffer)];
CRC_32_Buffer++;
}
return CRC_32;
}
//---------------------------------------------------------------------------
#if MEDIAINFO_FIXITY
static size_t Ffv1_TryToFixCRC(const int8u* Buffer, size_t Buffer_Size)
{
//looking for a bit flip
int8u* Buffer2=new int8u[Buffer_Size];
memcpy(Buffer2, Buffer, Buffer_Size);
vector<size_t> BitPositions;
size_t BitPosition_Max=Buffer_Size*8;
for (size_t BitPosition=0; BitPosition<BitPosition_Max; BitPosition++)
{
size_t BytePosition=BitPosition>>3;
size_t BitInBytePosition=BitPosition&0x7;
Buffer2[BytePosition]^=1<<BitInBytePosition;
int32u crc_left_New=FFv1_CRC_Compute(Buffer2, Buffer_Size);
if (!crc_left_New)
{
BitPositions.push_back(BitPosition);
}
Buffer2[BytePosition]^=1<<BitInBytePosition;
}
delete[] Buffer2; //Buffer2=NULL
return BitPositions.size()==1?BitPositions[0]:(size_t)-1;
}
#endif //MEDIAINFO_FIXITY
//***************************************************************************
// RangeCoder
//***************************************************************************
class RangeCoder
{
public:
RangeCoder(const int8u* Buffer, size_t Buffer_Size, const state_transitions default_state_transition);
void AssignStateTransitions(const state_transitions new_state_transition);
void ResizeBuffer(size_t Buffer_Size); //Adapt the buffer limit
size_t BytesUsed();
bool Underrun();
void ForceUnderrun();
bool get_rac(int8u* States);
int32u get_symbol_u(int8u* States);
int32s get_symbol_s(int8u* States);
int32u Current;
int32u Mask;
state_transitions zero_state;
state_transitions one_state;
private:
const int8u* Buffer_Beg;
const int8u* Buffer_Cur;
const int8u* Buffer_End;
};
//---------------------------------------------------------------------------
RangeCoder::RangeCoder (const int8u* Buffer, size_t Buffer_Size, const state_transitions default_state_transition)
{
//Assign buffer
Buffer_Beg=Buffer;
Buffer_Cur=Buffer;
Buffer_End=Buffer+Buffer_Size;
//Init
if (Buffer_Size)
Current=*Buffer_Cur;
Mask=0xFF;
Buffer_Cur++;
AssignStateTransitions(default_state_transition);
}
void RangeCoder::AssignStateTransitions (const state_transitions new_state_transition)
{
//Assign StateTransitions
std::memcpy (one_state, new_state_transition, state_transitions_size);
zero_state[0]=0;
for (size_t i=1; i<state_transitions_size; i++)
zero_state[i]=-one_state[state_transitions_size-i];
}
//---------------------------------------------------------------------------
void RangeCoder::ResizeBuffer(size_t Buffer_Size)
{
Buffer_End=Buffer_Beg+Buffer_Size;
}
//---------------------------------------------------------------------------
size_t RangeCoder::BytesUsed()
{
if (Buffer_Cur>Buffer_End)
return Buffer_End-Buffer_Beg;
return Buffer_Cur-Buffer_Beg-(Mask<0x100?0:1);
}
//---------------------------------------------------------------------------
bool RangeCoder::Underrun()
{
return (Buffer_Cur-(Mask<0x100?0:1)>Buffer_End)?true:false;
}
//---------------------------------------------------------------------------
void RangeCoder::ForceUnderrun()
{
Mask=0;
Buffer_Cur=Buffer_End+1;
}
//---------------------------------------------------------------------------
bool RangeCoder::get_rac(int8u* States)
{
// Next byte
if (Mask<0x100)
{
Current<<=8;
// If less, consume the next byte
// If equal, last byte assumed to be 0x00
// If more, underrun, we return 0
if (Buffer_Cur<Buffer_End)
{
Current|=*Buffer_Cur;
}
else if (Buffer_Cur>Buffer_End)
{
return false;
}
Buffer_Cur++;
Mask<<=8;
}
//Range Coder boolean value computing
int32u Mask2=(Mask*(*States))>>8;
Mask-=Mask2;
if (Current<Mask)
{
*States=zero_state[*States];
return false;
}
Current-=Mask;
Mask=Mask2;
*States=one_state[*States];
return true;
}
//---------------------------------------------------------------------------
int32u RangeCoder::get_symbol_u(int8u* States)
{
if (get_rac(States))
return 0;
int e = 0;
while (get_rac(States + 1 + min(e, 9))) // 1..10
{
e++;
if (e > 31)
{
ForceUnderrun(); // stream is buggy or unsupported, we disable it completely and we indicate that it is NOK
return 0;
}
}
int32u a = 1;
int i = e - 1;
while (i >= 0)
{
a <<= 1;
if (get_rac(States + 22 + min(i, 9))) // 22..31
++a;
i--;
}
return a;
}
//---------------------------------------------------------------------------
int32s RangeCoder::get_symbol_s(int8u* States)
{
if (get_rac(States))
return 0;
int e = 0;
while (get_rac(States + 1 + min(e, 9))) // 1..10
{
e++;
if (e > 31)
{
ForceUnderrun(); // stream is buggy or unsupported, we disable it completely and we indicate that it is NOK
return 0;
}
}
int32s a = 1;
int i = e - 1;
while (i >= 0)
{
a <<= 1;
if (get_rac(States + 22 + min(i, 9))) // 22..31
++a;
i--;
}
if (get_rac(States + 11 + min(e, 10))) // 11..21
return -a;
else
return a;
}
//***************************************************************************
// Info
//***************************************************************************
static const char* Ffv1_coder_type(int8u coder_type)
{
switch (coder_type)
{
case 0 :
return "Golomb Rice";
case 1 :
case 2 :
return "Range Coder";
default:
return "";
}
}
static const string Ffv1_colorspace_type(int8u colorspace_type, bool chroma_planes, bool alpha_plane)
{
string ToReturn;
switch (colorspace_type)
{
case 0 :
ToReturn=chroma_planes?"YUV":"Y";
break;
case 1 : ToReturn="RGB"; break;
default: return string();
}
if (alpha_plane)
ToReturn+='A';
return ToReturn;
}
static const char* Ffv1_picture_structure_ScanType (int8u picture_structure)
{
switch (picture_structure)
{
case 1 :
case 2 : return "Interlaced";
case 3 : return "Progressive";
default: return "";
}
}
static const char* Ffv1_picture_structure_ScanOrder (int8u picture_structure)
{
switch (picture_structure)
{
case 1 : return "TFF";
case 2 : return "BFF";
default: return "";
}
}
static const state_transitions Ffv1_default_state_transition =
{
0, 0, 0, 0, 0, 0, 0, 0, 20, 21, 22, 23, 24, 25, 26, 27,
28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 37, 38, 39, 40, 41, 42,
43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 56, 57,
58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73,
74, 75, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88,
89, 90, 91, 92, 93, 94, 94, 95, 96, 97, 98, 99,100,101,102,103,
104,105,106,107,108,109,110,111,112,113,114,114,115,116,117,118,
119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,133,
134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,
150,151,152,152,153,154,155,156,157,158,159,160,161,162,163,164,
165,166,167,168,169,170,171,171,172,173,174,175,176,177,178,179,
180,181,182,183,184,185,186,187,188,189,190,190,191,192,194,194,
195,196,197,198,199,200,201,202,202,204,205,206,207,208,209,209,
210,211,212,213,215,215,216,217,218,219,220,220,222,223,224,225,
226,227,227,229,229,230,231,232,234,234,235,236,237,238,239,240,
241,242,243,244,245,246,247,248,248, 0, 0, 0, 0, 0, 0, 0,
};
// Coming from FFv1 spec.
static const int8u log2_run[41]={
0 , 0, 0, 0, 1, 1, 1, 1,
2 , 2, 2, 2, 3, 3, 3, 3,
4 , 4, 5, 5, 6, 6, 7, 7,
8 , 9,10,11,12,13,14,15,
16,17,18,19,20,21,22,23,
24,
};
static const int32u run[41] =
{
1 << 0,
1 << 0,
1 << 0,
1 << 0,
1 << 1,
1 << 1,
1 << 1,
1 << 1,
1 << 2,
1 << 2,
1 << 2,
1 << 2,
1 << 3,
1 << 3,
1 << 3,
1 << 3,
1 << 4,
1 << 4,
1 << 5,
1 << 5,
1 << 6,
1 << 6,
1 << 7,
1 << 7,
1 << 8,
1 << 9,
1 << 10,
1 << 11,
1 << 12,
1 << 13,
1 << 14,
1 << 15,
1 << 16,
1 << 17,
1 << 18,
1 << 19,
1 << 20,
1 << 21,
1 << 22,
1 << 23,
1 << 24,
};
//***************************************************************************
// Slice
//***************************************************************************
//---------------------------------------------------------------------------
void Slice::contexts_clean()
{
for (size_t i = 0; i < MAX_PLANES; i++)
{
if (contexts[i])
delete[] contexts[i];
}
}
//---------------------------------------------------------------------------
void Slice::contexts_init(int32u plane_count, int32u quant_table_index[MAX_PLANES], int32u context_count[MAX_QUANT_TABLES])
{
contexts_clean();
for (size_t i = 0; i < MAX_PLANES; ++i)
{
if (i >= plane_count)
{
contexts[i] = NULL;
continue;
}
int32u idx = quant_table_index[i];
contexts[i] = new Context [context_count[idx]];
}
}
//***************************************************************************
// Constructor/Destructor
//***************************************************************************
//---------------------------------------------------------------------------
File_Ffv1::File_Ffv1()
:File__Analyze()
{
//Configuration
ParserName="FFV1";
#if MEDIAINFO_TRACE
Trace_Layers_Update(8); //Stream
#endif //MEDIAINFO_TRACE
StreamSource=IsStream;
//use Ffv1_default_state_transition by default
memcpy(state_transitions_table, Ffv1_default_state_transition,
sizeof(Ffv1_default_state_transition));
//Input
Width = (int32u)-1;
Height = (int32u)-1;
//Temp
for (size_t i=0; i < MAX_QUANT_TABLES; ++i)
{
plane_states[i] = NULL;
plane_states_maxsizes[i] = 0;
}
Parameters_IsValid=false;
ConfigurationRecord_IsPresent=false;
RC=NULL;
slices = NULL;
version = (int32u)-1;
picture_structure = (int32u)-1;
sample_aspect_ratio_num = 0;
sample_aspect_ratio_den = 0;
KeyFramePassed = false;
memset(context_count, 0, MAX_QUANT_TABLES*sizeof(int32u));
}
//---------------------------------------------------------------------------
File_Ffv1::~File_Ffv1()
{
//Temp
if (slices)
{
for (size_t y = 0; y < num_v_slices; ++y)
for (size_t x = 0; x < num_h_slices; ++x)
plane_states_clean(slices[x + y * num_h_slices].plane_states);
delete[] slices;
}
for (size_t i = 0; i < MAX_QUANT_TABLES; ++i)
{
if (!plane_states[i])
continue;
for (size_t j = 0; j < context_count[i]; ++j)
delete[] plane_states[i][j];
delete[] plane_states[i];
plane_states[i] = NULL;
}
delete RC; //RC=NULL
}
//***************************************************************************
// Streams management
//***************************************************************************
//---------------------------------------------------------------------------
void File_Ffv1::Streams_Accept()
{
Stream_Prepare(Stream_Video);
Fill(Stream_Video, 0, Video_Format, "FFV1");
if (version!=(int32u)-1)
{
Ztring Version=__T("Version ")+Ztring::ToZtring(version);
if (version>=3 && version<=4)
{
Version+=__T('.');
Version+=Ztring::ToZtring(micro_version);
}
Fill(Stream_Video, 0, Video_Format_Version, Version);
}
Fill(Stream_Video, 0, Video_BitRate_Mode, "VBR");
}
//***************************************************************************
// RangeCoder
//***************************************************************************
#if MEDIAINFO_TRACE
//---------------------------------------------------------------------------
void File_Ffv1::Get_RB (states &States, bool &Info, const char* Name)
{
Info=RC->get_rac(States);
if (Trace_Activated)
{
Element_Offset+=RC->BytesUsed();
Param(Name, Info);
Element_Offset-=RC->BytesUsed();
}
}
//---------------------------------------------------------------------------
void File_Ffv1::Get_RU (states &States, int32u &Info, const char* Name)
{
Info=RC->get_symbol_u(States);
if (Trace_Activated)
{
Element_Offset+=RC->BytesUsed();
Param(Name, Info);
Element_Offset-=RC->BytesUsed();
}
}
//---------------------------------------------------------------------------
void File_Ffv1::Get_RS (states &States, int32s &Info, const char* Name)
{
Info=RC->get_symbol_s(States);
if (Trace_Activated)
{
Element_Offset+=RC->BytesUsed();
Param(Name, Info);
Element_Offset-=RC->BytesUsed();
}
}
//---------------------------------------------------------------------------
void File_Ffv1::Get_RS (int8u* &States, int32s &Info, const char* Name)
{
Info=RC->get_symbol_s(States);
if (Trace_Activated)
{
Element_Offset+=RC->BytesUsed();
Param(Name, Info);
Element_Offset-=RC->BytesUsed();
}
}
//---------------------------------------------------------------------------
void File_Ffv1::Skip_RC (states &States, const char* Name)
{
int8u Info=RC->get_rac(States);
if (Trace_Activated)
{
Element_Offset+=RC->BytesUsed();
Param(Name, Info);
Element_Offset-=RC->BytesUsed();
}
}
//---------------------------------------------------------------------------
void File_Ffv1::Skip_RU (states &States, const char* Name)
{
if (Trace_Activated)
{
Element_Offset+=RC->BytesUsed();
Param(Name, RC->get_symbol_u(States));
Element_Offset-=RC->BytesUsed();
}
else
RC->get_symbol_u(States);
}
//---------------------------------------------------------------------------
void File_Ffv1::Skip_RS (states &States, const char* Name)
{
if (Trace_Activated)
{
Element_Offset+=RC->BytesUsed();
Param(Name, RC->get_symbol_s(States));
Element_Offset-=RC->BytesUsed();
}
else
RC->get_symbol_s(States);
}
#else //MEDIAINFO_TRACE
//---------------------------------------------------------------------------
void File_Ffv1::Get_RB_ (states &States, bool &Info)
{
Info=RC->get_rac(States);
}
//---------------------------------------------------------------------------
void File_Ffv1::Get_RU_ (states &States, int32u &Info)
{
Info=RC->get_symbol_u(States);
}
//---------------------------------------------------------------------------
void File_Ffv1::Get_RS_ (states &States, int32s &Info)
{
Info=RC->get_symbol_s(States);
}
//---------------------------------------------------------------------------
void File_Ffv1::Get_RS_ (int8u* &States, int32s &Info)
{
Info=RC->get_symbol_s(States);
}
//---------------------------------------------------------------------------
void File_Ffv1::Skip_RC_ (states &States)
{
RC->get_rac(States);
}
//---------------------------------------------------------------------------
void File_Ffv1::Skip_RU_ (states &States)
{
RC->get_symbol_u(States);
}
//---------------------------------------------------------------------------
void File_Ffv1::Skip_RS_ (states &States)
{
RC->get_symbol_s(States);
}
#endif //MEDIAINFO_TRACE
//***************************************************************************
// Buffer - Global
//***************************************************************************
//---------------------------------------------------------------------------
void File_Ffv1::Read_Buffer_OutOfBand()
{
ConfigurationRecord_IsPresent=true;
//Coherency tests
if (Buffer_Size<4)
{
Skip_XX(Element_Size, "ConfigurationRecord");
Param_Error("FFV1-HEADER-END:1");
return;
}
int32u CRC_32=FFv1_CRC_Compute(Buffer+Buffer_Offset, (size_t)Element_Size);
Element_Begin1("ConfigurationRecord");
delete RC; RC=new RangeCoder(Buffer, Buffer_Size-4, Ffv1_default_state_transition);
Parameters();
delete RC; RC=NULL;
if (Element_Offset+4<Element_Size)
Skip_XX(Element_Size-Element_Offset-4, "Reserved");
Skip_B4( "configuration_record_crc_parity");
if (CRC_32)
Param_Error("FFV1-HEADER-configuration_record_crc_parity:1");
Element_End0();
}
//---------------------------------------------------------------------------
void File_Ffv1::Skip_Frame()
{
Skip_XX(Element_Size-Element_Offset, "Data");
Frame_Count++;
delete RC;
RC = NULL;
Fill();
if (Config->ParseSpeed<1.0)
Finish();
}
//---------------------------------------------------------------------------
void File_Ffv1::Read_Buffer_Continue()
{
if (ConfigurationRecord_IsPresent && !Parameters_IsValid)
{
Skip_Frame();
return;
}
if (!RC)
RC = new RangeCoder(Buffer, Buffer_Size, Ffv1_default_state_transition);
states States;
memset(States, 128, states_size);
Element_Begin1("Frame");
Get_RB (States, keyframe, "keyframe");
if (intra && !keyframe)
Param_Error("FFV1-FRAME-key_frame-ISNOTINTRA:1");
if (keyframe)
KeyFramePassed=true;
if (!ConfigurationRecord_IsPresent && keyframe)
{
#if MEDIAINFO_TRACE
bool Trace_Activated_Save=Trace_Activated;
if (Trace_Activated && Frame_Count)
Trace_Activated=false; // Trace is relatively huge, temporarary deactivating it. TODO: an option for it
#endif //MEDIAINFO_TRACE
Parameters();
#if MEDIAINFO_TRACE
Trace_Activated=Trace_Activated_Save; // Trace is too huge, reactivating it.
#endif //MEDIAINFO_TRACE
}
if (!Parameters_IsValid || !KeyFramePassed)
{
Skip_Frame();
return;
}
int32u tail = (version >= 3) ? 3 : 0;
tail += ec == 1 ? 5 : 0;
vector<int32u> Slices_BufferSizes;
if (version>=3)
{
int64u Slices_BufferPos=Element_Size;
while (Slices_BufferPos)
{
if (Slices_BufferPos<tail)
{
//There is a problem
Slices_BufferSizes.clear();
break;
}
int32u Size=BigEndian2int24u(Buffer+Buffer_Offset+(size_t)Slices_BufferPos-tail);
Size+=tail;
if (Size>Slices_BufferPos)
{
//There is a problem
Slices_BufferSizes.clear();
break;
}
Slices_BufferPos-=Size;
Slices_BufferSizes.insert(Slices_BufferSizes.begin(), Size);
}
}
size_t Pos=0;
BuggySlices=false;
while (Element_Offset<Element_Size || (!Pos && coder_type)) // With some v0 RC, content may be in the last byte of the RC which is also in the Parameter() part
{
Element_Begin1("Slice");
int64u Element_Offset_Begin=Element_Offset;
int64u Element_Size_Save=Element_Size;
if (Pos<Slices_BufferSizes.size())
Element_Size=Element_Offset+Slices_BufferSizes[Pos];
int32u crc_left=0;
if (ec == 1)
crc_left=FFv1_CRC_Compute(Buffer+Buffer_Offset+(size_t)Element_Offset, (size_t)(Element_Size-Element_Offset));
Element_Size-=tail;
if (Pos)
{
delete RC; RC = new RangeCoder(Buffer+Buffer_Offset+(size_t)Element_Offset, Element_Size-Element_Offset, state_transitions_table);
}
else
{
RC->ResizeBuffer(Element_Size);
RC->AssignStateTransitions(state_transitions_table);
}
//SliceHeader
bool ParseContent;
if (version>=3)
{
ParseContent=SliceHeader(States);
if (!ParseContent)
BuggySlices=true;
}
else
ParseContent=true;
//SliceContent
#if MEDIAINFO_TRACE
if (ParseContent && (!Frame_Count || Trace_Activated)) // Parse slice only if trace feature is activated
{
SliceContent(States);
}
else
#endif //MEDIAINFO_TRACE
Skip_XX(Element_Size-Element_Offset, "SliceContent");
if (version<=1 && Element_Offset+5==Element_Size)
{
crc_left=FFv1_CRC_Compute(Buffer+Buffer_Offset+(size_t)Element_Offset_Begin, (size_t)(Element_Size-Element_Offset_Begin));
Element_Size-=5;
ec = 1;
if (Frame_Count==0)
Fill(Stream_Video, 0, "ErrorDetectionType", "Per slice");
}
if (Element_Offset<Element_Size)
{
Skip_XX(Element_Size-Element_Offset, "Junk");
Param_Error("FFV1-SLICE-JUNK:1");
}
//SliceFooter
Element_Size=Element_Size_Save;
if (version>=3 || ec == 1)
Element_Begin1("SliceFooter");
if (version>=3)
{
int32u slice_size;
Get_B3 (slice_size, "slice_size");
if (Element_Offset_Begin+slice_size+3!=Element_Offset)
Param_Error("FFV1-SLICE-slice_size:1");
}
{
if (ec == 1)
{
int8u error_status;
Get_B1 (error_status, "error_status");
if (error_status)
Param_Error("FFV1-SLICE-error_status:1");
Skip_B4( "slice_crc_parity");
if (crc_left)
{
Param_Error("FFV1-SLICE-slice_crc_parity:1");
#if MEDIAINFO_FIXITY
if (Config->TryToFix_Get())
{
size_t BitPosition=Ffv1_TryToFixCRC(Buffer+Buffer_Offset+(size_t)Element_Offset_Begin, Element_Offset-Element_Offset_Begin);
if (BitPosition!=(size_t)-1)
{
size_t BytePosition=BitPosition>>3;
size_t BitInBytePosition=BitPosition&0x7;
int8u Modified=Buffer[Buffer_Offset+(size_t)Element_Offset_Begin+BytePosition];
Modified^=1<<BitInBytePosition;
FixFile(File_Offset+Buffer_Offset+(size_t)Element_Offset_Begin+BytePosition, &Modified, 1)?Param_Info1("Fixed"):Param_Info1("Not fixed");
}
}
#endif //MEDIAINFO_FIXITY
}
}
}
if (version>=3 || ec==1)
Element_End0();
Element_End0();
Pos++;
}
//Integrity test
if (!BuggySlices && version>=3 && slices)
{
vector<size_t> SlicesPlaces;
size_t SlicesPlaces_Size=num_h_slices*num_v_slices;
SlicesPlaces.resize(num_h_slices*num_v_slices);
Slice* Slice_Max=slices+SlicesPlaces_Size;
current_slice=slices;
while (current_slice<Slice_Max)
{
if (current_slice->sample_buffer)
SlicesPlaces[current_slice->slice_y*num_h_slices+current_slice->slice_x]++;
current_slice++;
}
for (size_t i=0; i<SlicesPlaces_Size; i++)
if (SlicesPlaces[i]!=1)
{
Element_Error("FFV1-FRAME-END:1");
break;
}
}
Element_End0();
FILLING_BEGIN();
if (Frame_Count==0)
{
Fill(Stream_Video, 0, Video_ScanType, Ffv1_picture_structure_ScanType(picture_structure));
Fill(Stream_Video, 0, Video_ScanOrder, Ffv1_picture_structure_ScanOrder(picture_structure));
if (sample_aspect_ratio_num && sample_aspect_ratio_den)
Fill(Stream_Video, 0, Video_PixelAspectRatio, ((float64)sample_aspect_ratio_num)/sample_aspect_ratio_den);
}
Frame_Count++;
FILLING_END();
delete RC;
RC = NULL;
Fill();
if (Config->ParseSpeed<1.0)
Finish();
}
//***************************************************************************
// Elements
//***************************************************************************
//---------------------------------------------------------------------------
void File_Ffv1::Parameters()
{
Element_Begin1("Parameters");
//Parsing
states States;
memset(States, 128, states_size);
Get_RU (States, version, "version");
if ( ConfigurationRecord_IsPresent && version<=1)
{
Param_Error("FFV1-HEADER-version-OUTOFBAND:1");
Accept(); //TODO: better check without removing error info in trace
Element_End0();
return;
}
if (!ConfigurationRecord_IsPresent && version> 1)
{
Param_Error("FFV1-HEADER-version-OUTOFBAND:1");
Accept(); //TODO: better check without removing error info in trace
Element_End0();
return;
}
if (version==2 || version>3)
{
Param_Error(version==2?"FFV1-HEADER-version-EXPERIMENTAL:1":"FFV1-HEADER-version-LATERVERSION:1");
if (version==2 || version>4)
{
Accept();
Element_End0();
return;
}
}
if (version>=3)
Get_RU (States, micro_version, "micro_version");
if ((version==3 && micro_version<4))
{
Param_Error("FFV1-HEADER-micro_version-EXPERIMENTAL:1");
Accept();
Element_End0();
return;
}
if (version==4) // Version 4 is still experimental at the time of writing but contains micro_version so we show it
{
Accept();
Element_End0();
return;
}
if (Frame_Count==0)
Accept(); //TODO: better check without removing error info in trace
Get_RU (States, coder_type, "coder_type");
if (coder_type>2)
{
Param_Error("FFV1-HEADER-coder_type:1");
Element_End0();
return;
}
if (coder_type==2) //Range coder with custom state transition table
{
Element_Begin1("state_transition_deltas");
for (size_t i = 1; i < state_transitions_size; i++)
{
int32s state_transition_delta;
Get_RS (States, state_transition_delta, "state_transition_delta");
state_transition_delta+=RC->one_state[i];
Param_Info1(state_transition_delta);
if (state_transition_delta<0x00 || state_transition_delta>0xFF)
{
Param_Error("FFV1-HEADER-state_transition_delta:1");
Element_End0();
Element_End0();
return;
}
state_transitions_table[i]=(int8u)state_transition_delta;
}
Element_End0();
}
Get_RU (States, colorspace_type, "colorspace_type");
if (colorspace_type>1)
{
Param_Error("FFV1-HEADER-colorspace_type:1");
Element_End0();
Element_End0();
return;
}
if (version)
{
Get_RU (States, bits_per_raw_sample, "bits_per_raw_sample");
if (bits_per_raw_sample>64)
{
Param_Error("FFV1-HEADER-bits_per_raw_sample:1");
Element_End0();
return;
}
if (bits_per_raw_sample==0)
bits_per_raw_sample=8; // Spec decision due to old encoders
}
else
{
bits_per_raw_sample=8;
}
Get_RB (States, chroma_planes, "chroma_planes");
Get_RU (States, h_chroma_subsample_log2, "log2(h_chroma_subsample)");
Get_RU (States, v_chroma_subsample_log2, "log2(h_chroma_subsample)");
Get_RB (States, alpha_plane, "alpha_plane");
if (version>1)
{
Get_RU (States, num_h_slices, "num_h_slices_minus1");
if (num_h_slices>=Width)
{
Param_Error("FFV1-HEADER-num_h_slices:1");
Element_End0();
return;
}
Get_RU (States, num_v_slices, "num_v_slices_minus1");
if (num_v_slices>=Height)
{
Param_Error("FFV1-HEADER-num_v_slices:1");
Element_End0();
return;
}
num_h_slices++;
num_v_slices++;
Get_RU (States, quant_table_count, "quant_table_count");
if (quant_table_count>8)
{
Param_Error("FFV1-HEADER-quant_table_count:1");
Element_End0();
return;
}
}
else
{
num_h_slices=1;
num_v_slices=1;
quant_table_count=1;
}
for (size_t i=0; i<quant_table_count; i++)
if (!QuantizationTable(i))
{
Element_End0();
return;
}
for (size_t i=0; i<quant_table_count; i++)
{
if (version>=2)
Element_Begin1("initial_state");
bool present=false;
if (version>=2)
Get_RB (States, present, "states_coded");
if (coder_type && context_count[i]>plane_states_maxsizes[i])
{
states_context_plane plane_state_old = plane_states[i];
plane_states[i] = new int8u*[context_count[i]];
if (plane_state_old)
{
memcpy(plane_states[i], plane_state_old, plane_states_maxsizes[i] * sizeof(int8u*));
delete[] plane_state_old;
}
for (size_t j = plane_states_maxsizes[i]; j < context_count[i]; j++)
plane_states[i][j] = new int8u[states_size];
plane_states_maxsizes[i] = context_count[i];
}
for (size_t j = 0; j < context_count[i]; j++)
{
if (present)
{
Element_Begin1("initial_state_deltas");
for (size_t k = 0; k < states_size; k++)
{
int32s value;
Get_RS (States, value, "initial_state_delta");
if (coder_type)
plane_states[i][j][k] = value;
}
Element_End0();
}
else if (coder_type)
{
for (size_t k = 0; k < states_size; k++)
plane_states[i][j][k] = 128;
}
}
if (version>=2)
Element_End0();
}
if (version>=3)
{
Get_RU (States, ec, "ec");
if (ec>1)
{
Param_Error("FFV1-HEADER-ec:1");
Element_End0();
return;
}
if (micro_version)
{
Get_RU (States, intra, "intra");
if (intra>1)
{
Param_Error("FFV1-HEADER-intra:1");
Element_End0();
return;
}
}
else
intra=0;
}
else
{
ec=0;
intra=0;
}
Element_End0();
FILLING_BEGIN();
//Marking handling of 16-bit overflow computing
is_overflow_16bit=(colorspace_type==0 && bits_per_raw_sample==16 && (coder_type==1 || coder_type==2))?true:false; //TODO: check in FFmpeg the version when the stream is fixed. Note: only with YUV colorspace
//quant_table_index_Count
quant_table_index_count=1+(alpha_plane?1:0);
if (version < 4 || chroma_planes) // Warning: chroma is considered as 1 plane
quant_table_index_count++;
//Slices
if (!slices)
{
slices=new Slice[num_h_slices*num_v_slices];
current_slice=&slices[0];
}
if (version<=1)
{
current_slice->x=0;
current_slice->y=0;
current_slice->w=Width;
current_slice->h=Height;
quant_table_index[0]=0;
for (size_t i=1; i<quant_table_index_count; i++)
{
quant_table_index[i]=0;
context_count[i]=context_count[0];
}
}
//Filling
if (Frame_Count==0)
{
Fill(Stream_Video, 0, "coder_type", Ffv1_coder_type(coder_type));
Fill(Stream_Video, 0, Video_BitDepth, bits_per_raw_sample);
if (version>1)
{
Fill(Stream_Video, 0, "MaxSlicesCount", num_h_slices*num_v_slices);
if (version>=3)
{
if (ec)
Fill(Stream_Video, 0, "ErrorDetectionType", "Per slice");
if (micro_version && intra)
Fill(Stream_Video, 0, Video_Format_Settings_GOP, "N=1");
}
}
Fill(Stream_Video, 0, Video_ColorSpace, Ffv1_colorspace_type(colorspace_type, chroma_planes, alpha_plane));
if (colorspace_type==0 && chroma_planes)
{
string ChromaSubsampling;
switch (h_chroma_subsample_log2)
{
case 0 :
switch (v_chroma_subsample_log2)
{
case 0 : ChromaSubsampling="4:4:4"; break;
default: ;
}
break;
case 1 :
switch (v_chroma_subsample_log2)
{
case 0 : ChromaSubsampling="4:2:2"; break;
case 1 : ChromaSubsampling="4:2:0"; break;
default: ;
}
break;
case 2 :
switch (v_chroma_subsample_log2)
{
case 0 : ChromaSubsampling="4:1:1"; break;
case 1 : ChromaSubsampling="4:1:0"; break;
case 2 : ChromaSubsampling="4:1:0 (4x4)"; break;
default: ;
}
break;
default: ;
}
if (!ChromaSubsampling.empty() && alpha_plane)
ChromaSubsampling+=":4";
Fill(Stream_Video, 0, Video_ChromaSubsampling, ChromaSubsampling);
}
Parameters_IsValid=true;
}
FILLING_END();
}
//---------------------------------------------------------------------------
void File_Ffv1::SliceContent(states &States)
{
Element_Begin1("SliceContent");
#if MEDIAINFO_TRACE
bool Trace_Activated_Save=Trace_Activated;
if (Trace_Activated)
Trace_Activated=false; // Trace is too huge, deactivating it during pixel decoding
#endif //MEDIAINFO_TRACE
if (!coder_type)
{
if (version>=3)
{
int8u s = 129;
RC->get_rac(&s);
}
Element_Offset+=RC->BytesUsed(); // Computing how many bytes where consumed by the range coder
BS_Begin();
}
if (keyframe)
{
int8u plane_count=1+(alpha_plane?1:0);
if (version < 4 || chroma_planes) // Warning: chroma is considered as 1 plane
plane_count+=1;
if (!coder_type)
current_slice->contexts_init(plane_count, quant_table_index, context_count);
else
copy_plane_states_to_slice(plane_count);
}
current_slice->sample_buffer_new((current_slice->w + 6) * 3 * MAX_PLANES);
if (colorspace_type == 0)
{
// YCbCr
plane(0); // Y
if (chroma_planes)
{
int32u w = current_slice->w;
int32u h = current_slice->h;
current_slice->w = w >> h_chroma_subsample_log2;
if (w & ((1 << h_chroma_subsample_log2) - 1))
current_slice->w++; //Is ceil
current_slice->h = h >> v_chroma_subsample_log2;
if (h & ((1 << v_chroma_subsample_log2) - 1))
current_slice->h++; //Is ceil
plane(1); // Cb
plane(1); // Cr
current_slice->w = w;
current_slice->h = h;
}
if (alpha_plane)
plane(2); // Alpha
}
else if (colorspace_type == 1)
rgb();
if (coder_type)
{
int8u s = 129;
RC->get_rac(&s);
}
if (BS->BufferUnderRun || RC->Underrun())
Element_Error("FFV1-SLICE-SliceContent:1");
if (coder_type)
Skip_XX(RC->BytesUsed(), "slice_data");
else
BS_End();
#if MEDIAINFO_DECODE
//Decode(Buffer, Buffer_Size);
#endif //MEDIAINFO_DECODE
#if MEDIAINFO_TRACE
Trace_Activated=Trace_Activated_Save; // Trace is too huge, reactivating after during pixel decoding
#endif //MEDIAINFO_TRACE
Element_End0();
}
//---------------------------------------------------------------------------
bool File_Ffv1::SliceHeader(states &States)
{
Element_Begin1("SliceHeader");
memset(States, 128, states_size);
int32u slice_x, slice_y, slice_width_minus1, slice_height_minus1;
Get_RU (States, slice_x, "slice_x");
if (slice_x >= num_h_slices)
{
Param_Error("FFV1-SLICE-slice_xywh:1");
Element_End0();
return false;
}
Get_RU (States, slice_y, "slice_y");
if (slice_y >= num_h_slices)
{
Param_Error("FFV1-SLICE-slice_xywh:1");
Element_End0();
return false;
}
Get_RU (States, slice_width_minus1, "slice_width_minus1");
int32u slice_x2 = slice_x + slice_width_minus1 + 1; //right boundary
if (slice_x2 > num_h_slices)
{
Param_Error("FFV1-SLICE-slice_xywh:1");
Element_End0();
return false;
}
Get_RU (States, slice_height_minus1, "slice_height_minus1");
int32u slice_y2 = slice_y + slice_height_minus1 + 1; //bottom boundary
if (slice_y2 > num_v_slices)
{
Param_Error("FFV1-SLICE-slice_xywh:1");
Element_End0();
return false;
}
current_slice = &slices[slice_x + slice_y * num_h_slices];
current_slice->slice_x = slice_x;
current_slice->slice_y = slice_y;
current_slice->slice_w = slice_x2;
current_slice->slice_h = slice_y2;
//Computing boundaries, being careful about how are computed boundaries when there is not an integral number for Width / num_h_slices or Height / num_v_slices (the last slice has more pixels)
current_slice->x = slice_x * Width / num_h_slices;
current_slice->y = slice_y * Height / num_v_slices;
current_slice->w = slice_x2 * Width / num_h_slices - current_slice->x;
current_slice->h = slice_y2 * Height / num_v_slices - current_slice->y;
for (int8u i = 0; i < quant_table_index_count; i++)
{
Get_RU (States, quant_table_index[i], "quant_table_index");
if (quant_table_index[i]>=quant_table_count)
{
Param_Error("FFV1-SLICE-quant_table_index:1");
Element_End0();
return false;
}
}
Get_RU (States, picture_structure, "picture_structure");
if (picture_structure>3)
Param_Error("FFV1-SLICE-picture_structure:1");
Get_RU (States, sample_aspect_ratio_num, "sar_num");
Get_RU (States, sample_aspect_ratio_den, "sar_den");
if ((sample_aspect_ratio_num && !sample_aspect_ratio_den)) // || (!sample_aspect_ratio_num && sample_aspect_ratio_den)) // Second part is deactivated because FFmpeg creates such file when SAR is unknown
Param_Error("FFV1-SLICE-sar_den:1");
if (version > 3)
{
//TODO
}
RC->AssignStateTransitions(state_transitions_table);
Element_End0();
return true;
}
//---------------------------------------------------------------------------
void File_Ffv1::plane(int32u pos)
{
#if MEDIAINFO_TRACE_FFV1CONTENT
Element_Begin1("Plane");
#endif //MEDIAINFO_TRACE_FFV1CONTENT
if (bits_per_raw_sample <= 8)
bits_max = 8;
else
bits_max = bits_per_raw_sample;
bits_mask1 = ((1 << bits_max) - 1);
bits_mask2 = 1 << (bits_max - 1);
bits_mask3 = bits_mask2 - 1;
pixel_t *sample[2];
sample[0] = current_slice->sample_buffer + 3;
sample[1] = sample[0] + current_slice->w + 6;
memset(current_slice->sample_buffer, 0, 2 * (current_slice->w + 6) * sizeof(*current_slice->sample_buffer));
current_slice->run_index = 0;
for (size_t y = 0; y < current_slice->h; y++)
{
#if MEDIAINFO_TRACE_FFV1CONTENT
Element_Begin1("Line");
Element_Info1(y);
#endif //MEDIAINFO_TRACE_FFV1CONTENT
swap(sample[0], sample[1]);
sample[1][-1] = sample[0][0];
sample[0][current_slice->w] = sample[0][current_slice->w - 1];
line(pos, sample);
#if MEDIAINFO_TRACE_FFV1CONTENT
Element_End0();
#endif //MEDIAINFO_TRACE_FFV1CONTENT
}
#if MEDIAINFO_TRACE_FFV1CONTENT
Element_End0();
#endif //MEDIAINFO_TRACE_FFV1CONTENT
}
//---------------------------------------------------------------------------
void File_Ffv1::rgb()
{
#if MEDIAINFO_TRACE_FFV1CONTENT
Element_Begin1("rgb");
#endif //MEDIAINFO_TRACE_FFV1CONTENT
bits_max = bits_per_raw_sample + 1;
bits_mask1 = (1 << bits_max) - 1;
bits_mask2 = 1 << (bits_max - 1);
bits_mask3 = bits_mask2-1;
size_t c_max = alpha_plane ? 4 : 3;
pixel_t *sample[4][2];
current_slice->run_index = 0;
for (size_t x = 0; x < c_max; x++) {
sample[x][0] = current_slice->sample_buffer + x * 2 * (current_slice->w + 6) + 3;
sample[x][1] = sample[x][0] + current_slice->w + 6;
}
memset(current_slice->sample_buffer, 0, 8 * (current_slice->w + 6) * sizeof(*current_slice->sample_buffer));
for (size_t y = 0; y < current_slice->h; y++)
{
#if MEDIAINFO_TRACE_FFV1CONTENT
Element_Begin1("Line");
Element_Info1(y);
#endif //MEDIAINFO_TRACE_FFV1CONTENT
for (size_t c = 0; c < c_max; c++)
{
// Copy for next lines: 4.3 context
swap(sample[c][0], sample[c][1]);
sample[c][1][-1]= sample[c][0][0 ];
sample[c][0][current_slice->w]= sample[c][0][current_slice->w - 1];
line((c + 1) / 2, sample[c]);
}
#if MEDIAINFO_TRACE_FFV1CONTENT
Element_End0();
#endif //MEDIAINFO_TRACE_FFV1CONTENT
}
#if MEDIAINFO_TRACE_FFV1CONTENT
Element_End0();
#endif //MEDIAINFO_TRACE_FFV1CONTENT
}
//---------------------------------------------------------------------------
static inline int32s get_median_number(int32s one, int32s two, int32s three)
{
if (one > two)
{
// one > two > three
if (two > three)
return two;
// three > one > two
if (three > one)
return one;
// one > three > two
return three;
}
// three > two > one
if (three > two)
return two;
// two > one && two > three
// two > three > one
if (three > one)
return three;
return one;
}
//---------------------------------------------------------------------------
static inline int32s predict(pixel_t *current, pixel_t *current_top, bool is_overflow_16bit)
{
int32s LeftTop, Top, Left;
if (is_overflow_16bit)
{
LeftTop = (int16s)current_top[-1];
Top = (int16s)current_top[0];
Left = (int16s)current[-1];
}
else
{
LeftTop = current_top[-1];
Top = current_top[0];
Left = current[-1];
}
return get_median_number(Left, Left + Top - LeftTop, Top);
}
//---------------------------------------------------------------------------
static inline int get_context_3(pixel_t quant_table[MAX_CONTEXT_INPUTS][256], pixel_t *src, pixel_t *last)
{
const int LT = last[-1];
const int T = last[0];
const int RT = last[1];
const int L = src[-1];
return quant_table[0][(L - LT) & 0xFF]
+ quant_table[1][(LT - T) & 0xFF]
+ quant_table[2][(T - RT) & 0xFF];
}
static inline int get_context_5(pixel_t quant_table[MAX_CONTEXT_INPUTS][256], pixel_t *src, pixel_t *last)
{
const int LT = last[-1];
const int T = last[0];
const int RT = last[1];
const int L = src[-1];
const int TT = src[0];
const int LL = src[-2];
return quant_table[0][(L - LT) & 0xFF]
+ quant_table[1][(LT - T) & 0xFF]
+ quant_table[2][(T - RT) & 0xFF]
+ quant_table[3][(LL - L) & 0xFF]
+ quant_table[4][(TT - T) & 0xFF];
}
//---------------------------------------------------------------------------
int32s File_Ffv1::pixel_RC(int32s context)
{
int32s u;
Get_RS(Context_RC[context], u, "symbol");
return u;
}
//---------------------------------------------------------------------------
int32s File_Ffv1::pixel_GR(int32s context)
{
#if MEDIAINFO_TRACE_FFV1CONTENT
int32s u;
// New symbol, go to "run mode"
if (context == 0 && current_slice->run_mode == RUN_MODE_STOP)
current_slice->run_mode = RUN_MODE_PROCESSING;
// If not running, get the symbol
if (current_slice->run_mode == RUN_MODE_STOP)
{
u = get_symbol_with_bias_correlation(&Context_GR[context]);
#if MEDIAINFO_TRACE
Param("symbol", u);
#endif //MEDIAINFO_TRACE
return u;
}
if (current_slice->run_segment_length == 0 && current_slice->run_mode == RUN_MODE_PROCESSING) // Same symbol length
{
bool hits;
Get_SB (hits, "hits/miss");
//if (bsf.GetB()) // "hits"
if (hits) // "hits"
{
current_slice->run_segment_length = run[current_slice->run_index];
if (x + current_slice->run_segment_length <= current_slice->w) //Do not go further as the end of line
++current_slice->run_index;
}
else // "miss"
{
//current_slice->run_segment_length = bsf.Get4(log2_run[current_slice->run_index]);
int32u run_segment_length;
Get_S4 (log2_run[current_slice->run_index], run_segment_length, "run_segment_length");
current_slice->run_segment_length=(int32s)run_segment_length;
if (current_slice->run_index)
--current_slice->run_index;
current_slice->run_mode = RUN_MODE_INTERRUPTED;
}
}
current_slice->run_segment_length--;
if (current_slice->run_segment_length < 0) // we passed the length of same symbol, time to get the new symbol
{
u = get_symbol_with_bias_correlation(&Context_GR[context]);
#if MEDIAINFO_TRACE
Param("symbol", u);
#endif //MEDIAINFO_TRACE
if (u >= 0) // GR(u - 1, ...)
u++;
// Time for the new symbol length run
current_slice->run_mode_init();
current_slice->run_segment_length = 0;
} else // same symbol as previous pixel, no difference, waiting
u = 0;
return u;
#else //MEDIAINFO_TRACE_FFV1CONTENT
if (current_slice->run_mode == RUN_MODE_STOP)
{
if (context)
return get_symbol_with_bias_correlation(&Context_GR[context]); // If not running, get the symbol
current_slice->run_mode = RUN_MODE_PROCESSING; // New symbol, go to "run mode"
}
if (current_slice->run_segment_length == 0 && current_slice->run_mode == RUN_MODE_PROCESSING) // Same symbol length
{
if (BS->GetB()) // "hits"
{
current_slice->run_segment_length = run[current_slice->run_index];
if (x + current_slice->run_segment_length <= current_slice->w) //Do not go further as the end of line
++current_slice->run_index;
if (--current_slice->run_segment_length >= 0)
return 0;
}
else // "miss"
{
current_slice->run_mode = RUN_MODE_INTERRUPTED;
if (current_slice->run_index)
{
int8u count = log2_run[current_slice->run_index--];
if (count)
{
current_slice->run_segment_length = ((int32s)BS->Get4(count)) - 1;
if (current_slice->run_segment_length >= 0)
return 0;
}
else
current_slice->run_segment_length = -1;
}
else
current_slice->run_segment_length = -1;
}
}
else if (--current_slice->run_segment_length >= 0)
return 0;
// Time for the new symbol length run
current_slice->run_mode_init();
int32s u = get_symbol_with_bias_correlation(&Context_GR[context]);
if (u >= 0) // GR(u - 1, ...)
u++;
return u;
#endif //MEDIAINFO_TRACE_FFV1CONTENT
}
//---------------------------------------------------------------------------
void File_Ffv1::line(int pos, pixel_t *sample[2])
{
// TODO: slice_coding_mode (version 4)
quant_table_struct& quant_table = quant_tables[quant_table_index[pos]];
bool Is5 = quant_table[3][127] ? true : false;
pixel_t* s0c = sample[0];
pixel_t* s0e = s0c + current_slice->w;
pixel_t* s1c = sample[1];
if (coder_type)
{
Context_RC = current_slice->plane_states[pos];
while (s0c<s0e)
{
int32s context = Is5 ? get_context_5(quant_table, s1c, s0c) : get_context_3(quant_table, s1c, s0c);
int32s Value = predict(s1c, s0c, is_overflow_16bit);
#if MEDIAINFO_TRACE_FFV1CONTENT
if (context >= 0)
Value += pixel_RC(context);
else
Value -= pixel_RC(-context);
#else //MEDIAINFO_TRACE_FFV1CONTENT
if (context >= 0)
Value += RC->get_symbol_s(Context_RC[context]);
else
Value -= RC->get_symbol_s(Context_RC[-context]);
#endif //MEDIAINFO_TRACE_FFV1CONTENT;
*s1c = Value & bits_mask1;
s0c++;
s1c++;
}
}
else
{
current_slice->run_mode_init();
Context_GR = current_slice->contexts[pos];
x = 0;
while (s0c < s0e)
{
int32s context = Is5 ? get_context_5(quant_table, s1c, s0c) : get_context_3(quant_table, s1c, s0c);
*s1c = (predict(s1c, s0c, is_overflow_16bit) + (context >= 0 ? pixel_GR(context) : -pixel_GR(-context))) & bits_mask1;
s0c++;
s1c++;
x++;
}
}
}
//---------------------------------------------------------------------------
bool File_Ffv1::QuantizationTable(size_t i)
{
Element_Begin1("QuantizationTableSet");
FFV1::pixel_t scale = 1;
for (size_t j = 0; j < 5; j++)
{
if (!QuantizationTablePerContext(i, j, scale))
{
Element_End0();
return false;
}
}
context_count[i] = (scale + 1) / 2;
Element_End0();
return true;
}
//---------------------------------------------------------------------------
bool File_Ffv1::QuantizationTablePerContext(size_t i, size_t j, FFV1::pixel_t &scale)
{
Element_Begin1("QuantizationTable");
int8u States[states_size];
memset(States, 128, sizeof(States));
FFV1::pixel_t v = 0;
for (size_t k=0; k < 128;)
{
int32u len_minus1;
Get_RU (States, len_minus1, "len_minus1");
if (k+len_minus1 >= 128)
{
Param_Error("FFV1-HEADER-QuantizationTable-len:1");
Element_End0();
return false;
}
for (int32u a=0; a<=len_minus1; a++)
{
quant_tables[i][j][k] = scale * v;
k++;
}
v++;
}
for (int k = 1; k < 128; k++)
quant_tables[i][j][256 - k] = -quant_tables[i][j][k];
quant_tables[i][j][128] = -quant_tables[i][j][127];
scale *= 2 * v - 1;
if (scale > 32768U)
{
Element_Error("FFV1-HEADER-QuantizationTable-scale:1");
Element_End0();
return false;
}
Element_End0();
return true;
}
//***************************************************************************
// Helpers
//***************************************************************************
//---------------------------------------------------------------------------
int32s File_Ffv1::golomb_rice_decode(int k)
{
#if MEDIAINFO_TRACE_FFV1CONTENT
int32u q = 0;
int32u v;
//while (bsf.Remain() > 0 && q < PREFIX_MAX && !bsf.GetB())
while (Data_BS_Remain() > 0 && q < PREFIX_MAX)
{
bool Temp;
Get_SB(Temp, "golomb_rice_prefix_0");
if (Temp)
break;
++q;
}
if (q == PREFIX_MAX) // ESC
{
//v = bsf.Get4(bits_max) + 11;
Get_S4(bits_max, v, "escaped_value_minus_11");
v+=11;
}
else
{
//int32u remain = bsf.Get8(k); // Read k bits
int32u remain;
Get_S4(k, remain, "golomb_rice_remain");
int32u mul = q << k; // q * pow(2, k)
v = mul | remain;
}
// unsigned to signed
int32s code = (v >> 1) ^ -(v & 1);
return code;
#else //MEDIAINFO_TRACE_FFV1CONTENT
int8u q = 0;
while (BS->Remain() && !BS->GetB())
if (++q >= PREFIX_MAX)
{
int32s v = 11 + BS->Get4(bits_max);
// unsigned to signed
return (v >> 1) ^ -(v & 1);
}
int32s v = (q << k) | BS->Get4(k);
// unsigned to signed
return (v >> 1) ^ -(v & 1);
#endif //MEDIAINFO_TRACE_FFV1CONTENT
}
//---------------------------------------------------------------------------
int32s File_Ffv1::get_symbol_with_bias_correlation(Slice::ContextPtr context)
{
int k = 0;
// Step 8: compute the Golomb parameter k
for (k = 0; (context->N << k) < context->A; ++k);
// Step 10: Decode Golomb code (using limitation PREFIX_MAX == 12)
int32s code = golomb_rice_decode(k);
// Step 9: Mapping
int32s M = 2 * context->B + context->N;
code = code ^ (M >> 31);
// Step 11
context->B += code;
context->A += code >= 0 ? code : -code;
code += context->C;
context->update_correlation_value_and_shift();
// Step 7 (TODO better way)
bool neg = code & bits_mask2; // check if the number is negative
code = code & bits_mask3; // Keep only the n bits
if (neg)
code = - 1 - (~code & bits_mask3); // 0xFFFFFFFF - positive value on n bits
return code;
}
//---------------------------------------------------------------------------
void File_Ffv1::copy_plane_states_to_slice(int8u plane_count)
{
if (!coder_type)
return;
for (size_t i = 0; i < plane_count; i++)
{
int32u idx = quant_table_index[i];
if (current_slice->plane_states[i] && current_slice->plane_states_maxsizes[i] < context_count[idx] + 1)
{
for (size_t j = 0; current_slice->plane_states[i][j]; ++j)
delete[] current_slice->plane_states[i][j];
delete[] current_slice->plane_states[i];
current_slice->plane_states[i] = NULL;
}
if (!current_slice->plane_states[i])
{
current_slice->plane_states[i] = new int8u*[context_count[idx] + 1];
current_slice->plane_states_maxsizes[i] = context_count[idx] + 1;
memset(current_slice->plane_states[i], 0, (context_count[idx] + 1) * sizeof(int8u*));
}
for (size_t j = 0; j < context_count[idx]; j++)
{
if (!current_slice->plane_states[i][j])
current_slice->plane_states[i][j] = new int8u [states_size];
for (size_t k = 0; k < states_size; k++)
{
current_slice->plane_states[i][j][k] = plane_states[idx][j][k];
}
}
}
}
//---------------------------------------------------------------------------
void File_Ffv1::plane_states_clean(states_context_plane states[MAX_QUANT_TABLES])
{
if (!coder_type)
return;
for (size_t i = 0; i < MAX_QUANT_TABLES && states[i]; ++i)
{
for (size_t j = 0; states[i][j]; ++j)
delete[] states[i][j];
delete[] states[i];
states[i] = NULL;
}
}
} //NameSpace
#endif //MEDIAINFO_FFV1_YES
↑ V127 An overflow of the 32-bit 'v' variable is possible inside a long cycle which utilizes a memsize-type loop counter.
↑ V127 An overflow of the 32-bit 'Value' variable is possible inside a long cycle which utilizes a memsize-type loop counter.
↑ V547 Expression is always true.
↑ V1020 The function exited without calling the 'Element_End' function. Check lines: 760, 734.
↑ V1020 The function exited without calling the 'BS_End' function. Check lines: 1336, 1274.
↑ V756 The 'y' counter is not used inside a nested loop. Consider inspecting usage of 'c' counter.
↑ V688 The 'x' local variable possesses the same name as one of the class members, which can result in a confusion.
↑ V688 The 'x' local variable possesses the same name as one of the class members, which can result in a confusion.
↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: current_slice, micro_version, coder_type, colorspace_type, bits_per_raw_sample, h_chroma_subsample_log2, ...
↑ V826 Consider replacing the 'Slices_BufferSizes' std::vector with std::deque. Overall efficiency of operations will increase.