/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 | 296k | static bool equalsQTimeTag(Exiv2::DataBuf& buf, const char str[5]) { |
493 | 296k | return std::equal(buf.begin(), buf.begin() + 4, str, |
494 | 325k | [](auto b, auto s) { return std::tolower(b) == std::tolower(s); }); |
495 | 296k | } |
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 | 12.4k | static bool ignoreList(Exiv2::DataBuf& buf) { |
504 | 12.4k | const char ignoreList[13][5] = { |
505 | 12.4k | "mdat", "edts", "junk", "iods", "alis", "stsc", "stsz", "stco", "ctts", "stss", "skip", "wide", "cmvd", |
506 | 12.4k | }; |
507 | | |
508 | 12.4k | for (auto i : ignoreList) |
509 | 161k | if (equalsQTimeTag(buf, i)) |
510 | 83 | return true; |
511 | | |
512 | 12.4k | return false; |
513 | 12.4k | } |
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 | 6.21k | static bool dataIgnoreList(Exiv2::DataBuf& buf) { |
523 | 6.21k | const char ignoreList[8][5] = { |
524 | 6.21k | "moov", "mdia", "minf", "dinf", "alis", "stbl", "cmov", "meta", |
525 | 6.21k | }; |
526 | | |
527 | 6.21k | for (auto i : ignoreList) |
528 | 46.8k | if (equalsQTimeTag(buf, i)) |
529 | 614 | return true; |
530 | | |
531 | 5.59k | return false; |
532 | 6.21k | } |
533 | | } // namespace Exiv2::Internal |
534 | | |
535 | | namespace Exiv2 { |
536 | | |
537 | | using namespace Exiv2::Internal; |
538 | | |
539 | | QuickTimeVideo::QuickTimeVideo(BasicIo::UniquePtr io, const ImageCtorParams& params) : |
540 | 1.06k | Image(ImageType::qtime, mdNone, std::move(io), params), |
541 | 1.06k | mvhdTimeScale_(1), |
542 | 1.06k | mdhdTimeScale_(1), |
543 | 1.06k | currentStream_(Null), |
544 | 1.06k | max_recursion_depth_(params.max_recursion_depth()) { |
545 | 1.06k | } // QuickTimeVideo::QuickTimeVideo |
546 | | |
547 | 1.13k | std::string QuickTimeVideo::mimeType() const { |
548 | 1.13k | return "video/quicktime"; |
549 | 1.13k | } |
550 | | |
551 | 0 | void QuickTimeVideo::writeMetadata() { |
552 | 0 | } |
553 | | |
554 | 1.06k | void QuickTimeVideo::readMetadata() { |
555 | 1.06k | if (io_->open() != 0) |
556 | 0 | throw Error(ErrorCode::kerDataSourceOpenFailed, io_->path(), strError()); |
557 | | |
558 | | // Ensure that this is the correct image type |
559 | 1.06k | 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 | 1.06k | IoCloser closer(*io_); |
566 | 1.06k | clearMetadata(); |
567 | 1.06k | continueTraversing_ = true; |
568 | 1.06k | height_ = width_ = 1; |
569 | | |
570 | 1.06k | xmpData_["Xmp.video.FileSize"] = static_cast<double>(io_->size()) / 1048576.0; |
571 | 1.06k | xmpData_["Xmp.video.MimeType"] = mimeType(); |
572 | | |
573 | 7.64k | while (continueTraversing_) |
574 | 6.57k | decodeBlock(0); |
575 | | |
576 | 1.06k | xmpData_["Xmp.video.AspectRatio"] = getAspectRatio(width_, height_); |
577 | 1.06k | } // QuickTimeVideo::readMetadata |
578 | | |
579 | 7.19k | void QuickTimeVideo::decodeBlock(size_t recursion_depth, std::string const& entered_from) { |
580 | 7.19k | enforce(recursion_depth < max_recursion_depth_, Exiv2::ErrorCode::kerCorruptedMetadata); |
581 | | |
582 | 7.19k | const long bufMinSize = 4; |
583 | 7.19k | DataBuf buf(bufMinSize + 1); |
584 | 7.19k | uint64_t size = 0; |
585 | 7.19k | buf.data()[4] = '\0'; |
586 | | |
587 | 7.19k | io_->read(buf.data(), 4); |
588 | 7.19k | if (io_->eof()) { |
589 | 72 | continueTraversing_ = false; |
590 | 72 | return; |
591 | 72 | } |
592 | | |
593 | 7.12k | size = buf.read_uint32(0, bigEndian); |
594 | | |
595 | 7.12k | io_->readOrThrow(buf.data(), 4); |
596 | | |
597 | | // we have read 2x 4 bytes |
598 | 7.12k | size_t hdrsize = 8; |
599 | | |
600 | 7.12k | if (size == 1) { |
601 | | // The box size is encoded as a uint64_t, so we need to read another 8 bytes. |
602 | 1.79k | DataBuf data(8); |
603 | 1.79k | hdrsize += 8; |
604 | 1.79k | io_->readOrThrow(data.data(), data.size()); |
605 | 1.79k | size = data.read_uint64(0, bigEndian); |
606 | 5.33k | } else if (size == 0 && entered_from == "meta") { |
607 | 57 | size = buf.read_uint32(0, bigEndian); |
608 | 57 | io_->readOrThrow(buf.data(), 4, Exiv2::ErrorCode::kerCorruptedMetadata); |
609 | 57 | } |
610 | | |
611 | 7.12k | enforce(size >= hdrsize, Exiv2::ErrorCode::kerCorruptedMetadata); |
612 | 7.12k | enforce(size - hdrsize <= io_->size() - io_->tell(), Exiv2::ErrorCode::kerCorruptedMetadata); |
613 | 7.12k | enforce(size - hdrsize <= std::numeric_limits<size_t>::max(), Exiv2::ErrorCode::kerCorruptedMetadata); |
614 | | |
615 | | // std::cerr<<"Tag=>"<<buf.data()<<" size=>"<<size-hdrsize << '\n'; |
616 | 7.12k | const auto newsize = static_cast<size_t>(size - hdrsize); |
617 | 7.12k | if (ignoreList(buf)) { |
618 | 83 | discard(newsize); |
619 | 83 | return; |
620 | 83 | } |
621 | 7.04k | if (newsize > buf.size()) { |
622 | 4.11k | buf.resize(newsize); |
623 | 4.11k | } |
624 | 7.04k | tagDecoder(buf, newsize, recursion_depth + 1); |
625 | 7.04k | } // QuickTimeVideo::decodeBlock |
626 | | |
627 | 153 | static std::string readString(BasicIo& io, size_t size) { |
628 | 153 | enforce(size <= io.size() - io.tell(), Exiv2::ErrorCode::kerCorruptedMetadata); |
629 | 153 | Exiv2::DataBuf str(size + 1); |
630 | 153 | io.readOrThrow(str.data(), size); |
631 | 153 | str.write_uint8(size, 0); // nul-terminate string |
632 | 153 | return Exiv2::toString(str.data()); |
633 | 153 | } |
634 | | |
635 | 6.21k | void QuickTimeVideo::tagDecoder(Exiv2::DataBuf& buf, size_t size, size_t recursion_depth) { |
636 | 6.21k | enforce(recursion_depth < max_recursion_depth_, Exiv2::ErrorCode::kerCorruptedMetadata); |
637 | 6.21k | assert(buf.size() > 4); |
638 | | |
639 | 6.21k | if (ignoreList(buf)) |
640 | 0 | discard(size); |
641 | | |
642 | 6.21k | else if (dataIgnoreList(buf)) { |
643 | 614 | decodeBlock(recursion_depth + 1, Exiv2::toString(buf.data())); |
644 | 5.59k | } else if (equalsQTimeTag(buf, "ftyp")) |
645 | 1.10k | fileTypeDecoder(size); |
646 | | |
647 | 4.49k | else if (equalsQTimeTag(buf, "trak")) |
648 | 172 | setMediaStream(size); |
649 | | |
650 | 4.32k | else if (equalsQTimeTag(buf, "mvhd")) |
651 | 171 | movieHeaderDecoder(size); |
652 | | |
653 | 4.14k | else if (equalsQTimeTag(buf, "tkhd")) |
654 | 71 | trackHeaderDecoder(size); |
655 | | |
656 | 4.07k | else if (equalsQTimeTag(buf, "mdhd")) |
657 | 71 | mediaHeaderDecoder(size); |
658 | | |
659 | 4.00k | else if (equalsQTimeTag(buf, "hdlr")) |
660 | 28 | handlerDecoder(size); |
661 | | |
662 | 3.97k | else if (equalsQTimeTag(buf, "vmhd")) |
663 | 58 | videoHeaderDecoder(size); |
664 | | |
665 | 3.92k | else if (equalsQTimeTag(buf, "udta")) |
666 | 433 | userDataDecoder(size, recursion_depth + 1); |
667 | | |
668 | 3.48k | else if (equalsQTimeTag(buf, "dref")) |
669 | 5 | multipleEntriesDecoder(recursion_depth + 1); |
670 | | |
671 | 3.48k | else if (equalsQTimeTag(buf, "stsd")) |
672 | 154 | sampleDesc(size); |
673 | | |
674 | 3.32k | else if (equalsQTimeTag(buf, "stts")) |
675 | 88 | timeToSampleDecoder(); |
676 | | |
677 | 3.24k | else if (equalsQTimeTag(buf, "pnot")) |
678 | 21 | previewTagDecoder(size); |
679 | | |
680 | 3.22k | else if (equalsQTimeTag(buf, "tapt")) |
681 | 8 | trackApertureTagDecoder(size); |
682 | | |
683 | 3.21k | else if (equalsQTimeTag(buf, "keys")) |
684 | 0 | keysTagDecoder(size); |
685 | | |
686 | 3.21k | else if (equalsQTimeTag(buf, "url ")) { |
687 | 81 | if (currentStream_ == Video) |
688 | 3 | xmpData_["Xmp.video.URL"] = readString(*io_, size); |
689 | 78 | else if (currentStream_ == Audio) |
690 | 75 | xmpData_["Xmp.audio.URL"] = readString(*io_, size); |
691 | 3 | else |
692 | 3 | discard(size); |
693 | 81 | } |
694 | | |
695 | 3.13k | else if (equalsQTimeTag(buf, "urn ")) { |
696 | 0 | if (currentStream_ == Video) |
697 | 0 | xmpData_["Xmp.video.URN"] = readString(*io_, size); |
698 | 0 | else if (currentStream_ == Audio) |
699 | 0 | xmpData_["Xmp.audio.URN"] = readString(*io_, size); |
700 | 0 | else |
701 | 0 | discard(size); |
702 | 0 | } |
703 | | |
704 | 3.13k | else if (equalsQTimeTag(buf, "dcom")) { |
705 | 0 | xmpData_["Xmp.video.Compressor"] = readString(*io_, size); |
706 | 0 | } |
707 | | |
708 | 3.13k | else if (equalsQTimeTag(buf, "smhd")) { |
709 | 1 | io_->readOrThrow(buf.data(), 4); |
710 | 1 | io_->readOrThrow(buf.data(), 4); |
711 | 1 | xmpData_["Xmp.audio.Balance"] = buf.read_uint16(0, bigEndian); |
712 | 1 | } |
713 | | |
714 | 3.13k | else { |
715 | 3.13k | discard(size); |
716 | 3.13k | } |
717 | 6.21k | } // QuickTimeVideo::tagDecoder |
718 | | |
719 | 3.21k | void QuickTimeVideo::discard(size_t size) { |
720 | 3.21k | size_t cur_pos = io_->tell(); |
721 | 3.21k | io_->seek(cur_pos + size, BasicIo::beg); |
722 | 3.21k | } // QuickTimeVideo::discard |
723 | | |
724 | 21 | void QuickTimeVideo::previewTagDecoder(size_t size) { |
725 | 21 | DataBuf buf(4); |
726 | 21 | size_t cur_pos = io_->tell(); |
727 | 21 | io_->readOrThrow(buf.data(), 4); |
728 | 21 | xmpData_["Xmp.video.PreviewDate"] = buf.read_uint32(0, bigEndian); |
729 | 21 | io_->readOrThrow(buf.data(), 2); |
730 | 21 | xmpData_["Xmp.video.PreviewVersion"] = getShort(buf.data(), bigEndian); |
731 | | |
732 | 21 | io_->readOrThrow(buf.data(), 4); |
733 | 21 | if (equalsQTimeTag(buf, "PICT")) |
734 | 0 | xmpData_["Xmp.video.PreviewAtomType"] = "QuickDraw Picture"; |
735 | 21 | else |
736 | 21 | xmpData_["Xmp.video.PreviewAtomType"] = std::string{buf.c_str(), 4}; |
737 | | |
738 | 21 | io_->seek(cur_pos + size, BasicIo::beg); |
739 | 21 | } // QuickTimeVideo::previewTagDecoder |
740 | | |
741 | 0 | void QuickTimeVideo::keysTagDecoder(size_t size) { |
742 | 0 | DataBuf buf(4); |
743 | 0 | size_t cur_pos = io_->tell(); |
744 | 0 | io_->readOrThrow(buf.data(), 4); |
745 | 0 | xmpData_["Xmp.video.PreviewDate"] = buf.read_uint32(0, bigEndian); |
746 | 0 | io_->readOrThrow(buf.data(), 2); |
747 | 0 | xmpData_["Xmp.video.PreviewVersion"] = getShort(buf.data(), bigEndian); |
748 | |
|
749 | 0 | io_->readOrThrow(buf.data(), 4); |
750 | 0 | if (equalsQTimeTag(buf, "PICT")) |
751 | 0 | xmpData_["Xmp.video.PreviewAtomType"] = "QuickDraw Picture"; |
752 | 0 | else |
753 | 0 | xmpData_["Xmp.video.PreviewAtomType"] = std::string{buf.c_str(), 4}; |
754 | |
|
755 | 0 | io_->seek(cur_pos + size, BasicIo::beg); |
756 | 0 | } // QuickTimeVideo::keysTagDecoder |
757 | | |
758 | 8 | void QuickTimeVideo::trackApertureTagDecoder(size_t size) { |
759 | 8 | DataBuf buf(4); |
760 | 8 | DataBuf buf2(2); |
761 | 8 | size_t cur_pos = io_->tell(); |
762 | 8 | byte n = 3; |
763 | | |
764 | 32 | while (n--) { |
765 | 24 | io_->seek(4L, BasicIo::cur); |
766 | 24 | io_->readOrThrow(buf.data(), 4); |
767 | | |
768 | 24 | if (equalsQTimeTag(buf, "clef")) { |
769 | 4 | io_->seek(4L, BasicIo::cur); |
770 | 4 | io_->readOrThrow(buf.data(), 2); |
771 | 4 | io_->readOrThrow(buf2.data(), 2); |
772 | 4 | xmpData_["Xmp.video.CleanApertureWidth"] = |
773 | 4 | stringFormat("{}.{}", buf.read_uint16(0, bigEndian), buf2.read_uint16(0, bigEndian)); |
774 | 4 | io_->readOrThrow(buf.data(), 2); |
775 | 4 | io_->readOrThrow(buf2.data(), 2); |
776 | 4 | xmpData_["Xmp.video.CleanApertureHeight"] = |
777 | 4 | stringFormat("{}.{}", buf.read_uint16(0, bigEndian), buf2.read_uint16(0, bigEndian)); |
778 | 4 | } |
779 | | |
780 | 20 | else if (equalsQTimeTag(buf, "prof")) { |
781 | 0 | io_->seek(4L, BasicIo::cur); |
782 | 0 | io_->readOrThrow(buf.data(), 2); |
783 | 0 | io_->readOrThrow(buf2.data(), 2); |
784 | 0 | xmpData_["Xmp.video.ProductionApertureWidth"] = |
785 | 0 | stringFormat("{}.{}", buf.read_uint16(0, bigEndian), buf2.read_uint16(0, bigEndian)); |
786 | 0 | io_->readOrThrow(buf.data(), 2); |
787 | 0 | io_->readOrThrow(buf2.data(), 2); |
788 | 0 | xmpData_["Xmp.video.ProductionApertureHeight"] = |
789 | 0 | stringFormat("{}.{}", buf.read_uint16(0, bigEndian), buf2.read_uint16(0, bigEndian)); |
790 | 0 | } |
791 | | |
792 | 20 | else if (equalsQTimeTag(buf, "enof")) { |
793 | 0 | io_->seek(4L, BasicIo::cur); |
794 | 0 | io_->readOrThrow(buf.data(), 2); |
795 | 0 | io_->readOrThrow(buf2.data(), 2); |
796 | 0 | xmpData_["Xmp.video.EncodedPixelsWidth"] = |
797 | 0 | stringFormat("{}.{}", buf.read_uint16(0, bigEndian), buf2.read_uint16(0, bigEndian)); |
798 | 0 | io_->readOrThrow(buf.data(), 2); |
799 | 0 | io_->readOrThrow(buf2.data(), 2); |
800 | 0 | xmpData_["Xmp.video.EncodedPixelsHeight"] = |
801 | 0 | stringFormat("{}.{}", buf.read_uint16(0, bigEndian), buf2.read_uint16(0, bigEndian)); |
802 | 0 | } |
803 | 24 | } |
804 | 8 | io_->seek(cur_pos + size, BasicIo::beg); |
805 | 8 | } // QuickTimeVideo::trackApertureTagDecoder |
806 | | |
807 | 0 | void QuickTimeVideo::CameraTagsDecoder(size_t size) { |
808 | 0 | size_t cur_pos = io_->tell(); |
809 | 0 | DataBuf buf(50); |
810 | 0 | DataBuf buf2(4); |
811 | |
|
812 | 0 | io_->readOrThrow(buf.data(), 4); |
813 | 0 | if (equalsQTimeTag(buf, "NIKO")) { |
814 | 0 | io_->seek(cur_pos, BasicIo::beg); |
815 | |
|
816 | 0 | io_->readOrThrow(buf.data(), 24); |
817 | 0 | xmpData_["Xmp.video.Make"] = buf.data(); |
818 | 0 | io_->readOrThrow(buf.data(), 14); |
819 | 0 | xmpData_["Xmp.video.Model"] = buf.data(); |
820 | 0 | io_->readOrThrow(buf.data(), 4); |
821 | 0 | xmpData_["Xmp.video.ExposureTime"] = stringFormat("1/{}", std::ceil(buf.read_uint32(0, littleEndian) / 10.0)); |
822 | 0 | io_->readOrThrow(buf.data(), 4); |
823 | 0 | io_->readOrThrow(buf2.data(), 4); |
824 | 0 | xmpData_["Xmp.video.FNumber"] = |
825 | 0 | buf.read_uint32(0, littleEndian) / static_cast<double>(buf2.read_uint32(0, littleEndian)); |
826 | 0 | io_->readOrThrow(buf.data(), 4); |
827 | 0 | io_->readOrThrow(buf2.data(), 4); |
828 | 0 | xmpData_["Xmp.video.ExposureCompensation"] = |
829 | 0 | buf.read_uint32(0, littleEndian) / static_cast<double>(buf2.read_uint32(0, littleEndian)); |
830 | 0 | io_->readOrThrow(buf.data(), 10); |
831 | 0 | io_->readOrThrow(buf.data(), 4); |
832 | 0 | if (auto td = Exiv2::find(whiteBalance, buf.read_uint32(0, littleEndian))) |
833 | 0 | xmpData_["Xmp.video.WhiteBalance"] = _(td->label_); |
834 | 0 | io_->readOrThrow(buf.data(), 4); |
835 | 0 | io_->readOrThrow(buf2.data(), 4); |
836 | 0 | xmpData_["Xmp.video.FocalLength"] = |
837 | 0 | buf.read_uint32(0, littleEndian) / static_cast<double>(buf2.read_uint32(0, littleEndian)); |
838 | 0 | io_->seek(95L, BasicIo::cur); |
839 | 0 | io_->readOrThrow(buf.data(), 48); |
840 | 0 | buf.write_uint8(48, 0); |
841 | 0 | xmpData_["Xmp.video.Software"] = buf.data(); |
842 | 0 | io_->readOrThrow(buf.data(), 4); |
843 | 0 | xmpData_["Xmp.video.ISO"] = buf.read_uint32(0, littleEndian); |
844 | 0 | } |
845 | |
|
846 | 0 | io_->seek(cur_pos + size, BasicIo::beg); |
847 | 0 | } // QuickTimeVideo::CameraTagsDecoder |
848 | | |
849 | 435 | void QuickTimeVideo::userDataDecoder(size_t outer_size, size_t recursion_depth) { |
850 | 435 | enforce(recursion_depth < max_recursion_depth_, Exiv2::ErrorCode::kerCorruptedMetadata); |
851 | 435 | const size_t start_pos = io_->tell(); |
852 | 435 | const TagVocabulary* td; |
853 | 435 | const TagVocabulary* tv; |
854 | 435 | const TagVocabulary* tv_internal; |
855 | | |
856 | 435 | const long bufMinSize = 100; |
857 | 435 | DataBuf buf(bufMinSize); |
858 | 435 | size_t size_internal = outer_size; |
859 | 435 | std::memset(buf.data(), 0x0, buf.size()); |
860 | | |
861 | 583 | while ((size_internal / 4 != 0) && (size_internal > 0)) { |
862 | 575 | const size_t loop_start_pos = io_->tell(); |
863 | 575 | buf.data()[4] = '\0'; |
864 | 575 | io_->readOrThrow(buf.data(), 4); |
865 | 575 | const size_t size = buf.read_uint32(0, bigEndian); |
866 | 575 | if (size > size_internal) |
867 | 325 | break; |
868 | 250 | size_internal -= size; |
869 | 250 | io_->readOrThrow(buf.data(), 4); |
870 | | |
871 | 250 | if (buf.data()[0] == 169) |
872 | 0 | buf.data()[0] = ' '; |
873 | 250 | td = Exiv2::find(userDatatags, Exiv2::toString(buf.data())); |
874 | | |
875 | 250 | tv = Exiv2::find(userDataReferencetags, Exiv2::toString(buf.data())); |
876 | | |
877 | 250 | if (size <= 12) |
878 | 102 | break; |
879 | | |
880 | 148 | if (equalsQTimeTag(buf, "DcMD") || equalsQTimeTag(buf, "NCDT")) |
881 | 2 | userDataDecoder(size - 8, recursion_depth + 1); |
882 | | |
883 | 146 | else if (equalsQTimeTag(buf, "NCTG")) |
884 | 0 | NikonTagsDecoder(size - 8); |
885 | | |
886 | 146 | else if (equalsQTimeTag(buf, "TAGS")) |
887 | 0 | CameraTagsDecoder(size - 8); |
888 | | |
889 | 146 | else if (equalsQTimeTag(buf, "CNCV") || equalsQTimeTag(buf, "CNFV") || equalsQTimeTag(buf, "CNMN") || |
890 | 126 | equalsQTimeTag(buf, "NCHD") || equalsQTimeTag(buf, "FFMV")) { |
891 | 24 | enforce(tv, Exiv2::ErrorCode::kerCorruptedMetadata); |
892 | 24 | xmpData_[_(tv->label_)] = readString(*io_, size - 8); |
893 | 24 | } |
894 | | |
895 | 122 | else if (equalsQTimeTag(buf, "CMbo") || equalsQTimeTag(buf, "Cmbo")) { |
896 | 0 | enforce(tv, Exiv2::ErrorCode::kerCorruptedMetadata); |
897 | 0 | io_->readOrThrow(buf.data(), 2); |
898 | 0 | buf.data()[2] = '\0'; |
899 | 0 | tv_internal = Exiv2::find(cameraByteOrderTags, Exiv2::toString(buf.data())); |
900 | |
|
901 | 0 | if (tv_internal) |
902 | 0 | xmpData_[_(tv->label_)] = _(tv_internal->label_); |
903 | 0 | else |
904 | 0 | xmpData_[_(tv->label_)] = buf.data(); |
905 | 0 | } |
906 | | |
907 | 122 | else if (tv) { |
908 | 56 | io_->readOrThrow(buf.data(), 4); |
909 | 56 | xmpData_[_(tv->label_)] = readString(*io_, size - 12); |
910 | 56 | } |
911 | | |
912 | 66 | else if (td) |
913 | 12 | tagDecoder(buf, size - 8, recursion_depth + 1); |
914 | | |
915 | 148 | enforce(io_->tell() <= loop_start_pos + size, Exiv2::ErrorCode::kerCorruptedMetadata); |
916 | 148 | } |
917 | | |
918 | 435 | io_->seek(start_pos + outer_size, BasicIo::beg); |
919 | 435 | } // QuickTimeVideo::userDataDecoder |
920 | | |
921 | 0 | void QuickTimeVideo::NikonTagsDecoder(size_t size) { |
922 | 0 | size_t cur_pos = io_->tell(); |
923 | 0 | DataBuf buf(201); |
924 | 0 | DataBuf buf2(4 + 1); |
925 | 0 | uint32_t TagID = 0; |
926 | 0 | uint16_t dataLength = 0; |
927 | 0 | uint16_t dataType = 2; |
928 | 0 | const TagDetails* td; |
929 | 0 | const TagDetails* td2; |
930 | |
|
931 | 0 | for (int i = 0; i < 100; i++) { |
932 | 0 | io_->readOrThrow(buf.data(), 4); |
933 | 0 | TagID = buf.read_uint32(0, bigEndian); |
934 | 0 | td = Exiv2::find(NikonNCTGTags, TagID); |
935 | |
|
936 | 0 | io_->readOrThrow(buf.data(), 2); |
937 | 0 | dataType = buf.read_uint16(0, bigEndian); |
938 | |
|
939 | 0 | std::memset(buf.data(), 0x0, buf.size()); |
940 | 0 | io_->readOrThrow(buf.data(), 2); |
941 | |
|
942 | 0 | if (TagID == 0x2000023) { |
943 | 0 | size_t local_pos = io_->tell(); |
944 | 0 | dataLength = buf.read_uint16(0, bigEndian); |
945 | 0 | std::memset(buf.data(), 0x0, buf.size()); |
946 | |
|
947 | 0 | io_->readOrThrow(buf.data(), 4); |
948 | 0 | xmpData_["Xmp.video.PictureControlVersion"] = buf.data(); |
949 | 0 | io_->readOrThrow(buf.data(), 20); |
950 | 0 | xmpData_["Xmp.video.PictureControlName"] = buf.data(); |
951 | 0 | io_->readOrThrow(buf.data(), 20); |
952 | 0 | xmpData_["Xmp.video.PictureControlBase"] = buf.data(); |
953 | 0 | io_->readOrThrow(buf.data(), 4); |
954 | 0 | std::memset(buf.data(), 0x0, buf.size()); |
955 | |
|
956 | 0 | io_->readOrThrow(buf.data(), 1); |
957 | 0 | td2 = Exiv2::find(PictureControlAdjust, static_cast<int>(buf.data()[0]) & 7); |
958 | 0 | if (td2) |
959 | 0 | xmpData_["Xmp.video.PictureControlAdjust"] = _(td2->label_); |
960 | 0 | else |
961 | 0 | xmpData_["Xmp.video.PictureControlAdjust"] = static_cast<int>(buf.data()[0]) & 7; |
962 | |
|
963 | 0 | io_->readOrThrow(buf.data(), 1); |
964 | 0 | td2 = Exiv2::find(NormalSoftHard, static_cast<int>(buf.data()[0]) & 7); |
965 | 0 | if (td2) |
966 | 0 | xmpData_["Xmp.video.PictureControlQuickAdjust"] = _(td2->label_); |
967 | |
|
968 | 0 | io_->readOrThrow(buf.data(), 1); |
969 | 0 | td2 = Exiv2::find(NormalSoftHard, static_cast<int>(buf.data()[0]) & 7); |
970 | 0 | if (td2) |
971 | 0 | xmpData_["Xmp.video.Sharpness"] = _(td2->label_); |
972 | 0 | else |
973 | 0 | xmpData_["Xmp.video.Sharpness"] = static_cast<int>(buf.data()[0]) & 7; |
974 | |
|
975 | 0 | io_->readOrThrow(buf.data(), 1); |
976 | 0 | td2 = Exiv2::find(NormalSoftHard, static_cast<int>(buf.data()[0]) & 7); |
977 | 0 | if (td2) |
978 | 0 | xmpData_["Xmp.video.Contrast"] = _(td2->label_); |
979 | 0 | else |
980 | 0 | xmpData_["Xmp.video.Contrast"] = static_cast<int>(buf.data()[0]) & 7; |
981 | |
|
982 | 0 | io_->readOrThrow(buf.data(), 1); |
983 | 0 | td2 = Exiv2::find(NormalSoftHard, static_cast<int>(buf.data()[0]) & 7); |
984 | 0 | if (td2) |
985 | 0 | xmpData_["Xmp.video.Brightness"] = _(td2->label_); |
986 | 0 | else |
987 | 0 | xmpData_["Xmp.video.Brightness"] = static_cast<int>(buf.data()[0]) & 7; |
988 | |
|
989 | 0 | io_->readOrThrow(buf.data(), 1); |
990 | 0 | td2 = Exiv2::find(Saturation, static_cast<int>(buf.data()[0]) & 7); |
991 | 0 | if (td2) |
992 | 0 | xmpData_["Xmp.video.Saturation"] = _(td2->label_); |
993 | 0 | else |
994 | 0 | xmpData_["Xmp.video.Saturation"] = static_cast<int>(buf.data()[0]) & 7; |
995 | |
|
996 | 0 | io_->readOrThrow(buf.data(), 1); |
997 | 0 | xmpData_["Xmp.video.HueAdjustment"] = static_cast<int>(buf.data()[0]) & 7; |
998 | |
|
999 | 0 | io_->readOrThrow(buf.data(), 1); |
1000 | 0 | td2 = Exiv2::find(FilterEffect, static_cast<int>(buf.data()[0])); |
1001 | 0 | if (td2) |
1002 | 0 | xmpData_["Xmp.video.FilterEffect"] = _(td2->label_); |
1003 | 0 | else |
1004 | 0 | xmpData_["Xmp.video.FilterEffect"] = static_cast<int>(buf.data()[0]); |
1005 | |
|
1006 | 0 | io_->readOrThrow(buf.data(), 1); |
1007 | 0 | td2 = Exiv2::find(ToningEffect, static_cast<int>(buf.data()[0])); |
1008 | 0 | if (td2) |
1009 | 0 | xmpData_["Xmp.video.ToningEffect"] = _(td2->label_); |
1010 | 0 | else |
1011 | 0 | xmpData_["Xmp.video.ToningEffect"] = static_cast<int>(buf.data()[0]); |
1012 | |
|
1013 | 0 | io_->readOrThrow(buf.data(), 1); |
1014 | 0 | xmpData_["Xmp.video.ToningSaturation"] = static_cast<int>(buf.data()[0]); |
1015 | |
|
1016 | 0 | io_->seek(local_pos + dataLength, BasicIo::beg); |
1017 | 0 | } |
1018 | | |
1019 | 0 | else if (TagID == 0x2000024) { |
1020 | 0 | size_t local_pos = io_->tell(); |
1021 | 0 | dataLength = buf.read_uint16(0, bigEndian); |
1022 | 0 | std::memset(buf.data(), 0x0, buf.size()); |
1023 | |
|
1024 | 0 | io_->readOrThrow(buf.data(), 2); |
1025 | 0 | xmpData_["Xmp.video.TimeZone"] = Exiv2::getShort(buf.data(), bigEndian); |
1026 | 0 | io_->readOrThrow(buf.data(), 1); |
1027 | 0 | td2 = Exiv2::find(YesNo, static_cast<int>(buf.data()[0])); |
1028 | 0 | if (td2) |
1029 | 0 | xmpData_["Xmp.video.DayLightSavings"] = _(td2->label_); |
1030 | |
|
1031 | 0 | io_->readOrThrow(buf.data(), 1); |
1032 | 0 | td2 = Exiv2::find(DateDisplayFormat, static_cast<int>(buf.data()[0])); |
1033 | 0 | if (td2) |
1034 | 0 | xmpData_["Xmp.video.DateDisplayFormat"] = _(td2->label_); |
1035 | |
|
1036 | 0 | io_->seek(local_pos + dataLength, BasicIo::beg); |
1037 | 0 | } |
1038 | | |
1039 | 0 | else if (dataType == 2 || dataType == 7) { |
1040 | 0 | dataLength = buf.read_uint16(0, bigEndian); |
1041 | 0 | std::memset(buf.data(), 0x0, buf.size()); |
1042 | | |
1043 | | // Sanity check with an "unreasonably" large number |
1044 | 0 | if (dataLength >= buf.size()) { |
1045 | 0 | #ifndef SUPPRESS_WARNINGS |
1046 | 0 | EXV_ERROR << "Xmp.video Nikon Tags, dataLength was found to be larger than 200." |
1047 | 0 | << " Entries considered invalid. Not Processed.\n"; |
1048 | 0 | #endif |
1049 | 0 | io_->seek(io_->tell() + dataLength, BasicIo::beg); |
1050 | 0 | buf.data()[0] = '\0'; |
1051 | 0 | } else { |
1052 | 0 | io_->readOrThrow(buf.data(), dataLength); |
1053 | 0 | buf.data()[dataLength] = '\0'; |
1054 | 0 | } |
1055 | |
|
1056 | 0 | if (td) { |
1057 | 0 | xmpData_[_(td->label_)] = buf.data(); |
1058 | 0 | } |
1059 | 0 | } else if (dataType == 4) { |
1060 | 0 | dataLength = buf.read_uint16(0, bigEndian) * 4; |
1061 | 0 | std::memset(buf.data(), 0x0, buf.size()); |
1062 | 0 | io_->readOrThrow(buf.data(), 4); |
1063 | 0 | if (td) |
1064 | 0 | xmpData_[_(td->label_)] = buf.read_uint32(0, bigEndian); |
1065 | | |
1066 | | // Sanity check with an "unreasonably" large number |
1067 | 0 | if (dataLength > 200 || dataLength < 4) { |
1068 | 0 | #ifndef SUPPRESS_WARNINGS |
1069 | 0 | EXV_ERROR << "Xmp.video Nikon Tags, dataLength was found to be of inappropriate size." |
1070 | 0 | << " Entries considered invalid. Not Processed.\n"; |
1071 | 0 | #endif |
1072 | 0 | io_->seek(io_->tell() + dataLength - 4, BasicIo::beg); |
1073 | 0 | } else |
1074 | 0 | io_->readOrThrow(buf.data(), dataLength - 4); |
1075 | 0 | } else if (dataType == 3) { |
1076 | 0 | dataLength = buf.read_uint16(0, bigEndian) * 2; |
1077 | 0 | std::memset(buf.data(), 0x0, buf.size()); |
1078 | 0 | io_->readOrThrow(buf.data(), 2); |
1079 | 0 | if (td) |
1080 | 0 | xmpData_[_(td->label_)] = buf.read_uint16(0, bigEndian); |
1081 | | |
1082 | | // Sanity check with an "unreasonably" large number |
1083 | 0 | if (dataLength > 200 || dataLength < 2) { |
1084 | 0 | #ifndef SUPPRESS_WARNINGS |
1085 | 0 | EXV_ERROR << "Xmp.video Nikon Tags, dataLength was found to be of inappropriate size." |
1086 | 0 | << " Entries considered invalid. Not Processed.\n"; |
1087 | 0 | #endif |
1088 | 0 | io_->seek(io_->tell() + dataLength - 2, BasicIo::beg); |
1089 | 0 | } else |
1090 | 0 | io_->readOrThrow(buf.data(), dataLength - 2); |
1091 | 0 | } else if (dataType == 5) { |
1092 | 0 | dataLength = buf.read_uint16(0, bigEndian) * 8; |
1093 | 0 | std::memset(buf.data(), 0x0, buf.size()); |
1094 | 0 | io_->readOrThrow(buf.data(), 4); |
1095 | 0 | io_->readOrThrow(buf2.data(), 4); |
1096 | 0 | if (td) |
1097 | 0 | xmpData_[_(td->label_)] = |
1098 | 0 | static_cast<double>(buf.read_uint32(0, bigEndian)) / static_cast<double>(buf2.read_uint32(0, bigEndian)); |
1099 | | |
1100 | | // Sanity check with an "unreasonably" large number |
1101 | 0 | if (dataLength > 200 || dataLength < 8) { |
1102 | 0 | #ifndef SUPPRESS_WARNINGS |
1103 | 0 | EXV_ERROR << "Xmp.video Nikon Tags, dataLength was found to be of inappropriate size." |
1104 | 0 | << " Entries considered invalid. Not Processed.\n"; |
1105 | 0 | #endif |
1106 | 0 | io_->seek(io_->tell() + dataLength - 8, BasicIo::beg); |
1107 | 0 | } else |
1108 | 0 | io_->readOrThrow(buf.data(), dataLength - 8); |
1109 | 0 | } else if (dataType == 8) { |
1110 | 0 | dataLength = buf.read_uint16(0, bigEndian) * 2; |
1111 | 0 | std::memset(buf.data(), 0x0, buf.size()); |
1112 | 0 | io_->readOrThrow(buf.data(), 2); |
1113 | 0 | io_->readOrThrow(buf2.data(), 2); |
1114 | 0 | if (td) |
1115 | 0 | xmpData_[_(td->label_)] = stringFormat("{}.{}", buf.read_uint16(0, bigEndian), buf2.read_uint16(0, bigEndian)); |
1116 | | |
1117 | | // Sanity check with an "unreasonably" large number |
1118 | 0 | if (dataLength > 200 || dataLength < 4) { |
1119 | 0 | #ifndef SUPPRESS_WARNINGS |
1120 | 0 | EXV_ERROR << "Xmp.video Nikon Tags, dataLength was found to be of inappropriate size." |
1121 | 0 | << " Entries considered invalid. Not Processed.\n"; |
1122 | 0 | #endif |
1123 | 0 | io_->seek(io_->tell() + dataLength - 4, BasicIo::beg); |
1124 | 0 | } else |
1125 | 0 | io_->readOrThrow(buf.data(), dataLength - 4); |
1126 | 0 | } |
1127 | 0 | } |
1128 | |
|
1129 | 0 | io_->seek(cur_pos + size, BasicIo::beg); |
1130 | 0 | } // QuickTimeVideo::NikonTagsDecoder |
1131 | | |
1132 | 172 | void QuickTimeVideo::setMediaStream(size_t atom_size) { |
1133 | 172 | size_t current_position = io_->tell(); |
1134 | 172 | size_t search_end = Safe::add(current_position, atom_size); |
1135 | 172 | if (search_end > io_->size()) |
1136 | 0 | search_end = io_->size(); |
1137 | 172 | DataBuf buf(4 + 1); |
1138 | | |
1139 | 19.1k | while (!io_->eof() && Safe::add(io_->tell(), size_t{4}) <= search_end) { |
1140 | 19.0k | io_->readOrThrow(buf.data(), 4); |
1141 | 19.0k | if (equalsQTimeTag(buf, "hdlr")) { |
1142 | 134 | if (Safe::add(io_->tell(), size_t{12}) > search_end) |
1143 | 0 | break; |
1144 | 134 | io_->readOrThrow(buf.data(), 4); |
1145 | 134 | io_->readOrThrow(buf.data(), 4); |
1146 | 134 | io_->readOrThrow(buf.data(), 4); |
1147 | | |
1148 | 134 | if (equalsQTimeTag(buf, "vide")) |
1149 | 2 | currentStream_ = Video; |
1150 | 132 | else if (equalsQTimeTag(buf, "soun")) |
1151 | 109 | currentStream_ = Audio; |
1152 | 23 | else if (equalsQTimeTag(buf, "hint")) |
1153 | 0 | currentStream_ = Hint; |
1154 | 23 | else |
1155 | 23 | currentStream_ = GenMediaHeader; |
1156 | 134 | break; |
1157 | 134 | } |
1158 | 19.0k | } |
1159 | | |
1160 | 172 | io_->seek(current_position, BasicIo::beg); |
1161 | 172 | } // QuickTimeVideo::setMediaStream |
1162 | | |
1163 | 88 | void QuickTimeVideo::timeToSampleDecoder() { |
1164 | 88 | DataBuf buf(4 + 1); |
1165 | 88 | io_->readOrThrow(buf.data(), 4); |
1166 | 88 | io_->readOrThrow(buf.data(), 4); |
1167 | 88 | uint64_t totalframes = 0; |
1168 | 88 | uint64_t timeOfFrames = 0; |
1169 | 88 | const uint32_t noOfEntries = buf.read_uint32(0, bigEndian); |
1170 | | |
1171 | 1.25k | for (uint32_t i = 0; i < noOfEntries; i++) { |
1172 | 1.17k | io_->readOrThrow(buf.data(), 4); |
1173 | 1.17k | const uint64_t temp = buf.read_uint32(0, bigEndian); |
1174 | 1.17k | totalframes = Safe::add(totalframes, temp); |
1175 | 1.17k | io_->readOrThrow(buf.data(), 4); |
1176 | 1.17k | timeOfFrames = Safe::add(timeOfFrames, temp * buf.read_uint32(0, bigEndian)); |
1177 | 1.17k | } |
1178 | 88 | if (currentStream_ == Video) { |
1179 | 10 | if (timeOfFrames == 0) |
1180 | 0 | timeOfFrames = 1; |
1181 | 10 | xmpData_["Xmp.video.FrameRate"] = |
1182 | 10 | static_cast<double>(totalframes) * static_cast<double>(mdhdTimeScale_) / static_cast<double>(timeOfFrames); |
1183 | 10 | } |
1184 | 88 | } // QuickTimeVideo::timeToSampleDecoder |
1185 | | |
1186 | 154 | void QuickTimeVideo::sampleDesc(size_t size) { |
1187 | 154 | DataBuf buf(100); |
1188 | 154 | size_t cur_pos = io_->tell(); |
1189 | 154 | io_->readOrThrow(buf.data(), 4); |
1190 | 154 | io_->readOrThrow(buf.data(), 4); |
1191 | 154 | const uint32_t noOfEntries = buf.read_uint32(0, bigEndian); |
1192 | | |
1193 | 12.3k | for (uint32_t i = 0; i < noOfEntries; i++) { |
1194 | 12.1k | if (currentStream_ == Video) |
1195 | 4.51k | imageDescDecoder(); |
1196 | 7.65k | else if (currentStream_ == Audio) |
1197 | 7.64k | audioDescDecoder(); |
1198 | 10 | else |
1199 | 10 | break; |
1200 | 12.1k | } |
1201 | 154 | io_->seek(Safe::add(cur_pos, size), BasicIo::beg); |
1202 | 154 | } // QuickTimeVideo::sampleDesc |
1203 | | |
1204 | 7.64k | void QuickTimeVideo::audioDescDecoder() { |
1205 | 7.64k | DataBuf buf(40); |
1206 | 7.64k | std::memset(buf.data(), 0x0, buf.size()); |
1207 | 7.64k | buf.data()[4] = '\0'; |
1208 | 7.64k | io_->readOrThrow(buf.data(), 4); |
1209 | 7.64k | size_t size = 82; |
1210 | | |
1211 | 7.64k | const TagVocabulary* td; |
1212 | | |
1213 | 159k | for (int i = 0; size / 4 != 0; size -= 4, i++) { |
1214 | 152k | io_->readOrThrow(buf.data(), 4); |
1215 | 152k | switch (i) { |
1216 | 7.63k | case AudioFormat: |
1217 | 7.63k | td = Exiv2::find(qTimeFileType, Exiv2::toString(buf.data())); |
1218 | 7.63k | if (td) |
1219 | 11 | xmpData_["Xmp.audio.Compressor"] = _(td->label_); |
1220 | 7.62k | else |
1221 | 7.62k | xmpData_["Xmp.audio.Compressor"] = buf.data(); |
1222 | 7.63k | break; |
1223 | 7.62k | case AudioVendorID: |
1224 | 7.62k | td = Exiv2::find(vendorIDTags, Exiv2::toString(buf.data())); |
1225 | 7.62k | if (td) |
1226 | 3 | xmpData_["Xmp.audio.VendorID"] = _(td->label_); |
1227 | 7.62k | break; |
1228 | 7.61k | case AudioChannels: |
1229 | 7.61k | xmpData_["Xmp.audio.ChannelType"] = buf.read_uint16(0, bigEndian); |
1230 | 7.61k | xmpData_["Xmp.audio.BitsPerSample"] = ((buf.data()[2] * 256) + buf.data()[3]); |
1231 | 7.61k | break; |
1232 | 7.60k | case AudioSampleRate: |
1233 | 7.60k | xmpData_["Xmp.audio.SampleRate"] = |
1234 | 7.60k | buf.read_uint16(0, bigEndian) + ((buf.data()[2] * 256 + buf.data()[3]) * 0.01); |
1235 | 7.60k | break; |
1236 | 121k | default: |
1237 | 121k | break; |
1238 | 152k | } |
1239 | 152k | } |
1240 | 7.58k | io_->readOrThrow(buf.data(), static_cast<long>(size % 4)); // cause size is so small, this cast should be right. |
1241 | 7.58k | } // QuickTimeVideo::audioDescDecoder |
1242 | | |
1243 | 4.51k | void QuickTimeVideo::imageDescDecoder() { |
1244 | 4.51k | DataBuf buf(40); |
1245 | 4.51k | std::memset(buf.data(), 0x0, buf.size()); |
1246 | 4.51k | buf.data()[4] = '\0'; |
1247 | 4.51k | io_->readOrThrow(buf.data(), 4); |
1248 | 4.51k | size_t size = 82; |
1249 | | |
1250 | 4.51k | const TagVocabulary* td; |
1251 | | |
1252 | 53.9k | for (int i = 0; size / 4 != 0; size -= 4, i++) { |
1253 | 49.4k | io_->readOrThrow(buf.data(), 4); |
1254 | | |
1255 | 49.4k | switch (i) { |
1256 | 4.50k | case codec: |
1257 | 4.50k | td = Exiv2::find(qTimeFileType, Exiv2::toString(buf.data())); |
1258 | 4.50k | if (td) |
1259 | 50 | xmpData_["Xmp.video.Codec"] = _(td->label_); |
1260 | 4.45k | else |
1261 | 4.45k | xmpData_["Xmp.video.Codec"] = buf.data(); |
1262 | 4.50k | break; |
1263 | 4.49k | case VendorID: |
1264 | 4.49k | td = Exiv2::find(vendorIDTags, Exiv2::toString(buf.data())); |
1265 | 4.49k | if (td) |
1266 | 53 | xmpData_["Xmp.video.VendorID"] = _(td->label_); |
1267 | 4.49k | break; |
1268 | 4.49k | case SourceImageWidth_Height: |
1269 | 4.49k | xmpData_["Xmp.video.SourceImageWidth"] = buf.read_uint16(0, bigEndian); |
1270 | 4.49k | xmpData_["Xmp.video.SourceImageHeight"] = ((buf.data()[2] * 256) + buf.data()[3]); |
1271 | 4.49k | break; |
1272 | 4.49k | case XResolution: |
1273 | 4.49k | xmpData_["Xmp.video.XResolution"] = |
1274 | 4.49k | buf.read_uint16(0, bigEndian) + ((buf.data()[2] * 256 + buf.data()[3]) * 0.01); |
1275 | 4.49k | break; |
1276 | 4.49k | case YResolution: |
1277 | 4.49k | xmpData_["Xmp.video.YResolution"] = |
1278 | 4.49k | buf.read_uint16(0, bigEndian) + ((buf.data()[2] * 256 + buf.data()[3]) * 0.01); |
1279 | 4.49k | io_->readOrThrow(buf.data(), 3); |
1280 | 4.49k | size -= 3; |
1281 | 4.49k | break; |
1282 | 4.48k | case CompressorName: |
1283 | 4.48k | io_->readOrThrow(buf.data(), 32); |
1284 | 4.48k | size -= 32; |
1285 | 4.48k | xmpData_["Xmp.video.Compressor"] = buf.data(); |
1286 | 4.48k | break; |
1287 | 22.4k | default: |
1288 | 22.4k | break; |
1289 | 49.4k | } |
1290 | 49.4k | } |
1291 | 4.47k | io_->readOrThrow(buf.data(), static_cast<long>(size % 4)); |
1292 | 4.47k | xmpData_["Xmp.video.BitDepth"] = static_cast<int>(buf.read_uint8(0)); |
1293 | 4.47k | } // QuickTimeVideo::imageDescDecoder |
1294 | | |
1295 | 5 | void QuickTimeVideo::multipleEntriesDecoder(size_t recursion_depth) { |
1296 | 5 | enforce(recursion_depth < max_recursion_depth_, Exiv2::ErrorCode::kerCorruptedMetadata); |
1297 | 5 | DataBuf buf(4 + 1); |
1298 | 5 | io_->readOrThrow(buf.data(), 4); |
1299 | 5 | io_->readOrThrow(buf.data(), 4); |
1300 | 5 | uint32_t noOfEntries; |
1301 | | |
1302 | 5 | noOfEntries = buf.read_uint32(0, bigEndian); |
1303 | | |
1304 | 10 | for (uint32_t i = 0; i < noOfEntries && continueTraversing_; i++) { |
1305 | 5 | decodeBlock(recursion_depth + 1); |
1306 | 5 | } |
1307 | 5 | } // QuickTimeVideo::multipleEntriesDecoder |
1308 | | |
1309 | 58 | void QuickTimeVideo::videoHeaderDecoder(size_t size) { |
1310 | 58 | DataBuf buf(3); |
1311 | 58 | std::memset(buf.data(), 0x0, buf.size()); |
1312 | 58 | buf.data()[2] = '\0'; |
1313 | 58 | currentStream_ = Video; |
1314 | | |
1315 | 58 | const TagDetails* td; |
1316 | | |
1317 | 406 | for (int i = 0; size / 2 != 0; size -= 2, i++) { |
1318 | 348 | io_->readOrThrow(buf.data(), 2); |
1319 | | |
1320 | 348 | switch (i) { |
1321 | 58 | case GraphicsMode: |
1322 | 58 | td = Exiv2::find(graphicsModetags, buf.read_uint16(0, bigEndian)); |
1323 | 58 | if (td) |
1324 | 51 | xmpData_["Xmp.video.GraphicsMode"] = _(td->label_); |
1325 | 58 | break; |
1326 | 58 | case OpColor: |
1327 | 58 | xmpData_["Xmp.video.OpColor"] = buf.read_uint16(0, bigEndian); |
1328 | 58 | break; |
1329 | 232 | default: |
1330 | 232 | break; |
1331 | 348 | } |
1332 | 348 | } |
1333 | 58 | io_->readOrThrow(buf.data(), size % 2); |
1334 | 58 | } // QuickTimeVideo::videoHeaderDecoder |
1335 | | |
1336 | 28 | void QuickTimeVideo::handlerDecoder(size_t size) { |
1337 | 28 | size_t cur_pos = io_->tell(); |
1338 | 28 | DataBuf buf(100); |
1339 | 28 | std::memset(buf.data(), 0x0, buf.size()); |
1340 | 28 | buf.data()[4] = '\0'; |
1341 | | |
1342 | 28 | const TagVocabulary* tv; |
1343 | | |
1344 | 168 | for (int i = 0; i < 5; i++) { |
1345 | 140 | io_->readOrThrow(buf.data(), 4); |
1346 | | |
1347 | 140 | switch (i) { |
1348 | 28 | case HandlerClass: |
1349 | 28 | tv = Exiv2::find(handlerClassTags, Exiv2::toString(buf.data())); |
1350 | 28 | if (tv) { |
1351 | 24 | if (currentStream_ == Video) |
1352 | 2 | xmpData_["Xmp.video.HandlerClass"] = _(tv->label_); |
1353 | 22 | else if (currentStream_ == Audio) |
1354 | 0 | xmpData_["Xmp.audio.HandlerClass"] = _(tv->label_); |
1355 | 24 | } |
1356 | 28 | break; |
1357 | 28 | case HandlerType: |
1358 | 28 | tv = Exiv2::find(handlerTypeTags, Exiv2::toString(buf.data())); |
1359 | 28 | if (tv) { |
1360 | 14 | if (currentStream_ == Video) |
1361 | 2 | xmpData_["Xmp.video.HandlerType"] = _(tv->label_); |
1362 | 12 | else if (currentStream_ == Audio) |
1363 | 0 | xmpData_["Xmp.audio.HandlerType"] = _(tv->label_); |
1364 | 14 | } |
1365 | 28 | break; |
1366 | 28 | case HandlerVendorID: |
1367 | 28 | tv = Exiv2::find(vendorIDTags, Exiv2::toString(buf.data())); |
1368 | 28 | if (tv) { |
1369 | 10 | if (currentStream_ == Video) |
1370 | 2 | xmpData_["Xmp.video.HandlerVendorID"] = _(tv->label_); |
1371 | 8 | else if (currentStream_ == Audio) |
1372 | 0 | xmpData_["Xmp.audio.HandlerVendorID"] = _(tv->label_); |
1373 | 10 | } |
1374 | 28 | break; |
1375 | 140 | } |
1376 | 140 | } |
1377 | 28 | io_->seek(cur_pos + size, BasicIo::beg); |
1378 | 28 | } // QuickTimeVideo::handlerDecoder |
1379 | | |
1380 | 1.10k | void QuickTimeVideo::fileTypeDecoder(size_t size) { |
1381 | 1.10k | DataBuf buf(5); |
1382 | 1.10k | std::memset(buf.data(), 0x0, buf.size()); |
1383 | 1.10k | buf.data()[4] = '\0'; |
1384 | 1.10k | Exiv2::Value::UniquePtr v = Exiv2::Value::create(Exiv2::xmpSeq); |
1385 | 1.10k | const TagVocabulary* td; |
1386 | | |
1387 | 11.5k | for (int i = 0; size / 4 != 0; size -= 4, i++) { |
1388 | 10.4k | io_->readOrThrow(buf.data(), 4); |
1389 | 10.4k | td = Exiv2::find(qTimeFileType, Exiv2::toString(buf.data())); |
1390 | | |
1391 | 10.4k | switch (i) { |
1392 | 1.09k | case 0: |
1393 | 1.09k | if (td) |
1394 | 1.00k | xmpData_["Xmp.video.MajorBrand"] = _(td->label_); |
1395 | 1.09k | break; |
1396 | 948 | case 1: |
1397 | 948 | xmpData_["Xmp.video.MinorVersion"] = buf.read_uint32(0, bigEndian); |
1398 | 948 | break; |
1399 | 8.42k | default: |
1400 | 8.42k | if (td) |
1401 | 462 | v->read(_(td->label_)); |
1402 | 7.96k | else |
1403 | 7.96k | v->read(Exiv2::toString(buf.data())); |
1404 | 8.42k | break; |
1405 | 10.4k | } |
1406 | 10.4k | } |
1407 | 1.10k | xmpData_.add(Exiv2::XmpKey("Xmp.video.CompatibleBrands"), v.get()); |
1408 | 1.10k | io_->readOrThrow(buf.data(), size % 4); |
1409 | 1.10k | } // QuickTimeVideo::fileTypeDecoder |
1410 | | |
1411 | 71 | void QuickTimeVideo::mediaHeaderDecoder(size_t size) { |
1412 | 71 | DataBuf buf(5); |
1413 | 71 | std::memset(buf.data(), 0x0, buf.size()); |
1414 | 71 | buf.data()[4] = '\0'; |
1415 | 71 | int64_t time_scale = 1; |
1416 | | |
1417 | 714 | for (int i = 0; size / 4 != 0; size -= 4, i++) { |
1418 | 643 | io_->readOrThrow(buf.data(), 4); |
1419 | | |
1420 | 643 | switch (i) { |
1421 | 71 | case MediaHeaderVersion: |
1422 | 71 | if (currentStream_ == Video) |
1423 | 2 | xmpData_["Xmp.video.MediaHeaderVersion"] = static_cast<int>(buf.read_uint8(0)); |
1424 | 69 | else if (currentStream_ == Audio) |
1425 | 28 | xmpData_["Xmp.audio.MediaHeaderVersion"] = static_cast<int>(buf.read_uint8(0)); |
1426 | 71 | break; |
1427 | 71 | case MediaCreateDate: |
1428 | | // A 32-bit integer that specifies (in seconds since midnight, January 1, 1904) when the movie atom was created. |
1429 | 71 | if (currentStream_ == Video) |
1430 | 2 | xmpData_["Xmp.video.MediaCreateDate"] = buf.read_uint32(0, bigEndian); |
1431 | 69 | else if (currentStream_ == Audio) |
1432 | 28 | xmpData_["Xmp.audio.MediaCreateDate"] = buf.read_uint32(0, bigEndian); |
1433 | 71 | break; |
1434 | 71 | case MediaModifyDate: |
1435 | | // A 32-bit integer that specifies (in seconds since midnight, January 1, 1904) when the movie atom was created. |
1436 | 71 | if (currentStream_ == Video) |
1437 | 2 | xmpData_["Xmp.video.MediaModifyDate"] = buf.read_uint32(0, bigEndian); |
1438 | 69 | else if (currentStream_ == Audio) |
1439 | 28 | xmpData_["Xmp.audio.MediaModifyDate"] = buf.read_uint32(0, bigEndian); |
1440 | 71 | break; |
1441 | 70 | case MediaTimeScale: |
1442 | 70 | if (currentStream_ == Video) |
1443 | 2 | xmpData_["Xmp.video.MediaTimeScale"] = buf.read_uint32(0, bigEndian); |
1444 | 68 | else if (currentStream_ == Audio) |
1445 | 27 | xmpData_["Xmp.audio.MediaTimeScale"] = buf.read_uint32(0, bigEndian); |
1446 | 70 | time_scale = std::max(1U, buf.read_uint32(0, bigEndian)); |
1447 | 70 | mdhdTimeScale_ = time_scale; |
1448 | 70 | break; |
1449 | 70 | case MediaDuration: |
1450 | 70 | if (currentStream_ == Video) |
1451 | 2 | xmpData_["Xmp.video.MediaDuration"] = time_scale ? buf.read_uint32(0, bigEndian) / time_scale : 0; |
1452 | 68 | else if (currentStream_ == Audio) |
1453 | 27 | xmpData_["Xmp.audio.MediaDuration"] = time_scale ? buf.read_uint32(0, bigEndian) / time_scale : 0; |
1454 | 70 | break; |
1455 | 70 | case MediaLanguageCode: |
1456 | 70 | if (currentStream_ == Video) |
1457 | 2 | xmpData_["Xmp.video.MediaLangCode"] = buf.read_uint16(0, bigEndian); |
1458 | 68 | else if (currentStream_ == Audio) |
1459 | 27 | xmpData_["Xmp.audio.MediaLangCode"] = buf.read_uint16(0, bigEndian); |
1460 | 70 | break; |
1461 | | |
1462 | 220 | default: |
1463 | 220 | break; |
1464 | 643 | } |
1465 | 643 | } |
1466 | 71 | io_->readOrThrow(buf.data(), size % 4); |
1467 | 71 | } // QuickTimeVideo::mediaHeaderDecoder |
1468 | | |
1469 | 71 | void QuickTimeVideo::trackHeaderDecoder(size_t size) { |
1470 | 71 | DataBuf buf(5); |
1471 | 71 | std::memset(buf.data(), 0x0, buf.size()); |
1472 | 71 | buf.data()[4] = '\0'; |
1473 | 71 | int64_t temp = 0; |
1474 | | |
1475 | 1.56k | for (int i = 0; size / 4 != 0; size -= 4, i++) { |
1476 | 1.49k | io_->readOrThrow(buf.data(), 4); |
1477 | | |
1478 | 1.49k | switch (i) { |
1479 | 71 | case TrackHeaderVersion: |
1480 | 71 | if (currentStream_ == Video) |
1481 | 2 | xmpData_["Xmp.video.TrackHeaderVersion"] = static_cast<int>(buf.read_uint8(0)); |
1482 | 69 | else if (currentStream_ == Audio) |
1483 | 27 | xmpData_["Xmp.audio.TrackHeaderVersion"] = static_cast<int>(buf.read_uint8(0)); |
1484 | 71 | break; |
1485 | 71 | case TrackCreateDate: |
1486 | | // A 32-bit integer that specifies (in seconds since midnight, January 1, 1904) when the movie atom was created. |
1487 | 71 | if (currentStream_ == Video) |
1488 | 2 | xmpData_["Xmp.video.TrackCreateDate"] = buf.read_uint32(0, bigEndian); |
1489 | 69 | else if (currentStream_ == Audio) |
1490 | 27 | xmpData_["Xmp.audio.TrackCreateDate"] = buf.read_uint32(0, bigEndian); |
1491 | 71 | break; |
1492 | 71 | case TrackModifyDate: |
1493 | | // A 32-bit integer that specifies (in seconds since midnight, January 1, 1904) when the movie atom was created. |
1494 | 71 | if (currentStream_ == Video) |
1495 | 2 | xmpData_["Xmp.video.TrackModifyDate"] = buf.read_uint32(0, bigEndian); |
1496 | 69 | else if (currentStream_ == Audio) |
1497 | 27 | xmpData_["Xmp.audio.TrackModifyDate"] = buf.read_uint32(0, bigEndian); |
1498 | 71 | break; |
1499 | 71 | case TrackID: |
1500 | 71 | if (currentStream_ == Video) |
1501 | 2 | xmpData_["Xmp.video.TrackID"] = buf.read_uint32(0, bigEndian); |
1502 | 69 | else if (currentStream_ == Audio) |
1503 | 27 | xmpData_["Xmp.audio.TrackID"] = buf.read_uint32(0, bigEndian); |
1504 | 71 | break; |
1505 | 71 | case TrackDuration: |
1506 | 71 | if (currentStream_ == Video) |
1507 | 2 | xmpData_["Xmp.video.TrackDuration"] = mvhdTimeScale_ ? buf.read_uint32(0, bigEndian) / mvhdTimeScale_ : 0; |
1508 | 69 | else if (currentStream_ == Audio) |
1509 | 27 | xmpData_["Xmp.audio.TrackDuration"] = mvhdTimeScale_ ? buf.read_uint32(0, bigEndian) / mvhdTimeScale_ : 0; |
1510 | 71 | break; |
1511 | 71 | case TrackLayer: |
1512 | 71 | if (currentStream_ == Video) |
1513 | 2 | xmpData_["Xmp.video.TrackLayer"] = buf.read_uint16(0, bigEndian); |
1514 | 69 | else if (currentStream_ == Audio) |
1515 | 27 | xmpData_["Xmp.audio.TrackLayer"] = buf.read_uint16(0, bigEndian); |
1516 | 71 | break; |
1517 | 71 | case TrackVolume: |
1518 | 71 | if (currentStream_ == Video) |
1519 | 2 | xmpData_["Xmp.video.TrackVolume"] = (static_cast<int>(buf.read_uint8(0)) + (buf.data()[2] * 0.1)) * 100; |
1520 | 69 | else if (currentStream_ == Audio) |
1521 | 27 | xmpData_["Xmp.video.TrackVolume"] = (static_cast<int>(buf.read_uint8(0)) + (buf.data()[2] * 0.1)) * 100; |
1522 | 71 | break; |
1523 | 71 | case ImageWidth: |
1524 | 71 | if (currentStream_ == Video) { |
1525 | 2 | temp = buf.read_uint16(0, bigEndian) + static_cast<int64_t>((buf.data()[2] * 256 + buf.data()[3]) * 0.01); |
1526 | 2 | xmpData_["Xmp.video.Width"] = temp; |
1527 | 2 | width_ = temp; |
1528 | 2 | } |
1529 | 71 | break; |
1530 | 71 | case ImageHeight: |
1531 | 71 | if (currentStream_ == Video) { |
1532 | 2 | temp = buf.read_uint16(0, bigEndian) + static_cast<int64_t>((buf.data()[2] * 256 + buf.data()[3]) * 0.01); |
1533 | 2 | xmpData_["Xmp.video.Height"] = temp; |
1534 | 2 | height_ = temp; |
1535 | 2 | } |
1536 | 71 | break; |
1537 | 852 | default: |
1538 | 852 | break; |
1539 | 1.49k | } |
1540 | 1.49k | } |
1541 | 71 | io_->readOrThrow(buf.data(), size % 4); |
1542 | 71 | } // QuickTimeVideo::trackHeaderDecoder |
1543 | | |
1544 | 171 | void QuickTimeVideo::movieHeaderDecoder(size_t size) { |
1545 | 171 | DataBuf buf(5); |
1546 | 171 | std::memset(buf.data(), 0x0, buf.size()); |
1547 | 171 | buf.data()[4] = '\0'; |
1548 | | |
1549 | 4.44k | for (int i = 0; size / 4 != 0; size -= 4, i++) { |
1550 | 4.27k | io_->readOrThrow(buf.data(), 4); |
1551 | | |
1552 | 4.27k | switch (i) { |
1553 | 171 | case MovieHeaderVersion: |
1554 | 171 | xmpData_["Xmp.video.MovieHeaderVersion"] = static_cast<int>(buf.read_uint8(0)); |
1555 | 171 | break; |
1556 | 171 | case CreateDate: |
1557 | | // A 32-bit integer that specifies (in seconds since midnight, January 1, 1904) when the movie atom was created. |
1558 | 171 | xmpData_["Xmp.video.DateUTC"] = buf.read_uint32(0, bigEndian); |
1559 | 171 | break; |
1560 | 171 | case ModifyDate: |
1561 | | // A 32-bit integer that specifies (in seconds since midnight, January 1, 1904) when the movie atom was created. |
1562 | 171 | xmpData_["Xmp.video.ModificationDate"] = buf.read_uint32(0, bigEndian); |
1563 | 171 | break; |
1564 | 171 | case TimeScale: |
1565 | 171 | xmpData_["Xmp.video.TimeScale"] = buf.read_uint32(0, bigEndian); |
1566 | 171 | mvhdTimeScale_ = std::max(1U, buf.read_uint32(0, bigEndian)); |
1567 | 171 | break; |
1568 | 171 | case Duration: |
1569 | 171 | if (mvhdTimeScale_ != 0) { // To prevent division by zero |
1570 | 171 | xmpData_["Xmp.video.Duration"] = buf.read_uint32(0, bigEndian) * 1000 / mvhdTimeScale_; |
1571 | 171 | } |
1572 | 171 | break; |
1573 | 171 | case PreferredRate: |
1574 | 171 | xmpData_["Xmp.video.PreferredRate"] = |
1575 | 171 | buf.read_uint16(0, bigEndian) + ((buf.data()[2] * 256 + buf.data()[3]) * 0.01); |
1576 | 171 | break; |
1577 | 171 | case PreferredVolume: |
1578 | 171 | xmpData_["Xmp.video.PreferredVolume"] = (static_cast<int>(buf.read_uint8(0)) + (buf.data()[2] * 0.1)) * 100; |
1579 | 171 | break; |
1580 | 171 | case PreviewTime: |
1581 | 171 | xmpData_["Xmp.video.PreviewTime"] = buf.read_uint32(0, bigEndian); |
1582 | 171 | break; |
1583 | 171 | case PreviewDuration: |
1584 | 171 | xmpData_["Xmp.video.PreviewDuration"] = buf.read_uint32(0, bigEndian); |
1585 | 171 | break; |
1586 | 171 | case PosterTime: |
1587 | 171 | xmpData_["Xmp.video.PosterTime"] = buf.read_uint32(0, bigEndian); |
1588 | 171 | break; |
1589 | 171 | case SelectionTime: |
1590 | 171 | xmpData_["Xmp.video.SelectionTime"] = buf.read_uint32(0, bigEndian); |
1591 | 171 | break; |
1592 | 171 | case SelectionDuration: |
1593 | 171 | xmpData_["Xmp.video.SelectionDuration"] = buf.read_uint32(0, bigEndian); |
1594 | 171 | break; |
1595 | 171 | case CurrentTime: |
1596 | 171 | xmpData_["Xmp.video.CurrentTime"] = buf.read_uint32(0, bigEndian); |
1597 | 171 | break; |
1598 | 171 | case NextTrackID: |
1599 | 171 | xmpData_["Xmp.video.NextTrackID"] = buf.read_uint32(0, bigEndian); |
1600 | 171 | break; |
1601 | 1.88k | default: |
1602 | 1.88k | break; |
1603 | 4.27k | } |
1604 | 4.27k | } |
1605 | 171 | io_->readOrThrow(buf.data(), size % 4); |
1606 | 171 | } // QuickTimeVideo::movieHeaderDecoder |
1607 | | |
1608 | 1.06k | Image::UniquePtr newQTimeInstance(BasicIo::UniquePtr io, const ImageCtorParams& params) { |
1609 | 1.06k | auto image = std::make_unique<QuickTimeVideo>(std::move(io), params); |
1610 | 1.06k | if (!image->good()) { |
1611 | 4 | return nullptr; |
1612 | 4 | } |
1613 | 1.06k | return image; |
1614 | 1.06k | } |
1615 | | |
1616 | 11.8k | bool isQTimeType(BasicIo& iIo, bool advance) { |
1617 | 11.8k | auto buf = DataBuf(12); |
1618 | 11.8k | iIo.read(buf.data(), 12); |
1619 | | |
1620 | 11.8k | if (iIo.error() || iIo.eof()) { |
1621 | 240 | return false; |
1622 | 240 | } |
1623 | 11.5k | auto qTimeTags = std::array{"PICT", "free", "ftyp", "junk", "mdat", "moov", "pict", "pnot", "skip", "uuid", "wide"}; |
1624 | | |
1625 | 11.5k | bool matched = false; |
1626 | | |
1627 | 66.5k | for (auto const& tag : qTimeTags) { |
1628 | 66.5k | auto tmp = buf.cmpBytes(4, tag, 4); |
1629 | 66.5k | if (tmp == 0) { |
1630 | | // we only match if we actually know the video type. This is done |
1631 | | // to avoid matching just on ftyp because bmffimage also has that |
1632 | | // header. |
1633 | 7.77k | if (Exiv2::find(qTimeFileType, std::string{buf.c_str(8), 4})) { |
1634 | 3.18k | matched = true; |
1635 | 3.18k | } |
1636 | 7.77k | break; |
1637 | 7.77k | } |
1638 | 66.5k | } |
1639 | | |
1640 | 11.5k | if (!advance || !matched) { |
1641 | 11.5k | iIo.seek(0L, BasicIo::beg); |
1642 | 11.5k | } |
1643 | | |
1644 | 11.5k | return matched; |
1645 | 11.8k | } |
1646 | | |
1647 | | } // namespace Exiv2 |