|
|
|
// ***************************************************************** -*- C++ -*-
|
|
|
|
/* Spec:
|
|
|
|
* https://learn.microsoft.com/fr-fr/windows/win32/directshow/avi-riff-file-reference
|
|
|
|
* https://cdn.hackaday.io/files/274271173436768/avi.pdf
|
|
|
|
* http://abcavi.kibi.ru/docs/odml1.pdf
|
|
|
|
*/
|
|
|
|
// *****************************************************************************
|
|
|
|
|
|
|
|
// included header files
|
|
|
|
#include "riffvideo.hpp"
|
|
|
|
#include "enforce.hpp"
|
|
|
|
#include "error.hpp"
|
|
|
|
#include "futils.hpp"
|
|
|
|
#include "helper_functions.hpp"
|
|
|
|
#include "utils.hpp"
|
|
|
|
|
|
|
|
namespace Exiv2::Internal {
|
|
|
|
|
|
|
|
const std::map<std::string, std::string> infoTags = {{"AGES", "Xmp.video.Rated"},
|
|
|
|
{"CMNT", "Xmp.video.Comment"},
|
|
|
|
{"CODE", "Xmp.video.EncodedBy"},
|
|
|
|
{"COMM", "Xmp.video.Comment"},
|
|
|
|
{"DIRC", "Xmp.video.Director"},
|
|
|
|
{"DISP", "Xmp.audio.SchemeTitle"},
|
|
|
|
{"DTIM", "Xmp.video.DateTimeOriginal"},
|
|
|
|
{"GENR", "Xmp.video.Genre"},
|
|
|
|
{"IARL", "Xmp.video.ArchivalLocation"},
|
|
|
|
{"IART", "Xmp.video.Artist"},
|
|
|
|
{"IAS1", "Xmp.video.Edit1"},
|
|
|
|
{"IAS2", "Xmp.video.Edit2"},
|
|
|
|
{"IAS3", "Xmp.video.Edit3"},
|
|
|
|
{"IAS4", "Xmp.video.Edit4"},
|
|
|
|
{"IAS5", "Xmp.video.Edit5"},
|
|
|
|
{"IAS6", "Xmp.video.Edit6"},
|
|
|
|
{"IAS7", "Xmp.video.Edit7"},
|
|
|
|
{"IAS8", "Xmp.video.Edit8"},
|
|
|
|
{"IAS9", "Xmp.video.Edit9"},
|
|
|
|
{"IBSU", "Xmp.video.BaseURL"},
|
|
|
|
{"ICAS", "Xmp.audio.DefaultStream"},
|
|
|
|
{"ICDS", "Xmp.video.CostumeDesigner"},
|
|
|
|
{"ICMS", "Xmp.video.Commissioned"},
|
|
|
|
{"ICMT", "Xmp.video.Comment"},
|
|
|
|
{"ICNM", "Xmp.video.Cinematographer"},
|
|
|
|
{"ICNT", "Xmp.video.Country"},
|
|
|
|
{"ICOP", "Xmp.video.Copyright"},
|
|
|
|
{"ICRD", "Xmp.video.DateTimeDigitized"},
|
|
|
|
{"ICRP", "Xmp.video.Cropped"},
|
|
|
|
{"IDIM", "Xmp.video.Dimensions"},
|
|
|
|
{"IDPI", "Xmp.video.DotsPerInch"},
|
|
|
|
{"IDST", "Xmp.video.DistributedBy"},
|
|
|
|
{"IEDT", "Xmp.video.EditedBy"},
|
|
|
|
{"IENC", "Xmp.video.EncodedBy"},
|
|
|
|
{"IENG", "Xmp.video.Engineer"},
|
|
|
|
{"IGNR", "Xmp.video.Genre"},
|
|
|
|
{"IKEY", "Xmp.video.PerformerKeywords"},
|
|
|
|
{"ILGT", "Xmp.video.Lightness"},
|
|
|
|
{"ILGU", "Xmp.video.LogoURL"},
|
|
|
|
{"ILIU", "Xmp.video.LogoIconURL"},
|
|
|
|
{"ILNG", "Xmp.video.Language"},
|
|
|
|
{"IMBI", "Xmp.video.InfoBannerImage"},
|
|
|
|
{"IMBU", "Xmp.video.InfoBannerURL"},
|
|
|
|
{"IMED", "Xmp.video.Medium"},
|
|
|
|
{"IMIT", "Xmp.video.InfoText"},
|
|
|
|
{"IMIU", "Xmp.video.InfoURL"},
|
|
|
|
{"IMUS", "Xmp.video.MusicBy"},
|
|
|
|
{"INAM", "Xmp.video.Title"},
|
|
|
|
{"IPDS", "Xmp.video.ProductionDesigner"},
|
|
|
|
{"IPLT", "Xmp.video.NumOfColors"},
|
|
|
|
{"IPRD", "Xmp.video.Product"},
|
|
|
|
{"IPRO", "Xmp.video.ProducedBy"},
|
|
|
|
{"IRIP", "Xmp.video.RippedBy"},
|
|
|
|
{"IRTD", "Xmp.video.Rating"},
|
|
|
|
{"ISBJ", "Xmp.video.Subject"},
|
|
|
|
{"ISFT", "Xmp.video.Software"},
|
|
|
|
{"ISGN", "Xmp.video.SecondaryGenre"},
|
|
|
|
{"ISHP", "Xmp.video.Sharpness"},
|
|
|
|
{"ISRC", "Xmp.video.Source"},
|
|
|
|
{"ISRF", "Xmp.video.SourceForm"},
|
|
|
|
{"ISTD", "Xmp.video.ProductionStudio"},
|
|
|
|
{"ISTR", "Xmp.video.Starring"},
|
|
|
|
{"ITCH", "Xmp.video.Technician"},
|
|
|
|
{"IWMU", "Xmp.video.WatermarkURL"},
|
|
|
|
{"IWRI", "Xmp.video.WrittenBy"},
|
|
|
|
{"LANG", "Xmp.video.Language"},
|
|
|
|
{"LOCA", "Xmp.video.LocationInfo"},
|
|
|
|
{"PRT1", "Xmp.video.Part"},
|
|
|
|
{"PRT2", "Xmp.video.NumOfParts"},
|
|
|
|
{"RATE", "Xmp.video.Rate"},
|
|
|
|
{"STAR", "Xmp.video.Starring"},
|
|
|
|
{"STAT", "Xmp.video.Statistics"},
|
|
|
|
{"TAPE", "Xmp.video.TapeName"},
|
|
|
|
{"TCDO", "Xmp.video.EndTimecode"},
|
|
|
|
{"TCOD", "Xmp.video.StartTimecode"},
|
|
|
|
{"TITL", "Xmp.video.Title"},
|
|
|
|
{"TLEN", "Xmp.video.Length"},
|
|
|
|
{"TORG", "Xmp.video.Organization"},
|
|
|
|
{"TRCK", "Xmp.video.TrackNumber"},
|
|
|
|
{"TURL", "Xmp.video.URL"},
|
|
|
|
{"TVER", "Xmp.video.SoftwareVersion"},
|
|
|
|
{"VMAJ", "Xmp.video.VegasVersionMajor"},
|
|
|
|
{"VMIN", "Xmp.video.VegasVersionMinor"},
|
|
|
|
{"YEAR", "Xmp.video.Year"}};
|
|
|
|
|
|
|
|
const std::map<uint16_t, std::string> audioEncodingValues = {
|
|
|
|
{0x1, "Microsoft PCM"},
|
|
|
|
{0x2, "Microsoft ADPCM"},
|
|
|
|
{0x3, "Microsoft IEEE float"},
|
|
|
|
{0x4, "Compaq VSELP"},
|
|
|
|
{0x5, "IBM CVSD"},
|
|
|
|
{0x6, "Microsoft a-Law"},
|
|
|
|
{0x7, "Microsoft u-Law"},
|
|
|
|
{0x8, "Microsoft DTS"},
|
|
|
|
{0x9, "DRM"},
|
|
|
|
{0xa, "WMA 9 Speech"},
|
|
|
|
{0xb, "Microsoft Windows Media RT Voice"},
|
|
|
|
{0x10, "OKI-ADPCM"},
|
|
|
|
{0x11, "Intel IMA/DVI-ADPCM"},
|
|
|
|
{0x12, "Videologic Mediaspace ADPCM"},
|
|
|
|
{0x13, "Sierra ADPCM"},
|
|
|
|
{0x14, "Antex G.723 ADPCM"},
|
|
|
|
{0x15, "DSP Solutions DIGISTD"},
|
|
|
|
{0x16, "DSP Solutions DIGIFIX"},
|
|
|
|
{0x17, "Dialoic OKI ADPCM"},
|
|
|
|
{0x18, "Media Vision ADPCM"},
|
|
|
|
{0x19, "HP CU"},
|
|
|
|
{0x1a, "HP Dynamic Voice"},
|
|
|
|
{0x20, "Yamaha ADPCM"},
|
|
|
|
{0x21, "SONARC Speech Compression"},
|
|
|
|
{0x22, "DSP Group True Speech"},
|
|
|
|
{0x23, "Echo Speech Corp."},
|
|
|
|
{0x24, "Virtual Music Audiofile AF36"},
|
|
|
|
{0x25, "Audio Processing Tech."},
|
|
|
|
{0x26, "Virtual Music Audiofile AF10"},
|
|
|
|
{0x27, "Aculab Prosody 1612"},
|
|
|
|
{0x28, "Merging Tech. LRC"},
|
|
|
|
{0x30, "Dolby AC2"},
|
|
|
|
{0x31, "Microsoft GSM610"},
|
|
|
|
{0x32, "MSN Audio"},
|
|
|
|
{0x33, "Antex ADPCME"},
|
|
|
|
{0x34, "Control Resources VQLPC"},
|
|
|
|
{0x35, "DSP Solutions DIGIREAL"},
|
|
|
|
{0x36, "DSP Solutions DIGIADPCM"},
|
|
|
|
{0x37, "Control Resources CR10"},
|
|
|
|
{0x38, "Natural MicroSystems VBX ADPCM"},
|
|
|
|
{0x39, "Crystal Semiconductor IMA ADPCM"},
|
|
|
|
{0x3a, "Echo Speech ECHOSC3"},
|
|
|
|
{0x3b, "Rockwell ADPCM"},
|
|
|
|
{0x3c, "Rockwell DIGITALK"},
|
|
|
|
{0x3d, "Xebec Multimedia"},
|
|
|
|
{0x40, "Antex G.721 ADPCM"},
|
|
|
|
{0x41, "Antex G.728 CELP"},
|
|
|
|
{0x42, "Microsoft MSG723"},
|
|
|
|
{0x43, "IBM AVC ADPCM"},
|
|
|
|
{0x45, "ITU-T G.726"},
|
|
|
|
{0x50, "Microsoft MPEG"},
|
|
|
|
{0x51, "RT23 or PAC"},
|
|
|
|
{0x52, "InSoft RT24"},
|
|
|
|
{0x53, "InSoft PAC"},
|
|
|
|
{0x55, "MP3"},
|
|
|
|
{0x59, "Cirrus"},
|
|
|
|
{0x60, "Cirrus Logic"},
|
|
|
|
{0x61, "ESS Tech. PCM"},
|
|
|
|
{0x62, "Voxware Inc."},
|
|
|
|
{0x63, "Canopus ATRAC"},
|
|
|
|
{0x64, "APICOM G.726 ADPCM"},
|
|
|
|
{0x65, "APICOM G.722 ADPCM"},
|
|
|
|
{0x66, "Microsoft DSAT"},
|
|
|
|
{0x67, "Micorsoft DSAT DISPLAY"},
|
|
|
|
{0x69, "Voxware Byte Aligned"},
|
|
|
|
{0x70, "Voxware AC8"},
|
|
|
|
{0x71, "Voxware AC10"},
|
|
|
|
{0x72, "Voxware AC16"},
|
|
|
|
{0x73, "Voxware AC20"},
|
|
|
|
{0x74, "Voxware MetaVoice"},
|
|
|
|
{0x75, "Voxware MetaSound"},
|
|
|
|
{0x76, "Voxware RT29HW"},
|
|
|
|
{0x77, "Voxware VR12"},
|
|
|
|
{0x78, "Voxware VR18"},
|
|
|
|
{0x79, "Voxware TQ40"},
|
|
|
|
{0x7a, "Voxware SC3"},
|
|
|
|
{0x7b, "Voxware SC3"},
|
|
|
|
{0x80, "Soundsoft"},
|
|
|
|
{0x81, "Voxware TQ60"},
|
|
|
|
{0x82, "Microsoft MSRT24"},
|
|
|
|
{0x83, "AT&T G.729A"},
|
|
|
|
{0x84, "Motion Pixels MVI MV12"},
|
|
|
|
{0x85, "DataFusion G.726"},
|
|
|
|
{0x86, "DataFusion GSM610"},
|
|
|
|
{0x88, "Iterated Systems Audio"},
|
|
|
|
{0x89, "Onlive"},
|
|
|
|
{0x8a, "Multitude, Inc. FT SX20"},
|
|
|
|
{0x8b, "Infocom ITS A/S G.721 ADPCM"},
|
|
|
|
{0x8c, "Convedia G729"},
|
|
|
|
{0x8d, "Not specified congruency, Inc."},
|
|
|
|
{0x91, "Siemens SBC24"},
|
|
|
|
{0x92, "Sonic Foundry Dolby AC3 APDIF"},
|
|
|
|
{0x93, "MediaSonic G.723"},
|
|
|
|
{0x94, "Aculab Prosody 8kbps"},
|
|
|
|
{0x97, "ZyXEL ADPCM"},
|
|
|
|
{0x98, "Philips LPCBB"},
|
|
|
|
{0x99, "Studer Professional Audio Packed"},
|
|
|
|
{0xa0, "Malden PhonyTalk"},
|
|
|
|
{0xa1, "Racal Recorder GSM"},
|
|
|
|
{0xa2, "Racal Recorder G720.a"},
|
|
|
|
{0xa3, "Racal G723.1"},
|
|
|
|
{0xa4, "Racal Tetra ACELP"},
|
|
|
|
{0xb0, "NEC AAC NEC Corporation"},
|
|
|
|
{0xff, "AAC"},
|
|
|
|
{0x100, "Rhetorex ADPCM"},
|
|
|
|
{0x101, "IBM u-Law"},
|
|
|
|
{0x102, "IBM a-Law"},
|
|
|
|
{0x103, "IBM ADPCM"},
|
|
|
|
{0x111, "Vivo G.723"},
|
|
|
|
{0x112, "Vivo Siren"},
|
|
|
|
{0x120, "Philips Speech Processing CELP"},
|
|
|
|
{0x121, "Philips Speech Processing GRUNDIG"},
|
|
|
|
{0x123, "Digital G.723"},
|
|
|
|
{0x125, "Sanyo LD ADPCM"},
|
|
|
|
{0x130, "Sipro Lab ACEPLNET"},
|
|
|
|
{0x131, "Sipro Lab ACELP4800"},
|
|
|
|
{0x132, "Sipro Lab ACELP8V3"},
|
|
|
|
{0x133, "Sipro Lab G.729"},
|
|
|
|
{0x134, "Sipro Lab G.729A"},
|
|
|
|
{0x135, "Sipro Lab Kelvin"},
|
|
|
|
{0x136, "VoiceAge AMR"},
|
|
|
|
{0x140, "Dictaphone G.726 ADPCM"},
|
|
|
|
{0x150, "Qualcomm PureVoice"},
|
|
|
|
{0x151, "Qualcomm HalfRate"},
|
|
|
|
{0x155, "Ring Zero Systems TUBGSM"},
|
|
|
|
{0x160, "Microsoft Audio1"},
|
|
|
|
{0x161, "Windows Media Audio V2 V7 V8 V9 / DivX audio (WMA) / Alex AC3 Audio"},
|
|
|
|
{0x162, "Windows Media Audio Professional V9"},
|
|
|
|
{0x163, "Windows Media Audio Lossless V9"},
|
|
|
|
{0x164, "WMA Pro over S/PDIF"},
|
|
|
|
{0x170, "UNISYS NAP ADPCM"},
|
|
|
|
{0x171, "UNISYS NAP ULAW"},
|
|
|
|
{0x172, "UNISYS NAP ALAW"},
|
|
|
|
{0x173, "UNISYS NAP 16K"},
|
|
|
|
{0x174, "MM SYCOM ACM SYC008 SyCom Technologies"},
|
|
|
|
{0x175, "MM SYCOM ACM SYC701 G726L SyCom Technologies"},
|
|
|
|
{0x176, "MM SYCOM ACM SYC701 CELP54 SyCom Technologies"},
|
|
|
|
{0x177, "MM SYCOM ACM SYC701 CELP68 SyCom Technologies"},
|
|
|
|
{0x178, "Knowledge Adventure ADPCM"},
|
|
|
|
{0x180, "Fraunhofer IIS MPEG2AAC"},
|
|
|
|
{0x190, "Digital Theater Systems DTS DS"},
|
|
|
|
{0x200, "Creative Labs ADPCM"},
|
|
|
|
{0x202, "Creative Labs FASTSPEECH8"},
|
|
|
|
{0x203, "Creative Labs FASTSPEECH10"},
|
|
|
|
{0x210, "UHER ADPCM"},
|
|
|
|
{0x215, "Ulead DV ACM"},
|
|
|
|
{0x216, "Ulead DV ACM"},
|
|
|
|
{0x220, "Quarterdeck Corp."},
|
|
|
|
{0x230, "I-Link VC"},
|
|
|
|
{0x240, "Aureal Semiconductor Raw Sport"},
|
|
|
|
{0x241, "ESST AC3"},
|
|
|
|
{0x250, "Interactive Products HSX"},
|
|
|
|
{0x251, "Interactive Products RPELP"},
|
|
|
|
{0x260, "Consistent CS2"},
|
|
|
|
{0x270, "Sony SCX"},
|
|
|
|
{0x271, "Sony SCY"},
|
|
|
|
{0x272, "Sony ATRAC3"},
|
|
|
|
{0x273, "Sony SPC"},
|
|
|
|
{0x280, "TELUM Telum Inc."},
|
|
|
|
{0x281, "TELUMIA Telum Inc."},
|
|
|
|
{0x285, "Norcom Voice Systems ADPCM"},
|
|
|
|
{0x300, "Fujitsu FM TOWNS SND"},
|
|
|
|
{0x301, "Fujitsu (not specified)"},
|
|
|
|
{0x302, "Fujitsu (not specified)"},
|
|
|
|
{0x303, "Fujitsu (not specified)"},
|
|
|
|
{0x304, "Fujitsu (not specified)"},
|
|
|
|
{0x305, "Fujitsu (not specified)"},
|
|
|
|
{0x306, "Fujitsu (not specified)"},
|
|
|
|
{0x307, "Fujitsu (not specified)"},
|
|
|
|
{0x308, "Fujitsu (not specified)"},
|
|
|
|
{0x350, "Micronas Semiconductors, Inc. Development"},
|
|
|
|
{0x351, "Micronas Semiconductors, Inc. CELP833"},
|
|
|
|
{0x400, "Brooktree Digital"},
|
|
|
|
{0x401, "Intel Music Coder (IMC)"},
|
|
|
|
{0x402, "Ligos Indeo Audio"},
|
|
|
|
{0x450, "QDesign Music"},
|
|
|
|
{0x500, "On2 VP7 On2 Technologies"},
|
|
|
|
{0x501, "On2 VP6 On2 Technologies"},
|
|
|
|
{0x680, "AT&T VME VMPCM"},
|
|
|
|
{0x681, "AT&T TCP"},
|
|
|
|
{0x700, "YMPEG Alpha (dummy for MPEG-2 compressor)"},
|
|
|
|
{0x8ae, "ClearJump LiteWave (lossless)"},
|
|
|
|
{0x1000, "Olivetti GSM"},
|
|
|
|
{0x1001, "Olivetti ADPCM"},
|
|
|
|
{0x1002, "Olivetti CELP"},
|
|
|
|
{0x1003, "Olivetti SBC"},
|
|
|
|
{0x1004, "Olivetti OPR"},
|
|
|
|
{0x1100, "Lernout & Hauspie"},
|
|
|
|
{0x1101, "Lernout & Hauspie CELP codec"},
|
|
|
|
{0x1102, "Lernout & Hauspie SBC codec"},
|
|
|
|
{0x1103, "Lernout & Hauspie SBC codec"},
|
|
|
|
{0x1104, "Lernout & Hauspie SBC codec"},
|
|
|
|
{0x1400, "Norris Comm. Inc."},
|
|
|
|
{0x1401, "ISIAudio"},
|
|
|
|
{0x1500, "AT&T Soundspace Music Compression"},
|
|
|
|
{0x181c, "VoxWare RT24 speech codec"},
|
|
|
|
{0x181e, "Lucent elemedia AX24000P Music codec"},
|
|
|
|
{0x1971, "Sonic Foundry LOSSLESS"},
|
|
|
|
{0x1979, "Innings Telecom Inc. ADPCM"},
|
|
|
|
{0x1c07, "Lucent SX8300P speech codec"},
|
|
|
|
{0x1c0c, "Lucent SX5363S G.723 compliant codec"},
|
|
|
|
{0x1f03, "CUseeMe DigiTalk (ex-Rocwell)"},
|
|
|
|
{0x1fc4, "NCT Soft ALF2CD ACM"},
|
|
|
|
{0x2000, "FAST Multimedia DVM"},
|
|
|
|
{0x2001, "Dolby DTS (Digital Theater System)"},
|
|
|
|
{0x2002, "RealAudio 1 / 2 14.4"},
|
|
|
|
{0x2003, "RealAudio 1 / 2 28.8"},
|
|
|
|
{0x2004, "RealAudio G2 / 8 Cook (low bitrate)"},
|
|
|
|
{0x2005, "RealAudio 3 / 4 / 5 Music (DNET)"},
|
|
|
|
{0x2006, "RealAudio 10 AAC (RAAC)"},
|
|
|
|
{0x2007, "RealAudio 10 AAC+ (RACP)"},
|
|
|
|
{0x2500, "Reserved range to 0x2600 Microsoft"},
|
|
|
|
{0x3313, "makeAVIS (ffvfw fake AVI sound from AviSynth scripts)"},
|
|
|
|
{0x4143, "Divio MPEG-4 AAC audio"},
|
|
|
|
{0x4201, "Nokia adaptive multirate"},
|
|
|
|
{0x4243, "Divio G726 Divio, Inc."},
|
|
|
|
{0x434c, "LEAD Speech"},
|
|
|
|
{0x564c, "LEAD Vorbis"},
|
|
|
|
{0x5756, "WavPack Audio"},
|
|
|
|
{0x674f, "Ogg Vorbis (mode 1)"},
|
|
|
|
{0x6750, "Ogg Vorbis (mode 2)"},
|
|
|
|
{0x6751, "Ogg Vorbis (mode 3)"},
|
|
|
|
{0x676f, "Ogg Vorbis (mode 1+)"},
|
|
|
|
{0x6770, "Ogg Vorbis (mode 2+)"},
|
|
|
|
{0x6771, "Ogg Vorbis (mode 3+)"},
|
|
|
|
{0x7000, "3COM NBX 3Com Corporation"},
|
|
|
|
{0x706d, "FAAD AAC"},
|
|
|
|
{0x7a21, "GSM-AMR (CBR, no SID)"},
|
|
|
|
{0x7a22, "GSM-AMR (VBR, including SID)"},
|
|
|
|
{0xa100, "Comverse Infosys Ltd. G723 1"},
|
|
|
|
{0xa101, "Comverse Infosys Ltd. AVQSBC"},
|
|
|
|
{0xa102, "Comverse Infosys Ltd. OLDSBC"},
|
|
|
|
{0xa103, "Symbol Technologies G729A"},
|
|
|
|
{0xa104, "VoiceAge AMR WB VoiceAge Corporation"},
|
|
|
|
{0xa105, "Ingenient Technologies Inc. G726"},
|
|
|
|
{0xa106, "ISO/MPEG-4 advanced audio Coding"},
|
|
|
|
{0xa107, "Encore Software Ltd G726"},
|
|
|
|
{0xa109, "Speex ACM Codec xiph.org"},
|
|
|
|
{0xdfac, "DebugMode SonicFoundry Vegas FrameServer ACM Codec"},
|
|
|
|
{0xe708, "Unknown -"},
|
|
|
|
{0xf1ac, "Free Lossless Audio Codec FLAC"},
|
|
|
|
{0xfffe, "Extensible"},
|
|
|
|
{0xffff, "Development"}};
|
|
|
|
|
|
|
|
} // namespace Exiv2::Internal
|
|
|
|
// *****************************************************************************
|
|
|
|
// class member definitions
|
|
|
|
|
|
|
|
namespace Exiv2 {
|
|
|
|
|
|
|
|
enum streamTypeInfo { Audio = 1, MIDI, Text, Video };
|
|
|
|
|
|
|
|
RiffVideo::RiffVideo(BasicIo::UniquePtr io) : Image(ImageType::riff, mdNone, std::move(io)) {
|
|
|
|
} // RiffVideo::RiffVideo
|
|
|
|
|
|
|
|
std::string RiffVideo::mimeType() const {
|
|
|
|
return "video/riff";
|
|
|
|
}
|
|
|
|
|
|
|
|
void RiffVideo::writeMetadata() {
|
|
|
|
} // RiffVideo::writeMetadata
|
|
|
|
|
|
|
|
void RiffVideo::readMetadata() {
|
|
|
|
if (io_->open() != 0)
|
|
|
|
throw Error(ErrorCode::kerDataSourceOpenFailed, io_->path(), strError());
|
|
|
|
|
|
|
|
// Ensure that this is the correct image type
|
|
|
|
if (!isRiffType(*io_, false)) {
|
|
|
|
if (io_->error() || io_->eof())
|
|
|
|
throw Error(ErrorCode::kerFailedToReadImageData);
|
|
|
|
throw Error(ErrorCode::kerNotAnImage, "RIFF");
|
|
|
|
}
|
|
|
|
|
|
|
|
IoCloser closer(*io_);
|
|
|
|
clearMetadata();
|
|
|
|
|
|
|
|
xmpData_["Xmp.video.FileSize"] = io_->size();
|
|
|
|
xmpData_["Xmp.video.MimeType"] = mimeType();
|
|
|
|
|
|
|
|
HeaderReader header(io_);
|
|
|
|
xmpData_["Xmp.video.Container"] = header.getId();
|
|
|
|
|
|
|
|
xmpData_["Xmp.video.FileType"] = readStringTag(io_);
|
|
|
|
|
|
|
|
decodeBlocks();
|
|
|
|
} // RiffVideo::readMetadata
|
|
|
|
|
|
|
|
RiffVideo::HeaderReader::HeaderReader(BasicIo::UniquePtr& io) {
|
|
|
|
Internal::enforce(io->size() > io->tell() + DWORD + DWORD, Exiv2::ErrorCode::kerCorruptedMetadata);
|
|
|
|
id_ = readStringTag(io);
|
|
|
|
size_ = readDWORDTag(io);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool RiffVideo::equal(const std::string& str1, const std::string& str2) {
|
|
|
|
if (str1.size() != str2.size())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return Internal::upper(str1) == str2;
|
|
|
|
}
|
|
|
|
|
|
|
|
void RiffVideo::readList(HeaderReader& header_) {
|
|
|
|
std::string chunk_type = readStringTag(io_);
|
|
|
|
|
|
|
|
#ifdef EXIV2_DEBUG_MESSAGES
|
|
|
|
EXV_INFO << "-> Reading list : id= " << header_.getId() << " type= " << chunk_type << " size= " << header_.getSize()
|
|
|
|
<< "(" << io_->tell() << "/" << io_->size() << ")" << std::endl;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (equal(chunk_type, CHUNK_ID_INFO))
|
|
|
|
readInfoListChunk(header_.getSize());
|
|
|
|
else if (equal(chunk_type, CHUNK_ID_MOVI)) {
|
|
|
|
readMoviList(header_.getSize());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void RiffVideo::readChunk(HeaderReader& header_) {
|
|
|
|
#ifdef EXIV2_DEBUG_MESSAGES
|
|
|
|
if (header_.getSize())
|
|
|
|
EXV_INFO << "--> Reading Chunk : [" << header_.getId() << "] size= " << header_.getSize() << "(" << io_->tell()
|
|
|
|
<< "/" << io_->size() << ")" << std::endl;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (equal(header_.getId(), CHUNK_ID_AVIH))
|
|
|
|
readAviHeader();
|
|
|
|
else if (equal(header_.getId(), CHUNK_ID_STRH))
|
|
|
|
readStreamHeader();
|
|
|
|
else if (equal(header_.getId(), CHUNK_ID_STRF))
|
|
|
|
readStreamFormat(header_.getSize());
|
|
|
|
else if (equal(header_.getId(), CHUNK_ID_FMT)) {
|
|
|
|
streamType_ = Audio;
|
|
|
|
readStreamFormat(header_.getSize());
|
|
|
|
} else if (equal(header_.getId(), CHUNK_ID_STRD))
|
|
|
|
readStreamData(header_.getSize());
|
|
|
|
else if (equal(header_.getId(), CHUNK_ID_STRN))
|
|
|
|
StreamName(header_.getSize());
|
|
|
|
else if (equal(header_.getId(), CHUNK_ID_VPRP))
|
|
|
|
readVPRPChunk(header_.getSize());
|
|
|
|
else if (equal(header_.getId(), CHUNK_ID_IDX1))
|
|
|
|
readIndexChunk(header_.getSize());
|
|
|
|
else if (equal(header_.getId(), CHUNK_ID_DATA))
|
|
|
|
readDataChunk(header_.getSize());
|
|
|
|
else if (equal(header_.getId(), CHUNK_ID_JUNK))
|
|
|
|
readJunk(header_.getSize());
|
|
|
|
else {
|
|
|
|
#ifdef EXIV2_DEBUG_MESSAGES
|
|
|
|
if (header_.getSize())
|
|
|
|
EXV_WARNING << "--> Ignoring Chunk : " << header_.getId() << "] size= " << header_.getSize() << "(" << io_->tell()
|
|
|
|
<< "/" << io_->size() << ")" << std::endl;
|
|
|
|
#endif
|
|
|
|
io_->seekOrThrow(io_->tell() + header_.getSize(), BasicIo::beg, ErrorCode::kerFailedToReadImageData);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void RiffVideo::decodeBlocks() {
|
|
|
|
HeaderReader header(io_);
|
|
|
|
if (equal(header.getId(), CHUNK_ID_LIST)) {
|
|
|
|
readList(header);
|
|
|
|
} else {
|
|
|
|
readChunk(header);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!io_->eof() && io_->tell() < io_->size()) {
|
|
|
|
decodeBlocks();
|
|
|
|
}
|
|
|
|
|
|
|
|
} // RiffVideo::decodeBlock
|
|
|
|
|
|
|
|
void RiffVideo::readAviHeader() {
|
|
|
|
#ifdef EXIV2_DEBUG_MESSAGES
|
|
|
|
EXV_INFO << "--> dwMicroSecPerFrame = " << readDWORDTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> dwMaxBytesPerSec = " << readDWORDTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> dwPaddingGranularity = " << readDWORDTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> dwFlags = " << readDWORDTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> dwTotalFrames = " << readDWORDTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> dwInitialFrames = " << readDWORDTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> dwStreams = " << readDWORDTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> dwSuggestedBufferSize = " << readDWORDTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> dwWidth = " << readDWORDTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> dwHeight = " << readDWORDTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> dwReserved1 = " << readDWORDTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> dwReserved2 = " << readDWORDTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> dwReserved3 = " << readDWORDTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> dwReserved4 = " << readDWORDTag(io_) << std::endl;
|
|
|
|
if (LogMsg::info >= LogMsg::level() && LogMsg::handler())
|
|
|
|
io_->seekOrThrow(io_->tell() - DWORD * 14, BasicIo::beg, ErrorCode::kerFailedToReadImageData);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
uint32_t TimeBetweenFrames = readDWORDTag(io_);
|
|
|
|
xmpData_["Xmp.video.MicroSecPerFrame"] = TimeBetweenFrames;
|
|
|
|
double frame_rate = 1000000. / TimeBetweenFrames;
|
|
|
|
|
|
|
|
xmpData_["Xmp.video.MaxDataRate"] = readDWORDTag(io_); // MaximumDataRate
|
|
|
|
|
|
|
|
io_->seekOrThrow(io_->tell() + DWORD * 2, BasicIo::beg,
|
|
|
|
ErrorCode::kerFailedToReadImageData); // ignore PaddingGranularity and Flags
|
|
|
|
|
|
|
|
uint32_t frame_count = readDWORDTag(io_); // TotalNumberOfFrames
|
|
|
|
xmpData_["Xmp.video.FrameCount"] = frame_count;
|
|
|
|
|
|
|
|
io_->seekOrThrow(io_->tell() + DWORD, BasicIo::beg,
|
|
|
|
ErrorCode::kerFailedToReadImageData); // ignore NumberOfInitialFrames
|
|
|
|
|
|
|
|
xmpData_["Xmp.audio.ChannelType"] = getStreamType(readDWORDTag(io_)); // NumberOfStreams
|
|
|
|
|
|
|
|
xmpData_["Xmp.video.StreamCount"] = readDWORDTag(io_); // SuggestedBufferSize
|
|
|
|
|
|
|
|
uint32_t width = readDWORDTag(io_);
|
|
|
|
xmpData_["Xmp.video.Width"] = width;
|
|
|
|
|
|
|
|
uint32_t height = readDWORDTag(io_);
|
|
|
|
xmpData_["Xmp.video.Height"] = height;
|
|
|
|
|
|
|
|
io_->seekOrThrow(io_->tell() + DWORD * 4, BasicIo::beg,
|
|
|
|
ErrorCode::kerFailedToReadImageData); // TimeScale, DataRate, StartTime, DataLength
|
|
|
|
|
|
|
|
xmpData_["Xmp.video.AspectRatio"] = getAspectRatio(width, height);
|
|
|
|
|
|
|
|
fillDuration(frame_rate, frame_count);
|
|
|
|
}
|
|
|
|
|
|
|
|
void RiffVideo::readStreamHeader() {
|
|
|
|
std::string stream = readStringTag(io_);
|
|
|
|
streamType_ = (equal(stream, "VIDS")) ? Video : Audio;
|
|
|
|
|
|
|
|
#ifdef EXIV2_DEBUG_MESSAGES
|
|
|
|
EXV_INFO << "--> fccType = " << stream << std::endl;
|
|
|
|
EXV_INFO << "--> fccHandler = " << readStringTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> dwFlags = " << readDWORDTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> wPriority = " << readWORDTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> wLanguage = " << readWORDTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> dwInitialFrames = " << readDWORDTag(io_) << std::endl; // 20
|
|
|
|
EXV_INFO << "--> dwScale = " << readDWORDTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> dwRate = " << readDWORDTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> dwStart = " << readDWORDTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> dwLength = " << readDWORDTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> dwSuggestedBufferSize = " << readDWORDTag(io_) << std::endl; // 40
|
|
|
|
EXV_INFO << "--> dwSampleSize = " << readDWORDTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> Left = " << readWORDTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> top = " << readWORDTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> right = " << readWORDTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> bottom = " << readWORDTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> XXXXXX = " << readDWORDTag(io_) << std::endl; // 56
|
|
|
|
if (LogMsg::info >= LogMsg::level() && LogMsg::handler())
|
|
|
|
io_->seekOrThrow(io_->tell() - DWORD * 13, BasicIo::beg, ErrorCode::kerFailedToReadImageData);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
xmpData_["Xmp.video.Codec"] = readStringTag(io_); // DataHandler
|
|
|
|
|
|
|
|
io_->seekOrThrow(io_->tell() + DWORD * 2 + WORD * 2, BasicIo::beg,
|
|
|
|
ErrorCode::kerFailedToReadImageData); // dwFlags, wPriority, wLanguage, dwInitialFrames
|
|
|
|
|
|
|
|
uint32_t divisor = readDWORDTag(io_); // TimeScale
|
|
|
|
|
|
|
|
if (divisor) {
|
|
|
|
double rate = static_cast<double>(readDWORDTag(io_) / divisor);
|
|
|
|
xmpData_[(streamType_ == Video) ? "Xmp.video.FrameRate" : "Xmp.audio.SampleRate"] = rate;
|
|
|
|
}
|
|
|
|
io_->seekOrThrow(io_->tell() + DWORD, BasicIo::beg, ErrorCode::kerFailedToReadImageData); // dwStart
|
|
|
|
|
|
|
|
if (divisor) {
|
|
|
|
double frame_count = static_cast<double>(readDWORDTag(io_) / divisor); // DataLength
|
|
|
|
xmpData_[(streamType_ == Video) ? "Xmp.video.FrameCount" : "Xmp.audio.FrameCount"] = frame_count;
|
|
|
|
}
|
|
|
|
|
|
|
|
io_->seekOrThrow(io_->tell() + DWORD, BasicIo::beg, ErrorCode::kerFailedToReadImageData); // dwSuggestedBufferSize
|
|
|
|
|
|
|
|
xmpData_[(streamType_ == Video) ? "Xmp.video.VideoQuality" : "Xmp.video.StreamQuality"] = readDWORDTag(io_);
|
|
|
|
|
|
|
|
xmpData_[(streamType_ == Video) ? "Xmp.video.VideoSampleSize" : "Xmp.video.StreamSampleSize"] = readDWORDTag(io_);
|
|
|
|
io_->seekOrThrow(io_->tell() + DWORD * 2, BasicIo::beg, ErrorCode::kerFailedToReadImageData);
|
|
|
|
}
|
|
|
|
|
|
|
|
void RiffVideo::readStreamFormat(uint64_t size_) {
|
|
|
|
// The structure of the strf chunk depends on the media type. Video streams use the BITMAPINFOHEADER structure,
|
|
|
|
// whereas audio streams use the WAVEFORMATEX structure.
|
|
|
|
|
|
|
|
#ifdef EXIV2_DEBUG_MESSAGES
|
|
|
|
if (streamType_ == Audio) {
|
|
|
|
EXV_INFO << "--> wFormatTag = " << readWORDTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> nChannels = " << readWORDTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> nSamplesPerSec = " << readDWORDTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> nAvgBytesPerSec = " << readDWORDTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> nBlockAlign = " << readWORDTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> wBitsPerSample = " << readWORDTag(io_) << std::endl;
|
|
|
|
if (LogMsg::info >= LogMsg::level() && LogMsg::handler())
|
|
|
|
io_->seekOrThrow(io_->tell() - DWORD * 4, BasicIo::beg, ErrorCode::kerFailedToReadImageData);
|
|
|
|
} else if (streamType_ == Video) {
|
|
|
|
EXV_INFO << "--> biSize = " << readDWORDTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> biWidth = " << readDWORDTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> biHeight = " << readDWORDTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> biPlanes = " << readWORDTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> biBitCount = " << readWORDTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> biCompression = " << readDWORDTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> biSizeImage = " << readDWORDTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> biXPelsPerMeter = " << readDWORDTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> biYPelsPerMeter = " << readDWORDTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> biClrUsed = " << readDWORDTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> biClrImportant = " << readDWORDTag(io_) << std::endl;
|
|
|
|
if (LogMsg::info >= LogMsg::level() && LogMsg::handler())
|
|
|
|
io_->seekOrThrow(io_->tell() - DWORD * 10, BasicIo::beg, ErrorCode::kerFailedToReadImageData);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (streamType_ == Video) {
|
|
|
|
io_->seekOrThrow(io_->tell() + DWORD * 3, BasicIo::beg,
|
|
|
|
ErrorCode::kerFailedToReadImageData); // ignore biSize, biWidth, biHeight
|
|
|
|
xmpData_["Xmp.video.Planes"] = readWORDTag(io_);
|
|
|
|
xmpData_["Xmp.video.PixelDepth"] = readWORDTag(io_);
|
|
|
|
xmpData_["Xmp.video.Compressor"] = readStringTag(io_);
|
|
|
|
xmpData_["Xmp.video.ImageLength"] = readDWORDTag(io_);
|
|
|
|
xmpData_["Xmp.video.PixelPerMeterX"] = readQWORDTag(io_);
|
|
|
|
xmpData_["Xmp.video.PixelPerMeterY"] = readQWORDTag(io_);
|
|
|
|
uint32_t NumOfColours = readDWORDTag(io_);
|
|
|
|
if (NumOfColours == 0)
|
|
|
|
xmpData_["Xmp.video.NumOfColours"] = "Unspecified";
|
|
|
|
else
|
|
|
|
xmpData_["Xmp.video.NumOfColours"] = NumOfColours;
|
|
|
|
uint32_t NumIfImpColours = readDWORDTag(io_);
|
|
|
|
if (NumIfImpColours == 0)
|
|
|
|
xmpData_["Xmp.video.NumIfImpColours"] = "All";
|
|
|
|
else
|
|
|
|
xmpData_["Xmp.video.NumIfImpColours"] = NumIfImpColours;
|
|
|
|
} else if (streamType_ == Audio) {
|
|
|
|
uint16_t format_tag = readWORDTag(io_);
|
|
|
|
auto it = Internal::audioEncodingValues.find(format_tag);
|
|
|
|
if (it != Internal::audioEncodingValues.end()) {
|
|
|
|
xmpData_["Xmp.audio.Compressor"] = it->second;
|
|
|
|
} else {
|
|
|
|
xmpData_["Xmp.audio.Compressor"] = format_tag;
|
|
|
|
}
|
|
|
|
|
|
|
|
xmpData_["Xmp.audio.ChannelType"] = getStreamType(readDWORDTag(io_));
|
|
|
|
xmpData_["Xmp.audio.SampleRate"] = readDWORDTag(io_); // nSamplesPerSec
|
|
|
|
io_->seekOrThrow(io_->tell() + DWORD, BasicIo::beg, ErrorCode::kerFailedToReadImageData); // nAvgBytesPerSec
|
|
|
|
xmpData_["Xmp.audio.SampleType"] = readDWORDTag(io_); // nBlockAlign
|
|
|
|
xmpData_["Xmp.audio.BitsPerSample"] = readDWORDTag(io_); // wBitsPerSample
|
|
|
|
if (xmpData_["Xmp.video.FileType"].toString() == "AVI ")
|
|
|
|
io_->seekOrThrow(io_->tell() + DWORD, BasicIo::beg, ErrorCode::kerFailedToReadImageData); // cbSize
|
|
|
|
} else {
|
|
|
|
io_->seekOrThrow(io_->tell() + size_, BasicIo::beg, ErrorCode::kerFailedToReadImageData);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void RiffVideo::readStreamData(uint64_t size_) {
|
|
|
|
io_->seekOrThrow(io_->tell() + size_, BasicIo::beg, ErrorCode::kerFailedToReadImageData);
|
|
|
|
}
|
|
|
|
|
|
|
|
void RiffVideo::StreamName(uint64_t size_) {
|
|
|
|
// This element contains a name for the stream. That stream name should only use plain ASCII, especially not UTF-8.
|
|
|
|
io_->seekOrThrow(io_->tell() + size_, BasicIo::beg, ErrorCode::kerFailedToReadImageData);
|
|
|
|
}
|
|
|
|
|
|
|
|
void RiffVideo::readInfoListChunk(uint64_t size_) {
|
|
|
|
uint64_t current_size = DWORD;
|
|
|
|
while (current_size < size_) {
|
|
|
|
std::string type = readStringTag(io_);
|
|
|
|
size_t size = readDWORDTag(io_);
|
|
|
|
std::string content = readStringTag(io_, size);
|
|
|
|
auto it = Internal::infoTags.find(type);
|
|
|
|
if (it != Internal::infoTags.end()) {
|
|
|
|
xmpData_[it->second] = content;
|
|
|
|
}
|
|
|
|
current_size += DWORD * 2 + size;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void RiffVideo::readMoviList(uint64_t size_) {
|
|
|
|
io_->seekOrThrow(io_->tell() + size_ - DWORD, BasicIo::beg, ErrorCode::kerFailedToReadImageData);
|
|
|
|
}
|
|
|
|
|
|
|
|
void RiffVideo::readVPRPChunk(uint64_t size_) {
|
|
|
|
#ifdef EXIV2_DEBUG_MESSAGES
|
|
|
|
EXV_INFO << "--> VideoFormatToken = " << readDWORDTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> VideoStandard = " << readDWORDTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> VerticalRefreshRate = " << readDWORDTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> HTotalInT = " << readDWORDTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> VTotalInLines = " << readDWORDTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> FrameAspectRatio Height = " << readWORDTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> FrameAspectRatio Width = " << readWORDTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> FrameWidthInPixels = " << readDWORDTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> FrameHeightInLines = " << readDWORDTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> CompressedBMHeight = " << readDWORDTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> FieldPerFrame = " << readDWORDTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> CompressedBMWidth = " << readDWORDTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> ValidBMHeight = " << readDWORDTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> ValidBMWidth = " << readDWORDTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> ValidBMXOffset = " << readDWORDTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> ValidBMYOffset = " << readDWORDTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> VideoXOffsetInT = " << readDWORDTag(io_) << std::endl;
|
|
|
|
EXV_INFO << "--> VideoYValidStartLine = " << readDWORDTag(io_) << std::endl;
|
|
|
|
if (LogMsg::info >= LogMsg::level() && LogMsg::handler())
|
|
|
|
io_->seekOrThrow(io_->tell() - DWORD * 17, BasicIo::beg, ErrorCode::kerFailedToReadImageData);
|
|
|
|
#endif
|
|
|
|
io_->seekOrThrow(io_->tell() + size_, BasicIo::beg, ErrorCode::kerFailedToReadImageData);
|
|
|
|
}
|
|
|
|
|
|
|
|
void RiffVideo::readIndexChunk(uint64_t size_) {
|
|
|
|
#ifdef EXIV2_DEBUG_MESSAGES
|
|
|
|
uint64_t current_size = 0;
|
|
|
|
while (current_size < size_) {
|
|
|
|
EXV_DEBUG << "--> Identifier = " << readStringTag(io_) << "\t(" << current_size << "/" << size_ << ")"
|
|
|
|
<< std::endl;
|
|
|
|
EXV_DEBUG << "--> Flags = " << readDWORDTag(io_) << "\t(" << current_size << "/" << size_ << ")"
|
|
|
|
<< std::endl;
|
|
|
|
EXV_DEBUG << "--> Offset = " << readDWORDTag(io_) << "\t(" << current_size << "/" << size_ << ")"
|
|
|
|
<< std::endl;
|
|
|
|
EXV_DEBUG << "--> Length = " << readDWORDTag(io_) << "\t(" << current_size << "/" << size_ << ")"
|
|
|
|
<< std::endl;
|
|
|
|
current_size += DWORD * 4;
|
|
|
|
}
|
|
|
|
if (LogMsg::debug >= LogMsg::level() && LogMsg::handler())
|
|
|
|
io_->seekOrThrow(io_->tell() - size_, BasicIo::beg, ErrorCode::kerFailedToReadImageData);
|
|
|
|
#endif
|
|
|
|
io_->seekOrThrow(io_->tell() + size_, BasicIo::beg, ErrorCode::kerFailedToReadImageData);
|
|
|
|
}
|
|
|
|
|
|
|
|
void RiffVideo::readDataChunk(uint64_t size_) {
|
|
|
|
#ifdef EXIV2_DEBUG_MESSAGES
|
|
|
|
EXV_INFO << "--> Data = " << readStringTag(io_, static_cast<size_t>(size_)) << std::endl;
|
|
|
|
uint64_t readed_size = size_;
|
|
|
|
if (size_ % 2 != 0) {
|
|
|
|
EXV_INFO << "--> pad byte = " << readStringTag(io_, 1) << std::endl;
|
|
|
|
readed_size += 1;
|
|
|
|
}
|
|
|
|
if (LogMsg::info >= LogMsg::level() && LogMsg::handler())
|
|
|
|
io_->seekOrThrow(io_->tell() - readed_size, BasicIo::beg, ErrorCode::kerFailedToReadImageData);
|
|
|
|
#endif
|
|
|
|
io_->seekOrThrow(io_->tell() + size_, BasicIo::beg, ErrorCode::kerFailedToReadImageData);
|
|
|
|
if (size_ % 2 != 0)
|
|
|
|
io_->seekOrThrow(io_->tell() + 1, BasicIo::beg, ErrorCode::kerFailedToReadImageData);
|
|
|
|
}
|
|
|
|
|
|
|
|
void RiffVideo::readJunk(uint64_t size_) {
|
|
|
|
io_->seekOrThrow(io_->tell() + size_, BasicIo::beg, ErrorCode::kerFailedToReadImageData);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string RiffVideo::getStreamType(uint32_t stream) {
|
|
|
|
if (stream == 1)
|
|
|
|
return "Mono";
|
|
|
|
if (stream == 2)
|
|
|
|
return "Stereo";
|
|
|
|
if (stream == 5)
|
|
|
|
return "5.1 Surround Sound";
|
|
|
|
if (stream == 7)
|
|
|
|
return "7.1 Surround Sound";
|
|
|
|
return "Mono";
|
|
|
|
}
|
|
|
|
|
|
|
|
void RiffVideo::fillDuration(double frame_rate, size_t frame_count) {
|
|
|
|
if (frame_rate == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
auto duration = static_cast<uint64_t>(frame_count * 1000. / frame_rate);
|
|
|
|
xmpData_["Xmp.video.FileDataRate"] = io_->size() / (1048576. * duration);
|
|
|
|
xmpData_["Xmp.video.Duration"] = duration; // Duration in number of seconds
|
|
|
|
} // RiffVideo::fillDuration
|
|
|
|
|
|
|
|
Image::UniquePtr newRiffInstance(BasicIo::UniquePtr io, bool /*create*/) {
|
|
|
|
auto image = std::make_unique<RiffVideo>(std::move(io));
|
|
|
|
if (!image->good()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
return image;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isRiffType(BasicIo& iIo, bool advance) {
|
|
|
|
constexpr int len = 4;
|
|
|
|
const unsigned char RiffVideoId[len] = {'R', 'I', 'F', 'F'};
|
|
|
|
byte buf[len];
|
|
|
|
iIo.read(buf, len);
|
|
|
|
if (iIo.error() || iIo.eof()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
bool matched = (memcmp(buf, RiffVideoId, len) == 0);
|
|
|
|
if (!advance || !matched) {
|
|
|
|
iIo.seek(-1 * len, BasicIo::cur);
|
|
|
|
}
|
|
|
|
return matched;
|
|
|
|
}
|
|
|
|
} // namespace Exiv2
|