File Coverage

blib/lib/Image/ExifTool/QuickTime.pm
Criterion Covered Total %
statement 702 1145 61.3
branch 371 838 44.2
condition 221 509 43.4
subroutine 32 44 72.7
pod 0 34 0.0
total 1326 2570 51.6


line stmt bran cond sub pod time code
1             #------------------------------------------------------------------------------
2             # File: QuickTime.pm
3             #
4             # Description: Read QuickTime and MP4 meta information
5             #
6             # Revisions: 10/04/2005 - P. Harvey Created
7             # 12/19/2005 - P. Harvey Added MP4 support
8             # 09/22/2006 - P. Harvey Added M4A support
9             # 07/27/2010 - P. Harvey Updated to 2010-05-03 QuickTime spec
10             #
11             # References:
12             #
13             # 1) http://developer.apple.com/mac/library/documentation/QuickTime/QTFF/QTFFChap1/qtff1.html
14             # 2) http://search.cpan.org/dist/MP4-Info-1.04/
15             # 3) http://www.geocities.com/xhelmboyx/quicktime/formats/mp4-layout.txt
16             # 4) http://wiki.multimedia.cx/index.php?title=Apple_QuickTime
17             # 5) ISO 14496-12 (http://read.pudn.com/downloads64/ebook/226547/ISO_base_media_file_format.pdf)
18             # 6) ISO 14496-16 (http://www.iec-normen.de/previewpdf/info_isoiec14496-16%7Bed2.0%7Den.pdf)
19             # 7) http://atomicparsley.sourceforge.net/mpeg-4files.html
20             # 8) http://wiki.multimedia.cx/index.php?title=QuickTime_container
21             # 9) http://www.adobe.com/devnet/xmp/pdfs/XMPSpecificationPart3.pdf (Oct 2008)
22             # 10) http://code.google.com/p/mp4v2/wiki/iTunesMetadata
23             # 11) http://www.canieti.com.mx/assets/files/1011/IEC_100_1384_DC.pdf
24             # 12) QuickTime file format specification 2010-05-03
25             # 13) http://www.adobe.com/devnet/flv/pdf/video_file_format_spec_v10.pdf
26             # 14) http://standards.iso.org/ittf/PubliclyAvailableStandards/c051533_ISO_IEC_14496-12_2008.zip
27             # 15) http://getid3.sourceforge.net/source/module.audio-video.quicktime.phps
28             # 16) http://qtra.apple.com/atoms.html
29             # 17) http://www.etsi.org/deliver/etsi_ts/126200_126299/126244/10.01.00_60/ts_126244v100100p.pdf
30             # 18) https://github.com/appsec-labs/iNalyzer/blob/master/scinfo.m
31             # 19) http://nah6.com/~itsme/cvs-xdadevtools/iphone/tools/decodesinf.pl
32             # 20) https://developer.apple.com/legacy/library/documentation/quicktime/reference/QT7-1_Update_Reference/QT7-1_Update_Reference.pdf
33             # 21) Francois Bonzon private communication
34             # 22) https://developer.apple.com/library/mac/documentation/QuickTime/QTFF/Metadata/Metadata.html
35             # 23) http://atomicparsley.sourceforge.net/mpeg-4files.html
36             # 24) https://github.com/sergiomb2/libmp4v2/wiki/iTunesMetadata
37             # 25) https://cconcolato.github.io/mp4ra/atoms.html
38             # 26) https://github.com/SamsungVR/android_upload_sdk/blob/master/SDKLib/src/main/java/com/samsung/msca/samsungvr/sdk/UserVideo.java
39             # 27) https://exiftool.org/forum/index.php?topic=11517.0
40             # 28) https://docs.mp3tag.de/mapping/
41             # 29) https://developer.apple.com/documentation/quicktime-file-format/media_data_reference_atom
42             #------------------------------------------------------------------------------
43              
44             package Image::ExifTool::QuickTime;
45              
46 31     31   6814 use strict;
  31         78  
  31         1702  
47 31     31   200 use vars qw($VERSION $AUTOLOAD %stringEncoding %avType %dontInherit %eeBox);
  31         67  
  31         2982  
48 31     31   207 use Image::ExifTool qw(:DataAccess :Utils);
  31         72  
  31         10531  
49 31     31   2898 use Image::ExifTool::Exif;
  31         79  
  31         1617  
50 31     31   2006 use Image::ExifTool::GPS;
  31         74  
  31         663279  
51              
52             $VERSION = '3.29';
53              
54             sub ProcessMOV($$;$);
55             sub ProcessKeys($$$);
56             sub ProcessMetaKeys($$$);
57             sub ProcessMetaData($$$);
58             sub ProcessEncodingParams($$$);
59             sub ProcessSampleDesc($$$);
60             sub ProcessHybrid($$$);
61             sub ProcessRights($$$);
62             sub ProcessNextbase($$$);
63             sub Process_mrlh($$$);
64             sub Process_mrlv($$$);
65             sub Process_mrld($$$);
66             # ++vvvvvvvvvvvv++ (in QuickTimeStream.pl)
67             sub Process_mebx($$$);
68             sub Process_3gf($$$);
69             sub Process_gps0($$$);
70             sub Process_gsen($$$);
71             sub Process_gdat($$$);
72             sub Process_nbmt($$$);
73             sub ProcessKenwood($$$);
74             sub ProcessRIFFTrailer($$$);
75             sub ProcessTTAD($$$);
76             sub ProcessNMEA($$$);
77             sub ProcessGPSLog($$$);
78             sub ProcessGarminGPS($$$);
79             sub SaveMetaKeys($$$);
80             # ++^^^^^^^^^^^^++
81             sub ParseItemLocation($$);
82             sub ParseContentDescribes($$);
83             sub ParseItemInfoEntry($$);
84             sub ParseItemPropAssoc($$);
85             sub FixWrongFormat($);
86             sub GetMatrixStructure($$);
87             sub ConvertISO6709($);
88             sub ConvInvISO6709($);
89             sub ConvertChapterList($);
90             sub PrintChapter($);
91             sub PrintGPSCoordinates($);
92             sub PrintInvGPSCoordinates($);
93             sub UnpackLang($;$);
94             sub WriteKeys($$$);
95             sub WriteQuickTime($$$);
96             sub WriteMOV($$);
97             sub WriteNextbase($$$);
98             sub GetLangInfo($$);
99             sub CheckQTValue($$$);
100              
101             # MIME types for all entries in the ftypLookup with file extensions
102             # (defaults to 'video/mp4' if not found in this lookup)
103             my %mimeLookup = (
104             '3G2' => 'video/3gpp2',
105             '3GP' => 'video/3gpp',
106             AAX => 'audio/vnd.audible.aax',
107             DVB => 'video/vnd.dvb.file',
108             F4A => 'audio/mp4',
109             F4B => 'audio/mp4',
110             JP2 => 'image/jp2',
111             JPM => 'image/jpm',
112             JPX => 'image/jpx',
113             M4A => 'audio/mp4',
114             M4B => 'audio/mp4',
115             M4P => 'audio/mp4',
116             M4V => 'video/x-m4v',
117             MOV => 'video/quicktime',
118             MQV => 'video/quicktime',
119             HEIC => 'image/heic',
120             HEVC => 'image/heic-sequence',
121             HEICS=> 'image/heic-sequence',
122             HEIF => 'image/heif',
123             HEIFS=> 'image/heif-sequence',
124             AVIF => 'image/avif', #PH (NC)
125             CRX => 'video/x-canon-crx', # (will get overridden)
126             );
127              
128             # look up file type from ftyp atom type, with MIME type in comment if known
129             # (ref http://www.ftyps.com/)
130             my %ftypLookup = (
131             '3g2a' => '3GPP2 Media (.3G2) compliant with 3GPP2 C.S0050-0 V1.0', # video/3gpp2
132             '3g2b' => '3GPP2 Media (.3G2) compliant with 3GPP2 C.S0050-A V1.0.0', # video/3gpp2
133             '3g2c' => '3GPP2 Media (.3G2) compliant with 3GPP2 C.S0050-B v1.0', # video/3gpp2
134             '3ge6' => '3GPP (.3GP) Release 6 MBMS Extended Presentations', # video/3gpp
135             '3ge7' => '3GPP (.3GP) Release 7 MBMS Extended Presentations', # video/3gpp
136             '3gg6' => '3GPP Release 6 General Profile', # video/3gpp
137             '3gp1' => '3GPP Media (.3GP) Release 1 (probably non-existent)', # video/3gpp
138             '3gp2' => '3GPP Media (.3GP) Release 2 (probably non-existent)', # video/3gpp
139             '3gp3' => '3GPP Media (.3GP) Release 3 (probably non-existent)', # video/3gpp
140             '3gp4' => '3GPP Media (.3GP) Release 4', # video/3gpp
141             '3gp5' => '3GPP Media (.3GP) Release 5', # video/3gpp
142             '3gp6' => '3GPP Media (.3GP) Release 6 Basic Profile', # video/3gpp
143             '3gp6' => '3GPP Media (.3GP) Release 6 Progressive Download', # video/3gpp
144             '3gp6' => '3GPP Media (.3GP) Release 6 Streaming Servers', # video/3gpp
145             '3gs7' => '3GPP Media (.3GP) Release 7 Streaming Servers', # video/3gpp
146             'aax ' => 'Audible Enhanced Audiobook (.AAX)', #PH
147             'avc1' => 'MP4 Base w/ AVC ext [ISO 14496-12:2005]', # video/mp4
148             'CAEP' => 'Canon Digital Camera',
149             'caqv' => 'Casio Digital Camera',
150             'CDes' => 'Convergent Design',
151             'da0a' => 'DMB MAF w/ MPEG Layer II aud, MOT slides, DLS, JPG/PNG/MNG images',
152             'da0b' => 'DMB MAF, extending DA0A, with 3GPP timed text, DID, TVA, REL, IPMP',
153             'da1a' => 'DMB MAF audio with ER-BSAC audio, JPG/PNG/MNG images',
154             'da1b' => 'DMB MAF, extending da1a, with 3GPP timed text, DID, TVA, REL, IPMP',
155             'da2a' => 'DMB MAF aud w/ HE-AAC v2 aud, MOT slides, DLS, JPG/PNG/MNG images',
156             'da2b' => 'DMB MAF, extending da2a, with 3GPP timed text, DID, TVA, REL, IPMP',
157             'da3a' => 'DMB MAF aud with HE-AAC aud, JPG/PNG/MNG images',
158             'da3b' => 'DMB MAF, extending da3a w/ BIFS, 3GPP timed text, DID, TVA, REL, IPMP',
159             'dmb1' => 'DMB MAF supporting all the components defined in the specification',
160             'dmpf' => 'Digital Media Project', # various
161             'drc1' => 'Dirac (wavelet compression), encapsulated in ISO base media (MP4)',
162             'dv1a' => 'DMB MAF vid w/ AVC vid, ER-BSAC aud, BIFS, JPG/PNG/MNG images, TS',
163             'dv1b' => 'DMB MAF, extending dv1a, with 3GPP timed text, DID, TVA, REL, IPMP',
164             'dv2a' => 'DMB MAF vid w/ AVC vid, HE-AAC v2 aud, BIFS, JPG/PNG/MNG images, TS',
165             'dv2b' => 'DMB MAF, extending dv2a, with 3GPP timed text, DID, TVA, REL, IPMP',
166             'dv3a' => 'DMB MAF vid w/ AVC vid, HE-AAC aud, BIFS, JPG/PNG/MNG images, TS',
167             'dv3b' => 'DMB MAF, extending dv3a, with 3GPP timed text, DID, TVA, REL, IPMP',
168             'dvr1' => 'DVB (.DVB) over RTP', # video/vnd.dvb.file
169             'dvt1' => 'DVB (.DVB) over MPEG-2 Transport Stream', # video/vnd.dvb.file
170             'F4A ' => 'Audio for Adobe Flash Player 9+ (.F4A)', # audio/mp4
171             'F4B ' => 'Audio Book for Adobe Flash Player 9+ (.F4B)', # audio/mp4
172             'F4P ' => 'Protected Video for Adobe Flash Player 9+ (.F4P)', # video/mp4
173             'F4V ' => 'Video for Adobe Flash Player 9+ (.F4V)', # video/mp4
174             'isc2' => 'ISMACryp 2.0 Encrypted File', # ?/enc-isoff-generic
175             'iso2' => 'MP4 Base Media v2 [ISO 14496-12:2005]', # video/mp4 (or audio)
176             'iso3' => 'MP4 Base Media v3', # video/mp4 (or audio)
177             'iso4' => 'MP4 Base Media v4', # video/mp4 (or audio)
178             'iso5' => 'MP4 Base Media v5', # video/mp4 (or audio)
179             'iso6' => 'MP4 Base Media v6', # video/mp4 (or audio)
180             'iso7' => 'MP4 Base Media v7', # video/mp4 (or audio)
181             'iso8' => 'MP4 Base Media v8', # video/mp4 (or audio)
182             'iso9' => 'MP4 Base Media v9', # video/mp4 (or audio)
183             'isom' => 'MP4 Base Media v1 [IS0 14496-12:2003]', # video/mp4 (or audio)
184             'JP2 ' => 'JPEG 2000 Image (.JP2) [ISO 15444-1 ?]', # image/jp2
185             'JP20' => 'Unknown, from GPAC samples (prob non-existent)',
186             'jpm ' => 'JPEG 2000 Compound Image (.JPM) [ISO 15444-6]', # image/jpm
187             'jpx ' => 'JPEG 2000 with extensions (.JPX) [ISO 15444-2]', # image/jpx
188             'KDDI' => '3GPP2 EZmovie for KDDI 3G cellphones', # video/3gpp2
189             #LCAG => (found in CompatibleBrands of Leica MOV videos)
190             'M4A ' => 'Apple iTunes AAC-LC (.M4A) Audio', # audio/x-m4a
191             'M4B ' => 'Apple iTunes AAC-LC (.M4B) Audio Book', # audio/mp4
192             'M4P ' => 'Apple iTunes AAC-LC (.M4P) AES Protected Audio', # audio/mp4
193             'M4V ' => 'Apple iTunes Video (.M4V) Video', # video/x-m4v
194             'M4VH' => 'Apple TV (.M4V)', # video/x-m4v
195             'M4VP' => 'Apple iPhone (.M4V)', # video/x-m4v
196             'mj2s' => 'Motion JPEG 2000 [ISO 15444-3] Simple Profile', # video/mj2
197             'mjp2' => 'Motion JPEG 2000 [ISO 15444-3] General Profile', # video/mj2
198             'mmp4' => 'MPEG-4/3GPP Mobile Profile (.MP4/3GP) (for NTT)', # video/mp4
199             'mp21' => 'MPEG-21 [ISO/IEC 21000-9]', # various
200             'mp41' => 'MP4 v1 [ISO 14496-1:ch13]', # video/mp4
201             'mp42' => 'MP4 v2 [ISO 14496-14]', # video/mp4
202             'mp71' => 'MP4 w/ MPEG-7 Metadata [per ISO 14496-12]', # various
203             'MPPI' => 'Photo Player, MAF [ISO/IEC 23000-3]', # various
204             'mqt ' => 'Sony / Mobile QuickTime (.MQV) US Patent 7,477,830 (Sony Corp)', # video/quicktime
205             'MSNV' => 'MPEG-4 (.MP4) for SonyPSP', # audio/mp4
206             'NDAS' => 'MP4 v2 [ISO 14496-14] Nero Digital AAC Audio', # audio/mp4
207             'NDSC' => 'MPEG-4 (.MP4) Nero Cinema Profile', # video/mp4
208             'NDSH' => 'MPEG-4 (.MP4) Nero HDTV Profile', # video/mp4
209             'NDSM' => 'MPEG-4 (.MP4) Nero Mobile Profile', # video/mp4
210             'NDSP' => 'MPEG-4 (.MP4) Nero Portable Profile', # video/mp4
211             'NDSS' => 'MPEG-4 (.MP4) Nero Standard Profile', # video/mp4
212             'NDXC' => 'H.264/MPEG-4 AVC (.MP4) Nero Cinema Profile', # video/mp4
213             'NDXH' => 'H.264/MPEG-4 AVC (.MP4) Nero HDTV Profile', # video/mp4
214             'NDXM' => 'H.264/MPEG-4 AVC (.MP4) Nero Mobile Profile', # video/mp4
215             'NDXP' => 'H.264/MPEG-4 AVC (.MP4) Nero Portable Profile', # video/mp4
216             'NDXS' => 'H.264/MPEG-4 AVC (.MP4) Nero Standard Profile', # video/mp4
217             'odcf' => 'OMA DCF DRM Format 2.0 (OMA-TS-DRM-DCF-V2_0-20060303-A)', # various
218             'opf2' => 'OMA PDCF DRM Format 2.1 (OMA-TS-DRM-DCF-V2_1-20070724-C)',
219             'opx2' => 'OMA PDCF DRM + XBS extensions (OMA-TS-DRM_XBS-V1_0-20070529-C)',
220             'pana' => 'Panasonic Digital Camera',
221             'qt ' => 'Apple QuickTime (.MOV/QT)', # video/quicktime
222             'ROSS' => 'Ross Video',
223             'sdv ' => 'SD Memory Card Video', # various?
224             'ssc1' => 'Samsung stereoscopic, single stream',
225             'ssc2' => 'Samsung stereoscopic, dual stream',
226             'XAVC' => 'Sony XAVC', #PH
227             'heic' => 'High Efficiency Image Format HEVC still image (.HEIC)', # image/heic
228             'hevc' => 'High Efficiency Image Format HEVC sequence (.HEICS)', # image/heic-sequence
229             'mif1' => 'High Efficiency Image Format still image (.HEIF)', # image/heif
230             'msf1' => 'High Efficiency Image Format sequence (.HEIFS)', # image/heif-sequence
231             'heix' => 'High Efficiency Image Format still image (.HEIF)', # image/heif (ref PH, Canon 1DXmkIII)
232             'avif' => 'AV1 Image File Format (.AVIF)', # image/avif
233             'crx ' => 'Canon Raw (.CRX)', #PH (CR3 or CRM; use Canon CompressorVersion to decide)
234             );
235              
236             # use extension to determine file type
237             my %useExt = ( GLV => 'MP4' );
238              
239             # information for int32u date/time tags (time zero is Jan 1, 1904)
240             my %timeInfo = (
241             Notes => q{
242             converted from UTC to local time if the QuickTimeUTC option is set. This
243             tag is part of a binary data structure so it may not be deleted -- instead
244             the value is set to zero if the tag is deleted individually
245             },
246             Shift => 'Time',
247             Writable => 1,
248             Permanent => 1,
249             DelValue => 0,
250             # It is not uncommon for brain-dead software to use the wrong time zero, it should be
251             # Jan 1, 1904, so assume a time zero of Jan 1, 1970 if the date is before this
252             # Note: This value will be in UTC if generated by a system that is aware of the time zone
253             # (also note: this code is duplicated for the CreateDate tag)
254             RawConv => q{
255             if ($val) {
256             my $offset = (66 * 365 + 17) * 24 * 3600;
257             if ($val >= $offset or $$self{OPTIONS}{QuickTimeUTC}) {
258             $val -= $offset;
259             } elsif (not $$self{IsWriting}) {
260             $self->Warn('Patched incorrect time zero for QuickTime date/time tag',1);
261             }
262             } else {
263             undef $val if $self->Options('StrictDate');
264             }
265             return $val;
266             },
267             RawConvInv => q{
268             if ($val and $$self{FileType} eq 'CR3' and not $self->Options('QuickTimeUTC')) {
269             # convert to UTC
270             my $offset = (66 * 365 + 17) * 24 * 3600;
271             $val = ConvertUnixTime($val - $offset);
272             $val = GetUnixTime($val, 1) + $offset;
273             }
274             return $val;
275             },
276             # (all CR3 files store UTC times - PH)
277             ValueConv => 'ConvertUnixTime($val, $self->Options("QuickTimeUTC") || $$self{FileType} eq "CR3")',
278             ValueConvInv => q{
279             $val = GetUnixTime($val, $self->Options("QuickTimeUTC"));
280             return undef unless defined $val;
281             return $val unless $val;
282             return $val + (66 * 365 + 17) * 24 * 3600;
283             },
284             PrintConv => '$self->ConvertDateTime($val)',
285             PrintConvInv => q{
286             return $val if $val eq '0000:00:00 00:00:00';
287             return $self->InverseDateTime($val);
288             }
289             # (can't put Groups here because they aren't constant!)
290             );
291             # properties for ISO 8601 format date/time tags
292             my %iso8601Date = (
293             Shift => 'Time',
294             ValueConv => q{
295             require Image::ExifTool::XMP;
296             $val = Image::ExifTool::XMP::ConvertXMPDate($val);
297             $val =~ s/([-+]\d{2})(\d{2})$/$1:$2/; # add colon to timezone if necessary
298             return $val;
299             },
300             ValueConvInv => q{
301             require Image::ExifTool::XMP;
302             my $tmp = Image::ExifTool::XMP::FormatXMPDate($val);
303             ($val = $tmp) =~ s/([-+]\d{2}):(\d{2})$/$1$2/ if defined $tmp; # remove time zone colon
304             return $val;
305             },
306             PrintConv => '$self->ConvertDateTime($val)',
307             PrintConvInv => '$self->InverseDateTime($val,1)', # (add time zone if it didn't exist)
308             );
309             # information for duration tags
310             my %durationInfo = (
311             ValueConv => '$$self{TimeScale} ? $val / $$self{TimeScale} : $val',
312             PrintConv => '$$self{TimeScale} ? ConvertDuration($val) : $val',
313             );
314             # handle unknown tags
315             my %unknownInfo = (
316             Unknown => 1,
317             ValueConv => '$val =~ /^([\x20-\x7e]*)\0*$/ ? $1 : \$val',
318             );
319              
320             # multi-language text with 6-byte header
321             my %langText = ( IText => 6 );
322              
323             # parsing for most of the 3gp udta language text boxes
324             my %langText3gp = (
325             Notes => 'used in 3gp videos',
326             Avoid => 1,
327             IText => 6,
328             );
329              
330             # 4-character Vendor ID codes (ref PH)
331             my %vendorID = (
332             appl => 'Apple',
333             fe20 => 'Olympus (fe20)', # (FE200)
334             FFMP => 'FFmpeg',
335             'GIC '=> 'General Imaging Co.',
336             kdak => 'Kodak',
337             KMPI => 'Konica-Minolta',
338             leic => 'Leica',
339             mino => 'Minolta',
340             niko => 'Nikon',
341             NIKO => 'Nikon',
342             olym => 'Olympus',
343             pana => 'Panasonic',
344             pent => 'Pentax',
345             pr01 => 'Olympus (pr01)', # (FE100,FE110,FE115)
346             sany => 'Sanyo',
347             'SMI '=> 'Sorenson Media Inc.',
348             ZORA => 'Zoran Corporation',
349             'AR.D'=> 'Parrot AR.Drone',
350             ' KD '=> 'Kodak', # (FZ201)
351             );
352              
353             # QuickTime data atom encodings for string types (ref 12)
354             %stringEncoding = (
355             1 => 'UTF8',
356             2 => 'UTF16',
357             3 => 'ShiftJIS',
358             4 => 'UTF8',
359             5 => 'UTF16',
360             );
361              
362             # media types for which we have separate Keys tables (AudioKeys, VideoKeys)
363             %avType = (
364             soun => 'Audio',
365             vide => 'Video',
366             );
367              
368             # path to Keys/ItemList/UserData tags stored in tracks
369             my %trackPath = (
370             'MOV-Movie-Track-Meta-ItemList' => 'Keys',
371             'MOV-Movie-Track-UserData-Meta-ItemList' => 'ItemList',
372             'MOV-Movie-Track-UserData' => 'UserData',
373             );
374              
375             my %graphicsMode = (
376             # (ref http://homepage.mac.com/vanhoek/MovieGuts%20docs/64.html)
377             0x00 => 'srcCopy',
378             0x01 => 'srcOr',
379             0x02 => 'srcXor',
380             0x03 => 'srcBic',
381             0x04 => 'notSrcCopy',
382             0x05 => 'notSrcOr',
383             0x06 => 'notSrcXor',
384             0x07 => 'notSrcBic',
385             0x08 => 'patCopy',
386             0x09 => 'patOr',
387             0x0a => 'patXor',
388             0x0b => 'patBic',
389             0x0c => 'notPatCopy',
390             0x0d => 'notPatOr',
391             0x0e => 'notPatXor',
392             0x0f => 'notPatBic',
393             0x20 => 'blend',
394             0x21 => 'addPin',
395             0x22 => 'addOver',
396             0x23 => 'subPin',
397             0x24 => 'transparent',
398             0x25 => 'addMax',
399             0x26 => 'subOver',
400             0x27 => 'addMin',
401             0x31 => 'grayishTextOr',
402             0x32 => 'hilite',
403             0x40 => 'ditherCopy',
404             # the following ref ISO/IEC 15444-3
405             0x100 => 'Alpha',
406             0x101 => 'White Alpha',
407             0x102 => 'Pre-multiplied Black Alpha',
408             0x110 => 'Component Alpha',
409             );
410              
411             my %channelLabel = (
412             0xFFFFFFFF => 'Unknown',
413             0 => 'Unused',
414             100 => 'UseCoordinates',
415             1 => 'Left',
416             2 => 'Right',
417             3 => 'Center',
418             4 => 'LFEScreen',
419             5 => 'LeftSurround',
420             6 => 'RightSurround',
421             7 => 'LeftCenter',
422             8 => 'RightCenter',
423             9 => 'CenterSurround',
424             10 => 'LeftSurroundDirect',
425             11 => 'RightSurroundDirect',
426             12 => 'TopCenterSurround',
427             13 => 'VerticalHeightLeft',
428             14 => 'VerticalHeightCenter',
429             15 => 'VerticalHeightRight',
430             16 => 'TopBackLeft',
431             17 => 'TopBackCenter',
432             18 => 'TopBackRight',
433             33 => 'RearSurroundLeft',
434             34 => 'RearSurroundRight',
435             35 => 'LeftWide',
436             36 => 'RightWide',
437             37 => 'LFE2',
438             38 => 'LeftTotal',
439             39 => 'RightTotal',
440             40 => 'HearingImpaired',
441             41 => 'Narration',
442             42 => 'Mono',
443             43 => 'DialogCentricMix',
444             44 => 'CenterSurroundDirect',
445             45 => 'Haptic',
446             200 => 'Ambisonic_W',
447             201 => 'Ambisonic_X',
448             202 => 'Ambisonic_Y',
449             203 => 'Ambisonic_Z',
450             204 => 'MS_Mid',
451             205 => 'MS_Side',
452             206 => 'XY_X',
453             207 => 'XY_Y',
454             301 => 'HeadphonesLeft',
455             302 => 'HeadphonesRight',
456             304 => 'ClickTrack',
457             305 => 'ForeignLanguage',
458             400 => 'Discrete',
459             0x10000 => 'Discrete_0',
460             0x10001 => 'Discrete_1',
461             0x10002 => 'Discrete_2',
462             0x10003 => 'Discrete_3',
463             0x10004 => 'Discrete_4',
464             0x10005 => 'Discrete_5',
465             0x10006 => 'Discrete_6',
466             0x10007 => 'Discrete_7',
467             0x10008 => 'Discrete_8',
468             0x10009 => 'Discrete_9',
469             0x1000a => 'Discrete_10',
470             0x1000b => 'Discrete_11',
471             0x1000c => 'Discrete_12',
472             0x1000d => 'Discrete_13',
473             0x1000e => 'Discrete_14',
474             0x1000f => 'Discrete_15',
475             0x1ffff => 'Discrete_65535',
476             );
477              
478             my %qtFlags = ( #12
479             0 => 'undef', 22 => 'unsigned int', 71 => 'float[2] size',
480             1 => 'UTF-8', 23 => 'float', 72 => 'float[4] rect',
481             2 => 'UTF-16', 24 => 'double', 74 => 'int64s',
482             3 => 'ShiftJIS', 27 => 'BMP', 75 => 'int8u',
483             4 => 'UTF-8 sort', 28 => 'QT atom', 76 => 'int16u',
484             5 => 'UTF-16 sort', 65 => 'int8s', 77 => 'int32u',
485             13 => 'JPEG', 66 => 'int16s', 78 => 'int64u',
486             14 => 'PNG', 67 => 'int32s', 79 => 'double[3][3]',
487             21 => 'signed int', 70 => 'float[2] point',
488             );
489              
490             # properties which don't get inherited from the parent
491             # 1 = parent doesn't inherit this property
492             # 2 = parent doesn't inherit, but child does
493             %dontInherit = (
494             ispe => 1, # primary item must have an ispe and pixi, so no need to inherit these
495             pixi => 1,
496             irot => 1, # (tmap may have a different irot)
497             imir => 1, # (ditto)
498             pasp => 1, # (NC)
499             hvcC => 2, # (hvcC is a property of hvc1 referred to by primary grid)
500             colr => 2, # (colr is a property of primary grid or hvc1 referred to by primary)
501             );
502              
503             # tags that may be duplicated and directories that may contain duplicate tags
504             # (used only to avoid warnings when Validate-ing)
505             my %dupTagOK = ( mdat => 1, trak => 1, free => 1, infe => 1, sgpd => 1, dimg => 1, CCDT => 1,
506             sbgp => 1, csgm => 1, uuid => 1, cdsc => 1, maxr => 1, '----' => 1 );
507             my %dupDirOK = ( ipco => 1, iref => 1, '----' => 1 );
508              
509             # the usual atoms required to decode timed metadata with the ExtractEmbedded option
510             my %eeStd = ( stco => 'stbl', co64 => 'stbl', stsz => 'stbl', stz2 => 'stbl',
511             stsc => 'stbl', stts => 'stbl' );
512              
513             # atoms required for generating ImageDataHash
514             my %hashBox = ( vide => { %eeStd }, soun => { %eeStd } );
515              
516             # boxes and their containers for the various handler types that we want to save
517             # when the ExtractEmbedded is enabled (currently only the 'gps ' container name is
518             # used, but others have been checked against all available sample files and may be
519             # useful in the future if the names are used for different boxes on other locations)
520             %eeBox = (
521             # (note: vide is only processed if specific atoms exist in the VisualSampleDesc)
522             vide => { %eeStd, JPEG => 'stsd' },
523             text => { %eeStd },
524             meta => { %eeStd },
525             sbtl => { %eeStd },
526             data => { %eeStd },
527             camm => { %eeStd }, # (Insta360)
528             ctbx => { %eeStd }, # (GM cars)
529             '' => { 'gps ' => 'moov', 'GPS ' => 'main' }, # (no handler -- in top level 'moov' box, and main)
530             );
531             # boxes to save when ExtractEmbedded is set to 2 or higher
532             my %eeBox2 = (
533             vide => { avcC => 'stsd' }, # (parses H264 video stream)
534             );
535              
536             # image types in AVIF and HEIC files
537             my %isImageData = ( av01 => 1, avc1 => 1, hvc1 => 1, lhv1 => 1, hvt1 => 1 );
538              
539             my %userDefined = (
540             ALBUMARTISTSORT => 'AlbumArtistSort',
541             ASIN => 'ASIN',
542             );
543              
544             # QuickTime atoms
545             %Image::ExifTool::QuickTime::Main = (
546             PROCESS_PROC => \&ProcessMOV,
547             WRITE_PROC => \&WriteQuickTime, # (only needs to be defined for directories to process when writing)
548             GROUPS => { 2 => 'Video' },
549             meta => { # 'meta' is found here in my Sony ILCE-7S MP4 sample - PH
550             Name => 'Meta',
551             SubDirectory => {
552             TagTable => 'Image::ExifTool::QuickTime::Meta',
553             Start => 4, # skip 4-byte version number header
554             },
555             },
556             meco => { #ISO14496-12:2015
557             Name => 'OtherMeta',
558             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::OtherMeta' },
559             },
560             free => [
561             {
562             Name => 'KodakFree',
563             # (found in Kodak M5370 MP4 videos)
564             Condition => '$$valPt =~ /^\0\0\0.Seri/s',
565             SubDirectory => { TagTable => 'Image::ExifTool::Kodak::Free' },
566             },{
567             Name => 'Pittasoft',
568             # (Pittasoft Blackview dashcam MP4 videos)
569             Condition => '$$valPt =~ /^\0\0..(cprt|sttm|ptnm|ptrh|thum|gps |3gf )/s',
570             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Pittasoft' },
571             },{
572             Name => 'ThumbnailImage',
573             # (DJI Zenmuse XT2 thermal camera)
574             Groups => { 2 => 'Preview' },
575             Condition => '$$valPt =~ /^.{4}mdat\xff\xd8\xff/s',
576             RawConv => q{
577             my $len = unpack('N', $val);
578             return undef if $len <= 8 or $len > length($val);
579             return substr($val, 8, $len-8);
580             },
581             Binary => 1,
582             },{
583             Name => 'HighlightMarkers',
584             # (DJI Action 4, forum17700)
585             Notes => 'written by some DJI models',
586             Condition => '$$valPt =~ /^data.{4}hglg.{5}/s',
587             RawConv => q{
588             my $len = unpack 'x4N', $val;
589             return undef if $len < 13 or $len + 4 > length($val);
590             my $n = int(($len - 13) / 5);
591             my @a = map $_/1000, unpack "x17(xV)$n", $val;
592             return \@a;
593             },
594             },{
595             Unknown => 1,
596             Binary => 1,
597             },
598             # DJI videos also have block of offset/size of various atoms, eg)
599             # Atom name ???? Offset Size
600             # 0000: 63 6f 76 72 00 00 00 00 00 ed 6f da 00 0a 46 e0 [covr......o...F.]
601             # 0010: 73 6e 61 6c 00 00 00 00 00 f7 b6 d2 00 0a 46 e0 [snal..........F.]
602             # 0020: 68 67 6c 67 00 00 00 00 01 02 0a a2 00 00 00 21 [hglg...........!]
603             # 0030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [................]
604             # (also Samsung WB750 uncompressed thumbnail data starting with "SDIC\0")
605             ],
606             # fre1 - 4 bytes: "june" (Kodak PixPro SP360)
607             frea => {
608             Name => 'Kodak_frea',
609             SubDirectory => { TagTable => 'Image::ExifTool::Kodak::frea' },
610             },
611             skip => [
612             {
613             Name => 'CanonSkip',
614             Condition => '$$valPt =~ /^\0.{3}(CNDB|CNCV|CNMN|CNFV|CNTH|CNDM)/s',
615             SubDirectory => { TagTable => 'Image::ExifTool::Canon::Skip' },
616             },
617             {
618             Name => 'PreviewImage', # (found in DuDuBell M1 dashcam MOV files)
619             Groups => { 2 => 'Preview' },
620             Condition => '$$valPt =~ /^.{12}\xff\xd8\xff/',
621             Binary => 1,
622             RawConv => q{
623             my $len = Get32u(\$val, 8);
624             return undef unless length($val) >= $len + 12;
625             return substr($val, 12, $len);
626             },
627             },
628             {
629             Name => 'SkipInfo', # (found in 70mai Pro Plus+ MP4 videos)
630             # (look for something that looks like a QuickTime atom header)
631             Condition => '$$valPt =~ /^\0[\0-\x04]..[a-zA-Z ]{4}/s',
632             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::SkipInfo' },
633             },
634             {
635             Name => 'LigoGPSInfo',
636             Condition => '$$valPt =~ /^LIGOGPSINFO\0/ and $$self{OPTIONS}{ExtractEmbedded}',
637             SubDirectory => {
638             TagTable => 'Image::ExifTool::QuickTime::Stream',
639             ProcessProc => 'Image::ExifTool::LigoGPS::ProcessLigoGPS',
640             },
641             },
642             {
643             Name => 'Skip',
644             RawConv => q{
645             if ($val =~ /^LIGOGPSINFO\0/) {
646             $self->Warn('Use the ExtractEmbedded option to decode timed GPS',3);
647             return undef;
648             }
649             return $val;
650             },
651             Unknown => 1,
652             Binary => 1,
653             },
654             ],
655             wide => { Unknown => 1, Binary => 1 },
656             ftyp => { #MP4
657             Name => 'FileType',
658             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::FileType' },
659             },
660             pnot => {
661             Name => 'Preview',
662             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Preview' },
663             },
664             PICT => {
665             Name => 'PreviewPICT',
666             Groups => { 2 => 'Preview' },
667             Binary => 1,
668             },
669             pict => { #8
670             Name => 'PreviewPICT',
671             Groups => { 2 => 'Preview' },
672             Binary => 1,
673             },
674             # (note that moov is present for an HEIF sequence)
675             moov => {
676             Name => 'Movie',
677             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Movie' },
678             },
679             moof => {
680             Name => 'MovieFragment',
681             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::MovieFragment' },
682             },
683             # mfra - movie fragment random access: contains tfra (track fragment random access), and
684             # mfro (movie fragment random access offset) (ref 5)
685             mdat => { Name => 'MediaData', Unknown => 1, Binary => 1 },
686             'mdat-size' => {
687             Name => 'MediaDataSize',
688             RawConv => '$$self{MediaDataSize} = $val',
689             Notes => q{
690             not a real tag ID, this tag represents the size of the 'mdat' data in bytes
691             and is used in the AvgBitrate calculation
692             },
693             },
694             'mdat-offset' => {
695             Name => 'MediaDataOffset',
696             RawConv => '$$self{MediaDataOffset} = $val',
697             },
698             junk => { Unknown => 1, Binary => 1 }, #8
699             uuid => [
700             { #9 (MP4 files)
701             Name => 'XMP',
702             # *** this is where ExifTool writes XMP in MP4 videos (as per XMP spec) ***
703             Condition => '$$valPt=~/^\xbe\x7a\xcf\xcb\x97\xa9\x42\xe8\x9c\x71\x99\x94\x91\xe3\xaf\xac/',
704             WriteGroup => 'XMP', # (write main XMP tags here)
705             PreservePadding => 1,
706             SubDirectory => {
707             TagTable => 'Image::ExifTool::XMP::Main',
708             Start => 16,
709             },
710             },
711             { #11 (MP4 files)
712             Name => 'UUID-PROF',
713             Condition => '$$valPt=~/^PROF!\xd2\x4f\xce\xbb\x88\x69\x5c\xfa\xc9\xc7\x40/',
714             SubDirectory => {
715             TagTable => 'Image::ExifTool::QuickTime::Profile',
716             Start => 24, # uid(16) + version(1) + flags(3) + count(4)
717             },
718             },
719             { #PH (Flip MP4 files)
720             Name => 'UUID-Flip',
721             Condition => '$$valPt=~/^\x4a\xb0\x3b\x0f\x61\x8d\x40\x75\x82\xb2\xd9\xfa\xce\xd3\x5f\xf5/',
722             SubDirectory => {
723             TagTable => 'Image::ExifTool::QuickTime::Flip',
724             Start => 16,
725             },
726             },
727             # "\x98\x7f\xa3\xdf\x2a\x85\x43\xc0\x8f\x8f\xd9\x7c\x47\x1e\x8e\xea" - unknown data in Flip videos
728             { #PH (Canon CR3)
729             Name => 'UUID-Canon2',
730             WriteLast => 1, # MUST come after mdat or DPP will drop mdat when writing!
731             Condition => '$$valPt=~/^\x21\x0f\x16\x87\x91\x49\x11\xe4\x81\x11\x00\x24\x21\x31\xfc\xe4/',
732             SubDirectory => {
733             TagTable => 'Image::ExifTool::Canon::uuid2',
734             Start => 16,
735             },
736             },
737             { # (ref https://github.com/JamesHeinrich/getID3/blob/master/getid3/module.audio-video.quicktime.php)
738             Name => 'SensorData', # sensor data for the 360Fly
739             Condition => '$$valPt=~/^\xef\xe1\x58\x9a\xbb\x77\x49\xef\x80\x95\x27\x75\x9e\xb1\xdc\x6f/ and $$self{OPTIONS}{ExtractEmbedded}',
740             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Tags360Fly' },
741             },{
742             Name => 'SensorData',
743             Condition => '$$valPt=~/^\xef\xe1\x58\x9a\xbb\x77\x49\xef\x80\x95\x27\x75\x9e\xb1\xdc\x6f/',
744             Notes => 'raw 360Fly sensor data without ExtractEmbedded option',
745             RawConv => q{
746             $self->Warn('Use the ExtractEmbedded option to decode timed SensorData',3);
747             return \$val;
748             },
749             },
750             { #https://c2pa.org/specifications/
751             Name => 'JUMBF',
752             Condition => '$$valPt=~/^\xd8\xfe\xc3\xd6\x1b\x0e\x48\x3c\x92\x97\x58\x28\x87\x7e\xc4\x81.{4}manifest\0/s',
753             Deletable => 1,
754             SubDirectory => {
755             TagTable => 'Image::ExifTool::Jpeg2000::Main',
756             DirName => 'JUMBF',
757             # 16 bytes uuid
758             # +4 bytes 0
759             # +9 bytes "manifest\0"
760             # +8 bytes absolute(!!!) offset to C2PA uuid "merkle\0" box
761             # =37 bytes total
762             Start => 37,
763             },
764             },
765             { #https://c2pa.org/specifications/ (NC)
766             Name => 'CBOR',
767             Condition => '$$valPt=~/^\xd8\xfe\xc3\xd6\x1b\x0e\x48\x3c\x92\x97\x58\x28\x87\x7e\xc4\x81.{4}merkle\0/s',
768             Deletable => 1, # (NC)
769             SubDirectory => {
770             TagTable => 'Image::ExifTool::CBOR::Main',
771             # 16 bytes uuid
772             # +4 bytes 0
773             # +7 bytes "merkle\0"
774             # =27 bytes total
775             Start => 27,
776             },
777             },
778             { #PH (Canon CR3)
779             Name => 'PreviewImage',
780             Condition => '$$valPt=~/^\xea\xf4\x2b\x5e\x1c\x98\x4b\x88\xb9\xfb\xb7\xdc\x40\x6e\x4d\x16.{32}/s',
781             Groups => { 2 => 'Preview' },
782             PreservePadding => 1,
783             # 0x00 - undef[16]: UUID
784             # 0x10 - int32u[2]: "0 1" (version and/or item count?)
785             # 0x18 - int32u: PRVW atom size
786             # 0x20 - int32u: 'PRVW'
787             # 0x30 - int32u: 0
788             # 0x34 - int16u: 1
789             # 0x36 - int16u: image width
790             # 0x38 - int16u: image height
791             # 0x3a - int16u: 1
792             # 0x3c - int32u: preview length
793             RawConv => '$val = substr($val, 0x30); $self->ValidateImage(\$val, $tag)',
794             },
795             { #PH (Garmin MP4)
796             Name => 'ThumbnailImage',
797             Condition => '$$valPt=~/^\x11\x6e\x40\xdc\xb1\x86\x46\xe4\x84\x7c\xd9\xc0\xc3\x49\x10\x81.{8}\xff\xd8\xff/s',
798             Groups => { 2 => 'Preview' },
799             Binary => 1,
800             # 0x00 - undef[16]: UUID
801             # 0x10 - int32u[2]: ThumbnailLength
802             # 0x14 - int16u[2]: width/height of image (160/120)
803             RawConv => q{
804             my $len = Get32u(\$val, 0x10);
805             return undef unless length($val) >= $len + 0x18;
806             return substr($val, 0x18, $len);
807             },
808             },
809             # also seen 120-byte record in Garmin MP4's, starting like this (model name at byte 9):
810             # 0000: 47 52 4d 4e 00 00 00 01 00 44 43 35 37 00 00 00 [GRMN.....DC57...]
811             # 0000: 47 52 4d 4e 00 00 00 01 00 44 43 36 36 57 00 00 [GRMN.....DC66W..]
812             # and this in Garmin, followed by 8 bytes of 0's:
813             # 0000: db 11 98 3d 8f 65 43 8c bb b8 e1 ac 56 fe 6b 04
814             { #8
815             Name => 'UUID-Unknown',
816             %unknownInfo,
817             },
818             ],
819             _htc => {
820             Name => 'HTCInfo',
821             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::HTCInfo' },
822             },
823             udta => [{
824             Name => 'KenwoodData',
825             Condition => '$$valPt =~ /^VIDEOUUUUUUUUUUUUUUUUUUUUUU/',
826             SubDirectory => {
827             TagTable => 'Image::ExifTool::QuickTime::Stream',
828             ProcessProc => \&ProcessKenwood,
829             },
830             },{
831             Name => 'LigoJSON',
832             Condition => '$$valPt =~ /^LIGOGPSINFO \{/',
833             SubDirectory => {
834             TagTable => 'Image::ExifTool::QuickTime::Stream',
835             ProcessProc => 'Image::ExifTool::LigoGPS::ProcessLigoJSON',
836             },
837             },{
838             Name => 'GKUData',
839             Condition => '$$valPt =~ /^.{8}__V35AX_QVDATA__/',
840             SubDirectory => {
841             TagTable => 'Image::ExifTool::QuickTime::Stream',
842             ProcessProc => 'Image::ExifTool::LigoGPS::ProcessGKU',
843             },
844             },{
845             Name => 'FLIRData',
846             SubDirectory => { TagTable => 'Image::ExifTool::FLIR::UserData' },
847             }],
848             thum => { #PH
849             Name => 'ThumbnailImage',
850             Groups => { 2 => 'Preview' },
851             Binary => 1,
852             },
853             'thm ' => { #PH (70mai A800)
854             Name => 'ThumbnailImage',
855             Groups => { 2 => 'Preview' },
856             Binary => 1,
857             },
858             ardt => { #PH
859             Name => 'ARDroneFile',
860             ValueConv => 'length($val) > 4 ? substr($val,4) : $val', # remove length
861             },
862             prrt => { #PH
863             Name => 'ARDroneTelemetry',
864             Notes => q{
865             telemetry information for each video frame: status1, status2, time, pitch,
866             roll, yaw, speed, altitude
867             },
868             ValueConv => q{
869             my $size = length $val;
870             return \$val if $size < 12 or not $$self{OPTIONS}{Binary};
871             my $len = Get16u(\$val, 2);
872             my $str = '';
873             SetByteOrder('II');
874             my $pos = 12;
875             while ($pos + $len <= $size) {
876             my $s1 = Get16u(\$val, $pos);
877             # s2: 7=take-off?, 3=moving, 4=hovering, 9=landing?, 2=landed
878             my $s2 = Get16u(\$val, $pos + 2);
879             $str .= "$s1 $s2";
880             my $num = int(($len-4)/4);
881             my ($i, $v);
882             for ($i=0; $i<$num; ++$i) {
883             my $pt = $pos + 4 + $i * 4;
884             if ($i > 0 && $i < 4) {
885             $v = GetFloat(\$val, $pt); # pitch/roll/yaw
886             } else {
887             $v = Get32u(\$val, $pt);
888             # convert time to sec, and speed(NC)/altitude to metres
889             $v /= 1000 if $i <= 5;
890             }
891             $str .= " $v";
892             }
893             $str .= "\n";
894             $pos += $len;
895             }
896             SetByteOrder('MM');
897             return \$str;
898             },
899             },
900             udat => { #PH (GPS NMEA-format log written by Datakam Player software)
901             Name => 'GPSLog',
902             Binary => 1, # (actually ASCII, but very lengthy)
903             Notes => 'parsed to extract GPS separately when ExtractEmbedded is used',
904             RawConv => q{
905             $val =~ s/\0+$//; # remove trailing nulls
906             if (length $val and $$self{OPTIONS}{ExtractEmbedded}) {
907             my $tagTbl = GetTagTable('Image::ExifTool::QuickTime::Stream');
908             Image::ExifTool::QuickTime::ProcessGPSLog($self, { DataPt => \$val }, $tagTbl);
909             }
910             return $val;
911             },
912             },
913             # meta - proprietary XML information written by some Flip cameras - PH
914             # beam - 16 bytes found in an iPhone video
915             IDIT => { #PH (written by DuDuBell M1, VSYS M6L dashcams)
916             Name => 'DateTimeOriginal',
917             Description => 'Date/Time Original',
918             Groups => { 2 => 'Time' },
919             Format => 'string', # (removes trailing "\0")
920             Shift => 'Time',
921             Writable => 1,
922             Permanent => 1,
923             DelValue => '0000-00-00T00:00:00+0000',
924             ValueConv => '$val=~tr/-/:/; $val',
925             ValueConvInv => '$val=~s/(\d+):(\d+):/$1-$2-/; $val',
926             PrintConv => '$self->ConvertDateTime($val)',
927             PrintConvInv => '$self->InverseDateTime($val,1)', # (add time zone if it didn't exist)
928             },
929             gps0 => { #PH (DuDuBell M1, VSYS M6L)
930             Name => 'GPSTrack',
931             SubDirectory => {
932             TagTable => 'Image::ExifTool::QuickTime::Stream',
933             ProcessProc => \&Process_gps0,
934             },
935             },
936             gsen => { #PH (DuDuBell M1, VSYS M6L)
937             Name => 'GSensor',
938             SubDirectory => {
939             TagTable => 'Image::ExifTool::QuickTime::Stream',
940             ProcessProc => \&Process_gsen,
941             },
942             },
943             # gpsa - seen hex "01 20 00 00" (DuDuBell M1, VSYS M6L)
944             # gsea - 20 bytes hex "05 00's..." (DuDuBell M1) "05 08 02 01 ..." (VSYS M6L)
945             gdat => { # Base64-encoded JSON-format timed GPS (Nextbase software)
946             Name => 'GPSData',
947             SubDirectory => {
948             TagTable => 'Image::ExifTool::QuickTime::Stream',
949             ProcessProc => \&Process_gdat,
950             },
951             },
952             nbmt => { # (Nextbase)
953             Name => 'NextbaseMeta',
954             SubDirectory => {
955             TagTable => 'Image::ExifTool::QuickTime::Stream',
956             ProcessProc => \&Process_nbmt,
957             },
958             },
959             'GPS ' => { # GPS data written by 70mai dashcam (parsed in QuickTimeStream.pl)
960             Name => 'GPSDataList2',
961             Unknown => 1,
962             Binary => 1,
963             },
964             sefd => {
965             Name => 'SamsungTrailer',
966             SubDirectory => { TagTable => 'Image::ExifTool::Samsung::Trailer' },
967             },
968             # 'samn'? - seen in Vantrue N2S sample video
969             mpvd => {
970             Name => 'MotionPhotoVideo',
971             Notes => 'MP4-format video saved in Samsung motion-photo HEIC images.',
972             Binary => 1,
973             # note that this may be written and/or deleted, but can't currently be added back again
974             Writable => 'undef',
975             WriteLast => 1, # (must come after mdat according to https://developer.android.com/media/platform/motion-photo-format)
976             },
977             # '35AX'? - seen "AT" (Yada RoadCam Pro 4K dashcam)
978             cust => 'CustomInfo', # 70mai A810
979             SEAL => {
980             Name => 'SEAL',
981             SubDirectory => { TagTable => 'Image::ExifTool::XMP::SEAL' },
982             },
983             inst => {
984             Name => 'Insta360Info',
985             DontRead => 1, # don't read into memory when extracting
986             WriteLast => 1, # must go at end of file
987             SubDirectory => {
988             TagTable => 'Image::ExifTool::QuickTime::Stream',
989             ProcessProc => \&ProcessInsta360,
990             },
991             },
992             # Kandao tags (Kandao QooCam 3 Ultra)
993             kvar => {
994             Name => 'KVAR',
995             BlockExtract => 1,
996             Notes => q{
997             by default, data in this tag is parsed to extract some embedded metadata,
998             but it may also be extracted as a KVAR file via the "KVAR" tag or by setting
999             the API BlockExtract option
1000             },
1001             SubDirectory => { TagTable => 'Image::ExifTool::Kandao::Main' },
1002             },
1003             kfix => {
1004             Name => 'KFIX',
1005             SubDirectory => { TagTable => 'Image::ExifTool::Kandao::Main' },
1006             },
1007             kstb => { # (NC)
1008             Name => 'KSTB',
1009             SubDirectory => { TagTable => 'Image::ExifTool::Kandao::Main' },
1010             },
1011             );
1012              
1013             # stuff seen in 'skip' atom (70mai Pro Plus+ MP4 videos)
1014             %Image::ExifTool::QuickTime::SkipInfo = (
1015             PROCESS_PROC => \&ProcessMOV,
1016             GROUPS => { 2 => 'Video' },
1017             'ver ' => 'Version',
1018             # tima - int32u: seen 0x3c
1019             thma => {
1020             Name => 'ThumbnailImage',
1021             Groups => { 2 => 'Preview' },
1022             Binary => 1,
1023             },
1024             );
1025              
1026             # MPEG-4 'ftyp' atom
1027             # (ref http://developer.apple.com/mac/library/documentation/QuickTime/QTFF/QTFFChap1/qtff1.html)
1028             %Image::ExifTool::QuickTime::FileType = (
1029             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1030             GROUPS => { 2 => 'Video' },
1031             FORMAT => 'int32u',
1032             0 => {
1033             Name => 'MajorBrand',
1034             Format => 'undef[4]',
1035             PrintConv => \%ftypLookup,
1036             },
1037             1 => {
1038             Name => 'MinorVersion',
1039             Format => 'undef[4]',
1040             ValueConv => 'sprintf("%x.%x.%x", unpack("nCC", $val))',
1041             },
1042             2 => {
1043             Name => 'CompatibleBrands',
1044             Format => 'undef[$size-8]',
1045             List => 1, # (for documentation only)
1046             # ignore any entry with a null, and return others as a list
1047             ValueConv => 'my @a=($val=~/.{4}/sg); @a=grep(!/\0/,@a); \@a',
1048             },
1049             );
1050              
1051             # proprietary HTC atom (HTC One MP4 video)
1052             %Image::ExifTool::QuickTime::HTCInfo = (
1053             PROCESS_PROC => \&ProcessMOV,
1054             GROUPS => { 2 => 'Video' },
1055             NOTES => 'Tags written by some HTC camera phones.',
1056             slmt => {
1057             Name => 'Unknown_slmt',
1058             Unknown => 1,
1059             Format => 'int32u', # (observed values: 4)
1060             },
1061             );
1062              
1063             # atoms used in QTIF files
1064             %Image::ExifTool::QuickTime::ImageFile = (
1065             PROCESS_PROC => \&ProcessMOV,
1066             GROUPS => { 2 => 'Image' },
1067             NOTES => 'Tags used in QTIF QuickTime Image Files.',
1068             idsc => {
1069             Name => 'ImageDescription',
1070             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::VisualSampleDesc' },
1071             },
1072             idat => {
1073             Name => 'ImageData',
1074             Binary => 1,
1075             },
1076             iicc => {
1077             Name => 'ICC_Profile',
1078             SubDirectory => { TagTable => 'Image::ExifTool::ICC_Profile::Main' },
1079             },
1080             );
1081              
1082             # 'sv3d' atom information (ref https://github.com/google/spatial-media/blob/master/docs/spherical-video-v2-rfc.md)
1083             %Image::ExifTool::QuickTime::sv3d = (
1084             PROCESS_PROC => \&ProcessMOV,
1085             GROUPS => { 2 => 'Video' },
1086             NOTES => q{
1087             Tags defined by the Spherical Video V2 specification. See
1088             L
1089             for the specification.
1090             },
1091             svhd => {
1092             Name => 'MetadataSource',
1093             Format => 'undef',
1094             ValueConv => '$val=~tr/\0//d; $val', # (remove version/flags? and terminator?)
1095             },
1096             proj => {
1097             Name => 'Projection',
1098             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::proj' },
1099             },
1100             );
1101              
1102             # 'proj' atom information (ref https://github.com/google/spatial-media/blob/master/docs/spherical-video-v2-rfc.md)
1103             %Image::ExifTool::QuickTime::proj = (
1104             PROCESS_PROC => \&ProcessMOV,
1105             GROUPS => { 2 => 'Video' },
1106             prhd => {
1107             Name => 'ProjectionHeader',
1108             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::prhd' },
1109             },
1110             cbmp => {
1111             Name => 'CubemapProj',
1112             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::cbmp' },
1113             },
1114             equi => {
1115             Name => 'EquirectangularProj',
1116             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::equi' },
1117             },
1118             # mshp - MeshProjection (P.I.T.A. to decode, for not much reward, see ref)
1119             );
1120              
1121             # 'prhd' atom information (ref https://github.com/google/spatial-media/blob/master/docs/spherical-video-v2-rfc.md)
1122             %Image::ExifTool::QuickTime::prhd = (
1123             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1124             GROUPS => { 2 => 'Video' },
1125             FORMAT => 'fixed32s',
1126             # 0 - version (high 8 bits) / flags (low 24 bits)
1127             1 => 'PoseYawDegrees',
1128             2 => 'PosePitchDegrees',
1129             3 => 'PoseRollDegrees',
1130             );
1131              
1132             # 'cbmp' atom information (ref https://github.com/google/spatial-media/blob/master/docs/spherical-video-v2-rfc.md)
1133             %Image::ExifTool::QuickTime::cbmp = (
1134             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1135             GROUPS => { 2 => 'Video' },
1136             FORMAT => 'int32u',
1137             # 0 - version (high 8 bits) / flags (low 24 bits)
1138             1 => 'Layout',
1139             2 => 'Padding',
1140             );
1141              
1142             # 'equi' atom information (ref https://github.com/google/spatial-media/blob/master/docs/spherical-video-v2-rfc.md)
1143             %Image::ExifTool::QuickTime::equi = (
1144             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1145             GROUPS => { 2 => 'Video' },
1146             FORMAT => 'int32u', # (actually 0.32 fixed point)
1147             # 0 - version (high 8 bits) / flags (low 24 bits)
1148             1 => { Name => 'ProjectionBoundsTop', ValueConv => '$val / 4294967296' },
1149             2 => { Name => 'ProjectionBoundsBottom',ValueConv => '$val / 4294967296' },
1150             3 => { Name => 'ProjectionBoundsLeft', ValueConv => '$val / 4294967296' },
1151             4 => { Name => 'ProjectionBoundsRight', ValueConv => '$val / 4294967296' },
1152             );
1153              
1154             # 'btrt' atom information (ref http://lists.freedesktop.org/archives/gstreamer-commits/2011-October/054459.html)
1155             %Image::ExifTool::QuickTime::Bitrate = (
1156             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1157             GROUPS => { 2 => 'Video' },
1158             FORMAT => 'int32u',
1159             PRIORITY => 0, # often filled with zeros
1160             0 => 'BufferSize',
1161             1 => 'MaxBitrate',
1162             2 => 'AverageBitrate',
1163             );
1164              
1165             # 'clap' atom information (ref https://developer.apple.com/library/mac/technotes/tn2162/_index.html#//apple_ref/doc/uid/DTS40013070-CH1-TNTAG9)
1166             %Image::ExifTool::QuickTime::CleanAperture = (
1167             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1168             GROUPS => { 2 => 'Video' },
1169             FORMAT => 'rational64s',
1170             0 => 'CleanApertureWidth',
1171             1 => 'CleanApertureHeight',
1172             2 => 'CleanApertureOffsetX',
1173             3 => 'CleanApertureOffsetY',
1174             );
1175              
1176             # preview data block
1177             %Image::ExifTool::QuickTime::Preview = (
1178             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1179             WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
1180             CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
1181             GROUPS => { 2 => 'Image' },
1182             FORMAT => 'int16u',
1183             0 => {
1184             Name => 'PreviewDate',
1185             Format => 'int32u',
1186             Groups => { 2 => 'Time' },
1187             %timeInfo,
1188             },
1189             2 => 'PreviewVersion',
1190             3 => {
1191             Name => 'PreviewAtomType',
1192             Format => 'string[4]',
1193             },
1194             5 => 'PreviewAtomIndex',
1195             );
1196              
1197             # movie atoms
1198             %Image::ExifTool::QuickTime::Movie = (
1199             PROCESS_PROC => \&ProcessMOV,
1200             WRITE_PROC => \&WriteQuickTime,
1201             GROUPS => { 2 => 'Video' },
1202             mvhd => {
1203             Name => 'MovieHeader',
1204             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::MovieHeader' },
1205             },
1206             trak => {
1207             Name => 'Track',
1208             CanCreate => 0, # don't create this atom
1209             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Track' },
1210             },
1211             udta => {
1212             Name => 'UserData',
1213             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::UserData' },
1214             },
1215             meta => { # 'meta' is found here in my EX-F1 MOV sample - PH
1216             Name => 'Meta',
1217             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Meta' },
1218             },
1219             iods => {
1220             Name => 'InitialObjectDescriptor',
1221             Flags => ['Binary','Unknown'],
1222             },
1223             uuid => [
1224             { #11 (MP4 files) (also found in QuickTime::Track)
1225             Name => 'UUID-USMT',
1226             Condition => '$$valPt=~/^USMT!\xd2\x4f\xce\xbb\x88\x69\x5c\xfa\xc9\xc7\x40/',
1227             SubDirectory => {
1228             TagTable => 'Image::ExifTool::QuickTime::UserMedia',
1229             Start => 16,
1230             },
1231             },
1232             { #PH (Canon SX280)
1233             Name => 'UUID-Canon',
1234             Condition => '$$valPt=~/^\x85\xc0\xb6\x87\x82\x0f\x11\xe0\x81\x11\xf4\xce\x46\x2b\x6a\x48/',
1235             SubDirectory => {
1236             TagTable => 'Image::ExifTool::Canon::uuid',
1237             Start => 16,
1238             },
1239             },
1240             {
1241             Name => 'GarminGPS',
1242             Condition => q{
1243             $$valPt=~/^\x9b\x63\x0f\x8d\x63\x74\x40\xec\x82\x04\xbc\x5f\xf5\x09\x17\x28/ and
1244             $$self{OPTIONS}{ExtractEmbedded}
1245             },
1246             SubDirectory => {
1247             TagTable => 'Image::ExifTool::QuickTime::Stream',
1248             ProcessProc => \&ProcessGarminGPS,
1249             },
1250             },
1251             {
1252             Name => 'GarminGPS',
1253             Condition => '$$valPt=~/^\x9b\x63\x0f\x8d\x63\x74\x40\xec\x82\x04\xbc\x5f\xf5\x09\x17\x28/',
1254             Notes => 'Garmin GPS sensor data',
1255             RawConv => q{
1256             $self->Warn('Use the ExtractEmbedded option to decode timed Garmin GPS',3);
1257             return \$val;
1258             },
1259             },
1260             {
1261             Name => 'UUID-Unknown',
1262             %unknownInfo,
1263             },
1264             ],
1265             cmov => {
1266             Name => 'CompressedMovie',
1267             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::CMovie' },
1268             },
1269             htka => { # (written by HTC One M8 in slow-motion 1280x720 video - PH)
1270             Name => 'HTCTrack',
1271             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Track' },
1272             },
1273             'gps ' => { # GPS data written by Novatek cameras (parsed in QuickTimeStream.pl)
1274             Name => 'GPSDataList',
1275             Unknown => 1,
1276             Binary => 1,
1277             },
1278             meco => { #ISO14496-12:2015
1279             Name => 'OtherMeta',
1280             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::OtherMeta' },
1281             },
1282             # prfl - Profile (ref 12)
1283             # clip - clipping --> contains crgn (clip region) (ref 12)
1284             # mvex - movie extends --> contains mehd (movie extends header), trex (track extends) (ref 14)
1285             # ICAT - 4 bytes: "6350" (Nikon CoolPix S6900), "6500" (Panasonic FT7)
1286             # ctab - color table (ref 29)
1287             );
1288              
1289             # (ref CFFMediaFormat-2_1.pdf)
1290             %Image::ExifTool::QuickTime::MovieFragment = (
1291             PROCESS_PROC => \&ProcessMOV,
1292             WRITE_PROC => \&WriteQuickTime,
1293             GROUPS => { 2 => 'Video' },
1294             mfhd => {
1295             Name => 'MovieFragmentHeader',
1296             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::MovieFragHdr' },
1297             },
1298             traf => {
1299             Name => 'TrackFragment',
1300             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::TrackFragment' },
1301             },
1302             meta => { #ISO14496-12:2015
1303             Name => 'Meta',
1304             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Meta' },
1305             },
1306             );
1307              
1308             # (ref CFFMediaFormat-2_1.pdf)
1309             %Image::ExifTool::QuickTime::MovieFragHdr = (
1310             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1311             GROUPS => { 2 => 'Video' },
1312             FORMAT => 'int32u',
1313             1 => 'MovieFragmentSequence',
1314             );
1315              
1316             # (ref CFFMediaFormat-2_1.pdf)
1317             %Image::ExifTool::QuickTime::TrackFragment = (
1318             PROCESS_PROC => \&ProcessMOV,
1319             WRITE_PROC => \&WriteQuickTime,
1320             GROUPS => { 2 => 'Video' },
1321             meta => { #ISO14496-12:2015
1322             Name => 'Meta',
1323             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Meta' },
1324             },
1325             # tfhd - track fragment header
1326             # edts - edits --> contains elst (edit list) (ref PH)
1327             # tfdt - track fragment base media decode time
1328             # trik - trick play box
1329             # trun - track fragment run box
1330             # avcn - AVC NAL unit storage box
1331             # secn - sample encryption box
1332             # saio - sample auxiliary information offsets box
1333             # sbgp - sample to group box
1334             # sgpd - sample group description box
1335             # sdtp - independent and disposable samples (ref 5)
1336             # subs - sub-sample information (ref 5)
1337             );
1338              
1339             # movie header data block
1340             %Image::ExifTool::QuickTime::MovieHeader = (
1341             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1342             WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
1343             CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
1344             GROUPS => { 2 => 'Video' },
1345             FORMAT => 'int32u',
1346             DATAMEMBER => [ 0, 1, 2, 3, 4 ],
1347             0 => {
1348             Name => 'MovieHeaderVersion',
1349             Format => 'int8u',
1350             RawConv => '$$self{MovieHeaderVersion} = $val',
1351             },
1352             1 => {
1353             Name => 'CreateDate',
1354             Groups => { 2 => 'Time' },
1355             %timeInfo,
1356             RawConv => q{
1357             if ($val) {
1358             my $offset = (66 * 365 + 17) * 24 * 3600;
1359             if ($val >= $offset or $$self{OPTIONS}{QuickTimeUTC}) {
1360             $val -= $offset;
1361             } elsif (not $$self{IsWriting}) {
1362             $self->Warn('Patched incorrect time zero for QuickTime date/time tag',1);
1363             }
1364             } else {
1365             undef $val if $$self{OPTIONS}{StrictDate};
1366             }
1367             return $$self{CreateDate} = $val;
1368             },
1369             # this is int64u if MovieHeaderVersion == 1 (ref 13)
1370             Hook => '$$self{MovieHeaderVersion} and $format = "int64u", $varSize += 4',
1371             },
1372             2 => {
1373             Name => 'ModifyDate',
1374             Groups => { 2 => 'Time' },
1375             %timeInfo,
1376             # this is int64u if MovieHeaderVersion == 1 (ref 13)
1377             Hook => '$$self{MovieHeaderVersion} and $format = "int64u", $varSize += 4',
1378             },
1379             3 => {
1380             Name => 'TimeScale',
1381             RawConv => '$$self{TimeScale} = $val',
1382             },
1383             4 => {
1384             Name => 'Duration',
1385             %durationInfo,
1386             # this is int64u if MovieHeaderVersion == 1 (ref 13)
1387             Hook => '$$self{MovieHeaderVersion} and $format = "int64u", $varSize += 4',
1388             # (Note: this Duration seems to be the time of the key frame in
1389             # the NRT Metadata track of iPhone live-photo MOV videos)
1390             },
1391             5 => {
1392             Name => 'PreferredRate',
1393             ValueConv => '$val / 0x10000',
1394             },
1395             6 => {
1396             Name => 'PreferredVolume',
1397             Format => 'int16u',
1398             ValueConv => '$val / 256',
1399             PrintConv => 'sprintf("%.2f%%", $val * 100)',
1400             },
1401             9 => {
1402             Name => 'MatrixStructure',
1403             Format => 'fixed32s[9]',
1404             # (the right column is fixed 2.30 instead of 16.16)
1405             ValueConv => q{
1406             my @a = split ' ',$val;
1407             $_ /= 0x4000 foreach @a[2,5,8];
1408             return "@a";
1409             },
1410             },
1411             18 => { Name => 'PreviewTime', %durationInfo },
1412             19 => { Name => 'PreviewDuration', %durationInfo },
1413             20 => { Name => 'PosterTime', %durationInfo },
1414             21 => { Name => 'SelectionTime', %durationInfo },
1415             22 => { Name => 'SelectionDuration',%durationInfo },
1416             23 => { Name => 'CurrentTime', %durationInfo },
1417             24 => 'NextTrackID',
1418             );
1419              
1420             # track atoms
1421             %Image::ExifTool::QuickTime::Track = (
1422             PROCESS_PROC => \&ProcessMOV,
1423             WRITE_PROC => \&WriteQuickTime,
1424             GROUPS => { 1 => 'Track#', 2 => 'Video' },
1425             tkhd => {
1426             Name => 'TrackHeader',
1427             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::TrackHeader' },
1428             },
1429             udta => {
1430             Name => 'UserData',
1431             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::UserData' },
1432             },
1433             mdia => { #MP4
1434             Name => 'Media',
1435             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Media' },
1436             },
1437             meta => { #PH (MOV)
1438             Name => 'Meta',
1439             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Meta' },
1440             },
1441             tref => {
1442             Name => 'TrackRef',
1443             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::TrackRef' },
1444             },
1445             tapt => {
1446             Name => 'TrackAperture',
1447             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::TrackAperture' },
1448             },
1449             uuid => [
1450             { #11 (MP4 files) (also found in QuickTime::Movie)
1451             Name => 'UUID-USMT',
1452             Condition => '$$valPt=~/^USMT!\xd2\x4f\xce\xbb\x88\x69\x5c\xfa\xc9\xc7\x40/',
1453             SubDirectory => {
1454             TagTable => 'Image::ExifTool::QuickTime::UserMedia',
1455             Start => 16,
1456             },
1457             },
1458             { #https://github.com/google/spatial-media/blob/master/docs/spherical-video-rfc.md
1459             Name => 'SphericalVideoXML',
1460             # (this tag is readable/writable as a block through the Extra SphericalVideoXML tags)
1461             Condition => '$$valPt=~/^\xff\xcc\x82\x63\xf8\x55\x4a\x93\x88\x14\x58\x7a\x02\x52\x1f\xdd/',
1462             WriteGroup => 'GSpherical', # write only GSpherical XMP tags here
1463             MediaType => 'vide', # only write in video tracks
1464             SubDirectory => {
1465             TagTable => 'Image::ExifTool::XMP::Main',
1466             Start => 16,
1467             ProcessProc => 'Image::ExifTool::XMP::ProcessGSpherical',
1468             WriteProc => 'Image::ExifTool::XMP::WriteGSpherical',
1469             },
1470             },
1471             {
1472             Name => 'UUID-Unknown',
1473             %unknownInfo,
1474             },
1475             ],
1476             meco => { #ISO14492-12:2015 pg 83
1477             Name => 'OtherMeta',
1478             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::OtherMeta' },
1479             },
1480             # edts - edits --> contains elst (edit list)
1481             # clip - clipping --> contains crgn (clip region)
1482             # matt - track matt --> contains kmat (compressed matt)
1483             # load - track loading settings
1484             # imap - track input map --> contains ' in' --> contains ' ty', obid
1485             # prfl - Profile (ref 12)
1486             # txas - track exclude from autoselection (ref 29)
1487             );
1488              
1489             # track header data block
1490             %Image::ExifTool::QuickTime::TrackHeader = (
1491             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
1492             WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
1493             CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
1494             GROUPS => { 1 => 'Track#', 2 => 'Video' },
1495             FORMAT => 'int32u',
1496             DATAMEMBER => [ 0, 1, 2, 5, 7 ],
1497             0 => {
1498             Name => 'TrackHeaderVersion',
1499             Format => 'int8u',
1500             Priority => 0,
1501             RawConv => '$$self{TrackHeaderVersion} = $val',
1502             },
1503             1 => {
1504             Name => 'TrackCreateDate',
1505             Priority => 0,
1506             Groups => { 2 => 'Time' },
1507             %timeInfo,
1508             # this is int64u if TrackHeaderVersion == 1 (ref 13)
1509             Hook => '$$self{TrackHeaderVersion} and $format = "int64u", $varSize += 4',
1510             },
1511             2 => {
1512             Name => 'TrackModifyDate',
1513             Priority => 0,
1514             Groups => { 2 => 'Time' },
1515             %timeInfo,
1516             # this is int64u if TrackHeaderVersion == 1 (ref 13)
1517             Hook => '$$self{TrackHeaderVersion} and $format = "int64u", $varSize += 4',
1518             },
1519             3 => {
1520             Name => 'TrackID',
1521             Priority => 0,
1522             },
1523             5 => {
1524             Name => 'TrackDuration',
1525             Priority => 0,
1526             %durationInfo,
1527             # this is int64u if TrackHeaderVersion == 1 (ref 13)
1528             Hook => '$$self{TrackHeaderVersion} and $format = "int64u", $varSize += 4',
1529             },
1530             7 => { # (used only for writing MatrixStructure)
1531             Name => 'ImageSizeLookahead',
1532             Hidden => 1,
1533             Format => 'int32u[14]',
1534             RawConv => '$$self{ImageSizeLookahead} = $val; undef',
1535             },
1536             8 => {
1537             Name => 'TrackLayer',
1538             Format => 'int16u',
1539             Priority => 0,
1540             },
1541             9 => {
1542             Name => 'TrackVolume',
1543             Format => 'int16u',
1544             Priority => 0,
1545             ValueConv => '$val / 256',
1546             PrintConv => 'sprintf("%.2f%%", $val * 100)',
1547             },
1548             10 => {
1549             Name => 'MatrixStructure',
1550             Format => 'fixed32s[9]',
1551             Notes => 'writable for the video track via the Composite Rotation tag',
1552             Writable => 1,
1553             Protected => 1,
1554             Permanent => 1,
1555             # only set rotation if image size is non-zero
1556             RawConvInv => \&GetMatrixStructure,
1557             # (the right column is fixed 2.30 instead of 16.16)
1558             ValueConv => q{
1559             my @a = split ' ',$val;
1560             $_ /= 0x4000 foreach @a[2,5,8];
1561             return "@a";
1562             },
1563             ValueConvInv => q{
1564             my @a = split ' ',$val;
1565             $_ *= 0x4000 foreach @a[2,5,8];
1566             return "@a";
1567             },
1568             },
1569             19 => {
1570             Name => 'ImageWidth',
1571             Priority => 0,
1572             RawConv => \&FixWrongFormat,
1573             },
1574             20 => {
1575             Name => 'ImageHeight',
1576             Priority => 0,
1577             RawConv => \&FixWrongFormat,
1578             },
1579             );
1580              
1581             # user data atoms
1582             %Image::ExifTool::QuickTime::UserData = (
1583             PROCESS_PROC => \&ProcessMOV,
1584             WRITE_PROC => \&WriteQuickTime,
1585             CHECK_PROC => \&CheckQTValue,
1586             GROUPS => { 1 => 'UserData', 2 => 'Video' },
1587             WRITABLE => 1,
1588             PREFERRED => 1, # (preferred over Keys tags when writing)
1589             FORMAT => 'string',
1590             WRITE_GROUP => 'UserData',
1591             LANG_INFO => \&GetLangInfo,
1592             NOTES => q{
1593             Tag ID's beginning with the copyright symbol (hex 0xa9) are multi-language
1594             text. Alternate language tags are accessed by adding a dash followed by a
1595             3-character ISO 639-2 language code to the tag name. ExifTool will extract
1596             any multi-language user data tags found, even if they aren't in this table.
1597             Note when creating new tags,
1598             L tags are
1599             preferred over these, so to create the tag when a same-named ItemList tag
1600             exists, either "UserData" must be specified (eg. C<-UserData:Artist=Monet>
1601             on the command line), or the PREFERRED level must be changed via
1602             L.
1603             },
1604             "\xa9cpy" => { Name => 'Copyright', Groups => { 2 => 'Author' } },
1605             "\xa9day" => {
1606             Name => 'ContentCreateDate',
1607             Groups => { 2 => 'Time' },
1608             %iso8601Date,
1609             },
1610             "\xa9ART" => 'Artist', #PH (iTunes 8.0.2)
1611             "\xa9alb" => 'Album', #PH (iTunes 8.0.2)
1612             "\xa9arg" => 'Arranger', #12
1613             "\xa9ark" => 'ArrangerKeywords', #12
1614             "\xa9cmt" => 'Comment', #PH (iTunes 8.0.2)
1615             "\xa9cok" => 'ComposerKeywords', #12
1616             "\xa9com" => 'Composer', #12
1617             "\xa9dir" => 'Director', #12
1618             "\xa9ed1" => 'Edit1',
1619             "\xa9ed2" => 'Edit2',
1620             "\xa9ed3" => 'Edit3',
1621             "\xa9ed4" => 'Edit4',
1622             "\xa9ed5" => 'Edit5',
1623             "\xa9ed6" => 'Edit6',
1624             "\xa9ed7" => 'Edit7',
1625             "\xa9ed8" => 'Edit8',
1626             "\xa9ed9" => 'Edit9',
1627             "\xa9fmt" => 'Format',
1628             "\xa9gen" => 'Genre', #PH (iTunes 8.0.2)
1629             "\xa9grp" => 'Grouping', #PH (NC)
1630             "\xa9inf" => 'Information',
1631             "\xa9isr" => 'ISRCCode', #12
1632             "\xa9lab" => 'RecordLabelName', #12
1633             "\xa9lal" => 'RecordLabelURL', #12
1634             "\xa9lyr" => 'Lyrics', #PH (NC)
1635             "\xa9mak" => 'Make', #12
1636             "\xa9mal" => 'MakerURL', #12
1637             "\xa9mod" => 'Model', #PH
1638             "\xa9nam" => 'Title', #12
1639             "\xa9pdk" => 'ProducerKeywords', #12
1640             "\xa9phg" => 'RecordingCopyright', #12
1641             "\xa9prd" => 'Producer',
1642             "\xa9prf" => 'Performers',
1643             "\xa9prk" => 'PerformerKeywords', #12
1644             "\xa9prl" => 'PerformerURL',
1645             "\xa9req" => 'Requirements',
1646             "\xa9snk" => 'SubtitleKeywords', #12
1647             "\xa9snm" => 'Subtitle', #12
1648             "\xa9src" => 'SourceCredits', #12
1649             "\xa9swf" => 'SongWriter', #12
1650             "\xa9swk" => 'SongWriterKeywords', #12
1651             "\xa9swr" => 'SoftwareVersion', #12
1652             "\xa9too" => 'Encoder', #PH (NC)
1653             "\xa9trk" => 'Track', #PH (NC)
1654             "\xa9wrt" => { Name => 'Composer', Avoid => 1 }, # ("\xa9com" is preferred in UserData)
1655             "\xa9xyz" => { #PH (iPhone 3GS)
1656             Name => 'GPSCoordinates',
1657             Groups => { 2 => 'Location' },
1658             ValueConv => \&ConvertISO6709,
1659             ValueConvInv => \&ConvInvISO6709,
1660             PrintConv => \&PrintGPSCoordinates,
1661             PrintConvInv => \&PrintInvGPSCoordinates,
1662             },
1663             name => 'Name',
1664             WLOC => {
1665             Name => 'WindowLocation',
1666             Format => 'int16u',
1667             },
1668             LOOP => {
1669             Name => 'LoopStyle',
1670             Format => 'int32u',
1671             PrintConv => {
1672             1 => 'Normal',
1673             2 => 'Palindromic',
1674             },
1675             },
1676             SelO => {
1677             Name => 'PlaySelection',
1678             Format => 'int8u',
1679             },
1680             AllF => {
1681             Name => 'PlayAllFrames',
1682             Format => 'int8u',
1683             },
1684             meta => {
1685             Name => 'Meta',
1686             SubDirectory => {
1687             TagTable => 'Image::ExifTool::QuickTime::Meta',
1688             Start => 4, # must skip 4-byte version number header
1689             },
1690             },
1691             tnam => { #29 (NC)
1692             Name => 'TrackName',
1693             IText => 4,
1694             },
1695             'ptv '=> {
1696             Name => 'PrintToVideo',
1697             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Video' },
1698             },
1699             hnti => {
1700             Name => 'HintInfo',
1701             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::HintInfo' },
1702             },
1703             hinf => {
1704             Name => 'HintTrackInfo',
1705             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::HintTrackInfo' },
1706             },
1707             hinv => 'HintVersion', #PH (guess)
1708             XMP_ => { #PH (Adobe CS3 Bridge)
1709             Name => 'XMP',
1710             WriteGroup => 'XMP', # (write main tags here)
1711             # *** this is where ExifTool writes XMP in MOV videos (as per XMP spec) ***
1712             SubDirectory => { TagTable => 'Image::ExifTool::XMP::Main' },
1713             },
1714             # the following are 3gp tags, references:
1715             # http://atomicparsley.sourceforge.net
1716             # http://www.3gpp.org/ftp/tsg_sa/WG4_CODEC/TSGS4_25/Docs/
1717             # (note that all %langText3gp tags are Avoid => 1)
1718             cprt => { Name => 'Copyright', %langText3gp, Groups => { 2 => 'Author' } },
1719             auth => { Name => 'Author', %langText3gp, Groups => { 2 => 'Author' } },
1720             titl => { Name => 'Title', %langText3gp },
1721             dscp => { Name => 'Description',%langText3gp },
1722             perf => { Name => 'Performer', %langText3gp },
1723             gnre => { Name => 'Genre', %langText3gp },
1724             albm => { Name => 'Album', %langText3gp },
1725             coll => { Name => 'CollectionName', %langText3gp }, #17
1726             rtng => {
1727             Name => 'Rating',
1728             Writable => 'undef',
1729             Avoid => 1,
1730             # (4-byte flags, 4-char entity, 4-char criteria, 2-byte lang, string)
1731             IText => 14, # (14 bytes before string)
1732             Notes => 'string in the form "Entity=XXXX Criteria=XXXX XXXXX", used in 3gp videos',
1733             ValueConv => '$val=~s/^(.{4})(.{4})/Entity=$1 Criteria=$2 /i; $val',
1734             ValueConvInv => '$val=~s/Entity=(.{4}) Criteria=(.{4}) ?/$1$2/i; $val',
1735             },
1736             clsf => {
1737             Name => 'Classification',
1738             Writable => 'undef',
1739             Avoid => 1,
1740             # (4-byte flags, 4-char entity, 2-byte index, 2-byte lang, string)
1741             IText => 12,
1742             Notes => 'string in the form "Entity=XXXX Index=### XXXXX", used in 3gp videos',
1743             ValueConv => '$val=~s/^(.{4})(.{2})/"Entity=$1 Index=".unpack("n",$2)." "/ie; $val',
1744             ValueConvInv => '$val=~s/Entity=(.{4}) Index=(\d+) ?/$1.pack("n",$2)/ie; $val',
1745             },
1746             kywd => {
1747             Name => 'Keywords',
1748             # (4 byte flags, 2-byte lang, 1-byte count, count x pascal strings, ref 17)
1749             # (but I have also seen a simple string written by iPhone, so don't make writable yet)
1750             Notes => "not writable because Apple doesn't follow the 3gp specification",
1751             RawConv => q{
1752             my $sep = $self->Options('ListSep');
1753             return join($sep, split /\0+/, $val) unless $val =~ /^\0/; # (iPhone)
1754             return '' unless length $val >= 7;
1755             my $lang = Image::ExifTool::QuickTime::UnpackLang(Get16u(\$val, 4));
1756             $lang = $lang ? "($lang) " : '';
1757             my $num = Get8u(\$val, 6);
1758             my ($i, @vals);
1759             my $pos = 7;
1760             for ($i=0; $i<$num; ++$i) {
1761             last if $pos >= length $val;
1762             my $len = Get8u(\$val, $pos++);
1763             last if $pos + $len > length $val;
1764             my $v = substr($val, $pos, $len);
1765             $v = $self->Decode($v, 'UCS2') if $v =~ /^\xfe\xff/;
1766             push @vals, $v;
1767             $pos += $len;
1768             }
1769             return $lang . join($sep, @vals);
1770             },
1771             },
1772             loci => {
1773             Name => 'LocationInformation',
1774             Groups => { 2 => 'Location' },
1775             Writable => 'undef',
1776             IText => 6,
1777             Avoid => 1,
1778             NoDecode => 1, # (we'll decode the data ourself)
1779             Notes => q{
1780             string in the form "XXXXX Role=XXX Lat=XXX Lon=XXX Alt=XXX Body=XXX
1781             Notes=XXX", used in 3gp videos
1782             },
1783             # (4-byte flags, 2-byte lang, location string, 1-byte role, 4-byte fixed longitude,
1784             # 4-byte fixed latitude, 4-byte fixed altitude, body string, notes string)
1785             RawConv => q{
1786             my $str;
1787             if ($val =~ /^\xfe\xff/) {
1788             $val =~ s/^(\xfe\xff(.{2})*?)\0\0//s or return '';
1789             $str = $self->Decode($1, 'UCS2');
1790             } else {
1791             $val =~ s/^(.*?)\0//s or return '';
1792             $str = $self->Decode($1, 'UTF8');
1793             }
1794             $str = '(none)' unless length $str;
1795             return '' if length $val < 13;
1796             my $role = Get8u(\$val, 0);
1797             my $lon = GetFixed32s(\$val, 1);
1798             my $lat = GetFixed32s(\$val, 5);
1799             my $alt = GetFixed32s(\$val, 9);
1800             my $roleStr = {0=>'shooting',1=>'real',2=>'fictional',3=>'reserved'}->{$role};
1801             $str .= ' Role=' . ($roleStr || "unknown($role)");
1802             $str .= sprintf(' Lat=%.5f Lon=%.5f Alt=%.2f', $lat, $lon, $alt);
1803             $val = substr($val, 13);
1804             if ($val =~ s/^(\xfe\xff(.{2})*?)\0\0//s) {
1805             $str .= ' Body=' . $self->Decode($1, 'UCS2');
1806             } elsif ($val =~ s/^(.*?)\0//s) {
1807             $str .= ' Body=' . $self->Decode($1, 'UTF8');
1808             }
1809             if ($val =~ s/^(\xfe\xff(.{2})*?)\0\0//s) {
1810             $str .= ' Notes=' . $self->Decode($1, 'UCS2');
1811             } elsif ($val =~ s/^(.*?)\0//s) {
1812             $str .= ' Notes=' . $self->Decode($1, 'UTF8');
1813             }
1814             return $str;
1815             },
1816             RawConvInv => q{
1817             my ($role, $lat, $lon, $alt, $body, $note);
1818             $lat = $1 if $val =~ s/ Lat=([-+]?[.\d]+)//i;
1819             $lon = $1 if $val =~ s/ Lon=([-+]?[.\d]+)//i;
1820             $alt = $1 if $val =~ s/ Alt=([-+]?[.\d]+)//i;
1821             $note = $val =~ s/ Notes=(.*)//i ? $1 : '';
1822             $body = $val =~ s/ Body=(.*)//i ? $1 : '';
1823             $role = $val =~ s/ Role=(.*)//i ? $1 : '';
1824             $val = '' if $val eq '(none)';
1825             $role = {shooting=>0,real=>1,fictional=>2}->{lc $role} || 0;
1826             return $self->Encode($val, 'UTF8') . "\0" . Set8u($role) .
1827             SetFixed32s(defined $lon ? $lon : 999) .
1828             SetFixed32s(defined $lat ? $lat : 999) .
1829             SetFixed32s(defined $alt ? $alt : 0) .
1830             $self->Encode($body) . "\0" .
1831             $self->Encode($note) . "\0";
1832             },
1833             },
1834             yrrc => {
1835             Name => 'Year',
1836             Writable => 'undef',
1837             Groups => { 2 => 'Time' },
1838             Avoid => 1,
1839             Notes => 'used in 3gp videos',
1840             ValueConv => 'length($val) >= 6 ? unpack("x4n",$val) : ""',
1841             ValueConvInv => 'pack("Nn",0,$val)',
1842             },
1843             urat => { #17
1844             Name => 'UserRating',
1845             Writable => 'undef',
1846             Notes => 'used in 3gp videos',
1847             Avoid => 1,
1848             ValueConv => q{
1849             return '' unless length $val >= 8;
1850             unpack('x7C', $val);
1851             },
1852             ValueConvInv => 'pack("N2",0,$val)',
1853             },
1854             # tsel - TrackSelection (ref 17)
1855             # Apple tags (ref 16[dead] -- see ref 25 instead)
1856             angl => { Name => 'CameraAngle', Format => 'string' }, # (NC)
1857             clfn => { Name => 'ClipFileName', Format => 'string' }, # (NC)
1858             clid => { Name => 'ClipID', Format => 'string' }, # (NC)
1859             cmid => { Name => 'CameraID', Format => 'string' }, # (NC)
1860             cmnm => { # (NC)
1861             Name => 'Model',
1862             Description => 'Camera Model Name',
1863             Avoid => 1,
1864             Format => 'string', # (necessary to remove the trailing NULL)
1865             },
1866             date => {
1867             Name => 'DateTimeOriginal',
1868             Description => 'Date/Time Original',
1869             Groups => { 2 => 'Time' },
1870             Notes => q{
1871             Apple Photos has been reported to show a crazy date/time for some MP4 files
1872             containing this tag, but perhaps only if it is missing a time zone
1873             }, #forum10690/11125
1874             %iso8601Date,
1875             },
1876             manu => { # (SX280)
1877             Name => 'Make',
1878             Avoid => 1,
1879             # (with Canon there are 6 unknown bytes before the model: "\0\0\0\0\x15\xc7")
1880             RawConv => '$val=~s/^\0{4}..//s; $val=~s/\0.*//; $val',
1881             },
1882             modl => { # (Samsung GT-S8530, Canon SX280)
1883             Name => 'Model',
1884             Description => 'Camera Model Name',
1885             Avoid => 1,
1886             # (with Canon there are 6 unknown bytes before the model: "\0\0\0\0\x15\xc7")
1887             RawConv => '$val=~s/^\0{4}..//s; $val=~s/\0.*//; $val',
1888             },
1889             reel => { Name => 'ReelName', Format => 'string' }, # (NC)
1890             scen => { Name => 'Scene', Format => 'string' }, # (NC)
1891             shot => { Name => 'ShotName', Format => 'string' }, # (NC)
1892             slno => { Name => 'SerialNumber', Format => 'string' }, # (NC)
1893             apmd => { Name => 'ApertureMode', Format => 'undef' }, #20
1894             kgtt => { #http://lists.ffmpeg.org/pipermail/ffmpeg-devel-irc/2012-June/000707.html
1895             # 'TrackType' will expand to 'Track#Type' when found inside a track
1896             Name => 'TrackType',
1897             # set flag to process this as international text
1898             # even though the tag ID doesn't start with 0xa9
1899             IText => 4, # IText with 4-byte header
1900             },
1901             chpl => { # (Nero chapter list)
1902             Name => 'ChapterList',
1903             ValueConv => \&ConvertChapterList,
1904             PrintConv => \&PrintChapter,
1905             },
1906             # ndrm - 7 bytes (0 0 0 1 0 0 0) Nero Digital Rights Management? (PH)
1907             # other non-Apple tags (ref 16)
1908             # hpix - HipixRichPicture (ref 16, HIPIX)
1909             # strk - sub-track information (ref 16, ISO)
1910             #
1911             # Manufacturer-specific metadata
1912             #
1913             TAGS => [ #PH
1914             # these tags were initially discovered in a Pentax movie,
1915             # but similar information is found in videos from other manufacturers
1916             {
1917             Name => 'FujiFilmTags',
1918             Condition => '$$valPt =~ /^FUJIFILM DIGITAL CAMERA\0/',
1919             SubDirectory => {
1920             TagTable => 'Image::ExifTool::FujiFilm::MOV',
1921             ByteOrder => 'LittleEndian',
1922             },
1923             },
1924             {
1925             Name => 'KodakTags',
1926             Condition => '$$valPt =~ /^EASTMAN KODAK COMPANY/',
1927             SubDirectory => {
1928             TagTable => 'Image::ExifTool::Kodak::MOV',
1929             ByteOrder => 'LittleEndian',
1930             },
1931             },
1932             {
1933             Name => 'KonicaMinoltaTags',
1934             Condition => '$$valPt =~ /^KONICA MINOLTA DIGITAL CAMERA/',
1935             SubDirectory => {
1936             TagTable => 'Image::ExifTool::Minolta::MOV1',
1937             ByteOrder => 'LittleEndian',
1938             },
1939             },
1940             {
1941             Name => 'MinoltaTags',
1942             Condition => '$$valPt =~ /^MINOLTA DIGITAL CAMERA/',
1943             SubDirectory => {
1944             TagTable => 'Image::ExifTool::Minolta::MOV2',
1945             ByteOrder => 'LittleEndian',
1946             },
1947             },
1948             {
1949             Name => 'NikonTags',
1950             Condition => '$$valPt =~ /^NIKON DIGITAL CAMERA\0/',
1951             SubDirectory => {
1952             TagTable => 'Image::ExifTool::Nikon::MOV',
1953             ByteOrder => 'LittleEndian',
1954             },
1955             },
1956             {
1957             Name => 'OlympusTags1',
1958             Condition => '$$valPt =~ /^OLYMPUS DIGITAL CAMERA\0.{9}\x01\0/s',
1959             SubDirectory => {
1960             TagTable => 'Image::ExifTool::Olympus::MOV1',
1961             ByteOrder => 'LittleEndian',
1962             },
1963             },
1964             {
1965             Name => 'OlympusTags2',
1966             Condition => '$$valPt =~ /^OLYMPUS DIGITAL CAMERA(?!\0.{21}\x0a\0{3})/s',
1967             SubDirectory => {
1968             TagTable => 'Image::ExifTool::Olympus::MOV2',
1969             ByteOrder => 'LittleEndian',
1970             },
1971             },
1972             {
1973             Name => 'OlympusTags3',
1974             Condition => '$$valPt =~ /^OLYMPUS DIGITAL CAMERA\0/',
1975             SubDirectory => {
1976             TagTable => 'Image::ExifTool::Olympus::MP4',
1977             ByteOrder => 'LittleEndian',
1978             },
1979             },
1980             {
1981             Name => 'OlympusTags4',
1982             Condition => '$$valPt =~ /^.{16}OLYM\0/s',
1983             SubDirectory => {
1984             TagTable => 'Image::ExifTool::Olympus::MOV3',
1985             Start => 12,
1986             },
1987             },
1988             {
1989             Name => 'PentaxTags',
1990             Condition => '$$valPt =~ /^PENTAX DIGITAL CAMERA\0/',
1991             SubDirectory => {
1992             TagTable => 'Image::ExifTool::Pentax::MOV',
1993             ByteOrder => 'LittleEndian',
1994             },
1995             },
1996             {
1997             Name => 'SamsungTags',
1998             Condition => '$$valPt =~ /^SAMSUNG DIGITAL CAMERA\0/',
1999             SubDirectory => {
2000             TagTable => 'Image::ExifTool::Samsung::MP4',
2001             ByteOrder => 'LittleEndian',
2002             },
2003             },
2004             {
2005             Name => 'SanyoMOV',
2006             Condition => q{
2007             $$valPt =~ /^SANYO DIGITAL CAMERA\0/ and
2008             $$self{FileType} eq "MOV"
2009             },
2010             SubDirectory => {
2011             TagTable => 'Image::ExifTool::Sanyo::MOV',
2012             ByteOrder => 'LittleEndian',
2013             },
2014             },
2015             {
2016             Name => 'SanyoMP4',
2017             Condition => q{
2018             $$valPt =~ /^SANYO DIGITAL CAMERA\0/ and
2019             $$self{FileType} eq "MP4"
2020             },
2021             SubDirectory => {
2022             TagTable => 'Image::ExifTool::Sanyo::MP4',
2023             ByteOrder => 'LittleEndian',
2024             },
2025             },
2026             {
2027             Name => 'UnknownTags',
2028             Unknown => 1,
2029             Binary => 1
2030             },
2031             ],
2032             # ---- Canon ----
2033             CNCV => { Name => 'CompressorVersion', Format => 'string' }, #PH (5D Mark II)
2034             CNMN => {
2035             Name => 'Model', #PH (EOS 550D)
2036             Description => 'Camera Model Name',
2037             Avoid => 1,
2038             Format => 'string', # (necessary to remove the trailing NULL)
2039             },
2040             CNFV => { Name => 'FirmwareVersion', Format => 'string' }, #PH (EOS 550D)
2041             CNTH => { #PH (PowerShot S95)
2042             Name => 'CanonCNTH',
2043             SubDirectory => { TagTable => 'Image::ExifTool::Canon::CNTH' },
2044             },
2045             CNOP => { #PH (7DmkII)
2046             Name => 'CanonCNOP',
2047             SubDirectory => { TagTable => 'Image::ExifTool::Canon::CNOP' },
2048             },
2049             # CNDB - 2112 bytes (550D)
2050             # CNDM - 4 bytes - 0xff,0xd8,0xff,0xd9 (S95)
2051             # CNDG - 10232 bytes, mostly zeros (N100)
2052             # ---- Casio ----
2053             QVMI => { #PH
2054             Name => 'CasioQVMI',
2055             # Casio stores standard EXIF-format information in MOV videos (eg. EX-S880)
2056             SubDirectory => {
2057             TagTable => 'Image::ExifTool::Exif::Main',
2058             ProcessProc => \&Image::ExifTool::Exif::ProcessExif, # (because ProcessMOV is default)
2059             DirName => 'IFD0',
2060             Multi => 0, # (no NextIFD pointer)
2061             Start => 10,
2062             ByteOrder => 'BigEndian',
2063             },
2064             },
2065             # ---- FujiFilm ----
2066             FFMV => { #PH (FinePix HS20EXR)
2067             Name => 'FujiFilmFFMV',
2068             SubDirectory => { TagTable => 'Image::ExifTool::FujiFilm::FFMV' },
2069             },
2070             MVTG => { #PH (FinePix HS20EXR)
2071             Name => 'FujiFilmMVTG',
2072             SubDirectory => {
2073             TagTable => 'Image::ExifTool::Exif::Main',
2074             ProcessProc => \&Image::ExifTool::Exif::ProcessExif, # (because ProcessMOV is default)
2075             DirName => 'IFD0',
2076             Start => 16,
2077             Base => '$start',
2078             ByteOrder => 'LittleEndian',
2079             },
2080             },
2081             # ---- Garmin ---- (ref PH)
2082             uuid => [{
2083             Name => 'GarminSoftware', # (NC)
2084             Condition => '$$valPt =~ /^VIRBactioncamera/',
2085             RawConv => 'substr($val, 16)',
2086             RawConvInv => '"VIRBactioncamera$val"',
2087             },{
2088             Name => 'GarminModel', # (NC)
2089             Condition => '$$valPt =~ /^\xf7\x6c\xd7\x6a\x07\x5b\x4a\x1e\xb3\x1c\x0e\x7f\xab\x7e\x09\xd4/',
2090             Writable => 0,
2091             RawConv => q{
2092             return undef unless length($val) > 25;
2093             my $len = unpack('x24C', $val);
2094             return undef unless length($val) >= 25 + $len;
2095             return substr($val, 25, $len);
2096             },
2097             },{
2098             # have seen "28 f3 11 e2 b7 91 4f 6f 94 e2 4f 5d ea cb 3c 01" for RicohThetaZ1 accelerometer RADT data (not yet decoded)
2099             # also seen in Garmin MP4:
2100             # 51 0b 63 46 6c fd 4a 17 87 42 ea c9 ea ae b3 bd - seems to contain a duplicate of the trak atom
2101             # b3 e8 21 f4 fe 33 4e 10 8f 92 f5 e1 d4 36 c9 8a - 8 bytes of zeros
2102             Name => 'UUID-Unknown',
2103             Writable => 0,
2104             %unknownInfo,
2105             }],
2106             pmcc => {
2107             Name => 'GarminSettings',
2108             ValueConv => 'substr($val, 4)',
2109             ValueConvInv => '"\0\0\0\x01$val"',
2110             },
2111             # hmtp - 412 bytes: "\0\0\0\x01" then maybe "\0\0\0\x64" and the rest zeros
2112             # vrin - 12 bytes: "\0\0\0\x01" followed by 8 bytes of zero
2113             # ---- GoPro ---- (ref PH)
2114             GoPr => 'GoProType', # (Hero3+)
2115             FIRM => { Name => 'FirmwareVersion', Avoid => 1 }, # (Hero4)
2116             LENS => 'LensSerialNumber', # (Hero4)
2117             CAME => { # (Hero4)
2118             Name => 'SerialNumberHash',
2119             Description => 'Camera Serial Number Hash',
2120             ValueConv => 'unpack("H*",$val)',
2121             ValueConvInv => 'pack("H*",$val)',
2122             },
2123             # SETT? 12 bytes (Hero4)
2124             MUID => { Name => 'MediaUID', ValueConv => 'unpack("H*", $val)' },
2125             # HMMT? 404 bytes (Hero4, all zero)
2126             # BCID? 26 bytes (Hero5, all zero), 36 bytes GoPro Max
2127             # GUMI? 16 bytes (Hero5)
2128             "FOV\0" => 'FieldOfView', #forum8938 (Hero2) seen: "Wide"
2129             GPMF => {
2130             Name => 'GoProGPMF',
2131             SubDirectory => { TagTable => 'Image::ExifTool::GoPro::GPMF' },
2132             },
2133             # free (all zero)
2134             "\xa9TSC" => 'StartTimeScale', # (Hero6)
2135             "\xa9TSZ" => 'StartTimeSampleSize', # (Hero6)
2136             "\xa9TIM" => 'StartTimecode', #PH (NC)
2137             # ---- DJI ----
2138             # \xa9 tags written by DJI Phantom 3: (ref PH)
2139             "\xa9xsp" => 'SpeedX', #PH (guess)
2140             "\xa9ysp" => 'SpeedY', #PH (guess)
2141             "\xa9zsp" => 'SpeedZ', #PH (guess)
2142             "\xa9fpt" => 'Pitch', #PH
2143             "\xa9fyw" => 'Yaw', #PH
2144             "\xa9frl" => 'Roll', #PH
2145             "\xa9gpt" => 'CameraPitch', #PH
2146             "\xa9gyw" => 'CameraYaw', #PH
2147             "\xa9grl" => 'CameraRoll', #PH
2148             "\xa9enc" => 'EncoderID', #PH (forum9271)
2149             # and the following entries don't have the proper 4-byte header for \xa9 tags:
2150             "\xa9dji" => { Name => 'UserData_dji', Format => 'undef', Binary => 1, Unknown => 1, Hidden => 1 },
2151             "\xa9res" => { Name => 'UserData_res', Format => 'undef', Binary => 1, Unknown => 1, Hidden => 1 },
2152             "\xa9uid" => { Name => 'UserData_uid', Format => 'undef', Binary => 1, Unknown => 1, Hidden => 1 },
2153             "\xa9mdl" => {
2154             Name => 'Model',
2155             Notes => 'non-standard-format DJI tag',
2156             Format => 'string',
2157             Avoid => 1,
2158             },
2159             btec => {
2160             Name => 'GlamourSettings',
2161             SubDirectory => { TagTable => 'Image::ExifTool::DJI::Glamour' },
2162             },
2163             fsid => 'OriginalFilePath',
2164             # dbcm - seen "\0\0\0\x04"
2165             # ---- HTC ----
2166             htcb => {
2167             Name => 'HTCBinary',
2168             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::HTCBinary' },
2169             },
2170             # ---- Kodak ----
2171             DcMD => {
2172             Name => 'KodakDcMD',
2173             SubDirectory => { TagTable => 'Image::ExifTool::Kodak::DcMD' },
2174             },
2175             SNum => { Name => 'SerialNumber', Avoid => 1, Groups => { 2 => 'Camera' } },
2176             ptch => { Name => 'Pitch', Format => 'rational64s', Avoid => 1 }, # Units??
2177             _yaw => { Name => 'Yaw', Format => 'rational64s', Avoid => 1 }, # Units??
2178             roll => { Name => 'Roll', Format => 'rational64s', Avoid => 1 }, # Units??
2179             _cx_ => { Name => 'CX', Format => 'rational64s', Unknown => 1 },
2180             _cy_ => { Name => 'CY', Format => 'rational64s', Unknown => 1 },
2181             rads => { Name => 'Rads', Format => 'rational64s', Unknown => 1 },
2182             lvlm => { Name => 'LevelMeter', Format => 'rational64s', Unknown => 1 }, # (guess, Kodak proprietary)
2183             Lvlm => { Name => 'LevelMeter', Format => 'rational64s', Unknown => 1 }, # (guess, Kodak proprietary)
2184             pose => { Name => 'pose', SubDirectory => { TagTable => 'Image::ExifTool::Kodak::pose' } },
2185             # AMBA => Ambarella AVC atom (unknown data written by Kodak Playsport video cam)
2186             # tmlp - 1 byte: 0 (PixPro SP360/4KVR360)
2187             # pivi - 72 bytes (PixPro SP360)
2188             # pive - 12 bytes (PixPro SP360)
2189             # loop - 4 bytes: 0 0 0 0 (PixPro 4KVR360)
2190             # m cm - 2 bytes: 0 0 (PixPro 4KVR360)
2191             # m ev - 2 bytes: 0 0 (PixPro SP360/4KVR360) (exposure comp?)
2192             # m vr - 2 bytes: 0 1 (PixPro 4KVR360) (virtual reality?)
2193             # m wb - 4 bytes: 0 0 0 0 (PixPro SP360/4KVR360) (white balance?)
2194             # mclr - 4 bytes: 0 0 0 0 (PixPro SP360/4KVR360)
2195             # mmtr - 4 bytes: 0,6 0 0 0 (PixPro SP360/4KVR360)
2196             # mflr - 4 bytes: 0 0 0 0 (PixPro SP360)
2197             # lvlm - 24 bytes (PixPro SP360)
2198             # Lvlm - 24 bytes (PixPro 4KVR360)
2199             # ufdm - 4 bytes: 0 0 0 1 (PixPro SP360)
2200             # mtdt - 1 byte: 0 (PixPro SP360/4KVR360)
2201             # gdta - 75240 bytes (PixPro SP360)
2202             # EIS1 - 4 bytes: 03 07 00 00 (PixPro 4KVR360)
2203             # EIS2 - 4 bytes: 04 97 00 00 (PixPro 4KVR360)
2204             # ---- LG ----
2205             adzc => { Name => 'Unknown_adzc', Unknown => 1, Hidden => 1, %langText }, # "false\0/","true\0/"
2206             adze => { Name => 'Unknown_adze', Unknown => 1, Hidden => 1, %langText }, # "false\0/"
2207             adzm => { Name => 'Unknown_adzm', Unknown => 1, Hidden => 1, %langText }, # "\x0e\x04/","\x10\x06"
2208             # ---- Microsoft ----
2209             Xtra => { #PH (microsoft)
2210             Name => 'MicrosoftXtra',
2211             WriteGroup => 'Microsoft',
2212             SubDirectory => {
2213             DirName => 'Microsoft',
2214             TagTable => 'Image::ExifTool::Microsoft::Xtra',
2215             },
2216             },
2217             # ---- Minolta ----
2218             MMA0 => { #PH (DiMage 7Hi)
2219             Name => 'MinoltaMMA0',
2220             SubDirectory => { TagTable => 'Image::ExifTool::Minolta::MMA' },
2221             },
2222             MMA1 => { #PH (Dimage A2)
2223             Name => 'MinoltaMMA1',
2224             SubDirectory => { TagTable => 'Image::ExifTool::Minolta::MMA' },
2225             },
2226             # ---- Nikon ----
2227             NCDT => { #PH
2228             Name => 'NikonNCDT',
2229             SubDirectory => { TagTable => 'Image::ExifTool::Nikon::NCDT' },
2230             },
2231             # ---- Olympus ----
2232             scrn => { #PH (TG-810)
2233             Name => 'OlympusPreview',
2234             Condition => '$$valPt =~ /^.{4}\xff\xd8\xff\xdb/s',
2235             SubDirectory => { TagTable => 'Image::ExifTool::Olympus::scrn' },
2236             },
2237             # ---- Panasonic/Leica ----
2238             PANA => { #PH
2239             Name => 'PanasonicPANA',
2240             SubDirectory => { TagTable => 'Image::ExifTool::Panasonic::PANA' },
2241             },
2242             LEIC => { #PH
2243             Name => 'LeicaLEIC',
2244             SubDirectory => { TagTable => 'Image::ExifTool::Panasonic::PANA' },
2245             },
2246             # ---- Pentax ----
2247             thmb => [ # (apparently defined by 3gpp, ref 16)
2248             { #PH (Pentax Q)
2249             Name => 'MakerNotePentax5a',
2250             Condition => '$$valPt =~ /^PENTAX \0II/',
2251             SubDirectory => {
2252             TagTable => 'Image::ExifTool::Pentax::Main',
2253             ProcessProc => \&Image::ExifTool::Exif::ProcessExif, # (because ProcessMOV is default)
2254             Start => 10,
2255             Base => '$start - 10',
2256             ByteOrder => 'LittleEndian',
2257             },
2258             },{ #PH (TG-810)
2259             Name => 'OlympusThumbnail',
2260             Condition => '$$valPt =~ /^.{4}\xff\xd8\xff\xdb/s',
2261             SubDirectory => { TagTable => 'Image::ExifTool::Olympus::thmb' },
2262             },{ #17 (format is in bytes 3-7)
2263             Name => 'ThumbnailImage',
2264             Condition => '$$valPt =~ /^.{8}\xff\xd8\xff[\xdb\xe0]/s',
2265             Groups => { 2 => 'Preview' },
2266             RawConv => 'substr($val, 8)',
2267             Binary => 1,
2268             },{ #17 (format is in bytes 3-7)
2269             Name => 'ThumbnailPNG',
2270             Condition => '$$valPt =~ /^.{8}\x89PNG\r\n\x1a\n/s',
2271             Groups => { 2 => 'Preview' },
2272             RawConv => 'substr($val, 8)',
2273             Binary => 1,
2274             },{
2275             Name => 'UnknownThumbnail',
2276             Groups => { 2 => 'Preview' },
2277             Binary => 1,
2278             },
2279             ],
2280             PENT => { #PH
2281             Name => 'PentaxPENT',
2282             SubDirectory => {
2283             TagTable => 'Image::ExifTool::Pentax::PENT',
2284             ByteOrder => 'LittleEndian',
2285             },
2286             },
2287             PXTH => { #PH (Pentax K-01)
2288             Name => 'PentaxPreview',
2289             SubDirectory => { TagTable => 'Image::ExifTool::Pentax::PXTH' },
2290             },
2291             PXMN => [{ #PH (Pentax K-01)
2292             Name => 'MakerNotePentax5b',
2293             Condition => '$$valPt =~ /^PENTAX \0MM/',
2294             SubDirectory => {
2295             TagTable => 'Image::ExifTool::Pentax::Main',
2296             ProcessProc => \&Image::ExifTool::Exif::ProcessExif, # (because ProcessMOV is default)
2297             Start => 10,
2298             Base => '$start - 10',
2299             ByteOrder => 'BigEndian',
2300             },
2301             },{ #PH (Pentax 645Z)
2302             Name => 'MakerNotePentax5c',
2303             Condition => '$$valPt =~ /^PENTAX \0II/',
2304             SubDirectory => {
2305             TagTable => 'Image::ExifTool::Pentax::Main',
2306             ProcessProc => \&Image::ExifTool::Exif::ProcessExif, # (because ProcessMOV is default)
2307             Start => 10,
2308             Base => '$start - 10',
2309             ByteOrder => 'LittleEndian',
2310             },
2311             },{
2312             Name => 'MakerNoteRicohPentax2',
2313             # used by cameras such as the Ricoh GR III
2314             Condition => '$$valPt=~/^RICOH\0II/',
2315             SubDirectory => {
2316             TagTable => 'Image::ExifTool::Pentax::Main',
2317             ProcessProc => \&Image::ExifTool::Exif::ProcessExif, # (because ProcessMOV is default)
2318             Start => 8,
2319             Base => '$start - 8',
2320             ByteOrder => 'LittleEndian',
2321             },
2322             },{
2323             Name => 'MakerNoteRicohPentax3',
2324             Condition => '$$valPt=~/^RICOH\0MM/', # (just in case)
2325             SubDirectory => {
2326             TagTable => 'Image::ExifTool::Pentax::Main',
2327             ProcessProc => \&Image::ExifTool::Exif::ProcessExif, # (because ProcessMOV is default)
2328             Start => 8,
2329             Base => '$start - 8',
2330             ByteOrder => 'BigEndian',
2331             },
2332             },{
2333             Name => 'MakerNotePentaxUnknown',
2334             Binary => 1,
2335             }],
2336             # ---- Ricoh ----
2337             RICO => { #PH (G900SE)
2338             Name => 'RicohInfo',
2339             Condition => '$$valPt =~ /^\xff\xe1..Exif\0\0/s',
2340             SubDirectory => {
2341             TagTable => 'Image::ExifTool::JPEG::Main',
2342             ProcessProc => \&Image::ExifTool::ProcessJPEG,
2343             }
2344             },
2345             RTHU => { #PH (GR)
2346             Name => 'PreviewImage',
2347             Groups => { 2 => 'Preview' },
2348             RawConv => '$self->ValidateImage(\$val, $tag)',
2349             },
2350             RMKN => { #PH (GR)
2351             Name => 'RicohRMKN',
2352             SubDirectory => {
2353             TagTable => 'Image::ExifTool::Exif::Main',
2354             ProcessProc => \&Image::ExifTool::ProcessTIFF, # (because ProcessMOV is default)
2355             },
2356             },
2357             '@mak' => { Name => 'Make', Avoid => 1 },
2358             '@mod' => { Name => 'Model', Avoid => 1 },
2359             '@swr' => { Name => 'SoftwareVersion', Avoid => 1 },
2360             '@day' => {
2361             Name => 'ContentCreateDate',
2362             Notes => q{
2363             some stupid Ricoh programmer used the '@' symbol instead of the copyright
2364             symbol in these tag ID's for the Ricoh Theta Z1 and maybe other models
2365             },
2366             Groups => { 2 => 'Time' },
2367             Avoid => 1,
2368             # handle values in the form "2010-02-12T13:27:14-0800"
2369             %iso8601Date,
2370             },
2371             '@xyz' => { #PH (iPhone 3GS)
2372             Name => 'GPSCoordinates',
2373             Groups => { 2 => 'Location' },
2374             Avoid => 1,
2375             ValueConv => \&ConvertISO6709,
2376             ValueConvInv => \&ConvInvISO6709,
2377             PrintConv => \&PrintGPSCoordinates,
2378             PrintConvInv => \&PrintInvGPSCoordinates,
2379             },
2380             # RDT1 - pairs of int32u_BE, starting at byte 8: "458275 471846"
2381             # RDT2 - pairs of int32u_BE, starting at byte 8: "472276 468526"
2382             # RDT3 - pairs of int32u_BE, starting at byte 8: "876603 482191"
2383             # RDT4 - pairs of int32u_BE, starting at byte 8: "1955 484612"
2384             # RDT6 - empty
2385             # RDT7 - empty
2386             # RDT8 - empty
2387             # RDT9 - only 16-byte header?
2388             # the boxes below all have a similar header (little-endian):
2389             # 0 int32u - number of records
2390             # 4 int32u - sample rate (Hz)
2391             # 6 int16u - record length in bytes
2392             # 8 int16u - 0x0123 = little-endian, 0x3210 = big endian
2393             # 10 int16u[3] - all zeros
2394             # 16 - start of records (each record ends in an int64u timestamp "ts" in ns)
2395             RDTA => {
2396             Name => 'RicohRDTA',
2397             SubDirectory => { TagTable => 'Image::ExifTool::Ricoh::RDTA' },
2398             },
2399             RDTB => {
2400             Name => 'RicohRDTB',
2401             SubDirectory => { TagTable => 'Image::ExifTool::Ricoh::RDTB' },
2402             },
2403             RDTC => {
2404             Name => 'RicohRDTC',
2405             SubDirectory => { TagTable => 'Image::ExifTool::Ricoh::RDTC' },
2406             },
2407             # RDTD - int16s[3],ts: "353 -914 16354 0 775.829"
2408             RDTG => {
2409             Name => 'RicohRDTG',
2410             SubDirectory => { TagTable => 'Image::ExifTool::Ricoh::RDTG' },
2411             },
2412             # RDTI - float[4],ts: "0.00165951 0.005770059 0.06838259 0.1744695 775.862"
2413             RDTL => {
2414             Name => 'RicohRDTL',
2415             SubDirectory => { TagTable => 'Image::ExifTool::Ricoh::RDTL' },
2416             },
2417             # ---- Samsung ----
2418             vndr => 'Vendor', #PH (Samsung PL70)
2419             SDLN => 'PlayMode', #PH (NC, Samsung ST80 "SEQ_PLAY")
2420             INFO => {
2421             Name => 'SamsungINFO',
2422             SubDirectory => { TagTable => 'Image::ExifTool::Samsung::INFO' },
2423             },
2424             '@sec' => { #PH (Samsung WB30F)
2425             Name => 'SamsungSec',
2426             SubDirectory => { TagTable => 'Image::ExifTool::Samsung::sec' },
2427             },
2428             'smta' => { #PH (Samsung SM-C101)
2429             Name => 'SamsungSmta',
2430             SubDirectory => {
2431             TagTable => 'Image::ExifTool::Samsung::smta',
2432             Start => 4,
2433             },
2434             },
2435             cver => 'CodeVersion', #PH (guess, Samsung MV900F)
2436             # ducp - 4 bytes all zero (Samsung ST96,WB750), 52 bytes all zero (Samsung WB30F)
2437             # edli - 52 bytes all zero (Samsung WB30F)
2438             # @etc - 4 bytes all zero (Samsung WB30F)
2439             # saut - 4 bytes all zero (Samsung SM-N900T)
2440             # smrd - string "TRUEBLUE" (Samsung SM-C101, etc)
2441             # ---- Sigma ----
2442             SIGM => [{
2443             Name => 'SigmaEXIF',
2444             Condition => '$$valPt =~ /^(II\x2a\0|MM\0\x2a)/',
2445             SubDirectory => {
2446             TagTable => 'Image::ExifTool::Exif::Main',
2447             ProcessProc => \&Image::ExifTool::ProcessTIFF, # (because ProcessMOV is default)
2448             },
2449             },{
2450             Name => 'PreviewImage',
2451             # 32-byte header followed by preview image. Length at offset 6 in header
2452             Condition => 'length($$valPt) > 0x20 and length($$valPt) == unpack("x6V",$$valPt) + 0x20',
2453             Groups => { 2 => 'Preview' },
2454             SetBase => 1, # so $$self{BASE} will be set for correct offsets in verbose/html dumps
2455             RawConv => q{
2456             $val = substr($val, 0x20);
2457             my $pt = $self->ValidateImage(\$val, $tag);
2458             if ($pt) {
2459             $$self{BASE} += 0x20;
2460             $$self{DOC_NUM} = ++$$self{DOC_COUNT};
2461             $self->ExtractInfo($pt, { ReEntry => 1 });
2462             $$self{DOC_NUM} = 0;
2463             }
2464             return $pt;
2465             },
2466             }],
2467             # ---- TomTom Bandit Action Cam ----
2468             TTMD => {
2469             Name => 'TomTomMetaData',
2470             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::TomTom' },
2471             },
2472             # ---- Samsung Gear 360 ----
2473             vrot => {
2474             Name => 'AccelerometerData',
2475             Notes => q{
2476             accelerometer readings for each frame of the video, expressed as sets of
2477             yaw, pitch and roll angles in degrees
2478             },
2479             Format => 'rational64s',
2480             ValueConv => '$val =~ s/^-?\d+ //; \$val', # (ignore leading version/size words)
2481             },
2482             # m360 - 8 bytes "0 0 0 0 0 0 0 1"
2483             # opax - 164 bytes unknown (center and affine arrays? ref 26)
2484             # opai - 32 bytes (maybe contains a serial number starting at byte 16? - PH) (rgb gains, degamma, gamma? ref 26)
2485             # intv - 16 bytes all zero
2486             # ---- Xiaomi ----
2487             mcvr => {
2488             Name => 'PreviewImage',
2489             Groups => { 2 => 'Preview' },
2490             Binary => 1,
2491             },
2492             # ---- Insta360 ----
2493             nail => {
2494             Name => 'ThumbnailTIFF',
2495             Notes => 'image found in some Insta360 videos, converted to TIFF format',
2496             Groups => { 2 => 'Preview' },
2497             RawConv => q{
2498             return undef if length $val < 8;
2499             my ($w, $h) = unpack('NN', $val);
2500             return undef if length $val < $w * $h + 8;
2501             return MakeTiffHeader($w, $h, 1, 8) . substr($val, 8, $w * $h);
2502             },
2503             Binary => 1,
2504             },
2505             # ---- Nextbase ----
2506             info => 'FirmwareVersion',
2507             'time' => {
2508             Name => 'TimeStamp',
2509             Format => 'int32u', # (followed by 4 unknown bytes 00 0d 00 00)
2510             Writable => 0,
2511             Groups => { 2 => 'Time' },
2512             ValueConv => '$val =~ s/ .*//; ConvertUnixTime($val)',
2513             PrintConv => '$self->ConvertDateTime($val)',
2514             },
2515             infi => {
2516             Name => 'CameraInfo',
2517             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Nextbase' },
2518             },
2519             finm => {
2520             Name => 'OriginalFileName',
2521             Writable => 0,
2522             },
2523             # AMBA ? - (133 bytes)
2524             # nbpl ? - "FP-433-KC"
2525             nbpl => { Name => 'Unknown_nbpl', Unknown => 1, Hidden => 1 },
2526             # maca ? - b8 2d 28 15 f1 48
2527             # sern ? - 0d 69 42 74
2528             # nbid ? - 0d 69 42 74 65 df 72 65 03 de c0 fb 01 01 00 00
2529             # ---- Unknown ----
2530             # CDET - 128 bytes (unknown origin)
2531             # mtyp - 4 bytes all zero (some drone video)
2532             # kgrf - 8 bytes all zero ? (in udta inside trak atom)
2533             # kgcg - 128 bytes 0's and 1's
2534             # kgsi - 4 bytes "00 00 00 80"
2535             # FIEL - 18 bytes "FIEL\0\x01\0\0\0..."
2536             #
2537             # other 3rd-party tags
2538             # (ref http://code.google.com/p/mp4parser/source/browse/trunk/isoparser/src/main/resources/isoparser-default.properties?r=814)
2539             #
2540             ccid => 'ContentID',
2541             icnu => 'IconURI',
2542             infu => 'InfoURL',
2543             cdis => 'ContentDistributorID',
2544             albr => { Name => 'AlbumArtist', Groups => { 2 => 'Author' } },
2545             cvru => 'CoverURI',
2546             lrcu => 'LyricsURI',
2547              
2548             tags => { # found in Audible .m4b audio books (ref PH)
2549             Name => 'Audible_tags',
2550             SubDirectory => { TagTable => 'Image::ExifTool::Audible::tags' },
2551             },
2552             # ludt - directory containing 'tlou' tag
2553             );
2554              
2555             # Unknown information stored in HTC One (M8) videos - PH
2556             %Image::ExifTool::QuickTime::HTCBinary = (
2557             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
2558             GROUPS => { 0 => 'MakerNotes', 1 => 'HTC', 2 => 'Video' },
2559             TAG_PREFIX => 'HTCBinary',
2560             FORMAT => 'int32u',
2561             FIRST_ENTRY => 0,
2562             # 0 - values: 1
2563             # 1 - values: 0
2564             # 2 - values: 0
2565             # 3 - values: FileSize minus 12 (why?)
2566             # 4 - values: 12
2567             );
2568              
2569             # TomTom Bandit Action Cam metadata (ref PH)
2570             %Image::ExifTool::QuickTime::TomTom = (
2571             PROCESS_PROC => \&ProcessMOV,
2572             GROUPS => { 2 => 'Video' },
2573             NOTES => 'Tags found in TomTom Bandit Action Cam MP4 videos.',
2574             TTAD => {
2575             Name => 'TomTomAD',
2576             SubDirectory => {
2577             TagTable => 'Image::ExifTool::QuickTime::Stream',
2578             ProcessProc => \&Image::ExifTool::QuickTime::ProcessTTAD,
2579             },
2580             },
2581             TTHL => { Name => 'TomTomHL', Binary => 1, Unknown => 1 }, # (mostly zeros)
2582             # (TTID values are different for each video)
2583             TTID => { Name => 'TomTomID', ValueConv => 'unpack("x4H*",$val)' },
2584             TTVI => { Name => 'TomTomVI', Format => 'int32u', Unknown => 1 }, # seen: "0 1 61 508 508"
2585             # TTVD seen: "normal 720p 60fps 60fps 16/9 wide 1x"
2586             TTVD => { Name => 'TomTomVD', ValueConv => 'my @a = ($val =~ /[\x20-\x7e]+/g); "@a"', List => 1 },
2587             );
2588              
2589             # User-specific media data atoms (ref 11)
2590             %Image::ExifTool::QuickTime::UserMedia = (
2591             PROCESS_PROC => \&ProcessMOV,
2592             GROUPS => { 2 => 'Video' },
2593             MTDT => {
2594             Name => 'MetaData',
2595             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::MetaData' },
2596             },
2597             );
2598              
2599             # User-specific media data atoms (ref 11)
2600             %Image::ExifTool::QuickTime::MetaData = (
2601             PROCESS_PROC => \&ProcessMetaData,
2602             GROUPS => { 2 => 'Video' },
2603             TAG_PREFIX => 'MetaData',
2604             0x01 => 'Title',
2605             0x03 => {
2606             Name => 'ProductionDate',
2607             Groups => { 2 => 'Time' },
2608             Shift => 'Time',
2609             Writable => 1,
2610             Permanent => 1,
2611             DelValue => '0000/00/00 00:00:00',
2612             # translate from format "YYYY/mm/dd HH:MM:SS"
2613             ValueConv => '$val=~tr{/}{:}; $val',
2614             ValueConvInv => '$val=~s[^(\d{4}):(\d{2}):][$1/$2/]; $val',
2615             PrintConv => '$self->ConvertDateTime($val)',
2616             PrintConvInv => '$self->InverseDateTime($val)',
2617             },
2618             0x04 => 'Software',
2619             0x05 => 'Product',
2620             0x0a => {
2621             Name => 'TrackProperty',
2622             RawConv => 'my @a=unpack("Nnn",$val); "@a"',
2623             PrintConv => [
2624             { 0 => 'No presentation', BITMASK => { 0 => 'Main track' } },
2625             { 0 => 'No attributes', BITMASK => { 15 => 'Read only' } },
2626             '"Priority $val"',
2627             ],
2628             },
2629             0x0b => {
2630             Name => 'TimeZone',
2631             Groups => { 2 => 'Time' },
2632             Writable => 1,
2633             Permanent => 1,
2634             DelValue => 0,
2635             RawConv => 'Get16s(\$val,0)',
2636             RawConvInv => 'Set16s($val)',
2637             PrintConv => 'TimeZoneString($val)',
2638             PrintConvInv => q{
2639             return undef unless $val =~ /^([-+])(\d{1,2}):?(\d{2})$/'
2640             my $tzmin = $2 * 60 + $3;
2641             $tzmin = -$tzmin if $1 eq '-';
2642             return $tzmin;
2643             }
2644             },
2645             0x0c => {
2646             Name => 'ModifyDate',
2647             Groups => { 2 => 'Time' },
2648             Shift => 'Time',
2649             Writable => 1,
2650             Permanent => 1,
2651             DelValue => '0000/00/00 00:00:00',
2652             # translate from format "YYYY/mm/dd HH:MM:SS"
2653             ValueConv => '$val=~tr{/}{:}; $val',
2654             ValueConvInv => '$val=~s[^(\d{4}):(\d{2}):][$1/$2/]; $val',
2655             PrintConv => '$self->ConvertDateTime($val)',
2656             PrintConvInv => '$self->InverseDateTime($val)',
2657             },
2658             );
2659              
2660             # compressed movie atoms (ref http://wiki.multimedia.cx/index.php?title=QuickTime_container#cmov)
2661             %Image::ExifTool::QuickTime::CMovie = (
2662             PROCESS_PROC => \&ProcessMOV,
2663             GROUPS => { 2 => 'Video' },
2664             dcom => 'Compression',
2665             # cmvd - compressed moov atom data
2666             );
2667              
2668             # Profile atoms (ref 11)
2669             %Image::ExifTool::QuickTime::Profile = (
2670             PROCESS_PROC => \&ProcessMOV,
2671             GROUPS => { 2 => 'Video' },
2672             FPRF => {
2673             Name => 'FileGlobalProfile',
2674             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::FileProf' },
2675             },
2676             APRF => {
2677             Name => 'AudioProfile',
2678             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::AudioProf' },
2679             },
2680             VPRF => {
2681             Name => 'VideoProfile',
2682             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::VideoProf' },
2683             },
2684             OLYM => { #PH
2685             Name => 'OlympusOLYM',
2686             SubDirectory => {
2687             TagTable => 'Image::ExifTool::Olympus::OLYM',
2688             ByteOrder => 'BigEndian',
2689             },
2690             },
2691             # PREX - seen written by Sony ILCE-7M5 (apparently contains another video profile?)
2692             );
2693              
2694             # FPRF atom information (ref 11)
2695             %Image::ExifTool::QuickTime::FileProf = (
2696             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
2697             GROUPS => { 2 => 'Video' },
2698             FORMAT => 'int32u',
2699             0 => { Name => 'FileProfileVersion', Unknown => 1 }, # unknown = uninteresting
2700             1 => {
2701             Name => 'FileFunctionFlags',
2702             PrintConv => { BITMASK => {
2703             28 => 'Fragmented',
2704             29 => 'Additional tracks',
2705             30 => 'Edited', # (main AV track is edited)
2706             }},
2707             },
2708             # 2 - reserved
2709             );
2710              
2711             # APRF atom information (ref 11)
2712             %Image::ExifTool::QuickTime::AudioProf = (
2713             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
2714             GROUPS => { 2 => 'Audio' },
2715             FORMAT => 'int32u',
2716             0 => { Name => 'AudioProfileVersion', Unknown => 1 },
2717             1 => 'AudioTrackID',
2718             2 => {
2719             Name => 'AudioCodec',
2720             Format => 'undef[4]',
2721             },
2722             3 => {
2723             Name => 'AudioCodecInfo',
2724             Unknown => 1,
2725             PrintConv => 'sprintf("0x%.4x", $val)',
2726             },
2727             4 => {
2728             Name => 'AudioAttributes',
2729             PrintConv => { BITMASK => {
2730             0 => 'Encrypted',
2731             1 => 'Variable bitrate',
2732             2 => 'Dual mono',
2733             }},
2734             },
2735             5 => {
2736             Name => 'AudioAvgBitrate',
2737             ValueConv => '$val * 1000',
2738             PrintConv => 'ConvertBitrate($val)',
2739             },
2740             6 => {
2741             Name => 'AudioMaxBitrate',
2742             ValueConv => '$val * 1000',
2743             PrintConv => 'ConvertBitrate($val)',
2744             },
2745             7 => 'AudioSampleRate',
2746             8 => 'AudioChannels',
2747             );
2748              
2749             # VPRF atom information (ref 11)
2750             %Image::ExifTool::QuickTime::VideoProf = (
2751             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
2752             GROUPS => { 2 => 'Video' },
2753             FORMAT => 'int32u',
2754             0 => { Name => 'VideoProfileVersion', Unknown => 1 },
2755             1 => 'VideoTrackID',
2756             2 => {
2757             Name => 'VideoCodec',
2758             Format => 'undef[4]',
2759             },
2760             3 => {
2761             Name => 'VideoCodecInfo',
2762             Unknown => 1,
2763             PrintConv => 'sprintf("0x%.4x", $val)',
2764             },
2765             4 => {
2766             Name => 'VideoAttributes',
2767             PrintConv => { BITMASK => {
2768             0 => 'Encrypted',
2769             1 => 'Variable bitrate',
2770             2 => 'Variable frame rate',
2771             3 => 'Interlaced',
2772             }},
2773             },
2774             5 => {
2775             Name => 'VideoAvgBitrate',
2776             ValueConv => '$val * 1000',
2777             PrintConv => 'ConvertBitrate($val)',
2778             },
2779             6 => {
2780             Name => 'VideoMaxBitrate',
2781             ValueConv => '$val * 1000',
2782             PrintConv => 'ConvertBitrate($val)',
2783             },
2784             7 => {
2785             Name => 'VideoAvgFrameRate',
2786             Format => 'fixed32u',
2787             PrintConv => 'int($val * 1000 + 0.5) / 1000',
2788             },
2789             8 => {
2790             Name => 'VideoMaxFrameRate',
2791             Format => 'fixed32u',
2792             PrintConv => 'int($val * 1000 + 0.5) / 1000',
2793             },
2794             9 => {
2795             Name => 'VideoSize',
2796             Format => 'int16u[2]',
2797             PrintConv => '$val=~tr/ /x/; $val',
2798             },
2799             10 => {
2800             Name => 'PixelAspectRatio',
2801             Format => 'int16u[2]',
2802             PrintConv => '$val=~tr/ /:/; $val',
2803             },
2804             );
2805              
2806             # meta atoms
2807             %Image::ExifTool::QuickTime::Meta = (
2808             PROCESS_PROC => \&ProcessMOV,
2809             WRITE_PROC => \&WriteQuickTime,
2810             GROUPS => { 1 => 'Meta', 2 => 'Video' },
2811             ilst => {
2812             Name => 'ItemList',
2813             SubDirectory => {
2814             TagTable => 'Image::ExifTool::QuickTime::ItemList',
2815             HasData => 1, # process atoms as containers with 'data' elements
2816             },
2817             },
2818             # MP4 tags (ref 5)
2819             hdlr => {
2820             Name => 'Handler',
2821             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Handler' },
2822             },
2823             dinf => {
2824             Name => 'DataInfo', # (don't change this name -- used to recognize directory when writing)
2825             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::DataInfo' },
2826             },
2827             ipmc => {
2828             Name => 'IPMPControl',
2829             Flags => ['Binary','Unknown'],
2830             },
2831             iloc => {
2832             Name => 'ItemLocation',
2833             RawConv => \&ParseItemLocation,
2834             WriteHook => \&ParseItemLocation,
2835             Notes => 'parsed, but not extracted as a tag',
2836             },
2837             ipro => {
2838             Name => 'ItemProtection',
2839             Flags => ['Binary','Unknown'],
2840             },
2841             iinf => [{
2842             Name => 'ItemInformation',
2843             Condition => '$$self{LastItemID} = -1; $$valPt =~ /^\0/', # (check for version 0)
2844             SubDirectory => {
2845             TagTable => 'Image::ExifTool::QuickTime::ItemInfo',
2846             Start => 6, # (4-byte version/flags + 2-byte count)
2847             },
2848             },{
2849             Name => 'ItemInformation',
2850             SubDirectory => {
2851             TagTable => 'Image::ExifTool::QuickTime::ItemInfo',
2852             Start => 8, # (4-byte version/flags + 4-byte count)
2853             },
2854             }],
2855             'xml ' => {
2856             Name => 'XML',
2857             Flags => [ 'Binary', 'Protected' ],
2858             BlockExtract => 1,
2859             SubDirectory => {
2860             TagTable => 'Image::ExifTool::XMP::XML',
2861             IgnoreProp => { NonRealTimeMeta => 1 }, # ignore container for Sony 'nrtm'
2862             },
2863             },
2864             'keys' => [{
2865             Name => 'AudioKeys',
2866             Condition => '$$self{MediaType} eq "soun"',
2867             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::AudioKeys' },
2868             },{
2869             Name => 'VideoKeys',
2870             Condition => '$$self{MediaType} eq "vide"',
2871             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::VideoKeys' },
2872             },{
2873             Name => 'Keys',
2874             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Keys' },
2875             }],
2876             bxml => {
2877             Name => 'BinaryXML',
2878             Flags => ['Binary','Unknown'],
2879             },
2880             pitm => [{
2881             Name => 'PrimaryItemReference',
2882             Condition => '$$valPt =~ /^\0/', # (version 0?)
2883             RawConv => '$$self{PrimaryItem} = unpack("x4n",$val)',
2884             WriteHook => sub { my ($val,$et) = @_; $$et{PrimaryItem} = unpack("x4n",$val); },
2885             },{
2886             Name => 'PrimaryItemReference',
2887             RawConv => '$$self{PrimaryItem} = unpack("x4N",$val)',
2888             WriteHook => sub { my ($val,$et) = @_; $$et{PrimaryItem} = unpack("x4N",$val); },
2889             }],
2890             free => { #PH
2891             Name => 'Free',
2892             Flags => ['Binary','Unknown'],
2893             },
2894             iprp => {
2895             Name => 'ItemProperties',
2896             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::ItemProp' },
2897             },
2898             iref => {
2899             Name => 'ItemReference',
2900             # the version is needed to parse some of the item references
2901             Condition => '$$self{ItemRefVersion} = ord($$valPt); 1',
2902             SubDirectory => {
2903             TagTable => 'Image::ExifTool::QuickTime::ItemRef',
2904             Start => 4,
2905             },
2906             },
2907             idat => {
2908             Name => 'MetaImageSize', #PH (NC)
2909             Condition => '$$self{FileType} eq "HEIC"',
2910             Format => 'int16u',
2911             # (don't know what the first two numbers are for)
2912             PrintConv => '$val =~ s/^(\d+) (\d+) (\d+) (\d+)/${3}x$4/; $val',
2913             },
2914             uuid => [
2915             { #PH (Canon R5/R6 HIF)
2916             Name => 'MetaVersion', # (NC)
2917             Condition => '$$valPt=~/^\x85\xc0\xb6\x87\x82\x0f\x11\xe0\x81\x11\xf4\xce\x46\x2b\x6a\x48/',
2918             RawConv => 'substr($val, 0x14)',
2919             },
2920             {
2921             Name => 'UUID-Unknown',
2922             %unknownInfo,
2923             },
2924             ],
2925             grpl => {
2926             Name => 'Unknown_grpl',
2927             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::grpl' },
2928             },
2929             # ctry - country list (ref 29)
2930             # lang - language list (ref 29)
2931             );
2932              
2933             # unknown grpl container
2934             %Image::ExifTool::QuickTime::grpl = (
2935             PROCESS_PROC => \&ProcessMOV,
2936             GROUPS => { 2 => 'Video' },
2937             # altr - seen "00 00 00 00 00 00 00 41 00 00 00 02 00 00 00 42 00 00 00 2e"
2938             );
2939              
2940             # additional metadata container (ref ISO14496-12:2015)
2941             %Image::ExifTool::QuickTime::OtherMeta = (
2942             PROCESS_PROC => \&ProcessMOV,
2943             WRITE_PROC => \&WriteQuickTime,
2944             GROUPS => { 2 => 'Video' },
2945             mere => {
2946             Name => 'MetaRelation',
2947             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::MetaRelation' },
2948             },
2949             meta => {
2950             Name => 'Meta',
2951             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Meta' },
2952             },
2953             );
2954              
2955             # metabox relation (ref ISO14496-12:2015)
2956             %Image::ExifTool::QuickTime::MetaRelation = (
2957             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
2958             GROUPS => { 2 => 'Video' },
2959             FORMAT => 'int32u',
2960             # 0 => 'MetaRelationVersion',
2961             # 1 => 'FirstMetaboxHandlerType',
2962             # 2 => 'FirstMetaboxHandlerType',
2963             # 3 => { Name => 'MetaboxRelation', Format => 'int8u' },
2964             );
2965              
2966             %Image::ExifTool::QuickTime::ItemProp = (
2967             PROCESS_PROC => \&ProcessMOV,
2968             WRITE_PROC => \&WriteQuickTime,
2969             GROUPS => { 2 => 'Image' },
2970             ipco => {
2971             Name => 'ItemPropertyContainer',
2972             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::ItemPropCont' },
2973             },
2974             ipma => {
2975             Name => 'ItemPropertyAssociation',
2976             RawConv => \&ParseItemPropAssoc,
2977             # (comment out because we do this manually _before_ ipco when writing)
2978             # WriteHook => \&ParseItemPropAssoc,
2979             Notes => 'parsed, but not extracted as a tag',
2980             },
2981             );
2982              
2983             %Image::ExifTool::QuickTime::ItemPropCont = (
2984             PROCESS_PROC => \&ProcessMOV,
2985             WRITE_PROC => \&WriteQuickTime,
2986             CHECK_PROC => \&CheckQTValue,
2987             PERMANENT => 1, # (can't be deleted)
2988             GROUPS => { 2 => 'Image' },
2989             VARS => { START_INDEX => 1 }, # show verbose indices starting at 1
2990             colr => [{
2991             Name => 'ICC_Profile',
2992             Condition => '$$valPt =~ /^(prof|rICC)/',
2993             # (don't do this because Apple Preview won't display an HEIC with a 0-length profile)
2994             # Permanent => 0, # (in QuickTime, this writes a zero-length box instead of deleting)
2995             SubDirectory => {
2996             TagTable => 'Image::ExifTool::ICC_Profile::Main',
2997             Start => 4,
2998             },
2999             },{
3000             Name => 'ColorRepresentation',
3001             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::ColorRep' },
3002             }],
3003             irot => {
3004             Name => 'Rotation',
3005             Format => 'int8u',
3006             Writable => 'int8u',
3007             Protected => 1,
3008             PrintConv => {
3009             0 => 'Horizontal (normal)',
3010             1 => 'Rotate 270 CW',
3011             2 => 'Rotate 180',
3012             3 => 'Rotate 90 CW',
3013             },
3014             },
3015             imir => { # (applied before rotation)
3016             Name => 'Mirroring',
3017             Format => 'int8u',
3018             # yes, I realize this making this writable is useless without the ability to
3019             # create/delete this box because it is the existence of this box that is
3020             # significant. (The people who wrote the specification succeeded in making
3021             # this as complicated as possible because creating/deleting boxes from the
3022             # item property container is a real pain in the ass!)
3023             Writable => 'int8u',
3024             Protected => 1,
3025             PrintConv => {
3026             0 => 'Vertical',
3027             1 => 'Horizontal',
3028             # (it would have been great if the specification allowed for a "no mirroring" value)
3029             },
3030             },
3031             ispe => {
3032             Name => 'ImageSpatialExtent',
3033             Condition => '$$valPt =~ /^\0{4}/', # (version/flags == 0/0)
3034             RawConv => q{
3035             my @dim = unpack("x4N*", $val);
3036             return undef if @dim < 2;
3037             unless ($$self{DOC_NUM}) {
3038             $self->FoundTag(ImageWidth => $dim[0]);
3039             $self->FoundTag(ImageHeight => $dim[1]);
3040             }
3041             return join ' ', @dim;
3042             },
3043             PrintConv => '$val =~ tr/ /x/; $val',
3044             },
3045             pixi => {
3046             Name => 'ImagePixelDepth',
3047             Condition => '$$valPt =~ /^\0{4}./s', # (version/flags == 0/0 and count)
3048             RawConv => 'join " ", unpack("x5C*", $val)',
3049             },
3050             auxC => {
3051             Name => 'AuxiliaryImageType',
3052             Format => 'undef',
3053             RawConv => '$val = substr($val, 4); $val =~ s/\0.*//s; $val',
3054             },
3055             pasp => {
3056             Name => 'PixelAspectRatio',
3057             Format => 'int32u',
3058             Writable => 'int32u',
3059             Count => 2,
3060             Protected => 1,
3061             },
3062             rloc => {
3063             Name => 'RelativeLocation',
3064             Format => 'int32u',
3065             RawConv => '$val =~ s/^\S+\s+//; $val', # remove version/flags
3066             },
3067             clap => {
3068             Name => 'CleanAperture',
3069             Format => 'rational64s',
3070             Notes => '4 numbers: width, height, left and top',
3071             },
3072             hvcC => {
3073             Name => 'HEVCConfiguration',
3074             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::HEVCConfig' },
3075             },
3076             av1C => {
3077             Name => 'AV1Configuration',
3078             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::AV1Config' },
3079             },
3080             clli => {
3081             Name => 'ContentLightLevel',
3082             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::ContentLightLevel' },
3083             },
3084             # ref https://nokiatech.github.io/heif/technical.html
3085             # cclv - Content Color Volume
3086             # mdcv - Mastering Display Color Volume
3087             # rrtp - Required reference types
3088             # crtt - Creation time information
3089             # mdft - Modification time information
3090             # udes - User description
3091             # altt - Accessibility text
3092             # aebr - Auto exposure information
3093             # wbbr - White balance information
3094             # fobr - Focus information
3095             # afbr - Flash exposure information
3096             # dobr - Depth of field information
3097             # pano - Panorama information
3098             # iscl - Image Scaling
3099             );
3100              
3101             # ref https://aomediacodec.github.io/av1-spec/av1-spec.pdf
3102             # (NOTE: conversions are the same as Image::ExifTool::ICC_Profile::ColorRep tags)
3103             %Image::ExifTool::QuickTime::ColorRep = (
3104             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
3105             GROUPS => { 2 => 'Video' },
3106             FIRST_ENTRY => 0,
3107             0 => { Name => 'ColorProfiles', Format => 'undef[4]' },
3108             4 => {
3109             Name => 'ColorPrimaries',
3110             Format => 'int16u',
3111             PrintConv => {
3112             1 => 'BT.709',
3113             2 => 'Unspecified',
3114             4 => 'BT.470 System M (historical)',
3115             5 => 'BT.470 System B, G (historical)',
3116             6 => 'BT.601',
3117             7 => 'SMPTE 240',
3118             8 => 'Generic film (color filters using illuminant C)',
3119             9 => 'BT.2020, BT.2100',
3120             10 => 'SMPTE 428 (CIE 1931 XYZ)', #forum14766
3121             11 => 'SMPTE RP 431-2',
3122             12 => 'SMPTE EG 432-1',
3123             22 => 'EBU Tech. 3213-E',
3124             },
3125             },
3126             6 => {
3127             Name => 'TransferCharacteristics',
3128             Format => 'int16u',
3129             PrintConv => {
3130             0 => 'For future use (0)',
3131             1 => 'BT.709',
3132             2 => 'Unspecified',
3133             3 => 'For future use (3)',
3134             4 => 'BT.470 System M (historical)', # Gamma 2.2? (ref forum14960)
3135             5 => 'BT.470 System B, G (historical)', # Gamma 2.8? (ref forum14960)
3136             6 => 'BT.601',
3137             7 => 'SMPTE 240 M',
3138             8 => 'Linear',
3139             9 => 'Logarithmic (100 : 1 range)',
3140             10 => 'Logarithmic (100 * Sqrt(10) : 1 range)',
3141             11 => 'IEC 61966-2-4',
3142             12 => 'BT.1361',
3143             13 => 'sRGB or sYCC',
3144             14 => 'BT.2020 10-bit systems',
3145             15 => 'BT.2020 12-bit systems',
3146             16 => 'SMPTE ST 2084, ITU BT.2100 PQ',
3147             17 => 'SMPTE ST 428',
3148             18 => 'BT.2100 HLG, ARIB STD-B67',
3149             },
3150             },
3151             8 => {
3152             Name => 'MatrixCoefficients',
3153             Format => 'int16u',
3154             PrintConv => {
3155             0 => 'Identity matrix',
3156             1 => 'BT.709',
3157             2 => 'Unspecified',
3158             3 => 'For future use (3)',
3159             4 => 'US FCC 73.628',
3160             5 => 'BT.470 System B, G (historical)',
3161             6 => 'BT.601',
3162             7 => 'SMPTE 240 M',
3163             8 => 'YCgCo',
3164             9 => 'BT.2020 non-constant luminance, BT.2100 YCbCr',
3165             10 => 'BT.2020 constant luminance',
3166             11 => 'SMPTE ST 2085 YDzDx',
3167             12 => 'Chromaticity-derived non-constant luminance',
3168             13 => 'Chromaticity-derived constant luminance',
3169             14 => 'BT.2100 ICtCp',
3170             },
3171             },
3172             10 => {
3173             Name => 'VideoFullRangeFlag',
3174             Mask => 0x80,
3175             PrintConv => { 0 => 'Limited', 1 => 'Full' },
3176             },
3177             );
3178              
3179             # HEVC configuration (ref https://github.com/MPEGGroup/isobmff/blob/master/IsoLib/libisomediafile/src/HEVCConfigAtom.c)
3180             %Image::ExifTool::QuickTime::HEVCConfig = (
3181             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
3182             GROUPS => { 2 => 'Video' },
3183             FIRST_ENTRY => 0,
3184             0 => 'HEVCConfigurationVersion',
3185             1 => {
3186             Name => 'GeneralProfileSpace',
3187             Mask => 0xc0,
3188             PrintConv => { 0 => 'Conforming' },
3189             },
3190             1.1 => {
3191             Name => 'GeneralTierFlag',
3192             Mask => 0x20,
3193             PrintConv => {
3194             0 => 'Main Tier',
3195             1 => 'High Tier',
3196             },
3197             },
3198             1.2 => {
3199             Name => 'GeneralProfileIDC',
3200             Mask => 0x1f,
3201             PrintConv => {
3202             0 => 'No Profile',
3203             1 => 'Main',
3204             2 => 'Main 10',
3205             3 => 'Main Still Picture',
3206             4 => 'Format Range Extensions',
3207             5 => 'High Throughput',
3208             6 => 'Multiview Main',
3209             7 => 'Scalable Main',
3210             8 => '3D Main',
3211             9 => 'Screen Content Coding Extensions',
3212             10 => 'Scalable Format Range Extensions',
3213             11 => 'High Throughput Screen Content Coding Extensions',
3214             },
3215             },
3216             2 => {
3217             Name => 'GenProfileCompatibilityFlags',
3218             Format => 'int32u',
3219             PrintConv => { BITMASK => {
3220             31 => 'No Profile', # (bit 0 in stream)
3221             30 => 'Main', # (bit 1 in stream)
3222             29 => 'Main 10', # (bit 2 in stream)
3223             28 => 'Main Still Picture', # (bit 3 in stream)
3224             27 => 'Format Range Extensions',# (...)
3225             26 => 'High Throughput',
3226             25 => 'Multiview Main',
3227             24 => 'Scalable Main',
3228             23 => '3D Main',
3229             22 => 'Screen Content Coding Extensions',
3230             21 => 'Scalable Format Range Extensions',
3231             20 => 'High Throughput Screen Content Coding Extensions',
3232             }},
3233             },
3234             6 => {
3235             Name => 'ConstraintIndicatorFlags',
3236             Format => 'int8u[6]',
3237             },
3238             12 => {
3239             Name => 'GeneralLevelIDC',
3240             PrintConv => 'sprintf("%d (level %.1f)", $val, $val/30)',
3241             },
3242             13 => {
3243             Name => 'MinSpatialSegmentationIDC',
3244             Format => 'int16u',
3245             Mask => 0x0fff,
3246             },
3247             15 => {
3248             Name => 'ParallelismType',
3249             Mask => 0x03,
3250             },
3251             16 => {
3252             Name => 'ChromaFormat',
3253             Mask => 0x03,
3254             PrintConv => {
3255             0 => 'Monochrome',
3256             1 => '4:2:0',
3257             2 => '4:2:2',
3258             3 => '4:4:4',
3259             },
3260             },
3261             17 => {
3262             Name => 'BitDepthLuma',
3263             Mask => 0x07,
3264             ValueConv => '$val + 8',
3265             },
3266             18 => {
3267             Name => 'BitDepthChroma',
3268             Mask => 0x07,
3269             ValueConv => '$val + 8',
3270             },
3271             19 => {
3272             Name => 'AverageFrameRate',
3273             Format => 'int16u',
3274             ValueConv => '$val / 256',
3275             },
3276             21 => {
3277             Name => 'ConstantFrameRate',
3278             Mask => 0xc0,
3279             PrintConv => {
3280             0 => 'Unknown',
3281             1 => 'Constant Frame Rate',
3282             2 => 'Each Temporal Layer is Constant Frame Rate',
3283             },
3284             },
3285             21.1 => {
3286             Name => 'NumTemporalLayers',
3287             Mask => 0x38,
3288             },
3289             21.2 => {
3290             Name => 'TemporalIDNested',
3291             Mask => 0x04,
3292             PrintConv => { 0 => 'No', 1 => 'Yes' },
3293             },
3294             #21.3 => {
3295             # Name => 'NALUnitLengthSize',
3296             # Mask => 0x03,
3297             # ValueConv => '$val + 1',
3298             # PrintConv => { 1 => '8-bit', 2 => '16-bit', 4 => '32-bit' },
3299             #},
3300             #22 => 'NumberOfNALUnitArrays',
3301             # (don't decode the NAL unit arrays)
3302             );
3303              
3304             # HEVC configuration (ref https://aomediacodec.github.io/av1-isobmff/#av1codecconfigurationbox)
3305             %Image::ExifTool::QuickTime::AV1Config = (
3306             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
3307             GROUPS => { 2 => 'Video' },
3308             FIRST_ENTRY => 0,
3309             0 => {
3310             Name => 'AV1ConfigurationVersion',
3311             Mask => 0x7f,
3312             },
3313             1.0 => {
3314             Name => 'SeqProfile',
3315             Mask => 0xe0,
3316             Unknown => 1,
3317             },
3318             1.1 => {
3319             Name => 'SeqLevelIdx0',
3320             Mask => 0x1f,
3321             Unknown => 1,
3322             },
3323             2.0 => {
3324             Name => 'SeqTier0',
3325             Mask => 0x80,
3326             Unknown => 1,
3327             },
3328             2.1 => {
3329             Name => 'HighBitDepth',
3330             Mask => 0x40,
3331             Unknown => 1,
3332             },
3333             2.2 => {
3334             Name => 'TwelveBit',
3335             Mask => 0x20,
3336             Unknown => 1,
3337             },
3338             2.3 => {
3339             Name => 'ChromaFormat', # (Monochrome+SubSamplingX+SubSamplingY)
3340             Notes => 'bits: 0x04 = Monochrome, 0x02 = SubSamplingX, 0x01 = SubSamplingY',
3341             Mask => 0x1c,
3342             PrintConv => {
3343             0x00 => 'YUV 4:4:4',
3344             0x02 => 'YUV 4:2:2',
3345             0x03 => 'YUV 4:2:0',
3346             0x07 => 'Monochrome 4:0:0',
3347             },
3348             },
3349             2.4 => {
3350             Name => 'ChromaSamplePosition',
3351             Mask => 0x03,
3352             PrintConv => {
3353             0 => 'Unknown',
3354             1 => 'Vertical',
3355             2 => 'Colocated',
3356             3 => '(reserved)',
3357             },
3358             },
3359             3 => {
3360             Name => 'InitialDelaySamples',
3361             RawConv => '$val & 0x10 ? undef : ($val & 0x0f) + 1',
3362             Unknown => 1,
3363             },
3364             );
3365              
3366             # ref https://android.googlesource.com/platform/frameworks/av/+/master/media/libstagefright/MPEG4Writer.cpp
3367             %Image::ExifTool::QuickTime::ContentLightLevel = (
3368             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
3369             GROUPS => { 2 => 'Video' },
3370             FIRST_ENTRY => 0,
3371             FORMAT => 'int16u',
3372             0 => 'MaxContentLightLevel',
3373             1 => 'MaxPicAverageLightLevel',
3374             );
3375              
3376             %Image::ExifTool::QuickTime::ItemRef = (
3377             PROCESS_PROC => \&ProcessMOV,
3378             WRITE_PROC => \&WriteQuickTime,
3379             GROUPS => { 2 => 'Image' },
3380             # (Note: ExifTool's ItemRefVersion may be used to test the iref version number)
3381             NOTES => q{
3382             The Item reference entries listed in the table below contain information about
3383             the associations between items in the file. This information is used by
3384             ExifTool, but these entries are not extracted as tags.
3385             },
3386             dimg => {
3387             Name => 'DerivedImageRef',
3388             # also parse these for the ID of the primary 'tmap' item
3389             # (tone-mapped image in HDRGainMap HEIC by iPhone 15 and 16)
3390             RawConv => \&ParseContentDescribes,
3391             WriteHook => \&ParseContentDescribes,
3392             },
3393             thmb => { Name => 'ThumbnailRef', RawConv => 'undef' },
3394             auxl => { Name => 'AuxiliaryImageRef', RawConv => 'undef' },
3395             cdsc => {
3396             Name => 'ContentDescribes',
3397             RawConv => \&ParseContentDescribes,
3398             WriteHook => \&ParseContentDescribes,
3399             },
3400             );
3401              
3402             %Image::ExifTool::QuickTime::ItemInfo = (
3403             PROCESS_PROC => \&ProcessMOV,
3404             WRITE_PROC => \&WriteQuickTime,
3405             GROUPS => { 2 => 'Image' },
3406             # avc1 - AVC image
3407             # hvc1 - HEVC image
3408             # lhv1 - L-HEVC image
3409             # infe - ItemInformationEntry
3410             # infe types: avc1,hvc1,lhv1,Exif,xml1,iovl(overlay image),grid,mime,tmap,hvt1(tile image)
3411             # ('tmap' has something to do with the new gainmap written by iPhone 15 and 16)
3412             infe => {
3413             Name => 'ItemInfoEntry',
3414             RawConv => \&ParseItemInfoEntry,
3415             WriteHook => \&ParseItemInfoEntry,
3416             Notes => 'parsed, but not extracted as a tag',
3417             },
3418             );
3419              
3420             # track reference atoms
3421             %Image::ExifTool::QuickTime::TrackRef = (
3422             PROCESS_PROC => \&ProcessMOV,
3423             GROUPS => { 1 => 'Track#', 2 => 'Video' },
3424             chap => { Name => 'ChapterListTrackID', Format => 'int32u' },
3425             tmcd => { Name => 'TimecodeTrack', Format => 'int32u' },
3426             mpod => { #PH (FLIR MP4)
3427             Name => 'ElementaryStreamTrack',
3428             Format => 'int32u',
3429             ValueConv => '$val =~ s/^1 //; $val', # (why 2 numbers? -- ignore the first if "1")
3430             },
3431             # also: iTunesInfo
3432             cdsc => {
3433             Name => 'ContentDescribes',
3434             Format => 'int32u',
3435             PrintConv => '"Track $val"',
3436             },
3437             clcp => { Name => 'ClosedCaptionTrack', Format => 'int32u' }, #29
3438             fall => { Name => 'AlternateFormatTrack', Format => 'int32u' }, #29
3439             folw => { Name => 'SubtitleTrack', Format => 'int32u' }, #29
3440             forc => { Name => 'ForcedSubtitleTrack', Format => 'int32u' }, #29
3441             scpt => { Name => 'TranscriptTrack', Format => 'int32u' }, #29
3442             ssrc => { Name => 'Non-primarySourceTrack', Format => 'int32u' }, #29
3443             sync => { Name => 'SyncronizedTrack', Format => 'int32u' }, #29
3444             # hint - Original media for hint track (ref 29)
3445             # cdep (Structural Dependency QT tag?)
3446             );
3447              
3448             # track aperture mode dimensions atoms
3449             # (ref https://developer.apple.com/library/mac/#documentation/QuickTime/QTFF/QTFFChap2/qtff2.html)
3450             %Image::ExifTool::QuickTime::TrackAperture = (
3451             PROCESS_PROC => \&ProcessMOV,
3452             GROUPS => { 1 => 'Track#', 2 => 'Video' },
3453             clef => {
3454             Name => 'CleanApertureDimensions',
3455             Format => 'fixed32u',
3456             Count => 3,
3457             ValueConv => '$val =~ s/^.*? //; $val', # remove flags word
3458             PrintConv => '$val =~ tr/ /x/; $val',
3459             },
3460             prof => {
3461             Name => 'ProductionApertureDimensions',
3462             Format => 'fixed32u',
3463             Count => 3,
3464             ValueConv => '$val =~ s/^.*? //; $val',
3465             PrintConv => '$val =~ tr/ /x/; $val',
3466             },
3467             enof => {
3468             Name => 'EncodedPixelsDimensions',
3469             Format => 'fixed32u',
3470             Count => 3,
3471             ValueConv => '$val =~ s/^.*? //; $val',
3472             PrintConv => '$val =~ tr/ /x/; $val',
3473             },
3474             );
3475              
3476             # item list atoms
3477             # -> these atoms are unique, and contain one or more 'data' atoms
3478             %Image::ExifTool::QuickTime::ItemList = (
3479             PROCESS_PROC => \&ProcessMOV,
3480             WRITE_PROC => \&WriteQuickTime,
3481             CHECK_PROC => \&CheckQTValue,
3482             WRITABLE => 1,
3483             PREFERRED => 2, # (preferred over UserData and Keys tags when writing)
3484             FORMAT => 'string',
3485             GROUPS => { 1 => 'ItemList', 2 => 'Audio' },
3486             WRITE_GROUP => 'ItemList',
3487             LANG_INFO => \&GetLangInfo,
3488             NOTES => q{
3489             This is the preferred location for creating new QuickTime tags. Tags in
3490             this table support alternate languages which are accessed by adding a
3491             3-character ISO 639-2 language code and an optional ISO 3166-1 alpha 2
3492             country code to the tag name (eg. "ItemList:Title-fra" or
3493             "ItemList::Title-fra-FR"). When creating a new Meta box to contain the
3494             ItemList directory, by default ExifTool adds an 'mdir' (Metadata) Handler
3495             box because Apple software may ignore ItemList tags otherwise, but the API
3496             L option may be set to 0 to avoid this.
3497             },
3498             # in this table, binary 1 and 2-byte "data"-type tags are interpreted as
3499             # int8u and int16u. Multi-byte binary "data" tags are extracted as binary data.
3500             # (Note that the Preferred property is set to 0 for some tags to prevent them
3501             # from being created when a same-named tag already exists in the table)
3502             "\xa9ART" => 'Artist',
3503             "\xa9alb" => 'Album',
3504             "\xa9aut" => { Name => 'Author', Avoid => 1, Groups => { 2 => 'Author' } }, #forum10091 ('auth' is preferred)
3505             "\xa9cmt" => 'Comment',
3506             "\xa9com" => { Name => 'Composer', Avoid => 1, }, # ("\xa9wrt" is preferred in ItemList)
3507             "\xa9day" => {
3508             Name => 'ContentCreateDate',
3509             Groups => { 2 => 'Time' },
3510             %iso8601Date,
3511             },
3512             "\xa9des" => 'Description', #4
3513             "\xa9enc" => 'EncodedBy', #10
3514             "\xa9gen" => 'Genre',
3515             "\xa9grp" => 'Grouping',
3516             "\xa9lyr" => 'Lyrics',
3517             "\xa9nam" => 'Title',
3518             "\xa9too" => 'Encoder',
3519             "\xa9trk" => 'Track',
3520             "\xa9wrt" => 'Composer',
3521             #
3522             # the following tags written by AtomicParsley 0.9.6
3523             # (ref https://exiftool.org/forum/index.php?topic=11455.0)
3524             #
3525             "\xa9st3" => 'Subtitle',
3526             "\xa9con" => 'Conductor',
3527             "\xa9sol" => 'Soloist',
3528             "\xa9arg" => 'Arranger',
3529             "\xa9ope" => 'OriginalArtist',
3530             "\xa9dir" => 'Director',
3531             "\xa9ard" => 'ArtDirector',
3532             "\xa9sne" => 'SoundEngineer',
3533             "\xa9prd" => 'Producer',
3534             "\xa9xpd" => 'ExecutiveProducer',
3535             sdes => 'StoreDescription',
3536             #
3537             '----' => {
3538             Name => 'iTunesInfo',
3539             Deletable => 1, # (deletable via 'iTunes' group)
3540             SubDirectory => {
3541             TagTable => 'Image::ExifTool::QuickTime::iTunesInfo',
3542             DirName => 'iTunes', # (necessary for group 'iTunes' delete)
3543             },
3544             },
3545             aART => { Name => 'AlbumArtist', Groups => { 2 => 'Author' } },
3546             covr => { Name => 'CoverArt', Groups => { 2 => 'Preview' }, Binary => 1 },
3547             cpil => { #10
3548             Name => 'Compilation',
3549             Format => 'int8u', #27 (ref 23 contradicts what AtomicParsley actually writes, which is int8s)
3550             Writable => 'int8s',
3551             PrintConv => { 0 => 'No', 1 => 'Yes' },
3552             },
3553             disk => {
3554             Name => 'DiskNumber',
3555             Format => 'undef', # (necessary to prevent decoding as string!)
3556             ValueConv => q{
3557             return \$val unless length($val) >= 6;
3558             my @a = unpack 'x2nn', $val;
3559             return $a[1] ? join(' of ', @a) : $a[0];
3560             },
3561             ValueConvInv => q{
3562             my @a = $val =~ /\d+/g;
3563             return undef if @a == 0 or @a > 2;
3564             push @a, 0 if @a == 1;
3565             return pack('n3', 0, @a);
3566             },
3567             },
3568             pgap => { #10
3569             Name => 'PlayGap',
3570             Format => 'int8u', #23
3571             Writable => 'int8s', #27
3572             PrintConv => {
3573             0 => 'Insert Gap',
3574             1 => 'No Gap',
3575             },
3576             },
3577             tmpo => {
3578             Name => 'BeatsPerMinute',
3579             # marked as boolean but really int16u in my sample
3580             # (but written as int16s by iTunes and AtomicParsley, ref forum11506)
3581             Format => 'int16u',
3582             Writable => 'int16s',
3583             },
3584             trkn => {
3585             Name => 'TrackNumber',
3586             Format => 'undef', # (necessary to prevent decoding as string!)
3587             ValueConv => q{
3588             return \$val unless length($val) >= 6;
3589             my @a = unpack 'x2nn', $val;
3590             return $a[1] ? join(' of ', @a) : $a[0];
3591             },
3592             # (see forum11501 for discussion about the format used)
3593             ValueConvInv => q{
3594             my @a = $val =~ /\d+/g;
3595             return undef if @a == 0 or @a > 2;
3596             push @a, 0 if @a == 1;
3597             return pack('n4', 0, @a, 0);
3598             },
3599             },
3600             #
3601             # Note: it is possible that the tags below are not being decoded properly
3602             # because I don't have samples to verify many of these - PH
3603             #
3604             akID => { #10
3605             Name => 'AppleStoreAccountType',
3606             Format => 'int8u', #24
3607             Writable => 'int8s', #27
3608             PrintConv => {
3609             0 => 'iTunes',
3610             1 => 'AOL',
3611             },
3612             },
3613             albm => { Name => 'Album', Avoid => 1 }, #(ffmpeg source)
3614             apID => 'AppleStoreAccount',
3615             atID => {
3616             # (ref 10 called this AlbumTitleID or TVSeries)
3617             Name => 'ArtistID', #28 (or Track ID ref https://gist.github.com/maf654321/2b44c7b15d798f0c52ee)
3618             Format => 'int32u',
3619             Writable => 'int32s', #27
3620             },
3621             auth => { Name => 'Author', Groups => { 2 => 'Author' } },
3622             catg => 'Category', #7
3623             cnID => { #10
3624             Name => 'AppleStoreCatalogID',
3625             Format => 'int32u',
3626             Writable => 'int32s', #27
3627             },
3628             cmID => 'ComposerID', #28 (need sample to get format)
3629             cprt => { Name => 'Copyright', Groups => { 2 => 'Author' } },
3630             dscp => { Name => 'Description', Avoid => 1 },
3631             desc => { Name => 'Description', Avoid => 1 }, #7
3632             gnre => { #10
3633             Name => 'Genre',
3634             Avoid => 1,
3635             # (Note: see https://exiftool.org/forum/index.php?topic=11537.0)
3636             Format => 'undef',
3637             ValueConv => 'unpack("n",$val)',
3638             ValueConvInv => '$val =~ /^\d+$/ ? pack("n",$val) : undef',
3639             PrintConv => q{
3640             return $val unless $val =~ /^\d+$/;
3641             require Image::ExifTool::ID3;
3642             Image::ExifTool::ID3::PrintGenre($val - 1); # note the "- 1"
3643             },
3644             PrintConvInv => q{
3645             return $val if $val =~ /^[0-9]+$/;
3646             require Image::ExifTool::ID3;
3647             my $id = Image::ExifTool::ID3::GetGenreID($val);
3648             return unless defined $id and $id =~ /^\d+$/;
3649             return $id + 1;
3650             },
3651             },
3652             egid => 'EpisodeGlobalUniqueID', #7
3653             geID => { #10
3654             Name => 'GenreID',
3655             Format => 'int32u',
3656             Writable => 'int32s', #27
3657             SeparateTable => 1,
3658             # the following lookup is based on http://itunes.apple.com/WebObjects/MZStoreServices.woa/ws/genres
3659             # (see scripts/parse_genre to parse genre JSON file from above)
3660             PrintConv => { #21/PH
3661             2 => 'Music|Blues',
3662             3 => 'Music|Comedy',
3663             4 => "Music|Children's Music",
3664             5 => 'Music|Classical',
3665             6 => 'Music|Country',
3666             7 => 'Music|Electronic',
3667             8 => 'Music|Holiday',
3668             9 => 'Music|Classical|Opera',
3669             10 => 'Music|Singer/Songwriter',
3670             11 => 'Music|Jazz',
3671             12 => 'Music|Latino',
3672             13 => 'Music|New Age',
3673             14 => 'Music|Pop',
3674             15 => 'Music|R&B/Soul',
3675             16 => 'Music|Soundtrack',
3676             17 => 'Music|Dance',
3677             18 => 'Music|Hip-Hop/Rap',
3678             19 => 'Music|World',
3679             20 => 'Music|Alternative',
3680             21 => 'Music|Rock',
3681             22 => 'Music|Christian & Gospel',
3682             23 => 'Music|Vocal',
3683             24 => 'Music|Reggae',
3684             25 => 'Music|Easy Listening',
3685             26 => 'Podcasts',
3686             27 => 'Music|J-Pop',
3687             28 => 'Music|Enka',
3688             29 => 'Music|Anime',
3689             30 => 'Music|Kayokyoku',
3690             31 => 'Music Videos',
3691             32 => 'TV Shows',
3692             33 => 'Movies',
3693             34 => 'Music',
3694             35 => 'iPod Games',
3695             36 => 'App Store',
3696             37 => 'Tones',
3697             38 => 'Books',
3698             39 => 'Mac App Store',
3699             40 => 'Textbooks',
3700             50 => 'Music|Fitness & Workout',
3701             51 => 'Music|Pop|K-Pop',
3702             52 => 'Music|Karaoke',
3703             53 => 'Music|Instrumental',
3704             74 => 'Audiobooks|News',
3705             75 => 'Audiobooks|Programs & Performances',
3706             500 => 'Fitness Music',
3707             501 => 'Fitness Music|Pop',
3708             502 => 'Fitness Music|Dance',
3709             503 => 'Fitness Music|Hip-Hop',
3710             504 => 'Fitness Music|Rock',
3711             505 => 'Fitness Music|Alt/Indie',
3712             506 => 'Fitness Music|Latino',
3713             507 => 'Fitness Music|Country',
3714             508 => 'Fitness Music|World',
3715             509 => 'Fitness Music|New Age',
3716             510 => 'Fitness Music|Classical',
3717             1001 => 'Music|Alternative|College Rock',
3718             1002 => 'Music|Alternative|Goth Rock',
3719             1003 => 'Music|Alternative|Grunge',
3720             1004 => 'Music|Alternative|Indie Rock',
3721             1005 => 'Music|Alternative|New Wave',
3722             1006 => 'Music|Alternative|Punk',
3723             1007 => 'Music|Blues|Chicago Blues',
3724             1009 => 'Music|Blues|Classic Blues',
3725             1010 => 'Music|Blues|Contemporary Blues',
3726             1011 => 'Music|Blues|Country Blues',
3727             1012 => 'Music|Blues|Delta Blues',
3728             1013 => 'Music|Blues|Electric Blues',
3729             1014 => "Music|Children's Music|Lullabies",
3730             1015 => "Music|Children's Music|Sing-Along",
3731             1016 => "Music|Children's Music|Stories",
3732             1017 => 'Music|Classical|Avant-Garde',
3733             1018 => 'Music|Classical|Baroque Era',
3734             1019 => 'Music|Classical|Chamber Music',
3735             1020 => 'Music|Classical|Chant',
3736             1021 => 'Music|Classical|Choral',
3737             1022 => 'Music|Classical|Classical Crossover',
3738             1023 => 'Music|Classical|Early Music',
3739             1024 => 'Music|Classical|Impressionist',
3740             1025 => 'Music|Classical|Medieval Era',
3741             1026 => 'Music|Classical|Minimalism',
3742             1027 => 'Music|Classical|Modern Era',
3743             1028 => 'Music|Classical|Opera',
3744             1029 => 'Music|Classical|Orchestral',
3745             1030 => 'Music|Classical|Renaissance',
3746             1031 => 'Music|Classical|Romantic Era',
3747             1032 => 'Music|Classical|Wedding Music',
3748             1033 => 'Music|Country|Alternative Country',
3749             1034 => 'Music|Country|Americana',
3750             1035 => 'Music|Country|Bluegrass',
3751             1036 => 'Music|Country|Contemporary Bluegrass',
3752             1037 => 'Music|Country|Contemporary Country',
3753             1038 => 'Music|Country|Country Gospel',
3754             1039 => 'Music|Country|Honky Tonk',
3755             1040 => 'Music|Country|Outlaw Country',
3756             1041 => 'Music|Country|Traditional Bluegrass',
3757             1042 => 'Music|Country|Traditional Country',
3758             1043 => 'Music|Country|Urban Cowboy',
3759             1044 => 'Music|Dance|Breakbeat',
3760             1045 => 'Music|Dance|Exercise',
3761             1046 => 'Music|Dance|Garage',
3762             1047 => 'Music|Dance|Hardcore',
3763             1048 => 'Music|Dance|House',
3764             1049 => "Music|Dance|Jungle/Drum'n'bass",
3765             1050 => 'Music|Dance|Techno',
3766             1051 => 'Music|Dance|Trance',
3767             1052 => 'Music|Jazz|Big Band',
3768             1053 => 'Music|Jazz|Bop',
3769             1054 => 'Music|Easy Listening|Lounge',
3770             1055 => 'Music|Easy Listening|Swing',
3771             1056 => 'Music|Electronic|Ambient',
3772             1057 => 'Music|Electronic|Downtempo',
3773             1058 => 'Music|Electronic|Electronica',
3774             1060 => 'Music|Electronic|IDM/Experimental',
3775             1061 => 'Music|Electronic|Industrial',
3776             1062 => 'Music|Singer/Songwriter|Alternative Folk',
3777             1063 => 'Music|Singer/Songwriter|Contemporary Folk',
3778             1064 => 'Music|Singer/Songwriter|Contemporary Singer/Songwriter',
3779             1065 => 'Music|Singer/Songwriter|Folk-Rock',
3780             1066 => 'Music|Singer/Songwriter|New Acoustic',
3781             1067 => 'Music|Singer/Songwriter|Traditional Folk',
3782             1068 => 'Music|Hip-Hop/Rap|Alternative Rap',
3783             1069 => 'Music|Hip-Hop/Rap|Dirty South',
3784             1070 => 'Music|Hip-Hop/Rap|East Coast Rap',
3785             1071 => 'Music|Hip-Hop/Rap|Gangsta Rap',
3786             1072 => 'Music|Hip-Hop/Rap|Hardcore Rap',
3787             1073 => 'Music|Hip-Hop/Rap|Hip-Hop',
3788             1074 => 'Music|Hip-Hop/Rap|Latin Rap',
3789             1075 => 'Music|Hip-Hop/Rap|Old School Rap',
3790             1076 => 'Music|Hip-Hop/Rap|Rap',
3791             1077 => 'Music|Hip-Hop/Rap|Underground Rap',
3792             1078 => 'Music|Hip-Hop/Rap|West Coast Rap',
3793             1079 => 'Music|Holiday|Chanukah',
3794             1080 => 'Music|Holiday|Christmas',
3795             1081 => "Music|Holiday|Christmas: Children's",
3796             1082 => 'Music|Holiday|Christmas: Classic',
3797             1083 => 'Music|Holiday|Christmas: Classical',
3798             1084 => 'Music|Holiday|Christmas: Jazz',
3799             1085 => 'Music|Holiday|Christmas: Modern',
3800             1086 => 'Music|Holiday|Christmas: Pop',
3801             1087 => 'Music|Holiday|Christmas: R&B',
3802             1088 => 'Music|Holiday|Christmas: Religious',
3803             1089 => 'Music|Holiday|Christmas: Rock',
3804             1090 => 'Music|Holiday|Easter',
3805             1091 => 'Music|Holiday|Halloween',
3806             1092 => 'Music|Holiday|Holiday: Other',
3807             1093 => 'Music|Holiday|Thanksgiving',
3808             1094 => 'Music|Christian & Gospel|CCM',
3809             1095 => 'Music|Christian & Gospel|Christian Metal',
3810             1096 => 'Music|Christian & Gospel|Christian Pop',
3811             1097 => 'Music|Christian & Gospel|Christian Rap',
3812             1098 => 'Music|Christian & Gospel|Christian Rock',
3813             1099 => 'Music|Christian & Gospel|Classic Christian',
3814             1100 => 'Music|Christian & Gospel|Contemporary Gospel',
3815             1101 => 'Music|Christian & Gospel|Gospel',
3816             1103 => 'Music|Christian & Gospel|Praise & Worship',
3817             1104 => 'Music|Christian & Gospel|Southern Gospel',
3818             1105 => 'Music|Christian & Gospel|Traditional Gospel',
3819             1106 => 'Music|Jazz|Avant-Garde Jazz',
3820             1107 => 'Music|Jazz|Contemporary Jazz',
3821             1108 => 'Music|Jazz|Crossover Jazz',
3822             1109 => 'Music|Jazz|Dixieland',
3823             1110 => 'Music|Jazz|Fusion',
3824             1111 => 'Music|Jazz|Latin Jazz',
3825             1112 => 'Music|Jazz|Mainstream Jazz',
3826             1113 => 'Music|Jazz|Ragtime',
3827             1114 => 'Music|Jazz|Smooth Jazz',
3828             1115 => 'Music|Latino|Latin Jazz',
3829             1116 => 'Music|Latino|Contemporary Latin',
3830             1117 => 'Music|Latino|Pop Latino',
3831             1118 => 'Music|Latino|Raices', # (Raíces)
3832             1119 => 'Music|Latino|Urbano latino',
3833             1120 => 'Music|Latino|Baladas y Boleros',
3834             1121 => 'Music|Latino|Rock y Alternativo',
3835             1122 => 'Music|Brazilian',
3836             1123 => 'Music|Latino|Musica Mexicana', # (Música Mexicana)
3837             1124 => 'Music|Latino|Musica tropical', # (Música tropical)
3838             1125 => 'Music|New Age|Environmental',
3839             1126 => 'Music|New Age|Healing',
3840             1127 => 'Music|New Age|Meditation',
3841             1128 => 'Music|New Age|Nature',
3842             1129 => 'Music|New Age|Relaxation',
3843             1130 => 'Music|New Age|Travel',
3844             1131 => 'Music|Pop|Adult Contemporary',
3845             1132 => 'Music|Pop|Britpop',
3846             1133 => 'Music|Pop|Pop/Rock',
3847             1134 => 'Music|Pop|Soft Rock',
3848             1135 => 'Music|Pop|Teen Pop',
3849             1136 => 'Music|R&B/Soul|Contemporary R&B',
3850             1137 => 'Music|R&B/Soul|Disco',
3851             1138 => 'Music|R&B/Soul|Doo Wop',
3852             1139 => 'Music|R&B/Soul|Funk',
3853             1140 => 'Music|R&B/Soul|Motown',
3854             1141 => 'Music|R&B/Soul|Neo-Soul',
3855             1142 => 'Music|R&B/Soul|Quiet Storm',
3856             1143 => 'Music|R&B/Soul|Soul',
3857             1144 => 'Music|Rock|Adult Alternative',
3858             1145 => 'Music|Rock|American Trad Rock',
3859             1146 => 'Music|Rock|Arena Rock',
3860             1147 => 'Music|Rock|Blues-Rock',
3861             1148 => 'Music|Rock|British Invasion',
3862             1149 => 'Music|Rock|Death Metal/Black Metal',
3863             1150 => 'Music|Rock|Glam Rock',
3864             1151 => 'Music|Rock|Hair Metal',
3865             1152 => 'Music|Rock|Hard Rock',
3866             1153 => 'Music|Rock|Metal',
3867             1154 => 'Music|Rock|Jam Bands',
3868             1155 => 'Music|Rock|Prog-Rock/Art Rock',
3869             1156 => 'Music|Rock|Psychedelic',
3870             1157 => 'Music|Rock|Rock & Roll',
3871             1158 => 'Music|Rock|Rockabilly',
3872             1159 => 'Music|Rock|Roots Rock',
3873             1160 => 'Music|Rock|Singer/Songwriter',
3874             1161 => 'Music|Rock|Southern Rock',
3875             1162 => 'Music|Rock|Surf',
3876             1163 => 'Music|Rock|Tex-Mex',
3877             1165 => 'Music|Soundtrack|Foreign Cinema',
3878             1166 => 'Music|Soundtrack|Musicals',
3879             1167 => 'Music|Comedy|Novelty',
3880             1168 => 'Music|Soundtrack|Original Score',
3881             1169 => 'Music|Soundtrack|Soundtrack',
3882             1171 => 'Music|Comedy|Standup Comedy',
3883             1172 => 'Music|Soundtrack|TV Soundtrack',
3884             1173 => 'Music|Vocal|Standards',
3885             1174 => 'Music|Vocal|Traditional Pop',
3886             1175 => 'Music|Jazz|Vocal Jazz',
3887             1176 => 'Music|Vocal|Vocal Pop',
3888             1177 => 'Music|African|Afro-Beat',
3889             1178 => 'Music|African|Afro-Pop',
3890             1179 => 'Music|World|Cajun',
3891             1180 => 'Music|World|Celtic',
3892             1181 => 'Music|World|Celtic Folk',
3893             1182 => 'Music|World|Contemporary Celtic',
3894             1183 => 'Music|Reggae|Modern Dancehall',
3895             1184 => 'Music|World|Drinking Songs',
3896             1185 => 'Music|Indian|Indian Pop',
3897             1186 => 'Music|World|Japanese Pop',
3898             1187 => 'Music|World|Klezmer',
3899             1188 => 'Music|World|Polka',
3900             1189 => 'Music|World|Traditional Celtic',
3901             1190 => 'Music|World|Worldbeat',
3902             1191 => 'Music|World|Zydeco',
3903             1192 => 'Music|Reggae|Roots Reggae',
3904             1193 => 'Music|Reggae|Dub',
3905             1194 => 'Music|Reggae|Ska',
3906             1195 => 'Music|World|Caribbean',
3907             1196 => 'Music|World|South America',
3908             1197 => 'Music|Arabic',
3909             1198 => 'Music|World|North America',
3910             1199 => 'Music|World|Hawaii',
3911             1200 => 'Music|World|Australia',
3912             1201 => 'Music|World|Japan',
3913             1202 => 'Music|World|France',
3914             1203 => 'Music|African',
3915             1204 => 'Music|World|Asia',
3916             1205 => 'Music|World|Europe',
3917             1206 => 'Music|World|South Africa',
3918             1207 => 'Music|Jazz|Hard Bop',
3919             1208 => 'Music|Jazz|Trad Jazz',
3920             1209 => 'Music|Jazz|Cool Jazz',
3921             1210 => 'Music|Blues|Acoustic Blues',
3922             1211 => 'Music|Classical|High Classical',
3923             1220 => 'Music|Brazilian|Axe', # (Axé)
3924             1221 => 'Music|Brazilian|Bossa Nova',
3925             1222 => 'Music|Brazilian|Choro',
3926             1223 => 'Music|Brazilian|Forro', # (Forró)
3927             1224 => 'Music|Brazilian|Frevo',
3928             1225 => 'Music|Brazilian|MPB',
3929             1226 => 'Music|Brazilian|Pagode',
3930             1227 => 'Music|Brazilian|Samba',
3931             1228 => 'Music|Brazilian|Sertanejo',
3932             1229 => 'Music|Brazilian|Baile Funk',
3933             1230 => 'Music|Alternative|Chinese Alt',
3934             1231 => 'Music|Alternative|Korean Indie',
3935             1232 => 'Music|Chinese',
3936             1233 => 'Music|Chinese|Chinese Classical',
3937             1234 => 'Music|Chinese|Chinese Flute',
3938             1235 => 'Music|Chinese|Chinese Opera',
3939             1236 => 'Music|Chinese|Chinese Orchestral',
3940             1237 => 'Music|Chinese|Chinese Regional Folk',
3941             1238 => 'Music|Chinese|Chinese Strings',
3942             1239 => 'Music|Chinese|Taiwanese Folk',
3943             1240 => 'Music|Chinese|Tibetan Native Music',
3944             1241 => 'Music|Hip-Hop/Rap|Chinese Hip-Hop',
3945             1242 => 'Music|Hip-Hop/Rap|Korean Hip-Hop',
3946             1243 => 'Music|Korean',
3947             1244 => 'Music|Korean|Korean Classical',
3948             1245 => 'Music|Korean|Korean Trad Song',
3949             1246 => 'Music|Korean|Korean Trad Instrumental',
3950             1247 => 'Music|Korean|Korean Trad Theater',
3951             1248 => 'Music|Rock|Chinese Rock',
3952             1249 => 'Music|Rock|Korean Rock',
3953             1250 => 'Music|Pop|C-Pop',
3954             1251 => 'Music|Pop|Cantopop/HK-Pop',
3955             1252 => 'Music|Pop|Korean Folk-Pop',
3956             1253 => 'Music|Pop|Mandopop',
3957             1254 => 'Music|Pop|Tai-Pop',
3958             1255 => 'Music|Pop|Malaysian Pop',
3959             1256 => 'Music|Pop|Pinoy Pop',
3960             1257 => 'Music|Pop|Original Pilipino Music',
3961             1258 => 'Music|Pop|Manilla Sound',
3962             1259 => 'Music|Pop|Indo Pop',
3963             1260 => 'Music|Pop|Thai Pop',
3964             1261 => 'Music|Vocal|Trot',
3965             1262 => 'Music|Indian',
3966             1263 => 'Music|Indian|Bollywood',
3967             1264 => 'Music|Indian|Regional Indian|Tamil',
3968             1265 => 'Music|Indian|Regional Indian|Telugu',
3969             1266 => 'Music|Indian|Regional Indian',
3970             1267 => 'Music|Indian|Devotional & Spiritual',
3971             1268 => 'Music|Indian|Sufi',
3972             1269 => 'Music|Indian|Indian Classical',
3973             1270 => 'Music|Russian|Russian Chanson',
3974             1271 => 'Music|World|Dini',
3975             1272 => 'Music|Turkish|Halk',
3976             1273 => 'Music|Turkish|Sanat',
3977             1274 => 'Music|World|Dangdut',
3978             1275 => 'Music|World|Indonesian Religious',
3979             1276 => 'Music|World|Calypso',
3980             1277 => 'Music|World|Soca',
3981             1278 => 'Music|Indian|Ghazals',
3982             1279 => 'Music|Indian|Indian Folk',
3983             1280 => 'Music|Turkish|Arabesque',
3984             1281 => 'Music|African|Afrikaans',
3985             1282 => 'Music|World|Farsi',
3986             1283 => 'Music|World|Israeli',
3987             1284 => 'Music|Arabic|Khaleeji',
3988             1285 => 'Music|Arabic|North African',
3989             1286 => 'Music|Arabic|Arabic Pop',
3990             1287 => 'Music|Arabic|Islamic',
3991             1288 => 'Music|Soundtrack|Sound Effects',
3992             1289 => 'Music|Folk',
3993             1290 => 'Music|Orchestral',
3994             1291 => 'Music|Marching',
3995             1293 => 'Music|Pop|Oldies',
3996             1294 => 'Music|Country|Thai Country',
3997             1295 => 'Music|World|Flamenco',
3998             1296 => 'Music|World|Tango',
3999             1297 => 'Music|World|Fado',
4000             1298 => 'Music|World|Iberia',
4001             1299 => 'Music|Russian',
4002             1300 => 'Music|Turkish',
4003             1301 => 'Podcasts|Arts',
4004             1302 => 'Podcasts|Society & Culture|Personal Journals',
4005             1303 => 'Podcasts|Comedy',
4006             1304 => 'Podcasts|Education',
4007             1305 => 'Podcasts|Kids & Family',
4008             1306 => 'Podcasts|Arts|Food',
4009             1307 => 'Podcasts|Health',
4010             1309 => 'Podcasts|TV & Film',
4011             1310 => 'Podcasts|Music',
4012             1311 => 'Podcasts|News & Politics',
4013             1314 => 'Podcasts|Religion & Spirituality',
4014             1315 => 'Podcasts|Science & Medicine',
4015             1316 => 'Podcasts|Sports & Recreation',
4016             1318 => 'Podcasts|Technology',
4017             1320 => 'Podcasts|Society & Culture|Places & Travel',
4018             1321 => 'Podcasts|Business',
4019             1323 => 'Podcasts|Games & Hobbies',
4020             1324 => 'Podcasts|Society & Culture',
4021             1325 => 'Podcasts|Government & Organizations',
4022             1337 => 'Music Videos|Classical|Piano',
4023             1401 => 'Podcasts|Arts|Literature',
4024             1402 => 'Podcasts|Arts|Design',
4025             1404 => 'Podcasts|Games & Hobbies|Video Games',
4026             1405 => 'Podcasts|Arts|Performing Arts',
4027             1406 => 'Podcasts|Arts|Visual Arts',
4028             1410 => 'Podcasts|Business|Careers',
4029             1412 => 'Podcasts|Business|Investing',
4030             1413 => 'Podcasts|Business|Management & Marketing',
4031             1415 => 'Podcasts|Education|K-12',
4032             1416 => 'Podcasts|Education|Higher Education',
4033             1417 => 'Podcasts|Health|Fitness & Nutrition',
4034             1420 => 'Podcasts|Health|Self-Help',
4035             1421 => 'Podcasts|Health|Sexuality',
4036             1438 => 'Podcasts|Religion & Spirituality|Buddhism',
4037             1439 => 'Podcasts|Religion & Spirituality|Christianity',
4038             1440 => 'Podcasts|Religion & Spirituality|Islam',
4039             1441 => 'Podcasts|Religion & Spirituality|Judaism',
4040             1443 => 'Podcasts|Society & Culture|Philosophy',
4041             1444 => 'Podcasts|Religion & Spirituality|Spirituality',
4042             1446 => 'Podcasts|Technology|Gadgets',
4043             1448 => 'Podcasts|Technology|Tech News',
4044             1450 => 'Podcasts|Technology|Podcasting',
4045             1454 => 'Podcasts|Games & Hobbies|Automotive',
4046             1455 => 'Podcasts|Games & Hobbies|Aviation',
4047             1456 => 'Podcasts|Sports & Recreation|Outdoor',
4048             1459 => 'Podcasts|Arts|Fashion & Beauty',
4049             1460 => 'Podcasts|Games & Hobbies|Hobbies',
4050             1461 => 'Podcasts|Games & Hobbies|Other Games',
4051             1462 => 'Podcasts|Society & Culture|History',
4052             1463 => 'Podcasts|Religion & Spirituality|Hinduism',
4053             1464 => 'Podcasts|Religion & Spirituality|Other',
4054             1465 => 'Podcasts|Sports & Recreation|Professional',
4055             1466 => 'Podcasts|Sports & Recreation|College & High School',
4056             1467 => 'Podcasts|Sports & Recreation|Amateur',
4057             1468 => 'Podcasts|Education|Educational Technology',
4058             1469 => 'Podcasts|Education|Language Courses',
4059             1470 => 'Podcasts|Education|Training',
4060             1471 => 'Podcasts|Business|Business News',
4061             1472 => 'Podcasts|Business|Shopping',
4062             1473 => 'Podcasts|Government & Organizations|National',
4063             1474 => 'Podcasts|Government & Organizations|Regional',
4064             1475 => 'Podcasts|Government & Organizations|Local',
4065             1476 => 'Podcasts|Government & Organizations|Non-Profit',
4066             1477 => 'Podcasts|Science & Medicine|Natural Sciences',
4067             1478 => 'Podcasts|Science & Medicine|Medicine',
4068             1479 => 'Podcasts|Science & Medicine|Social Sciences',
4069             1480 => 'Podcasts|Technology|Software How-To',
4070             1481 => 'Podcasts|Health|Alternative Health',
4071             1482 => 'Podcasts|Arts|Books',
4072             1483 => 'Podcasts|Fiction',
4073             1484 => 'Podcasts|Fiction|Drama',
4074             1485 => 'Podcasts|Fiction|Science Fiction',
4075             1486 => 'Podcasts|Fiction|Comedy Fiction',
4076             1487 => 'Podcasts|History',
4077             1488 => 'Podcasts|True Crime',
4078             1489 => 'Podcasts|News',
4079             1490 => 'Podcasts|News|Business News',
4080             1491 => 'Podcasts|Business|Management',
4081             1492 => 'Podcasts|Business|Marketing',
4082             1493 => 'Podcasts|Business|Entrepreneurship',
4083             1494 => 'Podcasts|Business|Non-Profit',
4084             1495 => 'Podcasts|Comedy|Improv',
4085             1496 => 'Podcasts|Comedy|Comedy Interviews',
4086             1497 => 'Podcasts|Comedy|Stand-Up',
4087             1498 => 'Podcasts|Education|Language Learning',
4088             1499 => 'Podcasts|Education|How To',
4089             1500 => 'Podcasts|Education|Self-Improvement',
4090             1501 => 'Podcasts|Education|Courses',
4091             1502 => 'Podcasts|Leisure',
4092             1503 => 'Podcasts|Leisure|Automotive',
4093             1504 => 'Podcasts|Leisure|Aviation',
4094             1505 => 'Podcasts|Leisure|Hobbies',
4095             1506 => 'Podcasts|Leisure|Crafts',
4096             1507 => 'Podcasts|Leisure|Games',
4097             1508 => 'Podcasts|Leisure|Home & Garden',
4098             1509 => 'Podcasts|Leisure|Video Games',
4099             1510 => 'Podcasts|Leisure|Animation & Manga',
4100             1511 => 'Podcasts|Government',
4101             1512 => 'Podcasts|Health & Fitness',
4102             1513 => 'Podcasts|Health & Fitness|Alternative Health',
4103             1514 => 'Podcasts|Health & Fitness|Fitness',
4104             1515 => 'Podcasts|Health & Fitness|Nutrition',
4105             1516 => 'Podcasts|Health & Fitness|Sexuality',
4106             1517 => 'Podcasts|Health & Fitness|Mental Health',
4107             1518 => 'Podcasts|Health & Fitness|Medicine',
4108             1519 => 'Podcasts|Kids & Family|Education for Kids',
4109             1520 => 'Podcasts|Kids & Family|Stories for Kids',
4110             1521 => 'Podcasts|Kids & Family|Parenting',
4111             1522 => 'Podcasts|Kids & Family|Pets & Animals',
4112             1523 => 'Podcasts|Music|Music Commentary',
4113             1524 => 'Podcasts|Music|Music History',
4114             1525 => 'Podcasts|Music|Music Interviews',
4115             1526 => 'Podcasts|News|Daily News',
4116             1527 => 'Podcasts|News|Politics',
4117             1528 => 'Podcasts|News|Tech News',
4118             1529 => 'Podcasts|News|Sports News',
4119             1530 => 'Podcasts|News|News Commentary',
4120             1531 => 'Podcasts|News|Entertainment News',
4121             1532 => 'Podcasts|Religion & Spirituality|Religion',
4122             1533 => 'Podcasts|Science',
4123             1534 => 'Podcasts|Science|Natural Sciences',
4124             1535 => 'Podcasts|Science|Social Sciences',
4125             1536 => 'Podcasts|Science|Mathematics',
4126             1537 => 'Podcasts|Science|Nature',
4127             1538 => 'Podcasts|Science|Astronomy',
4128             1539 => 'Podcasts|Science|Chemistry',
4129             1540 => 'Podcasts|Science|Earth Sciences',
4130             1541 => 'Podcasts|Science|Life Sciences',
4131             1542 => 'Podcasts|Science|Physics',
4132             1543 => 'Podcasts|Society & Culture|Documentary',
4133             1544 => 'Podcasts|Society & Culture|Relationships',
4134             1545 => 'Podcasts|Sports',
4135             1546 => 'Podcasts|Sports|Soccer',
4136             1547 => 'Podcasts|Sports|Football',
4137             1548 => 'Podcasts|Sports|Basketball',
4138             1549 => 'Podcasts|Sports|Baseball',
4139             1550 => 'Podcasts|Sports|Hockey',
4140             1551 => 'Podcasts|Sports|Running',
4141             1552 => 'Podcasts|Sports|Rugby',
4142             1553 => 'Podcasts|Sports|Golf',
4143             1554 => 'Podcasts|Sports|Cricket',
4144             1555 => 'Podcasts|Sports|Wrestling',
4145             1556 => 'Podcasts|Sports|Tennis',
4146             1557 => 'Podcasts|Sports|Volleyball',
4147             1558 => 'Podcasts|Sports|Swimming',
4148             1559 => 'Podcasts|Sports|Wilderness',
4149             1560 => 'Podcasts|Sports|Fantasy Sports',
4150             1561 => 'Podcasts|TV & Film|TV Reviews',
4151             1562 => 'Podcasts|TV & Film|After Shows',
4152             1563 => 'Podcasts|TV & Film|Film Reviews',
4153             1564 => 'Podcasts|TV & Film|Film History',
4154             1565 => 'Podcasts|TV & Film|Film Interviews',
4155             1602 => 'Music Videos|Blues',
4156             1603 => 'Music Videos|Comedy',
4157             1604 => "Music Videos|Children's Music",
4158             1605 => 'Music Videos|Classical',
4159             1606 => 'Music Videos|Country',
4160             1607 => 'Music Videos|Electronic',
4161             1608 => 'Music Videos|Holiday',
4162             1609 => 'Music Videos|Classical|Opera',
4163             1610 => 'Music Videos|Singer/Songwriter',
4164             1611 => 'Music Videos|Jazz',
4165             1612 => 'Music Videos|Latin',
4166             1613 => 'Music Videos|New Age',
4167             1614 => 'Music Videos|Pop',
4168             1615 => 'Music Videos|R&B/Soul',
4169             1616 => 'Music Videos|Soundtrack',
4170             1617 => 'Music Videos|Dance',
4171             1618 => 'Music Videos|Hip-Hop/Rap',
4172             1619 => 'Music Videos|World',
4173             1620 => 'Music Videos|Alternative',
4174             1621 => 'Music Videos|Rock',
4175             1622 => 'Music Videos|Christian & Gospel',
4176             1623 => 'Music Videos|Vocal',
4177             1624 => 'Music Videos|Reggae',
4178             1625 => 'Music Videos|Easy Listening',
4179             1626 => 'Music Videos|Podcasts',
4180             1627 => 'Music Videos|J-Pop',
4181             1628 => 'Music Videos|Enka',
4182             1629 => 'Music Videos|Anime',
4183             1630 => 'Music Videos|Kayokyoku',
4184             1631 => 'Music Videos|Disney',
4185             1632 => 'Music Videos|French Pop',
4186             1633 => 'Music Videos|German Pop',
4187             1634 => 'Music Videos|German Folk',
4188             1635 => 'Music Videos|Alternative|Chinese Alt',
4189             1636 => 'Music Videos|Alternative|Korean Indie',
4190             1637 => 'Music Videos|Chinese',
4191             1638 => 'Music Videos|Chinese|Chinese Classical',
4192             1639 => 'Music Videos|Chinese|Chinese Flute',
4193             1640 => 'Music Videos|Chinese|Chinese Opera',
4194             1641 => 'Music Videos|Chinese|Chinese Orchestral',
4195             1642 => 'Music Videos|Chinese|Chinese Regional Folk',
4196             1643 => 'Music Videos|Chinese|Chinese Strings',
4197             1644 => 'Music Videos|Chinese|Taiwanese Folk',
4198             1645 => 'Music Videos|Chinese|Tibetan Native Music',
4199             1646 => 'Music Videos|Hip-Hop/Rap|Chinese Hip-Hop',
4200             1647 => 'Music Videos|Hip-Hop/Rap|Korean Hip-Hop',
4201             1648 => 'Music Videos|Korean',
4202             1649 => 'Music Videos|Korean|Korean Classical',
4203             1650 => 'Music Videos|Korean|Korean Trad Song',
4204             1651 => 'Music Videos|Korean|Korean Trad Instrumental',
4205             1652 => 'Music Videos|Korean|Korean Trad Theater',
4206             1653 => 'Music Videos|Rock|Chinese Rock',
4207             1654 => 'Music Videos|Rock|Korean Rock',
4208             1655 => 'Music Videos|Pop|C-Pop',
4209             1656 => 'Music Videos|Pop|Cantopop/HK-Pop',
4210             1657 => 'Music Videos|Pop|Korean Folk-Pop',
4211             1658 => 'Music Videos|Pop|Mandopop',
4212             1659 => 'Music Videos|Pop|Tai-Pop',
4213             1660 => 'Music Videos|Pop|Malaysian Pop',
4214             1661 => 'Music Videos|Pop|Pinoy Pop',
4215             1662 => 'Music Videos|Pop|Original Pilipino Music',
4216             1663 => 'Music Videos|Pop|Manilla Sound',
4217             1664 => 'Music Videos|Pop|Indo Pop',
4218             1665 => 'Music Videos|Pop|Thai Pop',
4219             1666 => 'Music Videos|Vocal|Trot',
4220             1671 => 'Music Videos|Brazilian',
4221             1672 => 'Music Videos|Brazilian|Axe', # (Axé)
4222             1673 => 'Music Videos|Brazilian|Baile Funk',
4223             1674 => 'Music Videos|Brazilian|Bossa Nova',
4224             1675 => 'Music Videos|Brazilian|Choro',
4225             1676 => 'Music Videos|Brazilian|Forro',
4226             1677 => 'Music Videos|Brazilian|Frevo',
4227             1678 => 'Music Videos|Brazilian|MPB',
4228             1679 => 'Music Videos|Brazilian|Pagode',
4229             1680 => 'Music Videos|Brazilian|Samba',
4230             1681 => 'Music Videos|Brazilian|Sertanejo',
4231             1682 => 'Music Videos|Classical|High Classical',
4232             1683 => 'Music Videos|Fitness & Workout',
4233             1684 => 'Music Videos|Instrumental',
4234             1685 => 'Music Videos|Jazz|Big Band',
4235             1686 => 'Music Videos|Pop|K-Pop',
4236             1687 => 'Music Videos|Karaoke',
4237             1688 => 'Music Videos|Rock|Heavy Metal',
4238             1689 => 'Music Videos|Spoken Word',
4239             1690 => 'Music Videos|Indian',
4240             1691 => 'Music Videos|Indian|Bollywood',
4241             1692 => 'Music Videos|Indian|Regional Indian|Tamil',
4242             1693 => 'Music Videos|Indian|Regional Indian|Telugu',
4243             1694 => 'Music Videos|Indian|Regional Indian',
4244             1695 => 'Music Videos|Indian|Devotional & Spiritual',
4245             1696 => 'Music Videos|Indian|Sufi',
4246             1697 => 'Music Videos|Indian|Indian Classical',
4247             1698 => 'Music Videos|Russian|Russian Chanson',
4248             1699 => 'Music Videos|World|Dini',
4249             1700 => 'Music Videos|Turkish|Halk',
4250             1701 => 'Music Videos|Turkish|Sanat',
4251             1702 => 'Music Videos|World|Dangdut',
4252             1703 => 'Music Videos|World|Indonesian Religious',
4253             1704 => 'Music Videos|Indian|Indian Pop',
4254             1705 => 'Music Videos|World|Calypso',
4255             1706 => 'Music Videos|World|Soca',
4256             1707 => 'Music Videos|Indian|Ghazals',
4257             1708 => 'Music Videos|Indian|Indian Folk',
4258             1709 => 'Music Videos|Turkish|Arabesque',
4259             1710 => 'Music Videos|African|Afrikaans',
4260             1711 => 'Music Videos|World|Farsi',
4261             1712 => 'Music Videos|World|Israeli',
4262             1713 => 'Music Videos|Arabic',
4263             1714 => 'Music Videos|Arabic|Khaleeji',
4264             1715 => 'Music Videos|Arabic|North African',
4265             1716 => 'Music Videos|Arabic|Arabic Pop',
4266             1717 => 'Music Videos|Arabic|Islamic',
4267             1718 => 'Music Videos|Soundtrack|Sound Effects',
4268             1719 => 'Music Videos|Folk',
4269             1720 => 'Music Videos|Orchestral',
4270             1721 => 'Music Videos|Marching',
4271             1723 => 'Music Videos|Pop|Oldies',
4272             1724 => 'Music Videos|Country|Thai Country',
4273             1725 => 'Music Videos|World|Flamenco',
4274             1726 => 'Music Videos|World|Tango',
4275             1727 => 'Music Videos|World|Fado',
4276             1728 => 'Music Videos|World|Iberia',
4277             1729 => 'Music Videos|Russian',
4278             1730 => 'Music Videos|Turkish',
4279             1731 => 'Music Videos|Alternative|College Rock',
4280             1732 => 'Music Videos|Alternative|Goth Rock',
4281             1733 => 'Music Videos|Alternative|Grunge',
4282             1734 => 'Music Videos|Alternative|Indie Rock',
4283             1735 => 'Music Videos|Alternative|New Wave',
4284             1736 => 'Music Videos|Alternative|Punk',
4285             1737 => 'Music Videos|Blues|Acoustic Blues',
4286             1738 => 'Music Videos|Blues|Chicago Blues',
4287             1739 => 'Music Videos|Blues|Classic Blues',
4288             1740 => 'Music Videos|Blues|Contemporary Blues',
4289             1741 => 'Music Videos|Blues|Country Blues',
4290             1742 => 'Music Videos|Blues|Delta Blues',
4291             1743 => 'Music Videos|Blues|Electric Blues',
4292             1744 => "Music Videos|Children's Music|Lullabies",
4293             1745 => "Music Videos|Children's Music|Sing-Along",
4294             1746 => "Music Videos|Children's Music|Stories",
4295             1747 => 'Music Videos|Christian & Gospel|CCM',
4296             1748 => 'Music Videos|Christian & Gospel|Christian Metal',
4297             1749 => 'Music Videos|Christian & Gospel|Christian Pop',
4298             1750 => 'Music Videos|Christian & Gospel|Christian Rap',
4299             1751 => 'Music Videos|Christian & Gospel|Christian Rock',
4300             1752 => 'Music Videos|Christian & Gospel|Classic Christian',
4301             1753 => 'Music Videos|Christian & Gospel|Contemporary Gospel',
4302             1754 => 'Music Videos|Christian & Gospel|Gospel',
4303             1755 => 'Music Videos|Christian & Gospel|Praise & Worship',
4304             1756 => 'Music Videos|Christian & Gospel|Southern Gospel',
4305             1757 => 'Music Videos|Christian & Gospel|Traditional Gospel',
4306             1758 => 'Music Videos|Classical|Avant-Garde',
4307             1759 => 'Music Videos|Classical|Baroque Era',
4308             1760 => 'Music Videos|Classical|Chamber Music',
4309             1761 => 'Music Videos|Classical|Chant',
4310             1762 => 'Music Videos|Classical|Choral',
4311             1763 => 'Music Videos|Classical|Classical Crossover',
4312             1764 => 'Music Videos|Classical|Early Music',
4313             1765 => 'Music Videos|Classical|Impressionist',
4314             1766 => 'Music Videos|Classical|Medieval Era',
4315             1767 => 'Music Videos|Classical|Minimalism',
4316             1768 => 'Music Videos|Classical|Modern Era',
4317             1769 => 'Music Videos|Classical|Orchestral',
4318             1770 => 'Music Videos|Classical|Renaissance',
4319             1771 => 'Music Videos|Classical|Romantic Era',
4320             1772 => 'Music Videos|Classical|Wedding Music',
4321             1773 => 'Music Videos|Comedy|Novelty',
4322             1774 => 'Music Videos|Comedy|Standup Comedy',
4323             1775 => 'Music Videos|Country|Alternative Country',
4324             1776 => 'Music Videos|Country|Americana',
4325             1777 => 'Music Videos|Country|Bluegrass',
4326             1778 => 'Music Videos|Country|Contemporary Bluegrass',
4327             1779 => 'Music Videos|Country|Contemporary Country',
4328             1780 => 'Music Videos|Country|Country Gospel',
4329             1781 => 'Music Videos|Country|Honky Tonk',
4330             1782 => 'Music Videos|Country|Outlaw Country',
4331             1783 => 'Music Videos|Country|Traditional Bluegrass',
4332             1784 => 'Music Videos|Country|Traditional Country',
4333             1785 => 'Music Videos|Country|Urban Cowboy',
4334             1786 => 'Music Videos|Dance|Breakbeat',
4335             1787 => 'Music Videos|Dance|Exercise',
4336             1788 => 'Music Videos|Dance|Garage',
4337             1789 => 'Music Videos|Dance|Hardcore',
4338             1790 => 'Music Videos|Dance|House',
4339             1791 => "Music Videos|Dance|Jungle/Drum'n'bass",
4340             1792 => 'Music Videos|Dance|Techno',
4341             1793 => 'Music Videos|Dance|Trance',
4342             1794 => 'Music Videos|Easy Listening|Lounge',
4343             1795 => 'Music Videos|Easy Listening|Swing',
4344             1796 => 'Music Videos|Electronic|Ambient',
4345             1797 => 'Music Videos|Electronic|Downtempo',
4346             1798 => 'Music Videos|Electronic|Electronica',
4347             1799 => 'Music Videos|Electronic|IDM/Experimental',
4348             1800 => 'Music Videos|Electronic|Industrial',
4349             1801 => 'Music Videos|Hip-Hop/Rap|Alternative Rap',
4350             1802 => 'Music Videos|Hip-Hop/Rap|Dirty South',
4351             1803 => 'Music Videos|Hip-Hop/Rap|East Coast Rap',
4352             1804 => 'Music Videos|Hip-Hop/Rap|Gangsta Rap',
4353             1805 => 'Music Videos|Hip-Hop/Rap|Hardcore Rap',
4354             1806 => 'Music Videos|Hip-Hop/Rap|Hip-Hop',
4355             1807 => 'Music Videos|Hip-Hop/Rap|Latin Rap',
4356             1808 => 'Music Videos|Hip-Hop/Rap|Old School Rap',
4357             1809 => 'Music Videos|Hip-Hop/Rap|Rap',
4358             1810 => 'Music Videos|Hip-Hop/Rap|Underground Rap',
4359             1811 => 'Music Videos|Hip-Hop/Rap|West Coast Rap',
4360             1812 => 'Music Videos|Holiday|Chanukah',
4361             1813 => 'Music Videos|Holiday|Christmas',
4362             1814 => "Music Videos|Holiday|Christmas: Children's",
4363             1815 => 'Music Videos|Holiday|Christmas: Classic',
4364             1816 => 'Music Videos|Holiday|Christmas: Classical',
4365             1817 => 'Music Videos|Holiday|Christmas: Jazz',
4366             1818 => 'Music Videos|Holiday|Christmas: Modern',
4367             1819 => 'Music Videos|Holiday|Christmas: Pop',
4368             1820 => 'Music Videos|Holiday|Christmas: R&B',
4369             1821 => 'Music Videos|Holiday|Christmas: Religious',
4370             1822 => 'Music Videos|Holiday|Christmas: Rock',
4371             1823 => 'Music Videos|Holiday|Easter',
4372             1824 => 'Music Videos|Holiday|Halloween',
4373             1825 => 'Music Videos|Holiday|Thanksgiving',
4374             1826 => 'Music Videos|Jazz|Avant-Garde Jazz',
4375             1828 => 'Music Videos|Jazz|Bop',
4376             1829 => 'Music Videos|Jazz|Contemporary Jazz',
4377             1830 => 'Music Videos|Jazz|Cool Jazz',
4378             1831 => 'Music Videos|Jazz|Crossover Jazz',
4379             1832 => 'Music Videos|Jazz|Dixieland',
4380             1833 => 'Music Videos|Jazz|Fusion',
4381             1834 => 'Music Videos|Jazz|Hard Bop',
4382             1835 => 'Music Videos|Jazz|Latin Jazz',
4383             1836 => 'Music Videos|Jazz|Mainstream Jazz',
4384             1837 => 'Music Videos|Jazz|Ragtime',
4385             1838 => 'Music Videos|Jazz|Smooth Jazz',
4386             1839 => 'Music Videos|Jazz|Trad Jazz',
4387             1840 => 'Music Videos|Latin|Alternative & Rock in Spanish',
4388             1841 => 'Music Videos|Latin|Baladas y Boleros',
4389             1842 => 'Music Videos|Latin|Contemporary Latin',
4390             1843 => 'Music Videos|Latin|Latin Jazz',
4391             1844 => 'Music Videos|Latin|Latin Urban',
4392             1845 => 'Music Videos|Latin|Pop in Spanish',
4393             1846 => 'Music Videos|Latin|Raices',
4394             1847 => 'Music Videos|Latin|Musica Mexicana', # (Música Mexicana)
4395             1848 => 'Music Videos|Latin|Salsa y Tropical',
4396             1849 => 'Music Videos|New Age|Healing',
4397             1850 => 'Music Videos|New Age|Meditation',
4398             1851 => 'Music Videos|New Age|Nature',
4399             1852 => 'Music Videos|New Age|Relaxation',
4400             1853 => 'Music Videos|New Age|Travel',
4401             1854 => 'Music Videos|Pop|Adult Contemporary',
4402             1855 => 'Music Videos|Pop|Britpop',
4403             1856 => 'Music Videos|Pop|Pop/Rock',
4404             1857 => 'Music Videos|Pop|Soft Rock',
4405             1858 => 'Music Videos|Pop|Teen Pop',
4406             1859 => 'Music Videos|R&B/Soul|Contemporary R&B',
4407             1860 => 'Music Videos|R&B/Soul|Disco',
4408             1861 => 'Music Videos|R&B/Soul|Doo Wop',
4409             1862 => 'Music Videos|R&B/Soul|Funk',
4410             1863 => 'Music Videos|R&B/Soul|Motown',
4411             1864 => 'Music Videos|R&B/Soul|Neo-Soul',
4412             1865 => 'Music Videos|R&B/Soul|Soul',
4413             1866 => 'Music Videos|Reggae|Modern Dancehall',
4414             1867 => 'Music Videos|Reggae|Dub',
4415             1868 => 'Music Videos|Reggae|Roots Reggae',
4416             1869 => 'Music Videos|Reggae|Ska',
4417             1870 => 'Music Videos|Rock|Adult Alternative',
4418             1871 => 'Music Videos|Rock|American Trad Rock',
4419             1872 => 'Music Videos|Rock|Arena Rock',
4420             1873 => 'Music Videos|Rock|Blues-Rock',
4421             1874 => 'Music Videos|Rock|British Invasion',
4422             1875 => 'Music Videos|Rock|Death Metal/Black Metal',
4423             1876 => 'Music Videos|Rock|Glam Rock',
4424             1877 => 'Music Videos|Rock|Hair Metal',
4425             1878 => 'Music Videos|Rock|Hard Rock',
4426             1879 => 'Music Videos|Rock|Jam Bands',
4427             1880 => 'Music Videos|Rock|Prog-Rock/Art Rock',
4428             1881 => 'Music Videos|Rock|Psychedelic',
4429             1882 => 'Music Videos|Rock|Rock & Roll',
4430             1883 => 'Music Videos|Rock|Rockabilly',
4431             1884 => 'Music Videos|Rock|Roots Rock',
4432             1885 => 'Music Videos|Rock|Singer/Songwriter',
4433             1886 => 'Music Videos|Rock|Southern Rock',
4434             1887 => 'Music Videos|Rock|Surf',
4435             1888 => 'Music Videos|Rock|Tex-Mex',
4436             1889 => 'Music Videos|Singer/Songwriter|Alternative Folk',
4437             1890 => 'Music Videos|Singer/Songwriter|Contemporary Folk',
4438             1891 => 'Music Videos|Singer/Songwriter|Contemporary Singer/Songwriter',
4439             1892 => 'Music Videos|Singer/Songwriter|Folk-Rock',
4440             1893 => 'Music Videos|Singer/Songwriter|New Acoustic',
4441             1894 => 'Music Videos|Singer/Songwriter|Traditional Folk',
4442             1895 => 'Music Videos|Soundtrack|Foreign Cinema',
4443             1896 => 'Music Videos|Soundtrack|Musicals',
4444             1897 => 'Music Videos|Soundtrack|Original Score',
4445             1898 => 'Music Videos|Soundtrack|Soundtrack',
4446             1899 => 'Music Videos|Soundtrack|TV Soundtrack',
4447             1900 => 'Music Videos|Vocal|Standards',
4448             1901 => 'Music Videos|Vocal|Traditional Pop',
4449             1902 => 'Music Videos|Jazz|Vocal Jazz',
4450             1903 => 'Music Videos|Vocal|Vocal Pop',
4451             1904 => 'Music Videos|African',
4452             1905 => 'Music Videos|African|Afro-Beat',
4453             1906 => 'Music Videos|African|Afro-Pop',
4454             1907 => 'Music Videos|World|Asia',
4455             1908 => 'Music Videos|World|Australia',
4456             1909 => 'Music Videos|World|Cajun',
4457             1910 => 'Music Videos|World|Caribbean',
4458             1911 => 'Music Videos|World|Celtic',
4459             1912 => 'Music Videos|World|Celtic Folk',
4460             1913 => 'Music Videos|World|Contemporary Celtic',
4461             1914 => 'Music Videos|World|Europe',
4462             1915 => 'Music Videos|World|France',
4463             1916 => 'Music Videos|World|Hawaii',
4464             1917 => 'Music Videos|World|Japan',
4465             1918 => 'Music Videos|World|Klezmer',
4466             1919 => 'Music Videos|World|North America',
4467             1920 => 'Music Videos|World|Polka',
4468             1921 => 'Music Videos|World|South Africa',
4469             1922 => 'Music Videos|World|South America',
4470             1923 => 'Music Videos|World|Traditional Celtic',
4471             1924 => 'Music Videos|World|Worldbeat',
4472             1925 => 'Music Videos|World|Zydeco',
4473             1926 => 'Music Videos|Christian & Gospel',
4474             1928 => 'Music Videos|Classical|Art Song',
4475             1929 => 'Music Videos|Classical|Brass & Woodwinds',
4476             1930 => 'Music Videos|Classical|Solo Instrumental',
4477             1931 => 'Music Videos|Classical|Contemporary Era',
4478             1932 => 'Music Videos|Classical|Oratorio',
4479             1933 => 'Music Videos|Classical|Cantata',
4480             1934 => 'Music Videos|Classical|Electronic',
4481             1935 => 'Music Videos|Classical|Sacred',
4482             1936 => 'Music Videos|Classical|Guitar',
4483             1938 => 'Music Videos|Classical|Violin',
4484             1939 => 'Music Videos|Classical|Cello',
4485             1940 => 'Music Videos|Classical|Percussion',
4486             1941 => 'Music Videos|Electronic|Dubstep',
4487             1942 => 'Music Videos|Electronic|Bass',
4488             1943 => 'Music Videos|Hip-Hop/Rap|UK Hip-Hop',
4489             1944 => 'Music Videos|Reggae|Lovers Rock',
4490             1945 => 'Music Videos|Alternative|EMO',
4491             1946 => 'Music Videos|Alternative|Pop Punk',
4492             1947 => 'Music Videos|Alternative|Indie Pop',
4493             1948 => 'Music Videos|New Age|Yoga',
4494             1949 => 'Music Videos|Pop|Tribute',
4495             1950 => 'Music Videos|Pop|Shows',
4496             1951 => 'Music Videos|Cuban',
4497             1952 => 'Music Videos|Cuban|Mambo',
4498             1953 => 'Music Videos|Cuban|Chachacha',
4499             1954 => 'Music Videos|Cuban|Guajira',
4500             1955 => 'Music Videos|Cuban|Son',
4501             1956 => 'Music Videos|Cuban|Bolero',
4502             1957 => 'Music Videos|Cuban|Guaracha',
4503             1958 => 'Music Videos|Cuban|Timba',
4504             1959 => 'Music Videos|Soundtrack|Video Game',
4505             1960 => 'Music Videos|Indian|Regional Indian|Punjabi|Punjabi Pop',
4506             1961 => 'Music Videos|Indian|Regional Indian|Bengali|Rabindra Sangeet',
4507             1962 => 'Music Videos|Indian|Regional Indian|Malayalam',
4508             1963 => 'Music Videos|Indian|Regional Indian|Kannada',
4509             1964 => 'Music Videos|Indian|Regional Indian|Marathi',
4510             1965 => 'Music Videos|Indian|Regional Indian|Gujarati',
4511             1966 => 'Music Videos|Indian|Regional Indian|Assamese',
4512             1967 => 'Music Videos|Indian|Regional Indian|Bhojpuri',
4513             1968 => 'Music Videos|Indian|Regional Indian|Haryanvi',
4514             1969 => 'Music Videos|Indian|Regional Indian|Odia',
4515             1970 => 'Music Videos|Indian|Regional Indian|Rajasthani',
4516             1971 => 'Music Videos|Indian|Regional Indian|Urdu',
4517             1972 => 'Music Videos|Indian|Regional Indian|Punjabi',
4518             1973 => 'Music Videos|Indian|Regional Indian|Bengali',
4519             1974 => 'Music Videos|Indian|Indian Classical|Carnatic Classical',
4520             1975 => 'Music Videos|Indian|Indian Classical|Hindustani Classical',
4521             1976 => 'Music Videos|African|Afro House',
4522             1977 => 'Music Videos|African|Afro Soul',
4523             1978 => 'Music Videos|African|Afrobeats',
4524             1979 => 'Music Videos|African|Benga',
4525             1980 => 'Music Videos|African|Bongo-Flava',
4526             1981 => 'Music Videos|African|Coupe-Decale',
4527             1982 => 'Music Videos|African|Gqom',
4528             1983 => 'Music Videos|African|Highlife',
4529             1984 => 'Music Videos|African|Kuduro',
4530             1985 => 'Music Videos|African|Kizomba',
4531             1986 => 'Music Videos|African|Kwaito',
4532             1987 => 'Music Videos|African|Mbalax',
4533             1988 => 'Music Videos|African|Ndombolo',
4534             1989 => 'Music Videos|African|Shangaan Electro',
4535             1990 => 'Music Videos|African|Soukous',
4536             1991 => 'Music Videos|African|Taarab',
4537             1992 => 'Music Videos|African|Zouglou',
4538             1993 => 'Music Videos|Turkish|Ozgun',
4539             1994 => 'Music Videos|Turkish|Fantezi',
4540             1995 => 'Music Videos|Turkish|Religious',
4541             1996 => 'Music Videos|Pop|Turkish Pop',
4542             1997 => 'Music Videos|Rock|Turkish Rock',
4543             1998 => 'Music Videos|Alternative|Turkish Alternative',
4544             1999 => 'Music Videos|Hip-Hop/Rap|Turkish Hip-Hop/Rap',
4545             2000 => 'Music Videos|African|Maskandi',
4546             2001 => 'Music Videos|Russian|Russian Romance',
4547             2002 => 'Music Videos|Russian|Russian Bard',
4548             2003 => 'Music Videos|Russian|Russian Pop',
4549             2004 => 'Music Videos|Russian|Russian Rock',
4550             2005 => 'Music Videos|Russian|Russian Hip-Hop',
4551             2006 => 'Music Videos|Arabic|Levant',
4552             2007 => 'Music Videos|Arabic|Levant|Dabke',
4553             2008 => 'Music Videos|Arabic|Maghreb Rai',
4554             2009 => 'Music Videos|Arabic|Khaleeji|Khaleeji Jalsat',
4555             2010 => 'Music Videos|Arabic|Khaleeji|Khaleeji Shailat',
4556             2011 => 'Music Videos|Tarab',
4557             2012 => 'Music Videos|Tarab|Iraqi Tarab',
4558             2013 => 'Music Videos|Tarab|Egyptian Tarab',
4559             2014 => 'Music Videos|Tarab|Khaleeji Tarab',
4560             2015 => 'Music Videos|Pop|Levant Pop',
4561             2016 => 'Music Videos|Pop|Iraqi Pop',
4562             2017 => 'Music Videos|Pop|Egyptian Pop',
4563             2018 => 'Music Videos|Pop|Maghreb Pop',
4564             2019 => 'Music Videos|Pop|Khaleeji Pop',
4565             2020 => 'Music Videos|Hip-Hop/Rap|Levant Hip-Hop',
4566             2021 => 'Music Videos|Hip-Hop/Rap|Egyptian Hip-Hop',
4567             2022 => 'Music Videos|Hip-Hop/Rap|Maghreb Hip-Hop',
4568             2023 => 'Music Videos|Hip-Hop/Rap|Khaleeji Hip-Hop',
4569             2024 => 'Music Videos|Alternative|Indie Levant',
4570             2025 => 'Music Videos|Alternative|Indie Egyptian',
4571             2026 => 'Music Videos|Alternative|Indie Maghreb',
4572             2027 => 'Music Videos|Electronic|Levant Electronic',
4573             2028 => "Music Videos|Electronic|Electro-Cha'abi",
4574             2029 => 'Music Videos|Electronic|Maghreb Electronic',
4575             2030 => 'Music Videos|Folk|Iraqi Folk',
4576             2031 => 'Music Videos|Folk|Khaleeji Folk',
4577             2032 => 'Music Videos|Dance|Maghreb Dance',
4578             4000 => 'TV Shows|Comedy',
4579             4001 => 'TV Shows|Drama',
4580             4002 => 'TV Shows|Animation',
4581             4003 => 'TV Shows|Action & Adventure',
4582             4004 => 'TV Shows|Classics',
4583             4005 => 'TV Shows|Kids & Family',
4584             4006 => 'TV Shows|Nonfiction',
4585             4007 => 'TV Shows|Reality TV',
4586             4008 => 'TV Shows|Sci-Fi & Fantasy',
4587             4009 => 'TV Shows|Sports',
4588             4010 => 'TV Shows|Teens',
4589             4011 => 'TV Shows|Latino TV',
4590             4401 => 'Movies|Action & Adventure',
4591             4402 => 'Movies|Anime',
4592             4403 => 'Movies|Classics',
4593             4404 => 'Movies|Comedy',
4594             4405 => 'Movies|Documentary',
4595             4406 => 'Movies|Drama',
4596             4407 => 'Movies|Foreign',
4597             4408 => 'Movies|Horror',
4598             4409 => 'Movies|Independent',
4599             4410 => 'Movies|Kids & Family',
4600             4411 => 'Movies|Musicals',
4601             4412 => 'Movies|Romance',
4602             4413 => 'Movies|Sci-Fi & Fantasy',
4603             4414 => 'Movies|Short Films',
4604             4415 => 'Movies|Special Interest',
4605             4416 => 'Movies|Thriller',
4606             4417 => 'Movies|Sports',
4607             4418 => 'Movies|Western',
4608             4419 => 'Movies|Urban',
4609             4420 => 'Movies|Holiday',
4610             4421 => 'Movies|Made for TV',
4611             4422 => 'Movies|Concert Films',
4612             4423 => 'Movies|Music Documentaries',
4613             4424 => 'Movies|Music Feature Films',
4614             4425 => 'Movies|Japanese Cinema',
4615             4426 => 'Movies|Jidaigeki',
4616             4427 => 'Movies|Tokusatsu',
4617             4428 => 'Movies|Korean Cinema',
4618             4429 => 'Movies|Russian',
4619             4430 => 'Movies|Turkish',
4620             4431 => 'Movies|Bollywood',
4621             4432 => 'Movies|Regional Indian',
4622             4433 => 'Movies|Middle Eastern',
4623             4434 => 'Movies|African',
4624             6000 => 'App Store|Business',
4625             6001 => 'App Store|Weather',
4626             6002 => 'App Store|Utilities',
4627             6003 => 'App Store|Travel',
4628             6004 => 'App Store|Sports',
4629             6005 => 'App Store|Social Networking',
4630             6006 => 'App Store|Reference',
4631             6007 => 'App Store|Productivity',
4632             6008 => 'App Store|Photo & Video',
4633             6009 => 'App Store|News',
4634             6010 => 'App Store|Navigation',
4635             6011 => 'App Store|Music',
4636             6012 => 'App Store|Lifestyle',
4637             6013 => 'App Store|Health & Fitness',
4638             6014 => 'App Store|Games',
4639             6015 => 'App Store|Finance',
4640             6016 => 'App Store|Entertainment',
4641             6017 => 'App Store|Education',
4642             6018 => 'App Store|Books',
4643             6020 => 'App Store|Medical',
4644             6021 => 'App Store|Magazines & Newspapers',
4645             6022 => 'App Store|Catalogs',
4646             6023 => 'App Store|Food & Drink',
4647             6024 => 'App Store|Shopping',
4648             6025 => 'App Store|Stickers',
4649             6026 => 'App Store|Developer Tools',
4650             6027 => 'App Store|Graphics & Design',
4651             7001 => 'App Store|Games|Action',
4652             7002 => 'App Store|Games|Adventure',
4653             7003 => 'App Store|Games|Casual',
4654             7004 => 'App Store|Games|Board',
4655             7005 => 'App Store|Games|Card',
4656             7006 => 'App Store|Games|Casino',
4657             7007 => 'App Store|Games|Dice',
4658             7008 => 'App Store|Games|Educational',
4659             7009 => 'App Store|Games|Family',
4660             7011 => 'App Store|Games|Music',
4661             7012 => 'App Store|Games|Puzzle',
4662             7013 => 'App Store|Games|Racing',
4663             7014 => 'App Store|Games|Role Playing',
4664             7015 => 'App Store|Games|Simulation',
4665             7016 => 'App Store|Games|Sports',
4666             7017 => 'App Store|Games|Strategy',
4667             7018 => 'App Store|Games|Trivia',
4668             7019 => 'App Store|Games|Word',
4669             8001 => 'Tones|Ringtones|Alternative',
4670             8002 => 'Tones|Ringtones|Blues',
4671             8003 => "Tones|Ringtones|Children's Music",
4672             8004 => 'Tones|Ringtones|Classical',
4673             8005 => 'Tones|Ringtones|Comedy',
4674             8006 => 'Tones|Ringtones|Country',
4675             8007 => 'Tones|Ringtones|Dance',
4676             8008 => 'Tones|Ringtones|Electronic',
4677             8009 => 'Tones|Ringtones|Enka',
4678             8010 => 'Tones|Ringtones|French Pop',
4679             8011 => 'Tones|Ringtones|German Folk',
4680             8012 => 'Tones|Ringtones|German Pop',
4681             8013 => 'Tones|Ringtones|Hip-Hop/Rap',
4682             8014 => 'Tones|Ringtones|Holiday',
4683             8015 => 'Tones|Ringtones|Inspirational',
4684             8016 => 'Tones|Ringtones|J-Pop',
4685             8017 => 'Tones|Ringtones|Jazz',
4686             8018 => 'Tones|Ringtones|Kayokyoku',
4687             8019 => 'Tones|Ringtones|Latin',
4688             8020 => 'Tones|Ringtones|New Age',
4689             8021 => 'Tones|Ringtones|Classical|Opera',
4690             8022 => 'Tones|Ringtones|Pop',
4691             8023 => 'Tones|Ringtones|R&B/Soul',
4692             8024 => 'Tones|Ringtones|Reggae',
4693             8025 => 'Tones|Ringtones|Rock',
4694             8026 => 'Tones|Ringtones|Singer/Songwriter',
4695             8027 => 'Tones|Ringtones|Soundtrack',
4696             8028 => 'Tones|Ringtones|Spoken Word',
4697             8029 => 'Tones|Ringtones|Vocal',
4698             8030 => 'Tones|Ringtones|World',
4699             8050 => 'Tones|Alert Tones|Sound Effects',
4700             8051 => 'Tones|Alert Tones|Dialogue',
4701             8052 => 'Tones|Alert Tones|Music',
4702             8053 => 'Tones|Ringtones',
4703             8054 => 'Tones|Alert Tones',
4704             8055 => 'Tones|Ringtones|Alternative|Chinese Alt',
4705             8056 => 'Tones|Ringtones|Alternative|College Rock',
4706             8057 => 'Tones|Ringtones|Alternative|Goth Rock',
4707             8058 => 'Tones|Ringtones|Alternative|Grunge',
4708             8059 => 'Tones|Ringtones|Alternative|Indie Rock',
4709             8060 => 'Tones|Ringtones|Alternative|Korean Indie',
4710             8061 => 'Tones|Ringtones|Alternative|New Wave',
4711             8062 => 'Tones|Ringtones|Alternative|Punk',
4712             8063 => 'Tones|Ringtones|Anime',
4713             8064 => 'Tones|Ringtones|Arabic',
4714             8065 => 'Tones|Ringtones|Arabic|Arabic Pop',
4715             8066 => 'Tones|Ringtones|Arabic|Islamic',
4716             8067 => 'Tones|Ringtones|Arabic|Khaleeji',
4717             8068 => 'Tones|Ringtones|Arabic|North African',
4718             8069 => 'Tones|Ringtones|Blues|Acoustic Blues',
4719             8070 => 'Tones|Ringtones|Blues|Chicago Blues',
4720             8071 => 'Tones|Ringtones|Blues|Classic Blues',
4721             8072 => 'Tones|Ringtones|Blues|Contemporary Blues',
4722             8073 => 'Tones|Ringtones|Blues|Country Blues',
4723             8074 => 'Tones|Ringtones|Blues|Delta Blues',
4724             8075 => 'Tones|Ringtones|Blues|Electric Blues',
4725             8076 => 'Tones|Ringtones|Brazilian',
4726             8077 => 'Tones|Ringtones|Brazilian|Axe', # (Axé)
4727             8078 => 'Tones|Ringtones|Brazilian|Baile Funk',
4728             8079 => 'Tones|Ringtones|Brazilian|Bossa Nova',
4729             8080 => 'Tones|Ringtones|Brazilian|Choro',
4730             8081 => 'Tones|Ringtones|Brazilian|Forro', # (Forró)
4731             8082 => 'Tones|Ringtones|Brazilian|Frevo',
4732             8083 => 'Tones|Ringtones|Brazilian|MPB',
4733             8084 => 'Tones|Ringtones|Brazilian|Pagode',
4734             8085 => 'Tones|Ringtones|Brazilian|Samba',
4735             8086 => 'Tones|Ringtones|Brazilian|Sertanejo',
4736             8087 => "Tones|Ringtones|Children's Music|Lullabies",
4737             8088 => "Tones|Ringtones|Children's Music|Sing-Along",
4738             8089 => "Tones|Ringtones|Children's Music|Stories",
4739             8090 => 'Tones|Ringtones|Chinese',
4740             8091 => 'Tones|Ringtones|Chinese|Chinese Classical',
4741             8092 => 'Tones|Ringtones|Chinese|Chinese Flute',
4742             8093 => 'Tones|Ringtones|Chinese|Chinese Opera',
4743             8094 => 'Tones|Ringtones|Chinese|Chinese Orchestral',
4744             8095 => 'Tones|Ringtones|Chinese|Chinese Regional Folk',
4745             8096 => 'Tones|Ringtones|Chinese|Chinese Strings',
4746             8097 => 'Tones|Ringtones|Chinese|Taiwanese Folk',
4747             8098 => 'Tones|Ringtones|Chinese|Tibetan Native Music',
4748             8099 => 'Tones|Ringtones|Christian & Gospel',
4749             8100 => 'Tones|Ringtones|Christian & Gospel|CCM',
4750             8101 => 'Tones|Ringtones|Christian & Gospel|Christian Metal',
4751             8102 => 'Tones|Ringtones|Christian & Gospel|Christian Pop',
4752             8103 => 'Tones|Ringtones|Christian & Gospel|Christian Rap',
4753             8104 => 'Tones|Ringtones|Christian & Gospel|Christian Rock',
4754             8105 => 'Tones|Ringtones|Christian & Gospel|Classic Christian',
4755             8106 => 'Tones|Ringtones|Christian & Gospel|Contemporary Gospel',
4756             8107 => 'Tones|Ringtones|Christian & Gospel|Gospel',
4757             8108 => 'Tones|Ringtones|Christian & Gospel|Praise & Worship',
4758             8109 => 'Tones|Ringtones|Christian & Gospel|Southern Gospel',
4759             8110 => 'Tones|Ringtones|Christian & Gospel|Traditional Gospel',
4760             8111 => 'Tones|Ringtones|Classical|Avant-Garde',
4761             8112 => 'Tones|Ringtones|Classical|Baroque Era',
4762             8113 => 'Tones|Ringtones|Classical|Chamber Music',
4763             8114 => 'Tones|Ringtones|Classical|Chant',
4764             8115 => 'Tones|Ringtones|Classical|Choral',
4765             8116 => 'Tones|Ringtones|Classical|Classical Crossover',
4766             8117 => 'Tones|Ringtones|Classical|Early Music',
4767             8118 => 'Tones|Ringtones|Classical|High Classical',
4768             8119 => 'Tones|Ringtones|Classical|Impressionist',
4769             8120 => 'Tones|Ringtones|Classical|Medieval Era',
4770             8121 => 'Tones|Ringtones|Classical|Minimalism',
4771             8122 => 'Tones|Ringtones|Classical|Modern Era',
4772             8123 => 'Tones|Ringtones|Classical|Orchestral',
4773             8124 => 'Tones|Ringtones|Classical|Renaissance',
4774             8125 => 'Tones|Ringtones|Classical|Romantic Era',
4775             8126 => 'Tones|Ringtones|Classical|Wedding Music',
4776             8127 => 'Tones|Ringtones|Comedy|Novelty',
4777             8128 => 'Tones|Ringtones|Comedy|Standup Comedy',
4778             8129 => 'Tones|Ringtones|Country|Alternative Country',
4779             8130 => 'Tones|Ringtones|Country|Americana',
4780             8131 => 'Tones|Ringtones|Country|Bluegrass',
4781             8132 => 'Tones|Ringtones|Country|Contemporary Bluegrass',
4782             8133 => 'Tones|Ringtones|Country|Contemporary Country',
4783             8134 => 'Tones|Ringtones|Country|Country Gospel',
4784             8135 => 'Tones|Ringtones|Country|Honky Tonk',
4785             8136 => 'Tones|Ringtones|Country|Outlaw Country',
4786             8137 => 'Tones|Ringtones|Country|Thai Country',
4787             8138 => 'Tones|Ringtones|Country|Traditional Bluegrass',
4788             8139 => 'Tones|Ringtones|Country|Traditional Country',
4789             8140 => 'Tones|Ringtones|Country|Urban Cowboy',
4790             8141 => 'Tones|Ringtones|Dance|Breakbeat',
4791             8142 => 'Tones|Ringtones|Dance|Exercise',
4792             8143 => 'Tones|Ringtones|Dance|Garage',
4793             8144 => 'Tones|Ringtones|Dance|Hardcore',
4794             8145 => 'Tones|Ringtones|Dance|House',
4795             8146 => "Tones|Ringtones|Dance|Jungle/Drum'n'bass",
4796             8147 => 'Tones|Ringtones|Dance|Techno',
4797             8148 => 'Tones|Ringtones|Dance|Trance',
4798             8149 => 'Tones|Ringtones|Disney',
4799             8150 => 'Tones|Ringtones|Easy Listening',
4800             8151 => 'Tones|Ringtones|Easy Listening|Lounge',
4801             8152 => 'Tones|Ringtones|Easy Listening|Swing',
4802             8153 => 'Tones|Ringtones|Electronic|Ambient',
4803             8154 => 'Tones|Ringtones|Electronic|Downtempo',
4804             8155 => 'Tones|Ringtones|Electronic|Electronica',
4805             8156 => 'Tones|Ringtones|Electronic|IDM/Experimental',
4806             8157 => 'Tones|Ringtones|Electronic|Industrial',
4807             8158 => 'Tones|Ringtones|Fitness & Workout',
4808             8159 => 'Tones|Ringtones|Folk',
4809             8160 => 'Tones|Ringtones|Hip-Hop/Rap|Alternative Rap',
4810             8161 => 'Tones|Ringtones|Hip-Hop/Rap|Chinese Hip-Hop',
4811             8162 => 'Tones|Ringtones|Hip-Hop/Rap|Dirty South',
4812             8163 => 'Tones|Ringtones|Hip-Hop/Rap|East Coast Rap',
4813             8164 => 'Tones|Ringtones|Hip-Hop/Rap|Gangsta Rap',
4814             8165 => 'Tones|Ringtones|Hip-Hop/Rap|Hardcore Rap',
4815             8166 => 'Tones|Ringtones|Hip-Hop/Rap|Hip-Hop',
4816             8167 => 'Tones|Ringtones|Hip-Hop/Rap|Korean Hip-Hop',
4817             8168 => 'Tones|Ringtones|Hip-Hop/Rap|Latin Rap',
4818             8169 => 'Tones|Ringtones|Hip-Hop/Rap|Old School Rap',
4819             8170 => 'Tones|Ringtones|Hip-Hop/Rap|Rap',
4820             8171 => 'Tones|Ringtones|Hip-Hop/Rap|Underground Rap',
4821             8172 => 'Tones|Ringtones|Hip-Hop/Rap|West Coast Rap',
4822             8173 => 'Tones|Ringtones|Holiday|Chanukah',
4823             8174 => 'Tones|Ringtones|Holiday|Christmas',
4824             8175 => "Tones|Ringtones|Holiday|Christmas: Children's",
4825             8176 => 'Tones|Ringtones|Holiday|Christmas: Classic',
4826             8177 => 'Tones|Ringtones|Holiday|Christmas: Classical',
4827             8178 => 'Tones|Ringtones|Holiday|Christmas: Jazz',
4828             8179 => 'Tones|Ringtones|Holiday|Christmas: Modern',
4829             8180 => 'Tones|Ringtones|Holiday|Christmas: Pop',
4830             8181 => 'Tones|Ringtones|Holiday|Christmas: R&B',
4831             8182 => 'Tones|Ringtones|Holiday|Christmas: Religious',
4832             8183 => 'Tones|Ringtones|Holiday|Christmas: Rock',
4833             8184 => 'Tones|Ringtones|Holiday|Easter',
4834             8185 => 'Tones|Ringtones|Holiday|Halloween',
4835             8186 => 'Tones|Ringtones|Holiday|Thanksgiving',
4836             8187 => 'Tones|Ringtones|Indian',
4837             8188 => 'Tones|Ringtones|Indian|Bollywood',
4838             8189 => 'Tones|Ringtones|Indian|Devotional & Spiritual',
4839             8190 => 'Tones|Ringtones|Indian|Ghazals',
4840             8191 => 'Tones|Ringtones|Indian|Indian Classical',
4841             8192 => 'Tones|Ringtones|Indian|Indian Folk',
4842             8193 => 'Tones|Ringtones|Indian|Indian Pop',
4843             8194 => 'Tones|Ringtones|Indian|Regional Indian',
4844             8195 => 'Tones|Ringtones|Indian|Sufi',
4845             8196 => 'Tones|Ringtones|Indian|Regional Indian|Tamil',
4846             8197 => 'Tones|Ringtones|Indian|Regional Indian|Telugu',
4847             8198 => 'Tones|Ringtones|Instrumental',
4848             8199 => 'Tones|Ringtones|Jazz|Avant-Garde Jazz',
4849             8201 => 'Tones|Ringtones|Jazz|Big Band',
4850             8202 => 'Tones|Ringtones|Jazz|Bop',
4851             8203 => 'Tones|Ringtones|Jazz|Contemporary Jazz',
4852             8204 => 'Tones|Ringtones|Jazz|Cool Jazz',
4853             8205 => 'Tones|Ringtones|Jazz|Crossover Jazz',
4854             8206 => 'Tones|Ringtones|Jazz|Dixieland',
4855             8207 => 'Tones|Ringtones|Jazz|Fusion',
4856             8208 => 'Tones|Ringtones|Jazz|Hard Bop',
4857             8209 => 'Tones|Ringtones|Jazz|Latin Jazz',
4858             8210 => 'Tones|Ringtones|Jazz|Mainstream Jazz',
4859             8211 => 'Tones|Ringtones|Jazz|Ragtime',
4860             8212 => 'Tones|Ringtones|Jazz|Smooth Jazz',
4861             8213 => 'Tones|Ringtones|Jazz|Trad Jazz',
4862             8214 => 'Tones|Ringtones|Pop|K-Pop',
4863             8215 => 'Tones|Ringtones|Karaoke',
4864             8216 => 'Tones|Ringtones|Korean',
4865             8217 => 'Tones|Ringtones|Korean|Korean Classical',
4866             8218 => 'Tones|Ringtones|Korean|Korean Trad Instrumental',
4867             8219 => 'Tones|Ringtones|Korean|Korean Trad Song',
4868             8220 => 'Tones|Ringtones|Korean|Korean Trad Theater',
4869             8221 => 'Tones|Ringtones|Latin|Alternative & Rock in Spanish',
4870             8222 => 'Tones|Ringtones|Latin|Baladas y Boleros',
4871             8223 => 'Tones|Ringtones|Latin|Contemporary Latin',
4872             8224 => 'Tones|Ringtones|Latin|Latin Jazz',
4873             8225 => 'Tones|Ringtones|Latin|Latin Urban',
4874             8226 => 'Tones|Ringtones|Latin|Pop in Spanish',
4875             8227 => 'Tones|Ringtones|Latin|Raices',
4876             8228 => 'Tones|Ringtones|Latin|Musica Mexicana', # (Música Mexicana)
4877             8229 => 'Tones|Ringtones|Latin|Salsa y Tropical',
4878             8230 => 'Tones|Ringtones|Marching Bands',
4879             8231 => 'Tones|Ringtones|New Age|Healing',
4880             8232 => 'Tones|Ringtones|New Age|Meditation',
4881             8233 => 'Tones|Ringtones|New Age|Nature',
4882             8234 => 'Tones|Ringtones|New Age|Relaxation',
4883             8235 => 'Tones|Ringtones|New Age|Travel',
4884             8236 => 'Tones|Ringtones|Orchestral',
4885             8237 => 'Tones|Ringtones|Pop|Adult Contemporary',
4886             8238 => 'Tones|Ringtones|Pop|Britpop',
4887             8239 => 'Tones|Ringtones|Pop|C-Pop',
4888             8240 => 'Tones|Ringtones|Pop|Cantopop/HK-Pop',
4889             8241 => 'Tones|Ringtones|Pop|Indo Pop',
4890             8242 => 'Tones|Ringtones|Pop|Korean Folk-Pop',
4891             8243 => 'Tones|Ringtones|Pop|Malaysian Pop',
4892             8244 => 'Tones|Ringtones|Pop|Mandopop',
4893             8245 => 'Tones|Ringtones|Pop|Manilla Sound',
4894             8246 => 'Tones|Ringtones|Pop|Oldies',
4895             8247 => 'Tones|Ringtones|Pop|Original Pilipino Music',
4896             8248 => 'Tones|Ringtones|Pop|Pinoy Pop',
4897             8249 => 'Tones|Ringtones|Pop|Pop/Rock',
4898             8250 => 'Tones|Ringtones|Pop|Soft Rock',
4899             8251 => 'Tones|Ringtones|Pop|Tai-Pop',
4900             8252 => 'Tones|Ringtones|Pop|Teen Pop',
4901             8253 => 'Tones|Ringtones|Pop|Thai Pop',
4902             8254 => 'Tones|Ringtones|R&B/Soul|Contemporary R&B',
4903             8255 => 'Tones|Ringtones|R&B/Soul|Disco',
4904             8256 => 'Tones|Ringtones|R&B/Soul|Doo Wop',
4905             8257 => 'Tones|Ringtones|R&B/Soul|Funk',
4906             8258 => 'Tones|Ringtones|R&B/Soul|Motown',
4907             8259 => 'Tones|Ringtones|R&B/Soul|Neo-Soul',
4908             8260 => 'Tones|Ringtones|R&B/Soul|Soul',
4909             8261 => 'Tones|Ringtones|Reggae|Modern Dancehall',
4910             8262 => 'Tones|Ringtones|Reggae|Dub',
4911             8263 => 'Tones|Ringtones|Reggae|Roots Reggae',
4912             8264 => 'Tones|Ringtones|Reggae|Ska',
4913             8265 => 'Tones|Ringtones|Rock|Adult Alternative',
4914             8266 => 'Tones|Ringtones|Rock|American Trad Rock',
4915             8267 => 'Tones|Ringtones|Rock|Arena Rock',
4916             8268 => 'Tones|Ringtones|Rock|Blues-Rock',
4917             8269 => 'Tones|Ringtones|Rock|British Invasion',
4918             8270 => 'Tones|Ringtones|Rock|Chinese Rock',
4919             8271 => 'Tones|Ringtones|Rock|Death Metal/Black Metal',
4920             8272 => 'Tones|Ringtones|Rock|Glam Rock',
4921             8273 => 'Tones|Ringtones|Rock|Hair Metal',
4922             8274 => 'Tones|Ringtones|Rock|Hard Rock',
4923             8275 => 'Tones|Ringtones|Rock|Metal',
4924             8276 => 'Tones|Ringtones|Rock|Jam Bands',
4925             8277 => 'Tones|Ringtones|Rock|Korean Rock',
4926             8278 => 'Tones|Ringtones|Rock|Prog-Rock/Art Rock',
4927             8279 => 'Tones|Ringtones|Rock|Psychedelic',
4928             8280 => 'Tones|Ringtones|Rock|Rock & Roll',
4929             8281 => 'Tones|Ringtones|Rock|Rockabilly',
4930             8282 => 'Tones|Ringtones|Rock|Roots Rock',
4931             8283 => 'Tones|Ringtones|Rock|Singer/Songwriter',
4932             8284 => 'Tones|Ringtones|Rock|Southern Rock',
4933             8285 => 'Tones|Ringtones|Rock|Surf',
4934             8286 => 'Tones|Ringtones|Rock|Tex-Mex',
4935             8287 => 'Tones|Ringtones|Singer/Songwriter|Alternative Folk',
4936             8288 => 'Tones|Ringtones|Singer/Songwriter|Contemporary Folk',
4937             8289 => 'Tones|Ringtones|Singer/Songwriter|Contemporary Singer/Songwriter',
4938             8290 => 'Tones|Ringtones|Singer/Songwriter|Folk-Rock',
4939             8291 => 'Tones|Ringtones|Singer/Songwriter|New Acoustic',
4940             8292 => 'Tones|Ringtones|Singer/Songwriter|Traditional Folk',
4941             8293 => 'Tones|Ringtones|Soundtrack|Foreign Cinema',
4942             8294 => 'Tones|Ringtones|Soundtrack|Musicals',
4943             8295 => 'Tones|Ringtones|Soundtrack|Original Score',
4944             8296 => 'Tones|Ringtones|Soundtrack|Sound Effects',
4945             8297 => 'Tones|Ringtones|Soundtrack|Soundtrack',
4946             8298 => 'Tones|Ringtones|Soundtrack|TV Soundtrack',
4947             8299 => 'Tones|Ringtones|Vocal|Standards',
4948             8300 => 'Tones|Ringtones|Vocal|Traditional Pop',
4949             8301 => 'Tones|Ringtones|Vocal|Trot',
4950             8302 => 'Tones|Ringtones|Jazz|Vocal Jazz',
4951             8303 => 'Tones|Ringtones|Vocal|Vocal Pop',
4952             8304 => 'Tones|Ringtones|African',
4953             8305 => 'Tones|Ringtones|African|Afrikaans',
4954             8306 => 'Tones|Ringtones|African|Afro-Beat',
4955             8307 => 'Tones|Ringtones|African|Afro-Pop',
4956             8308 => 'Tones|Ringtones|Turkish|Arabesque',
4957             8309 => 'Tones|Ringtones|World|Asia',
4958             8310 => 'Tones|Ringtones|World|Australia',
4959             8311 => 'Tones|Ringtones|World|Cajun',
4960             8312 => 'Tones|Ringtones|World|Calypso',
4961             8313 => 'Tones|Ringtones|World|Caribbean',
4962             8314 => 'Tones|Ringtones|World|Celtic',
4963             8315 => 'Tones|Ringtones|World|Celtic Folk',
4964             8316 => 'Tones|Ringtones|World|Contemporary Celtic',
4965             8317 => 'Tones|Ringtones|World|Dangdut',
4966             8318 => 'Tones|Ringtones|World|Dini',
4967             8319 => 'Tones|Ringtones|World|Europe',
4968             8320 => 'Tones|Ringtones|World|Fado',
4969             8321 => 'Tones|Ringtones|World|Farsi',
4970             8322 => 'Tones|Ringtones|World|Flamenco',
4971             8323 => 'Tones|Ringtones|World|France',
4972             8324 => 'Tones|Ringtones|Turkish|Halk',
4973             8325 => 'Tones|Ringtones|World|Hawaii',
4974             8326 => 'Tones|Ringtones|World|Iberia',
4975             8327 => 'Tones|Ringtones|World|Indonesian Religious',
4976             8328 => 'Tones|Ringtones|World|Israeli',
4977             8329 => 'Tones|Ringtones|World|Japan',
4978             8330 => 'Tones|Ringtones|World|Klezmer',
4979             8331 => 'Tones|Ringtones|World|North America',
4980             8332 => 'Tones|Ringtones|World|Polka',
4981             8333 => 'Tones|Ringtones|Russian',
4982             8334 => 'Tones|Ringtones|Russian|Russian Chanson',
4983             8335 => 'Tones|Ringtones|Turkish|Sanat',
4984             8336 => 'Tones|Ringtones|World|Soca',
4985             8337 => 'Tones|Ringtones|World|South Africa',
4986             8338 => 'Tones|Ringtones|World|South America',
4987             8339 => 'Tones|Ringtones|World|Tango',
4988             8340 => 'Tones|Ringtones|World|Traditional Celtic',
4989             8341 => 'Tones|Ringtones|Turkish',
4990             8342 => 'Tones|Ringtones|World|Worldbeat',
4991             8343 => 'Tones|Ringtones|World|Zydeco',
4992             8345 => 'Tones|Ringtones|Classical|Art Song',
4993             8346 => 'Tones|Ringtones|Classical|Brass & Woodwinds',
4994             8347 => 'Tones|Ringtones|Classical|Solo Instrumental',
4995             8348 => 'Tones|Ringtones|Classical|Contemporary Era',
4996             8349 => 'Tones|Ringtones|Classical|Oratorio',
4997             8350 => 'Tones|Ringtones|Classical|Cantata',
4998             8351 => 'Tones|Ringtones|Classical|Electronic',
4999             8352 => 'Tones|Ringtones|Classical|Sacred',
5000             8353 => 'Tones|Ringtones|Classical|Guitar',
5001             8354 => 'Tones|Ringtones|Classical|Piano',
5002             8355 => 'Tones|Ringtones|Classical|Violin',
5003             8356 => 'Tones|Ringtones|Classical|Cello',
5004             8357 => 'Tones|Ringtones|Classical|Percussion',
5005             8358 => 'Tones|Ringtones|Electronic|Dubstep',
5006             8359 => 'Tones|Ringtones|Electronic|Bass',
5007             8360 => 'Tones|Ringtones|Hip-Hop/Rap|UK Hip Hop',
5008             8361 => 'Tones|Ringtones|Reggae|Lovers Rock',
5009             8362 => 'Tones|Ringtones|Alternative|EMO',
5010             8363 => 'Tones|Ringtones|Alternative|Pop Punk',
5011             8364 => 'Tones|Ringtones|Alternative|Indie Pop',
5012             8365 => 'Tones|Ringtones|New Age|Yoga',
5013             8366 => 'Tones|Ringtones|Pop|Tribute',
5014             8367 => 'Tones|Ringtones|Pop|Shows',
5015             8368 => 'Tones|Ringtones|Cuban',
5016             8369 => 'Tones|Ringtones|Cuban|Mambo',
5017             8370 => 'Tones|Ringtones|Cuban|Chachacha',
5018             8371 => 'Tones|Ringtones|Cuban|Guajira',
5019             8372 => 'Tones|Ringtones|Cuban|Son',
5020             8373 => 'Tones|Ringtones|Cuban|Bolero',
5021             8374 => 'Tones|Ringtones|Cuban|Guaracha',
5022             8375 => 'Tones|Ringtones|Cuban|Timba',
5023             8376 => 'Tones|Ringtones|Soundtrack|Video Game',
5024             8377 => 'Tones|Ringtones|Indian|Regional Indian|Punjabi|Punjabi Pop',
5025             8378 => 'Tones|Ringtones|Indian|Regional Indian|Bengali|Rabindra Sangeet',
5026             8379 => 'Tones|Ringtones|Indian|Regional Indian|Malayalam',
5027             8380 => 'Tones|Ringtones|Indian|Regional Indian|Kannada',
5028             8381 => 'Tones|Ringtones|Indian|Regional Indian|Marathi',
5029             8382 => 'Tones|Ringtones|Indian|Regional Indian|Gujarati',
5030             8383 => 'Tones|Ringtones|Indian|Regional Indian|Assamese',
5031             8384 => 'Tones|Ringtones|Indian|Regional Indian|Bhojpuri',
5032             8385 => 'Tones|Ringtones|Indian|Regional Indian|Haryanvi',
5033             8386 => 'Tones|Ringtones|Indian|Regional Indian|Odia',
5034             8387 => 'Tones|Ringtones|Indian|Regional Indian|Rajasthani',
5035             8388 => 'Tones|Ringtones|Indian|Regional Indian|Urdu',
5036             8389 => 'Tones|Ringtones|Indian|Regional Indian|Punjabi',
5037             8390 => 'Tones|Ringtones|Indian|Regional Indian|Bengali',
5038             8391 => 'Tones|Ringtones|Indian|Indian Classical|Carnatic Classical',
5039             8392 => 'Tones|Ringtones|Indian|Indian Classical|Hindustani Classical',
5040             8393 => 'Tones|Ringtones|African|Afro House',
5041             8394 => 'Tones|Ringtones|African|Afro Soul',
5042             8395 => 'Tones|Ringtones|African|Afrobeats',
5043             8396 => 'Tones|Ringtones|African|Benga',
5044             8397 => 'Tones|Ringtones|African|Bongo-Flava',
5045             8398 => 'Tones|Ringtones|African|Coupe-Decale',
5046             8399 => 'Tones|Ringtones|African|Gqom',
5047             8400 => 'Tones|Ringtones|African|Highlife',
5048             8401 => 'Tones|Ringtones|African|Kuduro',
5049             8402 => 'Tones|Ringtones|African|Kizomba',
5050             8403 => 'Tones|Ringtones|African|Kwaito',
5051             8404 => 'Tones|Ringtones|African|Mbalax',
5052             8405 => 'Tones|Ringtones|African|Ndombolo',
5053             8406 => 'Tones|Ringtones|African|Shangaan Electro',
5054             8407 => 'Tones|Ringtones|African|Soukous',
5055             8408 => 'Tones|Ringtones|African|Taarab',
5056             8409 => 'Tones|Ringtones|African|Zouglou',
5057             8410 => 'Tones|Ringtones|Turkish|Ozgun',
5058             8411 => 'Tones|Ringtones|Turkish|Fantezi',
5059             8412 => 'Tones|Ringtones|Turkish|Religious',
5060             8413 => 'Tones|Ringtones|Pop|Turkish Pop',
5061             8414 => 'Tones|Ringtones|Rock|Turkish Rock',
5062             8415 => 'Tones|Ringtones|Alternative|Turkish Alternative',
5063             8416 => 'Tones|Ringtones|Hip-Hop/Rap|Turkish Hip-Hop/Rap',
5064             8417 => 'Tones|Ringtones|African|Maskandi',
5065             8418 => 'Tones|Ringtones|Russian|Russian Romance',
5066             8419 => 'Tones|Ringtones|Russian|Russian Bard',
5067             8420 => 'Tones|Ringtones|Russian|Russian Pop',
5068             8421 => 'Tones|Ringtones|Russian|Russian Rock',
5069             8422 => 'Tones|Ringtones|Russian|Russian Hip-Hop',
5070             8423 => 'Tones|Ringtones|Arabic|Levant',
5071             8424 => 'Tones|Ringtones|Arabic|Levant|Dabke',
5072             8425 => 'Tones|Ringtones|Arabic|Maghreb Rai',
5073             8426 => 'Tones|Ringtones|Arabic|Khaleeji|Khaleeji Jalsat',
5074             8427 => 'Tones|Ringtones|Arabic|Khaleeji|Khaleeji Shailat',
5075             8428 => 'Tones|Ringtones|Tarab',
5076             8429 => 'Tones|Ringtones|Tarab|Iraqi Tarab',
5077             8430 => 'Tones|Ringtones|Tarab|Egyptian Tarab',
5078             8431 => 'Tones|Ringtones|Tarab|Khaleeji Tarab',
5079             8432 => 'Tones|Ringtones|Pop|Levant Pop',
5080             8433 => 'Tones|Ringtones|Pop|Iraqi Pop',
5081             8434 => 'Tones|Ringtones|Pop|Egyptian Pop',
5082             8435 => 'Tones|Ringtones|Pop|Maghreb Pop',
5083             8436 => 'Tones|Ringtones|Pop|Khaleeji Pop',
5084             8437 => 'Tones|Ringtones|Hip-Hop/Rap|Levant Hip-Hop',
5085             8438 => 'Tones|Ringtones|Hip-Hop/Rap|Egyptian Hip-Hop',
5086             8439 => 'Tones|Ringtones|Hip-Hop/Rap|Maghreb Hip-Hop',
5087             8440 => 'Tones|Ringtones|Hip-Hop/Rap|Khaleeji Hip-Hop',
5088             8441 => 'Tones|Ringtones|Alternative|Indie Levant',
5089             8442 => 'Tones|Ringtones|Alternative|Indie Egyptian',
5090             8443 => 'Tones|Ringtones|Alternative|Indie Maghreb',
5091             8444 => 'Tones|Ringtones|Electronic|Levant Electronic',
5092             8445 => "Tones|Ringtones|Electronic|Electro-Cha'abi",
5093             8446 => 'Tones|Ringtones|Electronic|Maghreb Electronic',
5094             8447 => 'Tones|Ringtones|Folk|Iraqi Folk',
5095             8448 => 'Tones|Ringtones|Folk|Khaleeji Folk',
5096             8449 => 'Tones|Ringtones|Dance|Maghreb Dance',
5097             9002 => 'Books|Nonfiction',
5098             9003 => 'Books|Romance',
5099             9004 => 'Books|Travel & Adventure',
5100             9007 => 'Books|Arts & Entertainment',
5101             9008 => 'Books|Biographies & Memoirs',
5102             9009 => 'Books|Business & Personal Finance',
5103             9010 => 'Books|Children & Teens',
5104             9012 => 'Books|Humor',
5105             9015 => 'Books|History',
5106             9018 => 'Books|Religion & Spirituality',
5107             9019 => 'Books|Science & Nature',
5108             9020 => 'Books|Sci-Fi & Fantasy',
5109             9024 => 'Books|Lifestyle & Home',
5110             9025 => 'Books|Self-Development',
5111             9026 => 'Books|Comics & Graphic Novels',
5112             9027 => 'Books|Computers & Internet',
5113             9028 => 'Books|Cookbooks, Food & Wine',
5114             9029 => 'Books|Professional & Technical',
5115             9030 => 'Books|Parenting',
5116             9031 => 'Books|Fiction & Literature',
5117             9032 => 'Books|Mysteries & Thrillers',
5118             9033 => 'Books|Reference',
5119             9034 => 'Books|Politics & Current Events',
5120             9035 => 'Books|Sports & Outdoors',
5121             10001 => 'Books|Lifestyle & Home|Antiques & Collectibles',
5122             10002 => 'Books|Arts & Entertainment|Art & Architecture',
5123             10003 => 'Books|Religion & Spirituality|Bibles',
5124             10004 => 'Books|Self-Development|Spirituality',
5125             10005 => 'Books|Business & Personal Finance|Industries & Professions',
5126             10006 => 'Books|Business & Personal Finance|Marketing & Sales',
5127             10007 => 'Books|Business & Personal Finance|Small Business & Entrepreneurship',
5128             10008 => 'Books|Business & Personal Finance|Personal Finance',
5129             10009 => 'Books|Business & Personal Finance|Reference',
5130             10010 => 'Books|Business & Personal Finance|Careers',
5131             10011 => 'Books|Business & Personal Finance|Economics',
5132             10012 => 'Books|Business & Personal Finance|Investing',
5133             10013 => 'Books|Business & Personal Finance|Finance',
5134             10014 => 'Books|Business & Personal Finance|Management & Leadership',
5135             10015 => 'Books|Comics & Graphic Novels|Graphic Novels',
5136             10016 => 'Books|Comics & Graphic Novels|Manga',
5137             10017 => 'Books|Computers & Internet|Computers',
5138             10018 => 'Books|Computers & Internet|Databases',
5139             10019 => 'Books|Computers & Internet|Digital Media',
5140             10020 => 'Books|Computers & Internet|Internet',
5141             10021 => 'Books|Computers & Internet|Network',
5142             10022 => 'Books|Computers & Internet|Operating Systems',
5143             10023 => 'Books|Computers & Internet|Programming',
5144             10024 => 'Books|Computers & Internet|Software',
5145             10025 => 'Books|Computers & Internet|System Administration',
5146             10026 => 'Books|Cookbooks, Food & Wine|Beverages',
5147             10027 => 'Books|Cookbooks, Food & Wine|Courses & Dishes',
5148             10028 => 'Books|Cookbooks, Food & Wine|Special Diet',
5149             10029 => 'Books|Cookbooks, Food & Wine|Special Occasions',
5150             10030 => 'Books|Cookbooks, Food & Wine|Methods',
5151             10031 => 'Books|Cookbooks, Food & Wine|Reference',
5152             10032 => 'Books|Cookbooks, Food & Wine|Regional & Ethnic',
5153             10033 => 'Books|Cookbooks, Food & Wine|Specific Ingredients',
5154             10034 => 'Books|Lifestyle & Home|Crafts & Hobbies',
5155             10035 => 'Books|Professional & Technical|Design',
5156             10036 => 'Books|Arts & Entertainment|Theater',
5157             10037 => 'Books|Professional & Technical|Education',
5158             10038 => 'Books|Nonfiction|Family & Relationships',
5159             10039 => 'Books|Fiction & Literature|Action & Adventure',
5160             10040 => 'Books|Fiction & Literature|African American',
5161             10041 => 'Books|Fiction & Literature|Religious',
5162             10042 => 'Books|Fiction & Literature|Classics',
5163             10043 => 'Books|Fiction & Literature|Erotica',
5164             10044 => 'Books|Sci-Fi & Fantasy|Fantasy',
5165             10045 => 'Books|Fiction & Literature|Gay',
5166             10046 => 'Books|Fiction & Literature|Ghost',
5167             10047 => 'Books|Fiction & Literature|Historical',
5168             10048 => 'Books|Fiction & Literature|Horror',
5169             10049 => 'Books|Fiction & Literature|Literary',
5170             10050 => 'Books|Mysteries & Thrillers|Hard-Boiled',
5171             10051 => 'Books|Mysteries & Thrillers|Historical',
5172             10052 => 'Books|Mysteries & Thrillers|Police Procedural',
5173             10053 => 'Books|Mysteries & Thrillers|Short Stories',
5174             10054 => 'Books|Mysteries & Thrillers|British Detectives',
5175             10055 => 'Books|Mysteries & Thrillers|Women Sleuths',
5176             10056 => 'Books|Romance|Erotic Romance',
5177             10057 => 'Books|Romance|Contemporary',
5178             10058 => 'Books|Romance|Paranormal',
5179             10059 => 'Books|Romance|Historical',
5180             10060 => 'Books|Romance|Short Stories',
5181             10061 => 'Books|Romance|Suspense',
5182             10062 => 'Books|Romance|Western',
5183             10063 => 'Books|Sci-Fi & Fantasy|Science Fiction',
5184             10064 => 'Books|Sci-Fi & Fantasy|Science Fiction & Literature',
5185             10065 => 'Books|Fiction & Literature|Short Stories',
5186             10066 => 'Books|Reference|Foreign Languages',
5187             10067 => 'Books|Arts & Entertainment|Games',
5188             10068 => 'Books|Lifestyle & Home|Gardening',
5189             10069 => 'Books|Self-Development|Health & Fitness',
5190             10070 => 'Books|History|Africa',
5191             10071 => 'Books|History|Americas',
5192             10072 => 'Books|History|Ancient',
5193             10073 => 'Books|History|Asia',
5194             10074 => 'Books|History|Australia & Oceania',
5195             10075 => 'Books|History|Europe',
5196             10076 => 'Books|History|Latin America',
5197             10077 => 'Books|History|Middle East',
5198             10078 => 'Books|History|Military',
5199             10079 => 'Books|History|United States',
5200             10080 => 'Books|History|World',
5201             10081 => "Books|Children & Teens|Children's Fiction",
5202             10082 => "Books|Children & Teens|Children's Nonfiction",
5203             10083 => 'Books|Professional & Technical|Law',
5204             10084 => 'Books|Fiction & Literature|Literary Criticism',
5205             10085 => 'Books|Science & Nature|Mathematics',
5206             10086 => 'Books|Professional & Technical|Medical',
5207             10087 => 'Books|Arts & Entertainment|Music',
5208             10088 => 'Books|Science & Nature|Nature',
5209             10089 => 'Books|Arts & Entertainment|Performing Arts',
5210             10090 => 'Books|Lifestyle & Home|Pets',
5211             10091 => 'Books|Nonfiction|Philosophy',
5212             10092 => 'Books|Arts & Entertainment|Photography',
5213             10093 => 'Books|Fiction & Literature|Poetry',
5214             10094 => 'Books|Self-Development|Psychology',
5215             10095 => 'Books|Reference|Almanacs & Yearbooks',
5216             10096 => 'Books|Reference|Atlases & Maps',
5217             10097 => 'Books|Reference|Catalogs & Directories',
5218             10098 => 'Books|Reference|Consumer Guides',
5219             10099 => 'Books|Reference|Dictionaries & Thesauruses',
5220             10100 => 'Books|Reference|Encyclopedias',
5221             10101 => 'Books|Reference|Etiquette',
5222             10102 => 'Books|Reference|Quotations',
5223             10103 => 'Books|Reference|Words & Language',
5224             10104 => 'Books|Reference|Writing',
5225             10105 => 'Books|Religion & Spirituality|Bible Studies',
5226             10106 => 'Books|Religion & Spirituality|Buddhism',
5227             10107 => 'Books|Religion & Spirituality|Christianity',
5228             10108 => 'Books|Religion & Spirituality|Hinduism',
5229             10109 => 'Books|Religion & Spirituality|Islam',
5230             10110 => 'Books|Religion & Spirituality|Judaism',
5231             10111 => 'Books|Science & Nature|Astronomy',
5232             10112 => 'Books|Science & Nature|Chemistry',
5233             10113 => 'Books|Science & Nature|Earth Sciences',
5234             10114 => 'Books|Science & Nature|Essays',
5235             10115 => 'Books|Science & Nature|History',
5236             10116 => 'Books|Science & Nature|Life Sciences',
5237             10117 => 'Books|Science & Nature|Physics',
5238             10118 => 'Books|Science & Nature|Reference',
5239             10119 => 'Books|Self-Development|Self-Improvement',
5240             10120 => 'Books|Nonfiction|Social Science',
5241             10121 => 'Books|Sports & Outdoors|Baseball',
5242             10122 => 'Books|Sports & Outdoors|Basketball',
5243             10123 => 'Books|Sports & Outdoors|Coaching',
5244             10124 => 'Books|Sports & Outdoors|Extreme Sports',
5245             10125 => 'Books|Sports & Outdoors|Football',
5246             10126 => 'Books|Sports & Outdoors|Golf',
5247             10127 => 'Books|Sports & Outdoors|Hockey',
5248             10128 => 'Books|Sports & Outdoors|Mountaineering',
5249             10129 => 'Books|Sports & Outdoors|Outdoors',
5250             10130 => 'Books|Sports & Outdoors|Racket Sports',
5251             10131 => 'Books|Sports & Outdoors|Reference',
5252             10132 => 'Books|Sports & Outdoors|Soccer',
5253             10133 => 'Books|Sports & Outdoors|Training',
5254             10134 => 'Books|Sports & Outdoors|Water Sports',
5255             10135 => 'Books|Sports & Outdoors|Winter Sports',
5256             10136 => 'Books|Reference|Study Aids',
5257             10137 => 'Books|Professional & Technical|Engineering',
5258             10138 => 'Books|Nonfiction|Transportation',
5259             10139 => 'Books|Travel & Adventure|Africa',
5260             10140 => 'Books|Travel & Adventure|Asia',
5261             10141 => 'Books|Travel & Adventure|Specialty Travel',
5262             10142 => 'Books|Travel & Adventure|Canada',
5263             10143 => 'Books|Travel & Adventure|Caribbean',
5264             10144 => 'Books|Travel & Adventure|Latin America',
5265             10145 => 'Books|Travel & Adventure|Essays & Memoirs',
5266             10146 => 'Books|Travel & Adventure|Europe',
5267             10147 => 'Books|Travel & Adventure|Middle East',
5268             10148 => 'Books|Travel & Adventure|United States',
5269             10149 => 'Books|Nonfiction|True Crime',
5270             11001 => 'Books|Sci-Fi & Fantasy|Fantasy|Contemporary',
5271             11002 => 'Books|Sci-Fi & Fantasy|Fantasy|Epic',
5272             11003 => 'Books|Sci-Fi & Fantasy|Fantasy|Historical',
5273             11004 => 'Books|Sci-Fi & Fantasy|Fantasy|Paranormal',
5274             11005 => 'Books|Sci-Fi & Fantasy|Fantasy|Short Stories',
5275             11006 => 'Books|Sci-Fi & Fantasy|Science Fiction & Literature|Adventure',
5276             11007 => 'Books|Sci-Fi & Fantasy|Science Fiction & Literature|High Tech',
5277             11008 => 'Books|Sci-Fi & Fantasy|Science Fiction & Literature|Short Stories',
5278             11009 => 'Books|Professional & Technical|Education|Language Arts & Disciplines',
5279             11010 => 'Books|Communications & Media',
5280             11011 => 'Books|Communications & Media|Broadcasting',
5281             11012 => 'Books|Communications & Media|Digital Media',
5282             11013 => 'Books|Communications & Media|Journalism',
5283             11014 => 'Books|Communications & Media|Photojournalism',
5284             11015 => 'Books|Communications & Media|Print',
5285             11016 => 'Books|Communications & Media|Speech',
5286             11017 => 'Books|Communications & Media|Writing',
5287             11018 => 'Books|Arts & Entertainment|Art & Architecture|Urban Planning',
5288             11019 => 'Books|Arts & Entertainment|Dance',
5289             11020 => 'Books|Arts & Entertainment|Fashion',
5290             11021 => 'Books|Arts & Entertainment|Film',
5291             11022 => 'Books|Arts & Entertainment|Interior Design',
5292             11023 => 'Books|Arts & Entertainment|Media Arts',
5293             11024 => 'Books|Arts & Entertainment|Radio',
5294             11025 => 'Books|Arts & Entertainment|TV',
5295             11026 => 'Books|Arts & Entertainment|Visual Arts',
5296             11027 => 'Books|Biographies & Memoirs|Arts & Entertainment',
5297             11028 => 'Books|Biographies & Memoirs|Business',
5298             11029 => 'Books|Biographies & Memoirs|Culinary',
5299             11030 => 'Books|Biographies & Memoirs|Gay & Lesbian',
5300             11031 => 'Books|Biographies & Memoirs|Historical',
5301             11032 => 'Books|Biographies & Memoirs|Literary',
5302             11033 => 'Books|Biographies & Memoirs|Media & Journalism',
5303             11034 => 'Books|Biographies & Memoirs|Military',
5304             11035 => 'Books|Biographies & Memoirs|Politics',
5305             11036 => 'Books|Biographies & Memoirs|Religious',
5306             11037 => 'Books|Biographies & Memoirs|Science & Technology',
5307             11038 => 'Books|Biographies & Memoirs|Sports',
5308             11039 => 'Books|Biographies & Memoirs|Women',
5309             11040 => 'Books|Romance|New Adult',
5310             11042 => 'Books|Romance|Romantic Comedy',
5311             11043 => 'Books|Romance|Gay & Lesbian',
5312             11044 => 'Books|Fiction & Literature|Essays',
5313             11045 => 'Books|Fiction & Literature|Anthologies',
5314             11046 => 'Books|Fiction & Literature|Comparative Literature',
5315             11047 => 'Books|Fiction & Literature|Drama',
5316             11049 => 'Books|Fiction & Literature|Fairy Tales, Myths & Fables',
5317             11050 => 'Books|Fiction & Literature|Family',
5318             11051 => 'Books|Comics & Graphic Novels|Manga|School Drama',
5319             11052 => 'Books|Comics & Graphic Novels|Manga|Human Drama',
5320             11053 => 'Books|Comics & Graphic Novels|Manga|Family Drama',
5321             11054 => 'Books|Sports & Outdoors|Boxing',
5322             11055 => 'Books|Sports & Outdoors|Cricket',
5323             11056 => 'Books|Sports & Outdoors|Cycling',
5324             11057 => 'Books|Sports & Outdoors|Equestrian',
5325             11058 => 'Books|Sports & Outdoors|Martial Arts & Self Defense',
5326             11059 => 'Books|Sports & Outdoors|Motor Sports',
5327             11060 => 'Books|Sports & Outdoors|Rugby',
5328             11061 => 'Books|Sports & Outdoors|Running',
5329             11062 => 'Books|Self-Development|Diet & Nutrition',
5330             11063 => 'Books|Science & Nature|Agriculture',
5331             11064 => 'Books|Science & Nature|Atmosphere',
5332             11065 => 'Books|Science & Nature|Biology',
5333             11066 => 'Books|Science & Nature|Ecology',
5334             11067 => 'Books|Science & Nature|Environment',
5335             11068 => 'Books|Science & Nature|Geography',
5336             11069 => 'Books|Science & Nature|Geology',
5337             11070 => 'Books|Nonfiction|Social Science|Anthropology',
5338             11071 => 'Books|Nonfiction|Social Science|Archaeology',
5339             11072 => 'Books|Nonfiction|Social Science|Civics',
5340             11073 => 'Books|Nonfiction|Social Science|Government',
5341             11074 => 'Books|Nonfiction|Social Science|Social Studies',
5342             11075 => 'Books|Nonfiction|Social Science|Social Welfare',
5343             11076 => 'Books|Nonfiction|Social Science|Society',
5344             11077 => 'Books|Nonfiction|Philosophy|Aesthetics',
5345             11078 => 'Books|Nonfiction|Philosophy|Epistemology',
5346             11079 => 'Books|Nonfiction|Philosophy|Ethics',
5347             11080 => 'Books|Nonfiction|Philosophy|Language',
5348             11081 => 'Books|Nonfiction|Philosophy|Logic',
5349             11082 => 'Books|Nonfiction|Philosophy|Metaphysics',
5350             11083 => 'Books|Nonfiction|Philosophy|Political',
5351             11084 => 'Books|Nonfiction|Philosophy|Religion',
5352             11085 => 'Books|Reference|Manuals',
5353             11086 => 'Books|Kids',
5354             11087 => 'Books|Kids|Animals',
5355             11088 => 'Books|Kids|Basic Concepts',
5356             11089 => 'Books|Kids|Basic Concepts|Alphabet',
5357             11090 => 'Books|Kids|Basic Concepts|Body',
5358             11091 => 'Books|Kids|Basic Concepts|Colors',
5359             11092 => 'Books|Kids|Basic Concepts|Counting & Numbers',
5360             11093 => 'Books|Kids|Basic Concepts|Date & Time',
5361             11094 => 'Books|Kids|Basic Concepts|General',
5362             11095 => 'Books|Kids|Basic Concepts|Money',
5363             11096 => 'Books|Kids|Basic Concepts|Opposites',
5364             11097 => 'Books|Kids|Basic Concepts|Seasons',
5365             11098 => 'Books|Kids|Basic Concepts|Senses & Sensation',
5366             11099 => 'Books|Kids|Basic Concepts|Size & Shape',
5367             11100 => 'Books|Kids|Basic Concepts|Sounds',
5368             11101 => 'Books|Kids|Basic Concepts|Words',
5369             11102 => 'Books|Kids|Biography',
5370             11103 => 'Books|Kids|Careers & Occupations',
5371             11104 => 'Books|Kids|Computers & Technology',
5372             11105 => 'Books|Kids|Cooking & Food',
5373             11106 => 'Books|Kids|Arts & Entertainment',
5374             11107 => 'Books|Kids|Arts & Entertainment|Art',
5375             11108 => 'Books|Kids|Arts & Entertainment|Crafts',
5376             11109 => 'Books|Kids|Arts & Entertainment|Music',
5377             11110 => 'Books|Kids|Arts & Entertainment|Performing Arts',
5378             11111 => 'Books|Kids|Family',
5379             11112 => 'Books|Kids|Fiction',
5380             11113 => 'Books|Kids|Fiction|Action & Adventure',
5381             11114 => 'Books|Kids|Fiction|Animals',
5382             11115 => 'Books|Kids|Fiction|Classics',
5383             11116 => 'Books|Kids|Fiction|Comics & Graphic Novels',
5384             11117 => 'Books|Kids|Fiction|Culture, Places & People',
5385             11118 => 'Books|Kids|Fiction|Family & Relationships',
5386             11119 => 'Books|Kids|Fiction|Fantasy',
5387             11120 => 'Books|Kids|Fiction|Fairy Tales, Myths & Fables',
5388             11121 => 'Books|Kids|Fiction|Favorite Characters',
5389             11122 => 'Books|Kids|Fiction|Historical',
5390             11123 => 'Books|Kids|Fiction|Holidays & Celebrations',
5391             11124 => 'Books|Kids|Fiction|Monsters & Ghosts',
5392             11125 => 'Books|Kids|Fiction|Mysteries',
5393             11126 => 'Books|Kids|Fiction|Nature',
5394             11127 => 'Books|Kids|Fiction|Religion',
5395             11128 => 'Books|Kids|Fiction|Sci-Fi',
5396             11129 => 'Books|Kids|Fiction|Social Issues',
5397             11130 => 'Books|Kids|Fiction|Sports & Recreation',
5398             11131 => 'Books|Kids|Fiction|Transportation',
5399             11132 => 'Books|Kids|Games & Activities',
5400             11133 => 'Books|Kids|General Nonfiction',
5401             11134 => 'Books|Kids|Health',
5402             11135 => 'Books|Kids|History',
5403             11136 => 'Books|Kids|Holidays & Celebrations',
5404             11137 => 'Books|Kids|Holidays & Celebrations|Birthdays',
5405             11138 => 'Books|Kids|Holidays & Celebrations|Christmas & Advent',
5406             11139 => 'Books|Kids|Holidays & Celebrations|Easter & Lent',
5407             11140 => 'Books|Kids|Holidays & Celebrations|General',
5408             11141 => 'Books|Kids|Holidays & Celebrations|Halloween',
5409             11142 => 'Books|Kids|Holidays & Celebrations|Hanukkah',
5410             11143 => 'Books|Kids|Holidays & Celebrations|Other',
5411             11144 => 'Books|Kids|Holidays & Celebrations|Passover',
5412             11145 => 'Books|Kids|Holidays & Celebrations|Patriotic Holidays',
5413             11146 => 'Books|Kids|Holidays & Celebrations|Ramadan',
5414             11147 => 'Books|Kids|Holidays & Celebrations|Thanksgiving',
5415             11148 => "Books|Kids|Holidays & Celebrations|Valentine's Day",
5416             11149 => 'Books|Kids|Humor',
5417             11150 => 'Books|Kids|Humor|Jokes & Riddles',
5418             11151 => 'Books|Kids|Poetry',
5419             11152 => 'Books|Kids|Learning to Read',
5420             11153 => 'Books|Kids|Learning to Read|Chapter Books',
5421             11154 => 'Books|Kids|Learning to Read|Early Readers',
5422             11155 => 'Books|Kids|Learning to Read|Intermediate Readers',
5423             11156 => 'Books|Kids|Nursery Rhymes',
5424             11157 => 'Books|Kids|Government',
5425             11158 => 'Books|Kids|Reference',
5426             11159 => 'Books|Kids|Religion',
5427             11160 => 'Books|Kids|Science & Nature',
5428             11161 => 'Books|Kids|Social Issues',
5429             11162 => 'Books|Kids|Social Studies',
5430             11163 => 'Books|Kids|Sports & Recreation',
5431             11164 => 'Books|Kids|Transportation',
5432             11165 => 'Books|Young Adult',
5433             11166 => 'Books|Young Adult|Animals',
5434             11167 => 'Books|Young Adult|Biography',
5435             11168 => 'Books|Young Adult|Careers & Occupations',
5436             11169 => 'Books|Young Adult|Computers & Technology',
5437             11170 => 'Books|Young Adult|Cooking & Food',
5438             11171 => 'Books|Young Adult|Arts & Entertainment',
5439             11172 => 'Books|Young Adult|Arts & Entertainment|Art',
5440             11173 => 'Books|Young Adult|Arts & Entertainment|Crafts',
5441             11174 => 'Books|Young Adult|Arts & Entertainment|Music',
5442             11175 => 'Books|Young Adult|Arts & Entertainment|Performing Arts',
5443             11176 => 'Books|Young Adult|Family',
5444             11177 => 'Books|Young Adult|Fiction',
5445             11178 => 'Books|Young Adult|Fiction|Action & Adventure',
5446             11179 => 'Books|Young Adult|Fiction|Animals',
5447             11180 => 'Books|Young Adult|Fiction|Classics',
5448             11181 => 'Books|Young Adult|Fiction|Comics & Graphic Novels',
5449             11182 => 'Books|Young Adult|Fiction|Culture, Places & People',
5450             11183 => 'Books|Young Adult|Fiction|Dystopian',
5451             11184 => 'Books|Young Adult|Fiction|Family & Relationships',
5452             11185 => 'Books|Young Adult|Fiction|Fantasy',
5453             11186 => 'Books|Young Adult|Fiction|Fairy Tales, Myths & Fables',
5454             11187 => 'Books|Young Adult|Fiction|Favorite Characters',
5455             11188 => 'Books|Young Adult|Fiction|Historical',
5456             11189 => 'Books|Young Adult|Fiction|Holidays & Celebrations',
5457             11190 => 'Books|Young Adult|Fiction|Horror, Monsters & Ghosts',
5458             11191 => 'Books|Young Adult|Fiction|Crime & Mystery',
5459             11192 => 'Books|Young Adult|Fiction|Nature',
5460             11193 => 'Books|Young Adult|Fiction|Religion',
5461             11194 => 'Books|Young Adult|Fiction|Romance',
5462             11195 => 'Books|Young Adult|Fiction|Sci-Fi',
5463             11196 => 'Books|Young Adult|Fiction|Coming of Age',
5464             11197 => 'Books|Young Adult|Fiction|Sports & Recreation',
5465             11198 => 'Books|Young Adult|Fiction|Transportation',
5466             11199 => 'Books|Young Adult|Games & Activities',
5467             11200 => 'Books|Young Adult|General Nonfiction',
5468             11201 => 'Books|Young Adult|Health',
5469             11202 => 'Books|Young Adult|History',
5470             11203 => 'Books|Young Adult|Holidays & Celebrations',
5471             11204 => 'Books|Young Adult|Holidays & Celebrations|Birthdays',
5472             11205 => 'Books|Young Adult|Holidays & Celebrations|Christmas & Advent',
5473             11206 => 'Books|Young Adult|Holidays & Celebrations|Easter & Lent',
5474             11207 => 'Books|Young Adult|Holidays & Celebrations|General',
5475             11208 => 'Books|Young Adult|Holidays & Celebrations|Halloween',
5476             11209 => 'Books|Young Adult|Holidays & Celebrations|Hanukkah',
5477             11210 => 'Books|Young Adult|Holidays & Celebrations|Other',
5478             11211 => 'Books|Young Adult|Holidays & Celebrations|Passover',
5479             11212 => 'Books|Young Adult|Holidays & Celebrations|Patriotic Holidays',
5480             11213 => 'Books|Young Adult|Holidays & Celebrations|Ramadan',
5481             11214 => 'Books|Young Adult|Holidays & Celebrations|Thanksgiving',
5482             11215 => "Books|Young Adult|Holidays & Celebrations|Valentine's Day",
5483             11216 => 'Books|Young Adult|Humor',
5484             11217 => 'Books|Young Adult|Humor|Jokes & Riddles',
5485             11218 => 'Books|Young Adult|Poetry',
5486             11219 => 'Books|Young Adult|Politics & Government',
5487             11220 => 'Books|Young Adult|Reference',
5488             11221 => 'Books|Young Adult|Religion',
5489             11222 => 'Books|Young Adult|Science & Nature',
5490             11223 => 'Books|Young Adult|Coming of Age',
5491             11224 => 'Books|Young Adult|Social Studies',
5492             11225 => 'Books|Young Adult|Sports & Recreation',
5493             11226 => 'Books|Young Adult|Transportation',
5494             11227 => 'Books|Communications & Media',
5495             11228 => 'Books|Military & Warfare',
5496             11229 => 'Books|Romance|Inspirational',
5497             11231 => 'Books|Romance|Holiday',
5498             11232 => 'Books|Romance|Wholesome',
5499             11233 => 'Books|Romance|Military',
5500             11234 => 'Books|Arts & Entertainment|Art History',
5501             11236 => 'Books|Arts & Entertainment|Design',
5502             11243 => 'Books|Business & Personal Finance|Accounting',
5503             11244 => 'Books|Business & Personal Finance|Hospitality',
5504             11245 => 'Books|Business & Personal Finance|Real Estate',
5505             11246 => 'Books|Humor|Jokes & Riddles',
5506             11247 => 'Books|Religion & Spirituality|Comparative Religion',
5507             11255 => 'Books|Cookbooks, Food & Wine|Culinary Arts',
5508             11259 => 'Books|Mysteries & Thrillers|Cozy',
5509             11260 => 'Books|Politics & Current Events|Current Events',
5510             11261 => 'Books|Politics & Current Events|Foreign Policy & International Relations',
5511             11262 => 'Books|Politics & Current Events|Local Government',
5512             11263 => 'Books|Politics & Current Events|National Government',
5513             11264 => 'Books|Politics & Current Events|Political Science',
5514             11265 => 'Books|Politics & Current Events|Public Administration',
5515             11266 => 'Books|Politics & Current Events|World Affairs',
5516             11273 => 'Books|Nonfiction|Family & Relationships|Family & Childcare',
5517             11274 => 'Books|Nonfiction|Family & Relationships|Love & Romance',
5518             11275 => 'Books|Sci-Fi & Fantasy|Fantasy|Urban',
5519             11276 => 'Books|Reference|Foreign Languages|Arabic',
5520             11277 => 'Books|Reference|Foreign Languages|Bilingual Editions',
5521             11278 => 'Books|Reference|Foreign Languages|African Languages',
5522             11279 => 'Books|Reference|Foreign Languages|Ancient Languages',
5523             11280 => 'Books|Reference|Foreign Languages|Chinese',
5524             11281 => 'Books|Reference|Foreign Languages|English',
5525             11282 => 'Books|Reference|Foreign Languages|French',
5526             11283 => 'Books|Reference|Foreign Languages|German',
5527             11284 => 'Books|Reference|Foreign Languages|Hebrew',
5528             11285 => 'Books|Reference|Foreign Languages|Hindi',
5529             11286 => 'Books|Reference|Foreign Languages|Italian',
5530             11287 => 'Books|Reference|Foreign Languages|Japanese',
5531             11288 => 'Books|Reference|Foreign Languages|Korean',
5532             11289 => 'Books|Reference|Foreign Languages|Linguistics',
5533             11290 => 'Books|Reference|Foreign Languages|Other Languages',
5534             11291 => 'Books|Reference|Foreign Languages|Portuguese',
5535             11292 => 'Books|Reference|Foreign Languages|Russian',
5536             11293 => 'Books|Reference|Foreign Languages|Spanish',
5537             11294 => 'Books|Reference|Foreign Languages|Speech Pathology',
5538             11295 => 'Books|Science & Nature|Mathematics|Advanced Mathematics',
5539             11296 => 'Books|Science & Nature|Mathematics|Algebra',
5540             11297 => 'Books|Science & Nature|Mathematics|Arithmetic',
5541             11298 => 'Books|Science & Nature|Mathematics|Calculus',
5542             11299 => 'Books|Science & Nature|Mathematics|Geometry',
5543             11300 => 'Books|Science & Nature|Mathematics|Statistics',
5544             11301 => 'Books|Professional & Technical|Medical|Veterinary',
5545             11302 => 'Books|Professional & Technical|Medical|Neuroscience',
5546             11303 => 'Books|Professional & Technical|Medical|Immunology',
5547             11304 => 'Books|Professional & Technical|Medical|Nursing',
5548             11305 => 'Books|Professional & Technical|Medical|Pharmacology & Toxicology',
5549             11306 => 'Books|Professional & Technical|Medical|Anatomy & Physiology',
5550             11307 => 'Books|Professional & Technical|Medical|Dentistry',
5551             11308 => 'Books|Professional & Technical|Medical|Emergency Medicine',
5552             11309 => 'Books|Professional & Technical|Medical|Genetics',
5553             11310 => 'Books|Professional & Technical|Medical|Psychiatry',
5554             11311 => 'Books|Professional & Technical|Medical|Radiology',
5555             11312 => 'Books|Professional & Technical|Medical|Alternative Medicine',
5556             11317 => 'Books|Nonfiction|Philosophy|Political Philosophy',
5557             11319 => 'Books|Nonfiction|Philosophy|Philosophy of Language',
5558             11320 => 'Books|Nonfiction|Philosophy|Philosophy of Religion',
5559             11327 => 'Books|Nonfiction|Social Science|Sociology',
5560             11329 => 'Books|Professional & Technical|Engineering|Aeronautics',
5561             11330 => 'Books|Professional & Technical|Engineering|Chemical & Petroleum Engineering',
5562             11331 => 'Books|Professional & Technical|Engineering|Civil Engineering',
5563             11332 => 'Books|Professional & Technical|Engineering|Computer Science',
5564             11333 => 'Books|Professional & Technical|Engineering|Electrical Engineering',
5565             11334 => 'Books|Professional & Technical|Engineering|Environmental Engineering',
5566             11335 => 'Books|Professional & Technical|Engineering|Mechanical Engineering',
5567             11336 => 'Books|Professional & Technical|Engineering|Power Resources',
5568             11337 => 'Books|Comics & Graphic Novels|Manga|Boys',
5569             11338 => 'Books|Comics & Graphic Novels|Manga|Men',
5570             11339 => 'Books|Comics & Graphic Novels|Manga|Girls',
5571             11340 => 'Books|Comics & Graphic Novels|Manga|Women',
5572             11341 => 'Books|Comics & Graphic Novels|Manga|Other',
5573             11342 => 'Books|Comics & Graphic Novels|Manga|Yaoi',
5574             11343 => 'Books|Comics & Graphic Novels|Manga|Comic Essays',
5575             12001 => 'Mac App Store|Business',
5576             12002 => 'Mac App Store|Developer Tools',
5577             12003 => 'Mac App Store|Education',
5578             12004 => 'Mac App Store|Entertainment',
5579             12005 => 'Mac App Store|Finance',
5580             12006 => 'Mac App Store|Games',
5581             12007 => 'Mac App Store|Health & Fitness',
5582             12008 => 'Mac App Store|Lifestyle',
5583             12010 => 'Mac App Store|Medical',
5584             12011 => 'Mac App Store|Music',
5585             12012 => 'Mac App Store|News',
5586             12013 => 'Mac App Store|Photography',
5587             12014 => 'Mac App Store|Productivity',
5588             12015 => 'Mac App Store|Reference',
5589             12016 => 'Mac App Store|Social Networking',
5590             12017 => 'Mac App Store|Sports',
5591             12018 => 'Mac App Store|Travel',
5592             12019 => 'Mac App Store|Utilities',
5593             12020 => 'Mac App Store|Video',
5594             12021 => 'Mac App Store|Weather',
5595             12022 => 'Mac App Store|Graphics & Design',
5596             12201 => 'Mac App Store|Games|Action',
5597             12202 => 'Mac App Store|Games|Adventure',
5598             12203 => 'Mac App Store|Games|Casual',
5599             12204 => 'Mac App Store|Games|Board',
5600             12205 => 'Mac App Store|Games|Card',
5601             12206 => 'Mac App Store|Games|Casino',
5602             12207 => 'Mac App Store|Games|Dice',
5603             12208 => 'Mac App Store|Games|Educational',
5604             12209 => 'Mac App Store|Games|Family',
5605             12210 => 'Mac App Store|Games|Kids',
5606             12211 => 'Mac App Store|Games|Music',
5607             12212 => 'Mac App Store|Games|Puzzle',
5608             12213 => 'Mac App Store|Games|Racing',
5609             12214 => 'Mac App Store|Games|Role Playing',
5610             12215 => 'Mac App Store|Games|Simulation',
5611             12216 => 'Mac App Store|Games|Sports',
5612             12217 => 'Mac App Store|Games|Strategy',
5613             12218 => 'Mac App Store|Games|Trivia',
5614             12219 => 'Mac App Store|Games|Word',
5615             13001 => 'App Store|Magazines & Newspapers|News & Politics',
5616             13002 => 'App Store|Magazines & Newspapers|Fashion & Style',
5617             13003 => 'App Store|Magazines & Newspapers|Home & Garden',
5618             13004 => 'App Store|Magazines & Newspapers|Outdoors & Nature',
5619             13005 => 'App Store|Magazines & Newspapers|Sports & Leisure',
5620             13006 => 'App Store|Magazines & Newspapers|Automotive',
5621             13007 => 'App Store|Magazines & Newspapers|Arts & Photography',
5622             13008 => 'App Store|Magazines & Newspapers|Brides & Weddings',
5623             13009 => 'App Store|Magazines & Newspapers|Business & Investing',
5624             13010 => "App Store|Magazines & Newspapers|Children's Magazines",
5625             13011 => 'App Store|Magazines & Newspapers|Computers & Internet',
5626             13012 => 'App Store|Magazines & Newspapers|Cooking, Food & Drink',
5627             13013 => 'App Store|Magazines & Newspapers|Crafts & Hobbies',
5628             13014 => 'App Store|Magazines & Newspapers|Electronics & Audio',
5629             13015 => 'App Store|Magazines & Newspapers|Entertainment',
5630             13017 => 'App Store|Magazines & Newspapers|Health, Mind & Body',
5631             13018 => 'App Store|Magazines & Newspapers|History',
5632             13019 => 'App Store|Magazines & Newspapers|Literary Magazines & Journals',
5633             13020 => "App Store|Magazines & Newspapers|Men's Interest",
5634             13021 => 'App Store|Magazines & Newspapers|Movies & Music',
5635             13023 => 'App Store|Magazines & Newspapers|Parenting & Family',
5636             13024 => 'App Store|Magazines & Newspapers|Pets',
5637             13025 => 'App Store|Magazines & Newspapers|Professional & Trade',
5638             13026 => 'App Store|Magazines & Newspapers|Regional News',
5639             13027 => 'App Store|Magazines & Newspapers|Science',
5640             13028 => 'App Store|Magazines & Newspapers|Teens',
5641             13029 => 'App Store|Magazines & Newspapers|Travel & Regional',
5642             13030 => "App Store|Magazines & Newspapers|Women's Interest",
5643             15000 => 'Textbooks|Arts & Entertainment',
5644             15001 => 'Textbooks|Arts & Entertainment|Art & Architecture',
5645             15002 => 'Textbooks|Arts & Entertainment|Art & Architecture|Urban Planning',
5646             15003 => 'Textbooks|Arts & Entertainment|Art History',
5647             15004 => 'Textbooks|Arts & Entertainment|Dance',
5648             15005 => 'Textbooks|Arts & Entertainment|Design',
5649             15006 => 'Textbooks|Arts & Entertainment|Fashion',
5650             15007 => 'Textbooks|Arts & Entertainment|Film',
5651             15008 => 'Textbooks|Arts & Entertainment|Games',
5652             15009 => 'Textbooks|Arts & Entertainment|Interior Design',
5653             15010 => 'Textbooks|Arts & Entertainment|Media Arts',
5654             15011 => 'Textbooks|Arts & Entertainment|Music',
5655             15012 => 'Textbooks|Arts & Entertainment|Performing Arts',
5656             15013 => 'Textbooks|Arts & Entertainment|Photography',
5657             15014 => 'Textbooks|Arts & Entertainment|Theater',
5658             15015 => 'Textbooks|Arts & Entertainment|TV',
5659             15016 => 'Textbooks|Arts & Entertainment|Visual Arts',
5660             15017 => 'Textbooks|Biographies & Memoirs',
5661             15018 => 'Textbooks|Business & Personal Finance',
5662             15019 => 'Textbooks|Business & Personal Finance|Accounting',
5663             15020 => 'Textbooks|Business & Personal Finance|Careers',
5664             15021 => 'Textbooks|Business & Personal Finance|Economics',
5665             15022 => 'Textbooks|Business & Personal Finance|Finance',
5666             15023 => 'Textbooks|Business & Personal Finance|Hospitality',
5667             15024 => 'Textbooks|Business & Personal Finance|Industries & Professions',
5668             15025 => 'Textbooks|Business & Personal Finance|Investing',
5669             15026 => 'Textbooks|Business & Personal Finance|Management & Leadership',
5670             15027 => 'Textbooks|Business & Personal Finance|Marketing & Sales',
5671             15028 => 'Textbooks|Business & Personal Finance|Personal Finance',
5672             15029 => 'Textbooks|Business & Personal Finance|Real Estate',
5673             15030 => 'Textbooks|Business & Personal Finance|Reference',
5674             15031 => 'Textbooks|Business & Personal Finance|Small Business & Entrepreneurship',
5675             15032 => 'Textbooks|Children & Teens',
5676             15033 => 'Textbooks|Children & Teens|Fiction',
5677             15034 => 'Textbooks|Children & Teens|Nonfiction',
5678             15035 => 'Textbooks|Comics & Graphic Novels',
5679             15036 => 'Textbooks|Comics & Graphic Novels|Graphic Novels',
5680             15037 => 'Textbooks|Comics & Graphic Novels|Manga',
5681             15038 => 'Textbooks|Communications & Media',
5682             15039 => 'Textbooks|Communications & Media|Broadcasting',
5683             15040 => 'Textbooks|Communications & Media|Digital Media',
5684             15041 => 'Textbooks|Communications & Media|Journalism',
5685             15042 => 'Textbooks|Communications & Media|Photojournalism',
5686             15043 => 'Textbooks|Communications & Media|Print',
5687             15044 => 'Textbooks|Communications & Media|Speech',
5688             15045 => 'Textbooks|Communications & Media|Writing',
5689             15046 => 'Textbooks|Computers & Internet',
5690             15047 => 'Textbooks|Computers & Internet|Computers',
5691             15048 => 'Textbooks|Computers & Internet|Databases',
5692             15049 => 'Textbooks|Computers & Internet|Digital Media',
5693             15050 => 'Textbooks|Computers & Internet|Internet',
5694             15051 => 'Textbooks|Computers & Internet|Network',
5695             15052 => 'Textbooks|Computers & Internet|Operating Systems',
5696             15053 => 'Textbooks|Computers & Internet|Programming',
5697             15054 => 'Textbooks|Computers & Internet|Software',
5698             15055 => 'Textbooks|Computers & Internet|System Administration',
5699             15056 => 'Textbooks|Cookbooks, Food & Wine',
5700             15057 => 'Textbooks|Cookbooks, Food & Wine|Beverages',
5701             15058 => 'Textbooks|Cookbooks, Food & Wine|Courses & Dishes',
5702             15059 => 'Textbooks|Cookbooks, Food & Wine|Culinary Arts',
5703             15060 => 'Textbooks|Cookbooks, Food & Wine|Methods',
5704             15061 => 'Textbooks|Cookbooks, Food & Wine|Reference',
5705             15062 => 'Textbooks|Cookbooks, Food & Wine|Regional & Ethnic',
5706             15063 => 'Textbooks|Cookbooks, Food & Wine|Special Diet',
5707             15064 => 'Textbooks|Cookbooks, Food & Wine|Special Occasions',
5708             15065 => 'Textbooks|Cookbooks, Food & Wine|Specific Ingredients',
5709             15066 => 'Textbooks|Engineering',
5710             15067 => 'Textbooks|Engineering|Aeronautics',
5711             15068 => 'Textbooks|Engineering|Chemical & Petroleum Engineering',
5712             15069 => 'Textbooks|Engineering|Civil Engineering',
5713             15070 => 'Textbooks|Engineering|Computer Science',
5714             15071 => 'Textbooks|Engineering|Electrical Engineering',
5715             15072 => 'Textbooks|Engineering|Environmental Engineering',
5716             15073 => 'Textbooks|Engineering|Mechanical Engineering',
5717             15074 => 'Textbooks|Engineering|Power Resources',
5718             15075 => 'Textbooks|Fiction & Literature',
5719             15076 => 'Textbooks|Fiction & Literature|Latino',
5720             15077 => 'Textbooks|Fiction & Literature|Action & Adventure',
5721             15078 => 'Textbooks|Fiction & Literature|African American',
5722             15079 => 'Textbooks|Fiction & Literature|Anthologies',
5723             15080 => 'Textbooks|Fiction & Literature|Classics',
5724             15081 => 'Textbooks|Fiction & Literature|Comparative Literature',
5725             15082 => 'Textbooks|Fiction & Literature|Erotica',
5726             15083 => 'Textbooks|Fiction & Literature|Gay',
5727             15084 => 'Textbooks|Fiction & Literature|Ghost',
5728             15085 => 'Textbooks|Fiction & Literature|Historical',
5729             15086 => 'Textbooks|Fiction & Literature|Horror',
5730             15087 => 'Textbooks|Fiction & Literature|Literary',
5731             15088 => 'Textbooks|Fiction & Literature|Literary Criticism',
5732             15089 => 'Textbooks|Fiction & Literature|Poetry',
5733             15090 => 'Textbooks|Fiction & Literature|Religious',
5734             15091 => 'Textbooks|Fiction & Literature|Short Stories',
5735             15092 => 'Textbooks|Health, Mind & Body',
5736             15093 => 'Textbooks|Health, Mind & Body|Fitness',
5737             15094 => 'Textbooks|Health, Mind & Body|Self-Improvement',
5738             15095 => 'Textbooks|History',
5739             15096 => 'Textbooks|History|Africa',
5740             15097 => 'Textbooks|History|Americas',
5741             15098 => 'Textbooks|History|Americas|Canada',
5742             15099 => 'Textbooks|History|Americas|Latin America',
5743             15100 => 'Textbooks|History|Americas|United States',
5744             15101 => 'Textbooks|History|Ancient',
5745             15102 => 'Textbooks|History|Asia',
5746             15103 => 'Textbooks|History|Australia & Oceania',
5747             15104 => 'Textbooks|History|Europe',
5748             15105 => 'Textbooks|History|Middle East',
5749             15106 => 'Textbooks|History|Military',
5750             15107 => 'Textbooks|History|World',
5751             15108 => 'Textbooks|Humor',
5752             15109 => 'Textbooks|Language Studies',
5753             15110 => 'Textbooks|Language Studies|African Languages',
5754             15111 => 'Textbooks|Language Studies|Ancient Languages',
5755             15112 => 'Textbooks|Language Studies|Arabic',
5756             15113 => 'Textbooks|Language Studies|Bilingual Editions',
5757             15114 => 'Textbooks|Language Studies|Chinese',
5758             15115 => 'Textbooks|Language Studies|English',
5759             15116 => 'Textbooks|Language Studies|French',
5760             15117 => 'Textbooks|Language Studies|German',
5761             15118 => 'Textbooks|Language Studies|Hebrew',
5762             15119 => 'Textbooks|Language Studies|Hindi',
5763             15120 => 'Textbooks|Language Studies|Indigenous Languages',
5764             15121 => 'Textbooks|Language Studies|Italian',
5765             15122 => 'Textbooks|Language Studies|Japanese',
5766             15123 => 'Textbooks|Language Studies|Korean',
5767             15124 => 'Textbooks|Language Studies|Linguistics',
5768             15125 => 'Textbooks|Language Studies|Other Language',
5769             15126 => 'Textbooks|Language Studies|Portuguese',
5770             15127 => 'Textbooks|Language Studies|Russian',
5771             15128 => 'Textbooks|Language Studies|Spanish',
5772             15129 => 'Textbooks|Language Studies|Speech Pathology',
5773             15130 => 'Textbooks|Lifestyle & Home',
5774             15131 => 'Textbooks|Lifestyle & Home|Antiques & Collectibles',
5775             15132 => 'Textbooks|Lifestyle & Home|Crafts & Hobbies',
5776             15133 => 'Textbooks|Lifestyle & Home|Gardening',
5777             15134 => 'Textbooks|Lifestyle & Home|Pets',
5778             15135 => 'Textbooks|Mathematics',
5779             15136 => 'Textbooks|Mathematics|Advanced Mathematics',
5780             15137 => 'Textbooks|Mathematics|Algebra',
5781             15138 => 'Textbooks|Mathematics|Arithmetic',
5782             15139 => 'Textbooks|Mathematics|Calculus',
5783             15140 => 'Textbooks|Mathematics|Geometry',
5784             15141 => 'Textbooks|Mathematics|Statistics',
5785             15142 => 'Textbooks|Medicine',
5786             15143 => 'Textbooks|Medicine|Anatomy & Physiology',
5787             15144 => 'Textbooks|Medicine|Dentistry',
5788             15145 => 'Textbooks|Medicine|Emergency Medicine',
5789             15146 => 'Textbooks|Medicine|Genetics',
5790             15147 => 'Textbooks|Medicine|Immunology',
5791             15148 => 'Textbooks|Medicine|Neuroscience',
5792             15149 => 'Textbooks|Medicine|Nursing',
5793             15150 => 'Textbooks|Medicine|Pharmacology & Toxicology',
5794             15151 => 'Textbooks|Medicine|Psychiatry',
5795             15152 => 'Textbooks|Medicine|Psychology',
5796             15153 => 'Textbooks|Medicine|Radiology',
5797             15154 => 'Textbooks|Medicine|Veterinary',
5798             15155 => 'Textbooks|Mysteries & Thrillers',
5799             15156 => 'Textbooks|Mysteries & Thrillers|British Detectives',
5800             15157 => 'Textbooks|Mysteries & Thrillers|Hard-Boiled',
5801             15158 => 'Textbooks|Mysteries & Thrillers|Historical',
5802             15159 => 'Textbooks|Mysteries & Thrillers|Police Procedural',
5803             15160 => 'Textbooks|Mysteries & Thrillers|Short Stories',
5804             15161 => 'Textbooks|Mysteries & Thrillers|Women Sleuths',
5805             15162 => 'Textbooks|Nonfiction',
5806             15163 => 'Textbooks|Nonfiction|Family & Relationships',
5807             15164 => 'Textbooks|Nonfiction|Transportation',
5808             15165 => 'Textbooks|Nonfiction|True Crime',
5809             15166 => 'Textbooks|Parenting',
5810             15167 => 'Textbooks|Philosophy',
5811             15168 => 'Textbooks|Philosophy|Aesthetics',
5812             15169 => 'Textbooks|Philosophy|Epistemology',
5813             15170 => 'Textbooks|Philosophy|Ethics',
5814             15171 => 'Textbooks|Philosophy|Philosophy of Language',
5815             15172 => 'Textbooks|Philosophy|Logic',
5816             15173 => 'Textbooks|Philosophy|Metaphysics',
5817             15174 => 'Textbooks|Philosophy|Political Philosophy',
5818             15175 => 'Textbooks|Philosophy|Philosophy of Religion',
5819             15176 => 'Textbooks|Politics & Current Events',
5820             15177 => 'Textbooks|Politics & Current Events|Current Events',
5821             15178 => 'Textbooks|Politics & Current Events|Foreign Policy & International Relations',
5822             15179 => 'Textbooks|Politics & Current Events|Local Governments',
5823             15180 => 'Textbooks|Politics & Current Events|National Governments',
5824             15181 => 'Textbooks|Politics & Current Events|Political Science',
5825             15182 => 'Textbooks|Politics & Current Events|Public Administration',
5826             15183 => 'Textbooks|Politics & Current Events|World Affairs',
5827             15184 => 'Textbooks|Professional & Technical',
5828             15185 => 'Textbooks|Professional & Technical|Design',
5829             15186 => 'Textbooks|Professional & Technical|Language Arts & Disciplines',
5830             15187 => 'Textbooks|Professional & Technical|Engineering',
5831             15188 => 'Textbooks|Professional & Technical|Law',
5832             15189 => 'Textbooks|Professional & Technical|Medical',
5833             15190 => 'Textbooks|Reference',
5834             15191 => 'Textbooks|Reference|Almanacs & Yearbooks',
5835             15192 => 'Textbooks|Reference|Atlases & Maps',
5836             15193 => 'Textbooks|Reference|Catalogs & Directories',
5837             15194 => 'Textbooks|Reference|Consumer Guides',
5838             15195 => 'Textbooks|Reference|Dictionaries & Thesauruses',
5839             15196 => 'Textbooks|Reference|Encyclopedias',
5840             15197 => 'Textbooks|Reference|Etiquette',
5841             15198 => 'Textbooks|Reference|Quotations',
5842             15199 => 'Textbooks|Reference|Study Aids',
5843             15200 => 'Textbooks|Reference|Words & Language',
5844             15201 => 'Textbooks|Reference|Writing',
5845             15202 => 'Textbooks|Religion & Spirituality',
5846             15203 => 'Textbooks|Religion & Spirituality|Bible Studies',
5847             15204 => 'Textbooks|Religion & Spirituality|Bibles',
5848             15205 => 'Textbooks|Religion & Spirituality|Buddhism',
5849             15206 => 'Textbooks|Religion & Spirituality|Christianity',
5850             15207 => 'Textbooks|Religion & Spirituality|Comparative Religion',
5851             15208 => 'Textbooks|Religion & Spirituality|Hinduism',
5852             15209 => 'Textbooks|Religion & Spirituality|Islam',
5853             15210 => 'Textbooks|Religion & Spirituality|Judaism',
5854             15211 => 'Textbooks|Religion & Spirituality|Spirituality',
5855             15212 => 'Textbooks|Romance',
5856             15213 => 'Textbooks|Romance|Contemporary',
5857             15214 => 'Textbooks|Romance|Erotic Romance',
5858             15215 => 'Textbooks|Romance|Paranormal',
5859             15216 => 'Textbooks|Romance|Historical',
5860             15217 => 'Textbooks|Romance|Short Stories',
5861             15218 => 'Textbooks|Romance|Suspense',
5862             15219 => 'Textbooks|Romance|Western',
5863             15220 => 'Textbooks|Sci-Fi & Fantasy',
5864             15221 => 'Textbooks|Sci-Fi & Fantasy|Fantasy',
5865             15222 => 'Textbooks|Sci-Fi & Fantasy|Fantasy|Contemporary',
5866             15223 => 'Textbooks|Sci-Fi & Fantasy|Fantasy|Epic',
5867             15224 => 'Textbooks|Sci-Fi & Fantasy|Fantasy|Historical',
5868             15225 => 'Textbooks|Sci-Fi & Fantasy|Fantasy|Paranormal',
5869             15226 => 'Textbooks|Sci-Fi & Fantasy|Fantasy|Short Stories',
5870             15227 => 'Textbooks|Sci-Fi & Fantasy|Science Fiction',
5871             15228 => 'Textbooks|Sci-Fi & Fantasy|Science Fiction & Literature',
5872             15229 => 'Textbooks|Sci-Fi & Fantasy|Science Fiction & Literature|Adventure',
5873             15230 => 'Textbooks|Sci-Fi & Fantasy|Science Fiction & Literature|High Tech',
5874             15231 => 'Textbooks|Sci-Fi & Fantasy|Science Fiction & Literature|Short Stories',
5875             15232 => 'Textbooks|Science & Nature',
5876             15233 => 'Textbooks|Science & Nature|Agriculture',
5877             15234 => 'Textbooks|Science & Nature|Astronomy',
5878             15235 => 'Textbooks|Science & Nature|Atmosphere',
5879             15236 => 'Textbooks|Science & Nature|Biology',
5880             15237 => 'Textbooks|Science & Nature|Chemistry',
5881             15238 => 'Textbooks|Science & Nature|Earth Sciences',
5882             15239 => 'Textbooks|Science & Nature|Ecology',
5883             15240 => 'Textbooks|Science & Nature|Environment',
5884             15241 => 'Textbooks|Science & Nature|Essays',
5885             15242 => 'Textbooks|Science & Nature|Geography',
5886             15243 => 'Textbooks|Science & Nature|Geology',
5887             15244 => 'Textbooks|Science & Nature|History',
5888             15245 => 'Textbooks|Science & Nature|Life Sciences',
5889             15246 => 'Textbooks|Science & Nature|Nature',
5890             15247 => 'Textbooks|Science & Nature|Physics',
5891             15248 => 'Textbooks|Science & Nature|Reference',
5892             15249 => 'Textbooks|Social Science',
5893             15250 => 'Textbooks|Social Science|Anthropology',
5894             15251 => 'Textbooks|Social Science|Archaeology',
5895             15252 => 'Textbooks|Social Science|Civics',
5896             15253 => 'Textbooks|Social Science|Government',
5897             15254 => 'Textbooks|Social Science|Social Studies',
5898             15255 => 'Textbooks|Social Science|Social Welfare',
5899             15256 => 'Textbooks|Social Science|Society',
5900             15257 => 'Textbooks|Social Science|Society|African Studies',
5901             15258 => 'Textbooks|Social Science|Society|American Studies',
5902             15259 => 'Textbooks|Social Science|Society|Asia Pacific Studies',
5903             15260 => 'Textbooks|Social Science|Society|Cross-Cultural Studies',
5904             15261 => 'Textbooks|Social Science|Society|European Studies',
5905             15262 => 'Textbooks|Social Science|Society|Immigration & Emigration',
5906             15263 => 'Textbooks|Social Science|Society|Indigenous Studies',
5907             15264 => 'Textbooks|Social Science|Society|Latin & Caribbean Studies',
5908             15265 => 'Textbooks|Social Science|Society|Middle Eastern Studies',
5909             15266 => 'Textbooks|Social Science|Society|Race & Ethnicity Studies',
5910             15267 => 'Textbooks|Social Science|Society|Sexuality Studies',
5911             15268 => "Textbooks|Social Science|Society|Women's Studies",
5912             15269 => 'Textbooks|Social Science|Sociology',
5913             15270 => 'Textbooks|Sports & Outdoors',
5914             15271 => 'Textbooks|Sports & Outdoors|Baseball',
5915             15272 => 'Textbooks|Sports & Outdoors|Basketball',
5916             15273 => 'Textbooks|Sports & Outdoors|Coaching',
5917             15274 => 'Textbooks|Sports & Outdoors|Equestrian',
5918             15275 => 'Textbooks|Sports & Outdoors|Extreme Sports',
5919             15276 => 'Textbooks|Sports & Outdoors|Football',
5920             15277 => 'Textbooks|Sports & Outdoors|Golf',
5921             15278 => 'Textbooks|Sports & Outdoors|Hockey',
5922             15279 => 'Textbooks|Sports & Outdoors|Motor Sports',
5923             15280 => 'Textbooks|Sports & Outdoors|Mountaineering',
5924             15281 => 'Textbooks|Sports & Outdoors|Outdoors',
5925             15282 => 'Textbooks|Sports & Outdoors|Racket Sports',
5926             15283 => 'Textbooks|Sports & Outdoors|Reference',
5927             15284 => 'Textbooks|Sports & Outdoors|Soccer',
5928             15285 => 'Textbooks|Sports & Outdoors|Training',
5929             15286 => 'Textbooks|Sports & Outdoors|Water Sports',
5930             15287 => 'Textbooks|Sports & Outdoors|Winter Sports',
5931             15288 => 'Textbooks|Teaching & Learning',
5932             15289 => 'Textbooks|Teaching & Learning|Adult Education',
5933             15290 => 'Textbooks|Teaching & Learning|Curriculum & Teaching',
5934             15291 => 'Textbooks|Teaching & Learning|Educational Leadership',
5935             15292 => 'Textbooks|Teaching & Learning|Educational Technology',
5936             15293 => 'Textbooks|Teaching & Learning|Family & Childcare',
5937             15294 => 'Textbooks|Teaching & Learning|Information & Library Science',
5938             15295 => 'Textbooks|Teaching & Learning|Learning Resources',
5939             15296 => 'Textbooks|Teaching & Learning|Psychology & Research',
5940             15297 => 'Textbooks|Teaching & Learning|Special Education',
5941             15298 => 'Textbooks|Travel & Adventure',
5942             15299 => 'Textbooks|Travel & Adventure|Africa',
5943             15300 => 'Textbooks|Travel & Adventure|Americas',
5944             15301 => 'Textbooks|Travel & Adventure|Americas|Canada',
5945             15302 => 'Textbooks|Travel & Adventure|Americas|Latin America',
5946             15303 => 'Textbooks|Travel & Adventure|Americas|United States',
5947             15304 => 'Textbooks|Travel & Adventure|Asia',
5948             15305 => 'Textbooks|Travel & Adventure|Caribbean',
5949             15306 => 'Textbooks|Travel & Adventure|Essays & Memoirs',
5950             15307 => 'Textbooks|Travel & Adventure|Europe',
5951             15308 => 'Textbooks|Travel & Adventure|Middle East',
5952             15309 => 'Textbooks|Travel & Adventure|Oceania',
5953             15310 => 'Textbooks|Travel & Adventure|Specialty Travel',
5954             15311 => 'Textbooks|Comics & Graphic Novels|Comics',
5955             15312 => 'Textbooks|Reference|Manuals',
5956             16001 => 'App Store|Stickers|Emoji & Expressions',
5957             16003 => 'App Store|Stickers|Animals & Nature',
5958             16005 => 'App Store|Stickers|Art',
5959             16006 => 'App Store|Stickers|Celebrations',
5960             16007 => 'App Store|Stickers|Celebrities',
5961             16008 => 'App Store|Stickers|Comics & Cartoons',
5962             16009 => 'App Store|Stickers|Eating & Drinking',
5963             16010 => 'App Store|Stickers|Gaming',
5964             16014 => 'App Store|Stickers|Movies & TV',
5965             16015 => 'App Store|Stickers|Music',
5966             16017 => 'App Store|Stickers|People',
5967             16019 => 'App Store|Stickers|Places & Objects',
5968             16021 => 'App Store|Stickers|Sports & Activities',
5969             16025 => 'App Store|Stickers|Kids & Family',
5970             16026 => 'App Store|Stickers|Fashion',
5971             100000 => 'Music|Christian & Gospel',
5972             100001 => 'Music|Classical|Art Song',
5973             100002 => 'Music|Classical|Brass & Woodwinds',
5974             100003 => 'Music|Classical|Solo Instrumental',
5975             100004 => 'Music|Classical|Contemporary Era',
5976             100005 => 'Music|Classical|Oratorio',
5977             100006 => 'Music|Classical|Cantata',
5978             100007 => 'Music|Classical|Electronic',
5979             100008 => 'Music|Classical|Sacred',
5980             100009 => 'Music|Classical|Guitar',
5981             100010 => 'Music|Classical|Piano',
5982             100011 => 'Music|Classical|Violin',
5983             100012 => 'Music|Classical|Cello',
5984             100013 => 'Music|Classical|Percussion',
5985             100014 => 'Music|Electronic|Dubstep',
5986             100015 => 'Music|Electronic|Bass',
5987             100016 => 'Music|Hip-Hop/Rap|UK Hip-Hop',
5988             100017 => 'Music|Reggae|Lovers Rock',
5989             100018 => 'Music|Alternative|EMO',
5990             100019 => 'Music|Alternative|Pop Punk',
5991             100020 => 'Music|Alternative|Indie Pop',
5992             100021 => 'Music|New Age|Yoga',
5993             100022 => 'Music|Pop|Tribute',
5994             100023 => 'Music|Pop|Shows',
5995             100024 => 'Music|Cuban',
5996             100025 => 'Music|Cuban|Mambo',
5997             100026 => 'Music|Cuban|Chachacha',
5998             100027 => 'Music|Cuban|Guajira',
5999             100028 => 'Music|Cuban|Son',
6000             100029 => 'Music|Cuban|Bolero',
6001             100030 => 'Music|Cuban|Guaracha',
6002             100031 => 'Music|Cuban|Timba',
6003             100032 => 'Music|Soundtrack|Video Game',
6004             100033 => 'Music|Indian|Regional Indian|Punjabi|Punjabi Pop',
6005             100034 => 'Music|Indian|Regional Indian|Bengali|Rabindra Sangeet',
6006             100035 => 'Music|Indian|Regional Indian|Malayalam',
6007             100036 => 'Music|Indian|Regional Indian|Kannada',
6008             100037 => 'Music|Indian|Regional Indian|Marathi',
6009             100038 => 'Music|Indian|Regional Indian|Gujarati',
6010             100039 => 'Music|Indian|Regional Indian|Assamese',
6011             100040 => 'Music|Indian|Regional Indian|Bhojpuri',
6012             100041 => 'Music|Indian|Regional Indian|Haryanvi',
6013             100042 => 'Music|Indian|Regional Indian|Odia',
6014             100043 => 'Music|Indian|Regional Indian|Rajasthani',
6015             100044 => 'Music|Indian|Regional Indian|Urdu',
6016             100045 => 'Music|Indian|Regional Indian|Punjabi',
6017             100046 => 'Music|Indian|Regional Indian|Bengali',
6018             100047 => 'Music|Indian|Indian Classical|Carnatic Classical',
6019             100048 => 'Music|Indian|Indian Classical|Hindustani Classical',
6020             100049 => 'Music|African|Afro House',
6021             100050 => 'Music|African|Afro Soul',
6022             100051 => 'Music|African|Afrobeats',
6023             100052 => 'Music|African|Benga',
6024             100053 => 'Music|African|Bongo-Flava',
6025             100054 => 'Music|African|Coupe-Decale',
6026             100055 => 'Music|African|Gqom',
6027             100056 => 'Music|African|Highlife',
6028             100057 => 'Music|African|Kuduro',
6029             100058 => 'Music|African|Kizomba',
6030             100059 => 'Music|African|Kwaito',
6031             100060 => 'Music|African|Mbalax',
6032             100061 => 'Music|African|Ndombolo',
6033             100062 => 'Music|African|Shangaan Electro',
6034             100063 => 'Music|African|Soukous',
6035             100064 => 'Music|African|Taarab',
6036             100065 => 'Music|African|Zouglou',
6037             100066 => 'Music|Turkish|Ozgun',
6038             100067 => 'Music|Turkish|Fantezi',
6039             100068 => 'Music|Turkish|Religious',
6040             100069 => 'Music|Pop|Turkish Pop',
6041             100070 => 'Music|Rock|Turkish Rock',
6042             100071 => 'Music|Alternative|Turkish Alternative',
6043             100072 => 'Music|Hip-Hop/Rap|Turkish Hip-Hop/Rap',
6044             100073 => 'Music|African|Maskandi',
6045             100074 => 'Music|Russian|Russian Romance',
6046             100075 => 'Music|Russian|Russian Bard',
6047             100076 => 'Music|Russian|Russian Pop',
6048             100077 => 'Music|Russian|Russian Rock',
6049             100078 => 'Music|Russian|Russian Hip-Hop',
6050             100079 => 'Music|Arabic|Levant',
6051             100080 => 'Music|Arabic|Levant|Dabke',
6052             100081 => 'Music|Arabic|Maghreb Rai',
6053             100082 => 'Music|Arabic|Khaleeji|Khaleeji Jalsat',
6054             100083 => 'Music|Arabic|Khaleeji|Khaleeji Shailat',
6055             100084 => 'Music|Tarab',
6056             100085 => 'Music|Tarab|Iraqi Tarab',
6057             100086 => 'Music|Tarab|Egyptian Tarab',
6058             100087 => 'Music|Tarab|Khaleeji Tarab',
6059             100088 => 'Music|Pop|Levant Pop',
6060             100089 => 'Music|Pop|Iraqi Pop',
6061             100090 => 'Music|Pop|Egyptian Pop',
6062             100091 => 'Music|Pop|Maghreb Pop',
6063             100092 => 'Music|Pop|Khaleeji Pop',
6064             100093 => 'Music|Hip-Hop/Rap|Levant Hip-Hop',
6065             100094 => 'Music|Hip-Hop/Rap|Egyptian Hip-Hop',
6066             100095 => 'Music|Hip-Hop/Rap|Maghreb Hip-Hop',
6067             100096 => 'Music|Hip-Hop/Rap|Khaleeji Hip-Hop',
6068             100097 => 'Music|Alternative|Indie Levant',
6069             100098 => 'Music|Alternative|Indie Egyptian',
6070             100099 => 'Music|Alternative|Indie Maghreb',
6071             100100 => 'Music|Electronic|Levant Electronic',
6072             100101 => "Music|Electronic|Electro-Cha'abi",
6073             100102 => 'Music|Electronic|Maghreb Electronic',
6074             100103 => 'Music|Folk|Iraqi Folk',
6075             100104 => 'Music|Folk|Khaleeji Folk',
6076             100105 => 'Music|Dance|Maghreb Dance',
6077             40000000 => 'iTunes U',
6078             40000001 => 'iTunes U|Business & Economics',
6079             40000002 => 'iTunes U|Business & Economics|Economics',
6080             40000003 => 'iTunes U|Business & Economics|Finance',
6081             40000004 => 'iTunes U|Business & Economics|Hospitality',
6082             40000005 => 'iTunes U|Business & Economics|Management',
6083             40000006 => 'iTunes U|Business & Economics|Marketing',
6084             40000007 => 'iTunes U|Business & Economics|Personal Finance',
6085             40000008 => 'iTunes U|Business & Economics|Real Estate',
6086             40000009 => 'iTunes U|Engineering',
6087             40000010 => 'iTunes U|Engineering|Chemical & Petroleum Engineering',
6088             40000011 => 'iTunes U|Engineering|Civil Engineering',
6089             40000012 => 'iTunes U|Engineering|Computer Science',
6090             40000013 => 'iTunes U|Engineering|Electrical Engineering',
6091             40000014 => 'iTunes U|Engineering|Environmental Engineering',
6092             40000015 => 'iTunes U|Engineering|Mechanical Engineering',
6093             40000016 => 'iTunes U|Music, Art, & Design',
6094             40000017 => 'iTunes U|Music, Art, & Design|Architecture',
6095             40000019 => 'iTunes U|Music, Art, & Design|Art History',
6096             40000020 => 'iTunes U|Music, Art, & Design|Dance',
6097             40000021 => 'iTunes U|Music, Art, & Design|Film',
6098             40000022 => 'iTunes U|Music, Art, & Design|Design',
6099             40000023 => 'iTunes U|Music, Art, & Design|Interior Design',
6100             40000024 => 'iTunes U|Music, Art, & Design|Music',
6101             40000025 => 'iTunes U|Music, Art, & Design|Theater',
6102             40000026 => 'iTunes U|Health & Medicine',
6103             40000027 => 'iTunes U|Health & Medicine|Anatomy & Physiology',
6104             40000028 => 'iTunes U|Health & Medicine|Behavioral Science',
6105             40000029 => 'iTunes U|Health & Medicine|Dentistry',
6106             40000030 => 'iTunes U|Health & Medicine|Diet & Nutrition',
6107             40000031 => 'iTunes U|Health & Medicine|Emergency Medicine',
6108             40000032 => 'iTunes U|Health & Medicine|Genetics',
6109             40000033 => 'iTunes U|Health & Medicine|Gerontology',
6110             40000034 => 'iTunes U|Health & Medicine|Health & Exercise Science',
6111             40000035 => 'iTunes U|Health & Medicine|Immunology',
6112             40000036 => 'iTunes U|Health & Medicine|Neuroscience',
6113             40000037 => 'iTunes U|Health & Medicine|Pharmacology & Toxicology',
6114             40000038 => 'iTunes U|Health & Medicine|Psychiatry',
6115             40000039 => 'iTunes U|Health & Medicine|Global Health',
6116             40000040 => 'iTunes U|Health & Medicine|Radiology',
6117             40000041 => 'iTunes U|History',
6118             40000042 => 'iTunes U|History|Ancient History',
6119             40000043 => 'iTunes U|History|Medieval History',
6120             40000044 => 'iTunes U|History|Military History',
6121             40000045 => 'iTunes U|History|Modern History',
6122             40000046 => 'iTunes U|History|African History',
6123             40000047 => 'iTunes U|History|Asia-Pacific History',
6124             40000048 => 'iTunes U|History|European History',
6125             40000049 => 'iTunes U|History|Middle Eastern History',
6126             40000050 => 'iTunes U|History|North American History',
6127             40000051 => 'iTunes U|History|South American History',
6128             40000053 => 'iTunes U|Communications & Journalism',
6129             40000054 => 'iTunes U|Philosophy',
6130             40000055 => 'iTunes U|Religion & Spirituality',
6131             40000056 => 'iTunes U|Languages',
6132             40000057 => 'iTunes U|Languages|African Languages',
6133             40000058 => 'iTunes U|Languages|Ancient Languages',
6134             40000061 => 'iTunes U|Languages|English',
6135             40000063 => 'iTunes U|Languages|French',
6136             40000064 => 'iTunes U|Languages|German',
6137             40000065 => 'iTunes U|Languages|Italian',
6138             40000066 => 'iTunes U|Languages|Linguistics',
6139             40000068 => 'iTunes U|Languages|Spanish',
6140             40000069 => 'iTunes U|Languages|Speech Pathology',
6141             40000070 => 'iTunes U|Writing & Literature',
6142             40000071 => 'iTunes U|Writing & Literature|Anthologies',
6143             40000072 => 'iTunes U|Writing & Literature|Biography',
6144             40000073 => 'iTunes U|Writing & Literature|Classics',
6145             40000074 => 'iTunes U|Writing & Literature|Literary Criticism',
6146             40000075 => 'iTunes U|Writing & Literature|Fiction',
6147             40000076 => 'iTunes U|Writing & Literature|Poetry',
6148             40000077 => 'iTunes U|Mathematics',
6149             40000078 => 'iTunes U|Mathematics|Advanced Mathematics',
6150             40000079 => 'iTunes U|Mathematics|Algebra',
6151             40000080 => 'iTunes U|Mathematics|Arithmetic',
6152             40000081 => 'iTunes U|Mathematics|Calculus',
6153             40000082 => 'iTunes U|Mathematics|Geometry',
6154             40000083 => 'iTunes U|Mathematics|Statistics',
6155             40000084 => 'iTunes U|Science',
6156             40000085 => 'iTunes U|Science|Agricultural',
6157             40000086 => 'iTunes U|Science|Astronomy',
6158             40000087 => 'iTunes U|Science|Atmosphere',
6159             40000088 => 'iTunes U|Science|Biology',
6160             40000089 => 'iTunes U|Science|Chemistry',
6161             40000090 => 'iTunes U|Science|Ecology',
6162             40000091 => 'iTunes U|Science|Geography',
6163             40000092 => 'iTunes U|Science|Geology',
6164             40000093 => 'iTunes U|Science|Physics',
6165             40000094 => 'iTunes U|Social Science',
6166             40000095 => 'iTunes U|Law & Politics|Law',
6167             40000096 => 'iTunes U|Law & Politics|Political Science',
6168             40000097 => 'iTunes U|Law & Politics|Public Administration',
6169             40000098 => 'iTunes U|Social Science|Psychology',
6170             40000099 => 'iTunes U|Social Science|Social Welfare',
6171             40000100 => 'iTunes U|Social Science|Sociology',
6172             40000101 => 'iTunes U|Society',
6173             40000103 => 'iTunes U|Society|Asia Pacific Studies',
6174             40000104 => 'iTunes U|Society|European Studies',
6175             40000105 => 'iTunes U|Society|Indigenous Studies',
6176             40000106 => 'iTunes U|Society|Latin & Caribbean Studies',
6177             40000107 => 'iTunes U|Society|Middle Eastern Studies',
6178             40000108 => "iTunes U|Society|Women's Studies",
6179             40000109 => 'iTunes U|Teaching & Learning',
6180             40000110 => 'iTunes U|Teaching & Learning|Curriculum & Teaching',
6181             40000111 => 'iTunes U|Teaching & Learning|Educational Leadership',
6182             40000112 => 'iTunes U|Teaching & Learning|Family & Childcare',
6183             40000113 => 'iTunes U|Teaching & Learning|Learning Resources',
6184             40000114 => 'iTunes U|Teaching & Learning|Psychology & Research',
6185             40000115 => 'iTunes U|Teaching & Learning|Special Education',
6186             40000116 => 'iTunes U|Music, Art, & Design|Culinary Arts',
6187             40000117 => 'iTunes U|Music, Art, & Design|Fashion',
6188             40000118 => 'iTunes U|Music, Art, & Design|Media Arts',
6189             40000119 => 'iTunes U|Music, Art, & Design|Photography',
6190             40000120 => 'iTunes U|Music, Art, & Design|Visual Art',
6191             40000121 => 'iTunes U|Business & Economics|Entrepreneurship',
6192             40000122 => 'iTunes U|Communications & Journalism|Broadcasting',
6193             40000123 => 'iTunes U|Communications & Journalism|Digital Media',
6194             40000124 => 'iTunes U|Communications & Journalism|Journalism',
6195             40000125 => 'iTunes U|Communications & Journalism|Photojournalism',
6196             40000126 => 'iTunes U|Communications & Journalism|Print',
6197             40000127 => 'iTunes U|Communications & Journalism|Speech',
6198             40000128 => 'iTunes U|Communications & Journalism|Writing',
6199             40000129 => 'iTunes U|Health & Medicine|Nursing',
6200             40000130 => 'iTunes U|Languages|Arabic',
6201             40000131 => 'iTunes U|Languages|Chinese',
6202             40000132 => 'iTunes U|Languages|Hebrew',
6203             40000133 => 'iTunes U|Languages|Hindi',
6204             40000134 => 'iTunes U|Languages|Indigenous Languages',
6205             40000135 => 'iTunes U|Languages|Japanese',
6206             40000136 => 'iTunes U|Languages|Korean',
6207             40000137 => 'iTunes U|Languages|Other Languages',
6208             40000138 => 'iTunes U|Languages|Portuguese',
6209             40000139 => 'iTunes U|Languages|Russian',
6210             40000140 => 'iTunes U|Law & Politics',
6211             40000141 => 'iTunes U|Law & Politics|Foreign Policy & International Relations',
6212             40000142 => 'iTunes U|Law & Politics|Local Governments',
6213             40000143 => 'iTunes U|Law & Politics|National Governments',
6214             40000144 => 'iTunes U|Law & Politics|World Affairs',
6215             40000145 => 'iTunes U|Writing & Literature|Comparative Literature',
6216             40000146 => 'iTunes U|Philosophy|Aesthetics',
6217             40000147 => 'iTunes U|Philosophy|Epistemology',
6218             40000148 => 'iTunes U|Philosophy|Ethics',
6219             40000149 => 'iTunes U|Philosophy|Metaphysics',
6220             40000150 => 'iTunes U|Philosophy|Political Philosophy',
6221             40000151 => 'iTunes U|Philosophy|Logic',
6222             40000152 => 'iTunes U|Philosophy|Philosophy of Language',
6223             40000153 => 'iTunes U|Philosophy|Philosophy of Religion',
6224             40000154 => 'iTunes U|Social Science|Archaeology',
6225             40000155 => 'iTunes U|Social Science|Anthropology',
6226             40000156 => 'iTunes U|Religion & Spirituality|Buddhism',
6227             40000157 => 'iTunes U|Religion & Spirituality|Christianity',
6228             40000158 => 'iTunes U|Religion & Spirituality|Comparative Religion',
6229             40000159 => 'iTunes U|Religion & Spirituality|Hinduism',
6230             40000160 => 'iTunes U|Religion & Spirituality|Islam',
6231             40000161 => 'iTunes U|Religion & Spirituality|Judaism',
6232             40000162 => 'iTunes U|Religion & Spirituality|Other Religions',
6233             40000163 => 'iTunes U|Religion & Spirituality|Spirituality',
6234             40000164 => 'iTunes U|Science|Environment',
6235             40000165 => 'iTunes U|Society|African Studies',
6236             40000166 => 'iTunes U|Society|American Studies',
6237             40000167 => 'iTunes U|Society|Cross-cultural Studies',
6238             40000168 => 'iTunes U|Society|Immigration & Emigration',
6239             40000169 => 'iTunes U|Society|Race & Ethnicity Studies',
6240             40000170 => 'iTunes U|Society|Sexuality Studies',
6241             40000171 => 'iTunes U|Teaching & Learning|Educational Technology',
6242             40000172 => 'iTunes U|Teaching & Learning|Information/Library Science',
6243             40000173 => 'iTunes U|Languages|Dutch',
6244             40000174 => 'iTunes U|Languages|Luxembourgish',
6245             40000175 => 'iTunes U|Languages|Swedish',
6246             40000176 => 'iTunes U|Languages|Norwegian',
6247             40000177 => 'iTunes U|Languages|Finnish',
6248             40000178 => 'iTunes U|Languages|Danish',
6249             40000179 => 'iTunes U|Languages|Polish',
6250             40000180 => 'iTunes U|Languages|Turkish',
6251             40000181 => 'iTunes U|Languages|Flemish',
6252             50000024 => 'Audiobooks',
6253             50000040 => 'Audiobooks|Fiction',
6254             50000041 => 'Audiobooks|Arts & Entertainment',
6255             50000042 => 'Audiobooks|Biographies & Memoirs',
6256             50000043 => 'Audiobooks|Business & Personal Finance',
6257             50000044 => 'Audiobooks|Kids & Young Adults',
6258             50000045 => 'Audiobooks|Classics',
6259             50000046 => 'Audiobooks|Comedy',
6260             50000047 => 'Audiobooks|Drama & Poetry',
6261             50000048 => 'Audiobooks|Speakers & Storytellers',
6262             50000049 => 'Audiobooks|History',
6263             50000050 => 'Audiobooks|Languages',
6264             50000051 => 'Audiobooks|Mysteries & Thrillers',
6265             50000052 => 'Audiobooks|Nonfiction',
6266             50000053 => 'Audiobooks|Religion & Spirituality',
6267             50000054 => 'Audiobooks|Science & Nature',
6268             50000055 => 'Audiobooks|Sci Fi & Fantasy',
6269             50000056 => 'Audiobooks|Self-Development',
6270             50000057 => 'Audiobooks|Sports & Outdoors',
6271             50000058 => 'Audiobooks|Technology',
6272             50000059 => 'Audiobooks|Travel & Adventure',
6273             50000061 => 'Music|Spoken Word',
6274             50000063 => 'Music|Disney',
6275             50000064 => 'Music|French Pop',
6276             50000066 => 'Music|German Pop',
6277             50000068 => 'Music|German Folk',
6278             50000069 => 'Audiobooks|Romance',
6279             50000070 => 'Audiobooks|Audiobooks Latino',
6280             50000071 => 'Books|Comics & Graphic Novels|Manga|Action',
6281             50000072 => 'Books|Comics & Graphic Novels|Manga|Comedy',
6282             50000073 => 'Books|Comics & Graphic Novels|Manga|Erotica',
6283             50000074 => 'Books|Comics & Graphic Novels|Manga|Fantasy',
6284             50000075 => 'Books|Comics & Graphic Novels|Manga|Four Cell Manga',
6285             50000076 => 'Books|Comics & Graphic Novels|Manga|Gay & Lesbian',
6286             50000077 => 'Books|Comics & Graphic Novels|Manga|Hard-Boiled',
6287             50000078 => 'Books|Comics & Graphic Novels|Manga|Heroes',
6288             50000079 => 'Books|Comics & Graphic Novels|Manga|Historical Fiction',
6289             50000080 => 'Books|Comics & Graphic Novels|Manga|Mecha',
6290             50000081 => 'Books|Comics & Graphic Novels|Manga|Mystery',
6291             50000082 => 'Books|Comics & Graphic Novels|Manga|Nonfiction',
6292             50000083 => 'Books|Comics & Graphic Novels|Manga|Religious',
6293             50000084 => 'Books|Comics & Graphic Novels|Manga|Romance',
6294             50000085 => 'Books|Comics & Graphic Novels|Manga|Romantic Comedy',
6295             50000086 => 'Books|Comics & Graphic Novels|Manga|Science Fiction',
6296             50000087 => 'Books|Comics & Graphic Novels|Manga|Sports',
6297             50000088 => 'Books|Fiction & Literature|Light Novels',
6298             50000089 => 'Books|Comics & Graphic Novels|Manga|Horror',
6299             50000090 => 'Books|Comics & Graphic Novels|Comics',
6300             50000091 => 'Books|Romance|Multicultural',
6301             50000092 => 'Audiobooks|Erotica',
6302             50000093 => 'Audiobooks|Light Novels',
6303             },
6304             },
6305             grup => { Name => 'Grouping', Avoid => 1 }, #10
6306             hdvd => { #10
6307             Name => 'HDVideo',
6308             Format => 'int8u', #24
6309             Writable => 'int8s', #27
6310             PrintConv => { 0 => 'No', 1 => 'Yes' },
6311             },
6312             keyw => 'Keyword', #7
6313             ldes => 'LongDescription', #10
6314             pcst => { #7
6315             Name => 'Podcast',
6316             Format => 'int8u', #23
6317             Writable => 'int8s', #27
6318             PrintConv => { 0 => 'No', 1 => 'Yes' },
6319             },
6320             perf => 'Performer',
6321             plID => {
6322             # (ref 10 called this PlayListID or TVSeason)
6323             Name => 'AlbumID', #28
6324             Format => 'int64u',
6325             Writable => 'int32s', #27
6326             },
6327             purd => 'PurchaseDate', #7
6328             purl => 'PodcastURL', #7
6329             rtng => { #10
6330             Name => 'Rating',
6331             Format => 'int8u', #23
6332             Writable => 'int8s', #27
6333             PrintConv => {
6334             0 => 'none',
6335             1 => 'Explicit',
6336             2 => 'Clean',
6337             4 => 'Explicit (old)',
6338             },
6339             },
6340             sfID => { #10
6341             Name => 'AppleStoreCountry',
6342             Format => 'int32u',
6343             Writable => 'int32s', #27
6344             SeparateTable => 1,
6345             PrintConv => { #21
6346             143441 => 'United States', # US
6347             143442 => 'France', # FR
6348             143443 => 'Germany', # DE
6349             143444 => 'United Kingdom', # GB
6350             143445 => 'Austria', # AT
6351             143446 => 'Belgium', # BE
6352             143447 => 'Finland', # FI
6353             143448 => 'Greece', # GR
6354             143449 => 'Ireland', # IE
6355             143450 => 'Italy', # IT
6356             143451 => 'Luxembourg', # LU
6357             143452 => 'Netherlands', # NL
6358             143453 => 'Portugal', # PT
6359             143454 => 'Spain', # ES
6360             143455 => 'Canada', # CA
6361             143456 => 'Sweden', # SE
6362             143457 => 'Norway', # NO
6363             143458 => 'Denmark', # DK
6364             143459 => 'Switzerland', # CH
6365             143460 => 'Australia', # AU
6366             143461 => 'New Zealand', # NZ
6367             143462 => 'Japan', # JP
6368             143463 => 'Hong Kong', # HK
6369             143464 => 'Singapore', # SG
6370             143465 => 'China', # CN
6371             143466 => 'Republic of Korea', # KR
6372             143467 => 'India', # IN
6373             143468 => 'Mexico', # MX
6374             143469 => 'Russia', # RU
6375             143470 => 'Taiwan', # TW
6376             143471 => 'Vietnam', # VN
6377             143472 => 'South Africa', # ZA
6378             143473 => 'Malaysia', # MY
6379             143474 => 'Philippines', # PH
6380             143475 => 'Thailand', # TH
6381             143476 => 'Indonesia', # ID
6382             143477 => 'Pakistan', # PK
6383             143478 => 'Poland', # PL
6384             143479 => 'Saudi Arabia', # SA
6385             143480 => 'Turkey', # TR
6386             143481 => 'United Arab Emirates', # AE
6387             143482 => 'Hungary', # HU
6388             143483 => 'Chile', # CL
6389             143484 => 'Nepal', # NP
6390             143485 => 'Panama', # PA
6391             143486 => 'Sri Lanka', # LK
6392             143487 => 'Romania', # RO
6393             143489 => 'Czech Republic', # CZ
6394             143491 => 'Israel', # IL
6395             143492 => 'Ukraine', # UA
6396             143493 => 'Kuwait', # KW
6397             143494 => 'Croatia', # HR
6398             143495 => 'Costa Rica', # CR
6399             143496 => 'Slovakia', # SK
6400             143497 => 'Lebanon', # LB
6401             143498 => 'Qatar', # QA
6402             143499 => 'Slovenia', # SI
6403             143501 => 'Colombia', # CO
6404             143502 => 'Venezuela', # VE
6405             143503 => 'Brazil', # BR
6406             143504 => 'Guatemala', # GT
6407             143505 => 'Argentina', # AR
6408             143506 => 'El Salvador', # SV
6409             143507 => 'Peru', # PE
6410             143508 => 'Dominican Republic', # DO
6411             143509 => 'Ecuador', # EC
6412             143510 => 'Honduras', # HN
6413             143511 => 'Jamaica', # JM
6414             143512 => 'Nicaragua', # NI
6415             143513 => 'Paraguay', # PY
6416             143514 => 'Uruguay', # UY
6417             143515 => 'Macau', # MO
6418             143516 => 'Egypt', # EG
6419             143517 => 'Kazakhstan', # KZ
6420             143518 => 'Estonia', # EE
6421             143519 => 'Latvia', # LV
6422             143520 => 'Lithuania', # LT
6423             143521 => 'Malta', # MT
6424             143523 => 'Moldova', # MD
6425             143524 => 'Armenia', # AM
6426             143525 => 'Botswana', # BW
6427             143526 => 'Bulgaria', # BG
6428             143528 => 'Jordan', # JO
6429             143529 => 'Kenya', # KE
6430             143530 => 'Macedonia', # MK
6431             143531 => 'Madagascar', # MG
6432             143532 => 'Mali', # ML
6433             143533 => 'Mauritius', # MU
6434             143534 => 'Niger', # NE
6435             143535 => 'Senegal', # SN
6436             143536 => 'Tunisia', # TN
6437             143537 => 'Uganda', # UG
6438             143538 => 'Anguilla', # AI
6439             143539 => 'Bahamas', # BS
6440             143540 => 'Antigua and Barbuda', # AG
6441             143541 => 'Barbados', # BB
6442             143542 => 'Bermuda', # BM
6443             143543 => 'British Virgin Islands', # VG
6444             143544 => 'Cayman Islands', # KY
6445             143545 => 'Dominica', # DM
6446             143546 => 'Grenada', # GD
6447             143547 => 'Montserrat', # MS
6448             143548 => 'St. Kitts and Nevis', # KN
6449             143549 => 'St. Lucia', # LC
6450             143550 => 'St. Vincent and The Grenadines', # VC
6451             143551 => 'Trinidad and Tobago', # TT
6452             143552 => 'Turks and Caicos', # TC
6453             143553 => 'Guyana', # GY
6454             143554 => 'Suriname', # SR
6455             143555 => 'Belize', # BZ
6456             143556 => 'Bolivia', # BO
6457             143557 => 'Cyprus', # CY
6458             143558 => 'Iceland', # IS
6459             143559 => 'Bahrain', # BH
6460             143560 => 'Brunei Darussalam', # BN
6461             143561 => 'Nigeria', # NG
6462             143562 => 'Oman', # OM
6463             143563 => 'Algeria', # DZ
6464             143564 => 'Angola', # AO
6465             143565 => 'Belarus', # BY
6466             143566 => 'Uzbekistan', # UZ
6467             143568 => 'Azerbaijan', # AZ
6468             143571 => 'Yemen', # YE
6469             143572 => 'Tanzania', # TZ
6470             143573 => 'Ghana', # GH
6471             143575 => 'Albania', # AL
6472             143576 => 'Benin', # BJ
6473             143577 => 'Bhutan', # BT
6474             143578 => 'Burkina Faso', # BF
6475             143579 => 'Cambodia', # KH
6476             143580 => 'Cape Verde', # CV
6477             143581 => 'Chad', # TD
6478             143582 => 'Republic of the Congo', # CG
6479             143583 => 'Fiji', # FJ
6480             143584 => 'Gambia', # GM
6481             143585 => 'Guinea-Bissau', # GW
6482             143586 => 'Kyrgyzstan', # KG
6483             143587 => "Lao People's Democratic Republic", # LA
6484             143588 => 'Liberia', # LR
6485             143589 => 'Malawi', # MW
6486             143590 => 'Mauritania', # MR
6487             143591 => 'Federated States of Micronesia', # FM
6488             143592 => 'Mongolia', # MN
6489             143593 => 'Mozambique', # MZ
6490             143594 => 'Namibia', # NA
6491             143595 => 'Palau', # PW
6492             143597 => 'Papua New Guinea', # PG
6493             143598 => 'Sao Tome and Principe', # ST (São Tomé and Príncipe)
6494             143599 => 'Seychelles', # SC
6495             143600 => 'Sierra Leone', # SL
6496             143601 => 'Solomon Islands', # SB
6497             143602 => 'Swaziland', # SZ
6498             143603 => 'Tajikistan', # TJ
6499             143604 => 'Turkmenistan', # TM
6500             143605 => 'Zimbabwe', # ZW
6501             },
6502             },
6503             soaa => 'SortAlbumArtist', #10
6504             soal => 'SortAlbum', #10
6505             soar => 'SortArtist', #10
6506             soco => 'SortComposer', #10
6507             sonm => 'SortName', #10
6508             sosn => 'SortShow', #10
6509             stik => { #10
6510             Name => 'MediaType',
6511             Format => 'int8u', #23
6512             Writable => 'int8s', #27
6513             PrintConvColumns => 2,
6514             PrintConv => { #(http://weblog.xanga.com/gryphondwb/615474010/iphone-ringtones---what-did-itunes-741-really-do.html)
6515             0 => 'Movie (old)', #forum9059 (was Movie)
6516             1 => 'Normal (Music)',
6517             2 => 'Audiobook',
6518             5 => 'Whacked Bookmark',
6519             6 => 'Music Video',
6520             9 => 'Movie', #forum9059 (was Short Film)
6521             10 => 'TV Show',
6522             11 => 'Booklet',
6523             14 => 'Ringtone',
6524             21 => 'Podcast', #15
6525             23 => 'iTunes U', #forum9059
6526             },
6527             },
6528             rate => 'RatingPercent', #PH
6529             titl => { Name => 'Title', Avoid => 1 },
6530             tven => 'TVEpisodeID', #7
6531             tves => { #7/10
6532             Name => 'TVEpisode',
6533             Format => 'int32u',
6534             Writable => 'int32s', #27
6535             },
6536             tvnn => 'TVNetworkName', #7
6537             tvsh => 'TVShow', #10
6538             tvsn => { #7/10
6539             Name => 'TVSeason',
6540             Format => 'int32u',
6541             },
6542             yrrc => 'Year', #(ffmpeg source)
6543             itnu => { #PH (iTunes 10.5)
6544             Name => 'iTunesU',
6545             Format => 'int8u', #27
6546             Writable => 'int8s', #27
6547             Description => 'iTunes U',
6548             PrintConv => { 0 => 'No', 1 => 'Yes' },
6549             },
6550             #https://github.com/communitymedia/mediautilities/blob/master/src/net/sourceforge/jaad/mp4/boxes/BoxTypes.java
6551             gshh => { Name => 'GoogleHostHeader', Format => 'string' },
6552             gspm => { Name => 'GooglePingMessage', Format => 'string' },
6553             gspu => { Name => 'GooglePingURL', Format => 'string' },
6554             gssd => { Name => 'GoogleSourceData', Format => 'string' },
6555             gsst => { Name => 'GoogleStartTime', Format => 'string' },
6556             gstd => {
6557             Name => 'GoogleTrackDuration',
6558             Format => 'string',
6559             ValueConv => '$val / 1000',
6560             ValueConvInv => '$val * 1000',
6561             PrintConv => 'ConvertDuration($val)',
6562             PrintConvInv => q{
6563             my $sign = ($val =~ s/^-//) ? -1 : 1;
6564             my @a = $val =~ /(\d+(?:\.\d+)?)/g;
6565             unshift @a, 0 while @a < 4;
6566             return $sign * (((($a[0] * 24) + $a[1]) * 60 + $a[2]) * 60 + $a[3]);
6567             },
6568             },
6569              
6570             # atoms observed in AAX audiobooks (ref PH)
6571             "\xa9cpy" => { Name => 'Copyright', Avoid => 1, Groups => { 2 => 'Author' } },
6572             "\xa9pub" => 'Publisher',
6573             "\xa9nrt" => 'Narrator',
6574             '@pti' => 'ParentTitle', # (guess -- same as "\xa9nam")
6575             '@PST' => 'ParentShortTitle', # (guess -- same as "\xa9nam")
6576             '@ppi' => 'ParentProductID', # (guess -- same as 'prID')
6577             '@sti' => 'ShortTitle', # (guess -- same as "\xa9nam")
6578             prID => 'ProductID',
6579             rldt => { Name => 'ReleaseDate', Groups => { 2 => 'Time' }},
6580             CDEK => { Name => 'Unknown_CDEK', Unknown => 1 }, # eg: "B004ZMTFEG" - used in URL's ("asin=")
6581             CDET => { Name => 'Unknown_CDET', Unknown => 1 }, # eg: "ADBL"
6582             VERS => 'ProductVersion',
6583             GUID => 'GUID',
6584             AACR => { Name => 'Unknown_AACR', Unknown => 1 }, # eg: "CR!1T1H1QH6WX7T714G2BMFX3E9MC4S"
6585             # ausr - 30 bytes (User Alias?)
6586             "\xa9xyz" => { #PH (written by Google Photos)
6587             Name => 'GPSCoordinates',
6588             Groups => { 2 => 'Location' },
6589             ValueConv => \&ConvertISO6709,
6590             ValueConvInv => \&ConvInvISO6709,
6591             PrintConv => \&PrintGPSCoordinates,
6592             PrintConvInv => \&PrintInvGPSCoordinates,
6593             },
6594             # the following tags written by iTunes 12.5.1.21
6595             # (ref https://www.ventismedia.com/mantis/view.php?id=14963
6596             # https://community.mp3tag.de/t/x-mp4-new-tag-problems/19488)
6597             "\xa9wrk" => 'Work', #PH
6598             "\xa9mvn" => 'MovementName', #PH
6599             "\xa9mvi" => { #PH
6600             Name => 'MovementNumber',
6601             Format => 'int16u', #27
6602             Writable => 'int16s', #27
6603             },
6604             "\xa9mvc" => { #PH
6605             Name => 'MovementCount',
6606             Format => 'int16u', #27
6607             Writable => 'int16s', #27
6608             },
6609             shwm => { #PH
6610             Name => 'ShowMovement',
6611             Format => 'int8u', #27
6612             Writable => 'int8s', #27
6613             PrintConv => { 0 => 'No', 1 => 'Yes' },
6614             },
6615             ownr => 'Owner', #PH (obscure) (ref ChrisAdan private communication)
6616             'xid ' => 'ISRC', #PH
6617             # found in DJI Osmo Action4 video
6618             tnal => { Name => 'ThumbnailImage', Binary => 1, Groups => { 2 => 'Preview' } },
6619             snal => { Name => 'PreviewImage', Binary => 1, Groups => { 2 => 'Preview' } },
6620             );
6621              
6622             # tag decoded from timed face records
6623             %Image::ExifTool::QuickTime::FaceInfo = (
6624             PROCESS_PROC => \&ProcessMOV,
6625             GROUPS => { 2 => 'Video' },
6626             crec => {
6627             Name => 'FaceRec',
6628             SubDirectory => {
6629             TagTable => 'Image::ExifTool::QuickTime::FaceRec',
6630             },
6631             },
6632             );
6633              
6634             # tag decoded from timed face records
6635             %Image::ExifTool::QuickTime::FaceRec = (
6636             PROCESS_PROC => \&ProcessMOV,
6637             GROUPS => { 2 => 'Video' },
6638             cits => {
6639             Name => 'FaceItem',
6640             SubDirectory => {
6641             TagTable => 'Image::ExifTool::QuickTime::Keys',
6642             ProcessProc => \&Process_mebx,
6643             },
6644             },
6645             );
6646              
6647             # item list keys (ref PH)
6648             %Image::ExifTool::QuickTime::Keys = (
6649             PROCESS_PROC => \&ProcessKeys,
6650             WRITE_PROC => \&WriteKeys,
6651             CHECK_PROC => \&CheckQTValue,
6652             VARS => { LONG_TAGS => 8 },
6653             WRITABLE => 1,
6654             # (not PREFERRED when writing)
6655             GROUPS => { 1 => 'Keys' },
6656             WRITE_GROUP => 'Keys',
6657             LANG_INFO => \&GetLangInfo,
6658             NOTES => q{
6659             This directory contains a list of key names which are used to decode tags
6660             written by the "mdta" handler. Also in this table are a few tags found in
6661             timed metadata that are not yet writable by ExifTool. The prefix of
6662             "com.apple.quicktime." has been removed from most TagID's below. These tags
6663             support alternate languages in the same way as the
6664             L tags. Note
6665             that by default,
6666             L and
6667             L tags are
6668             preferred when writing, so to create a tag when a same-named tag exists in
6669             either of these tables, either the "Keys" location must be specified (eg.
6670             C<-Keys:Author=Phil> on the command line), or the PREFERRED level must be
6671             changed via L.
6672             },
6673             version => 'Version',
6674             album => 'Album',
6675             artist => { },
6676             artwork => { },
6677             author => { Name => 'Author', Groups => { 2 => 'Author' } },
6678             comment => { },
6679             copyright => { Name => 'Copyright', Groups => { 2 => 'Author' } },
6680             creationdate=> {
6681             Name => 'CreationDate',
6682             Groups => { 2 => 'Time' },
6683             %iso8601Date,
6684             },
6685             description => { },
6686             director => { },
6687             displayname => { Name => 'DisplayName' },
6688             title => { }, #22
6689             genre => { },
6690             information => { },
6691             keywords => { },
6692             producer => { }, #22
6693             make => { Name => 'Make', Groups => { 2 => 'Camera' } },
6694             model => { Name => 'Model', Groups => { 2 => 'Camera' } },
6695             publisher => { },
6696             software => { },
6697             year => { Groups => { 2 => 'Time' } },
6698             'location.ISO6709' => {
6699             Name => 'GPSCoordinates',
6700             Groups => { 2 => 'Location' },
6701             Notes => q{
6702             Google Photos may ignore this if the coordinates have more than 5 digits
6703             after the decimal
6704             },
6705             ValueConv => \&ConvertISO6709,
6706             ValueConvInv => \&ConvInvISO6709,
6707             PrintConv => \&PrintGPSCoordinates,
6708             PrintConvInv => \&PrintInvGPSCoordinates,
6709             },
6710             'location.name' => { Name => 'LocationName', Groups => { 2 => 'Location' } },
6711             'location.body' => { Name => 'LocationBody', Groups => { 2 => 'Location' } },
6712             'location.note' => { Name => 'LocationNote', Groups => { 2 => 'Location' } },
6713             'location.role' => {
6714             Name => 'LocationRole',
6715             Groups => { 2 => 'Location' },
6716             PrintConv => {
6717             0 => 'Shooting Location',
6718             1 => 'Real Location',
6719             2 => 'Fictional Location',
6720             },
6721             },
6722             'location.date' => {
6723             Name => 'LocationDate',
6724             Groups => { 2 => 'Time' },
6725             %iso8601Date,
6726             },
6727             'location.accuracy.horizontal' => { Name => 'LocationAccuracyHorizontal' },
6728             'live-photo.auto' => { Name => 'LivePhotoAuto', Writable => 'int8u' },
6729             'live-photo.vitality-score' => { Name => 'LivePhotoVitalityScore', Writable => 'float' },
6730             'live-photo.vitality-scoring-version' => { Name => 'LivePhotoVitalityScoringVersion', Writable => 'int64s' },
6731             'apple.photos.variation-identifier' => { Name => 'ApplePhotosVariationIdentifier', Writable => 'int64s' },
6732             'direction.facing' => { Name => 'CameraDirection', Groups => { 2 => 'Location' } },
6733             'direction.motion' => { Name => 'CameraMotion', Groups => { 2 => 'Location' } },
6734             'player.version' => 'PlayerVersion',
6735             'player.movie.visual.brightness'=> 'Brightness',
6736             'player.movie.visual.color' => 'Color',
6737             'player.movie.visual.tint' => 'Tint',
6738             'player.movie.visual.contrast' => 'Contrast',
6739             'player.movie.audio.gain' => 'AudioGain',
6740             'player.movie.audio.treble' => 'Treble',
6741             'player.movie.audio.bass' => 'Bass',
6742             'player.movie.audio.balance' => 'Balance',
6743             'player.movie.audio.pitchshift' => 'PitchShift',
6744             'player.movie.audio.mute' => {
6745             Name => 'Mute',
6746             Format => 'int8u',
6747             PrintConv => { 0 => 'Off', 1 => 'On' },
6748             },
6749             'rating.user' => 'UserRating', # (Canon ELPH 510 HS)
6750             'collection.user' => 'UserCollection', #22
6751             'Encoded_With' => 'EncodedWith',
6752             'content.identifier' => 'ContentIdentifier', #forum14874
6753             'encoder' => { }, # forum15418 (written by ffmpeg)
6754             #
6755             # the following tags aren't in the com.apple.quicktime namespace:
6756             # (Note: must add any non-'com' prefix to %fullKeysID in WriteQuickTime.pl
6757             # to avoid adding the 'com.apple.quicktime' prefix when writing)
6758             #
6759             'com.android.version' => 'AndroidVersion',
6760             'com.android.capture.fps' => { Name => 'AndroidCaptureFPS', Writable => 'float' },
6761             'com.android.manufacturer' => 'AndroidMake',
6762             'com.android.model' => 'AndroidModel',
6763             'com.xiaomi.preview_video_cover' => { Name => 'XiaomiPreviewVideoCover', Writable => 'int32s' },
6764             'com.xiaomi.hdr10' => { Name => 'XiaomiHDR10', Writable => 'int32s' },
6765             'xiaomi.exifInfo.videoinfo' => 'XiaomiExifInfo',
6766             'samsung.android.utc_offset' => { Name => 'AndroidTimeZone', Groups => { 2 => 'Time' } },
6767             #
6768             # also seen
6769             #
6770             # com.divergentmedia.clipwrap.model ('NEX-FS700EK')
6771             # com.divergentmedia.clipwrap.model1 ('49')
6772             # com.divergentmedia.clipwrap.model2 ('0')
6773             # com.divergentmedia.clipwrap.manufacturer ('Sony')
6774             # com.divergentmedia.clipwrap.originalDateTime ('2013/2/6 10:30:40+0200')
6775             #
6776             # seen in timed metadata (mebx), and added dynamically to the table via SaveMetaKeys()
6777             # NOTE: these tags are not writable! (timed metadata cannot yet be written)
6778             #
6779             # (mdta)com.apple.quicktime.video-orientation (dtyp=66, int16s)
6780             'video-orientation' => {
6781             Name => 'VideoOrientation',
6782             Writable => 0,
6783             PrintConv => \%Image::ExifTool::Exif::orientation, #PH (NC)
6784             },
6785             # (mdta)com.apple.quicktime.live-photo-info (dtyp=com.apple.quicktime.com.apple.quicktime.live-photo-info)
6786             'live-photo-info' => {
6787             Name => 'LivePhotoInfo',
6788             Writable => 0,
6789             # not sure what these values mean, but unpack them anyway - PH
6790             # (ignore the fact that the "f" and "l" unpacks won't work on a big-endian machine)
6791             ValueConv => 'join " ",unpack "VfVVf6c4lCCcclf4Vvv", $val',
6792             },
6793             # (mdta)com.apple.quicktime.still-image-time (dtyp=65, int8s)
6794             'still-image-time' => { # (found in live photo)
6795             Name => 'StillImageTime',
6796             Writable => 0,
6797             Notes => q{
6798             this tag always has a value of -1; the time of the still image is obtained
6799             from the associated SampleTime
6800             },
6801             },
6802             # (mdta)com.apple.quicktime.detected-face (dtyp='com.apple.quicktime.detected-face')
6803             'detected-face' => {
6804             Name => 'FaceInfo',
6805             Writable => 0,
6806             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::FaceInfo' },
6807             },
6808             # ---- detected-face fields ( ----
6809             # --> back here after a round trip through FaceInfo -> FaceRec -> FaceItem
6810             # (fiel)com.apple.quicktime.detected-face.bounds (dtyp=80, float[8])
6811             'detected-face.bounds' => {
6812             Name => 'DetectedFaceBounds',
6813             Writable => 0,
6814             # round to a reasonable number of decimal places
6815             PrintConv => 'my @a=split " ",$val;$_=int($_*1e6+.5)/1e6 foreach @a;join " ",@a',
6816             PrintConvInv => '$val',
6817             },
6818             # (fiel)com.apple.quicktime.detected-face.face-id (dtyp=77, int32u)
6819             'detected-face.face-id' => { Name => 'DetectedFaceID', Writable => 0 },
6820             # (fiel)com.apple.quicktime.detected-face.roll-angle (dtyp=23, float)
6821             'detected-face.roll-angle' => { Name => 'DetectedFaceRollAngle', Writable => 0 },
6822             # (fiel)com.apple.quicktime.detected-face.yaw-angle (dtyp=23, float)
6823             'detected-face.yaw-angle' => { Name => 'DetectedFaceYawAngle', Writable => 0 },
6824             # the following tags generated by ShutterEncoder when "preserve metadata" is selected (forum15610)
6825             major_brand => { Name => 'MajorBrand', Avoid => 1 },
6826             minor_version => { Name => 'MinorVersion', Avoid => 1 },
6827             compatible_brands => { Name => 'CompatibleBrands', Avoid => 1 },
6828             creation_time => {
6829             Name => 'CreationTime',
6830             Groups => { 2 => 'Time' },
6831             Avoid => 1,
6832             %iso8601Date,
6833             },
6834             # (mdta)com.apple.quicktime.scene-illuminance
6835             'scene-illuminance' => {
6836             Name => 'SceneIlluminance',
6837             Notes => 'milli-lux',
6838             ValueConv => 'unpack("N", $val)',
6839             Writable => 0, # (don't make this writable because it is found in timed metadata)
6840             },
6841             'full-frame-rate-playback-intent' => 'FullFrameRatePlaybackIntent', #forum16824
6842             #
6843             # seen in Apple ProRes RAW file
6844             #
6845             # (mdta)com.apple.proapps.manufacturer (eg. "Sony")
6846             # (mdta)com.apple.proapps.exif.{Exif}.FNumber (float, eg. 1.0)
6847             # (mdta)org.smpte.rdd18.lens.irisfnumber (eg. "F1.0")
6848             # (mdta)com.apple.proapps.exif.{Exif}.ShutterSpeedValue (float, eg. 1.006)
6849             # (mdta)org.smpte.rdd18.camera.shutterspeed_angle (eg. "179.2deg")
6850             # (mdta)org.smpte.rdd18.camera.neutraldensityfilterwheelsetting (eg. "ND1")
6851             # (mdta)org.smpte.rdd18.camera.whitebalance (eg. "4300K")
6852             # (mdta)com.apple.proapps.exif.{Exif}.ExposureIndex (float, eg. 4000)
6853             # (mdta)org.smpte.rdd18.camera.isosensitivity (eg. "4000")
6854             # (mdta)com.apple.proapps.image.{TIFF}.Make (eg. "Atmos")
6855             # (mdta)com.apple.proapps.image.{TIFF}.Model (eg. "ShogunInferno")
6856             # (mdta)com.apple.proapps.image.{TIFF}.Software (eg. "9.0")
6857             );
6858              
6859             # Keys tags in the audio track (ref PH)
6860             %Image::ExifTool::QuickTime::AudioKeys = (
6861             PROCESS_PROC => \&ProcessKeys,
6862             WRITE_PROC => \&WriteKeys,
6863             CHECK_PROC => \&CheckQTValue,
6864             WRITABLE => 1,
6865             GROUPS => { 1 => 'AudioKeys', 2 => 'Audio' },
6866             WRITE_GROUP => 'AudioKeys',
6867             LANG_INFO => \&GetLangInfo,
6868             NOTES => q{
6869             Keys tags written in the audio track by some Apple devices. These tags
6870             belong to the ExifTool AudioKeys family 1 gorup.
6871             },
6872             'player.movie.audio.gain' => 'AudioGain',
6873             'player.movie.audio.treble' => 'Treble',
6874             'player.movie.audio.bass' => 'Bass',
6875             'player.movie.audio.balance' => 'Balance',
6876             'player.movie.audio.pitchshift' => 'PitchShift',
6877             'player.movie.audio.mute' => {
6878             Name => 'Mute',
6879             Format => 'int8u',
6880             PrintConv => { 0 => 'Off', 1 => 'On' },
6881             },
6882             );
6883              
6884             # Keys tags in the video track (ref PH)
6885             %Image::ExifTool::QuickTime::VideoKeys = (
6886             PROCESS_PROC => \&ProcessKeys,
6887             WRITE_PROC => \&WriteKeys,
6888             CHECK_PROC => \&CheckQTValue,
6889             VARS => { LONG_TAGS => 2 },
6890             WRITABLE => 1,
6891             GROUPS => { 1 => 'VideoKeys', 2 => 'Camera' },
6892             WRITE_GROUP => 'VideoKeys',
6893             LANG_INFO => \&GetLangInfo,
6894             NOTES => q{
6895             Keys tags written in the video track. These tags belong to the ExifTool
6896             VideoKeys family 1 gorup.
6897             },
6898             'camera.identifier' => 'CameraIdentifier',
6899             'camera.lens_model' => 'LensModel',
6900             'camera.focal_length.35mm_equivalent' => 'FocalLengthIn35mmFormat',
6901             'camera.framereadouttimeinmicroseconds' => {
6902             Name => 'FrameReadoutTime',
6903             ValueConv => '$val * 1e-6',
6904             ValueConvInv => 'int($val * 1e6 + 0.5)',
6905             PrintConv => '$val * 1e6 . " microseconds"',
6906             PrintConvInv => '$val =~ s/ .*//; $val * 1e-6',
6907             },
6908             'com.apple.photos.captureMode' => 'CaptureMode',
6909             );
6910              
6911             # iTunes info ('----') atoms
6912             %Image::ExifTool::QuickTime::iTunesInfo = (
6913             PROCESS_PROC => \&ProcessMOV,
6914             GROUPS => { 1 => 'iTunes', 2 => 'Audio' },
6915             VARS => { LONG_TAGS => 1 }, # (hack for discrepancy in the way long tags are counted in BuildTagLookup)
6916             NOTES => q{
6917             ExifTool will extract any iTunesInfo tags that exist, even if they are not
6918             defined in this table. These tags belong to the family 1 "iTunes" group,
6919             and are not currently writable.
6920             },
6921             # 'mean'/'name'/'data' atoms form a triplet, but unfortunately
6922             # I haven't been able to find any documentation on this.
6923             # 'mean' is normally 'com.apple.iTunes'
6924             mean => {
6925             Name => 'Mean',
6926             # the 'Triplet' flag tells ProcessMOV() to generate
6927             # a single tag from the mean/name/data triplet
6928             Triplet => 1,
6929             Hidden => 2,
6930             },
6931             name => {
6932             Name => 'Name',
6933             Triplet => 1,
6934             Hidden => 2,
6935             },
6936             data => {
6937             Name => 'Data',
6938             Triplet => 1,
6939             Hidden => 2,
6940             },
6941             # the tag ID's below are composed from "mean/name",
6942             # but "mean/" is omitted if it is "com.apple.iTunes/":
6943             'iTunMOVI' => {
6944             Name => 'iTunMOVI',
6945             SubDirectory => { TagTable => 'Image::ExifTool::PLIST::Main' },
6946             },
6947             'tool' => {
6948             Name => 'iTunTool',
6949             Description => 'iTunTool',
6950             Format => 'int32u',
6951             PrintConv => 'sprintf("0x%.8x",$val)',
6952             },
6953             'iTunEXTC' => {
6954             Name => 'ContentRating',
6955             Notes => 'standard | rating | score | reasons',
6956             # eg. 'us-tv|TV-14|500|V', 'mpaa|PG-13|300|For violence and sexuality'
6957             # (see http://shadowofged.blogspot.ca/2008/06/itunes-content-ratings.html)
6958             },
6959             'iTunNORM' => {
6960             Name => 'VolumeNormalization',
6961             PrintConv => '$val=~s/ 0+(\w)/ $1/g; $val=~s/^\s+//; $val',
6962             },
6963             'iTunSMPB' => {
6964             Name => 'iTunSMPB',
6965             Description => 'iTunSMPB',
6966             # hex format, similar to iTunNORM, but 12 words instead of 10,
6967             # and 4th word is 16 hex digits (all others are 8)
6968             # (gives AAC encoder delay, ref http://code.google.com/p/l-smash/issues/detail?id=1)
6969             PrintConv => '$val=~s/ 0+(\w)/ $1/g; $val=~s/^\s+//; $val',
6970             },
6971             # (CDDB = Compact Disc DataBase)
6972             # iTunes_CDDB_1 = +<# tracks>+...
6973             'iTunes_CDDB_1' => 'CDDB1Info',
6974             'iTunes_CDDB_TrackNumber' => 'CDDBTrackNumber',
6975             'Encoding Params' => {
6976             Name => 'EncodingParams',
6977             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::EncodingParams' },
6978             },
6979             # also heard about 'iTunPGAP', but I haven't seen a sample
6980             # all tags below were added based on samples I have seen - PH
6981             DISCNUMBER => 'DiscNumber',
6982             TRACKNUMBER => 'TrackNumber',
6983             ARTISTS => 'Artists',
6984             CATALOGNUMBER => 'CatalogNumber',
6985             RATING => 'Rating',
6986             MEDIA => 'Media',
6987             SCRIPT => 'Script', # character set? (seen 'Latn')
6988             BARCODE => 'Barcode',
6989             LABEL => 'Label',
6990             MOOD => 'Mood',
6991             DIRECTOR => 'Director',
6992             DIRECTOR_OF_PHOTOGRAPHY => 'DirectorOfPhotography',
6993             PRODUCTION_DESIGNER => 'ProductionDesigner',
6994             COSTUME_DESIGNER => 'CostumeDesigner',
6995             SCREENPLAY_BY => 'ScreenplayBy',
6996             EDITED_BY => 'EditedBy',
6997             PRODUCER => 'Producer',
6998             IMDB_ID => { },
6999             TMDB_ID => { },
7000             Actors => { },
7001             TIPL => { },
7002             popularimeter => 'Popularimeter',
7003             'Dynamic Range (DR)'=> 'DynamicRange',
7004             initialkey => 'InitialKey',
7005             originalyear => 'OriginalYear',
7006             originaldate => 'OriginalDate',
7007             '~length' => 'Length', # play length? (ie. duration?)
7008             replaygain_track_gain=>'ReplayTrackGain',
7009             replaygain_track_peak=>'ReplayTrackPeak',
7010             'Volume Level (ReplayGain)'=> 'ReplayVolumeLevel',
7011             'Dynamic Range (R128)'=> 'DynamicRangeR128',
7012             'Volume Level (R128)' => 'VolumeLevelR128',
7013             'Peak Level (Sample)' => 'PeakLevelSample',
7014             'Peak Level (R128)' => 'PeakLevelR128',
7015             # also seen (many from forum12777):
7016             # 'MusicBrainz Album Release Country'
7017             # 'MusicBrainz Album Type'
7018             # 'MusicBrainz Album Status'
7019             # 'MusicBrainz Track Id'
7020             # 'MusicBrainz Release Track Id'
7021             # 'MusicBrainz Album Id'
7022             # 'MusicBrainz Album Artist Id'
7023             # 'MusicBrainz Artist Id'
7024             # 'Acoustid Id' (sic)
7025             # 'Tool Version'
7026             # 'Tool Name'
7027             # 'ISRC'
7028             # 'HDCD'
7029             # 'Waveform'
7030             );
7031              
7032             # iTunes audio encoding parameters
7033             # ref https://developer.apple.com/library/mac/#documentation/MusicAudio/Reference/AudioCodecServicesRef/Reference/reference.html
7034             %Image::ExifTool::QuickTime::EncodingParams = (
7035             PROCESS_PROC => \&ProcessEncodingParams,
7036             GROUPS => { 2 => 'Audio' },
7037             # (I have commented out the ones that don't have integer values because they
7038             # probably don't appear, and definitely wouldn't work with current decoding - PH)
7039              
7040             # global codec properties
7041             #'lnam' => 'AudioCodecName',
7042             #'lmak' => 'AudioCodecManufacturer',
7043             #'lfor' => 'AudioCodecFormat',
7044             'vpk?' => 'AudioHasVariablePacketByteSizes',
7045             #'ifm#' => 'AudioSupportedInputFormats',
7046             #'ofm#' => 'AudioSupportedOutputFormats',
7047             #'aisr' => 'AudioAvailableInputSampleRates',
7048             #'aosr' => 'AudioAvailableOutputSampleRates',
7049             'abrt' => 'AudioAvailableBitRateRange',
7050             'mnip' => 'AudioMinimumNumberInputPackets',
7051             'mnop' => 'AudioMinimumNumberOutputPackets',
7052             'cmnc' => 'AudioAvailableNumberChannels',
7053             'lmrc' => 'AudioDoesSampleRateConversion',
7054             #'aicl' => 'AudioAvailableInputChannelLayoutTags',
7055             #'aocl' => 'AudioAvailableOutputChannelLayoutTags',
7056             #'if4o' => 'AudioInputFormatsForOutputFormat',
7057             #'of4i' => 'AudioOutputFormatsForInputFormat',
7058             #'acfi' => 'AudioFormatInfo',
7059              
7060             # instance codec properties
7061             'tbuf' => 'AudioInputBufferSize',
7062             'pakf' => 'AudioPacketFrameSize',
7063             'pakb' => 'AudioMaximumPacketByteSize',
7064             #'ifmt' => 'AudioCurrentInputFormat',
7065             #'ofmt' => 'AudioCurrentOutputFormat',
7066             #'kuki' => 'AudioMagicCookie',
7067             'ubuf' => 'AudioUsedInputBufferSize',
7068             'init' => 'AudioIsInitialized',
7069             'brat' => 'AudioCurrentTargetBitRate',
7070             #'cisr' => 'AudioCurrentInputSampleRate',
7071             #'cosr' => 'AudioCurrentOutputSampleRate',
7072             'srcq' => 'AudioQualitySetting',
7073             #'brta' => 'AudioApplicableBitRateRange',
7074             #'isra' => 'AudioApplicableInputSampleRates',
7075             #'osra' => 'AudioApplicableOutputSampleRates',
7076             'pad0' => 'AudioZeroFramesPadded',
7077             'prmm' => 'AudioCodecPrimeMethod',
7078             #'prim' => 'AudioCodecPrimeInfo',
7079             #'icl ' => 'AudioInputChannelLayout',
7080             #'ocl ' => 'AudioOutputChannelLayout',
7081             #'acs ' => 'AudioCodecSettings',
7082             #'acfl' => 'AudioCodecFormatList',
7083             'acbf' => 'AudioBitRateControlMode',
7084             'vbrq' => 'AudioVBRQuality',
7085             'mdel' => 'AudioMinimumDelayMode',
7086              
7087             # deprecated
7088             'pakd' => 'AudioRequiresPacketDescription',
7089             #'brt#' => 'AudioAvailableBitRates',
7090             'acef' => 'AudioExtendFrequencies',
7091             'ursr' => 'AudioUseRecommendedSampleRate',
7092             'oppr' => 'AudioOutputPrecedence',
7093             #'loud' => 'AudioCurrentLoudnessStatistics',
7094              
7095             # others
7096             'vers' => 'AudioEncodingParamsVersion', #PH
7097             'cdcv' => { #PH
7098             Name => 'AudioComponentVersion',
7099             ValueConv => 'join ".", unpack("ncc", pack("N",$val))',
7100             },
7101             );
7102              
7103             # print to video data block
7104             %Image::ExifTool::QuickTime::Video = (
7105             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
7106             GROUPS => { 2 => 'Video' },
7107             0 => {
7108             Name => 'DisplaySize',
7109             PrintConv => {
7110             0 => 'Normal',
7111             1 => 'Double Size',
7112             2 => 'Half Size',
7113             3 => 'Full Screen',
7114             4 => 'Current Size',
7115             },
7116             },
7117             6 => {
7118             Name => 'SlideShow',
7119             PrintConv => {
7120             0 => 'No',
7121             1 => 'Yes',
7122             },
7123             },
7124             );
7125              
7126             # 'hnti' atoms
7127             %Image::ExifTool::QuickTime::HintInfo = (
7128             PROCESS_PROC => \&ProcessMOV,
7129             GROUPS => { 2 => 'Video' },
7130             'rtp ' => {
7131             Name => 'RealtimeStreamingProtocol',
7132             PrintConv => '$val=~s/^sdp /(SDP) /; $val',
7133             },
7134             'sdp ' => 'StreamingDataProtocol',
7135             );
7136              
7137             # 'hinf' atoms
7138             %Image::ExifTool::QuickTime::HintTrackInfo = (
7139             PROCESS_PROC => \&ProcessMOV,
7140             GROUPS => { 2 => 'Video' },
7141             trpY => { Name => 'TotalBytes', Format => 'int64u' }, #(documented)
7142             trpy => { Name => 'TotalBytes', Format => 'int64u' }, #(observed)
7143             totl => { Name => 'TotalBytes', Format => 'int32u' },
7144             nump => { Name => 'NumPackets', Format => 'int64u' },
7145             npck => { Name => 'NumPackets', Format => 'int32u' },
7146             tpyl => { Name => 'TotalBytesNoRTPHeaders', Format => 'int64u' },
7147             tpaY => { Name => 'TotalBytesNoRTPHeaders', Format => 'int32u' }, #(documented)
7148             tpay => { Name => 'TotalBytesNoRTPHeaders', Format => 'int32u' }, #(observed)
7149             maxr => {
7150             Name => 'MaxDataRate',
7151             Format => 'int32u',
7152             Count => 2,
7153             PrintConv => 'my @a=split(" ",$val);sprintf("%d bytes in %.3f s",$a[1],$a[0]/1000)',
7154             },
7155             dmed => { Name => 'MediaTrackBytes', Format => 'int64u' },
7156             dimm => { Name => 'ImmediateDataBytes', Format => 'int64u' },
7157             drep => { Name => 'RepeatedDataBytes', Format => 'int64u' },
7158             tmin => {
7159             Name => 'MinTransmissionTime',
7160             Format => 'int32u',
7161             PrintConv => 'sprintf("%.3f s",$val/1000)',
7162             },
7163             tmax => {
7164             Name => 'MaxTransmissionTime',
7165             Format => 'int32u',
7166             PrintConv => 'sprintf("%.3f s",$val/1000)',
7167             },
7168             pmax => { Name => 'LargestPacketSize', Format => 'int32u' },
7169             dmax => {
7170             Name => 'LargestPacketDuration',
7171             Format => 'int32u',
7172             PrintConv => 'sprintf("%.3f s",$val/1000)',
7173             },
7174             payt => {
7175             Name => 'PayloadType',
7176             Format => 'undef', # (necessary to prevent decoding as string!)
7177             ValueConv => 'unpack("N",$val) . " " . substr($val, 5)',
7178             PrintConv => '$val=~s/ /, /;$val',
7179             },
7180             );
7181              
7182             # MP4 media box (ref 5)
7183             %Image::ExifTool::QuickTime::Media = (
7184             PROCESS_PROC => \&ProcessMOV,
7185             WRITE_PROC => \&WriteQuickTime,
7186             GROUPS => { 1 => 'Track#', 2 => 'Video' },
7187             NOTES => 'MP4 media box.',
7188             mdhd => {
7189             Name => 'MediaHeader',
7190             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::MediaHeader' },
7191             },
7192             hdlr => {
7193             Name => 'Handler',
7194             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Handler' },
7195             },
7196             minf => {
7197             Name => 'MediaInfo',
7198             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::MediaInfo' },
7199             },
7200             elng => 'ExtendedLanguageTag', #29 (NC) eg. "zh-CN"
7201             );
7202              
7203             # MP4 media header box (ref 5)
7204             %Image::ExifTool::QuickTime::MediaHeader = (
7205             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
7206             WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
7207             CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
7208             GROUPS => { 1 => 'Track#', 2 => 'Video' },
7209             FORMAT => 'int32u',
7210             DATAMEMBER => [ 0, 1, 2, 3, 4 ],
7211             0 => {
7212             Name => 'MediaHeaderVersion',
7213             RawConv => '$$self{MediaHeaderVersion} = $val',
7214             },
7215             1 => {
7216             Name => 'MediaCreateDate',
7217             Groups => { 2 => 'Time' },
7218             %timeInfo,
7219             # this is int64u if MediaHeaderVersion == 1 (ref 5/13)
7220             Hook => '$$self{MediaHeaderVersion} and $format = "int64u", $varSize += 4',
7221             },
7222             2 => {
7223             Name => 'MediaModifyDate',
7224             Groups => { 2 => 'Time' },
7225             %timeInfo,
7226             # this is int64u if MediaHeaderVersion == 1 (ref 5/13)
7227             Hook => '$$self{MediaHeaderVersion} and $format = "int64u", $varSize += 4',
7228             },
7229             3 => {
7230             Name => 'MediaTimeScale',
7231             RawConv => '$$self{MediaTS} = $val',
7232             },
7233             4 => {
7234             Name => 'MediaDuration',
7235             RawConv => '$$self{MediaTS} ? $val / $$self{MediaTS} : $val',
7236             PrintConv => '$$self{MediaTS} ? ConvertDuration($val) : $val',
7237             # this is int64u if MediaHeaderVersion == 1 (ref 5/13)
7238             Hook => '$$self{MediaHeaderVersion} and $format = "int64u", $varSize += 4',
7239             },
7240             5 => {
7241             Name => 'MediaLanguageCode',
7242             Format => 'int16u',
7243             RawConv => '$val ? $val : undef',
7244             # allow both Macintosh (for MOV files) and ISO (for MP4 files) language codes
7245             ValueConv => '($val < 0x400 or $val == 0x7fff) ? $val : pack "C*", map { (($val>>$_)&0x1f)+0x60 } 10, 5, 0',
7246             PrintConv => q{
7247             return $val unless $val =~ /^\d+$/;
7248             require Image::ExifTool::Font;
7249             return $Image::ExifTool::Font::ttLang{Macintosh}{$val} || "Unknown ($val)";
7250             },
7251             },
7252             );
7253              
7254             # MP4 media information box (ref 5)
7255             %Image::ExifTool::QuickTime::MediaInfo = (
7256             PROCESS_PROC => \&ProcessMOV,
7257             WRITE_PROC => \&WriteQuickTime,
7258             GROUPS => { 1 => 'Track#', 2 => 'Video' },
7259             NOTES => 'MP4 media info box.',
7260             vmhd => {
7261             Name => 'VideoHeader',
7262             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::VideoHeader' },
7263             },
7264             smhd => {
7265             Name => 'AudioHeader',
7266             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::AudioHeader' },
7267             },
7268             hmhd => {
7269             Name => 'HintHeader',
7270             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::HintHeader' },
7271             },
7272             nmhd => {
7273             Name => 'NullMediaHeader',
7274             Flags => ['Binary','Unknown'],
7275             },
7276             dinf => {
7277             Name => 'DataInfo', # (don't change this name -- used to recognize directory when writing)
7278             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::DataInfo' },
7279             },
7280             gmhd => {
7281             Name => 'GenMediaHeader',
7282             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::GenMediaHeader' },
7283             },
7284             hdlr => { #PH
7285             Name => 'Handler',
7286             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Handler' },
7287             },
7288             stbl => {
7289             Name => 'SampleTable',
7290             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::SampleTable' },
7291             },
7292             );
7293              
7294             # MP4 video media header (ref 5)
7295             %Image::ExifTool::QuickTime::VideoHeader = (
7296             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
7297             GROUPS => { 2 => 'Video' },
7298             NOTES => 'MP4 video media header.',
7299             FORMAT => 'int16u',
7300             2 => {
7301             Name => 'GraphicsMode',
7302             PrintHex => 1,
7303             SeparateTable => 'GraphicsMode',
7304             PrintConv => \%graphicsMode,
7305             },
7306             3 => { Name => 'OpColor', Format => 'int16u[3]' },
7307             );
7308              
7309             # MP4 audio media header (ref 5)
7310             %Image::ExifTool::QuickTime::AudioHeader = (
7311             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
7312             GROUPS => { 2 => 'Audio' },
7313             NOTES => 'MP4 audio media header.',
7314             FORMAT => 'int16u',
7315             2 => { Name => 'Balance', Format => 'fixed16s' },
7316             );
7317              
7318             # MP4 hint media header (ref 5)
7319             %Image::ExifTool::QuickTime::HintHeader = (
7320             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
7321             NOTES => 'MP4 hint media header.',
7322             FORMAT => 'int16u',
7323             2 => 'MaxPDUSize',
7324             3 => 'AvgPDUSize',
7325             4 => { Name => 'MaxBitrate', Format => 'int32u', PrintConv => 'ConvertBitrate($val)' },
7326             6 => { Name => 'AvgBitrate', Format => 'int32u', PrintConv => 'ConvertBitrate($val)' },
7327             );
7328              
7329             # MP4 sample table box (ref 5)
7330             %Image::ExifTool::QuickTime::SampleTable = (
7331             PROCESS_PROC => \&ProcessMOV,
7332             WRITE_PROC => \&WriteQuickTime,
7333             GROUPS => { 2 => 'Video' },
7334             NOTES => 'MP4 sample table box.',
7335             stsd => [
7336             {
7337             Name => 'AudioSampleDesc',
7338             Condition => '$$self{HandlerType} and $$self{HandlerType} eq "soun"',
7339             SubDirectory => {
7340             TagTable => 'Image::ExifTool::QuickTime::AudioSampleDesc',
7341             ProcessProc => \&ProcessSampleDesc,
7342             },
7343             },{
7344             Name => 'VisualSampleDesc',
7345             Condition => '$$self{HandlerType} and $$self{HandlerType} eq "vide"',
7346             SubDirectory => {
7347             TagTable => 'Image::ExifTool::QuickTime::VisualSampleDesc',
7348             ProcessProc => \&ProcessSampleDesc,
7349             },
7350             },{
7351             Name => 'HintSampleDesc',
7352             Condition => '$$self{HandlerType} and $$self{HandlerType} eq "hint"',
7353             SubDirectory => {
7354             TagTable => 'Image::ExifTool::QuickTime::HintSampleDesc',
7355             ProcessProc => \&ProcessSampleDesc,
7356             },
7357             },{
7358             Name => 'MetaSampleDesc',
7359             Condition => '$$self{HandlerType} and $$self{HandlerType} eq "meta"',
7360             SubDirectory => {
7361             TagTable => 'Image::ExifTool::QuickTime::MetaSampleDesc',
7362             ProcessProc => \&ProcessSampleDesc,
7363             },
7364             },{
7365             Name => 'OtherSampleDesc',
7366             SubDirectory => {
7367             TagTable => 'Image::ExifTool::QuickTime::OtherSampleDesc',
7368             ProcessProc => \&ProcessSampleDesc,
7369             },
7370             },
7371             # (Note: "alis" HandlerType handled by the parent audio or video handler)
7372             ],
7373             stts => [ # decoding time-to-sample table
7374             {
7375             Name => 'VideoFrameRate',
7376             Notes => 'average rate calculated from time-to-sample table for video media',
7377             Condition => '$$self{MediaType} eq "vide"',
7378             Format => 'undef', # (necessary to prevent decoding as string!)
7379             # (must be RawConv so appropriate MediaTS is used in calculation)
7380             RawConv => 'Image::ExifTool::QuickTime::CalcSampleRate($self, \$val)',
7381             PrintConv => 'int($val * 1000 + 0.5) / 1000',
7382             },
7383             {
7384             Name => 'TimeToSampleTable',
7385             Format => 'undef',
7386             Flags => ['Binary','Unknown'],
7387             },
7388             ],
7389             ctts => {
7390             Name => 'CompositionTimeToSample',
7391             Flags => ['Binary','Unknown'],
7392             },
7393             stsc => {
7394             Name => 'SampleToChunk',
7395             Flags => ['Binary','Unknown'],
7396             },
7397             stsz => {
7398             Name => 'SampleSizes',
7399             Flags => ['Binary','Unknown'],
7400             },
7401             stz2 => {
7402             Name => 'CompactSampleSizes',
7403             Flags => ['Binary','Unknown'],
7404             },
7405             stco => {
7406             Name => 'ChunkOffset',
7407             Flags => ['Binary','Unknown'],
7408             },
7409             co64 => {
7410             Name => 'ChunkOffset64',
7411             Flags => ['Binary','Unknown'],
7412             },
7413             stss => {
7414             Name => 'SyncSampleTable',
7415             Flags => ['Binary','Unknown'],
7416             },
7417             stsh => {
7418             Name => 'ShadowSyncSampleTable',
7419             Flags => ['Binary','Unknown'],
7420             },
7421             padb => {
7422             Name => 'SamplePaddingBits',
7423             Flags => ['Binary','Unknown'],
7424             },
7425             stdp => {
7426             Name => 'SampleDegradationPriority',
7427             Flags => ['Binary','Unknown'],
7428             },
7429             sdtp => {
7430             Name => 'IdependentAndDisposableSamples',
7431             Flags => ['Binary','Unknown'],
7432             },
7433             sbgp => {
7434             Name => 'SampleToGroup',
7435             Flags => ['Binary','Unknown'],
7436             },
7437             sgpd => {
7438             Name => 'SampleGroupDescription',
7439             Flags => ['Binary','Unknown'],
7440             # bytes 4-7 give grouping type (ref ISO/IEC 14496-15:2014)
7441             # tsas - temporal sublayer sample
7442             # stsa - step-wise temporal layer access
7443             # avss - AVC sample
7444             # tscl - temporal layer scalability
7445             # sync - sync sample
7446             },
7447             subs => {
7448             Name => 'Sub-sampleInformation',
7449             Flags => ['Binary','Unknown'],
7450             },
7451             cslg => {
7452             Name => 'CompositionToDecodeTimelineMapping',
7453             Flags => ['Binary','Unknown'],
7454             },
7455             stps => {
7456             Name => 'PartialSyncSamples',
7457             ValueConv => 'join " ",unpack("x8N*",$val)',
7458             },
7459             # mark - 8 bytes all zero (GoPro)
7460             );
7461              
7462             # MP4 audio sample description box (ref 5/AtomicParsley 0.9.4 parsley.cpp)
7463             %Image::ExifTool::QuickTime::AudioSampleDesc = (
7464             PROCESS_PROC => \&ProcessHybrid,
7465             VARS => { ID_LABEL => 'ID/Index' },
7466             GROUPS => { 2 => 'Audio' },
7467             NOTES => q{
7468             MP4 audio sample description. This hybrid atom contains both data and child
7469             atoms.
7470             },
7471             4 => {
7472             Name => 'AudioFormat',
7473             Format => 'undef[4]',
7474             RawConv => q{
7475             $$self{AudioFormat} = $val;
7476             return undef unless $val =~ /^[\w ]{4}$/i;
7477             # check for protected audio format
7478             $self->OverrideFileType('M4P') if $val eq 'drms' and $$self{FileType} eq 'M4A';
7479             return $val;
7480             },
7481             # see this link for print conversions (not complete):
7482             # https://github.com/yannickcr/brooser/blob/master/php/librairies/getid3/module.audio-video.quicktime.php
7483             },
7484             # 14 - int16u DataReferenceIndex
7485             20 => { #PH
7486             Name => 'AudioVendorID',
7487             Condition => '$$self{AudioFormat} ne "mp4s"',
7488             Format => 'undef[4]',
7489             RawConv => '$val eq "\0\0\0\0" ? undef : $val',
7490             PrintConv => \%vendorID,
7491             SeparateTable => 'VendorID',
7492             },
7493             24 => { Name => 'AudioChannels', Format => 'int16u' },
7494             26 => { Name => 'AudioBitsPerSample', Format => 'int16u' },
7495             32 => { Name => 'AudioSampleRate', Format => 'fixed32u' },
7496             #
7497             # Observed offsets for child atoms of various AudioFormat types:
7498             #
7499             # AudioFormat Offset Child atoms
7500             # ----------- ------ ----------------
7501             # mp4a 52 * wave, chan, esds, SA3D(Insta360 spherical video params?,also GoPro Max and Garmin VIRB 360)
7502             # in24 52 wave, chan
7503             # "ms\0\x11" 52 wave
7504             # sowt 52 chan
7505             # mp4a 36 * esds, pinf
7506             # drms 36 esds, sinf
7507             # samr 36 damr
7508             # alac 36 alac
7509             # ac-3 36 dac3
7510             #
7511             # (* child atoms found at different offsets in mp4a)
7512             #
7513             pinf => {
7514             Name => 'PurchaseInfo',
7515             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::ProtectionInfo' },
7516             },
7517             sinf => { # "protection scheme information"
7518             Name => 'ProtectionInfo', #3
7519             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::ProtectionInfo' },
7520             },
7521             # f - 16/36 bytes
7522             # esds - 31/40/42/43 bytes - ES descriptor (ref 3)
7523             damr => { #3
7524             Name => 'DecodeConfig',
7525             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::DecodeConfig' },
7526             },
7527             wave => {
7528             Name => 'Wave',
7529             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Wave' },
7530             },
7531             chan => {
7532             Name => 'AudioChannelLayout',
7533             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::ChannelLayout' },
7534             },
7535             SA3D => { # written by Garmin VIRB360
7536             Name => 'SpatialAudio',
7537             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::SpatialAudio' },
7538             },
7539             btrt => {
7540             Name => 'BitrateInfo',
7541             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Bitrate' },
7542             },
7543             # alac - 28 bytes
7544             # adrm - AAX DRM atom? 148 bytes
7545             # aabd - AAX unknown 17kB (contains 'aavd' strings)
7546             # dapa - ? 203 bytes
7547             );
7548              
7549             # video and image sample description data block
7550             %Image::ExifTool::QuickTime::VisualSampleDesc = (
7551             PROCESS_PROC => \&ProcessHybrid,
7552             VARS => { ID_LABEL => 'ID/Index' },
7553             GROUPS => { 2 => 'Image' },
7554             FORMAT => 'int16u',
7555             2 => {
7556             Name => 'CompressorID',
7557             Format => 'string[4]',
7558             # not very useful since this isn't a complete list and name is given below
7559             # # ref http://developer.apple.com/mac/library/documentation/QuickTime/QTFF/QTFFChap3/qtff3.html
7560             # PrintConv => {
7561             # cvid => 'Cinepak',
7562             # jpeg => 'JPEG',
7563             # 'smc '=> 'Graphics',
7564             # 'rle '=> 'Animation',
7565             # rpza => 'Apple Video',
7566             # kpcd => 'Kodak Photo CD',
7567             # 'png '=> 'Portable Network Graphics',
7568             # mjpa => 'Motion-JPEG (format A)',
7569             # mjpb => 'Motion-JPEG (format B)',
7570             # SVQ1 => 'Sorenson video, version 1',
7571             # SVQ3 => 'Sorenson video, version 3',
7572             # mp4v => 'MPEG-4 video',
7573             # 'dvc '=> 'NTSC DV-25 video',
7574             # dvcp => 'PAL DV-25 video',
7575             # 'gif '=> 'Compuserve Graphics Interchange Format',
7576             # h263 => 'H.263 video',
7577             # tiff => 'Tagged Image File Format',
7578             # 'raw '=> 'Uncompressed RGB',
7579             # '2vuY'=> "Uncompressed Y'CbCr, 3x8-bit 4:2:2 (2vuY)",
7580             # 'yuv2'=> "Uncompressed Y'CbCr, 3x8-bit 4:2:2 (yuv2)",
7581             # v308 => "Uncompressed Y'CbCr, 8-bit 4:4:4",
7582             # v408 => "Uncompressed Y'CbCr, 8-bit 4:4:4:4",
7583             # v216 => "Uncompressed Y'CbCr, 10, 12, 14, or 16-bit 4:2:2",
7584             # v410 => "Uncompressed Y'CbCr, 10-bit 4:4:4",
7585             # v210 => "Uncompressed Y'CbCr, 10-bit 4:2:2",
7586             # hvc1 => 'HEVC', #PH
7587             # },
7588             },
7589             # 7 - int16u DataReferenceIndex
7590             10 => {
7591             Name => 'VendorID',
7592             Format => 'string[4]',
7593             RawConv => 'length $val ? $val : undef',
7594             PrintConv => \%vendorID,
7595             SeparateTable => 'VendorID',
7596             },
7597             # 14 - ("Quality" in QuickTime docs) ??
7598             16 => 'SourceImageWidth',
7599             17 => 'SourceImageHeight',
7600             18 => { Name => 'XResolution', Format => 'fixed32u' },
7601             20 => { Name => 'YResolution', Format => 'fixed32u' },
7602             # 24 => 'FrameCount', # always 1 (what good is this?)
7603             25 => {
7604             Name => 'CompressorName',
7605             Format => 'string[32]',
7606             # (sometimes this is a Pascal string, and sometimes it is a C string)
7607             RawConv => q{
7608             $val=substr($val,1,ord($1)) if $val=~/^([\0-\x1f])/ and ord($1)
7609             length $val ? $val : undef;
7610             },
7611             },
7612             41 => 'BitDepth',
7613             #
7614             # Observed offsets for child atoms of various CompressorID types:
7615             #
7616             # CompressorID Offset Child atoms
7617             # ----------- ------ ----------------
7618             # avc1 86 avcC, btrt, colr, pasp, fiel, clap, svcC
7619             # mp4v 86 esds, pasp
7620             # s263 86 d263
7621             #
7622             btrt => {
7623             Name => 'BitrateInfo',
7624             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Bitrate' },
7625             },
7626             # Reference for fiel, colr, pasp, clap:
7627             # https://developer.apple.com/library/mac/technotes/tn2162/_index.html#//apple_ref/doc/uid/DTS40013070-CH1-TNTAG9
7628             fiel => {
7629             Name => 'VideoFieldOrder',
7630             ValueConv => 'join(" ", unpack("C*",$val))',
7631             PrintConv => [{
7632             1 => 'Progressive',
7633             2 => '2:1 Interlaced',
7634             }],
7635             },
7636             colr => {
7637             Name => 'ColorRepresentation',
7638             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::ColorRep' },
7639             },
7640             pasp => {
7641             Name => 'PixelAspectRatio',
7642             ValueConv => 'join(":", unpack("N*",$val))',
7643             },
7644             clap => {
7645             Name => 'CleanAperture',
7646             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::CleanAperture' },
7647             },
7648             avcC => {
7649             # (see http://thompsonng.blogspot.ca/2010/11/mp4-file-format-part-2.html)
7650             Name => 'AVCConfiguration',
7651             Unknown => 1,
7652             Binary => 1,
7653             },
7654             JPEG => { # (found in CR3 images; used as a flag to identify JpgFromRaw 'vide' stream)
7655             Name => 'JPEGInfo',
7656             # (4 bytes all zero)
7657             Unknown => 1,
7658             Binary => 1,
7659             },
7660             # hvcC - HEVC configuration
7661             # svcC - 7 bytes: 00 00 00 00 ff e0 00
7662             # esds - elementary stream descriptor
7663             # d263
7664             gama => { Name => 'Gamma', Format => 'fixed32u' },
7665             # mjqt - default quantization table for MJPEG
7666             # mjht - default Huffman table for MJPEG
7667             # csgm ? (seen in hevc video)
7668             CMP1 => { # Canon CR3
7669             Name => 'CMP1',
7670             SubDirectory => { TagTable => 'Image::ExifTool::Canon::CMP1' },
7671             },
7672             CDI1 => { # Canon CR3
7673             Name => 'CDI1',
7674             SubDirectory => {
7675             TagTable => 'Image::ExifTool::Canon::CDI1',
7676             Start => 4,
7677             },
7678             },
7679             # JPEG - 4 bytes all 0 (Canon CR3)
7680             # free - (Canon CR3)
7681             #
7682             # spherical video v2 stuff (untested)
7683             #
7684             st3d => {
7685             Name => 'Stereoscopic3D',
7686             Format => 'int8u',
7687             ValueConv => '$val =~ s/.* //; $val', # (remove leading version/flags bytes?)
7688             PrintConv => {
7689             0 => 'Monoscopic',
7690             1 => 'Stereoscopic Top-Bottom',
7691             2 => 'Stereoscopic Left-Right',
7692             3 => 'Stereoscopic Stereo-Custom',
7693             4 => 'Stereoscopic Right-Left',
7694             },
7695             },
7696             sv3d => {
7697             Name => 'SphericalVideo',
7698             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::sv3d' },
7699             },
7700             );
7701              
7702             # MP4 hint sample description box (ref 5)
7703             # (ref https://developer.apple.com/library/mac/documentation/QuickTime/QTFF/QTFFChap3/qtff3.html#//apple_ref/doc/uid/TP40000939-CH205-SW1)
7704             %Image::ExifTool::QuickTime::HintSampleDesc = (
7705             PROCESS_PROC => \&ProcessHybrid,
7706             VARS => { ID_LABEL => 'ID/Index' },
7707             NOTES => 'MP4 hint sample description.',
7708             4 => { Name => 'HintFormat', Format => 'undef[4]' },
7709             # 14 - int16u DataReferenceIndex
7710             16 => { Name => 'HintTrackVersion', Format => 'int16u' },
7711             # 18 - int16u LastCompatibleHintTrackVersion
7712             20 => { Name => 'MaxPacketSize', Format => 'int32u' },
7713             #
7714             # Observed offsets for child atoms of various HintFormat types:
7715             #
7716             # HintFormat Offset Child atoms
7717             # ----------- ------ ----------------
7718             # "rtp " 24 tims
7719             #
7720             tims => { Name => 'RTPTimeScale', Format => 'int32u' },
7721             tsro => { Name => 'TimestampRandomOffset', Format => 'int32u' },
7722             snro => { Name => 'SequenceNumberRandomOffset', Format => 'int32u' },
7723             );
7724              
7725             # MP4 metadata sample description box
7726             %Image::ExifTool::QuickTime::MetaSampleDesc = (
7727             PROCESS_PROC => \&ProcessHybrid,
7728             NOTES => 'MP4 metadata sample description.',
7729             4 => {
7730             Name => 'MetaFormat',
7731             Format => 'undef[4]',
7732             RawConv => '$$self{MetaFormat} = $val',
7733             },
7734             8 => { # starts at 8 for MetaFormat eq 'camm', and 17 for 'mett'
7735             Name => 'MetaType',
7736             Format => 'undef[$size-8]',
7737             # may start at various locations!
7738             RawConv => '$$self{MetaType} = ($val=~/(application[^\0]+)/ ? $1 : undef)',
7739             },
7740             #
7741             # Observed offsets for child atoms of various MetaFormat types:
7742             #
7743             # MetaFormat Offset Child atoms
7744             # ----------- ------ ----------------
7745             # mebx 24 keys,btrt,lidp,lidl
7746             # fdsc - -
7747             # gpmd - -
7748             # rtmd - -
7749             # CTMD - -
7750             #
7751             'keys' => { #PH (iPhone7+ hevc)
7752             Name => 'Keys',
7753             SubDirectory => {
7754             TagTable => 'Image::ExifTool::QuickTime::Keys',
7755             ProcessProc => \&ProcessMetaKeys,
7756             },
7757             },
7758             btrt => {
7759             Name => 'BitrateInfo',
7760             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Bitrate' },
7761             },
7762             );
7763              
7764             # MP4 generic sample description box
7765             %Image::ExifTool::QuickTime::OtherSampleDesc = (
7766             PROCESS_PROC => \&ProcessHybrid,
7767             4 => {
7768             Name => 'OtherFormat',
7769             Format => 'undef[4]',
7770             RawConv => '$$self{MetaFormat} = $val', # (yes, use MetaFormat for this too)
7771             },
7772             24 => {
7773             Condition => '$$self{MetaFormat} eq "tmcd"',
7774             Name => 'PlaybackFrameRate', # (may differ from recorded FrameRate eg. ../pics/FujiFilmX-H1.mov)
7775             Format => 'rational64u',
7776             },
7777             #
7778             # Observed offsets for child atoms of various OtherFormat types:
7779             #
7780             # OtherFormat Offset Child atoms
7781             # ----------- ------ ----------------
7782             # avc1 86 avcC
7783             # mp4a 36 esds
7784             # mp4s 16 esds
7785             # tmcd 34 name
7786             # data - -
7787             #
7788             ftab => { Name => 'FontTable', Format => 'undef', ValueConv => 'substr($val, 5)' },
7789             name => { Name => 'OtherName', Format => 'undef', ValueConv => 'substr($val, 4)' },
7790             mrlh => { Name => 'MarlinHeader', SubDirectory => { TagTable => 'Image::ExifTool::GM::mrlh' } },
7791             mrlv => { Name => 'MarlinValues', SubDirectory => { TagTable => 'Image::ExifTool::GM::mrlv' } },
7792             mrld => { Name => 'MarlinDictionary',SubDirectory => { TagTable => 'Image::ExifTool::GM::mrld' } },
7793             # tbox - text box (ref 29)
7794             # styl - subtitle style (ref 29)
7795             );
7796              
7797             # AMR decode config box (ref 3)
7798             %Image::ExifTool::QuickTime::DecodeConfig = (
7799             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
7800             GROUPS => { 2 => 'Audio' },
7801             0 => {
7802             Name => 'EncoderVendor',
7803             Format => 'undef[4]',
7804             },
7805             4 => 'EncoderVersion',
7806             # 5 - int16u - packet modes
7807             # 7 - int8u - number of packet mode changes
7808             # 8 - int8u - bytes per packet
7809             );
7810              
7811             %Image::ExifTool::QuickTime::ProtectionInfo = (
7812             PROCESS_PROC => \&ProcessMOV,
7813             GROUPS => { 2 => 'Audio' },
7814             NOTES => 'Child atoms found in "sinf" and/or "pinf" atoms.',
7815             frma => 'OriginalFormat',
7816             # imif - IPMP information
7817             schm => {
7818             Name => 'SchemeType',
7819             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::SchemeType' },
7820             },
7821             schi => {
7822             Name => 'SchemeInfo',
7823             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::SchemeInfo' },
7824             },
7825             enda => {
7826             Name => 'Endianness',
7827             Format => 'int16u',
7828             PrintConv => {
7829             0 => 'Big-endian (Motorola, MM)',
7830             1 => 'Little-endian (Intel, II)',
7831             },
7832             },
7833             # skcr
7834             );
7835              
7836             %Image::ExifTool::QuickTime::Wave = (
7837             PROCESS_PROC => \&ProcessMOV,
7838             frma => 'PurchaseFileFormat',
7839             enda => {
7840             Name => 'Endianness',
7841             Format => 'int16u',
7842             PrintConv => {
7843             0 => 'Big-endian (Motorola, MM)',
7844             1 => 'Little-endian (Intel, II)',
7845             },
7846             },
7847             # "ms\0\x11" - 20 bytes
7848             );
7849              
7850             # audio channel layout (ref CoreAudioTypes.h)
7851             %Image::ExifTool::QuickTime::ChannelLayout = (
7852             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
7853             GROUPS => { 2 => 'Audio' },
7854             DATAMEMBER => [ 0, 8 ],
7855             NOTES => 'Audio channel layout.',
7856             # 0 - version and flags
7857             4 => {
7858             Name => 'LayoutFlags',
7859             Format => 'int16u',
7860             RawConv => '$$self{LayoutFlags} = $val',
7861             PrintConvColumns => 2,
7862             PrintConv => {
7863             0 => 'UseDescriptions',
7864             1 => 'UseBitmap',
7865             100 => 'Mono',
7866             101 => 'Stereo',
7867             102 => 'StereoHeadphones',
7868             100 => 'Mono',
7869             101 => 'Stereo',
7870             102 => 'StereoHeadphones',
7871             103 => 'MatrixStereo',
7872             104 => 'MidSide',
7873             105 => 'XY',
7874             106 => 'Binaural',
7875             107 => 'Ambisonic_B_Format',
7876             108 => 'Quadraphonic',
7877             109 => 'Pentagonal',
7878             110 => 'Hexagonal',
7879             111 => 'Octagonal',
7880             112 => 'Cube',
7881             113 => 'MPEG_3_0_A',
7882             114 => 'MPEG_3_0_B',
7883             115 => 'MPEG_4_0_A',
7884             116 => 'MPEG_4_0_B',
7885             117 => 'MPEG_5_0_A',
7886             118 => 'MPEG_5_0_B',
7887             119 => 'MPEG_5_0_C',
7888             120 => 'MPEG_5_0_D',
7889             121 => 'MPEG_5_1_A',
7890             122 => 'MPEG_5_1_B',
7891             123 => 'MPEG_5_1_C',
7892             124 => 'MPEG_5_1_D',
7893             125 => 'MPEG_6_1_A',
7894             126 => 'MPEG_7_1_A',
7895             127 => 'MPEG_7_1_B',
7896             128 => 'MPEG_7_1_C',
7897             129 => 'Emagic_Default_7_1',
7898             130 => 'SMPTE_DTV',
7899             131 => 'ITU_2_1',
7900             132 => 'ITU_2_2',
7901             133 => 'DVD_4',
7902             134 => 'DVD_5',
7903             135 => 'DVD_6',
7904             136 => 'DVD_10',
7905             137 => 'DVD_11',
7906             138 => 'DVD_18',
7907             139 => 'AudioUnit_6_0',
7908             140 => 'AudioUnit_7_0',
7909             141 => 'AAC_6_0',
7910             142 => 'AAC_6_1',
7911             143 => 'AAC_7_0',
7912             144 => 'AAC_Octagonal',
7913             145 => 'TMH_10_2_std',
7914             146 => 'TMH_10_2_full',
7915             147 => 'DiscreteInOrder',
7916             148 => 'AudioUnit_7_0_Front',
7917             149 => 'AC3_1_0_1',
7918             150 => 'AC3_3_0',
7919             151 => 'AC3_3_1',
7920             152 => 'AC3_3_0_1',
7921             153 => 'AC3_2_1_1',
7922             154 => 'AC3_3_1_1',
7923             155 => 'EAC_6_0_A',
7924             156 => 'EAC_7_0_A',
7925             157 => 'EAC3_6_1_A',
7926             158 => 'EAC3_6_1_B',
7927             159 => 'EAC3_6_1_C',
7928             160 => 'EAC3_7_1_A',
7929             161 => 'EAC3_7_1_B',
7930             162 => 'EAC3_7_1_C',
7931             163 => 'EAC3_7_1_D',
7932             164 => 'EAC3_7_1_E',
7933             165 => 'EAC3_7_1_F',
7934             166 => 'EAC3_7_1_G',
7935             167 => 'EAC3_7_1_H',
7936             168 => 'DTS_3_1',
7937             169 => 'DTS_4_1',
7938             170 => 'DTS_6_0_A',
7939             171 => 'DTS_6_0_B',
7940             172 => 'DTS_6_0_C',
7941             173 => 'DTS_6_1_A',
7942             174 => 'DTS_6_1_B',
7943             175 => 'DTS_6_1_C',
7944             176 => 'DTS_7_0',
7945             177 => 'DTS_7_1',
7946             178 => 'DTS_8_0_A',
7947             179 => 'DTS_8_0_B',
7948             180 => 'DTS_8_1_A',
7949             181 => 'DTS_8_1_B',
7950             182 => 'DTS_6_1_D',
7951             183 => 'AAC_7_1_B',
7952             0xffff => 'Unknown',
7953             },
7954             },
7955             6 => {
7956             Name => 'AudioChannels',
7957             Condition => '$$self{LayoutFlags} != 0 and $$self{LayoutFlags} != 1',
7958             Format => 'int16u',
7959             },
7960             8 => {
7961             Name => 'AudioChannelTypes',
7962             Condition => '$$self{LayoutFlags} == 1',
7963             Format => 'int32u',
7964             PrintConv => { BITMASK => {
7965             0 => 'Left',
7966             1 => 'Right',
7967             2 => 'Center',
7968             3 => 'LFEScreen',
7969             4 => 'LeftSurround',
7970             5 => 'RightSurround',
7971             6 => 'LeftCenter',
7972             7 => 'RightCenter',
7973             8 => 'CenterSurround',
7974             9 => 'LeftSurroundDirect',
7975             10 => 'RightSurroundDirect',
7976             11 => 'TopCenterSurround',
7977             12 => 'VerticalHeightLeft',
7978             13 => 'VerticalHeightCenter',
7979             14 => 'VerticalHeightRight',
7980             15 => 'TopBackLeft',
7981             16 => 'TopBackCenter',
7982             17 => 'TopBackRight',
7983             }},
7984             },
7985             12 => {
7986             Name => 'NumChannelDescriptions',
7987             Condition => '$$self{LayoutFlags} == 1',
7988             Format => 'int32u',
7989             RawConv => '$$self{NumChannelDescriptions} = $val',
7990             },
7991             16 => {
7992             Name => 'Channel1Label',
7993             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 0',
7994             Format => 'int32u',
7995             SeparateTable => 'ChannelLabel',
7996             PrintConv => \%channelLabel,
7997             },
7998             20 => {
7999             Name => 'Channel1Flags',
8000             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 0',
8001             Format => 'int32u',
8002             PrintConv => { BITMASK => { 0 => 'Rectangular', 1 => 'Spherical', 2 => 'Meters' }},
8003             },
8004             24 => {
8005             Name => 'Channel1Coordinates',
8006             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 0',
8007             Notes => q{
8008             3 numbers: for rectangular coordinates left/right, back/front, down/up; for
8009             spherical coordinates left/right degrees, down/up degrees, distance
8010             },
8011             Format => 'float[3]',
8012             },
8013             36 => {
8014             Name => 'Channel2Label',
8015             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 1',
8016             Format => 'int32u',
8017             SeparateTable => 'ChannelLabel',
8018             PrintConv => \%channelLabel,
8019             },
8020             40 => {
8021             Name => 'Channel2Flags',
8022             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 1',
8023             Format => 'int32u',
8024             PrintConv => { BITMASK => { 0 => 'Rectangular', 1 => 'Spherical', 2 => 'Meters' }},
8025             },
8026             44 => {
8027             Name => 'Channel2Coordinates',
8028             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 1',
8029             Format => 'float[3]',
8030             },
8031             56 => {
8032             Name => 'Channel3Label',
8033             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 2',
8034             Format => 'int32u',
8035             SeparateTable => 'ChannelLabel',
8036             PrintConv => \%channelLabel,
8037             },
8038             60 => {
8039             Name => 'Channel3Flags',
8040             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 2',
8041             Format => 'int32u',
8042             PrintConv => { BITMASK => { 0 => 'Rectangular', 1 => 'Spherical', 2 => 'Meters' }},
8043             },
8044             64 => {
8045             Name => 'Channel3Coordinates',
8046             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 2',
8047             Format => 'float[3]',
8048             },
8049             76 => {
8050             Name => 'Channel4Label',
8051             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 3',
8052             Format => 'int32u',
8053             SeparateTable => 'ChannelLabel',
8054             PrintConv => \%channelLabel,
8055             },
8056             80 => {
8057             Name => 'Channel4Flags',
8058             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 3',
8059             Format => 'int32u',
8060             PrintConv => { BITMASK => { 0 => 'Rectangular', 1 => 'Spherical', 2 => 'Meters' }},
8061             },
8062             84 => {
8063             Name => 'Channel4Coordinates',
8064             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 3',
8065             Format => 'float[3]',
8066             },
8067             96 => {
8068             Name => 'Channel5Label',
8069             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 4',
8070             Format => 'int32u',
8071             SeparateTable => 'ChannelLabel',
8072             PrintConv => \%channelLabel,
8073             },
8074             100 => {
8075             Name => 'Channel5Flags',
8076             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 4',
8077             Format => 'int32u',
8078             PrintConv => { BITMASK => { 0 => 'Rectangular', 1 => 'Spherical', 2 => 'Meters' }},
8079             },
8080             104 => {
8081             Name => 'Channel5Coordinates',
8082             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 4',
8083             Format => 'float[3]',
8084             },
8085             116 => {
8086             Name => 'Channel6Label',
8087             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 5',
8088             Format => 'int32u',
8089             SeparateTable => 'ChannelLabel',
8090             PrintConv => \%channelLabel,
8091             },
8092             120 => {
8093             Name => 'Channel6Flags',
8094             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 5',
8095             Format => 'int32u',
8096             PrintConv => { BITMASK => { 0 => 'Rectangular', 1 => 'Spherical', 2 => 'Meters' }},
8097             },
8098             124 => {
8099             Name => 'Channel6Coordinates',
8100             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 5',
8101             Format => 'float[3]',
8102             },
8103             136 => {
8104             Name => 'Channel7Label',
8105             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 6',
8106             Format => 'int32u',
8107             SeparateTable => 'ChannelLabel',
8108             PrintConv => \%channelLabel,
8109             },
8110             140 => {
8111             Name => 'Channel7Flags',
8112             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 6',
8113             Format => 'int32u',
8114             PrintConv => { BITMASK => { 0 => 'Rectangular', 1 => 'Spherical', 2 => 'Meters' }},
8115             },
8116             144 => {
8117             Name => 'Channel7Coordinates',
8118             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 6',
8119             Format => 'float[3]',
8120             },
8121             156 => {
8122             Name => 'Channel8Label',
8123             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 7',
8124             Format => 'int32u',
8125             SeparateTable => 'ChannelLabel',
8126             PrintConv => \%channelLabel,
8127             },
8128             160 => {
8129             Name => 'Channel8Flags',
8130             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 7',
8131             Format => 'int32u',
8132             PrintConv => { BITMASK => { 0 => 'Rectangular', 1 => 'Spherical', 2 => 'Meters' }},
8133             },
8134             164 => {
8135             Name => 'Channel8Coordinates',
8136             Condition => '$$self{LayoutFlags} == 1 and $$self{NumChannelDescriptions} > 7',
8137             Format => 'float[3]',
8138             },
8139             # (arbitrarily decode only first 8 channels)
8140             );
8141              
8142             # spatial audio (ref https://github.com/google/spatial-media/blob/master/docs/spatial-audio-rfc.md)
8143             %Image::ExifTool::QuickTime::SpatialAudio = (
8144             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
8145             GROUPS => { 2 => 'Audio' },
8146             NOTES => 'Spatial Audio tags.',
8147             0 => 'SpatialAudioVersion',
8148             1 => { Name => 'AmbisonicType', PrintConv => { 0 => 'Periphonic' } },
8149             2 => { Name => 'AmbisonicOrder', Format => 'int32u' },
8150             6 => { Name => 'AmbisonicChannelOrdering', PrintConv => { 0 => 'ACN' } },
8151             7 => { Name => 'AmbisonicNormalization', PrintConv => { 0 => 'SN3D' } },
8152             8 => { Name => 'AmbisonicChannels', Format => 'int32u' },
8153             12 => { Name => 'AmbisonicChannelMap', Format => 'int32u[$val{8}]' },
8154             );
8155              
8156             # scheme type atom
8157             # ref http://xhelmboyx.tripod.com/formats/mp4-layout.txt
8158             %Image::ExifTool::QuickTime::SchemeType = (
8159             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
8160             GROUPS => { 2 => 'Audio' },
8161             # 0 - 4 bytes version
8162             4 => { Name => 'SchemeType', Format => 'undef[4]' },
8163             8 => { Name => 'SchemeVersion', Format => 'int16u' },
8164             10 => { Name => 'SchemeURL', Format => 'string[$size-10]' },
8165             );
8166              
8167             %Image::ExifTool::QuickTime::SchemeInfo = (
8168             PROCESS_PROC => \&ProcessMOV,
8169             GROUPS => { 2 => 'Audio' },
8170             user => {
8171             Name => 'UserID',
8172             Groups => { 2 => 'Author' },
8173             ValueConv => '"0x" . unpack("H*",$val)',
8174             },
8175             cert => { # ref http://www.onvif.org/specs/stream/ONVIF-ExportFileFormat-Spec-v100.pdf
8176             Name => 'Certificate',
8177             ValueConv => '"0x" . unpack("H*",$val)',
8178             },
8179             'key ' => {
8180             Name => 'KeyID',
8181             ValueConv => '"0x" . unpack("H*",$val)',
8182             },
8183             iviv => {
8184             Name => 'InitializationVector',
8185             ValueConv => 'unpack("H*",$val)',
8186             },
8187             righ => {
8188             Name => 'Rights',
8189             Groups => { 2 => 'Author' },
8190             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Rights' },
8191             },
8192             name => { Name => 'UserName', Groups => { 2 => 'Author' } },
8193             # chtb - seen 632 bytes of random data
8194             # priv - private data
8195             # sign
8196             # adkm - Adobe DRM key management system (ref http://download.macromedia.com/f4v/video_file_format_spec_v10_1.pdf)
8197             # iKMS
8198             # iSFM
8199             # iSLT
8200             );
8201              
8202             %Image::ExifTool::QuickTime::Rights = (
8203             PROCESS_PROC => \&ProcessRights,
8204             GROUPS => { 2 => 'Audio' },
8205             veID => 'ItemVendorID', #PH ("VendorID" ref 19)
8206             plat => 'Platform', #18?
8207             aver => 'VersionRestrictions', #19 ("appversion?" ref 18)
8208             tran => 'TransactionID', #18
8209             song => 'ItemID', #19 ("appid" ref 18)
8210             tool => {
8211             Name => 'ItemTool', #PH (guess) ("itunes build?" ref 18)
8212             Format => 'string',
8213             },
8214             medi => 'MediaFlags', #PH (?)
8215             mode => 'ModeFlags', #PH (?) 0x04 is HD flag (https://compilr.com/heksesang/requiem-mac/UnDrm.java)
8216             # sing - seen 4 zeros
8217             # hi32 - seen "00 00 00 04"
8218             );
8219              
8220             # MP4 data information box (ref 5)
8221             %Image::ExifTool::QuickTime::DataInfo = (
8222             PROCESS_PROC => \&ProcessMOV,
8223             WRITE_PROC => \&WriteQuickTime, # (necessary to parse dref even though we don't change it)
8224             NOTES => 'MP4 data information box.',
8225             dref => {
8226             Name => 'DataRef',
8227             SubDirectory => {
8228             TagTable => 'Image::ExifTool::QuickTime::DataRef',
8229             Start => 8,
8230             },
8231             },
8232             );
8233              
8234             # Generic media header
8235             %Image::ExifTool::QuickTime::GenMediaHeader = (
8236             PROCESS_PROC => \&ProcessMOV,
8237             gmin => {
8238             Name => 'GenMediaInfo',
8239             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::GenMediaInfo' },
8240             },
8241             text => {
8242             Name => 'Text',
8243             Flags => ['Binary','Unknown'],
8244             },
8245             tmcd => {
8246             Name => 'TimeCode',
8247             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::TimeCode' },
8248             },
8249             # dbgi - used by DJI - seen "\0\0\0\0"
8250             );
8251              
8252             # TimeCode header
8253             %Image::ExifTool::QuickTime::TimeCode = (
8254             PROCESS_PROC => \&ProcessMOV,
8255             tcmi => {
8256             Name => 'TCMediaInfo',
8257             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::TCMediaInfo' },
8258             },
8259             );
8260              
8261             # TimeCode media info (ref 12)
8262             %Image::ExifTool::QuickTime::TCMediaInfo = (
8263             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
8264             GROUPS => { 2 => 'Video' },
8265             4 => {
8266             Name => 'TextFont',
8267             Format => 'int16u',
8268             PrintConv => { 0 => 'System' },
8269             },
8270             6 => {
8271             Name => 'TextFace',
8272             Format => 'int16u',
8273             PrintConv => {
8274             0 => 'Plain',
8275             BITMASK => {
8276             0 => 'Bold',
8277             1 => 'Italic',
8278             2 => 'Underline',
8279             3 => 'Outline',
8280             4 => 'Shadow',
8281             5 => 'Condense',
8282             6 => 'Extend',
8283             },
8284             },
8285             },
8286             8 => {
8287             Name => 'TextSize',
8288             Format => 'int16u',
8289             },
8290             # 10 - reserved
8291             12 => {
8292             Name => 'TextColor',
8293             Format => 'int16u[3]',
8294             },
8295             18 => {
8296             Name => 'BackgroundColor',
8297             Format => 'int16u[3]',
8298             },
8299             24 => {
8300             Name => 'FontName',
8301             Format => 'pstring',
8302             ValueConv => '$self->Decode($val, $self->Options("CharsetQuickTime"))',
8303             },
8304             );
8305              
8306             # Generic media info (ref http://sourceforge.jp/cvs/view/ntvrec/ntvrec/libqtime/gmin.h?view=co)
8307             %Image::ExifTool::QuickTime::GenMediaInfo = (
8308             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
8309             GROUPS => { 2 => 'Video' },
8310             0 => 'GenMediaVersion',
8311             1 => { Name => 'GenFlags', Format => 'int8u[3]' },
8312             4 => { Name => 'GenGraphicsMode',
8313             Format => 'int16u',
8314             PrintHex => 1,
8315             SeparateTable => 'GraphicsMode',
8316             PrintConv => \%graphicsMode,
8317             },
8318             6 => { Name => 'GenOpColor', Format => 'int16u[3]' },
8319             12 => { Name => 'GenBalance', Format => 'fixed16s' },
8320             );
8321              
8322             # MP4 data reference box (ref 5)
8323             %Image::ExifTool::QuickTime::DataRef = (
8324             PROCESS_PROC => \&ProcessMOV,
8325             WRITE_PROC => \&WriteQuickTime, # (necessary to parse dref even though we don't change it)
8326             NOTES => 'MP4 data reference box.',
8327             'url ' => {
8328             Name => 'URL',
8329             Format => 'undef', # (necessary to prevent decoding as string!)
8330             RawConv => q{
8331             # ignore if self-contained (flags bit 0 set)
8332             return undef if unpack("N",$val) & 0x01;
8333             $_ = substr($val,4); s/\0.*//s; $_;
8334             },
8335             },
8336             "url\0" => { # (written by GoPro)
8337             Name => 'URL',
8338             Format => 'undef', # (necessary to prevent decoding as string!)
8339             RawConv => q{
8340             # ignore if self-contained (flags bit 0 set)
8341             return undef if unpack("N",$val) & 0x01;
8342             $_ = substr($val,4); s/\0.*//s; $_;
8343             },
8344             },
8345             'urn ' => {
8346             Name => 'URN',
8347             Format => 'undef', # (necessary to prevent decoding as string!)
8348             RawConv => q{
8349             return undef if unpack("N",$val) & 0x01;
8350             $_ = substr($val,4); s/\0+/; /; s/\0.*//s; $_;
8351             },
8352             },
8353             );
8354              
8355             # MP4 handler box (ref 5)
8356             %Image::ExifTool::QuickTime::Handler = (
8357             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
8358             GROUPS => { 2 => 'Video' },
8359             4 => { #PH
8360             Name => 'HandlerClass',
8361             Format => 'undef[4]',
8362             RawConv => '$val eq "\0\0\0\0" ? undef : $val',
8363             PrintConv => {
8364             mhlr => 'Media Handler',
8365             dhlr => 'Data Handler',
8366             },
8367             },
8368             8 => {
8369             Name => 'HandlerType',
8370             Format => 'undef[4]',
8371             RawConv => q{
8372             unless ($$self{HasHandler}{$val} or not $Image::ExifTool::QuickTime::eeBox{$val}
8373             or $val eq 'vide' or $$self{OPTIONS}{ExtractEmbedded} or $$self{OPTIONS}{Validate})
8374             {
8375             Image::ExifTool::QuickTime::EEWarn($self);
8376             }
8377             $$self{HandlerType} = $val unless $val eq 'alis' or $val eq 'url ';
8378             $$self{MediaType} = $val if @{$$self{PATH}} > 1 and $$self{PATH}[-2] eq 'Media';
8379             $$self{HasHandler}{$val} = 1; # remember all our handlers
8380             return $val;
8381             },
8382             PrintConvColumns => 2,
8383             PrintConv => {
8384             alis => 'Alias Data', #PH
8385             crsm => 'Clock Reference', #3
8386             hint => 'Hint Track',
8387             ipsm => 'IPMP', #3
8388             m7sm => 'MPEG-7 Stream', #3
8389             meta => 'NRT Metadata', #PH
8390             mdir => 'Metadata', #3
8391             mdta => 'Metadata Tags', #PH
8392             mjsm => 'MPEG-J', #3
8393             ocsm => 'Object Content', #3
8394             odsm => 'Object Descriptor', #3
8395             priv => 'Private', #PH
8396             sdsm => 'Scene Description', #3
8397             soun => 'Audio Track',
8398             text => 'Text', #PH (but what type? subtitle?)
8399             tmcd => 'Time Code', #PH
8400             'url '=> 'URL', #3
8401             vide => 'Video Track',
8402             subp => 'Subpicture', #http://www.google.nl/patents/US7778526
8403             nrtm => 'Non-Real Time Metadata', #PH (Sony ILCE-7S) [how is this different from "meta"?]
8404             pict => 'Picture', # (HEIC images)
8405             camm => 'Camera Metadata', # (Insta360 MP4)
8406             psmd => 'Panasonic Static Metadata', #PH (Leica C-Lux CAM-DC25)
8407             data => 'Data', #PH (GPS and G-sensor data from DataKam)
8408             sbtl => 'Subtitle', #PH (TomTom Bandit Action Cam)
8409             },
8410             },
8411             12 => { #PH
8412             Name => 'HandlerVendorID',
8413             Format => 'undef[4]',
8414             RawConv => '$val eq "\0\0\0\0" ? undef : $val',
8415             PrintConv => \%vendorID,
8416             SeparateTable => 'VendorID',
8417             },
8418             24 => {
8419             Name => 'HandlerDescription',
8420             Format => 'string',
8421             # (sometimes this is a Pascal string, and sometimes it is a C string)
8422             RawConv => q{
8423             $val=substr($val,1,ord($1)) if $val=~/^([\0-\x1f])/ and ord($1)
8424             length $val ? $val : undef;
8425             },
8426             },
8427             );
8428              
8429             # Flip uuid data (ref PH)
8430             %Image::ExifTool::QuickTime::Flip = (
8431             PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
8432             FORMAT => 'int32u',
8433             FIRST_ENTRY => 0,
8434             NOTES => 'Found in MP4 files from Flip Video cameras.',
8435             GROUPS => { 1 => 'MakerNotes', 2 => 'Image' },
8436             1 => 'PreviewImageWidth',
8437             2 => 'PreviewImageHeight',
8438             13 => 'PreviewImageLength',
8439             14 => { # (confirmed for FlipVideoMinoHD)
8440             Name => 'SerialNumber',
8441             Groups => { 2 => 'Camera' },
8442             Format => 'string[16]',
8443             },
8444             28 => {
8445             Name => 'PreviewImage',
8446             Groups => { 2 => 'Preview' },
8447             Format => 'undef[$val{13}]',
8448             RawConv => '$self->ValidateImage(\$val, $tag)',
8449             },
8450             );
8451              
8452             # atoms in Pittasoft "free" atom
8453             %Image::ExifTool::QuickTime::Pittasoft = (
8454             PROCESS_PROC => \&ProcessMOV,
8455             NOTES => 'Tags found in Pittasoft Blackvue dashcam "free" data.',
8456             cprt => 'Copyright',
8457             thum => {
8458             Name => 'PreviewImage',
8459             Groups => { 2 => 'Preview' },
8460             Binary => 1,
8461             RawConv => q{
8462             return undef unless length $val > 4;
8463             my $len = unpack('N', $val);
8464             return undef unless length $val >= 4 + $len;
8465             return substr($val, 4, $len);
8466             },
8467             },
8468             ptnm => {
8469             Name => 'OriginalFileName',
8470             ValueConv => 'substr($val, 4, -1)',
8471             },
8472             ptrh => {
8473             SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Pittasoft' },
8474             # contains these atoms:
8475             # ptvi - 27 bytes: '..avc1...'
8476             # ptso - 16 bytes: '..mp4a...'
8477             },
8478             'gps ' => {
8479             Name => 'GPSLog',
8480             Binary => 1, # (ASCII NMEA track log with leading timestamps)
8481             Notes => 'parsed to extract GPS separately when ExtractEmbedded is used',
8482             RawConv => q{
8483             $val =~ s/\0+$//; # remove trailing nulls
8484             if (length $val and $$self{OPTIONS}{ExtractEmbedded}) {
8485             my $tagTbl = GetTagTable('Image::ExifTool::QuickTime::Stream');
8486             Image::ExifTool::QuickTime::ProcessGPSLog($self, { DataPt => \$val }, $tagTbl);
8487             }
8488             return $val;
8489             },
8490             },
8491             '3gf ' => {
8492             Name => 'AccelData',
8493             SubDirectory => {
8494             TagTable => 'Image::ExifTool::QuickTime::Stream',
8495             ProcessProc => \&Process_3gf,
8496             },
8497             },
8498             sttm => {
8499             Name => 'StartTime',
8500             Format => 'int64u',
8501             Groups => { 2 => 'Time' },
8502             RawConv => '$$self{StartTime} = $val',
8503             # (ms since Jan 1, 1970, in local time zone - PH)
8504             ValueConv => q{
8505             my $secs = int($val / 1000);
8506             return ConvertUnixTime($secs) . sprintf(".%03d",$val - $secs * 1000);
8507             },
8508             PrintConv => '$self->ConvertDateTime($val)',
8509             },
8510             );
8511              
8512             # Nextbase tags (ref PH)
8513             %Image::ExifTool::QuickTime::Nextbase = (
8514             GROUPS => { 1 => 'Nextbase', 2 => 'Camera' },
8515             PROCESS_PROC => \&ProcessNextbase,
8516             WRITE_PROC => \&WriteNextbase,
8517             VARS => { LONG_TAGS => 3 },
8518             NOTES => q{
8519             Tags found in 'infi' atom from some Nextbase videos. As well as these tags,
8520             other existing tags are also extracted. These tags are not currently
8521             writable but they may all be removed by deleting the Nextbase group.
8522             },
8523             'Wi-Fi SSID' => { },
8524             'Wi-Fi Password' => { },
8525             'Wi-Fi MAC Address' => { },
8526             'Model' => { },
8527             'Firmware' => { },
8528             'Serial No' => { Name => 'SerialNumber' },
8529             'FCC-ID' => { },
8530             'Battery Status' => { },
8531             'SD Card Manf ID' => { },
8532             'SD Card OEM ID' => { },
8533             'SD Card Model No' => { },
8534             'SD Card Serial No' => { },
8535             'SD Card Manf Date' => { },
8536             'SD Card Type' => { },
8537             'SD Card Used Space' => { },
8538             'SD Card Class' => { },
8539             'SD Card Size' => { },
8540             'SD Card Format' => { },
8541             'Wi-Fi SSID' => { },
8542             'Wi-Fi Password' => { },
8543             'Wi-Fi MAC Address' => { },
8544             'Bluetooth Name' => { },
8545             'Bluetooth MAC Address' => { },
8546             'Resolution' => { },
8547             'Exposure' => { },
8548             'Video Length' => { },
8549             'Audio' => { },
8550             'Time Stamp' => { Name => 'VideoTimeStamp' },
8551             'Speed Stamp' => { },
8552             'GPS Stamp' => { },
8553             'Model Stamp' => { },
8554             'Dual Files' => { },
8555             'Time Lapse' => { },
8556             'Number / License Plate' => { },
8557             'G Sensor' => { },
8558             'Image Stabilisation' => { },
8559             'Extreme Weather Mode' => { },
8560             'Screen Saver' => { },
8561             'Alerts' => { },
8562             'Recording History' => { },
8563             'Parking Mode' => { },
8564             'Language' => { },
8565             'Country' => { },
8566             'Time Zone / DST' => { Groups => { 2 => 'Time' } },
8567             'Time & Date' => { Name => 'TimeAndDate', Groups => { 2 => 'Time' } },
8568             'Speed Units' => { },
8569             'Device Sounds' => { },
8570             'Screen Dimming' => { },
8571             'Auto Power Off' => { },
8572             'Keep User Settings' => { },
8573             'System Info' => { },
8574             'Format SD Card' => { },
8575             'Default Settings' => { },
8576             'Emergency SOS' => { },
8577             'Reversing Camera' => { },
8578             'what3words' => { Name => 'What3Words' },
8579             'MyNextbase - Pairing' => { },
8580             'MyNextbase - Paired Device Name' => { },
8581             'Alexa' => { },
8582             'Alexa - Pairing' => { },
8583             'Alexa - Paired Device Name' => { },
8584             'Alexa - Privacy Mode' => { },
8585             'Alexa - Wake Word Language' => { },
8586             'Firmware Version' => { },
8587             'RTOS' => { },
8588             'Linux' => { },
8589             'NBCD' => { },
8590             'Alexa' => { },
8591             '2nd Cam' => { Name => 'SecondCam' },
8592             );
8593              
8594             # QuickTime composite tags
8595             %Image::ExifTool::QuickTime::Composite = (
8596             GROUPS => { 2 => 'Video' },
8597             Rotation => {
8598             Notes => q{
8599             degrees of clockwise camera rotation. Writing this tag updates QuickTime
8600             MatrixStructure for all tracks with a non-zero image size
8601             },
8602             Require => {
8603             0 => 'QuickTime:MatrixStructure',
8604             1 => 'QuickTime:HandlerType',
8605             },
8606             Writable => 1,
8607             Protected => 1,
8608             WriteAlso => {
8609             MatrixStructure => 'Image::ExifTool::QuickTime::GetRotationMatrix($val)',
8610             },
8611             ValueConv => 'Image::ExifTool::QuickTime::CalcRotation($self)',
8612             ValueConvInv => '$val',
8613             },
8614             AvgBitrate => {
8615             Priority => 0, # let QuickTime::AvgBitrate take priority
8616             Require => {
8617             0 => 'QuickTime::MediaDataSize',
8618             1 => 'QuickTime::Duration',
8619             },
8620             RawConv => q{
8621             return undef unless $val[1];
8622             $val[1] /= $$self{TimeScale} if $$self{TimeScale};
8623             my $key = 'MediaDataSize';
8624             my $size = $val[0];
8625             for (;;) {
8626             $key = $self->NextTagKey($key) or last;
8627             $size += $self->GetValue($key, 'ValueConv');
8628             }
8629             return int($size * 8 / $val[1] + 0.5);
8630             },
8631             PrintConv => 'ConvertBitrate($val)',
8632             },
8633             GPSLatitude => {
8634             Require => 'QuickTime:GPSCoordinates',
8635             Groups => { 2 => 'Location' },
8636             ValueConv => 'my @c = split " ", $val; $c[0]',
8637             PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")',
8638             },
8639             GPSLongitude => {
8640             Require => 'QuickTime:GPSCoordinates',
8641             Groups => { 2 => 'Location' },
8642             ValueConv => 'my @c = split " ", $val; $c[1]',
8643             PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "E")',
8644             },
8645             # split altitude into GPSAltitude/GPSAltitudeRef like EXIF and XMP
8646             GPSAltitude => {
8647             Require => 'QuickTime:GPSCoordinates',
8648             Groups => { 2 => 'Location' },
8649             Priority => 0, # (because it may not exist)
8650             ValueConv => 'my @c = split " ", $val; defined $c[2] ? abs($c[2]) : undef',
8651             PrintConv => '"$val m"',
8652             },
8653             GPSAltitudeRef => {
8654             Require => 'QuickTime:GPSCoordinates',
8655             Groups => { 2 => 'Location' },
8656             Priority => 0, # (because altitude information may not exist)
8657             ValueConv => 'my @c = split " ", $val; defined $c[2] ? ($c[2] < 0 ? 1 : 0) : undef',
8658             PrintConv => {
8659             0 => 'Above Sea Level',
8660             1 => 'Below Sea Level',
8661             },
8662             },
8663             GPSLatitude2 => {
8664             Name => 'GPSLatitude',
8665             Require => 'QuickTime:LocationInformation',
8666             Groups => { 2 => 'Location' },
8667             ValueConv => '$val =~ /Lat=([-+.\d]+)/; $1',
8668             PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")',
8669             },
8670             GPSLongitude2 => {
8671             Name => 'GPSLongitude',
8672             Require => 'QuickTime:LocationInformation',
8673             Groups => { 2 => 'Location' },
8674             ValueConv => '$val =~ /Lon=([-+.\d]+)/; $1',
8675             PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "E")',
8676             },
8677             GPSAltitude2 => {
8678             Name => 'GPSAltitude',
8679             Require => 'QuickTime:LocationInformation',
8680             Groups => { 2 => 'Location' },
8681             ValueConv => '$val =~ /Alt=([-+.\d]+)/; abs($1)',
8682             PrintConv => '"$val m"',
8683             },
8684             GPSAltitudeRef2 => {
8685             Name => 'GPSAltitudeRef',
8686             Require => 'QuickTime:LocationInformation',
8687             Groups => { 2 => 'Location' },
8688             ValueConv => '$val =~ /Alt=([-+.\d]+)/; $1 < 0 ? 1 : 0',
8689             PrintConv => {
8690             0 => 'Above Sea Level',
8691             1 => 'Below Sea Level',
8692             },
8693             },
8694             CDDBDiscPlayTime => {
8695             Require => 'CDDB1Info',
8696             Groups => { 2 => 'Audio' },
8697             ValueConv => '$val =~ /^..([a-z0-9]{4})/i ? hex($1) : undef',
8698             PrintConv => 'ConvertDuration($val)',
8699             },
8700             CDDBDiscTracks => {
8701             Require => 'CDDB1Info',
8702             Groups => { 2 => 'Audio' },
8703             ValueConv => '$val =~ /^.{6}([a-z0-9]{2})/i ? hex($1) : undef',
8704             },
8705             );
8706              
8707             # add our composite tags
8708             Image::ExifTool::AddCompositeTags('Image::ExifTool::QuickTime');
8709              
8710              
8711             #------------------------------------------------------------------------------
8712             # AutoLoad our routines when necessary
8713             #
8714             sub AUTOLOAD
8715             {
8716             # (Note: no need to autoload routines in QuickTimeStream that use Stream table)
8717 23 50   23   128 if ($AUTOLOAD eq 'Image::ExifTool::QuickTime::Process_mebx') {
8718 0         0 require 'Image/ExifTool/QuickTimeStream.pl';
8719 31     31   498 no strict 'refs';
  31         85  
  31         613005  
8720 0         0 return &$AUTOLOAD(@_);
8721             } else {
8722 23         177 return Image::ExifTool::DoAutoLoad($AUTOLOAD, @_);
8723             }
8724             }
8725              
8726             #------------------------------------------------------------------------------
8727             # Get rotation matrix
8728             # Inputs: 0) angle in degrees
8729             # Returns: 9-element rotation matrix as a string (with 0 x/y offsets)
8730             sub GetRotationMatrix($)
8731             {
8732 1     1 0 6 my $ang = 3.14159265358979323846264 * shift() / 180;
8733 1         27 my $cos = cos $ang;
8734 1         12 my $sin = sin $ang;
8735             # round to zero
8736 1 50       8 $cos = 0 if abs($cos) < 1e-12;
8737 1 50       6 $sin = 0 if abs($sin) < 1e-12;
8738 1         3 my $msn = -$sin;
8739 1         19 return "$cos $sin 0 $msn $cos 0 0 0 1";
8740             }
8741              
8742             #------------------------------------------------------------------------------
8743             # Get rotation angle from a matrix
8744             # Inputs: 0) rotation matrix as a string
8745             # Return: positive rotation angle in degrees rounded to 3 decimal points,
8746             # or undef on error
8747             sub GetRotationAngle($)
8748             {
8749 5     5 0 16 my $rotMatrix = shift;
8750 5         33 my @a = split ' ', $rotMatrix;
8751 5 50 66     38 return undef if $a[0]==0 and $a[1]==0;
8752             # calculate the rotation angle (assume uniform rotation)
8753 5         39 my $angle = atan2($a[1], $a[0]) * 180 / 3.14159;
8754 5 100       22 $angle += 360 if $angle < 0;
8755 5         68 return int($angle * 1000 + 0.5) / 1000;
8756             }
8757              
8758             #------------------------------------------------------------------------------
8759             # Calculate rotation of video track
8760             # Inputs: 0) ExifTool object ref
8761             # Returns: rotation angle or undef
8762             sub CalcRotation($)
8763             {
8764 5     5 0 13 my $et = shift;
8765 5         19 my $value = $$et{VALUE};
8766 5         13 my ($i, $track);
8767             # get the video track family 1 group (eg. "Track1");
8768 5         15 for ($i=0; ; ++$i) {
8769 11 100       38 my $idx = $i ? " ($i)" : '';
8770 11         22 my $tag = "HandlerType$idx";
8771 11 100       64 last unless $$value{$tag};
8772 10 100       41 next unless $$value{$tag} eq 'vide';
8773 4         27 $track = $et->GetGroup($tag, 1);
8774 4         12 last;
8775             }
8776 5 100       24 return undef unless $track;
8777             # get the video track matrix
8778 4         11 for ($i=0; ; ++$i) {
8779 12 100       43 my $idx = $i ? " ($i)" : '';
8780 12         26 my $tag = "MatrixStructure$idx";
8781 12 50       42 last unless $$value{$tag};
8782 12 100       57 next unless $et->GetGroup($tag, 1) eq $track;
8783 4         24 return GetRotationAngle($$value{$tag});
8784             }
8785 0         0 return undef;
8786             }
8787              
8788             #------------------------------------------------------------------------------
8789             # Get MatrixStructure for a given rotation angle
8790             # Inputs: 0) rotation angle (deg), 1) ExifTool ref
8791             # Returns: matrix structure as a string, or undef if it can't be rotated
8792             # - requires ImageSizeLookahead to determine the video image size, and doesn't
8793             # rotate matrix unless image size is valid
8794             sub GetMatrixStructure($$)
8795             {
8796 2     2 0 7 my ($val, $et) = @_;
8797 2         21 my @a = split ' ', $val;
8798             # pass straight through if it already has an offset
8799 2 50 33     21 return $val unless $a[6] == 0 and $a[7] == 0;
8800 2         18 my @s = split ' ', $$et{ImageSizeLookahead};
8801 2         12 my ($w, $h) = @s[12,13];
8802 2 100 66     21 return undef unless $w and $h; # don't rotate 0-sized track
8803 1         7 $_ = Image::ExifTool::QuickTime::FixWrongFormat($_) foreach $w,$h;
8804             # apply necessary offsets for the standard rotations
8805 1         9 my $angle = GetRotationAngle($val);
8806 1 50       4 return undef unless defined $angle;
8807 1 50       11 if ($angle == 90) {
    50          
    50          
8808 0         0 @a[6,7] = ($h, 0);
8809             } elsif ($angle == 180) {
8810 0         0 @a[6,7] = ($w, $h);
8811             } elsif ($angle == 270) {
8812 1         4 @a[6,7] = (0, $w);
8813             }
8814 1         14 return "@a";
8815             }
8816              
8817             #------------------------------------------------------------------------------
8818             # Determine the average sample rate from a time-to-sample table
8819             # Inputs: 0) ExifTool object ref, 1) time-to-sample table data ref
8820             # Returns: average sample rate (in Hz)
8821             sub CalcSampleRate($$)
8822             {
8823 26     26 0 97 my ($et, $valPt) = @_;
8824 26         164 my @dat = unpack('N*', $$valPt);
8825 26         74 my ($num, $dur) = (0, 0);
8826 26         50 my $i;
8827 26         176 for ($i=2; $i<@dat-1; $i+=2) {
8828 26         90 $num += $dat[$i]; # total number of samples
8829 26         120 $dur += $dat[$i] * $dat[$i+1]; # total sample duration
8830             }
8831 26 50 33     253 return undef unless $num and $dur and $$et{MediaTS};
      33        
8832 26         403 return $num * $$et{MediaTS} / $dur;
8833             }
8834              
8835             #------------------------------------------------------------------------------
8836             # Fix incorrect format for ImageWidth/Height as written by Pentax
8837             sub FixWrongFormat($)
8838             {
8839 98     98 0 271 my $val = shift;
8840 98 100       342 return undef unless $val;
8841 54 50       423 return $val & 0xfff00000 ? unpack('n',pack('N',$val)) : $val;
8842             }
8843              
8844             #------------------------------------------------------------------------------
8845             # Convert ISO 6709 string to standard lag/lon format
8846             # Inputs: 0) ISO 6709 string (lat, lon, and optional alt)
8847             # Returns: position in decimal degrees with altitude if available
8848             # Notes: Wikipedia indicates altitude may be in feet -- how is this specified?
8849             sub ConvertISO6709($)
8850             {
8851 0     0 0 0 my $val = shift;
8852 0 0       0 if ($val =~ /^([-+]\d{1,2}(?:\.\d*)?)([-+]\d{1,3}(?:\.\d*)?)([-+]\d+(?:\.\d*)?)?/) {
    0          
    0          
8853             # +DD.DDD+DDD.DDD+AA.AAA
8854 0         0 $val = ($1 + 0) . ' ' . ($2 + 0);
8855 0 0       0 $val .= ' ' . ($3 + 0) if $3;
8856             } elsif ($val =~ /^([-+])(\d{2})(\d{2}(?:\.\d*)?)([-+])(\d{3})(\d{2}(?:\.\d*)?)([-+]\d+(?:\.\d*)?)?/) {
8857             # +DDMM.MMM+DDDMM.MMM+AA.AAA
8858 0         0 my $lat = $2 + $3 / 60;
8859 0 0       0 $lat = -$lat if $1 eq '-';
8860 0         0 my $lon = $5 + $6 / 60;
8861 0 0       0 $lon = -$lon if $4 eq '-';
8862 0         0 $val = "$lat $lon";
8863 0 0       0 $val .= ' ' . ($7 + 0) if $7;
8864             } elsif ($val =~ /^([-+])(\d{2})(\d{2})(\d{2}(?:\.\d*)?)([-+])(\d{3})(\d{2})(\d{2}(?:\.\d*)?)([-+]\d+(?:\.\d*)?)?/) {
8865             # +DDMMSS.SSS+DDDMMSS.SSS+AA.AAA
8866 0         0 my $lat = $2 + $3 / 60 + $4 / 3600;
8867 0 0       0 $lat = -$lat if $1 eq '-';
8868 0         0 my $lon = $6 + $7 / 60 + $8 / 3600;
8869 0 0       0 $lon = -$lon if $5 eq '-';
8870 0         0 $val = "$lat $lon";
8871 0 0       0 $val .= ' ' . ($9 + 0) if $9;
8872             }
8873 0         0 return $val;
8874             }
8875              
8876             #------------------------------------------------------------------------------
8877             # Convert Nero chapter list (ref ffmpeg libavformat/movenc.c)
8878             # Inputs: 0) binary chpl data
8879             # Returns: chapter list
8880             sub ConvertChapterList($)
8881             {
8882 0     0 0 0 my $val = shift;
8883 0         0 my $size = length $val;
8884 0 0       0 return '' if $size < 9;
8885 0         0 my $num = Get8u(\$val, 8);
8886 0         0 my ($i, @chapters);
8887 0         0 my $pos = 9;
8888 0         0 for ($i=0; $i<$num; ++$i) {
8889 0 0       0 last if $pos + 9 > $size;
8890 0         0 my $dur = Get64u(\$val, $pos) / 10000000;
8891 0         0 my $len = Get8u(\$val, $pos + 8);
8892 0 0       0 last if $pos + 9 + $len > $size;
8893 0         0 my $title = substr($val, $pos + 9, $len);
8894 0         0 $pos += 9 + $len;
8895 0         0 push @chapters, "$dur $title";
8896             }
8897 0         0 return \@chapters; # return as a list
8898             }
8899              
8900             #------------------------------------------------------------------------------
8901             # Print conversion for a Nero chapter list item
8902             # Inputs: 0) ValueConv chapter string
8903             # Returns: formatted chapter string
8904             sub PrintChapter($)
8905             {
8906 0     0 0 0 my $val = shift;
8907 0 0       0 $val =~ /^(\S+) (.*)/ or return $val;
8908 0         0 my ($dur, $title) = ($1, $2);
8909 0         0 my $h = int($dur / 3600);
8910 0         0 $dur -= $h * 3600;
8911 0         0 my $m = int($dur / 60);
8912 0         0 my $s = $dur - $m * 60;
8913 0         0 my $ss = sprintf('%06.3f', $s);
8914 0 0       0 if ($ss >= 60) {
8915 0         0 $ss = '00.000';
8916 0 0       0 ++$m >= 60 and $m -= 60, ++$h;
8917             }
8918 0         0 return sprintf("[%d:%.2d:%s] %s",$h,$m,$ss,$title);
8919             }
8920              
8921             #------------------------------------------------------------------------------
8922             # Format GPSCoordinates for printing
8923             # Inputs: 0) string with numerical lat, lon and optional alt, separated by spaces
8924             # 1) ExifTool object reference
8925             # Returns: PrintConv value
8926             sub PrintGPSCoordinates($)
8927             {
8928 0     0 0 0 my ($val, $et) = @_;
8929 0         0 my @v = split ' ', $val;
8930 0         0 my $prt = Image::ExifTool::GPS::ToDMS($et, $v[0], 1, "N") . ', ' .
8931             Image::ExifTool::GPS::ToDMS($et, $v[1], 1, "E");
8932 0 0       0 if (defined $v[2]) {
8933 0 0       0 $prt .= ', ' . ($v[2] < 0 ? -$v[2] . ' m Below' : $v[2] . ' m Above') . ' Sea Level';
8934             }
8935 0         0 return $prt;
8936             }
8937              
8938             #------------------------------------------------------------------------------
8939             # Unpack packed ISO 639/T language code
8940             # Inputs: 0) packed language code (or undef/0), 1) true to not treat 'und' and 'eng' as default
8941             # Returns: language code, or undef/0 for default language, or 'err' for format error
8942             sub UnpackLang($;$)
8943             {
8944 17     17 0 43 my ($lang, $noDef) = @_;
8945 17 50       42 if ($lang) {
8946             # language code is packed in 5-bit characters
8947 17         51 $lang = pack 'C*', map { (($lang>>$_)&0x1f)+0x60 } 10, 5, 0;
  51         194  
8948             # validate language code
8949 17 50       106 if ($lang =~ /^[a-z]+$/) {
8950             # treat 'eng' or 'und' as the default language
8951 17 100 100     103 undef $lang if ($lang eq 'und' or $lang eq 'eng') and not $noDef;
      100        
8952             } else {
8953 0         0 $lang = 'err'; # invalid language code
8954             }
8955             }
8956 17         61 return $lang;
8957             }
8958              
8959             #------------------------------------------------------------------------------
8960             # Get language code string given QuickTime language and country codes
8961             # Inputs: 0) numerical language code, 1) numerical country code, 2) no defaults
8962             # Returns: language code string (ie. "fra-FR") or undef for default language
8963             # ex) 0x15c7 0x0000 is 'eng' with no country (ie. returns 'und' unless $noDef)
8964             # 0x15c7 0x5553 is 'eng-US'
8965             # 0x1a41 0x4652 is 'fra-FR'
8966             # 0x55c4 is 'und'
8967             sub GetLangCode($;$$)
8968             {
8969 11     11 0 37 my ($lang, $ctry, $noDef) = @_;
8970             # ignore country ('ctry') and language lists ('lang') for now
8971 11 50 66     53 undef $ctry if $ctry and $ctry <= 255;
8972 11 50 33     54 undef $lang if $lang and $lang <= 255;
8973 11         42 my $langCode = UnpackLang($lang, $noDef);
8974             # add country code if specified
8975 11 100       36 if ($ctry) {
8976 5         26 $ctry = unpack('a2',pack('n',$ctry)); # unpack as ISO 3166-1
8977             # treat 'ZZ' like a default country (see ref 12)
8978 5 50       18 undef $ctry if $ctry eq 'ZZ';
8979 5 50 33     53 if ($ctry and $ctry =~ /^[A-Z]{2}$/) {
8980 5 100 50     38 $langCode or $langCode = UnpackLang($lang,1) || 'und';
8981 5         18 $langCode .= "-$ctry";
8982             }
8983             }
8984 11         47 return $langCode;
8985             }
8986              
8987             #------------------------------------------------------------------------------
8988             # Get langInfo hash and save details about alt-lang tags
8989             # Inputs: 0) ExifTool ref, 1) tagInfo hash ref, 2) locale code
8990             # Returns: new tagInfo hash ref, or undef if invalid
8991             sub GetLangInfoQT($$$)
8992             {
8993 3     3 0 9 my ($et, $tagInfo, $langCode) = @_;
8994 3         20 my $langInfo = Image::ExifTool::GetLangInfo($tagInfo, $langCode);
8995 3 50       9 if ($langInfo) {
8996 3 100       14 $$et{QTLang} or $$et{QTLang} = [ ];
8997 3         7 push @{$$et{QTLang}}, $$langInfo{Name};
  3         11  
8998             }
8999 3         6 return $langInfo;
9000             }
9001              
9002             #------------------------------------------------------------------------------
9003             # Get variable-length integer from data (used by ParseItemLocation)
9004             # Inputs: 0) data ref, 1) start position, 2) integer size in bytes (0, 4 or 8),
9005             # 3) default value
9006             # Returns: integer value, and updates current position
9007             sub GetVarInt($$$;$)
9008             {
9009 78     78 0 210 my ($dataPt, $pos, $n, $default) = @_;
9010 78         144 my $len = length $$dataPt;
9011 78         138 $_[1] = $pos + $n; # update current position
9012 78 50       198 return undef if $pos + $n > $len;
9013 78 50       250 if ($n == 0) {
    50          
    0          
9014 0   0     0 return $default || 0;
9015             } elsif ($n == 4) {
9016 78         211 return Get32u($dataPt, $pos);
9017             } elsif ($n == 8) {
9018 0         0 return Get64u($dataPt, $pos);
9019             }
9020 0         0 return undef;
9021             }
9022              
9023             #------------------------------------------------------------------------------
9024             # Get null-terminated string from binary data (used by ParseItemInfoEntry)
9025             # Inputs: 0) data ref, 1) start position
9026             # Returns: string, and updates current position
9027             sub GetString($$)
9028             {
9029 30     30 0 87 my ($dataPt, $pos) = @_;
9030 30         67 my $len = length $$dataPt;
9031 30         85 my $str = '';
9032 30         91 while ($pos < $len) {
9033 240         428 my $ch = substr($$dataPt, $pos, 1);
9034 240         344 ++$pos;
9035 240 100       516 last if ord($ch) == 0;
9036 215         564 $str .= $ch;
9037             }
9038 30         85 $_[1] = $pos; # update current position
9039 30         149 return $str;
9040             }
9041              
9042             #------------------------------------------------------------------------------
9043             # Get a printable version of the tag ID
9044             # Inputs: 0) tag ID, 1) Flag: 0x01 - print as 4- or 8-digit hex value if necessary
9045             # 0x02 - put leading backslash before escaped character
9046             # Returns: Printable tag ID
9047             sub PrintableTagID($;$)
9048             {
9049 20     20 0 44 my $tag = $_[0];
9050 20         103 my $n = ($tag =~ s/([^-_a-zA-Z0-9])/'x'.unpack('H*',$1)/eg);
  0         0  
9051 20 0 33     70 if ($n and $_[1]) {
9052 0 0 0     0 if ($n > 2 and $_[1] & 0x01) {
    0          
9053 0         0 $tag = '0x' . unpack('H8', $_[0]);
9054 0         0 $tag =~ s/^0x0000/0x/;
9055             } elsif ($_[1] & 0x02) {
9056 0         0 ($tag = $_[0]) =~ s/([^-_a-zA-Z0-9])/'\\x'.unpack('H*',$1)/eg;
  0         0  
9057             }
9058             }
9059 20         55 return $tag;
9060             }
9061              
9062             #==============================================================================
9063             # The following ParseXxx routines parse various boxes to extract this
9064             # information about embedded items in a $$et{ItemInfo} hash, keyed by item ID:
9065             #
9066             # iloc:
9067             # ConstructionMethod - offset type: 0=file, 1=idat, 2=item
9068             # DataReferenceIndex - 0 for "this file", otherwise index in dref box
9069             # BaseOffset - base for file offsets
9070             # Extents - list of details for data in file:
9071             # 0) index (extent_index)
9072             # 1) offset (extent_offset)
9073             # 2) length (extent_length)
9074             # 3) nlen (length_size)
9075             # 4) lenPt (pointer to length word)
9076             # infe:
9077             # ProtectionIndex - index if item is protected (0 for unprotected)
9078             # Name - item name
9079             # ContentType - mime type of item
9080             # ContentEncoding - item encoding
9081             # URI - URI of a 'uri '-type item
9082             # infe - raw data for 'infe' box (when writing only) [retracted]
9083             # ipma:
9084             # Association - list of associated properties in the ipco container
9085             # Essential - list of "essential" flags for the associated properties
9086             # cdsc:
9087             # RefersTo - hash lookup of flags based on referred item ID
9088             # other:
9089             # DocNum - exiftool document number for this item
9090             #
9091             #------------------------------------------------------------------------------
9092             # Parse item location (iloc) box (ref ISO 14496-12:2015 pg.79)
9093             # Inputs: 0) iloc data, 1) ExifTool ref
9094             # Returns: undef, and fills in ExifTool ItemInfo hash
9095             # Notes: see also Handle_iloc() in WriteQuickTime.pl
9096             sub ParseItemLocation($$)
9097             {
9098 6     6 0 25 my ($val, $et) = @_;
9099 6         31 my ($i, $j, $num, $pos, $id);
9100 6         0 my ($extent_index, $extent_offset, $extent_length);
9101              
9102 6 100       42 my $verbose = $$et{IsWriting} ? 0 : $et->Options('Verbose');
9103 6   50     61 my $items = $$et{ItemInfo} || ($$et{ItemInfo} = { });
9104 6         15 my $len = length $val;
9105 6 50       46 return undef if $len < 8;
9106 6         33 my $ver = Get8u(\$val, 0);
9107 6         27 my $siz = Get16u(\$val, 4);
9108 6         17 my $noff = ($siz >> 12);
9109 6         20 my $nlen = ($siz >> 8) & 0x0f;
9110 6         15 my $nbas = ($siz >> 4) & 0x0f;
9111 6         16 my $nind = $siz & 0x0f;
9112 6 50       25 if ($ver < 2) {
9113 6         24 $num = Get16u(\$val, 6);
9114 6         20 $pos = 8;
9115             } else {
9116 0 0       0 return undef if $len < 10;
9117 0         0 $num = Get32u(\$val, 6);
9118 0         0 $pos = 10;
9119             }
9120 6         36 for ($i=0; $i<$num; ++$i) {
9121 20 50       55 if ($ver < 2) {
9122 20 50       57 return undef if $pos + 2 > $len;
9123 20         58 $id = Get16u(\$val, $pos);
9124 20         44 $pos += 2;
9125             } else {
9126 0 0       0 return undef if $pos + 4 > $len;
9127 0         0 $id = Get32u(\$val, $pos);
9128 0         0 $pos += 4;
9129             }
9130 20 50 33     112 if ($ver == 1 or $ver == 2) {
9131 0 0       0 return undef if $pos + 2 > $len;
9132 0         0 $$items{$id}{ConstructionMethod} = Get16u(\$val, $pos) & 0x0f;
9133 0         0 $pos += 2;
9134             }
9135 20 50       113 return undef if $pos + 2 > $len;
9136 20         61 $$items{$id}{DataReferenceIndex} = Get16u(\$val, $pos);
9137 20         42 $pos += 2;
9138 20         101 $$items{$id}{BaseOffset} = GetVarInt(\$val, $pos, $nbas);
9139 20 50       60 return undef if $pos + 2 > $len;
9140 20         56 my $ext_num = Get16u(\$val, $pos);
9141 20         40 $pos += 2;
9142 20         35 my @extents;
9143 20         71 for ($j=0; $j<$ext_num; ++$j) {
9144 20 50 33     115 if ($ver == 1 or $ver == 2) {
9145 0         0 $extent_index = GetVarInt(\$val, $pos, $nind, 1);
9146             }
9147 20         61 $extent_offset = GetVarInt(\$val, $pos, $noff);
9148 20         78 $extent_length = GetVarInt(\$val, $pos, $nlen);
9149 20 50       59 return undef unless defined $extent_length;
9150             $et->VPrint(1, "$$et{INDENT} Item $id: const_meth=",
9151             defined $$items{$id}{ConstructionMethod} ? $$items{$id}{ConstructionMethod} : '',
9152             sprintf(" base=0x%x offset=0x%x len=0x%x\n", $$items{$id}{BaseOffset},
9153 20 0       66 $extent_offset, $extent_length)) if $verbose;
    50          
9154 20         112 push @extents, [ $extent_index, $extent_offset, $extent_length, $nlen, $pos-$nlen ];
9155             }
9156             # save item location information keyed on 1-based item ID:
9157 20         97 $$items{$id}{Extents} = \@extents;
9158             }
9159 6         26 return undef;
9160             }
9161              
9162             #------------------------------------------------------------------------------
9163             # Parse content describes entry (cdsc) box
9164             # Inputs: 0) cdsc data, 1) ExifTool ref
9165             # Returns: undef, and fills in ExifTool ItemInfo hash
9166             sub ParseContentDescribes($$)
9167             {
9168 8     8 0 26 my ($val, $et) = @_;
9169 8         47 my ($id, $count, @to);
9170 8 50       31 if ($$et{ItemRefVersion}) {
9171 0 0       0 return undef if length $val < 10;
9172 0         0 ($id, $count, @to) = unpack('NnN*', $val);
9173             } else {
9174 8 50       28 return undef if length $val < 6;
9175 8         36 ($id, $count, @to) = unpack('nnn*', $val);
9176             }
9177 8 50       37 if ($count > @to) {
    50          
9178 0         0 my $str = 'Missing values in ContentDescribes box';
9179 0 0       0 $$et{IsWriting} ? $et->Error($str) : $et->Warn($str);
9180             } elsif ($count < @to) {
9181 0         0 $et->Warn('Ignored extra values in ContentDescribes box', 1);
9182 0         0 @to = $count;
9183             }
9184             # add all referenced item ID's to a "RefersTo" lookup
9185 8         83 $$et{ItemInfo}{$id}{RefersTo}{$_} = 1 foreach @to;
9186 8         30 return undef;
9187             }
9188              
9189             #------------------------------------------------------------------------------
9190             # Parse item information entry (infe) box (ref ISO 14496-12:2015 pg.82)
9191             # Inputs: 0) infe data, 1) ExifTool ref
9192             # Returns: undef, and fills in ExifTool ItemInfo hash
9193             sub ParseItemInfoEntry($$)
9194             {
9195 20     20 0 62 my ($val, $et) = @_;
9196 20         49 my $id;
9197              
9198 20 100       97 my $verbose = $$et{IsWriting} ? 0 : $et->Options('Verbose');
9199 20   50     76 my $items = $$et{ItemInfo} || ($$et{ItemInfo} = { });
9200 20         47 my $len = length $val;
9201 20 50       65 return undef if $len < 4;
9202 20         77 my $ver = Get8u(\$val, 0);
9203 20         45 my $pos = 4;
9204 20 50       59 return undef if $pos + 4 > $len;
9205 20 50 33     107 if ($ver == 0 or $ver == 1) {
9206 0         0 $id = Get16u(\$val, $pos);
9207 0         0 $$items{$id}{ProtectionIndex} = Get16u(\$val, $pos + 2);
9208 0         0 $pos += 4;
9209 0         0 $$items{$id}{Name} = GetString(\$val, $pos);
9210 0         0 $$items{$id}{ContentType} = GetString(\$val, $pos);
9211 0         0 $$items{$id}{ContentEncoding} = GetString(\$val, $pos);
9212             } else {
9213 20 50       106 if ($ver == 2) {
    0          
9214 20         70 $id = Get16u(\$val, $pos);
9215 20         51 $pos += 2;
9216             } elsif ($ver == 3) {
9217 0         0 $id = Get32u(\$val, $pos);
9218 0         0 $pos += 4;
9219             }
9220 20 50       61 return undef if $pos + 6 > $len;
9221 20         61 $$items{$id}{ProtectionIndex} = Get16u(\$val, $pos);
9222 20         64 my $type = substr($val, $pos + 2, 4);
9223 20         79 $$items{$id}{Type} = $type;
9224 20         40 $pos += 6;
9225 20         82 $$items{$id}{Name} = GetString(\$val, $pos);
9226 20 100       129 if ($type eq 'mime') {
    50          
9227 5         19 $$items{$id}{ContentType} = GetString(\$val, $pos);
9228 5         23 $$items{$id}{ContentEncoding} = GetString(\$val, $pos);
9229             } elsif ($type eq 'uri ') {
9230 0         0 $$items{$id}{URI} = GetString(\$val, $pos);
9231             }
9232             }
9233             #[retracted] # save raw infe box when writing in case we need to sort items later
9234             #[retracted] $$items{$id}{infe} = pack('N', length($val)+8) . 'infe' . $val if $$et{IsWriting};
9235             $et->VPrint(1, "$$et{INDENT} Item $id: Type=", $$items{$id}{Type} || '',
9236             ' Name=', $$items{$id}{Name} || '',
9237             ' ContentType=', $$items{$id}{ContentType} || '',
9238 20 0 0     65 ($$et{PrimaryItem} and $$et{PrimaryItem} == $id) ? ' (PrimaryItem)' : '',
    50 0        
      0        
      0        
9239             "\n") if $verbose > 1;
9240 20 50       90 unless ($id > $$et{LastItemID}) {
9241 0         0 $et->Warn('Item info entries are out of order'); #[retracted] unless $$et{IsWriting};
9242             #[retracted] $$et{ItemsNotSorted} = 1; # set flag indicating the items weren't sorted
9243             }
9244 20         52 $$et{LastItemID} = $id;
9245 20         92 return undef;
9246             }
9247              
9248             #------------------------------------------------------------------------------
9249             # Parse item property association (ipma) box (ref https://github.com/gpac/gpac/blob/master/src/isomedia/iff.c)
9250             # Inputs: 0) ipma data, 1) ExifTool ref
9251             # Returns: undef, and fills in ExifTool ItemInfo hash
9252             sub ParseItemPropAssoc($$)
9253             {
9254 6     6 0 47 my ($val, $et) = @_;
9255 6         20 my ($i, $j, $id);
9256              
9257 6 100       44 my $verbose = $$et{IsWriting} ? 0 : $et->Options('Verbose');
9258 6   50     29 my $items = $$et{ItemInfo} || ($$et{ItemInfo} = { });
9259 6         17 my $len = length $val;
9260 6 50       29 return undef if $len < 8;
9261 6         25 my $ver = Get8u(\$val, 0);
9262 6         21 my $flg = Get32u(\$val, 0);
9263 6         24 my $num = Get32u(\$val, 4);
9264 6         18 my $pos = 8;
9265 6         15 my $lastID = -1;
9266 6         29 for ($i=0; $i<$num; ++$i) {
9267 12 50       37 if ($ver == 0) {
9268 12 50       38 return undef if $pos + 3 > $len;
9269 12         43 $id = Get16u(\$val, $pos);
9270 12         53 $pos += 2;
9271             } else {
9272 0 0       0 return undef if $pos + 5 > $len;
9273 0         0 $id = Get32u(\$val, $pos);
9274 0         0 $pos += 4;
9275             }
9276 12         41 my $n = Get8u(\$val, $pos++);
9277 12         35 my (@association, @essential);
9278 12 50       37 if ($flg & 0x01) {
9279 0 0       0 return undef if $pos + $n * 2 > $len;
9280 0         0 for ($j=0; $j<$n; ++$j) {
9281 0         0 my $tmp = Get16u(\$val, $pos + $j * 2);
9282 0         0 push @association, $tmp & 0x7fff;
9283 0 0       0 push @essential, ($tmp & 0x8000) ? 1 : 0;
9284             }
9285 0         0 $pos += $n * 2;
9286             } else {
9287 12 50       47 return undef if $pos + $n > $len;
9288 12         45 for ($j=0; $j<$n; ++$j) {
9289 24         68 my $tmp = Get8u(\$val, $pos + $j);
9290 24         63 push @association, $tmp & 0x7f;
9291 24 100       95 push @essential, ($tmp & 0x80) ? 1 : 0;
9292             }
9293 12         24 $pos += $n;
9294             }
9295 12         70 $$items{$id}{Association} = \@association;
9296 12         56 $$items{$id}{Essential} = \@essential;
9297 12 50       41 $et->VPrint(1, "$$et{INDENT} Item $id properties: @association\n") if $verbose > 1;
9298             # (according to ISO/IEC 23008-12, these entries must be sorted by item ID)
9299 12 50       36 $et->Warn('Item property association entries are out of order') unless $id > $lastID;
9300 12         54 $lastID = $id;
9301             }
9302 6         29 return undef;
9303             }
9304              
9305             #------------------------------------------------------------------------------
9306             # Process item information now
9307             # Inputs: 0) ExifTool ref
9308             sub HandleItemInfo($)
9309             {
9310 62     62 0 186 my $et = shift;
9311 62         177 my $raf = $$et{RAF};
9312 62         166 my $items = $$et{ItemInfo};
9313 62         292 my $verbose = $et->Options('Verbose');
9314 62         142 my $buff;
9315              
9316             # extract information from EXIF/XMP metadata items
9317 62 100 66     254 if ($items and $raf) {
9318 3         8 push @{$$et{PATH}}, 'ItemInformation';
  3         12  
9319 3         14 my $curPos = $raf->Tell();
9320 3         10 my $primary = $$et{PrimaryItem};
9321 3         9 my $id;
9322 3         27 $et->VerboseDir('Processing items from ItemInformation', scalar(keys %$items));
9323 3         27 foreach $id (sort { $a <=> $b } keys %$items) {
  11         37  
9324 11         28 my $item = $$items{$id};
9325 11   50     64 my $type = $$item{ContentType} || $$item{Type} || next;
9326 11 50       28 if ($verbose) {
9327             # add up total length of this item for the verbose output
9328 0         0 my $len = 0;
9329 0 0 0     0 if ($$item{Extents} and @{$$item{Extents}}) {
  0         0  
9330 0         0 $len += $$_[2] foreach @{$$item{Extents}};
  0         0  
9331             }
9332 0 0       0 my $enc = $$item{ContentEncoding} ? ", $$item{ContentEncoding} encoded" : '';
9333 0         0 $et->VPrint(0, "$$et{INDENT}Item $id) '${type}' ($len bytes$enc)\n");
9334             }
9335             # get ExifTool name for this item
9336             my $name = { Exif => 'EXIF', 'application/rdf+xml' => 'XMP', jpeg => 'PreviewImage',
9337 11   100     81 'uri ' => 'PLIST' }->{$type} || '';
9338 11         54 my ($warn, $extent);
9339 11 50       31 if ($$item{ContentEncoding}) {
9340 0 0       0 if ($$item{ContentEncoding} ne 'deflate') {
    0          
9341             # (other possible values are 'gzip' and 'compress', but I don't have samples of these)
9342 0         0 $warn = "Can't currently decode $$item{ContentEncoding} encoded $type metadata";
9343 0         0 } elsif (not eval { require Compress::Zlib }) {
9344 0         0 $warn = "Install Compress::Zlib to decode deflated $type metadata";
9345             }
9346             }
9347 11 50       33 $warn = "Can't currently decode protected $type metadata" if $$item{ProtectionIndex};
9348             # Note: In HEIC's, these seem to indicate data in 'idat' instead of 'mdat'
9349 11   50     42 my $constMeth = $$item{ConstructionMethod} || 0;
9350 11 50       28 $warn = "Can't currently extract $type with construction method $constMeth" if $constMeth > 1;
9351 11 50 33     33 $warn = "No 'idat' for $type object with construction method 1" if $constMeth == 1 and not $$et{MediaDataInfo};
9352 11 50 33     76 $et->Warn($warn) if $warn and $name;
9353 11 50       33 $warn = 'Not this file' if $$item{DataReferenceIndex}; # (can only extract from "this file")
9354 11 50 33     41 unless (($$item{Extents} and @{$$item{Extents}}) or $warn) {
  11   33     53  
9355 0         0 $warn = "No Extents for $type item";
9356 0 0       0 $et->Warn($warn) if $name;
9357             }
9358 11 50       30 if ($warn) {
9359 0 0       0 $et->VPrint(0, "$$et{INDENT} [not extracted] ($warn)\n") if $verbose > 2;
9360 0         0 next;
9361             }
9362 11 50 100     49 my $base = ($$item{BaseOffset} || 0) + ($constMeth ? $$et{MediaDataInfo}[0] : 0);
9363 11 50       32 if ($verbose > 2) {
9364             # do verbose hex dump
9365 0         0 my $len = 0;
9366 0         0 undef $buff;
9367 0         0 my $val = '';
9368 0 0       0 my $maxLen = $verbose > 3 ? 2048 : 96;
9369 0         0 foreach $extent (@{$$item{Extents}}) {
  0         0  
9370 0         0 my $n = $$extent[2];
9371 0         0 my $more = $maxLen - $len;
9372 0 0 0     0 if ($more > 0 and $n) {
9373 0 0       0 $more = $n if $more > $n;
9374 0 0       0 $val .= $buff if defined $buff;
9375 0 0       0 $raf->Seek($$extent[1] + $base, 0) or last;
9376 0 0       0 $raf->Read($buff, $more) or last;
9377             }
9378 0         0 $len += $n;
9379             }
9380 0 0       0 if (defined $buff) {
9381 0 0       0 $buff = $val . $buff if length $val;
9382 0         0 $et->VerboseDump(\$buff, DataPos => $$item{Extents}[0][1] + $base);
9383 0         0 my $snip = $len - length $buff;
9384 0 0       0 $et->VPrint(0, "$$et{INDENT} [snip $snip bytes]\n") if $snip;
9385             }
9386             }
9387             # do hash of AVIF "av01" and HEIC image data
9388 11 50 66     53 if ($isImageData{$type} and $$et{ImageDataHash}) {
9389 0         0 my $hash = $$et{ImageDataHash};
9390 0         0 my $tot = 0;
9391 0         0 foreach $extent (@{$$item{Extents}}) {
  0         0  
9392 0 0       0 $raf->Seek($$extent[1] + $base, 0) or $et->Warn("Seek error in $type image data"), last;
9393 0         0 $tot += $et->ImageDataHash($raf, $$extent[2], "$type image", 1);
9394             }
9395 0 0       0 $et->VPrint(0, "$$et{INDENT}(ImageDataHash: $tot bytes of $type data)\n") if $tot;
9396             }
9397 11 100       35 next unless $name;
9398             # assemble the data for this item
9399 5         10 undef $buff;
9400 5         11 my $val = '';
9401 5         12 foreach $extent (@{$$item{Extents}}) {
  5         20  
9402 5 50       17 $val .= $buff if defined $buff;
9403 5 50       46 $raf->Seek($$extent[1] + $base, 0) or last;
9404 5 100       29 $raf->Read($buff, $$extent[2]) or last;
9405             }
9406 5 50       16 next unless defined $buff;
9407 5 50       17 $buff = $val . $buff if length $val;
9408 5 100       16 next unless length $buff; # ignore empty directories
9409 4 50       16 if ($$item{ContentEncoding}) {
9410 0         0 my ($v2, $stat);
9411 0         0 my $inflate = Compress::Zlib::inflateInit();
9412 0 0       0 $inflate and ($v2, $stat) = $inflate->inflate($buff);
9413 0 0 0     0 if ($inflate and $stat == Compress::Zlib::Z_STREAM_END()) {
9414 0         0 $buff = $v2;
9415 0         0 my $len = length $buff;
9416 0         0 $et->VPrint(0, "$$et{INDENT}Inflated Item $id) '${type}' ($len bytes)\n");
9417 0         0 $et->VerboseDump(\$buff);
9418             } else {
9419 0         0 $warn = "Error inflating $name metadata";
9420 0         0 $et->Warn($warn);
9421 0 0       0 $et->VPrint(0, "$$et{INDENT} [not extracted] ($warn)\n") if $verbose > 2;
9422 0         0 next;
9423             }
9424             }
9425 4         10 my ($start, $subTable, $proc);
9426 4         13 my $pos = $$item{Extents}[0][1] + $base;
9427 4 100 66     76 if ($name eq 'EXIF' and length $buff >= 4) {
    50          
    50          
9428 2 50       18 if ($buff =~ /^(MM\0\x2a|II\x2a\0)/) {
    50          
9429 0         0 $et->Warn('Missing Exif header');
9430             } elsif ($buff =~ /^Exif\0\0/) {
9431             # (haven't seen this yet, but it is just a matter of time
9432             # until someone screws it up like this)
9433 0         0 $et->Warn('Missing Exif header size');
9434 0         0 $start = 6;
9435             } else {
9436 2         8 my $n = unpack('N', $buff);
9437 2         5 $start = 4 + $n; # skip "Exif\0\0" header if it exists
9438 2 50       9 if ($start > length($buff)) {
9439 0         0 $et->Warn('Invalid EXIF header');
9440 0         0 next;
9441             }
9442 2 50       12 if ($$et{HTML_DUMP}) {
9443 0         0 $et->HDump($pos, 4, 'Exif header length', "Value: $n");
9444 0 0       0 $et->HDump($pos+4, $start-4, 'Exif header') if $n;
9445             }
9446             }
9447 2         31 $subTable = GetTagTable('Image::ExifTool::Exif::Main');
9448 2         9 $proc = \&Image::ExifTool::ProcessTIFF;
9449             } elsif ($name eq 'PreviewImage') {
9450             # take a quick stab at determining the size of the image
9451             # (based on JPEG previews found in Fuji X-H2S HIF images)
9452 0         0 my $type = 'PreviewImage';
9453 0 0       0 if ($buff =~ /^.{556}\xff\xc0\0\x11.(.{4})/s) {
9454 0         0 my ($h, $w) = unpack('n2', $1);
9455             # (not sure if $h is ever the long dimension, but test it just in case)
9456 0 0 0     0 if ($w == 160 or $h == 160) {
    0 0        
9457 0         0 $type = 'ThumbnailImage';
9458             } elsif ($w == 1920 or $h == 1920) {
9459 0         0 $type = 'OtherImage'; # (large preview)
9460             } # (PreviewImage is 640x480)
9461             }
9462 0         0 $et->FoundTag($type => $buff);
9463 0         0 next;
9464             } elsif ($name eq 'PLIST') {
9465             # extract PLIST information from 'uri ' if available
9466 0 0       0 next unless $buff =~ /^bplist00/;
9467 0         0 $subTable = GetTagTable('Image::ExifTool::PLIST::Main');
9468             } else {
9469 2         13 $subTable = GetTagTable('Image::ExifTool::XMP::Main');
9470             }
9471 4 100       13 $start or $start = 0;
9472 4         34 my %dirInfo = (
9473             DataPt => \$buff,
9474             DataLen => length $buff,
9475             DirStart => $start,
9476             DirLen => length($buff) - $start,
9477             DataPos => $pos,
9478             Base => $pos + $start, # (needed for HtmlDump and IsOffset tags in binary data)
9479             );
9480             # handle processing of metadata for sub-documents
9481 4 50 33     43 if (defined $primary and $$item{RefersTo} and not $$item{RefersTo}{$primary}) {
      33        
9482             # set document number if this doesn't refer to the primary document
9483 0         0 $$et{DOC_NUM} = ++$$et{DOC_COUNT};
9484             # associate this document number with the lowest item index
9485 0         0 my ($lowest) = sort { $a <=> $b } keys %{$$item{RefersTo}};
  0         0  
  0         0  
9486 0         0 $$items{$lowest}{DocNum} = $$et{DOC_NUM};
9487             }
9488 4         25 $et->ProcessDirectory(\%dirInfo, $subTable, $proc);
9489 4         27 delete $$et{DOC_NUM};
9490             }
9491 3 50       23 $raf->Seek($curPos, 0) or $et->Warn('Seek error'); # seek back to original position
9492 3         10 pop @{$$et{PATH}};
  3         13  
9493             }
9494             # process the item properties now that we should know their associations and document numbers
9495 62 100       206 if ($$et{ItemPropertyContainer}) {
9496 3         7 my ($dirInfo, $subTable, $proc) = @{$$et{ItemPropertyContainer}};
  3         14  
9497 3         10 $$et{IsItemProperty} = 1; # set item property flag
9498 3         17 $et->ProcessDirectory($dirInfo, $subTable, $proc);
9499 3         16 delete $$et{ItemPropertyContainer};
9500 3         11 delete $$et{IsItemProperty};
9501 3         24 delete $$et{DOC_NUM};
9502             }
9503 62         125 delete $$et{ItemInfo};
9504 62         243 delete $$et{MediaDataInfo};
9505             }
9506              
9507             #------------------------------------------------------------------------------
9508             # Warn if ExtractEmbedded option isn't used
9509             # Inputs: 0) ExifTool ref
9510             sub EEWarn($)
9511             {
9512 0     0 0 0 my $et = shift;
9513 0         0 $et->Warn('The ExtractEmbedded option may find more tags in the media data',3);
9514             }
9515              
9516             #------------------------------------------------------------------------------
9517             # Get quicktime format from flags word
9518             # Inputs: 0) quicktime atom flags, 1) data length
9519             # Returns: ExifTool format string
9520             sub QuickTimeFormat($$)
9521             {
9522 13     13 0 34 my ($flags, $len) = @_;
9523 13         29 my $format;
9524 13 50 33     123 if ($flags == 0x15 or $flags == 0x16) {
    50          
    50          
    50          
9525 0         0 $format = { 1=>'int8', 2=>'int16', 4=>'int32', 8=>'int64' }->{$len};
9526 0 0       0 $format .= $flags == 0x15 ? 's' : 'u' if $format;
    0          
9527             } elsif ($flags == 0x17) {
9528 0         0 $format = 'float';
9529             } elsif ($flags == 0x18) {
9530 0         0 $format = 'double';
9531             } elsif ($flags == 0x00) {
9532 0         0 $format = { 1=>'int8u', 2=>'int16u' }->{$len};
9533             }
9534 13         38 return $format;
9535             }
9536              
9537             #------------------------------------------------------------------------------
9538             # Process MPEG-4 MTDT atom (ref 11)
9539             # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
9540             # Returns: 1 on success
9541             sub ProcessMetaData($$$)
9542             {
9543 0     0 0 0 my ($et, $dirInfo, $tagTablePtr) = @_;
9544 0         0 my $dataPt = $$dirInfo{DataPt};
9545 0         0 my $dirLen = length $$dataPt;
9546 0         0 my $verbose = $et->Options('Verbose');
9547 0 0       0 return 0 unless $dirLen >= 2;
9548 0         0 my $count = Get16u($dataPt, 0);
9549 0 0       0 $verbose and $et->VerboseDir('MetaData', $count);
9550 0         0 my $i;
9551 0         0 my $pos = 2;
9552 0         0 for ($i=0; $i<$count; ++$i) {
9553 0 0       0 last if $pos + 10 > $dirLen;
9554 0         0 my $size = Get16u($dataPt, $pos);
9555 0 0 0     0 last if $size < 10 or $size + $pos > $dirLen;
9556 0         0 my $tag = Get32u($dataPt, $pos + 2);
9557 0         0 my $lang = Get16u($dataPt, $pos + 6);
9558 0         0 my $enc = Get16u($dataPt, $pos + 8);
9559 0         0 my $val = substr($$dataPt, $pos + 10, $size);
9560 0         0 my $tagInfo = $et->GetTagInfo($tagTablePtr, $tag);
9561 0 0       0 if ($tagInfo) {
9562             # convert language code to ASCII (ignore read-only bit)
9563 0         0 $lang = UnpackLang($lang);
9564             # handle alternate languages
9565 0 0       0 if ($lang) {
9566 0         0 my $langInfo = GetLangInfoQT($et, $tagInfo, $lang);
9567 0 0       0 $tagInfo = $langInfo if $langInfo;
9568             }
9569 0 0       0 $verbose and $et->VerboseInfo($tag, $tagInfo,
9570             Value => $val,
9571             DataPt => $dataPt,
9572             Start => $pos + 10,
9573             Size => $size - 10,
9574             );
9575             # convert from UTF-16 BE if necessary
9576 0 0       0 $val = $et->Decode($val, 'UCS2') if $enc == 1;
9577 0 0 0     0 if ($enc == 0 and $$tagInfo{Unknown}) {
9578             # binary data
9579 0         0 $et->FoundTag($tagInfo, \$val);
9580             } else {
9581 0         0 $et->FoundTag($tagInfo, $val);
9582             }
9583             }
9584 0         0 $pos += $size;
9585             }
9586 0         0 return 1;
9587             }
9588              
9589             #------------------------------------------------------------------------------
9590             # Process sample description table
9591             # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
9592             # Returns: 1 on success
9593             # (ref https://developer.apple.com/library/content/documentation/QuickTime/QTFF/QTFFChap2/qtff2.html#//apple_ref/doc/uid/TP40000939-CH204-25691)
9594             sub ProcessSampleDesc($$$)
9595             {
9596 48     48 0 147 my ($et, $dirInfo, $tagTablePtr) = @_;
9597 48         188 my $dataPt = $$dirInfo{DataPt};
9598 48   50     296 my $pos = $$dirInfo{DirStart} || 0;
9599 48   33     209 my $dirLen = $$dirInfo{DirLen} || (length($$dataPt) - $pos);
9600 48 50       224 return 0 if $pos + 8 > $dirLen;
9601              
9602 48         210 my $num = Get32u($dataPt, 4); # get number of sample entries in table
9603 48         136 $pos += 8;
9604 48         116 my ($i, $err);
9605 48         201 for ($i=0; $i<$num; ++$i) { # loop through sample entries
9606 48 50       169 $pos + 8 > $dirLen and $err = 1, last;
9607 48         163 my $size = Get32u($dataPt, $pos);
9608 48 50       182 $pos + $size > $dirLen and $err = 1, last;
9609 48         186 $$dirInfo{DirStart} = $pos;
9610 48         123 $$dirInfo{DirLen} = $size;
9611 48         257 ProcessHybrid($et, $dirInfo, $tagTablePtr);
9612 48         239 $pos += $size;
9613             }
9614 48 0 33     146 if ($err and $$et{HandlerType}) {
9615 0   0     0 my $grp = $$et{SET_GROUP1} || $$dirInfo{Parent} || 'unknown';
9616 0         0 $et->Warn("Truncated $$et{HandlerType} sample table for $grp");
9617             }
9618 48         156 return 1;
9619             }
9620              
9621             #------------------------------------------------------------------------------
9622             # Process hybrid binary data + QuickTime container (ref PH)
9623             # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
9624             # Returns: 1 on success
9625             sub ProcessHybrid($$$)
9626             {
9627 48     48 0 132 my ($et, $dirInfo, $tagTablePtr) = @_;
9628             # brute-force search for child atoms after first 8 bytes of binary data
9629 48         133 my $dataPt = $$dirInfo{DataPt};
9630 48   50     223 my $dirStart = $$dirInfo{DirStart} || 0;
9631 48   33     182 my $dirLen = $$dirInfo{DirLen} || length($$dataPt) - $dirStart;
9632 48         113 my $end = $dirStart + $dirLen;
9633 48         120 my $pos = $dirStart + 8; # skip length/version
9634 48         104 my $try = $pos;
9635 48         135 my $childPos;
9636              
9637 48         186 while ($pos <= $end - 8) {
9638 2664         4584 my $tag = substr($$dataPt, $try+4, 4);
9639             # look only for well-behaved tag ID's
9640 2664 100       8198 $tag =~ /[^\w ]/ and $try = ++$pos, next;
9641 124         298 my $size = Get32u($dataPt, $try);
9642 124 100       298 if ($size + $try == $end) {
9643             # the atom ends exactly at the end of the parent -- this must be it
9644 16         39 $childPos = $pos;
9645 16         55 $$dirInfo{DirLen} = $pos; # the binary data ends at the first child atom
9646 16         50 last;
9647             }
9648 108 100 100     440 if ($size < 8 or $size + $try > $end - 8) {
9649 88         191 $try = ++$pos; # fail. try next position
9650             } else {
9651 20         71 $try += $size; # could be another atom following this
9652             }
9653             }
9654             # process binary data
9655 48         210 $$dirInfo{MixedTags} = 1; # ignore non-integer tag ID's
9656 48         315 $et->ProcessBinaryData($dirInfo, $tagTablePtr);
9657             # process child atoms if found
9658 48 100       186 if ($childPos) {
9659 16         60 $$dirInfo{DirStart} = $childPos;
9660 16         53 $$dirInfo{DirLen} = $end - $childPos;
9661 16         88 ProcessMOV($et, $dirInfo, $tagTablePtr);
9662             }
9663 48         156 return 1;
9664             }
9665              
9666             #------------------------------------------------------------------------------
9667             # Process iTunes 'righ' atom (ref PH)
9668             # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
9669             # Returns: 1 on success
9670             sub ProcessRights($$$)
9671             {
9672 0     0 0 0 my ($et, $dirInfo, $tagTablePtr) = @_;
9673 0         0 my $dataPt = $$dirInfo{DataPt};
9674 0         0 my $dataPos = $$dirInfo{Base};
9675 0         0 my $dirLen = length $$dataPt;
9676 0   0     0 my $unknown = $$et{OPTIONS}{Unknown} || $$et{OPTIONS}{Verbose};
9677 0         0 my $pos;
9678 0         0 $et->VerboseDir('righ', $dirLen / 8);
9679 0         0 for ($pos = 0; $pos + 8 <= $dirLen; $pos += 8) {
9680 0         0 my $tag = substr($$dataPt, $pos, 4);
9681 0 0       0 last if $tag eq "\0\0\0\0";
9682 0         0 my $val = substr($$dataPt, $pos + 4, 4);
9683 0         0 my $tagInfo = $et->GetTagInfo($tagTablePtr, $tag);
9684 0 0       0 unless ($tagInfo) {
9685 0 0       0 next unless $unknown;
9686 0         0 my $name = PrintableTagID($tag);
9687 0         0 $tagInfo = {
9688             Name => "Unknown_$name",
9689             Description => "Unknown $name",
9690             Unknown => 1,
9691             },
9692             AddTagToTable($tagTablePtr, $tag, $tagInfo);
9693             }
9694 0 0       0 $val = '0x' . unpack('H*', $val) unless $$tagInfo{Format};
9695 0         0 $et->HandleTag($tagTablePtr, $tag, $val,
9696             DataPt => $dataPt,
9697             DataPos => $dataPos,
9698             Start => $pos + 4,
9699             Size => 4,
9700             );
9701             }
9702 0         0 return 1;
9703             }
9704              
9705             #------------------------------------------------------------------------------
9706             # Process Nextbase 'infi' atom (ref PH)
9707             # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
9708             # Returns: 1 on success
9709             sub ProcessNextbase($$$)
9710             {
9711 0     0 0 0 my ($et, $dirInfo, $tagTbl) = @_;
9712 0         0 my $dataPt = $$dirInfo{DataPt};
9713 0         0 $et->VerboseDir('Nextbase', undef, length($$dataPt));
9714 0         0 while ($$dataPt =~ /(.*?): +(.*)\x0d/g) {
9715 0         0 my ($id, $val) = ($1, $2);
9716 0 0       0 $$tagTbl{$id} or AddTagToTable($tagTbl, $id, { Name => Image::ExifTool::MakeTagName($id) });
9717 0         0 $et->HandleTag($tagTbl, $id, $val, Size => length($val));
9718             }
9719 0         0 return 1;
9720             }
9721              
9722             #------------------------------------------------------------------------------
9723             # Process iTunes Encoding Params (ref PH)
9724             # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
9725             # Returns: 1 on success
9726             sub ProcessEncodingParams($$$)
9727             {
9728 0     0 0 0 my ($et, $dirInfo, $tagTablePtr) = @_;
9729 0         0 my $dataPt = $$dirInfo{DataPt};
9730 0         0 my $dirLen = length $$dataPt;
9731 0         0 my $pos;
9732 0         0 $et->VerboseDir('Encoding Params', $dirLen / 8);
9733 0         0 for ($pos = 0; $pos + 8 <= $dirLen; $pos += 8) {
9734 0         0 my ($tag, $val) = unpack("x${pos}a4N", $$dataPt);
9735 0         0 $et->HandleTag($tagTablePtr, $tag, $val);
9736             }
9737 0         0 return 1;
9738             }
9739              
9740             #------------------------------------------------------------------------------
9741             # Read Meta Keys and add tags to ItemList table ('mdta' handler) (ref PH)
9742             # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
9743             # Returns: 1 on success
9744             sub ProcessKeys($$$)
9745             {
9746 17     17 0 35 local $_;
9747 17         49 my ($et, $dirInfo, $tagTablePtr) = @_;
9748 17         38 my $dataPt = $$dirInfo{DataPt};
9749 17         38 my $dirLen = length $$dataPt;
9750 17         28 my $out;
9751 17 50       65 if ($et->Options('Verbose')) {
9752 0         0 $et->VerboseDir('Keys');
9753 0         0 $out = $et->Options('TextOut');
9754             }
9755 17         39 my $pos = 8;
9756 17         36 my $index = 1;
9757 17         40 ++$$et{KeysCount}; # increment key count for this directory
9758 17         49 my $itemList = GetTagTable('Image::ExifTool::QuickTime::ItemList');
9759 17         42 my $userData = GetTagTable('Image::ExifTool::QuickTime::UserData');
9760 17         52 while ($pos < $dirLen - 4) {
9761 38         153 my $len = unpack("x${pos}N", $$dataPt);
9762 38 50 33     146 last if $len < 8 or $pos + $len > $dirLen;
9763 38         72 delete $$tagTablePtr{$index};
9764 38         90 my $ns = substr($$dataPt, $pos + 4, 4);
9765 38         95 my $tag = substr($$dataPt, $pos + 8, $len - 8);
9766 38         82 $tag =~ s/\0.*//s; # truncate at null
9767 38         52 my $full = $tag;
9768 38 50       243 $tag =~ s/^com\.(apple\.quicktime\.)?// if $ns eq 'mdta'; # remove apple quicktime domain
9769 38 50       95 $tag = "Tag_$ns" unless $tag;
9770 38         62 my $short = $tag;
9771 38         56 my $tagInfo;
9772 38         54 for (;;) {
9773 38 50       146 $tagInfo = $et->GetTagInfo($tagTablePtr, $tag) and last;
9774             # also try ItemList and UserData tables
9775 0 0       0 $tagInfo = $et->GetTagInfo($itemList, $tag) and last;
9776 0 0       0 $tagInfo = $et->GetTagInfo($userData, $tag) and last;
9777             # (I have some samples where the tag is a reversed ItemList or UserData tag ID)
9778 0 0       0 if ($tag =~ /^\w{3}\xa9$/) {
9779 0         0 $tag = pack('N', unpack('V', $tag));
9780 0 0       0 $tagInfo = $et->GetTagInfo($itemList, $tag) and last;
9781 0         0 $tagInfo = $et->GetTagInfo($userData, $tag);
9782 0         0 last;
9783             }
9784 0 0       0 if ($tag eq $full) {
9785 0         0 $tag = $short;
9786 0         0 last;
9787             }
9788 0         0 $tag = $full;
9789             }
9790 38         82 my ($newInfo, $msg);
9791 38 50 0     78 if ($tagInfo) {
    0          
9792             # copy tag information into new Keys tag
9793             $newInfo = {
9794             Name => $$tagInfo{Name},
9795             Format => $$tagInfo{Format},
9796             ValueConv => $$tagInfo{ValueConv},
9797             ValueConvInv => $$tagInfo{ValueConvInv},
9798             PrintConv => $$tagInfo{PrintConv},
9799             PrintConvInv => $$tagInfo{PrintConvInv},
9800             Writable => defined $$tagInfo{Writable} ? $$tagInfo{Writable} : 1,
9801             SubDirectory => $$tagInfo{SubDirectory},
9802 38 50       415 };
9803 38         91 my $groups = $$tagInfo{Groups};
9804 38 100       247 $$newInfo{Groups} = $groups ? { %$groups } : { };
9805 38   66     340 $$newInfo{Groups}{$_} or $$newInfo{Groups}{$_} = $$tagTablePtr{GROUPS}{$_} foreach 0..2;
9806             # set Keys group. This is necessary for logic when reading the associated ItemList entry,
9807             # but note that the group name will be overridden by TAG_EXTRA G1 for tags in a track
9808 38         95 $$newInfo{Groups}{1} = 'Keys';
9809             } elsif ($tag =~ /^[-\w. ]+$/ or $tag =~ /\w{4}/) {
9810             # create info for tags with reasonable id's
9811 0         0 my $name = ucfirst $tag;
9812 0         0 $name =~ tr/-0-9a-zA-Z_. //dc;
9813 0         0 $name =~ s/[. ]+(.?)/\U$1/g;
9814 0         0 $name =~ s/_([a-z])/_\U$1/g;
9815 0         0 $name =~ s/([a-z])_([A-Z])/$1$2/g;
9816 0 0       0 $name = "Tag_$name" if length $name < 2;
9817 0         0 $newInfo = { Name => $name, Groups => { 1 => 'Keys' } };
9818 0         0 $msg = ' (Unknown)';
9819 0         0 $et->VPrint(0, $$et{INDENT}, "[adding Keys:$tag]\n");
9820             }
9821             # substitute this tag in the ItemList table with the given index
9822 38         98 my $id = $$et{KeysCount} . '.' . $index;
9823 38 100       157 if (ref $$itemList{$id} eq 'HASH') {
9824             # delete other languages too if they exist
9825 30         67 my $oldInfo = $$itemList{$id};
9826 30 100       87 if ($$oldInfo{OtherLang}) {
9827 1         2 delete $$itemList{$_} foreach @{$$oldInfo{OtherLang}};
  1         7  
9828             }
9829 30         246 delete $$itemList{$id};
9830             }
9831 38 50       83 if ($newInfo) {
9832 38         80 $$newInfo{KeysID} = $tag; # save original ID for use in family 7 group name
9833 38         149 AddTagToTable($itemList, $id, $newInfo);
9834 38 50       86 $msg or $msg = '';
9835 38 50       110 $out and print $out "$$et{INDENT}Added ItemList Tag $id = ($ns) $full$msg\n";
9836             }
9837 38         59 $pos += $len;
9838 38         141 ++$index;
9839             }
9840 17         56 return 1;
9841             }
9842              
9843             #------------------------------------------------------------------------------
9844             # Process keys in MetaSampleDesc directory
9845             # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
9846             # Returns: 1 on success
9847             sub ProcessMetaKeys($$$)
9848             {
9849 0     0 0 0 my ($et, $dirInfo, $tagTablePtr) = @_;
9850             # save this information to decode timed metadata samples when ExtractEmbedded is used
9851 0 0       0 SaveMetaKeys($et, $dirInfo, $tagTablePtr) if $$et{OPTIONS}{ExtractEmbedded};
9852 0         0 return 1;
9853             }
9854              
9855             #------------------------------------------------------------------------------
9856             # Identify trailers at specified offset from end of file
9857             # Inputs: 0) RAF reference, 1) Offset from end of file
9858             # Returns: Array ref to first trailer in linked list: 0) name of trailer,
9859             # 1) absolute offset to start of this trailer, 2) trailer length,
9860             # 3) ref to next trailer. Or undef if no trailer found, or error string on error
9861             # - file position is returned to its original location
9862             sub IdentifyTrailers($)
9863             {
9864 48     48 0 109 my $raf = shift;
9865 48         129 my ($trailer, $nextTrail, $buff, $type, $len);
9866 48         188 my $pos = $raf->Tell();
9867 48         109 my $offset = 0; # positive offset back from end of file
9868 48   33     283 while ($raf->Seek(-40-$offset, 2) and $raf->Read($buff, 40) == 40) {
9869 48 50 33     672 if (substr($buff, 8) eq '8db42d694ccc418790edff439fe026bf') {
    50          
    50          
9870 0         0 ($type, $len) = ('Insta360', unpack('V',$buff));
9871             } elsif ($buff =~ /\&\&\&\&(.{4})$/) {
9872 0         0 ($type, $len) = ('LigoGPS', Get32u(\$buff, 36));
9873             } elsif ($buff =~ /~\0\x04\0zmie~\0\0\x06.{4}([\x10\x18])(\x04)$/s or
9874             $buff =~ /~\0\x04\0zmie~\0\0\x0a.{8}([\x10\x18])(\x08)$/s)
9875             {
9876 0         0 my $oldOrder = GetByteOrder();
9877 0 0       0 SetByteOrder($1 eq "\x10" ? 'MM' : 'II');
9878 0         0 $type = 'MIE';
9879 0 0       0 $len = ($2 eq "\x04") ? Get32u(\$buff, 34) : Get64u(\$buff, 30);
9880 0         0 SetByteOrder($oldOrder);
9881             } else {
9882 48         116 last;
9883             }
9884             # 0) trailer type, 1) trailer start, 2) trailer length, 3) pointer to next trailer
9885 0         0 $trailer = [ $type , $raf->Tell() - $len, $len, $nextTrail ];
9886 0         0 $nextTrail = $trailer;
9887 0         0 $offset += $len;
9888             }
9889 48 50       201 $raf->Seek($pos,0) or return 'Seek error';
9890 48         145 return $trailer;
9891             }
9892              
9893             #------------------------------------------------------------------------------
9894             # Process a QuickTime atom
9895             # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) optional tag table ref
9896             # Returns: 1 on success
9897             sub ProcessMOV($$;$)
9898             {
9899 486     486 0 1036 local $_;
9900 486         1288 my ($et, $dirInfo, $tagTablePtr) = @_;
9901 486         1178 my $raf = $$dirInfo{RAF};
9902 486         1037 my $dataPt = $$dirInfo{DataPt};
9903 486         2156 my $verbose = $et->Options('Verbose');
9904 486         1271 my $validate = $$et{OPTIONS}{Validate};
9905 486   100     1459 my $dirBase = $$dirInfo{Base} || 0;
9906 486         914 my $dataPos = $dirBase;
9907 486   100     1569 my $dirID = $$dirInfo{DirID} || '';
9908 486         1352 my $charsetQuickTime = $et->Options('CharsetQuickTime');
9909 486         1685 my ($buff, $tag, $size, $track, $isUserData, %triplet, $doDefaultLang, $index);
9910 486         0 my ($dirEnd, $unkOpt, %saveOptions, $atomCount, $warnStr, $trailer);
9911              
9912 486         1203 my $topLevel = not $$et{InQuickTime};
9913 486         1059 $$et{InQuickTime} = 1;
9914 486 100       1485 $$et{HandlerType} = $$et{MetaFormat} = $$et{MediaType} = '' if $topLevel;
9915              
9916 486 100       1420 unless (defined $$et{KeysCount}) {
9917 25         70 $$et{KeysCount} = 0; # initialize ItemList key directory count
9918 25         51 $doDefaultLang = 1; # flag to generate default language tags
9919             }
9920             # more convenient to package data as a RandomAccess file
9921 486 100       1194 unless ($raf) {
9922 461         2135 $raf = File::RandomAccess->new($dataPt);
9923 461 50 100     2644 $dirEnd = $dataPos + $$dirInfo{DirLen} + ($$dirInfo{DirStart} || 0) if $$dirInfo{DirLen};
9924             }
9925             # skip leading bytes if necessary
9926 486 100       1440 if ($$dirInfo{DirStart}) {
9927 108 50       550 $raf->Seek($$dirInfo{DirStart}, 1) or return 0;
9928 108         297 $dataPos += $$dirInfo{DirStart};
9929             }
9930             # read size/tag name atom header
9931 486 50       1578 $raf->Read($buff,8) == 8 or return 0;
9932 486         1004 $dataPos += 8;
9933 486 100       1169 if ($tagTablePtr) {
9934 461         1773 $isUserData = ($tagTablePtr eq \%Image::ExifTool::QuickTime::UserData);
9935             } else {
9936 25         120 $tagTablePtr = GetTagTable('Image::ExifTool::QuickTime::Main');
9937             }
9938 486         2336 ($size, $tag) = unpack('Na4', $buff);
9939 486   50     2528 my $fast = $$et{OPTIONS}{FastScan} || 0;
9940             # check for Insta360, LIGOGPSINFO or MIE trailer
9941 486 100 66     1563 if ($topLevel and not $fast) {
9942 25         101 $trailer = IdentifyTrailers($raf);
9943 25 50 33     112 $trailer and not ref $trailer and $et->Warn($trailer), return 0;
9944             }
9945 486 100       1084 if ($dataPt) {
9946 461 50       1158 $verbose and $et->VerboseDir($$dirInfo{DirName});
9947             } else {
9948             # check on file type if called with a RAF
9949 25 50       121 $$tagTablePtr{$tag} or return 0;
9950 25         45 my $fileType;
9951 25 100 66     166 if ($tag eq 'ftyp' and $size >= 12) {
9952             # read ftyp atom to see what type of file this is
9953 11 50       54 if ($raf->Read($buff, $size-8) == $size-8) {
9954 11 50       83 $raf->Seek(-($size-8), 1) or $et->Warn('Seek error'), return 0;
9955 11         48 my $type = substr($buff, 0, 4);
9956 11         56 $$et{save_ftyp} = $type;
9957             # see if we know the extension for this file type
9958 11 50 33     160 if ($ftypLookup{$type} and $ftypLookup{$type} =~ /\(\.(\w+)/) {
    0          
    0          
    0          
9959 11         62 $fileType = $1;
9960             # check compatible brands
9961             } elsif ($buff =~ /^.{8}(.{4})+(mp41|mp42|avc1)/s) {
9962 0         0 $fileType = 'MP4';
9963             } elsif ($buff =~ /^.{8}(.{4})+(f4v )/s) {
9964 0         0 $fileType = 'F4V';
9965             } elsif ($buff =~ /^.{8}(.{4})+(qt )/s) {
9966 0         0 $fileType = 'MOV';
9967             }
9968             }
9969 11 50       49 $fileType or $fileType = 'MP4'; # default to MP4
9970             # set file type from extension if appropriate
9971 11         79 my $ext = $$et{FILE_EXT};
9972 11 50 33     114 $fileType = $ext if $ext and $useExt{$ext} and $fileType eq $useExt{$ext};
      33        
9973 11   50     121 $et->SetFileType($fileType, $mimeLookup{$fileType} || 'video/mp4');
9974             # temporarily set ExtractEmbedded option for CRX files
9975 11 100       70 $saveOptions{ExtractEmbedded} = $et->Options(ExtractEmbedded => 1) if $fileType eq 'CRX';
9976             } else {
9977 14         76 $et->SetFileType(); # MOV
9978             }
9979 25         125 SetByteOrder('MM');
9980             # have XMP take priority except for HEIC
9981 25 50 66     180 $$et{PRIORITY_DIR} = 'XMP' unless $fileType and $fileType eq 'HEIC';
9982             }
9983 486 50       1186 $$raf{NoBuffer} = 1 if $fast; # disable buffering in FastScan mode
9984              
9985 486   100     2054 my $ee = $$et{OPTIONS}{ExtractEmbedded} || 0;
9986 486         2446 my $hash = $$et{ImageDataHash};
9987 486 100 66     1677 if ($ee or $hash) {
9988 134         305 $unkOpt = $$et{OPTIONS}{Unknown};
9989 134         8494 require 'Image/ExifTool/QuickTimeStream.pl';
9990             }
9991 486 100       1492 if ($$tagTablePtr{VARS}) {
9992 22         71 $index = $$tagTablePtr{VARS}{START_INDEX};
9993 22         61 $atomCount = $$tagTablePtr{VARS}{ATOM_COUNT};
9994             }
9995 486         1051 my $lastTag = '';
9996 486         938 my $lastPos = 0;
9997 486         892 for (;;) {
9998 1576         2744 my ($eeTag, $ignore);
9999 1576 50 33     4175 last if defined $atomCount and --$atomCount < 0;
10000 1576 100       3468 if ($size < 8) {
10001 4 50       22 if ($size == 0) {
10002 0 0       0 if ($dataPt) {
10003             # a zero size isn't legal for contained atoms, but Canon uses it to
10004             # terminate the CNTH atom (eg. CanonEOS100D.mov), so tolerate it here
10005 0         0 my $pos = $raf->Tell() - 4;
10006 0 0       0 $raf->Seek(0,2) or $et->Warn('Seek error'), return 0;
10007 0         0 my $str = $$dirInfo{DirName} . ' with ' . ($raf->Tell() - $pos) . ' bytes';
10008 0         0 $et->VPrint(0,"$$et{INDENT}\[Terminator found in $str remaining]");
10009             } else {
10010 0         0 my $t = PrintableTagID($tag,2);
10011 0         0 $et->VPrint(0,"$$et{INDENT}Tag '${t}' extends to end of file");
10012 0 0       0 if ($$tagTablePtr{"$tag-size"}) {
10013 0         0 my $pos = $raf->Tell();
10014 0 0       0 unless ($fast) {
10015 0 0       0 $raf->Seek(0, 2) or $et->Warn('Seek error'), return 0;
10016 0         0 $et->HandleTag($tagTablePtr, "$tag-size", $raf->Tell() - $pos);
10017             }
10018 0 0       0 $et->HandleTag($tagTablePtr, "$tag-offset", $pos) if $$tagTablePtr{"$tag-offset"};
10019             }
10020             }
10021 0         0 last;
10022             }
10023 4 50       21 $size == 1 or $warnStr = 'Invalid atom size', last;
10024             # read extended atom size
10025 4 50       19 $raf->Read($buff, 8) == 8 or $warnStr = 'Truncated atom header', last;
10026 4         11 $dataPos += 8;
10027 4         20 my ($hi, $lo) = unpack('NN', $buff);
10028 4 50 33     33 if ($hi or $lo > 0x7fffffff) {
10029 0 0       0 if ($hi > 0x7fffffff) {
    0          
    0          
10030 0         0 $warnStr = 'Invalid atom size';
10031 0         0 last;
10032             } elsif (not $et->Options('LargeFileSupport')) {
10033 0         0 $warnStr = 'End of processing at large atom (LargeFileSupport not enabled)';
10034 0         0 last;
10035             } elsif ($et->Options('LargeFileSupport') eq '2') {
10036 0         0 $et->Warn('Processing large atom (LargeFileSupport is 2)');
10037             }
10038             }
10039 4         12 $size = $hi * 4294967296 + $lo - 16;
10040 4 50       16 $size < 0 and $warnStr = 'Invalid extended size', last;
10041             } else {
10042 1572         2714 $size -= 8;
10043             }
10044 1576 50       3669 if ($validate) {
10045 0 0 0     0 $et->Warn("Invalid 'wide' atom size") if $tag eq 'wide' and $size;
10046 0 0       0 $$et{ValidatePath} or $$et{ValidatePath} = { };
10047 0         0 my $path = join('-', @{$$et{PATH}}, $tag);
  0         0  
10048 0 0       0 $path =~ s/-Track-/-$$et{SET_GROUP1}-/ if $$et{SET_GROUP1};
10049 0 0 0     0 if ($$et{ValidatePath}{$path} and not $dupTagOK{$tag} and not $dupDirOK{$dirID}) {
      0        
10050 0         0 my $i = Get32u(\$tag,0);
10051 0 0       0 my $str = $i < 255 ? "index $i" : "tag '" . PrintableTagID($tag,2) . "'";
10052 0         0 $et->Warn("Duplicate $str at " . join('-', @{$$et{PATH}}));
  0         0  
10053 0 0       0 $$et{ValidatePath} = { } if $path eq 'MOV-moov'; # avoid warnings for all contained dups
10054             }
10055 0         0 $$et{ValidatePath}{$path} = 1;
10056             }
10057 1576 50 66     4573 if ($isUserData and $$et{SET_GROUP1}) {
10058 0         0 my $tagInfo = $et->GetTagInfo($tagTablePtr, $tag);
10059 0 0 0     0 unless ($tagInfo and $$tagInfo{SubDirectory}) {
10060             # add track name to UserData tags inside tracks
10061 0         0 $tag = $$et{SET_GROUP1} . $tag;
10062 0 0 0     0 if (not $$tagTablePtr{$tag} and $tagInfo) {
10063 0         0 my %newInfo = %$tagInfo;
10064 0         0 foreach ('Name', 'Description') {
10065 0 0       0 next unless $$tagInfo{$_};
10066 0         0 $newInfo{$_} = $$et{SET_GROUP1} . $$tagInfo{$_};
10067 0         0 $newInfo{$_} =~ s/^(Track\d+)Track/$1/; # remove duplicate "Track" in name
10068             }
10069 0         0 AddTagToTable($tagTablePtr, $tag, \%newInfo);
10070             }
10071             }
10072             }
10073             # set flag to store additional information for ExtractEmbedded option
10074 1576         4512 my $handlerType = $$et{HandlerType};
10075 1576 100 100     10768 if ($eeBox{$handlerType} and $eeBox{$handlerType}{$tag}) {
    50 33        
    50 0        
      33        
      0        
10076 124 100 66     450 if ($ee or $hash) {
10077             # (there is another 'gps ' box with a track log that doesn't contain offsets)
10078 68 50 33     225 if ($tag ne 'gps ' or $eeBox{$handlerType}{$tag} eq $dirID) {
10079 68         119 $eeTag = 1;
10080 68         239 $$et{OPTIONS}{Unknown} = 1; # temporarily enable "Unknown" option
10081             }
10082             }
10083             } elsif ($ee > 1 and $eeBox2{$handlerType} and $eeBox2{$handlerType}{$tag}) {
10084 0         0 $eeTag = 1;
10085 0         0 $$et{OPTIONS}{Unknown} = 1;
10086             } elsif ($hash and $hashBox{$handlerType} and $hashBox{$handlerType}{$tag}) {
10087 0         0 $eeTag = 1;
10088 0         0 $$et{OPTIONS}{Unknown} = 1;
10089             }
10090 1576         6364 my $tagInfo = $et->GetTagInfo($tagTablePtr, $tag);
10091              
10092 1576 100       3747 $$et{OPTIONS}{Unknown} = $unkOpt if $eeTag; # restore Unknown option
10093              
10094             # allow numerical tag ID's
10095 1576 100       3976 unless ($tagInfo) {
10096 178         877 my $id = $$et{KeysCount} . '.' . unpack('N', $tag);
10097 178 100       719 if ($$tagTablePtr{$id}) {
10098 37         122 $tagInfo = $et->GetTagInfo($tagTablePtr, $id);
10099 37         66 $tag = $id;
10100             }
10101             }
10102             # generate tagInfo if Unknown option set
10103 1576 50 66     4690 if (not defined $tagInfo and ($$et{OPTIONS}{Unknown} or
      66        
10104             $verbose or $tag =~ /^\xa9/))
10105             {
10106 13         65 my $name = PrintableTagID($tag,1);
10107 13 50       58 if ($name =~ /^xa9(.*)/) {
10108 0         0 $tagInfo = {
10109             Name => "UserData_$1",
10110             Description => "User Data $1",
10111             };
10112             } else {
10113 13         151 $tagInfo = {
10114             Name => "Unknown_$name",
10115             Description => "Unknown $name",
10116             %unknownInfo,
10117             };
10118             }
10119 13         75 AddTagToTable($tagTablePtr, $tag, $tagInfo);
10120             }
10121             # save required tag sizes
10122 1576 100       5109 if ($$tagTablePtr{"$tag-size"}) {
10123 28         188 $et->HandleTag($tagTablePtr, "$tag-size", $size);
10124 28 50       274 $et->HandleTag($tagTablePtr, "$tag-offset", $raf->Tell()+$dirBase) if $$tagTablePtr{"$tag-offset"};
10125             }
10126             # save position/size of 'idat'
10127 1576 50       3866 $$et{MediaDataInfo} = [ $raf->Tell() + $dirBase, $size ] if $tag eq 'idat';
10128             # stop processing at mdat/idat if -fast2 is used
10129 1576 0 0     3344 last if $fast > 1 and ($tag eq 'mdat' or ($tag eq 'idat' and $$et{FileType} ne 'HEIC'));
      33        
10130             # load values only if associated with a tag (or verbose) and not too big
10131 1576 50       3462 if ($size > 0x2000000) { # start to get worried above 32 MiB
10132             # check for RIFF trailer (written by Auto-Vox dashcam)
10133 0 0       0 if ($buff =~ /^(gpsa|gps0|gsen|gsea)...\0/s) { # (yet seen only gpsa as first record)
    0          
10134 0         0 $et->VPrint(0, sprintf("Found RIFF trailer at offset 0x%x",$lastPos));
10135 0 0       0 if ($ee) {
10136 0 0       0 $raf->Seek(-8, 1) or last; # seek back to start of trailer
10137 0         0 my $tbl = GetTagTable('Image::ExifTool::QuickTime::Stream');
10138 0         0 ProcessRIFFTrailer($et, { RAF => $raf }, $tbl);
10139             } else {
10140 0         0 EEWarn($et);
10141             }
10142 0         0 last;
10143             } elsif ($buff eq 'CCCCCCCC') {
10144 0         0 $et->VPrint(0, sprintf("Found Kenwood trailer at offset 0x%x",$lastPos));
10145 0         0 my $tbl = GetTagTable('Image::ExifTool::QuickTime::Stream');
10146 0         0 ProcessKenwoodTrailer($et, { RAF => $raf }, $tbl);
10147 0         0 last;
10148             }
10149 0 0 0     0 if (not $tagInfo or $$tagInfo{Unknown}) {
    0          
    0          
10150 0         0 $ignore = 1;
10151             } elsif ($size > 0x8000000) {
10152 0         0 my $t = PrintableTagID($tag,2);
10153 0 0       0 $et->Warn("Skipping '${t}' atom > 128 MiB", $eeTag ? 2 : 1) and $ignore = 1;
    0          
10154             } elsif (not $eeTag) {
10155 0         0 my $t = PrintableTagID($tag,2);
10156 0 0       0 $et->Warn("Skipping '${t}' atom > 32 MiB", 2) and $ignore = 1;
10157             }
10158             }
10159 1576 100 66     11153 if (defined $tagInfo and not $ignore and not ($tagInfo and $$tagInfo{DontRead})) {
      66        
      66        
10160             # set document number for this item property if necessary
10161 1488 100       4442 if ($$et{IsItemProperty}) {
10162 12         33 my $items = $$et{ItemInfo};
10163 12         24 my ($id, $prop, $docNum, $lowest);
10164 12   50     44 my $primary = $$et{PrimaryItem} || 0;
10165 12   50     48 my $pitem = $$items{$primary} || { };
10166 12 100       64 $$pitem{RefersTo} or $$pitem{RefersTo} = { };
10167 12         85 ItemID: foreach $id (reverse sort { $a <=> $b } keys %$items) {
  44         128  
10168 44 100       125 next unless $$items{$id}{Association};
10169 24         40 my $item = $$items{$id};
10170 24         46 foreach $prop (@{$$item{Association}}) {
  24         60  
10171 45 100       120 next unless $prop == $index;
10172 12   50     46 my $dont = $dontInherit{$tag} || 0;
10173 12 100 0     90 if ($id == $primary or (not $dont and
    100 33        
      66        
      66        
      33        
10174             ($$item{RefersTo} and $$item{RefersTo}{$primary})) or
10175             # special case to assume this property belongs to the primary
10176             # image if it belongs to an item referred to by the primary
10177             ($dont != 1 and $$pitem{RefersTo}{$id}))
10178             {
10179             # this is associated with the primary item or an item describing
10180             # the primary item, so consider this part of the main document
10181 6         13 undef $docNum;
10182 6         12 undef $lowest;
10183 6         21 last ItemID;
10184             } elsif ($$item{DocNum}) {
10185             # this property is already associated with an item that has
10186             # an ExifTool document number, so use the lowest associated DocNum
10187 3 50 33     25 $docNum = $$item{DocNum} if not defined $docNum or $docNum > $$item{DocNum};
10188             } else {
10189             # keep track of the lowest associated item ID
10190 3         9 $lowest = $id;
10191             }
10192             }
10193             }
10194 12 100 100     82 if (not defined $docNum and defined $lowest) {
10195             # this is the first time we've seen metadata from this item,
10196             # so use a new document number
10197 3         12 $docNum = ++$$et{DOC_COUNT};
10198 3         9 $$items{$lowest}{DocNum} = $docNum;
10199             }
10200 12         39 $$et{DOC_NUM} = $docNum;
10201             }
10202 1488         2418 my $val;
10203 1488         5153 my $missing = $size - $raf->Read($val, $size);
10204 1488 50       3377 if ($missing) {
10205 0         0 my $t = PrintableTagID($tag,2);
10206 0         0 $warnStr = "Truncated '${t}' data (missing $missing bytes)";
10207 0         0 last;
10208             }
10209             # use value to get tag info if necessary
10210 1488 100       3423 $tagInfo or $tagInfo = $et->GetTagInfo($tagTablePtr, $tag, \$val);
10211 1488   100     5738 my $hasData = ($$dirInfo{HasData} and $val =~ /^....data\0/s);
10212 1488 0 33     4148 if ($verbose and defined $val and not $hasData) {
      33        
10213 0         0 my $tval;
10214 0 0 0     0 if ($tagInfo and $$tagInfo{Format}) {
10215 0         0 $tval = ReadValue(\$val, 0, $$tagInfo{Format}, $$tagInfo{Count}, length($val));
10216             }
10217             $et->VerboseInfo($tag, $tagInfo,
10218             Value => $tval,
10219             DataPt => \$val,
10220             DataPos => $dataPos,
10221             Size => $size,
10222             Format => $tagInfo ? $$tagInfo{Format} : undef,
10223 0 0       0 Index => $index,
10224             );
10225             # print iref item ID numbers
10226 0 0       0 if ($dirID eq 'iref') {
10227 0         0 my ($id, $count, @to, $i);
10228 0 0       0 if ($$et{ItemRefVersion}) {
10229 0 0       0 ($id, $count, @to) = unpack('NnN*', $val) if length $val >= 10;
10230             } else {
10231 0 0       0 ($id, $count, @to) = unpack('nnn*', $val) if length $val >= 6;
10232             }
10233 0 0       0 defined $id or $id = '', $count = 0;
10234 0 0       0 $id .= " (wrong count: $count)" if $count != @to;
10235             # convert sequential numbers to a range
10236 0         0 for ($i=1; $i<@to; ) {
10237 0 0 0     0 $to[$i-1] =~ /(\d+)$/ and $to[$i] == $1 + 1 or ++$i, next;
10238 0         0 $to[$i-1] =~ s/(-.*)?$/-$to[$i]/;
10239 0         0 splice @to, $i, 1;
10240             }
10241 0         0 $et->VPrint(1, "$$et{INDENT} Item $id refers to: ",join(',',@to),"\n");
10242             }
10243             }
10244             # extract metadata from stream if ExtractEmbedded option is enabled
10245 1488 100       3355 if ($eeTag) {
10246 68         397 ParseTag($et, $tag, \$val);
10247             # forget this tag if we generated it only for ExtractEmbedded
10248 68 100 66     544 undef $tagInfo if $tagInfo and $$tagInfo{Unknown} and not $unkOpt;
      100        
10249             }
10250              
10251             # handle iTunesInfo mean/name/data triplets
10252 1488 100 100     6238 if ($tagInfo and $$tagInfo{Triplet}) {
10253 9 100 66     65 if ($tag eq 'data' and $triplet{mean} and $triplet{name}) {
      33        
10254 3         8 $tag = $triplet{name};
10255             # add 'mean' to name unless it is 'com.apple.iTunes'
10256 3 50       13 $tag = $triplet{mean} . '/' . $tag unless $triplet{mean} eq 'com.apple.iTunes';
10257 3         12 $tagInfo = $et->GetTagInfo($tagTablePtr, $tag, \$val);
10258 3 50       9 unless ($tagInfo) {
10259 0         0 my $name = $triplet{name};
10260 0         0 my $desc = $name;
10261 0         0 $name =~ tr/-_a-zA-Z0-9//dc;
10262 0         0 $desc =~ tr/_/ /;
10263 0         0 $tagInfo = {
10264             Name => $name,
10265             Description => $desc,
10266             };
10267 0         0 $et->VPrint(0, $$et{INDENT}, "[adding QuickTime:$name]\n");
10268 0         0 AddTagToTable($tagTablePtr, $tag, $tagInfo);
10269             }
10270             # ignore 8-byte header
10271 3 50       14 $val = substr($val, 8) if length($val) >= 8;
10272 3 50 33     19 unless ($$tagInfo{Format} or $$tagInfo{SubDirectory}) {
10273             # extract as binary if it contains any non-ASCII or control characters
10274 3 50       15 if ($val =~ /[^\x20-\x7e]/) {
10275 0         0 my $buff = $val;
10276 0         0 $val = \$buff;
10277             }
10278             }
10279 3         6 $$tagInfo{List} = 1; # (allow any of these tags to have multiple data elements)
10280 3 50       10 $et->VerboseInfo($tag, $tagInfo, Value => $val) if $verbose;
10281             } else {
10282 6 50       39 $triplet{$tag} = substr($val,4) if length($val) > 4;
10283 6         16 undef $tagInfo; # don't store this tag
10284             }
10285             }
10286 1488 100       3295 if ($tagInfo) {
10287 1454         2385 my @found;
10288 1454         2809 my $subdir = $$tagInfo{SubDirectory};
10289 1454 100 66     4880 if ($subdir) {
    100          
    100          
10290 863   100     2997 my $start = $$subdir{Start} || 0;
10291 863         2185 my ($base, $dPos) = ($dataPos, 0);
10292 863 50       2305 if ($$subdir{Base}) {
10293 0         0 $dPos -= eval $$subdir{Base};
10294 0         0 $base -= $dPos;
10295             }
10296             my %dirInfo = (
10297             DataPt => \$val,
10298             DataLen => $size,
10299             DirStart => $start,
10300             DirLen => $size - $start,
10301             DirName => $$subdir{DirName} || $$tagInfo{Name},
10302             DirID => $tag,
10303             HasData => $$subdir{HasData},
10304             Multi => $$subdir{Multi},
10305             IgnoreProp => $$subdir{IgnoreProp}, # (XML hack)
10306 863   66     11412 DataPos => $dPos,
10307             Base => $base, # (needed for IsOffset tags in binary data)
10308             );
10309 863 50       2529 $dirInfo{BlockInfo} = $tagInfo if $$tagInfo{BlockExtract};
10310 863 100 66     2443 if ($$subdir{ByteOrder} and $$subdir{ByteOrder} =~ /^Little/) {
10311 9         35 SetByteOrder('II');
10312             }
10313 863         2173 my $oldGroup1 = $$et{SET_GROUP1};
10314 863 100 33     5084 if ($$tagInfo{SubDirectory} and $$tagInfo{SubDirectory}{TagTable} and
      66        
10315             $$tagInfo{SubDirectory}{TagTable} eq 'Image::ExifTool::QuickTime::Track')
10316             {
10317 48 100       217 $track or $track = 0;
10318 48         193 $$et{SET_GROUP1} = 'Track' . (++$track);
10319             }
10320 863         3576 my $subTable = GetTagTable($$subdir{TagTable});
10321 863         1816 my $proc = $$subdir{ProcessProc};
10322             # make ProcessMOV() the default processing procedure for subdirectories
10323 863 100 100     4350 $proc = \&ProcessMOV unless $proc or $$subTable{PROCESS_PROC};
10324 863 50       1992 if ($size > $start) {
10325             # delay processing of ipco box until after all other boxes
10326 863 100 66     3923 if ($tag eq 'ipco' and not $$et{IsItemProperty}) {
    50 33        
10327 3         16 $$et{ItemPropertyContainer} = [ \%dirInfo, $subTable, $proc ];
10328 3         25 $et->VPrint(0,"$$et{INDENT}\[Process ipco box later]");
10329             } elsif ($fast < 2 or not $$tagInfo{MakerNotes}) {
10330 860         3651 $et->ProcessDirectory(\%dirInfo, $subTable, $proc);
10331             }
10332             }
10333 863 100       3303 if ($tag eq 'stbl') {
    100          
10334             # process sample data when exiting SampleTable box if extracting embedded
10335 48 100 66     316 ProcessSamples($et) if $ee or $hash;
10336             } elsif ($tag eq 'minf') {
10337 48         146 $$et{HandlerType} = ''; # reset handler type at end of media info box
10338             }
10339 863         2234 $$et{SET_GROUP1} = $oldGroup1;
10340 863         2265 SetByteOrder('MM');
10341             } elsif ($hasData) {
10342             # handle atoms containing 'data' tags
10343             # (currently ignore contained atoms: 'itif', 'name', etc.)
10344 189         332 my $pos = 0;
10345 189         296 for (;;) {
10346 382 100       857 last if $pos + 16 > $size;
10347 193         941 my ($len, $type, $flags, $ctry, $lang) = unpack("x${pos}Na4Nnn", $val);
10348 193 50 33     742 last if $pos + $len > $size or not $len;
10349 193         359 my ($value, $langInfo, $oldDir);
10350 193         388 my $format = $$tagInfo{Format};
10351 193 50 33     838 if ($type eq 'data' and $len >= 16) {
10352 193         313 $pos += 16;
10353 193         325 $len -= 16;
10354 193         693 $value = substr($val, $pos, $len);
10355             # format flags (ref 12):
10356             # 0x0=binary, 0x1=UTF-8, 0x2=UTF-16, 0x3=ShiftJIS,
10357             # 0x4=UTF-8 0x5=UTF-16, 0xd=JPEG, 0xe=PNG,
10358             # 0x15=signed int, 0x16=unsigned int, 0x17=float,
10359             # 0x18=double, 0x1b=BMP, 0x1c='meta' atom
10360 193 100       611 if ($stringEncoding{$flags}) {
10361             # handle all string formats
10362 144         589 $value = $et->Decode($value, $stringEncoding{$flags});
10363             # (shouldn't be null terminated, but some software writes it anyway)
10364 144 50       519 $value =~ s/\0$// unless $$tagInfo{Binary};
10365             } else {
10366 49 100       216 if (not $format) {
    100          
10367 13         59 $format = QuickTimeFormat($flags, $len);
10368             } elsif ($format =~ /^int\d+([us])$/) {
10369             # adjust integer to available length (but not int64)
10370 16         126 my $fmt = { 1=>'int8', 2=>'int16', 4=>'int32' }->{$len};
10371 16 50       97 $format = $fmt . $1 if defined $fmt;
10372             }
10373 49 100       130 if ($format) {
    50          
10374 36         190 $value = ReadValue(\$value, 0, $format, $$tagInfo{Count}, $len);
10375             } elsif (not $$tagInfo{ValueConv}) {
10376             # make binary data a scalar reference unless a ValueConv exists
10377 13         31 my $buf = $value;
10378 13         36 $value = \$buf;
10379             }
10380             }
10381             }
10382 193 100 66     765 if ($ctry or $lang) {
10383 3         35 my $langCode = GetLangCode($lang, $ctry);
10384 3 50       8 if ($langCode) {
10385             # get tagInfo for other language
10386 3         12 $langInfo = GetLangInfoQT($et, $tagInfo, $langCode);
10387             # save other language tag ID's so we can delete later if necessary
10388 3 50       11 if ($langInfo) {
10389 3 50       13 $$tagInfo{OtherLang} or $$tagInfo{OtherLang} = [ ];
10390 3         7 push @{$$tagInfo{OtherLang}}, $$langInfo{TagID};
  3         11  
10391             }
10392             }
10393             }
10394 193 100       429 $langInfo or $langInfo = $tagInfo;
10395 193 50       686 my $str = $qtFlags{$flags} ? " ($qtFlags{$flags})" : '';
10396 193 0       474 $et->VerboseInfo($tag, $langInfo,
    50          
10397             Value => ref $value ? $$value : $value,
10398             DataPt => \$val,
10399             DataPos => $dataPos,
10400             Start => $pos,
10401             Size => $len,
10402             Format => $format,
10403             Index => $index,
10404             Extra => sprintf(", Type='${type}', Flags=0x%x%s, Lang=0x%.4x",$flags,$str,$lang),
10405             ) if $verbose;
10406 193 50       376 if (defined $value) {
10407             # use "Keys" in path instead of ItemList if this was defined by a Keys tag
10408             # (the only reason for this is to have "Keys" in the family 5 group name)
10409             # Note that the Keys group is specifically set by the ProcessKeys routine,
10410             # even though this tag would be in the ItemList table
10411 193   100     1027 my $isKeys = $$tagInfo{Groups} && $$tagInfo{Groups}{1} && $$tagInfo{Groups}{1} eq 'Keys';
10412 193 100       481 $isKeys and $oldDir = $$et{PATH}[-1], $$et{PATH}[-1] = 'Keys';
10413 193         687 push @found, $et->FoundTag($langInfo, $value);
10414 193 100       508 $$et{PATH}[-1] = $oldDir if $isKeys;
10415             }
10416 193         380 $pos += $len;
10417             }
10418             } elsif ($tag =~ /^\xa9/ or $$tagInfo{IText}) {
10419             # parse international text to extract all languages
10420 71         130 my $pos = 0;
10421 71 50       178 if ($$tagInfo{Format}) {
10422 0         0 push @found, $et->FoundTag($tagInfo, ReadValue(\$val, 0, $$tagInfo{Format}, undef, length($val)));
10423 0         0 $pos = $size;
10424             }
10425 71         108 for (;;) {
10426 142         236 my ($len, $lang);
10427 142 100 66     506 if ($$tagInfo{IText} and $$tagInfo{IText} >= 6) {
10428 4 100       17 last if $pos + $$tagInfo{IText} > $size;
10429 2         4 $pos += $$tagInfo{IText} - 2;
10430 2         6 $lang = unpack("x${pos}n", $val);
10431 2         4 $pos += 2;
10432 2         3 $len = $size - $pos;
10433             } else {
10434 138 100       375 last if $pos + 4 > $size;
10435 69         291 ($len, $lang) = unpack("x${pos}nn", $val);
10436 69         132 $pos += 4;
10437             # according to the QuickTime spec (ref 12), $len should include
10438             # 4 bytes for length and type words, but nobody (including
10439             # Apple, Pentax and Kodak) seems to add these in, so try
10440             # to allow for either
10441 69 50       178 if ($pos + $len > $size) {
10442 0         0 $len -= 4;
10443 0 0 0     0 last if $pos + $len > $size or $len < 0;
10444             }
10445             }
10446             # ignore any empty entries (or null padding) after the first
10447 71 50 33     234 next if not $len and $pos;
10448 71         187 my $str = substr($val, $pos, $len);
10449 71         110 my ($langInfo, $enc);
10450 71 50 33     335 if (($lang < 0x400 or $lang == 0x7fff) and $str !~ /^\xfe\xff/) {
      33        
10451             # this is a Macintosh language code
10452             # a language code of 0 is Macintosh english, so treat as default
10453 71 50       140 if ($lang) {
10454 0 0       0 if ($lang == 0x7fff) {
10455             # technically, ISO 639-2 doesn't have a 2-character
10456             # equivalent for 'und', but use 'un' anyway
10457 0         0 $lang = 'un';
10458             } else {
10459             # use Font.pm to look up language string
10460 0         0 require Image::ExifTool::Font;
10461 0         0 $lang = $Image::ExifTool::Font::ttLang{Macintosh}{$lang};
10462             }
10463             } else {
10464             # for the default language code of 0x0000, use UTF-8 instead
10465             # of the CharsetQuickTime setting if obviously UTF8
10466 71 50       270 $enc = 'UTF8' if Image::ExifTool::IsUTF8(\$str) > 0;
10467             }
10468             # the spec says only "Macintosh text encoding", but
10469             # allow this to be configured by the user
10470 71 50       203 $enc = $charsetQuickTime unless $enc;
10471             } else {
10472             # convert language code to ASCII (ignore read-only bit)
10473 0         0 $lang = UnpackLang($lang);
10474             # may be either UTF-8 or UTF-16BE
10475 0 0       0 $enc = $str=~s/^\xfe\xff// ? 'UTF16' : 'UTF8';
10476             }
10477 71 100       178 unless ($$tagInfo{NoDecode}) {
10478 70         245 $str = $et->Decode($str, $enc);
10479 70         178 $str =~ s/\0+$//; # remove any trailing nulls (eg. 3gp tags)
10480             }
10481 71 100 100     225 if ($$tagInfo{IText} and $$tagInfo{IText} > 6) {
10482 1         4 my $n = $$tagInfo{IText} - 6;
10483             # add back extra bytes (eg. 'rtng' box)
10484 1         4 $str = substr($val, $pos-$n-2, $n) . $str;
10485             }
10486 71 50       148 $langInfo = GetLangInfoQT($et, $tagInfo, $lang) if $lang;
10487 71   33     425 push @found, $et->FoundTag($langInfo || $tagInfo, $str);
10488 71         211 $pos += $len;
10489             }
10490             } else {
10491 331         791 my $format = $$tagInfo{Format};
10492 331 100       860 if ($format) {
10493 102         699 $val = ReadValue(\$val, 0, $format, $$tagInfo{Count}, length($val));
10494             }
10495 331         749 my $oldBase;
10496 331 50       1099 if ($$tagInfo{SetBase}) {
10497 0         0 $oldBase = $$et{BASE};
10498 0         0 $$et{BASE} = $dataPos;
10499             }
10500 331         1568 my $key = $et->FoundTag($tagInfo, $val);
10501 331         966 push @found, $key;
10502 331 50       913 $$et{BASE} = $oldBase if defined $oldBase;
10503             # decode if necessary (NOTE: must be done after RawConv)
10504 331 50 66     3057 if (defined $key and (not $format or $format =~ /^string/) and
      100        
      100        
      66        
      100        
      66        
      66        
10505             not $$tagInfo{Unknown} and not $$tagInfo{ValueConv} and
10506             not $$tagInfo{Binary} and defined $$et{VALUE}{$key} and not ref $val)
10507             {
10508 20         105 my $vp = \$$et{VALUE}{$key};
10509 20 50 66     225 if (not ref $$vp and length($$vp) <= 65536 and $$vp =~ /[\x80-\xff]/) {
      66        
10510             # the encoding of this is not specified, so use CharsetQuickTime
10511             # unless the string is valid UTF-8
10512 0 0       0 my $enc = Image::ExifTool::IsUTF8($vp) > 0 ? 'UTF8' : $charsetQuickTime;
10513 0         0 $$vp = $et->Decode($$vp, $enc);
10514             }
10515             }
10516             }
10517             # tweak family 1 group names for Keys/ItemList/UserData tags in a track
10518 1454 100 66     7986 if ($$et{SET_GROUP1} and ($dirID eq 'ilst' or $dirID eq 'udta') and @found) {
      100        
      66        
10519 4         9 my $type = $trackPath{join '-', @{$$et{PATH}}};
  4         27  
10520 4 50       19 if ($type) {
10521 4   33     21 my $grp = ($avType{$$et{MediaType}} || $$et{SET_GROUP1}) . $type;
10522 4   33     26 defined and $et->SetGroup($_, $grp) foreach @found;
10523             }
10524             }
10525             }
10526             } else {
10527 88 50       258 $et->VerboseInfo($tag, $tagInfo,
10528             Size => $size,
10529             Extra => sprintf(' at offset 0x%.4x', $raf->Tell()),
10530             ) if $verbose;
10531 88         313 my $seekTo = $raf->Tell() + $size;
10532 88 0 33     249 if ($tagInfo and $$tagInfo{DontRead} and $$tagInfo{SubDirectory}) {
      0        
10533             # ignore first trailer if it is the payload of this box
10534 0 0 0     0 $trailer = $$trailer[3] if $trailer and $$trailer[1] == $raf->Tell();
10535 0         0 my $subdir = $$tagInfo{SubDirectory};
10536             my %dirInfo = (
10537             RAF => $raf,
10538             DirName => $$tagInfo{Name},
10539 0         0 DirID => $tag,
10540             DirEnd => $seekTo,
10541             );
10542 0         0 my $subTable = GetTagTable($$subdir{TagTable});
10543 0         0 my $proc = $$subdir{ProcessProc};
10544             # make ProcessMOV() the default processing procedure for subdirectories
10545 0 0 0     0 $proc = \&ProcessMOV unless $proc or $$subTable{PROCESS_PROC};
10546 0         0 $et->ProcessDirectory(\%dirInfo, $subTable, $proc);
10547 0         0 $raf->Seek($seekTo);
10548             }
10549 88 50 33     367 unless ($raf->Seek($seekTo-1) and $raf->Read($buff, 1) == 1) {
10550 0 0       0 if (pack('N',$size) =~ /^]/) { # check for corrupted HEIC file downloaded from heic.digital
10551 0         0 $warnStr = sprintf('Extraneous HTML text appended to file at offset 0x%x', $lastPos);
10552             } else {
10553 0         0 my $t = PrintableTagID($tag,2);
10554 0         0 $warnStr = sprintf("Truncated '${t}' data at offset 0x%x", $lastPos);
10555             }
10556 0         0 last;
10557             }
10558             }
10559 1576 100       3741 $$et{MediaType} = '' if $tag eq 'trak'; # reset track type at end of track
10560 1576         2820 $dataPos += $size + 8; # point to start of next atom data
10561 1576 100 100     6337 last if $dirEnd and $dataPos >= $dirEnd; # (note: ignores last value if 0 bytes)
10562 1115         4096 $lastPos = $raf->Tell() + $dirBase;
10563 1115 50 33     3054 if ($trailer and $lastPos >= $$trailer[1]) {
10564 0         0 $et->Warn(sprintf('%s trailer at offset 0x%x (%d bytes)', @$trailer[0..2]), 1);
10565 0         0 last;
10566             }
10567 1115 100       3397 $raf->Read($buff, 8) == 8 or last;
10568 1090 100 100     5939 $lastTag = $tag if $$tagTablePtr{$tag} and $tag ne 'free'; # (Insta360 sometimes puts free block before trailer)
10569 1090         4338 ($size, $tag) = unpack('Na4', $buff);
10570 1090 100       3413 ++$index if defined $index;
10571             }
10572 486 50       1203 if ($warnStr) {
10573             # assume this is an unknown trailer if it comes immediately after
10574             # mdat or moov and has a tag name we don't recognize
10575 0 0 0     0 if (($lastTag eq 'mdat' or $lastTag eq 'moov') and
      0        
      0        
10576             (not $$tagTablePtr{$tag} or ref $$tagTablePtr{$tag} eq 'HASH' and $$tagTablePtr{$tag}{Unknown}))
10577             {
10578 0         0 $et->Warn('Unknown trailer with '.lcfirst($warnStr));
10579             } else {
10580 0         0 $et->Warn($warnStr);
10581             }
10582             }
10583             # tweak file type based on track content ("iso*" and "dash" ftyp only ["mp42" added in 13.39])
10584 486 0 66     1500 if ($topLevel and $$et{FileType} and $$et{FileType} eq 'MP4' and
      33        
      33        
      0        
      0        
      0        
      0        
10585             $$et{save_ftyp} and $$et{HasHandler} and $$et{save_ftyp} =~ /^(iso|dash|mp42)/ and
10586             $$et{HasHandler}{soun} and not $$et{HasHandler}{vide})
10587             {
10588 0         0 $et->OverrideFileType('M4A', 'audio/mp4');
10589             }
10590             # fill in missing defaults for alternate language tags
10591             # (the first language is taken as the default)
10592 486 100 100     1139 if ($doDefaultLang and $$et{QTLang}) {
10593 2         6 QTLang: foreach $tag (@{$$et{QTLang}}) {
  2         7  
10594 3 50       12 next unless defined $$et{VALUE}{$tag};
10595 3 50       11 my $langInfo = $$et{TAG_INFO}{$tag} or next;
10596 3 50       9 my $tagInfo = $$langInfo{SrcTagInfo} or next;
10597 3         7 my $infoHash = $$et{TAG_INFO};
10598 3         7 my $name = $$tagInfo{Name};
10599             # loop through all instances of this tag name and generate the default-language
10600             # version only if we don't already have a QuickTime tag with this name
10601 3         6 my ($i, $key);
10602 3         9 for ($i=0, $key=$name; $$infoHash{$key}; ++$i, $key="$name ($i)") {
10603 3 50       15 next QTLang if $et->GetGroup($key, 0) eq 'QuickTime';
10604             }
10605 0         0 $key = $et->FoundTag($tagInfo, $$et{VALUE}{$tag});
10606             # copy extra tag information (groups, etc) to the synthetic tag
10607 0         0 $$et{TAG_EXTRA}{$key} = $$et{TAG_EXTRA}{$tag};
10608 0         0 $et->VPrint(0, "(synthesized default-language tag for QuickTime:$$tagInfo{Name})");
10609             }
10610 2         8 delete $$et{QTLang};
10611             }
10612             # process item information now that we are done processing its 'meta' container
10613 486 100 100     2126 HandleItemInfo($et) if $topLevel or $dirID eq 'meta';
10614              
10615             # process linked list of trailers
10616 486         1182 for (; $trailer; $trailer=$$trailer[3]) {
10617 0 0       0 next if $lastPos > $$trailer[1]; # skip if we have already processed this as an atom
10618 0 0       0 last unless $raf->Seek($$trailer[1], 0);
10619 0 0 0     0 if ($$trailer[0] eq 'LigoGPS' and $raf->Read($buff, 8) == 8 and $buff =~ /skip$/i) {
    0 0        
    0 0        
10620 0 0       0 $ee or $et->Warn('Use the ExtractEmbedded option to decode timed GPS',3), next;
10621 0         0 my $len = Get32u(\$buff, 0) - 16;
10622 0 0 0     0 if ($len > 0 and $raf->Read($buff, $len) == $len and $buff =~ /^LIGOGPSINFO\0/) {
      0        
10623 0         0 my $tbl = GetTagTable('Image::ExifTool::QuickTime::Stream');
10624 0         0 my %dirInfo = ( DataPt => \$buff, DataPos => $$trailer[1] + 8, DirName => 'LigoGPSTrailer' );
10625 0         0 $et->VerboseDump(\$buff, DataPos => $dirInfo{DataPos});
10626 0         0 Image::ExifTool::LigoGPS::ProcessLigoGPS($et, \%dirInfo, $tbl);
10627             } else {
10628 0         0 $et->Warn('Unrecognized data in LigoGPS trailer');
10629             }
10630             } elsif ($$trailer[0] eq 'Insta360' and $ee) {
10631             # process Insta360 trailer if it exists
10632 0 0       0 $raf->Seek(0, 2) or $et->Warn('Seek error'), last;
10633 0         0 my $offset = $raf->Tell() - $$trailer[1] - $$trailer[2];
10634 0         0 ProcessInsta360($et, { RAF => $raf, DirName => $$trailer[0], Offset => $offset });
10635             } elsif ($$trailer[0] eq 'MIE') {
10636 0         0 require Image::ExifTool::MIE;
10637 0         0 Image::ExifTool::MIE::ProcessMIE($et, { RAF => $raf, DirName => 'MIE', Trailer => 1 });
10638             }
10639             }
10640             # brute force scan for metadata embedded in media data
10641             # (and process Insta360 trailer if it exists)
10642 486 100 100     1709 ScanMediaData($et) if $ee and $topLevel and not $$et{OPTIONS}{FastScan};
      66        
10643              
10644             # restore any changed options
10645 486         1430 $et->Options($_ => $saveOptions{$_}) foreach keys %saveOptions;
10646 486         3275 return 1;
10647             }
10648              
10649             #------------------------------------------------------------------------------
10650             # Process a QuickTime Image File
10651             # Inputs: 0) ExifTool object reference, 1) directory information reference
10652             # Returns: 1 on success
10653             sub ProcessQTIF($$)
10654             {
10655 0     0 0   my ($et, $dirInfo) = @_;
10656 0           my $table = GetTagTable('Image::ExifTool::QuickTime::ImageFile');
10657 0           return ProcessMOV($et, $dirInfo, $table);
10658             }
10659              
10660             #==============================================================================
10661             # Autoload LigoGPS module if necessary
10662             # NOTE: Switches to package LigoGPS!
10663             #
10664             package Image::ExifTool::LigoGPS;
10665 31     31   459 use vars qw($AUTOLOAD);
  31         84  
  31         5116  
10666             sub AUTOLOAD {
10667 0     0     require Image::ExifTool::LigoGPS;
10668 0 0         unless (defined &$AUTOLOAD) {
10669 0           my @caller = caller(0);
10670             # reproduce Perl's standard 'undefined subroutine' message:
10671 0           die "Undefined subroutine $AUTOLOAD called at $caller[1] line $caller[2]\n";
10672             }
10673 31     31   303 no strict 'refs';
  31         99  
  31         44653  
10674 0           return &$AUTOLOAD(@_); # call the function
10675             }
10676             #==============================================================================
10677              
10678             1; # end
10679              
10680             __END__