/* 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.
*/
//---------------------------------------------------------------------------
//
// Examples:
// http://samples.mplayerhq.hu/FLV/
//
// Reverse engineering
// http://osflash.org/documentation/amf/astypes
//
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
// Pre-compilation
#include "MediaInfo/PreComp.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
#include "MediaInfo/Setup.h"
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
#if defined(MEDIAINFO_FLV_YES)
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
#include "MediaInfo/Multiple/File_Flv.h"
#if defined(MEDIAINFO_AVC_YES)
#include "MediaInfo/Video/File_Avc.h"
#endif
#if defined(MEDIAINFO_HEVC_YES)
#include "MediaInfo/Video/File_Hevc.h"
#endif
#if defined(MEDIAINFO_AAC_YES)
#include "MediaInfo/Audio/File_Aac.h"
#endif
#if defined(MEDIAINFO_MPEGA_YES)
#include "MediaInfo/Audio/File_Mpega.h"
#endif
#if defined(MEDIAINFO_RM_YES)
#include "MediaInfo/Multiple/File_Rm.h"
#endif
#include "MediaInfo/MediaInfo_Config_MediaInfo.h"
#if MEDIAINFO_EVENTS
#include "MediaInfo/MediaInfo_Events.h"
#endif //MEDIAINFO_EVENTS
#include <algorithm>
#if MEDIAINFO_DEMUX
#include "ThirdParty/base64/base64.h"
#endif //MEDIAINFO_DEMUX
//---------------------------------------------------------------------------
namespace MediaInfoLib
{
//---------------------------------------------------------------------------
static const int16u Flv_Channels[]=
{
1,
2,
};
static const int16u Flv_Resolution[]=
{
8,
16,
};
static const int16u Flv_SamplingRate[]=
{
5500,
11025,
22050,
44100,
8000, //Special case for Nellymoser 8kHz mono
};
static const char* Flv_Format_Audio[16]=
{
"PCM",
"ADPCM",
"MPEG Audio",
"PCM",
"Nellymoser", //16 KHz
"Nellymoser", //8 KHz
"Nellymoser",
"ADPCM",
"ADPCM",
"",
"AAC",
"Speex",
"",
"",
"MPEG Audio", //8 KHz
"",
};
static const char* Flv_Format_Profile_Audio[16]=
{
"",
"",
"Layer 3",
"",
"",
"",
"",
"A-law",
"U-law",
"",
"",
"",
"",
"",
"Layer 3", //8 KHz
"",
};
static const char* Flv_Codec_Audio[16]=
{
"Uncompressed",
"ADPCM",
"MPEG-1 Audio Layer 3",
"",
"Nellymoser 16kHz mono",
"Nellymoser 8kHz mono",
"Nellymoser",
"ADPCM",
"ADPCM",
"",
"AAC",
"Speex",
"",
"",
"MPEG Audio Layer 3",
"",
};
static const char* Flv_CodecID_Hint_Audio[16]=
{
"",
"",
"MP3",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"MP3", //8 KHz
"",
};
static const char* Flv_Format_Video[16]=
{
"",
"",
"Sorenson Spark",
"Screen video",
"VP6",
"VP6",
"Screen video 2",
"AVC",
"",
"",
"",
"",
"HEVC",
"",
"",
"",
};
static const char* Flv_Format_Profile_Video[16]=
{
"",
"",
"",
"",
"",
"Alpha channel",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
};
static const char* Flv_Codec_Video[16]=
{
"",
"",
"Sorenson H263",
"Screen video",
"On2 VP6",
"On2 VP6 with alpha channel",
"Screen video 2",
"AVC",
"",
"",
"",
"",
"HEVC",
"",
"",
"",
};
static const char* Flv_CodecID_Hint_Video[16]=
{
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
};
static const char* Flv_H263_PictureSize[]=
{
"custom, 1 byte",
"custom, 2 bytes",
"CIF (352x288)",
"QCIF (176x144)",
"SQCIF (128x96)",
"320x240",
"160x120",
"",
};
static const int16u Flv_H263_WidthHeight[8][2]=
{
{ 0, 0},
{ 0, 0},
{352, 288},
{176, 144},
{128, 96},
{320, 240},
{160, 120},
{0, 0},
};
static const char* Flv_H263_PictureType[]=
{
"IntraFrame",
"InterFrame",
"InterFrame (Disposable)",
"",
};
static const char* Flv_VP6_FrameMode[]=
{
"IntraFrame",
"",
};
static const char* Flv_VP6_Marker[]=
{
"VP6.1/6.2",
"VP6.0",
};
static const char* Flv_VP6_Version[]=
{
"",
"",
"",
"",
"",
"",
"VP6.0/6.1",
"VP6.0 (Electronic Arts)",
"VP6.2",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
};
static const char* Flv_VP6_Version2[]=
{
"VP6.0",
"",
"",
"VP6.1/6.2",
};
static const char* Flv_FrameType[]=
{
"",
"KeyFrame",
"InterFrame",
"InterFrame (Disposable)",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
};
static const char* Flv_TagType[]=
{
"DOUBLE",
"UI8",
"SCRIPTDATASTRING",
"SCRIPTDATAOBJECT[n]",
"SCRIPTDATASTRING defining the MovieClip path",
"Null",
"Undefined",
"UI16",
"SCRIPTDATAVARIABLE[ECMAArrayLength]",
"EndOfObject",
"SCRIPTDATAVARIABLE[n]",
"SCRIPTDATADATE",
"SCRIPTDATALONGSTRING",
"Unsupported",
"Recordset",
"XML",
"TypedObject",
"AMF3 data",
};
static const char* Flv_Amf3Type[]=
{
"Undefined",
"Null",
"Boolean-false",
"Boolean-true",
"Integer",
"Number",
"String",
"XML",
"Data",
"Array",
"Object",
"XML String",
"ByteArray",
};
static const char* Flv_AVCPacketType(int8u Value)
{
switch (Value)
{
case 0 : return "AVC sequence header";
case 1 : return "NALU";
case 2 : return "end of sequence";
default: return "";
}
}
static const char* Flv_AACPacketType(int8u Value)
{
switch (Value)
{
case 0 : return "AAC sequence header";
case 1 : return "AAC Raw";
default: return "";
}
}
//***************************************************************************
// Constructor/Destructor
//***************************************************************************
//---------------------------------------------------------------------------
File_Flv::File_Flv()
:File__Analyze()
{
//File__Tags_Helper
Base=this;
//Configuration
ParserName="FLV";
#if MEDIAINFO_EVENTS
ParserIDs[0]=MediaInfo_Parser_Flv;
StreamIDs_Width[0]=2;
#endif //MEDIAINFO_EVENTS
#if MEDIAINFO_DEMUX
Demux_Level=2; //Container
#endif //MEDIAINFO_DEMUX
//Internal
Stream.resize(3); //Null, Video, Audio
//Temp
Searching_Duration=false;
MetaData_NotTrustable=false;
PreviousTagSize=(int32u)-1;
meta_filesize=(int64u)-1;
meta_duration=0;
}
//***************************************************************************
// Streams management
//***************************************************************************
//---------------------------------------------------------------------------
void File_Flv::Streams_Fill()
{
//Coherency
if (Count_Get(Stream_Video) && Count_Get(Stream_Audio) && !Retrieve(Stream_Video, 0, Video_BitRate).empty() && Retrieve(Stream_Audio, 0, Audio_BitRate).empty())
{
Fill(Stream_General, 0, General_OverallBitRate, Retrieve(Stream_Video, 0, Video_BitRate));
Clear(Stream_Video, 0, Video_BitRate);
}
//Trying to detect VFR
std::vector<int64u> video_stream_FrameRate_Between;
for (size_t Pos=1; Pos<video_stream_FrameRate.size(); Pos++)
video_stream_FrameRate_Between.push_back(video_stream_FrameRate[Pos]-video_stream_FrameRate[Pos-1]);
std::sort(video_stream_FrameRate_Between.begin(), video_stream_FrameRate_Between.end());
if (!video_stream_FrameRate_Between.empty())
{
if (video_stream_FrameRate_Between[0]*0.9<video_stream_FrameRate_Between[video_stream_FrameRate_Between.size()-1]
&& video_stream_FrameRate_Between[0]*1.1>video_stream_FrameRate_Between[video_stream_FrameRate_Between.size()-1])
{
float Time;
if (video_stream_FrameRate.size()>30)
Time=((float)(video_stream_FrameRate[30]-video_stream_FrameRate[0]))/30; //30 frames for handling 30 fps rounding problems
else
Time=((float)(video_stream_FrameRate[video_stream_FrameRate.size()-1]-video_stream_FrameRate[0]))/(video_stream_FrameRate.size()-1); //30 frames for handling 30 fps rounding problems
if (Time)
{
Fill(Stream_Video, 0, Video_FrameRate, 1000/Time);
Fill(Stream_Video, 0, Video_FrameRate_Mode, "CFR");
}
}
else
Fill(Stream_Video, 0, Video_FrameRate_Mode, "VFR");
}
//Parsers
if (Stream[Stream_Video].Parser!=NULL)
{
Fill(Stream[Stream_Video].Parser);
}
if (Stream[Stream_Audio].Parser!=NULL)
{
Fill(Stream[Stream_Audio].Parser);
//Special case: AAC
if (Stream[Stream_Audio].Parser->Retrieve(Stream_Audio, 0, Audio_Format)==__T("AAC")
|| Stream[Stream_Audio].Parser->Retrieve(Stream_Audio, 0, Audio_Format)==__T("MPEG Audio")
|| Stream[Stream_Audio].Parser->Retrieve(Stream_Audio, 0, Audio_Format)==__T("Vorbis"))
Clear(Stream_Audio, 0, Audio_BitDepth); //Resolution is not valid for AAC / MPEG Audio / Vorbis
}
//Delay
if (Stream[Stream_Video].Delay!=(int32u)-1)
{
Fill(Stream_Video, 0, Video_Delay, Stream[Stream_Video].Delay+Retrieve(Stream_Video, 0, Video_Delay).To_int32u(), 10, true);
Fill(Stream_Video, 0, Video_Delay_Source, "Container");
}
if (Stream[Stream_Audio].Delay!=(int32u)-1)
{
Fill(Stream_Audio, 0, Audio_Delay, Stream[Stream_Audio].Delay+Retrieve(Stream_Audio, 0, Audio_Delay).To_int32u(), 10, true);
Fill(Stream_Audio, 0, Audio_Delay_Source, "Container");
}
}
//---------------------------------------------------------------------------
void File_Flv::Streams_Finish()
{
//Duration
//if (meta_duration)
// Fill(Stream_General, 0, General_Duration, meta_duration, 10, true);
Streams_Finish_PerStream(Stream_Video);
Streams_Finish_PerStream(Stream_Audio);
/*
float64 FrameRate=Retrieve(Stream_Video, 0, Video_FrameRate).To_float64();
if (LastFrame_Time!=(int32u)-1 && FirstFrame_Time!=(int32u)-1)
Duration_Final=LastFrame_Time-FirstFrame_Time+((LastFrame_Type==9 && FrameRate)?((int64u)(1000/FrameRate)):0);
if (Duration_Final)
{
if (Count_Get(Stream_Video))
Fill(Stream_Video, 0, Video_Duration, Duration_Final, 10, true);
if (Count_Get(Stream_Audio))
Fill(Stream_Audio, 0, Audio_Duration, Duration_Final, 10, true);
//Integrity
if (Count_Get(Stream_Video) && File_Size!=(int64u)-1 && !Retrieve(Stream_Video, 0, Video_BitRate).empty() && !Retrieve(Stream_Video, 0, Video_Duration).empty())
{
int64u BitRate_Video_Meta=Retrieve(Stream_Video, 0, Video_BitRate).To_int64u();
int64u Duration=Retrieve(Stream_Video, 0, Video_Duration).To_int64u();
int64u BitRate_Video_Duration=File_Size*8*1000/Duration;
if (Count_Get(Stream_Audio) && !Retrieve(Stream_Audio, 0, Audio_BitRate).empty())
{
int64u BitRate_Audio=Retrieve(Stream_Audio, 0, Audio_BitRate).To_int64u();
if (BitRate_Audio<BitRate_Video_Duration)
BitRate_Video_Duration-=BitRate_Audio;
else if (BitRate_Audio)
BitRate_Video_Duration=0; //There is a problem
}
if (BitRate_Video_Meta<BitRate_Video_Duration/2 || BitRate_Video_Meta>BitRate_Video_Duration*2)
Clear(Stream_Video, 0, Video_BitRate);
}
}
*/
if (Stream[Stream_Video].Parser!=NULL)
{
File__Analyze::Finish(Stream[Stream_Video].Parser);
Merge(*Stream[Stream_Video].Parser, Stream_Video, 0, 0);
}
if (Stream[Stream_Audio].Parser!=NULL)
{
File__Analyze::Finish(Stream[Stream_Audio].Parser);
Merge(*Stream[Stream_Audio].Parser, Stream_Audio, 0, 0);
}
if (Retrieve(Stream_General, 0, General_Duration).empty() && Retrieve(Stream_Video, 0, Video_Duration).empty() && meta_duration)
Fill(Stream_General, 0, General_Duration, meta_duration, 0, true);
//Purge what is not needed anymore
if (!File_Name.empty()) //Only if this is not a buffer, with buffer we can have more data
Stream.clear();
}
//---------------------------------------------------------------------------
void File_Flv::Streams_Finish_PerStream(stream_t StreamKind)
{
if (Stream[StreamKind].TimeStamp!=(int32u)-1)
{
//Calculating the last timestamp (last block included)
if (!Stream[StreamKind].Durations.empty())
{
int64u Durations_Total=0;
for (size_t Pos=0; Pos<Stream[StreamKind].Durations.size(); Pos++)
Durations_Total+=Stream[StreamKind].Durations[Pos];
int32u Duration_Average=float32_int32s(((float32)Durations_Total)/Stream[StreamKind].Durations.size());
Stream[StreamKind].TimeStamp+=Duration_Average;
}
Fill(StreamKind, 0, "Duration", Stream[StreamKind].TimeStamp, 10, true);
}
}
//***************************************************************************
// Buffer - File header
//***************************************************************************
//---------------------------------------------------------------------------
bool File_Flv::FileHeader_Begin()
{
if (!File__Tags_Helper::FileHeader_Begin())
return false;
//Synchro
if (Buffer_Offset+3>Buffer_Size)
return false;
if (Buffer[Buffer_Offset+0]!=0x46 //"FLV"
|| Buffer[Buffer_Offset+1]!=0x4C
|| Buffer[Buffer_Offset+2]!=0x56)
{
File__Analyze::Reject();
return false;
}
if (Buffer_Offset+9>Buffer_Size)
return false;
return true;
}
//---------------------------------------------------------------------------
void File_Flv::FileHeader_Parse()
{
//Parsing
Element_Begin1("FLV header");
int32u Size;
int8u Version, Flags;
Skip_String(3, "Signature");
Get_B1 (Version, "Version");
Get_B1 (Flags, "Flags");
Get_Flags (Flags, 0, video_stream_Count, "Video");
Get_Flags (Flags, 2, audio_stream_Count, "Audio");
Get_B4 (Size, "Size");
if (Size>9)
Skip_XX(Size-9, "Unknown");
Element_End0();
FILLING_BEGIN();
//Integrity
if (Version==0 || Size<9)
{
File__Analyze::Reject();
return;
}
//Filling
File__Analyze::Accept();
Fill(Stream_General, 0, General_Format, "Flash Video");
if (!video_stream_Count && !audio_stream_Count)
{
//TODO: quick and awful hack for a file having both bools unset, should detect directly the streams
video_stream_Count=true;
audio_stream_Count=true;
}
if (video_stream_Count)
{
File__Analyze::Stream_Prepare(Stream_Video);
#if MEDIAINFO_DEMUX
if (Config->Demux_ForceIds_Get())
Fill(Stream_Video, 0, Video_ID, 9);
#endif //MEDIAINFO_DEMUX
video_stream_FrameRate_Detected=false;
}
else
video_stream_FrameRate_Detected=true;
if (audio_stream_Count)
{
File__Analyze::Stream_Prepare(Stream_Audio);
#if MEDIAINFO_DEMUX
if (Config->Demux_ForceIds_Get())
Fill(Stream_Audio, 0, Audio_ID, 8);
#endif //MEDIAINFO_DEMUX
}
if (Version>1)
{
File__Analyze::Finish();
return; //Version more than 1 is not supported
}
FILLING_ELSE()
File__Analyze::Reject();
FILLING_END();
}
//***************************************************************************
// Buffer - Synchro
//***************************************************************************
//---------------------------------------------------------------------------
bool File_Flv::Synchronize()
{
if (File_Offset+Buffer_Offset+4==File_Size)
return true; // Used by seek from end
//Synchronizing
while (Buffer_Offset+15<=Buffer_Size)
{
int32u BodyLength=BigEndian2int24u(Buffer+Buffer_Offset+5);
if ((Buffer[Buffer_Offset ]
|| Buffer[Buffer_Offset+1]
|| Buffer[Buffer_Offset+2]
|| Buffer[Buffer_Offset+3]>=11)
&& File_Offset+Buffer_Offset+15+BodyLength==File_Size)
break; //Last block
if (File_Offset+Buffer_Offset+15+BodyLength<File_Size)
{
if (Buffer_Offset+15+BodyLength+15>Buffer_Size)
return false; //Need more data
if ((Buffer[Buffer_Offset ]
|| Buffer[Buffer_Offset+1]
|| Buffer[Buffer_Offset+2]
|| Buffer[Buffer_Offset+3]>=11)
&& (BigEndian2int32u(Buffer+Buffer_Offset+15+BodyLength)==11+BodyLength // PreviousTagSize
|| BigEndian2int32u(Buffer+Buffer_Offset+15+BodyLength)==BodyLength)) // PreviousTagSize without 11, found in some buggy files
{
PreviousTagSize_Add11=(BigEndian2int32u(Buffer+Buffer_Offset+15+BodyLength)==BodyLength)?0:11;
break;
}
}
Buffer_Offset++;
}
//Parsing last bytes if needed
if (Buffer_Offset+15>Buffer_Size)
return false;
//Synched
return true;
}
//---------------------------------------------------------------------------
bool File_Flv::Synched_Test()
{
if (File_Offset+Buffer_Offset+4==File_Size)
return true; // Used by seek from end
//Must have enough buffer for having header
if (Buffer_Offset+15>Buffer_Size)
return false;
//Quick test of synchro
if (Buffer[Buffer_Offset ]==0
&& Buffer[Buffer_Offset+1]==0
&& Buffer[Buffer_Offset+2]==0
&& Buffer[Buffer_Offset+3]<PreviousTagSize_Add11
&& File_Offset+Buffer_Offset>9)
{
if (Searching_Duration)
{
//Error during parsing, stopping
File__Analyze::Finish();
Searching_Duration=false;
File__Analyze::GoTo(File_Size);
return true;
}
Synched=false;
return true;
}
//We continue
return true;
}
//***************************************************************************
// Buffer - Global
//***************************************************************************
//---------------------------------------------------------------------------
void File_Flv::Read_Buffer_Unsynched()
{
if (!Searching_Duration) //If Searching_Duration, we are looking for end in inverse order, no timestamp reset
{
Stream[Stream_Video].TimeStamp=(int32u)-1;
if (Stream[Stream_Video].Parser)
Stream[Stream_Video].Parser->Open_Buffer_Unsynch();
Stream[Stream_Audio].TimeStamp=(int32u)-1;
if (Stream[Stream_Audio].Parser)
Stream[Stream_Audio].Parser->Open_Buffer_Unsynch();
}
}
//***************************************************************************
// Buffer - Per element
//***************************************************************************
//---------------------------------------------------------------------------
void File_Flv::Header_Parse()
{
if (Searching_Duration && File_Offset+Buffer_Offset==File_Size-4)
{
Get_B4 (PreviousTagSize, "PreviousTagSize");
//Filling
Header_Fill_Code((int64u)-1, "End Of File");
Header_Fill_Size(4);
return;
}
//Parsing
int32u BodyLength;
int8u Type;
Get_B4 (PreviousTagSize, "PreviousTagSize");
if (File_Offset+Buffer_Offset+4<File_Size)
{
int32u Timestamp_Base;
int8u Timestamp_Extended;
Get_B1 (Type, "Type"); //Param_Info1(Type<19?Flv_Type[Type]:__T("Unknown"));
Get_B3 (BodyLength, "BodyLength");
Get_B3 (Timestamp_Base, "Timestamp_Base"); //in ms
Get_B1 (Timestamp_Extended, "Timestamp_Extended"); //TimeStamp = Timestamp_Extended*0x01000000+Timestamp_Base
Skip_B3( "StreamID");
// For audio, check if it's just an audio config.
bool Skip_Timestamps=false;
if (Type==0x08)
{
int16u Format_Info;
Peek_B2(Format_Info);
int8u Format=(Format_Info>>12)&0x0F;
if (Format==10 && (Format_Info&0xFF)==0) // AAC sequence header
Skip_Timestamps=true;
}
//Filling
if ((Type==0x08 && !Skip_Timestamps) || Type==0x09)
{
Time=(((int32u)Timestamp_Extended)<<24)|Timestamp_Base;
stream_t StreamKind=(Type==0x08)?Stream_Audio:Stream_Video;
if (Stream[StreamKind].Delay==(int32u)-1)
Stream[StreamKind].Delay=Time;
else if (Stream[StreamKind].TimeStamp!=(int32u)-1 && Time>Stream[StreamKind].TimeStamp)
Stream[StreamKind].Durations.push_back(Time-Stream[StreamKind].TimeStamp);
if (!Searching_Duration || Stream[StreamKind].TimeStamp==(int32u)-1)
Stream[StreamKind].TimeStamp=Time;
}
if (Type==0)
Trusted_IsNot("Wrong type");
}
else
{
Type=0;
BodyLength=0;
}
//Filling
Header_Fill_Code(Type, Ztring().From_Number(Type, 16));
Header_Fill_Size(Element_Offset+BodyLength);
}
//---------------------------------------------------------------------------
void File_Flv::Data_Parse()
{
switch (Element_Code)
{
case 0x00 : Element_Name("End Of File"); break;
case 0x08 : audio(); break;
case 0x09 : video(); break;
case 0x12 : meta(); break;
case 0xFA : Rm(); break;
case (int64u)-1 : //When searching the last frame
if (8+PreviousTagSize>File_Size)
{
Searching_Duration=false;
Open_Buffer_Unsynch(); //There is a problem, trying to sync
PreviousTagSize=1024*1024;
}
File__Analyze::GoTo(File_Size-PreviousTagSize-8, "FLV");
return;
default : if (Searching_Duration)
{
File__Analyze::Finish(); //This is surely a bad en of file, don't try anymore
return;
}
}
if (Searching_Duration)
{
if ((((Count_Get(Stream_Video)==0 || Stream[Stream_Video].TimeStamp!=(int32u)-1)
&& (Count_Get(Stream_Audio)==0 || Stream[Stream_Audio].TimeStamp!=(int32u)-1))
|| (File_Size>1024*1024*2 && File_Offset+Buffer_Offset-Header_Size-PreviousTagSize-4<File_Size-1024*1024))
&& Config->ParseSpeed<1.0)
File__Analyze::Finish();
else if (Element_Code==0xFA) //RM metadata have a malformed PreviousTagSize, always
{
//Trying to sync
Searching_Duration=false;
Open_Buffer_Unsynch(); //There is a problem, trying to sync
File__Analyze::GoToFromEnd(Header_Size+Element_Size+1024*1024);
return;
}
else
File__Analyze::GoTo(File_Offset+Buffer_Offset-Header_Size-PreviousTagSize-4);
}
else if (!Status[IsFilled] && !video_stream_Count && !audio_stream_Count && video_stream_FrameRate_Detected && File_Offset+1024*1024*2<File_Size && Config->ParseSpeed<1.0) //All streams are parsed
{
Fill();
//Trying to find the last frame for duration
Read_Buffer_Unsynched(); //This is not synched yet, so we call directly this method instead of Open_Buffer_Unsynched
File__Analyze::GoToFromEnd(4, "FLV");
Searching_Duration=true;
}
}
//***************************************************************************
// Elements
//***************************************************************************
//---------------------------------------------------------------------------
void File_Flv::video()
{
Element_Name("Video");
Stream[Stream_Video].PacketCount++;
Element_Info1(Stream[Stream_Video].PacketCount);
//Handling FrameRate
if (!video_stream_FrameRate_Detected)
{
if (video_stream_FrameRate.empty() || Time!=video_stream_FrameRate[video_stream_FrameRate.size()-1]) //if 2 block witht the same timestamp
video_stream_FrameRate.push_back(Time);
if (video_stream_FrameRate.size()>30)
video_stream_FrameRate_Detected=true;
}
if (Element_Size==0) //Header says that video is present, but there is only one null packet
{
Element_Info1("Null");
return;
}
//Needed?
if (!video_stream_Count && Config->ParseSpeed<1.0)
return; //No more need of Video stream
//Parsing
int8u Codec, FrameType;
Element_Begin1("Stream header");
BS_Begin();
Get_S1 (4, FrameType, "frameType"); Param_Info1(Flv_FrameType[FrameType]);
Get_S1 (4, Codec, "codecID"); Param_Info1(Flv_Codec_Video[Codec]); Element_Info1(Flv_Codec_Video[Codec]);
BS_End();
Element_End0();
FILLING_BEGIN();
//Filling
if (Retrieve(Stream_Video, 0, Video_Format).empty())
{
if (Count_Get(Stream_Video)==0)
File__Analyze::Stream_Prepare(Stream_Video);
Fill(Stream_Video, 0, Video_Format, Flv_Format_Video[Codec]);
Fill(Stream_Video, 0, Video_Format_Profile, Flv_Format_Profile_Video[Codec]);
Fill(Stream_Video, 0, Video_Codec, Flv_Codec_Video[Codec]);
Fill(Stream_Video, 0, Video_CodecID, Codec);
Fill(Stream_Video, 0, Video_CodecID_Hint, Flv_CodecID_Hint_Video[Codec]);
Fill(Stream_Video, 0, Video_BitDepth, 8); //FLV is not known to support another bit depth
MustSynchronize=true; // Now, synchronization test is possible
}
//Parsing video data
switch (Codec)
{
case 2 : video_H263(); break;
case 3 : video_ScreenVideo(1); break;
case 4 : video_VP6(false); break;
case 5 : video_VP6(true); break;
case 6 : video_ScreenVideo(2); break;
case 7 : video_AVC(); break;
case 12 : video_HEVC(); break;
default : Skip_XX(Element_Size-Element_Offset, "Unknown");
video_stream_Count=false; //No more need of Video stream;
}
FILLING_END();
#if MEDIAINFO_DEMUX
int8u Demux_Level_old=Demux_Level;
if (Stream[Stream_Video].Parser && Stream[Stream_Video].Parser->Demux_Level==2)
Demux_Level=4;
Demux(Buffer+Buffer_Offset+1, (size_t)(Element_Size-1), ContentType_MainStream);
Demux_Level=Demux_Level_old;
#endif //MEDIAINFO_DEMUX
}
//---------------------------------------------------------------------------
void File_Flv::video_H263()
{
//Parsing
int16u Width=0, Height=0;
int8u Version, PictureSize, PictureType;
bool ExtraInformationFlag;
BS_Begin();
Skip_S3(17, "PictureStartCode");
Get_S1 ( 5, Version, "Version");
if (Version>1)
return;
Skip_S1( 8, "TemporalReference");
Get_S1 ( 3, PictureSize, "PictureSize"); Param_Info1(Flv_H263_PictureSize[PictureSize]);
switch (PictureSize)
{
case 0 :
Get_S2 ( 8, Width, "Width");
Get_S2 ( 8, Height, "Height");
break;
case 1 :
Get_S2 (16, Width, "Width");
Get_S2 (16, Height, "Height");
break;
default :
if (PictureSize<8)
{
Width=Flv_H263_WidthHeight[PictureSize][0];
Height=Flv_H263_WidthHeight[PictureSize][1];
}
}
Get_S1 ( 2, PictureType, "PictureSize"); Param_Info1(Flv_H263_PictureType[PictureType]);
Skip_SB( "DeblockingFlag");
Skip_S1( 5, "Quantizer");
Get_SB ( ExtraInformationFlag, "ExtraInformationFlag");
while (ExtraInformationFlag)
{
Skip_S1( 8, "ExtraInformation");
Get_SB ( ExtraInformationFlag, "ExtraInformationFlag");
}
BS_End();
FILLING_BEGIN();
Fill(Stream_Video, 0, Video_Width, Width, 10, true);
Fill(Stream_Video, 0, Video_Height, Height, 10, true);
video_stream_Count=false; //No more need of Video stream
FILLING_END();
}
//---------------------------------------------------------------------------
void File_Flv::video_ScreenVideo(int8u Version)
{
//Parsing
int16u Width, Height;
BS_Begin();
Info_S1( 4, BlockWidth, "BlockWidth"); Param_Info1((BlockWidth+1)*16);
Get_S2 (12, Width, "ImageWidth");
Info_S1( 4, BlockHeight, "BlockHeight"); Param_Info1((BlockHeight+1)*16);
Get_S2 (12, Height, "ImageHeight");
if (Version==2)
{
Skip_S1(6, "Reserved");
Skip_SB( "has IFrameImage");
Skip_SB( "has PaletteInfo");
}
BS_End();
FILLING_BEGIN();
Fill(Stream_Video, 0, Video_Width, Width, 10, true);
Fill(Stream_Video, 0, Video_Height, Height, 10, true);
video_stream_Count=false; //No more need of Video stream
FILLING_END();
}
//---------------------------------------------------------------------------
// From: http://wiki.multimedia.cx/index.php?title=On2_VP6
//
void File_Flv::video_VP6(bool WithAlpha)
{
//Parsing
int8u HorizontalAdjustment, VerticalAdjustment;
bool FrameMode, Marker;
BS_Begin();
Get_S1 ( 4, HorizontalAdjustment, "HorizontalAdjustment");
Get_S1 ( 4, VerticalAdjustment, "VerticalAdjustment");
if (WithAlpha)
Skip_S3(24, "OffsetToAlpha");
Get_SB ( FrameMode, "FrameMode"); Param_Info1(Flv_VP6_FrameMode[FrameMode]);
Skip_S1( 6, "Quantization");
Get_SB ( Marker, "Marker"); Param_Info1(Flv_VP6_Marker[Marker]);
BS_End();
if (FrameMode)
{
if (Marker==1)
Skip_B2( "Offset");
}
else
{
int8u Version, Version2, Width, Height;
BS_Begin();
Get_S1 ( 5, Version, "Version"); Param_Info1(Flv_VP6_Version[Version]);
Get_S1 ( 2, Version2, "Version2"); Param_Info1(Flv_VP6_Version2[Version2]);
Skip_SB( "Interlace");
BS_End();
if (Marker || Version2==0)
Skip_B2( "Offset");
Skip_B1( "MacroBlock_Height");
Skip_B1( "MacroBlock_Width");
Get_B1 (Height, "Height"); Param_Info1(Ztring::ToZtring(Height*16)+__T(" pixels"));
Get_B1 (Width, "Width"); Param_Info1(Ztring::ToZtring(Width*16)+__T(" pixels"));
FILLING_BEGIN();
if (Width && Height)
{
Fill(Stream_Video, 0, Video_Width, Width*16-HorizontalAdjustment, 10, true);
Fill(Stream_Video, 0, Video_Height, Height*16-VerticalAdjustment, 10, true);
}
video_stream_Count=false; //No more need of Video stream
FILLING_END();
}
}
//---------------------------------------------------------------------------
void File_Flv::video_AVC()
{
int8u AVCPacketType;
Get_B1 (AVCPacketType, "AVCPacketType"); Param_Info1(Flv_AVCPacketType(AVCPacketType));
Info_B3(CompositionTime, "CompositionTime"); Param_Info1(Ztring::ToZtring((int32s)(CompositionTime+0xFF000000)));
switch (AVCPacketType)
{
case 0 :
#ifdef MEDIAINFO_AVC_YES
if (Stream[Stream_Video].Parser==NULL)
{
Stream[Stream_Video].Parser=new File_Avc;
Open_Buffer_Init(Stream[Stream_Video].Parser);
((File_Avc*)Stream[Stream_Video].Parser)->MustParse_SPS_PPS=true;
((File_Avc*)Stream[Stream_Video].Parser)->SizedBlocks=true;
((File_Avc*)Stream[Stream_Video].Parser)->MustSynchronize=false;
#if MEDIAINFO_DEMUX
if (Config->Demux_Avc_Transcode_Iso14496_15_to_Iso14496_10_Get())
{
Stream[Stream_Video].Parser->Demux_Level=2; //Container
Stream[Stream_Video].Parser->Demux_UnpacketizeContainer=true;
}
#endif //MEDIAINFO_DEMUX
}
//Parsing
Open_Buffer_Continue(Stream[Stream_Video].Parser);
//Demux
#if MEDIAINFO_DEMUX
switch (Config->Demux_InitData_Get())
{
case 0 : //In demux event
Demux_Level=2; //Container
Demux(Buffer+Buffer_Offset+2, (size_t)(Element_Size-2), ContentType_Header);
break;
case 1 : //In field
{
std::string Data_Raw((const char*)(Buffer+Buffer_Offset+2), (size_t)(Element_Size-2));
std::string Data_Base64(Base64::encode(Data_Raw));
Fill(Stream_Video, StreamPos_Last, "Demux_InitBytes", Data_Base64);
Fill_SetOptions(Stream_Video, StreamPos_Last, "Demux_InitBytes", "N NT");
}
break;
default : ;
}
#endif //MEDIAINFO_DEMUX
#else
Skip_XX(Element_Size-Element_Offset, "AVC Data");
video_stream_Count=false; //Unable to parse it
#endif
break;
case 1 :
#ifdef MEDIAINFO_AVC_YES
if (Stream[Stream_Video].Parser==NULL)
{
//Data before header, this is wrong
video_stream_Count=false;
break;
}
//Parsing
Open_Buffer_Continue(Stream[Stream_Video].Parser);
//Disabling this stream
if (Stream[Stream_Video].Parser->File_GoTo!=(int64u)-1 || Stream[Stream_Video].Parser->Count_Get(Stream_Video)>0 || (Config->ParseSpeed<1.0 && Stream[Stream_Video].PacketCount>=300))
{
Stream[Stream_Video].Parser->Open_Buffer_Unsynch();
video_stream_Count=false;
}
#else
Skip_XX(Element_Size-Element_Offset, "AVC Data");
video_stream_Count=false; //Unable to parse it
#endif
break;
default: Skip_XX(Element_Size-Element_Offset, "Unknown");
video_stream_Count=false; //Unable to parse it
}
}
//---------------------------------------------------------------------------
void File_Flv::video_HEVC()
{
int8u AVCPacketType;
Get_B1 (AVCPacketType, "AVCPacketType"); Param_Info1(Flv_AVCPacketType(AVCPacketType));
Info_B3(CompositionTime, "CompositionTime"); Param_Info1(Ztring::ToZtring((int32s)(CompositionTime+0xFF000000)));
switch (AVCPacketType)
{
case 0 :
#ifdef MEDIAINFO_HEVC_YES
if (Stream[Stream_Video].Parser==NULL)
{
Stream[Stream_Video].Parser=new File_Hevc;
Open_Buffer_Init(Stream[Stream_Video].Parser);
((File_Hevc*)Stream[Stream_Video].Parser)->MustParse_VPS_SPS_PPS=true;
((File_Hevc*)Stream[Stream_Video].Parser)->MustParse_VPS_SPS_PPS_FromFlv=true;
((File_Hevc*)Stream[Stream_Video].Parser)->MustSynchronize=false;
((File_Hevc*)Stream[Stream_Video].Parser)->SizedBlocks=true;
#if MEDIAINFO_DEMUX
if (Config->Demux_Hevc_Transcode_Iso14496_15_to_AnnexB_Get())
{
Stream[Stream_Video].Parser->Demux_Level=2; //Container
Stream[Stream_Video].Parser->Demux_UnpacketizeContainer=true;
}
#endif //MEDIAINFO_DEMUX
}
//Parsing
Open_Buffer_Continue(Stream[Stream_Video].Parser);
//Demux
#if MEDIAINFO_DEMUX
switch (Config->Demux_InitData_Get())
{
case 0 : //In demux event
Demux_Level=2; //Container
Demux(Buffer+Buffer_Offset+2, (size_t)(Element_Size-2), ContentType_Header);
break;
case 1 : //In field
{
std::string Data_Raw((const char*)(Buffer+Buffer_Offset+2), (size_t)(Element_Size-2));
std::string Data_Base64(Base64::encode(Data_Raw));
Fill(Stream_Video, StreamPos_Last, "Demux_InitBytes", Data_Base64);
Fill_SetOptions(Stream_Video, StreamPos_Last, "Demux_InitBytes", "N NT");
}
break;
default : ;
}
#endif //MEDIAINFO_DEMUX
#else
Skip_XX(Element_Size-Element_Offset, "HEVC Data");
video_stream_Count=false; //Unable to parse it
#endif
break;
case 1 :
#ifdef MEDIAINFO_HEVC_YES
if (Stream[Stream_Video].Parser==NULL)
{
//Data before header, this is wrong
video_stream_Count=false;
break;
}
//Parsing
Open_Buffer_Continue(Stream[Stream_Video].Parser);
//Disabling this stream
if (Stream[Stream_Video].Parser->File_GoTo!=(int64u)-1 || Stream[Stream_Video].Parser->Count_Get(Stream_Video)>0 || (Config->ParseSpeed<1.0 && Stream[Stream_Video].PacketCount>=300))
{
Stream[Stream_Video].Parser->Open_Buffer_Unsynch();
video_stream_Count=false;
}
#else
Skip_XX(Element_Size-Element_Offset, "HEVC Data");
video_stream_Count=false; //Unable to parse it
#endif
break;
default: Skip_XX(Element_Size-Element_Offset, "Unknown");
video_stream_Count=false; //Unable to parse it
}
}
//---------------------------------------------------------------------------
void File_Flv::audio()
{
Element_Name("Audio");
Stream[Stream_Audio].PacketCount++;
Element_Info1(Stream[Stream_Audio].PacketCount);
if (Element_Size==0) //Header says that audio is present, but there is only one null packet
{
Element_Info1("Null");
return;
}
//Needed?
if (!audio_stream_Count && Config->ParseSpeed<1.0)
return; //No more need of Audio stream
//Parsing
int8u codec, sampling_rate;
bool is_16bit, is_stereo;
Element_Begin1("Stream header");
BS_Begin();
Get_S1 (4, codec, "codec"); Param_Info1(Flv_Codec_Audio[codec]); Element_Info1(Flv_Codec_Audio[codec]);
Get_S1 (2, sampling_rate, "sampling_rate"); Param_Info1(Ztring::ToZtring(Flv_SamplingRate[sampling_rate])+__T(" Hz"));
Get_SB ( is_16bit, "is_16bit"); Param_Info1(Ztring::ToZtring(Flv_Resolution[is_16bit])+__T(" bits"));
Get_SB ( is_stereo, "is_stereo"); Param_Info1(Ztring::ToZtring(Flv_Channels[is_stereo])+__T(" channel(s)"));
BS_End();
Element_End0();
//Special case
if (codec==5) //Nellymoser 8kHz mono
{
sampling_rate=5; //8000 Hz forced
is_stereo=false; //Mono forced
}
if (codec!=10) // AAC has an header
{
Demux(Buffer+Buffer_Offset+(size_t)(Element_Offset+1), (size_t)(Element_Size-Element_Offset-1), ContentType_MainStream);
}
FILLING_BEGIN();
if (Retrieve(Stream_Audio, 0, Audio_Format).empty())
{
//Filling
if (Count_Get(Stream_Audio)==0)
File__Analyze::Stream_Prepare(Stream_Audio);
Fill(Stream_Audio, 0, Audio_Channel_s_, Flv_Channels[is_stereo], 10, true);
if (codec!=2 && codec!=10 && codec!=14) //MPEG Audio and AAC are not fixed bit depth
Fill(Stream_Audio, 0, Audio_BitDepth, Flv_Resolution[is_16bit], 10, true);
if (sampling_rate<4)
Fill(Stream_Audio, 0, Audio_SamplingRate, Flv_SamplingRate[sampling_rate], 10, true);
Fill(Stream_Audio, 0, Audio_Format, Flv_Format_Audio[codec]);
Fill(Stream_Audio, 0, Audio_Format_Profile, Flv_Format_Profile_Audio[codec]);
Fill(Stream_Audio, 0, Audio_Codec, Flv_Codec_Audio[codec]);
Fill(Stream_Audio, 0, Audio_CodecID, codec);
Fill(Stream_Audio, 0, Audio_CodecID_Hint, Flv_CodecID_Hint_Audio[codec]);
if (codec==1)
{
//ADPCM
Fill(Stream_Audio, 0, Audio_Format_Settings, "ShockWave");
Fill(Stream_Audio, 0, Audio_Format_Settings_Firm, "ShockWave");
Fill(Stream_Audio, 0, Audio_Codec_Settings, "SWF");
Fill(Stream_Audio, 0, Audio_Codec_Settings_Firm, "SWF");
}
MustSynchronize=true; // Now, synchronization test is possible
}
//Parsing audio data
switch (codec)
{
case 2 :
case 14 : audio_MPEG(); break;
case 10 : audio_AAC(); break;
default : Skip_XX(Element_Size-Element_Offset, "Unknown");
audio_stream_Count=false; //No more need of Audio stream
}
FILLING_END();
}
//---------------------------------------------------------------------------
void File_Flv::audio_MPEG()
{
#if defined(MEDIAINFO_MPEGA_YES)
if (Stream[Stream_Audio].Parser==NULL)
{
Stream[Stream_Audio].Parser=new File_Mpega;
Open_Buffer_Init(Stream[Stream_Audio].Parser);
((File_Mpega*)Stream[Stream_Audio].Parser)->FrameIsAlwaysComplete=true;
}
//Parsing
Open_Buffer_Continue(Stream[Stream_Audio].Parser);
//Disabling this stream
if (Stream[Stream_Audio].Parser->File_GoTo!=(int64u)-1 || Stream[Stream_Audio].Parser->Count_Get(Stream_Audio)>0)
{
Stream[Stream_Audio].Parser->Open_Buffer_Unsynch();
audio_stream_Count=false;
}
#endif
}
//---------------------------------------------------------------------------
void File_Flv::audio_AAC()
{
int8u AACPacketType;
Get_B1 (AACPacketType, "AACPacketType"); Param_Info1(Flv_AACPacketType(AACPacketType));
switch (AACPacketType)
{
case 0 :
#if defined(MEDIAINFO_AAC_YES)
if (Stream[Stream_Audio].Parser==NULL)
{
Stream[Stream_Audio].Parser=new File_Aac;
((File_Aac*)Stream[Stream_Audio].Parser)->Mode=File_Aac::Mode_AudioSpecificConfig;
Open_Buffer_Init(Stream[Stream_Audio].Parser);
}
//Parsing
Open_Buffer_Continue(Stream[Stream_Audio].Parser);
//Demux
#if MEDIAINFO_DEMUX
switch (Config->Demux_InitData_Get())
{
case 0 : //In demux event
Demux_Level=2; //Container
Demux(Buffer+Buffer_Offset+2, (size_t)(Element_Size-2), ContentType_Header);
break;
case 1 : //In field
{
std::string Data_Raw((const char*)(Buffer+Buffer_Offset+2), (size_t)(Element_Size-2));
std::string Data_Base64(Base64::encode(Data_Raw));
Fill(Stream_Audio, StreamPos_Last, "Demux_InitBytes", Data_Base64);
Fill_SetOptions(Stream_Audio, StreamPos_Last, "Demux_InitBytes", "N NT");
}
break;
default : ;
}
#endif //MEDIAINFO_DEMUX
#else
Skip_XX(Element_Size-Element_Offset, "AAC Data");
audio_stream_Count=false; //Unable to parse it
#endif
break;
case 1 :
//Parsing
Demux(Buffer+Buffer_Offset+(size_t)Element_Offset, (size_t)(Element_Size-Element_Offset), ContentType_MainStream);
if (Stream[Stream_Audio].Parser)
{
Open_Buffer_Continue(Stream[Stream_Audio].Parser);
Stream[Stream_Audio].Parser->Open_Buffer_Unsynch();
}
else
Skip_XX(Element_Size-Element_Offset, "Decoder config is missing");
audio_stream_Count=false; //No need of more
break;
default: Skip_XX(Element_Size-Element_Offset, "Unknown");
audio_stream_Count=false; //Unable to parse it
}
}
//---------------------------------------------------------------------------
void File_Flv::meta()
{
Element_Name("Meta");
//Parsing
meta_Level=0;
meta_SCRIPTDATAOBJECT();
if (MetaData_NotTrustable)
{
meta_duration=0;
Clear(Stream_Video, 0, Video_StreamSize);
Clear(Stream_Video, 0, Video_BitRate);
Clear(Stream_Video, 0, Video_Bits__Pixel_Frame_);
Clear(Stream_Audio, 0, Audio_StreamSize);
Clear(Stream_Audio, 0, Audio_BitRate);
Clear(Stream_General, 0, General_Duration);
Clear(Stream_General, 0, General_OverallBitRate);
}
}
//---------------------------------------------------------------------------
void File_Flv::meta_SCRIPTDATAOBJECT()
{
//Parsing Value
std::string StringData;
meta_SCRIPTDATAVALUE(StringData);
meta_SCRIPTDATAVALUE(StringData);
}
//---------------------------------------------------------------------------
void File_Flv::meta_SCRIPTDATAVARIABLE()
{
std::string StringData;
int16u StringLength;
Element_Begin0();
Get_B2 (StringLength, "StringLength");
Get_String(StringLength, StringData, "StringData");
Element_Name(StringData.c_str());
//Parsing Value
meta_SCRIPTDATAVALUE(StringData);
Element_End0();
}
//---------------------------------------------------------------------------
void File_Flv::meta_SCRIPTDATAVALUE(const std::string &StringData)
{
std::string StringDataModified(StringData);
if (!StringDataModified.empty() && StringDataModified[0]==__T('_'))
StringDataModified.erase(StringDataModified.begin());
//Parsing
int8u Type;
Get_B1 (Type, "Type"); Param_Info1C((Type<0x12), Flv_TagType[Type]);
switch (Type)
{
case 0x00 : //DOUBLE --> 64 bits Big endian float
{
float64 Value;
Get_BF8(Value, "Value");
if (Value==0)
break;
std::string ToFill;
Ztring ValueS;
stream_t StreamKind=Stream_General;
if (StringDataModified=="width") {ToFill="Width"; StreamKind=Stream_Video; ValueS.From_Number(Value, 0); video_stream_Count=true;} //1 file with FrameRate tag and video stream but no video present tag
else if (StringDataModified=="height") {ToFill="Height"; StreamKind=Stream_Video; ValueS.From_Number(Value, 0); video_stream_Count=true;} //1 file with FrameRate tag and video stream but no video present tag
else if (StringDataModified=="duration") meta_duration=Value*1000;
else if (StringDataModified=="audiodatarate") {ToFill="BitRate"; StreamKind=Stream_Audio; ValueS.From_Number(Value*1000, 0);}
else if (StringDataModified=="framerate") {ToFill="FrameRate"; StreamKind=Stream_Video; ValueS.From_Number(Value, 3); video_stream_FrameRate_Detected=true; video_stream_Count=true;} //1 file with FrameRate tag and video stream but no video present tag
else if (StringDataModified=="videoframerate") {ToFill="FrameRate"; StreamKind=Stream_Video; ValueS.From_Number(Value, 3); video_stream_FrameRate_Detected=true; video_stream_Count=true;} //1 file with FrameRate tag and video stream but no video present tag
else if (StringDataModified=="filesize") {meta_filesize=(int64u)Value;}
else if (StringDataModified=="audiosize") {ToFill="StreamSize"; StreamKind=Stream_Audio; ValueS.From_Number(Value, 0); if (Value>File_Size) MetaData_NotTrustable=true;}
else if (StringDataModified=="videosize") {ToFill="StreamSize"; StreamKind=Stream_Video; ValueS.From_Number(Value, 0); if (Value>File_Size) MetaData_NotTrustable=true; video_stream_Count=true;} //1 file with FrameRate tag and video stream but no video present tag
else if (StringDataModified=="videodatarate") {ToFill="BitRate"; StreamKind=Stream_Video; ValueS.From_Number(Value*1000, 0); video_stream_Count=true;} //1 file with FrameRate tag and video stream but no video present tag
else if (StringDataModified=="videocodecid") {; video_stream_Count=true;} //1 file with FrameRate tag and video stream but no video present tag
else if (StringDataModified=="audiodelay") {ToFill="Delay"; StreamKind=Stream_Audio; if (Value>0) ValueS.From_Number(Value*1000, 0);}
else if (StringDataModified=="audiosamplerate") {ToFill="SamplingRate"; StreamKind=Stream_Audio; if (Value>0) ValueS.From_Number(Value, 0);}
else if (StringDataModified=="audiosamplesize") {ToFill="BitDepth"; StreamKind=Stream_Audio; if (Value>0) ValueS.From_Number(Value, 0);}
else if (StringDataModified=="totalduration") {ToFill="Duration"; StreamKind=Stream_General; ValueS.From_Number(Value*1000, 0);}
else if (StringDataModified=="totaldatarate") {ToFill="OverallBitRate"; StreamKind=Stream_General; ValueS.From_Number(Value*1000, 0);}
else if (StringDataModified=="totalframes") {ToFill="FrameCount"; StreamKind=Stream_Video; ValueS.From_Number(Value, 0);}
else if (StringDataModified=="bytelength") {if (File_Size!=Value) MetaData_NotTrustable=true;}
else if (!(StringDataModified=="datasize"
|| StringDataModified=="lasttimestamp"
|| StringDataModified=="lastkeyframetimestamp"
|| StringDataModified=="lastkeyframelocation"
|| StringDataModified=="canSeekToEnd"
|| StringDataModified=="keyframes_times"
|| StringDataModified=="keyframes_filepositions"
|| StringDataModified=="aacaot"
|| StringDataModified=="audiochannels"
|| StringDataModified=="audiocodecid"
|| StringDataModified=="avclevel"
|| StringDataModified=="avcprofile"
|| StringDataModified=="moovPosition")) {StreamKind=Stream_General; ToFill=StringData; ValueS.From_Number(Value);}
#if MEDIAINFO_TRACE
if (ValueS.empty())
ValueS.From_Number(Value, 0);
Element_Info1(ValueS);
#endif //MEDIAINFO_TRACE
if (StreamKind==Stream_Video && ToFill=="FrameRate")
{
if (Retrieve(Stream_Video, 0, Video_FrameRate).To_float32()<1000 && Value>=1000)
ToFill.clear(); //Such incoherency was found in 1 file (e.g. 30 then 30000)
}
if (!ToFill.empty())
{
Fill(StreamKind, 0, ToFill.c_str(), ValueS, true);
if (ToFill=="FrameRate")
Fill(StreamKind, 0, "FrameRate_Mode", "CFR", Unlimited, true, true);
}
}
break;
case 0x01 : //UI8
{
int8u Value;
Get_B1 (Value, "Value");
std::string ToFill;
if (StringDataModified=="haskeyframes") {}
else if (StringDataModified=="hasKeyframes") {}
else if (StringDataModified=="hasVideo") {}
else if (StringDataModified=="stereo") {}
else if (StringDataModified=="canSeekToEnd") {}
else if (StringDataModified=="hasAudio") {}
else if (StringDataModified=="hasmetadata") {}
else if (StringDataModified=="hasMetadata") {}
else if (StringDataModified=="hasCuePoints") {}
else if (StringDataModified=="canseekontime") {}
else {ToFill=StringData;}
Element_Info1(Value);
Fill(Stream_General, 0, ToFill.c_str(), Value?"Yes":"No", Unlimited, true, true);
}
break;
case 0x02 : //SCRIPTDATASTRING
{
int16u Value_Size;
Get_B2 (Value_Size, "Value_Size");
if (Value_Size)
{
Ztring Value;
Get_UTF8(Value_Size, Value, "Value");
size_t ToFill=(size_t)-1;
std::string ToFillS;
if (StringDataModified=="creator") {ToFill=General_Encoded_Application;}
else if (StringDataModified=="creationdate") {ToFill=General_Encoded_Date; Value.Date_From_String(Value.To_UTF8().c_str());}
else if (StringDataModified=="encoder") {ToFill=General_Encoded_Application;}
else if (StringDataModified=="Encoded_With") {ToFill=General_Encoded_Application;}
else if (StringDataModified=="Encoded_By") {ToFill=General_Encoded_Application;}
else if (StringDataModified=="metadatacreator") {ToFill=General_Tagged_Application;}
else if (StringDataModified=="title") {ToFill=General_Title;}
else if (StringDataModified=="creation_time") {ToFill=General_Encoded_Date; Value.insert(0, __T("UTC "));}
else if (StringDataModified=="sourcedata") {}
else if (StringDataModified=="audiocodecid") {}
else if (StringDataModified=="videocodecid") {}
else if (!(StringDataModified=="major_brand"
|| StringDataModified=="minor_version"
|| StringDataModified=="compatible_brands"))
ToFillS=StringData;
if (Value.find(__T('\r'))!=std::string::npos)
Value.resize(Value.find(__T('\r')));
if (Value.find(__T('\n'))!=std::string::npos)
Value.resize(Value.find(__T('\n')));
Element_Info1(Value);
if (ToFill!=(size_t)-1)
Fill(Stream_General, 0, ToFill, Value, true);
else if (!ToFillS.empty())
Fill(Stream_General, 0, StringData.c_str(), Value, true);
}
}
break;
case 0x03 : //SCRIPTDATAOBJECT[n]
case 0x10 : //Typed object - SCRIPTDATAOBJECT[n]
{
std::string StringData2;
int16u StringLength2;
meta_Level++;
meta_LevelFinished[meta_Level]=false;
while (!meta_LevelFinished[meta_Level])
{
if (Element_Offset>=Element_Size)
break;
Element_Begin0();
Get_B2 (StringLength2, "StringLength2");
Get_String(StringLength2, StringData2, "StringData2");
Element_Name(StringData2.empty()?"EndOfObject":StringData2.c_str());
meta_SCRIPTDATAVALUE(StringData+'_'+StringData2);
Element_End0();
}
meta_Level--;
}
break;
case 0x04 : //SCRIPTDATASTRING defining the MovieClip path
{
int16u Value_Size;
Get_B2 (Value_Size, "Value_Size");
if (Value_Size)
{
Ztring Value;
Get_UTF8(Value_Size, Value, "Value");
if (Value==__T("unknown")) Value.clear();
Element_Info1C((!Value.empty()), Value);
Fill(Stream_General, 0, StringData.c_str(), Value, true);
}
}
break;
case 0x05 : //NULL
case 0x06 : //Undefined - NULL
case 0x0D : //Unsupported - NULL
break;
case 0x07 : //UI16
{
int16u Value;
Get_B2 (Value, "Value");
Element_Info1(Value);
Fill(Stream_General, 0, StringData.c_str(), Value, true);
}
break;
case 0x08 : //SCRIPTDATAVARIABLE[ECMAArrayLength]
{
int32u ECMAArrayLength;
Get_B4 (ECMAArrayLength, "ECMAArrayLength");
Element_Info1(Ztring::ToZtring(ECMAArrayLength)+__T(" elements"));
for (int32u Pos=0; Pos<ECMAArrayLength; Pos++)
{
meta_SCRIPTDATAVARIABLE();
if (meta_LevelFinished[meta_Level])
Pos=ECMAArrayLength; //Finished
}
}
break;
case 0x09 :
Element_Info1("EndOfObject");
meta_LevelFinished[meta_Level]=true;
break;
case 0x0A : //SCRIPTDATAVARIABLE[n]
case 0x0E : //RecordSet - SCRIPTDATAVARIABLE[n]
{
int32u Count;
Get_B4 (Count, "Count");
for (int32u Pos=0; Pos<Count; Pos++)
meta_SCRIPTDATAVALUE(StringData);
}
break;
case 0x0B : //SCRIPTDATADATE
{
float64 Value;
Get_BF8(Value, "Value");
Ztring ValueS;
ValueS.Date_From_Seconds_1970((int32u)(Value/1000));
Param_Info1(ValueS);
Skip_B2( "Local_Offset_Minutes");
std::string ToFill;
if (StringData=="metadatadate") {ToFill="Tagged_Date";}
else {ToFill=StringData;}
Element_Info1(ValueS);
Fill(Stream_General, 0, ToFill.c_str(), ValueS, true);
}
break;
case 0x0C : //SCRIPTDATALONGSTRING
case 0x0F : //XML - SCRIPTDATALONGSTRING
{
int32u Value_Size;
Get_B4 (Value_Size, "Value_Size");
if (Value_Size)
{
Ztring Value;
Get_UTF16B(Value_Size, Value, "Value");
std::string ToFill;
if (StringData=="creator") {ToFill="Encoded_Application";}
else if (StringData=="liveXML") {}
else if (StringData=="metadatacreator") {ToFill="Tagged_Application";}
else if (StringData=="creationdate") {ToFill="Encoded_Date"; Value.Date_From_String(Value.To_UTF8().c_str());}
else {ToFill=StringData;}
Element_Info1(Value);
if (!ToFill.empty())
Fill(Stream_General, 0, ToFill.c_str(), Value, true);
}
}
break;
case 0x11 : //AMF3 data
{
int32u TypeCode;
Get_B4 (TypeCode, "AMF3 type code"); Param_Info1C((TypeCode<0x0D), Flv_Amf3Type[TypeCode]);
switch (TypeCode)
{
case 0x00 : //undefined
case 0x01 : //null
case 0x02 : //boolean-false
case 0x03 : //boolean-true
break;
default : //Not implemented or unknown
Element_Offset=Element_Size;
}
}
break;
default : //Unknown
Element_Offset=Element_Size; //Forcing the end of parsing
}
}
//---------------------------------------------------------------------------
void File_Flv::Rm()
{
Element_Name("Real Media tags");
//Creating the parser
File_Rm MI;
Open_Buffer_Init(&MI);
//Parsing
Open_Buffer_Continue(&MI);
//Filling
File__Analyze::Finish(&MI);
Merge(MI, Stream_General, 0, 0);
}
} //NameSpace
#endif //MEDIAINFO_FLV_YES
↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: video_stream_Count, audio_stream_Count, video_stream_FrameRate_Detected, Time, meta_Level, PreviousTagSize_Add11.
↑ V1020 The function exited without calling the 'BS_End' function. Check lines: 996, 992.
↑ V550 An odd precise comparison. It's probably better to use a comparison with defined precision: fabs(Time) > Epsilon.
↑ V550 An odd precise comparison: Value == 0. It's probably better to use a comparison with defined precision: fabs(A - B) < Epsilon.
↑ V550 An odd precise comparison: File_Size != Value. It's probably better to use a comparison with defined precision: fabs(A - B) > Epsilon.
↑ V601 The 'true' value is implicitly cast to the integer type. Inspect the fifth argument.
↑ V688 The 'Time' local variable possesses the same name as one of the class members, which can result in a confusion.
↑ V807 Decreased performance. Consider creating a pointer to avoid using the 'Stream[Stream_Audio].Parser' expression repeatedly.
↑ V807 Decreased performance. Consider creating a reference to avoid using the 'Stream[StreamKind].Durations' expression repeatedly.
↑ V807 Decreased performance. Consider creating a pointer to avoid using the 'Stream[Stream_Video].Parser' expression repeatedly.
↑ V807 Decreased performance. Consider creating a pointer to avoid using the 'Stream[Stream_Video].Parser' expression repeatedly.
↑ V807 Decreased performance. Consider creating a pointer to avoid using the 'Stream[Stream_Audio].Parser' expression repeatedly.