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