1 // ***************************************************************** -*- C++ -*-
2 /*
3  * Copyright (C) 2004-2021 Exiv2 authors
4  * This program is part of the Exiv2 distribution.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA.
19  */
20 // *****************************************************************************
21 // included header files
22 #include "config.h"
23 
24 #ifdef EXV_ENABLE_VIDEO
25 #include "error.hpp"
26 #include "riffvideo.hpp"
27 #include "futils.hpp"
28 #include "basicio.hpp"
29 #include "tags.hpp"
30 #include "tags_int.hpp"
31 #include "types.hpp"
32 #include "tiffimage_int.hpp"
33 #include "image_int.hpp"
34 // + standard includes
35 #include <cmath>
36 
37 // *****************************************************************************
38 // class member definitions
39 namespace Exiv2 {
40     namespace Internal {
41 
42     /*!
43       @brief Dummy TIFF header structure.
44      */
45     class DummyTiffHeader : public TiffHeaderBase {
46     public:
47         //! @name Creators
48         //@{
49         //! Default constructor
50         DummyTiffHeader(ByteOrder byteOrder);
51         //! Destructor
52         ~DummyTiffHeader();
53         //@}
54 
55         //! @name Manipulators
56         //@{
57         //! Dummy read function. Does nothing and returns true.
58         bool read(const byte* pData, uint32_t size);
59         //@}
60 
61     }; // class TiffHeader
62 
DummyTiffHeader(ByteOrder byteOrder)63     DummyTiffHeader::DummyTiffHeader(ByteOrder byteOrder)
64         : TiffHeaderBase(42, 0, byteOrder, 0)
65     {
66     }
67 
~DummyTiffHeader()68     DummyTiffHeader::~DummyTiffHeader()
69     {
70     }
71 
read(const byte *,uint32_t)72     bool DummyTiffHeader::read(const byte* /*pData*/, uint32_t /*size*/)
73     {
74         return true;
75     }
76 
77     extern const TagVocabulary infoTags[] =  {
78         {   "AGES", "Xmp.video.Rated" },
79         {   "CMNT", "Xmp.video.Comment" },
80         {   "CODE", "Xmp.video.EncodedBy" },
81         {   "COMM", "Xmp.video.Comment" },
82         {   "DIRC", "Xmp.video.Director" },
83         {   "DISP", "Xmp.audio.SchemeTitle" },
84         {   "DTIM", "Xmp.video.DateTimeOriginal" },
85         {   "GENR", "Xmp.video.Genre" },
86         {   "IARL", "Xmp.video.ArchivalLocation" },
87         {   "IART", "Xmp.video.Artist" },
88         {   "IAS1", "Xmp.video.Edit1" },
89         {   "IAS2", "Xmp.video.Edit2" },
90         {   "IAS3", "Xmp.video.Edit3" },
91         {   "IAS4", "Xmp.video.Edit4" },
92         {   "IAS5", "Xmp.video.Edit5" },
93         {   "IAS6", "Xmp.video.Edit6" },
94         {   "IAS7", "Xmp.video.Edit7" },
95         {   "IAS8", "Xmp.video.Edit8" },
96         {   "IAS9", "Xmp.video.Edit9" },
97         {   "IBSU", "Xmp.video.BaseURL" },
98         {   "ICAS", "Xmp.audio.DefaultStream" },
99         {   "ICDS", "Xmp.video.CostumeDesigner" },
100         {   "ICMS", "Xmp.video.Commissioned" },
101         {   "ICMT", "Xmp.video.Comment" },
102         {   "ICNM", "Xmp.video.Cinematographer" },
103         {   "ICNT", "Xmp.video.Country" },
104         {   "ICOP", "Xmp.video.Copyright" },
105         {   "ICRD", "Xmp.video.DateTimeDigitized" },
106         {   "ICRP", "Xmp.video.Cropped" },
107         {   "IDIM", "Xmp.video.Dimensions" },
108         {   "IDPI", "Xmp.video.DotsPerInch" },
109         {   "IDST", "Xmp.video.DistributedBy" },
110         {   "IEDT", "Xmp.video.EditedBy" },
111         {   "IENC", "Xmp.video.EncodedBy" },
112         {   "IENG", "Xmp.video.Engineer" },
113         {   "IGNR", "Xmp.video.Genre" },
114         {   "IKEY", "Xmp.video.PerformerKeywords" },
115         {   "ILGT", "Xmp.video.Lightness" },
116         {   "ILGU", "Xmp.video.LogoURL" },
117         {   "ILIU", "Xmp.video.LogoIconURL" },
118         {   "ILNG", "Xmp.video.Language" },
119         {   "IMBI", "Xmp.video.InfoBannerImage" },
120         {   "IMBU", "Xmp.video.InfoBannerURL" },
121         {   "IMED", "Xmp.video.Medium" },
122         {   "IMIT", "Xmp.video.InfoText" },
123         {   "IMIU", "Xmp.video.InfoURL" },
124         {   "IMUS", "Xmp.video.MusicBy" },
125         {   "INAM", "Xmp.video.Title" },
126         {   "IPDS", "Xmp.video.ProductionDesigner" },
127         {   "IPLT", "Xmp.video.NumOfColors" },
128         {   "IPRD", "Xmp.video.Product" },
129         {   "IPRO", "Xmp.video.ProducedBy" },
130         {   "IRIP", "Xmp.video.RippedBy" },
131         {   "IRTD", "Xmp.video.Rating" },
132         {   "ISBJ", "Xmp.video.Subject" },
133         {   "ISFT", "Xmp.video.Software" },
134         {   "ISGN", "Xmp.video.SecondaryGenre" },
135         {   "ISHP", "Xmp.video.Sharpness" },
136         {   "ISRC", "Xmp.video.Source" },
137         {   "ISRF", "Xmp.video.SourceForm" },
138         {   "ISTD", "Xmp.video.ProductionStudio" },
139         {   "ISTR", "Xmp.video.Starring" },
140         {   "ITCH", "Xmp.video.Technician" },
141         {   "IWMU", "Xmp.video.WatermarkURL" },
142         {   "IWRI", "Xmp.video.WrittenBy" },
143         {   "LANG", "Xmp.video.Language" },
144         {   "LOCA", "Xmp.video.LocationInfo" },
145         {   "PRT1", "Xmp.video.Part" },
146         {   "PRT2", "Xmp.video.NumOfParts" },
147         {   "RATE", "Xmp.video.Rate" },
148         {   "STAR", "Xmp.video.Starring" },
149         {   "STAT", "Xmp.video.Statistics" },
150         {   "TAPE", "Xmp.video.TapeName" },
151         {   "TCDO", "Xmp.video.EndTimecode" },
152         {   "TCOD", "Xmp.video.StartTimecode" },
153         {   "TITL", "Xmp.video.Title" },
154         {   "TLEN", "Xmp.video.Length" },
155         {   "TORG", "Xmp.video.Organization" },
156         {   "TRCK", "Xmp.video.TrackNumber" },
157         {   "TURL", "Xmp.video.URL" },
158         {   "TVER", "Xmp.video.SoftwareVersion" },
159         {   "VMAJ", "Xmp.video.VegasVersionMajor" },
160         {   "VMIN", "Xmp.video.VegasVersionMinor" },
161         {   "YEAR", "Xmp.video.Year" }
162     };
163 
164     extern const TagDetails audioEncodingValues[] =  {
165         {   0x1, "Microsoft PCM" },
166         {   0x2, "Microsoft ADPCM" },
167         {   0x3, "Microsoft IEEE float" },
168         {   0x4, "Compaq VSELP" },
169         {   0x5, "IBM CVSD" },
170         {   0x6, "Microsoft a-Law" },
171         {   0x7, "Microsoft u-Law" },
172         {   0x8, "Microsoft DTS" },
173         {   0x9, "DRM" },
174         {   0xa, "WMA 9 Speech" },
175         {   0xb, "Microsoft Windows Media RT Voice" },
176         {   0x10, "OKI-ADPCM" },
177         {   0x11, "Intel IMA/DVI-ADPCM" },
178         {   0x12, "Videologic Mediaspace ADPCM" },
179         {   0x13, "Sierra ADPCM" },
180         {   0x14, "Antex G.723 ADPCM" },
181         {   0x15, "DSP Solutions DIGISTD" },
182         {   0x16, "DSP Solutions DIGIFIX" },
183         {   0x17, "Dialoic OKI ADPCM" },
184         {   0x18, "Media Vision ADPCM" },
185         {   0x19, "HP CU" },
186         {   0x1a, "HP Dynamic Voice" },
187         {   0x20, "Yamaha ADPCM" },
188         {   0x21, "SONARC Speech Compression" },
189         {   0x22, "DSP Group True Speech" },
190         {   0x23, "Echo Speech Corp." },
191         {   0x24, "Virtual Music Audiofile AF36" },
192         {   0x25, "Audio Processing Tech." },
193         {   0x26, "Virtual Music Audiofile AF10" },
194         {   0x27, "Aculab Prosody 1612" },
195         {   0x28, "Merging Tech. LRC" },
196         {   0x30, "Dolby AC2" },
197         {   0x31, "Microsoft GSM610" },
198         {   0x32, "MSN Audio" },
199         {   0x33, "Antex ADPCME" },
200         {   0x34, "Control Resources VQLPC" },
201         {   0x35, "DSP Solutions DIGIREAL" },
202         {   0x36, "DSP Solutions DIGIADPCM" },
203         {   0x37, "Control Resources CR10" },
204         {   0x38, "Natural MicroSystems VBX ADPCM" },
205         {   0x39, "Crystal Semiconductor IMA ADPCM" },
206         {   0x3a, "Echo Speech ECHOSC3" },
207         {   0x3b, "Rockwell ADPCM" },
208         {   0x3c, "Rockwell DIGITALK" },
209         {   0x3d, "Xebec Multimedia" },
210         {   0x40, "Antex G.721 ADPCM" },
211         {   0x41, "Antex G.728 CELP" },
212         {   0x42, "Microsoft MSG723" },
213         {   0x43, "IBM AVC ADPCM" },
214         {   0x45, "ITU-T G.726" },
215         {   0x50, "Microsoft MPEG" },
216         {   0x51, "RT23 or PAC" },
217         {   0x52, "InSoft RT24" },
218         {   0x53, "InSoft PAC" },
219         {   0x55, "MP3" },
220         {   0x59, "Cirrus" },
221         {   0x60, "Cirrus Logic" },
222         {   0x61, "ESS Tech. PCM" },
223         {   0x62, "Voxware Inc." },
224         {   0x63, "Canopus ATRAC" },
225         {   0x64, "APICOM G.726 ADPCM" },
226         {   0x65, "APICOM G.722 ADPCM" },
227         {   0x66, "Microsoft DSAT" },
228         {   0x67, "Micorsoft DSAT DISPLAY" },
229         {   0x69, "Voxware Byte Aligned" },
230         {   0x70, "Voxware AC8" },
231         {   0x71, "Voxware AC10" },
232         {   0x72, "Voxware AC16" },
233         {   0x73, "Voxware AC20" },
234         {   0x74, "Voxware MetaVoice" },
235         {   0x75, "Voxware MetaSound" },
236         {   0x76, "Voxware RT29HW" },
237         {   0x77, "Voxware VR12" },
238         {   0x78, "Voxware VR18" },
239         {   0x79, "Voxware TQ40" },
240         {   0x7a, "Voxware SC3" },
241         {   0x7b, "Voxware SC3" },
242         {   0x80, "Soundsoft" },
243         {   0x81, "Voxware TQ60" },
244         {   0x82, "Microsoft MSRT24" },
245         {   0x83, "AT&T G.729A" },
246         {   0x84, "Motion Pixels MVI MV12" },
247         {   0x85, "DataFusion G.726" },
248         {   0x86, "DataFusion GSM610" },
249         {   0x88, "Iterated Systems Audio" },
250         {   0x89, "Onlive" },
251         {   0x8a, "Multitude, Inc. FT SX20" },
252         {   0x8b, "Infocom ITS A/S G.721 ADPCM" },
253         {   0x8c, "Convedia G729" },
254         {   0x8d, "Not specified congruency, Inc." },
255         {   0x91, "Siemens SBC24" },
256         {   0x92, "Sonic Foundry Dolby AC3 APDIF" },
257         {   0x93, "MediaSonic G.723" },
258         {   0x94, "Aculab Prosody 8kbps" },
259         {   0x97, "ZyXEL ADPCM" },
260         {   0x98, "Philips LPCBB" },
261         {   0x99, "Studer Professional Audio Packed" },
262         {   0xa0, "Malden PhonyTalk" },
263         {   0xa1, "Racal Recorder GSM" },
264         {   0xa2, "Racal Recorder G720.a" },
265         {   0xa3, "Racal G723.1" },
266         {   0xa4, "Racal Tetra ACELP" },
267         {   0xb0, "NEC AAC NEC Corporation" },
268         {   0xff, "AAC" },
269         {   0x100, "Rhetorex ADPCM" },
270         {   0x101, "IBM u-Law" },
271         {   0x102, "IBM a-Law" },
272         {   0x103, "IBM ADPCM" },
273         {   0x111, "Vivo G.723" },
274         {   0x112, "Vivo Siren" },
275         {   0x120, "Philips Speech Processing CELP" },
276         {   0x121, "Philips Speech Processing GRUNDIG" },
277         {   0x123, "Digital G.723" },
278         {   0x125, "Sanyo LD ADPCM" },
279         {   0x130, "Sipro Lab ACEPLNET" },
280         {   0x131, "Sipro Lab ACELP4800" },
281         {   0x132, "Sipro Lab ACELP8V3" },
282         {   0x133, "Sipro Lab G.729" },
283         {   0x134, "Sipro Lab G.729A" },
284         {   0x135, "Sipro Lab Kelvin" },
285         {   0x136, "VoiceAge AMR" },
286         {   0x140, "Dictaphone G.726 ADPCM" },
287         {   0x150, "Qualcomm PureVoice" },
288         {   0x151, "Qualcomm HalfRate" },
289         {   0x155, "Ring Zero Systems TUBGSM" },
290         {   0x160, "Microsoft Audio1" },
291         {   0x161, "Windows Media Audio V2 V7 V8 V9 / DivX audio (WMA) / Alex AC3 Audio" },
292         {   0x162, "Windows Media Audio Professional V9" },
293         {   0x163, "Windows Media Audio Lossless V9" },
294         {   0x164, "WMA Pro over S/PDIF" },
295         {   0x170, "UNISYS NAP ADPCM" },
296         {   0x171, "UNISYS NAP ULAW" },
297         {   0x172, "UNISYS NAP ALAW" },
298         {   0x173, "UNISYS NAP 16K" },
299         {   0x174, "MM SYCOM ACM SYC008 SyCom Technologies" },
300         {   0x175, "MM SYCOM ACM SYC701 G726L SyCom Technologies" },
301         {   0x176, "MM SYCOM ACM SYC701 CELP54 SyCom Technologies" },
302         {   0x177, "MM SYCOM ACM SYC701 CELP68 SyCom Technologies" },
303         {   0x178, "Knowledge Adventure ADPCM" },
304         {   0x180, "Fraunhofer IIS MPEG2AAC" },
305         {   0x190, "Digital Theater Systems DTS DS" },
306         {   0x200, "Creative Labs ADPCM" },
307         {   0x202, "Creative Labs FASTSPEECH8" },
308         {   0x203, "Creative Labs FASTSPEECH10" },
309         {   0x210, "UHER ADPCM" },
310         {   0x215, "Ulead DV ACM" },
311         {   0x216, "Ulead DV ACM" },
312         {   0x220, "Quarterdeck Corp." },
313         {   0x230, "I-Link VC" },
314         {   0x240, "Aureal Semiconductor Raw Sport" },
315         {   0x241, "ESST AC3" },
316         {   0x250, "Interactive Products HSX" },
317         {   0x251, "Interactive Products RPELP" },
318         {   0x260, "Consistent CS2" },
319         {   0x270, "Sony SCX" },
320         {   0x271, "Sony SCY" },
321         {   0x272, "Sony ATRAC3" },
322         {   0x273, "Sony SPC" },
323         {   0x280, "TELUM Telum Inc." },
324         {   0x281, "TELUMIA Telum Inc." },
325         {   0x285, "Norcom Voice Systems ADPCM" },
326         {   0x300, "Fujitsu FM TOWNS SND" },
327         {   0x301, "Fujitsu (not specified)" },
328         {   0x302, "Fujitsu (not specified)" },
329         {   0x303, "Fujitsu (not specified)" },
330         {   0x304, "Fujitsu (not specified)" },
331         {   0x305, "Fujitsu (not specified)" },
332         {   0x306, "Fujitsu (not specified)" },
333         {   0x307, "Fujitsu (not specified)" },
334         {   0x308, "Fujitsu (not specified)" },
335         {   0x350, "Micronas Semiconductors, Inc. Development" },
336         {   0x351, "Micronas Semiconductors, Inc. CELP833" },
337         {   0x400, "Brooktree Digital" },
338         {   0x401, "Intel Music Coder (IMC)" },
339         {   0x402, "Ligos Indeo Audio" },
340         {   0x450, "QDesign Music" },
341         {   0x500, "On2 VP7 On2 Technologies" },
342         {   0x501, "On2 VP6 On2 Technologies" },
343         {   0x680, "AT&T VME VMPCM" },
344         {   0x681, "AT&T TCP" },
345         {   0x700, "YMPEG Alpha (dummy for MPEG-2 compressor)" },
346         {   0x8ae, "ClearJump LiteWave (lossless)" },
347         {   0x1000, "Olivetti GSM" },
348         {   0x1001, "Olivetti ADPCM" },
349         {   0x1002, "Olivetti CELP" },
350         {   0x1003, "Olivetti SBC" },
351         {   0x1004, "Olivetti OPR" },
352         {   0x1100, "Lernout & Hauspie" },
353         {   0x1101, "Lernout & Hauspie CELP codec" },
354         {   0x1102, "Lernout & Hauspie SBC codec" },
355         {   0x1103, "Lernout & Hauspie SBC codec" },
356         {   0x1104, "Lernout & Hauspie SBC codec" },
357         {   0x1400, "Norris Comm. Inc." },
358         {   0x1401, "ISIAudio" },
359         {   0x1500, "AT&T Soundspace Music Compression" },
360         {   0x181c, "VoxWare RT24 speech codec" },
361         {   0x181e, "Lucent elemedia AX24000P Music codec" },
362         {   0x1971, "Sonic Foundry LOSSLESS" },
363         {   0x1979, "Innings Telecom Inc. ADPCM" },
364         {   0x1c07, "Lucent SX8300P speech codec" },
365         {   0x1c0c, "Lucent SX5363S G.723 compliant codec" },
366         {   0x1f03, "CUseeMe DigiTalk (ex-Rocwell)" },
367         {   0x1fc4, "NCT Soft ALF2CD ACM" },
368         {   0x2000, "FAST Multimedia DVM" },
369         {   0x2001, "Dolby DTS (Digital Theater System)" },
370         {   0x2002, "RealAudio 1 / 2 14.4" },
371         {   0x2003, "RealAudio 1 / 2 28.8" },
372         {   0x2004, "RealAudio G2 / 8 Cook (low bitrate)" },
373         {   0x2005, "RealAudio 3 / 4 / 5 Music (DNET)" },
374         {   0x2006, "RealAudio 10 AAC (RAAC)" },
375         {   0x2007, "RealAudio 10 AAC+ (RACP)" },
376         {   0x2500, "Reserved range to 0x2600 Microsoft" },
377         {   0x3313, "makeAVIS (ffvfw fake AVI sound from AviSynth scripts)" },
378         {   0x4143, "Divio MPEG-4 AAC audio" },
379         {   0x4201, "Nokia adaptive multirate" },
380         {   0x4243, "Divio G726 Divio, Inc." },
381         {   0x434c, "LEAD Speech" },
382         {   0x564c, "LEAD Vorbis" },
383         {   0x5756, "WavPack Audio" },
384         {   0x674f, "Ogg Vorbis (mode 1)" },
385         {   0x6750, "Ogg Vorbis (mode 2)" },
386         {   0x6751, "Ogg Vorbis (mode 3)" },
387         {   0x676f, "Ogg Vorbis (mode 1+)" },
388         {   0x6770, "Ogg Vorbis (mode 2+)" },
389         {   0x6771, "Ogg Vorbis (mode 3+)" },
390         {   0x7000, "3COM NBX 3Com Corporation" },
391         {   0x706d, "FAAD AAC" },
392         {   0x7a21, "GSM-AMR (CBR, no SID)" },
393         {   0x7a22, "GSM-AMR (VBR, including SID)" },
394         {   0xa100, "Comverse Infosys Ltd. G723 1" },
395         {   0xa101, "Comverse Infosys Ltd. AVQSBC" },
396         {   0xa102, "Comverse Infosys Ltd. OLDSBC" },
397         {   0xa103, "Symbol Technologies G729A" },
398         {   0xa104, "VoiceAge AMR WB VoiceAge Corporation" },
399         {   0xa105, "Ingenient Technologies Inc. G726" },
400         {   0xa106, "ISO/MPEG-4 advanced audio Coding" },
401         {   0xa107, "Encore Software Ltd G726" },
402         {   0xa109, "Speex ACM Codec xiph.org" },
403         {   0xdfac, "DebugMode SonicFoundry Vegas FrameServer ACM Codec" },
404         {   0xe708, "Unknown -" },
405         {   0xf1ac, "Free Lossless Audio Codec FLAC" },
406         {   0xfffe, "Extensible" },
407         {   0xffff, "Development" }
408     };
409 
410     extern const TagDetails nikonAVITags[] =  {
411         {   0x0003, "Xmp.video.Make" },
412         {   0x0004, "Xmp.video.Model" },
413         {   0x0005, "Xmp.video.Software" },
414         {   0x0006, "Xmp.video.Equipment" },
415         {   0x0007, "Xmp.video.Orientation" },
416         {   0x0008, "Xmp.video.ExposureTime" },
417         {   0x0009, "Xmp.video.FNumber" },
418         {   0x000a, "Xmp.video.ExposureCompensation" },
419         {   0x000b, "Xmp.video.MaxApertureValue" },
420         {   0x000c, "Xmp.video.MeteringMode" },
421         {   0x000f, "Xmp.video.FocalLength" },
422         {   0x0010, "Xmp.video.XResolution" },
423         {   0x0011, "Xmp.video.YResolution" },
424         {   0x0012, "Xmp.video.ResolutionUnit" },
425         {   0x0013, "Xmp.video.DateTimeOriginal" },
426         {   0x0014, "Xmp.video.DateTimeDigitized" },
427         {   0x0016, "Xmp.video.duration" },
428         {   0x0018, "Xmp.video.FocusMode" },
429         {   0x001b, "Xmp.video.DigitalZoomRatio" },
430         {   0x001d, "Xmp.video.ColorMode" },
431         {   0x001e, "Xmp.video.Sharpness" },
432         {   0x001f, "Xmp.video.WhiteBalance" },
433         {   0x0020, "Xmp.video.ColorNoiseReduction" }
434     };
435 
436     /*
437     extern const TagDetails orientation[] =  {
438         {   1, "Horizontal (normal)" },
439         {   2, "Mirror horizontal" },
440         {   3, "Rotate 180" },
441         {   4, "Mirror vertical" },
442         {   5, "Mirror horizontal and rotate 270 CW" },
443         {   6, "Rotate 90 CW" },
444         {   7, "Mirror horizontal and rotate 90 CW" },
445         {   8, "Rotate 270 CW" }
446     };
447      */
448     extern const TagDetails meteringMode[] =  {
449         {   0, "Unknown" },
450         {   1, "Average" },
451         {   2, "Center-weighted average" },
452         {   3, "Spot" },
453         {   4, "Multi-spot" },
454         {   5, "Multi-segment" },
455         {   6, "Partial" },
456         { 255, "Other" }
457     };
458 
459     extern const TagDetails resolutionUnit[] =  {
460         {   1, "None" },
461         {   2, "inches" },
462         {   3, "cm" }
463     };
464 
465     /*!
466       @brief Function used to check equality of a Tags with a
467           particular string (ignores case while comparing).
468       @param buf Data buffer that will contain Tag to compare
469       @param str char* Pointer to string
470       @return Returns true if the buffer value is equal to string.
471      */
equalsRiffTag(Exiv2::DataBuf & buf,const char * str)472     bool equalsRiffTag(Exiv2::DataBuf& buf ,const char* str) {
473         for(int i = 0; i < 4; i++ )
474             if(toupper(buf.pData_[i]) != str[i])
475                 return false;
476         return true;
477     }
478 
479     enum streamTypeInfo {
480         Audio = 1, MIDI, Text, Video
481     };
482     enum streamHeaderTags {
483         codec = 1, sampleRate = 5, sampleCount = 8, quality = 10, sampleSize
484     };
485     enum bmptags {
486         imageWidth, imageHeight, planes, bitDepth, compression, imageLength, pixelsPerMeterX, pixelsPerMeterY, numColors, numImportantColors
487     };
488     enum audioFormatTags {
489         encoding, numberOfChannels, audioSampleRate, avgBytesPerSec = 4, bitsPerSample = 7
490     };
491     enum aviHeaderTags {
492         frameRate, maxDataRate, frameCount = 4, streamCount = 6, imageWidth_h = 8, imageHeight_h
493     };
494 }}                                      // namespace Internal, Exiv2
495 
496 namespace Exiv2 {
497     using namespace Exiv2::Internal;
498 
RiffVideo(BasicIo::AutoPtr io)499     RiffVideo::RiffVideo(BasicIo::AutoPtr io)
500             : Image(ImageType::riff, mdNone, io)
501     {
502     } // RiffVideo::RiffVideo
503 
mimeType() const504     std::string RiffVideo::mimeType() const
505     {
506         return "video/riff";
507     }
508 
509     const int RiffVideo::RIFF_TAG_SIZE = 0x4;
510     const char* RiffVideo::RIFF_CHUNK_HEADER_ICCP = "ICCP";
511     const char* RiffVideo::RIFF_CHUNK_HEADER_EXIF = "EXIF";
512     const char* RiffVideo::RIFF_CHUNK_HEADER_XMP  = "XMP ";
513 
514     /*!
515      @brief Function used to check equality of a Tags with a
516      particular string (ignores case while comparing).
517      @param buf Data buffer that will contain Tag to compare
518      @param str char* Pointer to string
519      @return Returns true if the buffer value is equal to string.
520      */
equalsRiffTag(Exiv2::DataBuf & buf,const char * str)521     bool RiffVideo::equalsRiffTag(Exiv2::DataBuf& buf, const char* str) {
522         for(int i = 0; i < 4; i++ )
523             if(toupper(buf.pData_[i]) != str[i])
524                 return false;
525         return true;
526     }
527 
printStructure(std::ostream & out,PrintStructureOption option,int depth)528     void RiffVideo::printStructure(std::ostream& out, PrintStructureOption option, int depth) {
529         if (io_->open() != 0) {
530             throw Error(kerDataSourceOpenFailed, io_->path(), strError());
531         }
532         // Ensure this is the correct image type
533         if (!isRiffType(*io_, true)) {
534             if (io_->error() || io_->eof()) throw Error(kerFailedToReadImageData);
535             throw Error(kerNotAnImage, "RIFF");
536         }
537 
538         bool bPrint  = option==kpsBasic || option==kpsRecursive;
539         if ( bPrint || option == kpsXMP || option == kpsIccProfile || option == kpsIptcErase ) {
540             byte      data [RIFF_TAG_SIZE * 2];
541             io_->read(data, RIFF_TAG_SIZE * 2);
542             uint64_t filesize = Exiv2::getULong(data + RIFF_TAG_SIZE, littleEndian);
543             DataBuf  chunkId(5)      ;
544             chunkId.pData_[4] = '\0' ;
545 
546             if ( bPrint ) {
547                 out << Internal::indent(depth)
548                     << "STRUCTURE OF RIFF FILE: "
549                     << io().path()
550                     << std::endl;
551                 out << Internal::indent(depth)
552                     << Internal::stringFormat(" Chunk |       Length |       Offset | Payload")
553                     << std::endl;
554             }
555 
556             io_->seek(0,BasicIo::beg); // rewind
557             while ( !io_->eof() && (uint64_t) io_->tell() < filesize) {
558                 uint64_t offset = (uint64_t) io_->tell();
559                 byte     size_buff[RIFF_TAG_SIZE];
560                 io_->read(chunkId.pData_, RIFF_TAG_SIZE);
561                 io_->read(size_buff, RIFF_TAG_SIZE);
562                 long size = Exiv2::getULong(size_buff, littleEndian);
563                 DataBuf payload(offset?size:RIFF_TAG_SIZE); // header is different from chunks
564                 io_->read(payload.pData_, payload.size_);
565 
566                 if ( bPrint ) {
567                     out << Internal::indent(depth)
568                         << Internal::stringFormat("  %s | %12u | %12u | ", (const char*)chunkId.pData_,size,(uint32_t)offset)
569                         << Internal::binaryToString(makeSlice(payload, 0, payload.size_ > 32 ? 32 : payload.size_))
570                         << std::endl;
571                 }
572 
573                 if ( equalsRiffTag(chunkId, RIFF_CHUNK_HEADER_EXIF) && option==kpsRecursive ) {
574                     // create memio object with the payload, then print the structure
575                     BasicIo::AutoPtr p = BasicIo::AutoPtr(new MemIo(payload.pData_,payload.size_));
576                     printTiffStructure(*p,out,option,depth);
577                 }
578 
579                 bool bPrintPayload = (equalsRiffTag(chunkId, RIFF_CHUNK_HEADER_XMP) && option==kpsXMP)
580                                      || (equalsRiffTag(chunkId, RIFF_CHUNK_HEADER_ICCP) && option==kpsIccProfile)
581                 ;
582                 if ( bPrintPayload ) {
583                     out.write((const char*) payload.pData_,payload.size_);
584                 }
585 
586                 if ( offset && io_->tell() % 2 ) io_->seek(+1, BasicIo::cur); // skip padding byte on sub-chunks
587             }
588         }
589     } // RiffVideo::printStructure
590 
writeMetadata()591     void RiffVideo::writeMetadata()
592     {
593     } // RiffVideo::writeMetadata
594 
readMetadata()595     void RiffVideo::readMetadata()
596     {
597         if (io_->open() != 0) throw Error(kerDataSourceOpenFailed, io_->path(), strError());
598 
599         // Ensure that this is the correct image type
600         if (!isRiffType(*io_, false)) {
601             if (io_->error() || io_->eof()) throw Error(kerFailedToReadImageData);
602             throw Error(kerNotAnImage, "RIFF");
603         }
604 
605         IoCloser closer(*io_);
606         clearMetadata();
607         continueTraversing_ = true;
608 
609         xmpData_["Xmp.video.FileSize"] = (double)io_->size()/(double)1048576;
610         xmpData_["Xmp.video.FileName"] = io_->path();
611         xmpData_["Xmp.video.MimeType"] = mimeType();
612 
613         const long bufMinSize = 4;
614         DataBuf buf(bufMinSize+1);
615         buf.pData_[4] = '\0';
616 
617         io_->read(buf.pData_, bufMinSize);
618         xmpData_["Xmp.video.Container"] = buf.pData_;
619 
620         io_->read(buf.pData_, bufMinSize);
621         io_->read(buf.pData_, bufMinSize);
622         xmpData_["Xmp.video.FileType"] = buf.pData_;
623 
624         while (continueTraversing_) decodeBlock();
625     } // RiffVideo::readMetadata
626 
decodeBlock()627     void RiffVideo::decodeBlock()
628     {
629         const long bufMinSize = 4;
630         DataBuf buf(bufMinSize+1);
631         DataBuf buf2(bufMinSize+1);
632         unsigned long size = 0;
633         buf.pData_[4] = '\0' ;
634         buf2.pData_[4] = '\0' ;
635 
636         io_->read(buf2.pData_, 4);
637 
638         if(io_->eof() || equalsRiffTag(buf2, "MOVI") || equalsRiffTag(buf2, "DATA")) {
639             continueTraversing_ = false;
640             return;
641         }
642         else if(equalsRiffTag(buf2, "HDRL") || equalsRiffTag(buf2, "STRL")) {
643             decodeBlock();
644         }
645         else {
646             io_->read(buf.pData_, 4);
647             size = Exiv2::getULong(buf.pData_, littleEndian);
648 
649         tagDecoder(buf2, size);
650         }
651     } // RiffVideo::decodeBlock
652 
tagDecoder(Exiv2::DataBuf & buf,unsigned long size)653     void RiffVideo::tagDecoder(Exiv2::DataBuf& buf, unsigned long size)
654     {
655         uint64_t cur_pos = io_->tell();
656         static bool listFlag = false, listEnd = false;
657 
658         if(equalsRiffTag(buf, "LIST")) {
659             listFlag = true;
660             listEnd = false;
661 
662             while((uint64_t)(io_->tell()) < cur_pos + size) decodeBlock();
663 
664             listEnd = true;
665             io_->seek(cur_pos + size, BasicIo::beg);
666         }
667         else if(equalsRiffTag(buf, "JUNK") && listEnd) {
668             junkHandler(size);
669         }
670         else if(equalsRiffTag(buf, "AVIH")) {
671             listFlag = false;
672             aviHeaderTagsHandler(size);
673         }
674         else if(equalsRiffTag(buf, "STRH")) {
675             listFlag = false;
676             streamHandler(size);
677         }
678         else if(equalsRiffTag(buf,"STRF") || equalsRiffTag(buf, "FMT ")) {
679             listFlag = false;
680             if(equalsRiffTag(buf,"FMT "))
681                 streamType_ = Audio;
682             streamFormatHandler(size);
683         }
684         else if(equalsRiffTag(buf, "STRN")) {
685             listFlag = false;
686             dateTimeOriginal(size, 1);
687         }
688         else if(equalsRiffTag(buf, "STRD")) {
689             listFlag = false;
690             streamDataTagHandler(size);
691         }
692         else if(equalsRiffTag(buf, "IDIT")) {
693             listFlag = false;
694             dateTimeOriginal(size);
695         }
696         else if(equalsRiffTag(buf, "INFO")) {
697             listFlag = false;
698             infoTagsHandler();
699         }
700         else if(equalsRiffTag(buf, "NCDT")) {
701             listFlag = false;
702             nikonTagsHandler();
703         }
704         else if(equalsRiffTag(buf, "ODML")) {
705             listFlag = false;
706             odmlTagsHandler();
707         }
708         else if (listFlag) {
709             // std::cout<<"|unprocessed|"<<buf.pData_;
710             skipListData();
711         }
712         else {
713             // std::cout<<"|unprocessed|"<<buf.pData_;
714             io_->seek(cur_pos + size, BasicIo::beg);
715         }
716     } // RiffVideo::tagDecoder
717 
streamDataTagHandler(long size)718     void RiffVideo::streamDataTagHandler(long size)
719     {
720         const long bufMinSize = 20000;
721         DataBuf buf(bufMinSize);
722         buf.pData_[4] = '\0';
723         uint64_t cur_pos = io_->tell();
724 
725             io_->read(buf.pData_, 8);
726 
727              if(equalsRiffTag(buf, "AVIF")) {
728 
729                  if (size - 4 < 0) {
730              #ifndef SUPPRESS_WARNINGS
731                      EXV_ERROR   << " Exif Tags found in this RIFF file are not of valid size ."
732                                  << " Entries considered invalid. Not Processed.\n";
733              #endif
734                  }
735                  else {
736                  io_->read(buf.pData_, size - 4);
737 
738                  IptcData iptcData;
739                  XmpData  xmpData;
740                  DummyTiffHeader tiffHeader(littleEndian);
741                  TiffParserWorker::decode(exifData_,
742                                           iptcData,
743                                           xmpData,
744                                           buf.pData_,
745                                           buf.size_,
746                                           Tag::root,
747                                           TiffMapping::findDecoder,
748                                           &tiffHeader);
749 
750          #ifndef SUPPRESS_WARNINGS
751                  if (!iptcData.empty()) {
752                      EXV_WARNING << "Ignoring IPTC information encoded in the Exif data.\n";
753                  }
754                  if (!xmpData.empty()) {
755                      EXV_WARNING << "Ignoring XMP information encoded in the Exif data.\n";
756                  }
757          #endif
758              }
759              }
760               // TODO decode CasioData and ZORA Tag
761         io_->seek(cur_pos + size, BasicIo::beg);
762 
763     } // RiffVideo::streamDataTagHandler
764 
dateTimeOriginal(long size,int i)765     void RiffVideo::dateTimeOriginal(long size, int i)
766     {
767         uint64_t cur_pos = io_->tell();
768         const long bufMinSize = 100;
769         DataBuf buf(bufMinSize);
770         io_->read(buf.pData_, size);
771         if(!i)
772             xmpData_["Xmp.video.DateUTC"] = buf.pData_;
773         else
774             xmpData_["Xmp.video.StreamName"] = buf.pData_;
775         io_->seek(cur_pos + size, BasicIo::beg);
776     } // RiffVideo::dateTimeOriginal
777 
odmlTagsHandler()778     void RiffVideo::odmlTagsHandler()
779     {
780         const long bufMinSize = 100;
781         DataBuf buf(bufMinSize);
782         buf.pData_[4] = '\0';
783         io_->seek(-12, BasicIo::cur);
784         io_->read(buf.pData_, 4);
785         unsigned long size = Exiv2::getULong(buf.pData_, littleEndian);
786         unsigned long size2 = size;
787 
788         uint64_t cur_pos = io_->tell();
789         io_->read(buf.pData_, 4); size -= 4;
790 
791         while(size > 0) {
792             io_->read(buf.pData_, 4); size -= 4;
793             if(equalsRiffTag(buf,"DMLH")) {
794                 io_->read(buf.pData_, 4); size -= 4;
795                 io_->read(buf.pData_, 4); size -= 4;
796                 xmpData_["Xmp.video.TotalFrameCount"] = Exiv2::getULong(buf.pData_, littleEndian);
797             }
798         }
799         io_->seek(cur_pos + size2, BasicIo::beg);
800     } // RiffVideo::odmlTagsHandler
801 
skipListData()802     void RiffVideo::skipListData()
803     {
804         const long bufMinSize = 4;
805         DataBuf buf(bufMinSize+1);
806         buf.pData_[4] = '\0';
807         io_->seek(-12, BasicIo::cur);
808         io_->read(buf.pData_, 4);
809         unsigned long size = Exiv2::getULong(buf.pData_, littleEndian);
810 
811         uint64_t cur_pos = io_->tell();
812         io_->seek(cur_pos + size, BasicIo::beg);
813     } // RiffVideo::skipListData
814 
nikonTagsHandler()815     void RiffVideo::nikonTagsHandler()
816     {
817         const long bufMinSize = 100;
818         DataBuf buf(bufMinSize), buf2(4+1);
819         buf.pData_[4] = '\0';
820         io_->seek(-12, BasicIo::cur);
821         io_->read(buf.pData_, 4);
822 
823         long internal_size = 0, tagID = 0, dataSize = 0, tempSize, size = Exiv2::getULong(buf.pData_, littleEndian);
824         tempSize = size; char str[9] = " . . . ";
825         uint64_t internal_pos, cur_pos; internal_pos = cur_pos = io_->tell();
826         const TagDetails* td;
827         double denominator = 1;
828         io_->read(buf.pData_, 4); tempSize -= 4;
829 
830         while(tempSize > 0) {
831             std::memset(buf.pData_, 0x0, buf.size_);
832             io_->read(buf.pData_, 4);
833             io_->read(buf2.pData_, 4);
834             int temp = internal_size = Exiv2::getULong(buf2.pData_, littleEndian);
835             internal_pos = io_->tell(); tempSize -= (internal_size + 8);
836 
837             if(equalsRiffTag(buf, "NCVR")) {
838                 while((long)temp > 3) {
839                     std::memset(buf.pData_, 0x0, buf.size_);
840                     io_->read(buf.pData_, 2);
841                     tagID = Exiv2::getULong(buf.pData_, littleEndian);
842                     io_->read(buf.pData_, 2);
843                     dataSize = Exiv2::getULong(buf.pData_, littleEndian);
844                     temp -= (4 + dataSize);
845 
846                     if(tagID == 0x0001) {
847                         if (dataSize <= 0) {
848                     #ifndef SUPPRESS_WARNINGS
849                             EXV_ERROR   << " Makernotes found in this RIFF file are not of valid size ."
850                                         << " Entries considered invalid. Not Processed.\n";
851                     #endif
852                         }
853                         else {
854                         io_->read(buf.pData_, dataSize);
855                         xmpData_["Xmp.video.MakerNoteType"] = buf.pData_;
856                         }
857                     }
858                     else if (tagID == 0x0002) {
859                         while(dataSize) {
860                             std::memset(buf.pData_, 0x0, buf.size_); io_->read(buf.pData_, 1);
861                             str[(4 - dataSize) * 2] = (char)(Exiv2::getULong(buf.pData_, littleEndian) + 48);
862                             --dataSize;
863                         }
864                         xmpData_["Xmp.video.MakerNoteVersion"] = str;
865                     }
866                 }
867             }
868             else if(equalsRiffTag(buf, "NCTG")) {
869                 while((long)temp > 3) {
870                     std::memset(buf.pData_, 0x0, buf.size_);
871                     io_->read(buf.pData_, 2);
872                     tagID = Exiv2::getULong(buf.pData_, littleEndian);
873                     io_->read(buf.pData_, 2);
874                     dataSize = Exiv2::getULong(buf.pData_, littleEndian);
875                     temp -= (4 + dataSize);
876                     td = find(nikonAVITags , tagID);
877 
878                     if (dataSize <= 0) {
879                 #ifndef SUPPRESS_WARNINGS
880                         EXV_ERROR   << " Makernotes found in this RIFF file are not of valid size ."
881                                     << " Entries considered invalid. Not Processed.\n";
882                 #endif
883                     }
884                     else {
885                     io_->read(buf.pData_, dataSize);
886 
887                     switch (tagID) {
888                     case 0x0003: case 0x0004: case 0x0005: case 0x0006:
889                     case 0x0013: case 0x0014: case 0x0018: case 0x001d:
890                     case 0x001e: case 0x001f: case 0x0020:
891                         xmpData_[exvGettext(td->label_)] = buf.pData_; break;
892 
893                     case 0x0007: case 0x0010: case 0x0011: case 0x000c:
894                     case 0x0012:
895                         xmpData_[exvGettext(td->label_)] = Exiv2::getULong(buf.pData_, littleEndian); break;
896 
897                     case 0x0008: case 0x0009: case 0x000a: case 0x000b:
898                     case 0x000f: case 0x001b: case 0x0016:
899                         buf2.pData_[0] = buf.pData_[4]; buf2.pData_[1] = buf.pData_[5];
900                         buf2.pData_[2] = buf.pData_[6]; buf2.pData_[3] = buf.pData_[7];
901                         denominator = (double)Exiv2::getLong(buf2.pData_, littleEndian);
902                         if (denominator != 0)
903                             xmpData_[exvGettext(td->label_)] = (double)Exiv2::getLong(buf.pData_, littleEndian) / denominator;
904                         else
905                             xmpData_[exvGettext(td->label_)] = 0;
906                         break;
907 
908                     default:
909                         break;
910                     }
911                     }
912                 }
913             }
914 
915             else if(equalsRiffTag(buf, "NCTH")) {//TODO Nikon Thumbnail Image
916             }
917 
918             else if(equalsRiffTag(buf, "NCVW")) {//TODO Nikon Preview Image
919             }
920 
921             io_->seek(internal_pos + internal_size, BasicIo::beg);
922         }
923 
924         if (size ==0) {
925             io_->seek(cur_pos + 4, BasicIo::beg);
926         }
927         else {
928             io_->seek(cur_pos + size, BasicIo::beg);
929         }
930     } // RiffVideo::nikonTagsHandler
931 
infoTagsHandler()932     void RiffVideo::infoTagsHandler()
933     {
934         const long bufMinSize = 10000;
935         DataBuf buf(bufMinSize);
936         buf.pData_[4] = '\0';
937         io_->seek(-12, BasicIo::cur);
938         io_->read(buf.pData_, 4);
939         long infoSize, size = Exiv2::getULong(buf.pData_, littleEndian);
940         long size_external = size;
941         const TagVocabulary* tv;
942 
943         uint64_t cur_pos = io_->tell();
944         io_->read(buf.pData_, 4); size -= 4;
945 
946         while(size > 3) {
947             io_->read(buf.pData_, 4); size -= 4;
948             if(!Exiv2::getULong(buf.pData_, littleEndian))
949                 break;
950             tv = find(infoTags , Exiv2::toString( buf.pData_));
951             io_->read(buf.pData_, 4); size -= 4;
952             infoSize = Exiv2::getULong(buf.pData_, littleEndian);
953 
954             if(infoSize >= 0) {
955                 size -= infoSize;
956                 io_->read(buf.pData_, infoSize);
957                 if(infoSize < 4)
958                     buf.pData_[infoSize] = '\0';
959             }
960 
961             if(tv)
962                 xmpData_[exvGettext(tv->label_)] = buf.pData_;
963             else
964                 continue;
965         }
966         io_->seek(cur_pos + size_external, BasicIo::beg);
967     } // RiffVideo::infoTagsHandler
968 
junkHandler(long size)969     void RiffVideo::junkHandler(long size)
970     {
971         const long bufMinSize = size;
972 
973         if (size < 0) {
974     #ifndef SUPPRESS_WARNINGS
975             EXV_ERROR   << " Junk Data found in this RIFF file are not of valid size ."
976                         << " Entries considered invalid. Not Processed.\n";
977     #endif
978             io_->seek(io_->tell() + 4, BasicIo::beg);
979         }
980 
981         else {
982         DataBuf buf(bufMinSize+1), buf2(4+1);
983         std::memset(buf.pData_, 0x0, buf.size_);
984         buf2.pData_[4] = '\0';
985         uint64_t cur_pos = io_->tell();
986 
987         io_->read(buf.pData_, 4);
988         //! Pentax Metadata and Tags
989         if(equalsRiffTag(buf, "PENT")) {
990 
991             io_->seek(cur_pos + 18, BasicIo::beg);
992             io_->read(buf.pData_, 26);
993             xmpData_["Xmp.video.Make"] = buf.pData_;
994 
995             io_->read(buf.pData_, 50);
996             xmpData_["Xmp.video.Model"] = buf.pData_;
997 
998             std::memset(buf.pData_, 0x0, buf.size_);
999             io_->read(buf.pData_, 8);
1000             buf2.pData_[0] = buf.pData_[4]; buf2.pData_[1] = buf.pData_[5];
1001             buf2.pData_[2] = buf.pData_[6]; buf2.pData_[3] = buf.pData_[7];
1002             xmpData_["Xmp.video.FNumber"] = (double)Exiv2::getLong(buf.pData_, littleEndian) / (double)Exiv2::getLong(buf2.pData_, littleEndian);;
1003 
1004             io_->seek(cur_pos + 131, BasicIo::beg);
1005             io_->read(buf.pData_, 26);
1006             xmpData_["Xmp.video.DateTimeOriginal"] = buf.pData_;
1007 
1008             io_->read(buf.pData_, 26);
1009             xmpData_["Xmp.video.DateTimeDigitized"] = buf.pData_;
1010 
1011             io_->seek(cur_pos + 299, BasicIo::beg);
1012             std::memset(buf.pData_, 0x0, buf.size_);
1013 
1014             io_->read(buf.pData_, 2);
1015             Exiv2::XmpTextValue tv(Exiv2::toString(Exiv2::getLong(buf.pData_, littleEndian)));
1016             xmpData_.add(Exiv2::XmpKey("Xmp.xmp.Thumbnails/xmpGImg:width"), &tv);
1017 
1018             io_->read(buf.pData_, 2);
1019             tv.read(Exiv2::toString(Exiv2::getLong(buf.pData_, littleEndian)));
1020             xmpData_.add(Exiv2::XmpKey("Xmp.xmp.Thumbnails/xmpGImg:height"), &tv);
1021 
1022             io_->read(buf.pData_, 4);
1023 
1024             /* TODO - Storing the image Thumbnail in Base64 Format
1025 
1026 
1027             uint64_t length = Exiv2::getLong(buf.pData_, littleEndian);
1028             io_->read(buf.pData_, length);
1029 
1030             char *rawStr = Exiv2::toString(buf.pData_);
1031             char *encodedStr;
1032 
1033             SXMPUtils::EncodeToBase64(rawStr, encodedStr);
1034 
1035             tv.read(Exiv2::toString(encodedStr));
1036             xmpData_.add(Exiv2::XmpKey("Xmp.xmp.Thumbnails/xmpGImg:image"), &tv);
1037         */
1038         }
1039         else {
1040             io_->seek(cur_pos, BasicIo::beg);
1041             io_->read(buf.pData_, size);
1042             xmpData_["Xmp.video.Junk"] = buf.pData_;
1043         }
1044 
1045         io_->seek(cur_pos + size, BasicIo::beg);
1046         }
1047     } // RiffVideo::junkHandler
1048 
aviHeaderTagsHandler(long size)1049     void RiffVideo::aviHeaderTagsHandler(long size)
1050     {
1051         const long bufMinSize = 4;
1052         DataBuf buf(bufMinSize+1);
1053         buf.pData_[4] = '\0';
1054         long width = 0, height = 0, frame_count = 0;
1055         double frame_rate = 1;
1056 
1057         uint64_t cur_pos = io_->tell();
1058 
1059         for(int i = 0; i <= 9; i++) {
1060             std::memset(buf.pData_, 0x0, buf.size_);
1061             io_->read(buf.pData_, bufMinSize);
1062 
1063             switch(i) {
1064             case frameRate:
1065                 xmpData_["Xmp.video.MicroSecPerFrame"] = Exiv2::getULong(buf.pData_, littleEndian);
1066                 frame_rate = (double)1000000/(double)Exiv2::getULong(buf.pData_, littleEndian);
1067                 break;
1068             case (maxDataRate):
1069                 xmpData_["Xmp.video.MaxDataRate"] = (double)Exiv2::getULong(buf.pData_, littleEndian)/(double)1024;
1070                 break;
1071             case frameCount:
1072                 xmpData_["Xmp.video.FrameCount"] = Exiv2::getULong(buf.pData_, littleEndian);
1073                 frame_count = Exiv2::getULong(buf.pData_, littleEndian);
1074                 break;
1075             case streamCount:
1076                 xmpData_["Xmp.video.StreamCount"] = Exiv2::getULong(buf.pData_, littleEndian);
1077                 break;
1078             case imageWidth_h:
1079                 width = Exiv2::getULong(buf.pData_, littleEndian);
1080                 xmpData_["Xmp.video.Width"] = width;
1081                 break;
1082             case imageHeight_h:
1083                 height = Exiv2::getULong(buf.pData_, littleEndian);
1084                 xmpData_["Xmp.video.Height"] = height;
1085                 break;
1086             }
1087         }
1088 
1089         fillAspectRatio(width, height);
1090         fillDuration(frame_rate, frame_count);
1091 
1092         io_->seek(cur_pos + size, BasicIo::beg);
1093     } // RiffVideo::aviHeaderTagsHandler
1094 
streamHandler(long size)1095     void RiffVideo::streamHandler(long size)
1096     {
1097         const long bufMinSize = 4;
1098         DataBuf buf(bufMinSize+1);
1099         buf.pData_[4]='\0';
1100         long divisor = 1;
1101         uint64_t cur_pos = io_->tell();
1102 
1103         io_->read(buf.pData_, bufMinSize);
1104         if(equalsRiffTag(buf, "VIDS"))
1105             streamType_ = Video;
1106         else if (equalsRiffTag(buf, "AUDS"))
1107             streamType_ = Audio;
1108 
1109         for(int i=1; i<=25; i++) {
1110             std::memset(buf.pData_, 0x0, buf.size_);
1111             io_->read(buf.pData_, bufMinSize);
1112 
1113             switch(i) {
1114             case codec:
1115                 if(streamType_ == Video)
1116                     xmpData_["Xmp.video.Codec"] = buf.pData_;
1117                 else if (streamType_ == Audio)
1118                     xmpData_["Xmp.audio.Codec"] = buf.pData_;
1119                 else
1120                     xmpData_["Xmp.video.Codec"] = buf.pData_;
1121                 break;
1122             case sampleRate:
1123                 divisor=Exiv2::getULong(buf.pData_, littleEndian);
1124                 break;
1125             case (sampleRate+1):
1126                 if(streamType_ == Video)
1127                     xmpData_["Xmp.video.FrameRate"] = returnSampleRate(buf,divisor);
1128                 else if (streamType_ == Audio)
1129                     xmpData_["Xmp.audio.SampleRate"] = returnSampleRate(buf,divisor);
1130                 else
1131                      xmpData_["Xmp.video.StreamSampleRate"] = returnSampleRate(buf,divisor);
1132                 break;
1133             case sampleCount:
1134                 if(streamType_ == Video)
1135                     xmpData_["Xmp.video.FrameCount"] = Exiv2::getULong(buf.pData_, littleEndian);
1136                 else if (streamType_ == Audio)
1137                     xmpData_["Xmp.audio.SampleCount"] = Exiv2::getULong(buf.pData_, littleEndian);
1138                 else
1139                     xmpData_["Xmp.video.StreamSampleCount"] = Exiv2::getULong(buf.pData_, littleEndian);
1140                 break;
1141             case quality:
1142                 if(streamType_ == Video)
1143                     xmpData_["Xmp.video.VideoQuality"] = Exiv2::getULong(buf.pData_, littleEndian);
1144                 else if(streamType_ != Audio)
1145                     xmpData_["Xmp.video.StreamQuality"] = Exiv2::getULong(buf.pData_, littleEndian);
1146                 break;
1147             case sampleSize:
1148                 if(streamType_ == Video)
1149                     xmpData_["Xmp.video.VideoSampleSize"] = Exiv2::getULong(buf.pData_, littleEndian);
1150                 else if(streamType_ != Audio)
1151                     xmpData_["Xmp.video.StreamSampleSize"] = Exiv2::getULong(buf.pData_, littleEndian);
1152                 break;
1153             }
1154 
1155         }
1156         io_->seek(cur_pos + size, BasicIo::beg);
1157     } // RiffVideo::streamHandler
1158 
streamFormatHandler(long size)1159     void RiffVideo::streamFormatHandler(long size)
1160     {
1161         const long bufMinSize = 4;
1162         DataBuf buf(bufMinSize+1);
1163         buf.pData_[4] = '\0';
1164         uint64_t cur_pos = io_->tell();
1165 
1166         if(streamType_ == Video) {
1167             io_->read(buf.pData_, bufMinSize);
1168 
1169             for(int i = 0; i <= 9; i++) {
1170                 std::memset(buf.pData_, 0x0, buf.size_);
1171 
1172                 switch(i) {
1173                 case imageWidth: //Will be used in case of debugging
1174                     io_->read(buf.pData_, bufMinSize); break;
1175                 case imageHeight: //Will be used in case of debugging
1176                     io_->read(buf.pData_, bufMinSize); break;
1177                 case planes:
1178                     io_->read(buf.pData_, 2);
1179                     xmpData_["Xmp.video.Planes"] = Exiv2::getUShort(buf.pData_, littleEndian); break;
1180                 case bitDepth:
1181                     io_->read(buf.pData_, 2);
1182                     xmpData_["Xmp.video.PixelDepth"] = Exiv2::getUShort(buf.pData_, littleEndian); break;
1183                 case compression:
1184                     io_->read(buf.pData_, bufMinSize);
1185                     xmpData_["Xmp.video.Compressor"] = buf.pData_; break;
1186                 case imageLength:
1187                     io_->read(buf.pData_, bufMinSize);
1188                     xmpData_["Xmp.video.ImageLength"] = Exiv2::getULong(buf.pData_, littleEndian); break;
1189                 case pixelsPerMeterX:
1190                     io_->read(buf.pData_, bufMinSize);
1191                     xmpData_["Xmp.video.PixelPerMeterX"] = Exiv2::getULong(buf.pData_, littleEndian); break;
1192                 case pixelsPerMeterY:
1193                     io_->read(buf.pData_, bufMinSize);
1194                     xmpData_["Xmp.video.PixelPerMeterY"] = Exiv2::getULong(buf.pData_, littleEndian); break;
1195                 case numColors:
1196                     io_->read(buf.pData_, bufMinSize);
1197                     if(Exiv2::getULong(buf.pData_, littleEndian) == 0) {
1198                         xmpData_["Xmp.video.NumOfColours"] = "Unspecified";
1199                     }
1200                     else {
1201                         xmpData_["Xmp.video.NumOfColours"] = Exiv2::getULong(buf.pData_, littleEndian);
1202                     }
1203                     break;
1204                 case numImportantColors:
1205                     io_->read(buf.pData_, bufMinSize);
1206                     if(Exiv2::getULong(buf.pData_, littleEndian)) {
1207                         xmpData_["Xmp.video.NumIfImpColours"] = Exiv2::getULong(buf.pData_, littleEndian);
1208                     }
1209                     else {
1210                         xmpData_["Xmp.video.NumOfImpColours"] = "All";
1211                     }
1212                     break;
1213                 }
1214             }
1215         }
1216         else if(streamType_ == Audio) {
1217             int c = 0;
1218             const TagDetails* td;
1219             for(int i = 0; i <= 7; i++) {
1220                 io_->read(buf.pData_, 2);
1221 
1222                 switch(i) {
1223                 case encoding:
1224                     td = find(audioEncodingValues , Exiv2::getUShort(buf.pData_, littleEndian));
1225                     if(td) {
1226                         xmpData_["Xmp.audio.Compressor"] = exvGettext(td->label_);
1227                     }
1228                     else {
1229                         xmpData_["Xmp.audio.Compressor"] = Exiv2::getUShort(buf.pData_, littleEndian);
1230                     }
1231                     break;
1232                 case numberOfChannels:
1233                     c = Exiv2::getUShort(buf.pData_, littleEndian);
1234                     if(c == 1) xmpData_["Xmp.audio.ChannelType"] = "Mono";
1235                     else if(c == 2) xmpData_["Xmp.audio.ChannelType"] = "Stereo";
1236                     else if(c == 5) xmpData_["Xmp.audio.ChannelType"] = "5.1 Surround Sound";
1237                     else if(c == 7) xmpData_["Xmp.audio.ChannelType"] = "7.1 Surround Sound";
1238                     else xmpData_["Xmp.audio.ChannelType"] = "Mono";
1239                     break;
1240                 case audioSampleRate:
1241                     xmpData_["Xmp.audio.SampleRate"] = Exiv2::getUShort(buf.pData_, littleEndian);
1242                     break;
1243                 case avgBytesPerSec:
1244                     xmpData_["Xmp.audio.SampleType"] = Exiv2::getUShort(buf.pData_, littleEndian);
1245                     break;
1246                 case bitsPerSample:
1247                     xmpData_["Xmp.audio.BitsPerSample"] = Exiv2::getUShort(buf.pData_,littleEndian);
1248                     io_->read(buf.pData_, 2);
1249                     break;
1250                 }
1251             }
1252         }
1253         io_->seek(cur_pos + size, BasicIo::beg);
1254     } // RiffVideo::streamFormatHandler
1255 
returnSampleRate(Exiv2::DataBuf & buf,long divisor)1256     double RiffVideo::returnSampleRate(Exiv2::DataBuf& buf, long divisor)
1257     {
1258         return ((double)Exiv2::getULong(buf.pData_, littleEndian) / (double)divisor);
1259     } // RiffVideo::returnSampleRate
1260 
printAudioEncoding(uint64_t i)1261     const char* RiffVideo::printAudioEncoding(uint64_t i)
1262     {
1263         const TagDetails* td;
1264         td = find(audioEncodingValues , i);
1265         if(td)
1266             return exvGettext(td->label_);
1267 
1268         return "Undefined";
1269     } // RiffVideo::printAudioEncoding
1270 
fillAspectRatio(long width,long height)1271     void RiffVideo::fillAspectRatio(long width, long height)
1272     {
1273         double aspectRatio = (double)width / (double)height;
1274         aspectRatio = floor(aspectRatio*10) / 10;
1275         xmpData_["Xmp.video.AspectRatio"] = aspectRatio;
1276 
1277         int aR = (int) ((aspectRatio*10.0)+0.1);
1278 
1279         switch  (aR) {
1280             case 13 : xmpData_["Xmp.video.AspectRatio"] = "4:3"     ; break;
1281             case 17 : xmpData_["Xmp.video.AspectRatio"] = "16:9"    ; break;
1282             case 10 : xmpData_["Xmp.video.AspectRatio"] = "1:1"     ; break;
1283             case 16 : xmpData_["Xmp.video.AspectRatio"] = "16:10"   ; break;
1284             case 22 : xmpData_["Xmp.video.AspectRatio"] = "2.21:1"  ; break;
1285             case 23 : xmpData_["Xmp.video.AspectRatio"] = "2.35:1"  ; break;
1286             case 12 : xmpData_["Xmp.video.AspectRatio"] = "5:4"     ; break;
1287             default : xmpData_["Xmp.video.AspectRatio"] = aspectRatio;break;
1288         }
1289     } // RiffVideo::fillAspectRatio
1290 
fillDuration(double frame_rate,long frame_count)1291     void RiffVideo::fillDuration(double frame_rate, long frame_count)
1292     {
1293         if(frame_rate == 0)
1294             return;
1295 
1296         uint64_t duration = static_cast<uint64_t>((double)frame_count * 1000. / frame_rate);
1297         xmpData_["Xmp.video.FileDataRate"] = (double)io_->size()/(double)(1048576*duration);
1298         xmpData_["Xmp.video.Duration"] = duration; //Duration in number of seconds
1299     } // RiffVideo::fillDuration
1300 
newRiffInstance(BasicIo::AutoPtr io,bool)1301     Image::AutoPtr newRiffInstance(BasicIo::AutoPtr io, bool /*create*/)
1302     {
1303         Image::AutoPtr image(new RiffVideo(io));
1304         if (!image->good()) {
1305             image.reset();
1306         }
1307         return image;
1308     }
1309 
isRiffType(BasicIo & iIo,bool advance)1310     bool isRiffType(BasicIo& iIo, bool advance)
1311     {
1312         const int32_t len = 2;
1313         const unsigned char RiffVideoId[4] = { 'R', 'I', 'F' ,'F'};
1314         byte buf[len];
1315         iIo.read(buf, len);
1316         if (iIo.error() || iIo.eof()) {
1317             return false;
1318         }
1319         bool matched = (memcmp(buf, RiffVideoId, len) == 0);
1320         if (!advance || !matched) {
1321             iIo.seek(-len, BasicIo::cur);
1322         }
1323         return matched;
1324     }
1325 
1326 }                                       // namespace Exiv2
1327 #endif // EXV_ENABLE_VIDEO
1328