Coverage Report

Created: 2026-05-30 06:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/exiv2/src/quicktimevideo.cpp
Line
Count
Source
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 "quicktimevideo.hpp"
23
#include "basicio.hpp"
24
#include "config.h"
25
#include "enforce.hpp"
26
#include "error.hpp"
27
#include "futils.hpp"
28
#include "helper_functions.hpp"
29
#include "image_int.hpp"
30
#include "properties.hpp"
31
#include "safe_op.hpp"
32
#include "tags.hpp"
33
#include "tags_int.hpp"
34
// + standard includes
35
#include <array>
36
#include <cmath>
37
#include <string>
38
// *****************************************************************************
39
// class member definitions
40
namespace Exiv2::Internal {
41
42
static constexpr TagVocabulary qTimeFileType[] = {
43
    {"3g2a", "3GPP2 Media (.3G2) compliant with 3GPP2 C.S0050-0 V1.0"},
44
    {"3g2b", "3GPP2 Media (.3G2) compliant with 3GPP2 C.S0050-A V1.0.0"},
45
    {"3g2c", "3GPP2 Media (.3G2) compliant with 3GPP2 C.S0050-B v1.0"},
46
    {"3ge6", "3GPP (.3GP) Release 6 MBMS Extended Presentations"},
47
    {"3ge7", "3GPP (.3GP) Release 7 MBMS Extended Presentations"},
48
    {"3gg6", "3GPP Release 6 General Profile"},
49
    {"3gp1", "3GPP Media (.3GP) Release 1 (probably non-existent)"},
50
    {"3gp2", "3GPP Media (.3GP) Release 2 (probably non-existent)"},
51
    {"3gp3", "3GPP Media (.3GP) Release 3 (probably non-existent)"},
52
    {"3gp4", "3GPP Media (.3GP) Release 4"},
53
    {"3gp5", "3GPP Media (.3GP) Release 5"},
54
    {"3gp6", "3GPP Media (.3GP) Release 6 Streaming Servers"},
55
    {"3gs7", "3GPP Media (.3GP) Release 7 Streaming Servers"},
56
    {"CAEP", "Canon Digital Camera"},
57
    {"CDes", "Convergent Design"},
58
    {"F4A ", "Audio for Adobe Flash Player 9+ (.F4A)"},
59
    {"F4B ", "Audio Book for Adobe Flash Player 9+ (.F4B)"},
60
    {"F4P ", "Protected Video for Adobe Flash Player 9+ (.F4P)"},
61
    {"F4V ", "Video for Adobe Flash Player 9+ (.F4V)"},
62
    {"JP2 ", "JPEG 2000 Image (.JP2) [ISO 15444-1 ?]"},
63
    {"JP20", "Unknown, from GPAC samples (prob non-existent)"},
64
    {"KDDI", "3GPP2 EZmovie for KDDI 3G cellphones"},
65
    {"M4A ", "Apple iTunes AAC-LC (.M4A) Audio"},
66
    {"M4B ", "Apple iTunes AAC-LC (.M4B) Audio Book"},
67
    {"M4P ", "Apple iTunes AAC-LC (.M4P) AES Protected Audio"},
68
    {"M4V ", "Apple iTunes Video (.M4V) Video"},
69
    {"M4VH", "Apple TV (.M4V)"},
70
    {"M4VP", "Apple iPhone (.M4V)"},
71
    {"MPPI", "Photo Player, MAF [ISO/IEC 23000-3]"},
72
    {"MSNV", "MPEG-4 (.MP4) for SonyPSP"},
73
    {"NDAS", "MP4 v2 [ISO 14496-14] Nero Digital AAC Audio"},
74
    {"NDSC", "MPEG-4 (.MP4) Nero Cinema Profile"},
75
    {"NDSH", "MPEG-4 (.MP4) Nero HDTV Profile"},
76
    {"NDSM", "MPEG-4 (.MP4) Nero Mobile Profile"},
77
    {"NDSP", "MPEG-4 (.MP4) Nero Portable Profile"},
78
    {"NDSS", "MPEG-4 (.MP4) Nero Standard Profile"},
79
    {"NDXC", "H.264/MPEG-4 AVC (.MP4) Nero Cinema Profile"},
80
    {"NDXH", "H.264/MPEG-4 AVC (.MP4) Nero HDTV Profile"},
81
    {"NDXM", "H.264/MPEG-4 AVC (.MP4) Nero Mobile Profile"},
82
    {"NDXP", "H.264/MPEG-4 AVC (.MP4) Nero Portable Profile"},
83
    {"NDXS", "H.264/MPEG-4 AVC (.MP4) Nero Standard Profile"},
84
    {"NIKO", "Nikon"},
85
    {"ROSS", "Ross Video"},
86
    {"avc1", "MP4 Base w/ AVC ext [ISO 14496-12:2005]"},
87
    {"caqv", "Casio Digital Camera"},
88
    {"da0a", "DMB MAF w/ MPEG Layer II aud, MOT slides, DLS, JPG/PNG/MNG images"},
89
    {"da0b", "DMB MAF, extending DA0A, with 3GPP timed text, DID, TVA, REL, IPMP"},
90
    {"da1a", "DMB MAF audio with ER-BSAC audio, JPG/PNG/MNG images"},
91
    {"da1b", "DMB MAF, extending da1a, with 3GPP timed text, DID, TVA, REL, IPMP"},
92
    {"da2a", "DMB MAF aud w/ HE-AAC v2 aud, MOT slides, DLS, JPG/PNG/MNG images"},
93
    {"da2b", "DMB MAF, extending da2a, with 3GPP timed text, DID, TVA, REL, IPMP"},
94
    {"da3a", "DMB MAF aud with HE-AAC aud, JPG/PNG/MNG images"},
95
    {"da3b", "DMB MAF, extending da3a w/ BIFS, 3GPP timed text, DID, TVA, REL, IPMP"},
96
    {"dmb1", "DMB MAF supporting all the components defined in the specification"},
97
    {"dmpf", "Digital Media Project"},
98
    {"drc1", "Dirac (wavelet compression), encapsulated in ISO base media (MP4)"},
99
    {"dv1a", "DMB MAF vid w/ AVC vid, ER-BSAC aud, BIFS, JPG/PNG/MNG images, TS"},
100
    {"dv1b", "DMB MAF, extending dv1a, with 3GPP timed text, DID, TVA, REL, IPMP"},
101
    {"dv2a", "DMB MAF vid w/ AVC vid, HE-AAC v2 aud, BIFS, JPG/PNG/MNG images, TS"},
102
    {"dv2b", "DMB MAF, extending dv2a, with 3GPP timed text, DID, TVA, REL, IPMP"},
103
    {"dv3a", "DMB MAF vid w/ AVC vid, HE-AAC aud, BIFS, JPG/PNG/MNG images, TS"},
104
    {"dv3b", "DMB MAF, extending dv3a, with 3GPP timed text, DID, TVA, REL, IPMP"},
105
    {"dvr1", "DVB (.DVB) over RTP"},
106
    {"dvt1", "DVB (.DVB) over MPEG-2 Transport Stream"},
107
    {"isc2", "ISMACryp 2.0 Encrypted File"},
108
    {"iso2", "MP4 Base Media v2 [ISO 14496-12:2005]"},
109
    {"isom", "MP4 Base Media v1 [IS0 14496-12:2003]"},
110
    {"jpm ", "JPEG 2000 Compound Image (.JPM) [ISO 15444-6]"},
111
    {"jpx ", "JPEG 2000 with extensions (.JPX) [ISO 15444-2]"},
112
    {"mj2s", "Motion JPEG 2000 [ISO 15444-3] Simple Profile"},
113
    {"mjp2", "Motion JPEG 2000 [ISO 15444-3] General Profile"},
114
    {"mmp4", "MPEG-4/3GPP Mobile Profile (.MP4/3GP) (for NTT)"},
115
    {"mp21", "MPEG-21 [ISO/IEC 21000-9]"},
116
    {"mp41", "MP4 v1 [ISO 14496-1:ch13]"},
117
    {"mp42", "MP4 v2 [ISO 14496-14]"},
118
    {"mp71", "MP4 w/ MPEG-7 Metadata [per ISO 14496-12]"},
119
    {"mqt ", "Sony / Mobile QuickTime (.MQV) US Patent 7,477,830 (Sony Corp)"},
120
    {"niko", "Nikon"},
121
    {"odcf", "OMA DCF DRM Format 2.0 (OMA-TS-DRM-DCF-V2_0-20060303-A)"},
122
    {"opf2", "OMA PDCF DRM Format 2.1 (OMA-TS-DRM-DCF-V2_1-20070724-C)"},
123
    {"opx2", "OMA PDCF DRM + XBS extensions (OMA-TS-DRM_XBS-V1_0-20070529-C)"},
124
    {"pana", "Panasonic Digital Camera"},
125
    {"qt  ", "Apple QuickTime (.MOV/QT)"},
126
    {"sdv ", "SD Memory Card Video"},
127
    {"ssc1", "Samsung stereoscopic, single stream"},
128
    {"ssc2", "Samsung stereoscopic, dual stream"},
129
};
130
131
static constexpr TagVocabulary handlerClassTags[] = {
132
    {"dhlr", "Data Handler"},
133
    {"mhlr", "Media Handler"},
134
};
135
136
static constexpr TagVocabulary handlerTypeTags[] = {
137
    {"alis", "Alias Data"},
138
    {"crsm", "Clock Reference"},
139
    {"hint", "Hint Track"},
140
    {"ipsm", "IPMP"},
141
    {"m7sm", "MPEG-7 Stream"},
142
    {"mdir", "Metadata"},
143
    {"mdta", "Metadata Tags"},
144
    {"mjsm", "MPEG-J"},
145
    {"ocsm", "Object Content"},
146
    {"odsm", "Object Descriptor"},
147
    {"sdsm", "Scene Description"},
148
    {"soun", "Audio Track"},
149
    {"text", "Text"},
150
    {"tmcd", "Time Code"},
151
    {"url ", "URL"},
152
    {"vide", "Video Track"},
153
};
154
155
static constexpr TagVocabulary vendorIDTags[] = {
156
    {"FFMP", "FFmpeg"},
157
    {"appl", "Apple"},
158
    {"olym", "Olympus"},
159
    {"GIC ", "General Imaging Co."},
160
    {"fe20", "Olympus (fe20)"},
161
    {"pana", "Panasonic"},
162
    {"KMPI", "Konica-Minolta"},
163
    {"kdak", "Kodak"},
164
    {"pent", "Pentax"},
165
    {"NIKO", "Nikon"},
166
    {"leic", "Leica"},
167
    {"pr01", "Olympus (pr01)"},
168
    {"SMI ", "Sorenson Media Inc."},
169
    {"mino", "Minolta"},
170
    {"sany", "Sanyo"},
171
    {"ZORA", "Zoran Corporation"},
172
    {"niko", "Nikon"},
173
};
174
175
static constexpr TagVocabulary cameraByteOrderTags[] = {
176
    {"II", "Little-endian (Intel, II)"},
177
    {"MM", "Big-endian (Motorola, MM)"},
178
};
179
180
static constexpr TagDetails graphicsModetags[] = {
181
    {0x0, "srcCopy"},
182
    {0x1, "srcOr"},
183
    {0x2, "srcXor"},
184
    {0x3, "srcBic"},
185
    {0x4, "notSrcCopy"},
186
    {0x5, "notSrcOr"},
187
    {0x6, "notSrcXor"},
188
    {0x7, "notSrcBic"},
189
    {0x8, "patCopy"},
190
    {0x9, "patOr"},
191
    {0xa, "patXor"},
192
    {0xb, "patBic"},
193
    {0xc, "notPatCopy"},
194
    {0xd, "notPatOr"},
195
    {0xe, "notPatXor"},
196
    {0xf, "notPatBic"},
197
    {0x20, "blend"},
198
    {0x21, "addPin"},
199
    {0x22, "addOver"},
200
    {0x23, "subPin"},
201
    {0x24, "transparent"},
202
    {0x25, "addMax"},
203
    {0x26, "subOver"},
204
    {0x27, "addMin"},
205
    {0x31, "grayishTextOr"},
206
    {0x32, "hilite"},
207
    {0x40, "ditherCopy"},
208
    {0x100, "Alpha"},
209
    {0x101, "White Alpha"},
210
    {0x102, "Pre-multiplied Black Alpha"},
211
    {0x110, "Component Alpha"},
212
};
213
214
static constexpr TagVocabulary userDatatags[] = {
215
    {"AllF", "PlayAllFrames"},
216
    {"CNCV", "CompressorVersion"},
217
    {"CNFV", "FirmwareVersion"},
218
    {"CNMN", "Model"},
219
    {"CNTH", "CanonCNTH"},
220
    {"DcMD", "DcMD"},
221
    {"FFMV", "FujiFilmFFMV"},
222
    {"INFO", "SamsungINFO"},
223
    {"LOOP", "LoopStyle"},
224
    {"MMA0", "MinoltaMMA0"},
225
    {"MMA1", "MinoltaMMA1"},
226
    {"MVTG", "FujiFilmMVTG"},
227
    {"NCDT", "NikonNCDT"},
228
    {"PANA", "PanasonicPANA"},
229
    {"PENT", "PentaxPENT"},
230
    {"PXMN", "MakerNotePentax5b"},
231
    {"PXTH", "PentaxPreview"},
232
    {"QVMI", "CasioQVMI"},
233
    {"SDLN", "PlayMode"},
234
    {"SelO", "PlaySelection"},
235
    {"TAGS", "KodakTags/KonicaMinoltaTags/MinoltaTags/NikonTags/OlympusTags/PentaxTags/SamsungTags/SanyoMOV/SanyoMP4"},
236
    {"WLOC", "WindowLocation"},
237
    {"XMP_", "XMP"},
238
    {"Xtra", "Xtra"},
239
    {"hinf", "HintTrackInfo"},
240
    {"hinv", "HintVersion"},
241
    {"hnti", "Hint"},
242
    {"meta", "Meta"},
243
    {"name", "Name"},
244
    {"ptv ", "PrintToVideo"},
245
    {"scrn", "OlympusPreview"},
246
    {"thmb", "MakerNotePentax5a/OlympusThumbnail"},
247
};
248
249
static constexpr TagVocabulary userDataReferencetags[] = {
250
    {"CNCV", "Xmp.video.CompressorVersion"},
251
    {"CNFV", "Xmp.video.FirmwareVersion"},
252
    {"CNMN", "Xmp.video.Model"},
253
    {"NCHD", "Xmp.video.MakerNoteType"},
254
    {"WLOC", "Xmp.video.WindowLocation"},
255
    {"SDLN", "Xmp.video.PlayMode"},
256
    {"FFMV", "Xmp.video.StreamName"},
257
    {"SelO", "Xmp.video.PlaySelection"},
258
    {"name", "Xmp.video.Name"},
259
    {"vndr", "Xmp.video.Vendor"},
260
    {" ART", "Xmp.video.Artist"},
261
    {" alb", "Xmp.video.Album"},
262
    {" arg", "Xmp.video.Arranger"},
263
    {" ark", "Xmp.video.ArrangerKeywords"},
264
    {" cmt", "Xmp.video.Comment"},
265
    {" cok", "Xmp.video.ComposerKeywords"},
266
    {" com", "Xmp.video.Composer"},
267
    {" cpy", "Xmp.video.Copyright"},
268
    {" day", "Xmp.video.CreateDate"},
269
    {" dir", "Xmp.video.Director"},
270
    {" ed1", "Xmp.video.Edit1"},
271
    {" ed2", "Xmp.video.Edit2"},
272
    {" ed3", "Xmp.video.Edit3"},
273
    {" ed4", "Xmp.video.Edit4"},
274
    {" ed5", "Xmp.video.Edit5"},
275
    {" ed6", "Xmp.video.Edit6"},
276
    {" ed7", "Xmp.video.Edit7"},
277
    {" ed8", "Xmp.video.Edit8"},
278
    {" ed9", "Xmp.video.Edit9"},
279
    {" enc", "Xmp.video.Encoder"},
280
    {" fmt", "Xmp.video.Format"},
281
    {" gen", "Xmp.video.Genre"},
282
    {" grp", "Xmp.video.Grouping"},
283
    {" inf", "Xmp.video.Information"},
284
    {" isr", "Xmp.video.ISRCCode"},
285
    {" lab", "Xmp.video.RecordLabelName"},
286
    {" lal", "Xmp.video.RecordLabelURL"},
287
    {" lyr", "Xmp.video.Lyrics"},
288
    {" mak", "Xmp.video.Make"},
289
    {" mal", "Xmp.video.MakerURL"},
290
    {" mod", "Xmp.video.Model"},
291
    {" nam", "Xmp.video.Title"},
292
    {" pdk", "Xmp.video.ProducerKeywords"},
293
    {" phg", "Xmp.video.RecordingCopyright"},
294
    {" prd", "Xmp.video.Producer"},
295
    {" prf", "Xmp.video.Performers"},
296
    {" prk", "Xmp.video.PerformerKeywords"},
297
    {" prl", "Xmp.video.PerformerURL"},
298
    {" req", "Xmp.video.Requirements"},
299
    {" snk", "Xmp.video.SubtitleKeywords"},
300
    {" snm", "Xmp.video.Subtitle"},
301
    {" src", "Xmp.video.SourceCredits"},
302
    {" swf", "Xmp.video.SongWriter"},
303
    {" swk", "Xmp.video.SongWriterKeywords"},
304
    {" swr", "Xmp.video.SoftwareVersion"},
305
    {" too", "Xmp.video.Encoder"},
306
    {" trk", "Xmp.video.Track"},
307
    {" wrt", "Xmp.video.Composer"},
308
    {" xyz", "Xmp.video.GPSCoordinates"},
309
    {"CMbo", "Xmp.video.CameraByteOrder"},
310
    {"Cmbo", "Xmp.video.CameraByteOrder"},
311
};
312
313
static constexpr TagDetails NikonNCTGTags[] = {
314
    {0x0001, "Xmp.video.Make"},
315
    {0x0002, "Xmp.video.Model"},
316
    {0x0003, "Xmp.video.Software"},
317
    {0x0011, "Xmp.video.CreationDate"},
318
    {0x0012, "Xmp.video.DateTimeOriginal"},
319
    {0x0013, "Xmp.video.FrameCount"},
320
    {0x0016, "Xmp.video.FrameRate"},
321
    {0x0022, "Xmp.video.FrameWidth"},
322
    {0x0023, "Xmp.video.FrameHeight"},
323
    {0x0032, "Xmp.audio.channelType"},
324
    {0x0033, "Xmp.audio.BitsPerSample"},
325
    {0x0034, "Xmp.audio.sampleRate"},
326
    {0x1108822, "Xmp.video.ExposureProgram"},
327
    {0x1109204, "Xmp.video.ExposureCompensation"},
328
    {0x1109207, "Xmp.video.MeteringMode"},
329
    {0x110a434, "Xmp.video.LensModel"},
330
    {0x1200000, "Xmp.video.GPSVersionID"},
331
    {0x1200001, "Xmp.video.GPSLatitudeRef"},
332
    {0x1200002, "Xmp.video.GPSLatitude"},
333
    {0x1200003, "Xmp.video.GPSLongitudeRef"},
334
    {0x1200004, "Xmp.video.GPSLongitude"},
335
    {0x1200005, "Xmp.video.GPSAltitudeRef"},
336
    {0x1200006, "Xmp.video.GPSAltitude"},
337
    {0x1200007, "Xmp.video.GPSTimeStamp"},
338
    {0x1200008, "Xmp.video.GPSSatellites"},
339
    {0x1200010, "Xmp.video.GPSImgDirectionRef"},
340
    {0x1200011, "Xmp.video.GPSImgDirection"},
341
    {0x1200012, "Xmp.video.GPSMapDatum"},
342
    {0x120001d, "Xmp.video.GPSDateStamp"},
343
    {0x2000001, "Xmp.video.MakerNoteVersion"},
344
    {0x2000005, "Xmp.video.WhiteBalance"},
345
    {0x200000b, "Xmp.video.WhiteBalanceFineTune"},
346
    {0x200001e, "Xmp.video.ColorSpace"},
347
    {0x2000023, "Xmp.video.PictureControlData"},
348
    {0x2000024, "Xmp.video.WorldTime"},
349
    {0x200002c, "Xmp.video.UnknownInfo"},
350
    {0x2000032, "Xmp.video.UnknownInfo2"},
351
    {0x2000039, "Xmp.video.LocationInfo"},
352
    {0x2000083, "Xmp.video.LensType"},
353
    {0x2000084, "Xmp.video.LensModel"},
354
    {0x20000ab, "Xmp.video.VariProgram"},
355
};
356
357
[[maybe_unused]] static constexpr TagDetails NikonColorSpace[] = {
358
    {1, "sRGB"},
359
    {2, "Adobe RGB"},
360
};
361
362
[[maybe_unused]] static constexpr TagVocabulary NikonGPS_Latitude_Longitude_ImgDirection_Reference[] = {
363
    {"N", "North"}, {"S", "South"}, {"E", "East"}, {"W", "West"}, {"M", "Magnetic North"}, {"T", "True North"},
364
};
365
366
[[maybe_unused]] static constexpr TagDetails NikonGPSAltitudeRef[] = {
367
    {0, "Above Sea Level"},
368
    {1, "Below Sea Level"},
369
};
370
371
[[maybe_unused]] static constexpr TagDetails NikonExposureProgram[] = {
372
    {0, "Not Defined"},
373
    {1, "Manual"},
374
    {2, "Program AE"},
375
    {3, "Aperture-priority AE"},
376
    {4, "Shutter speed priority AE"},
377
    {5, "Creative (Slow speed)"},
378
    {6, "Action (High speed)"},
379
    {7, "Portrait"},
380
    {8, "Landscape"},
381
};
382
383
[[maybe_unused]] static constexpr TagDetails NikonMeteringMode[] = {
384
    {0, "Unknown"}, {1, "Average"},    {2, "Center-weighted average"},
385
    {3, "Spot"},    {4, "Multi-spot"}, {5, "Multi-segment"},
386
    {6, "Partial"}, {255, "Other"},
387
};
388
389
static constexpr TagDetails PictureControlAdjust[] = {
390
    {0, "Default Settings"},
391
    {1, "Quick Adjust"},
392
    {2, "Full Control"},
393
};
394
395
//! Contrast and Sharpness
396
static constexpr TagDetails NormalSoftHard[] = {
397
    {0, "Normal"},
398
    {1, "Soft"},
399
    {2, "Hard"},
400
};
401
402
//! Saturation
403
static constexpr TagDetails Saturation[] = {
404
    {0, "Normal"},
405
    {1, "Low"},
406
    {2, "High"},
407
};
408
409
//! YesNo, used for DaylightSavings
410
static constexpr TagDetails YesNo[] = {
411
    {0, "No"},
412
    {1, "Yes"},
413
};
414
415
//! DateDisplayFormat
416
static constexpr TagDetails DateDisplayFormat[] = {
417
    {0, "Y/M/D"},
418
    {1, "M/D/Y"},
419
    {2, "D/M/Y"},
420
};
421
422
static constexpr TagDetails FilterEffect[] = {
423
    {0x80, "Off"}, {0x81, "Yellow"}, {0x82, "Orange"}, {0x83, "Red"}, {0x84, "Green"}, {0xff, "n/a"},
424
};
425
426
static constexpr TagDetails ToningEffect[] = {
427
    {0x80, "B&W"},         {0x81, "Sepia"},      {0x82, "Cyanotype"},  {0x83, "Red"},
428
    {0x84, "Yellow"},      {0x85, "Green"},      {0x86, "Blue-green"}, {0x87, "Blue"},
429
    {0x88, "Purple-blue"}, {0x89, "Red-purple"}, {0xff, "n/a"},
430
};
431
432
static constexpr TagDetails whiteBalance[] = {
433
    {0, "Auto"}, {1, "Daylight"}, {2, "Shade"}, {3, "Fluorescent"}, {4, "Tungsten"}, {5, "Manual"},
434
};
435
436
enum movieHeaderTags {
437
  MovieHeaderVersion,
438
  CreateDate,
439
  ModifyDate,
440
  TimeScale,
441
  Duration,
442
  PreferredRate,
443
  PreferredVolume,
444
  PreviewTime = 18,
445
  PreviewDuration,
446
  PosterTime,
447
  SelectionTime,
448
  SelectionDuration,
449
  CurrentTime,
450
  NextTrackID
451
};
452
enum trackHeaderTags {
453
  TrackHeaderVersion,
454
  TrackCreateDate,
455
  TrackModifyDate,
456
  TrackID,
457
  TrackDuration = 5,
458
  TrackLayer = 8,
459
  TrackVolume,
460
  ImageWidth = 19,
461
  ImageHeight
462
};
463
enum mediaHeaderTags {
464
  MediaHeaderVersion,
465
  MediaCreateDate,
466
  MediaModifyDate,
467
  MediaTimeScale,
468
  MediaDuration,
469
  MediaLanguageCode
470
};
471
enum handlerTags { HandlerClass = 1, HandlerType, HandlerVendorID };
472
enum videoHeaderTags { GraphicsMode = 2, OpColor };
473
enum stream { Video, Audio, Hint, Null, GenMediaHeader };
474
enum imageDescTags {
475
  codec,
476
  VendorID = 4,
477
  SourceImageWidth_Height = 7,
478
  XResolution,
479
  YResolution,
480
  CompressorName = 10,
481
  BitDepth
482
};
483
enum audioDescTags { AudioFormat, AudioVendorID = 4, AudioChannels, AudioSampleRate = 7, MOV_AudioFormat = 13 };
484
485
/*!
486
  @brief Function used to check equality of a Tags with a
487
      particular string (ignores case while comparing).
488
  @param buf Data buffer that will contain Tag to compare
489
  @param str char* Pointer to string
490
  @return Returns true if the buffer value is equal to string.
491
 */
492
46.8M
static bool equalsQTimeTag(Exiv2::DataBuf& buf, const char str[5]) {
493
46.8M
  return std::equal(buf.begin(), buf.begin() + 4, str,
494
53.4M
                    [](auto b, auto s) { return std::tolower(b) == std::tolower(s); });
495
46.8M
}
496
497
/*!
498
  @brief Function used to ignore Tags and values stored in them,
499
      since they are not necessary as metadata information
500
  @param buf Data buffer that will contain Tag to compare
501
  @return Returns true, if Tag is found in the ignoreList[]
502
 */
503
1.91M
static bool ignoreList(Exiv2::DataBuf& buf) {
504
1.91M
  const char ignoreList[13][5] = {
505
1.91M
      "mdat", "edts", "junk", "iods", "alis", "stsc", "stsz", "stco", "ctts", "stss", "skip", "wide", "cmvd",
506
1.91M
  };
507
508
1.91M
  for (auto i : ignoreList)
509
24.8M
    if (equalsQTimeTag(buf, i))
510
14.6k
      return true;
511
512
1.90M
  return false;
513
1.91M
}
514
515
/*!
516
  @brief Function used to ignore Tags, basically Tags which
517
      contain other tags inside them, since they are not necessary
518
      as metadata information
519
  @param buf Data buffer that will contain Tag to compare
520
  @return Returns true, if Tag is found in the ignoreList[]
521
 */
522
1.21M
static bool dataIgnoreList(Exiv2::DataBuf& buf) {
523
1.21M
  const char ignoreList[8][5] = {
524
1.21M
      "moov", "mdia", "minf", "dinf", "alis", "stbl", "cmov", "meta",
525
1.21M
  };
526
527
1.21M
  for (auto i : ignoreList)
528
9.66M
    if (equalsQTimeTag(buf, i))
529
570k
      return true;
530
531
643k
  return false;
532
1.21M
}
533
}  // namespace Exiv2::Internal
534
535
namespace Exiv2 {
536
537
using namespace Exiv2::Internal;
538
539
QuickTimeVideo::QuickTimeVideo(BasicIo::UniquePtr io, size_t max_recursion_depth) :
540
9.09k
    Image(ImageType::qtime, mdNone, std::move(io)),
541
9.09k
    mvhdTimeScale_(1),
542
9.09k
    mdhdTimeScale_(1),
543
9.09k
    currentStream_(Null),
544
9.09k
    max_recursion_depth_(max_recursion_depth) {
545
9.09k
}  // QuickTimeVideo::QuickTimeVideo
546
547
10.1k
std::string QuickTimeVideo::mimeType() const {
548
10.1k
  return "video/quicktime";
549
10.1k
}
550
551
223
void QuickTimeVideo::writeMetadata() {
552
223
}
553
554
9.07k
void QuickTimeVideo::readMetadata() {
555
9.07k
  if (io_->open() != 0)
556
0
    throw Error(ErrorCode::kerDataSourceOpenFailed, io_->path(), strError());
557
558
  // Ensure that this is the correct image type
559
9.07k
  if (!isQTimeType(*io_, false)) {
560
0
    if (io_->error() || io_->eof())
561
0
      throw Error(ErrorCode::kerFailedToReadImageData);
562
0
    throw Error(ErrorCode::kerNotAnImage, "QuickTime");
563
0
  }
564
565
9.07k
  IoCloser closer(*io_);
566
9.07k
  clearMetadata();
567
9.07k
  continueTraversing_ = true;
568
9.07k
  height_ = width_ = 1;
569
570
9.07k
  xmpData_["Xmp.video.FileSize"] = static_cast<double>(io_->size()) / 1048576.0;
571
9.07k
  xmpData_["Xmp.video.MimeType"] = mimeType();
572
573
129k
  while (continueTraversing_)
574
120k
    decodeBlock(0);
575
576
9.07k
  xmpData_["Xmp.video.AspectRatio"] = getAspectRatio(width_, height_);
577
9.07k
}  // QuickTimeVideo::readMetadata
578
579
710k
void QuickTimeVideo::decodeBlock(size_t recursion_depth, std::string const& entered_from) {
580
710k
  enforce(recursion_depth < max_recursion_depth_, Exiv2::ErrorCode::kerCorruptedMetadata);
581
582
710k
  const long bufMinSize = 4;
583
710k
  DataBuf buf(bufMinSize + 1);
584
710k
  uint64_t size = 0;
585
710k
  buf.data()[4] = '\0';
586
587
710k
  io_->read(buf.data(), 4);
588
710k
  if (io_->eof()) {
589
893
    continueTraversing_ = false;
590
893
    return;
591
893
  }
592
593
709k
  size = buf.read_uint32(0, bigEndian);
594
595
709k
  io_->readOrThrow(buf.data(), 4);
596
597
  // we have read 2x 4 bytes
598
709k
  size_t hdrsize = 8;
599
600
709k
  if (size == 1) {
601
    // The box size is encoded as a uint64_t, so we need to read another 8 bytes.
602
320
    DataBuf data(8);
603
320
    hdrsize += 8;
604
320
    io_->readOrThrow(data.data(), data.size());
605
320
    size = data.read_uint64(0, bigEndian);
606
709k
  } else if (size == 0 && entered_from == "meta") {
607
13.9k
    size = buf.read_uint32(0, bigEndian);
608
13.9k
    io_->readOrThrow(buf.data(), 4, Exiv2::ErrorCode::kerCorruptedMetadata);
609
13.9k
  }
610
611
709k
  enforce(size >= hdrsize, Exiv2::ErrorCode::kerCorruptedMetadata);
612
709k
  enforce(size - hdrsize <= io_->size() - io_->tell(), Exiv2::ErrorCode::kerCorruptedMetadata);
613
709k
  enforce(size - hdrsize <= std::numeric_limits<size_t>::max(), Exiv2::ErrorCode::kerCorruptedMetadata);
614
615
  // std::cerr<<"Tag=>"<<buf.data()<<"     size=>"<<size-hdrsize << '\n';
616
709k
  const auto newsize = static_cast<size_t>(size - hdrsize);
617
709k
  if (ignoreList(buf)) {
618
14.6k
    discard(newsize);
619
14.6k
    return;
620
14.6k
  }
621
694k
  if (newsize > buf.size()) {
622
654k
    buf.resize(newsize);
623
654k
  }
624
694k
  tagDecoder(buf, newsize, recursion_depth + 1);
625
694k
}  // QuickTimeVideo::decodeBlock
626
627
7.82k
static std::string readString(BasicIo& io, size_t size) {
628
7.82k
  enforce(size <= io.size() - io.tell(), Exiv2::ErrorCode::kerCorruptedMetadata);
629
7.82k
  Exiv2::DataBuf str(size + 1);
630
7.82k
  io.readOrThrow(str.data(), size);
631
7.82k
  str.write_uint8(size, 0);  // nul-terminate string
632
7.82k
  return Exiv2::toString(str.data());
633
7.82k
}
634
635
1.21M
void QuickTimeVideo::tagDecoder(Exiv2::DataBuf& buf, size_t size, size_t recursion_depth) {
636
1.21M
  enforce(recursion_depth < max_recursion_depth_, Exiv2::ErrorCode::kerCorruptedMetadata);
637
1.21M
  assert(buf.size() > 4);
638
639
1.21M
  if (ignoreList(buf))
640
0
    discard(size);
641
642
1.21M
  else if (dataIgnoreList(buf)) {
643
570k
    decodeBlock(recursion_depth + 1, Exiv2::toString(buf.data()));
644
643k
  } else if (equalsQTimeTag(buf, "ftyp"))
645
18.7k
    fileTypeDecoder(size);
646
647
624k
  else if (equalsQTimeTag(buf, "trak"))
648
6.82k
    setMediaStream(size);
649
650
618k
  else if (equalsQTimeTag(buf, "mvhd"))
651
473
    movieHeaderDecoder(size);
652
653
617k
  else if (equalsQTimeTag(buf, "tkhd"))
654
2.99k
    trackHeaderDecoder(size);
655
656
614k
  else if (equalsQTimeTag(buf, "mdhd"))
657
5.29k
    mediaHeaderDecoder(size);
658
659
609k
  else if (equalsQTimeTag(buf, "hdlr"))
660
21.3k
    handlerDecoder(size);
661
662
588k
  else if (equalsQTimeTag(buf, "vmhd"))
663
11.2k
    videoHeaderDecoder(size);
664
665
576k
  else if (equalsQTimeTag(buf, "udta"))
666
469k
    userDataDecoder(size, recursion_depth + 1);
667
668
107k
  else if (equalsQTimeTag(buf, "dref"))
669
9.03k
    multipleEntriesDecoder(recursion_depth + 1);
670
671
98.1k
  else if (equalsQTimeTag(buf, "stsd"))
672
4.45k
    sampleDesc(size);
673
674
93.6k
  else if (equalsQTimeTag(buf, "stts"))
675
2.96k
    timeToSampleDecoder();
676
677
90.6k
  else if (equalsQTimeTag(buf, "pnot"))
678
2.38k
    previewTagDecoder(size);
679
680
88.2k
  else if (equalsQTimeTag(buf, "tapt"))
681
2.29k
    trackApertureTagDecoder(size);
682
683
86.0k
  else if (equalsQTimeTag(buf, "keys"))
684
1.52k
    keysTagDecoder(size);
685
686
84.4k
  else if (equalsQTimeTag(buf, "url ")) {
687
949
    if (currentStream_ == Video)
688
262
      xmpData_["Xmp.video.URL"] = readString(*io_, size);
689
687
    else if (currentStream_ == Audio)
690
347
      xmpData_["Xmp.audio.URL"] = readString(*io_, size);
691
340
    else
692
340
      discard(size);
693
949
  }
694
695
83.5k
  else if (equalsQTimeTag(buf, "urn ")) {
696
3.50k
    if (currentStream_ == Video)
697
2.33k
      xmpData_["Xmp.video.URN"] = readString(*io_, size);
698
1.16k
    else if (currentStream_ == Audio)
699
638
      xmpData_["Xmp.audio.URN"] = readString(*io_, size);
700
531
    else
701
531
      discard(size);
702
3.50k
  }
703
704
80.0k
  else if (equalsQTimeTag(buf, "dcom")) {
705
606
    xmpData_["Xmp.video.Compressor"] = readString(*io_, size);
706
606
  }
707
708
79.4k
  else if (equalsQTimeTag(buf, "smhd")) {
709
800
    io_->readOrThrow(buf.data(), 4);
710
800
    io_->readOrThrow(buf.data(), 4);
711
800
    xmpData_["Xmp.audio.Balance"] = buf.read_uint16(0, bigEndian);
712
800
  }
713
714
78.6k
  else {
715
78.6k
    discard(size);
716
78.6k
  }
717
1.21M
}  // QuickTimeVideo::tagDecoder
718
719
94.1k
void QuickTimeVideo::discard(size_t size) {
720
94.1k
  size_t cur_pos = io_->tell();
721
94.1k
  io_->seek(cur_pos + size, BasicIo::beg);
722
94.1k
}  // QuickTimeVideo::discard
723
724
2.38k
void QuickTimeVideo::previewTagDecoder(size_t size) {
725
2.38k
  DataBuf buf(4);
726
2.38k
  size_t cur_pos = io_->tell();
727
2.38k
  io_->readOrThrow(buf.data(), 4);
728
2.38k
  xmpData_["Xmp.video.PreviewDate"] = buf.read_uint32(0, bigEndian);
729
2.38k
  io_->readOrThrow(buf.data(), 2);
730
2.38k
  xmpData_["Xmp.video.PreviewVersion"] = getShort(buf.data(), bigEndian);
731
732
2.38k
  io_->readOrThrow(buf.data(), 4);
733
2.38k
  if (equalsQTimeTag(buf, "PICT"))
734
239
    xmpData_["Xmp.video.PreviewAtomType"] = "QuickDraw Picture";
735
2.14k
  else
736
2.14k
    xmpData_["Xmp.video.PreviewAtomType"] = std::string{buf.c_str(), 4};
737
738
2.38k
  io_->seek(cur_pos + size, BasicIo::beg);
739
2.38k
}  // QuickTimeVideo::previewTagDecoder
740
741
1.52k
void QuickTimeVideo::keysTagDecoder(size_t size) {
742
1.52k
  DataBuf buf(4);
743
1.52k
  size_t cur_pos = io_->tell();
744
1.52k
  io_->readOrThrow(buf.data(), 4);
745
1.52k
  xmpData_["Xmp.video.PreviewDate"] = buf.read_uint32(0, bigEndian);
746
1.52k
  io_->readOrThrow(buf.data(), 2);
747
1.52k
  xmpData_["Xmp.video.PreviewVersion"] = getShort(buf.data(), bigEndian);
748
749
1.52k
  io_->readOrThrow(buf.data(), 4);
750
1.52k
  if (equalsQTimeTag(buf, "PICT"))
751
47
    xmpData_["Xmp.video.PreviewAtomType"] = "QuickDraw Picture";
752
1.47k
  else
753
1.47k
    xmpData_["Xmp.video.PreviewAtomType"] = std::string{buf.c_str(), 4};
754
755
1.52k
  io_->seek(cur_pos + size, BasicIo::beg);
756
1.52k
}  // QuickTimeVideo::keysTagDecoder
757
758
2.29k
void QuickTimeVideo::trackApertureTagDecoder(size_t size) {
759
2.29k
  DataBuf buf(4);
760
2.29k
  DataBuf buf2(2);
761
2.29k
  size_t cur_pos = io_->tell();
762
2.29k
  byte n = 3;
763
764
9.16k
  while (n--) {
765
6.86k
    io_->seek(4L, BasicIo::cur);
766
6.86k
    io_->readOrThrow(buf.data(), 4);
767
768
6.86k
    if (equalsQTimeTag(buf, "clef")) {
769
565
      io_->seek(4L, BasicIo::cur);
770
565
      io_->readOrThrow(buf.data(), 2);
771
565
      io_->readOrThrow(buf2.data(), 2);
772
565
      xmpData_["Xmp.video.CleanApertureWidth"] =
773
565
          stringFormat("{}.{}", buf.read_uint16(0, bigEndian), buf2.read_uint16(0, bigEndian));
774
565
      io_->readOrThrow(buf.data(), 2);
775
565
      io_->readOrThrow(buf2.data(), 2);
776
565
      xmpData_["Xmp.video.CleanApertureHeight"] =
777
565
          stringFormat("{}.{}", buf.read_uint16(0, bigEndian), buf2.read_uint16(0, bigEndian));
778
565
    }
779
780
6.30k
    else if (equalsQTimeTag(buf, "prof")) {
781
206
      io_->seek(4L, BasicIo::cur);
782
206
      io_->readOrThrow(buf.data(), 2);
783
206
      io_->readOrThrow(buf2.data(), 2);
784
206
      xmpData_["Xmp.video.ProductionApertureWidth"] =
785
206
          stringFormat("{}.{}", buf.read_uint16(0, bigEndian), buf2.read_uint16(0, bigEndian));
786
206
      io_->readOrThrow(buf.data(), 2);
787
206
      io_->readOrThrow(buf2.data(), 2);
788
206
      xmpData_["Xmp.video.ProductionApertureHeight"] =
789
206
          stringFormat("{}.{}", buf.read_uint16(0, bigEndian), buf2.read_uint16(0, bigEndian));
790
206
    }
791
792
6.09k
    else if (equalsQTimeTag(buf, "enof")) {
793
98
      io_->seek(4L, BasicIo::cur);
794
98
      io_->readOrThrow(buf.data(), 2);
795
98
      io_->readOrThrow(buf2.data(), 2);
796
98
      xmpData_["Xmp.video.EncodedPixelsWidth"] =
797
98
          stringFormat("{}.{}", buf.read_uint16(0, bigEndian), buf2.read_uint16(0, bigEndian));
798
98
      io_->readOrThrow(buf.data(), 2);
799
98
      io_->readOrThrow(buf2.data(), 2);
800
98
      xmpData_["Xmp.video.EncodedPixelsHeight"] =
801
98
          stringFormat("{}.{}", buf.read_uint16(0, bigEndian), buf2.read_uint16(0, bigEndian));
802
98
    }
803
6.86k
  }
804
2.29k
  io_->seek(cur_pos + size, BasicIo::beg);
805
2.29k
}  // QuickTimeVideo::trackApertureTagDecoder
806
807
5.52k
void QuickTimeVideo::CameraTagsDecoder(size_t size) {
808
5.52k
  size_t cur_pos = io_->tell();
809
5.52k
  DataBuf buf(50);
810
5.52k
  DataBuf buf2(4);
811
812
5.52k
  io_->readOrThrow(buf.data(), 4);
813
5.52k
  if (equalsQTimeTag(buf, "NIKO")) {
814
4.12k
    io_->seek(cur_pos, BasicIo::beg);
815
816
4.12k
    io_->readOrThrow(buf.data(), 24);
817
4.12k
    xmpData_["Xmp.video.Make"] = buf.data();
818
4.12k
    io_->readOrThrow(buf.data(), 14);
819
4.12k
    xmpData_["Xmp.video.Model"] = buf.data();
820
4.12k
    io_->readOrThrow(buf.data(), 4);
821
4.12k
    xmpData_["Xmp.video.ExposureTime"] = stringFormat("1/{}", std::ceil(buf.read_uint32(0, littleEndian) / 10.0));
822
4.12k
    io_->readOrThrow(buf.data(), 4);
823
4.12k
    io_->readOrThrow(buf2.data(), 4);
824
4.12k
    xmpData_["Xmp.video.FNumber"] =
825
4.12k
        buf.read_uint32(0, littleEndian) / static_cast<double>(buf2.read_uint32(0, littleEndian));
826
4.12k
    io_->readOrThrow(buf.data(), 4);
827
4.12k
    io_->readOrThrow(buf2.data(), 4);
828
4.12k
    xmpData_["Xmp.video.ExposureCompensation"] =
829
4.12k
        buf.read_uint32(0, littleEndian) / static_cast<double>(buf2.read_uint32(0, littleEndian));
830
4.12k
    io_->readOrThrow(buf.data(), 10);
831
4.12k
    io_->readOrThrow(buf.data(), 4);
832
4.12k
    if (auto td = Exiv2::find(whiteBalance, buf.read_uint32(0, littleEndian)))
833
1.94k
      xmpData_["Xmp.video.WhiteBalance"] = _(td->label_);
834
4.12k
    io_->readOrThrow(buf.data(), 4);
835
4.12k
    io_->readOrThrow(buf2.data(), 4);
836
4.12k
    xmpData_["Xmp.video.FocalLength"] =
837
4.12k
        buf.read_uint32(0, littleEndian) / static_cast<double>(buf2.read_uint32(0, littleEndian));
838
4.12k
    io_->seek(95L, BasicIo::cur);
839
4.12k
    io_->readOrThrow(buf.data(), 48);
840
4.12k
    buf.write_uint8(48, 0);
841
4.12k
    xmpData_["Xmp.video.Software"] = buf.data();
842
4.12k
    io_->readOrThrow(buf.data(), 4);
843
4.12k
    xmpData_["Xmp.video.ISO"] = buf.read_uint32(0, littleEndian);
844
4.12k
  }
845
846
5.52k
  io_->seek(cur_pos + size, BasicIo::beg);
847
5.52k
}  // QuickTimeVideo::CameraTagsDecoder
848
849
470k
void QuickTimeVideo::userDataDecoder(size_t outer_size, size_t recursion_depth) {
850
470k
  enforce(recursion_depth < max_recursion_depth_, Exiv2::ErrorCode::kerCorruptedMetadata);
851
470k
  size_t cur_pos = io_->tell();
852
470k
  const TagVocabulary* td;
853
470k
  const TagVocabulary* tv;
854
470k
  const TagVocabulary* tv_internal;
855
856
470k
  const long bufMinSize = 100;
857
470k
  DataBuf buf(bufMinSize);
858
470k
  size_t size_internal = outer_size;
859
470k
  std::memset(buf.data(), 0x0, buf.size());
860
861
1.05M
  while ((size_internal / 4 != 0) && (size_internal > 0)) {
862
1.04M
    buf.data()[4] = '\0';
863
1.04M
    io_->readOrThrow(buf.data(), 4);
864
1.04M
    const size_t size = buf.read_uint32(0, bigEndian);
865
1.04M
    if (size > size_internal)
866
446k
      break;
867
596k
    size_internal -= size;
868
596k
    io_->readOrThrow(buf.data(), 4);
869
870
596k
    if (buf.data()[0] == 169)
871
272
      buf.data()[0] = ' ';
872
596k
    td = Exiv2::find(userDatatags, Exiv2::toString(buf.data()));
873
874
596k
    tv = Exiv2::find(userDataReferencetags, Exiv2::toString(buf.data()));
875
876
596k
    if (size <= 12)
877
9.01k
      break;
878
879
587k
    if (equalsQTimeTag(buf, "DcMD") || equalsQTimeTag(buf, "NCDT"))
880
580
      userDataDecoder(size - 8, recursion_depth + 1);
881
882
587k
    else if (equalsQTimeTag(buf, "NCTG"))
883
10.7k
      NikonTagsDecoder(size - 8);
884
885
576k
    else if (equalsQTimeTag(buf, "TAGS"))
886
5.52k
      CameraTagsDecoder(size - 8);
887
888
570k
    else if (equalsQTimeTag(buf, "CNCV") || equalsQTimeTag(buf, "CNFV") || equalsQTimeTag(buf, "CNMN") ||
889
568k
             equalsQTimeTag(buf, "NCHD") || equalsQTimeTag(buf, "FFMV")) {
890
2.38k
      enforce(tv, Exiv2::ErrorCode::kerCorruptedMetadata);
891
2.38k
      xmpData_[_(tv->label_)] = readString(*io_, size - 8);
892
2.38k
    }
893
894
568k
    else if (equalsQTimeTag(buf, "CMbo") || equalsQTimeTag(buf, "Cmbo")) {
895
643
      enforce(tv, Exiv2::ErrorCode::kerCorruptedMetadata);
896
643
      io_->readOrThrow(buf.data(), 2);
897
643
      buf.data()[2] = '\0';
898
643
      tv_internal = Exiv2::find(cameraByteOrderTags, Exiv2::toString(buf.data()));
899
900
643
      if (tv_internal)
901
357
        xmpData_[_(tv->label_)] = _(tv_internal->label_);
902
286
      else
903
286
        xmpData_[_(tv->label_)] = buf.data();
904
643
    }
905
906
567k
    else if (tv) {
907
1.27k
      io_->readOrThrow(buf.data(), 4);
908
1.27k
      xmpData_[_(tv->label_)] = readString(*io_, size - 12);
909
1.27k
    }
910
911
566k
    else if (td)
912
524k
      tagDecoder(buf, size - 8, recursion_depth + 1);
913
587k
  }
914
915
470k
  io_->seek(cur_pos + outer_size, BasicIo::beg);
916
470k
}  // QuickTimeVideo::userDataDecoder
917
918
10.7k
void QuickTimeVideo::NikonTagsDecoder(size_t size) {
919
10.7k
  size_t cur_pos = io_->tell();
920
10.7k
  DataBuf buf(201);
921
10.7k
  DataBuf buf2(4 + 1);
922
10.7k
  uint32_t TagID = 0;
923
10.7k
  uint16_t dataLength = 0;
924
10.7k
  uint16_t dataType = 2;
925
10.7k
  const TagDetails* td;
926
10.7k
  const TagDetails* td2;
927
928
952k
  for (int i = 0; i < 100; i++) {
929
941k
    io_->readOrThrow(buf.data(), 4);
930
941k
    TagID = buf.read_uint32(0, bigEndian);
931
941k
    td = Exiv2::find(NikonNCTGTags, TagID);
932
933
941k
    io_->readOrThrow(buf.data(), 2);
934
941k
    dataType = buf.read_uint16(0, bigEndian);
935
936
941k
    std::memset(buf.data(), 0x0, buf.size());
937
941k
    io_->readOrThrow(buf.data(), 2);
938
939
941k
    if (TagID == 0x2000023) {
940
21.5k
      size_t local_pos = io_->tell();
941
21.5k
      dataLength = buf.read_uint16(0, bigEndian);
942
21.5k
      std::memset(buf.data(), 0x0, buf.size());
943
944
21.5k
      io_->readOrThrow(buf.data(), 4);
945
21.5k
      xmpData_["Xmp.video.PictureControlVersion"] = buf.data();
946
21.5k
      io_->readOrThrow(buf.data(), 20);
947
21.5k
      xmpData_["Xmp.video.PictureControlName"] = buf.data();
948
21.5k
      io_->readOrThrow(buf.data(), 20);
949
21.5k
      xmpData_["Xmp.video.PictureControlBase"] = buf.data();
950
21.5k
      io_->readOrThrow(buf.data(), 4);
951
21.5k
      std::memset(buf.data(), 0x0, buf.size());
952
953
21.5k
      io_->readOrThrow(buf.data(), 1);
954
21.5k
      td2 = Exiv2::find(PictureControlAdjust, static_cast<int>(buf.data()[0]) & 7);
955
21.5k
      if (td2)
956
13.6k
        xmpData_["Xmp.video.PictureControlAdjust"] = _(td2->label_);
957
7.88k
      else
958
7.88k
        xmpData_["Xmp.video.PictureControlAdjust"] = static_cast<int>(buf.data()[0]) & 7;
959
960
21.5k
      io_->readOrThrow(buf.data(), 1);
961
21.5k
      td2 = Exiv2::find(NormalSoftHard, static_cast<int>(buf.data()[0]) & 7);
962
21.5k
      if (td2)
963
13.4k
        xmpData_["Xmp.video.PictureControlQuickAdjust"] = _(td2->label_);
964
965
21.5k
      io_->readOrThrow(buf.data(), 1);
966
21.5k
      td2 = Exiv2::find(NormalSoftHard, static_cast<int>(buf.data()[0]) & 7);
967
21.5k
      if (td2)
968
14.2k
        xmpData_["Xmp.video.Sharpness"] = _(td2->label_);
969
7.33k
      else
970
7.33k
        xmpData_["Xmp.video.Sharpness"] = static_cast<int>(buf.data()[0]) & 7;
971
972
21.5k
      io_->readOrThrow(buf.data(), 1);
973
21.5k
      td2 = Exiv2::find(NormalSoftHard, static_cast<int>(buf.data()[0]) & 7);
974
21.5k
      if (td2)
975
11.7k
        xmpData_["Xmp.video.Contrast"] = _(td2->label_);
976
9.84k
      else
977
9.84k
        xmpData_["Xmp.video.Contrast"] = static_cast<int>(buf.data()[0]) & 7;
978
979
21.5k
      io_->readOrThrow(buf.data(), 1);
980
21.5k
      td2 = Exiv2::find(NormalSoftHard, static_cast<int>(buf.data()[0]) & 7);
981
21.5k
      if (td2)
982
13.6k
        xmpData_["Xmp.video.Brightness"] = _(td2->label_);
983
7.94k
      else
984
7.94k
        xmpData_["Xmp.video.Brightness"] = static_cast<int>(buf.data()[0]) & 7;
985
986
21.5k
      io_->readOrThrow(buf.data(), 1);
987
21.5k
      td2 = Exiv2::find(Saturation, static_cast<int>(buf.data()[0]) & 7);
988
21.5k
      if (td2)
989
12.3k
        xmpData_["Xmp.video.Saturation"] = _(td2->label_);
990
9.23k
      else
991
9.23k
        xmpData_["Xmp.video.Saturation"] = static_cast<int>(buf.data()[0]) & 7;
992
993
21.5k
      io_->readOrThrow(buf.data(), 1);
994
21.5k
      xmpData_["Xmp.video.HueAdjustment"] = static_cast<int>(buf.data()[0]) & 7;
995
996
21.5k
      io_->readOrThrow(buf.data(), 1);
997
21.5k
      td2 = Exiv2::find(FilterEffect, static_cast<int>(buf.data()[0]));
998
21.5k
      if (td2)
999
3.50k
        xmpData_["Xmp.video.FilterEffect"] = _(td2->label_);
1000
18.0k
      else
1001
18.0k
        xmpData_["Xmp.video.FilterEffect"] = static_cast<int>(buf.data()[0]);
1002
1003
21.5k
      io_->readOrThrow(buf.data(), 1);
1004
21.5k
      td2 = Exiv2::find(ToningEffect, static_cast<int>(buf.data()[0]));
1005
21.5k
      if (td2)
1006
5.47k
        xmpData_["Xmp.video.ToningEffect"] = _(td2->label_);
1007
16.0k
      else
1008
16.0k
        xmpData_["Xmp.video.ToningEffect"] = static_cast<int>(buf.data()[0]);
1009
1010
21.5k
      io_->readOrThrow(buf.data(), 1);
1011
21.5k
      xmpData_["Xmp.video.ToningSaturation"] = static_cast<int>(buf.data()[0]);
1012
1013
21.5k
      io_->seek(local_pos + dataLength, BasicIo::beg);
1014
21.5k
    }
1015
1016
919k
    else if (TagID == 0x2000024) {
1017
4.76k
      size_t local_pos = io_->tell();
1018
4.76k
      dataLength = buf.read_uint16(0, bigEndian);
1019
4.76k
      std::memset(buf.data(), 0x0, buf.size());
1020
1021
4.76k
      io_->readOrThrow(buf.data(), 2);
1022
4.76k
      xmpData_["Xmp.video.TimeZone"] = Exiv2::getShort(buf.data(), bigEndian);
1023
4.76k
      io_->readOrThrow(buf.data(), 1);
1024
4.76k
      td2 = Exiv2::find(YesNo, static_cast<int>(buf.data()[0]));
1025
4.76k
      if (td2)
1026
3.66k
        xmpData_["Xmp.video.DayLightSavings"] = _(td2->label_);
1027
1028
4.76k
      io_->readOrThrow(buf.data(), 1);
1029
4.76k
      td2 = Exiv2::find(DateDisplayFormat, static_cast<int>(buf.data()[0]));
1030
4.76k
      if (td2)
1031
1.90k
        xmpData_["Xmp.video.DateDisplayFormat"] = _(td2->label_);
1032
1033
4.76k
      io_->seek(local_pos + dataLength, BasicIo::beg);
1034
4.76k
    }
1035
1036
915k
    else if (dataType == 2 || dataType == 7) {
1037
11.6k
      dataLength = buf.read_uint16(0, bigEndian);
1038
11.6k
      std::memset(buf.data(), 0x0, buf.size());
1039
1040
      // Sanity check with an "unreasonably" large number
1041
11.6k
      if (dataLength >= buf.size()) {
1042
4.30k
#ifndef SUPPRESS_WARNINGS
1043
4.30k
        EXV_ERROR << "Xmp.video Nikon Tags, dataLength was found to be larger than 200."
1044
0
                  << " Entries considered invalid. Not Processed.\n";
1045
4.30k
#endif
1046
4.30k
        io_->seek(io_->tell() + dataLength, BasicIo::beg);
1047
4.30k
        buf.data()[0] = '\0';
1048
7.34k
      } else {
1049
7.34k
        io_->readOrThrow(buf.data(), dataLength);
1050
7.34k
        buf.data()[dataLength] = '\0';
1051
7.34k
      }
1052
1053
11.6k
      if (td) {
1054
1.93k
        xmpData_[_(td->label_)] = buf.data();
1055
1.93k
      }
1056
903k
    } else if (dataType == 4) {
1057
8.59k
      dataLength = buf.read_uint16(0, bigEndian) * 4;
1058
8.59k
      std::memset(buf.data(), 0x0, buf.size());
1059
8.59k
      io_->readOrThrow(buf.data(), 4);
1060
8.59k
      if (td)
1061
1.65k
        xmpData_[_(td->label_)] = buf.read_uint32(0, bigEndian);
1062
1063
      // Sanity check with an "unreasonably" large number
1064
8.59k
      if (dataLength > 200 || dataLength < 4) {
1065
7.76k
#ifndef SUPPRESS_WARNINGS
1066
7.76k
        EXV_ERROR << "Xmp.video Nikon Tags, dataLength was found to be of inappropriate size."
1067
0
                  << " Entries considered invalid. Not Processed.\n";
1068
7.76k
#endif
1069
7.76k
        io_->seek(io_->tell() + dataLength - 4, BasicIo::beg);
1070
7.76k
      } else
1071
829
        io_->readOrThrow(buf.data(), dataLength - 4);
1072
894k
    } else if (dataType == 3) {
1073
6.45k
      dataLength = buf.read_uint16(0, bigEndian) * 2;
1074
6.45k
      std::memset(buf.data(), 0x0, buf.size());
1075
6.45k
      io_->readOrThrow(buf.data(), 2);
1076
6.45k
      if (td)
1077
2.29k
        xmpData_[_(td->label_)] = buf.read_uint16(0, bigEndian);
1078
1079
      // Sanity check with an "unreasonably" large number
1080
6.45k
      if (dataLength > 200 || dataLength < 2) {
1081
5.90k
#ifndef SUPPRESS_WARNINGS
1082
5.90k
        EXV_ERROR << "Xmp.video Nikon Tags, dataLength was found to be of inappropriate size."
1083
0
                  << " Entries considered invalid. Not Processed.\n";
1084
5.90k
#endif
1085
5.90k
        io_->seek(io_->tell() + dataLength - 2, BasicIo::beg);
1086
5.90k
      } else
1087
549
        io_->readOrThrow(buf.data(), dataLength - 2);
1088
888k
    } else if (dataType == 5) {
1089
3.79k
      dataLength = buf.read_uint16(0, bigEndian) * 8;
1090
3.79k
      std::memset(buf.data(), 0x0, buf.size());
1091
3.79k
      io_->readOrThrow(buf.data(), 4);
1092
3.79k
      io_->readOrThrow(buf2.data(), 4);
1093
3.79k
      if (td)
1094
1.40k
        xmpData_[_(td->label_)] =
1095
1.40k
            static_cast<double>(buf.read_uint32(0, bigEndian)) / static_cast<double>(buf2.read_uint32(0, bigEndian));
1096
1097
      // Sanity check with an "unreasonably" large number
1098
3.79k
      if (dataLength > 200 || dataLength < 8) {
1099
3.35k
#ifndef SUPPRESS_WARNINGS
1100
3.35k
        EXV_ERROR << "Xmp.video Nikon Tags, dataLength was found to be of inappropriate size."
1101
0
                  << " Entries considered invalid. Not Processed.\n";
1102
3.35k
#endif
1103
3.35k
        io_->seek(io_->tell() + dataLength - 8, BasicIo::beg);
1104
3.35k
      } else
1105
448
        io_->readOrThrow(buf.data(), dataLength - 8);
1106
884k
    } else if (dataType == 8) {
1107
8.18k
      dataLength = buf.read_uint16(0, bigEndian) * 2;
1108
8.18k
      std::memset(buf.data(), 0x0, buf.size());
1109
8.18k
      io_->readOrThrow(buf.data(), 2);
1110
8.18k
      io_->readOrThrow(buf2.data(), 2);
1111
8.18k
      if (td)
1112
4.24k
        xmpData_[_(td->label_)] = stringFormat("{}.{}", buf.read_uint16(0, bigEndian), buf2.read_uint16(0, bigEndian));
1113
1114
      // Sanity check with an "unreasonably" large number
1115
8.18k
      if (dataLength > 200 || dataLength < 4) {
1116
7.26k
#ifndef SUPPRESS_WARNINGS
1117
7.26k
        EXV_ERROR << "Xmp.video Nikon Tags, dataLength was found to be of inappropriate size."
1118
0
                  << " Entries considered invalid. Not Processed.\n";
1119
7.26k
#endif
1120
7.26k
        io_->seek(io_->tell() + dataLength - 4, BasicIo::beg);
1121
7.26k
      } else
1122
925
        io_->readOrThrow(buf.data(), dataLength - 4);
1123
8.18k
    }
1124
941k
  }
1125
1126
10.7k
  io_->seek(cur_pos + size, BasicIo::beg);
1127
10.7k
}  // QuickTimeVideo::NikonTagsDecoder
1128
1129
6.82k
void QuickTimeVideo::setMediaStream(size_t atom_size) {
1130
6.82k
  size_t current_position = io_->tell();
1131
6.82k
  size_t search_end = Safe::add(current_position, atom_size);
1132
6.82k
  if (search_end > io_->size())
1133
0
    search_end = io_->size();
1134
6.82k
  DataBuf buf(4 + 1);
1135
1136
193k
  while (!io_->eof() && Safe::add(io_->tell(), size_t{4}) <= search_end) {
1137
189k
    io_->readOrThrow(buf.data(), 4);
1138
189k
    if (equalsQTimeTag(buf, "hdlr")) {
1139
3.02k
      if (Safe::add(io_->tell(), size_t{12}) > search_end)
1140
201
        break;
1141
2.82k
      io_->readOrThrow(buf.data(), 4);
1142
2.82k
      io_->readOrThrow(buf.data(), 4);
1143
2.82k
      io_->readOrThrow(buf.data(), 4);
1144
1145
2.82k
      if (equalsQTimeTag(buf, "vide"))
1146
105
        currentStream_ = Video;
1147
2.71k
      else if (equalsQTimeTag(buf, "soun"))
1148
1.24k
        currentStream_ = Audio;
1149
1.47k
      else if (equalsQTimeTag(buf, "hint"))
1150
9
        currentStream_ = Hint;
1151
1.46k
      else
1152
1.46k
        currentStream_ = GenMediaHeader;
1153
2.82k
      break;
1154
3.02k
    }
1155
189k
  }
1156
1157
6.82k
  io_->seek(current_position, BasicIo::beg);
1158
6.82k
}  // QuickTimeVideo::setMediaStream
1159
1160
2.96k
void QuickTimeVideo::timeToSampleDecoder() {
1161
2.96k
  DataBuf buf(4 + 1);
1162
2.96k
  io_->readOrThrow(buf.data(), 4);
1163
2.96k
  io_->readOrThrow(buf.data(), 4);
1164
2.96k
  uint64_t totalframes = 0;
1165
2.96k
  uint64_t timeOfFrames = 0;
1166
2.96k
  const uint32_t noOfEntries = buf.read_uint32(0, bigEndian);
1167
1168
7.10k
  for (uint32_t i = 0; i < noOfEntries; i++) {
1169
4.13k
    io_->readOrThrow(buf.data(), 4);
1170
4.13k
    const uint64_t temp = buf.read_uint32(0, bigEndian);
1171
4.13k
    totalframes = Safe::add(totalframes, temp);
1172
4.13k
    io_->readOrThrow(buf.data(), 4);
1173
4.13k
    timeOfFrames = Safe::add(timeOfFrames, temp * buf.read_uint32(0, bigEndian));
1174
4.13k
  }
1175
2.96k
  if (currentStream_ == Video) {
1176
852
    if (timeOfFrames == 0)
1177
138
      timeOfFrames = 1;
1178
852
    xmpData_["Xmp.video.FrameRate"] =
1179
852
        static_cast<double>(totalframes) * static_cast<double>(mdhdTimeScale_) / static_cast<double>(timeOfFrames);
1180
852
  }
1181
2.96k
}  // QuickTimeVideo::timeToSampleDecoder
1182
1183
4.45k
void QuickTimeVideo::sampleDesc(size_t size) {
1184
4.45k
  DataBuf buf(100);
1185
4.45k
  size_t cur_pos = io_->tell();
1186
4.45k
  io_->readOrThrow(buf.data(), 4);
1187
4.45k
  io_->readOrThrow(buf.data(), 4);
1188
4.45k
  const uint32_t noOfEntries = buf.read_uint32(0, bigEndian);
1189
1190
295k
  for (uint32_t i = 0; i < noOfEntries; i++) {
1191
292k
    if (currentStream_ == Video)
1192
177k
      imageDescDecoder();
1193
114k
    else if (currentStream_ == Audio)
1194
113k
      audioDescDecoder();
1195
1.44k
    else
1196
1.44k
      break;
1197
292k
  }
1198
4.45k
  io_->seek(Safe::add(cur_pos, size), BasicIo::beg);
1199
4.45k
}  // QuickTimeVideo::sampleDesc
1200
1201
113k
void QuickTimeVideo::audioDescDecoder() {
1202
113k
  DataBuf buf(40);
1203
113k
  std::memset(buf.data(), 0x0, buf.size());
1204
113k
  buf.data()[4] = '\0';
1205
113k
  io_->readOrThrow(buf.data(), 4);
1206
113k
  size_t size = 82;
1207
1208
113k
  const TagVocabulary* td;
1209
1210
2.37M
  for (int i = 0; size / 4 != 0; size -= 4, i++) {
1211
2.26M
    io_->readOrThrow(buf.data(), 4);
1212
2.26M
    switch (i) {
1213
113k
      case AudioFormat:
1214
113k
        td = Exiv2::find(qTimeFileType, Exiv2::toString(buf.data()));
1215
113k
        if (td)
1216
635
          xmpData_["Xmp.audio.Compressor"] = _(td->label_);
1217
112k
        else
1218
112k
          xmpData_["Xmp.audio.Compressor"] = buf.data();
1219
113k
        break;
1220
113k
      case AudioVendorID:
1221
113k
        td = Exiv2::find(vendorIDTags, Exiv2::toString(buf.data()));
1222
113k
        if (td)
1223
1.21k
          xmpData_["Xmp.audio.VendorID"] = _(td->label_);
1224
113k
        break;
1225
113k
      case AudioChannels:
1226
113k
        xmpData_["Xmp.audio.ChannelType"] = buf.read_uint16(0, bigEndian);
1227
113k
        xmpData_["Xmp.audio.BitsPerSample"] = ((buf.data()[2] * 256) + buf.data()[3]);
1228
113k
        break;
1229
113k
      case AudioSampleRate:
1230
113k
        xmpData_["Xmp.audio.SampleRate"] =
1231
113k
            buf.read_uint16(0, bigEndian) + ((buf.data()[2] * 256 + buf.data()[3]) * 0.01);
1232
113k
        break;
1233
1.80M
      default:
1234
1.80M
        break;
1235
2.26M
    }
1236
2.26M
  }
1237
113k
  io_->readOrThrow(buf.data(), static_cast<long>(size % 4));  // cause size is so small, this cast should be right.
1238
113k
}  // QuickTimeVideo::audioDescDecoder
1239
1240
177k
void QuickTimeVideo::imageDescDecoder() {
1241
177k
  DataBuf buf(40);
1242
177k
  std::memset(buf.data(), 0x0, buf.size());
1243
177k
  buf.data()[4] = '\0';
1244
177k
  io_->readOrThrow(buf.data(), 4);
1245
177k
  size_t size = 82;
1246
1247
177k
  const TagVocabulary* td;
1248
1249
2.12M
  for (int i = 0; size / 4 != 0; size -= 4, i++) {
1250
1.95M
    io_->readOrThrow(buf.data(), 4);
1251
1252
1.95M
    switch (i) {
1253
177k
      case codec:
1254
177k
        td = Exiv2::find(qTimeFileType, Exiv2::toString(buf.data()));
1255
177k
        if (td)
1256
3.19k
          xmpData_["Xmp.video.Codec"] = _(td->label_);
1257
174k
        else
1258
174k
          xmpData_["Xmp.video.Codec"] = buf.data();
1259
177k
        break;
1260
177k
      case VendorID:
1261
177k
        td = Exiv2::find(vendorIDTags, Exiv2::toString(buf.data()));
1262
177k
        if (td)
1263
2.37k
          xmpData_["Xmp.video.VendorID"] = _(td->label_);
1264
177k
        break;
1265
177k
      case SourceImageWidth_Height:
1266
177k
        xmpData_["Xmp.video.SourceImageWidth"] = buf.read_uint16(0, bigEndian);
1267
177k
        xmpData_["Xmp.video.SourceImageHeight"] = ((buf.data()[2] * 256) + buf.data()[3]);
1268
177k
        break;
1269
177k
      case XResolution:
1270
177k
        xmpData_["Xmp.video.XResolution"] =
1271
177k
            buf.read_uint16(0, bigEndian) + ((buf.data()[2] * 256 + buf.data()[3]) * 0.01);
1272
177k
        break;
1273
177k
      case YResolution:
1274
177k
        xmpData_["Xmp.video.YResolution"] =
1275
177k
            buf.read_uint16(0, bigEndian) + ((buf.data()[2] * 256 + buf.data()[3]) * 0.01);
1276
177k
        io_->readOrThrow(buf.data(), 3);
1277
177k
        size -= 3;
1278
177k
        break;
1279
177k
      case CompressorName:
1280
177k
        io_->readOrThrow(buf.data(), 32);
1281
177k
        size -= 32;
1282
177k
        xmpData_["Xmp.video.Compressor"] = buf.data();
1283
177k
        break;
1284
886k
      default:
1285
886k
        break;
1286
1.95M
    }
1287
1.95M
  }
1288
177k
  io_->readOrThrow(buf.data(), static_cast<long>(size % 4));
1289
177k
  xmpData_["Xmp.video.BitDepth"] = static_cast<int>(buf.read_uint8(0));
1290
177k
}  // QuickTimeVideo::imageDescDecoder
1291
1292
9.03k
void QuickTimeVideo::multipleEntriesDecoder(size_t recursion_depth) {
1293
9.03k
  enforce(recursion_depth < max_recursion_depth_, Exiv2::ErrorCode::kerCorruptedMetadata);
1294
9.03k
  DataBuf buf(4 + 1);
1295
9.03k
  io_->readOrThrow(buf.data(), 4);
1296
9.03k
  io_->readOrThrow(buf.data(), 4);
1297
9.03k
  uint32_t noOfEntries;
1298
1299
9.03k
  noOfEntries = buf.read_uint32(0, bigEndian);
1300
1301
28.7k
  for (uint32_t i = 0; i < noOfEntries && continueTraversing_; i++) {
1302
19.7k
    decodeBlock(recursion_depth + 1);
1303
19.7k
  }
1304
9.03k
}  // QuickTimeVideo::multipleEntriesDecoder
1305
1306
11.2k
void QuickTimeVideo::videoHeaderDecoder(size_t size) {
1307
11.2k
  DataBuf buf(3);
1308
11.2k
  std::memset(buf.data(), 0x0, buf.size());
1309
11.2k
  buf.data()[2] = '\0';
1310
11.2k
  currentStream_ = Video;
1311
1312
11.2k
  const TagDetails* td;
1313
1314
84.6k
  for (int i = 0; size / 2 != 0; size -= 2, i++) {
1315
73.3k
    io_->readOrThrow(buf.data(), 2);
1316
1317
73.3k
    switch (i) {
1318
10.4k
      case GraphicsMode:
1319
10.4k
        td = Exiv2::find(graphicsModetags, buf.read_uint16(0, bigEndian));
1320
10.4k
        if (td)
1321
8.65k
          xmpData_["Xmp.video.GraphicsMode"] = _(td->label_);
1322
10.4k
        break;
1323
8.35k
      case OpColor:
1324
8.35k
        xmpData_["Xmp.video.OpColor"] = buf.read_uint16(0, bigEndian);
1325
8.35k
        break;
1326
54.5k
      default:
1327
54.5k
        break;
1328
73.3k
    }
1329
73.3k
  }
1330
11.2k
  io_->readOrThrow(buf.data(), size % 2);
1331
11.2k
}  // QuickTimeVideo::videoHeaderDecoder
1332
1333
21.3k
void QuickTimeVideo::handlerDecoder(size_t size) {
1334
21.3k
  size_t cur_pos = io_->tell();
1335
21.3k
  DataBuf buf(100);
1336
21.3k
  std::memset(buf.data(), 0x0, buf.size());
1337
21.3k
  buf.data()[4] = '\0';
1338
1339
21.3k
  const TagVocabulary* tv;
1340
1341
128k
  for (int i = 0; i < 5; i++) {
1342
106k
    io_->readOrThrow(buf.data(), 4);
1343
1344
106k
    switch (i) {
1345
21.3k
      case HandlerClass:
1346
21.3k
        tv = Exiv2::find(handlerClassTags, Exiv2::toString(buf.data()));
1347
21.3k
        if (tv) {
1348
4.70k
          if (currentStream_ == Video)
1349
1.51k
            xmpData_["Xmp.video.HandlerClass"] = _(tv->label_);
1350
3.19k
          else if (currentStream_ == Audio)
1351
1.09k
            xmpData_["Xmp.audio.HandlerClass"] = _(tv->label_);
1352
4.70k
        }
1353
21.3k
        break;
1354
21.3k
      case HandlerType:
1355
21.3k
        tv = Exiv2::find(handlerTypeTags, Exiv2::toString(buf.data()));
1356
21.3k
        if (tv) {
1357
13.2k
          if (currentStream_ == Video)
1358
2.05k
            xmpData_["Xmp.video.HandlerType"] = _(tv->label_);
1359
11.1k
          else if (currentStream_ == Audio)
1360
1.95k
            xmpData_["Xmp.audio.HandlerType"] = _(tv->label_);
1361
13.2k
        }
1362
21.3k
        break;
1363
21.3k
      case HandlerVendorID:
1364
21.3k
        tv = Exiv2::find(vendorIDTags, Exiv2::toString(buf.data()));
1365
21.3k
        if (tv) {
1366
7.36k
          if (currentStream_ == Video)
1367
335
            xmpData_["Xmp.video.HandlerVendorID"] = _(tv->label_);
1368
7.03k
          else if (currentStream_ == Audio)
1369
1.10k
            xmpData_["Xmp.audio.HandlerVendorID"] = _(tv->label_);
1370
7.36k
        }
1371
21.3k
        break;
1372
106k
    }
1373
106k
  }
1374
21.3k
  io_->seek(cur_pos + size, BasicIo::beg);
1375
21.3k
}  // QuickTimeVideo::handlerDecoder
1376
1377
18.7k
void QuickTimeVideo::fileTypeDecoder(size_t size) {
1378
18.7k
  DataBuf buf(5);
1379
18.7k
  std::memset(buf.data(), 0x0, buf.size());
1380
18.7k
  buf.data()[4] = '\0';
1381
18.7k
  Exiv2::Value::UniquePtr v = Exiv2::Value::create(Exiv2::xmpSeq);
1382
18.7k
  const TagVocabulary* td;
1383
1384
569k
  for (int i = 0; size / 4 != 0; size -= 4, i++) {
1385
550k
    io_->readOrThrow(buf.data(), 4);
1386
550k
    td = Exiv2::find(qTimeFileType, Exiv2::toString(buf.data()));
1387
1388
550k
    switch (i) {
1389
18.3k
      case 0:
1390
18.3k
        if (td)
1391
12.6k
          xmpData_["Xmp.video.MajorBrand"] = _(td->label_);
1392
18.3k
        break;
1393
8.73k
      case 1:
1394
8.73k
        xmpData_["Xmp.video.MinorVersion"] = buf.read_uint32(0, bigEndian);
1395
8.73k
        break;
1396
523k
      default:
1397
523k
        if (td)
1398
4.31k
          v->read(_(td->label_));
1399
518k
        else
1400
518k
          v->read(Exiv2::toString(buf.data()));
1401
523k
        break;
1402
550k
    }
1403
550k
  }
1404
18.7k
  xmpData_.add(Exiv2::XmpKey("Xmp.video.CompatibleBrands"), v.get());
1405
18.7k
  io_->readOrThrow(buf.data(), size % 4);
1406
18.7k
}  // QuickTimeVideo::fileTypeDecoder
1407
1408
5.29k
void QuickTimeVideo::mediaHeaderDecoder(size_t size) {
1409
5.29k
  DataBuf buf(5);
1410
5.29k
  std::memset(buf.data(), 0x0, buf.size());
1411
5.29k
  buf.data()[4] = '\0';
1412
5.29k
  int64_t time_scale = 1;
1413
1414
31.1k
  for (int i = 0; size / 4 != 0; size -= 4, i++) {
1415
25.8k
    io_->readOrThrow(buf.data(), 4);
1416
1417
25.8k
    switch (i) {
1418
4.89k
      case MediaHeaderVersion:
1419
4.89k
        if (currentStream_ == Video)
1420
2.28k
          xmpData_["Xmp.video.MediaHeaderVersion"] = static_cast<int>(buf.read_uint8(0));
1421
2.61k
        else if (currentStream_ == Audio)
1422
533
          xmpData_["Xmp.audio.MediaHeaderVersion"] = static_cast<int>(buf.read_uint8(0));
1423
4.89k
        break;
1424
3.77k
      case MediaCreateDate:
1425
        // A 32-bit integer that specifies (in seconds since midnight, January 1, 1904) when the movie atom was created.
1426
3.77k
        if (currentStream_ == Video)
1427
1.43k
          xmpData_["Xmp.video.MediaCreateDate"] = buf.read_uint32(0, bigEndian);
1428
2.34k
        else if (currentStream_ == Audio)
1429
533
          xmpData_["Xmp.audio.MediaCreateDate"] = buf.read_uint32(0, bigEndian);
1430
3.77k
        break;
1431
3.77k
      case MediaModifyDate:
1432
        // A 32-bit integer that specifies (in seconds since midnight, January 1, 1904) when the movie atom was created.
1433
3.77k
        if (currentStream_ == Video)
1434
1.43k
          xmpData_["Xmp.video.MediaModifyDate"] = buf.read_uint32(0, bigEndian);
1435
2.34k
        else if (currentStream_ == Audio)
1436
533
          xmpData_["Xmp.audio.MediaModifyDate"] = buf.read_uint32(0, bigEndian);
1437
3.77k
        break;
1438
3.76k
      case MediaTimeScale:
1439
3.76k
        if (currentStream_ == Video)
1440
1.42k
          xmpData_["Xmp.video.MediaTimeScale"] = buf.read_uint32(0, bigEndian);
1441
2.33k
        else if (currentStream_ == Audio)
1442
533
          xmpData_["Xmp.audio.MediaTimeScale"] = buf.read_uint32(0, bigEndian);
1443
3.76k
        time_scale = std::max(1U, buf.read_uint32(0, bigEndian));
1444
3.76k
        mdhdTimeScale_ = time_scale;
1445
3.76k
        break;
1446
3.75k
      case MediaDuration:
1447
3.75k
        if (currentStream_ == Video)
1448
1.42k
          xmpData_["Xmp.video.MediaDuration"] = time_scale ? buf.read_uint32(0, bigEndian) / time_scale : 0;
1449
2.33k
        else if (currentStream_ == Audio)
1450
533
          xmpData_["Xmp.audio.MediaDuration"] = time_scale ? buf.read_uint32(0, bigEndian) / time_scale : 0;
1451
3.75k
        break;
1452
3.75k
      case MediaLanguageCode:
1453
3.75k
        if (currentStream_ == Video)
1454
1.42k
          xmpData_["Xmp.video.MediaLangCode"] = buf.read_uint16(0, bigEndian);
1455
2.33k
        else if (currentStream_ == Audio)
1456
533
          xmpData_["Xmp.audio.MediaLangCode"] = buf.read_uint16(0, bigEndian);
1457
3.75k
        break;
1458
1459
2.16k
      default:
1460
2.16k
        break;
1461
25.8k
    }
1462
25.8k
  }
1463
5.29k
  io_->readOrThrow(buf.data(), size % 4);
1464
5.29k
}  // QuickTimeVideo::mediaHeaderDecoder
1465
1466
2.99k
void QuickTimeVideo::trackHeaderDecoder(size_t size) {
1467
2.99k
  DataBuf buf(5);
1468
2.99k
  std::memset(buf.data(), 0x0, buf.size());
1469
2.99k
  buf.data()[4] = '\0';
1470
2.99k
  int64_t temp = 0;
1471
1472
61.2k
  for (int i = 0; size / 4 != 0; size -= 4, i++) {
1473
58.2k
    io_->readOrThrow(buf.data(), 4);
1474
1475
58.2k
    switch (i) {
1476
2.52k
      case TrackHeaderVersion:
1477
2.52k
        if (currentStream_ == Video)
1478
230
          xmpData_["Xmp.video.TrackHeaderVersion"] = static_cast<int>(buf.read_uint8(0));
1479
2.29k
        else if (currentStream_ == Audio)
1480
906
          xmpData_["Xmp.audio.TrackHeaderVersion"] = static_cast<int>(buf.read_uint8(0));
1481
2.52k
        break;
1482
2.51k
      case TrackCreateDate:
1483
        // A 32-bit integer that specifies (in seconds since midnight, January 1, 1904) when the movie atom was created.
1484
2.51k
        if (currentStream_ == Video)
1485
230
          xmpData_["Xmp.video.TrackCreateDate"] = buf.read_uint32(0, bigEndian);
1486
2.28k
        else if (currentStream_ == Audio)
1487
906
          xmpData_["Xmp.audio.TrackCreateDate"] = buf.read_uint32(0, bigEndian);
1488
2.51k
        break;
1489
2.51k
      case TrackModifyDate:
1490
        // A 32-bit integer that specifies (in seconds since midnight, January 1, 1904) when the movie atom was created.
1491
2.51k
        if (currentStream_ == Video)
1492
230
          xmpData_["Xmp.video.TrackModifyDate"] = buf.read_uint32(0, bigEndian);
1493
2.28k
        else if (currentStream_ == Audio)
1494
906
          xmpData_["Xmp.audio.TrackModifyDate"] = buf.read_uint32(0, bigEndian);
1495
2.51k
        break;
1496
2.50k
      case TrackID:
1497
2.50k
        if (currentStream_ == Video)
1498
230
          xmpData_["Xmp.video.TrackID"] = buf.read_uint32(0, bigEndian);
1499
2.27k
        else if (currentStream_ == Audio)
1500
906
          xmpData_["Xmp.audio.TrackID"] = buf.read_uint32(0, bigEndian);
1501
2.50k
        break;
1502
2.50k
      case TrackDuration:
1503
2.50k
        if (currentStream_ == Video)
1504
229
          xmpData_["Xmp.video.TrackDuration"] = mvhdTimeScale_ ? buf.read_uint32(0, bigEndian) / mvhdTimeScale_ : 0;
1505
2.27k
        else if (currentStream_ == Audio)
1506
905
          xmpData_["Xmp.audio.TrackDuration"] = mvhdTimeScale_ ? buf.read_uint32(0, bigEndian) / mvhdTimeScale_ : 0;
1507
2.50k
        break;
1508
2.46k
      case TrackLayer:
1509
2.46k
        if (currentStream_ == Video)
1510
197
          xmpData_["Xmp.video.TrackLayer"] = buf.read_uint16(0, bigEndian);
1511
2.26k
        else if (currentStream_ == Audio)
1512
904
          xmpData_["Xmp.audio.TrackLayer"] = buf.read_uint16(0, bigEndian);
1513
2.46k
        break;
1514
2.45k
      case TrackVolume:
1515
2.45k
        if (currentStream_ == Video)
1516
195
          xmpData_["Xmp.video.TrackVolume"] = (static_cast<int>(buf.read_uint8(0)) + (buf.data()[2] * 0.1)) * 100;
1517
2.26k
        else if (currentStream_ == Audio)
1518
904
          xmpData_["Xmp.video.TrackVolume"] = (static_cast<int>(buf.read_uint8(0)) + (buf.data()[2] * 0.1)) * 100;
1519
2.45k
        break;
1520
2.39k
      case ImageWidth:
1521
2.39k
        if (currentStream_ == Video) {
1522
151
          temp = buf.read_uint16(0, bigEndian) + static_cast<int64_t>((buf.data()[2] * 256 + buf.data()[3]) * 0.01);
1523
151
          xmpData_["Xmp.video.Width"] = temp;
1524
151
          width_ = temp;
1525
151
        }
1526
2.39k
        break;
1527
2.39k
      case ImageHeight:
1528
2.39k
        if (currentStream_ == Video) {
1529
151
          temp = buf.read_uint16(0, bigEndian) + static_cast<int64_t>((buf.data()[2] * 256 + buf.data()[3]) * 0.01);
1530
151
          xmpData_["Xmp.video.Height"] = temp;
1531
151
          height_ = temp;
1532
151
        }
1533
2.39k
        break;
1534
36.0k
      default:
1535
36.0k
        break;
1536
58.2k
    }
1537
58.2k
  }
1538
2.99k
  io_->readOrThrow(buf.data(), size % 4);
1539
2.99k
}  // QuickTimeVideo::trackHeaderDecoder
1540
1541
473
void QuickTimeVideo::movieHeaderDecoder(size_t size) {
1542
473
  DataBuf buf(5);
1543
473
  std::memset(buf.data(), 0x0, buf.size());
1544
473
  buf.data()[4] = '\0';
1545
1546
5.66k
  for (int i = 0; size / 4 != 0; size -= 4, i++) {
1547
5.19k
    io_->readOrThrow(buf.data(), 4);
1548
1549
5.19k
    switch (i) {
1550
321
      case MovieHeaderVersion:
1551
321
        xmpData_["Xmp.video.MovieHeaderVersion"] = static_cast<int>(buf.read_uint8(0));
1552
321
        break;
1553
306
      case CreateDate:
1554
        // A 32-bit integer that specifies (in seconds since midnight, January 1, 1904) when the movie atom was created.
1555
306
        xmpData_["Xmp.video.DateUTC"] = buf.read_uint32(0, bigEndian);
1556
306
        break;
1557
249
      case ModifyDate:
1558
        // A 32-bit integer that specifies (in seconds since midnight, January 1, 1904) when the movie atom was created.
1559
249
        xmpData_["Xmp.video.ModificationDate"] = buf.read_uint32(0, bigEndian);
1560
249
        break;
1561
240
      case TimeScale:
1562
240
        xmpData_["Xmp.video.TimeScale"] = buf.read_uint32(0, bigEndian);
1563
240
        mvhdTimeScale_ = std::max(1U, buf.read_uint32(0, bigEndian));
1564
240
        break;
1565
232
      case Duration:
1566
232
        if (mvhdTimeScale_ != 0) {  // To prevent division by zero
1567
232
          xmpData_["Xmp.video.Duration"] = buf.read_uint32(0, bigEndian) * 1000 / mvhdTimeScale_;
1568
232
        }
1569
232
        break;
1570
232
      case PreferredRate:
1571
232
        xmpData_["Xmp.video.PreferredRate"] =
1572
232
            buf.read_uint16(0, bigEndian) + ((buf.data()[2] * 256 + buf.data()[3]) * 0.01);
1573
232
        break;
1574
232
      case PreferredVolume:
1575
232
        xmpData_["Xmp.video.PreferredVolume"] = (static_cast<int>(buf.read_uint8(0)) + (buf.data()[2] * 0.1)) * 100;
1576
232
        break;
1577
146
      case PreviewTime:
1578
146
        xmpData_["Xmp.video.PreviewTime"] = buf.read_uint32(0, bigEndian);
1579
146
        break;
1580
146
      case PreviewDuration:
1581
146
        xmpData_["Xmp.video.PreviewDuration"] = buf.read_uint32(0, bigEndian);
1582
146
        break;
1583
142
      case PosterTime:
1584
142
        xmpData_["Xmp.video.PosterTime"] = buf.read_uint32(0, bigEndian);
1585
142
        break;
1586
137
      case SelectionTime:
1587
137
        xmpData_["Xmp.video.SelectionTime"] = buf.read_uint32(0, bigEndian);
1588
137
        break;
1589
137
      case SelectionDuration:
1590
137
        xmpData_["Xmp.video.SelectionDuration"] = buf.read_uint32(0, bigEndian);
1591
137
        break;
1592
137
      case CurrentTime:
1593
137
        xmpData_["Xmp.video.CurrentTime"] = buf.read_uint32(0, bigEndian);
1594
137
        break;
1595
136
      case NextTrackID:
1596
136
        xmpData_["Xmp.video.NextTrackID"] = buf.read_uint32(0, bigEndian);
1597
136
        break;
1598
2.40k
      default:
1599
2.40k
        break;
1600
5.19k
    }
1601
5.19k
  }
1602
473
  io_->readOrThrow(buf.data(), size % 4);
1603
473
}  // QuickTimeVideo::movieHeaderDecoder
1604
1605
9.09k
Image::UniquePtr newQTimeInstance(BasicIo::UniquePtr io, bool /*create*/) {
1606
9.09k
  auto image = std::make_unique<QuickTimeVideo>(std::move(io));
1607
9.09k
  if (!image->good()) {
1608
24
    return nullptr;
1609
24
  }
1610
9.07k
  return image;
1611
9.09k
}
1612
1613
50.7k
bool isQTimeType(BasicIo& iIo, bool advance) {
1614
50.7k
  auto buf = DataBuf(12);
1615
50.7k
  iIo.read(buf.data(), 12);
1616
1617
50.7k
  if (iIo.error() || iIo.eof()) {
1618
745
    return false;
1619
745
  }
1620
49.9k
  auto qTimeTags = std::array{"PICT", "free", "ftyp", "junk", "mdat", "moov", "pict", "pnot", "skip", "uuid", "wide"};
1621
1622
49.9k
  bool matched = false;
1623
1624
274k
  for (auto const& tag : qTimeTags) {
1625
274k
    auto tmp = buf.cmpBytes(4, tag, 4);
1626
274k
    if (tmp == 0) {
1627
      // we only match if we actually know the video type. This is done
1628
      // to avoid matching just on ftyp because bmffimage also has that
1629
      // header.
1630
38.3k
      if (Exiv2::find(qTimeFileType, std::string{buf.c_str(8), 4})) {
1631
27.2k
        matched = true;
1632
27.2k
      }
1633
38.3k
      break;
1634
38.3k
    }
1635
274k
  }
1636
1637
49.9k
  if (!advance || !matched) {
1638
49.9k
    iIo.seek(0L, BasicIo::beg);
1639
49.9k
  }
1640
1641
49.9k
  return matched;
1642
50.7k
}
1643
1644
}  // namespace Exiv2